fix(frontend): drop classic ui
This commit is contained in:
parent
c03f9bff0a
commit
4f4cb6738c
|
@ -51,6 +51,9 @@
|
||||||
- Enhance 全体的なパフォーマンス向上
|
- Enhance 全体的なパフォーマンス向上
|
||||||
- Fix: 読み込み直後にスクロールしようとすると途中で止まる場合があるのを修正
|
- Fix: 読み込み直後にスクロールしようとすると途中で止まる場合があるのを修正
|
||||||
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正
|
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正
|
||||||
|
- NOTE: 構造上クラシックUIを新しいデザインシステムに移行することが困難なため、クラシックUIが削除されました
|
||||||
|
- デッキUIでカラムを中央寄せにし、メインカラムの左右にウィジェットカラムを配置することである程度クラシックUIを再現できます
|
||||||
|
- また、デッキでナビゲーションバーを上部に表示するオプションを実装予定です
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- Enhance 全体的なパフォーマンス向上
|
- Enhance 全体的なパフォーマンス向上
|
||||||
|
|
|
@ -54,9 +54,6 @@ export async function mainBoot() {
|
||||||
case 'visitor':
|
case 'visitor':
|
||||||
rootComponent = await import('@/ui/visitor.vue').then(x => x.default);
|
rootComponent = await import('@/ui/visitor.vue').then(x => x.default);
|
||||||
break;
|
break;
|
||||||
case 'classic':
|
|
||||||
rootComponent = await import('@/ui/classic.vue').then(x => x.default);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
rootComponent = await import('@/ui/universal.vue').then(x => x.default);
|
rootComponent = await import('@/ui/universal.vue').then(x => x.default);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -145,13 +145,6 @@ export const navbarItemDef = reactive({
|
||||||
miLocalStorage.setItem('ui', 'deck');
|
miLocalStorage.setItem('ui', 'deck');
|
||||||
unisonReload();
|
unisonReload();
|
||||||
},
|
},
|
||||||
}, {
|
|
||||||
text: i18n.ts.classic,
|
|
||||||
active: ui === 'classic',
|
|
||||||
action: () => {
|
|
||||||
miLocalStorage.setItem('ui', 'classic');
|
|
||||||
unisonReload();
|
|
||||||
},
|
|
||||||
}], ev.currentTarget ?? ev.target);
|
}], ev.currentTarget ?? ev.target);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,212 +0,0 @@
|
||||||
<!--
|
|
||||||
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_/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: var(--MI_THEME-bg);
|
|
||||||
|
|
||||||
> .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>
|
|
|
@ -1,245 +0,0 @@
|
||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="npcljfve" :class="{ iconOnly }">
|
|
||||||
<button v-click-anime class="item _button account" @click="openAccountMenu">
|
|
||||||
<MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/>
|
|
||||||
</button>
|
|
||||||
<div class="post" data-cy-open-post-form @click="os.post">
|
|
||||||
<MkButton class="button" gradate full rounded>
|
|
||||||
<i class="ti ti-pencil ti-fw"></i><span v-if="!iconOnly" class="text">{{ i18n.ts.note }}</span>
|
|
||||||
</MkButton>
|
|
||||||
</div>
|
|
||||||
<div class="divider"></div>
|
|
||||||
<MkA v-click-anime class="item index" activeClass="active" to="/" exact>
|
|
||||||
<i class="ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span>
|
|
||||||
</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 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 class="text">{{ navbarItemDef[item].title }}</span>
|
|
||||||
<span v-if="navbarItemDef[item].indicated" class="indicator _blink">
|
|
||||||
<span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span>
|
|
||||||
<i v-else class="_indicatorCircle"></i>
|
|
||||||
</span>
|
|
||||||
</component>
|
|
||||||
</template>
|
|
||||||
<div class="divider"></div>
|
|
||||||
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'window' : null">
|
|
||||||
<i class="ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
|
|
||||||
</MkA>
|
|
||||||
<button v-click-anime class="item _button" @click="more">
|
|
||||||
<i class="ti ti-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span>
|
|
||||||
<span v-if="otherNavItemIndicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
|
|
||||||
</button>
|
|
||||||
<MkA v-click-anime class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
|
|
||||||
<i class="ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
|
|
||||||
</MkA>
|
|
||||||
<div class="divider"></div>
|
|
||||||
<div class="about">
|
|
||||||
<button v-click-anime class="item _button" @click="openInstanceMenu">
|
|
||||||
<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" draggable="false"/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<!--<MisskeyLogo class="misskey"/>-->
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { defineAsyncComponent, computed, watch, ref, useTemplateRef } from 'vue';
|
|
||||||
import { openInstanceMenu } from './_common_/common.js';
|
|
||||||
// import { host } from '@@/js/config.js';
|
|
||||||
import * as os from '@/os.js';
|
|
||||||
import { navbarItemDef } from '@/navbar.js';
|
|
||||||
import MkButton from '@/components/MkButton.vue';
|
|
||||||
// import { StickySidebar } from '@/utility/sticky-sidebar.js';
|
|
||||||
// import { mainRouter } from '@/router.js';
|
|
||||||
//import MisskeyLogo from '@assets/client/misskey.svg';
|
|
||||||
import { store } from '@/store.js';
|
|
||||||
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 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;
|
|
||||||
});
|
|
||||||
const el = useTemplateRef('el');
|
|
||||||
// let accounts = $ref([]);
|
|
||||||
// let connection = $ref(null);
|
|
||||||
const iconOnly = ref(false);
|
|
||||||
const settingsWindowed = ref(false);
|
|
||||||
|
|
||||||
function calcViewState() {
|
|
||||||
iconOnly.value = (window.innerWidth <= WINDOW_THRESHOLD) || (menuDisplay.value === 'sideIcon');
|
|
||||||
settingsWindowed.value = (window.innerWidth > WINDOW_THRESHOLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
function more(ev: MouseEvent) {
|
|
||||||
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
|
|
||||||
src: ev.currentTarget ?? ev.target,
|
|
||||||
}, {
|
|
||||||
closed: () => dispose(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function openAccountMenu(ev: MouseEvent) {
|
|
||||||
openAccountMenu_({
|
|
||||||
withExtraOperation: true,
|
|
||||||
}, ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(store.r.menuDisplay, () => {
|
|
||||||
calcViewState();
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.npcljfve {
|
|
||||||
$ui-font-size: 1em; // TODO: どこかに集約したい
|
|
||||||
$nav-icon-only-width: 78px; // TODO: どこかに集約したい
|
|
||||||
$avatar-size: 32px;
|
|
||||||
$avatar-margin: 8px;
|
|
||||||
|
|
||||||
padding: 0 16px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 260px;
|
|
||||||
|
|
||||||
&.iconOnly {
|
|
||||||
flex: 0 0 $nav-icon-only-width;
|
|
||||||
width: $nav-icon-only-width !important;
|
|
||||||
|
|
||||||
> .divider {
|
|
||||||
margin: 8px auto;
|
|
||||||
width: calc(100% - 32px);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .post {
|
|
||||||
> .button {
|
|
||||||
width: 46px;
|
|
||||||
height: 46px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .item {
|
|
||||||
padding-left: 0;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
font-size: $ui-font-size * 1.1;
|
|
||||||
line-height: 3.7rem;
|
|
||||||
|
|
||||||
> i,
|
|
||||||
> .avatar {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
> i {
|
|
||||||
left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .text {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .divider {
|
|
||||||
margin: 10px 0;
|
|
||||||
border-top: solid 0.5px var(--MI_THEME-divider);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .post {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 1;
|
|
||||||
padding: 16px 0;
|
|
||||||
background: var(--MI_THEME-bg);
|
|
||||||
|
|
||||||
> .button {
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .about {
|
|
||||||
fill: currentColor;
|
|
||||||
padding: 8px 0 16px 0;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
> .item {
|
|
||||||
display: block;
|
|
||||||
width: 32px;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
img {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .item {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
font-size: $ui-font-size;
|
|
||||||
line-height: 2.6rem;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
> i {
|
|
||||||
width: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> i,
|
|
||||||
> .avatar {
|
|
||||||
margin-right: $avatar-margin;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .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;
|
|
||||||
|
|
||||||
&:has(.itemIndicateValueIcon) {
|
|
||||||
animation: none;
|
|
||||||
left: auto;
|
|
||||||
right: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--MI_THEME-navHoverFg);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: var(--MI_THEME-navActive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,326 +0,0 @@
|
||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: syuilo and misskey-project
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="gbhvwtnk" :class="{ wallpaper }" :style="`--globalHeaderHeight:${globalHeaderHeight}px`">
|
|
||||||
<XHeaderMenu v-if="showMenuOnTop" v-get-size="(w, h) => globalHeaderHeight = h"/>
|
|
||||||
|
|
||||||
<div class="columns" :class="{ fullView, withGlobalHeader: showMenuOnTop }">
|
|
||||||
<div v-if="!showMenuOnTop" class="sidebar">
|
|
||||||
<XSidebar/>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="!pageMetadata?.needWideArea" ref="widgetsLeft" class="widgets left">
|
|
||||||
<XWidgets place="left" :marginTop="'var(--MI-margin)'" @mounted="attachSticky(widgetsLeft)"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<main class="main" @contextmenu.stop="onContextmenu">
|
|
||||||
<div class="content">
|
|
||||||
<StackingRouterView v-if="prefer.s['experimental.stackingRouterView']"/>
|
|
||||||
<RouterView v-else/>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<div v-if="isDesktop && !pageMetadata?.needWideArea" ref="widgetsRight" class="widgets right">
|
|
||||||
<XWidgets :place="showMenuOnTop ? 'right' : null" :marginTop="showMenuOnTop ? '0' : 'var(--MI-margin)'" @mounted="attachSticky(widgetsRight)"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Transition :name="prefer.s.animation ? 'tray-back' : ''">
|
|
||||||
<div
|
|
||||||
v-if="widgetsShowing"
|
|
||||||
class="tray-back _modalBg"
|
|
||||||
@click="widgetsShowing = false"
|
|
||||||
@touchstart.passive="widgetsShowing = false"
|
|
||||||
></div>
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
<Transition :name="prefer.s.animation ? 'tray' : ''">
|
|
||||||
<XWidgets v-if="widgetsShowing" class="tray"/>
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
<iframe v-if="prefer.s.aiChanMode" ref="live2d" class="ivnzpscs" src="https://misskey-dev.github.io/mascot-web/?scale=2&y=1.4"></iframe>
|
|
||||||
|
|
||||||
<XCommon/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { defineAsyncComponent, onMounted, provide, ref, computed, useTemplateRef } from 'vue';
|
|
||||||
import { instanceName } from '@@/js/config.js';
|
|
||||||
import { isLink } from '@@/js/is-link.js';
|
|
||||||
import XSidebar from './classic.sidebar.vue';
|
|
||||||
import XCommon from './_common_/common.vue';
|
|
||||||
import type { PageMetadata } from '@/page.js';
|
|
||||||
import { StickySidebar } from '@/utility/sticky-sidebar.js';
|
|
||||||
import * as os from '@/os.js';
|
|
||||||
import { provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
|
|
||||||
import { store } from '@/store.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
|
||||||
import { mainRouter } from '@/router.js';
|
|
||||||
import { prefer } from '@/preferences.js';
|
|
||||||
import { DI } from '@/di.js';
|
|
||||||
|
|
||||||
const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue'));
|
|
||||||
const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
|
|
||||||
|
|
||||||
const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
|
|
||||||
|
|
||||||
const DESKTOP_THRESHOLD = 1100;
|
|
||||||
|
|
||||||
const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
|
|
||||||
|
|
||||||
const pageMetadata = ref<null | PageMetadata>(null);
|
|
||||||
const widgetsShowing = ref(false);
|
|
||||||
const fullView = ref(false);
|
|
||||||
const globalHeaderHeight = ref(0);
|
|
||||||
const wallpaper = miLocalStorage.getItem('wallpaper') != null;
|
|
||||||
const showMenuOnTop = computed(() => store.s.menuDisplay === 'top');
|
|
||||||
const live2d = useTemplateRef('live2d');
|
|
||||||
const widgetsLeft = ref<HTMLElement>();
|
|
||||||
const widgetsRight = ref<HTMLElement>();
|
|
||||||
|
|
||||||
provide(DI.router, mainRouter);
|
|
||||||
provideMetadataReceiver((metadataGetter) => {
|
|
||||||
const info = metadataGetter();
|
|
||||||
pageMetadata.value = info;
|
|
||||||
if (pageMetadata.value) {
|
|
||||||
if (isRoot.value && pageMetadata.value.title === instanceName) {
|
|
||||||
window.document.title = pageMetadata.value.title;
|
|
||||||
} else {
|
|
||||||
window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
provideReactiveMetadata(pageMetadata);
|
|
||||||
provide('shouldHeaderThin', showMenuOnTop.value);
|
|
||||||
provide('forceSpacerMin', true);
|
|
||||||
|
|
||||||
function attachSticky(el: HTMLElement) {
|
|
||||||
const sticky = new StickySidebar(el, 0, store.s.menuDisplay === 'top' ? 60 : 0); // TODO: ヘッダーの高さを60pxと決め打ちしているのを直す
|
|
||||||
window.addEventListener('scroll', () => {
|
|
||||||
sticky.calc(window.scrollY);
|
|
||||||
}, { passive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
function top() {
|
|
||||||
window.scroll({ top: 0, behavior: 'smooth' });
|
|
||||||
}
|
|
||||||
|
|
||||||
function onContextmenu(ev: MouseEvent) {
|
|
||||||
if (isLink(ev.target)) return;
|
|
||||||
if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return;
|
|
||||||
if (window.getSelection().toString() !== '') return;
|
|
||||||
const path = mainRouter.getCurrentFullPath();
|
|
||||||
os.contextMenu([{
|
|
||||||
type: 'label',
|
|
||||||
text: path,
|
|
||||||
}, {
|
|
||||||
icon: fullView.value ? 'ti ti-minimize' : 'ti ti-maximize',
|
|
||||||
text: fullView.value ? i18n.ts.quitFullView : i18n.ts.fullView,
|
|
||||||
action: () => {
|
|
||||||
fullView.value = !fullView.value;
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
icon: 'ti ti-window-maximize',
|
|
||||||
text: i18n.ts.openInWindow,
|
|
||||||
action: () => {
|
|
||||||
os.pageWindow(path);
|
|
||||||
},
|
|
||||||
}], ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAiClick(ev) {
|
|
||||||
//if (this.live2d) this.live2d.click(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.innerWidth < 1024) {
|
|
||||||
const currentUI = miLocalStorage.getItem('ui');
|
|
||||||
miLocalStorage.setItem('ui_temp', currentUI ?? 'default');
|
|
||||||
miLocalStorage.setItem('ui', 'default');
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.document.documentElement.style.overflowY = 'scroll';
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
isDesktop.value = (window.innerWidth >= DESKTOP_THRESHOLD);
|
|
||||||
}, { passive: true });
|
|
||||||
|
|
||||||
if (prefer.s.aiChanMode) {
|
|
||||||
const iframeRect = live2d.value.getBoundingClientRect();
|
|
||||||
window.addEventListener('mousemove', ev => {
|
|
||||||
live2d.value.contentWindow.postMessage({
|
|
||||||
type: 'moveCursor',
|
|
||||||
body: {
|
|
||||||
x: ev.clientX - iframeRect.left,
|
|
||||||
y: ev.clientY - iframeRect.top,
|
|
||||||
},
|
|
||||||
}, '*');
|
|
||||||
}, { passive: true });
|
|
||||||
window.addEventListener('touchmove', ev => {
|
|
||||||
live2d.value.contentWindow.postMessage({
|
|
||||||
type: 'moveCursor',
|
|
||||||
body: {
|
|
||||||
x: ev.touches[0].clientX - iframeRect.left,
|
|
||||||
y: ev.touches[0].clientY - iframeRect.top,
|
|
||||||
},
|
|
||||||
}, '*');
|
|
||||||
}, { passive: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.tray-enter-active,
|
|
||||||
.tray-leave-active {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0);
|
|
||||||
transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
|
|
||||||
}
|
|
||||||
.tray-enter-from,
|
|
||||||
.tray-leave-active {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(240px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tray-back-enter-active,
|
|
||||||
.tray-back-leave-active {
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
|
|
||||||
}
|
|
||||||
.tray-back-enter-from,
|
|
||||||
.tray-back-leave-active {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gbhvwtnk {
|
|
||||||
$ui-font-size: 1em;
|
|
||||||
$widgets-hide-threshold: 1200px;
|
|
||||||
|
|
||||||
min-height: 100dvh;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&.wallpaper {
|
|
||||||
background: var(--MI_THEME-wallpaperOverlay);
|
|
||||||
//backdrop-filter: var(--MI-blur, blur(4px));
|
|
||||||
}
|
|
||||||
|
|
||||||
> .columns {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
max-width: 100%;
|
|
||||||
//margin: 32px 0;
|
|
||||||
|
|
||||||
&.fullView {
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
> .sidebar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .widgets {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .main {
|
|
||||||
margin: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
box-shadow: none;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .main {
|
|
||||||
min-width: 0;
|
|
||||||
width: 750px;
|
|
||||||
margin: 0 16px 0 0;
|
|
||||||
border-left: solid 1px var(--MI_THEME-divider);
|
|
||||||
border-right: solid 1px var(--MI_THEME-divider);
|
|
||||||
border-radius: 0;
|
|
||||||
overflow: clip;
|
|
||||||
--MI-margin: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .widgets {
|
|
||||||
//--MI_THEME-panelBorder: none;
|
|
||||||
width: 300px;
|
|
||||||
padding-bottom: calc(var(--MI-margin) + env(safe-area-inset-bottom, 0px));
|
|
||||||
|
|
||||||
@media (max-width: $widgets-hide-threshold) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.left {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .sidebar {
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.withGlobalHeader {
|
|
||||||
> .main {
|
|
||||||
margin-top: 0;
|
|
||||||
border: solid 1px var(--MI_THEME-divider);
|
|
||||||
border-radius: var(--MI-radius);
|
|
||||||
--MI-stickyTop: var(--globalHeaderHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .widgets {
|
|
||||||
--MI-stickyTop: var(--globalHeaderHeight);
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 850px) {
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
> .sidebar {
|
|
||||||
border-right: solid 0.5px var(--MI_THEME-divider);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .main {
|
|
||||||
margin: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
box-shadow: none;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .tray-back {
|
|
||||||
z-index: 1001;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .tray {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 1001;
|
|
||||||
height: 100dvh;
|
|
||||||
padding: var(--MI-margin) var(--MI-margin) calc(var(--MI-margin) + env(safe-area-inset-bottom, 0px));
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: auto;
|
|
||||||
background: var(--MI_THEME-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .ivnzpscs {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 300px;
|
|
||||||
height: 600px;
|
|
||||||
border: none;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
Loading…
Reference in New Issue