@deftui/react 0.2.5 → 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 (49) hide show
  1. package/README.md +2 -2
  2. package/dist/index.d.mts +7 -5
  3. package/dist/index.mjs +4 -4
  4. package/dist/styles.css +3 -0
  5. package/dist/vendor/README.md +10 -9
  6. package/dist/vendor/coss/calendar.d.mts +4 -0
  7. package/dist/vendor/coss/calendar.mjs +70 -0
  8. package/dist/vendor/coss/card.d.mts +23 -0
  9. package/dist/vendor/coss/card.mjs +160 -0
  10. package/dist/vendor/coss/dialog.d.mts +1 -1
  11. package/dist/vendor/coss/dialog.mjs +1 -1
  12. package/dist/vendor/coss/table.d.mts +15 -0
  13. package/dist/vendor/coss/table.mjs +73 -0
  14. package/dist/vendor/reui.io/cascader/cascader-async.d.mts +20 -0
  15. package/dist/vendor/reui.io/cascader/cascader-async.mjs +578 -0
  16. package/dist/vendor/reui.io/cascader/cascader-context.d.mts +12 -0
  17. package/dist/vendor/reui.io/cascader/cascader-context.mjs +56 -0
  18. package/dist/vendor/reui.io/cascader/cascader-footer.d.mts +60 -0
  19. package/dist/vendor/reui.io/cascader/cascader-footer.mjs +445 -0
  20. package/dist/vendor/reui.io/cascader/cascader-i18n.mjs +64 -0
  21. package/dist/vendor/reui.io/cascader/cascader-item.d.mts +92 -0
  22. package/dist/vendor/reui.io/cascader/cascader-item.mjs +533 -0
  23. package/dist/vendor/reui.io/cascader/cascader-lib.d.mts +3 -0
  24. package/dist/vendor/reui.io/cascader/cascader-lib.mjs +688 -0
  25. package/dist/vendor/reui.io/cascader/cascader-nav.d.mts +56 -0
  26. package/dist/vendor/reui.io/cascader/cascader-nav.mjs +310 -0
  27. package/dist/vendor/reui.io/cascader/cascader-types.d.mts +140 -0
  28. package/dist/vendor/reui.io/cascader/cascader-virtual.d.mts +18 -0
  29. package/dist/vendor/reui.io/cascader/cascader-virtual.mjs +159 -0
  30. package/dist/vendor/reui.io/cascader/cascader.d.mts +299 -0
  31. package/dist/vendor/reui.io/cascader/cascader.mjs +1470 -0
  32. package/dist/vendor/reui.io/timeline.d.mts +21 -0
  33. package/dist/vendor/reui.io/timeline.mjs +127 -0
  34. package/dist/vendor/tailored/lightbox.d.mts +1 -1
  35. package/dist/vendor/tailored/lightbox.mjs +1 -1
  36. package/dist/widgets/datetime.d.mts +1 -1
  37. package/dist/widgets/datetime.mjs +6 -1
  38. package/dist/widgets/export.d.mts +28 -0
  39. package/dist/widgets/index.d.mts +69 -20
  40. package/dist/widgets/index.mjs +61 -11
  41. package/package.json +2 -2
  42. package/dist/vendor/shadcn/calendar.d.mts +0 -10
  43. package/dist/vendor/shadcn/calendar.mjs +0 -107
  44. package/dist/vendor/shadcn/card.d.mts +0 -11
  45. package/dist/vendor/shadcn/card.mjs +0 -55
  46. package/dist/vendor/shadcn/table.d.mts +0 -9
  47. package/dist/vendor/shadcn/table.mjs +0 -59
  48. package/dist/vendor/tailored/file-tree.d.mts +0 -21
  49. package/dist/vendor/tailored/file-tree.mjs +0 -146
@@ -0,0 +1,533 @@
1
+ "use client";
2
+ import { useCascaderActions, useCascaderRender, useCascaderState } from "./cascader-context.mjs";
3
+ import { getCascaderCount, getCascaderMoreParent, getCascaderPath, isCascaderMoreNode } from "./cascader-lib.mjs";
4
+ import { Spinner } from "../../shadcn/spinner.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 { CheckIcon, ChevronRightIcon, LoaderCircleIcon, MinusIcon, RotateCwIcon } from "lucide-react";
11
+ import { Combobox } from "@base-ui/react";
12
+ // src/vendor/reui.io/cascader/cascader-item.tsx
13
+ const ROW_INSET_CLASS = "[--cascader-row-inset:8px]";
14
+ const ROW_FLUSH_CLASS = "ps-[var(--cascader-row-inset,8px)]! pe-[var(--cascader-row-inset,8px)]!";
15
+ const ROW_GUTTER_CLASS = "ps-[var(--cascader-row-inset,8px)]! pe-[calc(var(--cascader-row-inset,8px)*2_+_16px)]!";
16
+ const INDICATOR_CLASS = "pointer-events-none absolute flex items-center justify-center size-4 end-[var(--cascader-row-inset,8px)]! rtl:right-auto!";
17
+ const ROW_THEME_CLASS = "data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground rounded-sm py-1.5 [&_svg:not([class*='size-'])]:size-4";
18
+ /**
19
+ * The row's gap and type, per style, held apart from the rest of the theme.
20
+ *
21
+ * A row that pins its own gap or text size takes `ROW_SHELL_CLASS` and leaves
22
+ * these out, because a `style-<name>:` utility is emitted AFTER the plain one
23
+ * it would have to lose to. Not receiving the token beats overriding it with
24
+ * `!`, which a consumer then has to fight in turn.
25
+ */
26
+ const ROW_GAP_CLASS = "gap-2";
27
+ const ROW_TEXT_CLASS = "text-sm";
28
+ const ROW_SHELL_CLASS = `${ROW_THEME_CLASS} 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 ${ROW_INSET_CLASS}`;
29
+ const ROW_CLASS = `${ROW_SHELL_CLASS} ${ROW_GAP_CLASS} ${ROW_TEXT_CLASS}`;
30
+ const CASCADER_ACTION_INSET_CLASS = `${ROW_INSET_CLASS} ${ROW_FLUSH_CLASS}`;
31
+ /** A footer COMMAND: the row theme, so it cannot drift from the list, plus the
32
+ * hover and `focus-visible` fills a plain button never gets. Fills, never a
33
+ * ring: a ring under rows that all paint with a fill read as a different
34
+ * widget bolted on. `focus-visible`, not `focus`, so a pointer press does not
35
+ * leave a command painted as if it were still hovered. Disabled keys off
36
+ * `aria-disabled`, not `:disabled`, to keep it FOCUSABLE: a footer whose only
37
+ * row is disabled has no tab stop. */
38
+ const CASCADER_ACTION_CLASS = `${ROW_SHELL_CLASS} ${ROW_TEXT_CLASS} ${ROW_FLUSH_CLASS} 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`;
39
+ const AFFORDANCE_BOX_CLASS = "flex size-5 shrink-0 items-center justify-center rounded-sm";
40
+ const AFFORDANCE_HOVER_CLASS = "transition-[background-color,box-shadow,color] hover:bg-background hover:text-foreground! hover:**:text-foreground! hover:shadow-xs";
41
+ const CHECKBOX_MARK_CLASS = "text-primary-foreground! **:text-primary-foreground!";
42
+ const INDICATOR_MARK_CLASS = "text-foreground! **:text-foreground!";
43
+ const CASCADER_HOVER_EXPAND_DELAY = 150;
44
+ /** One row. Branch presses navigate instead of selecting, vetoed here with
45
+ * `preventBaseUIHandler()` on `onClick` (Enter too) and on `onMouseUp`. */
46
+ const CascaderItem = React$1.memo(function CascaderItem({ node, index, depth, showPath = false, as = "option", expanded, indent = 16, branch: branchProp, selectable: selectableProp, selected: selectedProp, indeterminate: indeterminateProp, selectedCount: selectedCountProp, loading, error, childrenLoading = false, childrenError = false, className, children, onClick, onMouseUp, style, ...props }) {
47
+ const { index: treeIndex, mode, labels, isBranch, isSelectable, isDisabled, isSelected, isIndeterminate, selectedDescendantCount, multiple, branchesSelectable, indicator: indicatorEnabled, expandTrigger = "click", navigate, navigateAt, toggleExpanded, commit, getState, virtualized, loadMore, retryLevel } = useCascaderActions();
48
+ const { renderItem, renderLabel } = useCascaderRender();
49
+ const branch = branchProp ?? isBranch(node);
50
+ const selectable = selectableProp ?? isSelectable(node);
51
+ const disabled = isDisabled(node);
52
+ const selected = selectedProp ?? isSelected(node);
53
+ const indeterminate = !selected && (indeterminateProp ?? isIndeterminate(node));
54
+ const count = branch ? getCascaderCount(treeIndex, node) : 0;
55
+ const nodeDepth = depth ?? treeIndex.depthOf.get(node.value) ?? 0;
56
+ const selectedDescendants = branch ? selectedCountProp ?? selectedDescendantCount(node) : 0;
57
+ const showsSelectedCount = multiple && selectedDescendants > 0;
58
+ const trailingCount = showsSelectedCount ? selectedDescendants : count;
59
+ const showCount = trailingCount > 0;
60
+ const leadingCheckbox = mode === "tree" && multiple;
61
+ const showsIndicator = indicatorEnabled || multiple;
62
+ const veto = React$1.useCallback((event) => {
63
+ event.preventBaseUIHandler?.();
64
+ }, []);
65
+ const handleClick = React$1.useCallback((event) => {
66
+ onClick?.(event);
67
+ if (disabled) return;
68
+ if (branch && !selectable) {
69
+ veto(event);
70
+ navigate(node);
71
+ }
72
+ }, [
73
+ onClick,
74
+ node,
75
+ disabled,
76
+ branch,
77
+ selectable,
78
+ veto,
79
+ navigate
80
+ ]);
81
+ const handleMouseUp = React$1.useCallback((event) => {
82
+ onMouseUp?.(event);
83
+ if (disabled) return;
84
+ if (branch && !selectable) veto(event);
85
+ }, [
86
+ onMouseUp,
87
+ disabled,
88
+ branch,
89
+ selectable,
90
+ veto
91
+ ]);
92
+ const handleExpanderClick = React$1.useCallback((event) => {
93
+ event.stopPropagation();
94
+ veto(event);
95
+ if (expanded) {
96
+ if (!disabled || getState().expanded.has(node.value)) toggleExpanded(node.value);
97
+ } else if (!disabled) navigate(node);
98
+ }, [
99
+ veto,
100
+ node,
101
+ disabled,
102
+ expanded,
103
+ getState,
104
+ toggleExpanded,
105
+ navigate
106
+ ]);
107
+ const handleExpanderMouseUp = React$1.useCallback((event) => {
108
+ event.stopPropagation();
109
+ veto(event);
110
+ }, [veto]);
111
+ const handleChevronClick = React$1.useCallback((event) => {
112
+ event.stopPropagation();
113
+ veto(event);
114
+ if (disabled) return;
115
+ if (as === "button") navigateAt(node, depth ?? 0);
116
+ else navigate(node);
117
+ }, [
118
+ veto,
119
+ node,
120
+ disabled,
121
+ as,
122
+ navigateAt,
123
+ depth,
124
+ navigate
125
+ ]);
126
+ const handleChevronMouseUp = React$1.useCallback((event) => veto(event), [veto]);
127
+ const handleButtonMouseDown = React$1.useCallback((event) => {
128
+ event.preventDefault();
129
+ }, []);
130
+ const handleButtonClick = React$1.useCallback((event) => {
131
+ onClick?.(event);
132
+ if (disabled) return;
133
+ if (branch && !selectable) {
134
+ navigateAt(node, depth ?? 0);
135
+ return;
136
+ }
137
+ if (selectable) commit(node);
138
+ }, [
139
+ onClick,
140
+ node,
141
+ disabled,
142
+ branch,
143
+ selectable,
144
+ navigateAt,
145
+ depth,
146
+ commit
147
+ ]);
148
+ const handleMoreClick = React$1.useCallback((event) => {
149
+ onClick?.(event);
150
+ veto(event);
151
+ const parent = getCascaderMoreParent(node);
152
+ if (parent == null) return;
153
+ if (error) retryLevel(parent);
154
+ else if (!loading) loadMore(parent);
155
+ }, [
156
+ onClick,
157
+ veto,
158
+ node,
159
+ error,
160
+ loading,
161
+ retryLevel,
162
+ loadMore
163
+ ]);
164
+ const hoverNavigates = expandTrigger === "hover" && mode === "columns" && as === "option" && branch && !disabled;
165
+ const hoverTimerRef = React$1.useRef(null);
166
+ const cancelHoverNavigate = React$1.useCallback(() => {
167
+ if (hoverTimerRef.current == null) return;
168
+ window.clearTimeout(hoverTimerRef.current);
169
+ hoverTimerRef.current = null;
170
+ }, []);
171
+ const handleHoverPointerEnter = React$1.useCallback(() => {
172
+ cancelHoverNavigate();
173
+ hoverTimerRef.current = window.setTimeout(() => {
174
+ hoverTimerRef.current = null;
175
+ navigate(node);
176
+ }, CASCADER_HOVER_EXPAND_DELAY);
177
+ }, [
178
+ cancelHoverNavigate,
179
+ navigate,
180
+ node
181
+ ]);
182
+ React$1.useEffect(() => {
183
+ if (!hoverNavigates) cancelHoverNavigate();
184
+ }, [hoverNavigates, cancelHoverNavigate]);
185
+ React$1.useEffect(() => cancelHoverNavigate, [cancelHoverNavigate]);
186
+ if (isCascaderMoreNode(node)) {
187
+ const text = error ? labels.error : loading ? node.count ? labels.loadingMore : labels.loading : labels.loadMore;
188
+ const moreStyle = mode === "tree" && nodeDepth > 0 ? {
189
+ ...style,
190
+ "--cascader-indent": `${nodeDepth * indent}px`
191
+ } : style;
192
+ const moreShared = {
193
+ "data-slot": "cascader-item",
194
+ "data-more": "",
195
+ "data-state": error ? "error" : loading ? "loading" : "idle",
196
+ "data-depth": nodeDepth,
197
+ style: moreStyle,
198
+ className: cn(ROW_SHELL_CLASS, ROW_FLUSH_CLASS, "text-muted-foreground justify-center gap-1.5 text-xs", !error && !loading && "hover:text-foreground", mode === "tree" && "ps-[calc(var(--cascader-row-inset,8px)_+_var(--cascader-indent,0px))]!", className)
199
+ };
200
+ const moreBody = children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
201
+ loading ? /* @__PURE__ */ jsx(LoaderCircleIcon, {
202
+ className: "size-3.5 animate-spin",
203
+ "aria-hidden": true
204
+ }) : null,
205
+ /* @__PURE__ */ jsx("span", { children: text }),
206
+ error ? /* @__PURE__ */ jsx("span", {
207
+ className: "text-foreground font-medium",
208
+ children: labels.retry
209
+ }) : null
210
+ ] });
211
+ if (as === "button") {
212
+ const buttonProps = { ...props };
213
+ delete buttonProps["aria-setsize"];
214
+ delete buttonProps["aria-posinset"];
215
+ delete buttonProps["aria-level"];
216
+ return /* @__PURE__ */ jsx("button", {
217
+ type: "button",
218
+ ...moreShared,
219
+ tabIndex: -1,
220
+ onClick: handleMoreClick,
221
+ ...buttonProps,
222
+ children: moreBody
223
+ });
224
+ }
225
+ return /* @__PURE__ */ jsx(Combobox.Item, {
226
+ ...moreShared,
227
+ value: node,
228
+ ...virtualized && index != null ? { index } : null,
229
+ ...mode === "tree" ? {
230
+ role: "treeitem",
231
+ "aria-level": nodeDepth + 1
232
+ } : null,
233
+ onClick: handleMoreClick,
234
+ onMouseUp: veto,
235
+ ...props,
236
+ children: moreBody
237
+ });
238
+ }
239
+ const itemState = {
240
+ branch,
241
+ selected,
242
+ disabled,
243
+ depth: nodeDepth,
244
+ count,
245
+ path: showPath ? getCascaderPath(treeIndex, node.value).slice(0, -1) : []
246
+ };
247
+ const custom = renderItem?.(node, itemState);
248
+ const customLabel = renderLabel?.(node, itemState);
249
+ const srDetails = [
250
+ branch ? labels.itemCount(count) : null,
251
+ showsSelectedCount ? labels.selectedCount(selectedDescendants) : null,
252
+ branch && mode !== "tree" ? labels.branchAffordance : null,
253
+ as === "button" && selected ? labels.selectedState : null,
254
+ as === "button" && indeterminate ? labels.partiallySelectedState : null
255
+ ].filter(Boolean).map((detail) => `, ${detail}`).join("");
256
+ const checkbox = /* @__PURE__ */ jsx("span", {
257
+ "data-slot": "cascader-item-checkbox",
258
+ className: "border-input in-data-[selected]:bg-primary in-data-[selected]:border-primary in-data-[selected]:text-primary-foreground in-data-[indeterminate]:bg-primary in-data-[indeterminate]:border-primary in-data-[indeterminate]:text-primary-foreground flex size-4 shrink-0 items-center justify-center rounded-[4px] border transition-colors",
259
+ children: indeterminate ? /* @__PURE__ */ jsx(MinusIcon, {
260
+ "data-slot": "cascader-item-dash",
261
+ className: cn(CHECKBOX_MARK_CLASS, "size-3")
262
+ }) : /* @__PURE__ */ jsx(CheckIcon, {
263
+ "data-slot": "cascader-item-tick",
264
+ className: cn(CHECKBOX_MARK_CLASS, "size-3 opacity-0 in-data-[selected]:opacity-100")
265
+ })
266
+ });
267
+ const indicator = /* @__PURE__ */ jsx(Fragment, { children: multiple ? checkbox : /* @__PURE__ */ jsx(CheckIcon, {
268
+ "data-slot": "cascader-item-check",
269
+ className: cn("pointer-events-none", INDICATOR_MARK_CLASS)
270
+ }) });
271
+ const body = custom ?? children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
272
+ mode === "tree" && branch ? /* @__PURE__ */ jsx("span", {
273
+ "data-slot": "cascader-item-expander",
274
+ "aria-hidden": "true",
275
+ "data-state": childrenError ? "error" : childrenLoading ? "loading" : "idle",
276
+ onClick: handleExpanderClick,
277
+ onMouseUp: handleExpanderMouseUp,
278
+ className: cn("-ms-0.5", childrenError ? "text-destructive" : "text-muted-foreground", AFFORDANCE_BOX_CLASS, AFFORDANCE_HOVER_CLASS, disabled && expanded && "pointer-events-auto", childrenLoading && "pointer-events-none"),
279
+ children: childrenLoading ? /* @__PURE__ */ jsx(Spinner, {
280
+ "aria-hidden": "true",
281
+ className: "size-4"
282
+ }) : childrenError ? /* @__PURE__ */ jsx(RotateCwIcon, { className: "size-4" }) : /* @__PURE__ */ jsx(ChevronRightIcon, { className: cn("size-4 transition-transform", expanded ? "rotate-90" : "rtl:-scale-x-100") })
283
+ }) : mode === "tree" ? /* @__PURE__ */ jsx("span", {
284
+ "data-slot": "cascader-item-expander-spacer",
285
+ "aria-hidden": "true",
286
+ className: cn("-ms-0.5", AFFORDANCE_BOX_CLASS)
287
+ }) : null,
288
+ leadingCheckbox ? selectable ? checkbox : /* @__PURE__ */ jsx("span", {
289
+ "data-slot": "cascader-item-checkbox-spacer",
290
+ "aria-hidden": "true",
291
+ className: "size-4 shrink-0"
292
+ }) : null,
293
+ node.icon ? /* @__PURE__ */ jsx("span", {
294
+ "data-slot": "cascader-item-icon",
295
+ className: "text-muted-foreground flex shrink-0 items-center justify-center",
296
+ children: node.icon
297
+ }) : null,
298
+ /* @__PURE__ */ jsxs("span", {
299
+ className: "flex min-w-0 flex-1 flex-col items-start gap-0.5",
300
+ children: [customLabel ?? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
301
+ className: "w-full truncate text-start",
302
+ children: node.label
303
+ }), node.description ? /* @__PURE__ */ jsx("span", {
304
+ "data-slot": "cascader-item-description",
305
+ className: "text-muted-foreground w-full truncate text-start text-xs",
306
+ children: node.description
307
+ }) : null] }), showPath ? /* @__PURE__ */ jsx(CascaderItemPath, { node }) : null]
308
+ }),
309
+ srDetails ? /* @__PURE__ */ jsx("span", {
310
+ "data-slot": "cascader-item-details",
311
+ className: "sr-only",
312
+ children: srDetails
313
+ }) : null,
314
+ branch && mode !== "tree" ? /* @__PURE__ */ jsxs("span", {
315
+ "data-slot": "cascader-item-trailing",
316
+ "aria-hidden": "true",
317
+ className: "text-muted-foreground ms-auto flex shrink-0 items-center gap-1 text-xs tabular-nums",
318
+ children: [showCount ? /* @__PURE__ */ jsx("span", {
319
+ "data-slot": "cascader-item-count",
320
+ ...showsSelectedCount ? { "data-selected-count": "" } : null,
321
+ className: cn(showsSelectedCount && "text-primary!"),
322
+ children: trailingCount
323
+ }) : null, /* @__PURE__ */ jsx("span", {
324
+ "data-slot": "cascader-item-chevron",
325
+ "data-state": childrenError ? "error" : childrenLoading ? "loading" : "idle",
326
+ onClick: selectable ? handleChevronClick : void 0,
327
+ onMouseUp: selectable ? handleChevronMouseUp : void 0,
328
+ onMouseDown: as === "button" ? handleButtonMouseDown : void 0,
329
+ className: cn("-me-0.5", AFFORDANCE_BOX_CLASS, selectable && AFFORDANCE_HOVER_CLASS, childrenError && "text-destructive", childrenLoading && "pointer-events-none"),
330
+ children: childrenLoading ? /* @__PURE__ */ jsx(Spinner, {
331
+ "aria-hidden": "true",
332
+ className: "size-4"
333
+ }) : childrenError ? /* @__PURE__ */ jsx(RotateCwIcon, { className: "size-4" }) : /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-4 rtl:-scale-x-100" })
334
+ })]
335
+ }) : null,
336
+ branch && mode === "tree" && showCount ? /* @__PURE__ */ jsx("span", {
337
+ "data-slot": "cascader-item-count",
338
+ ...showsSelectedCount ? { "data-selected-count": "" } : null,
339
+ "aria-hidden": "true",
340
+ className: cn("text-muted-foreground ms-auto shrink-0 text-xs tabular-nums", showsSelectedCount && "text-primary!"),
341
+ children: trailingCount
342
+ }) : null,
343
+ selectable && !leadingCheckbox && showsIndicator ? as === "button" ? (multiple || selected) && /* @__PURE__ */ jsx("span", {
344
+ "data-slot": "cascader-item-indicator",
345
+ className: INDICATOR_CLASS,
346
+ children: indicator
347
+ }) : /* @__PURE__ */ jsx(Combobox.ItemIndicator, {
348
+ keepMounted: multiple || indeterminate,
349
+ render: /* @__PURE__ */ jsx("span", {
350
+ "data-slot": "cascader-item-indicator",
351
+ className: INDICATOR_CLASS
352
+ }),
353
+ children: indicator
354
+ }) : null
355
+ ] });
356
+ const rowStyle = mode === "tree" && nodeDepth > 0 ? {
357
+ ...style,
358
+ "--cascader-indent": `${nodeDepth * indent}px`
359
+ } : style;
360
+ const shared = {
361
+ "data-slot": "cascader-item",
362
+ "data-branch": branch || void 0,
363
+ "data-selected": selected || void 0,
364
+ "data-indeterminate": indeterminate || void 0,
365
+ "data-depth": nodeDepth,
366
+ "data-expanded": expanded || void 0,
367
+ style: rowStyle,
368
+ className: cn(ROW_CLASS, leadingCheckbox || !showsIndicator || branch && !branchesSelectable ? ROW_FLUSH_CLASS : ROW_GUTTER_CLASS, mode === "tree" && "ps-[calc(var(--cascader-row-inset,8px)_+_var(--cascader-indent,0px))]!", className)
369
+ };
370
+ if (as === "button") {
371
+ const buttonProps = { ...props };
372
+ delete buttonProps["aria-setsize"];
373
+ delete buttonProps["aria-posinset"];
374
+ delete buttonProps["aria-level"];
375
+ return /* @__PURE__ */ jsx("button", {
376
+ type: "button",
377
+ ...shared,
378
+ tabIndex: -1,
379
+ disabled,
380
+ "aria-disabled": disabled || void 0,
381
+ onClick: handleButtonClick,
382
+ onMouseDown: handleButtonMouseDown,
383
+ ...buttonProps,
384
+ children: body
385
+ });
386
+ }
387
+ return /* @__PURE__ */ jsx(Combobox.Item, {
388
+ ...shared,
389
+ value: node,
390
+ ...virtualized && index != null ? { index } : null,
391
+ disabled,
392
+ ...mode === "tree" ? {
393
+ role: "treeitem",
394
+ "aria-level": nodeDepth + 1,
395
+ ...branch ? { "aria-expanded": !!expanded } : null
396
+ } : branch ? { "aria-haspopup": "listbox" } : null,
397
+ ...indeterminate ? { "aria-checked": "mixed" } : null,
398
+ ...hoverNavigates ? {
399
+ onPointerEnter: handleHoverPointerEnter,
400
+ onPointerLeave: cancelHoverNavigate
401
+ } : null,
402
+ onClick: handleClick,
403
+ onMouseUp: handleMouseUp,
404
+ ...props,
405
+ children: body
406
+ });
407
+ });
408
+ /** Ancestor chain under a deep or global hit: "Name" alone is ambiguous. */
409
+ function CascaderItemPath({ node }) {
410
+ const { index, labels } = useCascaderActions();
411
+ const ancestors = getCascaderPath(index, node.value).slice(0, -1);
412
+ if (!ancestors.length) return null;
413
+ return /* @__PURE__ */ jsx("span", {
414
+ "data-slot": "cascader-item-path",
415
+ className: "text-muted-foreground flex w-full items-center gap-0.5 truncate text-start text-xs",
416
+ children: ancestors.map((ancestor, i) => /* @__PURE__ */ jsxs(React$1.Fragment, { children: [i > 0 ? /* @__PURE__ */ jsx("span", {
417
+ "aria-hidden": "true",
418
+ children: labels.pathSeparator
419
+ }) : null, /* @__PURE__ */ jsx("span", {
420
+ className: "truncate",
421
+ children: ancestor.label
422
+ })] }, ancestor.value))
423
+ });
424
+ }
425
+ /** Every load-derived prop a row needs. A PAGING row reports the level it
426
+ * belongs to, a BRANCH row the level it OWNS; mutually exclusive, so one call
427
+ * answers both. Exported because a memoised row cannot read them itself. */
428
+ function getCascaderMoreProps(node, loadStates) {
429
+ const parent = getCascaderMoreParent(node);
430
+ if (parent == null) {
431
+ const own = loadStates.get(node.value);
432
+ return {
433
+ loading: false,
434
+ error: false,
435
+ childrenLoading: !!own?.loading,
436
+ childrenError: !!own?.error
437
+ };
438
+ }
439
+ const state = loadStates.get(parent);
440
+ return {
441
+ loading: !!state?.loading,
442
+ error: !!state?.error,
443
+ childrenLoading: false,
444
+ childrenError: false
445
+ };
446
+ }
447
+ /** Whatever the active view is: the current level, the deepest column, or the
448
+ * flattened tree rows. Lives here so `cascader.tsx` never imports a row
449
+ * component, which would cycle with the context. `index` is deliberately not
450
+ * passed; see `CascaderItemProps`. */
451
+ function CascaderItems({ children }) {
452
+ const { mode, isBranch, isSelectable, isSelected, isIndeterminate } = useCascaderActions();
453
+ const { renderedItems, deepResults, treeRows, loadStates } = useCascaderState();
454
+ const showPath = deepResults !== null;
455
+ if (mode === "tree") return /* @__PURE__ */ jsx(Fragment, { children: treeRows.map((row, i) => children ? /* @__PURE__ */ jsx(React$1.Fragment, { children: children(row.node, i) }, row.node.value) : /* @__PURE__ */ jsx(CascaderItem, {
456
+ node: row.node,
457
+ depth: row.depth,
458
+ expanded: row.expanded,
459
+ branch: row.branch,
460
+ selectable: isSelectable(row.node),
461
+ selected: isSelected(row.node),
462
+ indeterminate: isIndeterminate(row.node),
463
+ ...getCascaderMoreProps(row.node, loadStates),
464
+ "aria-setsize": row.setSize,
465
+ "aria-posinset": row.posInSet
466
+ }, row.node.value)) });
467
+ return /* @__PURE__ */ jsx(Fragment, { children: renderedItems.map((node, i) => children ? /* @__PURE__ */ jsx(React$1.Fragment, { children: children(node, i) }, node.value) : /* @__PURE__ */ jsx(CascaderItem, {
468
+ node,
469
+ showPath,
470
+ branch: isBranch(node),
471
+ selectable: isSelectable(node),
472
+ selected: isSelected(node),
473
+ indeterminate: isIndeterminate(node),
474
+ ...getCascaderMoreProps(node, loadStates),
475
+ "aria-setsize": renderedItems.length,
476
+ "aria-posinset": i + 1
477
+ }, node.value)) });
478
+ }
479
+ const CascaderGroupContext = React$1.createContext(false);
480
+ const CASCADER_LABEL_CLASS = `text-muted-foreground text-xs py-1.5 ${CASCADER_ACTION_INSET_CLASS} block truncate`;
481
+ const CASCADER_SEPARATOR_CLASS = "h-px bg-border my-1 mx-[calc(var(--cascader-list-pad,4px)*-1)]! shrink-0";
482
+ /** A run of related rows, named by the `CascaderLabel` inside it: the group is
483
+ * `role="group"` and Base UI points its `aria-labelledby` at the label, where a
484
+ * heading alone inside a listbox is dropped from the accessibility tree.
485
+ * Grouping is composition, not data: `CascaderItems` renders one flat run per
486
+ * level, so its indices line up with `listRef`. */
487
+ function CascaderGroup({ className, ...props }) {
488
+ return /* @__PURE__ */ jsx(CascaderGroupContext.Provider, {
489
+ value: true,
490
+ children: /* @__PURE__ */ jsx(Combobox.Group, {
491
+ "data-slot": "cascader-group",
492
+ className: cn("flex flex-col", className),
493
+ ...props
494
+ })
495
+ });
496
+ }
497
+ /** Inside a group: the real thing, with the id association Base UI wires up. */
498
+ function CascaderGroupLabel({ className, ...props }) {
499
+ return /* @__PURE__ */ jsx(Combobox.GroupLabel, {
500
+ "data-slot": "cascader-label",
501
+ className: cn(CASCADER_LABEL_CLASS, className),
502
+ ...props
503
+ });
504
+ }
505
+ function CascaderPlainLabel({ className, ...props }) {
506
+ const defaultProps = {
507
+ "data-slot": "cascader-label",
508
+ className: cn(CASCADER_LABEL_CLASS, className)
509
+ };
510
+ return useRender({
511
+ defaultTagName: "div",
512
+ render: props.render,
513
+ props: mergeProps(defaultProps, props)
514
+ });
515
+ }
516
+ /** A heading. Inside `CascaderGroup` it is the group's accessible name; outside
517
+ * one (the footer flyout is a menu, not a listbox) it is decoration only. */
518
+ function CascaderLabel(props) {
519
+ return React$1.useContext(CascaderGroupContext) ? /* @__PURE__ */ jsx(CascaderGroupLabel, { ...props }) : /* @__PURE__ */ jsx(CascaderPlainLabel, { ...props });
520
+ }
521
+ /** Decorative on purpose: Base UI's separator is `role="separator"`, which a
522
+ * `listbox` may not own, and a run that needs separating for a screen reader
523
+ * needs a `CascaderGroup`, not a line. A consumer can pass the role back. */
524
+ function CascaderSeparator({ className, ...props }) {
525
+ return /* @__PURE__ */ jsx(Combobox.Separator, {
526
+ "data-slot": "cascader-separator",
527
+ role: "presentation",
528
+ "aria-hidden": "true",
529
+ className: cn(CASCADER_SEPARATOR_CLASS, className),
530
+ ...props
531
+ });
532
+ }
533
+ export { CASCADER_ACTION_CLASS, CascaderGroup, CascaderItem, CascaderItems, CascaderLabel, CascaderSeparator, getCascaderMoreProps };
@@ -0,0 +1,3 @@
1
+ import "./cascader-types.mjs";
2
+ /** How `getCascaderCheckedValues` condenses a full-closure selection. */
3
+ export type CascaderCheckedStrategy = "all" | "parent" | "child";