-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Store sidebar width relative to container #2946
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,6 +53,16 @@ type SidebarResizableOptions = { | |
| storageKey?: string; | ||
| }; | ||
|
|
||
| const SidebarStoredWidthSchema = Schema.Union([ | ||
| Schema.Finite, | ||
| Schema.Struct({ | ||
| unit: Schema.Literal("fraction"), | ||
| value: Schema.Finite, | ||
| }), | ||
| ]); | ||
|
|
||
| type SidebarStoredWidth = typeof SidebarStoredWidthSchema.Type; | ||
|
|
||
| type SidebarResolvedResizableOptions = { | ||
| maxWidth: number; | ||
| minWidth: number; | ||
|
|
@@ -335,6 +345,26 @@ function clampSidebarWidth(width: number, options: SidebarResolvedResizableOptio | |
| return Math.max(options.minWidth, Math.min(width, options.maxWidth)); | ||
| } | ||
|
|
||
| function getSidebarWidthStorageBasis(wrapper: HTMLElement): number { | ||
| return wrapper.parentElement?.clientWidth || window.innerWidth; | ||
| } | ||
|
|
||
| function getStoredSidebarWidth( | ||
| storedWidth: SidebarStoredWidth, | ||
| wrapper: HTMLElement, | ||
| ): number | null { | ||
| if (typeof storedWidth === "number") { | ||
| return null; | ||
| } | ||
|
|
||
| return storedWidth.value * getSidebarWidthStorageBasis(wrapper); | ||
| } | ||
|
|
||
| function createStoredSidebarWidth(width: number, wrapper: HTMLElement): SidebarStoredWidth { | ||
| const basis = getSidebarWidthStorageBasis(wrapper); | ||
| return { unit: "fraction", value: basis > 0 ? width / basis : 0 }; | ||
| } | ||
|
|
||
| function SidebarRail({ | ||
| className, | ||
| onClick, | ||
|
|
@@ -380,7 +410,11 @@ function SidebarRail({ | |
| element.style.removeProperty("transition-duration"); | ||
| }); | ||
| if (resolvedResizable?.storageKey && typeof window !== "undefined") { | ||
| setLocalStorageItem(resolvedResizable.storageKey, resizeState.width, Schema.Finite); | ||
| setLocalStorageItem( | ||
| resolvedResizable.storageKey, | ||
| createStoredSidebarWidth(resizeState.width, resizeState.wrapper), | ||
| SidebarStoredWidthSchema, | ||
| ); | ||
| } | ||
| resolvedResizable?.onResize?.(resizeState.width); | ||
| resizeStateRef.current = null; | ||
|
|
@@ -545,16 +579,34 @@ function SidebarRail({ | |
|
|
||
| React.useEffect(() => { | ||
| if (!resolvedResizable?.storageKey || typeof window === "undefined") return; | ||
| const storageKey = resolvedResizable.storageKey; | ||
| const rail = railRef.current; | ||
| if (!rail) return; | ||
| const wrapper = rail.closest<HTMLElement>("[data-slot='sidebar-wrapper']"); | ||
| if (!wrapper) return; | ||
| const storageBasisElement = wrapper.parentElement; | ||
|
|
||
| const applyStoredWidth = () => { | ||
| const storedWidth = getLocalStorageItem(storageKey, SidebarStoredWidthSchema); | ||
| if (storedWidth === null) return; | ||
| const width = getStoredSidebarWidth(storedWidth, wrapper); | ||
| if (width === null) return; | ||
| const clampedWidth = clampSidebarWidth(width, resolvedResizable); | ||
| wrapper.style.setProperty("--sidebar-width", `${clampedWidth}px`); | ||
| resolvedResizable.onResize?.(clampedWidth); | ||
| }; | ||
|
|
||
| const storedWidth = getLocalStorageItem(resolvedResizable.storageKey, Schema.Finite); | ||
| if (storedWidth === null) return; | ||
| const clampedWidth = clampSidebarWidth(storedWidth, resolvedResizable); | ||
| wrapper.style.setProperty("--sidebar-width", `${clampedWidth}px`); | ||
| resolvedResizable.onResize?.(clampedWidth); | ||
| applyStoredWidth(); | ||
| window.addEventListener("resize", applyStoredWidth); | ||
| const resizeObserver = new ResizeObserver(applyStoredWidth); | ||
| if (storageBasisElement) { | ||
| resizeObserver.observe(storageBasisElement); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Resize reapplies stale storageMedium Severity While the user is dragging the sidebar rail, Reviewed by Cursor Bugbot for commit ce3f903. Configure here. |
||
|
|
||
| return () => { | ||
| window.removeEventListener("resize", applyStoredWidth); | ||
| resizeObserver?.disconnect(); | ||
| }; | ||
| }, [resolvedResizable]); | ||
|
|
||
| React.useEffect(() => { | ||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Legacy stored widths ignored
Medium Severity
SidebarStoredWidthSchemastill accepts legacy numeric pixel values from localStorage, butgetStoredSidebarWidthreturnsnullfor numbers soapplyStoredWidthnever restores them. Existing saved sidebar widths are silently dropped until the user drags the rail again.Reviewed by Cursor Bugbot for commit ce3f903. Configure here.