@kushagradhawan/kookie-ui 0.1.50 → 0.1.52
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.
- package/components.css +582 -116
- package/dist/cjs/components/_internal/shell-bottom.d.ts +31 -5
- package/dist/cjs/components/_internal/shell-bottom.d.ts.map +1 -1
- package/dist/cjs/components/_internal/shell-bottom.js +1 -1
- package/dist/cjs/components/_internal/shell-bottom.js.map +3 -3
- package/dist/cjs/components/_internal/shell-handles.d.ts.map +1 -1
- package/dist/cjs/components/_internal/shell-handles.js +1 -1
- package/dist/cjs/components/_internal/shell-handles.js.map +3 -3
- package/dist/cjs/components/_internal/shell-inspector.d.ts +23 -5
- package/dist/cjs/components/_internal/shell-inspector.d.ts.map +1 -1
- package/dist/cjs/components/_internal/shell-inspector.js +1 -1
- package/dist/cjs/components/_internal/shell-inspector.js.map +3 -3
- package/dist/cjs/components/_internal/shell-sidebar.d.ts +24 -6
- package/dist/cjs/components/_internal/shell-sidebar.d.ts.map +1 -1
- package/dist/cjs/components/_internal/shell-sidebar.js +1 -1
- package/dist/cjs/components/_internal/shell-sidebar.js.map +3 -3
- package/dist/cjs/components/chatbar.d.ts +21 -2
- package/dist/cjs/components/chatbar.d.ts.map +1 -1
- package/dist/cjs/components/chatbar.js +1 -1
- package/dist/cjs/components/chatbar.js.map +3 -3
- package/dist/cjs/components/shell.context.d.ts +88 -1
- package/dist/cjs/components/shell.context.d.ts.map +1 -1
- package/dist/cjs/components/shell.context.js +1 -1
- package/dist/cjs/components/shell.context.js.map +3 -3
- package/dist/cjs/components/shell.d.ts +51 -13
- package/dist/cjs/components/shell.d.ts.map +1 -1
- package/dist/cjs/components/shell.hooks.d.ts +7 -1
- package/dist/cjs/components/shell.hooks.d.ts.map +1 -1
- package/dist/cjs/components/shell.hooks.js +1 -1
- package/dist/cjs/components/shell.hooks.js.map +3 -3
- package/dist/cjs/components/shell.js +1 -1
- package/dist/cjs/components/shell.js.map +3 -3
- package/dist/cjs/components/shell.types.d.ts +1 -0
- package/dist/cjs/components/shell.types.d.ts.map +1 -1
- package/dist/cjs/components/shell.types.js +1 -1
- package/dist/cjs/components/shell.types.js.map +2 -2
- package/dist/esm/components/_internal/shell-bottom.d.ts +31 -5
- package/dist/esm/components/_internal/shell-bottom.d.ts.map +1 -1
- package/dist/esm/components/_internal/shell-bottom.js +1 -1
- package/dist/esm/components/_internal/shell-bottom.js.map +3 -3
- package/dist/esm/components/_internal/shell-handles.d.ts.map +1 -1
- package/dist/esm/components/_internal/shell-handles.js +1 -1
- package/dist/esm/components/_internal/shell-handles.js.map +3 -3
- package/dist/esm/components/_internal/shell-inspector.d.ts +23 -5
- package/dist/esm/components/_internal/shell-inspector.d.ts.map +1 -1
- package/dist/esm/components/_internal/shell-inspector.js +1 -1
- package/dist/esm/components/_internal/shell-inspector.js.map +3 -3
- package/dist/esm/components/_internal/shell-sidebar.d.ts +24 -6
- package/dist/esm/components/_internal/shell-sidebar.d.ts.map +1 -1
- package/dist/esm/components/_internal/shell-sidebar.js +1 -1
- package/dist/esm/components/_internal/shell-sidebar.js.map +3 -3
- package/dist/esm/components/chatbar.d.ts +21 -2
- package/dist/esm/components/chatbar.d.ts.map +1 -1
- package/dist/esm/components/chatbar.js +1 -1
- package/dist/esm/components/chatbar.js.map +3 -3
- package/dist/esm/components/shell.context.d.ts +88 -1
- package/dist/esm/components/shell.context.d.ts.map +1 -1
- package/dist/esm/components/shell.context.js +1 -1
- package/dist/esm/components/shell.context.js.map +3 -3
- package/dist/esm/components/shell.d.ts +51 -13
- package/dist/esm/components/shell.d.ts.map +1 -1
- package/dist/esm/components/shell.hooks.d.ts +7 -1
- package/dist/esm/components/shell.hooks.d.ts.map +1 -1
- package/dist/esm/components/shell.hooks.js +1 -1
- package/dist/esm/components/shell.hooks.js.map +3 -3
- package/dist/esm/components/shell.js +1 -1
- package/dist/esm/components/shell.js.map +3 -3
- package/dist/esm/components/shell.types.d.ts +1 -0
- package/dist/esm/components/shell.types.d.ts.map +1 -1
- package/dist/esm/components/shell.types.js.map +2 -2
- package/package.json +14 -3
- package/schemas/base-button.json +1 -1
- package/schemas/button.json +1 -1
- package/schemas/icon-button.json +1 -1
- package/schemas/index.json +6 -6
- package/schemas/toggle-button.json +1 -1
- package/schemas/toggle-icon-button.json +1 -1
- package/src/components/_internal/base-menu.css +16 -16
- package/src/components/_internal/base-sidebar-menu.css +23 -20
- package/src/components/_internal/base-sidebar.css +13 -0
- package/src/components/_internal/shell-bottom.tsx +176 -49
- package/src/components/_internal/shell-handles.tsx +29 -4
- package/src/components/_internal/shell-inspector.tsx +175 -43
- package/src/components/_internal/shell-sidebar.tsx +177 -93
- package/src/components/chatbar.css +240 -21
- package/src/components/chatbar.tsx +280 -291
- package/src/components/sheet.css +8 -16
- package/src/components/shell.context.tsx +79 -3
- package/src/components/shell.css +0 -1
- package/src/components/shell.hooks.ts +35 -0
- package/src/components/shell.tsx +574 -235
- package/src/components/shell.types.ts +2 -0
- package/src/components/sidebar.css +2 -2
- package/styles.css +582 -116
|
@@ -3,10 +3,10 @@ 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 } from '../shell.hooks.js';
|
|
6
|
+
import { useResponsivePresentation, useResponsiveValue } from '../shell.hooks.js';
|
|
7
7
|
import { PaneResizeContext } from './shell-resize.js';
|
|
8
8
|
import { SidebarHandle, PaneHandle } from './shell-handles.js';
|
|
9
|
-
import type { Breakpoint, PaneMode, PaneSizePersistence, PresentationValue, ResponsivePresentation, ResponsiveSidebarMode, SidebarMode } from '../shell.types.js';
|
|
9
|
+
import type { Breakpoint, PaneMode, PaneSizePersistence, PresentationValue, ResponsivePresentation, ResponsiveSidebarMode, SidebarMode, Responsive } from '../shell.types.js';
|
|
10
10
|
import { BREAKPOINTS } from '../shell.types.js';
|
|
11
11
|
|
|
12
12
|
interface PaneProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
@@ -32,33 +32,29 @@ interface PaneProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
|
32
32
|
persistence?: PaneSizePersistence;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
type
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
type SidebarStateChangeMeta = { reason: 'init' | 'toggle' | 'responsive' };
|
|
36
|
+
type SidebarControlledProps = { state: Responsive<SidebarMode>; onStateChange?: (state: SidebarMode, meta: SidebarStateChangeMeta) => void; defaultState?: never };
|
|
37
|
+
type SidebarUncontrolledProps = { defaultState?: SidebarMode | Partial<Record<Breakpoint, SidebarMode>>; onStateChange?: (state: SidebarMode, meta: SidebarStateChangeMeta) => void; state?: never };
|
|
38
|
+
type SidebarPublicProps = Omit<PaneProps, 'mode' | 'defaultMode' | 'onModeChange'> & {
|
|
39
|
+
// removed legacy mode props
|
|
40
|
+
thinSize?: number;
|
|
41
|
+
toggleModes?: 'both' | 'single';
|
|
42
|
+
// size API (width when expanded)
|
|
43
|
+
size?: number | string;
|
|
44
|
+
defaultSize?: number | string;
|
|
45
|
+
onSizeChange?: (size: number, meta: { reason: 'init' | 'resize' | 'controlled' }) => void;
|
|
46
|
+
sizeUpdate?: 'throttle' | 'debounce';
|
|
47
|
+
sizeUpdateMs?: number;
|
|
48
|
+
} & (SidebarControlledProps | SidebarUncontrolledProps);
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
mode?: SidebarMode;
|
|
49
|
-
defaultMode?: ResponsiveSidebarMode;
|
|
50
|
-
onModeChange?: (mode: SidebarMode) => void;
|
|
51
|
-
thinSize?: number;
|
|
52
|
-
toggleModes?: 'both' | 'single';
|
|
53
|
-
}
|
|
54
|
-
>(
|
|
50
|
+
type SidebarComponent = React.ForwardRefExoticComponent<SidebarPublicProps & React.RefAttributes<HTMLDivElement>> & { Handle: typeof SidebarHandle };
|
|
51
|
+
|
|
52
|
+
export const Sidebar = React.forwardRef<HTMLDivElement, SidebarPublicProps>(
|
|
55
53
|
(
|
|
56
54
|
{
|
|
57
55
|
className,
|
|
58
56
|
presentation = { initial: 'overlay', md: 'fixed' },
|
|
59
|
-
|
|
60
|
-
defaultMode = 'expanded',
|
|
61
|
-
onModeChange,
|
|
57
|
+
// removed legacy props
|
|
62
58
|
expandedSize = 288,
|
|
63
59
|
minSize = 200,
|
|
64
60
|
maxSize = 400,
|
|
@@ -78,6 +74,10 @@ export const Sidebar = React.forwardRef<
|
|
|
78
74
|
style,
|
|
79
75
|
thinSize = 64,
|
|
80
76
|
toggleModes,
|
|
77
|
+
// new state props (XOR)
|
|
78
|
+
state,
|
|
79
|
+
defaultState,
|
|
80
|
+
onStateChange,
|
|
81
81
|
...props
|
|
82
82
|
},
|
|
83
83
|
ref,
|
|
@@ -86,29 +86,7 @@ export const Sidebar = React.forwardRef<
|
|
|
86
86
|
const resolvedPresentation = useResponsivePresentation(presentation);
|
|
87
87
|
const isOverlay = resolvedPresentation === 'overlay';
|
|
88
88
|
const isStacked = resolvedPresentation === 'stacked';
|
|
89
|
-
//
|
|
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]);
|
|
89
|
+
// Phase sequencing is now CSS-driven; no JS-managed phase
|
|
112
90
|
const localRef = React.useRef<HTMLDivElement | null>(null);
|
|
113
91
|
const setRef = React.useCallback(
|
|
114
92
|
(node: HTMLDivElement | null) => {
|
|
@@ -122,6 +100,34 @@ export const Sidebar = React.forwardRef<
|
|
|
122
100
|
const handleChildren = childArray.filter((el: React.ReactElement) => React.isValidElement(el) && el.type === SidebarHandle);
|
|
123
101
|
const contentChildren = childArray.filter((el: React.ReactElement) => !(React.isValidElement(el) && el.type === SidebarHandle));
|
|
124
102
|
|
|
103
|
+
// Throttled/debounced emitter for onSizeChange
|
|
104
|
+
const emitSizeChange = React.useMemo(() => {
|
|
105
|
+
const cb = (props as any).onSizeChange as undefined | ((s: number, meta: { reason: 'init' | 'resize' | 'controlled' }) => void);
|
|
106
|
+
const strategy = (props as any).sizeUpdate as undefined | 'throttle' | 'debounce';
|
|
107
|
+
const ms = (props as any).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);
|
|
116
|
+
};
|
|
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
|
+
}, [(props as any).onSizeChange, (props as any).sizeUpdate, (props as any).sizeUpdateMs]);
|
|
130
|
+
|
|
125
131
|
// Register with shell
|
|
126
132
|
const sidebarId = React.useId();
|
|
127
133
|
React.useEffect(() => {
|
|
@@ -131,30 +137,78 @@ export const Sidebar = React.forwardRef<
|
|
|
131
137
|
};
|
|
132
138
|
}, [shell, sidebarId]);
|
|
133
139
|
|
|
134
|
-
//
|
|
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
|
+
// eslint-disable-next-line no-console
|
|
145
|
+
console.error('Shell.Sidebar: Do not pass both `state` and `defaultState`. Choose one.');
|
|
146
|
+
}
|
|
147
|
+
if (typeof (props as any).size !== 'undefined' && typeof (props as any).defaultSize !== 'undefined') {
|
|
148
|
+
// eslint-disable-next-line no-console
|
|
149
|
+
console.error('Shell.Sidebar: Do not pass both `size` and `defaultSize`. Choose one.');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Warn on mode switch between controlled/uncontrolled
|
|
154
|
+
React.useEffect(() => {
|
|
155
|
+
const isControlled = typeof state !== 'undefined';
|
|
156
|
+
if (wasControlledRef.current === null) {
|
|
157
|
+
wasControlledRef.current = isControlled;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (wasControlledRef.current !== isControlled) {
|
|
161
|
+
// eslint-disable-next-line no-console
|
|
162
|
+
console.warn('Shell.Sidebar: Switching between controlled and uncontrolled `state` is not supported.');
|
|
163
|
+
wasControlledRef.current = isControlled;
|
|
164
|
+
}
|
|
165
|
+
}, [state]);
|
|
166
|
+
|
|
167
|
+
// Resolve responsive controlled state at top level
|
|
168
|
+
const resolvedState = useResponsiveValue(state);
|
|
169
|
+
const resolvedDefaultState = useResponsiveValue(defaultState as any);
|
|
170
|
+
|
|
171
|
+
// Honor state/defaultState on mount when uncontrolled
|
|
135
172
|
const didInitRef = React.useRef(false);
|
|
136
173
|
React.useEffect(() => {
|
|
137
174
|
if (didInitRef.current) return;
|
|
175
|
+
if (!shell.currentBreakpointReady) return;
|
|
138
176
|
didInitRef.current = true;
|
|
139
|
-
|
|
140
|
-
|
|
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;
|
|
141
189
|
}
|
|
142
190
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
143
|
-
}, []);
|
|
191
|
+
}, [shell.currentBreakpointReady, resolvedDefaultState, resolvedState, state, defaultState]);
|
|
144
192
|
|
|
145
|
-
// Sync controlled
|
|
193
|
+
// Sync controlled state (responsive-aware)
|
|
146
194
|
React.useEffect(() => {
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}, [mode, shell]);
|
|
195
|
+
if (resolvedState === undefined) return;
|
|
196
|
+
if (shell.sidebarMode !== resolvedState) shell.setSidebarMode(resolvedState);
|
|
197
|
+
}, [resolvedState, shell.sidebarMode]);
|
|
151
198
|
|
|
152
199
|
// Emit mode changes
|
|
200
|
+
const lastNotifyModeRef = React.useRef<SidebarMode | null>(null);
|
|
153
201
|
React.useEffect(() => {
|
|
154
|
-
|
|
155
|
-
|
|
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
|
+
}
|
|
156
210
|
}
|
|
157
|
-
}, [shell.sidebarMode,
|
|
211
|
+
}, [shell.sidebarMode, state, onStateChange]);
|
|
158
212
|
|
|
159
213
|
// Emit expand/collapse events
|
|
160
214
|
React.useEffect(() => {
|
|
@@ -201,29 +255,12 @@ export const Sidebar = React.forwardRef<
|
|
|
201
255
|
};
|
|
202
256
|
}, [resizable, persistenceAdapter, onResize, isOverlay]);
|
|
203
257
|
|
|
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
258
|
// Register custom toggle behavior based on toggleModes (both|single)
|
|
222
259
|
const shellForToggle = useShell();
|
|
223
260
|
const resolveDefaultSidebarMode = React.useCallback((): SidebarMode => {
|
|
224
|
-
const resolved =
|
|
261
|
+
const resolved = defaultState ?? 'expanded';
|
|
225
262
|
return resolved === 'thin' || resolved === 'expanded' ? resolved : 'expanded';
|
|
226
|
-
}, [
|
|
263
|
+
}, [defaultState]);
|
|
227
264
|
|
|
228
265
|
React.useEffect(() => {
|
|
229
266
|
if (!shellForToggle.setSidebarToggleComputer) return;
|
|
@@ -254,15 +291,7 @@ export const Sidebar = React.forwardRef<
|
|
|
254
291
|
}
|
|
255
292
|
}, [shell.sidebarMode, thinSize, expandedSize]);
|
|
256
293
|
|
|
257
|
-
|
|
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]);
|
|
294
|
+
// Remove responsive default mode behavior entirely
|
|
266
295
|
|
|
267
296
|
const handleEl =
|
|
268
297
|
resizable && !isOverlay && shell.sidebarMode === 'expanded' ? (
|
|
@@ -284,6 +313,7 @@ export const Sidebar = React.forwardRef<
|
|
|
284
313
|
onResizeStart,
|
|
285
314
|
onResizeEnd: (size) => {
|
|
286
315
|
onResizeEnd?.(size);
|
|
316
|
+
emitSizeChange(size, { reason: 'resize' });
|
|
287
317
|
persistenceAdapter?.save?.(size);
|
|
288
318
|
},
|
|
289
319
|
target: 'sidebar',
|
|
@@ -299,6 +329,61 @@ export const Sidebar = React.forwardRef<
|
|
|
299
329
|
</PaneResizeContext.Provider>
|
|
300
330
|
) : null;
|
|
301
331
|
|
|
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;
|
|
334
|
+
|
|
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
|
+
}, []);
|
|
354
|
+
|
|
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
|
+
}, []);
|
|
371
|
+
|
|
372
|
+
// Controlled size sync
|
|
373
|
+
React.useEffect(() => {
|
|
374
|
+
if (!localRef.current) return;
|
|
375
|
+
const { size } = props as any;
|
|
376
|
+
if (typeof size === 'undefined') return;
|
|
377
|
+
const px = normalizeToPx(size);
|
|
378
|
+
if (typeof px === 'number' && Number.isFinite(px)) {
|
|
379
|
+
const minPx = typeof minSize === 'number' ? minSize : undefined;
|
|
380
|
+
const maxPx = typeof maxSize === 'number' ? maxSize : undefined;
|
|
381
|
+
const clamped = Math.min(maxPx ?? px, Math.max(minPx ?? px, px));
|
|
382
|
+
localRef.current.style.setProperty('--sidebar-size', `${clamped}px`);
|
|
383
|
+
emitSizeChange(clamped, { reason: 'controlled' });
|
|
384
|
+
}
|
|
385
|
+
}, [(props as any).size, minSize, maxSize, normalizeToPx, emitSizeChange]);
|
|
386
|
+
|
|
302
387
|
if (isOverlay) {
|
|
303
388
|
const open = shell.sidebarMode !== 'collapsed';
|
|
304
389
|
return (
|
|
@@ -311,23 +396,22 @@ export const Sidebar = React.forwardRef<
|
|
|
311
396
|
}}
|
|
312
397
|
>
|
|
313
398
|
<VisuallyHidden>
|
|
314
|
-
<Sheet.Title>
|
|
399
|
+
<Sheet.Title>Navigation</Sheet.Title>
|
|
315
400
|
</VisuallyHidden>
|
|
316
401
|
{contentChildren}
|
|
317
402
|
</Sheet.Content>
|
|
318
403
|
</Sheet.Root>
|
|
319
404
|
);
|
|
320
405
|
}
|
|
321
|
-
|
|
322
406
|
return (
|
|
323
407
|
<div
|
|
324
|
-
{...
|
|
408
|
+
{...domProps}
|
|
325
409
|
ref={setRef}
|
|
326
410
|
className={classNames('rt-ShellSidebar', className)}
|
|
327
411
|
data-mode={shell.sidebarMode}
|
|
328
412
|
data-peek={shell.peekTarget === 'sidebar' || undefined}
|
|
329
|
-
data-presentation={resolvedPresentation}
|
|
330
|
-
data-open={(isStacked && isContentVisible) || undefined}
|
|
413
|
+
data-presentation={shell.currentBreakpointReady ? resolvedPresentation : undefined}
|
|
414
|
+
data-open={(shell.currentBreakpointReady && isStacked && isContentVisible) || undefined}
|
|
331
415
|
style={{
|
|
332
416
|
...style,
|
|
333
417
|
['--sidebar-size' as any]: `${expandedSize}px`,
|
|
@@ -357,7 +441,7 @@ export const Sidebar = React.forwardRef<
|
|
|
357
441
|
: {}),
|
|
358
442
|
}}
|
|
359
443
|
>
|
|
360
|
-
<div className="rt-ShellSidebarContent" data-visible={isContentVisible || undefined}
|
|
444
|
+
<div className="rt-ShellSidebarContent" data-visible={isContentVisible || undefined}>
|
|
361
445
|
{contentChildren}
|
|
362
446
|
</div>
|
|
363
447
|
{handleEl}
|