diff options
Diffstat (limited to 'packages/client/src/ui/deck.vue')
| -rw-r--r-- | packages/client/src/ui/deck.vue | 186 |
1 files changed, 131 insertions, 55 deletions
diff --git a/packages/client/src/ui/deck.vue b/packages/client/src/ui/deck.vue index e538a93f06..19a99a95aa 100644 --- a/packages/client/src/ui/deck.vue +++ b/packages/client/src/ui/deck.vue @@ -1,37 +1,54 @@ <template> -<div class="mk-deck" :class="[{ isMobile }, `${deckStore.reactiveState.columnAlign.value}`]" :style="{ '--deckMargin': deckStore.reactiveState.columnMargin.value + 'px' }" - @contextmenu.self.prevent="onContextmenu" +<div + class="mk-deck" :class="[{ isMobile }]" > <XSidebar v-if="!isMobile"/> - <template v-for="ids in layout"> - <!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため --> - <section v-if="ids.length > 1" - class="folder column" - :style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }" - > - <DeckColumnCore v-for="id in ids" :ref="id" :key="id" :column="columns.find(c => c.id === id)" :is-stacked="true" @parent-focus="moveFocus(id, $event)"/> - </section> - <DeckColumnCore v-else - :ref="ids[0]" - :key="ids[0]" - class="column" - :column="columns.find(c => c.id === ids[0])" - :is-stacked="false" - :style="columns.find(c => c.id === ids[0])!.flexible ? { flex: 1, minWidth: '350px' } : { width: columns.find(c => c.id === ids[0])!.width + 'px' }" - @parent-focus="moveFocus(ids[0], $event)" - /> - </template> + <div class="main"> + <XStatusBars class="statusbars"/> + <div ref="columnsEl" class="columns" :class="deckStore.reactiveState.columnAlign.value" @contextmenu.self.prevent="onContextmenu"> + <template v-for="ids in layout"> + <!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため --> + <section + v-if="ids.length > 1" + class="folder column" + :style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }" + > + <DeckColumnCore v-for="id in ids" :ref="id" :key="id" :column="columns.find(c => c.id === id)" :is-stacked="true" @parent-focus="moveFocus(id, $event)"/> + </section> + <DeckColumnCore + v-else + :ref="ids[0]" + :key="ids[0]" + class="column" + :column="columns.find(c => c.id === ids[0])" + :is-stacked="false" + :style="columns.find(c => c.id === ids[0])!.flexible ? { flex: 1, minWidth: '350px' } : { width: columns.find(c => c.id === ids[0])!.width + 'px' }" + @parent-focus="moveFocus(ids[0], $event)" + /> + </template> + <div v-if="layout.length === 0" class="intro _panel"> + <div>{{ i18n.ts._deck.introduction }}</div> + <MkButton primary class="add" @click="addColumn">{{ i18n.ts._deck.addColumn }}</MkButton> + <div>{{ i18n.ts._deck.introduction2 }}</div> + </div> + <div class="sideMenu"> + <button v-tooltip.left="i18n.ts._deck.addColumn" class="_button button" @click="addColumn"><i class="fas fa-plus"></i></button> + <button v-tooltip.left="i18n.ts.settings" class="_button button settings" @click="showSettings"><i class="fas fa-cog"></i></button> + </div> + </div> + </div> <div v-if="isMobile" class="buttons"> <button class="button nav _button" @click="drawerMenuShowing = true"><i class="fas fa-bars"></i><span v-if="menuIndicated" class="indicator"><i class="fas fa-circle"></i></span></button> - <button class="button home _button" @click="$router.push('/')"><i class="fas fa-home"></i></button> - <button class="button notifications _button" @click="$router.push('/my/notifications')"><i class="fas fa-bell"></i><span v-if="$i?.hasUnreadNotification" class="indicator"><i class="fas fa-circle"></i></span></button> + <button class="button home _button" @click="mainRouter.push('/')"><i class="fas fa-home"></i></button> + <button class="button notifications _button" @click="mainRouter.push('/my/notifications')"><i class="fas fa-bell"></i><span v-if="$i?.hasUnreadNotification" class="indicator"><i class="fas fa-circle"></i></span></button> <button class="button post _button" @click="os.post()"><i class="fas fa-pencil-alt"></i></button> </div> <transition :name="$store.state.animation ? 'menu-back' : ''"> - <div v-if="drawerMenuShowing" + <div + v-if="drawerMenuShowing" class="menu-back _modalBg" @click="drawerMenuShowing = false" @touchstart.passive="drawerMenuShowing = false" @@ -47,19 +64,30 @@ </template> <script lang="ts" setup> -import { computed, provide, ref, watch } from 'vue'; +import { computed, defineAsyncComponent, onMounted, provide, ref, watch } from 'vue'; import { v4 as uuid } from 'uuid'; +import XCommon from './_common_/common.vue'; +import { deckStore, addColumn as addColumnToStore, loadDeck } from './deck/deck-store'; import DeckColumnCore from '@/ui/deck/column-core.vue'; import XSidebar from '@/ui/_common_/sidebar.vue'; import XDrawerMenu from '@/ui/_common_/sidebar-for-mobile.vue'; +import MkButton from '@/components/ui/button.vue'; import { getScrollContainer } from '@/scripts/scroll'; import * as os from '@/os'; import { menuDef } from '@/menu'; -import XCommon from './_common_/common.vue'; -import { deckStore, addColumn as addColumnToStore, loadDeck } from './deck/deck-store'; -import { useRoute } from 'vue-router'; import { $i } from '@/account'; import { i18n } from '@/i18n'; +import { mainRouter } from '@/router'; +const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); + +mainRouter.navHook = (path): boolean => { + const noMainColumn = !deckStore.state.columns.some(x => x.type === 'main'); + if (deckStore.state.navWindow || noMainColumn) { + os.pageWindow(path); + return true; + } + return false; +}; const isMobile = ref(window.innerWidth <= 500); window.addEventListener('resize', () => { @@ -68,7 +96,7 @@ window.addEventListener('resize', () => { const drawerMenuShowing = ref(false); -const route = useRoute(); +const route = 'TODO'; watch(route, () => { drawerMenuShowing.value = false; }); @@ -83,6 +111,12 @@ const menuIndicated = computed(() => { return false; }); +function showSettings() { + os.pageWindow('/settings/deck'); +} + +let columnsEl = $ref<HTMLElement>(); + const addColumn = async (ev) => { const columns = [ 'main', @@ -98,8 +132,8 @@ const addColumn = async (ev) => { const { canceled, result: column } = await os.select({ title: i18n.ts._deck.addColumn, items: columns.map(column => ({ - value: column, text: i18n.t('_deck._columns.' + column) - })) + value: column, text: i18n.t('_deck._columns.' + column), + })), }); if (canceled) return; @@ -119,17 +153,14 @@ const onContextmenu = (ev) => { }; provide('shouldSpacerMin', true); -if (deckStore.state.navWindow) { - provide('navHook', (url) => { - os.pageWindow(url); - }); -} document.documentElement.style.overflowY = 'hidden'; document.documentElement.style.scrollBehavior = 'auto'; window.addEventListener('wheel', (ev) => { - if (getScrollContainer(ev.target as HTMLElement) == null && ev.deltaX === 0) { - document.documentElement.scrollLeft += ev.deltaY; + if (ev.target === columnsEl && ev.deltaX === 0) { + columnsEl.scrollLeft += ev.deltaY; + } else if (getScrollContainer(ev.target as HTMLElement) == null && ev.deltaX === 0) { + columnsEl.scrollLeft += ev.deltaY; } }); loadDeck(); @@ -168,37 +199,82 @@ function moveFocus(id: string, direction: 'up' | 'down' | 'left' | 'right') { // TODO: ここではなくて、各カラムで自身の幅に応じて上書きするようにしたい --margin: var(--marginHalf); + --deckDividerThickness: 5px; + display: flex; // ほんとは単に 100vh と書きたいところだが... https://css-tricks.com/the-trick-to-viewport-units-on-mobile/ height: calc(var(--vh, 1vh) * 100); box-sizing: border-box; flex: 1; - padding: var(--deckMargin); - - &.center { - > .column:first-of-type { - margin-left: auto; - } - - > .column:last-of-type { - margin-right: auto; - } - } &.isMobile { padding-bottom: 100px; } - > .column { - flex-shrink: 0; - margin-right: var(--deckMargin); + > .main { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; - &.folder { + > .columns { + flex: 1; display: flex; - flex-direction: column; + overflow-x: auto; + overflow-y: clip; + + &.center { + > .column:first-of-type { + margin-left: auto; + } + + > .column:last-of-type { + margin-right: auto; + } + } + + > .column { + flex-shrink: 0; + border-right: solid var(--deckDividerThickness) var(--deckDivider); + + &:first-of-type { + border-left: solid var(--deckDividerThickness) var(--deckDivider); + } + + &.folder { + display: flex; + flex-direction: column; - > *:not(:last-child) { - margin-bottom: var(--deckMargin); + > *:not(:last-of-type) { + border-bottom: solid var(--deckDividerThickness) var(--deckDivider); + } + } + } + + > .intro { + padding: 32px; + height: min-content; + text-align: center; + margin: auto; + + > .add { + margin: 1em auto; + } + } + + > .sideMenu { + flex-shrink: 0; + margin-right: 0; + margin-left: auto; + display: flex; + flex-direction: column; + justify-content: center; + width: 32px; + + > .button { + width: 100%; + aspect-ratio: 1; + } } } } |