import { useEffect, useRef, useState } from "react"; const MOBILE_SIDEBAR_SWIPE_EDGE_PX = 32; const MOBILE_SIDEBAR_SWIPE_DISTANCE_PX = 56; export function useMobileSidebar() { const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); const mobileSidebarSwipeRef = useRef<{ startX: number; startY: number } | null>(null); useEffect(() => { const desktop = window.matchMedia("(min-width: 768px)"); function closeMobileSidebar() { if (desktop.matches) setMobileSidebarOpen(false); } closeMobileSidebar(); desktop.addEventListener("change", closeMobileSidebar); return () => desktop.removeEventListener("change", closeMobileSidebar); }, []); useEffect(() => { if (!mobileSidebarOpen) return; function onKeyDown(event: KeyboardEvent) { if (event.key !== "Escape" || window.matchMedia("(min-width: 768px)").matches) return; event.preventDefault(); setMobileSidebarOpen(false); queueMicrotask(() => { document.querySelector('[data-testid="open-navigation"]')?.focus(); }); } window.addEventListener("keydown", onKeyDown, true); return () => window.removeEventListener("keydown", onKeyDown, true); }, [mobileSidebarOpen]); function onMobileSidebarTouchStart(event: React.TouchEvent) { if ( mobileSidebarOpen || event.touches.length !== 1 || window.matchMedia("(min-width: 768px)").matches ) { mobileSidebarSwipeRef.current = null; return; } const touch = event.touches[0]; if (!touch) return; const rtl = document.documentElement.getAttribute("dir") === "rtl"; const startsAtEdge = rtl ? touch.clientX >= window.innerWidth - MOBILE_SIDEBAR_SWIPE_EDGE_PX : touch.clientX <= MOBILE_SIDEBAR_SWIPE_EDGE_PX; mobileSidebarSwipeRef.current = startsAtEdge ? { startX: touch.clientX, startY: touch.clientY } : null; } function onMobileSidebarTouchEnd(event: React.TouchEvent) { const swipe = mobileSidebarSwipeRef.current; mobileSidebarSwipeRef.current = null; const touch = event.changedTouches[0]; if (!swipe || !touch || mobileSidebarOpen || window.matchMedia("(min-width: 768px)").matches) { return; } const rtl = document.documentElement.getAttribute("dir") === "rtl"; const horizontal = rtl ? swipe.startX - touch.clientX : touch.clientX - swipe.startX; const vertical = Math.abs(touch.clientY - swipe.startY); if (horizontal >= MOBILE_SIDEBAR_SWIPE_DISTANCE_PX && horizontal > vertical * 1.25) { setMobileSidebarOpen(true); } } function onMobileSidebarTouchCancel() { mobileSidebarSwipeRef.current = null; } return { mobileSidebarOpen, setMobileSidebarOpen, onMobileSidebarTouchStart, onMobileSidebarTouchEnd, onMobileSidebarTouchCancel, }; }