mirror of
https://git.joinsharkey.org/Sharkey/Sharkey.git
synced 2024-11-23 12:23:09 +02:00
Tweak UI
This commit is contained in:
parent
c22ff4c556
commit
a88e486468
9 changed files with 312 additions and 61 deletions
|
@ -20,12 +20,16 @@ export default defineComponent({
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.rbusrurv {
|
||||
// 他のCSSからも参照されるので消さないように
|
||||
--formXPadding: 32px;
|
||||
--formYPadding: 32px;
|
||||
|
||||
line-height: 1.4em;
|
||||
background: var(--bg);
|
||||
padding: 32px;
|
||||
padding: var(--formYPadding) var(--formXPadding);
|
||||
|
||||
&:not(.wide).max-width_400px {
|
||||
padding: 32px 0;
|
||||
--formXPadding: 0px;
|
||||
|
||||
> ::v-deep(*) {
|
||||
._formPanel {
|
||||
|
|
|
@ -10,9 +10,17 @@
|
|||
}
|
||||
|
||||
._formLabel {
|
||||
position: sticky;
|
||||
top: var(--stickyTop, 0px);
|
||||
background: var(--bg);
|
||||
z-index: 2;
|
||||
font-size: 80%;
|
||||
padding: 0 16px 8px 16px;
|
||||
opacity: 0.8;
|
||||
margin: -8px calc(var(--formXPadding) * -1) 0 calc(var(--formXPadding) * -1);
|
||||
padding: 8px calc(16px + var(--formXPadding)) 8px calc(16px + var(--formXPadding));
|
||||
color: var(--fgTransparentWeak);
|
||||
background: var(--X17);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
|
|
|
@ -93,6 +93,10 @@ export default defineComponent({
|
|||
os.pageWindow(this.to);
|
||||
},
|
||||
|
||||
modalWindow() {
|
||||
os.modalPageWindow(this.to);
|
||||
},
|
||||
|
||||
popout() {
|
||||
popout(this.to);
|
||||
},
|
||||
|
@ -111,6 +115,8 @@ export default defineComponent({
|
|||
if (this.behavior) {
|
||||
if (this.behavior === 'window') {
|
||||
return this.window();
|
||||
} else if (this.behavior === 'modalWindow') {
|
||||
return this.modalWindow();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
211
src/client/components/modal-page-window.vue
Normal file
211
src/client/components/modal-page-window.vue
Normal file
|
@ -0,0 +1,211 @@
|
|||
<template>
|
||||
<MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')">
|
||||
<div class="hrmcaedk _popup _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }">
|
||||
<div class="header">
|
||||
<button class="_button" @click="back()" v-if="history.length > 0"><Fa :icon="faChevronLeft"/></button>
|
||||
<button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button>
|
||||
<span class="title">
|
||||
<XHeader :info="pageInfo" :with-back="false"/>
|
||||
</span>
|
||||
<button class="_button" @click="$refs.modal.close()"><Fa :icon="faTimes"/></button>
|
||||
</div>
|
||||
<div class="body _flat_">
|
||||
<component :is="component" v-bind="props" :ref="changePage"/>
|
||||
</div>
|
||||
</div>
|
||||
</MkModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExternalLinkAlt, faExpandAlt, faLink, faChevronLeft, faColumns, faTimes } from '@fortawesome/free-solid-svg-icons';
|
||||
import MkModal from '@client/components/ui/modal.vue';
|
||||
import XHeader from '@client/ui/_common_/header.vue';
|
||||
import { popout } from '@client/scripts/popout';
|
||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||
import { resolve } from '@client/router';
|
||||
import { url } from '@client/config';
|
||||
import * as symbols from '@client/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkModal,
|
||||
XHeader,
|
||||
},
|
||||
|
||||
inject: {
|
||||
sideViewHook: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
|
||||
provide() {
|
||||
return {
|
||||
navHook: (path) => {
|
||||
this.navigate(path);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
initialPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
initialComponent: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
initialProps: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
width: 850,
|
||||
height: 650,
|
||||
pageInfo: null,
|
||||
path: this.initialPath,
|
||||
component: this.initialComponent,
|
||||
props: this.initialProps,
|
||||
history: [],
|
||||
faChevronLeft, faTimes,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
url(): string {
|
||||
return url + this.path;
|
||||
},
|
||||
|
||||
contextmenu() {
|
||||
return [{
|
||||
type: 'label',
|
||||
text: this.path,
|
||||
}, {
|
||||
icon: faExpandAlt,
|
||||
text: this.$ts.showInPage,
|
||||
action: this.expand
|
||||
}, this.sideViewHook ? {
|
||||
icon: faColumns,
|
||||
text: this.$ts.openInSideView,
|
||||
action: () => {
|
||||
this.sideViewHook(this.path);
|
||||
this.$refs.window.close();
|
||||
}
|
||||
} : undefined, {
|
||||
icon: faExternalLinkAlt,
|
||||
text: this.$ts.popout,
|
||||
action: this.popout
|
||||
}, null, {
|
||||
icon: faExternalLinkAlt,
|
||||
text: this.$ts.openInNewTab,
|
||||
action: () => {
|
||||
window.open(this.url, '_blank');
|
||||
this.$refs.window.close();
|
||||
}
|
||||
}, {
|
||||
icon: faLink,
|
||||
text: this.$ts.copyLink,
|
||||
action: () => {
|
||||
copyToClipboard(this.url);
|
||||
}
|
||||
}];
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
changePage(page) {
|
||||
if (page == null) return;
|
||||
if (page[symbols.PAGE_INFO]) {
|
||||
this.pageInfo = page[symbols.PAGE_INFO];
|
||||
}
|
||||
},
|
||||
|
||||
navigate(path, record = true) {
|
||||
if (record) this.history.push(this.path);
|
||||
this.path = path;
|
||||
const { component, props } = resolve(path);
|
||||
this.component = component;
|
||||
this.props = props;
|
||||
},
|
||||
|
||||
back() {
|
||||
this.navigate(this.history.pop(), false);
|
||||
},
|
||||
|
||||
expand() {
|
||||
this.$router.push(this.path);
|
||||
this.$refs.window.close();
|
||||
},
|
||||
|
||||
popout() {
|
||||
popout(this.path, this.$el);
|
||||
this.$refs.window.close();
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.hrmcaedk {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
contain: content;
|
||||
|
||||
--root-margin: 24px;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
--root-margin: 16px;
|
||||
}
|
||||
|
||||
> .header {
|
||||
$height: 54px;
|
||||
$height-narrow: 42px;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0px 1px var(--divider);
|
||||
|
||||
> button {
|
||||
height: $height;
|
||||
width: $height;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
height: $height-narrow;
|
||||
width: $height-narrow;
|
||||
}
|
||||
}
|
||||
|
||||
> .title {
|
||||
flex: 1;
|
||||
line-height: $height;
|
||||
padding-left: 32px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
pointer-events: none;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
line-height: $height-narrow;
|
||||
padding-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
> button + .title {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> .body {
|
||||
overflow: auto;
|
||||
background: var(--bg);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')">
|
||||
<div class="ebkgoccj _popup _narrow_" @keydown="onKeydown" :style="{ width: `${width}px`, height: height ? `${height}px` : null }">
|
||||
<div class="ebkgoccj _popup _narrow_" @keydown="onKeydown" :style="{ width: `${width}px`, height: scroll ? (height ? `${height}px` : null) : (height ? `min(${height}px, 100%)` : '100%') }">
|
||||
<div class="header">
|
||||
<button class="_button" v-if="withOkButton" @click="$emit('close')"><Fa :icon="faTimes"/></button>
|
||||
<span class="title">
|
||||
|
@ -61,6 +61,11 @@ export default defineComponent({
|
|||
required: false,
|
||||
default: true,
|
||||
},
|
||||
scroll: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['click', 'close', 'closed', 'ok'],
|
||||
|
|
|
@ -203,6 +203,15 @@ export function pageWindow(path: string) {
|
|||
}, {}, 'closed');
|
||||
}
|
||||
|
||||
export function modalPageWindow(path: string) {
|
||||
const { component, props } = resolve(path);
|
||||
popup(import('@client/components/modal-page-window.vue'), {
|
||||
initialPath: path,
|
||||
initialComponent: markRaw(component),
|
||||
initialProps: props,
|
||||
}, {}, 'closed');
|
||||
}
|
||||
|
||||
export function dialog(props: Record<string, any>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(import('@client/components/dialog.vue'), props, {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<div class="vvcocwet" :class="{ wide: !narrow }" ref="el">
|
||||
<FormBase class="nav" v-if="!narrow || page == null" :force-wide="!narrow">
|
||||
<div class="nav" v-if="!narrow || page == null">
|
||||
<FormBase>
|
||||
<FormGroup>
|
||||
<template #label>{{ $ts.basicSettings }}</template>
|
||||
<FormLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><Fa :icon="faUser"/></template>{{ $ts.profile }}</FormLink>
|
||||
|
@ -35,6 +36,7 @@
|
|||
<FormButton @click="logout" danger>{{ $ts.logout }}</FormButton>
|
||||
</FormGroup>
|
||||
</FormBase>
|
||||
</div>
|
||||
<div class="main">
|
||||
<component :is="component" :key="page" @info="onInfo" v-bind="pageProps"/>
|
||||
</div>
|
||||
|
@ -64,7 +66,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
props: {
|
||||
page: {
|
||||
initialPage: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
|
@ -75,6 +77,7 @@ export default defineComponent({
|
|||
title: i18n.locale.settings,
|
||||
icon: faCog
|
||||
});
|
||||
const page = ref(props.initialPage);
|
||||
const narrow = ref(false);
|
||||
const view = ref(null);
|
||||
const el = ref(null);
|
||||
|
@ -83,8 +86,8 @@ export default defineComponent({
|
|||
};
|
||||
const pageProps = ref({});
|
||||
const component = computed(() => {
|
||||
if (props.page == null) return null;
|
||||
switch (props.page) {
|
||||
if (page.value == null) return null;
|
||||
switch (page.value) {
|
||||
case 'profile': return defineAsyncComponent(() => import('./profile.vue'));
|
||||
case 'privacy': return defineAsyncComponent(() => import('./privacy.vue'));
|
||||
case 'reaction': return defineAsyncComponent(() => import('./reaction.vue'));
|
||||
|
@ -117,10 +120,10 @@ export default defineComponent({
|
|||
case 'registry': return defineAsyncComponent(() => import('./registry.vue'));
|
||||
case 'experimental-features': return defineAsyncComponent(() => import('./experimental-features.vue'));
|
||||
}
|
||||
if (props.page.startsWith('registry/keys/system/')) {
|
||||
if (page.value.startsWith('registry/keys/system/')) {
|
||||
return defineAsyncComponent(() => import('./registry.keys.vue'));
|
||||
}
|
||||
if (props.page.startsWith('registry/value/system/')) {
|
||||
if (page.value.startsWith('registry/value/system/')) {
|
||||
return defineAsyncComponent(() => import('./registry.value.vue'));
|
||||
}
|
||||
});
|
||||
|
@ -128,12 +131,12 @@ export default defineComponent({
|
|||
watch(component, () => {
|
||||
pageProps.value = {};
|
||||
|
||||
if (props.page) {
|
||||
if (props.page.startsWith('registry/keys/system/')) {
|
||||
pageProps.value.scope = props.page.replace('registry/keys/system/', '').split('/');
|
||||
if (page.value) {
|
||||
if (page.value.startsWith('registry/keys/system/')) {
|
||||
pageProps.value.scope = page.value.replace('registry/keys/system/', '').split('/');
|
||||
}
|
||||
if (props.page.startsWith('registry/value/system/')) {
|
||||
const path = props.page.replace('registry/value/system/', '').split('/');
|
||||
if (page.value.startsWith('registry/value/system/')) {
|
||||
const path = page.value.replace('registry/value/system/', '').split('/');
|
||||
pageProps.value.xKey = path.pop();
|
||||
pageProps.value.scope = path;
|
||||
}
|
||||
|
@ -144,12 +147,20 @@ export default defineComponent({
|
|||
});
|
||||
}, { immediate: true });
|
||||
|
||||
watch(() => props.initialPage, () => {
|
||||
page.value = props.initialPage;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
narrow.value = el.value.offsetWidth < 1025;
|
||||
narrow.value = el.value.offsetWidth < 800;
|
||||
if (!narrow.value) {
|
||||
page.value = 'profile';
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
[symbols.PAGE_INFO]: INFO,
|
||||
page,
|
||||
narrow,
|
||||
view,
|
||||
el,
|
||||
|
@ -176,25 +187,20 @@ export default defineComponent({
|
|||
display: flex;
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
|
||||
> .nav {
|
||||
width: 32%;
|
||||
box-sizing: border-box;
|
||||
border-right: solid 0.5px var(--divider);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
> .main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: auto;
|
||||
--baseContentWidth: 100%;
|
||||
|
||||
::v-deep(._section) {
|
||||
padding: 0 0 32px 0;
|
||||
|
||||
& + ._section {
|
||||
padding-top: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ export const router = createRouter({
|
|||
{ path: '/@:user/pages/:page', component: page('page'), props: route => ({ pageName: route.params.page, username: route.params.user }) },
|
||||
{ path: '/@:user/pages/:pageName/view-source', component: page('page-editor/page-editor'), props: route => ({ initUser: route.params.user, initPageName: route.params.pageName }) },
|
||||
{ path: '/@:acct/room', props: true, component: page('room/room') },
|
||||
{ path: '/settings/:page(.*)?', name: 'settings', component: page('settings/index'), props: route => ({ page: route.params.page || null }) },
|
||||
{ path: '/settings/:page(.*)?', name: 'settings', component: page('settings/index'), props: route => ({ initialPage: route.params.page || null }) },
|
||||
{ path: '/announcements', component: page('announcements') },
|
||||
{ path: '/about', component: page('about') },
|
||||
{ path: '/about-misskey', component: page('about-misskey') },
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<Fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $ts.more }}</span>
|
||||
<i v-if="otherNavItemIndicated"><Fa :icon="faCircle"/></i>
|
||||
</button>
|
||||
<MkA class="item" active-class="active" to="/settings">
|
||||
<MkA class="item" active-class="active" to="/settings" :behavior="settingsWindowed ? 'modalWindow' : null">
|
||||
<Fa :icon="faCog" fixed-width/><span class="text">{{ $ts.settings }}</span>
|
||||
</MkA>
|
||||
</div>
|
||||
|
@ -57,6 +57,7 @@ export default defineComponent({
|
|||
connection: null,
|
||||
menuDef: sidebarDef,
|
||||
iconOnly: false,
|
||||
settingsWindowed: false,
|
||||
faGripVertical, faChevronLeft, faComments, faHashtag, faBroadcastTower, faFireAlt, faEllipsisH, faPencilAlt, faBars, faTimes, faBell, faSearch, faUserCog, faCog, faUser, faHome, faStar, faCircle, faAt, faEnvelope, faListUl, faPlus, faUserClock, faLaugh, faUsers, faTachometerAlt, faExchangeAlt, faGlobe, faChartBar, faCloud, faServer, faProjectDiagram
|
||||
};
|
||||
},
|
||||
|
@ -102,6 +103,7 @@ export default defineComponent({
|
|||
methods: {
|
||||
calcViewState() {
|
||||
this.iconOnly = (window.innerWidth <= 1400) || (this.$store.state.sidebarDisplay === 'icon');
|
||||
this.settingsWindowed = (window.innerWidth > 1400);
|
||||
},
|
||||
|
||||
post() {
|
||||
|
|
Loading…
Reference in a new issue