Skip to content

Commit

Permalink
refactor: カスタムディレクティブの型付け (taiyme#298)
Browse files Browse the repository at this point in the history
  • Loading branch information
taiyme authored and kakkokari-gtyih committed Oct 24, 2024
1 parent 15ae160 commit 1106af8
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 142 deletions.
8 changes: 4 additions & 4 deletions packages/frontend/src/directives/adaptive-bg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { Directive } from 'vue';
import type { ObjectDirective } from 'vue';
import { getBgColor } from '@/scripts/get-bg-color.js';

export default {
mounted(src, binding, vn) {
export const vAdaptiveBg: ObjectDirective<HTMLElement, null | undefined> = {
mounted(src) {
const parentBg = getBgColor(src.parentElement) ?? 'transparent';

const myBg = window.getComputedStyle(src).backgroundColor;
Expand All @@ -18,4 +18,4 @@ export default {
src.style.backgroundColor = myBg;
}
},
} as Directive;
};
8 changes: 4 additions & 4 deletions packages/frontend/src/directives/adaptive-border.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { Directive } from 'vue';
import type { ObjectDirective } from 'vue';
import { getBgColor } from '@/scripts/get-bg-color.js';

export default {
mounted(src, binding, vn) {
export const vAdaptiveBorder: ObjectDirective<HTMLElement, null | undefined> = {
mounted(src) {
const parentBg = getBgColor(src.parentElement) ?? 'transparent';

const myBg = window.getComputedStyle(src).backgroundColor;
Expand All @@ -18,4 +18,4 @@ export default {
src.style.borderColor = myBg;
}
},
} as Directive;
};
14 changes: 8 additions & 6 deletions packages/frontend/src/directives/anim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { Directive } from 'vue';
import type { ObjectDirective } from 'vue';

export default {
beforeMount(src, binding, vn) {
export const vAnim: ObjectDirective<HTMLElement, number | null | undefined> = {
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;
};
16 changes: 8 additions & 8 deletions packages/frontend/src/directives/appear.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { Directive } from 'vue';
import type { ObjectDirective } from 'vue';

export default {
mounted(src, binding, vn) {
export const vAppear: ObjectDirective<HTMLElement, (() => 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();
}
});
Expand All @@ -21,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;
};
16 changes: 8 additions & 8 deletions packages/frontend/src/directives/click-anime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { Directive } from 'vue';
import type { ObjectDirective } from 'vue';
import { defaultStore } from '@/store.js';

export default {
mounted(el: HTMLElement, binding, vn) {
export const vClickAnime: ObjectDirective<HTMLElement, null | undefined> = {
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');
Expand All @@ -27,14 +27,14 @@ export default {
});
});

el.addEventListener('click', () => {
src.addEventListener('click', () => {
target.classList.add('_anime_bounce');
target.classList.remove('_anime_bounce_ready');
});

el.addEventListener('animationend', () => {
src.addEventListener('animationend', () => {
target.classList.remove('_anime_bounce');
target.classList.add('_anime_bounce_standBy');
});
},
} as Directive;
};
52 changes: 26 additions & 26 deletions packages/frontend/src/directives/get-size.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,36 @@
* 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;
intersection?: IntersectionObserver;
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 height = src.clientHeight;
const width = src.clientWidth;
Expand All @@ -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);
Expand All @@ -36,24 +57,3 @@ function calc(src: Element) {

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>;
27 changes: 14 additions & 13 deletions packages/frontend/src/directives/hotkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +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';

export default {
mounted(el, binding) {
el._hotkey_global = binding.modifiers.global === true;
export const vHotkey: ObjectDirective<HTMLElement, Keymap | null | undefined, 'global'> = {
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;
};
65 changes: 40 additions & 25 deletions packages/frontend/src/directives/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

export default function(app: App) {
for (const [key, value] of Object.entries(directives)) {
Expand All @@ -24,16 +24,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;
}
}
8 changes: 4 additions & 4 deletions packages/frontend/src/directives/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { Directive } from 'vue';
import type { ObjectDirective } from 'vue';
import { getBgColor } from '@/scripts/get-bg-color.js';

export default {
mounted(src, binding, vn) {
export const vPanel: ObjectDirective<HTMLElement, null | undefined> = {
mounted(src) {
const parentBg = getBgColor(src.parentElement) ?? 'transparent';

const myBg = getComputedStyle(document.documentElement).getPropertyValue('--MI_THEME-panel');
Expand All @@ -18,4 +18,4 @@ export default {
src.style.backgroundColor = 'var(--MI_THEME-panel)';
}
},
} as Directive;
};
15 changes: 8 additions & 7 deletions packages/frontend/src/directives/ripple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +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';

export default {
mounted(el, binding, vn) {
export const vRipple: ObjectDirective<HTMLElement, boolean | null | undefined> = {
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(),
Expand Down
Loading

0 comments on commit 1106af8

Please sign in to comment.