@kushagradhawan/kookie-ui 0.1.72 → 0.1.74
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 +6 -2
- 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.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.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/shell.d.ts.map +1 -1
- package/dist/cjs/components/shell.js +1 -1
- package/dist/cjs/components/shell.js.map +3 -3
- package/dist/cjs/helpers/index.d.ts +1 -0
- package/dist/cjs/helpers/index.d.ts.map +1 -1
- package/dist/cjs/helpers/index.js +1 -1
- package/dist/cjs/helpers/index.js.map +2 -2
- package/dist/cjs/helpers/normalize-to-px.d.ts +10 -0
- package/dist/cjs/helpers/normalize-to-px.d.ts.map +1 -0
- package/dist/cjs/helpers/normalize-to-px.js +2 -0
- package/dist/cjs/helpers/normalize-to-px.js.map +7 -0
- 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.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.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/shell.d.ts.map +1 -1
- package/dist/esm/components/shell.js +1 -1
- package/dist/esm/components/shell.js.map +3 -3
- package/dist/esm/helpers/index.d.ts +1 -0
- package/dist/esm/helpers/index.d.ts.map +1 -1
- package/dist/esm/helpers/index.js +1 -1
- package/dist/esm/helpers/index.js.map +2 -2
- package/dist/esm/helpers/normalize-to-px.d.ts +10 -0
- package/dist/esm/helpers/normalize-to-px.d.ts.map +1 -0
- package/dist/esm/helpers/normalize-to-px.js +2 -0
- package/dist/esm/helpers/normalize-to-px.js.map +7 -0
- package/package.json +1 -1
- 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/shell-bottom.tsx +60 -30
- package/src/components/_internal/shell-handles.tsx +6 -1
- package/src/components/_internal/shell-inspector.tsx +60 -30
- package/src/components/_internal/shell-sidebar.tsx +62 -31
- package/src/components/shell.css +10 -11
- package/src/components/shell.tsx +83 -32
- package/src/helpers/index.ts +1 -0
- package/src/helpers/normalize-to-px.ts +42 -0
- package/styles.css +6 -2
package/src/components/shell.tsx
CHANGED
|
@@ -38,6 +38,7 @@ import { Bottom } from './_internal/shell-bottom.js';
|
|
|
38
38
|
import { Inspector } from './_internal/shell-inspector.js';
|
|
39
39
|
import type { PresentationValue, ResponsivePresentation, PaneMode, SidebarMode, PaneSizePersistence, Breakpoint, PaneTarget, Responsive, PaneBaseProps } from './shell.types.js';
|
|
40
40
|
import { _BREAKPOINTS } from './shell.types.js';
|
|
41
|
+
import { normalizeToPx } from '../helpers/normalize-to-px.js';
|
|
41
42
|
import {
|
|
42
43
|
ShellProvider,
|
|
43
44
|
useShell,
|
|
@@ -265,13 +266,57 @@ const Root = React.forwardRef<HTMLDivElement, ShellRootProps>(({ className, chil
|
|
|
265
266
|
(el) => React.isValidElement(el) && (el as any).type?.displayName === 'Shell.Inspector' && typeof (el as any).props?.open !== 'undefined' && Boolean((el as any).props?.open),
|
|
266
267
|
);
|
|
267
268
|
|
|
269
|
+
// Detect Sidebar initial state from props
|
|
270
|
+
const getSidebarInitialState = (): SidebarMode => {
|
|
271
|
+
const sidebarEl = initialChildren.find((el) => React.isValidElement(el) && (el as any).type?.displayName === 'Shell.Sidebar');
|
|
272
|
+
if (!sidebarEl) return 'expanded';
|
|
273
|
+
const props = (sidebarEl as any).props;
|
|
274
|
+
// Check controlled state first
|
|
275
|
+
if (typeof props?.state !== 'undefined') {
|
|
276
|
+
if (typeof props.state === 'string') return props.state as SidebarMode;
|
|
277
|
+
// Responsive object - use 'initial' breakpoint or first defined value
|
|
278
|
+
if (typeof props.state === 'object') {
|
|
279
|
+
return (props.state.initial ?? Object.values(props.state)[0] ?? 'expanded') as SidebarMode;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// Check defaultState
|
|
283
|
+
if (typeof props?.defaultState !== 'undefined') {
|
|
284
|
+
if (typeof props.defaultState === 'string') return props.defaultState as SidebarMode;
|
|
285
|
+
if (typeof props.defaultState === 'object') {
|
|
286
|
+
return (props.defaultState.initial ?? Object.values(props.defaultState)[0] ?? 'expanded') as SidebarMode;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return 'expanded';
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// Detect Bottom initial state from props
|
|
293
|
+
const getBottomInitialState = (): PaneMode => {
|
|
294
|
+
const bottomEl = initialChildren.find((el) => React.isValidElement(el) && (el as any).type?.displayName === 'Shell.Bottom');
|
|
295
|
+
if (!bottomEl) return 'collapsed';
|
|
296
|
+
const props = (bottomEl as any).props;
|
|
297
|
+
// Check controlled open first
|
|
298
|
+
if (typeof props?.open !== 'undefined') {
|
|
299
|
+
if (typeof props.open === 'boolean') return props.open ? 'expanded' : 'collapsed';
|
|
300
|
+
// Responsive object - use 'initial' breakpoint or first defined value
|
|
301
|
+
if (typeof props.open === 'object') {
|
|
302
|
+
const val = props.open.initial ?? Object.values(props.open)[0];
|
|
303
|
+
return val ? 'expanded' : 'collapsed';
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Check defaultOpen
|
|
307
|
+
if (typeof props?.defaultOpen !== 'undefined') {
|
|
308
|
+
return props.defaultOpen ? 'expanded' : 'collapsed';
|
|
309
|
+
}
|
|
310
|
+
return 'collapsed';
|
|
311
|
+
};
|
|
312
|
+
|
|
268
313
|
// Pane state management via reducer
|
|
269
314
|
const [paneState, dispatchPane] = React.useReducer(paneReducer, {
|
|
270
315
|
leftMode: hasPanelDefaultOpen || hasRailDefaultOpen ? 'expanded' : 'collapsed',
|
|
271
316
|
panelMode: hasPanelDefaultOpen ? 'expanded' : 'collapsed',
|
|
272
|
-
sidebarMode:
|
|
317
|
+
sidebarMode: getSidebarInitialState(),
|
|
273
318
|
inspectorMode: hasInspectorDefaultOpen || hasInspectorOpenControlled ? 'expanded' : 'collapsed',
|
|
274
|
-
bottomMode:
|
|
319
|
+
bottomMode: getBottomInitialState(),
|
|
275
320
|
});
|
|
276
321
|
const setLeftMode = React.useCallback((mode: PaneMode) => dispatchPane({ type: 'SET_LEFT_MODE', mode }), []);
|
|
277
322
|
const setPanelMode = React.useCallback((mode: PaneMode) => dispatchPane({ type: 'SET_PANEL_MODE', mode }), []);
|
|
@@ -867,15 +912,26 @@ const Panel = assignShellSlot(
|
|
|
867
912
|
sizeUpdateMs = 50,
|
|
868
913
|
} = initialProps;
|
|
869
914
|
const panelDomProps = extractPaneDomProps(initialProps, PANEL_DOM_PROP_KEYS);
|
|
915
|
+
// Ref for debounce cleanup
|
|
916
|
+
const debounceTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
917
|
+
// Cleanup debounce timeout on unmount or when dependencies change
|
|
918
|
+
React.useEffect(() => {
|
|
919
|
+
return () => {
|
|
920
|
+
if (debounceTimeoutRef.current) {
|
|
921
|
+
clearTimeout(debounceTimeoutRef.current);
|
|
922
|
+
debounceTimeoutRef.current = null;
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
}, [onSizeChange, sizeUpdate, sizeUpdateMs]);
|
|
870
926
|
// Throttled/debounced emitter for onSizeChange
|
|
871
927
|
const emitSizeChange = React.useMemo(() => {
|
|
872
928
|
if (!onSizeChange) return () => {};
|
|
873
929
|
if (sizeUpdate === 'debounce') {
|
|
874
|
-
let t: any = null;
|
|
875
930
|
const fn = (s: number, meta: PanelSizeChangeMeta) => {
|
|
876
|
-
if (
|
|
877
|
-
|
|
931
|
+
if (debounceTimeoutRef.current) clearTimeout(debounceTimeoutRef.current);
|
|
932
|
+
debounceTimeoutRef.current = setTimeout(() => {
|
|
878
933
|
onSizeChange?.(s, meta);
|
|
934
|
+
debounceTimeoutRef.current = null;
|
|
879
935
|
}, sizeUpdateMs);
|
|
880
936
|
};
|
|
881
937
|
return fn;
|
|
@@ -934,12 +990,16 @@ const Panel = assignShellSlot(
|
|
|
934
990
|
}, [shell, open]);
|
|
935
991
|
|
|
936
992
|
// Dev-only warning if switching controlled/uncontrolled between renders
|
|
993
|
+
const wasControlledRef = React.useRef<boolean | null>(null);
|
|
937
994
|
React.useEffect(() => {
|
|
938
995
|
const isControlled = typeof open !== 'undefined';
|
|
939
|
-
(
|
|
940
|
-
|
|
996
|
+
if (wasControlledRef.current === null) {
|
|
997
|
+
wasControlledRef.current = isControlled;
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
if (wasControlledRef.current !== isControlled) {
|
|
941
1001
|
console.warn('Shell.Panel: Switching between controlled and uncontrolled `open` is not supported.');
|
|
942
|
-
|
|
1002
|
+
wasControlledRef.current = isControlled;
|
|
943
1003
|
}
|
|
944
1004
|
}, [open]);
|
|
945
1005
|
|
|
@@ -971,26 +1031,8 @@ const Panel = assignShellSlot(
|
|
|
971
1031
|
|
|
972
1032
|
const isOverlay = shell.leftResolvedPresentation === 'overlay';
|
|
973
1033
|
|
|
974
|
-
// Normalize CSS lengths to px
|
|
975
|
-
const
|
|
976
|
-
if (value == null) return undefined;
|
|
977
|
-
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
978
|
-
const str = String(value).trim();
|
|
979
|
-
if (!str) return undefined;
|
|
980
|
-
if (str.endsWith('px')) return Number.parseFloat(str);
|
|
981
|
-
if (str.endsWith('rem')) {
|
|
982
|
-
const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize || '16') || 16;
|
|
983
|
-
return Number.parseFloat(str) * rem;
|
|
984
|
-
}
|
|
985
|
-
if (str.endsWith('%')) {
|
|
986
|
-
const pct = Number.parseFloat(str);
|
|
987
|
-
const base = document.documentElement.clientWidth || window.innerWidth || 0;
|
|
988
|
-
return (pct / 100) * base;
|
|
989
|
-
}
|
|
990
|
-
// Bare number-like string
|
|
991
|
-
const n = Number.parseFloat(str);
|
|
992
|
-
return Number.isFinite(n) ? n : undefined;
|
|
993
|
-
}, []);
|
|
1034
|
+
// Normalize CSS lengths to px helper
|
|
1035
|
+
const normalizeSizeToPx = React.useCallback((value: number | string | undefined) => normalizeToPx(value, 'horizontal'), []);
|
|
994
1036
|
|
|
995
1037
|
// Derive a default persistence adapter from paneId if none provided
|
|
996
1038
|
const persistenceAdapter = React.useMemo(() => {
|
|
@@ -1038,7 +1080,7 @@ const Panel = assignShellSlot(
|
|
|
1038
1080
|
React.useEffect(() => {
|
|
1039
1081
|
if (!localRef.current) return;
|
|
1040
1082
|
if (typeof size === 'undefined' && typeof defaultSize !== 'undefined') {
|
|
1041
|
-
const px =
|
|
1083
|
+
const px = normalizeSizeToPx(defaultSize);
|
|
1042
1084
|
if (typeof px === 'number' && Number.isFinite(px)) {
|
|
1043
1085
|
// Clamp to min/max if provided
|
|
1044
1086
|
const minPx = typeof minSize === 'number' ? minSize : undefined;
|
|
@@ -1055,7 +1097,7 @@ const Panel = assignShellSlot(
|
|
|
1055
1097
|
React.useEffect(() => {
|
|
1056
1098
|
if (!localRef.current) return;
|
|
1057
1099
|
if (typeof size === 'undefined') return;
|
|
1058
|
-
const px =
|
|
1100
|
+
const px = normalizeSizeToPx(size);
|
|
1059
1101
|
if (typeof px === 'number' && Number.isFinite(px)) {
|
|
1060
1102
|
const minPx = typeof minSize === 'number' ? minSize : undefined;
|
|
1061
1103
|
const maxPx = typeof maxSize === 'number' ? maxSize : undefined;
|
|
@@ -1063,7 +1105,7 @@ const Panel = assignShellSlot(
|
|
|
1063
1105
|
localRef.current.style.setProperty('--panel-size', `${clamped}px`);
|
|
1064
1106
|
emitSizeChange(clamped, { reason: 'controlled' });
|
|
1065
1107
|
}
|
|
1066
|
-
}, [size, minSize, maxSize,
|
|
1108
|
+
}, [size, minSize, maxSize, normalizeSizeToPx, emitSizeChange]);
|
|
1067
1109
|
|
|
1068
1110
|
// Ensure Left container width is auto whenever Panel is expanded in fixed presentation
|
|
1069
1111
|
React.useEffect(() => {
|
|
@@ -1258,7 +1300,16 @@ const Trigger = React.forwardRef<HTMLButtonElement, TriggerProps>(({ target, act
|
|
|
1258
1300
|
);
|
|
1259
1301
|
|
|
1260
1302
|
return (
|
|
1261
|
-
<button
|
|
1303
|
+
<button
|
|
1304
|
+
{...props}
|
|
1305
|
+
ref={ref}
|
|
1306
|
+
onClick={handleClick}
|
|
1307
|
+
onMouseEnter={handleMouseEnter}
|
|
1308
|
+
onMouseLeave={handleMouseLeave}
|
|
1309
|
+
data-shell-trigger={target}
|
|
1310
|
+
data-shell-action={action}
|
|
1311
|
+
aria-expanded={!isCollapsed}
|
|
1312
|
+
>
|
|
1262
1313
|
{children}
|
|
1263
1314
|
</button>
|
|
1264
1315
|
);
|
package/src/helpers/index.ts
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize CSS length values to pixels.
|
|
3
|
+
* Supports: px, rem, %, and bare numbers.
|
|
4
|
+
*
|
|
5
|
+
* @param value - The value to normalize (number, string, or undefined)
|
|
6
|
+
* @param orientation - 'horizontal' for width-based % or 'vertical' for height-based %
|
|
7
|
+
* @returns The value in pixels, or undefined if invalid
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeToPx(
|
|
10
|
+
value: number | string | undefined,
|
|
11
|
+
orientation: 'horizontal' | 'vertical' = 'horizontal',
|
|
12
|
+
): number | undefined {
|
|
13
|
+
if (value == null) return undefined;
|
|
14
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
15
|
+
|
|
16
|
+
const str = String(value).trim();
|
|
17
|
+
if (!str) return undefined;
|
|
18
|
+
|
|
19
|
+
// px: direct parse
|
|
20
|
+
if (str.endsWith('px')) return Number.parseFloat(str);
|
|
21
|
+
|
|
22
|
+
// rem: multiply by root font size
|
|
23
|
+
if (str.endsWith('rem')) {
|
|
24
|
+
const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize || '16') || 16;
|
|
25
|
+
return Number.parseFloat(str) * rem;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// %: calculate based on viewport dimension
|
|
29
|
+
if (str.endsWith('%')) {
|
|
30
|
+
const pct = Number.parseFloat(str);
|
|
31
|
+
const base =
|
|
32
|
+
orientation === 'horizontal'
|
|
33
|
+
? document.documentElement.clientWidth || window.innerWidth || 0
|
|
34
|
+
: document.documentElement.clientHeight || window.innerHeight || 0;
|
|
35
|
+
return (pct / 100) * base;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Bare number-like string
|
|
39
|
+
const n = Number.parseFloat(str);
|
|
40
|
+
return Number.isFinite(n) ? n : undefined;
|
|
41
|
+
}
|
|
42
|
+
|
package/styles.css
CHANGED
|
@@ -22091,10 +22091,12 @@
|
|
|
22091
22091
|
}
|
|
22092
22092
|
.rt-ShellSidebar[data-mode='collapsed']{
|
|
22093
22093
|
width: 0px;
|
|
22094
|
-
transition-delay: var(--motion-duration-small);
|
|
22095
22094
|
position: absolute;
|
|
22096
22095
|
inset-block: 0;
|
|
22097
22096
|
inset-inline-start: 0;
|
|
22097
|
+
flex-shrink: 0;
|
|
22098
|
+
flex-basis: 0;
|
|
22099
|
+
transition-delay: var(--motion-duration-small);
|
|
22098
22100
|
}
|
|
22099
22101
|
.rt-ShellSidebarContent{
|
|
22100
22102
|
display: flex;
|
|
@@ -22162,10 +22164,12 @@
|
|
|
22162
22164
|
}
|
|
22163
22165
|
.rt-ShellInspector[data-mode='collapsed']{
|
|
22164
22166
|
width: 0px;
|
|
22165
|
-
transition-delay: var(--motion-duration-small);
|
|
22166
22167
|
position: absolute;
|
|
22167
22168
|
inset-block: 0;
|
|
22168
22169
|
inset-inline-end: 0;
|
|
22170
|
+
flex-shrink: 0;
|
|
22171
|
+
flex-basis: 0;
|
|
22172
|
+
transition-delay: var(--motion-duration-small);
|
|
22169
22173
|
}
|
|
22170
22174
|
.rt-ShellInspectorContent{
|
|
22171
22175
|
display: flex;
|