@expcat/tigercat-react 2.0.4 → 2.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 (84) hide show
  1. package/README.md +25 -16
  2. package/dist/chunk-2YOP55OF.mjs +639 -0
  3. package/dist/{chunk-V6P7ZK6O.mjs → chunk-7EMCZVJ2.mjs} +1 -1
  4. package/dist/chunk-EW43YBPV.mjs +457 -0
  5. package/dist/{chunk-QJE3MZBO.mjs → chunk-FSMJLJFL.mjs} +1 -1
  6. package/dist/{chunk-BOGOQ44C.mjs → chunk-GCY55TBC.mjs} +1 -1
  7. package/dist/chunk-GR3Y3XWP.mjs +47 -0
  8. package/dist/chunk-IGBQGR6G.mjs +234 -0
  9. package/dist/{chunk-D33BIMGK.mjs → chunk-IR7VA6AO.mjs} +11 -1
  10. package/dist/chunk-J76R2S62.mjs +161 -0
  11. package/dist/{chunk-2WGEHH5A.mjs → chunk-JQJM65RJ.mjs} +1 -1
  12. package/dist/{chunk-7AFRHC7P.mjs → chunk-KC55B62J.mjs} +127 -42
  13. package/dist/chunk-KRDHM5KB.mjs +259 -0
  14. package/dist/chunk-M5MZWOHF.mjs +180 -0
  15. package/dist/chunk-MFPVVGNA.mjs +248 -0
  16. package/dist/chunk-NLGLBTSM.mjs +138 -0
  17. package/dist/chunk-NT7GW2FD.mjs +40 -0
  18. package/dist/chunk-OKYMTHFQ.mjs +279 -0
  19. package/dist/{chunk-LBXNMCXN.mjs → chunk-OZ5XLR22.mjs} +6 -1
  20. package/dist/{chunk-HHIW7V7B.mjs → chunk-PFL4HNWM.mjs} +1 -8
  21. package/dist/chunk-RUAPTIQ7.mjs +181 -0
  22. package/dist/chunk-SEOG5AGS.mjs +72 -0
  23. package/dist/chunk-TQ6RIFWR.mjs +135 -0
  24. package/dist/chunk-U4M3DC2P.mjs +48 -0
  25. package/dist/{chunk-YIB6ZXZF.mjs → chunk-UETARXIK.mjs} +239 -52
  26. package/dist/chunk-XT43B7ZI.mjs +58 -0
  27. package/dist/components/ActivityFeed.mjs +1 -1
  28. package/dist/components/AreaChart.mjs +2 -2
  29. package/dist/components/AspectRatio.d.mts +10 -0
  30. package/dist/components/AspectRatio.mjs +8 -0
  31. package/dist/components/BarChart.mjs +2 -2
  32. package/dist/components/Cascader.mjs +1 -1
  33. package/dist/components/ChatWindow.mjs +1 -1
  34. package/dist/components/CodeEditor.mjs +1 -1
  35. package/dist/components/ContextMenu.d.mts +36 -0
  36. package/dist/components/ContextMenu.mjs +17 -0
  37. package/dist/components/DataExport.mjs +1 -1
  38. package/dist/components/DataTableWithToolbar.mjs +5 -5
  39. package/dist/components/Highlight.d.mts +11 -0
  40. package/dist/components/Highlight.mjs +8 -0
  41. package/dist/components/ImageCompare.d.mts +15 -0
  42. package/dist/components/ImageCompare.mjs +8 -0
  43. package/dist/components/InputOTP.d.mts +33 -0
  44. package/dist/components/InputOTP.mjs +12 -0
  45. package/dist/components/Kbd.d.mts +10 -0
  46. package/dist/components/Kbd.mjs +8 -0
  47. package/dist/components/LineChart.mjs +2 -2
  48. package/dist/components/List.mjs +2 -2
  49. package/dist/components/LoadingBar.d.mts +8 -0
  50. package/dist/components/LoadingBar.mjs +138 -0
  51. package/dist/components/LoadingBarContainer.d.mts +8 -0
  52. package/dist/components/LoadingBarContainer.mjs +8 -0
  53. package/dist/components/LoadingBarRoot.d.mts +6 -0
  54. package/dist/components/LoadingBarRoot.mjs +6 -0
  55. package/dist/components/Marquee.d.mts +10 -0
  56. package/dist/components/Marquee.mjs +8 -0
  57. package/dist/components/MaskInput.d.mts +37 -0
  58. package/dist/components/MaskInput.mjs +10 -0
  59. package/dist/components/Masonry.d.mts +10 -0
  60. package/dist/components/Masonry.mjs +8 -0
  61. package/dist/components/NavigationMenu.d.mts +84 -0
  62. package/dist/components/NavigationMenu.mjs +23 -0
  63. package/dist/components/NotificationCenter.mjs +3 -3
  64. package/dist/components/PageHeader.d.mts +34 -0
  65. package/dist/components/PageHeader.mjs +11 -0
  66. package/dist/components/Pagination.mjs +1 -1
  67. package/dist/components/RadarChart.mjs +2 -2
  68. package/dist/components/ScatterChart.mjs +2 -2
  69. package/dist/components/ScrollArea.d.mts +10 -0
  70. package/dist/components/ScrollArea.mjs +8 -0
  71. package/dist/components/SplitButton.d.mts +38 -0
  72. package/dist/components/SplitButton.mjs +12 -0
  73. package/dist/components/Table.mjs +2 -2
  74. package/dist/components/TagsInput.d.mts +41 -0
  75. package/dist/components/TagsInput.mjs +13 -0
  76. package/dist/components/TreeSelect.mjs +2 -1
  77. package/dist/index.d.mts +17 -1
  78. package/dist/index.mjs +356 -0
  79. package/package.json +117 -2
  80. package/dist/{chunk-NCHUEGUW.mjs → chunk-H6MVW5YD.mjs} +3 -3
  81. package/dist/{chunk-UT5BV2VO.mjs → chunk-I4MWOYJS.mjs} +3 -3
  82. package/dist/{chunk-E7OTDBI6.mjs → chunk-NWVNTZOP.mjs} +3 -3
  83. package/dist/{chunk-FYINJRYN.mjs → chunk-RQ4PUK6G.mjs} +3 -3
  84. package/dist/{chunk-KMP5CVNK.mjs → chunk-UKY2EPS3.mjs} +3 -3
@@ -0,0 +1,72 @@
1
+ // src/components/Highlight.tsx
2
+ import React, { forwardRef, isValidElement } from "react";
3
+ import {
4
+ getHighlightMarkClasses,
5
+ getHighlightRootClasses,
6
+ getHighlightSegments,
7
+ resolveHighlightText
8
+ } from "@expcat/tigercat-core";
9
+ import { jsx } from "react/jsx-runtime";
10
+ function flattenReactText(node) {
11
+ if (node == null || typeof node === "boolean") return "";
12
+ if (typeof node === "string" || typeof node === "number") return String(node);
13
+ if (Array.isArray(node)) {
14
+ let out = "";
15
+ for (const item of node) out += flattenReactText(item);
16
+ return out;
17
+ }
18
+ if (isValidElement(node)) {
19
+ const props = node.props;
20
+ return flattenReactText(props.children);
21
+ }
22
+ return "";
23
+ }
24
+ var Highlight = forwardRef(
25
+ ({
26
+ text,
27
+ keywords,
28
+ caseSensitive = false,
29
+ global = true,
30
+ highlightClassName,
31
+ highlightStyle,
32
+ className,
33
+ style,
34
+ children,
35
+ ...rest
36
+ }, ref) => {
37
+ const source = resolveHighlightText(text, flattenReactText(children));
38
+ const segments = getHighlightSegments(source, keywords, { caseSensitive, global });
39
+ const markClasses = getHighlightMarkClasses(highlightClassName);
40
+ return /* @__PURE__ */ jsx(
41
+ "span",
42
+ {
43
+ ref,
44
+ "data-highlight": "",
45
+ "data-highlight-case-sensitive": caseSensitive ? "true" : "false",
46
+ "data-highlight-global": global ? "true" : "false",
47
+ className: getHighlightRootClasses(className),
48
+ style,
49
+ ...rest,
50
+ children: segments.map(
51
+ (segment) => segment.highlighted ? /* @__PURE__ */ jsx(
52
+ "mark",
53
+ {
54
+ className: markClasses,
55
+ style: highlightStyle,
56
+ "data-highlight-mark": "",
57
+ children: segment.text
58
+ },
59
+ `mark-${segment.start}-${segment.end}`
60
+ ) : /* @__PURE__ */ jsx(React.Fragment, { children: segment.text }, `text-${segment.start}-${segment.end}`)
61
+ )
62
+ }
63
+ );
64
+ }
65
+ );
66
+ Highlight.displayName = "Highlight";
67
+ var Highlight_default = Highlight;
68
+
69
+ export {
70
+ Highlight,
71
+ Highlight_default
72
+ };
@@ -0,0 +1,135 @@
1
+ // src/components/Marquee.tsx
2
+ import { forwardRef, useCallback, useState } from "react";
3
+ import {
4
+ getMarqueeContentClasses,
5
+ getMarqueeRootClasses,
6
+ getMarqueeTrackClasses,
7
+ getMarqueeTrackStyle,
8
+ injectMarqueeStyles,
9
+ isMarqueeFocusInside,
10
+ isMarqueePaused,
11
+ resolveMarqueeAriaLabel,
12
+ resolveMarqueeDirection,
13
+ resolveMarqueePauseOnHover,
14
+ resolveMarqueeRepeat
15
+ } from "@expcat/tigercat-core";
16
+ import { jsx } from "react/jsx-runtime";
17
+ var Marquee = forwardRef(
18
+ ({
19
+ direction,
20
+ duration,
21
+ pauseOnHover,
22
+ gap,
23
+ repeat,
24
+ ariaLabel,
25
+ className,
26
+ style,
27
+ children,
28
+ onMouseEnter,
29
+ onMouseLeave,
30
+ onFocus,
31
+ onBlur,
32
+ ...rest
33
+ }, ref) => {
34
+ injectMarqueeStyles();
35
+ const [hovered, setHovered] = useState(false);
36
+ const [focused, setFocused] = useState(false);
37
+ const resolvedDirection = resolveMarqueeDirection(direction);
38
+ const resolvedPauseOnHover = resolveMarqueePauseOnHover(pauseOnHover);
39
+ const copies = resolveMarqueeRepeat(repeat);
40
+ const paused = isMarqueePaused({
41
+ pauseOnHover: resolvedPauseOnHover,
42
+ hovered,
43
+ focused
44
+ });
45
+ const { "aria-label": ariaLabelAttr, ...domProps } = rest;
46
+ const resolvedAriaLabel = resolveMarqueeAriaLabel(
47
+ typeof ariaLabelAttr === "string" ? ariaLabelAttr : ariaLabel
48
+ );
49
+ const handleMouseEnter = useCallback(
50
+ (event) => {
51
+ if (resolvedPauseOnHover) setHovered(true);
52
+ onMouseEnter?.(event);
53
+ },
54
+ [onMouseEnter, resolvedPauseOnHover]
55
+ );
56
+ const handleMouseLeave = useCallback(
57
+ (event) => {
58
+ if (resolvedPauseOnHover) setHovered(false);
59
+ onMouseLeave?.(event);
60
+ },
61
+ [onMouseLeave, resolvedPauseOnHover]
62
+ );
63
+ const handleFocus = useCallback(
64
+ (event) => {
65
+ if (resolvedPauseOnHover) setFocused(true);
66
+ onFocus?.(event);
67
+ },
68
+ [onFocus, resolvedPauseOnHover]
69
+ );
70
+ const handleBlur = useCallback(
71
+ (event) => {
72
+ if (resolvedPauseOnHover && !isMarqueeFocusInside(event.currentTarget, event.relatedTarget)) {
73
+ setFocused(false);
74
+ }
75
+ onBlur?.(event);
76
+ },
77
+ [onBlur, resolvedPauseOnHover]
78
+ );
79
+ return /* @__PURE__ */ jsx(
80
+ "div",
81
+ {
82
+ ...domProps,
83
+ ref,
84
+ role: "region",
85
+ "aria-label": resolvedAriaLabel,
86
+ "data-marquee": "",
87
+ "data-marquee-direction": resolvedDirection,
88
+ "data-marquee-paused": paused ? "true" : "false",
89
+ "data-marquee-pause-on-hover": resolvedPauseOnHover ? "true" : "false",
90
+ className: getMarqueeRootClasses({
91
+ direction: resolvedDirection,
92
+ pauseOnHover: resolvedPauseOnHover,
93
+ repeat: copies,
94
+ className
95
+ }),
96
+ style,
97
+ onMouseEnter: handleMouseEnter,
98
+ onMouseLeave: handleMouseLeave,
99
+ onFocus: handleFocus,
100
+ onBlur: handleBlur,
101
+ children: /* @__PURE__ */ jsx(
102
+ "div",
103
+ {
104
+ "data-marquee-track": "",
105
+ className: getMarqueeTrackClasses(resolvedDirection),
106
+ style: getMarqueeTrackStyle({ duration, gap, repeat: copies }),
107
+ children: Array.from({ length: copies }, (_, index) => {
108
+ const clone = index > 0;
109
+ return /* @__PURE__ */ jsx(
110
+ "div",
111
+ {
112
+ className: getMarqueeContentClasses({
113
+ direction: resolvedDirection,
114
+ clone
115
+ }),
116
+ "data-marquee-content": "",
117
+ ...clone ? { "data-marquee-clone": "", "aria-hidden": true } : {},
118
+ children
119
+ },
120
+ clone ? `clone-${index}` : "content"
121
+ );
122
+ })
123
+ }
124
+ )
125
+ }
126
+ );
127
+ }
128
+ );
129
+ Marquee.displayName = "Marquee";
130
+ var Marquee_default = Marquee;
131
+
132
+ export {
133
+ Marquee,
134
+ Marquee_default
135
+ };
@@ -0,0 +1,48 @@
1
+ // src/components/LoadingBarRoot.ts
2
+ var loadingBarModulePromise = null;
3
+ var pendingCommands = [];
4
+ var resolvedApi = null;
5
+ function loadLoadingBarModule() {
6
+ loadingBarModulePromise ??= import("./components/LoadingBar.mjs");
7
+ return loadingBarModulePromise;
8
+ }
9
+ function applyCommand(api, command) {
10
+ if (command.type === "start") {
11
+ api.start(command.options);
12
+ return;
13
+ }
14
+ api[command.type]();
15
+ }
16
+ function enqueue(command) {
17
+ if (resolvedApi) {
18
+ applyCommand(resolvedApi, command);
19
+ return;
20
+ }
21
+ pendingCommands.push(command);
22
+ void loadLoadingBarModule().then(({ LoadingBar: LoadingBar2 }) => {
23
+ resolvedApi = LoadingBar2;
24
+ const queue = pendingCommands;
25
+ pendingCommands = [];
26
+ for (const item of queue) {
27
+ applyCommand(LoadingBar2, item);
28
+ }
29
+ });
30
+ }
31
+ var LoadingBar = {
32
+ start(options) {
33
+ enqueue({ type: "start", options });
34
+ },
35
+ finish() {
36
+ enqueue({ type: "finish" });
37
+ },
38
+ error() {
39
+ enqueue({ type: "error" });
40
+ },
41
+ clear() {
42
+ enqueue({ type: "clear" });
43
+ }
44
+ };
45
+
46
+ export {
47
+ LoadingBar
48
+ };
@@ -23,10 +23,15 @@ import {
23
23
  flattenCascaderOptions,
24
24
  filterCascaderOptions,
25
25
  isCascaderOptionExpandable,
26
+ getCascaderVirtualItemHeight,
27
+ getCascaderVirtualRange,
28
+ getCascaderVirtualAlignScrollTop,
29
+ CASCADER_DEFAULT_LIST_HEIGHT,
26
30
  getPickerComboboxAria,
27
31
  getPickerListboxAria,
28
32
  getPickerOptionAria,
29
33
  getPickerTriggerKeyAction,
34
+ getPickerNavigationIndex,
30
35
  icon20ViewBox,
31
36
  chevronDownSolidIcon20PathD,
32
37
  closeSolidIcon20PathD,
@@ -50,10 +55,90 @@ var CASCADER_KEYS = /* @__PURE__ */ new Set([
50
55
  "changeOnSelect",
51
56
  "separator",
52
57
  "emptyText",
58
+ "virtual",
59
+ "listHeight",
53
60
  "className",
54
61
  "locale"
55
62
  ]);
56
63
  var EMPTY_CASCADER_VALUE = [];
64
+ function selectedIndexInColumn(options, selectedValue) {
65
+ if (selectedValue === void 0) return -1;
66
+ return options.findIndex((option) => option.value === selectedValue);
67
+ }
68
+ var VirtualCascaderList = ({
69
+ items,
70
+ size,
71
+ listHeight,
72
+ activeIndex,
73
+ onActiveIndexChange,
74
+ className,
75
+ listboxProps,
76
+ renderItem
77
+ }) => {
78
+ const itemHeight = getCascaderVirtualItemHeight(size);
79
+ const itemCount = items.length;
80
+ const clampedActiveIndex = activeIndex >= 0 && activeIndex < itemCount ? activeIndex : -1;
81
+ const containerRef = useRef(null);
82
+ const [scrollTop, setScrollTop] = useState(
83
+ () => getCascaderVirtualAlignScrollTop(0, clampedActiveIndex, itemHeight, listHeight)
84
+ );
85
+ const maxScrollTop = Math.max(0, itemCount * itemHeight - listHeight);
86
+ const effectiveScrollTop = Math.min(Math.max(0, scrollTop), maxScrollTop);
87
+ const { startIndex, endIndex, totalHeight } = getCascaderVirtualRange(
88
+ effectiveScrollTop,
89
+ listHeight,
90
+ itemCount,
91
+ itemHeight
92
+ );
93
+ const fromIndex = itemCount <= 0 ? 0 : Math.min(startIndex, endIndex, itemCount - 1);
94
+ const toIndex = itemCount <= 0 ? -1 : Math.min(endIndex, itemCount - 1);
95
+ useEffect(() => {
96
+ const el = containerRef.current;
97
+ if (!el) return;
98
+ if (el.scrollTop !== effectiveScrollTop) el.scrollTop = effectiveScrollTop;
99
+ }, [effectiveScrollTop]);
100
+ useEffect(() => {
101
+ const el = containerRef.current;
102
+ if (!el || clampedActiveIndex < 0) return;
103
+ const next = getCascaderVirtualAlignScrollTop(
104
+ el.scrollTop,
105
+ clampedActiveIndex,
106
+ itemHeight,
107
+ listHeight
108
+ );
109
+ if (next !== el.scrollTop) {
110
+ el.scrollTop = next;
111
+ setScrollTop(next);
112
+ }
113
+ }, [clampedActiveIndex, itemHeight, listHeight]);
114
+ const visible = [];
115
+ if (fromIndex <= toIndex) {
116
+ for (let i = fromIndex; i <= toIndex; i++) {
117
+ visible.push(renderItem(items[i], i));
118
+ }
119
+ }
120
+ return /* @__PURE__ */ jsx(
121
+ "div",
122
+ {
123
+ ref: containerRef,
124
+ "data-tiger-cascader-virtual": "",
125
+ tabIndex: 0,
126
+ className,
127
+ style: { maxHeight: `${listHeight}px`, overflowY: "auto" },
128
+ onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
129
+ onKeyDown: (e) => {
130
+ if (e.key !== "ArrowDown" && e.key !== "ArrowUp" && e.key !== "Home" && e.key !== "End") {
131
+ return;
132
+ }
133
+ e.preventDefault();
134
+ e.stopPropagation();
135
+ onActiveIndexChange(getPickerNavigationIndex(items, clampedActiveIndex, e.key));
136
+ },
137
+ ...listboxProps,
138
+ children: /* @__PURE__ */ jsx("div", { style: { height: `${totalHeight}px`, position: "relative" }, children: /* @__PURE__ */ jsx("div", { style: { transform: `translateY(${fromIndex * itemHeight}px)` }, children: visible }) })
139
+ }
140
+ );
141
+ };
57
142
  var Cascader = (props) => {
58
143
  const {
59
144
  options = [],
@@ -71,6 +156,8 @@ var Cascader = (props) => {
71
156
  changeOnSelect = false,
72
157
  separator = " / ",
73
158
  emptyText,
159
+ virtual = false,
160
+ listHeight = CASCADER_DEFAULT_LIST_HEIGHT,
74
161
  className,
75
162
  locale
76
163
  } = props;
@@ -89,6 +176,13 @@ var Cascader = (props) => {
89
176
  const [uncontrolledSearchValue, setUncontrolledSearchValue] = useState(defaultSearchValue);
90
177
  const searchQuery = searchValue ?? uncontrolledSearchValue;
91
178
  const [activePath, setActivePath] = useState([]);
179
+ const [columnActiveIndices, setColumnActiveIndices] = useState([]);
180
+ const [searchActiveIndex, setSearchActiveIndex] = useState(-1);
181
+ const [indexedSearchQuery, setIndexedSearchQuery] = useState(searchQuery);
182
+ if (indexedSearchQuery !== searchQuery) {
183
+ setIndexedSearchQuery(searchQuery);
184
+ setSearchActiveIndex(-1);
185
+ }
92
186
  const wasOpenRef = useRef(false);
93
187
  const triggerRef = useRef(null);
94
188
  const dropdownRef = useRef(null);
@@ -135,6 +229,8 @@ var Cascader = (props) => {
135
229
  useEffect(() => {
136
230
  if (!isOpen) {
137
231
  wasOpenRef.current = false;
232
+ setColumnActiveIndices([]);
233
+ setSearchActiveIndex(-1);
138
234
  return;
139
235
  }
140
236
  if (wasOpenRef.current) return;
@@ -142,6 +238,17 @@ var Cascader = (props) => {
142
238
  setActivePath(value ? [...value] : []);
143
239
  updateSearchValue("");
144
240
  }, [isOpen, updateSearchValue, value]);
241
+ useEffect(() => {
242
+ if (!virtual || !isOpen) return;
243
+ setColumnActiveIndices(
244
+ (prev) => columns.map((col, i) => {
245
+ if (prev[i] !== void 0 && prev[i] >= 0 && prev[i] < col.options.length) {
246
+ return prev[i];
247
+ }
248
+ return selectedIndexInColumn(col.options, col.selectedValue);
249
+ })
250
+ );
251
+ }, [virtual, isOpen, columns]);
145
252
  const toggleOpen = useCallback(() => {
146
253
  if (disabled) return;
147
254
  setIsOpen((prev) => !prev);
@@ -149,9 +256,17 @@ var Cascader = (props) => {
149
256
  const handleOptionClick = useCallback(
150
257
  (option, level) => {
151
258
  if (option.disabled) return;
259
+ const optionIndex = columns[level]?.options.findIndex((item) => item.value === option.value);
152
260
  const newPath = activePath.slice(0, level);
153
261
  newPath.push(option.value);
154
262
  setActivePath(newPath);
263
+ if (optionIndex !== void 0 && optionIndex >= 0) {
264
+ setColumnActiveIndices((prev) => {
265
+ const next = prev.slice();
266
+ next[level] = optionIndex;
267
+ return next;
268
+ });
269
+ }
155
270
  const hasChildren = isCascaderOptionExpandable(option);
156
271
  if (!hasChildren) {
157
272
  onChange?.(newPath);
@@ -160,16 +275,24 @@ var Cascader = (props) => {
160
275
  onChange?.(newPath);
161
276
  }
162
277
  },
163
- [activePath, onChange, changeOnSelect]
278
+ [activePath, onChange, changeOnSelect, columns]
164
279
  );
165
280
  const handleOptionHover = useCallback(
166
281
  (option, level) => {
282
+ const optionIndex = columns[level]?.options.findIndex((item) => item.value === option.value);
283
+ if (optionIndex !== void 0 && optionIndex >= 0 && !option.disabled) {
284
+ setColumnActiveIndices((prev) => {
285
+ const next = prev.slice();
286
+ next[level] = optionIndex;
287
+ return next;
288
+ });
289
+ }
167
290
  if (expandTrigger !== "hover" || option.disabled) return;
168
291
  const newPath = activePath.slice(0, level);
169
292
  newPath.push(option.value);
170
293
  setActivePath(newPath);
171
294
  },
172
- [expandTrigger, activePath]
295
+ [expandTrigger, activePath, columns]
173
296
  );
174
297
  const handleSearchResultClick = useCallback(
175
298
  (valuePath, itemDisabled) => {
@@ -298,14 +421,46 @@ var Cascader = (props) => {
298
421
  "No results found",
299
422
  emptyText,
300
423
  mergedLocale?.common?.emptyText
301
- ) }) : /* @__PURE__ */ jsx(
424
+ ) }) : virtual ? /* @__PURE__ */ jsx(
425
+ VirtualCascaderList,
426
+ {
427
+ items: searchResults,
428
+ size,
429
+ listHeight,
430
+ activeIndex: searchActiveIndex,
431
+ onActiveIndexChange: setSearchActiveIndex,
432
+ listboxProps: getPickerListboxAria({ id: listboxId }),
433
+ renderItem: (item, index) => /* @__PURE__ */ jsx(
434
+ "div",
435
+ {
436
+ "data-option-index": index,
437
+ className: classNames(
438
+ cascaderSearchResultClasses,
439
+ item.disabled && "opacity-50 cursor-not-allowed"
440
+ ),
441
+ ...getPickerOptionAria({
442
+ selected: value?.join(",") === item.valuePath.join(","),
443
+ disabled: item.disabled
444
+ }),
445
+ onMouseEnter: () => {
446
+ if (!item.disabled) setSearchActiveIndex(index);
447
+ },
448
+ onClick: () => handleSearchResultClick(item.valuePath, item.disabled),
449
+ children: typeof searchable === "object" && searchable.render ? searchable.render(searchQuery, item.path) : item.label
450
+ },
451
+ item.valuePath.join(",")
452
+ )
453
+ },
454
+ searchQuery
455
+ ) : /* @__PURE__ */ jsx(
302
456
  "div",
303
457
  {
304
458
  className: "max-h-64 overflow-auto",
305
459
  ...getPickerListboxAria({ id: listboxId }),
306
- children: searchResults.map((item) => /* @__PURE__ */ jsx(
460
+ children: searchResults.map((item, index) => /* @__PURE__ */ jsx(
307
461
  "div",
308
462
  {
463
+ "data-option-index": index,
309
464
  className: classNames(
310
465
  cascaderSearchResultClasses,
311
466
  item.disabled && "opacity-50 cursor-not-allowed"
@@ -323,55 +478,87 @@ var Cascader = (props) => {
323
478
  )
324
479
  ) : (
325
480
  // Cascading columns
326
- /* @__PURE__ */ jsx("div", { className: "flex", children: columns.map((col, colIndex) => /* @__PURE__ */ jsx(
327
- "div",
328
- {
329
- className: cascaderColumnClasses,
330
- ...getPickerListboxAria({
331
- id: colIndex === 0 ? listboxId : void 0,
332
- label: `Level ${colIndex + 1}`
333
- }),
334
- "aria-label": `Level ${colIndex + 1}`,
335
- children: col.options.map((option) => {
336
- const isSelected = col.selectedValue === option.value;
337
- const hasChildren = isCascaderOptionExpandable(option);
338
- return /* @__PURE__ */ jsxs(
339
- "div",
340
- {
341
- className: getCascaderOptionClasses(isSelected, !!option.disabled, size),
342
- ...getPickerOptionAria({
343
- selected: isSelected,
344
- disabled: !!option.disabled
345
- }),
346
- onClick: () => handleOptionClick(option, colIndex),
347
- onMouseEnter: () => handleOptionHover(option, colIndex),
348
- children: [
349
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: option.label }),
350
- hasChildren && /* @__PURE__ */ jsx(
351
- "svg",
352
- {
353
- className: "w-4 h-4 text-[var(--tiger-text-muted,#9ca3af)]",
354
- xmlns: "http://www.w3.org/2000/svg",
355
- viewBox: "0 0 20 20",
356
- fill: "currentColor",
357
- children: /* @__PURE__ */ jsx(
358
- "path",
359
- {
360
- fillRule: "evenodd",
361
- d: "M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z",
362
- clipRule: "evenodd"
363
- }
364
- )
365
- }
366
- )
367
- ]
481
+ /* @__PURE__ */ jsx("div", { className: "flex", children: columns.map((col, colIndex) => {
482
+ const listboxProps = getPickerListboxAria({
483
+ id: colIndex === 0 ? listboxId : void 0,
484
+ label: `Level ${colIndex + 1}`
485
+ });
486
+ const renderColumnOption = (option, optionIndex) => {
487
+ const isSelected = col.selectedValue === option.value;
488
+ const hasChildren = isCascaderOptionExpandable(option);
489
+ return /* @__PURE__ */ jsxs(
490
+ "div",
491
+ {
492
+ "data-option-index": optionIndex,
493
+ className: getCascaderOptionClasses(isSelected, !!option.disabled, size),
494
+ ...getPickerOptionAria({
495
+ selected: isSelected,
496
+ disabled: !!option.disabled
497
+ }),
498
+ onClick: () => handleOptionClick(option, colIndex),
499
+ onMouseEnter: () => handleOptionHover(option, colIndex),
500
+ children: [
501
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: option.label }),
502
+ hasChildren && /* @__PURE__ */ jsx(
503
+ "svg",
504
+ {
505
+ className: "w-4 h-4 text-[var(--tiger-text-muted,#9ca3af)]",
506
+ xmlns: "http://www.w3.org/2000/svg",
507
+ viewBox: "0 0 20 20",
508
+ fill: "currentColor",
509
+ children: /* @__PURE__ */ jsx(
510
+ "path",
511
+ {
512
+ fillRule: "evenodd",
513
+ d: "M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z",
514
+ clipRule: "evenodd"
515
+ }
516
+ )
517
+ }
518
+ )
519
+ ]
520
+ },
521
+ option.value
522
+ );
523
+ };
524
+ if (virtual) {
525
+ return /* @__PURE__ */ jsx(
526
+ VirtualCascaderList,
527
+ {
528
+ items: col.options,
529
+ size,
530
+ listHeight,
531
+ activeIndex: columnActiveIndices[colIndex] ?? -1,
532
+ onActiveIndexChange: (index) => {
533
+ setColumnActiveIndices((prev) => {
534
+ const next = prev.slice();
535
+ next[colIndex] = index;
536
+ return next;
537
+ });
368
538
  },
369
- option.value
370
- );
371
- })
372
- },
373
- colIndex
374
- )) })
539
+ className: cascaderColumnClasses,
540
+ listboxProps: {
541
+ ...listboxProps,
542
+ "aria-label": `Level ${colIndex + 1}`
543
+ },
544
+ renderItem: renderColumnOption
545
+ },
546
+ colIndex
547
+ );
548
+ }
549
+ return /* @__PURE__ */ jsx(
550
+ "div",
551
+ {
552
+ className: cascaderColumnClasses,
553
+ ...listboxProps,
554
+ "aria-label": `Level ${colIndex + 1}`,
555
+ children: col.options.map(
556
+ (option, optionIndex) => renderColumnOption(option, optionIndex)
557
+ )
558
+ },
559
+ colIndex
560
+ );
561
+ }) })
375
562
  )
376
563
  ]
377
564
  }