@kushagradhawan/kookie-ui 0.1.48 → 0.1.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/components.css +1094 -95
  2. package/dist/cjs/components/_internal/shell-bottom.d.ts +31 -0
  3. package/dist/cjs/components/_internal/shell-bottom.d.ts.map +1 -0
  4. package/dist/cjs/components/_internal/shell-bottom.js +2 -0
  5. package/dist/cjs/components/_internal/shell-bottom.js.map +7 -0
  6. package/dist/cjs/components/_internal/shell-handles.d.ts +7 -0
  7. package/dist/cjs/components/_internal/shell-handles.d.ts.map +1 -0
  8. package/dist/cjs/components/_internal/shell-handles.js +2 -0
  9. package/dist/cjs/components/_internal/shell-handles.js.map +7 -0
  10. package/dist/cjs/components/_internal/shell-inspector.d.ts +31 -0
  11. package/dist/cjs/components/_internal/shell-inspector.d.ts.map +1 -0
  12. package/dist/cjs/components/_internal/shell-inspector.js +2 -0
  13. package/dist/cjs/components/_internal/shell-inspector.js.map +7 -0
  14. package/dist/cjs/components/_internal/shell-resize.d.ts +24 -0
  15. package/dist/cjs/components/_internal/shell-resize.d.ts.map +1 -0
  16. package/dist/cjs/components/_internal/shell-resize.js +2 -0
  17. package/dist/cjs/components/_internal/shell-resize.js.map +7 -0
  18. package/dist/cjs/components/_internal/shell-sidebar.d.ts +37 -0
  19. package/dist/cjs/components/_internal/shell-sidebar.d.ts.map +1 -0
  20. package/dist/cjs/components/_internal/shell-sidebar.js +2 -0
  21. package/dist/cjs/components/_internal/shell-sidebar.js.map +7 -0
  22. package/dist/cjs/components/schemas/index.d.ts +2 -0
  23. package/dist/cjs/components/schemas/index.d.ts.map +1 -1
  24. package/dist/cjs/components/schemas/index.js +1 -1
  25. package/dist/cjs/components/schemas/index.js.map +3 -3
  26. package/dist/cjs/components/schemas/shell.schema.d.ts +1025 -0
  27. package/dist/cjs/components/schemas/shell.schema.d.ts.map +1 -0
  28. package/dist/cjs/components/schemas/shell.schema.js +2 -0
  29. package/dist/cjs/components/schemas/shell.schema.js.map +7 -0
  30. package/dist/cjs/components/shell.context.d.ts +38 -0
  31. package/dist/cjs/components/shell.context.d.ts.map +1 -0
  32. package/dist/cjs/components/shell.context.js +2 -0
  33. package/dist/cjs/components/shell.context.js.map +7 -0
  34. package/dist/cjs/components/shell.d.ts +6 -68
  35. package/dist/cjs/components/shell.d.ts.map +1 -1
  36. package/dist/cjs/components/shell.hooks.d.ts +3 -0
  37. package/dist/cjs/components/shell.hooks.d.ts.map +1 -0
  38. package/dist/cjs/components/shell.hooks.js +2 -0
  39. package/dist/cjs/components/shell.hooks.js.map +7 -0
  40. package/dist/cjs/components/shell.js +1 -1
  41. package/dist/cjs/components/shell.js.map +3 -3
  42. package/dist/cjs/components/shell.types.d.ts +20 -0
  43. package/dist/cjs/components/shell.types.d.ts.map +1 -0
  44. package/dist/cjs/components/shell.types.js +2 -0
  45. package/dist/cjs/components/shell.types.js.map +7 -0
  46. package/dist/cjs/components/sidebar.d.ts +8 -2
  47. package/dist/cjs/components/sidebar.d.ts.map +1 -1
  48. package/dist/cjs/components/sidebar.js +1 -1
  49. package/dist/cjs/components/sidebar.js.map +3 -3
  50. package/dist/esm/components/_internal/shell-bottom.d.ts +31 -0
  51. package/dist/esm/components/_internal/shell-bottom.d.ts.map +1 -0
  52. package/dist/esm/components/_internal/shell-bottom.js +2 -0
  53. package/dist/esm/components/_internal/shell-bottom.js.map +7 -0
  54. package/dist/esm/components/_internal/shell-handles.d.ts +7 -0
  55. package/dist/esm/components/_internal/shell-handles.d.ts.map +1 -0
  56. package/dist/esm/components/_internal/shell-handles.js +2 -0
  57. package/dist/esm/components/_internal/shell-handles.js.map +7 -0
  58. package/dist/esm/components/_internal/shell-inspector.d.ts +31 -0
  59. package/dist/esm/components/_internal/shell-inspector.d.ts.map +1 -0
  60. package/dist/esm/components/_internal/shell-inspector.js +2 -0
  61. package/dist/esm/components/_internal/shell-inspector.js.map +7 -0
  62. package/dist/esm/components/_internal/shell-resize.d.ts +24 -0
  63. package/dist/esm/components/_internal/shell-resize.d.ts.map +1 -0
  64. package/dist/esm/components/_internal/shell-resize.js +2 -0
  65. package/dist/esm/components/_internal/shell-resize.js.map +7 -0
  66. package/dist/esm/components/_internal/shell-sidebar.d.ts +37 -0
  67. package/dist/esm/components/_internal/shell-sidebar.d.ts.map +1 -0
  68. package/dist/esm/components/_internal/shell-sidebar.js +2 -0
  69. package/dist/esm/components/_internal/shell-sidebar.js.map +7 -0
  70. package/dist/esm/components/schemas/index.d.ts +2 -0
  71. package/dist/esm/components/schemas/index.d.ts.map +1 -1
  72. package/dist/esm/components/schemas/index.js +1 -1
  73. package/dist/esm/components/schemas/index.js.map +3 -3
  74. package/dist/esm/components/schemas/shell.schema.d.ts +1025 -0
  75. package/dist/esm/components/schemas/shell.schema.d.ts.map +1 -0
  76. package/dist/esm/components/schemas/shell.schema.js +2 -0
  77. package/dist/esm/components/schemas/shell.schema.js.map +7 -0
  78. package/dist/esm/components/shell.context.d.ts +38 -0
  79. package/dist/esm/components/shell.context.d.ts.map +1 -0
  80. package/dist/esm/components/shell.context.js +2 -0
  81. package/dist/esm/components/shell.context.js.map +7 -0
  82. package/dist/esm/components/shell.d.ts +6 -68
  83. package/dist/esm/components/shell.d.ts.map +1 -1
  84. package/dist/esm/components/shell.hooks.d.ts +3 -0
  85. package/dist/esm/components/shell.hooks.d.ts.map +1 -0
  86. package/dist/esm/components/shell.hooks.js +2 -0
  87. package/dist/esm/components/shell.hooks.js.map +7 -0
  88. package/dist/esm/components/shell.js +1 -1
  89. package/dist/esm/components/shell.js.map +3 -3
  90. package/dist/esm/components/shell.types.d.ts +20 -0
  91. package/dist/esm/components/shell.types.d.ts.map +1 -0
  92. package/dist/esm/components/shell.types.js +2 -0
  93. package/dist/esm/components/shell.types.js.map +7 -0
  94. package/dist/esm/components/sidebar.d.ts +8 -2
  95. package/dist/esm/components/sidebar.d.ts.map +1 -1
  96. package/dist/esm/components/sidebar.js +1 -1
  97. package/dist/esm/components/sidebar.js.map +3 -3
  98. package/layout/utilities.css +168 -84
  99. package/layout.css +168 -84
  100. package/package.json +2 -1
  101. package/schemas/base-button.json +1 -1
  102. package/schemas/button.json +1 -1
  103. package/schemas/icon-button.json +1 -1
  104. package/schemas/index.json +6 -6
  105. package/schemas/shell-bottom.json +168 -0
  106. package/schemas/shell-content.json +34 -0
  107. package/schemas/shell-handle.json +34 -0
  108. package/schemas/shell-header.json +42 -0
  109. package/schemas/shell-inspector.json +171 -0
  110. package/schemas/shell-panel.json +167 -0
  111. package/schemas/shell-rail.json +132 -0
  112. package/schemas/shell-root.json +54 -0
  113. package/schemas/shell-sidebar.json +182 -0
  114. package/schemas/shell-trigger.json +76 -0
  115. package/schemas/toggle-button.json +1 -1
  116. package/schemas/toggle-icon-button.json +1 -1
  117. package/src/components/_internal/base-menu.css +4 -5
  118. package/src/components/_internal/base-sidebar-menu.css +0 -1
  119. package/src/components/_internal/base-sidebar.css +7 -0
  120. package/src/components/_internal/shell-bottom.tsx +251 -0
  121. package/src/components/_internal/shell-handles.tsx +193 -0
  122. package/src/components/_internal/shell-inspector.tsx +242 -0
  123. package/src/components/_internal/shell-resize.tsx +30 -0
  124. package/src/components/_internal/shell-sidebar.tsx +370 -0
  125. package/src/components/schemas/index.ts +46 -0
  126. package/src/components/schemas/shell.schema.ts +403 -0
  127. package/src/components/shell.context.tsx +59 -0
  128. package/src/components/shell.css +33 -18
  129. package/src/components/shell.hooks.ts +31 -0
  130. package/src/components/shell.tsx +387 -1682
  131. package/src/components/shell.types.ts +27 -0
  132. package/src/components/sidebar.css +233 -33
  133. package/src/components/sidebar.tsx +248 -214
  134. package/src/styles/tokens/blur.css +2 -2
  135. package/src/styles/tokens/color.css +2 -2
  136. package/styles.css +1267 -181
  137. package/tokens/base.css +5 -2
  138. package/tokens.css +5 -2
  139. package/utilities.css +168 -84
@@ -0,0 +1,30 @@
1
+ import * as React from 'react';
2
+
3
+ export interface PaneResizeContextValue {
4
+ containerRef: React.RefObject<HTMLDivElement | null>;
5
+ cssVarName: string;
6
+ minSize: number;
7
+ maxSize: number;
8
+ defaultSize: number;
9
+ orientation: 'vertical' | 'horizontal';
10
+ edge: 'start' | 'end';
11
+ computeNext: (clientPos: number, startClientPos: number, startSize: number) => number;
12
+ onResize?: (size: number) => void;
13
+ onResizeStart?: (size: number) => void;
14
+ onResizeEnd?: (size: number) => void;
15
+ target: 'left' | 'rail' | 'panel' | 'sidebar' | 'inspector' | 'bottom';
16
+ collapsible: boolean;
17
+ snapPoints?: number[];
18
+ snapTolerance: number;
19
+ collapseThreshold?: number;
20
+ requestCollapse?: () => void;
21
+ requestToggle?: () => void;
22
+ }
23
+
24
+ export const PaneResizeContext = React.createContext<PaneResizeContextValue | null>(null);
25
+
26
+ export function usePaneResize() {
27
+ const ctx = React.useContext(PaneResizeContext);
28
+ if (!ctx) throw new Error('Shell.Handle must be used within a resizable pane');
29
+ return ctx;
30
+ }
@@ -0,0 +1,370 @@
1
+ import * as React from 'react';
2
+ import classNames from 'classnames';
3
+ import * as Sheet from '../sheet.js';
4
+ import { VisuallyHidden } from '../visually-hidden.js';
5
+ import { useShell } from '../shell.context.js';
6
+ import { useResponsivePresentation } from '../shell.hooks.js';
7
+ import { PaneResizeContext } from './shell-resize.js';
8
+ import { SidebarHandle, PaneHandle } from './shell-handles.js';
9
+ import type { Breakpoint, PaneMode, PaneSizePersistence, PresentationValue, ResponsivePresentation, ResponsiveSidebarMode, SidebarMode } from '../shell.types.js';
10
+ import { BREAKPOINTS } from '../shell.types.js';
11
+
12
+ interface PaneProps extends React.ComponentPropsWithoutRef<'div'> {
13
+ presentation?: ResponsivePresentation;
14
+ mode?: PaneMode;
15
+ defaultMode?: any;
16
+ onModeChange?: (mode: PaneMode | SidebarMode) => void;
17
+ expandedSize?: number;
18
+ minSize?: number;
19
+ maxSize?: number;
20
+ resizable?: boolean;
21
+ collapsible?: boolean;
22
+ onExpand?: () => void;
23
+ onCollapse?: () => void;
24
+ onResize?: (size: number) => void;
25
+ resizer?: React.ReactNode;
26
+ onResizeStart?: (size: number) => void;
27
+ onResizeEnd?: (size: number) => void;
28
+ snapPoints?: number[];
29
+ snapTolerance?: number;
30
+ collapseThreshold?: number;
31
+ paneId?: string;
32
+ persistence?: PaneSizePersistence;
33
+ }
34
+
35
+ type SidebarComponent = React.ForwardRefExoticComponent<
36
+ Omit<PaneProps, 'mode' | 'defaultMode' | 'onModeChange'> & {
37
+ mode?: SidebarMode;
38
+ defaultMode?: ResponsiveSidebarMode;
39
+ onModeChange?: (mode: SidebarMode) => void;
40
+ thinSize?: number;
41
+ toggleModes?: 'both' | 'single';
42
+ } & React.RefAttributes<HTMLDivElement>
43
+ > & { Handle: typeof SidebarHandle };
44
+
45
+ export const Sidebar = React.forwardRef<
46
+ HTMLDivElement,
47
+ Omit<PaneProps, 'mode' | 'defaultMode' | 'onModeChange'> & {
48
+ mode?: SidebarMode;
49
+ defaultMode?: ResponsiveSidebarMode;
50
+ onModeChange?: (mode: SidebarMode) => void;
51
+ thinSize?: number;
52
+ toggleModes?: 'both' | 'single';
53
+ }
54
+ >(
55
+ (
56
+ {
57
+ className,
58
+ presentation = { initial: 'overlay', md: 'fixed' },
59
+ mode,
60
+ defaultMode = 'expanded',
61
+ onModeChange,
62
+ expandedSize = 288,
63
+ minSize = 200,
64
+ maxSize = 400,
65
+ resizable = false,
66
+ collapsible = true,
67
+ onExpand,
68
+ onCollapse,
69
+ onResize,
70
+ onResizeStart,
71
+ onResizeEnd,
72
+ snapPoints,
73
+ snapTolerance,
74
+ collapseThreshold,
75
+ paneId,
76
+ persistence,
77
+ children,
78
+ style,
79
+ thinSize = 64,
80
+ toggleModes,
81
+ ...props
82
+ },
83
+ ref,
84
+ ) => {
85
+ const shell = useShell();
86
+ const resolvedPresentation = useResponsivePresentation(presentation);
87
+ const isOverlay = resolvedPresentation === 'overlay';
88
+ const isStacked = resolvedPresentation === 'stacked';
89
+ // Use library-managed phase for thin ↔ expanded sequencing
90
+ const transitionPhase = (shell as any).sidebarPhase as 'idle' | 'hiding' | 'resizing' | 'showing' | undefined;
91
+ React.useLayoutEffect(() => {
92
+ if (isOverlay) return;
93
+ const containerEl = localRef.current;
94
+ if (!containerEl) return;
95
+ if (transitionPhase === 'hiding') {
96
+ // Freeze width while we fade out
97
+ try {
98
+ const rect = containerEl.getBoundingClientRect();
99
+ containerEl.style.width = `${Math.round(rect.width)}px`;
100
+ } catch {}
101
+ } else if (transitionPhase === 'resizing') {
102
+ // Release width so the CSS width transition happens
103
+ try {
104
+ containerEl.style.removeProperty('width');
105
+ } catch {}
106
+ } else if (transitionPhase === 'idle') {
107
+ try {
108
+ containerEl.style.removeProperty('width');
109
+ } catch {}
110
+ }
111
+ }, [transitionPhase, isOverlay]);
112
+ const localRef = React.useRef<HTMLDivElement | null>(null);
113
+ const setRef = React.useCallback(
114
+ (node: HTMLDivElement | null) => {
115
+ localRef.current = node;
116
+ if (typeof ref === 'function') ref(node);
117
+ else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
118
+ },
119
+ [ref],
120
+ );
121
+ const childArray = React.Children.toArray(children) as React.ReactElement[];
122
+ const handleChildren = childArray.filter((el: React.ReactElement) => React.isValidElement(el) && el.type === SidebarHandle);
123
+ const contentChildren = childArray.filter((el: React.ReactElement) => !(React.isValidElement(el) && el.type === SidebarHandle));
124
+
125
+ // Register with shell
126
+ const sidebarId = React.useId();
127
+ React.useEffect(() => {
128
+ shell.setHasSidebar(true);
129
+ return () => {
130
+ shell.setHasSidebar(false);
131
+ };
132
+ }, [shell, sidebarId]);
133
+
134
+ // Honor defaultMode on mount when uncontrolled
135
+ const didInitRef = React.useRef(false);
136
+ React.useEffect(() => {
137
+ if (didInitRef.current) return;
138
+ didInitRef.current = true;
139
+ if (mode === undefined && shell.sidebarMode !== (defaultMode as SidebarMode)) {
140
+ shell.setSidebarMode(defaultMode as SidebarMode);
141
+ }
142
+ // eslint-disable-next-line react-hooks/exhaustive-deps
143
+ }, []);
144
+
145
+ // Sync controlled mode
146
+ React.useEffect(() => {
147
+ if (mode !== undefined && shell.sidebarMode !== mode) {
148
+ shell.setSidebarMode(mode);
149
+ }
150
+ }, [mode, shell]);
151
+
152
+ // Emit mode changes
153
+ React.useEffect(() => {
154
+ if (mode === undefined) {
155
+ onModeChange?.(shell.sidebarMode);
156
+ }
157
+ }, [shell.sidebarMode, mode, onModeChange]);
158
+
159
+ // Emit expand/collapse events
160
+ React.useEffect(() => {
161
+ if (shell.sidebarMode === 'expanded') {
162
+ onExpand?.();
163
+ } else {
164
+ onCollapse?.();
165
+ }
166
+ }, [shell.sidebarMode, onExpand, onCollapse]);
167
+
168
+ // Option A: thin is width-only; content remains visible whenever not collapsed
169
+ const isContentVisible = shell.sidebarMode !== 'collapsed';
170
+
171
+ // Default persistence if paneId provided and none supplied (fixed only)
172
+ const persistenceAdapter = React.useMemo(() => {
173
+ if (!paneId || persistence) return persistence;
174
+ const key = `kookie-ui:shell:sidebar:${paneId}`;
175
+ const adapter: PaneSizePersistence = {
176
+ load: () => {
177
+ if (typeof window === 'undefined') return undefined;
178
+ const v = window.localStorage.getItem(key);
179
+ return v ? Number(v) : undefined;
180
+ },
181
+ save: (size: number) => {
182
+ if (typeof window === 'undefined') return;
183
+ window.localStorage.setItem(key, String(size));
184
+ },
185
+ };
186
+ return adapter;
187
+ }, [paneId, persistence]);
188
+
189
+ React.useEffect(() => {
190
+ let mounted = true;
191
+ (async () => {
192
+ if (!resizable || !persistenceAdapter?.load || isOverlay) return;
193
+ const loaded = await persistenceAdapter.load();
194
+ if (mounted && typeof loaded === 'number' && localRef.current) {
195
+ localRef.current.style.setProperty('--sidebar-size', `${loaded}px`);
196
+ onResize?.(loaded);
197
+ }
198
+ })();
199
+ return () => {
200
+ mounted = false;
201
+ };
202
+ }, [resizable, persistenceAdapter, onResize, isOverlay]);
203
+
204
+ // Always-follow responsive defaultMode for uncontrolled Sidebar (on breakpoint change only)
205
+ const resolveResponsiveMode = React.useCallback((): SidebarMode => {
206
+ if (typeof defaultMode === 'string') return defaultMode as SidebarMode;
207
+ const dm = defaultMode as Partial<Record<Breakpoint, SidebarMode>> | undefined;
208
+ if (dm && dm[shell.currentBreakpoint as Breakpoint]) {
209
+ return dm[shell.currentBreakpoint as Breakpoint] as SidebarMode;
210
+ }
211
+ const bpKeys = Object.keys(BREAKPOINTS) as Array<keyof typeof BREAKPOINTS>;
212
+ const order: Breakpoint[] = ([...bpKeys].reverse() as Breakpoint[]).concat('initial' as Breakpoint);
213
+ const startIdx = order.indexOf(shell.currentBreakpoint as Breakpoint);
214
+ for (let i = startIdx + 1; i < order.length; i++) {
215
+ const bp = order[i];
216
+ if (dm && dm[bp]) return dm[bp] as SidebarMode;
217
+ }
218
+ return 'collapsed';
219
+ }, [defaultMode, shell.currentBreakpoint]);
220
+
221
+ // Register custom toggle behavior based on toggleModes (both|single)
222
+ const shellForToggle = useShell();
223
+ const resolveDefaultSidebarMode = React.useCallback((): SidebarMode => {
224
+ const resolved = resolveResponsiveMode();
225
+ return resolved === 'thin' || resolved === 'expanded' ? resolved : 'expanded';
226
+ }, [resolveResponsiveMode]);
227
+
228
+ React.useEffect(() => {
229
+ if (!shellForToggle.setSidebarToggleComputer) return;
230
+ const strategy: 'both' | 'single' = toggleModes ?? 'both';
231
+ const compute = (current: SidebarMode): SidebarMode => {
232
+ if (strategy === 'both') {
233
+ if (current === 'collapsed') return 'thin';
234
+ if (current === 'thin') return 'expanded';
235
+ return 'collapsed';
236
+ }
237
+ const target = resolveDefaultSidebarMode();
238
+ if (current === 'collapsed') return target;
239
+ if (current === target) return 'collapsed';
240
+ return target;
241
+ };
242
+ shellForToggle.setSidebarToggleComputer(compute);
243
+ return () => {
244
+ shellForToggle.setSidebarToggleComputer?.((cur) => (cur === 'collapsed' ? 'thin' : cur === 'thin' ? 'expanded' : 'collapsed'));
245
+ };
246
+ }, [shellForToggle, toggleModes, resolveDefaultSidebarMode]);
247
+
248
+ const lastOverlayWidthRef = React.useRef<number>(expandedSize);
249
+ const lastOverlayModeRef = React.useRef<SidebarMode>('expanded');
250
+ React.useEffect(() => {
251
+ if (shell.sidebarMode !== 'collapsed') {
252
+ lastOverlayModeRef.current = shell.sidebarMode as SidebarMode;
253
+ lastOverlayWidthRef.current = shell.sidebarMode === 'thin' ? thinSize : expandedSize;
254
+ }
255
+ }, [shell.sidebarMode, thinSize, expandedSize]);
256
+
257
+ const lastSidebarBpRef = React.useRef<Breakpoint | null>(null);
258
+ React.useEffect(() => {
259
+ if (mode !== undefined) return;
260
+ if (!shell.currentBreakpointReady) return;
261
+ if (lastSidebarBpRef.current === shell.currentBreakpoint) return;
262
+ lastSidebarBpRef.current = shell.currentBreakpoint as Breakpoint;
263
+ const next = resolveResponsiveMode();
264
+ if (next !== shell.sidebarMode) shell.setSidebarMode(next);
265
+ }, [mode, shell.currentBreakpoint, shell.currentBreakpointReady, resolveResponsiveMode, shell.sidebarMode, shell.setSidebarMode]);
266
+
267
+ const handleEl =
268
+ resizable && !isOverlay && shell.sidebarMode === 'expanded' ? (
269
+ <PaneResizeContext.Provider
270
+ value={{
271
+ containerRef: localRef,
272
+ cssVarName: '--sidebar-size',
273
+ minSize,
274
+ maxSize,
275
+ defaultSize: expandedSize,
276
+ orientation: 'vertical',
277
+ edge: 'end',
278
+ computeNext: (client, startClient, startSize) => {
279
+ const isRtl = getComputedStyle(localRef.current!).direction === 'rtl';
280
+ const delta = client - startClient;
281
+ return startSize + (isRtl ? -delta : delta);
282
+ },
283
+ onResize,
284
+ onResizeStart,
285
+ onResizeEnd: (size) => {
286
+ onResizeEnd?.(size);
287
+ persistenceAdapter?.save?.(size);
288
+ },
289
+ target: 'sidebar',
290
+ collapsible,
291
+ snapPoints,
292
+ snapTolerance: snapTolerance ?? 8,
293
+ collapseThreshold,
294
+ requestCollapse: () => shell.setSidebarMode('collapsed'),
295
+ requestToggle: () => shell.togglePane('sidebar'),
296
+ }}
297
+ >
298
+ {handleChildren.length > 0 ? handleChildren.map((el, i) => React.cloneElement(el, { key: el.key ?? i })) : <PaneHandle />}
299
+ </PaneResizeContext.Provider>
300
+ ) : null;
301
+
302
+ if (isOverlay) {
303
+ const open = shell.sidebarMode !== 'collapsed';
304
+ return (
305
+ <Sheet.Root open={open} onOpenChange={(o) => shell.setSidebarMode(o ? 'expanded' : 'collapsed')}>
306
+ <Sheet.Content
307
+ side="start"
308
+ style={{ padding: 0 }}
309
+ width={{
310
+ initial: `${open ? (shell.sidebarMode === 'thin' ? thinSize : expandedSize) : lastOverlayWidthRef.current}px`,
311
+ }}
312
+ >
313
+ <VisuallyHidden>
314
+ <Sheet.Title>Sidebar</Sheet.Title>
315
+ </VisuallyHidden>
316
+ {contentChildren}
317
+ </Sheet.Content>
318
+ </Sheet.Root>
319
+ );
320
+ }
321
+
322
+ return (
323
+ <div
324
+ {...props}
325
+ ref={setRef}
326
+ className={classNames('rt-ShellSidebar', className)}
327
+ data-mode={shell.sidebarMode}
328
+ data-peek={shell.peekTarget === 'sidebar' || undefined}
329
+ data-presentation={resolvedPresentation}
330
+ data-open={(isStacked && isContentVisible) || undefined}
331
+ style={{
332
+ ...style,
333
+ ['--sidebar-size' as any]: `${expandedSize}px`,
334
+ ['--sidebar-thin-size' as any]: `${thinSize}px`,
335
+ ['--sidebar-min-size' as any]: `${minSize}px`,
336
+ ['--sidebar-max-size' as any]: `${maxSize}px`,
337
+ ...(shell.peekTarget === 'sidebar' && shell.sidebarMode === 'collapsed' && !isOverlay
338
+ ? (() => {
339
+ const strategy: 'both' | 'single' = toggleModes ?? 'both';
340
+ const current = shell.sidebarMode as SidebarMode;
341
+ let next: SidebarMode;
342
+ if (strategy === 'both') {
343
+ next = current === 'collapsed' ? 'thin' : current === 'thin' ? 'expanded' : 'collapsed';
344
+ } else {
345
+ const target = resolveDefaultSidebarMode();
346
+ next = current === 'collapsed' ? target : 'collapsed';
347
+ }
348
+ if (next === 'thin') {
349
+ return {
350
+ ['--peek-sidebar-width' as any]: `${thinSize}px`,
351
+ } as React.CSSProperties;
352
+ }
353
+ return {
354
+ ['--peek-sidebar-width' as any]: `var(--sidebar-size, ${expandedSize}px)`,
355
+ } as React.CSSProperties;
356
+ })()
357
+ : {}),
358
+ }}
359
+ >
360
+ <div className="rt-ShellSidebarContent" data-visible={isContentVisible || undefined} data-phase={transitionPhase && transitionPhase !== 'idle' ? transitionPhase : undefined}>
361
+ {contentChildren}
362
+ </div>
363
+ {handleEl}
364
+ </div>
365
+ );
366
+ },
367
+ ) as SidebarComponent;
368
+
369
+ Sidebar.displayName = 'Shell.Sidebar';
370
+ Sidebar.Handle = SidebarHandle;
@@ -39,6 +39,52 @@ export type { ToggleButtonProps } from './toggle-button.schema.js';
39
39
  export { ToggleIconButtonSchema, parseToggleIconButtonProps } from './toggle-icon-button.schema.js';
40
40
  export type { ToggleIconButtonProps } from './toggle-icon-button.schema.js';
41
41
 
42
+ // Shell schemas
43
+ export {
44
+ ShellRootSchema,
45
+ ShellHeaderSchema,
46
+ ShellRailSchema,
47
+ ShellPanelSchema,
48
+ ShellSidebarSchema,
49
+ ShellContentSchema,
50
+ ShellInspectorSchema,
51
+ ShellBottomSchema,
52
+ ShellTriggerSchema,
53
+ ShellHandleSchema,
54
+ parseShellRootProps,
55
+ parseShellHeaderProps,
56
+ parseShellRailProps,
57
+ parseShellPanelProps,
58
+ parseShellSidebarProps,
59
+ parseShellContentProps,
60
+ parseShellInspectorProps,
61
+ parseShellBottomProps,
62
+ parseShellTriggerProps,
63
+ parseShellHandleProps,
64
+ } from './shell.schema.js';
65
+ export type {
66
+ ShellRootProps,
67
+ ShellHeaderProps,
68
+ ShellRailProps,
69
+ ShellPanelProps,
70
+ ShellSidebarProps,
71
+ ShellContentProps,
72
+ ShellInspectorProps,
73
+ ShellBottomProps,
74
+ ShellTriggerProps,
75
+ ShellHandleProps,
76
+ PaneMode,
77
+ SidebarMode,
78
+ PresentationValue,
79
+ Breakpoint,
80
+ PaneTarget,
81
+ TriggerAction,
82
+ ResponsiveMode,
83
+ ResponsiveSidebarMode,
84
+ ResponsivePresentation,
85
+ PaneSizePersistence,
86
+ } from './shell.schema.js';
87
+
42
88
  /**
43
89
  * Generic parseProps helper for development-time validation
44
90
  *