authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-10-09 22:51:37-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-09 22:51:37-03:00
log033866c7aa55c53593ca31538f7d8ac99fdfe0fd
tree53db2f7a182b52d5b3da7b42c5794b6113112d21
parent42e1a0fba2938bd56761db63f466962342d7b779
signature Signed by PGP key B5690EEEBB952194

perf(web): sidebar drags restyle only the sidebar (#17659)


1 files changed, 23 insertions(+), 9 deletions(-)

apps/web/src/components/ui/sidebar.tsx+23-9
...@@ -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);
401406
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]);
443457
444 const handleClick = React.useCallback(458 const handleClick = React.useCallback(
445 (event: React.MouseEvent<HTMLButtonElement>) => {459 (event: React.MouseEvent<HTMLButtonElement>) => {