fix(client): better hover detection
This commit is contained in:
parent
bcf7530eef
commit
e804a299e0
4 changed files with 41 additions and 32 deletions
|
@ -19,10 +19,6 @@
|
|||
:reaction="notification.reaction ? notification.reaction.replace(/^:(\w+):$/, ':$1@.:') : notification.reaction"
|
||||
:custom-emojis="notification.note.emojis"
|
||||
:no-style="true"
|
||||
@touchstart.passive="onReactionMouseover"
|
||||
@mouseover="onReactionMouseover"
|
||||
@mouseleave="onReactionMouseleave"
|
||||
@touchend="onReactionMouseleave"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -151,7 +147,7 @@ export default defineComponent({
|
|||
os.api('users/groups/invitations/reject', { invitationId: props.notification.invitation.id });
|
||||
};
|
||||
|
||||
const { onMouseover: onReactionMouseover, onMouseleave: onReactionMouseleave } = useTooltip((showing) => {
|
||||
useTooltip(reactionRef, (showing) => {
|
||||
os.popup(XReactionTooltip, {
|
||||
showing,
|
||||
reaction: props.notification.reaction ? props.notification.reaction.replace(/^:(\w+):$/, ':$1@.:') : props.notification.reaction,
|
||||
|
@ -170,8 +166,6 @@ export default defineComponent({
|
|||
rejectFollowRequest,
|
||||
acceptGroupInvitation,
|
||||
rejectGroupInvitation,
|
||||
onReactionMouseover,
|
||||
onReactionMouseleave,
|
||||
elRef,
|
||||
reactionRef,
|
||||
};
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
class="hkzvhatu _button"
|
||||
:class="{ reacted: note.myReaction == reaction, canToggle }"
|
||||
@click="toggleReaction()"
|
||||
@touchstart.passive="onMouseover"
|
||||
@mouseover="onMouseover"
|
||||
@mouseleave="onMouseleave"
|
||||
@touchend="onMouseleave"
|
||||
>
|
||||
<XReactionIcon :reaction="reaction" :custom-emojis="note.emojis"/>
|
||||
<span>{{ count }}</span>
|
||||
|
@ -90,7 +86,7 @@ export default defineComponent({
|
|||
if (!props.isInitial) anime();
|
||||
});
|
||||
|
||||
const { onMouseover, onMouseleave } = useTooltip(async (showing) => {
|
||||
useTooltip(buttonRef, async (showing) => {
|
||||
const reactions = await os.api('notes/reactions', {
|
||||
noteId: props.note.id,
|
||||
type: props.reaction,
|
||||
|
@ -113,8 +109,6 @@ export default defineComponent({
|
|||
buttonRef,
|
||||
canToggle,
|
||||
toggleReaction,
|
||||
onMouseover,
|
||||
onMouseleave,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
ref="buttonRef"
|
||||
class="eddddedb _button canRenote"
|
||||
@click="renote()"
|
||||
@touchstart.passive="onMouseover"
|
||||
@mouseover="onMouseover"
|
||||
@mouseleave="onMouseleave"
|
||||
@touchend="onMouseleave"
|
||||
>
|
||||
<i class="fas fa-retweet"></i>
|
||||
<p v-if="count > 0" class="count">{{ count }}</p>
|
||||
|
@ -42,7 +38,7 @@ export default defineComponent({
|
|||
|
||||
const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
|
||||
|
||||
const { onMouseover, onMouseleave } = useTooltip(async (showing) => {
|
||||
useTooltip(buttonRef, async (showing) => {
|
||||
const renotes = await os.api('notes/renotes', {
|
||||
noteId: props.note.id,
|
||||
limit: 11
|
||||
|
@ -87,8 +83,6 @@ export default defineComponent({
|
|||
buttonRef,
|
||||
canRenote,
|
||||
renote,
|
||||
onMouseover,
|
||||
onMouseleave,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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<boolean>) => void) {
|
||||
export function useTooltip(
|
||||
elRef: Ref<HTMLElement | { $el: HTMLElement } | null | undefined>,
|
||||
onShow: (showing: Ref<boolean>) => 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<boolean>) => 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<boolean>) => 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<boolean>) => 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',
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue