@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
package/src/components/shell.tsx
CHANGED
|
@@ -29,13 +29,14 @@ import * as React from 'react';
|
|
|
29
29
|
import classNames from 'classnames';
|
|
30
30
|
import * as Sheet from './sheet.js';
|
|
31
31
|
import { VisuallyHidden } from './visually-hidden.js';
|
|
32
|
-
import { useResponsivePresentation,
|
|
32
|
+
import { useResponsivePresentation, useResponsiveInitialState } from './shell.hooks.js';
|
|
33
33
|
import { PaneResizeContext } from './_internal/shell-resize.js';
|
|
34
34
|
import { PaneHandle, PanelHandle } from './_internal/shell-handles.js';
|
|
35
|
+
import { omitPaneProps, extractPaneDomProps, mapResponsiveBooleanToPaneMode } from './_internal/shell-prop-helpers.js';
|
|
35
36
|
import { Sidebar } from './_internal/shell-sidebar.js';
|
|
36
37
|
import { Bottom } from './_internal/shell-bottom.js';
|
|
37
38
|
import { Inspector } from './_internal/shell-inspector.js';
|
|
38
|
-
import type { PresentationValue, ResponsivePresentation, PaneMode, SidebarMode, PaneSizePersistence, Breakpoint, PaneTarget, Responsive } from './shell.types.js';
|
|
39
|
+
import type { PresentationValue, ResponsivePresentation, PaneMode, SidebarMode, PaneSizePersistence, Breakpoint, PaneTarget, Responsive, PaneBaseProps } from './shell.types.js';
|
|
39
40
|
import { _BREAKPOINTS } from './shell.types.js';
|
|
40
41
|
import {
|
|
41
42
|
ShellProvider,
|
|
@@ -135,6 +136,26 @@ type PaneAction =
|
|
|
135
136
|
| { type: 'EXPAND_PANE'; target: PaneTarget }
|
|
136
137
|
| { type: 'COLLAPSE_PANE'; target: PaneTarget };
|
|
137
138
|
|
|
139
|
+
const SHELL_SLOT = Symbol('rtShellSlot');
|
|
140
|
+
|
|
141
|
+
function assignShellSlot<T extends React.ComponentType<any>>(component: T, slot: string): T {
|
|
142
|
+
(component as any)[SHELL_SLOT] = slot;
|
|
143
|
+
return component;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function isShellComponent(element: React.ReactElement, component: any): boolean {
|
|
147
|
+
if (!React.isValidElement(element)) return false;
|
|
148
|
+
const type: any = element.type;
|
|
149
|
+
if (type === component) return true;
|
|
150
|
+
const targetSlot = (component as any)?.[SHELL_SLOT];
|
|
151
|
+
return Boolean(type?.[SHELL_SLOT] && targetSlot && type[SHELL_SLOT] === targetSlot);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Tag imported slot components so isType remains stable after minification
|
|
155
|
+
assignShellSlot(Sidebar as any, 'Shell.Sidebar');
|
|
156
|
+
assignShellSlot(Inspector as any, 'Shell.Inspector');
|
|
157
|
+
assignShellSlot(Bottom as any, 'Shell.Bottom');
|
|
158
|
+
|
|
138
159
|
function paneReducer(state: PaneState, action: PaneAction): PaneState {
|
|
139
160
|
switch (action.type) {
|
|
140
161
|
case 'SET_LEFT_MODE': {
|
|
@@ -239,13 +260,17 @@ const Root = React.forwardRef<HTMLDivElement, ShellRootProps>(({ className, chil
|
|
|
239
260
|
const initialChildren = React.Children.toArray(children) as React.ReactElement[];
|
|
240
261
|
const hasPanelDefaultOpen = initialChildren.some((el) => React.isValidElement(el) && (el as any).type?.displayName === 'Shell.Panel' && Boolean((el as any).props?.defaultOpen));
|
|
241
262
|
const hasRailDefaultOpen = initialChildren.some((el) => React.isValidElement(el) && (el as any).type?.displayName === 'Shell.Rail' && Boolean((el as any).props?.defaultOpen));
|
|
263
|
+
const hasInspectorDefaultOpen = initialChildren.some((el) => React.isValidElement(el) && (el as any).type?.displayName === 'Shell.Inspector' && Boolean((el as any).props?.defaultOpen));
|
|
264
|
+
const hasInspectorOpenControlled = initialChildren.some(
|
|
265
|
+
(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
|
+
);
|
|
242
267
|
|
|
243
268
|
// Pane state management via reducer
|
|
244
269
|
const [paneState, dispatchPane] = React.useReducer(paneReducer, {
|
|
245
270
|
leftMode: hasPanelDefaultOpen || hasRailDefaultOpen ? 'expanded' : 'collapsed',
|
|
246
271
|
panelMode: hasPanelDefaultOpen ? 'expanded' : 'collapsed',
|
|
247
272
|
sidebarMode: 'expanded',
|
|
248
|
-
inspectorMode: 'collapsed',
|
|
273
|
+
inspectorMode: hasInspectorDefaultOpen || hasInspectorOpenControlled ? 'expanded' : 'collapsed',
|
|
249
274
|
bottomMode: 'collapsed',
|
|
250
275
|
});
|
|
251
276
|
const setLeftMode = React.useCallback((mode: PaneMode) => dispatchPane({ type: 'SET_LEFT_MODE', mode }), []);
|
|
@@ -496,7 +521,7 @@ const Root = React.forwardRef<HTMLDivElement, ShellRootProps>(({ className, chil
|
|
|
496
521
|
</ShellProvider>
|
|
497
522
|
</div>
|
|
498
523
|
);
|
|
499
|
-
});
|
|
524
|
+
}) as PanelComponent;
|
|
500
525
|
Root.displayName = 'Shell.Root';
|
|
501
526
|
|
|
502
527
|
// Header
|
|
@@ -518,26 +543,7 @@ const Header = React.forwardRef<HTMLElement, ShellHeaderProps>(({ className, hei
|
|
|
518
543
|
Header.displayName = 'Shell.Header';
|
|
519
544
|
|
|
520
545
|
// Pane Props Interface (shared by Panel, Sidebar, Inspector, Bottom)
|
|
521
|
-
|
|
522
|
-
presentation?: ResponsivePresentation;
|
|
523
|
-
expandedSize?: number;
|
|
524
|
-
minSize?: number;
|
|
525
|
-
maxSize?: number;
|
|
526
|
-
resizable?: boolean;
|
|
527
|
-
collapsible?: boolean;
|
|
528
|
-
onExpand?: () => void;
|
|
529
|
-
onCollapse?: () => void;
|
|
530
|
-
onResize?: (size: number) => void;
|
|
531
|
-
/** Optional custom content inside the resizer handle (kept unstyled). */
|
|
532
|
-
resizer?: React.ReactNode;
|
|
533
|
-
onResizeStart?: (size: number) => void;
|
|
534
|
-
onResizeEnd?: (size: number) => void;
|
|
535
|
-
snapPoints?: number[];
|
|
536
|
-
snapTolerance?: number;
|
|
537
|
-
collapseThreshold?: number;
|
|
538
|
-
paneId?: string;
|
|
539
|
-
persistence?: PaneSizePersistence;
|
|
540
|
-
}
|
|
546
|
+
type PaneProps = PaneBaseProps;
|
|
541
547
|
|
|
542
548
|
// Left container (auto-created for Rail+Panel)
|
|
543
549
|
interface LeftProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
@@ -549,6 +555,9 @@ interface LeftProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
|
549
555
|
collapsible?: boolean;
|
|
550
556
|
onExpand?: () => void;
|
|
551
557
|
onCollapse?: () => void;
|
|
558
|
+
mode?: never;
|
|
559
|
+
defaultMode?: never;
|
|
560
|
+
onModeChange?: never;
|
|
552
561
|
}
|
|
553
562
|
|
|
554
563
|
// Rail (special case)
|
|
@@ -566,166 +575,120 @@ type RailProps = React.ComponentPropsWithoutRef<'div'> & {
|
|
|
566
575
|
} & (RailControlledProps | RailUncontrolledProps);
|
|
567
576
|
|
|
568
577
|
// Left container - behaves like Inspector but contains Rail+Panel
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
)
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
578
|
+
const LEFT_DOM_OMIT_PROPS = ['open', 'defaultOpen', 'onOpenChange', 'mode', 'defaultMode', 'onModeChange'] as const;
|
|
579
|
+
|
|
580
|
+
const Left = React.forwardRef<HTMLDivElement, LeftProps>((initialProps, ref) => {
|
|
581
|
+
const { className, presentation = { initial: 'fixed', sm: 'fixed' }, collapsible: _collapsible = true, onExpand, onCollapse, children, style, ...restProps } = initialProps;
|
|
582
|
+
const propsOpen = restProps.open;
|
|
583
|
+
const propsDefaultOpen = restProps.defaultOpen;
|
|
584
|
+
const propsOnOpenChange = restProps.onOpenChange;
|
|
585
|
+
const domProps = omitPaneProps(restProps, LEFT_DOM_OMIT_PROPS);
|
|
586
|
+
const shell = useShell();
|
|
587
|
+
const resolvedPresentation = useResponsivePresentation(presentation);
|
|
588
|
+
const isOverlay = resolvedPresentation === 'overlay';
|
|
589
|
+
const isStacked = resolvedPresentation === 'stacked';
|
|
590
|
+
const localRef = React.useRef<HTMLDivElement | null>(null);
|
|
591
|
+
// Publish resolved presentation so Root can gate peeking in overlay
|
|
592
|
+
React.useEffect(() => {
|
|
593
|
+
(shell as any).onLeftPres?.(resolvedPresentation);
|
|
594
|
+
}, [shell, resolvedPresentation]);
|
|
595
|
+
const setRef = React.useCallback(
|
|
596
|
+
(node: HTMLDivElement | null) => {
|
|
597
|
+
localRef.current = node;
|
|
598
|
+
if (typeof ref === 'function') ref(node);
|
|
599
|
+
else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
600
|
+
},
|
|
601
|
+
[ref],
|
|
602
|
+
);
|
|
594
603
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
604
|
+
// Register with shell
|
|
605
|
+
React.useEffect(() => {
|
|
606
|
+
shell.setHasLeft(true);
|
|
607
|
+
return () => shell.setHasLeft(false);
|
|
608
|
+
}, [shell]);
|
|
609
|
+
|
|
610
|
+
const lastLeftModeRef = React.useRef<PaneMode | null>(null);
|
|
611
|
+
const initNotifiedRef = React.useRef(false);
|
|
612
|
+
const normalizedLeftControlled = React.useMemo(() => {
|
|
613
|
+
if (typeof propsOpen === 'undefined') return undefined;
|
|
614
|
+
return propsOpen ? 'expanded' : 'collapsed';
|
|
615
|
+
}, [propsOpen]);
|
|
616
|
+
const normalizedLeftDefault = React.useMemo(() => mapResponsiveBooleanToPaneMode(propsDefaultOpen), [propsDefaultOpen]);
|
|
617
|
+
useResponsiveInitialState<PaneMode>({
|
|
618
|
+
controlledValue: normalizedLeftControlled,
|
|
619
|
+
defaultValue: normalizedLeftDefault,
|
|
620
|
+
currentValue: shell.leftMode,
|
|
621
|
+
setValue: shell.setLeftMode,
|
|
622
|
+
breakpointReady: shell.currentBreakpointReady,
|
|
623
|
+
onInit: (initial) => propsOnOpenChange?.(initial === 'expanded', { reason: 'init' }),
|
|
624
|
+
});
|
|
599
625
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
if (typeof propsDefaultOpen === 'undefined') return;
|
|
609
|
-
didInitFromDefaultOpenRef.current = true;
|
|
610
|
-
const initial = Boolean(resolvedDefaultOpen);
|
|
611
|
-
shell.setLeftMode(initial ? 'expanded' : 'collapsed');
|
|
612
|
-
(props as any).onOpenChange?.(initial, { reason: 'init' });
|
|
613
|
-
}, [shell, propsOpen, propsDefaultOpen, resolvedDefaultOpen, props]);
|
|
614
|
-
React.useEffect(() => {
|
|
615
|
-
// Controlled Left via Rail.open
|
|
616
|
-
if (typeof propsOpen !== 'undefined') {
|
|
617
|
-
const shouldOpen = Boolean(propsOpen);
|
|
618
|
-
shell.setLeftMode(shouldOpen ? 'expanded' : 'collapsed');
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
// defaultOpen is applied in Rail; Left no longer follows responsive defaults
|
|
622
|
-
}, [shell, propsOpen]);
|
|
626
|
+
// Emit mode changes (uncontrolled toggles + init)
|
|
627
|
+
React.useEffect(() => {
|
|
628
|
+
if (typeof propsOpen !== 'undefined') return; // controlled, notifications only via parent changes
|
|
629
|
+
if (lastLeftModeRef.current !== null && lastLeftModeRef.current !== shell.leftMode) {
|
|
630
|
+
propsOnOpenChange?.(shell.leftMode === 'expanded', { reason: 'toggle' });
|
|
631
|
+
}
|
|
632
|
+
lastLeftModeRef.current = shell.leftMode;
|
|
633
|
+
}, [shell, propsOnOpenChange, propsOpen]);
|
|
623
634
|
|
|
624
|
-
|
|
625
|
-
|
|
635
|
+
// Emit expand/collapse events
|
|
636
|
+
React.useEffect(() => {
|
|
637
|
+
if (shell.leftMode === 'expanded') {
|
|
638
|
+
onExpand?.();
|
|
639
|
+
} else {
|
|
640
|
+
onCollapse?.();
|
|
641
|
+
}
|
|
642
|
+
}, [shell.leftMode, onExpand, onCollapse]);
|
|
626
643
|
|
|
627
|
-
|
|
628
|
-
React.useEffect(() => {
|
|
629
|
-
if (typeof (props as any).open !== 'undefined') return; // controlled, notifications only via parent changes
|
|
630
|
-
if (!initNotifiedRef.current && Boolean(resolvedDefaultOpen) && shell.leftMode === 'expanded') {
|
|
631
|
-
(props as any).onOpenChange?.(true, { reason: 'init' });
|
|
632
|
-
initNotifiedRef.current = true;
|
|
633
|
-
}
|
|
634
|
-
if (lastLeftModeRef.current !== null && lastLeftModeRef.current !== shell.leftMode) {
|
|
635
|
-
(props as any).onOpenChange?.(shell.leftMode === 'expanded', { reason: 'toggle' });
|
|
636
|
-
}
|
|
637
|
-
lastLeftModeRef.current = shell.leftMode;
|
|
638
|
-
}, [shell, resolvedDefaultOpen, props]);
|
|
644
|
+
const _isExpanded = shell.leftMode === 'expanded';
|
|
639
645
|
|
|
640
|
-
|
|
641
|
-
React.useEffect(() => {
|
|
642
|
-
if (shell.leftMode === 'expanded') {
|
|
643
|
-
onExpand?.();
|
|
644
|
-
} else {
|
|
645
|
-
onCollapse?.();
|
|
646
|
-
}
|
|
647
|
-
}, [shell.leftMode, onExpand, onCollapse]);
|
|
648
|
-
|
|
649
|
-
const _isExpanded = shell.leftMode === 'expanded';
|
|
650
|
-
|
|
651
|
-
// Left is not resizable; width derives from Rail/Panel.
|
|
652
|
-
|
|
653
|
-
if (isOverlay) {
|
|
654
|
-
const open = shell.leftMode === 'expanded';
|
|
655
|
-
// Compute overlay width from child Rail/Panel expanded sizes
|
|
656
|
-
const childArray = React.Children.toArray(children) as React.ReactElement[];
|
|
657
|
-
const isType = (el: React.ReactElement, comp: any) => React.isValidElement(el) && el.type === comp;
|
|
658
|
-
const railEl = childArray.find((el) => isType(el, Rail));
|
|
659
|
-
const panelEl = childArray.find((el) => isType(el, Panel));
|
|
660
|
-
const railSize = typeof (railEl as any)?.props?.expandedSize === 'number' ? (railEl as any).props.expandedSize : 64;
|
|
661
|
-
const panelSize = typeof (panelEl as any)?.props?.expandedSize === 'number' ? (panelEl as any).props.expandedSize : 288;
|
|
662
|
-
const hasRail = Boolean(railEl);
|
|
663
|
-
const hasPanel = Boolean(panelEl);
|
|
664
|
-
const overlayPx = (hasRail ? railSize : 0) + (shell.panelMode === 'expanded' && hasPanel ? panelSize : 0);
|
|
665
|
-
return (
|
|
666
|
-
<Sheet.Root open={open} onOpenChange={(o) => shell.setLeftMode(o ? 'expanded' : 'collapsed')}>
|
|
667
|
-
<Sheet.Content
|
|
668
|
-
side="start"
|
|
669
|
-
style={{ padding: 0 }}
|
|
670
|
-
width={{
|
|
671
|
-
initial: `${overlayPx}px`,
|
|
672
|
-
}}
|
|
673
|
-
>
|
|
674
|
-
<VisuallyHidden>
|
|
675
|
-
<Sheet.Title>Navigation</Sheet.Title>
|
|
676
|
-
</VisuallyHidden>
|
|
677
|
-
<div className="rt-ShellLeft">{children}</div>
|
|
678
|
-
</Sheet.Content>
|
|
679
|
-
</Sheet.Root>
|
|
680
|
-
);
|
|
681
|
-
}
|
|
646
|
+
// Left is not resizable; width derives from Rail/Panel.
|
|
682
647
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
ref={setRef}
|
|
703
|
-
className={classNames('rt-ShellLeft', className)}
|
|
704
|
-
data-mode={shell.leftMode}
|
|
705
|
-
data-peek={shell.peekTarget === 'left' || shell.peekTarget === 'rail' || shell.peekTarget === 'panel' || undefined}
|
|
706
|
-
data-presentation={resolvedPresentation}
|
|
707
|
-
style={{
|
|
708
|
-
...style,
|
|
648
|
+
if (isOverlay) {
|
|
649
|
+
const open = shell.leftMode === 'expanded';
|
|
650
|
+
// Compute overlay width from child Rail/Panel expanded sizes
|
|
651
|
+
const childArray = React.Children.toArray(children) as React.ReactElement[];
|
|
652
|
+
const isType = (el: React.ReactElement, comp: any) => React.isValidElement(el) && el.type === comp;
|
|
653
|
+
const railEl = childArray.find((el) => isType(el, Rail));
|
|
654
|
+
const panelEl = childArray.find((el) => isType(el, Panel));
|
|
655
|
+
const railSize = typeof (railEl as any)?.props?.expandedSize === 'number' ? (railEl as any).props.expandedSize : 64;
|
|
656
|
+
const panelSize = typeof (panelEl as any)?.props?.expandedSize === 'number' ? (panelEl as any).props.expandedSize : 288;
|
|
657
|
+
const hasRail = Boolean(railEl);
|
|
658
|
+
const hasPanel = Boolean(panelEl);
|
|
659
|
+
const overlayPx = (hasRail ? railSize : 0) + (shell.panelMode === 'expanded' && hasPanel ? panelSize : 0);
|
|
660
|
+
return (
|
|
661
|
+
<Sheet.Root open={open} onOpenChange={(o) => shell.setLeftMode(o ? 'expanded' : 'collapsed')}>
|
|
662
|
+
<Sheet.Content
|
|
663
|
+
side="start"
|
|
664
|
+
style={{ padding: 0 }}
|
|
665
|
+
width={{
|
|
666
|
+
initial: `${overlayPx}px`,
|
|
709
667
|
}}
|
|
710
|
-
data-open={open || undefined}
|
|
711
668
|
>
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
669
|
+
<VisuallyHidden>
|
|
670
|
+
<Sheet.Title>Navigation</Sheet.Title>
|
|
671
|
+
</VisuallyHidden>
|
|
672
|
+
<div className="rt-ShellLeft">{children}</div>
|
|
673
|
+
</Sheet.Content>
|
|
674
|
+
</Sheet.Root>
|
|
675
|
+
);
|
|
676
|
+
}
|
|
716
677
|
|
|
717
|
-
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
678
|
+
if (isStacked) {
|
|
679
|
+
const open = shell.leftMode === 'expanded';
|
|
680
|
+
// Compute floating width from child Rail/Panel expanded sizes (like overlay)
|
|
681
|
+
const childArray = React.Children.toArray(children) as React.ReactElement[];
|
|
682
|
+
const isType = (el: React.ReactElement, comp: any) => React.isValidElement(el) && el.type === comp;
|
|
683
|
+
const railEl = childArray.find((el) => isType(el, Rail));
|
|
684
|
+
const panelEl = childArray.find((el) => isType(el, Panel));
|
|
685
|
+
const _railSize = typeof (railEl as any)?.props?.expandedSize === 'number' ? (railEl as any).props.expandedSize : 64;
|
|
686
|
+
const _panelSize = typeof (panelEl as any)?.props?.expandedSize === 'number' ? (panelEl as any).props.expandedSize : 288;
|
|
687
|
+
const _hasRail = Boolean(railEl);
|
|
688
|
+
const _hasPanel = Boolean(panelEl);
|
|
689
|
+
const _includePanel = _hasPanel && (shell.panelMode === 'expanded' || shell.peekTarget === 'panel');
|
|
728
690
|
|
|
691
|
+
// Strip control props from DOM spread
|
|
729
692
|
return (
|
|
730
693
|
<div
|
|
731
694
|
{...domProps}
|
|
@@ -737,28 +700,48 @@ const Left = React.forwardRef<HTMLDivElement, LeftProps>(
|
|
|
737
700
|
style={{
|
|
738
701
|
...style,
|
|
739
702
|
}}
|
|
703
|
+
data-open={open || undefined}
|
|
740
704
|
>
|
|
741
705
|
{children}
|
|
742
706
|
</div>
|
|
743
707
|
);
|
|
744
|
-
}
|
|
745
|
-
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// Strip control/legacy props from DOM spread
|
|
711
|
+
return (
|
|
712
|
+
<div
|
|
713
|
+
{...domProps}
|
|
714
|
+
ref={setRef}
|
|
715
|
+
className={classNames('rt-ShellLeft', className)}
|
|
716
|
+
data-mode={shell.leftMode}
|
|
717
|
+
data-peek={shell.peekTarget === 'left' || shell.peekTarget === 'rail' || shell.peekTarget === 'panel' || undefined}
|
|
718
|
+
data-presentation={resolvedPresentation}
|
|
719
|
+
style={{
|
|
720
|
+
...style,
|
|
721
|
+
}}
|
|
722
|
+
>
|
|
723
|
+
{children}
|
|
724
|
+
</div>
|
|
725
|
+
);
|
|
726
|
+
});
|
|
746
727
|
Left.displayName = 'Shell.Left';
|
|
728
|
+
assignShellSlot(Left as any, 'Shell.Left');
|
|
747
729
|
|
|
748
|
-
const Rail = React.forwardRef<HTMLDivElement, RailProps>((
|
|
730
|
+
const Rail = React.forwardRef<HTMLDivElement, RailProps>((initialProps, ref) => {
|
|
731
|
+
const { className, presentation, expandedSize = 64, collapsible, onExpand, onCollapse, children, style, open, defaultOpen, onOpenChange, ...domProps } = initialProps;
|
|
749
732
|
const shell = useShell();
|
|
750
733
|
|
|
751
734
|
// Dev guards
|
|
752
735
|
const wasControlledRef = React.useRef<boolean | null>(null);
|
|
753
736
|
if (process.env.NODE_ENV !== 'production') {
|
|
754
|
-
if (typeof
|
|
737
|
+
if (typeof open !== 'undefined' && typeof defaultOpen !== 'undefined') {
|
|
755
738
|
console.error('Shell.Rail: Do not pass both `open` and `defaultOpen`. Choose one.');
|
|
756
739
|
}
|
|
757
740
|
}
|
|
758
741
|
|
|
759
742
|
// Warn on controlled/uncontrolled mode switch
|
|
760
743
|
React.useEffect(() => {
|
|
761
|
-
const isControlled = typeof
|
|
744
|
+
const isControlled = typeof open !== 'undefined';
|
|
762
745
|
if (wasControlledRef.current === null) {
|
|
763
746
|
wasControlledRef.current = isControlled;
|
|
764
747
|
return;
|
|
@@ -767,7 +750,7 @@ const Rail = React.forwardRef<HTMLDivElement, RailProps>(({ className, presentat
|
|
|
767
750
|
console.warn('Shell.Rail: Switching between controlled and uncontrolled `open` is not supported.');
|
|
768
751
|
wasControlledRef.current = isControlled;
|
|
769
752
|
}
|
|
770
|
-
}, [
|
|
753
|
+
}, [open]);
|
|
771
754
|
|
|
772
755
|
// Register expanded size with Left container
|
|
773
756
|
React.useEffect(() => {
|
|
@@ -777,8 +760,6 @@ const Rail = React.forwardRef<HTMLDivElement, RailProps>(({ className, presentat
|
|
|
777
760
|
const isExpanded = shell.leftMode === 'expanded';
|
|
778
761
|
|
|
779
762
|
// Strip unknown open/defaultOpen props from DOM by not spreading them
|
|
780
|
-
const { defaultOpen: _defaultOpenIgnored, open: _openIgnored, onOpenChange: _onOpenChangeIgnored, ...domProps } = props as any;
|
|
781
|
-
|
|
782
763
|
return (
|
|
783
764
|
<div
|
|
784
765
|
{...domProps}
|
|
@@ -798,6 +779,7 @@ const Rail = React.forwardRef<HTMLDivElement, RailProps>(({ className, presentat
|
|
|
798
779
|
);
|
|
799
780
|
});
|
|
800
781
|
Rail.displayName = 'Shell.Rail';
|
|
782
|
+
assignShellSlot(Rail as any, 'Shell.Rail');
|
|
801
783
|
|
|
802
784
|
// Panel
|
|
803
785
|
type HandleComponent = React.ForwardRefExoticComponent<React.ComponentPropsWithoutRef<'div'> & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -836,9 +818,23 @@ type _InspectorComponent = React.ForwardRefExoticComponent<PaneProps & React.Ref
|
|
|
836
818
|
|
|
837
819
|
type _BottomComponent = React.ForwardRefExoticComponent<PaneProps & React.RefAttributes<HTMLDivElement>> & { Handle: HandleComponent };
|
|
838
820
|
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
821
|
+
const PANEL_DOM_PROP_KEYS = [
|
|
822
|
+
'className',
|
|
823
|
+
'children',
|
|
824
|
+
'defaultOpen',
|
|
825
|
+
'open',
|
|
826
|
+
'onOpenChange',
|
|
827
|
+
'size',
|
|
828
|
+
'defaultSize',
|
|
829
|
+
'onSizeChange',
|
|
830
|
+
'sizeUpdate',
|
|
831
|
+
'sizeUpdateMs',
|
|
832
|
+
'style',
|
|
833
|
+
] as const satisfies readonly (keyof PanelPublicProps)[];
|
|
834
|
+
|
|
835
|
+
const Panel = assignShellSlot(
|
|
836
|
+
React.forwardRef<HTMLDivElement, PanelPublicProps>((initialProps, ref) => {
|
|
837
|
+
const {
|
|
842
838
|
className,
|
|
843
839
|
defaultOpen,
|
|
844
840
|
open,
|
|
@@ -865,10 +861,8 @@ const Panel = React.forwardRef<HTMLDivElement, PanelPublicProps>(
|
|
|
865
861
|
onSizeChange,
|
|
866
862
|
sizeUpdate,
|
|
867
863
|
sizeUpdateMs = 50,
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
ref,
|
|
871
|
-
) => {
|
|
864
|
+
} = initialProps;
|
|
865
|
+
const panelDomProps = extractPaneDomProps(initialProps, PANEL_DOM_PROP_KEYS);
|
|
872
866
|
// Throttled/debounced emitter for onSizeChange
|
|
873
867
|
const emitSizeChange = React.useMemo(() => {
|
|
874
868
|
if (!onSizeChange) return () => {};
|
|
@@ -1141,16 +1135,6 @@ const Panel = React.forwardRef<HTMLDivElement, PanelPublicProps>(
|
|
|
1141
1135
|
</PaneResizeContext.Provider>
|
|
1142
1136
|
) : null;
|
|
1143
1137
|
|
|
1144
|
-
// Strip control props from DOM spread
|
|
1145
|
-
const {
|
|
1146
|
-
defaultOpen: _panelDefaultOpenIgnored,
|
|
1147
|
-
open: _panelOpenIgnored,
|
|
1148
|
-
onOpenChange: _panelOnOpenChangeIgnored,
|
|
1149
|
-
size: _panelSizeIgnored,
|
|
1150
|
-
defaultSize: _panelDefaultSizeIgnored,
|
|
1151
|
-
...panelDomProps
|
|
1152
|
-
} = props as any;
|
|
1153
|
-
|
|
1154
1138
|
return (
|
|
1155
1139
|
<div
|
|
1156
1140
|
{...panelDomProps}
|
|
@@ -1170,7 +1154,8 @@ const Panel = React.forwardRef<HTMLDivElement, PanelPublicProps>(
|
|
|
1170
1154
|
{handleEl}
|
|
1171
1155
|
</div>
|
|
1172
1156
|
);
|
|
1173
|
-
},
|
|
1157
|
+
}),
|
|
1158
|
+
'Shell.Panel',
|
|
1174
1159
|
) as PanelComponent;
|
|
1175
1160
|
Panel.displayName = 'Shell.Panel';
|
|
1176
1161
|
Panel.Handle = PanelHandle;
|
|
@@ -1182,6 +1167,7 @@ interface ShellContentProps extends React.ComponentPropsWithoutRef<'main'> {}
|
|
|
1182
1167
|
|
|
1183
1168
|
const Content = React.forwardRef<HTMLElement, ShellContentProps>(({ className, ...props }, ref) => <main {...props} ref={ref} className={classNames('rt-ShellContent', className)} />);
|
|
1184
1169
|
Content.displayName = 'Shell.Content';
|
|
1170
|
+
assignShellSlot(Content as any, 'Shell.Content');
|
|
1185
1171
|
|
|
1186
1172
|
// Inspector moved to ./_internal/shell-inspector
|
|
1187
1173
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
|
|
1
3
|
export type PresentationValue = 'fixed' | 'overlay' | 'stacked';
|
|
2
4
|
|
|
3
5
|
export type ResponsivePresentation = PresentationValue | Partial<Record<'initial' | 'xs' | 'sm' | 'md' | 'lg' | 'xl', PresentationValue>>;
|
|
@@ -16,6 +18,27 @@ export type PaneSizePersistence = {
|
|
|
16
18
|
save?: (size: number) => void | Promise<void>;
|
|
17
19
|
};
|
|
18
20
|
|
|
21
|
+
export interface PaneBaseProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
22
|
+
presentation?: ResponsivePresentation;
|
|
23
|
+
expandedSize?: number;
|
|
24
|
+
minSize?: number;
|
|
25
|
+
maxSize?: number;
|
|
26
|
+
height?: string | number;
|
|
27
|
+
resizable?: boolean;
|
|
28
|
+
collapsible?: boolean;
|
|
29
|
+
onExpand?: () => void;
|
|
30
|
+
onCollapse?: () => void;
|
|
31
|
+
onResize?: (size: number) => void;
|
|
32
|
+
resizer?: React.ReactNode;
|
|
33
|
+
onResizeStart?: (size: number) => void;
|
|
34
|
+
onResizeEnd?: (size: number) => void;
|
|
35
|
+
snapPoints?: number[];
|
|
36
|
+
snapTolerance?: number;
|
|
37
|
+
collapseThreshold?: number;
|
|
38
|
+
paneId?: string;
|
|
39
|
+
persistence?: PaneSizePersistence;
|
|
40
|
+
}
|
|
41
|
+
|
|
19
42
|
export const _BREAKPOINTS = {
|
|
20
43
|
xs: '(min-width: 520px)',
|
|
21
44
|
sm: '(min-width: 768px)',
|