@nocoo/basalt 2.0.2 → 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 (300) hide show
  1. package/README.md +134 -15
  2. package/ai/COMPATIBILITY.md +180 -0
  3. package/ai/INTEGRATION.md +1760 -0
  4. package/ai/RECIPES.md +161 -0
  5. package/ai/USAGE.md +99 -0
  6. package/ai/registry.json +38099 -0
  7. package/ai/sources.json +10 -0
  8. package/dist/assets/brand-mark.d.ts +1 -0
  9. package/dist/assets/brand-mark.js +7 -0
  10. package/dist/assets/brand-mark.js.map +1 -0
  11. package/dist/charts/area.d.ts +58 -5
  12. package/dist/charts/area.js +20 -12
  13. package/dist/charts/area.js.map +1 -1
  14. package/dist/charts/bar.d.ts +54 -5
  15. package/dist/charts/bar.js +16 -10
  16. package/dist/charts/bar.js.map +1 -1
  17. package/dist/charts/bullet.d.ts +18 -1
  18. package/dist/charts/bullet.js +6 -3
  19. package/dist/charts/bullet.js.map +1 -1
  20. package/dist/charts/charts.d.ts +58 -5
  21. package/dist/charts/charts.js +13 -2
  22. package/dist/charts/charts.js.map +1 -1
  23. package/dist/charts/config.d.ts +7 -0
  24. package/dist/charts/config.js +5 -1
  25. package/dist/charts/config.js.map +1 -1
  26. package/dist/charts/custom-chart.d.ts +58 -5
  27. package/dist/charts/custom-chart.js +13 -2
  28. package/dist/charts/custom-chart.js.map +1 -1
  29. package/dist/charts/donut.d.ts +18 -1
  30. package/dist/charts/donut.js +6 -3
  31. package/dist/charts/donut.js.map +1 -1
  32. package/dist/charts/frame.d.ts +40 -3
  33. package/dist/charts/frame.js +55 -28
  34. package/dist/charts/frame.js.map +1 -1
  35. package/dist/charts/funnel.d.ts +18 -1
  36. package/dist/charts/funnel.js +5 -1
  37. package/dist/charts/funnel.js.map +1 -1
  38. package/dist/charts/gauge.d.ts +18 -1
  39. package/dist/charts/gauge.js +38 -20
  40. package/dist/charts/gauge.js.map +1 -1
  41. package/dist/charts/grouped-bar.d.ts +54 -5
  42. package/dist/charts/grouped-bar.js +16 -10
  43. package/dist/charts/grouped-bar.js.map +1 -1
  44. package/dist/charts/heatmap-calendar.d.ts +61 -0
  45. package/dist/charts/heatmap-calendar.js +287 -96
  46. package/dist/charts/heatmap-calendar.js.map +1 -1
  47. package/dist/charts/heatmap-matrix.d.ts +99 -0
  48. package/dist/charts/heatmap-matrix.js +249 -0
  49. package/dist/charts/heatmap-matrix.js.map +1 -0
  50. package/dist/charts/line.d.ts +66 -5
  51. package/dist/charts/line.js +37 -30
  52. package/dist/charts/line.js.map +1 -1
  53. package/dist/charts/palette.d.ts +1 -0
  54. package/dist/charts/palette.js +7 -1
  55. package/dist/charts/palette.js.map +1 -1
  56. package/dist/charts/radar.d.ts +18 -1
  57. package/dist/charts/radar.js +26 -19
  58. package/dist/charts/radar.js.map +1 -1
  59. package/dist/charts/sankey.d.ts +18 -1
  60. package/dist/charts/sankey.js +4 -1
  61. package/dist/charts/sankey.js.map +1 -1
  62. package/dist/charts/series.d.ts +5 -7
  63. package/dist/charts/series.js.map +1 -1
  64. package/dist/charts/slot-bar.d.ts +38 -6
  65. package/dist/charts/slot-bar.js +5 -2
  66. package/dist/charts/slot-bar.js.map +1 -1
  67. package/dist/charts/sparkline.d.ts +35 -5
  68. package/dist/charts/sparkline.js +22 -18
  69. package/dist/charts/sparkline.js.map +1 -1
  70. package/dist/charts/stacked-bar.d.ts +58 -5
  71. package/dist/charts/stacked-bar.js +19 -12
  72. package/dist/charts/stacked-bar.js.map +1 -1
  73. package/dist/charts/stat-card.d.ts +51 -1
  74. package/dist/charts/stat-card.js +66 -39
  75. package/dist/charts/stat-card.js.map +1 -1
  76. package/dist/charts/timeline.d.ts +3 -0
  77. package/dist/charts/timeline.js +4 -3
  78. package/dist/charts/timeline.js.map +1 -1
  79. package/dist/charts/timeseries.d.ts +58 -5
  80. package/dist/charts/timeseries.js +13 -2
  81. package/dist/charts/timeseries.js.map +1 -1
  82. package/dist/charts/tooltip.d.ts +57 -0
  83. package/dist/charts/tooltip.js +119 -2
  84. package/dist/charts/tooltip.js.map +1 -1
  85. package/dist/components/accordion.d.ts +117 -2
  86. package/dist/components/accordion.js +20 -13
  87. package/dist/components/accordion.js.map +1 -1
  88. package/dist/components/alert-dialog.d.ts +131 -13
  89. package/dist/components/alert-dialog.js +13 -10
  90. package/dist/components/alert-dialog.js.map +1 -1
  91. package/dist/components/app-header.js +18 -17
  92. package/dist/components/app-header.js.map +1 -1
  93. package/dist/components/app-shell.js +15 -14
  94. package/dist/components/app-shell.js.map +1 -1
  95. package/dist/components/autocomplete.d.ts +4 -0
  96. package/dist/components/autocomplete.js.map +1 -1
  97. package/dist/components/avatar.d.ts +76 -3
  98. package/dist/components/avatar.js.map +1 -1
  99. package/dist/components/badge.d.ts +15 -1
  100. package/dist/components/badge.js +17 -16
  101. package/dist/components/badge.js.map +1 -1
  102. package/dist/components/banner.d.ts +88 -2
  103. package/dist/components/banner.js +41 -40
  104. package/dist/components/banner.js.map +1 -1
  105. package/dist/components/basalt-mark.js +14 -8
  106. package/dist/components/basalt-mark.js.map +1 -1
  107. package/dist/components/battery-meter.d.ts +10 -0
  108. package/dist/components/battery-meter.js +56 -0
  109. package/dist/components/battery-meter.js.map +1 -0
  110. package/dist/components/breadcrumbs.d.ts +27 -7
  111. package/dist/components/breadcrumbs.js +21 -20
  112. package/dist/components/breadcrumbs.js.map +1 -1
  113. package/dist/components/button.d.ts +48 -2
  114. package/dist/components/button.js +87 -36
  115. package/dist/components/button.js.map +1 -1
  116. package/dist/components/chat-bubble.d.ts +24 -0
  117. package/dist/components/chat-bubble.js +26 -0
  118. package/dist/components/chat-bubble.js.map +1 -0
  119. package/dist/components/chat-composer.d.ts +45 -0
  120. package/dist/components/chat-composer.js +74 -0
  121. package/dist/components/chat-composer.js.map +1 -0
  122. package/dist/components/chat-header.d.ts +25 -0
  123. package/dist/components/chat-header.js +34 -0
  124. package/dist/components/chat-header.js.map +1 -0
  125. package/dist/components/chat-inbox.d.ts +33 -0
  126. package/dist/components/chat-inbox.js +44 -0
  127. package/dist/components/chat-inbox.js.map +1 -0
  128. package/dist/components/checkbox.js +84 -53
  129. package/dist/components/checkbox.js.map +1 -1
  130. package/dist/components/clipboard-text.d.ts +13 -2
  131. package/dist/components/clipboard-text.js +4 -4
  132. package/dist/components/clipboard-text.js.map +1 -1
  133. package/dist/components/collapsible.d.ts +58 -15
  134. package/dist/components/collapsible.js.map +1 -1
  135. package/dist/components/command-palette.d.ts +151 -61
  136. package/dist/components/command-palette.js.map +1 -1
  137. package/dist/components/confirm-dialog.d.ts +1 -1
  138. package/dist/components/confirm-dialog.js +6 -4
  139. package/dist/components/confirm-dialog.js.map +1 -1
  140. package/dist/components/context-menu.d.ts +210 -2
  141. package/dist/components/context-menu.js +7 -4
  142. package/dist/components/context-menu.js.map +1 -1
  143. package/dist/components/data-table.d.ts +40 -2
  144. package/dist/components/data-table.js +168 -120
  145. package/dist/components/data-table.js.map +1 -1
  146. package/dist/components/date-picker.d.ts +53 -2
  147. package/dist/components/date-picker.js +387 -219
  148. package/dist/components/date-picker.js.map +1 -1
  149. package/dist/components/delete-resource.d.ts +7 -1
  150. package/dist/components/delete-resource.js +22 -18
  151. package/dist/components/delete-resource.js.map +1 -1
  152. package/dist/components/description-list.js +16 -15
  153. package/dist/components/description-list.js.map +1 -1
  154. package/dist/components/dialog.d.ts +141 -12
  155. package/dist/components/dialog.js +43 -39
  156. package/dist/components/dialog.js.map +1 -1
  157. package/dist/components/dock.d.ts +39 -0
  158. package/dist/components/dock.js +97 -0
  159. package/dist/components/dock.js.map +1 -0
  160. package/dist/components/dropdown-menu.d.ts +229 -15
  161. package/dist/components/dropdown-menu.js +9 -6
  162. package/dist/components/dropdown-menu.js.map +1 -1
  163. package/dist/components/editable-nav-item.d.ts +33 -0
  164. package/dist/components/editable-nav-item.js +90 -0
  165. package/dist/components/editable-nav-item.js.map +1 -0
  166. package/dist/components/empty.d.ts +22 -2
  167. package/dist/components/empty.js +22 -13
  168. package/dist/components/empty.js.map +1 -1
  169. package/dist/components/fab.d.ts +23 -0
  170. package/dist/components/fab.js +25 -0
  171. package/dist/components/fab.js.map +1 -0
  172. package/dist/components/field.js +31 -30
  173. package/dist/components/field.js.map +1 -1
  174. package/dist/components/file-dropzone.d.ts +37 -0
  175. package/dist/components/file-dropzone.js +103 -0
  176. package/dist/components/file-dropzone.js.map +1 -0
  177. package/dist/components/filter-bar.d.ts +34 -0
  178. package/dist/components/filter-bar.js +52 -0
  179. package/dist/components/filter-bar.js.map +1 -0
  180. package/dist/components/flow.js +19 -18
  181. package/dist/components/flow.js.map +1 -1
  182. package/dist/components/grid.d.ts +3 -1
  183. package/dist/components/grid.js.map +1 -1
  184. package/dist/components/hover-card.d.ts +111 -1
  185. package/dist/components/hover-card.js +9 -6
  186. package/dist/components/hover-card.js.map +1 -1
  187. package/dist/components/icon-picker.d.ts +34 -0
  188. package/dist/components/icon-picker.js +77 -0
  189. package/dist/components/icon-picker.js.map +1 -0
  190. package/dist/components/inline-editable.d.ts +46 -0
  191. package/dist/components/inline-editable.js +124 -0
  192. package/dist/components/inline-editable.js.map +1 -0
  193. package/dist/components/input-area.js +3 -3
  194. package/dist/components/input-group.js +7 -7
  195. package/dist/components/input.js +3 -3
  196. package/dist/components/label.js +18 -17
  197. package/dist/components/label.js.map +1 -1
  198. package/dist/components/layer-card.d.ts +10 -2
  199. package/dist/components/layer-card.js +71 -68
  200. package/dist/components/layer-card.js.map +1 -1
  201. package/dist/components/loader.d.ts +10 -2
  202. package/dist/components/loader.js.map +1 -1
  203. package/dist/components/menu-bar.d.ts +174 -6
  204. package/dist/components/menu-bar.js +33 -29
  205. package/dist/components/menu-bar.js.map +1 -1
  206. package/dist/components/meter.d.ts +27 -2
  207. package/dist/components/meter.js.map +1 -1
  208. package/dist/components/multi-select.d.ts +64 -0
  209. package/dist/components/multi-select.js +218 -0
  210. package/dist/components/multi-select.js.map +1 -0
  211. package/dist/components/navigation-menu.d.ts +88 -5
  212. package/dist/components/navigation-menu.js.map +1 -1
  213. package/dist/components/overlay.js +6 -5
  214. package/dist/components/overlay.js.map +1 -1
  215. package/dist/components/page-header.js +23 -21
  216. package/dist/components/page-header.js.map +1 -1
  217. package/dist/components/pagination.js +7 -7
  218. package/dist/components/popover.d.ts +136 -16
  219. package/dist/components/popover.js +32 -28
  220. package/dist/components/popover.js.map +1 -1
  221. package/dist/components/radio.js +38 -37
  222. package/dist/components/radio.js.map +1 -1
  223. package/dist/components/resource-list.d.ts +26 -1
  224. package/dist/components/resource-list.js +28 -15
  225. package/dist/components/resource-list.js.map +1 -1
  226. package/dist/components/responsive-master-detail.d.ts +27 -0
  227. package/dist/components/responsive-master-detail.js +85 -0
  228. package/dist/components/responsive-master-detail.js.map +1 -0
  229. package/dist/components/section-rule.js +23 -22
  230. package/dist/components/section-rule.js.map +1 -1
  231. package/dist/components/select.d.ts +27 -3
  232. package/dist/components/select.js +11 -11
  233. package/dist/components/select.js.map +1 -1
  234. package/dist/components/sheet.d.ts +115 -11
  235. package/dist/components/sheet.js +37 -30
  236. package/dist/components/sheet.js.map +1 -1
  237. package/dist/components/sidebar.d.ts +109 -17
  238. package/dist/components/sidebar.js +109 -108
  239. package/dist/components/sidebar.js.map +1 -1
  240. package/dist/components/skeleton-line.d.ts +22 -2
  241. package/dist/components/skeleton-line.js.map +1 -1
  242. package/dist/components/slider.d.ts +61 -1
  243. package/dist/components/slider.js +31 -15
  244. package/dist/components/slider.js.map +1 -1
  245. package/dist/components/stat-strip.js +16 -15
  246. package/dist/components/stat-strip.js.map +1 -1
  247. package/dist/components/switch.js +78 -47
  248. package/dist/components/switch.js.map +1 -1
  249. package/dist/components/table-of-contents.js +18 -17
  250. package/dist/components/table-of-contents.js.map +1 -1
  251. package/dist/components/table-pager.js +20 -19
  252. package/dist/components/table-pager.js.map +1 -1
  253. package/dist/components/table.d.ts +9 -3
  254. package/dist/components/table.js +19 -18
  255. package/dist/components/table.js.map +1 -1
  256. package/dist/components/tabs.js +29 -22
  257. package/dist/components/tabs.js.map +1 -1
  258. package/dist/components/tag-badge.d.ts +58 -0
  259. package/dist/components/tag-badge.js +72 -0
  260. package/dist/components/tag-badge.js.map +1 -0
  261. package/dist/components/tag-color-picker.d.ts +20 -0
  262. package/dist/components/tag-color-picker.js +42 -0
  263. package/dist/components/tag-color-picker.js.map +1 -0
  264. package/dist/components/text.js +17 -16
  265. package/dist/components/text.js.map +1 -1
  266. package/dist/components/toast.d.ts +173 -3
  267. package/dist/components/toast.js +1 -1
  268. package/dist/components/toast.js.map +1 -1
  269. package/dist/components/toggle-group.d.ts +88 -2
  270. package/dist/components/toggle-group.js +32 -31
  271. package/dist/components/toggle-group.js.map +1 -1
  272. package/dist/components/toggle.d.ts +35 -2
  273. package/dist/components/toggle.js +12 -11
  274. package/dist/components/toggle.js.map +1 -1
  275. package/dist/components/tooltip.d.ts +144 -6
  276. package/dist/components/tooltip.js +9 -6
  277. package/dist/components/tooltip.js.map +1 -1
  278. package/dist/components/typeahead-field.js +193 -132
  279. package/dist/components/typeahead-field.js.map +1 -1
  280. package/dist/components/upload-queue.d.ts +51 -0
  281. package/dist/components/upload-queue.js +116 -0
  282. package/dist/components/upload-queue.js.map +1 -0
  283. package/dist/providers/accent.d.ts +46 -2
  284. package/dist/providers/accent.js +358 -172
  285. package/dist/providers/accent.js.map +1 -1
  286. package/dist/providers/link.d.ts +14 -4
  287. package/dist/providers/link.js.map +1 -1
  288. package/dist/providers/theme.d.ts +38 -2
  289. package/dist/providers/theme.js +160 -27
  290. package/dist/providers/theme.js.map +1 -1
  291. package/dist/styles/base.css +83 -0
  292. package/dist/styles/standalone.css +820 -120
  293. package/dist/styles/tailwind.css +21 -7
  294. package/dist/styles/tokens.css +146 -91
  295. package/dist/utils/control-surface.d.ts +2 -1
  296. package/dist/utils/control-surface.js +4 -4
  297. package/dist/utils/control-surface.js.map +1 -1
  298. package/dist/utils/selection-indicator.js +2 -2
  299. package/dist/utils/selection-indicator.js.map +1 -1
  300. package/package.json +8 -4
@@ -3,165 +3,226 @@ import { cn as e } from "../utils/cn.js";
3
3
  import { overlayItemClass as t, overlayPanelClass as n } from "./overlay.js";
4
4
  import { Input as r } from "./input.js";
5
5
  import { useEffect as i, useId as a, useRef as o, useState as s } from "react";
6
- import { jsx as c, jsxs as l } from "react/jsx-runtime";
6
+ import { jsx as c, jsxs as ee } from "react/jsx-runtime";
7
+ import * as l from "@radix-ui/react-popover";
7
8
  //#region src/components/typeahead-field.tsx
8
- function u({ items: u, value: p, defaultValue: m = "", onValueChange: h, name: g, placeholder: _ = "Select…", disabled: v = !1, loading: y = !1, size: b = "default", className: x, id: S, allowFreeform: C, "aria-label": w, "aria-describedby": T, "aria-invalid": E, onBlur: D, ...O }) {
9
- let [k, A] = s(m), j = p ?? k, [M, N] = s(d(u, j)), [P, F] = s(p), [I, L] = s(!1), [R, z] = s(null), B = a(), V = o(null), H = o(!1);
10
- p !== P && (F(p), p === void 0 ? P !== void 0 && (A(P), N(d(u, P))) : N(d(u, p)));
11
- let U = d(u, j), [W, G] = s(U);
12
- U !== W && (G(U), (!I || M === W) && N(U)), i(() => {
13
- let e = V.current?.form;
9
+ function u({ items: u, value: p, defaultValue: m = "", onValueChange: h, name: g, placeholder: _ = "Select…", disabled: v = !1, loading: y = !1, size: b = "default", className: x, id: S, allowFreeform: C, "aria-label": w, "aria-describedby": T, "aria-invalid": te, onBlur: ne, ...E }) {
10
+ let [D, O] = s(m), k = p ?? D, [A, j] = s(d(u, k)), [M, N] = s(p), [P, F] = s(!1), [I, L] = s(null), R = a(), z = o(null), B = o(!1), V = o(null);
11
+ p !== M && (N(p), p === void 0 ? M !== void 0 && (O(M), j(d(u, M))) : j(d(u, p)));
12
+ let H = d(u, k), [U, W] = s(H);
13
+ H !== U && (W(H), (!P || A === U) && j(H));
14
+ let G = o({
15
+ defaultValue: m,
16
+ items: u,
17
+ value: p
18
+ });
19
+ i(() => {
20
+ G.current = {
21
+ defaultValue: m,
22
+ items: u,
23
+ value: p
24
+ };
25
+ }), i(() => {
26
+ let e = z.current?.form;
14
27
  if (!e) return;
15
- let t = (e) => {
16
- queueMicrotask(() => {
17
- e.defaultPrevented || (p === void 0 ? (A(m), N(d(u, m))) : N(d(u, p)), L(!1), z(null));
18
- });
28
+ let t = !1, n = /* @__PURE__ */ new Set(), r = (e) => {
29
+ let r = setTimeout(() => {
30
+ if (n.delete(r), t || e.defaultPrevented) return;
31
+ let i = G.current;
32
+ i.value === void 0 ? (O(i.defaultValue), j(d(i.items, i.defaultValue))) : j(d(i.items, i.value)), F(!1), L(null);
33
+ }, 0);
34
+ n.add(r);
19
35
  };
20
- return e.addEventListener("reset", t), () => e.removeEventListener("reset", t);
21
- }, [
22
- m,
23
- u,
24
- p
25
- ]);
26
- let K = f(u, M, !C), q = R === null || K.length === 0 ? null : Math.min(R, K.length - 1), J = q === null ? void 0 : K[q], Y = I && K.length > 0, X = v || y;
36
+ return e.addEventListener("reset", r), () => {
37
+ t = !0;
38
+ for (let e of n) clearTimeout(e);
39
+ n.clear(), e.removeEventListener("reset", r);
40
+ };
41
+ }, []);
42
+ let K = f(u, A, !C), q = I === null || K.length === 0 ? null : Math.min(I, K.length - 1), J = q === null ? void 0 : K[q], Y = P && K.length > 0, X = v || y;
27
43
  i(() => {
28
- if (!I) return;
29
- let e = f(u, M, !C).length;
30
- z((t) => t === null || e === 0 ? null : Math.min(t, e - 1));
44
+ if (!Y || q === null) return;
45
+ let e = document.getElementById(`${R}-opt-${q}`);
46
+ typeof e?.scrollIntoView == "function" && e.scrollIntoView({ block: "nearest" });
47
+ }, [
48
+ q,
49
+ R,
50
+ Y
51
+ ]), i(() => {
52
+ if (!P) return;
53
+ let e = f(u, A, !C).length;
54
+ L((t) => t === null || e === 0 ? null : Math.min(t, e - 1));
31
55
  }, [
32
56
  C,
33
57
  u,
34
- I,
35
- M
58
+ P,
59
+ A
36
60
  ]);
37
- function Z(e, t) {
38
- p === void 0 ? (A(e), N(t)) : N(d(u, p)), L(!1), z(null), h?.(e), V.current && document.activeElement !== V.current && (H.current = !0, V.current.focus());
61
+ function Z(e, t, n = !0) {
62
+ p === void 0 ? (O(e), j(t)) : j(d(u, p)), F(!1), L(null), h?.(e), n && z.current && document.activeElement !== z.current && (B.current = !0, z.current.focus());
39
63
  }
40
- function Q(e) {
41
- e.disabled || Z(e.value, e.label);
64
+ function Q(e, t = !0) {
65
+ e.disabled || Z(e.value, e.label, t);
42
66
  }
43
- function $(e) {
44
- let t = e.trim(), n = u.find((e) => !e.disabled && (e.label.toLowerCase() === t.toLowerCase() || e.value === t));
45
- if (n) {
46
- Q(n);
67
+ function $(e, t = !0) {
68
+ let n = e.trim(), r = u.find((e) => !e.disabled && (e.label.toLowerCase() === n.toLowerCase() || e.value === n));
69
+ if (r) {
70
+ Q(r, t);
47
71
  return;
48
72
  }
49
- Z(t, t);
73
+ Z(n, n, t);
50
74
  }
51
75
  return i(() => {
52
- if (!I) return;
76
+ if (!P) return;
53
77
  let e = (e) => {
54
- e.key !== "Escape" || e.isComposing || (e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(), L(!1), N(d(u, j)), z(null));
78
+ e.key !== "Escape" || e.isComposing || (e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(), F(!1), j(d(u, k)), L(null));
55
79
  };
56
80
  return window.addEventListener("keydown", e, !0), document.addEventListener("keydown", e, !0), () => {
57
81
  window.removeEventListener("keydown", e, !0), document.removeEventListener("keydown", e, !0);
58
82
  };
59
83
  }, [
60
84
  u,
61
- I,
62
- j
63
- ]), /* @__PURE__ */ l("div", {
64
- ...O,
65
- className: e("relative w-full", x),
66
- onBlur: (e) => {
67
- if (D?.(e), !e.currentTarget.contains(e.relatedTarget)) {
68
- if (C) {
69
- if (M.trim() !== d(u, j)) {
70
- $(M);
71
- return;
72
- }
73
- } else N(d(u, j));
74
- L(!1), z(null);
75
- }
85
+ P,
86
+ k
87
+ ]), /* @__PURE__ */ c(l.Root, {
88
+ open: Y,
89
+ onOpenChange: (e) => {
90
+ e || (F(!1), L(null), C || j(d(u, k)));
76
91
  },
77
- children: [
78
- g ? /* @__PURE__ */ c("input", {
79
- type: "hidden",
80
- name: g,
81
- value: j,
82
- disabled: X
83
- }) : null,
84
- /* @__PURE__ */ c(r, {
85
- ref: V,
86
- id: S,
87
- value: M,
88
- disabled: X,
89
- size: b,
90
- "aria-busy": y || void 0,
91
- "aria-invalid": E,
92
- "aria-describedby": T,
93
- onChange: (e) => {
94
- N(e.target.value), L(!0), z(null);
95
- },
96
- onFocus: () => {
97
- if (!X) {
98
- if (H.current) {
99
- H.current = !1;
92
+ children: /* @__PURE__ */ ee("div", {
93
+ ...E,
94
+ className: e("relative w-full", x),
95
+ onBlur: (e) => {
96
+ ne?.(e);
97
+ let t = e.relatedTarget, n = document.getElementById(R);
98
+ if (!e.currentTarget.contains(t) && !n?.contains(t)) {
99
+ if (C) {
100
+ if (A.trim() !== d(u, k)) {
101
+ $(A, !1);
100
102
  return;
101
103
  }
102
- z(null), L(!0);
103
- }
104
- },
105
- onClick: () => {
106
- X || (I || z(null), L(!0));
107
- },
108
- onKeyDown: (e) => {
109
- if (!(e.nativeEvent.isComposing || e.key === "Process")) {
110
- if (e.key === "ArrowDown") {
111
- if (e.preventDefault(), L(!0), K.length === 0) {
112
- z(null);
113
- return;
104
+ } else j(d(u, k));
105
+ F(!1), L(null);
106
+ }
107
+ },
108
+ children: [
109
+ g ? /* @__PURE__ */ c("input", {
110
+ type: "hidden",
111
+ name: g,
112
+ value: k,
113
+ disabled: X
114
+ }) : null,
115
+ /* @__PURE__ */ c(l.Anchor, {
116
+ asChild: !0,
117
+ children: /* @__PURE__ */ c(r, {
118
+ ref: z,
119
+ id: S,
120
+ value: A,
121
+ disabled: X,
122
+ size: b,
123
+ "aria-busy": y || void 0,
124
+ "aria-invalid": te,
125
+ "aria-describedby": T,
126
+ onChange: (e) => {
127
+ j(e.target.value), F(!0), L(null);
128
+ },
129
+ onFocus: () => {
130
+ if (!X) {
131
+ if (B.current) {
132
+ B.current = !1;
133
+ return;
134
+ }
135
+ L(null), F(!0);
114
136
  }
115
- z((e) => e === null ? 0 : (e + 1) % K.length);
116
- return;
117
- }
118
- if (e.key === "ArrowUp") {
119
- if (e.preventDefault(), L(!0), K.length === 0) {
120
- z(null);
121
- return;
122
- }
123
- z((e) => e === null ? K.length - 1 : (e - 1 + K.length) % K.length);
124
- return;
125
- }
126
- if (e.key === "Enter") {
127
- if (I && J) {
128
- e.preventDefault(), Q(J);
129
- return;
137
+ },
138
+ onClick: () => {
139
+ X || (P || L(null), F(!0));
140
+ },
141
+ onKeyDown: (e) => {
142
+ if (!(e.nativeEvent.isComposing || e.key === "Process")) {
143
+ if (e.key === "ArrowDown") {
144
+ if (e.preventDefault(), F(!0), K.length === 0) {
145
+ L(null);
146
+ return;
147
+ }
148
+ L((e) => e === null ? 0 : (e + 1) % K.length);
149
+ return;
150
+ }
151
+ if (e.key === "ArrowUp") {
152
+ if (e.preventDefault(), F(!0), K.length === 0) {
153
+ L(null);
154
+ return;
155
+ }
156
+ L((e) => e === null ? K.length - 1 : (e - 1 + K.length) % K.length);
157
+ return;
158
+ }
159
+ if (e.key === "Enter") {
160
+ if (P && J) {
161
+ e.preventDefault(), Q(J);
162
+ return;
163
+ }
164
+ C && A.trim() !== d(u, k) && (e.preventDefault(), $(A));
165
+ }
130
166
  }
131
- C && M.trim() !== d(u, j) && (e.preventDefault(), $(M));
132
- }
133
- }
134
- },
135
- placeholder: _,
136
- role: "combobox",
137
- "aria-label": w ?? (S ? void 0 : _),
138
- "aria-expanded": Y,
139
- "aria-autocomplete": "list",
140
- "aria-controls": Y ? B : void 0,
141
- "aria-activedescendant": Y && J && q !== null ? `${B}-opt-${q}` : void 0
142
- }),
143
- Y ? /* @__PURE__ */ c("div", {
144
- id: B,
145
- role: "listbox",
146
- className: n("absolute top-full z-20 mt-1 w-full"),
147
- children: K.map((n, r) => /* @__PURE__ */ c("button", {
148
- type: "button",
149
- id: `${B}-opt-${r}`,
150
- role: "option",
151
- tabIndex: -1,
152
- disabled: n.disabled,
153
- "aria-disabled": n.disabled || void 0,
154
- "aria-selected": r === q,
155
- className: e(t("hover:bg-basalt-accent"), r === q && "bg-basalt-accent", n.disabled && "opacity-50"),
156
- onMouseEnter: () => {
157
- n.disabled || z(r);
167
+ },
168
+ placeholder: _,
169
+ role: "combobox",
170
+ "aria-label": w ?? (S ? void 0 : _),
171
+ "aria-expanded": Y,
172
+ "aria-autocomplete": "list",
173
+ "aria-controls": Y ? R : void 0,
174
+ "aria-activedescendant": Y && J && q !== null ? `${R}-opt-${q}` : void 0
175
+ })
176
+ }),
177
+ Y ? /* @__PURE__ */ c(l.Portal, { children: /* @__PURE__ */ c(l.Content, {
178
+ side: "bottom",
179
+ sideOffset: 4,
180
+ align: "start",
181
+ avoidCollisions: !0,
182
+ collisionPadding: 8,
183
+ onPointerMove: (e) => {
184
+ V.current && V.current.x === e.clientX && V.current.y === e.clientY || (V.current = {
185
+ x: e.clientX,
186
+ y: e.clientY
187
+ });
188
+ },
189
+ onEscapeKeyDown: (e) => {
190
+ let t = e;
191
+ (t.isComposing || t.nativeEvent?.isComposing) && e.preventDefault();
192
+ },
193
+ onOpenAutoFocus: (e) => e.preventDefault(),
194
+ onCloseAutoFocus: (e) => e.preventDefault(),
195
+ onInteractOutside: (e) => {
196
+ let t = e.target;
197
+ z.current?.contains(t) && e.preventDefault();
158
198
  },
159
- onMouseDown: (e) => e.preventDefault(),
160
- onClick: () => Q(n),
161
- children: n.label
162
- }, `${B}-opt-${r}`))
163
- }) : null
164
- ]
199
+ id: R,
200
+ role: "listbox",
201
+ className: n("w-[var(--radix-popover-trigger-width)] max-h-[var(--radix-popover-content-available-height)] overflow-y-auto"),
202
+ children: K.map((n, r) => /* @__PURE__ */ c("button", {
203
+ type: "button",
204
+ id: `${R}-opt-${r}`,
205
+ role: "option",
206
+ tabIndex: -1,
207
+ disabled: n.disabled,
208
+ "aria-disabled": n.disabled || void 0,
209
+ "aria-selected": r === q,
210
+ className: e(t("hover:bg-basalt-accent"), r === q && "bg-basalt-accent", n.disabled && "opacity-50"),
211
+ onPointerMove: (e) => {
212
+ if (n.disabled) return;
213
+ let { clientX: t, clientY: i } = e;
214
+ V.current && V.current.x === t && V.current.y === i || (V.current = {
215
+ x: t,
216
+ y: i
217
+ }, L(r));
218
+ },
219
+ onMouseDown: (e) => e.preventDefault(),
220
+ onClick: () => Q(n),
221
+ children: n.label
222
+ }, `${R}-opt-${r}`))
223
+ }) }) : null
224
+ ]
225
+ })
165
226
  });
166
227
  }
167
228
  function d(e, t) {
@@ -1 +1 @@
1
- {"version":3,"file":"typeahead-field.js","names":[],"sources":["../../src/components/typeahead-field.tsx"],"sourcesContent":["import { type ComponentPropsWithoutRef, useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Input, type InputSize } from \"./input\";\nimport { overlayItemClass, overlayPanelClass } from \"./overlay\";\n\nexport type TypeaheadItem = {\n\tvalue: string;\n\tlabel: string;\n\tdisabled?: boolean;\n};\n\nexport function TypeaheadField({\n\titems,\n\tvalue,\n\tdefaultValue = \"\",\n\tonValueChange,\n\tname,\n\tplaceholder = \"Select…\",\n\tdisabled = false,\n\tloading = false,\n\tsize = \"default\",\n\tclassName,\n\tid,\n\tallowFreeform,\n\t\"aria-label\": ariaLabel,\n\t\"aria-describedby\": ariaDescribedBy,\n\t\"aria-invalid\": ariaInvalid,\n\tonBlur,\n\t...rest\n}: {\n\titems: TypeaheadItem[];\n\tvalue?: string;\n\tdefaultValue?: string;\n\tonValueChange?: (value: string) => void;\n\tname?: string;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tloading?: boolean;\n\tsize?: InputSize;\n\tclassName?: string;\n\tid?: string;\n\tallowFreeform: boolean;\n\t\"aria-label\"?: string;\n\t\"aria-describedby\"?: string;\n\t\"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\";\n} & Omit<ComponentPropsWithoutRef<\"div\">, \"children\" | \"defaultValue\" | \"onChange\">) {\n\tconst [uncontrolled, setUncontrolled] = useState(defaultValue);\n\tconst selected = value ?? uncontrolled;\n\tconst [query, setQuery] = useState(displayOf(items, selected));\n\tconst [prevValue, setPrevValue] = useState(value);\n\tconst [open, setOpen] = useState(false);\n\tconst [active, setActive] = useState<number | null>(null);\n\tconst listId = useId();\n\tconst inputRef = useRef<HTMLInputElement>(null);\n\tconst skipFocusOpen = useRef(false);\n\tif (value !== prevValue) {\n\t\tsetPrevValue(value);\n\t\tif (value !== undefined) {\n\t\t\tsetQuery(displayOf(items, value));\n\t\t} else if (prevValue !== undefined) {\n\t\t\tsetUncontrolled(prevValue);\n\t\t\tsetQuery(displayOf(items, prevValue));\n\t\t}\n\t}\n\tconst selectedDisplay = displayOf(items, selected);\n\tconst [prevDisplay, setPrevDisplay] = useState(selectedDisplay);\n\tif (selectedDisplay !== prevDisplay) {\n\t\tsetPrevDisplay(selectedDisplay);\n\t\tif (!open || query === prevDisplay) {\n\t\t\tsetQuery(selectedDisplay);\n\t\t}\n\t}\n\tuseEffect(() => {\n\t\tconst node = inputRef.current;\n\t\tconst form = node?.form;\n\t\tif (!form) {\n\t\t\treturn;\n\t\t}\n\t\tconst onReset = (event: Event) => {\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tif (event.defaultPrevented) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (value === undefined) {\n\t\t\t\t\tsetUncontrolled(defaultValue);\n\t\t\t\t\tsetQuery(displayOf(items, defaultValue));\n\t\t\t\t} else {\n\t\t\t\t\tsetQuery(displayOf(items, value));\n\t\t\t\t}\n\t\t\t\tsetOpen(false);\n\t\t\t\tsetActive(null);\n\t\t\t});\n\t\t};\n\t\tform.addEventListener(\"reset\", onReset);\n\t\treturn () => form.removeEventListener(\"reset\", onReset);\n\t}, [defaultValue, items, value]);\n\tconst filtered = filterItems(items, query, !allowFreeform);\n\tconst activeIndex =\n\t\tactive === null || filtered.length === 0 ? null : Math.min(active, filtered.length - 1);\n\tconst activeItem = activeIndex === null ? undefined : filtered[activeIndex];\n\tconst listOpen = open && filtered.length > 0;\n\tconst busy = disabled || loading;\n\n\tuseEffect(() => {\n\t\tif (!open) {\n\t\t\treturn;\n\t\t}\n\t\tconst nextLen = filterItems(items, query, !allowFreeform).length;\n\t\tsetActive((current) => {\n\t\t\tif (current === null || nextLen === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn Math.min(current, nextLen - 1);\n\t\t});\n\t}, [allowFreeform, items, open, query]);\n\n\tfunction commitValue(next: string, display: string) {\n\t\tif (value === undefined) {\n\t\t\tsetUncontrolled(next);\n\t\t\tsetQuery(display);\n\t\t} else {\n\t\t\tsetQuery(displayOf(items, value));\n\t\t}\n\t\tsetOpen(false);\n\t\tsetActive(null);\n\t\tonValueChange?.(next);\n\t\tif (inputRef.current && document.activeElement !== inputRef.current) {\n\t\t\tskipFocusOpen.current = true;\n\t\t\tinputRef.current.focus();\n\t\t}\n\t}\n\n\tfunction commitItem(item: TypeaheadItem) {\n\t\tif (item.disabled) {\n\t\t\treturn;\n\t\t}\n\t\tcommitValue(item.value, item.label);\n\t}\n\n\tfunction commitFreeform(text: string) {\n\t\tconst trimmed = text.trim();\n\t\tconst match = items.find(\n\t\t\t(item) =>\n\t\t\t\t!item.disabled &&\n\t\t\t\t(item.label.toLowerCase() === trimmed.toLowerCase() || item.value === trimmed),\n\t\t);\n\t\tif (match) {\n\t\t\tcommitItem(match);\n\t\t\treturn;\n\t\t}\n\t\tcommitValue(trimmed, trimmed);\n\t}\n\n\tuseEffect(() => {\n\t\tif (!open) {\n\t\t\treturn;\n\t\t}\n\t\tconst onKey = (event: KeyboardEvent) => {\n\t\t\tif (event.key !== \"Escape\" || event.isComposing) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t\tevent.stopImmediatePropagation();\n\t\t\tsetOpen(false);\n\t\t\tsetQuery(displayOf(items, selected));\n\t\t\tsetActive(null);\n\t\t};\n\t\twindow.addEventListener(\"keydown\", onKey, true);\n\t\tdocument.addEventListener(\"keydown\", onKey, true);\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"keydown\", onKey, true);\n\t\t\tdocument.removeEventListener(\"keydown\", onKey, true);\n\t\t};\n\t}, [items, open, selected]);\n\n\treturn (\n\t\t<div\n\t\t\t{...rest}\n\t\t\tclassName={cn(\"relative w-full\", className)}\n\t\t\tonBlur={(event) => {\n\t\t\t\tonBlur?.(event);\n\t\t\t\tif (!event.currentTarget.contains(event.relatedTarget as Node | null)) {\n\t\t\t\t\tif (allowFreeform) {\n\t\t\t\t\t\tif (query.trim() !== displayOf(items, selected)) {\n\t\t\t\t\t\t\tcommitFreeform(query);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsetQuery(displayOf(items, selected));\n\t\t\t\t\t}\n\t\t\t\t\tsetOpen(false);\n\t\t\t\t\tsetActive(null);\n\t\t\t\t}\n\t\t\t}}\n\t\t>\n\t\t\t{name ? <input type=\"hidden\" name={name} value={selected} disabled={busy} /> : null}\n\t\t\t<Input\n\t\t\t\tref={inputRef}\n\t\t\t\tid={id}\n\t\t\t\tvalue={query}\n\t\t\t\tdisabled={busy}\n\t\t\t\tsize={size}\n\t\t\t\taria-busy={loading || undefined}\n\t\t\t\taria-invalid={ariaInvalid}\n\t\t\t\taria-describedby={ariaDescribedBy}\n\t\t\t\tonChange={(event) => {\n\t\t\t\t\tsetQuery(event.target.value);\n\t\t\t\t\tsetOpen(true);\n\t\t\t\t\tsetActive(null);\n\t\t\t\t}}\n\t\t\t\tonFocus={() => {\n\t\t\t\t\tif (busy) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (skipFocusOpen.current) {\n\t\t\t\t\t\tskipFocusOpen.current = false;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tsetActive(null);\n\t\t\t\t\tsetOpen(true);\n\t\t\t\t}}\n\t\t\t\tonClick={() => {\n\t\t\t\t\tif (busy) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (!open) {\n\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t}\n\t\t\t\t\tsetOpen(true);\n\t\t\t\t}}\n\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\tif (event.nativeEvent.isComposing || event.key === \"Process\") {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (event.key === \"ArrowDown\") {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\tif (filtered.length === 0) {\n\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsetActive((current) => (current === null ? 0 : (current + 1) % filtered.length));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (event.key === \"ArrowUp\") {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\tif (filtered.length === 0) {\n\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsetActive((current) =>\n\t\t\t\t\t\t\tcurrent === null\n\t\t\t\t\t\t\t\t? filtered.length - 1\n\t\t\t\t\t\t\t\t: (current - 1 + filtered.length) % filtered.length,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (event.key === \"Enter\") {\n\t\t\t\t\t\tif (open && activeItem) {\n\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\tcommitItem(activeItem);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (allowFreeform && query.trim() !== displayOf(items, selected)) {\n\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\tcommitFreeform(query);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\tplaceholder={placeholder}\n\t\t\t\trole=\"combobox\"\n\t\t\t\taria-label={ariaLabel ?? (id ? undefined : placeholder)}\n\t\t\t\taria-expanded={listOpen}\n\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\taria-controls={listOpen ? listId : undefined}\n\t\t\t\taria-activedescendant={\n\t\t\t\t\tlistOpen && activeItem && activeIndex !== null\n\t\t\t\t\t\t? `${listId}-opt-${activeIndex}`\n\t\t\t\t\t\t: undefined\n\t\t\t\t}\n\t\t\t/>\n\t\t\t{listOpen ? (\n\t\t\t\t<div\n\t\t\t\t\tid={listId}\n\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\tclassName={overlayPanelClass(\"absolute top-full z-20 mt-1 w-full\")}\n\t\t\t\t>\n\t\t\t\t\t{filtered.map((item, index) => (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tkey={`${listId}-opt-${index}`}\n\t\t\t\t\t\t\tid={`${listId}-opt-${index}`}\n\t\t\t\t\t\t\trole=\"option\"\n\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\tdisabled={item.disabled}\n\t\t\t\t\t\t\taria-disabled={item.disabled || undefined}\n\t\t\t\t\t\t\taria-selected={index === activeIndex}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\toverlayItemClass(\"hover:bg-basalt-accent\"),\n\t\t\t\t\t\t\t\tindex === activeIndex && \"bg-basalt-accent\",\n\t\t\t\t\t\t\t\titem.disabled && \"opacity-50\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tonMouseEnter={() => {\n\t\t\t\t\t\t\t\tif (!item.disabled) {\n\t\t\t\t\t\t\t\t\tsetActive(index);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonMouseDown={(event) => event.preventDefault()}\n\t\t\t\t\t\t\tonClick={() => commitItem(item)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{item.label}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t</div>\n\t);\n}\n\nfunction displayOf(items: TypeaheadItem[], value: string) {\n\treturn items.find((item) => item.value === value)?.label ?? value;\n}\n\nfunction filterItems(items: TypeaheadItem[], query: string, showAllWhenEmpty: boolean) {\n\tconst needle = query.trim().toLowerCase();\n\tif (needle === \"\") {\n\t\treturn showAllWhenEmpty ? items : [];\n\t}\n\treturn items.filter((item) => item.label.toLowerCase().includes(needle));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"typeahead-field.js","names":[],"sources":["../../src/components/typeahead-field.tsx"],"sourcesContent":["import * as PopoverPrimitive from \"@radix-ui/react-popover\";\nimport { type ComponentPropsWithoutRef, useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Input, type InputSize } from \"./input\";\nimport { OVERLAY_GAP, overlayItemClass, overlayPanelClass } from \"./overlay\";\n\nexport type TypeaheadItem = {\n\tvalue: string;\n\tlabel: string;\n\tdisabled?: boolean;\n};\n\nexport function TypeaheadField({\n\titems,\n\tvalue,\n\tdefaultValue = \"\",\n\tonValueChange,\n\tname,\n\tplaceholder = \"Select…\",\n\tdisabled = false,\n\tloading = false,\n\tsize = \"default\",\n\tclassName,\n\tid,\n\tallowFreeform,\n\t\"aria-label\": ariaLabel,\n\t\"aria-describedby\": ariaDescribedBy,\n\t\"aria-invalid\": ariaInvalid,\n\tonBlur,\n\t...rest\n}: {\n\titems: TypeaheadItem[];\n\tvalue?: string;\n\tdefaultValue?: string;\n\tonValueChange?: (value: string) => void;\n\tname?: string;\n\tplaceholder?: string;\n\tdisabled?: boolean;\n\tloading?: boolean;\n\tsize?: InputSize;\n\tclassName?: string;\n\tid?: string;\n\tallowFreeform: boolean;\n\t\"aria-label\"?: string;\n\t\"aria-describedby\"?: string;\n\t\"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\";\n} & Omit<ComponentPropsWithoutRef<\"div\">, \"children\" | \"defaultValue\" | \"onChange\">) {\n\tconst [uncontrolled, setUncontrolled] = useState(defaultValue);\n\tconst selected = value ?? uncontrolled;\n\tconst [query, setQuery] = useState(displayOf(items, selected));\n\tconst [prevValue, setPrevValue] = useState(value);\n\tconst [open, setOpen] = useState(false);\n\tconst [active, setActive] = useState<number | null>(null);\n\tconst listId = useId();\n\tconst inputRef = useRef<HTMLInputElement>(null);\n\tconst skipFocusOpen = useRef(false);\n\tconst lastPointerPos = useRef<{ x: number; y: number } | null>(null);\n\tif (value !== prevValue) {\n\t\tsetPrevValue(value);\n\t\tif (value !== undefined) {\n\t\t\tsetQuery(displayOf(items, value));\n\t\t} else if (prevValue !== undefined) {\n\t\t\tsetUncontrolled(prevValue);\n\t\t\tsetQuery(displayOf(items, prevValue));\n\t\t}\n\t}\n\tconst selectedDisplay = displayOf(items, selected);\n\tconst [prevDisplay, setPrevDisplay] = useState(selectedDisplay);\n\tif (selectedDisplay !== prevDisplay) {\n\t\tsetPrevDisplay(selectedDisplay);\n\t\tif (!open || query === prevDisplay) {\n\t\t\tsetQuery(selectedDisplay);\n\t\t}\n\t}\n\tconst latestConfigRef = useRef({\n\t\tdefaultValue,\n\t\titems,\n\t\tvalue,\n\t});\n\tuseEffect(() => {\n\t\tlatestConfigRef.current = {\n\t\t\tdefaultValue,\n\t\t\titems,\n\t\t\tvalue,\n\t\t};\n\t});\n\n\tuseEffect(() => {\n\t\tconst node = inputRef.current;\n\t\tconst form = node?.form;\n\t\tif (!form) {\n\t\t\treturn;\n\t\t}\n\t\tlet disposed = false;\n\t\tconst timers = new Set<ReturnType<typeof setTimeout>>();\n\t\tconst onReset = (event: Event) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\ttimers.delete(timer);\n\t\t\t\tif (disposed || event.defaultPrevented) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst cfg = latestConfigRef.current;\n\t\t\t\tif (cfg.value === undefined) {\n\t\t\t\t\tsetUncontrolled(cfg.defaultValue);\n\t\t\t\t\tsetQuery(displayOf(cfg.items, cfg.defaultValue));\n\t\t\t\t} else {\n\t\t\t\t\tsetQuery(displayOf(cfg.items, cfg.value));\n\t\t\t\t}\n\t\t\t\tsetOpen(false);\n\t\t\t\tsetActive(null);\n\t\t\t}, 0);\n\t\t\ttimers.add(timer);\n\t\t};\n\t\tform.addEventListener(\"reset\", onReset);\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\tfor (const timer of timers) {\n\t\t\t\tclearTimeout(timer);\n\t\t\t}\n\t\t\ttimers.clear();\n\t\t\tform.removeEventListener(\"reset\", onReset);\n\t\t};\n\t}, []);\n\tconst filtered = filterItems(items, query, !allowFreeform);\n\tconst activeIndex =\n\t\tactive === null || filtered.length === 0 ? null : Math.min(active, filtered.length - 1);\n\tconst activeItem = activeIndex === null ? undefined : filtered[activeIndex];\n\tconst listOpen = open && filtered.length > 0;\n\tconst busy = disabled || loading;\n\n\tuseEffect(() => {\n\t\tif (!listOpen || activeIndex === null) {\n\t\t\treturn;\n\t\t}\n\t\tconst optionElement = document.getElementById(`${listId}-opt-${activeIndex}`);\n\t\tif (typeof optionElement?.scrollIntoView === \"function\") {\n\t\t\toptionElement.scrollIntoView({ block: \"nearest\" });\n\t\t}\n\t}, [activeIndex, listId, listOpen]);\n\n\tuseEffect(() => {\n\t\tif (!open) {\n\t\t\treturn;\n\t\t}\n\t\tconst nextLen = filterItems(items, query, !allowFreeform).length;\n\t\tsetActive((current) => {\n\t\t\tif (current === null || nextLen === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn Math.min(current, nextLen - 1);\n\t\t});\n\t}, [allowFreeform, items, open, query]);\n\n\tfunction commitValue(next: string, display: string, restoreFocus = true) {\n\t\tif (value === undefined) {\n\t\t\tsetUncontrolled(next);\n\t\t\tsetQuery(display);\n\t\t} else {\n\t\t\tsetQuery(displayOf(items, value));\n\t\t}\n\t\tsetOpen(false);\n\t\tsetActive(null);\n\t\tonValueChange?.(next);\n\t\tif (restoreFocus && inputRef.current && document.activeElement !== inputRef.current) {\n\t\t\tskipFocusOpen.current = true;\n\t\t\tinputRef.current.focus();\n\t\t}\n\t}\n\n\tfunction commitItem(item: TypeaheadItem, restoreFocus = true) {\n\t\tif (item.disabled) {\n\t\t\treturn;\n\t\t}\n\t\tcommitValue(item.value, item.label, restoreFocus);\n\t}\n\n\tfunction commitFreeform(text: string, restoreFocus = true) {\n\t\tconst trimmed = text.trim();\n\t\tconst match = items.find(\n\t\t\t(item) =>\n\t\t\t\t!item.disabled &&\n\t\t\t\t(item.label.toLowerCase() === trimmed.toLowerCase() || item.value === trimmed),\n\t\t);\n\t\tif (match) {\n\t\t\tcommitItem(match, restoreFocus);\n\t\t\treturn;\n\t\t}\n\t\tcommitValue(trimmed, trimmed, restoreFocus);\n\t}\n\n\tuseEffect(() => {\n\t\tif (!open) {\n\t\t\treturn;\n\t\t}\n\t\tconst onKey = (event: KeyboardEvent) => {\n\t\t\tif (event.key !== \"Escape\" || event.isComposing) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t\tevent.stopImmediatePropagation();\n\t\t\tsetOpen(false);\n\t\t\tsetQuery(displayOf(items, selected));\n\t\t\tsetActive(null);\n\t\t};\n\t\twindow.addEventListener(\"keydown\", onKey, true);\n\t\tdocument.addEventListener(\"keydown\", onKey, true);\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"keydown\", onKey, true);\n\t\t\tdocument.removeEventListener(\"keydown\", onKey, true);\n\t\t};\n\t}, [items, open, selected]);\n\n\treturn (\n\t\t<PopoverPrimitive.Root\n\t\t\topen={listOpen}\n\t\t\tonOpenChange={(nextOpen) => {\n\t\t\t\tif (!nextOpen) {\n\t\t\t\t\tsetOpen(false);\n\t\t\t\t\tsetActive(null);\n\t\t\t\t\tif (!allowFreeform) {\n\t\t\t\t\t\tsetQuery(displayOf(items, selected));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}}\n\t\t>\n\t\t\t<div\n\t\t\t\t{...rest}\n\t\t\t\tclassName={cn(\"relative w-full\", className)}\n\t\t\t\tonBlur={(event) => {\n\t\t\t\t\tonBlur?.(event);\n\t\t\t\t\tconst related = event.relatedTarget as Node | null;\n\t\t\t\t\tconst listbox = document.getElementById(listId);\n\t\t\t\t\tif (!event.currentTarget.contains(related) && !listbox?.contains(related)) {\n\t\t\t\t\t\tif (allowFreeform) {\n\t\t\t\t\t\t\tif (query.trim() !== displayOf(items, selected)) {\n\t\t\t\t\t\t\t\tcommitFreeform(query, false);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsetQuery(displayOf(items, selected));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsetOpen(false);\n\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{name ? <input type=\"hidden\" name={name} value={selected} disabled={busy} /> : null}\n\t\t\t\t<PopoverPrimitive.Anchor asChild>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\tid={id}\n\t\t\t\t\t\tvalue={query}\n\t\t\t\t\t\tdisabled={busy}\n\t\t\t\t\t\tsize={size}\n\t\t\t\t\t\taria-busy={loading || undefined}\n\t\t\t\t\t\taria-invalid={ariaInvalid}\n\t\t\t\t\t\taria-describedby={ariaDescribedBy}\n\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\tsetQuery(event.target.value);\n\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonFocus={() => {\n\t\t\t\t\t\t\tif (busy) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (skipFocusOpen.current) {\n\t\t\t\t\t\t\t\tskipFocusOpen.current = false;\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\tif (busy) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!open) {\n\t\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\tif (event.nativeEvent.isComposing || event.key === \"Process\") {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"ArrowDown\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t\t\tif (filtered.length === 0) {\n\t\t\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsetActive((current) => (current === null ? 0 : (current + 1) % filtered.length));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tsetOpen(true);\n\t\t\t\t\t\t\t\tif (filtered.length === 0) {\n\t\t\t\t\t\t\t\t\tsetActive(null);\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsetActive((current) =>\n\t\t\t\t\t\t\t\t\tcurrent === null\n\t\t\t\t\t\t\t\t\t\t? filtered.length - 1\n\t\t\t\t\t\t\t\t\t\t: (current - 1 + filtered.length) % filtered.length,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"Enter\") {\n\t\t\t\t\t\t\t\tif (open && activeItem) {\n\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\tcommitItem(activeItem);\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (allowFreeform && query.trim() !== displayOf(items, selected)) {\n\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\tcommitFreeform(query);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\t\trole=\"combobox\"\n\t\t\t\t\t\taria-label={ariaLabel ?? (id ? undefined : placeholder)}\n\t\t\t\t\t\taria-expanded={listOpen}\n\t\t\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\t\t\taria-controls={listOpen ? listId : undefined}\n\t\t\t\t\t\taria-activedescendant={\n\t\t\t\t\t\t\tlistOpen && activeItem && activeIndex !== null\n\t\t\t\t\t\t\t\t? `${listId}-opt-${activeIndex}`\n\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t</PopoverPrimitive.Anchor>\n\t\t\t\t{listOpen ? (\n\t\t\t\t\t<PopoverPrimitive.Portal>\n\t\t\t\t\t\t<PopoverPrimitive.Content\n\t\t\t\t\t\t\tside=\"bottom\"\n\t\t\t\t\t\t\tsideOffset={OVERLAY_GAP}\n\t\t\t\t\t\t\talign=\"start\"\n\t\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\t\tonPointerMove={(event) => {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\tlastPointerPos.current &&\n\t\t\t\t\t\t\t\t\tlastPointerPos.current.x === event.clientX &&\n\t\t\t\t\t\t\t\t\tlastPointerPos.current.y === event.clientY\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tlastPointerPos.current = { x: event.clientX, y: event.clientY };\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonEscapeKeyDown={(e) => {\n\t\t\t\t\t\t\t\tconst evt = e as unknown as {\n\t\t\t\t\t\t\t\t\tisComposing?: boolean;\n\t\t\t\t\t\t\t\t\tnativeEvent?: { isComposing?: boolean };\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tif (evt.isComposing || evt.nativeEvent?.isComposing) {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tonOpenAutoFocus={(e) => e.preventDefault()}\n\t\t\t\t\t\t\tonCloseAutoFocus={(e) => e.preventDefault()}\n\t\t\t\t\t\t\tonInteractOutside={(e) => {\n\t\t\t\t\t\t\t\tconst target = e.target as Node | null;\n\t\t\t\t\t\t\t\tif (inputRef.current?.contains(target)) {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tid={listId}\n\t\t\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\t\t\tclassName={overlayPanelClass(\n\t\t\t\t\t\t\t\t\"w-[var(--radix-popover-trigger-width)] max-h-[var(--radix-popover-content-available-height)] overflow-y-auto\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{filtered.map((item, index) => (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tkey={`${listId}-opt-${index}`}\n\t\t\t\t\t\t\t\t\tid={`${listId}-opt-${index}`}\n\t\t\t\t\t\t\t\t\trole=\"option\"\n\t\t\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\t\t\tdisabled={item.disabled}\n\t\t\t\t\t\t\t\t\taria-disabled={item.disabled || undefined}\n\t\t\t\t\t\t\t\t\taria-selected={index === activeIndex}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\toverlayItemClass(\"hover:bg-basalt-accent\"),\n\t\t\t\t\t\t\t\t\t\tindex === activeIndex && \"bg-basalt-accent\",\n\t\t\t\t\t\t\t\t\t\titem.disabled && \"opacity-50\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\tonPointerMove={(event) => {\n\t\t\t\t\t\t\t\t\t\tif (item.disabled) {\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tconst { clientX, clientY } = event;\n\t\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\t\tlastPointerPos.current &&\n\t\t\t\t\t\t\t\t\t\t\tlastPointerPos.current.x === clientX &&\n\t\t\t\t\t\t\t\t\t\t\tlastPointerPos.current.y === clientY\n\t\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tlastPointerPos.current = { x: clientX, y: clientY };\n\t\t\t\t\t\t\t\t\t\tsetActive(index);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonMouseDown={(event) => event.preventDefault()}\n\t\t\t\t\t\t\t\t\tonClick={() => commitItem(item)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{item.label}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</PopoverPrimitive.Content>\n\t\t\t\t\t</PopoverPrimitive.Portal>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t</PopoverPrimitive.Root>\n\t);\n}\n\nfunction displayOf(items: TypeaheadItem[], value: string) {\n\treturn items.find((item) => item.value === value)?.label ?? value;\n}\n\nfunction filterItems(items: TypeaheadItem[], query: string, showAllWhenEmpty: boolean) {\n\tconst needle = query.trim().toLowerCase();\n\tif (needle === \"\") {\n\t\treturn showAllWhenEmpty ? items : [];\n\t}\n\treturn items.filter((item) => item.label.toLowerCase().includes(needle));\n}\n"],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { ReactNode } from "react";
2
+ export interface UploadFile {
3
+ /** Stable queue identifier. */
4
+ id: string;
5
+ /** Display filename. */
6
+ name: string;
7
+ /** Optional size in bytes. */
8
+ size?: number;
9
+ /** Application-owned transport state. */
10
+ status: "queued" | "uploading" | "success" | "error" | "cancelled";
11
+ /** Progress percentage; omitted means indeterminate. */
12
+ progress?: number;
13
+ /** Application's error message. */
14
+ error?: string;
15
+ /** Optional image/icon. The caller creates and revokes any object URLs. */
16
+ preview?: ReactNode;
17
+ }
18
+ export interface UploadLabels {
19
+ queued?: string;
20
+ uploading?: string;
21
+ success?: string;
22
+ error?: string;
23
+ cancelled?: string;
24
+ cancel?: string;
25
+ retry?: string;
26
+ remove?: string;
27
+ }
28
+ export interface UploadItemProps {
29
+ /** Current queue entry. The component does not start a network request. */
30
+ file: UploadFile;
31
+ /** Cancel a queued or uploading entry. */
32
+ onCancel?: (id: string) => void;
33
+ /** Retry a failed or cancelled entry. */
34
+ onRetry?: (id: string) => void;
35
+ /** Remove an entry that is not currently uploading. */
36
+ onRemove?: (id: string) => void;
37
+ /** Localized statuses and action labels. */
38
+ labels?: UploadLabels;
39
+ /** Additional root classes. */
40
+ className?: string;
41
+ }
42
+ export declare function UploadItem({ file, onCancel, onRetry, onRemove, labels, className, }: UploadItemProps): import("react").JSX.Element;
43
+ export interface UploadQueueProps extends Omit<UploadItemProps, "file"> {
44
+ /** Accessible queue name. */
45
+ label: string;
46
+ /** Application-owned queue entries. */
47
+ files: readonly UploadFile[];
48
+ /** Empty queue text. @default "No files selected." */
49
+ emptyLabel?: string;
50
+ }
51
+ export declare function UploadQueue({ label, files, emptyLabel, className, ...actions }: UploadQueueProps): import("react").JSX.Element;
@@ -0,0 +1,116 @@
1
+ "use client";
2
+ import { cn as e } from "../utils/cn.js";
3
+ import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
4
+ import { Button as n } from "./button.js";
5
+ import { Check as r, FileText as i, RotateCcw as a, Trash2 as o, X as s } from "lucide-react";
6
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
7
+ //#region src/components/upload-queue.tsx
8
+ var u = {
9
+ queued: "Queued",
10
+ uploading: "Uploading",
11
+ success: "Uploaded",
12
+ error: "Upload failed",
13
+ cancelled: "Cancelled",
14
+ cancel: "Cancel",
15
+ retry: "Retry",
16
+ remove: "Remove"
17
+ };
18
+ function d({ file: d, onCancel: f, onRetry: p, onRemove: m, labels: h, className: g }) {
19
+ let _ = {
20
+ ...u,
21
+ ...h
22
+ }, v = typeof d.progress == "number" && Number.isFinite(d.progress) ? Math.max(0, Math.min(100, d.progress)) : void 0, y = d.status === "queued" || d.status === "uploading";
23
+ return /* @__PURE__ */ l("div", {
24
+ className: e(t, "flex min-w-0 items-start gap-3 rounded-basalt-lg border border-basalt-border bg-basalt-card p-3", g),
25
+ children: [
26
+ /* @__PURE__ */ c("span", {
27
+ className: "flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-basalt-md bg-basalt-accent",
28
+ children: d.preview ?? /* @__PURE__ */ c(i, {
29
+ className: "size-5 text-basalt-muted-foreground",
30
+ "aria-hidden": "true"
31
+ })
32
+ }),
33
+ /* @__PURE__ */ l("div", {
34
+ className: "min-w-0 flex-1 space-y-1",
35
+ children: [
36
+ /* @__PURE__ */ c("p", {
37
+ className: "break-words text-sm font-medium",
38
+ children: d.name
39
+ }),
40
+ /* @__PURE__ */ l("p", {
41
+ className: "text-xs text-basalt-muted-foreground",
42
+ children: [d.size !== void 0 && `${Math.ceil(d.size / 1024).toLocaleString()} KB · `, _[d.status]]
43
+ }),
44
+ d.status === "uploading" && /* @__PURE__ */ c("div", {
45
+ role: "progressbar",
46
+ "aria-label": d.name,
47
+ "aria-valuemin": 0,
48
+ "aria-valuemax": 100,
49
+ "aria-valuenow": v,
50
+ className: "h-1.5 overflow-hidden rounded-full bg-basalt-accent",
51
+ children: /* @__PURE__ */ c("div", {
52
+ style: { width: v === void 0 ? "40%" : `${v}%` },
53
+ className: e("h-full rounded-full bg-basalt-primary", v === void 0 && "animate-pulse motion-reduce:animate-none")
54
+ })
55
+ }),
56
+ d.status === "error" && d.error && /* @__PURE__ */ c("p", {
57
+ role: "alert",
58
+ className: "break-words text-xs text-basalt-destructive",
59
+ children: d.error
60
+ })
61
+ ]
62
+ }),
63
+ /* @__PURE__ */ l("div", {
64
+ className: "flex shrink-0 items-center gap-1",
65
+ children: [
66
+ d.status === "success" && /* @__PURE__ */ c(r, {
67
+ className: "size-4 text-basalt-success",
68
+ "aria-hidden": "true"
69
+ }),
70
+ y && f && /* @__PURE__ */ c(n, {
71
+ size: "icon",
72
+ variant: "ghost",
73
+ "aria-label": `${_.cancel} ${d.name}`,
74
+ onClick: () => f(d.id),
75
+ children: /* @__PURE__ */ c(s, {})
76
+ }),
77
+ (d.status === "error" || d.status === "cancelled") && p && /* @__PURE__ */ c(n, {
78
+ size: "icon",
79
+ variant: "ghost",
80
+ "aria-label": `${_.retry} ${d.name}`,
81
+ onClick: () => p(d.id),
82
+ children: /* @__PURE__ */ c(a, {})
83
+ }),
84
+ d.status !== "uploading" && m && /* @__PURE__ */ c(n, {
85
+ size: "icon",
86
+ variant: "ghost",
87
+ "aria-label": `${_.remove} ${d.name}`,
88
+ onClick: () => m(d.id),
89
+ children: /* @__PURE__ */ c(o, {})
90
+ })
91
+ ]
92
+ })
93
+ ]
94
+ });
95
+ }
96
+ function f({ label: n, files: r, emptyLabel: i = "No files selected.", className: a, ...o }) {
97
+ return /* @__PURE__ */ c("div", {
98
+ className: e(t, "min-w-0 space-y-2", a),
99
+ children: r.length === 0 ? /* @__PURE__ */ c("p", {
100
+ role: "status",
101
+ className: "text-sm text-basalt-muted-foreground",
102
+ children: i
103
+ }) : /* @__PURE__ */ c("ul", {
104
+ "aria-label": n,
105
+ className: "m-0 list-none space-y-2 p-0",
106
+ children: r.map((e) => /* @__PURE__ */ c("li", { children: /* @__PURE__ */ c(d, {
107
+ file: e,
108
+ ...o
109
+ }) }, e.id))
110
+ })
111
+ });
112
+ }
113
+ //#endregion
114
+ export { d as UploadItem, f as UploadQueue };
115
+
116
+ //# sourceMappingURL=upload-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-queue.js","names":[],"sources":["../../src/components/upload-queue.tsx"],"sourcesContent":["import { Check, FileText, RotateCcw, Trash2, X } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { Button } from \"./button\";\n\nexport interface UploadFile {\n\t/** Stable queue identifier. */\n\tid: string;\n\t/** Display filename. */\n\tname: string;\n\t/** Optional size in bytes. */\n\tsize?: number;\n\t/** Application-owned transport state. */\n\tstatus: \"queued\" | \"uploading\" | \"success\" | \"error\" | \"cancelled\";\n\t/** Progress percentage; omitted means indeterminate. */\n\tprogress?: number;\n\t/** Application's error message. */\n\terror?: string;\n\t/** Optional image/icon. The caller creates and revokes any object URLs. */\n\tpreview?: ReactNode;\n}\nexport interface UploadLabels {\n\tqueued?: string;\n\tuploading?: string;\n\tsuccess?: string;\n\terror?: string;\n\tcancelled?: string;\n\tcancel?: string;\n\tretry?: string;\n\tremove?: string;\n}\nconst DEFAULT_LABELS = {\n\tqueued: \"Queued\",\n\tuploading: \"Uploading\",\n\tsuccess: \"Uploaded\",\n\terror: \"Upload failed\",\n\tcancelled: \"Cancelled\",\n\tcancel: \"Cancel\",\n\tretry: \"Retry\",\n\tremove: \"Remove\",\n};\n\nexport interface UploadItemProps {\n\t/** Current queue entry. The component does not start a network request. */\n\tfile: UploadFile;\n\t/** Cancel a queued or uploading entry. */\n\tonCancel?: (id: string) => void;\n\t/** Retry a failed or cancelled entry. */\n\tonRetry?: (id: string) => void;\n\t/** Remove an entry that is not currently uploading. */\n\tonRemove?: (id: string) => void;\n\t/** Localized statuses and action labels. */\n\tlabels?: UploadLabels;\n\t/** Additional root classes. */\n\tclassName?: string;\n}\n\nexport function UploadItem({\n\tfile,\n\tonCancel,\n\tonRetry,\n\tonRemove,\n\tlabels,\n\tclassName,\n}: UploadItemProps) {\n\tconst text = { ...DEFAULT_LABELS, ...labels };\n\tconst progress =\n\t\ttypeof file.progress === \"number\" && Number.isFinite(file.progress)\n\t\t\t? Math.max(0, Math.min(100, file.progress))\n\t\t\t: undefined;\n\tconst active = file.status === \"queued\" || file.status === \"uploading\";\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\tBASALT_UI_CLASS,\n\t\t\t\t\"flex min-w-0 items-start gap-3 rounded-basalt-lg border border-basalt-border bg-basalt-card p-3\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t<span className=\"flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-basalt-md bg-basalt-accent\">\n\t\t\t\t{file.preview ?? (\n\t\t\t\t\t<FileText className=\"size-5 text-basalt-muted-foreground\" aria-hidden=\"true\" />\n\t\t\t\t)}\n\t\t\t</span>\n\t\t\t<div className=\"min-w-0 flex-1 space-y-1\">\n\t\t\t\t<p className=\"break-words text-sm font-medium\">{file.name}</p>\n\t\t\t\t<p className=\"text-xs text-basalt-muted-foreground\">\n\t\t\t\t\t{file.size !== undefined && `${Math.ceil(file.size / 1024).toLocaleString()} KB · `}\n\t\t\t\t\t{text[file.status]}\n\t\t\t\t</p>\n\t\t\t\t{file.status === \"uploading\" && (\n\t\t\t\t\t<div\n\t\t\t\t\t\trole=\"progressbar\"\n\t\t\t\t\t\taria-label={file.name}\n\t\t\t\t\t\taria-valuemin={0}\n\t\t\t\t\t\taria-valuemax={100}\n\t\t\t\t\t\taria-valuenow={progress}\n\t\t\t\t\t\tclassName=\"h-1.5 overflow-hidden rounded-full bg-basalt-accent\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tstyle={{ width: progress === undefined ? \"40%\" : `${progress}%` }}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"h-full rounded-full bg-basalt-primary\",\n\t\t\t\t\t\t\t\tprogress === undefined && \"animate-pulse motion-reduce:animate-none\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t{file.status === \"error\" && file.error && (\n\t\t\t\t\t<p role=\"alert\" className=\"break-words text-xs text-basalt-destructive\">\n\t\t\t\t\t\t{file.error}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"flex shrink-0 items-center gap-1\">\n\t\t\t\t{file.status === \"success\" && (\n\t\t\t\t\t<Check className=\"size-4 text-basalt-success\" aria-hidden=\"true\" />\n\t\t\t\t)}\n\t\t\t\t{active && onCancel && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\taria-label={`${text.cancel} ${file.name}`}\n\t\t\t\t\t\tonClick={() => onCancel(file.id)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<X />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t\t{(file.status === \"error\" || file.status === \"cancelled\") && onRetry && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\taria-label={`${text.retry} ${file.name}`}\n\t\t\t\t\t\tonClick={() => onRetry(file.id)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<RotateCcw />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t\t{file.status !== \"uploading\" && onRemove && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\taria-label={`${text.remove} ${file.name}`}\n\t\t\t\t\t\tonClick={() => onRemove(file.id)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Trash2 />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport interface UploadQueueProps extends Omit<UploadItemProps, \"file\"> {\n\t/** Accessible queue name. */\n\tlabel: string;\n\t/** Application-owned queue entries. */\n\tfiles: readonly UploadFile[];\n\t/** Empty queue text. @default \"No files selected.\" */\n\temptyLabel?: string;\n}\n\nexport function UploadQueue({\n\tlabel,\n\tfiles,\n\temptyLabel = \"No files selected.\",\n\tclassName,\n\t...actions\n}: UploadQueueProps) {\n\treturn (\n\t\t<div className={cn(BASALT_UI_CLASS, \"min-w-0 space-y-2\", className)}>\n\t\t\t{files.length === 0 ? (\n\t\t\t\t<p role=\"status\" className=\"text-sm text-basalt-muted-foreground\">\n\t\t\t\t\t{emptyLabel}\n\t\t\t\t</p>\n\t\t\t) : (\n\t\t\t\t<ul aria-label={label} className=\"m-0 list-none space-y-2 p-0\">\n\t\t\t\t\t{files.map((file) => (\n\t\t\t\t\t\t<li key={file.id}>\n\t\t\t\t\t\t\t<UploadItem file={file} {...actions} />\n\t\t\t\t\t\t</li>\n\t\t\t\t\t))}\n\t\t\t\t</ul>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}