From 851d79029a56c68f663eab7e3252ff00a409f666 Mon Sep 17 00:00:00 2001 From: spinline Date: Sat, 21 Feb 2026 01:46:21 +0300 Subject: [PATCH] feat(ui): add mobile long-press support for context menus --- frontend/src/components/ui/context_menu.rs | 49 +++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/ui/context_menu.rs b/frontend/src/components/ui/context_menu.rs index 4faf6c5..0ef5c22 100644 --- a/frontend/src/components/ui/context_menu.rs +++ b/frontend/src/components/ui/context_menu.rs @@ -296,7 +296,7 @@ pub fn ContextMenuContent( }} }}; - // Right-click on trigger + // Right-click on trigger (desktop) trigger.addEventListener('contextmenu', (e) => {{ e.preventDefault(); e.stopPropagation(); @@ -307,6 +307,53 @@ pub fn ContextMenuContent( openMenu(e.clientX, e.clientY); }}); + // Long-press on trigger (mobile) + let touchTimer = null; + let touchStartX = 0; + let touchStartY = 0; + const LONG_PRESS_DURATION = 500; + const MOVE_THRESHOLD = 10; + + trigger.addEventListener('touchstart', (e) => {{ + const touch = e.touches[0]; + touchStartX = touch.clientX; + touchStartY = touch.clientY; + + touchTimer = setTimeout(() => {{ + e.preventDefault(); + if (isOpen) {{ + closeMenu(); + }} + openMenu(touchStartX, touchStartY); + }}, LONG_PRESS_DURATION); + }}, {{ passive: false }}); + + trigger.addEventListener('touchmove', (e) => {{ + if (touchTimer) {{ + const touch = e.touches[0]; + const dx = Math.abs(touch.clientX - touchStartX); + const dy = Math.abs(touch.clientY - touchStartY); + if (dx > MOVE_THRESHOLD || dy > MOVE_THRESHOLD) {{ + clearTimeout(touchTimer); + touchTimer = null; + }} + }} + }}); + + trigger.addEventListener('touchend', () => {{ + if (touchTimer) {{ + clearTimeout(touchTimer); + touchTimer = null; + }} + }}); + + trigger.addEventListener('touchcancel', () => {{ + if (touchTimer) {{ + clearTimeout(touchTimer); + touchTimer = null; + }} + }}); + // Close when action is clicked const actions = menu.querySelectorAll('[data-context-close]'); actions.forEach(action => {{