diff --git a/src/client/components/modal-page-window.vue b/src/client/components/modal-page-window.vue index 7be4045a84..ddf8ac446e 100644 --- a/src/client/components/modal-page-window.vue +++ b/src/client/components/modal-page-window.vue @@ -2,12 +2,9 @@ <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" @contextmenu="onContextmenu"> - <button class="_button" @click="back()" v-if="history.length > 0"><i class="fas fa-chevron-left"></i></button> - <button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button> <span class="title"> - <XHeader :info="pageInfo" :with-back="false"/> + <XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="$refs.modal.close()"/> </span> - <button class="_button" @click="$refs.modal.close()"><i class="fas fa-times"></i></button> </div> <div class="body _flat_"> <keep-alive> @@ -177,35 +174,19 @@ export default defineComponent({ 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; + height: $height; font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - pointer-events: none; @media (max-width: 500px) { - line-height: $height-narrow; + height: $height-narrow; padding-left: 16px; } } - - > button + .title { - padding-left: 0; - } } > .body { diff --git a/src/client/components/page-window.vue b/src/client/components/page-window.vue index 26499f7054..c83b040dd8 100644 --- a/src/client/components/page-window.vue +++ b/src/client/components/page-window.vue @@ -3,16 +3,12 @@ :initial-width="500" :initial-height="500" :can-resize="true" - :close-right="true" + :close-button="false" :contextmenu="contextmenu" @closed="$emit('closed')" > <template #header> - <XHeader :info="pageInfo" :with-back="false"/> - </template> - <template #buttons> - <button class="_button" @click="back()" v-if="history.length > 0"><i class="fas fa-chevron-left"></i></button> - <button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button> + <XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="close()"/> </template> <div class="yrolvcoq _flat_"> <component :is="component" v-bind="props" :ref="changePage"/> @@ -139,6 +135,10 @@ export default defineComponent({ this.navigate(this.history.pop(), false); }, + close() { + this.$refs.window.close(); + }, + expand() { this.$router.push(this.path); this.$refs.window.close(); @@ -155,6 +155,5 @@ export default defineComponent({ <style lang="scss" scoped> .yrolvcoq { min-height: 100%; - background: var(--bg); } </style> diff --git a/src/client/components/ui/window.vue b/src/client/components/ui/window.vue index ce621ac6fd..f8b7d82d4a 100644 --- a/src/client/components/ui/window.vue +++ b/src/client/components/ui/window.vue @@ -3,15 +3,11 @@ <div class="ebkgocck" :class="{ front }" v-if="showing"> <div class="body _popup _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown"> <div class="header" :class="{ mini }" @contextmenu.prevent.stop="onContextmenu"> - <slot v-if="closeRight" name="buttons"><button class="_button" style="pointer-events: none;"></button></slot> - <button v-else class="_button" @click="close()"><i class="fas fa-times"></i></button> + <button v-if="closeButton" class="_button" @click="close()"><i class="fas fa-times"></i></button> <span class="title" @mousedown.prevent="onHeaderMousedown" @touchstart.prevent="onHeaderMousedown"> <slot name="header"></slot> </span> - - <button v-if="closeRight" class="_button" @click="close()"><i class="fas fa-times"></i></button> - <slot v-else name="buttons"><button class="_button" style="pointer-events: none;"></button></slot> </div> <div class="body" v-if="padding"> <div class="_section"> @@ -86,10 +82,10 @@ export default defineComponent({ required: false, default: false, }, - closeRight: { + closeButton: { type: Boolean, required: false, - default: false, + default: true, }, mini: { type: Boolean, diff --git a/src/client/pages/timeline.vue b/src/client/pages/timeline.vue index 966146d92b..55c4743264 100644 --- a/src/client/pages/timeline.vue +++ b/src/client/pages/timeline.vue @@ -1,8 +1,8 @@ <template> <div class="cmuxhskf _root" v-hotkey.global="keymap"> - <XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/> - <XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/> - <div class="tabs _block"> + <XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block _isolated"/> + <XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block _isolated" fixed/> + <div class="tabs"> <div class="left"> <button class="_button tab" @click="() => { src = 'home'; saveSrc(); }" :class="{ active: src === 'home' }" v-tooltip="$ts._timelines.home"><i class="fas fa-home"></i></button> <button class="_button tab" @click="() => { src = 'local'; saveSrc(); }" :class="{ active: src === 'local' }" v-tooltip="$ts._timelines.local" v-if="isLocalTimelineAvailable"><i class="fas fa-comments"></i></button> @@ -20,7 +20,6 @@ </div> <div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div> <XTimeline ref="tl" - class="_gap" :key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src === 'channel' ? `channel:${channel.id}` : src" :src="src" :list="list ? list.id : null" @@ -62,6 +61,7 @@ export default defineComponent({ queue: 0, [symbols.PAGE_INFO]: computed(() => ({ title: this.$ts.timeline, + subtitle: this.src === 'local' ? this.$ts._timelines.local : this.src === 'social' ? this.$ts._timelines.social : this.src === 'global' ? this.$ts._timelines.global : this.$ts._timelines.home, icon: this.src === 'local' ? 'fas fa-comments' : this.src === 'social' ? 'fas fa-share-alt' : this.src === 'global' ? 'fas fa-globe' : 'fas fa-home', actions: [{ icon: 'fas fa-calendar-alt', @@ -211,6 +211,8 @@ export default defineComponent({ <style lang="scss" scoped> .cmuxhskf { + background: var(--bg); + > .new { position: sticky; top: calc(var(--stickyTop, 0px) + 16px); diff --git a/src/client/ui/_common_/header.vue b/src/client/ui/_common_/header.vue index 83aa669b44..24b9046717 100644 --- a/src/client/ui/_common_/header.vue +++ b/src/client/ui/_common_/header.vue @@ -1,22 +1,29 @@ <template> <div class="fdidabkb" :class="{ center }" :style="`--height:${height};`" :key="key"> <transition :name="$store.state.animation ? 'header' : ''" mode="out-in" appear> - <button class="_button back" v-if="withBack && canBack" @click.stop="back()" v-tooltip="$ts.goBack"><i class="fas fa-chevron-left"></i></button> + <div class="buttons left" v-if="backButton"> + <button class="_button button back" @click.stop="$emit('back')" v-tooltip="$ts.goBack"><i class="fas fa-chevron-left"></i></button> + </div> </transition> <template v-if="info"> <div class="titleContainer"> + <i v-if="info.icon" class="icon" :class="info.icon"></i> + <MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/> + <div class="title"> - <i v-if="info.icon" class="icon" :class="info.icon"></i> - <MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/> - <MkUserName v-if="info.userName" :user="info.userName" :nowrap="false" class="text"/> - <span v-else-if="info.title" class="text">{{ info.title }}</span> + <MkUserName v-if="info.userName" :user="info.userName" :nowrap="false" class="title"/> + <div v-else-if="info.title" class="title">{{ info.title }}</div> + <div class="subtitle" v-if="info.subtitle"> + {{ info.subtitle }} + </div> </div> </div> - <div class="buttons"> + <div class="buttons right"> <template v-if="info.actions && showActions"> - <button v-for="action in info.actions" class="_button button" @click.stop="action.handler" v-tooltip="action.text"><i :class="action.icon"></i></button> + <button v-for="action in info.actions" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" v-tooltip="action.text"><i :class="action.icon"></i></button> </template> - <button v-if="showMenu" class="_button button" @click.stop="menu"><i class="fas fa-ellipsis-h"></i></button> + <button v-if="shouldShowMenu" class="_button button" @click.stop="showMenu" v-tooltip="$ts.menu"><i class="fas fa-ellipsis-h"></i></button> + <button v-if="closeButton" class="_button button" @click.stop="$emit('close')" v-tooltip="$ts.close"><i class="fas fa-times"></i></button> </div> </template> </div> @@ -32,10 +39,18 @@ export default defineComponent({ info: { required: true }, - withBack: { + menu: { + required: false + }, + backButton: { type: Boolean, required: false, - default: true, + default: false, + }, + closeButton: { + type: Boolean, + required: false, + default: false, }, center: { type: Boolean, @@ -46,7 +61,6 @@ export default defineComponent({ data() { return { - canBack: false, showActions: false, height: 0, key: 0, @@ -54,10 +68,11 @@ export default defineComponent({ }, computed: { - showMenu() { + shouldShowMenu() { if (this.info.actions != null && !this.showActions) return true; if (this.info.menu != null) return true; if (this.info.share != null) return true; + if (this.menu != null) return true; return false; } }, @@ -66,13 +81,6 @@ export default defineComponent({ info() { this.key++; }, - - $route: { - handler(to, from) { - this.canBack = (window.history.length > 0 && !['index'].includes(to.name)); - }, - immediate: true - }, }, mounted() { @@ -85,10 +93,6 @@ export default defineComponent({ }, methods: { - back() { - if (this.canBack) this.$router.back(); - }, - share() { navigator.share({ url: url + this.info.path, @@ -96,7 +100,7 @@ export default defineComponent({ }); }, - menu(ev) { + showMenu(ev) { let menu = this.info.menu ? this.info.menu() : []; if (!this.showActions && this.info.actions) { menu = [...this.info.actions.map(x => ({ @@ -113,6 +117,10 @@ export default defineComponent({ action: this.share }); } + if (this.menu) { + if (menu.length > 0) menu.push(null); + menu = menu.concat(this.menu); + } modalMenu(menu, ev.currentTarget || ev.target); } } @@ -121,62 +129,92 @@ export default defineComponent({ <style lang="scss" scoped> .fdidabkb { + display: flex; + &.center { text-align: center; > .titleContainer { margin: 0 auto; } - } - > .back { - position: absolute; - z-index: 1; - top: 0; - left: 0; - height: var(--height); - width: var(--height); + > .buttons { + &.right { + margin-left: 0; + } + } } > .buttons { - position: absolute; - z-index: 1; - top: 0; - right: 0; + --margin: 8px; + display: flex; + align-items: center; + height: var(--height); + margin: 0 var(--margin); + + &.right { + margin-left: auto; + } + + &:empty { + width: var(--height); + } > .button { - height: var(--height); - width: var(--height); + display: flex; + align-items: center; + justify-content: center; + height: calc(var(--height) - (var(--margin) * 2)); + width: calc(var(--height) - (var(--margin) * 2)); + box-sizing: border-box; + position: relative; + border-radius: 5px; + + &:hover { + background: rgba(0, 0, 0, 0.05); + } + + &.highlighted { + color: var(--accent); + } } } > .titleContainer { + display: flex; + align-items: center; overflow: auto; white-space: nowrap; - width: calc(100% - (var(--height) * 2)); + text-align: left; + + > .avatar { + $size: 32px; + display: inline-block; + width: $size; + height: $size; + vertical-align: bottom; + margin: 0 8px; + pointer-events: none; + } + + > .icon { + margin-right: 8px; + } > .title { - display: inline-block; - vertical-align: bottom; - white-space: nowrap; + min-width: 0; overflow: hidden; text-overflow: ellipsis; - padding: 0 16px; - position: relative; - height: var(--height); + white-space: nowrap; + line-height: 1.1; - > .icon + .text { - margin-left: 8px; - } - - > .avatar { - $size: 32px; - display: inline-block; - width: $size; - height: $size; - vertical-align: bottom; - margin: calc((var(--height) - #{$size}) / 2) 8px calc((var(--height) - #{$size}) / 2) 0; - pointer-events: none; + > .subtitle { + opacity: 0.6; + font-size: 0.8em; + font-weight: normal; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } } } diff --git a/src/client/ui/deck/main-column.vue b/src/client/ui/deck/main-column.vue index 0b61ff6e3a..4c591022a5 100644 --- a/src/client/ui/deck/main-column.vue +++ b/src/client/ui/deck/main-column.vue @@ -1,7 +1,7 @@ <template> <XColumn v-if="deckStore.state.alwaysShowMainColumn || $route.name !== 'index'" :column="column" :is-stacked="isStacked"> <template #header> - <XHeader :info="pageInfo"/> + <XHeader :info="pageInfo" :back-button="true" @back="back()"/> </template> <router-view v-slot="{ Component }" class="_flat_"> @@ -56,6 +56,10 @@ export default defineComponent({ } }, + back() { + history.back(); + }, + onContextmenu(e) { const isLink = (el: HTMLElement) => { if (el.tagName === 'A') return true; diff --git a/src/client/ui/default.side.vue b/src/client/ui/default.side.vue index 5c8de80378..dca16cdb3e 100644 --- a/src/client/ui/default.side.vue +++ b/src/client/ui/default.side.vue @@ -4,7 +4,7 @@ <header class="header" @contextmenu.prevent.stop="onContextmenu"> <button class="_button" @click="back()" v-if="history.length > 0"><i class="fas fa-chevron-left"></i></button> <button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button> - <XHeader class="title" :info="pageInfo" :with-back="false"/> + <XHeader class="title" :info="pageInfo" :back-button="false"/> <button class="_button" @click="close()"><i class="fas fa-times"></i></button> </header> <component :is="component" v-bind="props" :ref="changePage"/> diff --git a/src/client/ui/default.vue b/src/client/ui/default.vue index d257b048d0..645f67c123 100644 --- a/src/client/ui/default.vue +++ b/src/client/ui/default.vue @@ -14,7 +14,7 @@ <main class="main _panel" @contextmenu.stop="onContextmenu"> <header class="header" @click="onHeaderClick"> - <XHeader :info="pageInfo"/> + <XHeader :info="pageInfo" :back-button="true" @back="back()"/> </header> <div class="content" :class="{ _flat_: !fullView }"> <router-view v-slot="{ Component }"> @@ -157,6 +157,10 @@ export default defineComponent({ window.scroll({ top: 0, behavior: 'smooth' }); }, + back() { + history.back(); + }, + showDrawerNav() { this.$refs.drawerNav.show(); }, @@ -287,7 +291,7 @@ export default defineComponent({ min-width: 0; width: 750px; margin: 0 16px 0 0; - background: var(--bg); + background: var(--panel); border-radius: 0; --margin: 12px; @@ -296,14 +300,13 @@ export default defineComponent({ z-index: 1000; top: var(--globalHeaderHeight, 0px); height: $header-height; - line-height: $header-height; -webkit-backdrop-filter: blur(32px); backdrop-filter: blur(32px); background-color: var(--header); + border-bottom: solid 0.5px var(--divider); } > .content { - background: var(--bg); --stickyTop: calc(var(--globalHeaderHeight, 0px) + #{$header-height}); }