summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-04-11 21:09:35 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-04-11 21:09:35 +0900
commita88e486468b53145d7745411db02fe507ddffb78 (patch)
tree08d4726422ef4d30ccb67f3f8b5275e00f800707 /src/client
parentResolve #7425 (diff)
downloadsharkey-a88e486468b53145d7745411db02fe507ddffb78.tar.gz
sharkey-a88e486468b53145d7745411db02fe507ddffb78.tar.bz2
sharkey-a88e486468b53145d7745411db02fe507ddffb78.zip
Tweak UI
Diffstat (limited to 'src/client')
-rw-r--r--src/client/components/form/base.vue8
-rw-r--r--src/client/components/form/form.scss12
-rw-r--r--src/client/components/global/a.vue6
-rw-r--r--src/client/components/modal-page-window.vue211
-rw-r--r--src/client/components/ui/modal-window.vue7
-rw-r--r--src/client/os.ts9
-rw-r--r--src/client/pages/settings/index.vue114
-rw-r--r--src/client/router.ts2
-rw-r--r--src/client/ui/default.sidebar.vue4
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() {