mirror of
https://github.com/misskey-dev/misskey.git
synced 2025-04-28 21:45:18 +02:00
enhance(frontend): デッキのオプションを追加
This commit is contained in:
parent
e0d8702839
commit
87a7238976
8 changed files with 417 additions and 60 deletions
|
@ -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 全体的なパフォーマンス向上
|
||||
|
|
24
locales/index.d.ts
vendored
24
locales/index.d.ts
vendored
|
@ -5362,6 +5362,18 @@ export interface Locale extends ILocale {
|
|||
* 圧縮
|
||||
*/
|
||||
"compress": string;
|
||||
/**
|
||||
* 右
|
||||
*/
|
||||
"right": string;
|
||||
/**
|
||||
* 下
|
||||
*/
|
||||
"bottom": string;
|
||||
/**
|
||||
* 上
|
||||
*/
|
||||
"top": string;
|
||||
"_chat": {
|
||||
/**
|
||||
* まだメッセージはありません
|
||||
|
@ -10065,6 +10077,18 @@ export interface Locale extends ILocale {
|
|||
* カラムの寄せ
|
||||
*/
|
||||
"columnAlign": string;
|
||||
/**
|
||||
* カラム間のマージン
|
||||
*/
|
||||
"columnGap": string;
|
||||
/**
|
||||
* デッキメニューの位置
|
||||
*/
|
||||
"deckMenuPosition": string;
|
||||
/**
|
||||
* ナビゲーションバーの位置
|
||||
*/
|
||||
"navbarPosition": string;
|
||||
/**
|
||||
* カラムを追加
|
||||
*/
|
||||
|
|
|
@ -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: "カラムの設定"
|
||||
|
|
|
@ -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'));
|
||||
|
||||
|
|
|
@ -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': {
|
||||
|
|
214
packages/frontend/src/ui/_common_/navbar-h.vue
Normal file
214
packages/frontend/src/ui/_common_/navbar-h.vue
Normal file
|
@ -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>
|
|
@ -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;
|
||||
|
|
|
@ -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'],
|
||||
|
|
Loading…
Add table
Reference in a new issue