summaryrefslogtreecommitdiff
path: root/packages/frontend/src
diff options
context:
space:
mode:
authortamaina <tamaina@hotmail.co.jp>2023-08-09 09:08:47 +0900
committerGitHub <noreply@github.com>2023-08-09 09:08:47 +0900
commite6f3dd81ba4ad492158e278ecb0f9d9893bf2958 (patch)
treec20e9671edd38da0bba9c42dd6c71263274014a7 /packages/frontend/src
parentfix(backend/ApNoteService): try retrieving again when failed by duplication (... (diff)
downloadmisskey-e6f3dd81ba4ad492158e278ecb0f9d9893bf2958.tar.gz
misskey-e6f3dd81ba4ad492158e278ecb0f9d9893bf2958.tar.bz2
misskey-e6f3dd81ba4ad492158e278ecb0f9d9893bf2958.zip
fix(frontend): MkPopupMenuがドロワーで子メニューの出現と同時にpopupをresolveさせるのをやめさせる (#11441)
* fix(frontend): MkPopupMenuがドロワーで子メニューの出現と同時にpopupをresolveさせるのをやめさせる * fix * noCache * :v: * fix * ???? * a * a * :v: * fix emoji picker * ????? * close * 1 * fix2 * :v: * fix * :v: * :v: * :v: * preferClick * :v: * fix lint * a * rm nocache
Diffstat (limited to 'packages/frontend/src')
-rw-r--r--packages/frontend/src/components/MkMenu.vue64
-rw-r--r--packages/frontend/src/components/MkPopupMenu.vue46
-rw-r--r--packages/frontend/src/scripts/get-note-menu.ts4
-rw-r--r--packages/frontend/src/scripts/get-user-menu.ts4
-rw-r--r--packages/frontend/src/types/menu.ts2
5 files changed, 86 insertions, 34 deletions
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 3d4e45b1f4..3b20856e12 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -39,11 +39,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitchButton :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)" />
<span :class="$style.switchText">{{ item.text }}</span>
</button>
- <button v-else-if="item.type === 'parent'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="showChildren(item, $event)">
+ <div v-else-if="item.type === 'parent'" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)">
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
<span>{{ item.text }}</span>
<span :class="$style.caret"><i class="ti ti-chevron-right ti-fw"></i></span>
- </button>
+ </div>
<button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
<MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/>
@@ -56,19 +56,24 @@ SPDX-License-Identifier: AGPL-3.0-only
</span>
</div>
<div v-if="childMenu">
- <XChild ref="child" :items="childMenu" :targetElement="childTarget" :rootElement="itemsEl" showing @actioned="childActioned"/>
+ <XChild ref="child" :items="childMenu" :targetElement="childTarget" :rootElement="itemsEl" showing @actioned="childActioned" @close="close(false)"/>
</div>
</div>
</template>
-<script lang="ts" setup>
-import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
+<script lang="ts">
+import { Ref, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { focusPrev, focusNext } from '@/scripts/focus';
import MkSwitchButton from '@/components/MkSwitch.button.vue';
-import { MenuItem, InnerMenuItem, OuterMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu';
+import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu';
import * as os from '@/os';
import { i18n } from '@/i18n';
+import { isTouchUsing } from '@/scripts/touch';
+
+const childrenCache = new WeakMap<MenuParent, MenuItem[]>();
+</script>
+<script lang="ts" setup>
const XChild = defineAsyncComponent(() => import('./MkMenu.child.vue'));
const props = defineProps<{
@@ -82,6 +87,7 @@ const props = defineProps<{
const emit = defineEmits<{
(ev: 'close', actioned?: boolean): void;
+ (ev: 'hide'): void;
}>();
let itemsEl = $shallowRef<HTMLDivElement>();
@@ -98,6 +104,8 @@ let keymap = $computed(() => ({
let childShowingItem = $ref<MenuItem | null>();
+let preferClick = isTouchUsing || props.asDrawer;
+
watch(() => props.items, () => {
const items: (MenuItem | MenuPending)[] = [...props.items].filter(item => item !== undefined);
@@ -117,7 +125,7 @@ watch(() => props.items, () => {
immediate: true,
});
-let childMenu = ref<MenuItem[] | null>();
+const childMenu = ref<MenuItem[] | null>();
let childTarget = $shallowRef<HTMLElement | null>();
function closeChild() {
@@ -130,11 +138,11 @@ function childActioned() {
close(true);
}
-function onGlobalMousedown(event: MouseEvent) {
+const onGlobalMousedown = (event: MouseEvent) => {
if (childTarget && (event.target === childTarget || childTarget.contains(event.target))) return;
if (child && child.checkHit(event)) return;
closeChild();
-}
+};
let childCloseTimer: null | number = null;
function onItemMouseEnter(item) {
@@ -146,31 +154,30 @@ function onItemMouseLeave(item) {
if (childCloseTimer) window.clearTimeout(childCloseTimer);
}
-let childrenCache = new WeakMap<MenuParent, OuterMenuItem[]>();
async function showChildren(item: MenuParent, ev: MouseEvent) {
- const children = ref<OuterMenuItem[]>([]);
- if (childrenCache.has(item)) {
- children.value = childrenCache.get(item)!;
- } else {
- if (typeof item.children === 'function') {
- children.value = [{
- type: 'pending',
- }];
- Promise.resolve(item.children()).then(x => {
- children.value = x;
- childrenCache.set(item, x);
- });
+ const children = await (async () => {
+ if (childrenCache.has(item)) {
+ return childrenCache.get(item)!;
} else {
- children.value = item.children;
+ if (typeof item.children === 'function') {
+ return Promise.resolve(item.children());
+ } else {
+ return item.children;
+ }
}
- }
+ })();
+
+ childrenCache.set(item, children);
if (props.asDrawer) {
- os.popupMenu(children, ev.currentTarget ?? ev.target);
- close();
+ os.popupMenu(children, ev.currentTarget ?? ev.target).finally(() => {
+ emit('close');
+ });
+ emit('hide');
} else {
childTarget = ev.currentTarget ?? ev.target;
- childMenu = children;
+ // これでもリアクティビティは保たれる
+ childMenu.value = children;
childShowingItem = item;
}
}
@@ -200,7 +207,7 @@ function switchItem(item: MenuSwitch & { ref: any }) {
onMounted(() => {
if (props.viaKeyboard) {
nextTick(() => {
- focusNext(itemsEl.children[0], true, false);
+ if (itemsEl) focusNext(itemsEl.children[0], true, false);
});
}
@@ -348,6 +355,7 @@ onBeforeUnmount(() => {
}
&.parent {
+ pointer-events: auto;
display: flex;
align-items: center;
cursor: default;
diff --git a/packages/frontend/src/components/MkPopupMenu.vue b/packages/frontend/src/components/MkPopupMenu.vue
index 6e7656f745..ee7dbecf05 100644
--- a/packages/frontend/src/components/MkPopupMenu.vue
+++ b/packages/frontend/src/components/MkPopupMenu.vue
@@ -4,13 +4,13 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<MkModal ref="modal" v-slot="{ type, maxHeight }" :zPriority="'high'" :src="src" :transparentBg="true" @click="modal.close()" @close="emit('closing')" @closed="emit('closed')">
- <MkMenu :items="items" :align="align" :width="width" :max-height="maxHeight" :asDrawer="type === 'drawer'" :class="{ [$style.drawer]: type === 'drawer' }" @close="modal.close()"/>
+<MkModal ref="modal" v-slot="{ type, maxHeight }" :manualShowing="manualShowing" :zPriority="'high'" :src="src" :transparentBg="true" @click="click" @close="onModalClose" @closed="onModalClosed">
+ <MkMenu :items="items" :align="align" :width="width" :max-height="maxHeight" :asDrawer="type === 'drawer'" :class="{ [$style.drawer]: type === 'drawer' }" @close="onMenuClose" @hide="hide"/>
</MkModal>
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
import MkModal from './MkModal.vue';
import MkMenu from './MkMenu.vue';
import { MenuItem } from '@/types/menu';
@@ -29,6 +29,46 @@ const emit = defineEmits<{
}>();
let modal = $shallowRef<InstanceType<typeof MkModal>>();
+const manualShowing = ref(true);
+const hiding = ref(false);
+
+function click() {
+ close();
+}
+
+function onModalClose() {
+ emit('closing');
+}
+
+function onMenuClose() {
+ close();
+ if (hiding.value) {
+ // hidingであればclosedを発火
+ emit('closed');
+ }
+}
+
+function onModalClosed() {
+ if (!hiding.value) {
+ // hidingでなければclosedを発火
+ emit('closed');
+ }
+}
+
+function hide() {
+ manualShowing.value = false;
+ hiding.value = true;
+
+ // closeは呼ぶ必要がある
+ modal?.close();
+}
+
+function close() {
+ manualShowing.value = false;
+
+ // closeは呼ぶ必要がある
+ modal?.close();
+}
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 20cea45ee3..8e29fc0c9b 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -418,7 +418,9 @@ export function getNoteMenu(props: {
const cleanup = () => {
if (_DEV_) console.log('note menu cleanup', cleanups);
- cleanups.forEach(cleanup => cleanup());
+ for (const cl of cleanups) {
+ cl();
+ }
};
return {
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index 445560b0c3..69a6f75c12 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -330,7 +330,9 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router
const cleanup = () => {
if (_DEV_) console.log('user menu cleanup', cleanups);
- cleanups.forEach(cleanup => cleanup());
+ for (const cl of cleanups) {
+ cl();
+ }
};
return {
diff --git a/packages/frontend/src/types/menu.ts b/packages/frontend/src/types/menu.ts
index b2ba6290c4..66061fcd70 100644
--- a/packages/frontend/src/types/menu.ts
+++ b/packages/frontend/src/types/menu.ts
@@ -16,7 +16,7 @@ export type MenuA = { type: 'a', href: string, target?: string, download?: strin
export type MenuUser = { type: 'user', user: Misskey.entities.User, active?: boolean, indicate?: boolean, action: MenuAction };
export type MenuSwitch = { type: 'switch', ref: Ref<boolean>, text: string, disabled?: boolean };
export type MenuButton = { type?: 'button', text: string, icon?: string, indicate?: boolean, danger?: boolean, active?: boolean, avatar?: Misskey.entities.User; action: MenuAction };
-export type MenuParent = { type: 'parent', text: string, icon?: string, children: OuterMenuItem[] | (() => Promise<OuterMenuItem[]> | OuterMenuItem[]) };
+export type MenuParent = { type: 'parent', text: string, icon?: string, children: MenuItem[] | (() => Promise<MenuItem[]> | MenuItem[]) };
export type MenuPending = { type: 'pending' };