Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 3.8k
perf(workflow): scope resize CSS-var writes to the container, not :root#5738
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
67420f44b86960d7579ed558ea274a8b2b9File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -12,44 +12,55 @@ import { useSidebarStore } from '@/stores/sidebar/store' | ||
| * add `is-resizing` class directly to the DOM (no React | ||
| * round-trip, so the CSS width transition is suppressed from the | ||
| * very first frame) | ||
| * pointermove → write to --sidebar-width inside a requestAnimationFrame | ||
| * callback (aligns work with the browser paint cycle) | ||
| * pointermove → write --sidebar-width to `.sidebar-shell-outer` (the element | ||
| * that sizes the rail) inside a requestAnimationFrame callback. | ||
| * Scoping the variable to that subtree keeps the style recalc | ||
| * local; writing it to `:root` instead forces a whole-document | ||
| * recalc (~150x slower on a large canvas). | ||
| * pointerup → cancel any pending RAF, tear down, persist final width to | ||
| * Zustand once (one React re-render to save to localStorage) | ||
| * Zustand once (writes the authoritative `:root` value for | ||
| * on-demand readers), then drop the scoped override | ||
| * | ||
| * The drag is torn down by `pointerup`, `pointercancel`, or window `blur`, so an | ||
| * interrupted gesture (release outside the window, alt-tab, context menu, the OS | ||
| * stealing focus) can never leave the `is-resizing` / `sidebar-resizing` classes | ||
| * stuck — which would otherwise freeze the sidebar at a tiny width with the | ||
| * collapse transition permanently disabled. A single-flight guard prevents | ||
| * stacking listeners across rapid presses, and an unmount cleanup tears down a | ||
| * drag still in flight when the sidebar unmounts (e.g. route change). | ||
| * stacking listeners across rapid presses, and unmounting mid-drag finalizes it | ||
| * the same way a release does — persisting the last width and dropping the | ||
| * scoped override — which matters because `.sidebar-shell-outer` lives in the | ||
| * workspace chrome and outlives the sidebar, so a stranded override would | ||
| * otherwise win over the committed `:root` value. | ||
| */ | ||
| export function useSidebarResize() { | ||
| const setSidebarWidth = useSidebarStore((s) => s.setSidebarWidth) | ||
| const cleanupRef = useRef<(() => void) | null>(null) | ||
| const teardownRef = useRef<(() => void) | null>(null) | ||
| const handlePointerDown = useCallback( | ||
| (e: React.PointerEvent<HTMLElement>) => { | ||
| if (cleanupRef.current) return | ||
| if (teardownRef.current) return | ||
| const handle = e.currentTarget | ||
| const pointerId = e.pointerId | ||
| const sidebar = document.querySelector<HTMLElement>('.sidebar-container') | ||
| const shell = document.querySelector<HTMLElement>('.sidebar-shell-outer') | ||
| const target = shell ?? document.documentElement | ||
| sidebar?.classList.add('is-resizing') | ||
| document.documentElement.classList.add('sidebar-resizing') | ||
| document.body.style.cursor = 'ew-resize' | ||
| document.body.style.userSelect = 'none' | ||
| handle.setPointerCapture?.(pointerId) | ||
| let rafId: number | null = null | ||
| let lastWidth: number | null = null | ||
| const onPointerMove = (ev: PointerEvent) => { | ||
| const max = Math.max(SIDEBAR_WIDTH.MIN, window.innerWidth * SIDEBAR_WIDTH.MAX_PERCENTAGE) | ||
| const clamped = Math.min(Math.max(ev.clientX, SIDEBAR_WIDTH.MIN), max) | ||
| lastWidth = clamped | ||
| if (rafId !== null) cancelAnimationFrame(rafId) | ||
| rafId = requestAnimationFrame(() => { | ||
| const max = Math.max(SIDEBAR_WIDTH.MIN, window.innerWidth * SIDEBAR_WIDTH.MAX_PERCENTAGE) | ||
| const clamped = Math.min(Math.max(ev.clientX, SIDEBAR_WIDTH.MIN), max) | ||
| document.documentElement.style.setProperty('--sidebar-width', `${clamped}px`) | ||
| target.style.setProperty('--sidebar-width', `${clamped}px`) | ||
| rafId = null | ||
| }) | ||
| } | ||
| @@ -68,17 +79,18 @@ export function useSidebarResize() { | ||
| document.removeEventListener('pointerup', endDrag) | ||
| document.removeEventListener('pointercancel', endDrag) | ||
| window.removeEventListener('blur', endDrag) | ||
| cleanupRef.current = null | ||
| teardownRef.current = null | ||
| } | ||
| function endDrag() { | ||
| cleanup() | ||
| const raw = document.documentElement.style.getPropertyValue('--sidebar-width') | ||
| const finalWidth = Number.parseFloat(raw) | ||
| if (!Number.isNaN(finalWidth)) setSidebarWidth(finalWidth) | ||
| if (lastWidth !== null) { | ||
| setSidebarWidth(lastWidth) | ||
waleedlatif1 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (target !== document.documentElement) target.style.removeProperty('--sidebar-width') | ||
| } | ||
waleedlatif1 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| cleanupRef.current = cleanup | ||
| teardownRef.current = endDrag | ||
| document.addEventListener('pointermove', onPointerMove) | ||
| document.addEventListener('pointerup', endDrag) | ||
| document.addEventListener('pointercancel', endDrag) | ||
| @@ -87,7 +99,7 @@ export function useSidebarResize() { | ||
| [setSidebarWidth] | ||
| ) | ||
| useEffect(() => () => cleanupRef.current?.(), []) | ||
| useEffect(() => () => teardownRef.current?.(), []) | ||
| return { handlePointerDown } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.