From 38b6e9fca62b0cac5a0f239094ce0a65e6e6de9b Mon Sep 17 00:00:00 2001 From: taDuc Date: Thu, 4 Jun 2026 20:12:04 +0700 Subject: [PATCH] fix: improve mobile touch interactions by disabling default browser gestures and updating sidebar resizing logic --- .../editor/ReplayPreviewLayerPanel.tsx | 6 +- .../preview/PublicPreviewClientPage.tsx | 23 +++-- src/uhm/components/ui/TimelineBar.tsx | 44 ++++++++-- src/uhm/components/wiki/PublicWikiSidebar.tsx | 84 +++++++++---------- 4 files changed, 99 insertions(+), 58 deletions(-) diff --git a/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx b/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx index 3c34065..05f27ca 100644 --- a/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx +++ b/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx @@ -219,11 +219,13 @@ export default function ReplayPreviewLayerPanel({ borderRadius: 20, padding: "14px 10px", width: 58, + boxSizing: "border-box", alignItems: "center", boxShadow: "0 20px 48px rgba(2, 6, 23, 0.45)", backdropFilter: "blur(12px)", maxHeight: "100%", - overflow: "hidden", + overflowX: "hidden", + overflowY: "hidden", }} > {renderStyles()} @@ -233,7 +235,9 @@ export default function ReplayPreviewLayerPanel({ style={{ flexGrow: 1, overflowY: "auto", + overflowX: "hidden", width: "100%", + boxSizing: "border-box", display: "flex", flexDirection: "column", alignItems: "center", diff --git a/src/uhm/components/preview/PublicPreviewClientPage.tsx b/src/uhm/components/preview/PublicPreviewClientPage.tsx index 467149e..61f7812 100644 --- a/src/uhm/components/preview/PublicPreviewClientPage.tsx +++ b/src/uhm/components/preview/PublicPreviewClientPage.tsx @@ -242,6 +242,21 @@ export default function PublicPreviewClientPage({ localStorage.setItem("timeline-year", String(timelineYear)); } }, [timelineYear]); + + // Prevent global browser zoom on multi-touch pinch gestures for this entire route + useEffect(() => { + const preventZoom = (e: TouchEvent) => { + if (e.touches.length > 1) { + e.preventDefault(); + } + }; + + document.addEventListener("touchmove", preventZoom, { passive: false }); + return () => { + document.removeEventListener("touchmove", preventZoom); + }; + }, []); + useEffect(() => { if (!loadInteractiveMap) return; const timeoutId = window.setTimeout(() => { @@ -408,12 +423,8 @@ export default function PublicPreviewClientPage({ if (isLargeScreen) { return "min(392px, calc(100vw - 120px))"; } - if (isLayerPanelVisible) { - return `calc(100vw - 104px)`; - } else { - return `calc(100vw - 36px)`; - } - }, [isLargeScreen, isLayerPanelVisible]); + return "min(280px, calc(100vw - 86px))"; + }, [isLargeScreen]); const searchBarWrapperStyle = useMemo(() => { if (isLargeScreen) { diff --git a/src/uhm/components/ui/TimelineBar.tsx b/src/uhm/components/ui/TimelineBar.tsx index 75000b3..5f15e39 100644 --- a/src/uhm/components/ui/TimelineBar.tsx +++ b/src/uhm/components/ui/TimelineBar.tsx @@ -227,8 +227,14 @@ export default function TimelineBar({ onMouseDown={() => startChangingYear(-1)} onMouseUp={stopChangingYear} onMouseLeave={stopChangingYear} - onTouchStart={() => startChangingYear(-1)} - onTouchEnd={stopChangingYear} + onTouchStart={(e) => { + e.preventDefault(); + startChangingYear(-1); + }} + onTouchEnd={(e) => { + e.preventDefault(); + stopChangingYear(); + }} disabled={effectiveDisabled} className={styles.adjustBtn} style={{ width: 32, height: 32, borderRadius: 8, fontSize: 16 }} @@ -264,8 +270,14 @@ export default function TimelineBar({ onMouseDown={() => startChangingYear(1)} onMouseUp={stopChangingYear} onMouseLeave={stopChangingYear} - onTouchStart={() => startChangingYear(1)} - onTouchEnd={stopChangingYear} + onTouchStart={(e) => { + e.preventDefault(); + startChangingYear(1); + }} + onTouchEnd={(e) => { + e.preventDefault(); + stopChangingYear(); + }} disabled={effectiveDisabled} className={styles.adjustBtn} style={{ width: 32, height: 32, borderRadius: 8, fontSize: 16 }} @@ -397,8 +409,14 @@ export default function TimelineBar({ onMouseDown={() => startChangingYear(-1)} onMouseUp={stopChangingYear} onMouseLeave={stopChangingYear} - onTouchStart={() => startChangingYear(-1)} - onTouchEnd={stopChangingYear} + onTouchStart={(e) => { + e.preventDefault(); + startChangingYear(-1); + }} + onTouchEnd={(e) => { + e.preventDefault(); + stopChangingYear(); + }} disabled={effectiveDisabled} className={styles.adjustBtn} title="Giảm 1 năm" @@ -411,8 +429,14 @@ export default function TimelineBar({ onMouseDown={() => startChangingYear(1)} onMouseUp={stopChangingYear} onMouseLeave={stopChangingYear} - onTouchStart={() => startChangingYear(1)} - onTouchEnd={stopChangingYear} + onTouchStart={(e) => { + e.preventDefault(); + startChangingYear(1); + }} + onTouchEnd={(e) => { + e.preventDefault(); + stopChangingYear(); + }} disabled={effectiveDisabled} className={styles.adjustBtn} title="Tăng 1 năm" @@ -714,6 +738,8 @@ function CanvasTimelineRuler({ } }, [year, drawYear]); + + const handleWheel = (e: React.WheelEvent) => { if (disabled) return; e.preventDefault(); @@ -840,6 +866,7 @@ function CanvasTimelineRuler({ border: "1px solid rgba(255, 255, 255, 0.08)", overflow: "hidden", cursor: disabled ? "not-allowed" : "ew-resize", + touchAction: "none", }} onWheel={handleWheel} > @@ -849,6 +876,7 @@ function CanvasTimelineRuler({ display: "block", width: "100%", height: "100%", + touchAction: "none", }} onPointerDown={handlePointerDown} onPointerMove={handlePointerMove} diff --git a/src/uhm/components/wiki/PublicWikiSidebar.tsx b/src/uhm/components/wiki/PublicWikiSidebar.tsx index 8ffa4ba..6d4aed5 100644 --- a/src/uhm/components/wiki/PublicWikiSidebar.tsx +++ b/src/uhm/components/wiki/PublicWikiSidebar.tsx @@ -205,47 +205,7 @@ function PublicWikiSidebar({ window.addEventListener("pointerup", onUp); }; - const handleHeightPointerDown = (event: React.PointerEvent) => { - event.preventDefault(); - const startY = event.clientY; - const startHeight = sidebarHeight || 400; - // Tạo đường ghost ảo nằm ngang chỉ vị trí kéo chiều cao - const ghost = document.createElement("div"); - ghost.style.position = "fixed"; - ghost.style.left = "0"; - ghost.style.right = "0"; - ghost.style.height = "4px"; - ghost.style.backgroundColor = "#38bdf8"; - ghost.style.boxShadow = "0 0 12px rgba(56, 189, 248, 0.8)"; - ghost.style.zIndex = "99999"; - ghost.style.cursor = "row-resize"; - ghost.style.pointerEvents = "none"; - - const startScreenY = window.innerHeight - startHeight; - ghost.style.top = `${startScreenY}px`; - document.body.appendChild(ghost); - - const onMove = (e: PointerEvent) => { - ghost.style.top = `${e.clientY}px`; - }; - - const onUp = (e: PointerEvent) => { - window.removeEventListener("pointermove", onMove); - window.removeEventListener("pointerup", onUp); - if (ghost.parentNode) { - ghost.parentNode.removeChild(ghost); - } - const deltaY = startY - e.clientY; - const nextHeight = Math.max(200, Math.min(window.innerHeight * 0.9, startHeight + deltaY)); - if (onSidebarHeightChange) { - onSidebarHeightChange(nextHeight); - } - }; - - window.addEventListener("pointermove", onMove); - window.addEventListener("pointerup", onUp); - }; const [activeHeadingId, setActiveHeadingId] = useState(null); const processedWiki = useMemo(() => { @@ -327,6 +287,27 @@ function PublicWikiSidebar({ return () => root.removeEventListener("click", handleClick); }, [onWikiLinkRequest, renderHtml]); + const isExpanded = useMemo(() => { + if (typeof window === "undefined") return false; + const fullHeight = Math.round(window.innerHeight * 0.70); + return (sidebarHeight || 400) >= fullHeight; + }, [sidebarHeight]); + + const handleHeightToggle = () => { + if (typeof window === "undefined") return; + const halfHeight = Math.round(window.innerHeight * 0.45); + const fullHeight = Math.round(window.innerHeight * 0.85); + const currentHeight = sidebarHeight || 400; + + const nextHeight = Math.abs(currentHeight - halfHeight) < Math.abs(currentHeight - fullHeight) + ? fullHeight + : halfHeight; + + if (onSidebarHeightChange) { + onSidebarHeightChange(nextHeight); + } + }; + const [isMobileOrTablet, setIsMobileOrTablet] = useState(false); useEffect(() => { const checkDevice = () => setIsMobileOrTablet(window.innerWidth < 1024); @@ -359,17 +340,18 @@ function PublicWikiSidebar({ {/* Grab Handle for bottom sheet on mobile */} {isMobileOrTablet ? (
+ + +
) : null}