From e804a299e068afcbfb2fe36cc429b9a502a7b2d9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 5 Dec 2021 20:01:52 +0900 Subject: fix(client): better hover detection --- packages/client/src/scripts/use-tooltip.ts | 49 +++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 11 deletions(-) (limited to 'packages/client/src/scripts/use-tooltip.ts') diff --git a/packages/client/src/scripts/use-tooltip.ts b/packages/client/src/scripts/use-tooltip.ts index f72dcb162d..0df4baca7b 100644 --- a/packages/client/src/scripts/use-tooltip.ts +++ b/packages/client/src/scripts/use-tooltip.ts @@ -1,8 +1,16 @@ -import { Ref, ref } from 'vue'; -import { isScreenTouching, isTouchUsing } from './touch'; +import { Ref, ref, watch } from 'vue'; -export function useTooltip(onShow: (showing: Ref) => void) { +export function useTooltip( + elRef: Ref, + onShow: (showing: Ref) => void, +): void { let isHovering = false; + + // iOS(Androidも?)では、要素をタップした直後に(おせっかいで)mouseoverイベントを発火させたりするため、それを無視するためのフラグ + // 無視しないと、画面に触れてないのにツールチップが出たりし、ユーザビリティが損なわれる + // TODO: 一度でもタップすると二度とマウスでツールチップ出せなくなるのをどうにかする 定期的にfalseに戻すとか...? + let shouldIgnoreMouseover = false; + let timeoutId: number; let changeShowingState: (() => void) | null; @@ -11,11 +19,6 @@ export function useTooltip(onShow: (showing: Ref) => void) { close(); if (!isHovering) return; - // iOS(Androidも?)では、要素をタップした直後に(おせっかいで)mouseoverイベントを発火させたりするため、その対策 - // これが無いと、画面に触れてないのにツールチップが出たりしてしまう - // TODO: タッチとマウス両方使っている環境では、マウス操作でツールチップ出せなくなるのをどうにかする - if (isTouchUsing && !isScreenTouching) return; - const showing = ref(true); onShow(showing); changeShowingState = () => { @@ -32,6 +35,7 @@ export function useTooltip(onShow: (showing: Ref) => void) { const onMouseover = () => { if (isHovering) return; + if (shouldIgnoreMouseover) return; isHovering = true; timeoutId = window.setTimeout(open, 300); }; @@ -43,8 +47,31 @@ export function useTooltip(onShow: (showing: Ref) => void) { close(); }; - return { - onMouseover, - onMouseleave, + const onTouchstart = () => { + shouldIgnoreMouseover = true; + if (isHovering) return; + isHovering = true; + timeoutId = window.setTimeout(open, 300); + }; + + 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 }); + } + }, { + immediate: true, + flush: 'post', + }); } -- cgit v1.2.3-freya