summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2025-03-30 20:44:00 +0900
committersyuilo <4439005+syuilo@users.noreply.github.com>2025-03-30 20:44:00 +0900
commit87a723897611fff9b5ddda8a8bec3cdb427a21dc (patch)
tree808415a4dfd20dfd3ff1a6456c47ed55e1f8c300
parentperf(frontend): tweak MkRange (diff)
downloadsharkey-87a723897611fff9b5ddda8a8bec3cdb427a21dc.tar.gz
sharkey-87a723897611fff9b5ddda8a8bec3cdb427a21dc.tar.bz2
sharkey-87a723897611fff9b5ddda8a8bec3cdb427a21dc.zip
enhance(frontend): デッキのオプションを追加
-rw-r--r--CHANGELOG.md6
-rw-r--r--locales/index.d.ts24
-rw-r--r--locales/ja-JP.yml6
-rw-r--r--packages/frontend/src/pages/settings/deck.vue33
-rw-r--r--packages/frontend/src/preferences/def.ts11
-rw-r--r--packages/frontend/src/ui/_common_/navbar-h.vue214
-rw-r--r--packages/frontend/src/ui/deck.vue166
-rw-r--r--packages/frontend/src/utility/autogen/settings-search-index.ts17
8 files changed, 417 insertions, 60 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0268127cbe..0ecec21953 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -39,6 +39,9 @@
- Enhance: プラグインの管理が強化されました
- インストール/アンインストール/設定の変更時にリロード不要になりました
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように
+- Enhance: デッキUIでカラム間のマージンを設定できるように
+- Enhance: デッキUIでデッキメニューの位置を設定できるように
+- Enhance: デッキUIでナビゲーションバーの位置を設定できるように
- Enhance: アイコンのスクロール追従を無効化してパフォーマンス向上できるように
- Enhance: CWの注釈テキストが入力されていない場合, Postボタンを非アクティブに
- Enhance: CWを無効にした場合, 注釈テキストが最大入力文字数を超えていても投稿できるように
@@ -52,8 +55,7 @@
- Fix: 読み込み直後にスクロールしようとすると途中で止まる場合があるのを修正
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正
- NOTE: 構造上クラシックUIを新しいデザインシステムに移行することが困難なため、クラシックUIが削除されました
- - デッキUIでカラムを中央寄せにし、メインカラムの左右にウィジェットカラムを配置することである程度クラシックUIを再現できます
- - また、デッキでナビゲーションバーを上部に表示するオプションを実装予定です
+ - デッキUIでカラムを中央寄せにし、メインカラムの左右にウィジェットカラムを配置し、ナビゲーションバーを上部に表示することである程度クラシックUIを再現できます
### Server
- Enhance 全体的なパフォーマンス向上
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 3dfd0e6029..3645639305 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -5362,6 +5362,18 @@ export interface Locale extends ILocale {
* 圧縮
*/
"compress": string;
+ /**
+ * 右
+ */
+ "right": string;
+ /**
+ * 下
+ */
+ "bottom": string;
+ /**
+ * 上
+ */
+ "top": string;
"_chat": {
/**
* まだメッセージはありません
@@ -10066,6 +10078,18 @@ export interface Locale extends ILocale {
*/
"columnAlign": string;
/**
+ * カラム間のマージン
+ */
+ "columnGap": string;
+ /**
+ * デッキメニューの位置
+ */
+ "deckMenuPosition": string;
+ /**
+ * ナビゲーションバーの位置
+ */
+ "navbarPosition": string;
+ /**
* カラムを追加
*/
"addColumn": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index eb1505270c..0e20001d6b 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1336,6 +1336,9 @@ chat: "チャット"
migrateOldSettings: "旧設定情報を移行"
migrateOldSettings_description: "通常これは自動で行われていますが、何らかの理由により上手く移行されなかった場合は手動で移行処理をトリガーできます。現在の設定情報は上書きされます。"
compress: "圧縮"
+right: "右"
+bottom: "下"
+top: "上"
_chat:
noMessagesYet: "まだメッセージはありません"
@@ -2662,6 +2665,9 @@ _notification:
_deck:
alwaysShowMainColumn: "常にメインカラムを表示"
columnAlign: "カラムの寄せ"
+ columnGap: "カラム間のマージン"
+ deckMenuPosition: "デッキメニューの位置"
+ navbarPosition: "ナビゲーションバーの位置"
addColumn: "カラムを追加"
newNoteNotificationSettings: "新着ノート通知の設定"
configureColumn: "カラムの設定"
diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue
index 9b2b40374e..902e058e0d 100644
--- a/packages/frontend/src/pages/settings/deck.vue
+++ b/packages/frontend/src/pages/settings/deck.vue
@@ -45,6 +45,35 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkRadios>
</MkPreferenceContainer>
</SearchMarker>
+
+ <SearchMarker :keywords="['menu', 'position']">
+ <MkPreferenceContainer k="deck.menuPosition">
+ <MkRadios v-model="menuPosition">
+ <template #label><SearchLabel>{{ i18n.ts._deck.deckMenuPosition }}</SearchLabel></template>
+ <option value="right">{{ i18n.ts.right }}</option>
+ <option value="bottom">{{ i18n.ts.bottom }}</option>
+ </MkRadios>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['navbar', 'position']">
+ <MkPreferenceContainer k="deck.navbarPosition">
+ <MkRadios v-model="navbarPosition">
+ <template #label><SearchLabel>{{ i18n.ts._deck.navbarPosition }}</SearchLabel></template>
+ <option value="left">{{ i18n.ts.left }}</option>
+ <option value="top">{{ i18n.ts.top }}</option>
+ <option value="bottom">{{ i18n.ts.bottom }}</option>
+ </MkRadios>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['column', 'gap', 'margin']">
+ <MkPreferenceContainer k="deck.columnGap">
+ <MkRange v-model="columnGap" :min="3" :max="100" :step="1" :continuousUpdate="true">
+ <template #label><SearchLabel>{{ i18n.ts._deck.columnGap }}</SearchLabel></template>
+ </MkRange>
+ </MkPreferenceContainer>
+ </SearchMarker>
</div>
</SearchMarker>
</template>
@@ -53,6 +82,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed, ref } from 'vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkRadios from '@/components/MkRadios.vue';
+import MkRange from '@/components/MkRange.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { prefer } from '@/preferences.js';
@@ -62,6 +92,9 @@ const navWindow = prefer.model('deck.navWindow');
const useSimpleUiForNonRootPages = prefer.model('deck.useSimpleUiForNonRootPages');
const alwaysShowMainColumn = prefer.model('deck.alwaysShowMainColumn');
const columnAlign = prefer.model('deck.columnAlign');
+const columnGap = prefer.model('deck.columnGap');
+const menuPosition = prefer.model('deck.menuPosition');
+const navbarPosition = prefer.model('deck.navbarPosition');
const profilesSyncEnabled = ref(prefer.isSyncEnabled('deck.profiles'));
diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts
index 37fa9471ee..5aa463f045 100644
--- a/packages/frontend/src/preferences/def.ts
+++ b/packages/frontend/src/preferences/def.ts
@@ -371,7 +371,16 @@ export const PREF_DEF = {
default: true,
},
'deck.columnAlign': {
- default: 'left' as 'left' | 'right' | 'center',
+ default: 'center' as 'left' | 'right' | 'center',
+ },
+ 'deck.columnGap': {
+ default: 6,
+ },
+ 'deck.menuPosition': {
+ default: 'bottom' as 'right' | 'bottom',
+ },
+ 'deck.navbarPosition': {
+ default: 'left' as 'left' | 'top' | 'bottom',
},
'chat.showSenderName': {
diff --git a/packages/frontend/src/ui/_common_/navbar-h.vue b/packages/frontend/src/ui/_common_/navbar-h.vue
new file mode 100644
index 0000000000..c93935dd26
--- /dev/null
+++ b/packages/frontend/src/ui/_common_/navbar-h.vue
@@ -0,0 +1,214 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div class="azykntjl">
+ <div class="body">
+ <div class="left">
+ <button v-click-anime class="item _button instance" @click="openInstanceMenu">
+ <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" draggable="false"/>
+ </button>
+ <MkA v-click-anime v-tooltip="i18n.ts.timeline" class="item index" activeClass="active" to="/" exact>
+ <i class="ti ti-home ti-fw"></i>
+ </MkA>
+ <template v-for="item in menu">
+ <div v-if="item === '-'" class="divider"></div>
+ <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime v-tooltip="navbarItemDef[item].title" class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
+ <i class="ti-fw" :class="navbarItemDef[item].icon"></i>
+ <span v-if="navbarItemDef[item].indicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
+ </component>
+ </template>
+ <div class="divider"></div>
+ <MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip="i18n.ts.controlPanel" class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'window' : null">
+ <i class="ti ti-dashboard ti-fw"></i>
+ </MkA>
+ <button v-click-anime class="item _button" @click="more">
+ <i class="ti ti-dots ti-fw"></i>
+ <span v-if="otherNavItemIndicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
+ </button>
+ </div>
+ <div class="right">
+ <MkA v-click-anime v-tooltip="i18n.ts.settings" class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
+ <i class="ti ti-settings ti-fw"></i>
+ </MkA>
+ <button v-click-anime class="item _button account" @click="openAccountMenu">
+ <MkAvatar :user="$i" class="avatar"/><MkAcct class="acct" :user="$i"/>
+ </button>
+ <div class="post" @click="os.post()">
+ <MkButton class="button" gradate full rounded>
+ <i class="ti ti-pencil ti-fw"></i>
+ </MkButton>
+ </div>
+ </div>
+ </div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
+import { openInstanceMenu } from './common.js';
+import * as os from '@/os.js';
+import { navbarItemDef } from '@/navbar.js';
+import MkButton from '@/components/MkButton.vue';
+import { instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import { prefer } from '@/preferences.js';
+import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
+import { $i } from '@/i.js';
+
+const WINDOW_THRESHOLD = 1400;
+
+const settingsWindowed = ref(window.innerWidth > WINDOW_THRESHOLD);
+const menu = ref(prefer.s.menu);
+// const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
+const otherNavItemIndicated = computed<boolean>(() => {
+ for (const def in navbarItemDef) {
+ if (menu.value.includes(def)) continue;
+ if (navbarItemDef[def].indicated) return true;
+ }
+ return false;
+});
+
+function more(ev: MouseEvent) {
+ const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
+ src: ev.currentTarget ?? ev.target,
+ anchor: { x: 'center', y: 'bottom' },
+ }, {
+ closed: () => dispose(),
+ });
+}
+
+function openAccountMenu(ev: MouseEvent) {
+ openAccountMenu_({
+ withExtraOperation: true,
+ }, ev);
+}
+
+onMounted(() => {
+ window.addEventListener('resize', () => {
+ settingsWindowed.value = (window.innerWidth >= WINDOW_THRESHOLD);
+ }, { passive: true });
+});
+
+</script>
+
+<style lang="scss" scoped>
+.azykntjl {
+ $height: 60px;
+ $avatar-size: 32px;
+ $avatar-margin: 8px;
+
+ position: sticky;
+ top: 0;
+ z-index: 1000;
+ width: 100%;
+ height: $height;
+ background: color(from var(--MI_THEME-bg) srgb r g b / 0.75);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
+
+ > .body {
+ max-width: 1380px;
+ margin: 0 auto;
+ display: flex;
+
+ > .right,
+ > .left {
+
+ > .item {
+ position: relative;
+ font-size: 0.9em;
+ display: inline-block;
+ padding: 0 12px;
+ line-height: $height;
+
+ > i,
+ > .avatar {
+ margin-right: 0;
+ }
+
+ > i {
+ left: 10px;
+ }
+
+ > .avatar {
+ width: $avatar-size;
+ height: $avatar-size;
+ vertical-align: middle;
+ }
+
+ > .indicator {
+ position: absolute;
+ top: 0;
+ left: 0;
+ color: var(--MI_THEME-navIndicator);
+ font-size: 8px;
+ }
+
+ &:hover {
+ text-decoration: none;
+ color: var(--MI_THEME-navHoverFg);
+ }
+
+ &.active {
+ color: var(--MI_THEME-navActive);
+ }
+ }
+
+ > .divider {
+ display: inline-block;
+ height: 16px;
+ margin: 0 10px;
+ border-right: solid 0.5px var(--MI_THEME-divider);
+ }
+
+ > .instance {
+ display: inline-block;
+ position: relative;
+ width: 56px;
+ height: 100%;
+ vertical-align: bottom;
+
+ > img {
+ display: inline-block;
+ width: 24px;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ margin: auto;
+ }
+ }
+
+ > .post {
+ display: inline-block;
+
+ > .button {
+ width: 40px;
+ height: 40px;
+ padding: 0;
+ min-width: 0;
+ }
+ }
+
+ > .account {
+ display: inline-flex;
+ align-items: center;
+ vertical-align: top;
+ margin-right: 8px;
+
+ > .acct {
+ margin-left: 8px;
+ }
+ }
+ }
+
+ > .right {
+ margin-left: auto;
+ }
+ }
+}
+</style>
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index 3f3bc32fad..3de8137404 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -4,37 +4,43 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div :class="[$style.root, { [$style.rootIsMobile]: isMobile }]">
- <XSidebar v-if="!isMobile"/>
+<div :class="[$style.root, { [$style.withWallpaper]: withWallpaper }]">
+ <XSidebar v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'left'"/>
<div :class="$style.main">
+ <XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/>
+
<XAnnouncements v-if="$i"/>
<XStatusBars/>
- <div ref="columnsEl" :class="[$style.sections, { [$style.center]: prefer.r['deck.columnAlign'].value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.self="onWheel">
- <!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
- <section
- v-for="ids in layout"
- :class="$style.section"
- :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' }"
- @wheel.self="onWheel"
- >
- <component
- :is="columnComponents[columns.find(c => c.id === id)!.type] ?? XTlColumn"
- v-for="id in ids"
- :ref="id"
- :key="id"
- :class="$style.column"
- :column="columns.find(c => c.id === id)!"
- :isStacked="ids.length > 1"
- @headerWheel="onWheel"
- />
- </section>
- <div v-if="layout.length === 0" class="_panel" :class="$style.onboarding">
- <div>{{ i18n.ts._deck.introduction }}</div>
- <MkButton primary style="margin: 1em auto;" @click="addColumn">{{ i18n.ts._deck.addColumn }}</MkButton>
- <div>{{ i18n.ts._deck.introduction2 }}</div>
+
+ <div :class="$style.columnsWrapper">
+ <div ref="columnsEl" :class="[$style.columns, { [$style.center]: prefer.r['deck.columnAlign'].value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.self="onWheel">
+ <!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
+ <section
+ v-for="ids in layout"
+ :class="$style.section"
+ :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' }"
+ @wheel.self="onWheel"
+ >
+ <component
+ :is="columnComponents[columns.find(c => c.id === id)!.type] ?? XTlColumn"
+ v-for="id in ids"
+ :ref="id"
+ :key="id"
+ :class="$style.column"
+ :column="columns.find(c => c.id === id)!"
+ :isStacked="ids.length > 1"
+ @headerWheel="onWheel"
+ />
+ </section>
+ <div v-if="layout.length === 0" class="_panel" :class="$style.onboarding">
+ <div>{{ i18n.ts._deck.introduction }}</div>
+ <MkButton primary style="margin: 1em auto;" @click="addColumn">{{ i18n.ts._deck.addColumn }}</MkButton>
+ <div>{{ i18n.ts._deck.introduction2 }}</div>
+ </div>
</div>
- <div :class="$style.sideMenu">
+
+ <div v-if="prefer.r['deck.menuPosition'].value === 'right'" :class="$style.sideMenu">
<div :class="$style.sideMenuTop">
<button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.sideMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
<button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.sideMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
@@ -47,18 +53,33 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
- </div>
- <div v-if="isMobile" :class="$style.nav">
- <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator" class="_blink"><i class="_indicatorCircle"></i></span></button>
- <button :class="$style.navButton" class="_button" @click="mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ti ti-home"></i></button>
- <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')">
- <i :class="$style.navButtonIcon" class="ti ti-bell"></i>
- <span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator" class="_blink">
- <span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span>
- </span>
- </button>
- <button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" class="ti ti-pencil"></i></button>
+ <div v-if="prefer.r['deck.menuPosition'].value === 'bottom'" :class="$style.bottomMenu">
+ <div :class="$style.bottomMenuLeft">
+ <button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.bottomMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
+ <button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.bottomMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
+ </div>
+ <div :class="$style.bottomMenuMiddle">
+ <button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.bottomMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button>
+ </div>
+ <div :class="$style.bottomMenuRight">
+ <button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.bottomMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings"></i></button>
+ </div>
+ </div>
+
+ <XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'bottom'"/>
+
+ <div v-if="isMobile" :class="$style.nav">
+ <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator" class="_blink"><i class="_indicatorCircle"></i></span></button>
+ <button :class="$style.navButton" class="_button" @click="mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ti ti-home"></i></button>
+ <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')">
+ <i :class="$style.navButtonIcon" class="ti ti-bell"></i>
+ <span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator" class="_blink">
+ <span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span>
+ </span>
+ </button>
+ <button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" class="ti ti-pencil"></i></button>
+ </div>
</div>
<Transition
@@ -92,10 +113,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { computed, defineAsyncComponent, ref, useTemplateRef } from 'vue';
+import { computed, defineAsyncComponent, ref, useTemplateRef, watch } from 'vue';
import { v4 as uuid } from 'uuid';
import XCommon from './_common_/common.vue';
import XSidebar from '@/ui/_common_/navbar.vue';
+import XNavbarH from '@/ui/_common_/navbar-h.vue';
import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
@@ -116,6 +138,8 @@ import XDirectColumn from '@/ui/deck/direct-column.vue';
import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue';
import { mainRouter } from '@/router.js';
import { columns, layout, columnTypes, switchProfileMenu, addColumn as addColumnToStore, deleteProfile as deleteProfile_ } from '@/deck.js';
+import { miLocalStorage } from '@/local-storage.js';
+
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue'));
@@ -148,7 +172,9 @@ window.addEventListener('resize', () => {
});
const snapScroll = deviceKind === 'smartphone' || deviceKind === 'tablet';
+const withWallpaper = miLocalStorage.getItem('wallpaper') != null;
const drawerMenuShowing = ref(false);
+const gap = prefer.r['deck.columnGap'];
/*
const route = 'TODO';
@@ -249,16 +275,18 @@ async function deleteProfile() {
--MI-margin: var(--MI-marginHalf);
- --columnGap: 6px;
+ --columnGap: v-bind("gap + 'px'");
display: flex;
height: 100dvh;
box-sizing: border-box;
flex: 1;
-}
-.rootIsMobile {
- padding-bottom: 100px;
+ &.withWallpaper {
+ .main {
+ background: transparent;
+ }
+ }
}
.main {
@@ -266,15 +294,23 @@ async function deleteProfile() {
min-width: 0;
display: flex;
flex-direction: column;
+ background: var(--MI_THEME-deckBg);
}
-.sections {
+.columnsWrapper {
+ flex: 1;
+ display: flex;
+ flex-direction: row;
+}
+
+.columns {
flex: 1;
display: flex;
overflow-x: auto;
overflow-y: clip;
overscroll-behavior: contain;
- background: var(--MI_THEME-deckBg);
+ padding: var(--columnGap);
+ gap: var(--columnGap);
&.center {
> .section:first-of-type {
@@ -294,15 +330,10 @@ async function deleteProfile() {
.section {
display: flex;
flex-direction: column;
- scroll-snap-align: start;
flex-shrink: 0;
- padding-top: var(--columnGap);
- padding-bottom: var(--columnGap);
- padding-left: var(--columnGap);
-
- > .column:not(:last-of-type) {
- margin-bottom: var(--columnGap);
- }
+ gap: var(--columnGap);
+ scroll-snap-align: start;
+ scroll-margin-left: var(--columnGap);
}
.onboarding {
@@ -341,6 +372,33 @@ async function deleteProfile() {
margin-top: auto;
}
+.bottomMenu {
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ height: 32px;
+}
+
+.bottomMenuButton {
+ display: block;
+ height: 100%;
+ aspect-ratio: 1;
+}
+
+.bottomMenuLeft {
+ margin-right: auto;
+}
+
+.bottomMenuMiddle {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.bottomMenuRight {
+ margin-left: auto;
+}
+
.menuBg {
z-index: 1001;
}
@@ -360,10 +418,6 @@ async function deleteProfile() {
}
.nav {
- position: fixed;
- z-index: 1000;
- bottom: 0;
- left: 0;
padding: 12px 12px max(12px, env(safe-area-inset-bottom, 0px)) 12px;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts
index df621beb7d..1563f19c34 100644
--- a/packages/frontend/src/utility/autogen/settings-search-index.ts
+++ b/packages/frontend/src/utility/autogen/settings-search-index.ts
@@ -682,7 +682,7 @@ export const searchIndexes: SearchIndexItem[] = [
id: '9bNikHWzQ',
children: [
{
- id: 'appYJbpkK',
+ id: 't6XtfnRm9',
label: i18n.ts._settings.showNavbarSubButtons,
keywords: ['navbar', 'sidebar', 'toggle', 'button', 'sub'],
},
@@ -880,6 +880,21 @@ export const searchIndexes: SearchIndexItem[] = [
label: i18n.ts._deck.columnAlign,
keywords: ['column', 'align'],
},
+ {
+ id: 'gtdEA4FTa',
+ label: i18n.ts._deck.deckMenuPosition,
+ keywords: ['menu', 'position'],
+ },
+ {
+ id: 'DHVFdPBT6',
+ label: i18n.ts._deck.navbarPosition,
+ keywords: ['navbar', 'position'],
+ },
+ {
+ id: '3UQ8rUssZ',
+ label: i18n.ts._deck.columnGap,
+ keywords: ['column', 'gap', 'margin'],
+ },
],
label: i18n.ts.deck,
keywords: ['deck', 'ui'],