Automerge backed Markdown editor for the Atmosphere
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485// src/layout/hooks/useAutoCloseSidebars.tsimport { useEffect, useRef, type RefObject } from 'react'import { useLayout } from '../LayoutContext'import { MOBILE_BREAKPOINT } from '../layout-state'import { consumeGestureReturnMode } from './gesture-return-mode'import { isAutoCloseSuppressed, suppressNextGesture } from './gesture-suppression'
/** * Hook that auto-closes the sidebar panel on editor interaction at mobile viewports. * * Mobile state machine for editor interaction: * - **full** → gesture return mode (ribbon or hidden, depending on how sidebar was opened) * - **ribbon** → **ribbon** (no change — ribbon is not dismissed by editor tap) * - **hidden** → **hidden** (no change — must NOT promote to ribbon) * * If a gesture opened the sidebar, auto-close returns to the pre-gesture mode * (ribbon or hidden) via the shared gesture-return-mode module. If the sidebar * was opened via burger menu (no gesture return mode set), defaults to ribbon * to preserve the traditional auto-close behavior. * * The right panel is collapsed on any editor interaction at mobile viewports. * * @param editorRef - Ref to the editor container element */export function useAutoCloseSidebars(editorRef: RefObject<HTMLElement | null>): void { const { state, setSidebarMode, setRightPanelCollapsed } = useLayout() // Track state in refs so the event handler reads the latest values // without re-registering listeners on every state change. const sidebarModeRef = useRef(state.sidebarMode) useEffect(() => { sidebarModeRef.current = state.sidebarMode }, [state.sidebarMode]) const rightPanelCollapsedRef = useRef(state.rightPanelCollapsed) useEffect(() => { rightPanelCollapsedRef.current = state.rightPanelCollapsed }, [state.rightPanelCollapsed]) useEffect(() => { const container = editorRef.current if (!container) return const handleInteraction = () => { // Only auto-close on mobile viewports (< 768px) if (window.innerWidth >= MOBILE_BREAKPOINT) return
// Suppress if a gesture just fired (prevents undo of gesture-initiated changes) if (isAutoCloseSuppressed()) return // Bail out early if there's nothing to close if (sidebarModeRef.current !== 'full' && rightPanelCollapsedRef.current) return // Only transition from full. Do NOT promote hidden → ribbon. // Use gesture return mode if set (gesture-opened → returns to prior state), // otherwise default to 'ribbon' (burger-menu-opened → traditional auto-close). let closed = false if (sidebarModeRef.current === 'full') { setSidebarMode(consumeGestureReturnMode('ribbon')) closed = true } if (!rightPanelCollapsedRef.current) { setRightPanelCollapsed(true) closed = true } // Suppress rapid follow-up gestures so closing the sidebar doesn't // immediately open the right panel (or vice versa) during animation. if (closed) { suppressNextGesture() } } // Listen for all interaction types container.addEventListener('pointerdown', handleInteraction) container.addEventListener('focusin', handleInteraction) container.addEventListener('scroll', handleInteraction, true) // Capture for nested scrollables return () => { container.removeEventListener('pointerdown', handleInteraction) container.removeEventListener('focusin', handleInteraction) container.removeEventListener('scroll', handleInteraction, true) } }, [editorRef, setSidebarMode, setRightPanelCollapsed])}