| ... | @@ -369,9 +369,6 @@ function SidebarRail({ | ... | @@ -369,9 +369,6 @@ function SidebarRail({ |
| 369 | const suppressClickRef = React.useRef(false); | 369 | const suppressClickRef = React.useRef(false); |
| 370 | const resolvedResizable = sidebarInstance?.resizable ?? null; | 370 | const resolvedResizable = sidebarInstance?.resizable ?? null; |
| 371 | const latestResizable = React.useRef(resolvedResizable); | 371 | const latestResizable = React.useRef(resolvedResizable); |
| 372 | React.useLayoutEffect(() => { | | |
| 373 | latestResizable.current = resolvedResizable; | | |
| 374 | }, [resolvedResizable]); | | |
| 375 | const canResize = resolvedResizable !== null && open; | 372 | const canResize = resolvedResizable !== null && open; |
| 376 | const railLabel = canResize ? "Resize Sidebar" : "Toggle Sidebar"; | 373 | const railLabel = canResize ? "Resize Sidebar" : "Toggle Sidebar"; |
| 377 | const railTitle = canResize ? "Drag to resize sidebar" : "Toggle Sidebar"; | 374 | const railTitle = canResize ? "Drag to resize sidebar" : "Toggle Sidebar"; |
| ... | @@ -390,14 +387,22 @@ function SidebarRail({ | ... | @@ -390,14 +387,22 @@ function SidebarRail({ |
| 390 | sidebarContainer.getBoundingClientRect().width, | 387 | sidebarContainer.getBoundingClientRect().width, |
| 391 | resolvedResizable, | 388 | resolvedResizable, |
| 392 | ); | 389 | ); |
| 393 | const transitionTargets = [ | 390 | // Drag frames write width to the gap and container only. Changing the |
| | 391 | // inherited --sidebar-width on the wrapper restyles the whole app, so it is |
| | 392 | // written once when the drag commits. |
| | 393 | const widthTargets = [ |
| 394 | sidebarRoot.querySelector<HTMLElement>("[data-slot='sidebar-gap']"), | 394 | sidebarRoot.querySelector<HTMLElement>("[data-slot='sidebar-gap']"), |
| 395 | sidebarContainer, | 395 | sidebarContainer, |
| 396 | ].filter((element): element is HTMLElement => element !== null); | 396 | ].filter((element): element is HTMLElement => element !== null); |
| 397 | transitionTargets.forEach((element) => { | 397 | const setTargetWidth = (value: number) => { |
| | 398 | widthTargets.forEach((element) => { |
| | 399 | element.style.setProperty("width", `${value}px`); |
| | 400 | }); |
| | 401 | }; |
| | 402 | widthTargets.forEach((element) => { |
| 398 | element.style.setProperty("transition-duration", "0ms"); | 403 | element.style.setProperty("transition-duration", "0ms"); |
| 399 | }); | 404 | }); |
| 400 | wrapper.style.setProperty("--sidebar-width", `${width}px`); | 405 | setTargetWidth(width); |
| 401 | | 406 | |
| 402 | return { | 407 | return { |
| 403 | width, | 408 | width, |
| ... | @@ -416,12 +421,13 @@ function SidebarRail({ | ... | @@ -416,12 +421,13 @@ function SidebarRail({ |
| 416 | wrapper, | 421 | wrapper, |
| 417 | }) ?? true; | 422 | }) ?? true; |
| 418 | if (accepted) { | 423 | if (accepted) { |
| 419 | wrapper.style.setProperty("--sidebar-width", `${nextWidth}px`); | 424 | setTargetWidth(nextWidth); |
| 420 | width = nextWidth; | 425 | width = nextWidth; |
| 421 | } | 426 | } |
| 422 | return width; | 427 | return width; |
| 423 | }, | 428 | }, |
| 424 | finish(finalWidth, moved) { | 429 | finish(finalWidth, moved) { |
| | 430 | wrapper.style.setProperty("--sidebar-width", `${finalWidth}px`); |
| 425 | suppressClickRef.current = moved; | 431 | suppressClickRef.current = moved; |
| 426 | const options = latestResizable.current; | 432 | const options = latestResizable.current; |
| 427 | if (options?.storageKey) { | 433 | if (options?.storageKey) { |
| ... | @@ -434,12 +440,20 @@ function SidebarRail({ | ... | @@ -434,12 +440,20 @@ function SidebarRail({ |
| 434 | options?.onResize?.(finalWidth); | 440 | options?.onResize?.(finalWidth); |
| 435 | }, | 441 | }, |
| 436 | cleanup() { | 442 | cleanup() { |
| 437 | transitionTargets.forEach((element) => { | 443 | widthTargets.forEach((element) => { |
| | 444 | element.style.removeProperty("width"); |
| 438 | element.style.removeProperty("transition-duration"); | 445 | element.style.removeProperty("transition-duration"); |
| 439 | }); | 446 | }); |
| 440 | }, | 447 | }, |
| 441 | }; | 448 | }; |
| 442 | }); | 449 | // Toggling the sidebar cancels a drag so the inline width never pins a |
| | 450 | // collapsed sidebar open. |
| | 451 | }, String(open)); |
| | 452 | React.useLayoutEffect(() => { |
| | 453 | latestResizable.current = resolvedResizable; |
| | 454 | // Bounds follow the window; keep an active drag's inline width inside them. |
| | 455 | resize.refresh(); |
| | 456 | }, [resolvedResizable]); |
| 443 | | 457 | |
| 444 | const handleClick = React.useCallback( | 458 | const handleClick = React.useCallback( |
| 445 | (event: React.MouseEvent<HTMLButtonElement>) => { | 459 | (event: React.MouseEvent<HTMLButtonElement>) => { |