@godxjp/ui 20.0.0 → 20.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 (250) hide show
  1. package/README.md +8 -2
  2. package/dist/app/theme-axes.d.ts +2 -0
  3. package/dist/app/theme-axes.js +45 -0
  4. package/dist/components/charts/chart-cartesian.d.ts +2 -1
  5. package/dist/components/charts/chart-cartesian.js +52 -3
  6. package/dist/components/charts/chart-category-axis.d.ts +55 -0
  7. package/dist/components/charts/chart-category-axis.js +93 -0
  8. package/dist/components/charts/chart-frame.d.ts +10 -1
  9. package/dist/components/charts/chart-frame.js +19 -3
  10. package/dist/components/charts/compact-bar-trend.d.ts +1 -1
  11. package/dist/components/charts/compact-bar-trend.js +2 -0
  12. package/dist/components/charts/pie-chart.d.ts +1 -1
  13. package/dist/components/charts/pie-chart.js +12 -1
  14. package/dist/components/charts/recharts-peer.d.ts +49 -0
  15. package/dist/components/charts/recharts-peer.js +45 -0
  16. package/dist/components/data-display/card.d.ts +12 -1
  17. package/dist/components/data-display/card.js +22 -4
  18. package/dist/components/data-display/code-block.js +6 -2
  19. package/dist/components/data-display/data-table.d.ts +39 -4
  20. package/dist/components/data-display/data-table.js +802 -270
  21. package/dist/components/data-display/descriptions.d.ts +27 -6
  22. package/dist/components/data-display/descriptions.js +58 -17
  23. package/dist/components/data-display/index.d.ts +2 -0
  24. package/dist/components/data-display/index.js +2 -0
  25. package/dist/components/data-display/list-row.d.ts +10 -1
  26. package/dist/components/data-display/list-row.js +7 -1
  27. package/dist/components/data-display/popover.d.ts +2 -2
  28. package/dist/components/data-display/popover.js +61 -11
  29. package/dist/components/data-display/range-timeline.d.ts +38 -0
  30. package/dist/components/data-display/range-timeline.js +161 -0
  31. package/dist/components/data-display/scroll-area.js +13 -2
  32. package/dist/components/data-display/service-launcher-card.js +10 -10
  33. package/dist/components/data-display/table.d.ts +11 -3
  34. package/dist/components/data-display/table.js +48 -18
  35. package/dist/components/data-display/tree-list.js +4 -9
  36. package/dist/components/data-entry/calendar.d.ts +2 -2
  37. package/dist/components/data-entry/calendar.js +12 -1
  38. package/dist/components/data-entry/cascader.d.ts +1 -1
  39. package/dist/components/data-entry/cascader.js +188 -24
  40. package/dist/components/data-entry/color-picker.d.ts +1 -1
  41. package/dist/components/data-entry/color-picker.js +17 -5
  42. package/dist/components/data-entry/control-appearance.d.ts +64 -0
  43. package/dist/components/data-entry/control-appearance.js +39 -0
  44. package/dist/components/data-entry/control-surface.d.ts +61 -0
  45. package/dist/components/data-entry/control-surface.js +39 -0
  46. package/dist/components/data-entry/date-picker.d.ts +1 -1
  47. package/dist/components/data-entry/date-picker.js +344 -113
  48. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  49. package/dist/components/data-entry/date-range-picker.js +278 -140
  50. package/dist/components/data-entry/form-field.d.ts +1 -1
  51. package/dist/components/data-entry/form-field.js +39 -4
  52. package/dist/components/data-entry/form.d.ts +4 -0
  53. package/dist/components/data-entry/form.js +4 -2
  54. package/dist/components/data-entry/index.d.ts +7 -3
  55. package/dist/components/data-entry/index.js +10 -1
  56. package/dist/components/data-entry/input-otp.d.ts +1 -1
  57. package/dist/components/data-entry/input.d.ts +12 -34
  58. package/dist/components/data-entry/input.js +92 -24
  59. package/dist/components/data-entry/month-picker.d.ts +1 -1
  60. package/dist/components/data-entry/month-picker.js +47 -10
  61. package/dist/components/data-entry/month-range-picker.d.ts +1 -1
  62. package/dist/components/data-entry/month-range-picker.js +51 -11
  63. package/dist/components/data-entry/number-input.d.ts +7 -0
  64. package/dist/components/data-entry/number-input.js +147 -98
  65. package/dist/components/data-entry/password-input.d.ts +1 -1
  66. package/dist/components/data-entry/radio.d.ts +1 -1
  67. package/dist/components/data-entry/radio.js +61 -13
  68. package/dist/components/data-entry/search-input.d.ts +1 -1
  69. package/dist/components/data-entry/search-input.js +16 -2
  70. package/dist/components/data-entry/search-select.d.ts +2 -2
  71. package/dist/components/data-entry/search-select.js +209 -78
  72. package/dist/components/data-entry/select.d.ts +22 -4
  73. package/dist/components/data-entry/select.js +215 -163
  74. package/dist/components/data-entry/slider.d.ts +9 -1
  75. package/dist/components/data-entry/slider.js +87 -9
  76. package/dist/components/data-entry/switch.d.ts +3 -0
  77. package/dist/components/data-entry/switch.js +29 -3
  78. package/dist/components/data-entry/textarea.d.ts +14 -56
  79. package/dist/components/data-entry/textarea.js +80 -33
  80. package/dist/components/data-entry/time-picker.d.ts +2 -2
  81. package/dist/components/data-entry/time-picker.js +333 -109
  82. package/dist/components/data-entry/time-range-picker.d.ts +5 -0
  83. package/dist/components/data-entry/time-range-picker.js +89 -0
  84. package/dist/components/data-entry/transfer.d.ts +1 -1
  85. package/dist/components/data-entry/transfer.js +86 -28
  86. package/dist/components/data-entry/tree-select.d.ts +1 -1
  87. package/dist/components/data-entry/tree-select.js +201 -113
  88. package/dist/components/data-entry/tree-utils.js +7 -14
  89. package/dist/components/data-entry/upload-files.d.ts +2 -0
  90. package/dist/components/data-entry/upload-files.js +31 -0
  91. package/dist/components/data-entry/upload-request.d.ts +4 -0
  92. package/dist/components/data-entry/upload-request.js +53 -0
  93. package/dist/components/data-entry/upload-types.d.ts +28 -0
  94. package/dist/components/data-entry/upload-types.js +2 -0
  95. package/dist/components/data-entry/upload.d.ts +2 -2
  96. package/dist/components/data-entry/upload.js +475 -115
  97. package/dist/components/feedback/dialog.js +16 -19
  98. package/dist/components/general/button.d.ts +1 -1
  99. package/dist/components/general/button.js +5 -1
  100. package/dist/components/general/index.d.ts +1 -0
  101. package/dist/components/general/index.js +2 -0
  102. package/dist/components/general/typography.d.ts +3 -0
  103. package/dist/components/general/typography.js +15 -1
  104. package/dist/components/general/visually-hidden.d.ts +3 -0
  105. package/dist/components/general/visually-hidden.js +9 -0
  106. package/dist/components/layout/app-launcher.d.ts +34 -0
  107. package/dist/components/layout/app-launcher.js +228 -0
  108. package/dist/components/layout/app-shell.d.ts +2 -0
  109. package/dist/components/layout/app-shell.js +20 -7
  110. package/dist/components/layout/breadcrumb.d.ts +14 -2
  111. package/dist/components/layout/breadcrumb.js +46 -4
  112. package/dist/components/layout/flex.d.ts +1 -1
  113. package/dist/components/layout/flex.js +23 -2
  114. package/dist/components/layout/index.d.ts +3 -0
  115. package/dist/components/layout/index.js +5 -1
  116. package/dist/components/layout/org-switcher.js +20 -1
  117. package/dist/components/layout/page-container.d.ts +1 -1
  118. package/dist/components/layout/page-container.js +5 -3
  119. package/dist/components/layout/responsive-grid.d.ts +16 -2
  120. package/dist/components/layout/responsive-grid.js +29 -2
  121. package/dist/components/layout/topbar-item.d.ts +3 -0
  122. package/dist/components/layout/topbar-item.js +18 -3
  123. package/dist/components/layout/topbar.d.ts +1 -1
  124. package/dist/components/layout/topbar.js +27 -6
  125. package/dist/components/navigation/dropdown-menu.d.ts +30 -2
  126. package/dist/components/navigation/dropdown-menu.js +68 -11
  127. package/dist/components/navigation/pagination.d.ts +2 -2
  128. package/dist/components/navigation/pagination.js +155 -83
  129. package/dist/components/navigation/steps.d.ts +2 -2
  130. package/dist/components/navigation/steps.js +29 -4
  131. package/dist/components/navigation/tabs.d.ts +5 -33
  132. package/dist/components/navigation/tabs.js +232 -64
  133. package/dist/components/ui/input-otp.d.ts +26 -28
  134. package/dist/components/ui/input-otp.js +50 -7
  135. package/dist/components/ui/password-input.d.ts +36 -2
  136. package/dist/components/ui/password-input.js +27 -7
  137. package/dist/components/ui/rating.d.ts +25 -0
  138. package/dist/components/ui/rating.js +67 -27
  139. package/dist/components/ui/segmented.d.ts +9 -0
  140. package/dist/components/ui/segmented.js +17 -2
  141. package/dist/components/ui/separator.d.ts +4 -0
  142. package/dist/components/ui/separator.js +13 -0
  143. package/dist/components/ui/tag-input.d.ts +45 -0
  144. package/dist/components/ui/tag-input.js +109 -27
  145. package/dist/form/form-context.d.ts +29 -0
  146. package/dist/form/form-context.js +44 -4
  147. package/dist/form/form-field-array.d.ts +22 -0
  148. package/dist/form/form-field-array.js +50 -0
  149. package/dist/form/form-field-control.d.ts +2 -1
  150. package/dist/form/form-field-control.js +116 -40
  151. package/dist/form/form-root.d.ts +2 -1
  152. package/dist/form/form-root.js +142 -23
  153. package/dist/form/index.d.ts +3 -1
  154. package/dist/form/index.js +12 -1
  155. package/dist/i18n/messages/en.json +56 -5
  156. package/dist/i18n/messages/ja.json +56 -5
  157. package/dist/i18n/messages/vi.json +56 -5
  158. package/dist/inertia/index.d.ts +20 -0
  159. package/dist/inertia/index.js +60 -1
  160. package/dist/lib/control-styles.d.ts +25 -3
  161. package/dist/lib/control-styles.js +9 -3
  162. package/dist/lib/datetime/picker-format.d.ts +8 -0
  163. package/dist/lib/datetime/picker-format.js +54 -0
  164. package/dist/props/components/charts.prop.d.ts +21 -0
  165. package/dist/props/components/data-display.prop.d.ts +55 -4
  166. package/dist/props/components/data-entry.prop.d.ts +687 -43
  167. package/dist/props/components/form.prop.d.ts +142 -4
  168. package/dist/props/components/general.prop.d.ts +17 -1
  169. package/dist/props/components/index.d.ts +2 -2
  170. package/dist/props/components/layout.prop.d.ts +163 -6
  171. package/dist/props/components/navigation.prop.d.ts +113 -3
  172. package/dist/props/registry.d.ts +376 -10
  173. package/dist/props/registry.js +493 -9
  174. package/dist/props/vocabulary/data.prop.d.ts +179 -1
  175. package/dist/props/vocabulary/index.d.ts +4 -4
  176. package/dist/props/vocabulary/interaction.prop.d.ts +51 -2
  177. package/dist/props/vocabulary/navigation.prop.d.ts +40 -0
  178. package/dist/props/vocabulary/shared.prop.d.ts +33 -0
  179. package/dist/styles/badge-layout.css +2 -1
  180. package/dist/styles/card-layout.css +26 -4
  181. package/dist/styles/chart-layout.css +15 -0
  182. package/dist/styles/control.css +515 -14
  183. package/dist/styles/data-display-layout.css +241 -6
  184. package/dist/styles/data-entry-layout.css +13 -0
  185. package/dist/styles/layout.css +221 -4
  186. package/dist/styles/navigation-layout.css +227 -0
  187. package/dist/styles/shell-layout.css +229 -5
  188. package/dist/styles/table-layout.css +95 -6
  189. package/dist/styles/text-layout.css +5 -0
  190. package/dist/tokens/base.css +1 -0
  191. package/dist/tokens/components/badge.css +1 -0
  192. package/dist/tokens/components/chart.css +6 -0
  193. package/dist/tokens/components/control.css +79 -2
  194. package/dist/tokens/components/data-display.css +9 -0
  195. package/dist/tokens/components/descriptions.css +11 -0
  196. package/dist/tokens/components/flex.css +8 -0
  197. package/dist/tokens/components/navigation.css +55 -0
  198. package/dist/tokens/components/shell.css +50 -2
  199. package/dist/tokens/components/table.css +14 -0
  200. package/dist/tokens/semantic/layout.css +9 -0
  201. package/docs/CONSUMER-RULES.md +13 -0
  202. package/docs/DESIGN-AUTHORITY.md +43 -6
  203. package/docs/DEVELOPMENT.md +4 -3
  204. package/docs/FORMS.md +151 -6
  205. package/docs/FRAME-COVERAGE-REPORT.md +29 -16
  206. package/docs/README.md +1 -1
  207. package/docs/STANDARDS-vocabulary-tokens.md +1 -1
  208. package/docs/TESTING.md +15 -6
  209. package/docs/charts/cjk-category-axis.tsx +81 -0
  210. package/docs/data-display/card/index.tsx +22 -0
  211. package/docs/data-display/data-table/examples/antd-parity.tsx +257 -0
  212. package/docs/data-display/data-table/index.tsx +23 -0
  213. package/docs/data-display/descriptions.tsx +41 -0
  214. package/docs/data-display/scroll-area.tsx +31 -25
  215. package/docs/data-display/table.tsx +104 -45
  216. package/docs/data-display/timeline.tsx +41 -0
  217. package/docs/data-entry/date-picker.tsx +85 -0
  218. package/docs/data-entry/date-range-picker.tsx +26 -0
  219. package/docs/data-entry/form-dynamic-fields.tsx +199 -0
  220. package/docs/data-entry/form.tsx +74 -4
  221. package/docs/data-entry/input-otp.tsx +24 -0
  222. package/docs/data-entry/input.tsx +46 -1
  223. package/docs/data-entry/month-range-picker.tsx +1 -1
  224. package/docs/data-entry/select.tsx +27 -0
  225. package/docs/data-entry/switch.tsx +41 -0
  226. package/docs/data-entry/textarea.tsx +38 -0
  227. package/docs/data-entry/time-picker.tsx +85 -1
  228. package/docs/data-entry/time-range-picker.tsx +55 -0
  229. package/docs/data-entry/transfer.tsx +17 -0
  230. package/docs/data-entry/upload.tsx +56 -12
  231. package/docs/feedback/tooltip.tsx +1 -1
  232. package/docs/general/activity.tsx +2 -2
  233. package/docs/general/button/index.tsx +14 -1
  234. package/docs/general/typography.tsx +14 -1
  235. package/docs/layout/app-launcher.tsx +151 -0
  236. package/docs/layout/app-shell.tsx +11 -0
  237. package/docs/layout/flex.tsx +50 -0
  238. package/docs/layout/responsive-grid.tsx +21 -1
  239. package/docs/layout/topbar.tsx +5 -9
  240. package/docs/navigation/app-setting-picker.tsx +11 -0
  241. package/docs/navigation/breadcrumb.tsx +48 -0
  242. package/docs/navigation/dropdown-menu.tsx +21 -0
  243. package/docs/navigation/pagination.tsx +52 -0
  244. package/docs/navigation/steps.tsx +37 -0
  245. package/docs/navigation/tabs.tsx +97 -1
  246. package/docs/query/button-refetch.tsx +1 -0
  247. package/package.json +19 -2
  248. package/scripts/_agent-setup.mjs +19 -2
  249. package/scripts/init-guinea-pig.mjs +26 -4
  250. package/scripts/ui-audit.mjs +243 -30
@@ -1,15 +1,22 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import { ChevronDown, ChevronRight, ChevronsUpDown, X } from "lucide-react";
4
+ import { ChevronDown, ChevronRight, ChevronsUpDown, Loader2, X } from "lucide-react";
5
5
  import { useTranslation } from "../../i18n/use-translation.js";
6
6
  import { cn } from "../../lib/utils.js";
7
7
  import { pickFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
8
- import { controlTriggerClass } from "../../lib/control-styles.js";
8
+ import { controlSurfaceTriggerClass } from "../../lib/control-styles.js";
9
+ import {
10
+ applyMaxTagCount,
11
+ controlSurfaceAttrs,
12
+ resolveAllowClear,
13
+ resolveAriaInvalid
14
+ } from "./control-surface.js";
9
15
  import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
10
16
  import { ScrollArea } from "../data-display/scroll-area.js";
11
17
  import { Checkbox } from "./checkbox.js";
12
- import { Command, CommandInput } from "./command.js";
18
+ import { SearchInput } from "./search-input.js";
19
+ import { Separator } from "../layout/separator.js";
13
20
  import {
14
21
  collectAllExpandableKeys,
15
22
  filterVisibleTree,
@@ -54,15 +61,33 @@ function TreeSelectRoot({
54
61
  treeDefaultExpandAll,
55
62
  placeholder,
56
63
  disabled,
57
- allowClear = true,
64
+ readOnly,
65
+ name,
66
+ allowClear,
58
67
  className,
59
68
  id,
60
69
  fieldNames,
70
+ size,
71
+ status,
72
+ variant,
73
+ loading = false,
74
+ open: openProp,
75
+ defaultOpen = false,
76
+ onOpenChange,
77
+ loadData,
78
+ treeTitleRender,
79
+ maxTagCount,
80
+ maxTagPlaceholder,
81
+ notFoundContent,
82
+ autoClearSearchValue = true,
83
+ search: searchProp,
84
+ onSearchChange,
61
85
  ...ariaProps
62
86
  }) {
63
87
  const { t } = useTranslation();
64
88
  const fieldA11y = pickFieldA11y(ariaProps);
65
- const identity = useFieldIdentity({ id, "data-field": fieldA11y["data-field"] });
89
+ const identity = useFieldIdentity({ id, name, "data-field": fieldA11y["data-field"] });
90
+ const resolvedName = name ?? identity.name;
66
91
  const reactId = React.useId();
67
92
  const treeId = `${id ?? reactId}-tree`;
68
93
  const options = React.useMemo(
@@ -70,8 +95,28 @@ function TreeSelectRoot({
70
95
  [treeDataProp, fieldNames]
71
96
  );
72
97
  const checkable = treeCheckable ?? multiple;
73
- const [open, setOpen] = React.useState(false);
74
- const [search, setSearch] = React.useState("");
98
+ const [internalSearch, setInternalSearch] = React.useState("");
99
+ const isSearchControlled = searchProp !== void 0;
100
+ const search = isSearchControlled ? searchProp : internalSearch;
101
+ const setSearch = React.useCallback(
102
+ (next) => {
103
+ if (!isSearchControlled) setInternalSearch(next);
104
+ onSearchChange?.(next);
105
+ },
106
+ [isSearchControlled, onSearchChange]
107
+ );
108
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
109
+ const isOpenControlled = openProp !== void 0;
110
+ const open = isOpenControlled ? openProp : internalOpen;
111
+ const setOpen = React.useCallback(
112
+ (next) => {
113
+ if (readOnly && next) return;
114
+ if (!isOpenControlled) setInternalOpen(next);
115
+ onOpenChange?.(next);
116
+ if (!next && autoClearSearchValue) setSearch("");
117
+ },
118
+ [isOpenControlled, onOpenChange, autoClearSearchValue, setSearch, readOnly]
119
+ );
75
120
  const [expandedKeys, setExpandedKeys] = React.useState(
76
121
  () => new Set(treeDefaultExpandAll ? collectAllExpandableKeys(options) : [])
77
122
  );
@@ -89,7 +134,16 @@ function TreeSelectRoot({
89
134
  if (!isControlled) setInternal(next);
90
135
  onValueChange?.(checkable || multiple ? next : next[0] ?? void 0);
91
136
  };
92
- const toggleExpand = (key) => {
137
+ const requestedLoads = React.useRef(/* @__PURE__ */ new Set());
138
+ const toggleExpand = (node) => {
139
+ const key = node.value;
140
+ const willExpand = !expandedKeys.has(key);
141
+ if (willExpand && loadData && !(node.children?.length ?? 0) && node.isLeaf !== true) {
142
+ if (!requestedLoads.current.has(key)) {
143
+ requestedLoads.current.add(key);
144
+ void loadData(node);
145
+ }
146
+ }
93
147
  setExpandedKeys((prev) => {
94
148
  const next = new Set(prev);
95
149
  if (next.has(key)) next.delete(key);
@@ -116,10 +170,20 @@ function TreeSelectRoot({
116
170
  commit(next);
117
171
  };
118
172
  const displayKeys = displayValues(selected, options, showCheckedStrategy, treeCheckStrictly);
119
- const displayLabel = displayKeys.map((v) => {
173
+ const displayItems = displayKeys.map((v) => {
120
174
  const label = findNodeByValue(options, v)?.label;
121
- return label ? reactNodeText(label) : v;
122
- }).join(", ");
175
+ return { value: v, label: label ? reactNodeText(label) : v };
176
+ });
177
+ const {
178
+ visible: visibleItems,
179
+ omitted: omittedItems,
180
+ overflow
181
+ } = applyMaxTagCount(displayItems, maxTagCount, maxTagPlaceholder);
182
+ const displayLabel = omittedItems.length === 0 ? visibleItems.map((item) => item.label).join(", ") : /* @__PURE__ */ jsxs(Fragment, { children: [
183
+ visibleItems.map((item) => item.label).join(", "),
184
+ ", ",
185
+ /* @__PURE__ */ jsx("span", { "data-slot": "tree-select-overflow", children: overflow ?? t("dataEntry.selection.overflow", { count: omittedItems.length }) })
186
+ ] });
123
187
  const clearValue = (e) => {
124
188
  e.stopPropagation();
125
189
  commit([]);
@@ -150,7 +214,7 @@ function TreeSelectRoot({
150
214
  case "ArrowRight": {
151
215
  event.preventDefault();
152
216
  if (hasChildren && !expanded) {
153
- toggleExpand(node.value);
217
+ toggleExpand(node);
154
218
  } else if (hasChildren && expanded) {
155
219
  focusByOffset(node.value, 1);
156
220
  }
@@ -159,7 +223,7 @@ function TreeSelectRoot({
159
223
  case "ArrowLeft": {
160
224
  event.preventDefault();
161
225
  if (hasChildren && expanded) {
162
- toggleExpand(node.value);
226
+ toggleExpand(node);
163
227
  }
164
228
  break;
165
229
  }
@@ -173,6 +237,9 @@ function TreeSelectRoot({
173
237
  break;
174
238
  }
175
239
  };
240
+ const clearControl = resolveAllowClear(allowClear, true, t("dataEntry.treeSelect.clear"));
241
+ const showClear = clearControl.enabled && displayKeys.length > 0 && !disabled && !readOnly && !loading;
242
+ const surface = controlSurfaceAttrs({ variant, status, size });
176
243
  return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
177
244
  /* @__PURE__ */ jsxs("div", { className: "relative", children: [
178
245
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
@@ -186,125 +253,146 @@ function TreeSelectRoot({
186
253
  "aria-haspopup": "tree",
187
254
  "aria-controls": open ? treeId : void 0,
188
255
  ...fieldA11y,
256
+ ...surface,
257
+ "aria-invalid": resolveAriaInvalid(fieldA11y["aria-invalid"], status),
258
+ "aria-busy": loading || void 0,
259
+ "aria-readonly": readOnly || void 0,
189
260
  disabled,
190
261
  className: cn(
191
- controlTriggerClass,
262
+ controlSurfaceTriggerClass,
192
263
  "w-full justify-between",
193
- allowClear && displayKeys.length > 0 && !disabled && "ui-tree-select-trigger-affixed",
264
+ showClear && "ui-tree-select-trigger-affixed",
194
265
  !displayKeys.length && "text-muted-foreground",
195
266
  className
196
267
  ),
197
268
  children: [
198
269
  /* @__PURE__ */ jsx("span", { className: "truncate", children: displayKeys.length ? displayLabel : resolvedPlaceholder }),
199
- /* @__PURE__ */ jsx("span", { className: "ms-2 flex shrink-0 items-center", children: !(allowClear && displayKeys.length > 0 && !disabled) && /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ui-tree-select-chevron", "aria-hidden": "true" }) })
270
+ /* @__PURE__ */ jsx("span", { className: "ms-2 flex shrink-0 items-center", children: loading ? /* @__PURE__ */ jsx(
271
+ Loader2,
272
+ {
273
+ "data-slot": "tree-select-loading",
274
+ className: "ui-tree-select-chevron animate-spin",
275
+ "aria-hidden": "true"
276
+ }
277
+ ) : showClear ? null : /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ui-tree-select-chevron", "aria-hidden": "true" }) })
200
278
  ]
201
279
  }
202
280
  ) }),
203
- allowClear && displayKeys.length > 0 && !disabled && /* @__PURE__ */ jsx(
281
+ showClear && /* @__PURE__ */ jsx(
204
282
  "button",
205
283
  {
206
284
  type: "button",
207
- "aria-label": t("dataEntry.treeSelect.clear"),
285
+ "aria-label": clearControl.label,
208
286
  className: "ui-tree-select-clear",
209
287
  onClick: clearValue,
210
- children: /* @__PURE__ */ jsx(X, { className: "ui-control-affix-icon", "aria-hidden": "true" })
288
+ children: clearControl.clearIcon ?? /* @__PURE__ */ jsx(X, { className: "ui-control-affix-icon", "aria-hidden": "true" })
211
289
  }
212
- )
290
+ ),
291
+ resolvedName ? selected.map((entry) => /* @__PURE__ */ jsx("input", { type: "hidden", name: resolvedName, value: entry, readOnly: true }, entry)) : null
213
292
  ] }),
214
293
  /* @__PURE__ */ jsxs(PopoverContent, { className: "ui-tree-select-popover", align: "start", children: [
215
- showSearch && /* @__PURE__ */ jsx(Command, { shouldFilter: false, children: /* @__PURE__ */ jsx(
216
- CommandInput,
217
- {
218
- placeholder: t("dataEntry.treeSelect.searchPlaceholder"),
219
- value: search,
220
- onValueChange: setSearch
221
- }
222
- ) }),
223
- /* @__PURE__ */ jsx(ScrollArea, { className: "ui-tree-select-list", children: /* @__PURE__ */ jsx(
224
- "div",
225
- {
226
- id: treeId,
227
- role: "tree",
228
- "aria-multiselectable": Boolean(checkable) || Boolean(multiple),
229
- className: "ui-tree-select-panel",
230
- children: visible.length === 0 ? /* @__PURE__ */ jsx("p", { className: "ui-tree-select-empty", children: t("dataEntry.treeSelect.empty") }) : visible.map(({ node, depth, hasChildren }) => {
231
- const expanded = expandedKeys.has(node.value);
232
- const isSelected = selected.includes(node.value);
233
- return /* @__PURE__ */ jsxs(
234
- "div",
235
- {
236
- ref: (el) => {
237
- treeItemRefs.current.set(node.value, el);
294
+ showSearch && /* @__PURE__ */ jsxs(Fragment, { children: [
295
+ /* @__PURE__ */ jsx(
296
+ SearchInput,
297
+ {
298
+ variant: "borderless",
299
+ ariaLabel: t("dataEntry.treeSelect.searchPlaceholder"),
300
+ placeholder: t("dataEntry.treeSelect.searchPlaceholder"),
301
+ value: search,
302
+ onValueChange: setSearch
303
+ }
304
+ ),
305
+ /* @__PURE__ */ jsx(Separator, {})
306
+ ] }),
307
+ /* @__PURE__ */ jsxs(ScrollArea, { className: "ui-tree-select-list", children: [
308
+ /* @__PURE__ */ jsx(
309
+ "div",
310
+ {
311
+ id: treeId,
312
+ role: "tree",
313
+ "aria-multiselectable": Boolean(checkable) || Boolean(multiple),
314
+ className: "ui-tree-select-panel",
315
+ children: visible.map(({ node, depth, hasChildren }) => {
316
+ const expanded = showSearch && search.trim() ? hasChildren : expandedKeys.has(node.value);
317
+ const isSelected = selected.includes(node.value);
318
+ const expandable = hasChildren || Boolean(loadData && node.isLeaf === false && !node.children?.length);
319
+ return /* @__PURE__ */ jsxs(
320
+ "div",
321
+ {
322
+ ref: (el) => {
323
+ treeItemRefs.current.set(node.value, el);
324
+ },
325
+ role: "treeitem",
326
+ tabIndex: node.disabled ? -1 : rovingKey === node.value ? 0 : -1,
327
+ "aria-expanded": expandable ? expanded : void 0,
328
+ "aria-selected": isSelected,
329
+ onFocus: () => setActiveKey(node.value),
330
+ onKeyDown: (event) => onTreeItemKeyDown(event, node, expandable, expanded),
331
+ "data-selected": isSelected ? "true" : "false",
332
+ "data-disabled": node.disabled ? "" : void 0,
333
+ className: "ui-tree-select-row ui-focus-ring",
334
+ style: { "--tree-select-depth": depth },
335
+ children: [
336
+ /* @__PURE__ */ jsx(
337
+ "button",
338
+ {
339
+ type: "button",
340
+ tabIndex: -1,
341
+ "aria-label": expanded ? t("dataEntry.treeSelect.collapse") : t("dataEntry.treeSelect.expand"),
342
+ "data-leaf": expandable ? void 0 : "",
343
+ className: "ui-tree-select-toggle",
344
+ onClick: () => toggleExpand(node),
345
+ children: expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "ui-tree-select-toggle-icon", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "ui-tree-select-toggle-icon", "aria-hidden": "true" })
346
+ }
347
+ ),
348
+ checkable ? (
349
+ // The row must NOT WRAP the Checkbox in a `<label>`: the Checkbox is itself a
350
+ // `<label>` around a real `<input>`, and a label nested in a label is
351
+ // invalid HTML — the browser resolves neither, so the box loses its
352
+ // accessible name. The node text sits BESIDE the box as a `for=`-associated
353
+ // label instead, which both names it and keeps the text clickable.
354
+ /* @__PURE__ */ jsxs("div", { className: "ui-tree-select-label", children: [
355
+ /* @__PURE__ */ jsx(
356
+ Checkbox,
357
+ {
358
+ id: `${treeId}-${node.value}-box`,
359
+ checked: isSelected,
360
+ tabIndex: -1,
361
+ disabled: Boolean(node.disabled) || Boolean(node.disableCheckbox),
362
+ onCheckedChange: () => toggleSelect(node),
363
+ "aria-labelledby": `${treeId}-${node.value}-label`
364
+ }
365
+ ),
366
+ /* @__PURE__ */ jsx(
367
+ "label",
368
+ {
369
+ className: "truncate",
370
+ id: `${treeId}-${node.value}-label`,
371
+ htmlFor: `${treeId}-${node.value}-box`,
372
+ children: treeTitleRender ? treeTitleRender(node) : node.label
373
+ }
374
+ )
375
+ ] })
376
+ ) : /* @__PURE__ */ jsx(
377
+ "button",
378
+ {
379
+ type: "button",
380
+ tabIndex: -1,
381
+ className: "flex-1 truncate text-start",
382
+ disabled: node.disabled,
383
+ onClick: () => toggleSelect(node),
384
+ children: treeTitleRender ? treeTitleRender(node) : node.label
385
+ }
386
+ )
387
+ ]
238
388
  },
239
- role: "treeitem",
240
- tabIndex: node.disabled ? -1 : rovingKey === node.value ? 0 : -1,
241
- "aria-expanded": hasChildren ? expanded : void 0,
242
- "aria-selected": isSelected,
243
- onFocus: () => setActiveKey(node.value),
244
- onKeyDown: (event) => onTreeItemKeyDown(event, node, hasChildren, expanded),
245
- "data-selected": isSelected ? "true" : "false",
246
- "data-disabled": node.disabled ? "" : void 0,
247
- className: "ui-tree-select-row ui-focus-ring",
248
- style: { "--tree-select-depth": depth },
249
- children: [
250
- /* @__PURE__ */ jsx(
251
- "button",
252
- {
253
- type: "button",
254
- tabIndex: -1,
255
- "aria-label": expanded ? t("dataEntry.treeSelect.collapse") : t("dataEntry.treeSelect.expand"),
256
- "data-leaf": hasChildren ? void 0 : "",
257
- className: "ui-tree-select-toggle",
258
- onClick: () => toggleExpand(node.value),
259
- children: expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "ui-tree-select-toggle-icon", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "ui-tree-select-toggle-icon", "aria-hidden": "true" })
260
- }
261
- ),
262
- checkable ? (
263
- // The row must NOT WRAP the Checkbox in a `<label>`: the Checkbox is itself a
264
- // `<label>` around a real `<input>`, and a label nested in a label is
265
- // invalid HTML — the browser resolves neither, so the box loses its
266
- // accessible name. The node text sits BESIDE the box as a `for=`-associated
267
- // label instead, which both names it and keeps the text clickable.
268
- /* @__PURE__ */ jsxs("div", { className: "ui-tree-select-label", children: [
269
- /* @__PURE__ */ jsx(
270
- Checkbox,
271
- {
272
- id: `${treeId}-${node.value}-box`,
273
- checked: isSelected,
274
- tabIndex: -1,
275
- disabled: Boolean(node.disabled) || Boolean(node.disableCheckbox),
276
- onCheckedChange: () => toggleSelect(node),
277
- "aria-labelledby": `${treeId}-${node.value}-label`
278
- }
279
- ),
280
- /* @__PURE__ */ jsx(
281
- "label",
282
- {
283
- className: "truncate",
284
- id: `${treeId}-${node.value}-label`,
285
- htmlFor: `${treeId}-${node.value}-box`,
286
- children: node.label
287
- }
288
- )
289
- ] })
290
- ) : /* @__PURE__ */ jsx(
291
- "button",
292
- {
293
- type: "button",
294
- tabIndex: -1,
295
- className: "flex-1 truncate text-start",
296
- disabled: node.disabled,
297
- onClick: () => toggleSelect(node),
298
- children: node.label
299
- }
300
- )
301
- ]
302
- },
303
- node.value
304
- );
305
- })
306
- }
307
- ) })
389
+ node.value
390
+ );
391
+ })
392
+ }
393
+ ),
394
+ visible.length === 0 && /* @__PURE__ */ jsx("p", { role: "status", className: "ui-tree-select-empty", children: notFoundContent ?? t("dataEntry.treeSelect.empty") })
395
+ ] })
308
396
  ] })
309
397
  ] });
310
398
  }
@@ -108,21 +108,14 @@ function flattenVisibleTree(options, expandedKeys, depth = 0) {
108
108
  function filterVisibleTree(options, query) {
109
109
  const q = query.trim().toLowerCase();
110
110
  if (!q) return flattenVisibleTree(options, new Set(collectAllExpandableKeys(options)));
111
- const expanded = /* @__PURE__ */ new Set();
112
- function walk(nodes, depth) {
113
- let branchMatch = false;
114
- for (const node of nodes) {
115
- const selfMatch = reactNodeText(node.label).toLowerCase().includes(q);
116
- const childMatch = node.children?.length ? walk(node.children, depth + 1) : false;
117
- if (selfMatch || childMatch) {
118
- branchMatch = true;
119
- if (childMatch) expanded.add(node.value);
120
- }
121
- }
122
- return branchMatch;
111
+ function matches(nodes, depth) {
112
+ return nodes.flatMap((node) => {
113
+ const children = node.isLeaf ? [] : matches(node.children ?? [], depth + 1);
114
+ if (!reactNodeText(node.label).toLowerCase().includes(q) && children.length === 0) return [];
115
+ return [{ node, depth, hasChildren: children.length > 0 }, ...children];
116
+ });
123
117
  }
124
- walk(options, 0);
125
- return flattenVisibleTree(options, expanded);
118
+ return matches(options, 0);
126
119
  }
127
120
  function collectAllExpandableKeys(options) {
128
121
  const keys = [];
@@ -0,0 +1,2 @@
1
+ /** Folder drops use entry readers because DataTransfer.files contains only the top-level items. */
2
+ export declare function readDroppedFiles(transfer: DataTransfer, directory: boolean): Promise<File[]>;
@@ -0,0 +1,31 @@
1
+ async function readDroppedFiles(transfer, directory) {
2
+ if (!directory || !transfer.items?.length) return Array.from(transfer.files);
3
+ const entries = Array.from(transfer.items).map((item) => item.webkitGetAsEntry?.()).filter((entry) => entry != null);
4
+ if (!entries.length) return Array.from(transfer.files);
5
+ const result = [];
6
+ const visit = async (entry, parent) => {
7
+ const path = parent ? `${parent}/${entry.name}` : entry.name;
8
+ if (entry.isFile) {
9
+ const file = await new Promise(
10
+ (resolve, reject) => entry.file(resolve, reject)
11
+ );
12
+ Object.defineProperty(file, "webkitRelativePath", { value: path, configurable: true });
13
+ result.push(file);
14
+ return;
15
+ }
16
+ if (!entry.isDirectory) return;
17
+ const reader = entry.createReader();
18
+ for (; ; ) {
19
+ const children = await new Promise(
20
+ (resolve, reject) => reader.readEntries(resolve, reject)
21
+ );
22
+ if (!children.length) break;
23
+ for (const child of children) await visit(child, path);
24
+ }
25
+ };
26
+ for (const entry of entries) await visit(entry, "");
27
+ return result;
28
+ }
29
+ export {
30
+ readDroppedFiles
31
+ };
@@ -0,0 +1,4 @@
1
+ import type { UploadProp } from "../../props/components/data-entry.prop.js";
2
+ import type { UploadRequestContext, UploadResult } from "./upload-types.js";
3
+ /** Default multipart transport. Custom storage protocols use onUpload with the same abort/progress contract. */
4
+ export declare function uploadRequest(file: File, options: Pick<UploadProp, "action" | "method" | "headers" | "data" | "name" | "withCredentials">, context: UploadRequestContext): Promise<UploadResult>;
@@ -0,0 +1,53 @@
1
+ async function uploadRequest(file, options, context) {
2
+ const action = typeof options.action === "function" ? await options.action(file) : options.action;
3
+ const data = typeof options.data === "function" ? await options.data(file) : options.data;
4
+ context.signal.throwIfAborted();
5
+ if (!action) throw new Error("Upload requires action or onUpload");
6
+ return new Promise((resolve, reject) => {
7
+ const xhr = new XMLHttpRequest();
8
+ const abort = () => xhr.abort();
9
+ const cleanup = () => context.signal.removeEventListener("abort", abort);
10
+ xhr.open(options.method ?? "POST", action);
11
+ xhr.withCredentials = options.withCredentials ?? false;
12
+ for (const [key, value] of Object.entries(options.headers ?? {}))
13
+ xhr.setRequestHeader(key, value);
14
+ xhr.upload.onprogress = (event) => {
15
+ if (event.lengthComputable) context.onProgress(event.loaded / event.total * 100);
16
+ };
17
+ xhr.onload = () => {
18
+ cleanup();
19
+ if (xhr.status < 200 || xhr.status >= 300) {
20
+ reject(new Error(`HTTP ${xhr.status}`));
21
+ return;
22
+ }
23
+ let response = xhr.responseText;
24
+ try {
25
+ response = JSON.parse(xhr.responseText);
26
+ } catch {
27
+ }
28
+ const result = response && typeof response === "object" ? response : {};
29
+ resolve({
30
+ response,
31
+ mediaId: typeof result.mediaId === "string" ? result.mediaId : void 0,
32
+ url: typeof result.url === "string" ? result.url : void 0,
33
+ previewUrl: typeof result.previewUrl === "string" ? result.previewUrl : void 0
34
+ });
35
+ };
36
+ xhr.onerror = () => {
37
+ cleanup();
38
+ reject(new Error("Network error"));
39
+ };
40
+ xhr.onabort = () => {
41
+ cleanup();
42
+ reject(new DOMException("Upload aborted", "AbortError"));
43
+ };
44
+ context.signal.addEventListener("abort", abort, { once: true });
45
+ const body = new FormData();
46
+ for (const [key, value] of Object.entries(data ?? {})) body.append(key, value);
47
+ body.append(options.name ?? "file", file, file.name);
48
+ xhr.send(body);
49
+ });
50
+ }
51
+ export {
52
+ uploadRequest
53
+ };
@@ -12,6 +12,10 @@ export type UploadFileItem = {
12
12
  mediaId?: string;
13
13
  status: UploadFileStatus;
14
14
  error?: string;
15
+ percent?: number;
16
+ response?: unknown;
17
+ url?: string;
18
+ relativePath?: string;
15
19
  /** Local file before / during upload */
16
20
  file?: File;
17
21
  /** Undo clears this before save — media-service has no restore API. */
@@ -33,3 +37,27 @@ export type UploadCommitAction = {
33
37
  export declare function createUploadItem(file: File, partial?: Partial<UploadFileItem>): UploadFileItem;
34
38
  export declare function revokePreviewUrl(item: UploadFileItem | null | undefined): void;
35
39
  export declare function collectUploadCommitActions(items: UploadFileItem[]): UploadCommitAction;
40
+ /** Returning this from beforeUpload excludes the file entirely. false keeps it staged. */
41
+ export declare const UPLOAD_LIST_IGNORE: unique symbol;
42
+ export type UploadRequestContext = {
43
+ signal: AbortSignal;
44
+ onProgress: (percent: number) => void;
45
+ };
46
+ export type UploadResult = {
47
+ mediaId?: string;
48
+ previewUrl?: string;
49
+ url?: string;
50
+ response?: unknown;
51
+ };
52
+ export type UploadRejection = {
53
+ file: File;
54
+ reason: "accept" | "size" | "count" | "beforeUpload";
55
+ error?: unknown;
56
+ };
57
+ export type UploadItemActions = {
58
+ remove: () => void;
59
+ preview: () => void;
60
+ download: () => void;
61
+ upload: () => void;
62
+ cancel: () => void;
63
+ };
@@ -34,7 +34,9 @@ function collectUploadCommitActions(items) {
34
34
  promoteMediaIds: [...new Set(promoteMediaIds)]
35
35
  };
36
36
  }
37
+ const UPLOAD_LIST_IGNORE = /* @__PURE__ */ Symbol("upload-list-ignore");
37
38
  export {
39
+ UPLOAD_LIST_IGNORE,
38
40
  collectUploadCommitActions,
39
41
  createUploadItem,
40
42
  revokePreviewUrl
@@ -2,6 +2,6 @@ import * as React from "react";
2
2
  import type { UploadProp } from "../../props/components/data-entry.prop.js";
3
3
  export type { UploadProp, UploadProp as UploadProps, UploadFileItemProp, UploadVariantProp, } from "../../props/components/data-entry.prop.js";
4
4
  export type { UploadFileItem, UploadVariant, UploadCommitAction } from "./upload-types.js";
5
- export { collectUploadCommitActions, createUploadItem } from "./upload-types.js";
5
+ export { collectUploadCommitActions, createUploadItem, UPLOAD_LIST_IGNORE } from "./upload-types.js";
6
6
  export { useUploadDraft } from "./use-upload-draft.js";
7
- export declare function Upload({ variant, triggerSize, triggerVariant, value, defaultValue, onValueChange, accept: acceptProp, multiple: multipleProp, maxCount: maxCountProp, maxSizeBytes, disabled, removable, onUpload, id, className, children, ...ariaProps }: UploadProp): React.JSX.Element;
7
+ export declare function Upload({ variant, triggerSize, triggerVariant, value, defaultValue, onValueChange, accept: acceptProp, multiple: multipleProp, maxCount: maxCountProp, maxSizeBytes, disabled: disabledProp, readOnly, directory, pastable, openFileDialogOnClick, name, action, method, headers, data, withCredentials, beforeUpload, onReject, onRemove, onPreview, onDownload, previewFile, onDrop, showUploadList, itemRender, removable, onUpload, id, className, children, ...ariaProps }: UploadProp): React.JSX.Element;