This commit is contained in:
kakkokari-gtyih 2025-03-20 15:57:48 +09:00
parent 25f719a5ec
commit 291161ca31
8 changed files with 41 additions and 27 deletions

View File

@ -52,16 +52,12 @@ function chosen(emoji: string) {
const windowEl = useTemplateRef('window'); const windowEl = useTemplateRef('window');
function onCloseRequested() { function close() {
windowEl.value?.close(); windowEl.value?.close();
} }
onMounted(() => { defineExpose({
globalEvents.on('requestCloseEmojiPickerWindow', onCloseRequested); close,
});
onBeforeUnmount(() => {
globalEvents.off('requestCloseEmojiPickerWindow', onCloseRequested);
}); });
</script> </script>

View File

@ -1104,9 +1104,7 @@ onMounted(() => {
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
// MkPostFormDialogDialog emoijPicker.closeWindow();
// Dialog2
globalEvents.emit('requestCloseEmojiPickerWindow');
}); });
defineExpose({ defineExpose({

View File

@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { useTemplateRef } from 'vue'; import { useTemplateRef } from 'vue';
import MkModal from '@/components/MkModal.vue'; import MkModal from '@/components/MkModal.vue';
import MkPostForm from '@/components/MkPostForm.vue'; import MkPostForm from '@/components/MkPostForm.vue';
import { globalEvents } from '@/events.js'; import { emojiPicker } from '@/utility/emoji-picker.js';
import type { PostFormProps } from '@/types/post-form.js'; import type { PostFormProps } from '@/types/post-form.js';
const props = withDefaults(defineProps<PostFormProps & { const props = withDefaults(defineProps<PostFormProps & {
@ -53,10 +53,7 @@ function onPosted() {
} }
function onModalClose() { function onModalClose() {
// MkPostFormonBeforeUnmountDialog emojiPicker.closeWindow();
//
// Dialog2
globalEvents.emit('requestCloseEmojiPickerWindow');
} }
function onModalClosed() { function onModalClosed() {

View File

@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:width="400" :width="400"
:height="500" :height="500"
@close="onCloseModalWindow" @close="onCloseModalWindow"
@closed="console.log('MkRoleSelectDialog: closed') ; $emit('dispose')" @closed="emit('closed')"
> >
<template #header>{{ title }}</template> <template #header>{{ title }}</template>
<MkSpacer :marginMin="20" :marginMax="28"> <MkSpacer :marginMin="20" :marginMax="28">
@ -58,7 +58,7 @@ import MkLoading from '@/components/global/MkLoading.vue';
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'done', value: Misskey.entities.Role[]), (ev: 'done', value: Misskey.entities.Role[]),
(ev: 'close'), (ev: 'close'),
(ev: 'dispose'), (ev: 'closed'),
}>(); }>();
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{

View File

@ -10,5 +10,4 @@ export const globalEvents = new EventEmitter<{
themeChanging: () => void; themeChanging: () => void;
themeChanged: () => void; themeChanged: () => void;
clientNotification: (notification: Misskey.entities.Notification) => void; clientNotification: (notification: Misskey.entities.Notification) => void;
requestCloseEmojiPickerWindow: () => void;
}>(); }>();

View File

@ -5,10 +5,10 @@
// TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する // TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する
import { markRaw, ref, defineAsyncComponent, nextTick } from 'vue'; import { markRaw, ref, shallowRef, defineAsyncComponent, nextTick } from 'vue';
import { EventEmitter } from 'eventemitter3'; import { EventEmitter } from 'eventemitter3';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import type { Component, Ref } from 'vue'; import type { Component, Ref, ShallowRef } from 'vue';
import type { ComponentProps as CP } from 'vue-component-type-helpers'; import type { ComponentProps as CP } from 'vue-component-type-helpers';
import type { Form, GetFormResultType } from '@/utility/form.js'; import type { Form, GetFormResultType } from '@/utility/form.js';
import type { MenuItem } from '@/types/menu.js'; import type { MenuItem } from '@/types/menu.js';
@ -141,6 +141,7 @@ let popupIdCount = 0;
export const popups = ref<{ export const popups = ref<{
id: number; id: number;
component: Component; component: Component;
componentRef: ShallowRef<Component | null>;
props: Record<string, any>; props: Record<string, any>;
events: Record<string, any>; events: Record<string, any>;
}[]>([]); }[]>([]);
@ -178,13 +179,21 @@ type EmitsExtractor<T> = {
[K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize<E> : K extends string ? never : K]: T[K]; [K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize<E> : K extends string ? never : K]: T[K];
}; };
export function popup<T extends Component>( export function popup<
T extends Component,
TI extends T extends new (...args: unknown[]) => infer I ? I : T,
>(
component: T, component: T,
props: ComponentProps<T>, props: ComponentProps<T>,
events: ComponentEmit<T> = {} as ComponentEmit<T>, events: ComponentEmit<T> = {} as ComponentEmit<T>,
): { dispose: () => void } { ): {
dispose: () => void;
componentRef: ShallowRef<TI | null>;
} {
markRaw(component); markRaw(component);
const componentRef = shallowRef<TI | null>(null);
const id = ++popupIdCount; const id = ++popupIdCount;
const dispose = () => { const dispose = () => {
// このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ // このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ
@ -194,6 +203,7 @@ export function popup<T extends Component>(
}; };
const state = { const state = {
component, component,
componentRef,
props, props,
events, events,
id, id,
@ -203,6 +213,7 @@ export function popup<T extends Component>(
return { return {
dispose, dispose,
componentRef,
}; };
} }
@ -627,14 +638,17 @@ export async function selectRole(params: {
{ canceled: false; result: Misskey.entities.Role[] } { canceled: false; result: Misskey.entities.Role[] }
> { > {
return new Promise((resolve) => { return new Promise((resolve) => {
popup(defineAsyncComponent(() => import('@/components/MkRoleSelectDialog.vue')), params, { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkRoleSelectDialog.vue')), params, {
done: roles => { done: roles => {
resolve({ canceled: false, result: roles }); resolve({ canceled: false, result: roles });
}, },
close: () => { close: () => {
resolve({ canceled: true, result: undefined }); resolve({ canceled: true, result: undefined });
}, },
}, 'dispose'); closed: () => {
dispose();
},
});
}); });
} }

View File

@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:is="popup.component" :is="popup.component"
v-for="popup in popups" v-for="popup in popups"
:key="popup.id" :key="popup.id"
:ref="(el: Component | null) => popup.componentRef = el"
v-bind="popup.props" v-bind="popup.props"
v-on="popup.events" v-on="popup.events"
/> />

View File

@ -3,8 +3,9 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { defineAsyncComponent, ref, watch } from 'vue'; import { defineAsyncComponent, ref, shallowRef, watch } from 'vue';
import type { Ref } from 'vue'; import type { Ref, ShallowRef } from 'vue';
import type MkEmojiPickerWindow_TypeOnly from '@/components/MkEmojiPickerWindow.vue';
import { popup } from '@/os.js'; import { popup } from '@/os.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
@ -18,6 +19,7 @@ class EmojiPicker {
private src: Ref<HTMLElement | null> = ref(null); private src: Ref<HTMLElement | null> = ref(null);
private isWindow: boolean = false; private isWindow: boolean = false;
private windowComponentEl: ShallowRef<InstanceType<typeof MkEmojiPickerWindow_TypeOnly> | null> = shallowRef(null);
private windowShowing: boolean = false; private windowShowing: boolean = false;
private dialogShowing = ref(false); private dialogShowing = ref(false);
@ -73,7 +75,7 @@ class EmojiPicker {
if (this.isWindow) { if (this.isWindow) {
if (this.windowShowing) return; if (this.windowShowing) return;
this.windowShowing = true; this.windowShowing = true;
const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerWindow.vue')), { const { dispose, componentRef } = popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerWindow.vue')), {
src: opts.src, src: opts.src,
pinnedEmojis: this.emojisRef, pinnedEmojis: this.emojisRef,
asReactionPicker: false, asReactionPicker: false,
@ -87,6 +89,7 @@ class EmojiPicker {
dispose(); dispose();
}, },
}); });
this.windowComponentEl.value = componentRef.value;
} else { } else {
this.src.value = opts.src; this.src.value = opts.src;
this.dialogShowing.value = true; this.dialogShowing.value = true;
@ -94,6 +97,12 @@ class EmojiPicker {
this.onClosed = opts.onClosed; this.onClosed = opts.onClosed;
} }
} }
public closeWindow() {
if (this.isWindow && this.windowComponentEl.value) {
this.windowComponentEl.value.close();
}
}
} }
export const emojiPicker = new EmojiPicker(); export const emojiPicker = new EmojiPicker();