@republicroad/seal-appshell 1.1.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.
Files changed (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +74 -0
  3. package/dist/assets/custom-node-schema.json.d.ts +549 -0
  4. package/dist/components/__tests__/skinned-decision-graph.test.d.ts +1 -0
  5. package/dist/components/__tests__/skinned-header.test.d.ts +1 -0
  6. package/dist/components/__tests__/skinned-panels.test.d.ts +1 -0
  7. package/dist/components/__tests__/skinned-simulator-panel.test.d.ts +1 -0
  8. package/dist/components/__tests__/skinned-simulator.test.d.ts +1 -0
  9. package/dist/components/custom-node/__tests__/custom-node-button.test.d.ts +1 -0
  10. package/dist/components/custom-node/__tests__/schema-container-tab.test.d.ts +1 -0
  11. package/dist/components/custom-node/crypto-node.d.ts +5 -0
  12. package/dist/components/custom-node/current-date-node.d.ts +8 -0
  13. package/dist/components/custom-node/http-request-node.d.ts +5 -0
  14. package/dist/components/custom-node/key-value-editor.d.ts +16 -0
  15. package/dist/components/custom-node/locked-corner-badge.d.ts +2 -0
  16. package/dist/components/custom-node/query-list-node.d.ts +5 -0
  17. package/dist/components/custom-node/schema-container-tab.d.ts +13 -0
  18. package/dist/components/icons/code.d.ts +3 -0
  19. package/dist/components/icons/flash-circle.d.ts +3 -0
  20. package/dist/components/reui/alert.d.ts +9 -0
  21. package/dist/components/reui/autocomplete.d.ts +40 -0
  22. package/dist/components/reui/badge.d.ts +14 -0
  23. package/dist/components/reui/cascader/cascader-async.d.ts +86 -0
  24. package/dist/components/reui/cascader/cascader-columns.d.ts +26 -0
  25. package/dist/components/reui/cascader/cascader-context.d.ts +190 -0
  26. package/dist/components/reui/cascader/cascader-footer.d.ts +83 -0
  27. package/dist/components/reui/cascader/cascader-i18n.d.ts +13 -0
  28. package/dist/components/reui/cascader/cascader-item.d.ts +112 -0
  29. package/dist/components/reui/cascader/cascader-lib.d.ts +137 -0
  30. package/dist/components/reui/cascader/cascader-nav.d.ts +56 -0
  31. package/dist/components/reui/cascader/cascader-types.d.ts +179 -0
  32. package/dist/components/reui/cascader/cascader-virtual.d.ts +62 -0
  33. package/dist/components/reui/cascader/cascader.d.ts +325 -0
  34. package/dist/components/shell-header.d.ts +20 -0
  35. package/dist/components/skinned-decision-graph.d.ts +19 -0
  36. package/dist/components/ui/alert-dialog.d.ts +20 -0
  37. package/dist/components/ui/avatar.d.ts +5 -0
  38. package/dist/components/ui/badge.d.ts +9 -0
  39. package/dist/components/ui/button.d.ts +11 -0
  40. package/dist/components/ui/dropdown-menu.d.ts +27 -0
  41. package/dist/components/ui/field.d.ts +24 -0
  42. package/dist/components/ui/input.d.ts +3 -0
  43. package/dist/components/ui/label.d.ts +4 -0
  44. package/dist/components/ui/scroll-area.d.ts +4 -0
  45. package/dist/components/ui/select.d.ts +13 -0
  46. package/dist/components/ui/separator.d.ts +3 -0
  47. package/dist/components/ui/sheet.d.ts +23 -0
  48. package/dist/components/ui/sonner.d.ts +5 -0
  49. package/dist/components/ui/spinner.d.ts +2 -0
  50. package/dist/components/ui/switch.d.ts +3 -0
  51. package/dist/components/ui/tabs.d.ts +6 -0
  52. package/dist/components/ui/toggle-group.d.ts +13 -0
  53. package/dist/components/ui/toggle.d.ts +12 -0
  54. package/dist/components/ui/tooltip.d.ts +7 -0
  55. package/dist/components/version-history/__tests__/version-history-panel.test.d.ts +1 -0
  56. package/dist/components/version-history/version-history-panel.d.ts +40 -0
  57. package/dist/context/theme.provider.d.ts +29 -0
  58. package/dist/hooks/useCustomNodes.d.ts +16 -0
  59. package/dist/index.d.ts +24 -0
  60. package/dist/index.js +121959 -0
  61. package/dist/integration/decision-graph-appshell.stories.d.ts +10 -0
  62. package/dist/lib/__tests__/crypto-protocol.test.d.ts +1 -0
  63. package/dist/lib/__tests__/custom-node-registry.test.d.ts +1 -0
  64. package/dist/lib/__tests__/http-request-protocol.test.d.ts +1 -0
  65. package/dist/lib/__tests__/json-path-protocol.test.d.ts +1 -0
  66. package/dist/lib/__tests__/storage-key.test.d.ts +1 -0
  67. package/dist/lib/auth/__tests__/adapter.test.d.ts +1 -0
  68. package/dist/lib/auth/adapter.d.ts +7 -0
  69. package/dist/lib/auth-client.d.ts +3 -0
  70. package/dist/lib/crypto-protocol.d.ts +28 -0
  71. package/dist/lib/custom-node-plans.d.ts +24 -0
  72. package/dist/lib/custom-node-registry.d.ts +13 -0
  73. package/dist/lib/custom-node-schema-source.d.ts +6 -0
  74. package/dist/lib/custom-node-types.d.ts +73 -0
  75. package/dist/lib/http-request-protocol.d.ts +47 -0
  76. package/dist/lib/json-path-protocol.d.ts +11 -0
  77. package/dist/lib/roster-source.d.ts +12 -0
  78. package/dist/lib/storage-key.d.ts +5 -0
  79. package/dist/lib/user-resolver.d.ts +6 -0
  80. package/dist/lib/utils.d.ts +2 -0
  81. package/dist/reui/icons/animated/duotone/lock.d.ts +3 -0
  82. package/dist/reui/icons/animated/outline/play-circle.d.ts +3 -0
  83. package/dist/reui/icons/default/outline/plus-circle.d.ts +3 -0
  84. package/dist/reui/icons/default/outline/shield-search.d.ts +3 -0
  85. package/dist/reui/icons/default/outline/trash-square.d.ts +3 -0
  86. package/dist/setupTests.d.ts +0 -0
  87. package/dist/shell/__tests__/default-simulate.test.d.ts +1 -0
  88. package/dist/shell/__tests__/graphs-http-adapter.test.d.ts +1 -0
  89. package/dist/shell/__tests__/indexed-db-adapter.test.d.ts +0 -0
  90. package/dist/shell/__tests__/restore.test.d.ts +0 -0
  91. package/dist/shell/default-simulate.d.ts +3 -0
  92. package/dist/shell/editor-shell.context.d.ts +22 -0
  93. package/dist/shell/execute-simulate.d.ts +8 -0
  94. package/dist/shell/graphs-http-adapter.d.ts +7 -0
  95. package/dist/shell/index.d.ts +10 -0
  96. package/dist/shell/indexed-db-adapter.d.ts +16 -0
  97. package/dist/shell/persistence.d.ts +89 -0
  98. package/dist/shell/restore.d.ts +21 -0
  99. package/dist/shell/types.d.ts +20 -0
  100. package/dist/skin/__tests__/apply.test.d.ts +1 -0
  101. package/dist/skin/apply.d.ts +7 -0
  102. package/dist/skin/layout.d.ts +17 -0
  103. package/dist/skin/types.d.ts +73 -0
  104. package/dist/style.css +2 -0
  105. package/package.json +73 -0
@@ -0,0 +1,5 @@
1
+ import { default as React } from 'react';
2
+ export declare const HttpRequestTab: React.FC<{
3
+ id: string;
4
+ }>;
5
+ export declare const httpRequestNode: import('../..').CustomNodeSpec;
@@ -0,0 +1,16 @@
1
+ import { default as React } from 'react';
2
+ export declare const Hint: React.FC<{
3
+ label: string;
4
+ children: React.ReactElement;
5
+ }>;
6
+ export interface KeyValueEditorProps {
7
+ label: string;
8
+ addLabel: string;
9
+ deleteLabel: string;
10
+ valuePlaceholder: string;
11
+ rawPlaceholder: string;
12
+ value: string;
13
+ onChange: (next: string) => void;
14
+ }
15
+ /** `{ k: expr, ... }` 对象字面量 ⇄ 键值行编辑器;无法解析时自动切原始表达式模式 */
16
+ export declare const KeyValueEditor: React.FC<KeyValueEditorProps>;
@@ -0,0 +1,2 @@
1
+ /** 画布节点角标:config.locked === true 时显示,标记此节点有专属页面设计 */
2
+ export declare function LockedCornerBadge(): import('react').JSX.Element;
@@ -0,0 +1,5 @@
1
+ import { default as React } from 'react';
2
+ export declare const QueryListTab: React.FC<{
3
+ id: string;
4
+ }>;
5
+ export declare const queryListNode: import('../..').CustomNodeSpec;
@@ -0,0 +1,13 @@
1
+ import { default as React } from 'react';
2
+ import { CustomFunctionTool } from '../../lib/custom-node-types';
3
+ type SchemaContainerTabProps = {
4
+ id: string;
5
+ tools: CustomFunctionTool[];
6
+ };
7
+ /**
8
+ * schema 驱动集合容器节点的编辑面板(udf-lab 缺陷修复):
9
+ * 左列 key 文本框可编辑;右列函数用下拉展示(限定命名空间工具集);
10
+ * 参数按位置序渲染表达式输入(字面量参数由调用方自带引号)。
11
+ */
12
+ export declare const SchemaContainerTab: React.FC<SchemaContainerTabProps>;
13
+ export {};
@@ -0,0 +1,3 @@
1
+ export default function CodeIcon({ className }: {
2
+ className?: string;
3
+ }): import('react').JSX.Element;
@@ -0,0 +1,3 @@
1
+ export default function FlashCircleIcon({ className }: {
2
+ className?: string;
3
+ }): import('react').JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { VariantProps } from 'class-variance-authority';
2
+ declare const alertVariants: (props?: ({
3
+ variant?: "default" | "destructive" | "success" | "warning" | "info" | "invert" | null | undefined;
4
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
5
+ declare function Alert({ className, variant, ...props }: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>): import('react').JSX.Element;
6
+ declare function AlertTitle({ className, ...props }: React.ComponentProps<'div'>): import('react').JSX.Element;
7
+ declare function AlertDescription({ className, ...props }: React.ComponentProps<'div'>): import('react').JSX.Element;
8
+ declare function AlertAction({ className, ...props }: React.ComponentProps<'div'>): import('react').JSX.Element;
9
+ export { Alert, AlertTitle, AlertDescription, AlertAction };
@@ -0,0 +1,40 @@
1
+ import { Autocomplete as AutocompletePrimitive } from '@base-ui/react/autocomplete';
2
+ import { VariantProps } from 'class-variance-authority';
3
+ declare const inputVariants: (props?: ({
4
+ size?: "default" | "sm" | "lg" | null | undefined;
5
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
6
+ declare const Autocomplete: typeof AutocompletePrimitive.Root;
7
+ declare function AutocompleteValue({ ...props }: AutocompletePrimitive.Value.Props): import('react').JSX.Element;
8
+ declare function AutocompleteInput({ className, size, showClear, showTrigger, ...props }: Omit<AutocompletePrimitive.Input.Props, 'size'> & VariantProps<typeof inputVariants> & {
9
+ showClear?: boolean;
10
+ showTrigger?: boolean;
11
+ }): import('react').JSX.Element;
12
+ declare function AutocompleteStatus({ className, ...props }: AutocompletePrimitive.Status.Props): import('react').JSX.Element;
13
+ declare function AutocompletePortal({ ...props }: AutocompletePrimitive.Portal.Props): import('react').JSX.Element;
14
+ declare function AutocompleteBackdrop({ ...props }: AutocompletePrimitive.Backdrop.Props): import('react').JSX.Element;
15
+ declare function AutocompletePositioner({ className, ...props }: AutocompletePrimitive.Positioner.Props): import('react').JSX.Element;
16
+ declare function AutocompleteList({ className, scrollAreaClassName, ...props }: AutocompletePrimitive.List.Props & {
17
+ scrollAreaClassName?: string;
18
+ scrollFade?: boolean;
19
+ scrollbarGutter?: boolean;
20
+ }): import('react').JSX.Element;
21
+ declare function AutocompleteCollection({ ...props }: React.ComponentProps<typeof AutocompletePrimitive.Collection>): import('react').JSX.Element;
22
+ declare function AutocompleteRow({ className, ...props }: React.ComponentProps<typeof AutocompletePrimitive.Row>): import('react').JSX.Element;
23
+ declare function AutocompleteItem({ className, ...props }: React.ComponentProps<typeof AutocompletePrimitive.Item>): import('react').JSX.Element;
24
+ export interface AutocompleteContentProps extends React.ComponentProps<typeof AutocompletePrimitive.Popup> {
25
+ align?: AutocompletePrimitive.Positioner.Props['align'];
26
+ sideOffset?: AutocompletePrimitive.Positioner.Props['sideOffset'];
27
+ alignOffset?: AutocompletePrimitive.Positioner.Props['alignOffset'];
28
+ side?: AutocompletePrimitive.Positioner.Props['side'];
29
+ anchor?: AutocompletePrimitive.Positioner.Props['anchor'];
30
+ showBackdrop?: boolean;
31
+ }
32
+ declare function AutocompleteContent({ className, children, showBackdrop, align, sideOffset, alignOffset, side, anchor, ...props }: AutocompleteContentProps): import('react').JSX.Element;
33
+ declare function AutocompleteGroup({ ...props }: React.ComponentProps<typeof AutocompletePrimitive.Group>): import('react').JSX.Element;
34
+ declare function AutocompleteGroupLabel({ className, ...props }: React.ComponentProps<typeof AutocompletePrimitive.GroupLabel>): import('react').JSX.Element;
35
+ declare function AutocompleteEmpty({ className, ...props }: React.ComponentProps<typeof AutocompletePrimitive.Empty>): import('react').JSX.Element;
36
+ declare function AutocompleteClear({ className, ...props }: React.ComponentProps<typeof AutocompletePrimitive.Clear>): import('react').JSX.Element;
37
+ declare function AutocompleteTrigger({ className, ...props }: React.ComponentProps<typeof AutocompletePrimitive.Trigger>): import('react').JSX.Element;
38
+ declare function AutocompleteArrow({ ...props }: React.ComponentProps<typeof AutocompletePrimitive.Arrow>): import('react').JSX.Element;
39
+ declare function AutocompleteSeparator({ className, ...props }: React.ComponentProps<typeof AutocompletePrimitive.Separator>): import('react').JSX.Element;
40
+ export { Autocomplete, AutocompleteValue, AutocompleteTrigger, AutocompleteInput, AutocompleteStatus, AutocompletePortal, AutocompleteBackdrop, AutocompletePositioner, AutocompleteContent, AutocompleteList, AutocompleteCollection, AutocompleteRow, AutocompleteItem, AutocompleteGroup, AutocompleteGroupLabel, AutocompleteEmpty, AutocompleteClear, AutocompleteArrow, AutocompleteSeparator, };
@@ -0,0 +1,14 @@
1
+ import { useRender } from '@base-ui/react/use-render';
2
+ import { VariantProps } from 'class-variance-authority';
3
+ declare const badgeVariants: (props?: ({
4
+ variant?: "default" | "destructive" | "outline" | "secondary" | "focus" | "success" | "warning" | "info" | "invert" | "primary-light" | "warning-light" | "success-light" | "info-light" | "destructive-light" | "invert-light" | "focus-light" | "primary-outline" | "warning-outline" | "success-outline" | "info-outline" | "destructive-outline" | "invert-outline" | "focus-outline" | null | undefined;
5
+ size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
6
+ radius?: "default" | "full" | null | undefined;
7
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
8
+ interface BadgeProps extends useRender.ComponentProps<'span'> {
9
+ variant?: VariantProps<typeof badgeVariants>['variant'];
10
+ size?: VariantProps<typeof badgeVariants>['size'];
11
+ radius?: VariantProps<typeof badgeVariants>['radius'];
12
+ }
13
+ declare function Badge({ className, variant, size, radius, render, ...props }: BadgeProps): import('react').ReactElement<any, string | import('react').JSXElementConstructor<any>>;
14
+ export { Badge, badgeVariants, type BadgeProps };
@@ -0,0 +1,86 @@
1
+ import { CascaderIndex, CascaderLoadContext, CascaderLoadReason, CascaderLoadResult, CascaderLoadState, CascaderNode, CascaderSearchContext } from '../../../components/reui/cascader/cascader-types';
2
+ /**
3
+ * Async data for the cascader. Loaded pages live in their own store and are
4
+ * MERGED onto the index built from `items`, never folded into that build:
5
+ * `items` changes identity on any parent re-render, and folding would discard
6
+ * every level the user drilled into. Map membership is the load discriminator:
7
+ * no `states` entry means never fetched, while an entry with no `loading`, no
8
+ * `error` and no children means empty for real.
9
+ */
10
+ /** Fetches one level. `node` is `null` for the root level. */
11
+ export type CascaderGetChildren<T = unknown> = (node: CascaderNode<T> | null, context: CascaderLoadContext) => CascaderNode<T>[] | CascaderLoadResult<T> | Promise<CascaderNode<T>[] | CascaderLoadResult<T>>;
12
+ /** Server-side search, replacing the local index scan while the query is set. */
13
+ export type CascaderOnSearch<T = unknown> = (query: string, context: CascaderSearchContext) => CascaderNode<T>[] | CascaderLoadResult<T> | Promise<CascaderNode<T>[] | CascaderLoadResult<T>>;
14
+ /** Resolves a selected value to its ancestor chain, root first, node last. */
15
+ export type CascaderResolveValue<T = unknown> = (value: string, context: CascaderLoadContext) => CascaderNode<T>[] | Promise<CascaderNode<T>[]>;
16
+ export interface CascaderLoaderStore<T = unknown> {
17
+ /** Keyed by LEVEL: a parent's value, or `CASCADER_ROOT_KEY` for the root. */
18
+ pages: Map<string, CascaderNode<T>[]>;
19
+ states: Map<string, CascaderLoadState>;
20
+ /** Keyed by node value: search hits and resolved selections, level-less. */
21
+ detached: Map<string, CascaderNode<T>>;
22
+ }
23
+ export interface UseCascaderLoaderOptions<T = unknown> {
24
+ /** The index built from `items`, before any pages are merged in. */
25
+ base: CascaderIndex<T>;
26
+ getChildren?: CascaderGetChildren<T>;
27
+ onSearch?: CascaderOnSearch<T>;
28
+ resolveValue?: CascaderResolveValue<T>;
29
+ /** Milliseconds of quiet before `onSearch` fires. */
30
+ searchDebounce?: number;
31
+ /** Changing this drops every cached page, state and detached node. */
32
+ loadKey?: unknown;
33
+ /** Speculatively fetch a branch's children when it is highlighted. */
34
+ prefetch?: boolean;
35
+ /** Called when a request fails. Never for an aborted or superseded one. */
36
+ onLoadError?: (error: unknown, context: {
37
+ parent: string | null;
38
+ reason: string;
39
+ }) => void;
40
+ /** Whether the panel is live: the popup is open, or the cascader is inline. */
41
+ enabled: boolean;
42
+ query: string;
43
+ /** Level keys that are currently on screen. Root is `CASCADER_ROOT_KEY`. */
44
+ levels: string[];
45
+ /** The navigation path, handed to `onSearch` as its scope. */
46
+ path: string[];
47
+ /** Current selection, for `resolveValue`. */
48
+ values: string[];
49
+ }
50
+ export interface CascaderLoader<T = unknown> {
51
+ /** Whether a `getChildren` loader is configured at all. */
52
+ active: boolean;
53
+ store: CascaderLoaderStore<T>;
54
+ states: ReadonlyMap<string, CascaderLoadState>;
55
+ /**
56
+ * Async search hits, `null` when no `onSearch` is running. EMPTY while the
57
+ * first request is in flight, so the level behind is not shown as the answer.
58
+ */
59
+ searchResults: CascaderNode<T>[] | null;
60
+ searchState: CascaderLoadState | null;
61
+ /**
62
+ * Fetches a level's FIRST page. No-ops on a `states` entry (in flight,
63
+ * loaded or failed) or when `items` fills the level; pages are never
64
+ * consulted, so a `resolveValue` chain still fetches. The level effect fires
65
+ * only for on-screen levels, so a branch merely PRESSED is asked for by hand.
66
+ */
67
+ ensureLevel: (parentKey: string, reason: CascaderLoadReason) => void;
68
+ /** Fetches the next page of a level. No-ops unless one is available. */
69
+ loadMore: (parentKey: string) => void;
70
+ /** Refires a failed level. No-ops unless that level is in an error state. */
71
+ retryLevel: (parentKey: string) => void;
72
+ /** Schedules a speculative fetch. Safe to call on every highlight move. */
73
+ prefetchNode: (node: CascaderNode<T> | null | undefined) => void;
74
+ /**
75
+ * Evicts ONE level: aborts its request, drops its `states`/`pages` entries
76
+ * and paging latch, so the level effect refetches it. `null` = root level.
77
+ */
78
+ invalidateLevel: (value: string | null) => void;
79
+ }
80
+ /**
81
+ * The loader. A SIBLING of the `buildCascaderIndex` memo, never inside it: the
82
+ * build stays pure in `items`, the merge pure in that build plus this store.
83
+ */
84
+ export declare function useCascaderLoader<T = unknown>({ base, getChildren, onSearch, resolveValue, searchDebounce, loadKey, prefetch, onLoadError, enabled, query, levels, path, values, }: UseCascaderLoaderOptions<T>): CascaderLoader<T>;
85
+ /** One level's load state, `null` when never fetched. Omit for the root. */
86
+ export declare function useCascaderLoadState(parent?: string | null): CascaderLoadState | null;
@@ -0,0 +1,26 @@
1
+ import { CascaderColumn } from '../../../components/reui/cascader/cascader-context';
2
+ import * as React from 'react';
3
+ export interface CascaderColumnsProps extends Omit<React.ComponentProps<'div'>, 'children'> {
4
+ /** Width of each column. */
5
+ columnWidth?: number | string;
6
+ /** Height CAP per column. Falls back to the root `maxHeight`, then 24rem. */
7
+ maxHeight?: number | string;
8
+ /** Replaces the default panel; the seam a windowed column plugs into. */
9
+ children?: (column: CascaderColumn) => React.ReactNode;
10
+ }
11
+ /**
12
+ * Miller columns: the open trail side by side, one panel per level. Only the
13
+ * DEEPEST column is a real listbox (Base UI owns exactly one list); the trail
14
+ * behind is plain buttons, which keeps one state machine instead of a second,
15
+ * 2D one. `CascaderInput` moves between columns with ArrowLeft/ArrowRight.
16
+ */
17
+ declare function CascaderColumns({ className, columnWidth, maxHeight: maxHeightProp, children, ...props }: CascaderColumnsProps): React.JSX.Element | null;
18
+ export interface CascaderColumnPanelProps {
19
+ column: CascaderColumn;
20
+ /** Replaces the panel's rows; the empty state still wins on an empty column. */
21
+ children?: React.ReactNode;
22
+ /** Containing block for the windowed column's absolutely positioned rows. */
23
+ virtualized?: boolean;
24
+ }
25
+ declare function CascaderColumnPanel({ column, children, virtualized }: CascaderColumnPanelProps): React.JSX.Element;
26
+ export { CascaderColumnPanel, CascaderColumns };
@@ -0,0 +1,190 @@
1
+ import { CascaderActionItem, CascaderChangeReason, CascaderFlatNode, CascaderIndex, CascaderLabels, CascaderLoadState, CascaderMode, CascaderNode, CascaderSearchScope } from '../../../components/reui/cascader/cascader-types';
2
+ import * as React from 'react';
3
+ /**
4
+ * Four contexts, not one: actions (config and callbacks, near-stable), state
5
+ * (every keystroke), render (`renderItem` identity) and highlight (every arrow
6
+ * key and pointer move). One combined context re-rendered every row on every
7
+ * keystroke, which is what made `React.memo` on the row worth nothing.
8
+ */
9
+ export interface CascaderColumn<T = unknown> {
10
+ parent: CascaderNode<T> | null;
11
+ items: CascaderNode<T>[];
12
+ depth: number;
13
+ /** The node in this column that is drilled into, if any. */
14
+ activeValue: string | null;
15
+ /** Whether this is the deepest column, the one Base UI owns. */
16
+ active: boolean;
17
+ }
18
+ export interface CascaderItemState<T = unknown> {
19
+ branch: boolean;
20
+ selected: boolean;
21
+ disabled: boolean;
22
+ depth: number;
23
+ count: number;
24
+ /** Ancestor chain, root first. Populated for deep-search rows. */
25
+ path: CascaderNode<T>[];
26
+ }
27
+ /**
28
+ * Everything derived from the query, the path and the selection. One keystroke
29
+ * rebuilds most of it, so never subscribe to it from a row.
30
+ */
31
+ export interface CascaderStateContextValue<T = unknown> {
32
+ /** Same `useMemo` identity as the actions context's `index`. */
33
+ index: CascaderIndex<T>;
34
+ path: string[];
35
+ expanded: ReadonlySet<string>;
36
+ query: string;
37
+ currentParent: CascaderNode<T> | null;
38
+ /** Rows for the current level, already filtered. */
39
+ levelItems: CascaderNode<T>[];
40
+ deepResults: CascaderNode<T>[] | null;
41
+ /** What `Combobox.Root` is currently rendering, in render order. */
42
+ renderedItems: CascaderNode<T>[];
43
+ columns: CascaderColumn<T>[];
44
+ treeRows: CascaderFlatNode<T>[];
45
+ selectedValues: string[];
46
+ /** Selected nodes below each value, at any depth. Absent means zero. */
47
+ selectedDescendants: ReadonlyMap<string, number>;
48
+ /**
49
+ * Per level, keyed by parent value or `CASCADER_ROOT_KEY`. MEMBERSHIP is the
50
+ * discriminator: no entry means never fetched, an entry with no `loading`, no
51
+ * `error` and no children means fetched and genuinely empty.
52
+ */
53
+ loadStates: ReadonlyMap<string, CascaderLoadState>;
54
+ /** Async search, `null` when idle. Separate: a search belongs to no level. */
55
+ searchState: CascaderLoadState | null;
56
+ announcement: string;
57
+ }
58
+ declare const CascaderStateContext: React.Context<CascaderStateContextValue<unknown> | undefined>;
59
+ /**
60
+ * One provider serves every `T`, so the context holds an erased `unknown` value
61
+ * and this cast restores it. The primitive never inspects the payload.
62
+ */
63
+ export declare function useCascaderState<T = unknown>(): CascaderStateContextValue<T>;
64
+ /**
65
+ * Config and callbacks, slow enough that a memoised row can subscribe: the
66
+ * mutators are `[]`-dep callbacks over a latest-props ref. The three predicates
67
+ * are the exception, read DURING RENDER where a ref written in an effect would
68
+ * return the previous commit's answer, so each is memoised on its own input.
69
+ */
70
+ export interface CascaderActionsContextValue<T = unknown> {
71
+ index: CascaderIndex<T>;
72
+ mode: CascaderMode;
73
+ multiple: boolean;
74
+ /** Multi-select only: a commit propagates over the LOADED subtree. */
75
+ cascade: boolean;
76
+ /** Whether a BRANCH is committable. Per-LIST: the check gutter is a COLUMN. */
77
+ branchesSelectable: boolean;
78
+ /** Draws the SINGLE-SELECT check and its gutter. Ignored in multi-select. */
79
+ indicator: boolean;
80
+ expandTrigger?: 'click' | 'hover';
81
+ actions: CascaderActionItem[];
82
+ searchScope: CascaderSearchScope;
83
+ maxHeight?: number | string;
84
+ inline: boolean;
85
+ invalid: boolean;
86
+ /**
87
+ * Id prefix; columns are `${baseId}-column-${depth}`. The SCHEME is contract:
88
+ * the filters primitive's `FilterMenuPinKeeper` restores its highlight across
89
+ * a live re-pin through `${baseId}-column-0` and the `cascader-item` slot.
90
+ */
91
+ baseId: string;
92
+ labels: CascaderLabels;
93
+ /**
94
+ * Whether rows are WINDOWED. Also handed to `Combobox.Root`, which is what
95
+ * makes an explicit row `index` legal: forwarding one while this is false
96
+ * makes `aria-activedescendant` resolve to nothing. Latched per level.
97
+ */
98
+ virtualized: boolean;
99
+ /** Mounts a windowing renderer, returns its unregister. LAYOUT effect only. */
100
+ registerVirtualRenderer: () => () => void;
101
+ /** The root's `virtualize` prop. `undefined` means "decide by count". */
102
+ virtualize?: boolean;
103
+ virtualizeThreshold: number;
104
+ estimateRowSize: number;
105
+ overscan: number;
106
+ /** Tells "this level is empty" from "not fetched yet" before a load state. */
107
+ hasLoader: boolean;
108
+ /** Next page of a level. Latched: a page with nothing new is not re-asked. */
109
+ loadMore: (parentKey: string) => void;
110
+ retryLevel: (parentKey: string) => void;
111
+ /**
112
+ * Evicts one level's async cache, `null` for the root, so membership reads
113
+ * never-loaded. A level that is on screen when evicted refetches at once.
114
+ */
115
+ invalidateLevel: (value: string | null) => void;
116
+ /** Index as of the last commit. Use in the stable callbacks, not `index`. */
117
+ getIndex: () => CascaderIndex<T>;
118
+ getState: () => CascaderStateContextValue<T>;
119
+ /** Highlighted row or null. A getter: the highlight moves per arrow key. */
120
+ getHighlighted: () => CascaderNode<T> | null;
121
+ setPath: (next: string[] | ((prev: string[]) => string[])) => void;
122
+ pushLevel: (value: string) => void;
123
+ popLevel: () => void;
124
+ goToDepth: (depth: number) => void;
125
+ toggleExpanded: (value: string) => void;
126
+ /**
127
+ * Registers a footer submenu as open or closed. `Combobox` has no
128
+ * `FloatingTree`, so one Escape would dismiss the flyout AND the cascader;
129
+ * the root's `onOpenChange` guard cancels the close while any is open, from a
130
+ * ref so it reads as of that event without re-rendering the root.
131
+ */
132
+ setFlyoutOpen: (key: string, open: boolean) => void;
133
+ hasOpenFlyout: () => boolean;
134
+ setQuery: (next: string) => void;
135
+ /**
136
+ * Replaces the selection. `onValueChange` diffs it against the current one
137
+ * for its node and reason; pass `reason` only when the caller knows better.
138
+ */
139
+ setSelection: (values: string[], reason?: CascaderChangeReason) => void;
140
+ /** Commits a node, for rows outside the listbox such as ancestor columns. */
141
+ commit: (node: CascaderNode<T>) => void;
142
+ navigate: (node: CascaderNode<T>) => void;
143
+ /** Into `node` as a child of `depth`, replacing anything deeper. */
144
+ navigateAt: (node: CascaderNode<T>, depth: number) => void;
145
+ /** Never undefined: falls back to a remembered label, then a synthetic node. */
146
+ resolveNode: (value: string) => CascaderNode<T>;
147
+ isBranch: (node: CascaderNode<T>) => boolean;
148
+ isSelectable: (node: CascaderNode<T>) => boolean;
149
+ isSelected: (node: CascaderNode<T>) => boolean;
150
+ /** Always `false` without `cascade`: partial selection needs propagation. */
151
+ isIndeterminate: (node: CascaderNode<T>) => boolean;
152
+ /**
153
+ * O(1) read of `selectedDescendants`; a memoised row may not subscribe.
154
+ * Unlike `isIndeterminate` this answers in every mode.
155
+ */
156
+ selectedDescendantCount: (node: CascaderNode<T>) => number;
157
+ }
158
+ declare const CascaderActionsContext: React.Context<CascaderActionsContextValue<unknown> | undefined>;
159
+ export declare function useCascaderActions<T = unknown>(): CascaderActionsContextValue<T>;
160
+ /**
161
+ * Their own context, republished every render. They cannot ride on the actions
162
+ * context: an inline closure read off a memoised object is whichever closure
163
+ * that object captured, so the row would call a stale prop over stale state.
164
+ */
165
+ export interface CascaderRenderContextValue<T = unknown> {
166
+ renderItem?: (node: CascaderNode<T>, state: CascaderItemState<T>) => React.ReactNode;
167
+ renderLabel?: (node: CascaderNode<T>, state: CascaderItemState<T>) => React.ReactNode;
168
+ }
169
+ declare const CascaderRenderContext: React.Context<CascaderRenderContextValue<unknown>>;
170
+ export declare function useCascaderRender<T = unknown>(): CascaderRenderContextValue<T>;
171
+ export interface CascaderHighlight {
172
+ index: number;
173
+ value: string | null;
174
+ }
175
+ /**
176
+ * An external store, deliberately NOT React state: `onItemHighlighted` fires on
177
+ * every arrow key AND every pointer move over the list, so `setState` would
178
+ * re-render the whole root at mousemove rate. A store re-renders subscribers
179
+ * only, which inside the primitive is the virtualizer.
180
+ */
181
+ export interface CascaderHighlightStore {
182
+ subscribe: (onStoreChange: () => void) => () => void;
183
+ getSnapshot: () => CascaderHighlight;
184
+ set: (next: CascaderHighlight) => void;
185
+ }
186
+ export declare function createCascaderHighlightStore(): CascaderHighlightStore;
187
+ declare const CascaderHighlightContext: React.Context<CascaderHighlightStore>;
188
+ /** Subscribes to the highlight. Re-renders ONLY the calling component. */
189
+ export declare function useCascaderHighlight(): CascaderHighlight;
190
+ export { CascaderActionsContext, CascaderHighlightContext, CascaderRenderContext, CascaderStateContext };
@@ -0,0 +1,83 @@
1
+ import { Popover as PopoverPrimitive } from '@base-ui/react';
2
+ import * as React from 'react';
3
+ export type CascaderFooterProps = React.ComponentProps<'div'>;
4
+ /**
5
+ * Actions pinned below the list, a SIBLING of `CascaderList`. Children win
6
+ * over the root's `actions` prop; with neither it renders nothing.
7
+ */
8
+ declare function CascaderFooter({ className, children, onKeyDown, ...props }: CascaderFooterProps): React.JSX.Element | null;
9
+ export interface CascaderActionProps extends Omit<React.ComponentProps<'button'>, 'onSelect'> {
10
+ icon?: React.ReactNode;
11
+ /** Fires on press, after `onClick`, and not at all when disabled. */
12
+ onSelect?: () => void;
13
+ }
14
+ /**
15
+ * One footer command, shaped like a row and deliberately NOT one. A real
16
+ * `<button>`, never a `Combobox.Item`: an item would join the arrow-key ring,
17
+ * appear in `filteredItems`, and vanish the moment a query matched nothing -
18
+ * exactly when "Create new attribute" is most useful. Disabled is
19
+ * `aria-disabled`, never the native attribute: that one is not a tab stop and
20
+ * the panel's Tab order reads `button:not([disabled])`, so a footer whose ONLY
21
+ * row is a disabled command had no stop after the search field (measured on
22
+ * `c-cascader-8`). Staying focusable costs the guards below, plus the
23
+ * greyed-out look: `CASCADER_ACTION_CLASS` keys that off `aria-disabled`
24
+ * instead of `:disabled`.
25
+ */
26
+ declare function CascaderAction({ className, icon, children, onSelect, onClick, onKeyDown, disabled, ...props }: CascaderActionProps): React.JSX.Element;
27
+ interface CascaderSubmenuContextValue {
28
+ rowRef: React.MutableRefObject<HTMLButtonElement | null>;
29
+ open: boolean;
30
+ setOpen: (open: boolean) => void;
31
+ close: () => void;
32
+ /** Names the flyout: a menu is labelled by the control that opens it. */
33
+ triggerId: string;
34
+ /**
35
+ * Whether the pending open came from the KEYBOARD. Base UI's own `openType`
36
+ * calls the opening arrow a POINTER open, because the trigger intercepts it
37
+ * and calls `setOpen` (measured: focus landed on the popup, not the first
38
+ * entry). A ref, so reading it in the focus phase cannot render.
39
+ */
40
+ keyboardRef: React.MutableRefObject<boolean>;
41
+ }
42
+ /** The flyout's own state. `close()` is the one a custom entry usually wants. */
43
+ export declare function useCascaderSubmenu(): CascaderSubmenuContextValue;
44
+ export interface CascaderSubmenuProps {
45
+ open?: boolean;
46
+ defaultOpen?: boolean;
47
+ onOpenChange?: (open: boolean) => void;
48
+ children?: React.ReactNode;
49
+ }
50
+ /**
51
+ * A footer row plus the flyout it opens. Registers with the cascader root
52
+ * while open, which turns one Escape into two. Cleared in an EFFECT, so the
53
+ * flyout still reads as open during the event that closed it.
54
+ */
55
+ declare function CascaderSubmenu({ open: openProp, defaultOpen, onOpenChange, children }: CascaderSubmenuProps): React.JSX.Element;
56
+ export interface CascaderSubmenuTriggerProps extends Omit<React.ComponentProps<'button'>, 'onSelect'> {
57
+ icon?: React.ReactNode;
58
+ }
59
+ /**
60
+ * The footer row that opens the flyout, and its anchor. `aria-haspopup="menu"`
61
+ * rather than the `dialog` Base UI would announce: what opens is a list of
62
+ * commands with roving focus. `disabled` is intercepted, not forwarded:
63
+ * `Popover.Trigger` runs it through `useButton`, which writes the NATIVE
64
+ * attribute for a native `<button>` and takes the row out of the panel's Tab
65
+ * ring, the defect `CascaderAction` documents, and this component exposes no
66
+ * `focusableWhenDisabled` to opt out. Published as `aria-disabled`, with the
67
+ * arrow keys, Enter and Space, and the click closed by hand (`useClick`
68
+ * ignores `defaultPrevented`).
69
+ */
70
+ declare function CascaderSubmenuTrigger({ className, icon, children, onKeyDown, onClick, disabled, ...props }: CascaderSubmenuTriggerProps): React.JSX.Element;
71
+ export interface CascaderSubmenuContentProps extends PopoverPrimitive.Popup.Props, Pick<PopoverPrimitive.Positioner.Props, 'side' | 'align' | 'sideOffset' | 'alignOffset'> {
72
+ }
73
+ /**
74
+ * The flyout itself. `Portal` with NO `container`: a nested portal resolves to
75
+ * the parent portal node, the mechanism the file header describes.
76
+ */
77
+ declare function CascaderSubmenuContent({ className, children, onKeyDown, side, align, sideOffset, alignOffset, ...props }: CascaderSubmenuContentProps): React.JSX.Element;
78
+ /**
79
+ * Whether the footer would render anything, for a wrapper (a separator, a grid
80
+ * row) that has to make the same call `CascaderFooter` already makes.
81
+ */
82
+ export declare function useCascaderHasActions(): boolean;
83
+ export { CascaderAction, CascaderFooter, CascaderSubmenu, CascaderSubmenuContent, CascaderSubmenuTrigger };
@@ -0,0 +1,13 @@
1
+ import { CascaderLabels } from '../../../components/reui/cascader/cascader-types';
2
+ /**
3
+ * English defaults. Every string the primitive can render lives here, so a
4
+ * consumer can translate the whole surface by passing `labels`.
5
+ */
6
+ export declare const CASCADER_LABELS: CascaderLabels;
7
+ /**
8
+ * Shallow-merges consumer overrides over the defaults, so `labels` can carry a
9
+ * single key without restating the rest.
10
+ */
11
+ export declare function resolveCascaderLabels(labels?: Partial<CascaderLabels>): CascaderLabels;
12
+ /** Resolves the search placeholder, which may be a string or a function. */
13
+ export declare function resolveCascaderSearchLabel(labels: CascaderLabels, parentLabel?: string): string;
@@ -0,0 +1,112 @@
1
+ import { Combobox as ComboboxPrimitive } from '@base-ui/react';
2
+ import { CascaderLoadState, CascaderNode } from '../../../components/reui/cascader/cascader-types';
3
+ import * as React from 'react';
4
+ type VetoableEvent = {
5
+ preventBaseUIHandler?: () => void;
6
+ };
7
+ /** A row press. The veto hook is optional: an `as="button"` row is outside Base
8
+ * UI's listbox and gets a plain React event. */
9
+ export type CascaderRowEvent = React.MouseEvent<HTMLElement> & VetoableEvent;
10
+ /** A footer COMMAND: the row theme, so it cannot drift from the list, plus the
11
+ * hover and `focus-visible` fills a plain button never gets. Fills, never a
12
+ * ring: a ring under rows that all paint with a fill read as a different
13
+ * widget bolted on. `focus-visible`, not `focus`, so a pointer press does not
14
+ * leave a command painted as if it were still hovered. Disabled keys off
15
+ * `aria-disabled`, not `:disabled`, to keep it FOCUSABLE: a footer whose only
16
+ * row is disabled has no tab stop. */
17
+ export declare const CASCADER_ACTION_CLASS = "data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground [&_svg:not([class*='size-'])]:size-4 rounded-md py-1 relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [--cascader-row-inset:6px] text-sm ps-[var(--cascader-row-inset,8px)]! pe-[var(--cascader-row-inset,8px)]! hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground cursor-pointer gap-2 transition-colors aria-disabled:pointer-events-none aria-disabled:opacity-50";
18
+ export interface CascaderItemProps extends Omit<ComboboxPrimitive.Item.Props, 'value' | 'children' | 'className' | 'style' | 'onClick' | 'onMouseUp'> {
19
+ /** No state callback: the row merges `className` and `style` itself. */
20
+ className?: string;
21
+ style?: React.CSSProperties;
22
+ onClick?: (event: CascaderRowEvent) => void;
23
+ onMouseUp?: (event: CascaderRowEvent) => void;
24
+ node: CascaderNode;
25
+ /** Explicit render index. Required while virtualized and DELIBERATELY IGNORED
26
+ * otherwise: it self-registers the row and breaks `aria-activedescendant`. */
27
+ index?: number;
28
+ /** Indentation depth. Drives `--cascader-indent` in tree mode. */
29
+ depth?: number;
30
+ /** Renders the ancestor chain under the label, for deep-search results. */
31
+ showPath?: boolean;
32
+ /** `option` renders a real `Combobox.Item`; `button` renders identical markup
33
+ * outside the listbox, for the ancestor columns in columns mode. */
34
+ as?: 'option' | 'button';
35
+ /** Tree mode: this row is an expanded branch. */
36
+ expanded?: boolean;
37
+ /** Indent each depth by this many pixels. Tree mode only. */
38
+ indent?: number;
39
+ /** Whether the node has children. Defaults to the cascader's own answer, as do
40
+ * the three below: callers pass them because they already hold the level,
41
+ * which keeps a memoised row off the volatile context. */
42
+ branch?: boolean;
43
+ /** Whether the node may be committed. Defaults to the cascader's own answer. */
44
+ selectable?: boolean;
45
+ /** Whether the node is currently selected. Defaults to the cascader's answer. */
46
+ selected?: boolean;
47
+ /** Some but not all of the loaded subtree selected. `cascade` only. */
48
+ indeterminate?: boolean;
49
+ /** Selected nodes below this one, at any depth. Drives the trailing count. */
50
+ selectedCount?: number;
51
+ /** Paging row only: a request for this level is in flight. */
52
+ loading?: boolean;
53
+ /** Paging row only: this level's last request failed. */
54
+ error?: boolean;
55
+ /** Branch row only: this node's OWN children are being fetched. The chevron
56
+ * (or, in tree mode, the expander) becomes a spinner IN PLACE, in the same
57
+ * 16px box: no skeleton row appears below, nothing reflows. */
58
+ childrenLoading?: boolean;
59
+ /** Branch row only: the last fetch of this node's children failed. The same
60
+ * box becomes the retry. The press path is unchanged: it goes through
61
+ * `navigate`, which refires the failed level rather than moving. */
62
+ childrenError?: boolean;
63
+ children?: React.ReactNode;
64
+ }
65
+ /** One row. Branch presses navigate instead of selecting, vetoed here with
66
+ * `preventBaseUIHandler()` on `onClick` (Enter too) and on `onMouseUp`. */
67
+ declare const CascaderItem: React.NamedExoticComponent<CascaderItemProps>;
68
+ /** Every load-derived prop a row needs. A PAGING row reports the level it
69
+ * belongs to, a BRANCH row the level it OWNS; mutually exclusive, so one call
70
+ * answers both. Exported because a memoised row cannot read them itself. */
71
+ export declare function getCascaderMoreProps(node: CascaderNode, loadStates: ReadonlyMap<string, CascaderLoadState>): {
72
+ loading: boolean;
73
+ error: boolean;
74
+ childrenLoading: boolean;
75
+ childrenError: boolean;
76
+ };
77
+ export interface CascaderItemsProps {
78
+ /** Replaces the default row for every item. */
79
+ children?: (node: CascaderNode, index: number) => React.ReactNode;
80
+ }
81
+ /** Whatever the active view is: the current level, the deepest column, or the
82
+ * flattened tree rows. Lives here so `cascader.tsx` never imports a row
83
+ * component, which would cycle with the context. `index` is deliberately not
84
+ * passed; see `CascaderItemProps`. */
85
+ declare function CascaderItems({ children }: CascaderItemsProps): React.JSX.Element;
86
+ export interface CascaderGroupProps extends Omit<ComboboxPrimitive.Group.Props, 'className'> {
87
+ /** Base UI also accepts a state callback here; the group has no state. */
88
+ className?: string;
89
+ }
90
+ /** A run of related rows, named by the `CascaderLabel` inside it: the group is
91
+ * `role="group"` and Base UI points its `aria-labelledby` at the label, where a
92
+ * heading alone inside a listbox is dropped from the accessibility tree.
93
+ * Grouping is composition, not data: `CascaderItems` renders one flat run per
94
+ * level, so its indices line up with `listRef`. */
95
+ declare function CascaderGroup({ className, ...props }: CascaderGroupProps): React.JSX.Element;
96
+ export interface CascaderLabelProps extends Omit<ComboboxPrimitive.GroupLabel.Props, 'className' | 'style'> {
97
+ /** No state callback; outside a group this is not a Base UI part at all. */
98
+ className?: string;
99
+ style?: React.CSSProperties;
100
+ }
101
+ /** A heading. Inside `CascaderGroup` it is the group's accessible name; outside
102
+ * one (the footer flyout is a menu, not a listbox) it is decoration only. */
103
+ declare function CascaderLabel(props: CascaderLabelProps): React.JSX.Element;
104
+ export interface CascaderSeparatorProps extends Omit<ComboboxPrimitive.Separator.Props, 'className'> {
105
+ /** Base UI also accepts a state callback here; only the orientation varies. */
106
+ className?: string;
107
+ }
108
+ /** Decorative on purpose: Base UI's separator is `role="separator"`, which a
109
+ * `listbox` may not own, and a run that needs separating for a screen reader
110
+ * needs a `CascaderGroup`, not a line. A consumer can pass the role back. */
111
+ declare function CascaderSeparator({ className, ...props }: CascaderSeparatorProps): React.JSX.Element;
112
+ export { CascaderGroup, CascaderItem, CascaderItems, CascaderLabel, CascaderSeparator };