@deftui/react 0.3.0 → 0.3.1

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 (29) hide show
  1. package/dist/index.d.mts +4 -3
  2. package/dist/index.mjs +2 -3
  3. package/dist/vendor/README.md +2 -1
  4. package/dist/vendor/reui.io/cascader/cascader-async.d.mts +20 -0
  5. package/dist/vendor/reui.io/cascader/cascader-async.mjs +578 -0
  6. package/dist/vendor/reui.io/cascader/cascader-context.d.mts +12 -0
  7. package/dist/vendor/reui.io/cascader/cascader-context.mjs +56 -0
  8. package/dist/vendor/reui.io/cascader/cascader-footer.d.mts +60 -0
  9. package/dist/vendor/reui.io/cascader/cascader-footer.mjs +445 -0
  10. package/dist/vendor/reui.io/cascader/cascader-i18n.mjs +64 -0
  11. package/dist/vendor/reui.io/cascader/cascader-item.d.mts +92 -0
  12. package/dist/vendor/reui.io/cascader/cascader-item.mjs +533 -0
  13. package/dist/vendor/reui.io/cascader/cascader-lib.d.mts +3 -0
  14. package/dist/vendor/reui.io/cascader/cascader-lib.mjs +688 -0
  15. package/dist/vendor/reui.io/cascader/cascader-nav.d.mts +56 -0
  16. package/dist/vendor/reui.io/cascader/cascader-nav.mjs +310 -0
  17. package/dist/vendor/reui.io/cascader/cascader-types.d.mts +140 -0
  18. package/dist/vendor/reui.io/cascader/cascader-virtual.d.mts +18 -0
  19. package/dist/vendor/reui.io/cascader/cascader-virtual.mjs +159 -0
  20. package/dist/vendor/reui.io/cascader/cascader.d.mts +299 -0
  21. package/dist/vendor/reui.io/cascader/cascader.mjs +1470 -0
  22. package/dist/vendor/reui.io/timeline.d.mts +21 -0
  23. package/dist/vendor/reui.io/timeline.mjs +127 -0
  24. package/dist/widgets/export.d.mts +28 -0
  25. package/dist/widgets/index.d.mts +55 -11
  26. package/dist/widgets/index.mjs +45 -2
  27. package/package.json +2 -2
  28. package/dist/vendor/tailored/file-tree.d.mts +0 -21
  29. package/dist/vendor/tailored/file-tree.mjs +0 -146
@@ -0,0 +1,56 @@
1
+ import { CascaderCollapse, CascaderNode, CascaderValueDisplay } from "./cascader-types.mjs";
2
+ import * as React$1 from "react";
3
+ import { useRender } from "@base-ui/react/use-render";
4
+ import { Combobox } from "@base-ui/react";
5
+ export type CascaderNavProps = useRender.ComponentProps<"div">;
6
+ /**
7
+ * Header: back control, search input, and the separator under them. The
8
+ * breadcrumb belongs with the list below: it describes the rows, not the field.
9
+ */
10
+ declare function CascaderNav({ className, ...props }: CascaderNavProps): React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>>;
11
+ export interface CascaderBackProps extends Omit<useRender.ComponentProps<"button">, "children"> {
12
+ children?: React$1.ReactNode;
13
+ }
14
+ /** Pops one level. Renders nothing at the root, so no dead header space. */
15
+ declare function CascaderBack({ className, children, ...props }: CascaderBackProps): React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>> | null;
16
+ export interface CascaderBreadcrumbProps extends Omit<useRender.ComponentProps<"nav">, "children"> {
17
+ /** Maximum visible node segments before the middle collapses. */
18
+ maxSegments?: number;
19
+ collapse?: CascaderCollapse;
20
+ /** Clicking a segment navigates back to that level. Defaults to true. */
21
+ interactive?: boolean;
22
+ }
23
+ /**
24
+ * Compact trail of the current level AND its ancestors, collapsed by the same
25
+ * helper as `CascaderValue` so panel and trigger cannot disagree. The current
26
+ * level IS the last crumb: dropping it read as a rendering bug, a trail naming
27
+ * only the places you are not.
28
+ */
29
+ declare function CascaderBreadcrumb({ className, maxSegments, collapse, interactive, ...props }: CascaderBreadcrumbProps): React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>> | null;
30
+ export interface CascaderInputProps extends Combobox.Input.Props {
31
+ /** Renders the back control inline, before the field. Defaults to true. */
32
+ showBack?: boolean;
33
+ }
34
+ /**
35
+ * Search field. MUST render inside the positioner: only there does Base UI skip
36
+ * the refill from the committed selection that would fight every level swap.
37
+ */
38
+ declare function CascaderInput({ className, showBack, placeholder, onKeyDown, "aria-describedby": ariaDescribedBy, ...props }: CascaderInputProps): React$1.JSX.Element;
39
+ export interface CascaderValueProps extends Omit<useRender.ComponentProps<"span">, "children"> {
40
+ /** `path` shows the full trail, `leaf` only the node, `count` only a total. */
41
+ display?: CascaderValueDisplay;
42
+ maxSegments?: number;
43
+ collapse?: CascaderCollapse;
44
+ separator?: React$1.ReactNode;
45
+ /** Renders the selected node's icon before the trail. */
46
+ showIcon?: boolean;
47
+ placeholder?: React$1.ReactNode;
48
+ /** Replaces the whole rendering. Receives the resolved selection and path. */
49
+ children?: (selected: CascaderNode[], path: CascaderNode[]) => React$1.ReactNode;
50
+ }
51
+ /**
52
+ * Trigger display. Defaults to the collapsed selection path rather than a bare
53
+ * leaf label, which in a nested picker is frequently ambiguous.
54
+ */
55
+ declare function CascaderValue({ className, display, maxSegments, collapse, separator, showIcon, placeholder, children, ...props }: CascaderValueProps): React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>>;
56
+ export { CascaderBack, CascaderBreadcrumb, CascaderInput, CascaderNav, CascaderValue };
@@ -0,0 +1,310 @@
1
+ "use client";
2
+ import { useCascaderActions, useCascaderState } from "./cascader-context.mjs";
3
+ import { resolveCascaderSearchLabel } from "./cascader-i18n.mjs";
4
+ import { collapseCascaderPath, getCascaderFooterStops, getCascaderPath, isCascaderRtl } from "./cascader-lib.mjs";
5
+ import { cn } from "cn";
6
+ import { mergeProps } from "@base-ui/react/merge-props";
7
+ import * as React$1 from "react";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ import { useRender } from "@base-ui/react/use-render";
10
+ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
11
+ import { Combobox } from "@base-ui/react";
12
+ import { useDirection } from "@base-ui/react/direction-provider";
13
+ // src/vendor/reui.io/cascader/cascader-nav.tsx
14
+ function PathChevron() {
15
+ return /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-3 shrink-0 opacity-50 rtl:-scale-x-100" });
16
+ }
17
+ /**
18
+ * Header: back control, search input, and the separator under them. The
19
+ * breadcrumb belongs with the list below: it describes the rows, not the field.
20
+ */
21
+ function CascaderNav({ className, ...props }) {
22
+ const defaultProps = {
23
+ "data-slot": "cascader-nav",
24
+ className: cn("border-border/60 flex shrink-0 flex-col gap-1 border-b px-1.5 py-1", className)
25
+ };
26
+ return useRender({
27
+ defaultTagName: "div",
28
+ render: props.render,
29
+ props: mergeProps(defaultProps, props)
30
+ });
31
+ }
32
+ /** Pops one level. Renders nothing at the root, so no dead header space. */
33
+ function CascaderBack({ className, children, ...props }) {
34
+ const { popLevel, labels, mode } = useCascaderActions();
35
+ const { path } = useCascaderState();
36
+ const hidden = mode !== "drill" || path.length === 0;
37
+ const defaultProps = {
38
+ "data-slot": "cascader-back",
39
+ type: "button",
40
+ "aria-label": labels.back,
41
+ onClick: () => popLevel(),
42
+ className: cn("text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring/50 flex shrink-0 items-center justify-center rounded-md outline-hidden transition-colors focus-visible:ring-2", "size-6", className),
43
+ children: children ?? /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "size-4 rtl:-scale-x-100" })
44
+ };
45
+ const element = useRender({
46
+ defaultTagName: "button",
47
+ render: props.render,
48
+ props: mergeProps(defaultProps, props)
49
+ });
50
+ return hidden ? null : element;
51
+ }
52
+ /**
53
+ * Compact trail of the current level AND its ancestors, collapsed by the same
54
+ * helper as `CascaderValue` so panel and trigger cannot disagree. The current
55
+ * level IS the last crumb: dropping it read as a rendering bug, a trail naming
56
+ * only the places you are not.
57
+ */
58
+ function CascaderBreadcrumb({ className, maxSegments = 3, collapse = "middle", interactive = true, ...props }) {
59
+ const { goToDepth, mode, labels } = useCascaderActions();
60
+ const { path, index } = useCascaderState();
61
+ const nodes = path.map((value) => index.byValue.get(value)).filter(Boolean);
62
+ const segments = collapseCascaderPath(nodes, {
63
+ maxSegments,
64
+ collapse
65
+ });
66
+ const currentValue = nodes.length ? nodes[nodes.length - 1].value : null;
67
+ const hidden = mode !== "drill" || nodes.length === 0;
68
+ const goTo = (node) => {
69
+ const depth = path.indexOf(node.value);
70
+ if (depth < 0) return;
71
+ goToDepth(depth + 1);
72
+ };
73
+ const defaultProps = {
74
+ "data-slot": "cascader-breadcrumb",
75
+ "aria-label": labels.breadcrumbLabel,
76
+ className: cn("text-muted-foreground flex min-w-0 shrink-0 items-center gap-0.5 pt-1.5 pb-0.5 text-xs", "px-3", className),
77
+ children: segments.map((segment, i) => /* @__PURE__ */ jsxs(React$1.Fragment, { children: [i > 0 ? /* @__PURE__ */ jsx(PathChevron, {}) : null, segment.type === "ellipsis" ? /* @__PURE__ */ jsx("span", {
78
+ "data-slot": "cascader-breadcrumb-ellipsis",
79
+ title: segment.hidden.map((n) => n.label).join(` ${labels.pathSeparator} `),
80
+ className: "shrink-0",
81
+ children: "…"
82
+ }) : interactive && segment.node.value !== currentValue ? /* @__PURE__ */ jsx("button", {
83
+ type: "button",
84
+ "data-slot": "cascader-breadcrumb-item",
85
+ onClick: () => goTo(segment.node),
86
+ className: "hover:text-foreground focus-visible:ring-ring/50 max-w-32 truncate rounded-sm outline-hidden transition-colors focus-visible:ring-2",
87
+ children: segment.node.label
88
+ }) : /* @__PURE__ */ jsx("span", {
89
+ "data-slot": "cascader-breadcrumb-item",
90
+ ...segment.node.value === currentValue ? { "aria-current": "page" } : null,
91
+ className: cn("max-w-32 truncate", segment.node.value === currentValue && "text-foreground font-medium"),
92
+ children: segment.node.label
93
+ })] }, segment.type === "node" ? segment.node.value : `gap-${i}`))
94
+ };
95
+ const element = useRender({
96
+ defaultTagName: "nav",
97
+ render: props.render,
98
+ props: mergeProps(defaultProps, props)
99
+ });
100
+ return hidden ? null : element;
101
+ }
102
+ /**
103
+ * Search field. MUST render inside the positioner: only there does Base UI skip
104
+ * the refill from the committed selection that would fight every level swap.
105
+ */
106
+ function CascaderInput({ className, showBack = true, placeholder, onKeyDown, "aria-describedby": ariaDescribedBy, ...props }) {
107
+ const { labels, popLevel, mode, getHighlighted, isBranch, isDisabled, navigate, index, toggleExpanded, inline, invalid, baseId, searchScope } = useCascaderActions();
108
+ const direction = useDirection();
109
+ const { currentParent, query, path, expanded, renderedItems, treeRows } = useCascaderState();
110
+ const resolvedPlaceholder = placeholder ?? resolveCascaderSearchLabel(labels, searchScope === "global" ? void 0 : currentParent?.label);
111
+ const backVisible = showBack && mode === "drill" && path.length > 0;
112
+ const hintId = `${baseId}-hint`;
113
+ const listId = `${baseId}-column-${mode === "columns" ? path.length : 0}`;
114
+ const hintRef = React$1.useRef(null);
115
+ const [hintDir, setHintDir] = React$1.useState("ltr");
116
+ React$1.useLayoutEffect(() => {
117
+ const hint = hintRef.current;
118
+ if (!hint) return;
119
+ setHintDir(isCascaderRtl(hint, direction) ? "rtl" : "ltr");
120
+ }, [direction]);
121
+ /**
122
+ * Moves the highlight to `targetIndex` in `treeRows`. Base UI has no
123
+ * imperative setter (`actionsRef` is `{ unmount }`), so the move is arrow
124
+ * presses, sound only because `useListNavigation` fires `onItemHighlighted`
125
+ * SYNCHRONOUSLY per keydown. A press that fails to close the gap ends it.
126
+ */
127
+ const moveHighlightTo = (field, targetIndex) => {
128
+ const rowIndex = () => {
129
+ const highlighted = getHighlighted();
130
+ if (!highlighted) return -1;
131
+ return treeRows.findIndex((row) => row.node.value === highlighted.value);
132
+ };
133
+ let current = rowIndex();
134
+ for (let step = 0; step < treeRows.length; step += 1) {
135
+ if (current === -1 || current === targetIndex) return;
136
+ const key = current > targetIndex ? "ArrowUp" : "ArrowDown";
137
+ field.dispatchEvent(new KeyboardEvent("keydown", {
138
+ key,
139
+ bubbles: true,
140
+ cancelable: true
141
+ }));
142
+ const next = rowIndex();
143
+ if (next === -1 || !(key === "ArrowUp" ? next < current && next >= targetIndex : next > current && next <= targetIndex)) return;
144
+ current = next;
145
+ }
146
+ };
147
+ /**
148
+ * The tree pattern's two level keys; Base UI leaves both alone (list
149
+ * navigation is vertical-only, the chip handler no-ops without
150
+ * `Combobox.Chips`). Passed in rather than hardcoded because RTL swaps them:
151
+ * APG defines them as "toward the children", not as a physical arrow.
152
+ */
153
+ const handleTreeKeyDown = (event, field, caretAtStart, caretAtEnd, forwardKey, backKey) => {
154
+ const forward = event.key === forwardKey;
155
+ if (!forward && event.key !== backKey) return;
156
+ if (forward ? !caretAtEnd : !caretAtStart) return;
157
+ const highlighted = getHighlighted();
158
+ if (!highlighted) return;
159
+ const rowIndex = treeRows.findIndex((row) => row.node.value === highlighted.value);
160
+ if (rowIndex < 0) return;
161
+ const row = treeRows[rowIndex];
162
+ if (forward) {
163
+ if (!row.branch) return;
164
+ event.preventDefault();
165
+ if (!row.expanded) {
166
+ navigate(row.node);
167
+ return;
168
+ }
169
+ const child = treeRows[rowIndex + 1];
170
+ if (child && child.depth === row.depth + 1) moveHighlightTo(field, rowIndex + 1);
171
+ return;
172
+ }
173
+ const heldByQuery = isDisabled(row.node) && !expanded.has(row.node.value);
174
+ if (row.branch && row.expanded && !heldByQuery) {
175
+ event.preventDefault();
176
+ toggleExpanded(row.node.value);
177
+ return;
178
+ }
179
+ const parentValue = index.parentOf.get(row.node.value);
180
+ if (!parentValue) return;
181
+ const parentIndex = treeRows.findIndex((entry) => entry.node.value === parentValue);
182
+ if (parentIndex < 0) return;
183
+ event.preventDefault();
184
+ moveHighlightTo(field, parentIndex);
185
+ };
186
+ const handleKeyDown = (event) => {
187
+ onKeyDown?.(event);
188
+ if (event.defaultPrevented) return;
189
+ const field = event.currentTarget;
190
+ const caretAtStart = field.selectionStart === 0 && field.selectionEnd === 0;
191
+ const caretAtEnd = field.selectionStart === query.length && field.selectionEnd === query.length;
192
+ if (event.key === "ArrowDown" && !event.altKey) {
193
+ const rowCount = mode === "tree" ? treeRows.length : renderedItems.length;
194
+ const lastValue = mode === "tree" ? treeRows[rowCount - 1]?.node.value : renderedItems[rowCount - 1]?.value;
195
+ const highlighted = getHighlighted();
196
+ if (rowCount === 0 || highlighted != null && highlighted.value === lastValue) {
197
+ const panel = field.closest("[data-slot=\"cascader-panel\"]");
198
+ const stop = panel ? getCascaderFooterStops(panel)[0] : void 0;
199
+ if (stop) {
200
+ event.preventDefault();
201
+ stop.focus();
202
+ return;
203
+ }
204
+ }
205
+ }
206
+ const rtl = isCascaderRtl(field, direction);
207
+ const forwardKey = rtl ? "ArrowLeft" : "ArrowRight";
208
+ const backKey = rtl ? "ArrowRight" : "ArrowLeft";
209
+ if (mode === "tree") {
210
+ handleTreeKeyDown(event, field, caretAtStart, caretAtEnd, forwardKey, backKey);
211
+ return;
212
+ }
213
+ if (event.key === forwardKey && caretAtEnd) {
214
+ const highlighted = getHighlighted();
215
+ if (highlighted && isBranch(highlighted)) {
216
+ event.preventDefault();
217
+ navigate(highlighted);
218
+ return;
219
+ }
220
+ }
221
+ if (event.key === backKey && caretAtStart || event.key === "Backspace" && query.length === 0) {
222
+ if (path.length > 0) {
223
+ event.preventDefault();
224
+ popLevel();
225
+ }
226
+ }
227
+ };
228
+ return /* @__PURE__ */ jsxs("div", {
229
+ "data-slot": "cascader-input-row",
230
+ className: cn("flex shrink-0 items-center gap-1", "h-8"),
231
+ children: [
232
+ showBack ? /* @__PURE__ */ jsx(CascaderBack, {}) : null,
233
+ /* @__PURE__ */ jsx(Combobox.Input, {
234
+ "data-slot": "cascader-input",
235
+ placeholder: resolvedPlaceholder,
236
+ onKeyDown: handleKeyDown,
237
+ "aria-describedby": [ariaDescribedBy, hintId].filter(Boolean).join(" "),
238
+ ...mode === "tree" ? { "aria-haspopup": "tree" } : null,
239
+ ...inline ? {
240
+ "aria-expanded": true,
241
+ "aria-controls": listId
242
+ } : null,
243
+ ...invalid ? {
244
+ "aria-invalid": true,
245
+ "data-invalid": ""
246
+ } : null,
247
+ className: cn("placeholder:text-muted-foreground h-full w-full min-w-0 flex-1 bg-transparent px-1.5 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50", backVisible && "ps-0", className),
248
+ ...props
249
+ }),
250
+ /* @__PURE__ */ jsx("span", {
251
+ id: hintId,
252
+ ref: hintRef,
253
+ className: "sr-only",
254
+ children: labels.keyboardHint(mode, hintDir)
255
+ })
256
+ ]
257
+ });
258
+ }
259
+ /**
260
+ * Trigger display. Defaults to the collapsed selection path rather than a bare
261
+ * leaf label, which in a nested picker is frequently ambiguous.
262
+ */
263
+ function CascaderValue({ className, display = "path", maxSegments = 3, collapse = "middle", separator, showIcon = true, placeholder, children, ...props }) {
264
+ const { labels, multiple, resolveNode } = useCascaderActions();
265
+ const { index, selectedValues } = useCascaderState();
266
+ const selected = selectedValues.map(resolveNode);
267
+ const resolvedPath = selectedValues.length === 1 ? getCascaderPath(index, selectedValues[0]) : [];
268
+ const path = resolvedPath.length > 0 ? resolvedPath : selected.length === 1 ? selected : [];
269
+ let content;
270
+ if (children) content = children(selected, path);
271
+ else if (selectedValues.length === 0) content = /* @__PURE__ */ jsx("span", {
272
+ className: "text-muted-foreground truncate",
273
+ children: placeholder
274
+ });
275
+ else if (display === "count" || multiple && selectedValues.length > 1) content = labels.selectedCount(selectedValues.length);
276
+ else {
277
+ const leaf = path[path.length - 1] ?? selected[0];
278
+ const segments = display === "leaf" ? [{
279
+ type: "node",
280
+ node: leaf
281
+ }] : collapseCascaderPath(path, {
282
+ maxSegments,
283
+ collapse
284
+ });
285
+ content = /* @__PURE__ */ jsxs(Fragment, { children: [showIcon && leaf?.icon ? /* @__PURE__ */ jsx("span", {
286
+ "data-slot": "cascader-value-icon",
287
+ className: "text-muted-foreground flex shrink-0 items-center",
288
+ children: leaf.icon
289
+ }) : null, segments.map((segment, i) => /* @__PURE__ */ jsxs(React$1.Fragment, { children: [i > 0 ? separator ?? /* @__PURE__ */ jsx(PathChevron, {}) : null, segment.type === "ellipsis" ? /* @__PURE__ */ jsx("span", {
290
+ "data-slot": "cascader-value-ellipsis",
291
+ title: segment.hidden.map((n) => n.label).join(` ${labels.pathSeparator} `),
292
+ className: "text-muted-foreground shrink-0",
293
+ children: "…"
294
+ }) : /* @__PURE__ */ jsx("span", {
295
+ className: cn("truncate", i < segments.length - 1 && "text-muted-foreground"),
296
+ children: segment.node?.label
297
+ })] }, segment.type === "node" ? segment.node.value : `gap-${i}`))] });
298
+ }
299
+ const defaultProps = {
300
+ "data-slot": "cascader-value",
301
+ className: cn("flex min-w-0 items-center gap-1 truncate", className),
302
+ children: content
303
+ };
304
+ return useRender({
305
+ defaultTagName: "span",
306
+ render: props.render,
307
+ props: mergeProps(defaultProps, props)
308
+ });
309
+ }
310
+ export { CascaderBack, CascaderBreadcrumb, CascaderInput, CascaderNav, CascaderValue };
@@ -0,0 +1,140 @@
1
+ import * as React$1 from "react";
2
+ /**
3
+ * A node in the cascader tree. Two input shapes normalize to one internal
4
+ * index: nested (nodes carry `children`), or flat adjacency plus the root's
5
+ * `getParent`, which skips the re-nesting step for large datasets.
6
+ */
7
+ export interface CascaderNode<T = unknown> {
8
+ /** Stable id. Also the committed selection value. */
9
+ value: string;
10
+ label: string;
11
+ icon?: React$1.ReactNode;
12
+ description?: string;
13
+ children?: CascaderNode<T>[];
14
+ /** Declares a branch before load; async nodes read as leaves without it. */
15
+ hasChildren?: boolean;
16
+ /** Trailing count. Defaults to known children; set it for async nodes. */
17
+ count?: number;
18
+ /** Listed, never selected or opened; on a branch, its whole subtree too. */
19
+ disabled?: boolean;
20
+ keywords?: string[];
21
+ /**
22
+ * Only read on a node nested inside a `getChildren` result. For the level a
23
+ * node OWNS the authoritative signal is that level's `CascaderLoadResult`.
24
+ */
25
+ hasMore?: boolean;
26
+ data?: T;
27
+ }
28
+ /**
29
+ * One footer ACTION. Deliberately not a `CascaderNode`, so a command can never
30
+ * be passed where an option is expected and join the ring, filter or selection.
31
+ */
32
+ export interface CascaderActionItem {
33
+ /** Stable key. Falls back to the label when it is a string, then the index. */
34
+ value?: string;
35
+ label: React$1.ReactNode;
36
+ icon?: React$1.ReactNode;
37
+ disabled?: boolean;
38
+ /** Ignored when `items` is present - a flyout opens instead. */
39
+ onSelect?: () => void;
40
+ /** Turns the row into a submenu trigger. One level deep on purpose. */
41
+ items?: CascaderActionItem[];
42
+ /** Consecutive entries sharing a heading are drawn under one. */
43
+ group?: string;
44
+ }
45
+ /** Panel layout. See the docs for the keyboard map of each. */
46
+ export type CascaderMode = "drill" | "columns" | "tree";
47
+ export type CascaderSearchScope = "level" | "deep" | "global";
48
+ /**
49
+ * Which nodes may be committed. The predicate arm is generic over the payload
50
+ * rather than quantified per call: a per-call `<T>` would force every predicate
51
+ * to accept EVERY payload, so `(node: CascaderNode<Member>) => boolean` could
52
+ * never satisfy it.
53
+ */
54
+ export type CascaderSelectable<T = unknown> = "leaf" | "any" | ((node: CascaderNode<T>) => boolean);
55
+ export type CascaderCollapse = "middle" | "start" | "none";
56
+ export type CascaderValueDisplay = "path" | "leaf" | "count";
57
+ export type CascaderChangeReason = "select" | "deselect" | "clear";
58
+ /** Second argument to `onValueChange`: resolved nodes, so no re-lookup. */
59
+ export interface CascaderChangeDetails<T = unknown> {
60
+ /** The node committed or toggled. Null when the selection was cleared. */
61
+ node: CascaderNode<T> | null;
62
+ /** Ancestor chain of `node`, root first, node last. */
63
+ path: CascaderNode<T>[];
64
+ nodes: CascaderNode<T>[];
65
+ reason: CascaderChangeReason;
66
+ }
67
+ /** Why a level was fetched. `resolve`: `resolveValue` hit an unloaded node. */
68
+ export type CascaderLoadReason = "level" | "more" | "prefetch" | "retry" | "resolve";
69
+ /** Argument handed to `getChildren` and `resolveValue`. */
70
+ export interface CascaderLoadContext {
71
+ /**
72
+ * Aborted when the request is superseded, `loadKey` changes or the cascader
73
+ * unmounts, and for a `getChildren` load also when the popup closes.
74
+ */
75
+ signal: AbortSignal;
76
+ cursor?: string;
77
+ reason?: CascaderLoadReason;
78
+ }
79
+ /** Value returned by `getChildren`. A bare array is also accepted. */
80
+ export interface CascaderLoadResult<T = unknown> {
81
+ items: CascaderNode<T>[];
82
+ nextCursor?: string;
83
+ /** Defaults to whether `nextCursor` was supplied. */
84
+ hasMore?: boolean;
85
+ }
86
+ /** Argument handed to `onSearch`. */
87
+ export interface CascaderSearchContext {
88
+ signal: AbortSignal;
89
+ /** The path the user is searching within, deepest last. Empty under a
90
+ * global search, whose scope is the whole tree. */
91
+ path: string[];
92
+ }
93
+ /**
94
+ * Every user facing string, so the primitive ships no hardcoded copy. The
95
+ * callbacks take plain labels, not nodes: a `CascaderNode<T>` parameter would
96
+ * force this object to carry the item generic. `*Announcement` is live-region.
97
+ */
98
+ export interface CascaderLabels {
99
+ /** `parentLabel` is absent at the root and under a global search. */
100
+ search: string | ((parentLabel?: string) => string);
101
+ back: string;
102
+ /** A level's FIRST page. `loadingMore` is the next, `loadMore` its idle row. */
103
+ loading: string;
104
+ loadingMore: string;
105
+ loadMore: string;
106
+ error: string;
107
+ retry: string;
108
+ empty: string;
109
+ /** Rendered by `CascaderValue` when `display="count"`. */
110
+ selectedCount: (count: number) => string;
111
+ breadcrumbLabel: string;
112
+ chipsLabel: string;
113
+ removeChip: (label: string) => string;
114
+ /** Trail separator. Not every locale writes one with a slash. */
115
+ pathSeparator: string;
116
+ /** Names the root level wherever there is no parent node to name it. */
117
+ rootLevel: string;
118
+ itemCount: (count: number) => string;
119
+ /** Appended to a branch row's name outside tree mode: it opens another list. */
120
+ branchAffordance: string;
121
+ /** Columns-trail rows are plain buttons, so they carry no `aria-selected`. */
122
+ selectedState: string;
123
+ /** Same for the mixed state, which a `role="button"` row may not carry. */
124
+ partiallySelectedState: string;
125
+ columnsLabel: string;
126
+ actionsLabel: string;
127
+ submenuAffordance: string;
128
+ panelLabel: string;
129
+ /** Read per mode AND per direction: the level keys mirror in RTL. */
130
+ keyboardHint: (mode: CascaderMode, dir: "ltr" | "rtl") => string;
131
+ rootAnnouncement: (count: number) => string;
132
+ expandedAnnouncement: (label: string, count: number) => string;
133
+ collapsedAnnouncement: (label: string) => string;
134
+ levelAnnouncement: (parentLabel: string, depth: number, count: number) => string;
135
+ resultsAnnouncement: (count: number) => string;
136
+ maxReachedAnnouncement: (max: number) => string;
137
+ /** `count` is the descendants swept along; `selecting` is the direction. */
138
+ cascadeAnnouncement: (label: string, count: number, selecting: boolean) => string;
139
+ searchingAnnouncement: string;
140
+ }
@@ -0,0 +1,18 @@
1
+ import "./cascader-context.mjs";
2
+ import * as React$1 from "react";
3
+ import "@tanstack/react-virtual";
4
+ export interface CascaderVirtualItemsProps {
5
+ /** Row height before measurement. Defaults to the root `estimateRowSize`. */
6
+ estimateSize?: number;
7
+ /** Rows rendered beyond each edge. Defaults to the root `overscan`. */
8
+ overscan?: number;
9
+ }
10
+ /**
11
+ * Windowed replacement for `CascaderItems`, dropped inside `CascaderList`. The
12
+ * registration is a LAYOUT effect because the root derives `virtualized` from
13
+ * it and the flip has to land before the browser paints. Until the root has
14
+ * seen it, this renders exactly what `CascaderItems` does, so no frame carries
15
+ * indexed rows while Base UI still owns the composite list.
16
+ */
17
+ declare function CascaderVirtualItems(props: CascaderVirtualItemsProps): React$1.JSX.Element;
18
+ export { CascaderVirtualItems };