@kushagradhawan/kookie-ui 0.1.69 → 0.1.71
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/dist/cjs/components/_internal/shell-bottom.d.ts +2 -21
- 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-inspector.d.ts +10 -21
- 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-prop-helpers.d.ts +7 -0
- package/dist/cjs/components/_internal/shell-prop-helpers.d.ts.map +1 -0
- package/dist/cjs/components/_internal/shell-prop-helpers.js +2 -0
- package/dist/cjs/components/_internal/shell-prop-helpers.js.map +7 -0
- package/dist/cjs/components/_internal/shell-sidebar.d.ts +4 -21
- 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 +12 -3
- 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/schemas/shell.schema.d.ts +70 -70
- package/dist/cjs/components/shell.context.d.ts +1 -0
- package/dist/cjs/components/shell.context.d.ts.map +1 -1
- package/dist/cjs/components/shell.context.js.map +2 -2
- package/dist/cjs/components/shell.d.ts +6 -26
- package/dist/cjs/components/shell.d.ts.map +1 -1
- package/dist/cjs/components/shell.hooks.d.ts +19 -2
- 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 +21 -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 +2 -21
- 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-inspector.d.ts +10 -21
- 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-prop-helpers.d.ts +7 -0
- package/dist/esm/components/_internal/shell-prop-helpers.d.ts.map +1 -0
- package/dist/esm/components/_internal/shell-prop-helpers.js +2 -0
- package/dist/esm/components/_internal/shell-prop-helpers.js.map +7 -0
- package/dist/esm/components/_internal/shell-sidebar.d.ts +4 -21
- 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 +12 -3
- 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/schemas/shell.schema.d.ts +70 -70
- package/dist/esm/components/shell.context.d.ts +1 -0
- package/dist/esm/components/shell.context.d.ts.map +1 -1
- package/dist/esm/components/shell.context.js.map +2 -2
- package/dist/esm/components/shell.d.ts +6 -26
- package/dist/esm/components/shell.d.ts.map +1 -1
- package/dist/esm/components/shell.hooks.d.ts +19 -2
- 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 +21 -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 +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 +305 -321
- package/src/components/_internal/shell-inspector.tsx +310 -320
- package/src/components/_internal/shell-prop-helpers.ts +53 -0
- package/src/components/_internal/shell-sidebar.tsx +370 -384
- package/src/components/chatbar.tsx +22 -6
- package/src/components/shell.context.tsx +1 -0
- package/src/components/shell.hooks.ts +67 -2
- package/src/components/shell.tsx +186 -200
- package/src/components/shell.types.ts +23 -0
|
@@ -3,39 +3,22 @@ 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,
|
|
6
|
+
import { useResponsivePresentation, useResponsiveInitialState } from '../shell.hooks.js';
|
|
7
7
|
import { PaneResizeContext } from './shell-resize.js';
|
|
8
8
|
import { InspectorHandle, PaneHandle } from './shell-handles.js';
|
|
9
9
|
import { _BREAKPOINTS } from '../shell.types.js';
|
|
10
|
-
import type { Breakpoint, PaneMode, PaneSizePersistence, ResponsivePresentation } from '../shell.types.js';
|
|
11
|
-
|
|
12
|
-
interface PaneProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
13
|
-
presentation?: ResponsivePresentation;
|
|
14
|
-
// legacy mode removed
|
|
15
|
-
expandedSize?: number;
|
|
16
|
-
minSize?: number;
|
|
17
|
-
maxSize?: number;
|
|
18
|
-
resizable?: boolean;
|
|
19
|
-
collapsible?: boolean;
|
|
20
|
-
onExpand?: () => void;
|
|
21
|
-
onCollapse?: () => void;
|
|
22
|
-
onResize?: (size: number) => void;
|
|
23
|
-
resizer?: React.ReactNode;
|
|
24
|
-
onResizeStart?: (size: number) => void;
|
|
25
|
-
onResizeEnd?: (size: number) => void;
|
|
26
|
-
snapPoints?: number[];
|
|
27
|
-
snapTolerance?: number;
|
|
28
|
-
collapseThreshold?: number;
|
|
29
|
-
paneId?: string;
|
|
30
|
-
persistence?: PaneSizePersistence;
|
|
31
|
-
}
|
|
10
|
+
import type { Breakpoint, PaneMode, PaneSizePersistence, ResponsivePresentation, PaneBaseProps } from '../shell.types.js';
|
|
11
|
+
import { extractPaneDomProps, mapResponsiveBooleanToPaneMode } from './shell-prop-helpers.js';
|
|
32
12
|
|
|
33
13
|
type InspectorOpenChangeMeta = { reason: 'init' | 'toggle' | 'responsive' };
|
|
34
14
|
type InspectorControlledProps = { open: boolean | Partial<Record<Breakpoint, boolean>>; onOpenChange?: (open: boolean, meta: InspectorOpenChangeMeta) => void; defaultOpen?: never };
|
|
35
15
|
type InspectorUncontrolledProps = { defaultOpen?: boolean | Partial<Record<Breakpoint, boolean>>; onOpenChange?: (open: boolean, meta: InspectorOpenChangeMeta) => void; open?: never };
|
|
36
16
|
type InspectorSizeChangeMeta = { reason: 'init' | 'resize' | 'controlled' };
|
|
37
|
-
type
|
|
38
|
-
|
|
17
|
+
type InspectorSizeControlledProps = { size: number | string; defaultSize?: never };
|
|
18
|
+
type InspectorSizeUncontrolledProps = { defaultSize?: number | string; size?: never };
|
|
19
|
+
type InspectorPublicProps = PaneBaseProps &
|
|
20
|
+
(InspectorControlledProps | InspectorUncontrolledProps) &
|
|
21
|
+
(InspectorSizeControlledProps | InspectorSizeUncontrolledProps) & {
|
|
39
22
|
onSizeChange?: (size: number, meta: InspectorSizeChangeMeta) => void;
|
|
40
23
|
sizeUpdate?: 'throttle' | 'debounce';
|
|
41
24
|
sizeUpdateMs?: number;
|
|
@@ -43,334 +26,341 @@ type InspectorPublicProps = PaneProps &
|
|
|
43
26
|
|
|
44
27
|
type InspectorComponent = React.ForwardRefExoticComponent<InspectorPublicProps & React.RefAttributes<HTMLDivElement>> & { Handle: typeof InspectorHandle };
|
|
45
28
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
29
|
+
const INSPECTOR_DOM_PROP_KEYS = [
|
|
30
|
+
'className',
|
|
31
|
+
'children',
|
|
32
|
+
'defaultOpen',
|
|
33
|
+
'open',
|
|
34
|
+
'onOpenChange',
|
|
35
|
+
'size',
|
|
36
|
+
'defaultSize',
|
|
37
|
+
'onSizeChange',
|
|
38
|
+
'sizeUpdate',
|
|
39
|
+
'sizeUpdateMs',
|
|
40
|
+
'style',
|
|
41
|
+
] as const satisfies readonly (keyof InspectorPublicProps)[];
|
|
42
|
+
|
|
43
|
+
export const Inspector = React.forwardRef<HTMLDivElement, InspectorPublicProps>((initialProps, ref) => {
|
|
44
|
+
const {
|
|
45
|
+
className,
|
|
46
|
+
presentation = { initial: 'overlay', lg: 'fixed' },
|
|
47
|
+
defaultOpen,
|
|
48
|
+
open,
|
|
49
|
+
onOpenChange,
|
|
50
|
+
expandedSize = 320,
|
|
51
|
+
minSize = 200,
|
|
52
|
+
maxSize = 500,
|
|
53
|
+
resizable = false,
|
|
54
|
+
collapsible = true,
|
|
55
|
+
onExpand,
|
|
56
|
+
onCollapse,
|
|
57
|
+
onResize,
|
|
58
|
+
onResizeStart,
|
|
59
|
+
onResizeEnd,
|
|
60
|
+
snapPoints,
|
|
61
|
+
snapTolerance,
|
|
62
|
+
collapseThreshold,
|
|
63
|
+
paneId,
|
|
64
|
+
persistence,
|
|
65
|
+
children,
|
|
66
|
+
style,
|
|
67
|
+
onSizeChange,
|
|
68
|
+
sizeUpdate,
|
|
69
|
+
sizeUpdateMs = 50,
|
|
70
|
+
size,
|
|
71
|
+
defaultSize,
|
|
72
|
+
} = initialProps;
|
|
73
|
+
const inspectorDomProps = extractPaneDomProps(initialProps, INSPECTOR_DOM_PROP_KEYS);
|
|
74
|
+
const shell = useShell();
|
|
75
|
+
const resolvedPresentation = useResponsivePresentation(presentation);
|
|
76
|
+
const isOverlay = resolvedPresentation === 'overlay';
|
|
77
|
+
const isStacked = resolvedPresentation === 'stacked';
|
|
78
|
+
const localRef = React.useRef<HTMLDivElement | null>(null);
|
|
79
|
+
const setRef = React.useCallback(
|
|
80
|
+
(node: HTMLDivElement | null) => {
|
|
81
|
+
localRef.current = node;
|
|
82
|
+
if (typeof ref === 'function') ref(node);
|
|
83
|
+
else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
74
84
|
},
|
|
75
|
-
ref,
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const isStacked = resolvedPresentation === 'stacked';
|
|
81
|
-
const localRef = React.useRef<HTMLDivElement | null>(null);
|
|
82
|
-
const setRef = React.useCallback(
|
|
83
|
-
(node: HTMLDivElement | null) => {
|
|
84
|
-
localRef.current = node;
|
|
85
|
-
if (typeof ref === 'function') ref(node);
|
|
86
|
-
else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
87
|
-
},
|
|
88
|
-
[ref],
|
|
89
|
-
);
|
|
90
|
-
const childArray = React.Children.toArray(children) as React.ReactElement[];
|
|
91
|
-
const handleChildren = childArray.filter((el: React.ReactElement) => React.isValidElement(el) && el.type === InspectorHandle);
|
|
92
|
-
const contentChildren = childArray.filter((el: React.ReactElement) => !(React.isValidElement(el) && el.type === InspectorHandle));
|
|
85
|
+
[ref],
|
|
86
|
+
);
|
|
87
|
+
const childArray = React.Children.toArray(children) as React.ReactElement[];
|
|
88
|
+
const handleChildren = childArray.filter((el: React.ReactElement) => React.isValidElement(el) && el.type === InspectorHandle);
|
|
89
|
+
const contentChildren = childArray.filter((el: React.ReactElement) => !(React.isValidElement(el) && el.type === InspectorHandle));
|
|
93
90
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}, ms);
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
if (strategy === 'throttle') {
|
|
113
|
-
let last = 0;
|
|
114
|
-
return (s: number, meta: InspectorSizeChangeMeta) => {
|
|
115
|
-
const now = Date.now();
|
|
116
|
-
if (now - last >= ms) {
|
|
117
|
-
last = now;
|
|
118
|
-
cb(s, meta);
|
|
119
|
-
}
|
|
120
|
-
};
|
|
91
|
+
// Throttled/debounced emitter for onSizeChange
|
|
92
|
+
const normalizedControlledOpen = React.useMemo(() => mapResponsiveBooleanToPaneMode(open), [open]);
|
|
93
|
+
const normalizedDefaultOpen = React.useMemo(() => mapResponsiveBooleanToPaneMode(defaultOpen), [defaultOpen]);
|
|
94
|
+
const openIsResponsive = typeof open === 'object' && open !== null;
|
|
95
|
+
useResponsiveInitialState<PaneMode>({
|
|
96
|
+
controlledValue: normalizedControlledOpen,
|
|
97
|
+
defaultValue: normalizedDefaultOpen,
|
|
98
|
+
currentValue: shell.inspectorMode,
|
|
99
|
+
setValue: shell.setInspectorMode,
|
|
100
|
+
breakpointReady: shell.currentBreakpointReady,
|
|
101
|
+
controlledIsResponsive: openIsResponsive,
|
|
102
|
+
onResponsiveChange: (next) => onOpenChange?.(next === 'expanded', { reason: 'responsive' }),
|
|
103
|
+
onInit: (initial) => {
|
|
104
|
+
if (typeof open === 'undefined') {
|
|
105
|
+
onOpenChange?.(initial === 'expanded', { reason: 'init' });
|
|
121
106
|
}
|
|
122
|
-
|
|
123
|
-
|
|
107
|
+
},
|
|
108
|
+
});
|
|
124
109
|
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
110
|
+
const emitSizeChange = React.useMemo(() => {
|
|
111
|
+
const cb = onSizeChange as undefined | ((s: number, meta: InspectorSizeChangeMeta) => void);
|
|
112
|
+
const strategy = sizeUpdate as undefined | 'throttle' | 'debounce';
|
|
113
|
+
const ms = sizeUpdateMs ?? 50;
|
|
114
|
+
if (!cb) return () => {};
|
|
115
|
+
if (strategy === 'debounce') {
|
|
116
|
+
let t: any = null;
|
|
117
|
+
return (s: number, meta: InspectorSizeChangeMeta) => {
|
|
118
|
+
if (t) clearTimeout(t);
|
|
119
|
+
t = setTimeout(() => {
|
|
120
|
+
cb(s, meta);
|
|
121
|
+
}, ms);
|
|
122
|
+
};
|
|
131
123
|
}
|
|
124
|
+
if (strategy === 'throttle') {
|
|
125
|
+
let last = 0;
|
|
126
|
+
return (s: number, meta: InspectorSizeChangeMeta) => {
|
|
127
|
+
const now = Date.now();
|
|
128
|
+
if (now - last >= ms) {
|
|
129
|
+
last = now;
|
|
130
|
+
cb(s, meta);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return (s: number, meta: InspectorSizeChangeMeta) => cb(s, meta);
|
|
135
|
+
}, [onSizeChange, sizeUpdate, sizeUpdateMs]);
|
|
132
136
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (wasControlledRef.current !== isControlled) {
|
|
141
|
-
console.warn('Shell.Inspector: Switching between controlled and uncontrolled `open` is not supported.');
|
|
142
|
-
wasControlledRef.current = isControlled;
|
|
143
|
-
}
|
|
144
|
-
}, [open]);
|
|
145
|
-
|
|
146
|
-
const responsiveNotifiedRef = React.useRef(false);
|
|
147
|
-
const didInitFromDefaultOpenRef = React.useRef(false);
|
|
148
|
-
const resolvedDefaultOpen = useResponsiveValue(defaultOpen);
|
|
149
|
-
React.useEffect(() => {
|
|
150
|
-
if (!shell.currentBreakpointReady) return;
|
|
151
|
-
if (didInitFromDefaultOpenRef.current) return;
|
|
152
|
-
if (typeof open !== 'undefined') return; // controlled ignores default
|
|
153
|
-
if (typeof defaultOpen === 'undefined') return;
|
|
154
|
-
const initialOpen = Boolean(resolvedDefaultOpen);
|
|
155
|
-
shell.setInspectorMode(initialOpen ? 'expanded' : 'collapsed');
|
|
156
|
-
if (initialOpen) onOpenChange?.(true, { reason: 'init' });
|
|
157
|
-
didInitFromDefaultOpenRef.current = true;
|
|
158
|
-
}, [shell, resolvedDefaultOpen, defaultOpen, open, onOpenChange]);
|
|
159
|
-
|
|
160
|
-
// Controlled responsive open
|
|
161
|
-
const resolvedOpen = useResponsiveValue(open);
|
|
162
|
-
React.useEffect(() => {
|
|
163
|
-
if (typeof resolvedOpen === 'undefined') return;
|
|
164
|
-
const shouldExpand = Boolean(resolvedOpen);
|
|
165
|
-
if (shouldExpand && shell.inspectorMode !== 'expanded') shell.setInspectorMode('expanded');
|
|
166
|
-
if (!shouldExpand && shell.inspectorMode !== 'collapsed') shell.setInspectorMode('collapsed');
|
|
167
|
-
}, [shell, resolvedOpen]);
|
|
168
|
-
|
|
169
|
-
// Removed boolean-only mount init; handled in responsive init effect above
|
|
137
|
+
// Dev guards
|
|
138
|
+
const wasControlledRef = React.useRef<boolean | null>(null);
|
|
139
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
140
|
+
if (typeof open !== 'undefined' && typeof defaultOpen !== 'undefined') {
|
|
141
|
+
console.error('Shell.Inspector: Do not pass both `open` and `defaultOpen`. Choose one.');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
170
144
|
|
|
171
|
-
|
|
145
|
+
// Warn on controlled/uncontrolled mode switch
|
|
146
|
+
React.useEffect(() => {
|
|
147
|
+
const isControlled = typeof open !== 'undefined';
|
|
148
|
+
if (wasControlledRef.current === null) {
|
|
149
|
+
wasControlledRef.current = isControlled;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (wasControlledRef.current !== isControlled) {
|
|
153
|
+
console.warn('Shell.Inspector: Switching between controlled and uncontrolled `open` is not supported.');
|
|
154
|
+
wasControlledRef.current = isControlled;
|
|
155
|
+
}
|
|
156
|
+
}, [open]);
|
|
172
157
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
158
|
+
const initNotifiedRef = React.useRef(false);
|
|
159
|
+
const lastInspectorModeRef = React.useRef<PaneMode | null>(null);
|
|
160
|
+
React.useEffect(() => {
|
|
161
|
+
// Notify init open
|
|
162
|
+
if (!initNotifiedRef.current && typeof open === 'undefined' && defaultOpen && shell.inspectorMode === 'expanded') {
|
|
163
|
+
onOpenChange?.(true, { reason: 'init' });
|
|
164
|
+
initNotifiedRef.current = true;
|
|
165
|
+
}
|
|
181
166
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
onOpenChange?.(shell.inspectorMode === 'expanded', { reason: 'toggle' });
|
|
187
|
-
}
|
|
188
|
-
responsiveNotifiedRef.current = false;
|
|
189
|
-
}
|
|
190
|
-
lastInspectorModeRef.current = shell.inspectorMode;
|
|
167
|
+
// Notify toggles when uncontrolled (avoid double-notify after responsive change)
|
|
168
|
+
if (typeof open === 'undefined') {
|
|
169
|
+
if (lastInspectorModeRef.current !== null && lastInspectorModeRef.current !== shell.inspectorMode) {
|
|
170
|
+
onOpenChange?.(shell.inspectorMode === 'expanded', { reason: 'toggle' });
|
|
191
171
|
}
|
|
192
|
-
|
|
172
|
+
lastInspectorModeRef.current = shell.inspectorMode;
|
|
173
|
+
}
|
|
174
|
+
}, [shell.inspectorMode, open, defaultOpen, onOpenChange]);
|
|
193
175
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
176
|
+
React.useEffect(() => {
|
|
177
|
+
if (shell.inspectorMode === 'expanded') {
|
|
178
|
+
onExpand?.();
|
|
179
|
+
} else {
|
|
180
|
+
onCollapse?.();
|
|
181
|
+
}
|
|
182
|
+
}, [shell.inspectorMode, onExpand, onCollapse]);
|
|
201
183
|
|
|
202
|
-
|
|
184
|
+
const isExpanded = shell.inspectorMode === 'expanded';
|
|
203
185
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
186
|
+
const persistenceAdapter = React.useMemo(() => {
|
|
187
|
+
if (!paneId || persistence) return persistence;
|
|
188
|
+
const key = `kookie-ui:shell:inspector:${paneId}`;
|
|
189
|
+
const adapter: PaneSizePersistence = {
|
|
190
|
+
load: () => {
|
|
191
|
+
if (typeof window === 'undefined') return undefined;
|
|
192
|
+
try {
|
|
210
193
|
const v = window.localStorage.getItem(key);
|
|
211
194
|
return v ? Number(v) : undefined;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
195
|
+
} catch (err) {
|
|
196
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
197
|
+
console.warn('Shell.Inspector: failed to load persisted size', err);
|
|
198
|
+
}
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
save: (size: number) => {
|
|
203
|
+
if (typeof window === 'undefined') return;
|
|
204
|
+
try {
|
|
215
205
|
window.localStorage.setItem(key, String(size));
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
React.useEffect(() => {
|
|
222
|
-
let mounted = true;
|
|
223
|
-
(async () => {
|
|
224
|
-
if (!resizable || !persistenceAdapter?.load || isOverlay) return;
|
|
225
|
-
const loaded = await persistenceAdapter.load();
|
|
226
|
-
if (mounted && typeof loaded === 'number' && localRef.current) {
|
|
227
|
-
localRef.current.style.setProperty('--inspector-size', `${loaded}px`);
|
|
228
|
-
onResize?.(loaded);
|
|
206
|
+
} catch (err) {
|
|
207
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
208
|
+
console.warn('Shell.Inspector: failed to save persisted size', err);
|
|
209
|
+
}
|
|
229
210
|
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}, [resizable, persistenceAdapter, onResize, isOverlay]);
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
return adapter;
|
|
214
|
+
}, [paneId, persistence]);
|
|
235
215
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
},
|
|
259
|
-
target: 'inspector',
|
|
260
|
-
collapsible,
|
|
261
|
-
snapPoints,
|
|
262
|
-
snapTolerance: snapTolerance ?? 8,
|
|
263
|
-
collapseThreshold,
|
|
264
|
-
requestCollapse: () => shell.setInspectorMode('collapsed'),
|
|
265
|
-
requestToggle: () => shell.togglePane('inspector'),
|
|
266
|
-
}}
|
|
267
|
-
>
|
|
268
|
-
{handleChildren.length > 0 ? handleChildren.map((el, i) => React.cloneElement(el, { key: el.key ?? i })) : <PaneHandle />}
|
|
269
|
-
</PaneResizeContext.Provider>
|
|
270
|
-
) : null;
|
|
216
|
+
React.useEffect(() => {
|
|
217
|
+
let mounted = true;
|
|
218
|
+
if (!resizable || !persistenceAdapter?.load || isOverlay) return undefined;
|
|
219
|
+
const loaded = persistenceAdapter.load();
|
|
220
|
+
const applyLoaded = (value?: number) => {
|
|
221
|
+
if (!mounted || typeof value !== 'number' || !localRef.current) return;
|
|
222
|
+
localRef.current.style.setProperty('--inspector-size', `${value}px`);
|
|
223
|
+
onResize?.(value);
|
|
224
|
+
};
|
|
225
|
+
if (loaded instanceof Promise) {
|
|
226
|
+
loaded.then(applyLoaded).catch((err) => {
|
|
227
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
228
|
+
console.warn('Shell.Inspector: failed to load persisted size', err);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
} else {
|
|
232
|
+
applyLoaded(loaded);
|
|
233
|
+
}
|
|
234
|
+
return () => {
|
|
235
|
+
mounted = false;
|
|
236
|
+
};
|
|
237
|
+
}, [resizable, persistenceAdapter, onResize, isOverlay]);
|
|
271
238
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
239
|
+
const handleEl =
|
|
240
|
+
resizable && !isOverlay && isExpanded ? (
|
|
241
|
+
<PaneResizeContext.Provider
|
|
242
|
+
value={{
|
|
243
|
+
containerRef: localRef,
|
|
244
|
+
cssVarName: '--inspector-size',
|
|
245
|
+
minSize,
|
|
246
|
+
maxSize,
|
|
247
|
+
defaultSize: expandedSize,
|
|
248
|
+
orientation: 'vertical',
|
|
249
|
+
edge: 'start',
|
|
250
|
+
computeNext: (client, startClient, startSize) => {
|
|
251
|
+
const isRtl = getComputedStyle(localRef.current!).direction === 'rtl';
|
|
252
|
+
const delta = client - startClient;
|
|
253
|
+
return startSize + (isRtl ? delta : -delta);
|
|
254
|
+
},
|
|
255
|
+
onResize,
|
|
256
|
+
onResizeStart,
|
|
257
|
+
onResizeEnd: (size) => {
|
|
258
|
+
onResizeEnd?.(size);
|
|
259
|
+
emitSizeChange(size, { reason: 'resize' });
|
|
260
|
+
persistenceAdapter?.save?.(size);
|
|
261
|
+
},
|
|
262
|
+
target: 'inspector',
|
|
263
|
+
collapsible,
|
|
264
|
+
snapPoints,
|
|
265
|
+
snapTolerance: snapTolerance ?? 8,
|
|
266
|
+
collapseThreshold,
|
|
267
|
+
requestCollapse: () => shell.setInspectorMode('collapsed'),
|
|
268
|
+
requestToggle: () => shell.togglePane('inspector'),
|
|
269
|
+
}}
|
|
270
|
+
>
|
|
271
|
+
{handleChildren.length > 0 ? handleChildren.map((el, i) => React.cloneElement(el, { key: el.key ?? i })) : <PaneHandle />}
|
|
272
|
+
</PaneResizeContext.Provider>
|
|
273
|
+
) : null;
|
|
291
274
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
275
|
+
// Normalize CSS lengths to px
|
|
276
|
+
const normalizeToPx = React.useCallback((value: number | string | undefined): number | undefined => {
|
|
277
|
+
if (value == null) return undefined;
|
|
278
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
279
|
+
const str = String(value).trim();
|
|
280
|
+
if (!str) return undefined;
|
|
281
|
+
if (str.endsWith('px')) return Number.parseFloat(str);
|
|
282
|
+
if (str.endsWith('rem')) {
|
|
283
|
+
const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize || '16') || 16;
|
|
284
|
+
return Number.parseFloat(str) * rem;
|
|
285
|
+
}
|
|
286
|
+
if (str.endsWith('%')) {
|
|
287
|
+
const pct = Number.parseFloat(str);
|
|
288
|
+
const base = document.documentElement.clientWidth || window.innerWidth || 0;
|
|
289
|
+
return (pct / 100) * base;
|
|
290
|
+
}
|
|
291
|
+
const n = Number.parseFloat(str);
|
|
292
|
+
return Number.isFinite(n) ? n : undefined;
|
|
293
|
+
}, []);
|
|
307
294
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const px = normalizeToPx(controlledSize);
|
|
295
|
+
// Apply defaultSize on mount when uncontrolled
|
|
296
|
+
React.useEffect(() => {
|
|
297
|
+
if (!localRef.current) return;
|
|
298
|
+
if (typeof size === 'undefined' && typeof defaultSize !== 'undefined') {
|
|
299
|
+
const px = normalizeToPx(defaultSize);
|
|
314
300
|
if (typeof px === 'number' && Number.isFinite(px)) {
|
|
315
301
|
const minPx = typeof minSize === 'number' ? minSize : undefined;
|
|
316
302
|
const maxPx = typeof maxSize === 'number' ? maxSize : undefined;
|
|
317
303
|
const clamped = Math.min(maxPx ?? px, Math.max(minPx ?? px, px));
|
|
318
304
|
localRef.current.style.setProperty('--inspector-size', `${clamped}px`);
|
|
319
|
-
emitSizeChange(clamped, { reason: '
|
|
305
|
+
emitSizeChange(clamped, { reason: 'init' });
|
|
320
306
|
}
|
|
321
|
-
}, [controlledSize, minSize, maxSize, normalizeToPx, emitSizeChange]);
|
|
322
|
-
|
|
323
|
-
if (isOverlay) {
|
|
324
|
-
const open = shell.inspectorMode === 'expanded';
|
|
325
|
-
return (
|
|
326
|
-
<Sheet.Root open={open} onOpenChange={(o) => shell.setInspectorMode(o ? 'expanded' : 'collapsed')}>
|
|
327
|
-
<Sheet.Content side="end" style={{ padding: 0 }} width={{ initial: `${expandedSize}px` }}>
|
|
328
|
-
<VisuallyHidden>
|
|
329
|
-
<Sheet.Title>Inspector</Sheet.Title>
|
|
330
|
-
</VisuallyHidden>
|
|
331
|
-
{contentChildren}
|
|
332
|
-
</Sheet.Content>
|
|
333
|
-
</Sheet.Root>
|
|
334
|
-
);
|
|
335
307
|
}
|
|
308
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
309
|
+
}, []);
|
|
336
310
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
311
|
+
// Controlled size sync
|
|
312
|
+
const controlledSize = size;
|
|
313
|
+
React.useEffect(() => {
|
|
314
|
+
if (!localRef.current) return;
|
|
315
|
+
if (typeof controlledSize === 'undefined') return;
|
|
316
|
+
const px = normalizeToPx(controlledSize);
|
|
317
|
+
if (typeof px === 'number' && Number.isFinite(px)) {
|
|
318
|
+
const minPx = typeof minSize === 'number' ? minSize : undefined;
|
|
319
|
+
const maxPx = typeof maxSize === 'number' ? maxSize : undefined;
|
|
320
|
+
const clamped = Math.min(maxPx ?? px, Math.max(minPx ?? px, px));
|
|
321
|
+
localRef.current.style.setProperty('--inspector-size', `${clamped}px`);
|
|
322
|
+
emitSizeChange(clamped, { reason: 'controlled' });
|
|
323
|
+
}
|
|
324
|
+
}, [controlledSize, minSize, maxSize, normalizeToPx, emitSizeChange]);
|
|
349
325
|
|
|
326
|
+
if (isOverlay) {
|
|
327
|
+
const open = shell.inspectorMode === 'expanded';
|
|
350
328
|
return (
|
|
351
|
-
<
|
|
352
|
-
{
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
data-peek={shell.peekTarget === 'inspector' || undefined}
|
|
357
|
-
data-presentation={shell.currentBreakpointReady ? resolvedPresentation : undefined}
|
|
358
|
-
data-open={(shell.currentBreakpointReady && isStacked && isExpanded) || undefined}
|
|
359
|
-
style={{
|
|
360
|
-
...style,
|
|
361
|
-
['--inspector-size' as any]: `${expandedSize}px`,
|
|
362
|
-
['--inspector-min-size' as any]: `${minSize}px`,
|
|
363
|
-
['--inspector-max-size' as any]: `${maxSize}px`,
|
|
364
|
-
}}
|
|
365
|
-
>
|
|
366
|
-
<div className="rt-ShellInspectorContent" data-visible={isExpanded || undefined}>
|
|
329
|
+
<Sheet.Root open={open} onOpenChange={(o) => shell.setInspectorMode(o ? 'expanded' : 'collapsed')}>
|
|
330
|
+
<Sheet.Content side="end" style={{ padding: 0 }} width={{ initial: `${expandedSize}px` }}>
|
|
331
|
+
<VisuallyHidden>
|
|
332
|
+
<Sheet.Title>Inspector</Sheet.Title>
|
|
333
|
+
</VisuallyHidden>
|
|
367
334
|
{contentChildren}
|
|
368
|
-
</
|
|
369
|
-
|
|
370
|
-
</div>
|
|
335
|
+
</Sheet.Content>
|
|
336
|
+
</Sheet.Root>
|
|
371
337
|
);
|
|
372
|
-
}
|
|
373
|
-
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Strip control/size props from DOM spread
|
|
341
|
+
return (
|
|
342
|
+
<div
|
|
343
|
+
{...inspectorDomProps}
|
|
344
|
+
ref={setRef}
|
|
345
|
+
className={classNames('rt-ShellInspector', className)}
|
|
346
|
+
data-mode={shell.inspectorMode}
|
|
347
|
+
data-peek={shell.peekTarget === 'inspector' || undefined}
|
|
348
|
+
data-presentation={shell.currentBreakpointReady ? resolvedPresentation : undefined}
|
|
349
|
+
data-open={(shell.currentBreakpointReady && isStacked && isExpanded) || undefined}
|
|
350
|
+
style={{
|
|
351
|
+
...style,
|
|
352
|
+
['--inspector-size' as any]: `${expandedSize}px`,
|
|
353
|
+
['--inspector-min-size' as any]: `${minSize}px`,
|
|
354
|
+
['--inspector-max-size' as any]: `${maxSize}px`,
|
|
355
|
+
}}
|
|
356
|
+
>
|
|
357
|
+
<div className="rt-ShellInspectorContent" data-visible={isExpanded || undefined}>
|
|
358
|
+
{contentChildren}
|
|
359
|
+
</div>
|
|
360
|
+
{handleEl}
|
|
361
|
+
</div>
|
|
362
|
+
);
|
|
363
|
+
}) as InspectorComponent;
|
|
374
364
|
|
|
375
365
|
Inspector.displayName = 'Shell.Inspector';
|
|
376
366
|
Inspector.Handle = InspectorHandle;
|