refactor(frontend): カスタムディレクティブの型付け など
(cherry picked from commit 49c9215b61a70e020c5b28a68756bfdc0df3cac7)
This commit is contained in:
parent
a61a2c5f87
commit
1c34a03b7a
|
@ -64,13 +64,13 @@ initialize({
|
||||||
initLocalStorage();
|
initLocalStorage();
|
||||||
queueMicrotask(() => {
|
queueMicrotask(() => {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
import('../src/components'),
|
import('../src/directives/index.js'),
|
||||||
import('../src/directives'),
|
import('../src/components/index.js'),
|
||||||
import('../src/widgets'),
|
import('../src/widgets/index.js'),
|
||||||
import('../src/scripts/theme'),
|
import('../src/scripts/theme.js'),
|
||||||
import('../src/store'),
|
import('../src/store.js'),
|
||||||
import('../src/os'),
|
import('../src/os.js'),
|
||||||
]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { defaultStore }, os]) => {
|
]).then(([{ default: directives }, { default: components }, { default: widgets }, { applyTheme }, { defaultStore }, os]) => {
|
||||||
setup((app) => {
|
setup((app) => {
|
||||||
moduleInitialized = true;
|
moduleInitialized = true;
|
||||||
if (app[appInitialized]) {
|
if (app[appInitialized]) {
|
||||||
|
@ -78,8 +78,8 @@ queueMicrotask(() => {
|
||||||
}
|
}
|
||||||
app[appInitialized] = true;
|
app[appInitialized] = true;
|
||||||
loadTheme(applyTheme);
|
loadTheme(applyTheme);
|
||||||
components(app);
|
|
||||||
directives(app);
|
directives(app);
|
||||||
|
components(app);
|
||||||
widgets(app);
|
widgets(app);
|
||||||
misskeyOS = os;
|
misskeyOS = os;
|
||||||
if (isChromatic()) {
|
if (isChromatic()) {
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
import { computed, watch, version as vueVersion, App } from 'vue';
|
import { computed, watch, version as vueVersion, App } from 'vue';
|
||||||
import { compareVersions } from 'compare-versions';
|
import { compareVersions } from 'compare-versions';
|
||||||
import { version, lang, updateLocale, locale } from '@@/js/config.js';
|
import { version, lang, updateLocale, locale } from '@@/js/config.js';
|
||||||
import widgets from '@/widgets/index.js';
|
|
||||||
import directives from '@/directives/index.js';
|
import directives from '@/directives/index.js';
|
||||||
import components from '@/components/index.js';
|
import components from '@/components/index.js';
|
||||||
|
import widgets from '@/widgets/index.js';
|
||||||
import { applyTheme } from '@/scripts/theme.js';
|
import { applyTheme } from '@/scripts/theme.js';
|
||||||
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
|
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
|
||||||
import { updateI18n, i18n } from '@/i18n.js';
|
import { updateI18n, i18n } from '@/i18n.js';
|
||||||
|
@ -239,9 +239,9 @@ export async function common(createVue: () => App<Element>) {
|
||||||
app.config.performance = true;
|
app.config.performance = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
widgets(app);
|
|
||||||
directives(app);
|
directives(app);
|
||||||
components(app);
|
components(app);
|
||||||
|
widgets(app);
|
||||||
|
|
||||||
// https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114239210
|
// https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114239210
|
||||||
// なぜか2回実行されることがあるため、mountするdivを1つに制限する
|
// なぜか2回実行されることがあるため、mountするdivを1つに制限する
|
||||||
|
|
|
@ -54,7 +54,7 @@ import { defineAsyncComponent, ref } from 'vue';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { widgets as widgetDefs } from '@/widgets/index.js';
|
import { widgetDefs } from '@/widgets/index.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { isLink } from '@@/js/is-link.js';
|
import { isLink } from '@@/js/is-link.js';
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { App } from 'vue';
|
import type { App } from 'vue';
|
||||||
|
|
||||||
import Mfm from './global/MkMfm.js';
|
import Mfm from './global/MkMfm.js';
|
||||||
import MkA from './global/MkA.vue';
|
import MkA from './global/MkA.vue';
|
||||||
|
@ -34,28 +34,28 @@ export default function(app: App) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const components = {
|
export const components = {
|
||||||
I18n: I18n,
|
I18n,
|
||||||
RouterView: RouterView,
|
RouterView,
|
||||||
Mfm: Mfm,
|
Mfm,
|
||||||
MkA: MkA,
|
MkA,
|
||||||
MkAcct: MkAcct,
|
MkAcct,
|
||||||
MkAvatar: MkAvatar,
|
MkAd,
|
||||||
MkEmoji: MkEmoji,
|
MkAvatar,
|
||||||
MkCondensedLine: MkCondensedLine,
|
MkCondensedLine,
|
||||||
MkCustomEmoji: MkCustomEmoji,
|
MkCustomEmoji,
|
||||||
MkUserName: MkUserName,
|
MkEllipsis,
|
||||||
MkEllipsis: MkEllipsis,
|
MkEmoji,
|
||||||
MkTime: MkTime,
|
MkError,
|
||||||
MkUrl: MkUrl,
|
MkFooterSpacer,
|
||||||
MkLoading: MkLoading,
|
MkLazy,
|
||||||
MkError: MkError,
|
MkLoading,
|
||||||
MkAd: MkAd,
|
MkPageHeader,
|
||||||
MkPageHeader: MkPageHeader,
|
MkSpacer,
|
||||||
MkSpacer: MkSpacer,
|
MkStickyContainer,
|
||||||
MkFooterSpacer: MkFooterSpacer,
|
MkTime,
|
||||||
MkStickyContainer: MkStickyContainer,
|
MkUrl,
|
||||||
MkLazy: MkLazy,
|
MkUserName,
|
||||||
};
|
} as const;
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
declare module '@vue/runtime-core' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
@ -64,21 +64,21 @@ declare module '@vue/runtime-core' {
|
||||||
Mfm: typeof Mfm;
|
Mfm: typeof Mfm;
|
||||||
MkA: typeof MkA;
|
MkA: typeof MkA;
|
||||||
MkAcct: typeof MkAcct;
|
MkAcct: typeof MkAcct;
|
||||||
|
MkAd: typeof MkAd;
|
||||||
MkAvatar: typeof MkAvatar;
|
MkAvatar: typeof MkAvatar;
|
||||||
MkEmoji: typeof MkEmoji;
|
|
||||||
MkCondensedLine: typeof MkCondensedLine;
|
MkCondensedLine: typeof MkCondensedLine;
|
||||||
MkCustomEmoji: typeof MkCustomEmoji;
|
MkCustomEmoji: typeof MkCustomEmoji;
|
||||||
MkUserName: typeof MkUserName;
|
|
||||||
MkEllipsis: typeof MkEllipsis;
|
MkEllipsis: typeof MkEllipsis;
|
||||||
MkTime: typeof MkTime;
|
MkEmoji: typeof MkEmoji;
|
||||||
MkUrl: typeof MkUrl;
|
|
||||||
MkLoading: typeof MkLoading;
|
|
||||||
MkError: typeof MkError;
|
MkError: typeof MkError;
|
||||||
MkAd: typeof MkAd;
|
MkFooterSpacer: typeof MkFooterSpacer;
|
||||||
|
MkLazy: typeof MkLazy;
|
||||||
|
MkLoading: typeof MkLoading;
|
||||||
MkPageHeader: typeof MkPageHeader;
|
MkPageHeader: typeof MkPageHeader;
|
||||||
MkSpacer: typeof MkSpacer;
|
MkSpacer: typeof MkSpacer;
|
||||||
MkFooterSpacer: typeof MkFooterSpacer;
|
|
||||||
MkStickyContainer: typeof MkStickyContainer;
|
MkStickyContainer: typeof MkStickyContainer;
|
||||||
MkLazy: typeof MkLazy;
|
MkTime: typeof MkTime;
|
||||||
|
MkUrl: typeof MkUrl;
|
||||||
|
MkUserName: typeof MkUserName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ObjectDirective } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
import { getBgColor } from '@/scripts/get-bg-color.js';
|
|
||||||
|
|
||||||
export const vAdaptiveBg: ObjectDirective<HTMLElement, null | undefined> = {
|
type VAdaptiveBg = ObjectDirective<HTMLElement, null | undefined>;
|
||||||
mounted(src) {
|
|
||||||
|
export const vAdaptiveBg = {
|
||||||
|
async mounted(src) {
|
||||||
|
const [
|
||||||
|
{ getBgColor },
|
||||||
|
] = await Promise.all([
|
||||||
|
import('@/scripts/get-bg-color.js'),
|
||||||
|
]);
|
||||||
|
|
||||||
const parentBg = getBgColor(src.parentElement) ?? 'transparent';
|
const parentBg = getBgColor(src.parentElement) ?? 'transparent';
|
||||||
|
|
||||||
const myBg = window.getComputedStyle(src).backgroundColor;
|
const myBg = window.getComputedStyle(src).backgroundColor;
|
||||||
|
@ -18,4 +25,4 @@ export const vAdaptiveBg: ObjectDirective<HTMLElement, null | undefined> = {
|
||||||
src.style.backgroundColor = myBg;
|
src.style.backgroundColor = myBg;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
} satisfies VAdaptiveBg as VAdaptiveBg;
|
||||||
|
|
|
@ -4,10 +4,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ObjectDirective } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
import { getBgColor } from '@/scripts/get-bg-color.js';
|
|
||||||
|
|
||||||
export const vAdaptiveBorder: ObjectDirective<HTMLElement, null | undefined> = {
|
type VAdaptiveBorder = ObjectDirective<HTMLElement, null | undefined>;
|
||||||
mounted(src) {
|
|
||||||
|
export const vAdaptiveBorder = {
|
||||||
|
async mounted(src) {
|
||||||
|
const [
|
||||||
|
{ getBgColor },
|
||||||
|
] = await Promise.all([
|
||||||
|
import('@/scripts/get-bg-color.js'),
|
||||||
|
]);
|
||||||
|
|
||||||
const parentBg = getBgColor(src.parentElement) ?? 'transparent';
|
const parentBg = getBgColor(src.parentElement) ?? 'transparent';
|
||||||
|
|
||||||
const myBg = window.getComputedStyle(src).backgroundColor;
|
const myBg = window.getComputedStyle(src).backgroundColor;
|
||||||
|
@ -18,4 +25,4 @@ export const vAdaptiveBorder: ObjectDirective<HTMLElement, null | undefined> = {
|
||||||
src.style.borderColor = myBg;
|
src.style.borderColor = myBg;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
} satisfies VAdaptiveBorder as VAdaptiveBorder;
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
|
|
||||||
import type { ObjectDirective } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
|
|
||||||
export const vAnim: ObjectDirective<HTMLElement, number | null | undefined> = {
|
type VAnim = ObjectDirective<HTMLElement, number | null | undefined>;
|
||||||
beforeMount(src) {
|
|
||||||
|
export const vAnim = {
|
||||||
|
async beforeMount(src) {
|
||||||
src.style.opacity = '0';
|
src.style.opacity = '0';
|
||||||
src.style.transform = 'scale(0.9)';
|
src.style.transform = 'scale(0.9)';
|
||||||
// ページネーションと相性が悪いので
|
// ページネーションと相性が悪いので
|
||||||
|
@ -16,10 +18,10 @@ export const vAnim: ObjectDirective<HTMLElement, number | null | undefined> = {
|
||||||
src.classList.add('_zoom');
|
src.classList.add('_zoom');
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted(src) {
|
async mounted(src) {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
src.style.opacity = '1';
|
src.style.opacity = '1';
|
||||||
src.style.transform = 'none';
|
src.style.transform = 'none';
|
||||||
}, 1);
|
}, 1);
|
||||||
},
|
},
|
||||||
};
|
} satisfies VAnim as VAnim;
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
|
|
||||||
import type { ObjectDirective } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
|
|
||||||
export const vAppear: ObjectDirective<HTMLElement, (() => unknown) | null | undefined> = {
|
type VAppear = ObjectDirective<HTMLElement, (() => unknown) | null | undefined>;
|
||||||
mounted(src, binding) {
|
|
||||||
|
export const vAppear = {
|
||||||
|
async mounted(src, binding) {
|
||||||
const fn = binding.value;
|
const fn = binding.value;
|
||||||
if (fn == null) return;
|
if (fn == null) return;
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ export const vAppear: ObjectDirective<HTMLElement, (() => unknown) | null | unde
|
||||||
src._observer_ = observer;
|
src._observer_ = observer;
|
||||||
},
|
},
|
||||||
|
|
||||||
unmounted(src) {
|
async unmounted(src) {
|
||||||
src._observer_?.disconnect();
|
src._observer_?.disconnect();
|
||||||
},
|
},
|
||||||
};
|
} satisfies VAppear as VAppear;
|
||||||
|
|
|
@ -4,10 +4,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ObjectDirective } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
import { defaultStore } from '@/store.js';
|
|
||||||
|
|
||||||
export const vClickAnime: ObjectDirective<HTMLElement, null | undefined> = {
|
type VClickAnime = ObjectDirective<HTMLElement, null | undefined>;
|
||||||
mounted(src) {
|
|
||||||
|
export const vClickAnime = {
|
||||||
|
async mounted(src) {
|
||||||
|
const [
|
||||||
|
{ defaultStore },
|
||||||
|
] = await Promise.all([
|
||||||
|
import('@/store.js'),
|
||||||
|
]);
|
||||||
|
|
||||||
if (!defaultStore.state.animation) return;
|
if (!defaultStore.state.animation) return;
|
||||||
|
|
||||||
const target = src.children[0];
|
const target = src.children[0];
|
||||||
|
@ -37,4 +44,4 @@ export const vClickAnime: ObjectDirective<HTMLElement, null | undefined> = {
|
||||||
target.classList.add('_anime_bounce_standBy');
|
target.classList.add('_anime_bounce_standBy');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
} satisfies VClickAnime as VClickAnime;
|
||||||
|
|
|
@ -11,8 +11,10 @@ const mountings = new Map<HTMLElement, {
|
||||||
fn: (w: number, h: number) => void;
|
fn: (w: number, h: number) => void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
export const vGetSize: ObjectDirective<HTMLElement, ((w: number, h: number) => unknown) | null | undefined> = {
|
type VGetSize = ObjectDirective<HTMLElement, ((w: number, h: number) => unknown) | null | undefined>;
|
||||||
mounted(src, binding) {
|
|
||||||
|
export const vGetSize = {
|
||||||
|
async mounted(src, binding) {
|
||||||
const resize = new ResizeObserver(() => {
|
const resize = new ResizeObserver(() => {
|
||||||
calc(src);
|
calc(src);
|
||||||
});
|
});
|
||||||
|
@ -22,15 +24,15 @@ export const vGetSize: ObjectDirective<HTMLElement, ((w: number, h: number) => u
|
||||||
calc(src);
|
calc(src);
|
||||||
},
|
},
|
||||||
|
|
||||||
unmounted(src, binding) {
|
async unmounted(src, binding) {
|
||||||
if (binding.value != null) binding.value(0, 0);
|
binding.value(0, 0);
|
||||||
const info = mountings.get(src);
|
const info = mountings.get(src);
|
||||||
if (!info) return;
|
if (!info) return;
|
||||||
info.resize.disconnect();
|
info.resize.disconnect();
|
||||||
if (info.intersection) info.intersection.disconnect();
|
if (info.intersection) info.intersection.disconnect();
|
||||||
mountings.delete(src);
|
mountings.delete(src);
|
||||||
},
|
},
|
||||||
};
|
} satisfies VGetSize as VGetSize;
|
||||||
|
|
||||||
function calc(src: HTMLElement) {
|
function calc(src: HTMLElement) {
|
||||||
const info = mountings.get(src);
|
const info = mountings.get(src);
|
||||||
|
|
|
@ -5,10 +5,17 @@
|
||||||
|
|
||||||
import type { ObjectDirective } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
import type { Keymap } from '@/scripts/hotkey.js';
|
import type { Keymap } from '@/scripts/hotkey.js';
|
||||||
import { makeHotkey } from '@/scripts/hotkey.js';
|
|
||||||
|
|
||||||
export const vHotkey: ObjectDirective<HTMLElement, Keymap | null | undefined, 'global'> = {
|
type VHotkey = ObjectDirective<HTMLElement, Keymap | null | undefined, 'global'>;
|
||||||
mounted(src, binding) {
|
|
||||||
|
export const vHotkey = {
|
||||||
|
async mounted(src, binding) {
|
||||||
|
const [
|
||||||
|
{ makeHotkey },
|
||||||
|
] = await Promise.all([
|
||||||
|
import('@/scripts/hotkey.js'),
|
||||||
|
]);
|
||||||
|
|
||||||
src._hotkey_global = binding.modifiers.global === true;
|
src._hotkey_global = binding.modifiers.global === true;
|
||||||
|
|
||||||
src._keyHandler = makeHotkey(binding.value);
|
src._keyHandler = makeHotkey(binding.value);
|
||||||
|
@ -20,11 +27,11 @@ export const vHotkey: ObjectDirective<HTMLElement, Keymap | null | undefined, 'g
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
unmounted(src) {
|
async unmounted(src) {
|
||||||
if (src._hotkey_global) {
|
if (src._hotkey_global) {
|
||||||
document.removeEventListener('keydown', src._keyHandler);
|
document.removeEventListener('keydown', src._keyHandler);
|
||||||
} else {
|
} else {
|
||||||
src.removeEventListener('keydown', src._keyHandler);
|
src.removeEventListener('keydown', src._keyHandler);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
} satisfies VHotkey as VHotkey;
|
||||||
|
|
|
@ -4,10 +4,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ObjectDirective } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
import { getBgColor } from '@/scripts/get-bg-color.js';
|
|
||||||
|
|
||||||
export const vPanel: ObjectDirective<HTMLElement, null | undefined> = {
|
type VPanel = ObjectDirective<HTMLElement, null | undefined>;
|
||||||
mounted(src) {
|
|
||||||
|
export const vPanel = {
|
||||||
|
async mounted(src) {
|
||||||
|
const [
|
||||||
|
{ getBgColor },
|
||||||
|
] = await Promise.all([
|
||||||
|
import('@/scripts/get-bg-color.js'),
|
||||||
|
]);
|
||||||
|
|
||||||
const parentBg = getBgColor(src.parentElement) ?? 'transparent';
|
const parentBg = getBgColor(src.parentElement) ?? 'transparent';
|
||||||
|
|
||||||
const myBg = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel');
|
const myBg = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel');
|
||||||
|
@ -18,4 +25,4 @@ export const vPanel: ObjectDirective<HTMLElement, null | undefined> = {
|
||||||
src.style.backgroundColor = 'var(--MI_THEME-panel)';
|
src.style.backgroundColor = 'var(--MI_THEME-panel)';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
} satisfies VPanel as VPanel;
|
||||||
|
|
|
@ -4,11 +4,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ObjectDirective } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
import { popup } from '@/os.js';
|
|
||||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
|
|
||||||
export const vRipple: ObjectDirective<HTMLElement, boolean | null | undefined> = {
|
type VRipple = ObjectDirective<HTMLElement, boolean | null | undefined>;
|
||||||
mounted(src, binding) {
|
|
||||||
|
export const vRipple = {
|
||||||
|
async mounted(src, binding) {
|
||||||
|
const [
|
||||||
|
{ popup },
|
||||||
|
] = await Promise.all([
|
||||||
|
import('@/os.js'),
|
||||||
|
]);
|
||||||
|
|
||||||
// 明示的に false であればバインドしない
|
// 明示的に false であればバインドしない
|
||||||
if (binding.value === false) return;
|
if (binding.value === false) return;
|
||||||
|
|
||||||
|
@ -23,4 +30,4 @@ export const vRipple: ObjectDirective<HTMLElement, boolean | null | undefined> =
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
} satisfies VRipple as VRipple;
|
||||||
|
|
|
@ -6,16 +6,23 @@
|
||||||
// TODO: useTooltip関数使うようにしたい
|
// TODO: useTooltip関数使うようにしたい
|
||||||
// ただディレクティブ内でonUnmountedなどのcomposition api使えるのか不明
|
// ただディレクティブ内でonUnmountedなどのcomposition api使えるのか不明
|
||||||
|
|
||||||
import { defineAsyncComponent, ref } from 'vue';
|
import { type ObjectDirective, defineAsyncComponent, ref } from 'vue';
|
||||||
import type { ObjectDirective } from 'vue';
|
|
||||||
import { isTouchUsing } from '@/scripts/touch.js';
|
|
||||||
import { popup, alert } from '@/os.js';
|
|
||||||
|
|
||||||
const start = isTouchUsing ? 'touchstart' : 'mouseenter';
|
type VTooltip = ObjectDirective<HTMLElement, string | null | undefined, 'noDelay' | 'mfm' | 'top' | 'right' | 'bottom' | 'left', 'dialog'>;
|
||||||
const end = isTouchUsing ? 'touchend' : 'mouseleave';
|
|
||||||
|
export const vTooltip = {
|
||||||
|
async mounted(src, binding) {
|
||||||
|
const [
|
||||||
|
{ alert, popup },
|
||||||
|
{ isTouchUsing },
|
||||||
|
] = await Promise.all([
|
||||||
|
import('@/os.js'),
|
||||||
|
import('@/scripts/touch.js'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const start = isTouchUsing ? 'touchstart' : 'mouseenter';
|
||||||
|
const end = isTouchUsing ? 'touchend' : 'mouseleave';
|
||||||
|
|
||||||
export const vTooltip: ObjectDirective<HTMLElement, string | null | undefined, 'noDelay' | 'mfm' | 'top' | 'right' | 'bottom' | 'left', 'dialog'> = {
|
|
||||||
mounted(src, binding) {
|
|
||||||
const delay = binding.modifiers.noDelay ? 0 : 100;
|
const delay = binding.modifiers.noDelay ? 0 : 100;
|
||||||
|
|
||||||
const self = (src as any)._tooltipDirective_ = {} as any;
|
const self = (src as any)._tooltipDirective_ = {} as any;
|
||||||
|
@ -97,13 +104,13 @@ export const vTooltip: ObjectDirective<HTMLElement, string | null | undefined, '
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
updated(src, binding) {
|
async updated(src, binding) {
|
||||||
const self = (src as any)._tooltipDirective_;
|
const self = (src as any)._tooltipDirective_;
|
||||||
self.text = binding.value as string;
|
self.text = binding.value as string;
|
||||||
},
|
},
|
||||||
|
|
||||||
unmounted(src) {
|
async unmounted(src) {
|
||||||
const self = (src as any)._tooltipDirective_;
|
const self = (src as any)._tooltipDirective_;
|
||||||
window.clearInterval(self.checkTimer);
|
window.clearInterval(self.checkTimer);
|
||||||
},
|
},
|
||||||
};
|
} satisfies VTooltip as VTooltip;
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { defineAsyncComponent, ref } from 'vue';
|
import { type ObjectDirective, defineAsyncComponent, ref } from 'vue';
|
||||||
import type { ObjectDirective } from 'vue';
|
|
||||||
import { popup } from '@/os.js';
|
import { popup } from '@/os.js';
|
||||||
|
|
||||||
export const vUserPreview: ObjectDirective<HTMLElement, string | null | undefined> = {
|
type VUserPreview = ObjectDirective<HTMLElement, string | null | undefined>;
|
||||||
mounted(src, binding) {
|
|
||||||
|
export const vUserPreview = {
|
||||||
|
async mounted(src, binding) {
|
||||||
if (binding.value == null) return;
|
if (binding.value == null) return;
|
||||||
|
|
||||||
// TODO: 新たにプロパティを作るのをやめMapを使う
|
// TODO: 新たにプロパティを作るのをやめMapを使う
|
||||||
|
@ -18,21 +19,21 @@ export const vUserPreview: ObjectDirective<HTMLElement, string | null | undefine
|
||||||
self.preview = new UserPreview(src, binding.value);
|
self.preview = new UserPreview(src, binding.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
unmounted(src, binding) {
|
async unmounted(src, binding) {
|
||||||
if (binding.value == null) return;
|
if (binding.value == null) return;
|
||||||
|
|
||||||
const self = src._userPreviewDirective_;
|
const self = src._userPreviewDirective_;
|
||||||
self.preview.detach();
|
self.preview.detach();
|
||||||
},
|
},
|
||||||
};
|
} satisfies VUserPreview as VUserPreview;
|
||||||
|
|
||||||
class UserPreview {
|
class UserPreview {
|
||||||
private el: HTMLElement;
|
private el;
|
||||||
private user: string;
|
private user;
|
||||||
private showTimer: number | null = null;
|
private showTimer;
|
||||||
private hideTimer: number | null = null;
|
private hideTimer;
|
||||||
private checkTimer: number | null = null;
|
private checkTimer;
|
||||||
private promise: { cancel: () => void } | null = null;
|
private promise;
|
||||||
|
|
||||||
constructor(el, user) {
|
constructor(el, user) {
|
||||||
this.el = el;
|
this.el = el;
|
||||||
|
|
|
@ -3,40 +3,75 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { App, defineAsyncComponent } from 'vue';
|
import { type App, defineAsyncComponent } from 'vue';
|
||||||
|
|
||||||
|
const WidgetProfile = defineAsyncComponent(() => import('@/widgets/WidgetProfile.vue'));
|
||||||
|
const WidgetInstanceInfo = defineAsyncComponent(() => import('@/widgets/WidgetInstanceInfo.vue'));
|
||||||
|
const WidgetMemo = defineAsyncComponent(() => import('@/widgets/WidgetMemo.vue'));
|
||||||
|
const WidgetNotifications = defineAsyncComponent(() => import('@/widgets/WidgetNotifications.vue'));
|
||||||
|
const WidgetTimeline = defineAsyncComponent(() => import('@/widgets/WidgetTimeline.vue'));
|
||||||
|
const WidgetCalendar = defineAsyncComponent(() => import('@/widgets/WidgetCalendar.vue'));
|
||||||
|
const WidgetRss = defineAsyncComponent(() => import('@/widgets/WidgetRss.vue'));
|
||||||
|
const WidgetRssTicker = defineAsyncComponent(() => import('@/widgets/WidgetRssTicker.vue'));
|
||||||
|
const WidgetTrends = defineAsyncComponent(() => import('@/widgets/WidgetTrends.vue'));
|
||||||
|
const WidgetClock = defineAsyncComponent(() => import('@/widgets/WidgetClock.vue'));
|
||||||
|
const WidgetActivity = defineAsyncComponent(() => import('@/widgets/WidgetActivity.vue'));
|
||||||
|
const WidgetPhotos = defineAsyncComponent(() => import('@/widgets/WidgetPhotos.vue'));
|
||||||
|
const WidgetDigitalClock = defineAsyncComponent(() => import('@/widgets/WidgetDigitalClock.vue'));
|
||||||
|
const WidgetUnixClock = defineAsyncComponent(() => import('@/widgets/WidgetUnixClock.vue'));
|
||||||
|
const WidgetFederation = defineAsyncComponent(() => import('@/widgets/WidgetFederation.vue'));
|
||||||
|
const WidgetInstanceCloud = defineAsyncComponent(() => import('@/widgets/WidgetInstanceCloud.vue'));
|
||||||
|
const WidgetPostForm = defineAsyncComponent(() => import('@/widgets/WidgetPostForm.vue'));
|
||||||
|
const WidgetSlideshow = defineAsyncComponent(() => import('@/widgets/WidgetSlideshow.vue'));
|
||||||
|
const WidgetServerMetric = defineAsyncComponent(() => import('@/widgets/server-metric/index.vue'));
|
||||||
|
const WidgetOnlineUsers = defineAsyncComponent(() => import('@/widgets/WidgetOnlineUsers.vue'));
|
||||||
|
const WidgetJobQueue = defineAsyncComponent(() => import('@/widgets/WidgetJobQueue.vue'));
|
||||||
|
const WidgetButton = defineAsyncComponent(() => import('@/widgets/WidgetButton.vue'));
|
||||||
|
const WidgetAiscript = defineAsyncComponent(() => import('@/widgets/WidgetAiscript.vue'));
|
||||||
|
const WidgetAiscriptApp = defineAsyncComponent(() => import('@/widgets/WidgetAiscriptApp.vue'));
|
||||||
|
const WidgetAichan = defineAsyncComponent(() => import('@/widgets/WidgetAichan.vue'));
|
||||||
|
const WidgetUserList = defineAsyncComponent(() => import('@/widgets/WidgetUserList.vue'));
|
||||||
|
const WidgetClicker = defineAsyncComponent(() => import('@/widgets/WidgetClicker.vue'));
|
||||||
|
const WidgetBirthdayFollowings = defineAsyncComponent(() => import('@/widgets/WidgetBirthdayFollowings.vue'));
|
||||||
|
|
||||||
export default function(app: App) {
|
export default function(app: App) {
|
||||||
app.component('WidgetProfile', defineAsyncComponent(() => import('./WidgetProfile.vue')));
|
for (const [key, value] of Object.entries(widgets)) {
|
||||||
app.component('WidgetInstanceInfo', defineAsyncComponent(() => import('./WidgetInstanceInfo.vue')));
|
app.component(key, value);
|
||||||
app.component('WidgetMemo', defineAsyncComponent(() => import('./WidgetMemo.vue')));
|
}
|
||||||
app.component('WidgetNotifications', defineAsyncComponent(() => import('./WidgetNotifications.vue')));
|
|
||||||
app.component('WidgetTimeline', defineAsyncComponent(() => import('./WidgetTimeline.vue')));
|
|
||||||
app.component('WidgetCalendar', defineAsyncComponent(() => import('./WidgetCalendar.vue')));
|
|
||||||
app.component('WidgetRss', defineAsyncComponent(() => import('./WidgetRss.vue')));
|
|
||||||
app.component('WidgetRssTicker', defineAsyncComponent(() => import('./WidgetRssTicker.vue')));
|
|
||||||
app.component('WidgetTrends', defineAsyncComponent(() => import('./WidgetTrends.vue')));
|
|
||||||
app.component('WidgetClock', defineAsyncComponent(() => import('./WidgetClock.vue')));
|
|
||||||
app.component('WidgetActivity', defineAsyncComponent(() => import('./WidgetActivity.vue')));
|
|
||||||
app.component('WidgetPhotos', defineAsyncComponent(() => import('./WidgetPhotos.vue')));
|
|
||||||
app.component('WidgetDigitalClock', defineAsyncComponent(() => import('./WidgetDigitalClock.vue')));
|
|
||||||
app.component('WidgetUnixClock', defineAsyncComponent(() => import('./WidgetUnixClock.vue')));
|
|
||||||
app.component('WidgetFederation', defineAsyncComponent(() => import('./WidgetFederation.vue')));
|
|
||||||
app.component('WidgetPostForm', defineAsyncComponent(() => import('./WidgetPostForm.vue')));
|
|
||||||
app.component('WidgetSlideshow', defineAsyncComponent(() => import('./WidgetSlideshow.vue')));
|
|
||||||
app.component('WidgetServerMetric', defineAsyncComponent(() => import('./server-metric/index.vue')));
|
|
||||||
app.component('WidgetOnlineUsers', defineAsyncComponent(() => import('./WidgetOnlineUsers.vue')));
|
|
||||||
app.component('WidgetJobQueue', defineAsyncComponent(() => import('./WidgetJobQueue.vue')));
|
|
||||||
app.component('WidgetInstanceCloud', defineAsyncComponent(() => import('./WidgetInstanceCloud.vue')));
|
|
||||||
app.component('WidgetButton', defineAsyncComponent(() => import('./WidgetButton.vue')));
|
|
||||||
app.component('WidgetAiscript', defineAsyncComponent(() => import('./WidgetAiscript.vue')));
|
|
||||||
app.component('WidgetAiscriptApp', defineAsyncComponent(() => import('./WidgetAiscriptApp.vue')));
|
|
||||||
app.component('WidgetAichan', defineAsyncComponent(() => import('./WidgetAichan.vue')));
|
|
||||||
app.component('WidgetUserList', defineAsyncComponent(() => import('./WidgetUserList.vue')));
|
|
||||||
app.component('WidgetClicker', defineAsyncComponent(() => import('./WidgetClicker.vue')));
|
|
||||||
app.component('WidgetBirthdayFollowings', defineAsyncComponent(() => import('./WidgetBirthdayFollowings.vue')));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const widgets = [
|
const widgets = {
|
||||||
|
WidgetProfile,
|
||||||
|
WidgetInstanceInfo,
|
||||||
|
WidgetMemo,
|
||||||
|
WidgetNotifications,
|
||||||
|
WidgetTimeline,
|
||||||
|
WidgetCalendar,
|
||||||
|
WidgetRss,
|
||||||
|
WidgetRssTicker,
|
||||||
|
WidgetTrends,
|
||||||
|
WidgetClock,
|
||||||
|
WidgetActivity,
|
||||||
|
WidgetPhotos,
|
||||||
|
WidgetDigitalClock,
|
||||||
|
WidgetUnixClock,
|
||||||
|
WidgetFederation,
|
||||||
|
WidgetInstanceCloud,
|
||||||
|
WidgetPostForm,
|
||||||
|
WidgetSlideshow,
|
||||||
|
WidgetServerMetric,
|
||||||
|
WidgetOnlineUsers,
|
||||||
|
WidgetJobQueue,
|
||||||
|
WidgetButton,
|
||||||
|
WidgetAiscript,
|
||||||
|
WidgetAiscriptApp,
|
||||||
|
WidgetAichan,
|
||||||
|
WidgetUserList,
|
||||||
|
WidgetClicker,
|
||||||
|
WidgetBirthdayFollowings,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const widgetDefs = [
|
||||||
'profile',
|
'profile',
|
||||||
'instanceInfo',
|
'instanceInfo',
|
||||||
'memo',
|
'memo',
|
||||||
|
@ -65,4 +100,37 @@ export const widgets = [
|
||||||
'userList',
|
'userList',
|
||||||
'clicker',
|
'clicker',
|
||||||
'birthdayFollowings',
|
'birthdayFollowings',
|
||||||
];
|
] as const;
|
||||||
|
|
||||||
|
declare module '@vue/runtime-core' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
WidgetProfile: typeof WidgetProfile;
|
||||||
|
WidgetInstanceInfo: typeof WidgetInstanceInfo;
|
||||||
|
WidgetMemo: typeof WidgetMemo;
|
||||||
|
WidgetNotifications: typeof WidgetNotifications;
|
||||||
|
WidgetTimeline: typeof WidgetTimeline;
|
||||||
|
WidgetCalendar: typeof WidgetCalendar;
|
||||||
|
WidgetRss: typeof WidgetRss;
|
||||||
|
WidgetRssTicker: typeof WidgetRssTicker;
|
||||||
|
WidgetTrends: typeof WidgetTrends;
|
||||||
|
WidgetClock: typeof WidgetClock;
|
||||||
|
WidgetActivity: typeof WidgetActivity;
|
||||||
|
WidgetPhotos: typeof WidgetPhotos;
|
||||||
|
WidgetDigitalClock: typeof WidgetDigitalClock;
|
||||||
|
WidgetUnixClock: typeof WidgetUnixClock;
|
||||||
|
WidgetFederation: typeof WidgetFederation;
|
||||||
|
WidgetInstanceCloud: typeof WidgetInstanceCloud;
|
||||||
|
WidgetPostForm: typeof WidgetPostForm;
|
||||||
|
WidgetSlideshow: typeof WidgetSlideshow;
|
||||||
|
WidgetServerMetric: typeof WidgetServerMetric;
|
||||||
|
WidgetOnlineUsers: typeof WidgetOnlineUsers;
|
||||||
|
WidgetJobQueue: typeof WidgetJobQueue;
|
||||||
|
WidgetButton: typeof WidgetButton;
|
||||||
|
WidgetAiscript: typeof WidgetAiscript;
|
||||||
|
WidgetAiscriptApp: typeof WidgetAiscriptApp;
|
||||||
|
WidgetAichan: typeof WidgetAichan;
|
||||||
|
WidgetUserList: typeof WidgetUserList;
|
||||||
|
WidgetClicker: typeof WidgetClicker;
|
||||||
|
WidgetBirthdayFollowings: typeof WidgetBirthdayFollowings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
import { describe, test, assert, afterEach } from 'vitest';
|
import { describe, test, assert, afterEach } from 'vitest';
|
||||||
import { render, cleanup, type RenderResult } from '@testing-library/vue';
|
import { render, cleanup, type RenderResult } from '@testing-library/vue';
|
||||||
import { defaultStoreState } from './init.js';
|
import { defaultStoreState } from './init.js';
|
||||||
import { getEmojiName } from '@@/js/emojilist.js';
|
import { getEmojiName } from '../../frontend-shared/js/emojilist.js';
|
||||||
import { components } from '@/components/index.js';
|
|
||||||
import { directives } from '@/directives/index.js';
|
import { directives } from '@/directives/index.js';
|
||||||
|
import { components } from '@/components/index.js';
|
||||||
import MkEmoji from '@/components/global/MkEmoji.vue';
|
import MkEmoji from '@/components/global/MkEmoji.vue';
|
||||||
|
|
||||||
describe('Emoji', () => {
|
describe('Emoji', () => {
|
||||||
|
|
|
@ -7,8 +7,8 @@ import { describe, test, assert, afterEach } from 'vitest';
|
||||||
import { render, cleanup, type RenderResult } from '@testing-library/vue';
|
import { render, cleanup, type RenderResult } from '@testing-library/vue';
|
||||||
import './init';
|
import './init';
|
||||||
import type * as Misskey from 'misskey-js';
|
import type * as Misskey from 'misskey-js';
|
||||||
import { components } from '@/components/index.js';
|
|
||||||
import { directives } from '@/directives/index.js';
|
import { directives } from '@/directives/index.js';
|
||||||
|
import { components } from '@/components/index.js';
|
||||||
import MkMediaImage from '@/components/MkMediaImage.vue';
|
import MkMediaImage from '@/components/MkMediaImage.vue';
|
||||||
|
|
||||||
describe('MkMediaImage', () => {
|
describe('MkMediaImage', () => {
|
||||||
|
|
|
@ -7,8 +7,8 @@ import { describe, test, assert, afterEach } from 'vitest';
|
||||||
import { render, cleanup, type RenderResult } from '@testing-library/vue';
|
import { render, cleanup, type RenderResult } from '@testing-library/vue';
|
||||||
import './init';
|
import './init';
|
||||||
import type { summaly } from '@misskey-dev/summaly';
|
import type { summaly } from '@misskey-dev/summaly';
|
||||||
import { components } from '@/components/index.js';
|
|
||||||
import { directives } from '@/directives/index.js';
|
import { directives } from '@/directives/index.js';
|
||||||
|
import { components } from '@/components/index.js';
|
||||||
import MkUrlPreview from '@/components/MkUrlPreview.vue';
|
import MkUrlPreview from '@/components/MkUrlPreview.vue';
|
||||||
|
|
||||||
type SummalyResult = Awaited<ReturnType<typeof summaly>>;
|
type SummalyResult = Awaited<ReturnType<typeof summaly>>;
|
||||||
|
|
Loading…
Reference in a new issue