diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2021-04-11 21:09:35 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2021-04-11 21:09:35 +0900 |
| commit | a88e486468b53145d7745411db02fe507ddffb78 (patch) | |
| tree | 08d4726422ef4d30ccb67f3f8b5275e00f800707 /src | |
| parent | Resolve #7425 (diff) | |
| download | sharkey-a88e486468b53145d7745411db02fe507ddffb78.tar.gz sharkey-a88e486468b53145d7745411db02fe507ddffb78.tar.bz2 sharkey-a88e486468b53145d7745411db02fe507ddffb78.zip | |
Tweak UI
Diffstat (limited to 'src')
| -rw-r--r-- | src/client/components/form/base.vue | 8 | ||||
| -rw-r--r-- | src/client/components/form/form.scss | 12 | ||||
| -rw-r--r-- | src/client/components/global/a.vue | 6 | ||||
| -rw-r--r-- | src/client/components/modal-page-window.vue | 211 | ||||
| -rw-r--r-- | src/client/components/ui/modal-window.vue | 7 | ||||
| -rw-r--r-- | src/client/os.ts | 9 | ||||
| -rw-r--r-- | src/client/pages/settings/index.vue | 114 | ||||
| -rw-r--r-- | src/client/router.ts | 2 | ||||
| -rw-r--r-- | src/client/ui/default.sidebar.vue | 4 |
9 files changed, 312 insertions, 61 deletions
diff --git a/src/client/components/form/base.vue b/src/client/components/form/base.vue index 249b49c675..b2e429d6bf 100644 --- a/src/client/components/form/base.vue +++ b/src/client/components/form/base.vue @@ -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 { diff --git a/src/client/components/form/form.scss b/src/client/components/form/form.scss index c7f4373544..d9486430be 100644 --- a/src/client/components/form/form.scss +++ b/src/client/components/form/form.scss @@ -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; diff --git a/src/client/components/global/a.vue b/src/client/components/global/a.vue index a8a597b2bb..7ad62a7310 100644 --- a/src/client/components/global/a.vue +++ b/src/client/components/global/a.vue @@ -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(); } } diff --git a/src/client/components/modal-page-window.vue b/src/client/components/modal-page-window.vue new file mode 100644 index 0000000000..a9a5c0ce57 --- /dev/null +++ b/src/client/components/modal-page-window.vue @@ -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> diff --git a/src/client/components/ui/modal-window.vue b/src/client/components/ui/modal-window.vue index ca17ae6093..90b803801d 100644 --- a/src/client/components/ui/modal-window.vue +++ b/src/client/components/ui/modal-window.vue @@ -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'], diff --git a/src/client/os.ts b/src/client/os.ts index e1707531de..b159cf509d 100644 --- a/src/client/os.ts +++ b/src/client/os.ts @@ -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, { diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index a7ec086bb5..0387d66ae8 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -1,40 +1,42 @@ <template> <div class="vvcocwet" :class="{ wide: !narrow }" ref="el"> - <FormBase class="nav" v-if="!narrow || page == null" :force-wide="!narrow"> - <FormGroup> - <template #label>{{ $ts.basicSettings }}</template> - <FormLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><Fa :icon="faUser"/></template>{{ $ts.profile }}</FormLink> - <FormLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><Fa :icon="faLockOpen"/></template>{{ $ts.privacy }}</FormLink> - <FormLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><Fa :icon="faLaugh"/></template>{{ $ts.reaction }}</FormLink> - <FormLink :active="page === 'drive'" replace to="/settings/drive"><template #icon><Fa :icon="faCloud"/></template>{{ $ts.drive }}</FormLink> - <FormLink :active="page === 'notifications'" replace to="/settings/notifications"><template #icon><Fa :icon="faBell"/></template>{{ $ts.notifications }}</FormLink> - <FormLink :active="page === 'email'" replace to="/settings/email"><template #icon><Fa :icon="faEnvelope"/></template>{{ $ts.email }}</FormLink> - <FormLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><Fa :icon="faShareAlt"/></template>{{ $ts.integration }}</FormLink> - <FormLink :active="page === 'security'" replace to="/settings/security"><template #icon><Fa :icon="faLock"/></template>{{ $ts.security }}</FormLink> - </FormGroup> - <FormGroup> - <template #label>{{ $ts.clientSettings }}</template> - <FormLink :active="page === 'general'" replace to="/settings/general"><template #icon><Fa :icon="faCogs"/></template>{{ $ts.general }}</FormLink> - <FormLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><Fa :icon="faPalette"/></template>{{ $ts.theme }}</FormLink> - <FormLink :active="page === 'sidebar'" replace to="/settings/sidebar"><template #icon><Fa :icon="faListUl"/></template>{{ $ts.sidebar }}</FormLink> - <FormLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><Fa :icon="faMusic"/></template>{{ $ts.sounds }}</FormLink> - <FormLink :active="page === 'plugin'" replace to="/settings/plugin"><template #icon><Fa :icon="faPlug"/></template>{{ $ts.plugins }}</FormLink> - </FormGroup> - <FormGroup> - <template #label>{{ $ts.otherSettings }}</template> - <FormLink :active="page === 'import-export'" replace to="/settings/import-export"><template #icon><Fa :icon="faBoxes"/></template>{{ $ts.importAndExport }}</FormLink> - <FormLink :active="page === 'mute-block'" replace to="/settings/mute-block"><template #icon><Fa :icon="faBan"/></template>{{ $ts.muteAndBlock }}</FormLink> - <FormLink :active="page === 'word-mute'" replace to="/settings/word-mute"><template #icon><Fa :icon="faCommentSlash"/></template>{{ $ts.wordMute }}</FormLink> - <FormLink :active="page === 'api'" replace to="/settings/api"><template #icon><Fa :icon="faKey"/></template>API</FormLink> - <FormLink :active="page === 'other'" replace to="/settings/other"><template #icon><Fa :icon="faEllipsisH"/></template>{{ $ts.other }}</FormLink> - </FormGroup> - <FormGroup> - <FormButton @click="clear">{{ $ts.clearCache }}</FormButton> - </FormGroup> - <FormGroup> - <FormButton @click="logout" danger>{{ $ts.logout }}</FormButton> - </FormGroup> - </FormBase> + <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> + <FormLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><Fa :icon="faLockOpen"/></template>{{ $ts.privacy }}</FormLink> + <FormLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><Fa :icon="faLaugh"/></template>{{ $ts.reaction }}</FormLink> + <FormLink :active="page === 'drive'" replace to="/settings/drive"><template #icon><Fa :icon="faCloud"/></template>{{ $ts.drive }}</FormLink> + <FormLink :active="page === 'notifications'" replace to="/settings/notifications"><template #icon><Fa :icon="faBell"/></template>{{ $ts.notifications }}</FormLink> + <FormLink :active="page === 'email'" replace to="/settings/email"><template #icon><Fa :icon="faEnvelope"/></template>{{ $ts.email }}</FormLink> + <FormLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><Fa :icon="faShareAlt"/></template>{{ $ts.integration }}</FormLink> + <FormLink :active="page === 'security'" replace to="/settings/security"><template #icon><Fa :icon="faLock"/></template>{{ $ts.security }}</FormLink> + </FormGroup> + <FormGroup> + <template #label>{{ $ts.clientSettings }}</template> + <FormLink :active="page === 'general'" replace to="/settings/general"><template #icon><Fa :icon="faCogs"/></template>{{ $ts.general }}</FormLink> + <FormLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><Fa :icon="faPalette"/></template>{{ $ts.theme }}</FormLink> + <FormLink :active="page === 'sidebar'" replace to="/settings/sidebar"><template #icon><Fa :icon="faListUl"/></template>{{ $ts.sidebar }}</FormLink> + <FormLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><Fa :icon="faMusic"/></template>{{ $ts.sounds }}</FormLink> + <FormLink :active="page === 'plugin'" replace to="/settings/plugin"><template #icon><Fa :icon="faPlug"/></template>{{ $ts.plugins }}</FormLink> + </FormGroup> + <FormGroup> + <template #label>{{ $ts.otherSettings }}</template> + <FormLink :active="page === 'import-export'" replace to="/settings/import-export"><template #icon><Fa :icon="faBoxes"/></template>{{ $ts.importAndExport }}</FormLink> + <FormLink :active="page === 'mute-block'" replace to="/settings/mute-block"><template #icon><Fa :icon="faBan"/></template>{{ $ts.muteAndBlock }}</FormLink> + <FormLink :active="page === 'word-mute'" replace to="/settings/word-mute"><template #icon><Fa :icon="faCommentSlash"/></template>{{ $ts.wordMute }}</FormLink> + <FormLink :active="page === 'api'" replace to="/settings/api"><template #icon><Fa :icon="faKey"/></template>API</FormLink> + <FormLink :active="page === 'other'" replace to="/settings/other"><template #icon><Fa :icon="faEllipsisH"/></template>{{ $ts.other }}</FormLink> + </FormGroup> + <FormGroup> + <FormButton @click="clear">{{ $ts.clearCache }}</FormButton> + </FormGroup> + <FormGroup> + <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; - } - } } } } diff --git a/src/client/router.ts b/src/client/router.ts index 53516db97b..3effb2edbe 100644 --- a/src/client/router.ts +++ b/src/client/router.ts @@ -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') }, diff --git a/src/client/ui/default.sidebar.vue b/src/client/ui/default.sidebar.vue index 710a9b1f85..6323393f22 100644 --- a/src/client/ui/default.sidebar.vue +++ b/src/client/ui/default.sidebar.vue @@ -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() { |