@kushagradhawan/kookie-ui 0.1.70 → 0.1.72

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 (164) hide show
  1. package/README.md +4 -0
  2. package/components.css +63 -380
  3. package/dist/cjs/components/_internal/base-button.d.ts.map +1 -1
  4. package/dist/cjs/components/_internal/base-button.js +1 -1
  5. package/dist/cjs/components/_internal/base-button.js.map +3 -3
  6. package/dist/cjs/components/_internal/shell-bottom.d.ts +2 -21
  7. package/dist/cjs/components/_internal/shell-bottom.d.ts.map +1 -1
  8. package/dist/cjs/components/_internal/shell-bottom.js +1 -1
  9. package/dist/cjs/components/_internal/shell-bottom.js.map +3 -3
  10. package/dist/cjs/components/_internal/shell-inspector.d.ts +10 -21
  11. package/dist/cjs/components/_internal/shell-inspector.d.ts.map +1 -1
  12. package/dist/cjs/components/_internal/shell-inspector.js +1 -1
  13. package/dist/cjs/components/_internal/shell-inspector.js.map +3 -3
  14. package/dist/cjs/components/_internal/shell-prop-helpers.d.ts +7 -0
  15. package/dist/cjs/components/_internal/shell-prop-helpers.d.ts.map +1 -0
  16. package/dist/cjs/components/_internal/shell-prop-helpers.js +2 -0
  17. package/dist/cjs/components/_internal/shell-prop-helpers.js.map +7 -0
  18. package/dist/cjs/components/_internal/shell-sidebar.d.ts +4 -21
  19. package/dist/cjs/components/_internal/shell-sidebar.d.ts.map +1 -1
  20. package/dist/cjs/components/_internal/shell-sidebar.js +1 -1
  21. package/dist/cjs/components/_internal/shell-sidebar.js.map +3 -3
  22. package/dist/cjs/components/button.d.ts.map +1 -1
  23. package/dist/cjs/components/button.js +1 -1
  24. package/dist/cjs/components/button.js.map +3 -3
  25. package/dist/cjs/components/chatbar.d.ts +11 -2
  26. package/dist/cjs/components/chatbar.d.ts.map +1 -1
  27. package/dist/cjs/components/chatbar.js +1 -1
  28. package/dist/cjs/components/chatbar.js.map +3 -3
  29. package/dist/cjs/components/icon-button.d.ts.map +1 -1
  30. package/dist/cjs/components/icon-button.js +2 -2
  31. package/dist/cjs/components/icon-button.js.map +3 -3
  32. package/dist/cjs/components/schemas/shell.schema.d.ts +70 -70
  33. package/dist/cjs/components/shell.context.d.ts +1 -0
  34. package/dist/cjs/components/shell.context.d.ts.map +1 -1
  35. package/dist/cjs/components/shell.context.js.map +2 -2
  36. package/dist/cjs/components/shell.d.ts +6 -26
  37. package/dist/cjs/components/shell.d.ts.map +1 -1
  38. package/dist/cjs/components/shell.hooks.d.ts +19 -2
  39. package/dist/cjs/components/shell.hooks.d.ts.map +1 -1
  40. package/dist/cjs/components/shell.hooks.js +1 -1
  41. package/dist/cjs/components/shell.hooks.js.map +3 -3
  42. package/dist/cjs/components/shell.js +1 -1
  43. package/dist/cjs/components/shell.js.map +3 -3
  44. package/dist/cjs/components/shell.types.d.ts +21 -0
  45. package/dist/cjs/components/shell.types.d.ts.map +1 -1
  46. package/dist/cjs/components/shell.types.js +1 -1
  47. package/dist/cjs/components/shell.types.js.map +2 -2
  48. package/dist/cjs/components/toggle-button.d.ts.map +1 -1
  49. package/dist/cjs/components/toggle-button.js +1 -1
  50. package/dist/cjs/components/toggle-button.js.map +3 -3
  51. package/dist/cjs/components/toggle-icon-button.d.ts.map +1 -1
  52. package/dist/cjs/components/toggle-icon-button.js +1 -1
  53. package/dist/cjs/components/toggle-icon-button.js.map +3 -3
  54. package/dist/cjs/hooks/index.d.ts +2 -0
  55. package/dist/cjs/hooks/index.d.ts.map +1 -1
  56. package/dist/cjs/hooks/index.js +1 -1
  57. package/dist/cjs/hooks/index.js.map +3 -3
  58. package/dist/cjs/hooks/use-live-announcer.d.ts.map +1 -1
  59. package/dist/cjs/hooks/use-live-announcer.js +2 -2
  60. package/dist/cjs/hooks/use-live-announcer.js.map +3 -3
  61. package/dist/cjs/hooks/use-toggle-state.d.ts +37 -0
  62. package/dist/cjs/hooks/use-toggle-state.d.ts.map +1 -0
  63. package/dist/cjs/hooks/use-toggle-state.js +2 -0
  64. package/dist/cjs/hooks/use-toggle-state.js.map +7 -0
  65. package/dist/cjs/hooks/use-tooltip-wrapper.d.ts +29 -0
  66. package/dist/cjs/hooks/use-tooltip-wrapper.d.ts.map +1 -0
  67. package/dist/cjs/hooks/use-tooltip-wrapper.js +2 -0
  68. package/dist/cjs/hooks/use-tooltip-wrapper.js.map +7 -0
  69. package/dist/esm/components/_internal/base-button.d.ts.map +1 -1
  70. package/dist/esm/components/_internal/base-button.js +1 -1
  71. package/dist/esm/components/_internal/base-button.js.map +3 -3
  72. package/dist/esm/components/_internal/shell-bottom.d.ts +2 -21
  73. package/dist/esm/components/_internal/shell-bottom.d.ts.map +1 -1
  74. package/dist/esm/components/_internal/shell-bottom.js +1 -1
  75. package/dist/esm/components/_internal/shell-bottom.js.map +3 -3
  76. package/dist/esm/components/_internal/shell-inspector.d.ts +10 -21
  77. package/dist/esm/components/_internal/shell-inspector.d.ts.map +1 -1
  78. package/dist/esm/components/_internal/shell-inspector.js +1 -1
  79. package/dist/esm/components/_internal/shell-inspector.js.map +3 -3
  80. package/dist/esm/components/_internal/shell-prop-helpers.d.ts +7 -0
  81. package/dist/esm/components/_internal/shell-prop-helpers.d.ts.map +1 -0
  82. package/dist/esm/components/_internal/shell-prop-helpers.js +2 -0
  83. package/dist/esm/components/_internal/shell-prop-helpers.js.map +7 -0
  84. package/dist/esm/components/_internal/shell-sidebar.d.ts +4 -21
  85. package/dist/esm/components/_internal/shell-sidebar.d.ts.map +1 -1
  86. package/dist/esm/components/_internal/shell-sidebar.js +1 -1
  87. package/dist/esm/components/_internal/shell-sidebar.js.map +3 -3
  88. package/dist/esm/components/button.d.ts.map +1 -1
  89. package/dist/esm/components/button.js +1 -1
  90. package/dist/esm/components/button.js.map +3 -3
  91. package/dist/esm/components/chatbar.d.ts +11 -2
  92. package/dist/esm/components/chatbar.d.ts.map +1 -1
  93. package/dist/esm/components/chatbar.js +1 -1
  94. package/dist/esm/components/chatbar.js.map +3 -3
  95. package/dist/esm/components/icon-button.d.ts.map +1 -1
  96. package/dist/esm/components/icon-button.js +2 -2
  97. package/dist/esm/components/icon-button.js.map +3 -3
  98. package/dist/esm/components/schemas/shell.schema.d.ts +70 -70
  99. package/dist/esm/components/shell.context.d.ts +1 -0
  100. package/dist/esm/components/shell.context.d.ts.map +1 -1
  101. package/dist/esm/components/shell.context.js.map +2 -2
  102. package/dist/esm/components/shell.d.ts +6 -26
  103. package/dist/esm/components/shell.d.ts.map +1 -1
  104. package/dist/esm/components/shell.hooks.d.ts +19 -2
  105. package/dist/esm/components/shell.hooks.d.ts.map +1 -1
  106. package/dist/esm/components/shell.hooks.js +1 -1
  107. package/dist/esm/components/shell.hooks.js.map +3 -3
  108. package/dist/esm/components/shell.js +1 -1
  109. package/dist/esm/components/shell.js.map +3 -3
  110. package/dist/esm/components/shell.types.d.ts +21 -0
  111. package/dist/esm/components/shell.types.d.ts.map +1 -1
  112. package/dist/esm/components/shell.types.js.map +2 -2
  113. package/dist/esm/components/toggle-button.d.ts.map +1 -1
  114. package/dist/esm/components/toggle-button.js +1 -1
  115. package/dist/esm/components/toggle-button.js.map +3 -3
  116. package/dist/esm/components/toggle-icon-button.d.ts.map +1 -1
  117. package/dist/esm/components/toggle-icon-button.js +1 -1
  118. package/dist/esm/components/toggle-icon-button.js.map +3 -3
  119. package/dist/esm/hooks/index.d.ts +2 -0
  120. package/dist/esm/hooks/index.d.ts.map +1 -1
  121. package/dist/esm/hooks/index.js +1 -1
  122. package/dist/esm/hooks/index.js.map +3 -3
  123. package/dist/esm/hooks/use-live-announcer.d.ts.map +1 -1
  124. package/dist/esm/hooks/use-live-announcer.js +2 -2
  125. package/dist/esm/hooks/use-live-announcer.js.map +3 -3
  126. package/dist/esm/hooks/use-toggle-state.d.ts +37 -0
  127. package/dist/esm/hooks/use-toggle-state.d.ts.map +1 -0
  128. package/dist/esm/hooks/use-toggle-state.js +2 -0
  129. package/dist/esm/hooks/use-toggle-state.js.map +7 -0
  130. package/dist/esm/hooks/use-tooltip-wrapper.d.ts +29 -0
  131. package/dist/esm/hooks/use-tooltip-wrapper.d.ts.map +1 -0
  132. package/dist/esm/hooks/use-tooltip-wrapper.js +2 -0
  133. package/dist/esm/hooks/use-tooltip-wrapper.js.map +7 -0
  134. package/package.json +4 -4
  135. package/schemas/base-button.json +1 -1
  136. package/schemas/button.json +1 -1
  137. package/schemas/icon-button.json +1 -1
  138. package/schemas/index.json +6 -6
  139. package/schemas/toggle-button.json +1 -1
  140. package/schemas/toggle-icon-button.json +1 -1
  141. package/src/components/_internal/base-button.css +136 -614
  142. package/src/components/_internal/base-button.tsx +15 -13
  143. package/src/components/_internal/shell-bottom.tsx +305 -321
  144. package/src/components/_internal/shell-inspector.tsx +310 -320
  145. package/src/components/_internal/shell-prop-helpers.ts +53 -0
  146. package/src/components/_internal/shell-sidebar.tsx +370 -384
  147. package/src/components/button.tsx +13 -42
  148. package/src/components/chatbar.tsx +7 -3
  149. package/src/components/icon-button.tsx +20 -44
  150. package/src/components/image.css +10 -8
  151. package/src/components/shell.context.tsx +1 -0
  152. package/src/components/shell.hooks.ts +67 -2
  153. package/src/components/shell.tsx +199 -209
  154. package/src/components/shell.types.ts +23 -0
  155. package/src/components/toggle-button.tsx +30 -59
  156. package/src/components/toggle-icon-button.tsx +29 -51
  157. package/src/hooks/index.ts +2 -0
  158. package/src/hooks/use-live-announcer.ts +34 -7
  159. package/src/hooks/use-toggle-state.ts +72 -0
  160. package/src/hooks/use-tooltip-wrapper.ts +28 -0
  161. package/src/styles/tokens/color.css +11 -1
  162. package/styles.css +70 -381
  163. package/tokens/base.css +7 -1
  164. package/tokens.css +7 -1
@@ -3,39 +3,23 @@ import classNames from 'classnames';
3
3
  import * as Sheet from '../sheet.js';
4
4
  import { VisuallyHidden } from '../visually-hidden.js';
5
5
  import { useShell } from '../shell.context.js';
6
- import { useResponsivePresentation, useResponsiveValue } from '../shell.hooks.js';
6
+ import { useResponsivePresentation, useResponsiveInitialState } from '../shell.hooks.js';
7
7
  import { PaneResizeContext } from './shell-resize.js';
8
+ import { extractPaneDomProps } from './shell-prop-helpers.js';
8
9
  import { SidebarHandle, PaneHandle } from './shell-handles.js';
9
- import type { Breakpoint, PaneMode, PaneSizePersistence, ResponsivePresentation, SidebarMode, Responsive } from '../shell.types.js';
10
+ import type { Breakpoint, PaneMode, PaneSizePersistence, ResponsivePresentation, SidebarMode, Responsive, PaneBaseProps } from '../shell.types.js';
10
11
  import { _BREAKPOINTS } from '../shell.types.js';
11
12
 
12
- interface PaneProps extends React.ComponentPropsWithoutRef<'div'> {
13
- presentation?: ResponsivePresentation;
13
+ type SidebarPaneProps = PaneBaseProps & {
14
14
  mode?: PaneMode;
15
15
  defaultMode?: any;
16
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
- }
17
+ };
34
18
 
35
19
  type SidebarStateChangeMeta = { reason: 'init' | 'toggle' | 'responsive' };
36
20
  type SidebarControlledProps = { state: Responsive<SidebarMode>; onStateChange?: (state: SidebarMode, meta: SidebarStateChangeMeta) => void; defaultState?: never };
37
21
  type SidebarUncontrolledProps = { defaultState?: SidebarMode | Partial<Record<Breakpoint, SidebarMode>>; onStateChange?: (state: SidebarMode, meta: SidebarStateChangeMeta) => void; state?: never };
38
- type SidebarPublicProps = Omit<PaneProps, 'mode' | 'defaultMode' | 'onModeChange'> & {
22
+ type SidebarPublicProps = Omit<SidebarPaneProps, 'mode' | 'defaultMode' | 'onModeChange'> & {
39
23
  // removed legacy mode props
40
24
  thinSize?: number;
41
25
  toggleModes?: 'both' | 'single';
@@ -49,406 +33,408 @@ type SidebarPublicProps = Omit<PaneProps, 'mode' | 'defaultMode' | 'onModeChange
49
33
 
50
34
  type SidebarComponent = React.ForwardRefExoticComponent<SidebarPublicProps & React.RefAttributes<HTMLDivElement>> & { Handle: typeof SidebarHandle };
51
35
 
52
- export const Sidebar = React.forwardRef<HTMLDivElement, SidebarPublicProps>(
53
- (
54
- {
55
- className,
56
- presentation = { initial: 'overlay', md: 'fixed' },
57
- // removed legacy props
58
- expandedSize = 288,
59
- minSize = 200,
60
- maxSize = 400,
61
- resizable = false,
62
- collapsible = true,
63
- onExpand,
64
- onCollapse,
65
- onResize,
66
- onResizeStart,
67
- onResizeEnd,
68
- snapPoints,
69
- snapTolerance,
70
- collapseThreshold,
71
- paneId,
72
- persistence,
73
- children,
74
- style,
75
- thinSize = 64,
76
- toggleModes,
77
- // new state props (XOR)
78
- state,
79
- defaultState,
80
- onStateChange,
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
- // Phase sequencing is now CSS-driven; no JS-managed phase
90
- const localRef = React.useRef<HTMLDivElement | null>(null);
91
- const setRef = React.useCallback(
92
- (node: HTMLDivElement | null) => {
93
- localRef.current = node;
94
- if (typeof ref === 'function') ref(node);
95
- else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
96
- },
97
- [ref],
98
- );
99
- const childArray = React.Children.toArray(children) as React.ReactElement[];
100
- const handleChildren = childArray.filter((el: React.ReactElement) => React.isValidElement(el) && el.type === SidebarHandle);
101
- const contentChildren = childArray.filter((el: React.ReactElement) => !(React.isValidElement(el) && el.type === SidebarHandle));
36
+ const SIDEBAR_DOM_PROP_KEYS = [
37
+ 'className',
38
+ 'children',
39
+ 'state',
40
+ 'defaultState',
41
+ 'onStateChange',
42
+ 'thinSize',
43
+ 'toggleModes',
44
+ 'size',
45
+ 'defaultSize',
46
+ 'onSizeChange',
47
+ 'sizeUpdate',
48
+ 'sizeUpdateMs',
49
+ 'style',
50
+ ] as const satisfies readonly (keyof SidebarPublicProps)[];
102
51
 
103
- // Throttled/debounced emitter for onSizeChange
104
- const onSizeChange = (props as any).onSizeChange;
105
- const sizeUpdate = (props as any).sizeUpdate;
106
- const sizeUpdateMs = (props as any).sizeUpdateMs;
107
- const emitSizeChange = React.useMemo(() => {
108
- const cb = onSizeChange as undefined | ((s: number, meta: { reason: 'init' | 'resize' | 'controlled' }) => void);
109
- const strategy = sizeUpdate as undefined | 'throttle' | 'debounce';
110
- const ms = sizeUpdateMs ?? 50;
111
- if (!cb) return () => {};
112
- if (strategy === 'debounce') {
113
- let t: any = null;
114
- return (s: number, meta: { reason: 'init' | 'resize' | 'controlled' }) => {
115
- if (t) clearTimeout(t);
116
- t = setTimeout(() => {
117
- cb(s, meta);
118
- }, ms);
119
- };
120
- }
121
- if (strategy === 'throttle') {
122
- let last = 0;
123
- return (s: number, meta: { reason: 'init' | 'resize' | 'controlled' }) => {
124
- const now = Date.now();
125
- if (now - last >= ms) {
126
- last = now;
127
- cb(s, meta);
128
- }
129
- };
130
- }
131
- return (s: number, meta: { reason: 'init' | 'resize' | 'controlled' }) => cb(s, meta);
132
- }, [onSizeChange, sizeUpdate, sizeUpdateMs]);
52
+ export const Sidebar = React.forwardRef<HTMLDivElement, SidebarPublicProps>((initialProps, ref) => {
53
+ const {
54
+ className,
55
+ presentation = { initial: 'overlay', md: 'fixed' },
56
+ expandedSize = 288,
57
+ minSize = 200,
58
+ maxSize = 400,
59
+ resizable = false,
60
+ collapsible = true,
61
+ onExpand,
62
+ onCollapse,
63
+ onResize,
64
+ onResizeStart,
65
+ onResizeEnd,
66
+ snapPoints,
67
+ snapTolerance,
68
+ collapseThreshold,
69
+ paneId,
70
+ persistence,
71
+ children,
72
+ style,
73
+ thinSize = 64,
74
+ toggleModes,
75
+ state,
76
+ defaultState,
77
+ onStateChange,
78
+ size,
79
+ defaultSize,
80
+ onSizeChange,
81
+ sizeUpdate,
82
+ sizeUpdateMs = 50,
83
+ } = initialProps;
84
+ const sidebarDomProps = extractPaneDomProps(initialProps, SIDEBAR_DOM_PROP_KEYS);
85
+ const shell = useShell();
86
+ const resolvedPresentation = useResponsivePresentation(presentation);
87
+ const isOverlay = resolvedPresentation === 'overlay';
88
+ const isStacked = resolvedPresentation === 'stacked';
89
+ // Phase sequencing is now CSS-driven; no JS-managed phase
90
+ const localRef = React.useRef<HTMLDivElement | null>(null);
91
+ const setRef = React.useCallback(
92
+ (node: HTMLDivElement | null) => {
93
+ localRef.current = node;
94
+ if (typeof ref === 'function') ref(node);
95
+ else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
96
+ },
97
+ [ref],
98
+ );
99
+ const childArray = React.Children.toArray(children) as React.ReactElement[];
100
+ const handleChildren = childArray.filter((el: React.ReactElement) => React.isValidElement(el) && el.type === SidebarHandle);
101
+ const contentChildren = childArray.filter((el: React.ReactElement) => !(React.isValidElement(el) && el.type === SidebarHandle));
133
102
 
134
- // Register with shell
135
- const sidebarId = React.useId();
136
- React.useEffect(() => {
137
- shell.setHasSidebar(true);
138
- return () => {
139
- shell.setHasSidebar(false);
103
+ // Throttled/debounced emitter for onSizeChange
104
+ const emitSizeChange = React.useMemo(() => {
105
+ const cb = onSizeChange as undefined | ((s: number, meta: { reason: 'init' | 'resize' | 'controlled' }) => void);
106
+ const strategy = sizeUpdate as undefined | 'throttle' | 'debounce';
107
+ const ms = sizeUpdateMs ?? 50;
108
+ if (!cb) return () => {};
109
+ if (strategy === 'debounce') {
110
+ let t: any = null;
111
+ return (s: number, meta: { reason: 'init' | 'resize' | 'controlled' }) => {
112
+ if (t) clearTimeout(t);
113
+ t = setTimeout(() => {
114
+ cb(s, meta);
115
+ }, ms);
140
116
  };
141
- }, [shell, sidebarId]);
142
-
143
- // Dev guards
144
- const wasControlledRef = React.useRef<boolean | null>(null);
145
- if (process.env.NODE_ENV !== 'production') {
146
- if (typeof state !== 'undefined' && typeof defaultState !== 'undefined') {
147
- console.error('Shell.Sidebar: Do not pass both `state` and `defaultState`. Choose one.');
148
- }
149
- if (typeof (props as any).size !== 'undefined' && typeof (props as any).defaultSize !== 'undefined') {
150
- console.error('Shell.Sidebar: Do not pass both `size` and `defaultSize`. Choose one.');
151
- }
152
117
  }
118
+ if (strategy === 'throttle') {
119
+ let last = 0;
120
+ return (s: number, meta: { reason: 'init' | 'resize' | 'controlled' }) => {
121
+ const now = Date.now();
122
+ if (now - last >= ms) {
123
+ last = now;
124
+ cb(s, meta);
125
+ }
126
+ };
127
+ }
128
+ return (s: number, meta: { reason: 'init' | 'resize' | 'controlled' }) => cb(s, meta);
129
+ }, [onSizeChange, sizeUpdate, sizeUpdateMs]);
153
130
 
154
- // Warn on mode switch between controlled/uncontrolled
155
- React.useEffect(() => {
156
- const isControlled = typeof state !== 'undefined';
157
- if (wasControlledRef.current === null) {
158
- wasControlledRef.current = isControlled;
159
- return;
160
- }
161
- if (wasControlledRef.current !== isControlled) {
162
- console.warn('Shell.Sidebar: Switching between controlled and uncontrolled `state` is not supported.');
163
- wasControlledRef.current = isControlled;
164
- }
165
- }, [state]);
131
+ // Register with shell
132
+ const sidebarId = React.useId();
133
+ React.useEffect(() => {
134
+ shell.setHasSidebar(true);
135
+ return () => {
136
+ shell.setHasSidebar(false);
137
+ };
138
+ }, [shell, sidebarId]);
166
139
 
167
- // Resolve responsive controlled state at top level
168
- const resolvedState = useResponsiveValue(state);
169
- const resolvedDefaultState = useResponsiveValue(defaultState as any);
140
+ // Dev guards
141
+ const wasControlledRef = React.useRef<boolean | null>(null);
142
+ if (process.env.NODE_ENV !== 'production') {
143
+ if (typeof state !== 'undefined' && typeof defaultState !== 'undefined') {
144
+ console.error('Shell.Sidebar: Do not pass both `state` and `defaultState`. Choose one.');
145
+ }
146
+ if (typeof size !== 'undefined' && typeof defaultSize !== 'undefined') {
147
+ console.error('Shell.Sidebar: Do not pass both `size` and `defaultSize`. Choose one.');
148
+ }
149
+ }
170
150
 
171
- // Honor state/defaultState on mount when uncontrolled
172
- const didInitRef = React.useRef(false);
173
- React.useEffect(() => {
174
- if (didInitRef.current) return;
175
- if (!shell.currentBreakpointReady) return;
176
- didInitRef.current = true;
177
- // Controlled state may be responsive; use resolved value
178
- if (typeof state !== 'undefined' && resolvedState) {
179
- if (shell.sidebarMode !== resolvedState) shell.setSidebarMode(resolvedState);
180
- return;
181
- }
182
- if (typeof defaultState !== 'undefined') {
183
- const initialState = (resolvedDefaultState ?? defaultState) as SidebarMode;
184
- if (shell.sidebarMode !== initialState) {
185
- shell.setSidebarMode(initialState);
186
- }
187
- onStateChange?.(initialState, { reason: 'init' });
188
- return;
189
- }
190
- // eslint-disable-next-line react-hooks/exhaustive-deps
191
- }, [shell.currentBreakpointReady, resolvedDefaultState, resolvedState, state, defaultState]);
151
+ // Warn on mode switch between controlled/uncontrolled
152
+ React.useEffect(() => {
153
+ const isControlled = typeof state !== 'undefined';
154
+ if (wasControlledRef.current === null) {
155
+ wasControlledRef.current = isControlled;
156
+ return;
157
+ }
158
+ if (wasControlledRef.current !== isControlled) {
159
+ console.warn('Shell.Sidebar: Switching between controlled and uncontrolled `state` is not supported.');
160
+ wasControlledRef.current = isControlled;
161
+ }
162
+ }, [state]);
192
163
 
193
- // Sync controlled state (responsive-aware)
194
- React.useEffect(() => {
195
- if (resolvedState === undefined) return;
196
- if (shell.sidebarMode !== resolvedState) shell.setSidebarMode(resolvedState);
197
- }, [shell, resolvedState]);
164
+ // Resolve responsive controlled state at top level
165
+ const stateIsResponsive = typeof state === 'object' && state !== null;
166
+ const { resolvedDefault: resolvedSidebarDefault } = useResponsiveInitialState<SidebarMode>({
167
+ controlledValue: state,
168
+ defaultValue: defaultState,
169
+ currentValue: shell.sidebarMode as SidebarMode,
170
+ setValue: shell.setSidebarMode,
171
+ breakpointReady: shell.currentBreakpointReady,
172
+ controlledIsResponsive: stateIsResponsive,
173
+ onResponsiveChange: (next) => onStateChange?.(next, { reason: 'responsive' }),
174
+ onInit: (initial) => onStateChange?.(initial, { reason: 'init' }),
175
+ });
198
176
 
199
- // Emit mode changes
200
- const lastNotifyModeRef = React.useRef<SidebarMode | null>(null);
201
- React.useEffect(() => {
202
- // notify new API when uncontrolled; skip first run to avoid masking init
203
- if (typeof state === 'undefined') {
204
- if (lastNotifyModeRef.current === null) {
205
- lastNotifyModeRef.current = shell.sidebarMode as SidebarMode;
206
- } else if (lastNotifyModeRef.current !== shell.sidebarMode) {
207
- lastNotifyModeRef.current = shell.sidebarMode as SidebarMode;
208
- onStateChange?.(shell.sidebarMode as SidebarMode, { reason: 'toggle' });
209
- }
177
+ // Emit mode changes
178
+ const lastNotifyModeRef = React.useRef<SidebarMode | null>(null);
179
+ React.useEffect(() => {
180
+ // notify new API when uncontrolled; skip first run to avoid masking init
181
+ if (typeof state === 'undefined') {
182
+ if (lastNotifyModeRef.current === null) {
183
+ lastNotifyModeRef.current = shell.sidebarMode as SidebarMode;
184
+ } else if (lastNotifyModeRef.current !== shell.sidebarMode) {
185
+ lastNotifyModeRef.current = shell.sidebarMode as SidebarMode;
186
+ onStateChange?.(shell.sidebarMode as SidebarMode, { reason: 'toggle' });
210
187
  }
211
- }, [shell.sidebarMode, state, onStateChange]);
188
+ }
189
+ }, [shell.sidebarMode, state, onStateChange]);
212
190
 
213
- // Emit expand/collapse events
214
- React.useEffect(() => {
215
- if (shell.sidebarMode === 'expanded') {
216
- onExpand?.();
217
- } else {
218
- onCollapse?.();
219
- }
220
- }, [shell.sidebarMode, onExpand, onCollapse]);
191
+ // Emit expand/collapse events
192
+ React.useEffect(() => {
193
+ if (shell.sidebarMode === 'expanded') {
194
+ onExpand?.();
195
+ } else {
196
+ onCollapse?.();
197
+ }
198
+ }, [shell.sidebarMode, onExpand, onCollapse]);
221
199
 
222
- // Option A: thin is width-only; content remains visible whenever not collapsed
223
- const isContentVisible = shell.sidebarMode !== 'collapsed';
200
+ // Option A: thin is width-only; content remains visible whenever not collapsed
201
+ const isContentVisible = shell.sidebarMode !== 'collapsed';
224
202
 
225
- // Default persistence if paneId provided and none supplied (fixed only)
226
- const persistenceAdapter = React.useMemo(() => {
227
- if (!paneId || persistence) return persistence;
228
- const key = `kookie-ui:shell:sidebar:${paneId}`;
229
- const adapter: PaneSizePersistence = {
230
- load: () => {
231
- if (typeof window === 'undefined') return undefined;
203
+ // Default persistence if paneId provided and none supplied (fixed only)
204
+ const persistenceAdapter = React.useMemo(() => {
205
+ if (!paneId || persistence) return persistence;
206
+ const key = `kookie-ui:shell:sidebar:${paneId}`;
207
+ const adapter: PaneSizePersistence = {
208
+ load: () => {
209
+ if (typeof window === 'undefined') return undefined;
210
+ try {
232
211
  const v = window.localStorage.getItem(key);
233
212
  return v ? Number(v) : undefined;
234
- },
235
- save: (size: number) => {
236
- if (typeof window === 'undefined') return;
213
+ } catch (err) {
214
+ if (process.env.NODE_ENV !== 'production') {
215
+ console.warn('Shell.Sidebar: failed to load persisted size', err);
216
+ }
217
+ return undefined;
218
+ }
219
+ },
220
+ save: (size: number) => {
221
+ if (typeof window === 'undefined') return;
222
+ try {
237
223
  window.localStorage.setItem(key, String(size));
238
- },
239
- };
240
- return adapter;
241
- }, [paneId, persistence]);
242
-
243
- React.useEffect(() => {
244
- let mounted = true;
245
- (async () => {
246
- if (!resizable || !persistenceAdapter?.load || isOverlay) return;
247
- const loaded = await persistenceAdapter.load();
248
- if (mounted && typeof loaded === 'number' && localRef.current) {
249
- localRef.current.style.setProperty('--sidebar-size', `${loaded}px`);
250
- onResize?.(loaded);
224
+ } catch (err) {
225
+ if (process.env.NODE_ENV !== 'production') {
226
+ console.warn('Shell.Sidebar: failed to save persisted size', err);
227
+ }
251
228
  }
252
- })();
253
- return () => {
254
- mounted = false;
255
- };
256
- }, [resizable, persistenceAdapter, onResize, isOverlay]);
229
+ },
230
+ };
231
+ return adapter;
232
+ }, [paneId, persistence]);
257
233
 
258
- // Register custom toggle behavior based on toggleModes (both|single)
259
- const shellForToggle = useShell();
260
- const resolveDefaultSidebarMode = React.useCallback((): SidebarMode => {
261
- const resolved = defaultState ?? 'expanded';
262
- return resolved === 'thin' || resolved === 'expanded' ? resolved : 'expanded';
263
- }, [defaultState]);
234
+ React.useEffect(() => {
235
+ let mounted = true;
236
+ (async () => {
237
+ if (!resizable || !persistenceAdapter?.load || isOverlay) return;
238
+ const loaded = await persistenceAdapter.load();
239
+ if (mounted && typeof loaded === 'number' && localRef.current) {
240
+ localRef.current.style.setProperty('--sidebar-size', `${loaded}px`);
241
+ onResize?.(loaded);
242
+ }
243
+ })();
244
+ return () => {
245
+ mounted = false;
246
+ };
247
+ }, [resizable, persistenceAdapter, onResize, isOverlay]);
264
248
 
265
- React.useEffect(() => {
266
- if (!shellForToggle.setSidebarToggleComputer) return;
267
- const strategy: 'both' | 'single' = toggleModes ?? 'both';
268
- const compute = (current: SidebarMode): SidebarMode => {
269
- if (strategy === 'both') {
270
- if (current === 'collapsed') return 'thin';
271
- if (current === 'thin') return 'expanded';
272
- return 'collapsed';
273
- }
274
- const target = resolveDefaultSidebarMode();
275
- if (current === 'collapsed') return target;
276
- if (current === target) return 'collapsed';
277
- return target;
278
- };
279
- shellForToggle.setSidebarToggleComputer(compute);
280
- return () => {
281
- shellForToggle.setSidebarToggleComputer?.((cur) => (cur === 'collapsed' ? 'thin' : cur === 'thin' ? 'expanded' : 'collapsed'));
282
- };
283
- }, [shellForToggle, toggleModes, resolveDefaultSidebarMode]);
249
+ // Register custom toggle behavior based on toggleModes (both|single)
250
+ const shellForToggle = useShell();
251
+ const resolveDefaultSidebarMode = React.useCallback((): SidebarMode => {
252
+ const resolved = resolvedSidebarDefault ?? (typeof defaultState === 'string' ? defaultState : undefined) ?? 'expanded';
253
+ return resolved === 'thin' || resolved === 'expanded' ? resolved : 'expanded';
254
+ }, [resolvedSidebarDefault, defaultState]);
284
255
 
285
- const lastOverlayWidthRef = React.useRef<number>(expandedSize);
286
- const lastOverlayModeRef = React.useRef<SidebarMode>('expanded');
287
- React.useEffect(() => {
288
- if (shell.sidebarMode !== 'collapsed') {
289
- lastOverlayModeRef.current = shell.sidebarMode as SidebarMode;
290
- lastOverlayWidthRef.current = shell.sidebarMode === 'thin' ? thinSize : expandedSize;
256
+ React.useEffect(() => {
257
+ if (!shellForToggle.setSidebarToggleComputer) return;
258
+ const strategy: 'both' | 'single' = toggleModes ?? 'both';
259
+ const compute = (current: SidebarMode): SidebarMode => {
260
+ if (strategy === 'both') {
261
+ if (current === 'collapsed') return 'thin';
262
+ if (current === 'thin') return 'expanded';
263
+ return 'collapsed';
291
264
  }
292
- }, [shell.sidebarMode, thinSize, expandedSize]);
265
+ const target = resolveDefaultSidebarMode();
266
+ if (current === 'collapsed') return target;
267
+ if (current === target) return 'collapsed';
268
+ return target;
269
+ };
270
+ shellForToggle.setSidebarToggleComputer(compute);
271
+ return () => {
272
+ shellForToggle.setSidebarToggleComputer?.((cur) => (cur === 'collapsed' ? 'thin' : cur === 'thin' ? 'expanded' : 'collapsed'));
273
+ };
274
+ }, [shellForToggle, toggleModes, resolveDefaultSidebarMode]);
293
275
 
294
- // Remove responsive default mode behavior entirely
295
-
296
- const handleEl =
297
- resizable && !isOverlay && shell.sidebarMode === 'expanded' ? (
298
- <PaneResizeContext.Provider
299
- value={{
300
- containerRef: localRef,
301
- cssVarName: '--sidebar-size',
302
- minSize,
303
- maxSize,
304
- defaultSize: expandedSize,
305
- orientation: 'vertical',
306
- edge: 'end',
307
- computeNext: (client, startClient, startSize) => {
308
- const isRtl = getComputedStyle(localRef.current!).direction === 'rtl';
309
- const delta = client - startClient;
310
- return startSize + (isRtl ? -delta : delta);
311
- },
312
- onResize,
313
- onResizeStart,
314
- onResizeEnd: (size) => {
315
- onResizeEnd?.(size);
316
- emitSizeChange(size, { reason: 'resize' });
317
- persistenceAdapter?.save?.(size);
318
- },
319
- target: 'sidebar',
320
- collapsible,
321
- snapPoints,
322
- snapTolerance: snapTolerance ?? 8,
323
- collapseThreshold,
324
- requestCollapse: () => shell.setSidebarMode('collapsed'),
325
- requestToggle: () => shell.togglePane('sidebar'),
326
- }}
327
- >
328
- {handleChildren.length > 0 ? handleChildren.map((el, i) => React.cloneElement(el, { key: el.key ?? i })) : <PaneHandle />}
329
- </PaneResizeContext.Provider>
330
- ) : null;
276
+ const lastOverlayWidthRef = React.useRef<number>(expandedSize);
277
+ const lastOverlayModeRef = React.useRef<SidebarMode>('expanded');
278
+ React.useEffect(() => {
279
+ if (shell.sidebarMode !== 'collapsed') {
280
+ lastOverlayModeRef.current = shell.sidebarMode as SidebarMode;
281
+ lastOverlayWidthRef.current = shell.sidebarMode === 'thin' ? thinSize : expandedSize;
282
+ }
283
+ }, [shell.sidebarMode, thinSize, expandedSize]);
331
284
 
332
- // Strip new API props from DOM
333
- const { state: _s, defaultState: _ds, onStateChange: _osc, size: _sz, defaultSize: _dsz, onSizeChange: _onsc, sizeUpdate: _szu, sizeUpdateMs: _szums, ...domProps } = props as any;
285
+ // Remove responsive default mode behavior entirely
334
286
 
335
- // Normalize CSS lengths to px
336
- const normalizeToPx = React.useCallback((value: number | string | undefined): number | undefined => {
337
- if (value == null) return undefined;
338
- if (typeof value === 'number' && Number.isFinite(value)) return value;
339
- const str = String(value).trim();
340
- if (!str) return undefined;
341
- if (str.endsWith('px')) return Number.parseFloat(str);
342
- if (str.endsWith('rem')) {
343
- const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize || '16') || 16;
344
- return Number.parseFloat(str) * rem;
345
- }
346
- if (str.endsWith('%')) {
347
- const pct = Number.parseFloat(str);
348
- const base = document.documentElement.clientWidth || window.innerWidth || 0;
349
- return (pct / 100) * base;
350
- }
351
- const n = Number.parseFloat(str);
352
- return Number.isFinite(n) ? n : undefined;
353
- }, []);
287
+ const handleEl =
288
+ resizable && !isOverlay && shell.sidebarMode === 'expanded' ? (
289
+ <PaneResizeContext.Provider
290
+ value={{
291
+ containerRef: localRef,
292
+ cssVarName: '--sidebar-size',
293
+ minSize,
294
+ maxSize,
295
+ defaultSize: expandedSize,
296
+ orientation: 'vertical',
297
+ edge: 'end',
298
+ computeNext: (client, startClient, startSize) => {
299
+ const isRtl = getComputedStyle(localRef.current!).direction === 'rtl';
300
+ const delta = client - startClient;
301
+ return startSize + (isRtl ? -delta : delta);
302
+ },
303
+ onResize,
304
+ onResizeStart,
305
+ onResizeEnd: (size) => {
306
+ onResizeEnd?.(size);
307
+ emitSizeChange(size, { reason: 'resize' });
308
+ persistenceAdapter?.save?.(size);
309
+ },
310
+ target: 'sidebar',
311
+ collapsible,
312
+ snapPoints,
313
+ snapTolerance: snapTolerance ?? 8,
314
+ collapseThreshold,
315
+ requestCollapse: () => shell.setSidebarMode('collapsed'),
316
+ requestToggle: () => shell.togglePane('sidebar'),
317
+ }}
318
+ >
319
+ {handleChildren.length > 0 ? handleChildren.map((el, i) => React.cloneElement(el, { key: el.key ?? i })) : <PaneHandle />}
320
+ </PaneResizeContext.Provider>
321
+ ) : null;
354
322
 
355
- // Apply defaultSize on mount when uncontrolled
356
- React.useEffect(() => {
357
- if (!localRef.current) return;
358
- const { size, defaultSize } = props as any;
359
- if (typeof size === 'undefined' && typeof defaultSize !== 'undefined') {
360
- const px = normalizeToPx(defaultSize);
361
- if (typeof px === 'number' && Number.isFinite(px)) {
362
- const minPx = typeof minSize === 'number' ? minSize : undefined;
363
- const maxPx = typeof maxSize === 'number' ? maxSize : undefined;
364
- const clamped = Math.min(maxPx ?? px, Math.max(minPx ?? px, px));
365
- localRef.current.style.setProperty('--sidebar-size', `${clamped}px`);
366
- emitSizeChange(clamped, { reason: 'init' });
367
- }
368
- }
369
- // eslint-disable-next-line react-hooks/exhaustive-deps
370
- }, []);
323
+ // Normalize CSS lengths to px
324
+ const normalizeToPx = React.useCallback((value: number | string | undefined): number | undefined => {
325
+ if (value == null) return undefined;
326
+ if (typeof value === 'number' && Number.isFinite(value)) return value;
327
+ const str = String(value).trim();
328
+ if (!str) return undefined;
329
+ if (str.endsWith('px')) return Number.parseFloat(str);
330
+ if (str.endsWith('rem')) {
331
+ const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize || '16') || 16;
332
+ return Number.parseFloat(str) * rem;
333
+ }
334
+ if (str.endsWith('%')) {
335
+ const pct = Number.parseFloat(str);
336
+ const base = document.documentElement.clientWidth || window.innerWidth || 0;
337
+ return (pct / 100) * base;
338
+ }
339
+ const n = Number.parseFloat(str);
340
+ return Number.isFinite(n) ? n : undefined;
341
+ }, []);
371
342
 
372
- // Controlled size sync
373
- const controlledSize = (props as any).size;
374
- React.useEffect(() => {
375
- if (!localRef.current) return;
376
- if (typeof controlledSize === 'undefined') return;
377
- const px = normalizeToPx(controlledSize);
343
+ // Apply defaultSize on mount when uncontrolled
344
+ React.useEffect(() => {
345
+ if (!localRef.current) return;
346
+ if (typeof size === 'undefined' && typeof defaultSize !== 'undefined') {
347
+ const px = normalizeToPx(defaultSize);
378
348
  if (typeof px === 'number' && Number.isFinite(px)) {
379
349
  const minPx = typeof minSize === 'number' ? minSize : undefined;
380
350
  const maxPx = typeof maxSize === 'number' ? maxSize : undefined;
381
351
  const clamped = Math.min(maxPx ?? px, Math.max(minPx ?? px, px));
382
352
  localRef.current.style.setProperty('--sidebar-size', `${clamped}px`);
383
- emitSizeChange(clamped, { reason: 'controlled' });
353
+ emitSizeChange(clamped, { reason: 'init' });
384
354
  }
385
- }, [controlledSize, minSize, maxSize, normalizeToPx, emitSizeChange]);
355
+ }
356
+ // eslint-disable-next-line react-hooks/exhaustive-deps
357
+ }, []);
386
358
 
387
- if (isOverlay) {
388
- const open = shell.sidebarMode !== 'collapsed';
389
- return (
390
- <Sheet.Root open={open} onOpenChange={(o) => shell.setSidebarMode(o ? 'expanded' : 'collapsed')}>
391
- <Sheet.Content
392
- side="start"
393
- style={{ padding: 0 }}
394
- width={{
395
- initial: `${open ? (shell.sidebarMode === 'thin' ? thinSize : expandedSize) : lastOverlayWidthRef.current}px`,
396
- }}
397
- >
398
- <VisuallyHidden>
399
- <Sheet.Title>Navigation</Sheet.Title>
400
- </VisuallyHidden>
401
- {contentChildren}
402
- </Sheet.Content>
403
- </Sheet.Root>
404
- );
359
+ // Controlled size sync
360
+ const controlledSize = size;
361
+ React.useEffect(() => {
362
+ if (!localRef.current) return;
363
+ if (typeof controlledSize === 'undefined') return;
364
+ const px = normalizeToPx(controlledSize);
365
+ if (typeof px === 'number' && Number.isFinite(px)) {
366
+ const minPx = typeof minSize === 'number' ? minSize : undefined;
367
+ const maxPx = typeof maxSize === 'number' ? maxSize : undefined;
368
+ const clamped = Math.min(maxPx ?? px, Math.max(minPx ?? px, px));
369
+ localRef.current.style.setProperty('--sidebar-size', `${clamped}px`);
370
+ emitSizeChange(clamped, { reason: 'controlled' });
405
371
  }
372
+ }, [controlledSize, minSize, maxSize, normalizeToPx, emitSizeChange]);
373
+
374
+ if (isOverlay) {
375
+ const open = shell.sidebarMode !== 'collapsed';
406
376
  return (
407
- <div
408
- {...domProps}
409
- ref={setRef}
410
- className={classNames('rt-ShellSidebar', className)}
411
- data-mode={shell.sidebarMode}
412
- data-peek={shell.peekTarget === 'sidebar' || undefined}
413
- data-presentation={shell.currentBreakpointReady ? resolvedPresentation : undefined}
414
- data-open={(shell.currentBreakpointReady && isStacked && isContentVisible) || undefined}
415
- style={{
416
- ...style,
417
- ['--sidebar-size' as any]: `${expandedSize}px`,
418
- ['--sidebar-thin-size' as any]: `${thinSize}px`,
419
- ['--sidebar-min-size' as any]: `${minSize}px`,
420
- ['--sidebar-max-size' as any]: `${maxSize}px`,
421
- ...(shell.peekTarget === 'sidebar' && shell.sidebarMode === 'collapsed' && !isOverlay
422
- ? (() => {
423
- const strategy: 'both' | 'single' = toggleModes ?? 'both';
424
- const current = shell.sidebarMode as SidebarMode;
425
- let next: SidebarMode;
426
- if (strategy === 'both') {
427
- next = current === 'collapsed' ? 'thin' : current === 'thin' ? 'expanded' : 'collapsed';
428
- } else {
429
- const target = resolveDefaultSidebarMode();
430
- next = current === 'collapsed' ? target : 'collapsed';
431
- }
432
- if (next === 'thin') {
433
- return {
434
- ['--peek-sidebar-width' as any]: `${thinSize}px`,
435
- } as React.CSSProperties;
436
- }
377
+ <Sheet.Root open={open} onOpenChange={(o) => shell.setSidebarMode(o ? 'expanded' : 'collapsed')}>
378
+ <Sheet.Content
379
+ side="start"
380
+ style={{ padding: 0 }}
381
+ width={{
382
+ initial: `${open ? (shell.sidebarMode === 'thin' ? thinSize : expandedSize) : lastOverlayWidthRef.current}px`,
383
+ }}
384
+ >
385
+ <VisuallyHidden>
386
+ <Sheet.Title>Navigation</Sheet.Title>
387
+ </VisuallyHidden>
388
+ {contentChildren}
389
+ </Sheet.Content>
390
+ </Sheet.Root>
391
+ );
392
+ }
393
+ return (
394
+ <div
395
+ {...sidebarDomProps}
396
+ ref={setRef}
397
+ className={classNames('rt-ShellSidebar', className)}
398
+ data-mode={shell.sidebarMode}
399
+ data-peek={shell.peekTarget === 'sidebar' || undefined}
400
+ data-presentation={shell.currentBreakpointReady ? resolvedPresentation : undefined}
401
+ data-open={(shell.currentBreakpointReady && isStacked && isContentVisible) || undefined}
402
+ style={{
403
+ ...style,
404
+ ['--sidebar-size' as any]: `${expandedSize}px`,
405
+ ['--sidebar-thin-size' as any]: `${thinSize}px`,
406
+ ['--sidebar-min-size' as any]: `${minSize}px`,
407
+ ['--sidebar-max-size' as any]: `${maxSize}px`,
408
+ ...(shell.peekTarget === 'sidebar' && shell.sidebarMode === 'collapsed' && !isOverlay
409
+ ? (() => {
410
+ const strategy: 'both' | 'single' = toggleModes ?? 'both';
411
+ const current = shell.sidebarMode as SidebarMode;
412
+ let next: SidebarMode;
413
+ if (strategy === 'both') {
414
+ next = current === 'collapsed' ? 'thin' : current === 'thin' ? 'expanded' : 'collapsed';
415
+ } else {
416
+ const target = resolveDefaultSidebarMode();
417
+ next = current === 'collapsed' ? target : 'collapsed';
418
+ }
419
+ if (next === 'thin') {
437
420
  return {
438
- ['--peek-sidebar-width' as any]: `var(--sidebar-size, ${expandedSize}px)`,
421
+ ['--peek-sidebar-width' as any]: `${thinSize}px`,
439
422
  } as React.CSSProperties;
440
- })()
441
- : {}),
442
- }}
443
- >
444
- <div className="rt-ShellSidebarContent" data-visible={isContentVisible || undefined}>
445
- {contentChildren}
446
- </div>
447
- {handleEl}
423
+ }
424
+ return {
425
+ ['--peek-sidebar-width' as any]: `var(--sidebar-size, ${expandedSize}px)`,
426
+ } as React.CSSProperties;
427
+ })()
428
+ : {}),
429
+ }}
430
+ >
431
+ <div className="rt-ShellSidebarContent" data-visible={isContentVisible || undefined}>
432
+ {contentChildren}
448
433
  </div>
449
- );
450
- },
451
- ) as SidebarComponent;
434
+ {handleEl}
435
+ </div>
436
+ );
437
+ }) as SidebarComponent;
452
438
 
453
439
  Sidebar.displayName = 'Shell.Sidebar';
454
440
  Sidebar.Handle = SidebarHandle;