feat(qr): improve mobile UX for scanner and generator

This commit is contained in:
2026-03-04 05:21:53 +00:00
parent a367d364df
commit 4711102407
3 changed files with 114 additions and 46 deletions

View File

@@ -3,11 +3,60 @@ import { showTooltip, hideTooltip, tooltipState } from '../composables/useToolti
export const tooltipDirective = {
mounted(el, binding) {
el._tooltipText = binding.value;
let touchTimeout = null;
let isTouch = false;
el.addEventListener('mouseenter', () => showTooltip(el, el._tooltipText));
el.addEventListener('mouseleave', hideTooltip);
el.addEventListener('focus', () => showTooltip(el, el._tooltipText));
el.addEventListener('blur', hideTooltip);
el._handleMouseEnter = () => {
if (!isTouch) showTooltip(el, el._tooltipText);
};
el._handleMouseLeave = () => {
if (!isTouch) hideTooltip();
};
el._handleFocus = () => {
if (!isTouch) showTooltip(el, el._tooltipText);
};
el._handleBlur = () => {
if (!isTouch) hideTooltip();
};
el._handleTouchStart = () => {
isTouch = true;
if (touchTimeout) clearTimeout(touchTimeout);
touchTimeout = setTimeout(() => {
showTooltip(el, el._tooltipText);
}, 400); // 400ms long press threshold
};
el._handleTouchEnd = () => {
if (touchTimeout) clearTimeout(touchTimeout);
hideTooltip();
// Block ensuing simulated mouseenter events
setTimeout(() => { isTouch = false; }, 500);
};
el._handleTouchCancel = () => {
if (touchTimeout) clearTimeout(touchTimeout);
hideTooltip();
setTimeout(() => { isTouch = false; }, 500);
};
el._handleContextMenu = (e) => {
// Prevent the OS context menu if we're showing a tooltip via long press
if (isTouch && tooltipState.isVisible && tooltipState.text === el._tooltipText) {
e.preventDefault();
}
};
el.addEventListener('mouseenter', el._handleMouseEnter);
el.addEventListener('mouseleave', el._handleMouseLeave);
el.addEventListener('focus', el._handleFocus);
el.addEventListener('blur', el._handleBlur);
el.addEventListener('touchstart', el._handleTouchStart, { passive: true });
el.addEventListener('touchend', el._handleTouchEnd);
el.addEventListener('touchmove', el._handleTouchCancel, { passive: true });
el.addEventListener('touchcancel', el._handleTouchCancel);
el.addEventListener('contextmenu', el._handleContextMenu);
},
updated(el, binding) {
el._tooltipText = binding.value;
@@ -19,10 +68,17 @@ export const tooltipDirective = {
}
},
unmounted(el) {
el.removeEventListener('mouseenter', () => showTooltip(el, el._tooltipText));
el.removeEventListener('mouseleave', hideTooltip);
el.removeEventListener('focus', () => showTooltip(el, el._tooltipText));
el.removeEventListener('blur', hideTooltip);
if (el._handleMouseEnter) {
el.removeEventListener('mouseenter', el._handleMouseEnter);
el.removeEventListener('mouseleave', el._handleMouseLeave);
el.removeEventListener('focus', el._handleFocus);
el.removeEventListener('blur', el._handleBlur);
el.removeEventListener('touchstart', el._handleTouchStart);
el.removeEventListener('touchend', el._handleTouchEnd);
el.removeEventListener('touchmove', el._handleTouchCancel);
el.removeEventListener('touchcancel', el._handleTouchCancel);
el.removeEventListener('contextmenu', el._handleContextMenu);
}
hideTooltip();
}
};