1
0
mirror of https://github.com/MisskeyIO/misskey synced 2024-12-23 02:58:18 +09:00
MisskeyIO/packages/frontend/src/scripts/use-tooltip.ts
Shun Sakai c2370a1be6
chore: 著作権とライセンスについての情報を各ファイルに追加する (#11348)
* chore: Add the SPDX information to each file

Add copyright and licensing information as defined in version 3.0 of
the REUSE Specification.

* tweak format

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2023-07-27 14:31:52 +09:00

92 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Ref, ref, watch, onUnmounted } from 'vue';
export function useTooltip(
elRef: Ref<HTMLElement | { $el: HTMLElement } | null | undefined>,
onShow: (showing: Ref<boolean>) => void,
delay = 300,
): void {
let isHovering = false;
// iOS(Androidも)では、要素をタップした直後に(おせっかいで)mouseoverイベントを発火させたりするため、それを無視するためのフラグ
// 無視しないと、画面に触れてないのにツールチップが出たりし、ユーザビリティが損なわれる
// TODO: 一度でもタップすると二度とマウスでツールチップ出せなくなるのをどうにかする 定期的にfalseに戻すとか...
let shouldIgnoreMouseover = false;
let timeoutId: number;
let changeShowingState: (() => void) | null;
const open = () => {
close();
if (!isHovering) return;
if (elRef.value == null) return;
const el = elRef.value instanceof Element ? elRef.value : elRef.value.$el;
if (!document.body.contains(el)) return; // openしようとしたときに既に元要素がDOMから消えている場合があるため
const showing = ref(true);
onShow(showing);
changeShowingState = () => {
showing.value = false;
};
};
const close = () => {
if (changeShowingState != null) {
changeShowingState();
changeShowingState = null;
}
};
const onMouseover = () => {
if (isHovering) return;
if (shouldIgnoreMouseover) return;
isHovering = true;
timeoutId = window.setTimeout(open, delay);
};
const onMouseleave = () => {
if (!isHovering) return;
isHovering = false;
window.clearTimeout(timeoutId);
close();
};
const onTouchstart = () => {
shouldIgnoreMouseover = true;
if (isHovering) return;
isHovering = true;
timeoutId = window.setTimeout(open, delay);
};
const onTouchend = () => {
if (!isHovering) return;
isHovering = false;
window.clearTimeout(timeoutId);
close();
};
const stop = watch(elRef, () => {
if (elRef.value) {
stop();
const el = elRef.value instanceof Element ? elRef.value : elRef.value.$el;
el.addEventListener('mouseover', onMouseover, { passive: true });
el.addEventListener('mouseleave', onMouseleave, { passive: true });
el.addEventListener('touchstart', onTouchstart, { passive: true });
el.addEventListener('touchend', onTouchend, { passive: true });
el.addEventListener('click', close, { passive: true });
}
}, {
immediate: true,
flush: 'post',
});
onUnmounted(() => {
close();
});
}