refactor: カスタムディレクティブの型付け (taiyme#298)
This commit is contained in:
parent
15ae1605ec
commit
1106af8d2d
|
@ -3,11 +3,11 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Directive } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
import { getBgColor } from '@/scripts/get-bg-color.js';
|
import { getBgColor } from '@/scripts/get-bg-color.js';
|
||||||
|
|
||||||
export default {
|
export const vAdaptiveBg: ObjectDirective<HTMLElement, null | undefined> = {
|
||||||
mounted(src, binding, vn) {
|
mounted(src) {
|
||||||
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 +18,4 @@ export default {
|
||||||
src.style.backgroundColor = myBg;
|
src.style.backgroundColor = myBg;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
} as Directive;
|
};
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Directive } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
import { getBgColor } from '@/scripts/get-bg-color.js';
|
import { getBgColor } from '@/scripts/get-bg-color.js';
|
||||||
|
|
||||||
export default {
|
export const vAdaptiveBorder: ObjectDirective<HTMLElement, null | undefined> = {
|
||||||
mounted(src, binding, vn) {
|
mounted(src) {
|
||||||
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 +18,4 @@ export default {
|
||||||
src.style.borderColor = myBg;
|
src.style.borderColor = myBg;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
} as Directive;
|
};
|
||||||
|
|
|
@ -3,21 +3,23 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Directive } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
|
|
||||||
export default {
|
export const vAnim: ObjectDirective<HTMLElement, number | null | undefined> = {
|
||||||
beforeMount(src, binding, vn) {
|
beforeMount(src) {
|
||||||
src.style.opacity = '0';
|
src.style.opacity = '0';
|
||||||
src.style.transform = 'scale(0.9)';
|
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');
|
src.classList.add('_zoom');
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted(src, binding, vn) {
|
mounted(src) {
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
src.style.opacity = '1';
|
src.style.opacity = '1';
|
||||||
src.style.transform = 'none';
|
src.style.transform = 'none';
|
||||||
}, 1);
|
}, 1);
|
||||||
},
|
},
|
||||||
} as Directive;
|
};
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Directive } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
|
|
||||||
export default {
|
export const vAppear: ObjectDirective<HTMLElement, (() => unknown) | null | undefined> = {
|
||||||
mounted(src, binding, vn) {
|
mounted(src, binding) {
|
||||||
const fn = binding.value;
|
const fn = binding.value;
|
||||||
if (fn == null) return;
|
if (fn == null) return;
|
||||||
|
|
||||||
const observer = new IntersectionObserver(entries => {
|
const observer = new IntersectionObserver((entries) => {
|
||||||
if (entries.some(entry => entry.isIntersecting)) {
|
if (entries.some((entry) => entry.isIntersecting)) {
|
||||||
fn();
|
fn();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -21,7 +21,7 @@ export default {
|
||||||
src._observer_ = observer;
|
src._observer_ = observer;
|
||||||
},
|
},
|
||||||
|
|
||||||
unmounted(src, binding, vn) {
|
unmounted(src) {
|
||||||
if (src._observer_) src._observer_.disconnect();
|
src._observer_?.disconnect();
|
||||||
},
|
},
|
||||||
} as Directive;
|
};
|
||||||
|
|
|
@ -3,20 +3,20 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Directive } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
export default {
|
export const vClickAnime: ObjectDirective<HTMLElement, null | undefined> = {
|
||||||
mounted(el: HTMLElement, binding, vn) {
|
mounted(src) {
|
||||||
if (!defaultStore.state.animation) return;
|
if (!defaultStore.state.animation) return;
|
||||||
|
|
||||||
const target = el.children[0];
|
const target = src.children[0];
|
||||||
|
|
||||||
if (target == null) return;
|
if (target == null) return;
|
||||||
|
|
||||||
target.classList.add('_anime_bounce_standBy');
|
target.classList.add('_anime_bounce_standBy');
|
||||||
|
|
||||||
el.addEventListener('mousedown', () => {
|
src.addEventListener('mousedown', () => {
|
||||||
target.classList.remove('_anime_bounce');
|
target.classList.remove('_anime_bounce');
|
||||||
|
|
||||||
target.classList.add('_anime_bounce_standBy');
|
target.classList.add('_anime_bounce_standBy');
|
||||||
|
@ -27,14 +27,14 @@ export default {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('click', () => {
|
src.addEventListener('click', () => {
|
||||||
target.classList.add('_anime_bounce');
|
target.classList.add('_anime_bounce');
|
||||||
target.classList.remove('_anime_bounce_ready');
|
target.classList.remove('_anime_bounce_ready');
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('animationend', () => {
|
src.addEventListener('animationend', () => {
|
||||||
target.classList.remove('_anime_bounce');
|
target.classList.remove('_anime_bounce');
|
||||||
target.classList.add('_anime_bounce_standBy');
|
target.classList.add('_anime_bounce_standBy');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
} as Directive;
|
};
|
||||||
|
|
|
@ -3,15 +3,36 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Directive } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
|
|
||||||
const mountings = new Map<Element, {
|
const mountings = new Map<HTMLElement, {
|
||||||
resize: ResizeObserver;
|
resize: ResizeObserver;
|
||||||
intersection?: IntersectionObserver;
|
intersection?: IntersectionObserver;
|
||||||
fn: (w: number, h: number) => void;
|
fn: (w: number, h: number) => void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function calc(src: Element) {
|
export const vGetSize: ObjectDirective<HTMLElement, ((w: number, h: number) => 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) {
|
||||||
|
if (binding.value != null) 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 info = mountings.get(src);
|
||||||
const height = src.clientHeight;
|
const height = src.clientHeight;
|
||||||
const width = src.clientWidth;
|
const width = src.clientWidth;
|
||||||
|
@ -22,8 +43,8 @@ function calc(src: Element) {
|
||||||
if (!height) {
|
if (!height) {
|
||||||
// IntersectionObserverで表示検出する
|
// IntersectionObserverで表示検出する
|
||||||
if (!info.intersection) {
|
if (!info.intersection) {
|
||||||
info.intersection = new IntersectionObserver(entries => {
|
info.intersection = new IntersectionObserver((entries) => {
|
||||||
if (entries.some(entry => entry.isIntersecting)) calc(src);
|
if (entries.some((entry) => entry.isIntersecting)) calc(src);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
info.intersection.observe(src);
|
info.intersection.observe(src);
|
||||||
|
@ -36,24 +57,3 @@ function calc(src: Element) {
|
||||||
|
|
||||||
info.fn(width, height);
|
info.fn(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
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<Element, (w: number, h: number) => void>;
|
|
||||||
|
|
|
@ -3,27 +3,28 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* 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';
|
import { makeHotkey } from '@/scripts/hotkey.js';
|
||||||
|
|
||||||
export default {
|
export const vHotkey: ObjectDirective<HTMLElement, Keymap | null | undefined, 'global'> = {
|
||||||
mounted(el, binding) {
|
mounted(src, binding) {
|
||||||
el._hotkey_global = binding.modifiers.global === true;
|
src._hotkey_global = binding.modifiers.global === true;
|
||||||
|
|
||||||
el._keyHandler = makeHotkey(binding.value);
|
src._keyHandler = makeHotkey(binding.value);
|
||||||
|
|
||||||
if (el._hotkey_global) {
|
if (src._hotkey_global) {
|
||||||
document.addEventListener('keydown', el._keyHandler, { passive: false });
|
document.addEventListener('keydown', src._keyHandler, { passive: false });
|
||||||
} else {
|
} else {
|
||||||
el.addEventListener('keydown', el._keyHandler, { passive: false });
|
src.addEventListener('keydown', src._keyHandler, { passive: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
unmounted(el) {
|
unmounted(src) {
|
||||||
if (el._hotkey_global) {
|
if (src._hotkey_global) {
|
||||||
document.removeEventListener('keydown', el._keyHandler);
|
document.removeEventListener('keydown', src._keyHandler);
|
||||||
} else {
|
} else {
|
||||||
el.removeEventListener('keydown', el._keyHandler);
|
src.removeEventListener('keydown', src._keyHandler);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
} as Directive;
|
};
|
||||||
|
|
|
@ -3,19 +3,19 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { App } from 'vue';
|
import type { App } from 'vue';
|
||||||
|
|
||||||
import userPreview from './user-preview.js';
|
import { vAdaptiveBg } from '@/directives/adaptive-bg.js';
|
||||||
import getSize from './get-size.js';
|
import { vAdaptiveBorder } from '@/directives/adaptive-border.js';
|
||||||
import ripple from './ripple.js';
|
import { vAnim } from '@/directives/anim.js';
|
||||||
import tooltip from './tooltip.js';
|
import { vAppear } from '@/directives/appear.js';
|
||||||
import hotkey from './hotkey.js';
|
import { vClickAnime } from '@/directives/click-anime.js';
|
||||||
import appear from './appear.js';
|
import { vGetSize } from '@/directives/get-size.js';
|
||||||
import anim from './anim.js';
|
import { vHotkey } from '@/directives/hotkey.js';
|
||||||
import clickAnime from './click-anime.js';
|
import { vPanel } from '@/directives/panel.js';
|
||||||
import panel from './panel.js';
|
import { vRipple } from '@/directives/ripple.js';
|
||||||
import adaptiveBorder from './adaptive-border.js';
|
import { vTooltip } from '@/directives/tooltip.js';
|
||||||
import adaptiveBg from './adaptive-bg.js';
|
import { vUserPreview } from '@/directives/user-preview.js';
|
||||||
|
|
||||||
export default function(app: App) {
|
export default function(app: App) {
|
||||||
for (const [key, value] of Object.entries(directives)) {
|
for (const [key, value] of Object.entries(directives)) {
|
||||||
|
@ -24,16 +24,31 @@ export default function(app: App) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const directives = {
|
export const directives = {
|
||||||
'userPreview': userPreview,
|
'adaptive-bg': vAdaptiveBg,
|
||||||
'user-preview': userPreview,
|
'adaptive-border': vAdaptiveBorder,
|
||||||
'get-size': getSize,
|
'anim': vAnim,
|
||||||
'ripple': ripple,
|
'appear': vAppear,
|
||||||
'tooltip': tooltip,
|
'click-anime': vClickAnime,
|
||||||
'hotkey': hotkey,
|
'get-size': vGetSize,
|
||||||
'appear': appear,
|
'hotkey': vHotkey,
|
||||||
'anim': anim,
|
'panel': vPanel,
|
||||||
'click-anime': clickAnime,
|
'ripple': vRipple,
|
||||||
'panel': panel,
|
'tooltip': vTooltip,
|
||||||
'adaptive-border': adaptiveBorder,
|
'user-preview': vUserPreview,
|
||||||
'adaptive-bg': adaptiveBg,
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Directive } from 'vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
import { getBgColor } from '@/scripts/get-bg-color.js';
|
import { getBgColor } from '@/scripts/get-bg-color.js';
|
||||||
|
|
||||||
export default {
|
export const vPanel: ObjectDirective<HTMLElement, null | undefined> = {
|
||||||
mounted(src, binding, vn) {
|
mounted(src) {
|
||||||
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 +18,4 @@ export default {
|
||||||
src.style.backgroundColor = 'var(--MI_THEME-panel)';
|
src.style.backgroundColor = 'var(--MI_THEME-panel)';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
} as Directive;
|
};
|
||||||
|
|
|
@ -3,19 +3,20 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
import type { ObjectDirective } from 'vue';
|
||||||
import { popup } from '@/os.js';
|
import { popup } from '@/os.js';
|
||||||
|
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
|
|
||||||
export default {
|
export const vRipple: ObjectDirective<HTMLElement, boolean | null | undefined> = {
|
||||||
mounted(el, binding, vn) {
|
mounted(src, binding) {
|
||||||
// 明示的に false であればバインドしない
|
// 明示的に false であればバインドしない
|
||||||
if (binding.value === false) return;
|
if (binding.value === false) return;
|
||||||
|
|
||||||
el.addEventListener('click', () => {
|
src.addEventListener('click', () => {
|
||||||
const rect = el.getBoundingClientRect();
|
const rect = src.getBoundingClientRect();
|
||||||
|
|
||||||
const x = rect.left + (el.offsetWidth / 2);
|
const x = rect.left + (src.offsetWidth / 2);
|
||||||
const y = rect.top + (el.offsetHeight / 2);
|
const y = rect.top + (src.offsetHeight / 2);
|
||||||
|
|
||||||
const { dispose } = popup(MkRippleEffect, { x, y }, {
|
const { dispose } = popup(MkRippleEffect, { x, y }, {
|
||||||
end: () => dispose(),
|
end: () => dispose(),
|
||||||
|
|
|
@ -6,18 +6,19 @@
|
||||||
// TODO: useTooltip関数使うようにしたい
|
// TODO: useTooltip関数使うようにしたい
|
||||||
// ただディレクティブ内でonUnmountedなどのcomposition api使えるのか不明
|
// ただディレクティブ内で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 { isTouchUsing } from '@/scripts/touch.js';
|
||||||
import { popup, alert } from '@/os.js';
|
import { popup, alert } from '@/os.js';
|
||||||
|
|
||||||
const start = isTouchUsing ? 'touchstart' : 'mouseenter';
|
const start = isTouchUsing ? 'touchstart' : 'mouseenter';
|
||||||
const end = isTouchUsing ? 'touchend' : 'mouseleave';
|
const end = isTouchUsing ? 'touchend' : 'mouseleave';
|
||||||
|
|
||||||
export default {
|
export const vTooltip: ObjectDirective<HTMLElement, string | null | undefined, 'noDelay' | 'mfm' | 'top' | 'right' | 'bottom' | 'left', 'dialog'> = {
|
||||||
mounted(el: HTMLElement, binding, vn) {
|
mounted(src, binding) {
|
||||||
const delay = binding.modifiers.noDelay ? 0 : 100;
|
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.text = binding.value as string;
|
||||||
self._close = null;
|
self._close = null;
|
||||||
|
@ -34,7 +35,7 @@ export default {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (binding.arg === 'dialog') {
|
if (binding.arg === 'dialog') {
|
||||||
el.addEventListener('click', (ev) => {
|
src.addEventListener('click', (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
alert({
|
alert({
|
||||||
|
@ -46,7 +47,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.show = () => {
|
self.show = () => {
|
||||||
if (!document.body.contains(el)) return;
|
if (!document.body.contains(src)) return;
|
||||||
if (self._close) return;
|
if (self._close) return;
|
||||||
if (self.text == null) return;
|
if (self.text == null) return;
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ export default {
|
||||||
text: self.text,
|
text: self.text,
|
||||||
asMfm: binding.modifiers.mfm,
|
asMfm: binding.modifiers.mfm,
|
||||||
direction: binding.modifiers.left ? 'left' : binding.modifiers.right ? 'right' : binding.modifiers.top ? 'top' : binding.modifiers.bottom ? 'bottom' : 'top',
|
direction: binding.modifiers.left ? 'left' : binding.modifiers.right ? 'right' : binding.modifiers.top ? 'top' : binding.modifiers.bottom ? 'bottom' : 'top',
|
||||||
targetElement: el,
|
targetElement: src,
|
||||||
}, {
|
}, {
|
||||||
closed: () => dispose(),
|
closed: () => dispose(),
|
||||||
});
|
});
|
||||||
|
@ -66,11 +67,11 @@ export default {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
el.addEventListener('selectstart', ev => {
|
src.addEventListener('selectstart', (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener(start, (ev) => {
|
src.addEventListener(start, () => {
|
||||||
window.clearTimeout(self.showTimer);
|
window.clearTimeout(self.showTimer);
|
||||||
window.clearTimeout(self.hideTimer);
|
window.clearTimeout(self.hideTimer);
|
||||||
if (delay === 0) {
|
if (delay === 0) {
|
||||||
|
@ -80,7 +81,7 @@ export default {
|
||||||
}
|
}
|
||||||
}, { passive: true });
|
}, { passive: true });
|
||||||
|
|
||||||
el.addEventListener(end, () => {
|
src.addEventListener(end, () => {
|
||||||
window.clearTimeout(self.showTimer);
|
window.clearTimeout(self.showTimer);
|
||||||
window.clearTimeout(self.hideTimer);
|
window.clearTimeout(self.hideTimer);
|
||||||
if (delay === 0) {
|
if (delay === 0) {
|
||||||
|
@ -90,19 +91,19 @@ export default {
|
||||||
}
|
}
|
||||||
}, { passive: true });
|
}, { passive: true });
|
||||||
|
|
||||||
el.addEventListener('click', () => {
|
src.addEventListener('click', () => {
|
||||||
window.clearTimeout(self.showTimer);
|
window.clearTimeout(self.showTimer);
|
||||||
self.close();
|
self.close();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
updated(el, binding) {
|
updated(src, binding) {
|
||||||
const self = el._tooltipDirective_;
|
const self = (src as any)._tooltipDirective_;
|
||||||
self.text = binding.value as string;
|
self.text = binding.value as string;
|
||||||
},
|
},
|
||||||
|
|
||||||
unmounted(el, binding, vn) {
|
unmounted(src) {
|
||||||
const self = el._tooltipDirective_;
|
const self = (src as any)._tooltipDirective_;
|
||||||
window.clearInterval(self.checkTimer);
|
window.clearInterval(self.checkTimer);
|
||||||
},
|
},
|
||||||
} as Directive;
|
};
|
||||||
|
|
|
@ -3,10 +3,30 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* 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';
|
import { popup } from '@/os.js';
|
||||||
|
|
||||||
export class UserPreview {
|
export const vUserPreview: ObjectDirective<HTMLElement, string | null | undefined> = {
|
||||||
|
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 el;
|
||||||
private user;
|
private user;
|
||||||
private showTimer;
|
private showTimer;
|
||||||
|
@ -102,22 +122,3 @@ export class UserPreview {
|
||||||
this.el.removeEventListener('click', this.onClick);
|
this.el.removeEventListener('click', this.onClick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
Loading…
Reference in a new issue