@expcat/tigercat-react 2.1.0 → 2.1.2

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 (144) hide show
  1. package/dist/{chunk-RUAPTIQ7.mjs → chunk-246DAUEF.mjs} +2 -2
  2. package/dist/{chunk-PU3JY6RE.mjs → chunk-2JMI7TCM.mjs} +1 -1
  3. package/dist/{chunk-GCY55TBC.mjs → chunk-2XZHYYZ2.mjs} +106 -91
  4. package/dist/chunk-2YBLMVDT.mjs +428 -0
  5. package/dist/{chunk-25D5TXZH.mjs → chunk-32MCQMBJ.mjs} +8 -6
  6. package/dist/{chunk-6MGQAJ62.mjs → chunk-3GPGSNOQ.mjs} +71 -16
  7. package/dist/{chunk-M5MZWOHF.mjs → chunk-3HHEVMHE.mjs} +1 -1
  8. package/dist/{chunk-HSBN5MLS.mjs → chunk-3LACOSJQ.mjs} +1 -1
  9. package/dist/{chunk-F6WV53BS.mjs → chunk-3LDDXHW2.mjs} +28 -11
  10. package/dist/{chunk-IXQAECFP.mjs → chunk-3QNIFKVF.mjs} +12 -14
  11. package/dist/{chunk-UD4DTVJZ.mjs → chunk-4C2X5PZO.mjs} +57 -25
  12. package/dist/chunk-5JETWHLJ.mjs +28 -0
  13. package/dist/{chunk-VA6PZWOH.mjs → chunk-5SEMRHBD.mjs} +14 -6
  14. package/dist/{chunk-MXXAJSRS.mjs → chunk-6RMBUZZE.mjs} +11 -11
  15. package/dist/{chunk-EKILTLUO.mjs → chunk-72CJNKDY.mjs} +1 -1
  16. package/dist/{chunk-XE4YFBOZ.mjs → chunk-AUD3VOLO.mjs} +1 -1
  17. package/dist/{chunk-I4MWOYJS.mjs → chunk-AYFKHWJS.mjs} +13 -7
  18. package/dist/{chunk-RPSHILHD.mjs → chunk-BXAH2GH3.mjs} +8 -2
  19. package/dist/{chunk-EW5EO7VE.mjs → chunk-BYD7EAWK.mjs} +1 -1
  20. package/dist/{chunk-L55UK45A.mjs → chunk-CRIEUKMD.mjs} +5 -2
  21. package/dist/{chunk-DKNVSQSV.mjs → chunk-CUAXRBKO.mjs} +12 -0
  22. package/dist/{chunk-NWVNTZOP.mjs → chunk-DECUASRW.mjs} +14 -16
  23. package/dist/{chunk-XMTEMBQV.mjs → chunk-EAAWB3CK.mjs} +16 -13
  24. package/dist/{chunk-IR7VA6AO.mjs → chunk-EG7EMDKK.mjs} +1 -1
  25. package/dist/{chunk-HOYBHB7B.mjs → chunk-FNK2VWPP.mjs} +1 -1
  26. package/dist/{chunk-RJPNBYK5.mjs → chunk-FQS24AUH.mjs} +12 -3
  27. package/dist/{chunk-LADKKOJT.mjs → chunk-GBEDWXLL.mjs} +16 -10
  28. package/dist/{chunk-2ENQZECC.mjs → chunk-HAXRHSCN.mjs} +4 -3
  29. package/dist/{chunk-K4GIKSZE.mjs → chunk-I2BENK5X.mjs} +2 -1
  30. package/dist/{chunk-DNOPEJ5I.mjs → chunk-JZZPLKTW.mjs} +7 -4
  31. package/dist/{chunk-7KCQMXJA.mjs → chunk-KDURW5AS.mjs} +4 -3
  32. package/dist/{chunk-TFMEHUVZ.mjs → chunk-KKN7KLTB.mjs} +0 -1
  33. package/dist/{chunk-PFL4HNWM.mjs → chunk-M6ZPTGBV.mjs} +1 -2
  34. package/dist/{chunk-PXRZ6CKY.mjs → chunk-MJVSZ2UJ.mjs} +46 -16
  35. package/dist/{chunk-LZKBDQE4.mjs → chunk-MYI3FPG4.mjs} +23 -4
  36. package/dist/{chunk-OIGEBCAS.mjs → chunk-NADODEZX.mjs} +69 -1
  37. package/dist/{chunk-UKY2EPS3.mjs → chunk-NJWTZ5MO.mjs} +54 -42
  38. package/dist/{chunk-ASLRAZZN.mjs → chunk-NXJMVH26.mjs} +5 -2
  39. package/dist/{chunk-773LH7BK.mjs → chunk-O6VYD4E4.mjs} +24 -2
  40. package/dist/{chunk-G7D4QV7A.mjs → chunk-O7TV5OKS.mjs} +33 -6
  41. package/dist/{chunk-DPWRXXXW.mjs → chunk-OJ6SHHOZ.mjs} +3 -3
  42. package/dist/{chunk-UYJJLHVA.mjs → chunk-PMQJ6AYT.mjs} +23 -21
  43. package/dist/{chunk-KC55B62J.mjs → chunk-PY2VUNEJ.mjs} +143 -46
  44. package/dist/{chunk-BEJZPJIK.mjs → chunk-QOAOOD53.mjs} +10 -4
  45. package/dist/{chunk-JQJM65RJ.mjs → chunk-QU3QXUTC.mjs} +49 -23
  46. package/dist/{chunk-RQ4PUK6G.mjs → chunk-R7YZ6FKP.mjs} +24 -13
  47. package/dist/{chunk-YI6YWH66.mjs → chunk-RE5ZLA5D.mjs} +6 -6
  48. package/dist/{chunk-UCBW5RJN.mjs → chunk-RGVU4EMA.mjs} +11 -6
  49. package/dist/{chunk-IBSOUQGB.mjs → chunk-RRGPDFDI.mjs} +7 -4
  50. package/dist/{chunk-ZH5ZBLAN.mjs → chunk-RUQ3F3MV.mjs} +34 -7
  51. package/dist/{chunk-KVCI2346.mjs → chunk-S2V3HGLZ.mjs} +8 -7
  52. package/dist/{chunk-PGD5RPKF.mjs → chunk-SVIJ2UZA.mjs} +2 -1
  53. package/dist/{chunk-UETARXIK.mjs → chunk-SXWEFZOF.mjs} +246 -53
  54. package/dist/{chunk-H6MVW5YD.mjs → chunk-TEVEXDY2.mjs} +24 -13
  55. package/dist/{chunk-DJOFM2VO.mjs → chunk-TO6MZFNQ.mjs} +52 -40
  56. package/dist/{chunk-ZCGIZOMT.mjs → chunk-V7HJ5HLC.mjs} +6 -10
  57. package/dist/{chunk-SIDDYP3O.mjs → chunk-VJWSKJLJ.mjs} +1 -1
  58. package/dist/{chunk-ZG56T75O.mjs → chunk-WTHWLCRL.mjs} +51 -11
  59. package/dist/{chunk-KP6DEYWB.mjs → chunk-X4L3W7RQ.mjs} +1 -1
  60. package/dist/{chunk-ZMRTETN2.mjs → chunk-X6C6MNG5.mjs} +4 -3
  61. package/dist/{chunk-OGG2TQDG.mjs → chunk-XVHTJQ4T.mjs} +12 -1
  62. package/dist/{chunk-FSMJLJFL.mjs → chunk-Y6UH3EKE.mjs} +1 -1
  63. package/dist/{chunk-7EMCZVJ2.mjs → chunk-YH7FKFRM.mjs} +4 -4
  64. package/dist/chunk-YJ5T4XZN.mjs +81 -0
  65. package/dist/{chunk-VVNFXJMN.mjs → chunk-YNDPQVEK.mjs} +8 -1
  66. package/dist/{chunk-QGZ32NFQ.mjs → chunk-Z23KOLNU.mjs} +4 -3
  67. package/dist/{chunk-5CKMYMTS.mjs → chunk-ZATZRXMW.mjs} +27 -8
  68. package/dist/components/ActivityFeed.mjs +2 -2
  69. package/dist/components/Affix.mjs +1 -1
  70. package/dist/components/Anchor.mjs +1 -1
  71. package/dist/components/AreaChart.mjs +4 -3
  72. package/dist/components/AutoComplete.mjs +1 -1
  73. package/dist/components/BarChart.mjs +4 -3
  74. package/dist/components/Calendar.mjs +1 -1
  75. package/dist/components/Cascader.mjs +1 -1
  76. package/dist/components/ChartCanvas.mjs +1 -1
  77. package/dist/components/ChatWindow.mjs +3 -3
  78. package/dist/components/Code.mjs +3 -1
  79. package/dist/components/CollapsePanel.mjs +1 -1
  80. package/dist/components/ColorPicker.d.mts +3 -1
  81. package/dist/components/ColorPicker.mjs +3 -1
  82. package/dist/components/CommentThread.mjs +2 -2
  83. package/dist/components/DataExport.mjs +2 -2
  84. package/dist/components/DataTableWithToolbar.mjs +7 -7
  85. package/dist/components/DonutChart.mjs +4 -4
  86. package/dist/components/Dropdown.mjs +1 -1
  87. package/dist/components/FileManager.mjs +2 -1
  88. package/dist/components/FloatButton.mjs +1 -1
  89. package/dist/components/FormItem.mjs +1 -1
  90. package/dist/components/FormWizard.mjs +1 -1
  91. package/dist/components/FunnelChart.mjs +3 -3
  92. package/dist/components/Gantt.d.mts +3 -1
  93. package/dist/components/Gantt.mjs +2 -2
  94. package/dist/components/GaugeChart.mjs +2 -2
  95. package/dist/components/HeatmapChart.mjs +3 -3
  96. package/dist/components/Image.mjs +4 -4
  97. package/dist/components/ImageGroup.mjs +2 -2
  98. package/dist/components/ImagePreview.mjs +1 -1
  99. package/dist/components/Input.mjs +1 -1
  100. package/dist/components/InputNumber.d.mts +1 -1
  101. package/dist/components/InputNumber.mjs +1 -1
  102. package/dist/components/Kanban.mjs +2 -2
  103. package/dist/components/LineChart.mjs +4 -3
  104. package/dist/components/List.mjs +2 -2
  105. package/dist/components/Loading.mjs +1 -1
  106. package/dist/components/MarkdownEditor.mjs +1 -1
  107. package/dist/components/MaskInput.mjs +1 -1
  108. package/dist/components/NotificationCenter.mjs +4 -4
  109. package/dist/components/OrgChart.mjs +2 -2
  110. package/dist/components/Pagination.mjs +1 -1
  111. package/dist/components/PieChart.mjs +3 -3
  112. package/dist/components/Popconfirm.mjs +2 -2
  113. package/dist/components/Popover.mjs +2 -2
  114. package/dist/components/QRCode.d.mts +2 -2
  115. package/dist/components/QRCode.mjs +1 -1
  116. package/dist/components/RadarChart.mjs +3 -3
  117. package/dist/components/Rate.mjs +1 -1
  118. package/dist/components/RichTextEditor.mjs +1 -1
  119. package/dist/components/ScatterChart.mjs +4 -3
  120. package/dist/components/ScrollSpy.mjs +1 -1
  121. package/dist/components/Select.mjs +1 -1
  122. package/dist/components/Signature.mjs +1 -1
  123. package/dist/components/SplitButton.mjs +2 -2
  124. package/dist/components/Splitter.mjs +1 -1
  125. package/dist/components/Stepper.d.mts +2 -0
  126. package/dist/components/Stepper.mjs +2 -1
  127. package/dist/components/SunburstChart.mjs +3 -3
  128. package/dist/components/Switch.d.mts +5 -1
  129. package/dist/components/Switch.mjs +2 -1
  130. package/dist/components/Table.mjs +3 -3
  131. package/dist/components/TaskBoard.mjs +1 -1
  132. package/dist/components/Textarea.mjs +1 -1
  133. package/dist/components/Tooltip.mjs +2 -2
  134. package/dist/components/Tour.mjs +1 -1
  135. package/dist/components/TreeMapChart.mjs +3 -3
  136. package/dist/components/TreeSelect.mjs +1 -1
  137. package/dist/components/Upload.mjs +1 -1
  138. package/dist/hooks/useChartInteraction.d.mts +5 -0
  139. package/dist/hooks/useChartInteraction.mjs +1 -1
  140. package/dist/index.d.mts +1 -1
  141. package/dist/index.mjs +57 -0
  142. package/package.json +2 -2
  143. package/dist/chunk-CB4SK5AU.mjs +0 -235
  144. package/dist/chunk-KPACLRQH.mjs +0 -55
@@ -33,7 +33,7 @@ import {
33
33
  getPictureCardClasses,
34
34
  getUploadStatusIconClasses
35
35
  } from "@expcat/tigercat-core";
36
- import { jsx, jsxs } from "react/jsx-runtime";
36
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
37
37
  var spinnerSvg = getSpinnerSVG("spinner");
38
38
  var Upload = ({
39
39
  accept,
@@ -133,6 +133,11 @@ var Upload = ({
133
133
  onExceed?.(prepared.rejectedExceedFiles, fileList);
134
134
  }
135
135
  let nextFileList = [...fileList];
136
+ const notifyFileList = (file) => {
137
+ const nextList = [...nextFileList];
138
+ updateFileList(nextList);
139
+ onChange?.(file, nextList);
140
+ };
136
141
  for (const file of prepared.acceptedFiles) {
137
142
  const uploadFile = fileToUploadFile(file);
138
143
  nextFileList = [...nextFileList, uploadFile];
@@ -141,7 +146,7 @@ var Upload = ({
141
146
  onChange(uploadFile, nextFileList);
142
147
  }
143
148
  if (autoUpload && !queue) {
144
- await uploadOne(file, uploadFile, () => updateFileList([...nextFileList]));
149
+ await uploadOne(file, uploadFile, () => notifyFileList(uploadFile));
145
150
  }
146
151
  }
147
152
  if (autoUpload && queue) {
@@ -154,7 +159,7 @@ var Upload = ({
154
159
  async (item) => {
155
160
  const uploadFile = nextFileList.find((candidate) => candidate.uid === item.id);
156
161
  if (!uploadFile) return;
157
- await uploadOne(item.file, uploadFile, () => updateFileList([...nextFileList]));
162
+ await uploadOne(item.file, uploadFile, () => notifyFileList(uploadFile));
158
163
  },
159
164
  { concurrency: maxConcurrent, onChange: onQueueChange }
160
165
  );
@@ -266,7 +271,7 @@ var Upload = ({
266
271
  };
267
272
  const renderUploadButton = () => {
268
273
  if (drag) {
269
- return /* @__PURE__ */ jsxs(
274
+ return /* @__PURE__ */ jsx(
270
275
  "div",
271
276
  {
272
277
  className: getDragAreaClasses(isDragging, disabled),
@@ -279,7 +284,7 @@ var Upload = ({
279
284
  tabIndex: disabled ? -1 : 0,
280
285
  "aria-disabled": disabled,
281
286
  "aria-label": labels.dragAreaAriaLabel,
282
- children: [
287
+ children: children || /* @__PURE__ */ jsxs(Fragment, { children: [
283
288
  /* @__PURE__ */ jsx(
284
289
  "svg",
285
290
  {
@@ -308,7 +313,7 @@ var Upload = ({
308
313
  maxSize && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500", children: interpolateUploadLabel(labels.maxSizeInfoText, {
309
314
  maxSize: formatFileSize(maxSize)
310
315
  }) })
311
- ]
316
+ ] })
312
317
  }
313
318
  );
314
319
  }
@@ -155,14 +155,17 @@ var FormWizard = ({
155
155
  ]);
156
156
  const handleStepChange = useCallback(
157
157
  async (nextIndex) => {
158
- if (nextIndex === currentIndex || steps[nextIndex]?.disabled) return;
159
- if (nextIndex > currentIndex) {
158
+ if (nextIndex === currentIndex) return;
159
+ const direction2 = nextIndex > currentIndex ? 1 : -1;
160
+ if (direction2 === 1) {
160
161
  const ok = await runBeforeNext();
161
162
  if (!ok) return;
162
163
  }
163
- setCurrent(nextIndex);
164
+ const target = findNextUnskipped(nextIndex, direction2);
165
+ if (target === currentIndex) return;
166
+ setCurrent(target);
164
167
  },
165
- [currentIndex, runBeforeNext, setCurrent, steps]
168
+ [currentIndex, runBeforeNext, setCurrent, findNextUnskipped]
166
169
  );
167
170
  const contentNode = useMemo(() => {
168
171
  if (!currentStep) return null;
@@ -21,6 +21,25 @@ import {
21
21
  getFormItemAsteriskClasses
22
22
  } from "@expcat/tigercat-core";
23
23
  import { jsx, jsxs } from "react/jsx-runtime";
24
+ function isEventTarget(value) {
25
+ return value !== null && typeof value === "object";
26
+ }
27
+ function extractFormChangeValue(argument) {
28
+ if (argument === void 0) {
29
+ return { found: false };
30
+ }
31
+ if (argument !== null && typeof argument === "object" && "target" in argument) {
32
+ const target = argument.target;
33
+ if (isEventTarget(target)) {
34
+ const type = typeof target.type === "string" ? target.type.toLowerCase() : "";
35
+ if (type === "checkbox" || type === "radio") {
36
+ return { found: true, value: target.checked };
37
+ }
38
+ return { found: true, value: target.value };
39
+ }
40
+ }
41
+ return { found: true, value: argument };
42
+ }
24
43
  function hasRequiredRule(maybeRules) {
25
44
  if (!maybeRules) return false;
26
45
  const ruleArr = Array.isArray(maybeRules) ? maybeRules : [maybeRules];
@@ -129,11 +148,19 @@ var FormItem = ({
129
148
  formContext.validateField(name, rules, "blur");
130
149
  }
131
150
  }, [name, formContext, rules]);
132
- const handleChange = useCallback(() => {
133
- if (name && formContext) {
151
+ const handleChange = useCallback(
152
+ (argument) => {
153
+ if (!name || !formContext) {
154
+ return;
155
+ }
156
+ const extracted = extractFormChangeValue(argument);
157
+ if (extracted.found) {
158
+ formContext.updateValue(name, extracted.value);
159
+ }
134
160
  formContext.validateField(name, rules, "change");
135
- }
136
- }, [name, formContext, rules]);
161
+ },
162
+ [name, formContext, rules]
163
+ );
137
164
  const hasError = !!errorMessage;
138
165
  const popupErrorVisible = showMessage && hasError && errorDisplayMode === "popup";
139
166
  const overlay = useAnchoredOverlay({
@@ -172,9 +199,9 @@ var FormItem = ({
172
199
  onlyChild.props.onBlur?.(event);
173
200
  handleBlur();
174
201
  },
175
- onChange: (event) => {
176
- onlyChild.props.onChange?.(event);
177
- handleChange();
202
+ onChange: (...args) => {
203
+ onlyChild.props.onChange?.(...args);
204
+ handleChange(args[0]);
178
205
  }
179
206
  };
180
207
  if (!isNativeElement) {
@@ -63,7 +63,7 @@ function useSelectState(props) {
63
63
  options = [],
64
64
  size = "md",
65
65
  disabled = false,
66
- placeholder = "Select an option",
66
+ placeholder,
67
67
  searchable = false,
68
68
  searchValue,
69
69
  defaultSearchValue = "",
@@ -95,6 +95,7 @@ function useSelectState(props) {
95
95
  () => getSelectLabels(mergedLocale, labelsOverride),
96
96
  [mergedLocale, labelsOverride]
97
97
  );
98
+ const resolvedPlaceholder = resolveLocaleText(labels.placeholder, placeholder);
98
99
  const divProps = {};
99
100
  for (const [k, v] of Object.entries(props)) {
100
101
  if (!SELECT_KEYS.has(k)) divProps[k] = v;
@@ -132,7 +133,7 @@ function useSelectState(props) {
132
133
  const displayText = useMemo(() => {
133
134
  if (isMultiple) {
134
135
  const values = Array.isArray(value) ? value : [];
135
- if (values.length === 0) return placeholder;
136
+ if (values.length === 0) return resolvedPlaceholder;
136
137
  const labels2 = [...allOptions, ...createdOptions].filter((opt) => values.includes(opt.value)).map((opt) => opt.label);
137
138
  if (maxTagCount !== void 0 && labels2.length > maxTagCount) {
138
139
  const visible = labels2.slice(0, maxTagCount);
@@ -140,9 +141,9 @@ function useSelectState(props) {
140
141
  }
141
142
  return labels2.join(", ");
142
143
  }
143
- if (value === void 0 || value === null || value === "") return placeholder;
144
- return [...allOptions, ...createdOptions].find((opt) => opt.value === value)?.label ?? placeholder;
145
- }, [isMultiple, value, allOptions, createdOptions, placeholder, maxTagCount]);
144
+ if (value === void 0 || value === null || value === "") return resolvedPlaceholder;
145
+ return [...allOptions, ...createdOptions].find((opt) => opt.value === value)?.label ?? resolvedPlaceholder;
146
+ }, [isMultiple, value, allOptions, createdOptions, resolvedPlaceholder, maxTagCount]);
146
147
  const showClearButton = useMemo(
147
148
  () => clearable && !disabled && value !== void 0 && value !== null && value !== "" && (!Array.isArray(value) || value.length > 0),
148
149
  [clearable, disabled, value]
@@ -434,10 +435,10 @@ function useSelectState(props) {
434
435
  virtual,
435
436
  listHeight,
436
437
  disabled,
437
- placeholder,
438
+ placeholder: resolvedPlaceholder,
438
439
  searchable,
439
440
  clearable,
440
- emptyText: resolveLocaleText("No options found", emptyText, mergedLocale?.common?.emptyText),
441
+ emptyText: resolveLocaleText(labels.emptyText, emptyText),
441
442
  createOptionText,
442
443
  className,
443
444
  divProps,
@@ -7,6 +7,7 @@ import { useState, useMemo, useCallback } from "react";
7
7
  import {
8
8
  rateBaseClasses,
9
9
  getRateStarClasses,
10
+ rateHalfStarInnerClasses,
10
11
  rateActiveColor,
11
12
  rateInactiveColor,
12
13
  rateHoverColor,
@@ -126,7 +127,7 @@ var Rate = ({
126
127
  isHovering ? rateHoverColor : rateActiveColor
127
128
  ),
128
129
  style: { width: "50%" },
129
- children: isChar ? /* @__PURE__ */ jsx("span", { children: character }) : starIcon
130
+ children: isChar ? /* @__PURE__ */ jsx("span", { className: rateHalfStarInnerClasses, children: character }) : /* @__PURE__ */ jsx("svg", { viewBox: starViewBox, fill: "currentColor", className: rateHalfStarInnerClasses, children: /* @__PURE__ */ jsx("path", { d: starPathD }) })
130
131
  }
131
132
  )
132
133
  ] }) : /* @__PURE__ */ jsx("span", { className: colorClass, children: isChar ? /* @__PURE__ */ jsx("span", { children: character }) : starIcon });
@@ -32,6 +32,8 @@ import {
32
32
  getPickerOptionAria,
33
33
  getPickerTriggerKeyAction,
34
34
  getPickerNavigationIndex,
35
+ getPickerOptionId,
36
+ findFirstEnabledIndex,
35
37
  icon20ViewBox,
36
38
  chevronDownSolidIcon20PathD,
37
39
  closeSolidIcon20PathD,
@@ -178,6 +180,7 @@ var Cascader = (props) => {
178
180
  const [activePath, setActivePath] = useState([]);
179
181
  const [columnActiveIndices, setColumnActiveIndices] = useState([]);
180
182
  const [searchActiveIndex, setSearchActiveIndex] = useState(-1);
183
+ const [focusedColumnIndex, setFocusedColumnIndex] = useState(0);
181
184
  const [indexedSearchQuery, setIndexedSearchQuery] = useState(searchQuery);
182
185
  if (indexedSearchQuery !== searchQuery) {
183
186
  setIndexedSearchQuery(searchQuery);
@@ -231,6 +234,7 @@ var Cascader = (props) => {
231
234
  wasOpenRef.current = false;
232
235
  setColumnActiveIndices([]);
233
236
  setSearchActiveIndex(-1);
237
+ setFocusedColumnIndex(0);
234
238
  return;
235
239
  }
236
240
  if (wasOpenRef.current) return;
@@ -239,16 +243,21 @@ var Cascader = (props) => {
239
243
  updateSearchValue("");
240
244
  }, [isOpen, updateSearchValue, value]);
241
245
  useEffect(() => {
242
- if (!virtual || !isOpen) return;
246
+ if (!isOpen) return;
243
247
  setColumnActiveIndices(
244
248
  (prev) => columns.map((col, i) => {
245
- if (prev[i] !== void 0 && prev[i] >= 0 && prev[i] < col.options.length) {
249
+ if (prev[i] !== void 0 && prev[i] >= 0 && prev[i] < col.options.length && !col.options[prev[i]]?.disabled) {
246
250
  return prev[i];
247
251
  }
248
252
  return selectedIndexInColumn(col.options, col.selectedValue);
249
253
  })
250
254
  );
251
- }, [virtual, isOpen, columns]);
255
+ setFocusedColumnIndex((prev) => {
256
+ const last = columns.length - 1;
257
+ if (last < 0) return 0;
258
+ return prev > last ? last : prev;
259
+ });
260
+ }, [isOpen, columns]);
252
261
  const toggleOpen = useCallback(() => {
253
262
  if (disabled) return;
254
263
  setIsOpen((prev) => !prev);
@@ -266,6 +275,7 @@ var Cascader = (props) => {
266
275
  next[level] = optionIndex;
267
276
  return next;
268
277
  });
278
+ setFocusedColumnIndex(level);
269
279
  }
270
280
  const hasChildren = isCascaderOptionExpandable(option);
271
281
  if (!hasChildren) {
@@ -286,6 +296,7 @@ var Cascader = (props) => {
286
296
  next[level] = optionIndex;
287
297
  return next;
288
298
  });
299
+ setFocusedColumnIndex(level);
289
300
  }
290
301
  if (expandTrigger !== "hover" || option.disabled) return;
291
302
  const newPath = activePath.slice(0, level);
@@ -309,8 +320,142 @@ var Cascader = (props) => {
309
320
  },
310
321
  [onChange]
311
322
  );
323
+ const isOptionDisabled = useCallback((option) => !!option.disabled, []);
324
+ const getColumnOptionDomId = useCallback(
325
+ (colIndex, optionIndex) => `${listboxId}-col${colIndex}-opt${optionIndex}`,
326
+ [listboxId]
327
+ );
328
+ const getCurrentColumnIndex = useCallback(() => {
329
+ const last = columns.length - 1;
330
+ if (last < 0) return 0;
331
+ if (focusedColumnIndex >= 0 && focusedColumnIndex <= last) return focusedColumnIndex;
332
+ return last;
333
+ }, [columns.length, focusedColumnIndex]);
334
+ const commitActiveOption = useCallback(() => {
335
+ if (isSearchMode) {
336
+ const item = searchResults[searchActiveIndex];
337
+ if (!item || item.disabled) return;
338
+ handleSearchResultClick(item.valuePath, item.disabled);
339
+ return;
340
+ }
341
+ const colIndex = getCurrentColumnIndex();
342
+ const col = columns[colIndex];
343
+ const idx = columnActiveIndices[colIndex] ?? -1;
344
+ const option = col?.options[idx];
345
+ if (!option || option.disabled) return;
346
+ handleOptionClick(option, colIndex);
347
+ if (isCascaderOptionExpandable(option)) {
348
+ const nextCol = colIndex + 1;
349
+ const childOptions = option.children ?? [];
350
+ const nextIdx = findFirstEnabledIndex(childOptions, isOptionDisabled);
351
+ setColumnActiveIndices((prev) => {
352
+ const next = prev.slice();
353
+ next[colIndex] = idx;
354
+ next[nextCol] = nextIdx;
355
+ return next;
356
+ });
357
+ setFocusedColumnIndex(nextCol);
358
+ }
359
+ }, [
360
+ isSearchMode,
361
+ searchResults,
362
+ searchActiveIndex,
363
+ handleSearchResultClick,
364
+ getCurrentColumnIndex,
365
+ columns,
366
+ columnActiveIndices,
367
+ handleOptionClick,
368
+ isOptionDisabled
369
+ ]);
370
+ const handleOpenListKeyDown = useCallback(
371
+ (e, fromSearchInput = false) => {
372
+ const key = e.key;
373
+ if (fromSearchInput && key === " ") return false;
374
+ if (isSearchMode) {
375
+ if (key === "ArrowDown" || key === "ArrowUp" || key === "Home" || key === "End") {
376
+ e.preventDefault();
377
+ e.stopPropagation();
378
+ setSearchActiveIndex(
379
+ (current) => getPickerNavigationIndex(searchResults, current, key, (item) => !!item.disabled)
380
+ );
381
+ return true;
382
+ }
383
+ if (key === "Enter" || key === " ") {
384
+ e.preventDefault();
385
+ e.stopPropagation();
386
+ commitActiveOption();
387
+ return true;
388
+ }
389
+ return false;
390
+ }
391
+ const colIndex = getCurrentColumnIndex();
392
+ const col = columns[colIndex];
393
+ if (!col) return false;
394
+ if (key === "ArrowDown" || key === "ArrowUp" || key === "Home" || key === "End") {
395
+ e.preventDefault();
396
+ e.stopPropagation();
397
+ const current = columnActiveIndices[colIndex] ?? -1;
398
+ const next = getPickerNavigationIndex(col.options, current, key, isOptionDisabled);
399
+ setColumnActiveIndices((prev) => {
400
+ const nextIndices = prev.slice();
401
+ nextIndices[colIndex] = next;
402
+ return nextIndices;
403
+ });
404
+ setFocusedColumnIndex(colIndex);
405
+ return true;
406
+ }
407
+ if (key === "ArrowRight") {
408
+ e.preventDefault();
409
+ e.stopPropagation();
410
+ const idx = columnActiveIndices[colIndex] ?? -1;
411
+ const option = col.options[idx];
412
+ if (option && !option.disabled && isCascaderOptionExpandable(option)) {
413
+ handleOptionClick(option, colIndex);
414
+ const childOptions = option.children ?? [];
415
+ const nextIdx = findFirstEnabledIndex(childOptions, isOptionDisabled);
416
+ setColumnActiveIndices((prev) => {
417
+ const next = prev.slice();
418
+ next[colIndex] = idx;
419
+ next[colIndex + 1] = nextIdx;
420
+ return next;
421
+ });
422
+ setFocusedColumnIndex(colIndex + 1);
423
+ }
424
+ return true;
425
+ }
426
+ if (key === "ArrowLeft") {
427
+ e.preventDefault();
428
+ e.stopPropagation();
429
+ if (activePath.length > 0) {
430
+ setActivePath(activePath.slice(0, -1));
431
+ setFocusedColumnIndex(Math.max(0, colIndex - 1));
432
+ }
433
+ return true;
434
+ }
435
+ if (key === "Enter" || key === " ") {
436
+ e.preventDefault();
437
+ e.stopPropagation();
438
+ commitActiveOption();
439
+ return true;
440
+ }
441
+ return false;
442
+ },
443
+ [
444
+ isSearchMode,
445
+ searchResults,
446
+ commitActiveOption,
447
+ getCurrentColumnIndex,
448
+ columns,
449
+ columnActiveIndices,
450
+ isOptionDisabled,
451
+ handleOptionClick,
452
+ activePath
453
+ ]
454
+ );
312
455
  const handleTriggerKeyDown = useCallback(
313
456
  (e) => {
457
+ if (disabled) return;
458
+ if (isOpen && handleOpenListKeyDown(e)) return;
314
459
  const action = getPickerTriggerKeyAction(e.key, isOpen);
315
460
  if (action === "none") return;
316
461
  e.preventDefault();
@@ -322,69 +467,99 @@ var Cascader = (props) => {
322
467
  setIsOpen(false);
323
468
  }
324
469
  },
325
- [toggleOpen, isOpen]
470
+ [disabled, isOpen, handleOpenListKeyDown, toggleOpen]
471
+ );
472
+ const handleDropdownKeyDown = useCallback(
473
+ (e) => {
474
+ if (e.target instanceof HTMLInputElement) return;
475
+ if (!isOpen) return;
476
+ handleOpenListKeyDown(e);
477
+ },
478
+ [isOpen, handleOpenListKeyDown]
479
+ );
480
+ const handleSearchKeyDown = useCallback(
481
+ (e) => {
482
+ switch (e.key) {
483
+ case " ":
484
+ e.stopPropagation();
485
+ return;
486
+ case "ArrowDown":
487
+ case "ArrowUp":
488
+ case "Enter":
489
+ handleOpenListKeyDown(e, true);
490
+ return;
491
+ case "Escape":
492
+ e.preventDefault();
493
+ e.stopPropagation();
494
+ setIsOpen(false);
495
+ triggerRef.current?.focus();
496
+ return;
497
+ default:
498
+ return;
499
+ }
500
+ },
501
+ [handleOpenListKeyDown]
326
502
  );
327
503
  const hasValue = value && value.length > 0;
328
504
  const showClearBtn = clearable && hasValue && !disabled;
505
+ const currentCol = getCurrentColumnIndex();
506
+ const currentOpt = columnActiveIndices[currentCol] ?? -1;
507
+ const activeOptionId = !isOpen ? void 0 : isSearchMode ? searchActiveIndex >= 0 ? getPickerOptionId(listboxId, searchActiveIndex) : void 0 : currentOpt >= 0 ? getColumnOptionDomId(currentCol, currentOpt) : void 0;
329
508
  return /* @__PURE__ */ jsxs("div", { className: cascaderBaseClasses, "data-testid": "cascader", ...divProps, children: [
330
- /* @__PURE__ */ jsxs(
509
+ /* @__PURE__ */ jsx(
331
510
  "button",
332
511
  {
333
512
  ref: triggerRef,
334
513
  type: "button",
335
- className: triggerClasses,
514
+ className: classNames(triggerClasses, showClearBtn ? "pr-14" : "pr-9"),
336
515
  disabled,
337
- ...getPickerComboboxAria({ expanded: isOpen, listboxId }),
516
+ ...getPickerComboboxAria({ expanded: isOpen, listboxId, activeOptionId }),
338
517
  onClick: toggleOpen,
339
518
  onKeyDown: handleTriggerKeyDown,
340
- children: [
341
- /* @__PURE__ */ jsx(
342
- "span",
343
- {
344
- className: classNames(
345
- "flex-1 text-left truncate",
346
- !hasValue && "text-[var(--tiger-cascader-placeholder,var(--tiger-text-muted,#9ca3af))]"
347
- ),
348
- children: hasValue ? displayLabel : placeholder
349
- }
350
- ),
351
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
352
- showClearBtn && /* @__PURE__ */ jsx(
353
- "span",
354
- {
355
- className: "flex items-center",
356
- role: "button",
357
- "aria-label": resolveLocaleText("Clear selection", mergedLocale?.common?.clearText),
358
- onClick: handleClear,
359
- children: /* @__PURE__ */ jsx(
360
- "svg",
361
- {
362
- className: "w-4 h-4 text-[var(--tiger-cascader-icon,var(--tiger-text-muted,#9ca3af))] hover:text-[var(--tiger-cascader-icon-hover,var(--tiger-text-muted,#6b7280))]",
363
- xmlns: "http://www.w3.org/2000/svg",
364
- viewBox: icon20ViewBox,
365
- fill: "currentColor",
366
- children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: closeSolidIcon20PathD, clipRule: "evenodd" })
367
- }
368
- )
369
- }
519
+ children: /* @__PURE__ */ jsx(
520
+ "span",
521
+ {
522
+ className: classNames(
523
+ "flex-1 text-left truncate",
524
+ !hasValue && "text-[var(--tiger-cascader-placeholder,var(--tiger-text-muted,#9ca3af))]"
370
525
  ),
371
- /* @__PURE__ */ jsx(
372
- "svg",
373
- {
374
- className: classNames(
375
- "w-5 h-5 text-[var(--tiger-cascader-icon,var(--tiger-text-muted,#9ca3af))] transition-transform",
376
- isOpen && "rotate-180"
377
- ),
378
- xmlns: "http://www.w3.org/2000/svg",
379
- viewBox: icon20ViewBox,
380
- fill: "currentColor",
381
- children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: chevronDownSolidIcon20PathD, clipRule: "evenodd" })
382
- }
383
- )
384
- ] })
385
- ]
526
+ children: hasValue ? displayLabel : placeholder
527
+ }
528
+ )
386
529
  }
387
530
  ),
531
+ /* @__PURE__ */ jsxs("span", { className: "pointer-events-none absolute inset-y-0 right-3 flex items-center gap-1", children: [
532
+ showClearBtn && /* @__PURE__ */ jsx(
533
+ "button",
534
+ {
535
+ type: "button",
536
+ className: "pointer-events-auto inline-flex rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--tiger-cascader-ring,var(--tiger-primary,#2563eb))]",
537
+ "data-tiger-cascader-clear": "",
538
+ "aria-label": resolveLocaleText("Clear selection", mergedLocale?.common?.clearText),
539
+ onClick: handleClear,
540
+ children: /* @__PURE__ */ jsx(
541
+ "svg",
542
+ {
543
+ className: "w-4 h-4 text-[var(--tiger-cascader-icon,var(--tiger-text-muted,#9ca3af))] hover:text-[var(--tiger-cascader-icon-hover,var(--tiger-text-muted,#6b7280))]",
544
+ xmlns: "http://www.w3.org/2000/svg",
545
+ viewBox: icon20ViewBox,
546
+ fill: "currentColor",
547
+ children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: closeSolidIcon20PathD, clipRule: "evenodd" })
548
+ }
549
+ )
550
+ }
551
+ ),
552
+ /* @__PURE__ */ jsx("span", { className: classNames("inline-flex", isOpen && "rotate-180"), "aria-hidden": "true", children: /* @__PURE__ */ jsx(
553
+ "svg",
554
+ {
555
+ className: "w-5 h-5 text-[var(--tiger-cascader-icon,var(--tiger-text-muted,#9ca3af))] transition-transform",
556
+ xmlns: "http://www.w3.org/2000/svg",
557
+ viewBox: icon20ViewBox,
558
+ fill: "currentColor",
559
+ children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: chevronDownSolidIcon20PathD, clipRule: "evenodd" })
560
+ }
561
+ ) })
562
+ ] }),
388
563
  renderOverlayPortal(
389
564
  isOpen ? /* @__PURE__ */ jsxs(
390
565
  "div",
@@ -397,6 +572,7 @@ var Cascader = (props) => {
397
572
  ),
398
573
  style: overlay.floatingStyles,
399
574
  "data-positioned": overlay.positioned,
575
+ onKeyDown: handleDropdownKeyDown,
400
576
  children: [
401
577
  searchable && /* @__PURE__ */ jsx(
402
578
  "input",
@@ -409,6 +585,7 @@ var Cascader = (props) => {
409
585
  ),
410
586
  value: searchQuery,
411
587
  onChange: (e) => updateSearchValue(e.target.value),
588
+ onKeyDown: handleSearchKeyDown,
412
589
  "aria-label": resolveLocaleText(
413
590
  "Search options",
414
591
  mergedLocale?.common?.searchPlaceholder
@@ -433,9 +610,12 @@ var Cascader = (props) => {
433
610
  renderItem: (item, index) => /* @__PURE__ */ jsx(
434
611
  "div",
435
612
  {
613
+ id: getPickerOptionId(listboxId, index),
436
614
  "data-option-index": index,
615
+ "data-active": searchActiveIndex === index || void 0,
437
616
  className: classNames(
438
617
  cascaderSearchResultClasses,
618
+ searchActiveIndex === index && "bg-[var(--tiger-cascader-option-bg-selected,var(--tiger-outline-bg-hover,#eff6ff))]",
439
619
  item.disabled && "opacity-50 cursor-not-allowed"
440
620
  ),
441
621
  ...getPickerOptionAria({
@@ -460,15 +640,21 @@ var Cascader = (props) => {
460
640
  children: searchResults.map((item, index) => /* @__PURE__ */ jsx(
461
641
  "div",
462
642
  {
643
+ id: getPickerOptionId(listboxId, index),
463
644
  "data-option-index": index,
645
+ "data-active": searchActiveIndex === index || void 0,
464
646
  className: classNames(
465
647
  cascaderSearchResultClasses,
648
+ searchActiveIndex === index && "bg-[var(--tiger-cascader-option-bg-selected,var(--tiger-outline-bg-hover,#eff6ff))]",
466
649
  item.disabled && "opacity-50 cursor-not-allowed"
467
650
  ),
468
651
  ...getPickerOptionAria({
469
652
  selected: value?.join(",") === item.valuePath.join(","),
470
653
  disabled: item.disabled
471
654
  }),
655
+ onMouseEnter: () => {
656
+ if (!item.disabled) setSearchActiveIndex(index);
657
+ },
472
658
  onClick: () => handleSearchResultClick(item.valuePath, item.disabled),
473
659
  children: typeof searchable === "object" && searchable.render ? searchable.render(searchQuery, item.path) : item.label
474
660
  },
@@ -485,12 +671,19 @@ var Cascader = (props) => {
485
671
  });
486
672
  const renderColumnOption = (option, optionIndex) => {
487
673
  const isSelected = col.selectedValue === option.value;
674
+ const isActive = (columnActiveIndices[colIndex] ?? -1) === optionIndex;
488
675
  const hasChildren = isCascaderOptionExpandable(option);
489
676
  return /* @__PURE__ */ jsxs(
490
677
  "div",
491
678
  {
679
+ id: getColumnOptionDomId(colIndex, optionIndex),
492
680
  "data-option-index": optionIndex,
493
- className: getCascaderOptionClasses(isSelected, !!option.disabled, size),
681
+ "data-active": isActive || void 0,
682
+ className: getCascaderOptionClasses(
683
+ isSelected || isActive,
684
+ !!option.disabled,
685
+ size
686
+ ),
494
687
  ...getPickerOptionAria({
495
688
  selected: isSelected,
496
689
  disabled: !!option.disabled