Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/sidenav-resizable-collapse-persistence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@astryxdesign/core': patch
---

[fix] SideNav: collapsing a resizable SideNav with `resizable.autoSaveId` no longer persists a width of 0, which made the nav restore as an invisible zero-width panel after a reload. Persisted entries now store `{size, isCollapsed}`, so a reload restores the collapsed icon rail and expanding returns to the previously saved width. Legacy entries still load: a plain `0` (written by the old collapse path) restores as collapsed, so already-affected users recover without clearing localStorage, and plain width entries keep their width without overriding `defaultIsCollapsed`. `useResizable` gains an `initialIsCollapsed` config so a component that owns collapse state can seed the hook. Several callback fixes ride along: SideNav's `onCollapsedChange` now fires exactly once per toggle click (previously twice — directly and again via the resize hook); `useResizable`'s `onCollapseChange` now fires when `resize()` moves the region out of the collapsed state, matching the drag path and its documented contract; and collapse notifications are now driven from live state rather than the state captured at the last render, so a drag gesture reports one collapse (not one per pointer move), dragging back above the collapse threshold re-expands mid-gesture, and two imperative calls in the same tick report the region's real final state. Toggling a `resizable` SideNav whose `collapsible` is unset is now a no-op instead of leaving the imperative collapse handle reporting a collapse that never happened. Note for SSR apps: restoring a collapsed session changes the first client render, which React reports as a recoverable hydration mismatch (the same class as the existing persisted-width mismatch).

@AKnassa
4 changes: 2 additions & 2 deletions packages/core/src/Resizable/Resizable.doc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export const docs = {
{
name: 'autoSaveId',
type: 'string',
description: 'Key for persisting sizes to localStorage.',
description: 'Key for persisting sizes and collapse state to localStorage.',
},
],
},
Expand Down Expand Up @@ -212,7 +212,7 @@ export const docsDense = {
collapsedSize: 'px threshold triggering collapse during drag',
snaps: 'px values to snap to during resize',
shrinkOrder: 'cascade priority: lower number shrinks first',
autoSaveId: 'key for persisting sizes to localStorage',
autoSaveId: 'key for persisting sizes + collapse state to localStorage',
},
},
{
Expand Down
10 changes: 8 additions & 2 deletions packages/core/src/Resizable/useResizable.doc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ export const docs = {
{
name: 'autoSaveId',
type: 'string',
description: 'Key for localStorage persistence of size across sessions.',
description: 'Key for localStorage persistence of size and collapse state across sessions.',
},
{
name: 'initialIsCollapsed',
type: 'boolean',
description: 'Initial collapse state; wins over a persisted collapse flag. For components that own collapse state (e.g. SideNav).',
},
],
returns: [
Expand Down Expand Up @@ -96,7 +101,8 @@ export const docsDense = {
maxSizePx: 'max size in px.',
collapsible: 'whether dragging below collapsed threshold collapses region to zero.',
snaps: 'px values to snap to during drag.',
autoSaveId: 'key for localStorage persistence of size across sessions.',
autoSaveId: 'key for localStorage persistence of size + collapse state across sessions.',
initialIsCollapsed: 'initial collapse state; wins over persisted collapse flag.',
},
returnDescriptions: {
size: 'current size in px.',
Expand Down
Loading
Loading