@exegia/corpora-ui 0.19.0 → 0.20.0
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-lib/components/blocks/nav/sidebar/__tests__/use-ai-sidebar.test.d.ts +1 -0
- package/dist-lib/components/blocks/nav/sidebar/ai-sidebar.d.ts +11 -2
- package/dist-lib/components/blocks/nav/sidebar/type.d.ts +83 -1
- package/dist-lib/components/blocks/nav/sidebar/use-ai-sidebar.d.ts +22 -31
- package/dist-lib/components/blocks/nav/sidebar/utils.d.ts +5 -0
- package/dist-lib/components/blocks/scaffold/constants.d.ts +5 -0
- package/dist-lib/components/blocks/scaffold/scaffold-canvas.d.ts +2 -0
- package/dist-lib/components/blocks/scaffold/scaffold-panel.d.ts +1 -1
- package/dist-lib/components/blocks/scaffold/scaffold-tab.d.ts +6 -1
- package/dist-lib/components/blocks/scaffold/type.d.ts +28 -0
- package/dist-lib/components/blocks/scaffold/use-panel-visibility.d.ts +19 -0
- package/dist-lib/components/blocks/scaffold/utils.d.ts +6 -0
- package/dist-lib/components/composed/tree/__tests__/tree.test.d.ts +1 -0
- package/dist-lib/components/composed/tree/__tests__/use-tree.test.d.ts +1 -0
- package/dist-lib/components/composed/tree/constants.d.ts +35 -0
- package/dist-lib/components/composed/tree/index.d.ts +4 -0
- package/dist-lib/components/composed/tree/tree-context.d.ts +4 -0
- package/dist-lib/components/composed/tree/tree-node.d.ts +7 -0
- package/dist-lib/components/composed/tree/tree.d.ts +19 -0
- package/dist-lib/components/composed/tree/type.d.ts +192 -0
- package/dist-lib/components/composed/tree/use-tree-dnd.d.ts +7 -0
- package/dist-lib/components/composed/tree/use-tree.d.ts +20 -0
- package/dist-lib/components/composed/tree/utils.d.ts +28 -0
- package/dist-lib/components/user-avatar/audio-wave.d.ts +2 -0
- package/dist-lib/components/user-avatar/component.d.ts +10 -0
- package/dist-lib/components/user-avatar/fallback.d.ts +5 -0
- package/dist-lib/components/user-avatar/index.d.ts +13 -0
- package/dist-lib/components/user-avatar/type.d.ts +26 -0
- package/dist-lib/components/user-avatar/utils.d.ts +2 -0
- package/dist-lib/index.d.ts +1 -0
- package/dist-lib/index.js +2141 -1139
- package/dist-lib/index.js.map +1 -1
- package/package.json +2 -1
- package/src/components/beste/piece/browser-frame.tsx +9 -6
- package/src/components/blocks/nav/sidebar/__tests__/use-ai-sidebar.test.tsx +310 -0
- package/src/components/blocks/nav/sidebar/ai-sidebar.tsx +90 -60
- package/src/components/blocks/nav/sidebar/type.ts +105 -1
- package/src/components/blocks/nav/sidebar/use-ai-sidebar.ts +293 -116
- package/src/components/blocks/nav/sidebar/utils.ts +28 -0
- package/src/components/blocks/scaffold/__tests__/scaffold.test.tsx +160 -1
- package/src/components/blocks/scaffold/constants.ts +7 -0
- package/src/components/blocks/scaffold/panel-menu-button.tsx +3 -0
- package/src/components/blocks/scaffold/scaffold-canvas.tsx +48 -5
- package/src/components/blocks/scaffold/scaffold-inspector.tsx +3 -2
- package/src/components/blocks/scaffold/scaffold-main.tsx +1 -1
- package/src/components/blocks/scaffold/scaffold-panel.tsx +41 -5
- package/src/components/blocks/scaffold/scaffold-root.tsx +18 -3
- package/src/components/blocks/scaffold/scaffold-sidebar.tsx +1 -1
- package/src/components/blocks/scaffold/scaffold-tab.tsx +45 -7
- package/src/components/blocks/scaffold/type.ts +28 -0
- package/src/components/blocks/scaffold/use-panel-visibility.ts +180 -0
- package/src/components/blocks/scaffold/utils.ts +19 -0
- package/src/components/composed/tree/__tests__/tree.test.tsx +400 -0
- package/src/components/composed/tree/__tests__/use-tree.test.tsx +332 -0
- package/src/components/composed/tree/constants.ts +60 -0
- package/src/components/composed/tree/index.ts +13 -0
- package/src/components/composed/tree/tree-context.ts +13 -0
- package/src/components/composed/tree/tree-node.tsx +451 -0
- package/src/components/composed/tree/tree.tsx +278 -0
- package/src/components/composed/tree/type.ts +212 -0
- package/src/components/composed/tree/use-tree-dnd.ts +148 -0
- package/src/components/composed/tree/use-tree.ts +306 -0
- package/src/components/composed/tree/utils.ts +172 -0
- package/src/components/composed/user-avatar.tsx +4 -1
- package/src/components/docs/component-preview.tsx +11 -11
- package/src/components/user-avatar/audio-wave.tsx +22 -0
- package/src/components/user-avatar/component.tsx +53 -0
- package/src/components/user-avatar/fallback.tsx +26 -0
- package/src/components/user-avatar/index.ts +15 -0
- package/src/components/user-avatar/type.ts +35 -0
- package/src/components/user-avatar/utils.ts +8 -0
- package/src/index.css +211 -153
- package/src/index.ts +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { AISidebarComponentProps } from './type';
|
|
2
|
+
/**
|
|
3
|
+
* Resource tree for the app rail: nested rows with drag-and-drop reorder,
|
|
4
|
+
* inline rename, per-row action menus and full keyboard navigation.
|
|
5
|
+
*
|
|
6
|
+
* Two ways to drive it. Pass the data and handlers as props and the block
|
|
7
|
+
* owns its state, or hold a `useAISidebar` controller and pass it as
|
|
8
|
+
* `controller` — the same rendering, but select, expand, rename, reorder
|
|
9
|
+
* and menu state are then callable from anywhere in your app.
|
|
10
|
+
*/
|
|
11
|
+
export declare function AISidebar(props: AISidebarComponentProps): import("react").JSX.Element;
|
|
@@ -17,7 +17,10 @@ export interface SidebarResourceMenuControls {
|
|
|
17
17
|
close: () => void;
|
|
18
18
|
rename: () => void;
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
/** Options for `useAISidebar`. `items`, `activeId` and `expandedIds` are
|
|
21
|
+
* each controlled when passed and hook-owned via their `default*` twin
|
|
22
|
+
* otherwise. */
|
|
23
|
+
export interface UseAISidebarOptions {
|
|
21
24
|
items?: SidebarResource[];
|
|
22
25
|
defaultItems?: SidebarResource[];
|
|
23
26
|
onItemsChange?: (items: SidebarResource[]) => void;
|
|
@@ -28,7 +31,77 @@ export interface AISidebarProps {
|
|
|
28
31
|
activeId?: string | null;
|
|
29
32
|
defaultActiveId?: string | null;
|
|
30
33
|
onActiveChange?: (id: string) => void;
|
|
34
|
+
expandedIds?: string[];
|
|
31
35
|
defaultExpandedIds?: string[];
|
|
36
|
+
onExpandedChange?: (ids: string[]) => void;
|
|
37
|
+
}
|
|
38
|
+
/** Everything the sidebar can do, callable from outside the block — a
|
|
39
|
+
* toolbar, a command palette, a route change. Returned by `useAISidebar`
|
|
40
|
+
* and accepted by `<AISidebar controller={…} />`. */
|
|
41
|
+
export interface AISidebarController {
|
|
42
|
+
items: SidebarResource[];
|
|
43
|
+
/** Visible rows in order, each with its depth and parent id. */
|
|
44
|
+
flat: FlatResource[];
|
|
45
|
+
getItem: (id: string) => SidebarResource | undefined;
|
|
46
|
+
selectedId: string | null;
|
|
47
|
+
select: (id: string) => void;
|
|
48
|
+
expandedIds: ReadonlySet<string>;
|
|
49
|
+
isExpanded: (id: string) => boolean;
|
|
50
|
+
expand: (id: string) => void;
|
|
51
|
+
collapse: (id: string) => void;
|
|
52
|
+
toggleExpanded: (id: string) => void;
|
|
53
|
+
expandAll: () => void;
|
|
54
|
+
collapseAll: () => void;
|
|
55
|
+
/** Open every ancestor of `id`, leaving the rest of the tree alone. */
|
|
56
|
+
reveal: (id: string) => void;
|
|
57
|
+
/** Roving-focus target. `focus` also moves DOM focus to the row. */
|
|
58
|
+
focusedId: string | null;
|
|
59
|
+
focus: (id: string) => void;
|
|
60
|
+
renamingId: string | null;
|
|
61
|
+
startRename: (id: string) => void;
|
|
62
|
+
cancelRename: () => void;
|
|
63
|
+
/** Commit a rename and leave rename mode. Applied optimistically and
|
|
64
|
+
* rolled back if `onRename` rejects. Blank and unchanged labels are
|
|
65
|
+
* dropped. */
|
|
66
|
+
rename: (id: string, label: string) => void;
|
|
67
|
+
menuOpenId: string | null;
|
|
68
|
+
openMenu: (id: string) => void;
|
|
69
|
+
closeMenu: () => void;
|
|
70
|
+
/** Reorder, applied optimistically and rolled back if `onMove` rejects.
|
|
71
|
+
* Refuses moves the data forbids (into itself, into a non-container). */
|
|
72
|
+
move: (move: SidebarResourceMove) => Promise<void>;
|
|
73
|
+
/** A move is in flight — further moves are refused until it settles. */
|
|
74
|
+
movePending: boolean;
|
|
75
|
+
/** Live-region text for the last move or rename outcome. */
|
|
76
|
+
announcement: string;
|
|
77
|
+
/** @internal Row wiring the view threads through — drag state, the
|
|
78
|
+
* hover pill and roving keyboard nav all belong to the rendering. */
|
|
79
|
+
dnd: AISidebarDndState;
|
|
80
|
+
hover: AISidebarHoverState;
|
|
81
|
+
onRowKeyDown: (event: KeyboardEvent<HTMLDivElement>, row: FlatResource) => void;
|
|
82
|
+
setRowRef: (id: string, node: HTMLDivElement | null) => void;
|
|
83
|
+
}
|
|
84
|
+
/** @internal */
|
|
85
|
+
export interface AISidebarDndState {
|
|
86
|
+
draggingId: string | null;
|
|
87
|
+
dropTarget: DropTarget | null;
|
|
88
|
+
onRootDragOver: (event: DragEvent<HTMLDivElement>) => void;
|
|
89
|
+
onDrop: (event: DragEvent<HTMLDivElement>) => void;
|
|
90
|
+
onRowDragStart: (event: DragEvent<HTMLDivElement>, id: string) => void;
|
|
91
|
+
onRowDragEnd: () => void;
|
|
92
|
+
onRowDragOver: (event: DragEvent<HTMLDivElement>, row: FlatResource) => void;
|
|
93
|
+
}
|
|
94
|
+
/** @internal */
|
|
95
|
+
export interface AISidebarHoverState {
|
|
96
|
+
hoveredId: string | null;
|
|
97
|
+
/** Shared motion layoutId that lets the pill travel between rows. */
|
|
98
|
+
layoutId: string;
|
|
99
|
+
onRowHover: (id: string, hovered: boolean) => void;
|
|
100
|
+
clear: () => void;
|
|
101
|
+
}
|
|
102
|
+
export interface AISidebarProps extends UseAISidebarOptions {
|
|
103
|
+
/** Never set on the data form — `controller` selects the other one. */
|
|
104
|
+
controller?: never;
|
|
32
105
|
renderIcon?: (item: SidebarResource) => ReactNode;
|
|
33
106
|
renderMenu?: (item: SidebarResource, controls: SidebarResourceMenuControls) => ReactNode;
|
|
34
107
|
/**
|
|
@@ -39,6 +112,15 @@ export interface AISidebarProps {
|
|
|
39
112
|
ariaLabel?: string;
|
|
40
113
|
className?: string;
|
|
41
114
|
}
|
|
115
|
+
/** Presentational props, shared by both forms of `<AISidebar>`. */
|
|
116
|
+
export type AISidebarViewProps = Pick<AISidebarProps, "renderIcon" | "renderMenu" | "renderActionsTrigger" | "ariaLabel" | "className">;
|
|
117
|
+
/** Drive the block from a `useAISidebar` controller instead of raw props —
|
|
118
|
+
* the controller carries the data and every handler. */
|
|
119
|
+
export interface AISidebarControllerProps extends AISidebarViewProps {
|
|
120
|
+
controller: AISidebarController;
|
|
121
|
+
}
|
|
122
|
+
/** Either form: raw props, or a `useAISidebar` controller. */
|
|
123
|
+
export type AISidebarComponentProps = AISidebarProps | AISidebarControllerProps;
|
|
42
124
|
export interface FlatResource {
|
|
43
125
|
item: SidebarResource;
|
|
44
126
|
depth: number;
|
|
@@ -1,31 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
handleRootDragOver: (event: DragEvent<HTMLDivElement>) => void;
|
|
24
|
-
handleDrop: (event: DragEvent<HTMLDivElement>) => void;
|
|
25
|
-
handleRowDragStart: (event: DragEvent<HTMLDivElement>, id: string) => void;
|
|
26
|
-
handleRowDragEnd: () => void;
|
|
27
|
-
handleRowDragOver: (event: DragEvent<HTMLDivElement>, targetRow: FlatResource) => void;
|
|
28
|
-
commitRename: (row: FlatResource, label: string) => void;
|
|
29
|
-
handleMenuOpenChange: (row: FlatResource, open: boolean) => void;
|
|
30
|
-
setRowRef: (id: string, node: HTMLDivElement | null) => void;
|
|
31
|
-
};
|
|
1
|
+
import { AISidebarController, UseAISidebarOptions } from './type.ts';
|
|
2
|
+
export type { UseAISidebarOptions };
|
|
3
|
+
/**
|
|
4
|
+
* The AI sidebar's headless controller — every behaviour the block
|
|
5
|
+
* performs, callable from outside it: select, expand/collapse (one row,
|
|
6
|
+
* every row, or just the ancestors of one), move focus, rename, open a
|
|
7
|
+
* row's action menu, reorder.
|
|
8
|
+
*
|
|
9
|
+
* `items`, `activeId` and `expandedIds` are each controlled when passed
|
|
10
|
+
* and hook-owned via their `default*` twin otherwise. Reorder and rename
|
|
11
|
+
* are applied to the data optimistically either way and rolled back if
|
|
12
|
+
* `onMove`/`onRename` rejects, so a consumer never reduces the tree
|
|
13
|
+
* itself — `moveResource` and `renameResource` are exported for the ones
|
|
14
|
+
* who keep their data elsewhere.
|
|
15
|
+
*
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const sidebar = useAISidebar({ defaultItems: resources })
|
|
18
|
+
* <AISidebar controller={sidebar} />
|
|
19
|
+
* <Button onClick={sidebar.collapseAll}>Collapse all</Button>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function useAISidebar(options: UseAISidebarOptions): AISidebarController;
|
|
@@ -6,6 +6,11 @@ export declare const ROW_REVEAL: {
|
|
|
6
6
|
export declare function canContain(item: SidebarResource): boolean;
|
|
7
7
|
export declare function flattenResources(items: SidebarResource[], expanded: Set<string>, depth?: number, parentId?: string | null): FlatResource[];
|
|
8
8
|
export declare function findResource(items: SidebarResource[], id: string): SidebarResource | undefined;
|
|
9
|
+
/** Ids of every ancestor of `id` (nearest last); empty when absent. */
|
|
10
|
+
export declare function ancestorIdsOf(items: SidebarResource[], id: string): string[];
|
|
11
|
+
/** Ids of every row that can hold children and has some — the set
|
|
12
|
+
* `expandAll` opens. */
|
|
13
|
+
export declare function expandableIdsOf(items: SidebarResource[]): string[];
|
|
9
14
|
export declare function containsResource(item: SidebarResource, id: string): boolean;
|
|
10
15
|
export declare function removeResource(items: SidebarResource[], id: string): {
|
|
11
16
|
items: SidebarResource[];
|
|
@@ -5,6 +5,11 @@ export declare const SCAFFOLD_INSPECTOR_WIDTH = 272;
|
|
|
5
5
|
/** Most panels the canvas holds at once — omit Actions' `onAdd` when the
|
|
6
6
|
* count reaches it so the Add segment hides. */
|
|
7
7
|
export declare const SCAFFOLD_PANEL_CAPACITY = 3;
|
|
8
|
+
/** Narrowest a panel may render, in px. When the canvas can't grant every
|
|
9
|
+
* panel this width, the scaffold hides panels until the rest fit. */
|
|
10
|
+
export declare const SCAFFOLD_PANEL_MIN_WIDTH = 320;
|
|
11
|
+
/** Gap in px between side-by-side panels on the canvas (`gap-2`). */
|
|
12
|
+
export declare const SCAFFOLD_CANVAS_GAP = 8;
|
|
8
13
|
/** Gap in px between the drawer/actions and the viewport edges. */
|
|
9
14
|
export declare const SCAFFOLD_EDGE_GUTTER = 10;
|
|
10
15
|
/** Vertical room in px reserved above the canvas for the actions cluster. */
|
|
@@ -3,5 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
/**
|
|
4
4
|
* The panel row below the action cluster: 8px gutters, 8px gaps, panels
|
|
5
5
|
* side by side. Key each `Scaffold.Panel` child so closes animate out.
|
|
6
|
+
* Measures its own width and hides id'd panels (oldest activation first)
|
|
7
|
+
* whenever it can't grant each one `SCAFFOLD_PANEL_MIN_WIDTH`.
|
|
6
8
|
*/
|
|
7
9
|
export declare function ScaffoldCanvas({ className, children, ...rest }: ScaffoldCanvasProps): React.ReactElement;
|
|
@@ -7,4 +7,4 @@ import * as React from "react";
|
|
|
7
7
|
* the two cards with a layout morph — the strip glides up and grows into the
|
|
8
8
|
* primary slot while the primary card shrinks down into the strip.
|
|
9
9
|
*/
|
|
10
|
-
export declare function ScaffoldPanel({ children, SecondaryPanel, onSwap, onCloseSecondary, name, swapLabel, sound, className, }: ScaffoldPanelProps): React.ReactElement;
|
|
10
|
+
export declare function ScaffoldPanel({ id, children, SecondaryPanel, onSwap, onCloseSecondary, name, swapLabel, sound, className, }: ScaffoldPanelProps): React.ReactElement;
|
|
@@ -5,5 +5,10 @@ import * as React from "react";
|
|
|
5
5
|
* affordance. Render one per open panel as Actions children, keyed —
|
|
6
6
|
* closes animate out of the pill. Omit `onClose` on the last remaining
|
|
7
7
|
* panel's tab; the canvas keeps at least one panel open.
|
|
8
|
+
*
|
|
9
|
+
* With `panelId` (matching a `Scaffold.Panel`'s `id`), the tab also fronts
|
|
10
|
+
* that panel's visibility: a hidden panel dims its tab behind an eye-off
|
|
11
|
+
* icon, and pressing the label toggles the panel — showing one past
|
|
12
|
+
* capacity hides the least-recently-activated panel in its place.
|
|
8
13
|
*/
|
|
9
|
-
export declare function ScaffoldTab({ children, onClose, closeLabel, sound, className, ...rest }: ScaffoldTabProps): React.ReactElement;
|
|
14
|
+
export declare function ScaffoldTab({ children, panelId, onClose, closeLabel, sound, className, ...rest }: ScaffoldTabProps): React.ReactElement;
|
|
@@ -7,6 +7,25 @@ export interface ScaffoldContextValue {
|
|
|
7
7
|
inspectorWidth: number;
|
|
8
8
|
setInspectorOpen: (open: boolean) => void;
|
|
9
9
|
toggleInspector: () => void;
|
|
10
|
+
/** How many panels currently fit side by side, from the measured canvas
|
|
11
|
+
* width at `SCAFFOLD_PANEL_MIN_WIDTH` per panel (1..`SCAFFOLD_PANEL_CAPACITY`). */
|
|
12
|
+
panelCapacity: number;
|
|
13
|
+
/** Ids of id'd panels the scaffold is currently hiding — auto-hidden by
|
|
14
|
+
* capacity pressure or toggled away from a tab. */
|
|
15
|
+
hiddenPanelIds: readonly string[];
|
|
16
|
+
isPanelHidden: (id: string) => boolean;
|
|
17
|
+
/** Show/hide an id'd panel. Showing past capacity auto-hides the
|
|
18
|
+
* least-recently-activated visible panel; the last visible one never hides. */
|
|
19
|
+
togglePanelVisibility: (id: string) => void;
|
|
20
|
+
/** Id of the panel whose tab the pointer is resting on — the canvas
|
|
21
|
+
* spotlights it by fading every other id'd panel to 0.35 opacity. */
|
|
22
|
+
hoveredPanelId: string | null;
|
|
23
|
+
/** @internal Tabs report pointer enter/leave on their label. */
|
|
24
|
+
setPanelHovered: (id: string, hovered: boolean) => void;
|
|
25
|
+
/** @internal Canvas reports its ordered panel ids. */
|
|
26
|
+
registerPanelIds: (ids: readonly string[]) => void;
|
|
27
|
+
/** @internal Canvas reports its measured width. */
|
|
28
|
+
setCanvasWidth: (width: number) => void;
|
|
10
29
|
}
|
|
11
30
|
export interface ScaffoldRootProps extends ComponentProps<"div"> {
|
|
12
31
|
/** Controlled inspector state — pair with `onInspectorOpenChange`, or
|
|
@@ -41,6 +60,11 @@ export interface ScaffoldActionsProps extends MotionSafe<Omit<ComponentProps<"di
|
|
|
41
60
|
}
|
|
42
61
|
export type TScaffoldPanelChild<T extends ScaffoldSubPanelProps = ScaffoldSubPanelProps> = ReactElement<T, React.JSXElementConstructor<T>>;
|
|
43
62
|
export interface ScaffoldPanelProps {
|
|
63
|
+
/** Stable id opting the panel into responsive hiding: the canvas hides
|
|
64
|
+
* id'd panels when it can't grant each `SCAFFOLD_PANEL_MIN_WIDTH`, and a
|
|
65
|
+
* `Scaffold.Tab` with the matching `panelId` reflects and toggles it.
|
|
66
|
+
* Omitted, the panel always renders. */
|
|
67
|
+
id?: string;
|
|
44
68
|
/** The panel's content, rendered in a card with a drop shadow. */
|
|
45
69
|
children: ReactNode;
|
|
46
70
|
/** The panel's content, rendered in a card with a drop shadow. */
|
|
@@ -64,6 +88,10 @@ export interface ScaffoldPanelProps {
|
|
|
64
88
|
export interface ScaffoldTabProps extends MotionSafe<Omit<ComponentProps<"div">, "children">> {
|
|
65
89
|
/** The tab's label. */
|
|
66
90
|
children?: ReactNode;
|
|
91
|
+
/** Id of the panel this tab fronts (`Scaffold.Panel`'s `id`). The tab
|
|
92
|
+
* then shows whether the panel is hidden and pressing its label toggles
|
|
93
|
+
* visibility — showing past capacity hides another panel in its place. */
|
|
94
|
+
panelId?: string;
|
|
67
95
|
/** Renders the tab's close button. The button renders only while this
|
|
68
96
|
* is present — omit it to pin the tab, e.g. on the last remaining
|
|
69
97
|
* panel (the canvas keeps at least one open). */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface PanelVisibility {
|
|
2
|
+
/** How many panels currently fit side by side (1..capacity cap). */
|
|
3
|
+
panelCapacity: number;
|
|
4
|
+
/** Ids of panels currently hidden (auto + user), for tabs to reflect. */
|
|
5
|
+
hiddenPanelIds: readonly string[];
|
|
6
|
+
isPanelHidden: (id: string) => boolean;
|
|
7
|
+
togglePanelVisibility: (id: string) => void;
|
|
8
|
+
/** Id of the panel whose tab is hovered — the canvas spotlights it. */
|
|
9
|
+
hoveredPanelId: string | null;
|
|
10
|
+
/** @internal Tabs report pointer enter/leave on their label. */
|
|
11
|
+
setPanelHovered: (id: string, hovered: boolean) => void;
|
|
12
|
+
/** @internal Canvas reports its ordered panel ids each render. */
|
|
13
|
+
registerPanelIds: (ids: readonly string[]) => void;
|
|
14
|
+
/** @internal Canvas reports its measured width. */
|
|
15
|
+
setCanvasWidth: (width: number) => void;
|
|
16
|
+
}
|
|
17
|
+
/** Owns which panels are hidden as the canvas gains and loses room.
|
|
18
|
+
* Lives in `Scaffold.Root`; Canvas and Tab reach it through context. */
|
|
19
|
+
export declare function usePanelVisibility(): PanelVisibility;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { Variants } from 'motion/react';
|
|
2
2
|
import { ClassNameValue } from 'tailwind-merge';
|
|
3
3
|
import { TSubPanelVariant } from './type.ts';
|
|
4
|
+
/**
|
|
5
|
+
* How many panels fit on a canvas of `width` px, granting each
|
|
6
|
+
* `SCAFFOLD_PANEL_MIN_WIDTH` plus the gaps between them. Clamped to
|
|
7
|
+
* [1, SCAFFOLD_PANEL_CAPACITY]; an unmeasured canvas (`null`) fits the cap.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getPanelCapacity(width: number | null): number;
|
|
4
10
|
/** Desktop backdrop the whole scaffold sits on — a soft warm-gray wash. */
|
|
5
11
|
export declare const scaffoldBackgroundClass: ClassNameValue;
|
|
6
12
|
/** Card surface shared by a panel's primary area and secondary strip. */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Transition, Variants } from 'motion/react';
|
|
2
|
+
/** `--ease-smooth-out` as a motion/react easing tuple. */
|
|
3
|
+
export declare const TREE_EASE: readonly [0.22, 1, 0.36, 1];
|
|
4
|
+
/** Branch open — `--duration-fast` territory (accordion open). */
|
|
5
|
+
export declare const TREE_OPEN_DURATION = 0.25;
|
|
6
|
+
/** Branch close — quicker than open, per open/close asymmetry. */
|
|
7
|
+
export declare const TREE_CLOSE_DURATION = 0.15;
|
|
8
|
+
/** Chevron flip and label fades. */
|
|
9
|
+
export declare const TREE_MICRO_DURATION = 0.15;
|
|
10
|
+
/** Sidebar rail label reveal/hide when `collapsed` flips. */
|
|
11
|
+
export declare const TREE_COLLAPSE_DURATION = 0.2;
|
|
12
|
+
/** The collapsed rail's width in px — an icon row plus its padding. Kept in
|
|
13
|
+
* sync with the `w-11` resting class the rail falls back to before its
|
|
14
|
+
* expanded width has been measured. */
|
|
15
|
+
export declare const RAIL_COLLAPSED_WIDTH = 44;
|
|
16
|
+
/** Rail label text fade — starts as the width growth is finishing, so the
|
|
17
|
+
* label never reads as clipped mid-grow. */
|
|
18
|
+
export declare const TREE_LABEL_REVEAL_DELAY = 0.01;
|
|
19
|
+
/** A branch's children group: grows open with a soft stagger, folds shut
|
|
20
|
+
* faster. Height rides layout ("auto"), paint fades. */
|
|
21
|
+
export declare const TREE_BRANCH_VARIANTS: Variants;
|
|
22
|
+
/** Branch close — a plain target, NOT the "closed" variant label: a
|
|
23
|
+
* labeled exit orchestrates every variant child and AnimatePresence then
|
|
24
|
+
* waits on the whole subtree before unmounting. */
|
|
25
|
+
export declare const TREE_BRANCH_EXIT: {
|
|
26
|
+
height: number;
|
|
27
|
+
opacity: number;
|
|
28
|
+
transition: {
|
|
29
|
+
duration: number;
|
|
30
|
+
ease: readonly [0.22, 1, 0.36, 1];
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
/** One row inside an opening branch — a short blurred rise. */
|
|
34
|
+
export declare const TREE_ROW_VARIANTS: Variants;
|
|
35
|
+
export declare const TREE_MICRO_TRANSITION: Transition;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { TreeProps } from './type';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* A nested item tree with four shapes: `navigation` (app nav — 3-level
|
|
5
|
+
* data promotes the top level to collapsible section names), `toc`
|
|
6
|
+
* (routes on top, in-page `#` anchors at the leaves), `sidebar` (a
|
|
7
|
+
* single-level rail that collapses to icons) and `files` (a compact file
|
|
8
|
+
* explorer with rename, drag-and-drop and trailing row actions).
|
|
9
|
+
*
|
|
10
|
+
* Two ways to drive it. Data-driven like the sidebar block: pass `items`,
|
|
11
|
+
* wire `onNavigate` into your router. Or hold a `useTree` controller and
|
|
12
|
+
* pass it as `tree` — same rendering, but expand, collapse, select, rename
|
|
13
|
+
* and reorder are then callable from anywhere in your app.
|
|
14
|
+
*
|
|
15
|
+
* Branches expand with a soft height morph and row stagger; the rail's
|
|
16
|
+
* labels fold away when `collapsed` flips. Arrow keys walk visible rows,
|
|
17
|
+
* expand and collapse; F2 renames in `files`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function Tree(props: TreeProps): React.ReactElement;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
/** One node in the tree. Nesting depth carries meaning per variant — see
|
|
3
|
+
* `TreeProps["variant"]`. */
|
|
4
|
+
export interface TreeNode {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
/** Leading icon. Required in practice for `sidebar` — it is all that
|
|
8
|
+
* remains of a row while the rail is collapsed. */
|
|
9
|
+
icon?: React.ReactNode;
|
|
10
|
+
/** Renders an anchor. Without one the row is a button; `toc` leaves
|
|
11
|
+
* default to `#{id}` so they scroll to the matching heading. */
|
|
12
|
+
href?: string;
|
|
13
|
+
target?: "_blank" | "_self" | "_parent" | "_top";
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** Start expanded. Ancestors of `activeId` expand regardless. */
|
|
16
|
+
defaultOpen?: boolean;
|
|
17
|
+
/** Trailing hint — a count, a "New" pill. */
|
|
18
|
+
badge?: React.ReactNode;
|
|
19
|
+
/** Fires on selection, before the tree-level `onNavigate`. */
|
|
20
|
+
onSelect?: () => void;
|
|
21
|
+
children?: TreeNode[];
|
|
22
|
+
}
|
|
23
|
+
export type TreeVariant = "navigation" | "toc" | "sidebar" | "files";
|
|
24
|
+
/** Where a dragged row lands relative to its drop target. */
|
|
25
|
+
export type TreeDropPosition = "before" | "after" | "inside";
|
|
26
|
+
interface TreeBaseProps {
|
|
27
|
+
items: TreeNode[];
|
|
28
|
+
/** Never set on the data form — `tree` selects the controller form. */
|
|
29
|
+
tree?: never;
|
|
30
|
+
/** `id` of the current entry — matches any depth. Its ancestors expand. */
|
|
31
|
+
activeId?: string;
|
|
32
|
+
/** Fires for every selection (rows and leaves alike), after the row's
|
|
33
|
+
* anchor default — wire your router's navigate here. */
|
|
34
|
+
onNavigate?: (node: TreeNode) => void;
|
|
35
|
+
/** Expand/collapse cues. Silent until `bindSounds()`. */
|
|
36
|
+
sound?: boolean;
|
|
37
|
+
ariaLabel?: string;
|
|
38
|
+
className?: string;
|
|
39
|
+
}
|
|
40
|
+
interface TreeReadonlyProps {
|
|
41
|
+
collapsed?: never;
|
|
42
|
+
onMove?: never;
|
|
43
|
+
onRename?: never;
|
|
44
|
+
renderTrailing?: never;
|
|
45
|
+
}
|
|
46
|
+
/** Drive the tree from a `useTree` controller instead of raw props. The
|
|
47
|
+
* controller carries the variant, the data and every handler; only the
|
|
48
|
+
* presentational props stay here. */
|
|
49
|
+
export interface TreeControllerProps {
|
|
50
|
+
tree: TreeController;
|
|
51
|
+
items?: never;
|
|
52
|
+
ariaLabel?: string;
|
|
53
|
+
className?: string;
|
|
54
|
+
/** Row actions revealed on hover/focus (`files` only — ignored by the
|
|
55
|
+
* other variants, which the controller form cannot type-gate). */
|
|
56
|
+
renderTrailing?: (node: TreeNode) => React.ReactNode;
|
|
57
|
+
}
|
|
58
|
+
/** Either form: raw props, or a `useTree` controller via `tree`. */
|
|
59
|
+
export type TreeProps = TreeDataProps | TreeControllerProps;
|
|
60
|
+
/** The four shapes a Tree takes. Editing props only exist on `files`;
|
|
61
|
+
* `collapsed` only on `sidebar` — the union is the contract. */
|
|
62
|
+
export type TreeDataProps = TreeBaseProps & (({
|
|
63
|
+
/** Nested app navigation. With 3 levels of nodes the top level
|
|
64
|
+
* becomes collapsible section names (styled as headings, not
|
|
65
|
+
* links); with 2 it renders plain link rows. */
|
|
66
|
+
variant: "navigation";
|
|
67
|
+
} & TreeReadonlyProps) | ({
|
|
68
|
+
/** Table of contents: top-level nodes are routes, leaf nodes are
|
|
69
|
+
* in-page anchors (`#{id}` unless the node carries an `href`). */
|
|
70
|
+
variant: "toc";
|
|
71
|
+
} & TreeReadonlyProps) | ({
|
|
72
|
+
/** Single-level icon rail. `collapsed` shrinks rows to their
|
|
73
|
+
* leading icon; nested children are ignored. */
|
|
74
|
+
variant: "sidebar";
|
|
75
|
+
collapsed?: boolean;
|
|
76
|
+
} & Omit<TreeReadonlyProps, "collapsed">) | {
|
|
77
|
+
/** File explorer: only leaves navigate, folders toggle. Compact
|
|
78
|
+
* spacing; rename, drag-and-drop and trailing actions enabled. */
|
|
79
|
+
variant: "files";
|
|
80
|
+
/** Enables drag-and-drop. Reorder `items` yourself: `parentId`
|
|
81
|
+
* `null` means the root list, `index` is the slot among the new
|
|
82
|
+
* siblings (computed on the list without the dragged node). */
|
|
83
|
+
onMove?: (id: string, parentId: string | null, index: number) => void;
|
|
84
|
+
/** Enables inline rename (double-click or F2). */
|
|
85
|
+
onRename?: (id: string, label: string) => void;
|
|
86
|
+
/** Row actions revealed on hover/focus — a menu, a delete icon. */
|
|
87
|
+
renderTrailing?: (node: TreeNode) => React.ReactNode;
|
|
88
|
+
collapsed?: never;
|
|
89
|
+
});
|
|
90
|
+
/** Options for `useTree`. Each of `items`, `activeId` and `collapsed` is
|
|
91
|
+
* controlled when passed and hook-owned when its `default*` twin is used
|
|
92
|
+
* instead. */
|
|
93
|
+
export interface UseTreeOptions {
|
|
94
|
+
/** Which shape the tree takes — gates rename/reorder (`files`) and the
|
|
95
|
+
* collapsible rail (`sidebar`). */
|
|
96
|
+
variant: TreeVariant;
|
|
97
|
+
/** Controlled data. With it, `rename`/`move` only report the edit unless
|
|
98
|
+
* you also pass `onItemsChange`. */
|
|
99
|
+
items?: TreeNode[];
|
|
100
|
+
/** Hook-owned data — `rename` and `move` apply the edit themselves. */
|
|
101
|
+
defaultItems?: TreeNode[];
|
|
102
|
+
/** Next tree after a `rename`/`move`. Also enables self-applying edits
|
|
103
|
+
* on top of controlled `items`. */
|
|
104
|
+
onItemsChange?: (items: TreeNode[]) => void;
|
|
105
|
+
/** Controlled selection. Ancestors of the active node expand. */
|
|
106
|
+
activeId?: string;
|
|
107
|
+
defaultActiveId?: string;
|
|
108
|
+
/** Fires for every selection (rows and leaves alike), after the node's
|
|
109
|
+
* own `onSelect` — wire your router's navigate here. */
|
|
110
|
+
onNavigate?: (node: TreeNode) => void;
|
|
111
|
+
/** Start with these ids expanded instead of the `defaultOpen` set. */
|
|
112
|
+
defaultExpandedIds?: Iterable<string>;
|
|
113
|
+
onExpandedChange?: (ids: string[]) => void;
|
|
114
|
+
/** Controlled rail fold (`sidebar` only). */
|
|
115
|
+
collapsed?: boolean;
|
|
116
|
+
defaultCollapsed?: boolean;
|
|
117
|
+
onCollapsedChange?: (collapsed: boolean) => void;
|
|
118
|
+
/** Enables rename (`files`). Not needed when the hook owns the data. */
|
|
119
|
+
onRename?: (id: string, label: string) => void;
|
|
120
|
+
/** Enables drag-and-drop (`files`). `parentId` `null` means the root
|
|
121
|
+
* list, `index` is the slot among the new siblings (computed on the list
|
|
122
|
+
* without the moved node). Not needed when the hook owns the data. */
|
|
123
|
+
onMove?: (id: string, parentId: string | null, index: number) => void;
|
|
124
|
+
/** Expand/collapse cues. Silent until `bindSounds()`. */
|
|
125
|
+
sound?: boolean;
|
|
126
|
+
}
|
|
127
|
+
/** Everything the tree can do, callable from outside the component.
|
|
128
|
+
* Returned by `useTree` and accepted by `<Tree tree={…} />`. */
|
|
129
|
+
export interface TreeController {
|
|
130
|
+
variant: TreeVariant;
|
|
131
|
+
items: TreeNode[];
|
|
132
|
+
/** `navigation` with 3 levels of nodes — depth 0 renders as sections. */
|
|
133
|
+
sectioned: boolean;
|
|
134
|
+
/** Ids of the section headings, empty unless `sectioned`. Pass one to
|
|
135
|
+
* `expand`/`collapse`/`toggleExpanded` to work a whole section. */
|
|
136
|
+
sectionIds: string[];
|
|
137
|
+
sound: boolean;
|
|
138
|
+
getNode: (id: string) => TreeNode | null;
|
|
139
|
+
expandedIds: ReadonlySet<string>;
|
|
140
|
+
isExpanded: (id: string) => boolean;
|
|
141
|
+
expand: (id: string) => void;
|
|
142
|
+
collapse: (id: string) => void;
|
|
143
|
+
toggleExpanded: (id: string) => void;
|
|
144
|
+
expandAll: () => void;
|
|
145
|
+
collapseAll: () => void;
|
|
146
|
+
/** Open every ancestor of `id`, leaving the rest of the tree alone. */
|
|
147
|
+
reveal: (id: string) => void;
|
|
148
|
+
/** Rail folded to icons — always `false` outside `sidebar`. */
|
|
149
|
+
collapsed: boolean;
|
|
150
|
+
setCollapsed: (collapsed: boolean) => void;
|
|
151
|
+
toggleCollapsed: () => void;
|
|
152
|
+
activeId?: string;
|
|
153
|
+
/** Select a node: runs its `onSelect`, then `onNavigate`. Inert on
|
|
154
|
+
* disabled and unknown nodes. */
|
|
155
|
+
select: (id: string) => void;
|
|
156
|
+
/** Whether rename is wired up at all (`files` + a handler or owned data). */
|
|
157
|
+
canRename: boolean;
|
|
158
|
+
/** Rename target id, `null` while idle. */
|
|
159
|
+
renamingId: string | null;
|
|
160
|
+
startRename: (id: string) => void;
|
|
161
|
+
cancelRename: () => void;
|
|
162
|
+
/** Commit a rename and leave rename mode. Blank or unchanged labels are
|
|
163
|
+
* dropped. */
|
|
164
|
+
rename: (id: string, label: string) => void;
|
|
165
|
+
/** Whether reorder is wired up at all. */
|
|
166
|
+
canMove: boolean;
|
|
167
|
+
move: (id: string, parentId: string | null, index: number) => void;
|
|
168
|
+
dnd: TreeDndContextValue;
|
|
169
|
+
}
|
|
170
|
+
/** @internal What a row needs from the root, threaded through context so
|
|
171
|
+
* the recursive rows stay prop-light. */
|
|
172
|
+
export interface TreeContextValue {
|
|
173
|
+
tree: TreeController;
|
|
174
|
+
renderTrailing?: (node: TreeNode) => React.ReactNode;
|
|
175
|
+
}
|
|
176
|
+
/** @internal Drag state shared by every row (all `null`/no-op outside
|
|
177
|
+
* `files`). */
|
|
178
|
+
export interface TreeDndContextValue {
|
|
179
|
+
enabled: boolean;
|
|
180
|
+
draggedId: string | null;
|
|
181
|
+
/** Row the pointer is over and where the drop would land. */
|
|
182
|
+
dropTarget: {
|
|
183
|
+
id: string;
|
|
184
|
+
position: TreeDropPosition;
|
|
185
|
+
} | null;
|
|
186
|
+
onRowDragStart: (event: React.DragEvent, id: string) => void;
|
|
187
|
+
onRowDragOver: (event: React.DragEvent, node: TreeNode) => void;
|
|
188
|
+
onRowDragLeave: (event: React.DragEvent) => void;
|
|
189
|
+
onRowDrop: (event: React.DragEvent, node: TreeNode) => void;
|
|
190
|
+
onRowDragEnd: () => void;
|
|
191
|
+
}
|
|
192
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TreeDndContextValue, TreeNode, UseTreeOptions } from './type';
|
|
2
|
+
/** Owns drag state for the `files` variant: which row is in flight, which
|
|
3
|
+
* row the pointer rests on and where the drop would land. Everything is
|
|
4
|
+
* component state — `dataTransfer` is set for interop but never read, so
|
|
5
|
+
* the flow also works where the store is unavailable (tests, some
|
|
6
|
+
* webviews). Without `onMove` every handler is inert. */
|
|
7
|
+
export declare function useTreeDnd(items: readonly TreeNode[], onMove: UseTreeOptions["onMove"]): TreeDndContextValue;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { TreeController, UseTreeOptions } from './type';
|
|
2
|
+
/**
|
|
3
|
+
* The tree's headless controller — every behaviour the component exposes,
|
|
4
|
+
* callable from outside it: expand/collapse (a node, a section, the whole
|
|
5
|
+
* tree), fold the `sidebar` rail, select a node, rename, reorder.
|
|
6
|
+
*
|
|
7
|
+
* Each piece of state is controlled-or-uncontrolled in the usual React
|
|
8
|
+
* shape: pass `items`/`activeId`/`collapsed` to own it yourself, or
|
|
9
|
+
* `defaultItems`/`defaultActiveId`/`defaultCollapsed` to let the hook. When
|
|
10
|
+
* the hook owns `items` (or you pass `onItemsChange`), `rename` and `move`
|
|
11
|
+
* apply the edit to the data themselves — otherwise they only report it
|
|
12
|
+
* through `onRename`/`onMove` and you reduce the tree yourself.
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const tree = useTree({ variant: "files", defaultItems: files })
|
|
16
|
+
* <Tree tree={tree} />
|
|
17
|
+
* <Button onClick={tree.collapseAll}>Collapse all</Button>
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function useTree(options: UseTreeOptions): TreeController;
|