diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index 4492e38fdb3c..b65152a49153 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -3,30 +3,30 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { App } from 'vue'; +import type { App } from 'vue'; -import Mfm from './global/MkMfm.js'; -import MkA from './global/MkA.vue'; -import MkAcct from './global/MkAcct.vue'; -import MkAvatar from './global/MkAvatar.vue'; -import MkEmoji from './global/MkEmoji.vue'; -import MkCondensedLine from './global/MkCondensedLine.vue'; -import MkCustomEmoji from './global/MkCustomEmoji.vue'; -import MkUserName from './global/MkUserName.vue'; -import MkEllipsis from './global/MkEllipsis.vue'; -import MkTime from './global/MkTime.vue'; -import MkUrl from './global/MkUrl.vue'; -import I18n from './global/I18n.vue'; -import RouterView from './global/RouterView.vue'; -import MkLoading from './global/MkLoading.vue'; -import MkError from './global/MkError.vue'; -import MkAd from './global/MkAd.vue'; -import MkPageHeader from './global/MkPageHeader.vue'; -import MkSpacer from './global/MkSpacer.vue'; -import MkFooterSpacer from './global/MkFooterSpacer.vue'; -import MkStickyContainer from './global/MkStickyContainer.vue'; -import MkLazy from './global/MkLazy.vue'; -import TmsNoCache from './global/TmsNoCache.vue'; +import I18n from '@/components/global/I18n.vue'; +import RouterView from '@/components/global/RouterView.vue'; +import Mfm from '@/components/global/MkMfm.js'; +import MkA from '@/components/global/MkA.vue'; +import MkAcct from '@/components/global/MkAcct.vue'; +import MkAd from '@/components/global/MkAd.vue'; +import MkAvatar from '@/components/global/MkAvatar.vue'; +import MkCondensedLine from '@/components/global/MkCondensedLine.vue'; +import MkCustomEmoji from '@/components/global/MkCustomEmoji.vue'; +import MkEllipsis from '@/components/global/MkEllipsis.vue'; +import MkEmoji from '@/components/global/MkEmoji.vue'; +import MkError from '@/components/global/MkError.vue'; +import MkFooterSpacer from '@/components/global/MkFooterSpacer.vue'; +import MkLazy from '@/components/global/MkLazy.vue'; +import MkLoading from '@/components/global/MkLoading.vue'; +import MkPageHeader from '@/components/global/MkPageHeader.vue'; +import MkSpacer from '@/components/global/MkSpacer.vue'; +import MkStickyContainer from '@/components/global/MkStickyContainer.vue'; +import MkTime from '@/components/global/MkTime.vue'; +import MkUrl from '@/components/global/MkUrl.vue'; +import MkUserName from '@/components/global/MkUserName.vue'; +import TmsNoCache from '@/components/global/TmsNoCache.vue'; // eslint-disable-next-line import/no-default-export export default function(app: App) { @@ -41,24 +41,24 @@ export const components = { Mfm: Mfm, MkA: MkA, MkAcct: MkAcct, + MkAd: MkAd, MkAvatar: MkAvatar, - MkEmoji: MkEmoji, MkCondensedLine: MkCondensedLine, MkCustomEmoji: MkCustomEmoji, - MkUserName: MkUserName, MkEllipsis: MkEllipsis, - MkTime: MkTime, - MkUrl: MkUrl, - MkLoading: MkLoading, + MkEmoji: MkEmoji, MkError: MkError, - MkAd: MkAd, + MkFooterSpacer: MkFooterSpacer, + MkLazy: MkLazy, + MkLoading: MkLoading, MkPageHeader: MkPageHeader, MkSpacer: MkSpacer, - MkFooterSpacer: MkFooterSpacer, MkStickyContainer: MkStickyContainer, - MkLazy: MkLazy, + MkTime: MkTime, + MkUrl: MkUrl, + MkUserName: MkUserName, TmsNoCache: TmsNoCache, -}; +} as const; declare module '@vue/runtime-core' { export interface GlobalComponents { @@ -67,22 +67,22 @@ declare module '@vue/runtime-core' { Mfm: typeof Mfm; MkA: typeof MkA; MkAcct: typeof MkAcct; + MkAd: typeof MkAd; MkAvatar: typeof MkAvatar; - MkEmoji: typeof MkEmoji; MkCondensedLine: typeof MkCondensedLine; MkCustomEmoji: typeof MkCustomEmoji; - MkUserName: typeof MkUserName; MkEllipsis: typeof MkEllipsis; - MkTime: typeof MkTime; - MkUrl: typeof MkUrl; - MkLoading: typeof MkLoading; + MkEmoji: typeof MkEmoji; MkError: typeof MkError; - MkAd: typeof MkAd; + MkFooterSpacer: typeof MkFooterSpacer; + MkLazy: typeof MkLazy; + MkLoading: typeof MkLoading; MkPageHeader: typeof MkPageHeader; MkSpacer: typeof MkSpacer; - MkFooterSpacer: typeof MkFooterSpacer; MkStickyContainer: typeof MkStickyContainer; - MkLazy: typeof MkLazy; + MkTime: typeof MkTime; + MkUrl: typeof MkUrl; + MkUserName: typeof MkUserName; TmsNoCache: typeof TmsNoCache; } } diff --git a/packages/frontend/src/directives/adaptive-bg.ts b/packages/frontend/src/directives/adaptive-bg.ts index dee0372d65a0..8ebcaddfbd4a 100644 --- a/packages/frontend/src/directives/adaptive-bg.ts +++ b/packages/frontend/src/directives/adaptive-bg.ts @@ -3,12 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Directive } from 'vue'; +import type { ObjectDirective } from 'vue'; import { getBgColor } from '@/scripts/tms/get-bg-color.js'; -// eslint-disable-next-line import/no-default-export -export default { - mounted(src, binding, vn) { +export const vAdaptiveBg: ObjectDirective = { + mounted(src) { const parentBg = getBgColor(src.parentElement) ?? 'transparent'; const myBg = window.getComputedStyle(src).backgroundColor; @@ -19,4 +18,4 @@ export default { src.style.backgroundColor = myBg; } }, -} as Directive; +}; diff --git a/packages/frontend/src/directives/adaptive-border.ts b/packages/frontend/src/directives/adaptive-border.ts index a6280372af76..c8ce11672844 100644 --- a/packages/frontend/src/directives/adaptive-border.ts +++ b/packages/frontend/src/directives/adaptive-border.ts @@ -3,12 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Directive } from 'vue'; +import type { ObjectDirective } from 'vue'; import { getBgColor } from '@/scripts/tms/get-bg-color.js'; -// eslint-disable-next-line import/no-default-export -export default { - mounted(src, binding, vn) { +export const vAdaptiveBorder: ObjectDirective = { + mounted(src) { const parentBg = getBgColor(src.parentElement) ?? 'transparent'; const myBg = window.getComputedStyle(src).backgroundColor; @@ -19,4 +18,4 @@ export default { src.style.borderColor = myBg; } }, -} as Directive; +}; diff --git a/packages/frontend/src/directives/anim.ts b/packages/frontend/src/directives/anim.ts index 800f8712445a..089bd919d55f 100644 --- a/packages/frontend/src/directives/anim.ts +++ b/packages/frontend/src/directives/anim.ts @@ -3,22 +3,23 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Directive } from 'vue'; +import type { ObjectDirective } from 'vue'; -// eslint-disable-next-line import/no-default-export -export default { - beforeMount(src, binding, vn) { +export const vAnim: ObjectDirective = { + beforeMount(src) { src.style.opacity = '0'; src.style.transform = 'scale(0.9)'; // ページネーションと相性が悪いので - // if (typeof binding.value === 'number') src.style.transitionDelay = `${binding.value * 30}ms`; + // if (typeof binding.value === 'number') { + // src.style.transitionDelay = `${binding.value * 30}ms`; + // } src.classList.add('_zoom'); }, - mounted(src, binding, vn) { + mounted(src) { window.setTimeout(() => { src.style.opacity = '1'; src.style.transform = 'none'; }, 1); }, -} as Directive; +}; diff --git a/packages/frontend/src/directives/appear.ts b/packages/frontend/src/directives/appear.ts index de0378d3f908..790ab16ab586 100644 --- a/packages/frontend/src/directives/appear.ts +++ b/packages/frontend/src/directives/appear.ts @@ -3,16 +3,15 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Directive } from 'vue'; +import type { ObjectDirective } from 'vue'; -// eslint-disable-next-line import/no-default-export -export default { - mounted(src, binding, vn) { +export const vAppear: ObjectDirective unknown) | null | undefined> = { + mounted(src, binding) { const fn = binding.value; if (fn == null) return; - const observer = new IntersectionObserver(entries => { - if (entries.some(entry => entry.isIntersecting)) { + const observer = new IntersectionObserver((entries) => { + if (entries.some((entry) => entry.isIntersecting)) { fn(); } }); @@ -22,7 +21,7 @@ export default { src._observer_ = observer; }, - unmounted(src, binding, vn) { - if (src._observer_) src._observer_.disconnect(); + unmounted(src) { + src._observer_?.disconnect(); }, -} as Directive; +}; diff --git a/packages/frontend/src/directives/click-anime.ts b/packages/frontend/src/directives/click-anime.ts index 235d80b4bb62..b34b44c9b99f 100644 --- a/packages/frontend/src/directives/click-anime.ts +++ b/packages/frontend/src/directives/click-anime.ts @@ -3,21 +3,20 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Directive } from 'vue'; +import type { ObjectDirective } from 'vue'; import { defaultStore } from '@/store.js'; -// eslint-disable-next-line import/no-default-export -export default { - mounted(el: HTMLElement, binding, vn) { +export const vClickAnime: ObjectDirective = { + mounted(src) { if (!defaultStore.state.animation) return; - const target = el.children[0]; + const target = src.children[0]; if (target == null) return; target.classList.add('_anime_bounce_standBy'); - el.addEventListener('mousedown', () => { + src.addEventListener('mousedown', () => { target.classList.remove('_anime_bounce'); target.classList.add('_anime_bounce_standBy'); @@ -28,14 +27,14 @@ export default { }, { passive: true }); }, { passive: true }); - el.addEventListener('click', () => { + src.addEventListener('click', () => { target.classList.add('_anime_bounce'); target.classList.remove('_anime_bounce_ready'); }, { passive: true }); - el.addEventListener('animationend', () => { + src.addEventListener('animationend', () => { target.classList.remove('_anime_bounce'); target.classList.add('_anime_bounce_standBy'); }, { passive: true }); }, -} as Directive; +}; diff --git a/packages/frontend/src/directives/follow-append.ts b/packages/frontend/src/directives/follow-append.ts deleted file mode 100644 index 6d58373468f9..000000000000 --- a/packages/frontend/src/directives/follow-append.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { Directive } from 'vue'; -import { getScrollContainer, getScrollPosition } from '@@/js/scroll.js'; - -// eslint-disable-next-line import/no-default-export -export default { - mounted(src, binding, vn) { - if (binding.value === false) return; - - let isBottom = true; - - const container = getScrollContainer(src)!; - container.addEventListener('scroll', () => { - const pos = getScrollPosition(container); - const viewHeight = container.clientHeight; - const height = container.scrollHeight; - isBottom = (pos + viewHeight > height - 32); - }, { passive: true }); - container.scrollTop = container.scrollHeight; - - const ro = new ResizeObserver((entries, observer) => { - if (isBottom) { - const height = container.scrollHeight; - container.scrollTop = height; - } - }); - - ro.observe(src); - - // TODO: 新たにプロパティを作るのをやめMapを使う - src._ro_ = ro; - }, - - unmounted(src, binding, vn) { - if (src._ro_) src._ro_.unobserve(src); - }, -} as Directive; diff --git a/packages/frontend/src/directives/get-size.ts b/packages/frontend/src/directives/get-size.ts index 55801b974bc6..195ed650705a 100644 --- a/packages/frontend/src/directives/get-size.ts +++ b/packages/frontend/src/directives/get-size.ts @@ -3,15 +3,36 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Directive } from 'vue'; +import type { ObjectDirective } from 'vue'; -const mountings = new Map void; }>(); -function calc(src: Element) { +export const vGetSize: ObjectDirective unknown) | null | undefined> = { + mounted(src, binding) { + const resize = new ResizeObserver(() => { + calc(src); + }); + resize.observe(src); + + mountings.set(src, { resize, fn: binding.value }); + calc(src); + }, + + unmounted(src, binding) { + binding.value(0, 0); + const info = mountings.get(src); + if (!info) return; + info.resize.disconnect(); + if (info.intersection) info.intersection.disconnect(); + mountings.delete(src); + }, +}; + +function calc(src: HTMLElement) { const info = mountings.get(src); const height = src.clientHeight; const width = src.clientWidth; @@ -22,8 +43,8 @@ function calc(src: Element) { if (!height) { // IntersectionObserverで表示検出する if (!info.intersection) { - info.intersection = new IntersectionObserver(entries => { - if (entries.some(entry => entry.isIntersecting)) calc(src); + info.intersection = new IntersectionObserver((entries) => { + if (entries.some((entry) => entry.isIntersecting)) calc(src); }); } info.intersection.observe(src); @@ -36,25 +57,3 @@ function calc(src: Element) { info.fn(width, height); } - -// eslint-disable-next-line import/no-default-export -export default { - mounted(src, binding, vn) { - const resize = new ResizeObserver((entries, observer) => { - calc(src); - }); - resize.observe(src); - - mountings.set(src, { resize, fn: binding.value }); - calc(src); - }, - - unmounted(src, binding, vn) { - binding.value(0, 0); - const info = mountings.get(src); - if (!info) return; - info.resize.disconnect(); - if (info.intersection) info.intersection.disconnect(); - mountings.delete(src); - }, -} as Directive void>; diff --git a/packages/frontend/src/directives/hotkey.ts b/packages/frontend/src/directives/hotkey.ts index 9e81f76d2da8..af02ed89be2e 100644 --- a/packages/frontend/src/directives/hotkey.ts +++ b/packages/frontend/src/directives/hotkey.ts @@ -3,28 +3,28 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Directive } from 'vue'; +import type { ObjectDirective } from 'vue'; +import type { Keymap } from '@/scripts/hotkey.js'; import { makeHotkey } from '@/scripts/hotkey.js'; -// eslint-disable-next-line import/no-default-export -export default { - mounted(el, binding) { - el._hotkey_global = binding.modifiers.global === true; +export const vHotkey: ObjectDirective = { + mounted(src, binding) { + src._hotkey_global = binding.modifiers.global === true; - el._keyHandler = makeHotkey(binding.value); + src._keyHandler = makeHotkey(binding.value); - if (el._hotkey_global) { - document.addEventListener('keydown', el._keyHandler, { passive: false }); + if (src._hotkey_global) { + document.addEventListener('keydown', src._keyHandler, { passive: false }); } else { - el.addEventListener('keydown', el._keyHandler, { passive: false }); + src.addEventListener('keydown', src._keyHandler, { passive: false }); } }, - unmounted(el) { - if (el._hotkey_global) { - document.removeEventListener('keydown', el._keyHandler); + unmounted(src) { + if (src._hotkey_global) { + document.removeEventListener('keydown', src._keyHandler); } else { - el.removeEventListener('keydown', el._keyHandler); + src.removeEventListener('keydown', src._keyHandler); } }, -} as Directive; +}; diff --git a/packages/frontend/src/directives/index.ts b/packages/frontend/src/directives/index.ts index 58ddfc0795e8..04ec508e452e 100644 --- a/packages/frontend/src/directives/index.ts +++ b/packages/frontend/src/directives/index.ts @@ -3,19 +3,19 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { App } from 'vue'; +import type { App } from 'vue'; -import userPreview from './user-preview.js'; -import getSize from './get-size.js'; -import ripple from './ripple.js'; -import tooltip from './tooltip.js'; -import hotkey from './hotkey.js'; -import appear from './appear.js'; -import anim from './anim.js'; -import clickAnime from './click-anime.js'; -import panel from './panel.js'; -import adaptiveBorder from './adaptive-border.js'; -import adaptiveBg from './adaptive-bg.js'; +import { vAdaptiveBg } from '@/directives/adaptive-bg.js'; +import { vAdaptiveBorder } from '@/directives/adaptive-border.js'; +import { vAnim } from '@/directives/anim.js'; +import { vAppear } from '@/directives/appear.js'; +import { vClickAnime } from '@/directives/click-anime.js'; +import { vGetSize } from '@/directives/get-size.js'; +import { vHotkey } from '@/directives/hotkey.js'; +import { vPanel } from '@/directives/panel.js'; +import { vRipple } from '@/directives/ripple.js'; +import { vTooltip } from '@/directives/tooltip.js'; +import { vUserPreview } from '@/directives/user-preview.js'; // eslint-disable-next-line import/no-default-export export default function(app: App) { @@ -25,16 +25,31 @@ export default function(app: App) { } export const directives = { - 'userPreview': userPreview, - 'user-preview': userPreview, - 'get-size': getSize, - 'ripple': ripple, - 'tooltip': tooltip, - 'hotkey': hotkey, - 'appear': appear, - 'anim': anim, - 'click-anime': clickAnime, - 'panel': panel, - 'adaptive-border': adaptiveBorder, - 'adaptive-bg': adaptiveBg, -}; + 'adaptive-bg': vAdaptiveBg, + 'adaptive-border': vAdaptiveBorder, + 'anim': vAnim, + 'appear': vAppear, + 'click-anime': vClickAnime, + 'get-size': vGetSize, + 'hotkey': vHotkey, + 'panel': vPanel, + 'ripple': vRipple, + 'tooltip': vTooltip, + 'user-preview': vUserPreview, +} as const; + +declare module '@vue/runtime-core' { + export interface GlobalDirectives { + vAdaptiveBg: typeof vAdaptiveBg; + vAdaptiveBorder: typeof vAdaptiveBorder; + vAnim: typeof vAnim; + vAppear: typeof vAppear; + vClickAnime: typeof vClickAnime; + vGetSize: typeof vGetSize; + vHotkey: typeof vHotkey; + vPanel: typeof vPanel; + vRipple: typeof vRipple; + vTooltip: typeof vTooltip; + vUserPreview: typeof vUserPreview; + } +} diff --git a/packages/frontend/src/directives/panel.ts b/packages/frontend/src/directives/panel.ts index 8cddf3931c77..524188bfb78c 100644 --- a/packages/frontend/src/directives/panel.ts +++ b/packages/frontend/src/directives/panel.ts @@ -3,12 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Directive } from 'vue'; +import type { ObjectDirective } from 'vue'; import { getBgColor } from '@/scripts/tms/get-bg-color.js'; -// eslint-disable-next-line import/no-default-export -export default { - mounted(src, binding, vn) { +export const vPanel: ObjectDirective = { + mounted(src) { const parentBg = getBgColor(src.parentElement) ?? 'transparent'; const myBg = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel'); @@ -19,4 +18,4 @@ export default { src.style.backgroundColor = 'var(--MI_THEME-panel)'; } }, -} as Directive; +}; diff --git a/packages/frontend/src/directives/ripple.ts b/packages/frontend/src/directives/ripple.ts index 2c3681e9b691..afc9d0a4be18 100644 --- a/packages/frontend/src/directives/ripple.ts +++ b/packages/frontend/src/directives/ripple.ts @@ -3,20 +3,20 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import MkRippleEffect from '@/components/MkRippleEffect.vue'; +import type { ObjectDirective } from 'vue'; import { popup } from '@/os.js'; +import MkRippleEffect from '@/components/MkRippleEffect.vue'; -// eslint-disable-next-line import/no-default-export -export default { - mounted(el, binding, vn) { +export const vRipple: ObjectDirective = { + mounted(src, binding) { // 明示的に false であればバインドしない if (binding.value === false) return; - el.addEventListener('click', () => { - const rect = el.getBoundingClientRect(); + src.addEventListener('click', () => { + const rect = src.getBoundingClientRect(); - const x = rect.left + (el.offsetWidth / 2); - const y = rect.top + (el.offsetHeight / 2); + const x = rect.left + (src.offsetWidth / 2); + const y = rect.top + (src.offsetHeight / 2); const { dispose } = popup(MkRippleEffect, { x, y }, { end: () => dispose(), diff --git a/packages/frontend/src/directives/tooltip.ts b/packages/frontend/src/directives/tooltip.ts index c4222c40d0fb..9fed64caeaef 100644 --- a/packages/frontend/src/directives/tooltip.ts +++ b/packages/frontend/src/directives/tooltip.ts @@ -6,19 +6,19 @@ // TODO: useTooltip関数使うようにしたい // ただディレクティブ内でonUnmountedなどのcomposition api使えるのか不明 -import { defineAsyncComponent, Directive, ref } from 'vue'; +import { 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'; const end = isTouchUsing ? 'touchend' : 'mouseleave'; -// eslint-disable-next-line import/no-default-export -export default { - mounted(el: HTMLElement, binding, vn) { +export const vTooltip: ObjectDirective = { + mounted(src, binding) { const delay = binding.modifiers.noDelay ? 0 : 100; - const self = (el as any)._tooltipDirective_ = {} as any; + const self = (src as any)._tooltipDirective_ = {} as any; self.text = binding.value as string; self._close = null; @@ -35,7 +35,7 @@ export default { }; if (binding.arg === 'dialog') { - el.addEventListener('click', (ev) => { + src.addEventListener('click', (ev) => { ev.preventDefault(); ev.stopPropagation(); alert({ @@ -46,7 +46,7 @@ export default { } self.show = () => { - if (!document.body.contains(el)) return; + if (!document.body.contains(src)) return; if (self._close) return; if (self.text == null) return; @@ -56,7 +56,7 @@ export default { text: self.text, asMfm: binding.modifiers.mfm, direction: binding.modifiers.left ? 'left' : binding.modifiers.right ? 'right' : binding.modifiers.top ? 'top' : binding.modifiers.bottom ? 'bottom' : 'top', - targetElement: el, + targetElement: src, }, { closed: () => dispose(), }); @@ -66,11 +66,11 @@ export default { }; }; - el.addEventListener('selectstart', ev => { + src.addEventListener('selectstart', (ev) => { ev.preventDefault(); }, { passive: false }); - el.addEventListener(start, () => { + src.addEventListener(start, () => { window.clearTimeout(self.showTimer); window.clearTimeout(self.hideTimer); if (delay === 0) { @@ -80,7 +80,7 @@ export default { } }, { passive: true }); - el.addEventListener(end, () => { + src.addEventListener(end, () => { window.clearTimeout(self.showTimer); window.clearTimeout(self.hideTimer); if (delay === 0) { @@ -90,19 +90,19 @@ export default { } }, { passive: true }); - el.addEventListener('click', () => { + src.addEventListener('click', () => { window.clearTimeout(self.showTimer); self.close(); }, { passive: true }); }, - updated(el, binding) { - const self = el._tooltipDirective_; + updated(src, binding) { + const self = (src as any)._tooltipDirective_; self.text = binding.value as string; }, - unmounted(el, binding, vn) { - const self = el._tooltipDirective_; + unmounted(src) { + const self = (src as any)._tooltipDirective_; window.clearInterval(self.checkTimer); }, -} as Directive; +}; diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts index 3c7322fc91a8..01f7c6eeec9c 100644 --- a/packages/frontend/src/directives/user-preview.ts +++ b/packages/frontend/src/directives/user-preview.ts @@ -3,10 +3,30 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { defineAsyncComponent, Directive, ref } from 'vue'; +import { defineAsyncComponent, ref } from 'vue'; +import type { ObjectDirective } from 'vue'; import { popup } from '@/os.js'; -export class UserPreview { +export const vUserPreview: ObjectDirective = { + mounted(src, binding) { + if (binding.value == null) return; + + // TODO: 新たにプロパティを作るのをやめMapを使う + // ただメモリ的には↓の方が省メモリかもしれないので検討中 + const self = (src as any)._userPreviewDirective_ = {} as any; + + self.preview = new UserPreview(src, binding.value); + }, + + unmounted(src, binding) { + if (binding.value == null) return; + + const self = src._userPreviewDirective_; + self.preview.detach(); + }, +}; + +class UserPreview { private el; private user; private showTimer; @@ -102,23 +122,3 @@ export class UserPreview { this.el.removeEventListener('click', this.onClick); } } - -// eslint-disable-next-line import/no-default-export -export default { - mounted(el: HTMLElement, binding, vn) { - if (binding.value == null) return; - - // TODO: 新たにプロパティを作るのをやめMapを使う - // ただメモリ的には↓の方が省メモリかもしれないので検討中 - const self = (el as any)._userPreviewDirective_ = {} as any; - - self.preview = new UserPreview(el, binding.value); - }, - - unmounted(el, binding, vn) { - if (binding.value == null) return; - - const self = el._userPreviewDirective_; - self.preview.detach(); - }, -} as Directive;