feat(frontend): モバイルデバイスで折りたたまれたUIの展開表示に全画面ページを使用できるように
This commit is contained in:
parent
65c2adee36
commit
23542530e1
|
@ -32,6 +32,7 @@
|
||||||
- チャットなど、一部の機能は引き続き設定に関わらずWebsocket接続が行われます
|
- チャットなど、一部の機能は引き続き設定に関わらずWebsocket接続が行われます
|
||||||
- Feat: 絵文字をミュート可能にする機能
|
- Feat: 絵文字をミュート可能にする機能
|
||||||
- 絵文字(ユニコードの絵文字・カスタム絵文字)毎にミュートし、不可視化することができるようになりました
|
- 絵文字(ユニコードの絵文字・カスタム絵文字)毎にミュートし、不可視化することができるようになりました
|
||||||
|
- Feat: モバイルデバイスで折りたたまれたUIの展開表示に全画面ページを使用できるように(実験的)
|
||||||
- Enhance: メモリ使用量を軽減しました
|
- Enhance: メモリ使用量を軽減しました
|
||||||
- Enhance: 画像の高品質なプレースホルダを無効化してパフォーマンスを向上させるオプションを追加
|
- Enhance: 画像の高品質なプレースホルダを無効化してパフォーマンスを向上させるオプションを追加
|
||||||
- Enhance: 招待されているが参加していないルームを開いたときに、招待を承認するかどうか尋ねるように
|
- Enhance: 招待されているが参加していないルームを開いたときに、招待を承認するかどうか尋ねるように
|
||||||
|
|
|
@ -19,13 +19,42 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.headerRight">
|
<div :class="$style.headerRight">
|
||||||
<span :class="$style.headerRightText"><slot name="suffix"></slot></span>
|
<span :class="$style.headerRightText"><slot name="suffix"></slot></span>
|
||||||
<i v-if="opened" class="ti ti-chevron-up icon"></i>
|
<i v-if="asPage" class="ti ti-chevron-right icon"></i>
|
||||||
|
<i v-else-if="opened" class="ti ti-chevron-up icon"></i>
|
||||||
<i v-else class="ti ti-chevron-down icon"></i>
|
<i v-else class="ti ti-chevron-down icon"></i>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : undefined, overflow: maxHeight ? `auto` : undefined }" :aria-hidden="!opened">
|
<div v-if="asPage">
|
||||||
|
<Teleport v-if="opened" defer :to="`#v-${pageId}-header`">
|
||||||
|
<slot name="label"></slot>
|
||||||
|
</Teleport>
|
||||||
|
<Teleport v-if="opened" defer :to="`#v-${pageId}-body`">
|
||||||
|
<MkStickyContainer>
|
||||||
|
<template #header>
|
||||||
|
<div v-if="$slots.header" :class="$style.inBodyHeader">
|
||||||
|
<slot name="header"></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div v-if="withSpacer" class="_spacer" :style="{ '--MI_SPACER-min': props.spacerMin + 'px', '--MI_SPACER-max': props.spacerMax + 'px' }">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div v-if="$slots.footer" :class="$style.inBodyFooter">
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MkStickyContainer>
|
||||||
|
</Teleport>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : undefined, overflow: maxHeight ? `auto` : undefined }" :aria-hidden="!opened">
|
||||||
<Transition
|
<Transition
|
||||||
:enterActiveClass="prefer.s.animation ? $style.transition_toggle_enterActive : ''"
|
:enterActiveClass="prefer.s.animation ? $style.transition_toggle_enterActive : ''"
|
||||||
:leaveActiveClass="prefer.s.animation ? $style.transition_toggle_leaveActive : ''"
|
:leaveActiveClass="prefer.s.animation ? $style.transition_toggle_leaveActive : ''"
|
||||||
|
@ -70,6 +99,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { nextTick, onMounted, ref, useTemplateRef } from 'vue';
|
import { nextTick, onMounted, ref, useTemplateRef } from 'vue';
|
||||||
import { prefer } from '@/preferences.js';
|
import { prefer } from '@/preferences.js';
|
||||||
import { getBgColor } from '@/utility/get-bg-color.js';
|
import { getBgColor } from '@/utility/get-bg-color.js';
|
||||||
|
import { pageFolderTeleportCount, popup } from '@/os.js';
|
||||||
|
import MkFolderPage from '@/components/MkFolderPage.vue';
|
||||||
|
import { deviceKind } from '@/utility/device-kind.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
defaultOpen?: boolean;
|
defaultOpen?: boolean;
|
||||||
|
@ -77,18 +109,21 @@ const props = withDefaults(defineProps<{
|
||||||
withSpacer?: boolean;
|
withSpacer?: boolean;
|
||||||
spacerMin?: number;
|
spacerMin?: number;
|
||||||
spacerMax?: number;
|
spacerMax?: number;
|
||||||
|
canPage?: boolean;
|
||||||
}>(), {
|
}>(), {
|
||||||
defaultOpen: false,
|
defaultOpen: false,
|
||||||
maxHeight: null,
|
maxHeight: null,
|
||||||
withSpacer: true,
|
withSpacer: true,
|
||||||
spacerMin: 14,
|
spacerMin: 14,
|
||||||
spacerMax: 22,
|
spacerMax: 22,
|
||||||
|
canPage: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const rootEl = useTemplateRef('rootEl');
|
const rootEl = useTemplateRef('rootEl');
|
||||||
|
const asPage = props.canPage && deviceKind === 'smartphone' && prefer.s['experimental.enableFolderPageView'];
|
||||||
const bgSame = ref(false);
|
const bgSame = ref(false);
|
||||||
const opened = ref(props.defaultOpen);
|
const opened = ref(asPage ? false : props.defaultOpen);
|
||||||
const openedAtLeastOnce = ref(props.defaultOpen);
|
const openedAtLeastOnce = ref(opened.value);
|
||||||
|
|
||||||
//#region interpolate-sizeに対応していないブラウザ向け(TODO: 主要ブラウザが対応したら消す)
|
//#region interpolate-sizeに対応していないブラウザ向け(TODO: 主要ブラウザが対応したら消す)
|
||||||
function enter(el: Element) {
|
function enter(el: Element) {
|
||||||
|
@ -126,7 +161,22 @@ function afterLeave(el: Element) {
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
function toggle() {
|
let pageId = pageFolderTeleportCount.value;
|
||||||
|
pageFolderTeleportCount.value += 1000;
|
||||||
|
|
||||||
|
async function toggle() {
|
||||||
|
if (asPage && !opened.value) {
|
||||||
|
pageId++;
|
||||||
|
const { dispose } = await popup(MkFolderPage, {
|
||||||
|
pageId,
|
||||||
|
}, {
|
||||||
|
closed: () => {
|
||||||
|
opened.value = false;
|
||||||
|
dispose();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!opened.value) {
|
if (!opened.value) {
|
||||||
openedAtLeastOnce.value = true;
|
openedAtLeastOnce.value = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Transition
|
||||||
|
name="x"
|
||||||
|
:enterActiveClass="prefer.s.animation ? $style.transition_x_enterActive : ''"
|
||||||
|
:leaveActiveClass="prefer.s.animation ? $style.transition_x_leaveActive : ''"
|
||||||
|
:enterFromClass="prefer.s.animation ? $style.transition_x_enterFrom : ''"
|
||||||
|
:leaveToClass="prefer.s.animation ? $style.transition_x_leaveTo : ''"
|
||||||
|
:duration="300" appear @afterLeave="onClosed"
|
||||||
|
>
|
||||||
|
<div v-show="showing" :class="[$style.root]" :style="{ zIndex }">
|
||||||
|
<div :class="[$style.bg]" :style="{ zIndex }"></div>
|
||||||
|
<div :class="[$style.content]" :style="{ zIndex }">
|
||||||
|
<div :class="$style.header">
|
||||||
|
<button :class="$style.back" class="_button" @click="closePage"><i class="ti ti-chevron-left"></i></button>
|
||||||
|
<div :id="`v-${pageId}-header`" :class="$style.title"></div>
|
||||||
|
<div :class="$style.spacer"></div>
|
||||||
|
</div>
|
||||||
|
<div :id="`v-${pageId}-body`"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { claimZIndex } from '@/os.js';
|
||||||
|
import { prefer } from '@/preferences.js';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
pageId: number,
|
||||||
|
}>(), {
|
||||||
|
pageId: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(_: 'closed'): void
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const zIndex = claimZIndex('middle');
|
||||||
|
const showing = ref(true);
|
||||||
|
|
||||||
|
function closePage() {
|
||||||
|
showing.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClosed() {
|
||||||
|
emit('closed');
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.transition_x_enterActive {
|
||||||
|
> .bg {
|
||||||
|
transition: opacity 0.3s !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .content {
|
||||||
|
transition: transform 0.3s cubic-bezier(0,0,.25,1) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.transition_x_leaveActive {
|
||||||
|
> .bg {
|
||||||
|
transition: opacity 0.3s !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .content {
|
||||||
|
transition: transform 0.3s cubic-bezier(0,0,.25,1) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.transition_x_enterFrom,
|
||||||
|
.transition_x_leaveTo {
|
||||||
|
> .bg {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .content {
|
||||||
|
pointer-events: none;
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.root {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--MI_THEME-modalBg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: auto;
|
||||||
|
background: var(--MI_THEME-bg);
|
||||||
|
container-type: size;
|
||||||
|
overflow: auto;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
--height: 48px;
|
||||||
|
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: var(--height);
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: color(from var(--MI_THEME-panel) srgb r g b / 0.75);
|
||||||
|
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
|
||||||
|
backdrop-filter: var(--MI-blur, blur(15px));
|
||||||
|
border-bottom: solid 0.5px var(--MI_THEME-divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
.back {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: var(--height);
|
||||||
|
height: var(--height);
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--MI_THEME-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 0 auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
width: var(--height);
|
||||||
|
height: var(--height);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -790,3 +790,5 @@ export function launchUploader(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const pageFolderTeleportCount = ref(0);
|
||||||
|
|
|
@ -96,6 +96,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkSwitch v-model="stackingRouterView">
|
<MkSwitch v-model="stackingRouterView">
|
||||||
<template #label>Enable stacking router view</template>
|
<template #label>Enable stacking router view</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
<MkSwitch v-model="enableFolderPageView">
|
||||||
|
<template #label>Enable folder page view</template>
|
||||||
|
</MkSwitch>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
</SearchMarker>
|
</SearchMarker>
|
||||||
|
@ -157,6 +160,7 @@ const enableCondensedLine = prefer.model('enableCondensedLine');
|
||||||
const skipNoteRender = prefer.model('skipNoteRender');
|
const skipNoteRender = prefer.model('skipNoteRender');
|
||||||
const devMode = prefer.model('devMode');
|
const devMode = prefer.model('devMode');
|
||||||
const stackingRouterView = prefer.model('experimental.stackingRouterView');
|
const stackingRouterView = prefer.model('experimental.stackingRouterView');
|
||||||
|
const enableFolderPageView = prefer.model('experimental.enableFolderPageView');
|
||||||
|
|
||||||
watch(skipNoteRender, async () => {
|
watch(skipNoteRender, async () => {
|
||||||
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
|
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
|
||||||
|
|
|
@ -417,4 +417,7 @@ export const PREF_DEF = {
|
||||||
'experimental.stackingRouterView': {
|
'experimental.stackingRouterView': {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
'experimental.enableFolderPageView': {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
} satisfies PreferencesDefinition;
|
} satisfies PreferencesDefinition;
|
||||||
|
|
Loading…
Reference in New Issue