@visulima/tui-kit 0.0.0 β 1.0.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/CHANGELOG.md +6 -0
- package/LICENSE.md +2095 -0
- package/README.md +115 -29
- package/dist/accordion.d.ts +59 -0
- package/dist/accordion.js +1 -0
- package/dist/alert.d.ts +26 -0
- package/dist/alert.js +1 -0
- package/dist/animate-presence.d.ts +18 -0
- package/dist/animate-presence.js +1 -0
- package/dist/approval-prompt.d.ts +65 -0
- package/dist/approval-prompt.js +1 -0
- package/dist/area-chart.d.ts +66 -0
- package/dist/area-chart.js +1 -0
- package/dist/badge.d.ts +22 -0
- package/dist/badge.js +1 -0
- package/dist/bar-chart.d.ts +67 -0
- package/dist/bar-chart.js +1 -0
- package/dist/big-text.d.ts +81 -0
- package/dist/big-text.js +1 -0
- package/dist/blink-dot.d.ts +36 -0
- package/dist/blink-dot.js +1 -0
- package/dist/breadcrumb.d.ts +33 -0
- package/dist/breadcrumb.js +1 -0
- package/dist/button.d.ts +45 -0
- package/dist/button.js +1 -0
- package/dist/calendar.d.ts +72 -0
- package/dist/calendar.js +1 -0
- package/dist/card.d.ts +57 -0
- package/dist/card.js +1 -0
- package/dist/checkbox-group.d.ts +59 -0
- package/dist/checkbox-group.js +1 -0
- package/dist/checkbox.d.ts +48 -0
- package/dist/checkbox.js +1 -0
- package/dist/code.d.ts +48 -0
- package/dist/code.js +2 -0
- package/dist/collapsible.d.ts +53 -0
- package/dist/collapsible.js +1 -0
- package/dist/color-picker.d.ts +71 -0
- package/dist/color-picker.js +1 -0
- package/dist/command-block.d.ts +47 -0
- package/dist/command-block.js +4 -0
- package/dist/command-palette.d.ts +82 -0
- package/dist/command-palette.js +1 -0
- package/dist/confirm-dialog.d.ts +75 -0
- package/dist/confirm-dialog.js +1 -0
- package/dist/confirm-input.d.ts +38 -0
- package/dist/confirm-input.js +1 -0
- package/dist/console-overlay.d.ts +54 -0
- package/dist/console-overlay.js +1 -0
- package/dist/content-switcher.d.ts +57 -0
- package/dist/content-switcher.js +1 -0
- package/dist/controlled-scroll-view.d.ts +3 -0
- package/dist/controlled-scroll-view.js +1 -0
- package/dist/data-grid.d.ts +49 -0
- package/dist/data-grid.js +1 -0
- package/dist/date-picker.d.ts +65 -0
- package/dist/date-picker.js +1 -0
- package/dist/definition-list.d.ts +44 -0
- package/dist/definition-list.js +1 -0
- package/dist/dialog.d.ts +110 -0
- package/dist/dialog.js +1 -0
- package/dist/diff-view.d.ts +85 -0
- package/dist/diff-view.js +1 -0
- package/dist/digits.d.ts +30 -0
- package/dist/digits.js +1 -0
- package/dist/directory-tree.d.ts +51 -0
- package/dist/directory-tree.js +1 -0
- package/dist/divider.d.ts +49 -0
- package/dist/divider.js +1 -0
- package/dist/drawer.d.ts +53 -0
- package/dist/drawer.js +1 -0
- package/dist/file-change.d.ts +40 -0
- package/dist/file-change.js +1 -0
- package/dist/file-picker-entry.d.ts +2 -0
- package/dist/file-picker-entry.js +1 -0
- package/dist/form.d.ts +81 -0
- package/dist/form.js +1 -0
- package/dist/gauge.d.ts +87 -0
- package/dist/gauge.js +1 -0
- package/dist/gradient.d.ts +42 -0
- package/dist/gradient.js +1 -0
- package/dist/grid.d.ts +40 -0
- package/dist/grid.js +1 -0
- package/dist/heading.d.ts +33 -0
- package/dist/heading.js +1 -0
- package/dist/heatmap.d.ts +62 -0
- package/dist/heatmap.js +1 -0
- package/dist/help.d.ts +52 -0
- package/dist/help.js +1 -0
- package/dist/histogram.d.ts +62 -0
- package/dist/histogram.js +1 -0
- package/dist/image.d.ts +26 -0
- package/dist/image.js +1 -0
- package/dist/index.d.ts +125 -0
- package/dist/index.js +1 -0
- package/dist/json.d.ts +58 -0
- package/dist/json.js +1 -0
- package/dist/kbd.d.ts +31 -0
- package/dist/kbd.js +1 -0
- package/dist/line-chart.d.ts +109 -0
- package/dist/line-chart.js +1 -0
- package/dist/link.d.ts +38 -0
- package/dist/link.js +1 -0
- package/dist/loading-indicator.d.ts +29 -0
- package/dist/loading-indicator.js +1 -0
- package/dist/log.d.ts +38 -0
- package/dist/log.js +1 -0
- package/dist/markdown.d.ts +40 -0
- package/dist/markdown.js +1 -0
- package/dist/masked-input.d.ts +50 -0
- package/dist/masked-input.js +1 -0
- package/dist/menu.d.ts +78 -0
- package/dist/menu.js +1 -0
- package/dist/message-bubble.d.ts +43 -0
- package/dist/message-bubble.js +1 -0
- package/dist/model-badge.d.ts +39 -0
- package/dist/model-badge.js +1 -0
- package/dist/model-selector.d.ts +52 -0
- package/dist/model-selector.js +1 -0
- package/dist/multi-progress.d.ts +55 -0
- package/dist/multi-progress.js +1 -0
- package/dist/multi-select.d.ts +65 -0
- package/dist/multi-select.js +1 -0
- package/dist/notification-badge.d.ts +47 -0
- package/dist/notification-badge.js +1 -0
- package/dist/number-input.d.ts +85 -0
- package/dist/number-input.js +1 -0
- package/dist/operation-tree.d.ts +49 -0
- package/dist/operation-tree.js +1 -0
- package/dist/option-list.d.ts +47 -0
- package/dist/option-list.js +1 -0
- package/dist/ordered-list.d.ts +24 -0
- package/dist/ordered-list.js +1 -0
- package/dist/packem_shared/ControlledScrollView-BzlbIUph.js +1 -0
- package/dist/packem_shared/FilePicker-V5H2fb-v.js +1 -0
- package/dist/packem_shared/ScrollBar-C9i3z3Ft.js +1 -0
- package/dist/packem_shared/ScrollBarBox-DZEC8YnV.js +1 -0
- package/dist/packem_shared/ScrollList-Djtb2vVy.js +1 -0
- package/dist/packem_shared/ScrollView-ESxpl6Hy.js +1 -0
- package/dist/packem_shared/TreeNodeMap-cJlYqckB.js +1 -0
- package/dist/packem_shared/TreeView-q0l446NV.js +1 -0
- package/dist/packem_shared/controlled-scroll-view-DerVKKv6.js +1 -0
- package/dist/packem_shared/controlled-scroll-view.d-vF4wevEJ.d.ts +54 -0
- package/dist/packem_shared/file-picker.d-Cie6aAMD.d.ts +104 -0
- package/dist/packem_shared/line-chart-BbhMhcZg.js +1 -0
- package/dist/packem_shared/scroll-bar-box.d-DSBbBsRc.d.ts +30 -0
- package/dist/packem_shared/scroll-bar.d-B4sD1hBR.d.ts +18 -0
- package/dist/packem_shared/scroll-list.d-BP0UZSY4.d.ts +12 -0
- package/dist/packem_shared/scroll-view.d-BqKpI-F-.d.ts +119 -0
- package/dist/packem_shared/treeViewTheme-Dq3ekRAC.js +1 -0
- package/dist/packem_shared/use-tree-view.d-dXTKoDPM.d.ts +350 -0
- package/dist/packem_shared/useTreeView-D0JEqT6W.js +1 -0
- package/dist/packem_shared/useTreeViewState-BiTJakkh.js +1 -0
- package/dist/packem_shared/variant-config-BJqpNo7H.js +1 -0
- package/dist/packem_shared/variant-config.d-D5NnrrQK.d.ts +2 -0
- package/dist/paginator.d.ts +69 -0
- package/dist/paginator.js +1 -0
- package/dist/paragraph.d.ts +38 -0
- package/dist/paragraph.js +1 -0
- package/dist/path-input.d.ts +67 -0
- package/dist/path-input.js +1 -0
- package/dist/pie-chart.d.ts +50 -0
- package/dist/pie-chart.js +1 -0
- package/dist/placeholder.d.ts +55 -0
- package/dist/placeholder.js +1 -0
- package/dist/popover.d.ts +46 -0
- package/dist/popover.js +1 -0
- package/dist/progress-bar.d.ts +51 -0
- package/dist/progress-bar.js +1 -0
- package/dist/progress-circle.d.ts +59 -0
- package/dist/progress-circle.js +1 -0
- package/dist/qr-code.d.ts +45 -0
- package/dist/qr-code.js +1 -0
- package/dist/r/accordion.json +24 -0
- package/dist/r/alert.json +24 -0
- package/dist/r/animate-presence.json +22 -0
- package/dist/r/approval-prompt.json +24 -0
- package/dist/r/area-chart.json +27 -0
- package/dist/r/badge.json +24 -0
- package/dist/r/bar-chart.json +24 -0
- package/dist/r/big-text.json +23 -0
- package/dist/r/blink-dot.json +24 -0
- package/dist/r/breadcrumb.json +24 -0
- package/dist/r/button.json +24 -0
- package/dist/r/calendar.json +24 -0
- package/dist/r/card.json +26 -0
- package/dist/r/chart-utils.json +24 -0
- package/dist/r/checkbox-group.json +24 -0
- package/dist/r/checkbox.json +24 -0
- package/dist/r/code.json +23 -0
- package/dist/r/collapsible.json +24 -0
- package/dist/r/color-picker.json +24 -0
- package/dist/r/command-block.json +26 -0
- package/dist/r/command-palette.json +24 -0
- package/dist/r/confirm-dialog.json +24 -0
- package/dist/r/confirm-input.json +22 -0
- package/dist/r/console-overlay.json +22 -0
- package/dist/r/content-switcher.json +24 -0
- package/dist/r/controlled-scroll-view.json +24 -0
- package/dist/r/data-grid.json +24 -0
- package/dist/r/date-picker.json +26 -0
- package/dist/r/definition-list.json +24 -0
- package/dist/r/dialog.json +24 -0
- package/dist/r/diff-view.json +24 -0
- package/dist/r/digits.json +24 -0
- package/dist/r/directory-tree.json +24 -0
- package/dist/r/divider.json +24 -0
- package/dist/r/drawer.json +24 -0
- package/dist/r/file-change.json +24 -0
- package/dist/r/file-picker.json +44 -0
- package/dist/r/form.json +24 -0
- package/dist/r/gauge.json +24 -0
- package/dist/r/gradient.json +24 -0
- package/dist/r/grid.json +22 -0
- package/dist/r/heading.json +24 -0
- package/dist/r/heatmap.json +24 -0
- package/dist/r/help.json +22 -0
- package/dist/r/histogram.json +26 -0
- package/dist/r/image.json +24 -0
- package/dist/r/json.json +24 -0
- package/dist/r/kbd.json +24 -0
- package/dist/r/line-chart.json +26 -0
- package/dist/r/link.json +23 -0
- package/dist/r/loading-indicator.json +27 -0
- package/dist/r/log.json +24 -0
- package/dist/r/markdown.json +29 -0
- package/dist/r/masked-input.json +22 -0
- package/dist/r/menu.json +24 -0
- package/dist/r/message-bubble.json +24 -0
- package/dist/r/model-badge.json +24 -0
- package/dist/r/model-selector.json +24 -0
- package/dist/r/multi-progress.json +24 -0
- package/dist/r/multi-select.json +24 -0
- package/dist/r/notification-badge.json +24 -0
- package/dist/r/number-input.json +24 -0
- package/dist/r/operation-tree.json +26 -0
- package/dist/r/option-list.json +24 -0
- package/dist/r/ordered-list.json +22 -0
- package/dist/r/paginator.json +22 -0
- package/dist/r/paragraph.json +24 -0
- package/dist/r/path-input.json +26 -0
- package/dist/r/pie-chart.json +24 -0
- package/dist/r/placeholder.json +24 -0
- package/dist/r/popover.json +24 -0
- package/dist/r/progress-bar.json +22 -0
- package/dist/r/progress-circle.json +24 -0
- package/dist/r/qr-code.json +24 -0
- package/dist/r/radio-group.json +24 -0
- package/dist/r/registry.json +1051 -0
- package/dist/r/scatter-plot.json +27 -0
- package/dist/r/scroll-bar-box.json +24 -0
- package/dist/r/scroll-bar.json +24 -0
- package/dist/r/scroll-list.json +24 -0
- package/dist/r/scroll-view.json +24 -0
- package/dist/r/scroll.json +58 -0
- package/dist/r/search-input.json +26 -0
- package/dist/r/select-input-indicator.json +24 -0
- package/dist/r/select-input-item.json +24 -0
- package/dist/r/select-input.json +28 -0
- package/dist/r/shimmer-text.json +24 -0
- package/dist/r/sidebar.json +24 -0
- package/dist/r/slider.json +24 -0
- package/dist/r/sparkline.json +24 -0
- package/dist/r/spinner.json +23 -0
- package/dist/r/status-line.json +24 -0
- package/dist/r/status-message.json +24 -0
- package/dist/r/stepper.json +24 -0
- package/dist/r/stopwatch.json +22 -0
- package/dist/r/streaming-text.json +24 -0
- package/dist/r/switch.json +24 -0
- package/dist/r/tab.json +22 -0
- package/dist/r/table.json +23 -0
- package/dist/r/tabs.json +26 -0
- package/dist/r/tag-input.json +24 -0
- package/dist/r/tag.json +24 -0
- package/dist/r/text-input.json +22 -0
- package/dist/r/textarea.json +22 -0
- package/dist/r/thinking-block.json +24 -0
- package/dist/r/time-picker.json +24 -0
- package/dist/r/timer.json +22 -0
- package/dist/r/toast.json +24 -0
- package/dist/r/token-usage.json +24 -0
- package/dist/r/tool-call.json +24 -0
- package/dist/r/tooltip.json +24 -0
- package/dist/r/transition.json +22 -0
- package/dist/r/tree-select.json +24 -0
- package/dist/r/tree-view.json +64 -0
- package/dist/r/unordered-list.json +22 -0
- package/dist/r/use-linked-scroll.json +24 -0
- package/dist/r/variant-config.json +22 -0
- package/dist/radio-group.d.ts +74 -0
- package/dist/radio-group.js +1 -0
- package/dist/scatter-plot.d.ts +35 -0
- package/dist/scatter-plot.js +1 -0
- package/dist/scroll-bar-box.d.ts +3 -0
- package/dist/scroll-bar-box.js +1 -0
- package/dist/scroll-bar.d.ts +3 -0
- package/dist/scroll-bar.js +1 -0
- package/dist/scroll-list.d.ts +4 -0
- package/dist/scroll-list.js +1 -0
- package/dist/scroll-view.d.ts +3 -0
- package/dist/scroll-view.js +1 -0
- package/dist/search-input.d.ts +55 -0
- package/dist/search-input.js +1 -0
- package/dist/select-input-indicator.d.ts +32 -0
- package/dist/select-input-indicator.js +1 -0
- package/dist/select-input-item.d.ts +41 -0
- package/dist/select-input-item.js +1 -0
- package/dist/select-input.d.ts +110 -0
- package/dist/select-input.js +1 -0
- package/dist/shimmer-text.d.ts +47 -0
- package/dist/shimmer-text.js +1 -0
- package/dist/sidebar.d.ts +47 -0
- package/dist/sidebar.js +1 -0
- package/dist/slider.d.ts +103 -0
- package/dist/slider.js +1 -0
- package/dist/sparkline.d.ts +42 -0
- package/dist/sparkline.js +1 -0
- package/dist/spinner.d.ts +17 -0
- package/dist/spinner.js +1 -0
- package/dist/status-line.d.ts +37 -0
- package/dist/status-line.js +1 -0
- package/dist/status-message.d.ts +21 -0
- package/dist/status-message.js +1 -0
- package/dist/stepper.d.ts +48 -0
- package/dist/stepper.js +1 -0
- package/dist/stopwatch.d.ts +49 -0
- package/dist/stopwatch.js +1 -0
- package/dist/streaming-text.d.ts +46 -0
- package/dist/streaming-text.js +1 -0
- package/dist/switch.d.ts +55 -0
- package/dist/switch.js +1 -0
- package/dist/tab.d.ts +19 -0
- package/dist/tab.js +1 -0
- package/dist/table.d.ts +119 -0
- package/dist/table.js +1 -0
- package/dist/tabs.d.ts +107 -0
- package/dist/tabs.js +1 -0
- package/dist/tag-input.d.ts +71 -0
- package/dist/tag-input.js +1 -0
- package/dist/tag.d.ts +35 -0
- package/dist/tag.js +1 -0
- package/dist/text-input.d.ts +48 -0
- package/dist/text-input.js +1 -0
- package/dist/textarea.d.ts +68 -0
- package/dist/textarea.js +2 -0
- package/dist/thinking-block.d.ts +57 -0
- package/dist/thinking-block.js +1 -0
- package/dist/time-picker.d.ts +67 -0
- package/dist/time-picker.js +1 -0
- package/dist/timer.d.ts +59 -0
- package/dist/timer.js +1 -0
- package/dist/toast.d.ts +49 -0
- package/dist/toast.js +1 -0
- package/dist/token-usage.d.ts +50 -0
- package/dist/token-usage.js +1 -0
- package/dist/tool-call.d.ts +33 -0
- package/dist/tool-call.js +1 -0
- package/dist/tooltip.d.ts +43 -0
- package/dist/tooltip.js +1 -0
- package/dist/transition.d.ts +74 -0
- package/dist/transition.js +1 -0
- package/dist/tree-select.d.ts +65 -0
- package/dist/tree-select.js +1 -0
- package/dist/tree-view-entry.d.ts +4 -0
- package/dist/tree-view-entry.js +1 -0
- package/dist/unordered-list.d.ts +30 -0
- package/dist/unordered-list.js +1 -0
- package/dist/use-linked-scroll.d.ts +42 -0
- package/dist/use-linked-scroll.js +1 -0
- package/package.json +553 -7
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { Props as Props$1 } from '@visulima/tui/components/box';
|
|
2
|
+
import { Props as Props$2 } from '@visulima/tui/components/text';
|
|
3
|
+
import { ReactNode, ReactElement } from 'react';
|
|
4
|
+
type StyleFunctionProps = {
|
|
5
|
+
readonly depth?: number;
|
|
6
|
+
readonly hasChildren?: boolean;
|
|
7
|
+
readonly isExpanded?: boolean;
|
|
8
|
+
readonly isFocused?: boolean;
|
|
9
|
+
readonly isLoading?: boolean;
|
|
10
|
+
readonly isSelected?: boolean;
|
|
11
|
+
};
|
|
12
|
+
declare const theme: {
|
|
13
|
+
styles: {
|
|
14
|
+
container: () => Props$1;
|
|
15
|
+
expandIndicator: (_props: StyleFunctionProps) => Props$2;
|
|
16
|
+
focusIndicator: () => Props$2;
|
|
17
|
+
indent: ({
|
|
18
|
+
depth
|
|
19
|
+
}: StyleFunctionProps) => Props$1;
|
|
20
|
+
label: ({
|
|
21
|
+
isFocused,
|
|
22
|
+
isSelected
|
|
23
|
+
}: StyleFunctionProps) => Props$2;
|
|
24
|
+
loadingIndicator: () => Props$2;
|
|
25
|
+
node: ({
|
|
26
|
+
isFocused
|
|
27
|
+
}: StyleFunctionProps) => Props$1;
|
|
28
|
+
selectedIndicator: () => Props$2;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
type Theme = typeof theme;
|
|
32
|
+
/**
|
|
33
|
+
* A single node in the tree. Generic over the user's data shape.
|
|
34
|
+
* The `id` field MUST be unique across the entire tree.
|
|
35
|
+
*/
|
|
36
|
+
type TreeNode<T = Record<string, unknown>> = {
|
|
37
|
+
/** Child nodes. Undefined or empty array means leaf node. */
|
|
38
|
+
readonly children?: TreeNode<T>[];
|
|
39
|
+
/** Arbitrary user data attached to this node. */
|
|
40
|
+
readonly data?: T;
|
|
41
|
+
/** Unique identifier for this node. Used as the key for all lookups. */
|
|
42
|
+
readonly id: string;
|
|
43
|
+
/**
|
|
44
|
+
* Explicitly marks this node as a parent that can have children loaded
|
|
45
|
+
* asynchronously via `loadChildren`. When true, the node shows an expand
|
|
46
|
+
* indicator even if `children` is empty or undefined, and expanding it
|
|
47
|
+
* triggers the `loadChildren` callback.
|
|
48
|
+
*/
|
|
49
|
+
readonly isParent?: boolean;
|
|
50
|
+
/** Display label. Used by the default renderer. */
|
|
51
|
+
readonly label: string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* For async/lazy loading: a function that resolves children on demand.
|
|
55
|
+
*/
|
|
56
|
+
type AsyncChildrenFunction<T = Record<string, unknown>> = (node: TreeNode<T>) => Promise<TreeNode<T>[]>;
|
|
57
|
+
/**
|
|
58
|
+
* State passed to custom renderers and theme functions.
|
|
59
|
+
*/
|
|
60
|
+
type TreeNodeState = {
|
|
61
|
+
/** Depth of this node in the tree (root = 0). */
|
|
62
|
+
readonly depth: number;
|
|
63
|
+
/** Whether this node has children (or a lazy loader). */
|
|
64
|
+
readonly hasChildren: boolean;
|
|
65
|
+
/** Whether this node is expanded (children visible). */
|
|
66
|
+
readonly isExpanded: boolean;
|
|
67
|
+
/** Whether this node is currently focused (cursor is on it). */
|
|
68
|
+
readonly isFocused: boolean;
|
|
69
|
+
/** Whether children are currently loading (async mode). */
|
|
70
|
+
readonly isLoading: boolean;
|
|
71
|
+
/** Whether this node is selected (in select/multi-select mode). */
|
|
72
|
+
readonly isSelected: boolean;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Props received by a custom node renderer.
|
|
76
|
+
*/
|
|
77
|
+
type TreeNodeRendererProps<T = Record<string, unknown>> = {
|
|
78
|
+
/** The tree node data. */
|
|
79
|
+
readonly node: TreeNode<T>;
|
|
80
|
+
/** Current state of this node. */
|
|
81
|
+
readonly state: TreeNodeState;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Selection mode for the tree view.
|
|
85
|
+
*/
|
|
86
|
+
type SelectionMode = "multiple" | "none" | "single";
|
|
87
|
+
/**
|
|
88
|
+
* A flattened representation of a tree node with navigation links.
|
|
89
|
+
*/
|
|
90
|
+
type FlatNode<T = Record<string, unknown>> = {
|
|
91
|
+
/** Ordered list of direct children IDs. */
|
|
92
|
+
readonly childrenIds: ReadonlyArray<string>;
|
|
93
|
+
/** Depth in the tree (0 for roots). */
|
|
94
|
+
readonly depth: number;
|
|
95
|
+
/** Index in the flattened DFS order (across ALL nodes, not just visible). */
|
|
96
|
+
readonly flatIndex: number;
|
|
97
|
+
/** Whether this node has children. */
|
|
98
|
+
readonly hasChildren: boolean;
|
|
99
|
+
/** Next sibling's ID, or undefined. */
|
|
100
|
+
readonly nextSiblingId: string | undefined;
|
|
101
|
+
/** The original tree node. */
|
|
102
|
+
readonly node: TreeNode<T>;
|
|
103
|
+
/** Parent's id, or undefined if root. */
|
|
104
|
+
readonly parentId: string | undefined;
|
|
105
|
+
/** Previous sibling's ID, or undefined. */
|
|
106
|
+
readonly previousSiblingId: string | undefined;
|
|
107
|
+
/** Total number of siblings (including this node). */
|
|
108
|
+
readonly siblingCount: number;
|
|
109
|
+
/** Zero-based index among siblings. */
|
|
110
|
+
readonly siblingIndex: number;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* A flattened map of all tree nodes built via DFS traversal.
|
|
114
|
+
* Stores parent/child/sibling links for O(1) navigation.
|
|
115
|
+
*/
|
|
116
|
+
declare class TreeNodeMap<T = Record<string, unknown>> {
|
|
117
|
+
/** All node IDs in DFS order. */
|
|
118
|
+
readonly orderedIds: string[];
|
|
119
|
+
/** Root-level node IDs. */
|
|
120
|
+
readonly rootIds: string[];
|
|
121
|
+
/** Map from node ID to FlatNode. */
|
|
122
|
+
private readonly map;
|
|
123
|
+
constructor(data: TreeNode<T>[]);
|
|
124
|
+
/**
|
|
125
|
+
* Iterate over all entries.
|
|
126
|
+
*/
|
|
127
|
+
entries(): IterableIterator<[string, FlatNode<T>]>;
|
|
128
|
+
/**
|
|
129
|
+
* Get a flat node by ID.
|
|
130
|
+
*/
|
|
131
|
+
get(id: string): FlatNode<T> | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* Total number of nodes in the tree.
|
|
134
|
+
*/
|
|
135
|
+
get size(): number;
|
|
136
|
+
/**
|
|
137
|
+
* Given a set of expanded node IDs, return the ordered list of
|
|
138
|
+
* VISIBLE node IDs (i.e., a node is visible if all its ancestors
|
|
139
|
+
* are expanded).
|
|
140
|
+
*
|
|
141
|
+
* Uses iterative DFS, skipping collapsed subtrees.
|
|
142
|
+
*/
|
|
143
|
+
getVisibleIds(expandedIds: ReadonlySet<string>): string[];
|
|
144
|
+
/**
|
|
145
|
+
* Check if a node is a descendant of another node.
|
|
146
|
+
*/
|
|
147
|
+
isDescendantOf(nodeId: string, ancestorId: string): boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Insert dynamically-loaded children under a parent node.
|
|
150
|
+
* Returns a new TreeNodeMap (immutable operation).
|
|
151
|
+
*
|
|
152
|
+
* Rebuilds the full tree from the current map state (not the original
|
|
153
|
+
* input data), so successive calls for different parents are safe.
|
|
154
|
+
*/
|
|
155
|
+
withChildren(parentId: string, children: TreeNode<T>[]): TreeNodeMap<T>;
|
|
156
|
+
private buildFromData;
|
|
157
|
+
}
|
|
158
|
+
type Props<T = Record<string, unknown>> = {
|
|
159
|
+
/**
|
|
160
|
+
* Accessible label for the tree container.
|
|
161
|
+
* Useful when multiple tree views are on screen.
|
|
162
|
+
* @default "Tree view"
|
|
163
|
+
*/
|
|
164
|
+
readonly ariaLabel?: string;
|
|
165
|
+
/**
|
|
166
|
+
* The tree data. Array of root-level nodes.
|
|
167
|
+
*
|
|
168
|
+
* **Important:** Must be referentially stable across renders (e.g. stored
|
|
169
|
+
* in state or wrapped in `useMemo`). Passing a new array reference on
|
|
170
|
+
* every render will reset focus, expansion, and selection state.
|
|
171
|
+
*/
|
|
172
|
+
readonly data: TreeNode<T>[];
|
|
173
|
+
/**
|
|
174
|
+
* Set of node IDs that are expanded by default.
|
|
175
|
+
* If not provided, all nodes start collapsed.
|
|
176
|
+
*/
|
|
177
|
+
readonly defaultExpanded?: ReadonlySet<string> | "all";
|
|
178
|
+
/**
|
|
179
|
+
* Set of node IDs that are selected by default.
|
|
180
|
+
*/
|
|
181
|
+
readonly defaultSelected?: ReadonlySet<string>;
|
|
182
|
+
/**
|
|
183
|
+
* When disabled, user input is ignored.
|
|
184
|
+
* @default false
|
|
185
|
+
*/
|
|
186
|
+
readonly isDisabled?: boolean;
|
|
187
|
+
/**
|
|
188
|
+
* Async function to load children on demand.
|
|
189
|
+
*/
|
|
190
|
+
readonly loadChildren?: AsyncChildrenFunction<T>;
|
|
191
|
+
/** Called when expanded set changes. */
|
|
192
|
+
readonly onExpandChange?: (expandedIds: ReadonlySet<string>) => void;
|
|
193
|
+
/** Called when the focused node changes. */
|
|
194
|
+
readonly onFocusChange?: (nodeId: string) => void;
|
|
195
|
+
/**
|
|
196
|
+
* Called when `loadChildren` rejects. Receives the node ID and error.
|
|
197
|
+
*/
|
|
198
|
+
readonly onLoadError?: (nodeId: string, error: Error) => void;
|
|
199
|
+
/** Called when selection changes. */
|
|
200
|
+
readonly onSelectChange?: (selectedIds: ReadonlySet<string>) => void;
|
|
201
|
+
/**
|
|
202
|
+
* Custom node renderer. Receives node + state, returns Ink JSX.
|
|
203
|
+
*/
|
|
204
|
+
readonly renderNode?: (props: TreeNodeRendererProps<T>) => ReactNode;
|
|
205
|
+
/**
|
|
206
|
+
* Selection mode.
|
|
207
|
+
* - "none": No selection behavior (default).
|
|
208
|
+
* - "single": One node can be selected at a time.
|
|
209
|
+
* - "multiple": Multiple nodes can be selected (checkboxes shown).
|
|
210
|
+
* @default "none"
|
|
211
|
+
*/
|
|
212
|
+
readonly selectionMode?: SelectionMode;
|
|
213
|
+
/**
|
|
214
|
+
* Number of visible nodes in the viewport (for virtualization).
|
|
215
|
+
* @default Infinity (no virtualization)
|
|
216
|
+
*/
|
|
217
|
+
readonly visibleNodeCount?: number;
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Renders an interactive tree view with keyboard navigation.
|
|
221
|
+
*
|
|
222
|
+
* ```tsx
|
|
223
|
+
* <TreeView
|
|
224
|
+
* data={[
|
|
225
|
+
* { id: "1", label: "Root", children: [
|
|
226
|
+
* { id: "1.1", label: "Child" },
|
|
227
|
+
* ]},
|
|
228
|
+
* ]}
|
|
229
|
+
* defaultExpanded="all"
|
|
230
|
+
* />
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
declare function TreeView<T = Record<string, unknown>>({
|
|
234
|
+
ariaLabel,
|
|
235
|
+
data,
|
|
236
|
+
defaultExpanded,
|
|
237
|
+
defaultSelected,
|
|
238
|
+
isDisabled,
|
|
239
|
+
loadChildren,
|
|
240
|
+
onExpandChange,
|
|
241
|
+
onFocusChange,
|
|
242
|
+
onLoadError,
|
|
243
|
+
onSelectChange,
|
|
244
|
+
renderNode,
|
|
245
|
+
selectionMode,
|
|
246
|
+
visibleNodeCount
|
|
247
|
+
}: Props<T>): ReactElement;
|
|
248
|
+
type UseTreeViewStateProps<T = Record<string, unknown>> = {
|
|
249
|
+
/** The tree data. Array of root-level nodes. */
|
|
250
|
+
readonly data: TreeNode<T>[];
|
|
251
|
+
/**
|
|
252
|
+
* Set of node IDs that are expanded by default.
|
|
253
|
+
* If not provided, all nodes start collapsed.
|
|
254
|
+
*/
|
|
255
|
+
readonly defaultExpanded?: ReadonlySet<string> | "all";
|
|
256
|
+
/**
|
|
257
|
+
* Set of node IDs that are selected by default.
|
|
258
|
+
*/
|
|
259
|
+
readonly defaultSelected?: ReadonlySet<string>;
|
|
260
|
+
/** Called when expanded set changes. */
|
|
261
|
+
readonly onExpandChange?: (expandedIds: ReadonlySet<string>) => void;
|
|
262
|
+
/** Called when the focused node changes. */
|
|
263
|
+
readonly onFocusChange?: (nodeId: string) => void;
|
|
264
|
+
/** Called when selection changes. */
|
|
265
|
+
readonly onSelectChange?: (selectedIds: ReadonlySet<string>) => void;
|
|
266
|
+
/**
|
|
267
|
+
* Selection mode.
|
|
268
|
+
* @default "none"
|
|
269
|
+
*/
|
|
270
|
+
readonly selectionMode?: SelectionMode;
|
|
271
|
+
/**
|
|
272
|
+
* Number of visible nodes in the viewport (for virtualization).
|
|
273
|
+
* @default Infinity
|
|
274
|
+
*/
|
|
275
|
+
readonly visibleNodeCount?: number;
|
|
276
|
+
};
|
|
277
|
+
type TreeViewState<T = Record<string, unknown>> = {
|
|
278
|
+
readonly collapse: () => void;
|
|
279
|
+
readonly collapseAll: () => void;
|
|
280
|
+
readonly collapseNode: (nodeId: string) => void;
|
|
281
|
+
readonly expand: () => void;
|
|
282
|
+
readonly expandAll: () => void;
|
|
283
|
+
readonly expandedIds: ReadonlySet<string>;
|
|
284
|
+
readonly expandNode: (nodeId: string) => void;
|
|
285
|
+
readonly focusedId: string | undefined;
|
|
286
|
+
readonly focusFirst: () => void;
|
|
287
|
+
readonly focusFirstChild: () => void;
|
|
288
|
+
readonly focusLast: () => void;
|
|
289
|
+
readonly focusNext: () => void;
|
|
290
|
+
readonly focusParent: () => void;
|
|
291
|
+
readonly focusPrevious: () => void;
|
|
292
|
+
readonly hasScrollDown: boolean;
|
|
293
|
+
readonly hasScrollUp: boolean;
|
|
294
|
+
readonly insertChildren: (parentId: string, children: TreeNode<T>[]) => void;
|
|
295
|
+
readonly loadingIds: ReadonlySet<string>;
|
|
296
|
+
readonly nodeMap: TreeNodeMap<T>;
|
|
297
|
+
readonly select: () => void;
|
|
298
|
+
readonly selectedIds: ReadonlySet<string>;
|
|
299
|
+
readonly setChildrenError: (nodeId: string) => void;
|
|
300
|
+
readonly setLoading: (nodeId: string, isLoading: boolean) => void;
|
|
301
|
+
readonly toggleExpanded: () => void;
|
|
302
|
+
readonly viewportFromIndex: number;
|
|
303
|
+
readonly viewportNodes: {
|
|
304
|
+
node: TreeNode<T>;
|
|
305
|
+
state: TreeNodeState;
|
|
306
|
+
}[];
|
|
307
|
+
readonly viewportToIndex: number;
|
|
308
|
+
readonly visibleCount: number;
|
|
309
|
+
};
|
|
310
|
+
declare function useTreeViewState<T = Record<string, unknown>>({
|
|
311
|
+
data,
|
|
312
|
+
defaultExpanded,
|
|
313
|
+
defaultSelected,
|
|
314
|
+
onExpandChange,
|
|
315
|
+
onFocusChange,
|
|
316
|
+
onSelectChange,
|
|
317
|
+
selectionMode,
|
|
318
|
+
visibleNodeCount
|
|
319
|
+
}: UseTreeViewStateProps<T>): TreeViewState<T>;
|
|
320
|
+
type UseTreeViewProps<T = Record<string, unknown>> = {
|
|
321
|
+
/**
|
|
322
|
+
* When disabled, user input is ignored.
|
|
323
|
+
* @default false
|
|
324
|
+
*/
|
|
325
|
+
readonly isDisabled?: boolean;
|
|
326
|
+
/**
|
|
327
|
+
* Async function to load children on demand.
|
|
328
|
+
*/
|
|
329
|
+
readonly loadChildren?: AsyncChildrenFunction<T>;
|
|
330
|
+
/**
|
|
331
|
+
* Called when `loadChildren` rejects.
|
|
332
|
+
*/
|
|
333
|
+
readonly onLoadError?: (nodeId: string, error: Error) => void;
|
|
334
|
+
/**
|
|
335
|
+
* Selection mode.
|
|
336
|
+
*/
|
|
337
|
+
readonly selectionMode: SelectionMode;
|
|
338
|
+
/**
|
|
339
|
+
* The tree view state from `useTreeViewState`.
|
|
340
|
+
*/
|
|
341
|
+
readonly state: TreeViewState<T>;
|
|
342
|
+
};
|
|
343
|
+
declare function useTreeView<T>({
|
|
344
|
+
isDisabled,
|
|
345
|
+
loadChildren,
|
|
346
|
+
onLoadError,
|
|
347
|
+
selectionMode,
|
|
348
|
+
state
|
|
349
|
+
}: UseTreeViewProps<T>): void;
|
|
350
|
+
export { AsyncChildrenFunction as A, FlatNode as F, Props as P, SelectionMode as S, TreeNode as T, UseTreeViewProps as U, TreeNodeMap as a, TreeNodeRendererProps as b, TreeNodeState as c, TreeView as d, TreeViewState as e, Theme as f, UseTreeViewStateProps as g, useTreeViewState as h, theme as t, useTreeView as u };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import g from"@visulima/tui/hooks/use-input";import{useRef as s,useCallback as I}from"react";function C({isDisabled:h=!1,loadChildren:d,onLoadError:i,selectionMode:f,state:e}){const u=s(new Set),n=s(e);n.current=e;const a=s(d);a.current=d;const l=s(i);l.current=i;const p=I(async t=>{if(u.current.has(t))return;const r=a.current;if(!r)return;const o=n.current.nodeMap.get(t);if(!(!o||o.childrenIds.length>0)){u.current.add(t),n.current.setLoading(t,!0);try{const c=await r(o.node);n.current.insertChildren(t,c),n.current.expandNode(t)}catch(c){const x=c instanceof Error?c:new Error(String(c));n.current.setChildrenError(t),l.current?.(t,x)}finally{n.current.setLoading(t,!1),u.current.delete(t)}}},[]);g((t,r)=>{if(r.downArrow){e.focusNext();return}if(r.upArrow){e.focusPrevious();return}if(r.rightArrow){if(e.focusedId&&e.expandedIds.has(e.focusedId))e.focusFirstChild();else if(e.focusedId){if(d){const o=e.nodeMap.get(e.focusedId);if(o&&o.hasChildren&&o.childrenIds.length===0){p(e.focusedId).catch(()=>{});return}}e.expand()}return}if(r.leftArrow){e.focusedId&&e.expandedIds.has(e.focusedId)?e.collapse():e.focusParent();return}if(r.return){f==="none"?e.toggleExpanded():e.select();return}if(t===" "){f==="multiple"?e.select():e.toggleExpanded();return}if(t==="\x1B[H"||t==="\x1B[1~"||t==="\x1BOH"){e.focusFirst();return}(t==="\x1B[F"||t==="\x1B[4~"||t==="\x1BOF")&&e.focusLast()},{isActive:!h})}export{C as useTreeView};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{useReducer as H,useState as J,useRef as v,useEffect as b,useMemo as K,useCallback as l}from"react";import{TreeNodeMap as Q}from"./TreeNodeMap-cJlYqckB.js";function p(e){const t=new Map;for(const[d,s]of e.entries())t.set(s,d);return t}function f(e,t){if(e.visibleNodeCount>=e.visibleIds.length)return{viewportFromIndex:0,viewportToIndex:e.visibleIds.length};let d=e.viewportFromIndex,s=e.viewportToIndex;return t>=s&&(s=t+1,d=s-e.visibleNodeCount),t<d&&(d=t,s=d+e.visibleNodeCount),{viewportFromIndex:Math.max(0,d),viewportToIndex:Math.min(e.visibleIds.length,s)}}function w(e,t){if(e.visibleNodeCount>=t.length)return{viewportFromIndex:0,viewportToIndex:t.length};const d=e.focusedId?t.indexOf(e.focusedId):-1;if(d<0)return{viewportFromIndex:0,viewportToIndex:Math.min(e.visibleNodeCount,t.length)};let s=Math.min(e.viewportToIndex,t.length),o=Math.max(0,s-e.visibleNodeCount);return d>=s&&(s=d+1,o=s-e.visibleNodeCount),d<o&&(o=d,s=o+e.visibleNodeCount),{viewportFromIndex:Math.max(0,o),viewportToIndex:Math.min(t.length,s)}}function x(e,t){switch(t.type){case"collapse":return e.focusedId?x(e,{nodeId:e.focusedId,type:"collapse-node"}):e;case"collapse-all":{const d=new Set,s=e.nodeMap.getVisibleIds(d);return{...e,expandedIds:d,focusedId:s[0],previousExpandedIds:e.expandedIds,viewportFromIndex:0,viewportToIndex:Math.min(e.visibleNodeCount,s.length),visibleIdIndex:p(s),visibleIds:s}}case"collapse-node":{const{nodeId:d}=t;if(!e.expandedIds.has(d))return e;const s=new Set(e.expandedIds);s.delete(d);const o=e.nodeMap.getVisibleIds(s);let r=e.focusedId;return r!==void 0&&r!==d&&e.nodeMap.isDescendantOf(r,d)&&(r=d),{...e,expandedIds:s,focusedId:r,previousExpandedIds:e.expandedIds,visibleIdIndex:p(o),visibleIds:o,...w({...e,focusedId:r},o)}}case"expand":return e.focusedId?x(e,{nodeId:e.focusedId,type:"expand-node"}):e;case"expand-all":{const d=new Set;for(const[o,r]of e.nodeMap.entries())r.hasChildren&&d.add(o);const s=e.nodeMap.getVisibleIds(d);return{...e,expandedIds:d,previousExpandedIds:e.expandedIds,visibleIdIndex:p(s),visibleIds:s,...w({...e},s)}}case"expand-node":{const{nodeId:d}=t;if(!e.nodeMap.get(d)?.hasChildren||e.expandedIds.has(d))return e;const s=new Set(e.expandedIds);s.add(d);const o=e.nodeMap.getVisibleIds(s);return{...e,expandedIds:s,previousExpandedIds:e.expandedIds,visibleIdIndex:p(o),visibleIds:o,...w({...e},o)}}case"focus-first":return e.visibleIds.length===0?e:{...e,focusedId:e.visibleIds[0],viewportFromIndex:0,viewportToIndex:Math.min(e.visibleNodeCount,e.visibleIds.length)};case"focus-first-child":{if(!e.focusedId)return e;const d=e.nodeMap.get(e.focusedId);if(!d||d.childrenIds.length===0||!e.expandedIds.has(e.focusedId))return e;const s=d.childrenIds[0],o=e.visibleIdIndex.get(s)??-1;return o<0?e:{...e,focusedId:s,...f(e,o)}}case"focus-last":{if(e.visibleIds.length===0)return e;const d=e.visibleIds.length-1;return{...e,focusedId:e.visibleIds[d],...f(e,d)}}case"focus-next":{if(!e.focusedId)return e;const d=e.visibleIdIndex.get(e.focusedId)??-1;if(d<0||d>=e.visibleIds.length-1)return e;const s=e.visibleIds[d+1];return{...e,focusedId:s,...f(e,d+1)}}case"focus-parent":{if(!e.focusedId)return e;const d=e.nodeMap.get(e.focusedId);if(!d?.parentId)return e;const s=e.visibleIdIndex.get(d.parentId)??-1;return s<0?e:{...e,focusedId:d.parentId,...f(e,s)}}case"focus-previous":{if(!e.focusedId)return e;const d=e.visibleIdIndex.get(e.focusedId)??-1;if(d<=0)return e;const s=e.visibleIds[d-1];return{...e,focusedId:s,...f(e,d-1)}}case"insert-children":{if(!e.nodeMap.get(t.parentId))return e;const d=e.nodeMap.withChildren(t.parentId,t.children),s=d.getVisibleIds(e.expandedIds);return{...e,nodeMap:d,visibleIdIndex:p(s),visibleIds:s}}case"reset":return t.state;case"select":{if(!e.focusedId||e.selectionMode==="none")return e;if(e.selectionMode==="single"){const s=new Set([e.focusedId]);return{...e,previousSelectedIds:e.selectedIds,selectedIds:s}}const d=new Set(e.selectedIds);return d.has(e.focusedId)?d.delete(e.focusedId):d.add(e.focusedId),{...e,previousSelectedIds:e.selectedIds,selectedIds:d}}case"set-children-error":{const d=new Set(e.loadingIds);return d.delete(t.nodeId),{...e,loadingIds:d}}case"set-loading":{const d=new Set(e.loadingIds);return t.isLoading?d.add(t.nodeId):d.delete(t.nodeId),{...e,loadingIds:d}}case"toggle-expanded":return e.focusedId?e.expandedIds.has(e.focusedId)?x(e,{type:"collapse"}):x(e,{type:"expand"}):e;default:return e}}function y({data:e,defaultExpanded:t,defaultSelected:d,selectionMode:s,visibleNodeCount:o}){const r=new Q(e);let I;if(t==="all"){I=new Set;for(const[h,g]of r.entries())g.hasChildren&&I.add(h)}else t?I=new Set(t):I=new Set;const c=r.getVisibleIds(I),n=s!=="none"&&d?new Set(d):new Set,i=Math.min(o,c.length);return{expandedIds:I,focusedId:c[0],loadingIds:new Set,nodeMap:r,previousExpandedIds:I,previousSelectedIds:n,selectedIds:n,selectionMode:s,viewportFromIndex:0,viewportToIndex:i,visibleIdIndex:p(c),visibleIds:c,visibleNodeCount:o}}function Y({data:e,defaultExpanded:t,defaultSelected:d,onExpandChange:s,onFocusChange:o,onSelectChange:r,selectionMode:I="none",visibleNodeCount:c=1/0}){const[n,i]=H(x,{data:e,defaultExpanded:t,defaultSelected:d,selectionMode:I,visibleNodeCount:c},y),[h,g]=J(e);e!==h&&(i({state:y({data:e,defaultExpanded:t,defaultSelected:d,selectionMode:I,visibleNodeCount:c}),type:"reset"}),g(e));const M=v(o);M.current=o;const m=v(s);m.current=s;const S=v(r);S.current=r;const C=v(!0);b(()=>{if(C.current){C.current=!1;return}n.focusedId&&M.current?.(n.focusedId)},[n.focusedId]),b(()=>{n.expandedIds!==n.previousExpandedIds&&m.current?.(n.expandedIds)},[n.expandedIds,n.previousExpandedIds]),b(()=>{n.selectedIds!==n.previousSelectedIds&&S.current?.(n.selectedIds)},[n.selectedIds,n.previousSelectedIds]);const N=K(()=>n.visibleIds.slice(n.viewportFromIndex,n.viewportToIndex).map(a=>{const u=n.nodeMap.get(a);return{node:u.node,state:{depth:u.depth,hasChildren:u.hasChildren,isExpanded:n.expandedIds.has(a),isFocused:a===n.focusedId,isLoading:n.loadingIds.has(a),isSelected:n.selectedIds.has(a)}}}),[n.visibleIds,n.viewportFromIndex,n.viewportToIndex,n.focusedId,n.expandedIds,n.selectedIds,n.nodeMap,n.loadingIds]),F=l(()=>{i({type:"focus-next"})},[]),T=l(()=>{i({type:"focus-previous"})},[]),E=l(()=>{i({type:"focus-first"})},[]),V=l(()=>{i({type:"focus-last"})},[]),L=l(()=>{i({type:"expand"})},[]),D=l(a=>{i({nodeId:a,type:"expand-node"})},[]),A=l(()=>{i({type:"collapse"})},[]),O=l(a=>{i({nodeId:a,type:"collapse-node"})},[]),P=l(()=>{i({type:"toggle-expanded"})},[]),R=l(()=>{i({type:"expand-all"})},[]),k=l(()=>{i({type:"collapse-all"})},[]),U=l(()=>{i({type:"select"})},[]),j=l(()=>{i({type:"focus-parent"})},[]),q=l(()=>{i({type:"focus-first-child"})},[]),z=l((a,u)=>{i({isLoading:u,nodeId:a,type:"set-loading"})},[]),B=l(a=>{i({nodeId:a,type:"set-children-error"})},[]),G=l((a,u)=>{i({children:u,parentId:a,type:"insert-children"})},[]);return{collapse:A,collapseAll:k,collapseNode:O,expand:L,expandAll:R,expandedIds:n.expandedIds,expandNode:D,focusedId:n.focusedId,focusFirst:E,focusFirstChild:q,focusLast:V,focusNext:F,focusParent:j,focusPrevious:T,hasScrollDown:n.viewportToIndex<n.visibleIds.length,hasScrollUp:n.viewportFromIndex>0,insertChildren:G,loadingIds:n.loadingIds,nodeMap:n.nodeMap,select:U,selectedIds:n.selectedIds,setChildrenError:B,setLoading:z,toggleExpanded:P,viewportFromIndex:n.viewportFromIndex,viewportNodes:N,viewportToIndex:n.viewportToIndex,visibleCount:n.visibleIds.length}}export{y as createDefaultState,x as reducer,Y as useTreeViewState};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const o={error:{color:"red",icon:"β"},info:{color:"blue",icon:"βΉ"},success:{color:"green",icon:"β"},warning:{color:"yellow",icon:"β "}};export{o};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ReactNode, ReactElement } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Metadata about the current page state, passed to the render-prop children.
|
|
4
|
+
*/
|
|
5
|
+
type PageMeta = {
|
|
6
|
+
readonly currentPage: number;
|
|
7
|
+
readonly endIndex: number;
|
|
8
|
+
readonly isFirstPage: boolean;
|
|
9
|
+
readonly isLastPage: boolean;
|
|
10
|
+
readonly startIndex: number;
|
|
11
|
+
readonly totalPages: number;
|
|
12
|
+
};
|
|
13
|
+
type Props<T> = {
|
|
14
|
+
/**
|
|
15
|
+
* Render-prop that receives the current page items and page metadata.
|
|
16
|
+
*/
|
|
17
|
+
readonly children: (pageItems: ReadonlyArray<T>, meta: PageMeta) => ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* Uncontrolled initial page (0-indexed).
|
|
20
|
+
* @default 0
|
|
21
|
+
*/
|
|
22
|
+
readonly defaultPage?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Color for the active page indicator.
|
|
25
|
+
* @default "cyan"
|
|
26
|
+
*/
|
|
27
|
+
readonly indicatorColor?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Whether keyboard navigation is active.
|
|
30
|
+
* @default true
|
|
31
|
+
*/
|
|
32
|
+
readonly isFocused?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* The full list of items to paginate.
|
|
35
|
+
*/
|
|
36
|
+
readonly items: ReadonlyArray<T>;
|
|
37
|
+
/**
|
|
38
|
+
* Called when the page changes.
|
|
39
|
+
*/
|
|
40
|
+
readonly onChange?: (page: number) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Controlled current page (0-indexed). When provided, the component is controlled.
|
|
43
|
+
*/
|
|
44
|
+
readonly page?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Number of items per page.
|
|
47
|
+
*/
|
|
48
|
+
readonly pageSize: number;
|
|
49
|
+
/**
|
|
50
|
+
* Indicator style.
|
|
51
|
+
* - `"dots"`: `β β β β`
|
|
52
|
+
* - `"numeric"`: `βΉ 1 / 4 βΊ`
|
|
53
|
+
* - `"fraction"`: `1/4`
|
|
54
|
+
* @default "dots"
|
|
55
|
+
*/
|
|
56
|
+
readonly style?: "dots" | "fraction" | "numeric";
|
|
57
|
+
};
|
|
58
|
+
declare const Paginator: <T>({
|
|
59
|
+
children,
|
|
60
|
+
defaultPage,
|
|
61
|
+
indicatorColor,
|
|
62
|
+
isFocused,
|
|
63
|
+
items,
|
|
64
|
+
onChange,
|
|
65
|
+
page: controlledPage,
|
|
66
|
+
pageSize,
|
|
67
|
+
style
|
|
68
|
+
}: Props<T>) => ReactElement;
|
|
69
|
+
export { PageMeta, Paginator, type Props as PaginatorProps, Props, Paginator as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsxs as s,jsx as e}from"react/jsx-runtime";import P from"@visulima/tui/components/box";import r from"@visulima/tui/components/text";import j from"@visulima/tui/hooks/use-input";import{useState as F,useCallback as M}from"react";const S=({color:a,currentPage:c,style:u,totalPages:l})=>{if(u==="fraction")return s(r,{children:[e(r,{color:a,children:c+1}),s(r,{dimColor:!0,children:["/",l]})]});if(u==="numeric"){const h=c>0?e(r,{color:a,children:"βΉ "}):e(r,{dimColor:!0,children:"βΉ "}),i=c<l-1?e(r,{color:a,children:" βΊ"}):e(r,{dimColor:!0,children:" βΊ"});return s(P,{children:[h,e(r,{color:a,children:c+1}),s(r,{dimColor:!0,children:[" ","/",l]}),i]})}const d=Array.from({length:l},(h,i)=>i===c?e(r,{color:a,children:"β"},i):e(r,{dimColor:!0,children:"β"},i));return e(P,{gap:1,children:d})},T=({children:a,defaultPage:c=0,indicatorColor:u="cyan",isFocused:l=!0,items:d,onChange:h,page:i,pageSize:g,style:y="dots"})=>{const f=i!==void 0,[w,A]=F(c),v=f?i:w,o=Math.max(1,Math.ceil(d.length/g)),n=Math.max(0,Math.min(v,o-1)),m=M(C=>{const t=Math.max(0,Math.min(C,o-1));f||A(t),h?.(t)},[o,f,h]);j(M((C,t)=>{t.leftArrow||t.pageUp?m(n-1):t.rightArrow||t.pageDown?m(n+1):t.home?m(0):t.end&&m(o-1)},[n,o,m]),{isActive:l});const p=n*g,x=Math.min(p+g,d.length),D=d.slice(p,x),I={currentPage:n,endIndex:x,isFirstPage:n===0,isLastPage:n===o-1,startIndex:p,totalPages:o};return s(P,{flexDirection:"column",children:[a(D,I),o>1&&e(S,{color:u,currentPage:n,style:y,totalPages:o})]})};export{T as Paginator,T as default};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AnsiColors } from '@visulima/colorize';
|
|
2
|
+
import { ReactNode, ReactElement } from 'react';
|
|
3
|
+
import { LiteralUnion } from 'type-fest';
|
|
4
|
+
type Props = {
|
|
5
|
+
/**
|
|
6
|
+
* Paragraph body.
|
|
7
|
+
*/
|
|
8
|
+
readonly children: ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* Foreground color.
|
|
11
|
+
*/
|
|
12
|
+
readonly color?: LiteralUnion<AnsiColors, string>;
|
|
13
|
+
/**
|
|
14
|
+
* Render the paragraph with dim color.
|
|
15
|
+
*/
|
|
16
|
+
readonly dimColor?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Blank lines to add after the paragraph.
|
|
19
|
+
* @default 1
|
|
20
|
+
*/
|
|
21
|
+
readonly marginBottom?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Blank lines to add before the paragraph.
|
|
24
|
+
* @default 0
|
|
25
|
+
*/
|
|
26
|
+
readonly marginTop?: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Wraps body text with a trailing blank line. Useful for README-style layouts.
|
|
30
|
+
*/
|
|
31
|
+
declare function Paragraph({
|
|
32
|
+
children,
|
|
33
|
+
color,
|
|
34
|
+
dimColor,
|
|
35
|
+
marginBottom,
|
|
36
|
+
marginTop
|
|
37
|
+
}: Props): ReactElement;
|
|
38
|
+
export { Paragraph, type Props as ParagraphProps, Props, Paragraph as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as o}from"react/jsx-runtime";import a from"@visulima/tui/components/box";import l from"@visulima/tui/components/text";function p({children:r,color:i,dimColor:m=!1,marginBottom:n=1,marginTop:t=0}){return o(a,{flexDirection:"column",marginBottom:n,marginTop:t,children:o(l,{color:i,dimColor:m,children:r})})}export{p as Paragraph,p as default};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { AnsiColors } from '@visulima/colorize';
|
|
2
|
+
import { ReactElement } from 'react';
|
|
3
|
+
import { LiteralUnion } from 'type-fest';
|
|
4
|
+
type Props = {
|
|
5
|
+
/**
|
|
6
|
+
* Accent color for the focused border and caret.
|
|
7
|
+
* @default "blue"
|
|
8
|
+
*/
|
|
9
|
+
readonly accentColor?: LiteralUnion<AnsiColors, string>;
|
|
10
|
+
/**
|
|
11
|
+
* Auto-focus on mount.
|
|
12
|
+
*/
|
|
13
|
+
readonly autoFocus?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Initial path when uncontrolled.
|
|
16
|
+
*/
|
|
17
|
+
readonly defaultValue?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Override how completions are produced (e.g. to sandbox or mock the
|
|
20
|
+
* filesystem). Defaults to reading the real filesystem.
|
|
21
|
+
*/
|
|
22
|
+
readonly getCompletions?: (input: string) => ReadonlyArray<string>;
|
|
23
|
+
/**
|
|
24
|
+
* Disable input and dim the display.
|
|
25
|
+
*/
|
|
26
|
+
readonly isDisabled?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Fires whenever the path text changes.
|
|
29
|
+
*/
|
|
30
|
+
readonly onChange?: (path: string) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Fires on Enter with the current path.
|
|
33
|
+
*/
|
|
34
|
+
readonly onSubmit?: (path: string) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Placeholder shown when empty.
|
|
37
|
+
* @default "Enter a pathβ¦"
|
|
38
|
+
*/
|
|
39
|
+
readonly placeholder?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Show the first few completion candidates beneath the field.
|
|
42
|
+
* @default true
|
|
43
|
+
*/
|
|
44
|
+
readonly showSuggestions?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Controlled path. When provided, `defaultValue` is ignored.
|
|
47
|
+
*/
|
|
48
|
+
readonly value?: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* A text input specialised for filesystem paths. Tab completes against the
|
|
52
|
+
* directory being typed (cycling through matches on repeated presses), and a
|
|
53
|
+
* short list of candidates is shown below the field.
|
|
54
|
+
*/
|
|
55
|
+
declare function PathInput({
|
|
56
|
+
accentColor,
|
|
57
|
+
autoFocus,
|
|
58
|
+
defaultValue,
|
|
59
|
+
getCompletions,
|
|
60
|
+
isDisabled,
|
|
61
|
+
onChange,
|
|
62
|
+
onSubmit,
|
|
63
|
+
placeholder,
|
|
64
|
+
showSuggestions,
|
|
65
|
+
value: controlledValue
|
|
66
|
+
}: Props): ReactElement;
|
|
67
|
+
export { PathInput, type Props as PathInputProps, Props, PathInput as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsxs as f,jsx as a}from"react/jsx-runtime";import{readdirSync as E,statSync as I}from"node:fs";import{basename as j,dirname as w,join as L}from"node:path";import g from"@visulima/tui/components/box";import c from"@visulima/tui/components/text";import M from"@visulima/tui/hooks/use-focus";import R from"@visulima/tui/hooks/use-input";import{useState as V,useRef as D,useMemo as X,useCallback as W}from"react";const _=/\/$/,q=r=>{const u=r.endsWith("/")?r:w(r),p=r.endsWith("/")?"":j(r);try{return E(u.length===0?".":u).filter(o=>o.startsWith(p)).map(o=>{const e=L(u,o);try{return I(e).isDirectory()?`${e}/`:e}catch{return e}}).toSorted((o,e)=>o.localeCompare(e))}catch{return[]}},C=5;function T({accentColor:r="blue",autoFocus:u=!1,defaultValue:p="",getCompletions:o=q,isDisabled:e=!1,onChange:b,onSubmit:v,placeholder:k="Enter a pathβ¦",showSuggestions:F=!0,value:S}){const{isFocused:d}=M({autoFocus:u,isActive:!e}),x=S!==void 0,[P,A]=V(p),t=S??P,s=D(null),y=D(b);y.current=b;const i=X(()=>o(t),[o,t]),m=W(n=>{x||A(n),y.current?.(n)},[x]),B=W((n,l)=>{if(l.tab){s.current??={index:0,matches:i};const{index:$,matches:h}=s.current;h.length>0&&(s.current={index:$+1,matches:h},m(h[$%h.length]));return}if(l.return){v?.(t);return}if(l.backspace||l.delete){s.current=null,m(t.slice(0,-1));return}n.length>0&&n.codePointAt(0)>=32&&(s.current=null,m(`${t}${n}`))},[i,v,t,m]);return R(B,{isActive:d&&!e}),f(g,{flexDirection:"column",children:[f(g,{borderColor:d?r:void 0,borderStyle:"round",paddingX:1,children:[a(c,{color:r,children:"π "}),t.length===0?a(c,{dimColor:!0,children:k}):a(c,{dimColor:e,children:t}),d&&t.length>0?a(c,{color:r,children:"β"}):void 0]}),F&&d&&i.length>0?f(g,{flexDirection:"column",marginLeft:1,children:[i.slice(0,C).map((n,l)=>f(c,{dimColor:!0,children:[j(n.replace(_,"")),n.endsWith("/")?"/":""]},l)),i.length>C?a(c,{dimColor:!0,children:`β¦and ${i.length-C} more`}):void 0]}):void 0]})}export{T as PathInput,T as default};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { AnsiColors } from '@visulima/colorize';
|
|
2
|
+
import { ReactElement } from 'react';
|
|
3
|
+
import { LiteralUnion } from 'type-fest';
|
|
4
|
+
type PieSlice = {
|
|
5
|
+
/**
|
|
6
|
+
* Per-slice color override. Falls back to the chart palette.
|
|
7
|
+
*/
|
|
8
|
+
readonly color?: LiteralUnion<AnsiColors, string>;
|
|
9
|
+
readonly label?: string;
|
|
10
|
+
readonly value: number;
|
|
11
|
+
};
|
|
12
|
+
type Props = {
|
|
13
|
+
/**
|
|
14
|
+
* Slices to render. Non-positive values are ignored.
|
|
15
|
+
*/
|
|
16
|
+
readonly data: ReadonlyArray<PieSlice>;
|
|
17
|
+
/**
|
|
18
|
+
* Format a slice's legend value. Defaults to `value (percent%)`.
|
|
19
|
+
*/
|
|
20
|
+
readonly formatLegend?: (slice: PieSlice, percent: number) => string;
|
|
21
|
+
/**
|
|
22
|
+
* Fallback palette cycled through when a slice has no color.
|
|
23
|
+
* @default ["cyan", "magenta", "yellow", "green", "blue", "red"]
|
|
24
|
+
*/
|
|
25
|
+
readonly palette?: ReadonlyArray<LiteralUnion<AnsiColors, string>>;
|
|
26
|
+
/**
|
|
27
|
+
* Render a legend with per-slice values and percentages.
|
|
28
|
+
* @default true
|
|
29
|
+
*/
|
|
30
|
+
readonly showLegend?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Diameter of the disc in character rows. Width is derived to keep the
|
|
33
|
+
* circle roughly round given the 2x4 braille cell aspect.
|
|
34
|
+
* @default 8
|
|
35
|
+
*/
|
|
36
|
+
readonly size?: number;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* A filled pie chart rendered at 2x4 braille sub-cell resolution. Each pixel of
|
|
40
|
+
* the disc is assigned to the slice whose angular wedge contains it, so slices
|
|
41
|
+
* keep clean colors. An optional legend lists values and percentages.
|
|
42
|
+
*/
|
|
43
|
+
declare function PieChart({
|
|
44
|
+
data,
|
|
45
|
+
formatLegend,
|
|
46
|
+
palette,
|
|
47
|
+
showLegend,
|
|
48
|
+
size
|
|
49
|
+
}: Props): ReactElement;
|
|
50
|
+
export { PieChart, type Props as PieChartProps, PieSlice, Props, PieChart as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsxs as C,jsx as s}from"react/jsx-runtime";import{createBrailleGrid as E}from"@visulima/tui/canvas";import M from"@visulima/tui/components/box";import F from"@visulima/tui/components/canvas";import $ from"@visulima/tui/components/text";import{useMemo as j}from"react";const G=["cyan","magenta","yellow","green","blue","red"],B=(r,i,l)=>l.color??i[r%i.length]??"cyan",S=(r,i)=>`${r.label??""} ${r.value} (${i.toFixed(0)}%)`.trim();function N({data:r,formatLegend:i=S,palette:l=G,showLegend:D=!0,size:L=8}){const c=j(()=>r.filter(t=>t.value>0),[r]),h=j(()=>c.reduce((t,n)=>t+n.value,0),[c]),u=Math.max(2,L),x=Math.max(2,Math.round(u*4/2/2));return C(M,{flexDirection:"row",gap:2,children:[s(F,{draw:t=>{if(t.clear(),h<=0)return;const n=t.width*2,v=t.height*4,w=n/2,P=v/2,y=Math.max(1,Math.min(w,P)-1),m=[];let g=0;c.forEach((o,e)=>{const a=g/h*Math.PI*2;g+=o.value;const d=g/h*Math.PI*2;m.push({color:B(e,l,o),end:d,start:a})});const p=new Map,z=o=>{const e=p.get(o);if(e!==void 0)return e;const a=E(t.width,t.height);return p.set(o,a),a};for(let o=0;o<v;o+=1)for(let e=0;e<n;e+=1){const a=e-w,d=o-P;if(a*a+d*d>y*y)continue;let f=Math.atan2(a,-d);f<0&&(f+=Math.PI*2);const I=m.find(b=>f>=b.start&&f<b.end)??m[m.length-1];I&&z(I.color).plotPoint(e,o)}for(const[o,e]of p)e.flush(t,{color:o})},height:u,version:[r,h,l,u,x],width:x}),D&&c.length>0?s(M,{flexDirection:"column",children:c.map((t,n)=>C(M,{gap:1,children:[s($,{color:B(n,l,t),children:"β"}),s($,{dimColor:!0,children:i(t,t.value/h*100)})]},n))}):void 0]})}export{N as PieChart,N as default};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { AnsiColors } from '@visulima/colorize';
|
|
2
|
+
import { ReactElement } from 'react';
|
|
3
|
+
import { LiteralUnion } from 'type-fest';
|
|
4
|
+
type Props = {
|
|
5
|
+
/**
|
|
6
|
+
* Whether to pulse the skeleton blocks (shimmer).
|
|
7
|
+
* @default true
|
|
8
|
+
*/
|
|
9
|
+
readonly animated?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Glyph repeated to fill each placeholder block.
|
|
12
|
+
* @default "β" (full block)
|
|
13
|
+
*/
|
|
14
|
+
readonly character?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Block color.
|
|
17
|
+
* @default "gray"
|
|
18
|
+
*/
|
|
19
|
+
readonly color?: LiteralUnion<AnsiColors, string>;
|
|
20
|
+
/**
|
|
21
|
+
* Milliseconds between pulses.
|
|
22
|
+
* @default 500
|
|
23
|
+
*/
|
|
24
|
+
readonly interval?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Number of skeleton rows to render.
|
|
27
|
+
* @default 3
|
|
28
|
+
*/
|
|
29
|
+
readonly rows?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Total width reference. When omitted, the block flexes to fill its
|
|
32
|
+
* parent.
|
|
33
|
+
*/
|
|
34
|
+
readonly width?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Relative widths of each row (0β1). The list is sampled cyclically.
|
|
37
|
+
* @default [1, 0.85, 0.6]
|
|
38
|
+
*/
|
|
39
|
+
readonly widths?: ReadonlyArray<number>;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Skeleton loading indicator. Renders a stack of solid blocks whose widths
|
|
43
|
+
* vary to mimic text placeholders. Pair with `async` data fetching.
|
|
44
|
+
* @returns A `ReactElement` rendering the skeleton rows.
|
|
45
|
+
*/
|
|
46
|
+
declare function Placeholder({
|
|
47
|
+
animated,
|
|
48
|
+
character,
|
|
49
|
+
color,
|
|
50
|
+
interval,
|
|
51
|
+
rows,
|
|
52
|
+
width,
|
|
53
|
+
widths
|
|
54
|
+
}: Props): ReactElement;
|
|
55
|
+
export { Placeholder, type Props as PlaceholderProps, Props, Placeholder as default };
|