@nocoo/basalt 2.2.0 → 3.0.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 (319) hide show
  1. package/README.md +2 -0
  2. package/ai/COMPATIBILITY.md +11 -1
  3. package/ai/DESIGN.md +342 -0
  4. package/ai/INTEGRATION.md +179 -128
  5. package/ai/RECIPES.md +51 -26
  6. package/ai/USAGE.md +4 -4
  7. package/ai/registry.json +2836 -270
  8. package/dist/charts/config.d.ts +11 -11
  9. package/dist/charts/config.js +4 -4
  10. package/dist/charts/config.js.map +1 -1
  11. package/dist/charts/date-navigation.js +10 -10
  12. package/dist/charts/date-navigation.js.map +1 -1
  13. package/dist/charts/donut.js +1 -1
  14. package/dist/charts/donut.js.map +1 -1
  15. package/dist/charts/frame.d.ts +1 -1
  16. package/dist/charts/frame.js +4 -4
  17. package/dist/charts/frame.js.map +1 -1
  18. package/dist/charts/gauge.js +5 -5
  19. package/dist/charts/gauge.js.map +1 -1
  20. package/dist/charts/heatmap-calendar.js +11 -11
  21. package/dist/charts/heatmap-calendar.js.map +1 -1
  22. package/dist/charts/heatmap-matrix.js +8 -8
  23. package/dist/charts/heatmap-matrix.js.map +1 -1
  24. package/dist/charts/item-list.js +1 -1
  25. package/dist/charts/item-list.js.map +1 -1
  26. package/dist/charts/legend.js +19 -19
  27. package/dist/charts/legend.js.map +1 -1
  28. package/dist/charts/palette.js +2 -2
  29. package/dist/charts/palette.js.map +1 -1
  30. package/dist/charts/slot-bar.js +2 -2
  31. package/dist/charts/slot-bar.js.map +1 -1
  32. package/dist/charts/sparkline.js +38 -23
  33. package/dist/charts/sparkline.js.map +1 -1
  34. package/dist/charts/stat-card.d.ts +2 -2
  35. package/dist/charts/stat-card.js +61 -58
  36. package/dist/charts/stat-card.js.map +1 -1
  37. package/dist/charts/timeline.js +18 -9
  38. package/dist/charts/timeline.js.map +1 -1
  39. package/dist/charts/tooltip.js +16 -16
  40. package/dist/charts/tooltip.js.map +1 -1
  41. package/dist/components/accordion.js +1 -1
  42. package/dist/components/accordion.js.map +1 -1
  43. package/dist/components/alert-dialog.js +4 -4
  44. package/dist/components/alert-dialog.js.map +1 -1
  45. package/dist/components/app-header.js +8 -8
  46. package/dist/components/app-header.js.map +1 -1
  47. package/dist/components/app-shell.js +1 -1
  48. package/dist/components/app-shell.js.map +1 -1
  49. package/dist/components/approval-card.d.ts +13 -0
  50. package/dist/components/approval-card.js +161 -0
  51. package/dist/components/approval-card.js.map +1 -0
  52. package/dist/components/avatar.d.ts +12 -0
  53. package/dist/components/avatar.js +39 -18
  54. package/dist/components/avatar.js.map +1 -1
  55. package/dist/components/badge.js +8 -8
  56. package/dist/components/badge.js.map +1 -1
  57. package/dist/components/banner.d.ts +2 -2
  58. package/dist/components/banner.js +9 -8
  59. package/dist/components/banner.js.map +1 -1
  60. package/dist/components/basalt-mark.js +1 -1
  61. package/dist/components/basalt-mark.js.map +1 -1
  62. package/dist/components/battery-meter.js +5 -5
  63. package/dist/components/battery-meter.js.map +1 -1
  64. package/dist/components/breadcrumbs.js +6 -6
  65. package/dist/components/breadcrumbs.js.map +1 -1
  66. package/dist/components/button.js +10 -10
  67. package/dist/components/button.js.map +1 -1
  68. package/dist/components/chat-bubble.js +4 -3
  69. package/dist/components/chat-bubble.js.map +1 -1
  70. package/dist/components/chat-composer.d.ts +21 -3
  71. package/dist/components/chat-composer.js +126 -60
  72. package/dist/components/chat-composer.js.map +1 -1
  73. package/dist/components/chat-header.js +6 -6
  74. package/dist/components/chat-header.js.map +1 -1
  75. package/dist/components/chat-inbox.js +34 -30
  76. package/dist/components/chat-inbox.js.map +1 -1
  77. package/dist/components/chat-markdown.d.ts +8 -0
  78. package/dist/components/chat-markdown.js +122 -0
  79. package/dist/components/chat-markdown.js.map +1 -0
  80. package/dist/components/chat-message.d.ts +26 -0
  81. package/dist/components/chat-message.js +145 -0
  82. package/dist/components/chat-message.js.map +1 -0
  83. package/dist/components/checkbox.js +11 -11
  84. package/dist/components/checkbox.js.map +1 -1
  85. package/dist/components/clipboard-text.js +3 -3
  86. package/dist/components/clipboard-text.js.map +1 -1
  87. package/dist/components/code.d.ts +22 -20
  88. package/dist/components/code.js +94 -32
  89. package/dist/components/code.js.map +1 -1
  90. package/dist/components/collapsible.js +8 -4
  91. package/dist/components/collapsible.js.map +1 -1
  92. package/dist/components/command-palette.js +73 -47
  93. package/dist/components/command-palette.js.map +1 -1
  94. package/dist/components/context-cards.d.ts +15 -0
  95. package/dist/components/context-cards.js +148 -0
  96. package/dist/components/context-cards.js.map +1 -0
  97. package/dist/components/context-menu.d.ts +1 -1
  98. package/dist/components/context-menu.js +18 -14
  99. package/dist/components/context-menu.js.map +1 -1
  100. package/dist/components/data-table.d.ts +8 -2
  101. package/dist/components/data-table.js +161 -106
  102. package/dist/components/data-table.js.map +1 -1
  103. package/dist/components/date-picker.js +13 -13
  104. package/dist/components/date-picker.js.map +1 -1
  105. package/dist/components/delete-resource.js +1 -1
  106. package/dist/components/delete-resource.js.map +1 -1
  107. package/dist/components/description-list.js +3 -3
  108. package/dist/components/description-list.js.map +1 -1
  109. package/dist/components/dialog.d.ts +2 -2
  110. package/dist/components/dialog.js +8 -8
  111. package/dist/components/dialog.js.map +1 -1
  112. package/dist/components/diff-table.d.ts +15 -0
  113. package/dist/components/diff-table.js +146 -0
  114. package/dist/components/diff-table.js.map +1 -0
  115. package/dist/components/dock.js +2 -2
  116. package/dist/components/dock.js.map +1 -1
  117. package/dist/components/dropdown-menu.js +23 -18
  118. package/dist/components/dropdown-menu.js.map +1 -1
  119. package/dist/components/editable-nav-item.js +63 -58
  120. package/dist/components/editable-nav-item.js.map +1 -1
  121. package/dist/components/empty.js +6 -6
  122. package/dist/components/empty.js.map +1 -1
  123. package/dist/components/fab.js +1 -1
  124. package/dist/components/fab.js.map +1 -1
  125. package/dist/components/field.js +3 -3
  126. package/dist/components/field.js.map +1 -1
  127. package/dist/components/file-dropzone.js +7 -7
  128. package/dist/components/file-dropzone.js.map +1 -1
  129. package/dist/components/filter-bar.js +6 -6
  130. package/dist/components/filter-bar.js.map +1 -1
  131. package/dist/components/flow.js +4 -3
  132. package/dist/components/flow.js.map +1 -1
  133. package/dist/components/grid.d.ts +3 -1
  134. package/dist/components/grid.js +15 -8
  135. package/dist/components/grid.js.map +1 -1
  136. package/dist/components/hover-card.js +1 -1
  137. package/dist/components/hover-card.js.map +1 -1
  138. package/dist/components/icon-picker.d.ts +8 -2
  139. package/dist/components/icon-picker.js +75 -47
  140. package/dist/components/icon-picker.js.map +1 -1
  141. package/dist/components/inline-editable.js +6 -6
  142. package/dist/components/inline-editable.js.map +1 -1
  143. package/dist/components/input-area.js +3 -3
  144. package/dist/components/input-area.js.map +1 -1
  145. package/dist/components/input-group.js +5 -5
  146. package/dist/components/input-group.js.map +1 -1
  147. package/dist/components/input.js +4 -4
  148. package/dist/components/input.js.map +1 -1
  149. package/dist/components/label.js +3 -3
  150. package/dist/components/label.js.map +1 -1
  151. package/dist/components/layer-card.d.ts +9 -4
  152. package/dist/components/layer-card.js +72 -67
  153. package/dist/components/layer-card.js.map +1 -1
  154. package/dist/components/loader.d.ts +16 -10
  155. package/dist/components/loader.js +46 -53
  156. package/dist/components/loader.js.map +1 -1
  157. package/dist/components/loading-screen.js +3 -3
  158. package/dist/components/loading-screen.js.map +1 -1
  159. package/dist/components/menu-bar.js +32 -27
  160. package/dist/components/menu-bar.js.map +1 -1
  161. package/dist/components/meter.d.ts +5 -5
  162. package/dist/components/meter.js +25 -12
  163. package/dist/components/meter.js.map +1 -1
  164. package/dist/components/multi-select.js +66 -62
  165. package/dist/components/multi-select.js.map +1 -1
  166. package/dist/components/navigation-menu.d.ts +2 -2
  167. package/dist/components/navigation-menu.js +22 -8
  168. package/dist/components/navigation-menu.js.map +1 -1
  169. package/dist/components/overlay.js +2 -2
  170. package/dist/components/overlay.js.map +1 -1
  171. package/dist/components/page-header.d.ts +3 -1
  172. package/dist/components/page-header.js +12 -12
  173. package/dist/components/page-header.js.map +1 -1
  174. package/dist/components/pagination.js +3 -3
  175. package/dist/components/pagination.js.map +1 -1
  176. package/dist/components/popover.js +18 -23
  177. package/dist/components/popover.js.map +1 -1
  178. package/dist/components/prompt-bar.d.ts +16 -0
  179. package/dist/components/prompt-bar.js +49 -0
  180. package/dist/components/prompt-bar.js.map +1 -0
  181. package/dist/components/radio.js +9 -9
  182. package/dist/components/radio.js.map +1 -1
  183. package/dist/components/recommendation-card.d.ts +13 -0
  184. package/dist/components/recommendation-card.js +123 -0
  185. package/dist/components/recommendation-card.js.map +1 -0
  186. package/dist/components/resource-list.js +1 -1
  187. package/dist/components/resource-list.js.map +1 -1
  188. package/dist/components/responsive-master-detail.js +4 -4
  189. package/dist/components/responsive-master-detail.js.map +1 -1
  190. package/dist/components/scroll-area.js +3 -3
  191. package/dist/components/scroll-area.js.map +1 -1
  192. package/dist/components/section-rule.d.ts +5 -3
  193. package/dist/components/section-rule.js +12 -12
  194. package/dist/components/section-rule.js.map +1 -1
  195. package/dist/components/segment-control.js +3 -3
  196. package/dist/components/segment-control.js.map +1 -1
  197. package/dist/components/select.js +47 -32
  198. package/dist/components/select.js.map +1 -1
  199. package/dist/components/sensitive-input.js +17 -19
  200. package/dist/components/sensitive-input.js.map +1 -1
  201. package/dist/components/sheet.d.ts +4 -4
  202. package/dist/components/sheet.js +11 -10
  203. package/dist/components/sheet.js.map +1 -1
  204. package/dist/components/sidebar.js +146 -137
  205. package/dist/components/sidebar.js.map +1 -1
  206. package/dist/components/skeleton-line.d.ts +1 -1
  207. package/dist/components/skeleton-line.js +2 -2
  208. package/dist/components/skeleton-line.js.map +1 -1
  209. package/dist/components/slider.js +2 -2
  210. package/dist/components/slider.js.map +1 -1
  211. package/dist/components/stat-strip.js +5 -4
  212. package/dist/components/stat-strip.js.map +1 -1
  213. package/dist/components/switch.js +6 -6
  214. package/dist/components/switch.js.map +1 -1
  215. package/dist/components/table-of-contents.js +5 -5
  216. package/dist/components/table-of-contents.js.map +1 -1
  217. package/dist/components/table-pager.js +2 -2
  218. package/dist/components/table-pager.js.map +1 -1
  219. package/dist/components/table.js +4 -4
  220. package/dist/components/table.js.map +1 -1
  221. package/dist/components/tabs.js +7 -6
  222. package/dist/components/tabs.js.map +1 -1
  223. package/dist/components/tag-badge.d.ts +10 -10
  224. package/dist/components/tag-badge.js +11 -11
  225. package/dist/components/tag-badge.js.map +1 -1
  226. package/dist/components/tag-color-picker.d.ts +8 -1
  227. package/dist/components/tag-color-picker.js +33 -25
  228. package/dist/components/tag-color-picker.js.map +1 -1
  229. package/dist/components/text.js +5 -5
  230. package/dist/components/text.js.map +1 -1
  231. package/dist/components/theme-toggle.js +1 -1
  232. package/dist/components/theme-toggle.js.map +1 -1
  233. package/dist/components/thinking.d.ts +16 -0
  234. package/dist/components/thinking.js +85 -0
  235. package/dist/components/thinking.js.map +1 -0
  236. package/dist/components/toast.js +25 -20
  237. package/dist/components/toast.js.map +1 -1
  238. package/dist/components/toggle-group.js +6 -5
  239. package/dist/components/toggle-group.js.map +1 -1
  240. package/dist/components/toggle.js +6 -6
  241. package/dist/components/toggle.js.map +1 -1
  242. package/dist/components/tool-chips.d.ts +12 -0
  243. package/dist/components/tool-chips.js +138 -0
  244. package/dist/components/tool-chips.js.map +1 -0
  245. package/dist/components/toolbar.js +3 -3
  246. package/dist/components/toolbar.js.map +1 -1
  247. package/dist/components/tooltip.js +1 -1
  248. package/dist/components/tooltip.js.map +1 -1
  249. package/dist/components/typeahead-field.js +59 -56
  250. package/dist/components/typeahead-field.js.map +1 -1
  251. package/dist/components/upload-queue.js +15 -14
  252. package/dist/components/upload-queue.js.map +1 -1
  253. package/dist/models/agent-feedback.d.ts +44 -0
  254. package/dist/models/agent-feedback.js +28 -0
  255. package/dist/models/agent-feedback.js.map +1 -0
  256. package/dist/models/avatar.d.ts +2 -0
  257. package/dist/models/avatar.js +15 -0
  258. package/dist/models/avatar.js.map +1 -0
  259. package/dist/models/code.d.ts +6 -0
  260. package/dist/models/code.js +24 -0
  261. package/dist/models/code.js.map +1 -0
  262. package/dist/models/context-cards.d.ts +13 -0
  263. package/dist/models/context-cards.js +17 -0
  264. package/dist/models/context-cards.js.map +1 -0
  265. package/dist/models/diff-table.d.ts +12 -0
  266. package/dist/models/diff-table.js +9 -0
  267. package/dist/models/diff-table.js.map +1 -0
  268. package/dist/models/recommendation.d.ts +26 -0
  269. package/dist/models/recommendation.js +23 -0
  270. package/dist/models/recommendation.js.map +1 -0
  271. package/dist/styles/base.css +14 -2
  272. package/dist/styles/standalone.css +1865 -929
  273. package/dist/styles/tailwind.css +179 -13
  274. package/dist/styles/tokens.css +679 -20
  275. package/dist/utils/cn.js +25 -4
  276. package/dist/utils/cn.js.map +1 -1
  277. package/dist/utils/control-surface.d.ts +1 -1
  278. package/dist/utils/control-surface.js +1 -1
  279. package/dist/utils/control-surface.js.map +1 -1
  280. package/dist/utils/navigation.d.ts +3 -0
  281. package/dist/utils/navigation.js +8 -0
  282. package/dist/utils/navigation.js.map +1 -0
  283. package/dist/utils/selection-indicator.d.ts +1 -1
  284. package/dist/utils/selection-indicator.js +49 -72
  285. package/dist/utils/selection-indicator.js.map +1 -1
  286. package/dist/utils/use-hover-highlight.d.ts +4 -0
  287. package/dist/utils/use-hover-highlight.js +80 -0
  288. package/dist/utils/use-hover-highlight.js.map +1 -0
  289. package/dist/viewmodels/use-approval-card.d.ts +20 -0
  290. package/dist/viewmodels/use-approval-card.js +83 -0
  291. package/dist/viewmodels/use-approval-card.js.map +1 -0
  292. package/dist/viewmodels/use-chat-composer.d.ts +16 -0
  293. package/dist/viewmodels/use-chat-composer.js +34 -0
  294. package/dist/viewmodels/use-chat-composer.js.map +1 -0
  295. package/dist/viewmodels/use-chat-message.d.ts +16 -0
  296. package/dist/viewmodels/use-chat-message.js +26 -0
  297. package/dist/viewmodels/use-chat-message.js.map +1 -0
  298. package/dist/viewmodels/use-code-block.d.ts +8 -0
  299. package/dist/viewmodels/use-code-block.js +38 -0
  300. package/dist/viewmodels/use-code-block.js.map +1 -0
  301. package/dist/viewmodels/use-context-cards.d.ts +17 -0
  302. package/dist/viewmodels/use-context-cards.js +18 -0
  303. package/dist/viewmodels/use-context-cards.js.map +1 -0
  304. package/dist/viewmodels/use-diff-table.d.ts +16 -0
  305. package/dist/viewmodels/use-diff-table.js +42 -0
  306. package/dist/viewmodels/use-diff-table.js.map +1 -0
  307. package/dist/viewmodels/use-loader.d.ts +4 -0
  308. package/dist/viewmodels/use-loader.js +22 -0
  309. package/dist/viewmodels/use-loader.js.map +1 -0
  310. package/dist/viewmodels/use-recommendation-card.d.ts +17 -0
  311. package/dist/viewmodels/use-recommendation-card.js +41 -0
  312. package/dist/viewmodels/use-recommendation-card.js.map +1 -0
  313. package/dist/viewmodels/use-thinking.d.ts +13 -0
  314. package/dist/viewmodels/use-thinking.js +18 -0
  315. package/dist/viewmodels/use-thinking.js.map +1 -0
  316. package/dist/viewmodels/use-tool-chips.d.ts +10 -0
  317. package/dist/viewmodels/use-tool-chips.js +20 -0
  318. package/dist/viewmodels/use-tool-chips.js.map +1 -0
  319. package/package.json +3 -1
@@ -4,19 +4,32 @@ import { jsx as t, jsxs as n } from "react/jsx-runtime";
4
4
  import * as r from "@radix-ui/react-progress";
5
5
  //#region src/components/meter.tsx
6
6
  function i({ value: i = 0, label: a, customValue: o, hideValue: s = !1, className: c, "aria-label": l }) {
7
+ let u = Number.isFinite(i) ? Math.min(100, Math.max(0, i)) : null, d = Math.round((u ?? 0) / 100 * 17), f = Math.floor(d / 3), p = f + Math.ceil(d / 3);
7
8
  return /* @__PURE__ */ n("div", {
8
- className: e("w-full space-y-1", c),
9
- children: [a || !s && o ? /* @__PURE__ */ n("div", {
10
- className: "flex justify-between text-xs text-basalt-muted-foreground",
11
- children: [/* @__PURE__ */ t("span", { children: a }), s ? null : /* @__PURE__ */ t("span", { children: o ?? `${i}%` })]
12
- }) : null, /* @__PURE__ */ t(r.Root, {
13
- value: i,
14
- "aria-label": l ?? a,
15
- className: "relative h-2 overflow-hidden rounded-full bg-basalt-muted",
16
- children: /* @__PURE__ */ t(r.Indicator, {
17
- className: "h-full bg-basalt-primary transition-[width]",
18
- style: { width: `${Math.min(100, Math.max(0, i))}%` }
19
- })
9
+ className: e("w-full space-y-basalt-space-sm", c),
10
+ children: [a ? /* @__PURE__ */ t("div", {
11
+ className: "text-basalt-sm text-basalt-muted-foreground",
12
+ children: a
13
+ }) : null, /* @__PURE__ */ n("div", {
14
+ className: "flex items-center gap-basalt-space-lg",
15
+ children: [/* @__PURE__ */ t(r.Root, {
16
+ "data-basalt-meter": "",
17
+ value: u,
18
+ "aria-label": l ?? a,
19
+ "aria-valuetext": u === null ? "Unavailable" : o,
20
+ className: "h-basalt-3_5 min-w-0 flex-1 overflow-hidden rounded-basalt-sm bg-basalt-muted p-basalt-space-xs",
21
+ children: /* @__PURE__ */ t(r.Indicator, {
22
+ className: "flex h-full gap-basalt-space-xs",
23
+ "aria-hidden": "true",
24
+ children: Array.from({ length: 17 }, (n, r) => /* @__PURE__ */ t("span", {
25
+ "data-filled": r < d,
26
+ className: e("h-full min-w-0 flex-1 rounded-basalt-sm", r >= d ? "bg-basalt-foreground/10" : r < f ? "bg-basalt-danger" : r < p ? "bg-[hsl(var(--basalt-chart-yellow))]" : "bg-[hsl(var(--basalt-chart-green))]")
27
+ }, `segment-${r}`))
28
+ })
29
+ }), s ? null : /* @__PURE__ */ t("span", {
30
+ className: "shrink-0 text-basalt-sm tabular-nums text-basalt-foreground",
31
+ children: o ?? (u === null ? "Unavailable" : `${u}%`)
32
+ })]
20
33
  })]
21
34
  });
22
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"meter.js","names":["Progress","Meter","ariaLabel","cn","className","label","hideValue","customValue","value"],"sources":["../../src/components/meter.tsx"],"sourcesContent":["import * as Progress from \"@radix-ui/react-progress\";\nimport { cn } from \"../utils/cn\";\n\nexport interface MeterProps {\n\t/**\n\t * Percentage value representing current meter progress (0..100).\n\t *\n\t * Note: Rendered bar width is clamped to [0, 100]%; callers should provide 0..100,\n\t * while underlying Radix validates and manages accessible value attributes.\n\t * Does not support arbitrary native HTML props, ref forwarding, min/max, or change callbacks.\n\t * @default 0\n\t */\n\tvalue?: number;\n\t/**\n\t * Text label displayed above the meter bar and used as accessible name fallback.\n\t */\n\tlabel?: string;\n\t/**\n\t * Custom text overriding the displayed percentage value text next to the label.\n\t */\n\tcustomValue?: string;\n\t/**\n\t * Whether to hide the displayed value text next to the label.\n\t * @default false\n\t */\n\thideValue?: boolean;\n\t/**\n\t * Additional CSS classes applied to the root container.\n\t */\n\tclassName?: string;\n\t/**\n\t * Accessible label announced for assistive technologies. Takes precedence over `label`.\n\t */\n\t\"aria-label\"?: string;\n}\n\nexport function Meter({\n\tvalue = 0,\n\tlabel,\n\tcustomValue,\n\thideValue = false,\n\tclassName,\n\t\"aria-label\": ariaLabel,\n}: MeterProps) {\n\treturn (\n\t\t<div className={cn(\"w-full space-y-1\", className)}>\n\t\t\t{label || (!hideValue && customValue) ? (\n\t\t\t\t<div className=\"flex justify-between text-xs text-basalt-muted-foreground\">\n\t\t\t\t\t<span>{label}</span>\n\t\t\t\t\t{hideValue ? null : <span>{customValue ?? `${value}%`}</span>}\n\t\t\t\t</div>\n\t\t\t) : null}\n\t\t\t<Progress.Root\n\t\t\t\tvalue={value}\n\t\t\t\taria-label={ariaLabel ?? label}\n\t\t\t\tclassName=\"relative h-2 overflow-hidden rounded-full bg-basalt-muted\"\n\t\t\t>\n\t\t\t\t<Progress.Indicator\n\t\t\t\t\tclassName=\"h-full bg-basalt-primary transition-[width]\"\n\t\t\t\t\tstyle={{ width: `${Math.min(100, Math.max(0, value))}%` }}\n\t\t\t\t/>\n\t\t\t</Progress.Root>\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;AAoCA,SAAgBC,EAAM,EACrB,WAAQ,GACR,UACA,gBACA,eAAY,IACZ,cACA,cAAcC,KACA;CACd,OACC,kBAAC,OAAD;EAAK,WAAWC,EAAG,oBAAoBC,CAAS;EAAhD,UAAA,CACEC,KAAU,CAACC,KAAaC,IACxB,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACC,kBAAC,QAAD,EAAA,UAAOF,EAAY,CAAA,GAClBC,IAAY,OAAO,kBAAC,QAAD,EAAA,UAAOC,KAAe,GAAGC,EAAM,GAAS,CAAA,CACxD;EACF,CAAA,IAAA,MACJ,kBAACR,EAAS,MAAV;GACQ;GACP,cAAYE,KAAaG;GACzB,WAAU;GAEV,UAAA,kBAACL,EAAS,WAAV;IACC,WAAU;IACV,OAAO,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,KAAK,IAAI,GAAGQ,CAAK,CAAC,EAAE,GAAG;GACxD,CAAA;EACa,CAAA,CACX;;AAEP"}
1
+ {"version":3,"file":"meter.js","names":["Progress","Meter","ariaLabel","reading","value","filled","redEnd","yellowEnd","cn","className","label","customValue","index","hideValue"],"sources":["../../src/components/meter.tsx"],"sourcesContent":["import * as Progress from \"@radix-ui/react-progress\";\nimport { cn } from \"../utils/cn\";\n\nexport interface MeterProps {\n\t/**\n\t * Percentage value representing current meter progress (0..100).\n\t *\n\t * Values are clamped to [0, 100] for both segments and accessibility.\n\t * Non-finite readings are displayed as unavailable.\n\t * Does not support arbitrary native HTML props, ref forwarding, min/max, or change callbacks.\n\t * @default 0\n\t */\n\tvalue?: number;\n\t/**\n\t * Text label displayed above the segmented meter and used as accessible name fallback.\n\t */\n\tlabel?: string;\n\t/**\n\t * Custom text overriding the value displayed beside the segments.\n\t */\n\tcustomValue?: string;\n\t/**\n\t * Whether to hide the value displayed beside the segments.\n\t * @default false\n\t */\n\thideValue?: boolean;\n\t/**\n\t * Additional CSS classes applied to the root container.\n\t */\n\tclassName?: string;\n\t/**\n\t * Accessible label announced for assistive technologies. Takes precedence over `label`.\n\t */\n\t\"aria-label\"?: string;\n}\n\nexport function Meter({\n\tvalue = 0,\n\tlabel,\n\tcustomValue,\n\thideValue = false,\n\tclassName,\n\t\"aria-label\": ariaLabel,\n}: MeterProps) {\n\tconst reading = Number.isFinite(value) ? Math.min(100, Math.max(0, value)) : null;\n\tconst filled = Math.round(((reading ?? 0) / 100) * 17);\n\tconst redEnd = Math.floor(filled / 3);\n\tconst yellowEnd = redEnd + Math.ceil(filled / 3);\n\treturn (\n\t\t<div className={cn(\"w-full space-y-basalt-space-sm\", className)}>\n\t\t\t{label ? <div className=\"text-basalt-sm text-basalt-muted-foreground\">{label}</div> : null}\n\t\t\t<div className=\"flex items-center gap-basalt-space-lg\">\n\t\t\t\t<Progress.Root\n\t\t\t\t\tdata-basalt-meter=\"\"\n\t\t\t\t\tvalue={reading}\n\t\t\t\t\taria-label={ariaLabel ?? label}\n\t\t\t\t\taria-valuetext={reading === null ? \"Unavailable\" : customValue}\n\t\t\t\t\tclassName=\"h-basalt-3_5 min-w-0 flex-1 overflow-hidden rounded-basalt-sm bg-basalt-muted p-basalt-space-xs\"\n\t\t\t\t>\n\t\t\t\t\t<Progress.Indicator className=\"flex h-full gap-basalt-space-xs\" aria-hidden=\"true\">\n\t\t\t\t\t\t{Array.from({ length: 17 }, (_, index) => (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={`segment-${index}`}\n\t\t\t\t\t\t\t\tdata-filled={index < filled}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"h-full min-w-0 flex-1 rounded-basalt-sm\",\n\t\t\t\t\t\t\t\t\tindex >= filled\n\t\t\t\t\t\t\t\t\t\t? \"bg-basalt-foreground/10\"\n\t\t\t\t\t\t\t\t\t\t: index < redEnd\n\t\t\t\t\t\t\t\t\t\t\t? \"bg-basalt-danger\"\n\t\t\t\t\t\t\t\t\t\t\t: index < yellowEnd\n\t\t\t\t\t\t\t\t\t\t\t\t? \"bg-[hsl(var(--basalt-chart-yellow))]\"\n\t\t\t\t\t\t\t\t\t\t\t\t: \"bg-[hsl(var(--basalt-chart-green))]\",\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</Progress.Indicator>\n\t\t\t\t</Progress.Root>\n\t\t\t\t{hideValue ? null : (\n\t\t\t\t\t<span className=\"shrink-0 text-basalt-sm tabular-nums text-basalt-foreground\">\n\t\t\t\t\t\t{customValue ?? (reading === null ? \"Unavailable\" : `${reading}%`)}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;AAoCA,SAAgBC,EAAM,EACrB,WAAQ,GACR,UACA,gBACA,eAAY,IACZ,cACA,cAAcC,KACA;CACd,IAAMC,IAAU,OAAO,SAASC,CAAK,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAGA,CAAK,CAAC,IAAI,MACvEC,IAAS,KAAK,OAAQF,KAAW,KAAK,MAAO,EAAE,GAC/CG,IAAS,KAAK,MAAMD,IAAS,CAAC,GAC9BE,IAAYD,IAAS,KAAK,KAAKD,IAAS,CAAC;CAC/C,OACC,kBAAC,OAAD;EAAK,WAAWG,EAAG,kCAAkCC,CAAS;EAA9D,UAAA,CACEC,IAAQ,kBAAC,OAAD;GAAK,WAAU;GAA+C,UAAAA;EAAW,CAAA,IAAI,MACtF,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACC,kBAACV,EAAS,MAAV;IACC,qBAAkB;IAClB,OAAOG;IACP,cAAYD,KAAaQ;IACzB,kBAAgBP,MAAY,OAAO,gBAAgBQ;IACnD,WAAU;IAEV,UAAA,kBAACX,EAAS,WAAV;KAAoB,WAAU;KAAkC,eAAY;KAC1E,UAAA,MAAM,KAAK,EAAE,QAAQ,GAAG,IAAI,GAAG,MAC/B,kBAAC,QAAD;MAEC,eAAaY,IAAQP;MACrB,WAAWG,EACV,2CACAI,KAASP,IACN,4BACAO,IAAQN,IACP,qBACAM,IAAQL,IACP,yCACA,qCACN;KACA,GAZK,WAAWK,GAYhB,CACD;IACkB,CAAA;GACN,CAAA,GACdC,IAAY,OACZ,kBAAC,QAAD;IAAM,WAAU;IACd,UAAAF,MAAgBR,MAAY,OAAO,gBAAgB,GAAGA,EAAQ;GAC1D,CAAA,CAEH;EACD,CAAA,CAAA;;AAEP"}
@@ -2,37 +2,38 @@
2
2
  import { cn as e } from "../utils/cn.js";
3
3
  import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
4
4
  import { Button as n } from "./button.js";
5
- import { Input as r } from "./input.js";
6
- import { Popover as ee, PopoverContent as te, PopoverTrigger as ne } from "./popover.js";
7
- import { useEffect as i, useId as a, useRef as o, useState as s } from "react";
8
- import { Check as c, ChevronDown as l, X as u } from "lucide-react";
5
+ import { useHoverHighlight as r } from "../utils/use-hover-highlight.js";
6
+ import { Input as i } from "./input.js";
7
+ import { Popover as a, PopoverContent as ee, PopoverTrigger as o } from "./popover.js";
8
+ import { useEffect as s, useId as c, useRef as l, useState as u } from "react";
9
+ import { Check as te, ChevronDown as ne, X as re } from "lucide-react";
9
10
  import { jsx as d, jsxs as f } from "react/jsx-runtime";
10
11
  //#region src/components/multi-select.tsx
11
- function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange: _, query: v, defaultQuery: y = "", onQueryChange: re, open: b, defaultOpen: ie = !1, onOpenChange: ae, filterOptions: oe = !0, loading: x = !1, disabled: S = !1, name: C, form: w, placeholder: se = "Select options", searchPlaceholder: ce = "Search options…", emptyLabel: T = "No options found.", loadingLabel: E = "Loading options…", removeLabel: D = "Remove", showChips: O = !0, formatSelectionCount: k, className: A }) {
12
- let [le, j] = s(g), [ue, M] = s(y), [de, N] = s(ie), [P, F] = s(null), I = o(null), L = o(null), R = o(null), z = o(null), B = o({
12
+ function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange: ie, query: _, defaultQuery: v = "", onQueryChange: ae, open: y, defaultOpen: oe = !1, onOpenChange: se, filterOptions: ce = !0, loading: b = !1, disabled: x = !1, name: S, form: C, placeholder: le = "Select options", searchPlaceholder: ue = "Search options…", emptyLabel: w = "No options found.", loadingLabel: T = "Loading options…", removeLabel: E = "Remove", showChips: D = !0, formatSelectionCount: O, className: de }) {
13
+ let [fe, k] = u(g), [pe, A] = u(v), [j, M] = u(oe), [N, P] = u(null), F = l(null), I = l(null), L = l(null), R = r(L), z = l(null), B = l({
13
14
  value: h,
14
15
  defaultValue: g,
15
- query: v,
16
- defaultQuery: y,
17
- open: b
16
+ query: _,
17
+ defaultQuery: v,
18
+ open: y
18
19
  });
19
- i(() => {
20
+ s(() => {
20
21
  B.current = {
21
22
  value: h,
22
23
  defaultValue: g,
23
- query: v,
24
- defaultQuery: y,
25
- open: b
24
+ query: _,
25
+ defaultQuery: v,
26
+ open: y
26
27
  };
27
- }), i(() => {
28
- let e = L.current?.form;
28
+ }), s(() => {
29
+ let e = I.current?.form;
29
30
  if (!e) return;
30
31
  let t = /* @__PURE__ */ new Set();
31
32
  function n(e) {
32
33
  let n = setTimeout(() => {
33
34
  if (t.delete(n), e.defaultPrevented) return;
34
35
  let r = B.current;
35
- r.value === void 0 && j(r.defaultValue), r.query === void 0 && M(r.defaultQuery), r.open === void 0 && N(!1), F(null);
36
+ r.value === void 0 && k(r.defaultValue), r.query === void 0 && A(r.defaultQuery), r.open === void 0 && M(!1), P(null);
36
37
  }, 0);
37
38
  t.add(n);
38
39
  }
@@ -40,13 +41,13 @@ function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange
40
41
  for (let e of t) clearTimeout(e);
41
42
  e.removeEventListener("reset", n);
42
43
  };
43
- }, [w]);
44
- let V = a(), H = h ?? le, U = v ?? ue, W = (b ?? de) && !S, G = m.filter((e) => !oe || `${e.label} ${e.description ?? ""}`.toLocaleLowerCase().includes(U.trim().toLocaleLowerCase())), K = G.filter((e) => !e.disabled), q = K.some((e) => e.value === P) ? P : K[0]?.value, J = G.findIndex((e) => e.value === q), Y = k ? k(H.length) : `${H.length} selected`;
44
+ }, [C]);
45
+ let V = c(), H = h ?? fe, U = _ ?? pe, W = (y ?? j) && !x, G = m.filter((e) => !ce || `${e.label} ${e.description ?? ""}`.toLocaleLowerCase().includes(U.trim().toLocaleLowerCase())), K = G.filter((e) => !e.disabled), q = K.some((e) => e.value === N) ? N : K[0]?.value, J = G.findIndex((e) => e.value === q), Y = O ? O(H.length) : `${H.length} selected`;
45
46
  function X(e) {
46
- S || (b === void 0 && N(e), ae?.(e));
47
+ x || (y === void 0 && M(e), se?.(e));
47
48
  }
48
49
  function Z(e) {
49
- S || (h === void 0 && j(e), _?.(e));
50
+ x || (h === void 0 && k(e), ie?.(e));
50
51
  }
51
52
  function Q(e) {
52
53
  e.disabled || Z(H.includes(e.value) ? H.filter((t) => t !== e.value) : [...H, e.value]);
@@ -57,49 +58,49 @@ function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange
57
58
  e !== void 0 && Z(H.filter((t) => t !== e));
58
59
  }
59
60
  return /* @__PURE__ */ f("div", {
60
- className: e(t, "min-w-0 space-y-2", A),
61
+ className: e(t, "min-w-0 space-y-basalt-space-lg", de),
61
62
  children: [
62
- O && H.length > 0 && /* @__PURE__ */ d("div", {
63
+ D && H.length > 0 && /* @__PURE__ */ d("div", {
63
64
  role: "group",
64
- className: "flex flex-wrap gap-1.5",
65
+ className: "flex flex-wrap gap-basalt-space-md",
65
66
  "aria-label": `${p}: ${Y}`,
66
67
  children: H.map((e) => {
67
68
  let t = m.find((t) => t.value === e), n = t?.label ?? e;
68
69
  return /* @__PURE__ */ f("span", {
69
- className: "inline-flex max-w-full items-center gap-1 rounded-basalt-md border border-basalt-border bg-basalt-accent py-0.5 pl-2 text-xs",
70
+ className: "inline-flex max-w-full items-center gap-basalt-space-sm rounded-basalt-md border border-basalt-border bg-basalt-selected py-basalt-space-xs pl-basalt-space-lg text-basalt-sm",
70
71
  children: [/* @__PURE__ */ d("span", {
71
72
  className: "min-w-0 break-words",
72
73
  children: n
73
74
  }), /* @__PURE__ */ d("button", {
74
75
  type: "button",
75
- disabled: S || t?.disabled,
76
- "aria-label": `${D} ${n}`,
77
- className: "rounded p-1 focus-visible:outline-2 focus-visible:outline-basalt-ring disabled:opacity-50",
76
+ disabled: x || t?.disabled,
77
+ "aria-label": `${E} ${n}`,
78
+ className: "rounded-basalt-sm p-basalt-space-sm focus-visible:outline-2 focus-visible:outline-basalt-ring disabled:opacity-50",
78
79
  onClick: () => Z(H.filter((t) => t !== e)),
79
- children: /* @__PURE__ */ d(u, {
80
- className: "size-3",
80
+ children: /* @__PURE__ */ d(re, {
81
+ className: "size-basalt-icon-sm",
81
82
  "aria-hidden": "true"
82
83
  })
83
84
  })]
84
85
  }, e);
85
86
  })
86
87
  }),
87
- !S && C && H.map((e) => /* @__PURE__ */ d("input", {
88
+ !x && S && H.map((e) => /* @__PURE__ */ d("input", {
88
89
  type: "hidden",
89
- name: C,
90
- form: w,
90
+ name: S,
91
+ form: C,
91
92
  value: e
92
93
  }, e)),
93
- /* @__PURE__ */ f(ee, {
94
+ /* @__PURE__ */ f(a, {
94
95
  open: W,
95
96
  onOpenChange: X,
96
- children: [/* @__PURE__ */ d(ne, {
97
+ children: [/* @__PURE__ */ d(o, {
97
98
  asChild: !0,
98
99
  children: /* @__PURE__ */ f(n, {
99
- ref: L,
100
- form: w,
100
+ ref: I,
101
+ form: C,
101
102
  variant: "outline",
102
- disabled: S,
103
+ disabled: x,
103
104
  "aria-label": p,
104
105
  "aria-describedby": `${V}-count`,
105
106
  className: "w-full justify-between",
@@ -108,41 +109,41 @@ function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange
108
109
  },
109
110
  children: [/* @__PURE__ */ d("span", {
110
111
  className: "truncate",
111
- children: H.length ? Y : se
112
- }), /* @__PURE__ */ d(l, { "aria-hidden": "true" })]
112
+ children: H.length ? Y : le
113
+ }), /* @__PURE__ */ d(ne, { "aria-hidden": "true" })]
113
114
  })
114
- }), /* @__PURE__ */ f(te, {
115
+ }), /* @__PURE__ */ f(ee, {
115
116
  align: "start",
116
- className: "w-72 max-w-[calc(100vw-2rem)] p-2",
117
+ className: "w-basalt-72 max-w-[calc(100vw-2rem)] p-basalt-space-lg",
117
118
  "aria-label": p,
118
119
  onOpenAutoFocus: (e) => {
119
- e.preventDefault(), I.current?.focus();
120
+ e.preventDefault(), F.current?.focus();
120
121
  },
121
122
  children: [
122
- /* @__PURE__ */ d(r, {
123
- ref: I,
123
+ /* @__PURE__ */ d(i, {
124
+ ref: F,
124
125
  role: "combobox",
125
126
  "aria-label": `${p}: search`,
126
127
  "aria-expanded": W,
127
128
  "aria-autocomplete": "list",
128
129
  "aria-controls": `${V}-list`,
129
- "aria-activedescendant": !x && J >= 0 ? `${V}-option-${J}` : void 0,
130
- placeholder: ce,
130
+ "aria-activedescendant": !b && J >= 0 ? `${V}-option-${J}` : void 0,
131
+ placeholder: ue,
131
132
  value: U,
132
133
  onChange: (e) => {
133
- v === void 0 && M(e.target.value), re?.(e.target.value), F(null);
134
+ _ === void 0 && A(e.target.value), ae?.(e.target.value), P(null);
134
135
  },
135
136
  onKeyDown: (e) => {
136
137
  if (!e.nativeEvent.isComposing) {
137
138
  if (e.key === "ArrowDown" || e.key === "ArrowUp") {
138
- if (e.preventDefault(), !x && K.length) {
139
+ if (e.preventDefault(), !b && K.length) {
139
140
  let t = K.findIndex((e) => e.value === q), n = K[(t + (e.key === "ArrowDown" ? 1 : -1) + K.length) % K.length];
140
- F(n.value), R.current?.querySelector(`[data-option-index="${G.indexOf(n)}"]`)?.scrollIntoView({ block: "nearest" });
141
+ P(n.value), L.current?.querySelector(`[data-option-index="${G.indexOf(n)}"]`)?.scrollIntoView({ block: "nearest" });
141
142
  }
142
143
  } else if (e.key === "Enter") {
143
144
  e.preventDefault();
144
145
  let t = K.find((e) => e.value === q);
145
- !x && t && Q(t);
146
+ !b && t && Q(t);
146
147
  } else e.key === "Backspace" && !U && (e.preventDefault(), $());
147
148
  }
148
149
  }
@@ -153,28 +154,31 @@ function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange
153
154
  role: "listbox",
154
155
  "aria-label": p,
155
156
  "aria-multiselectable": "true",
156
- "aria-busy": x,
157
- className: "mt-2 max-h-60 overflow-y-auto",
158
- children: !x && G.map((t, n) => /* @__PURE__ */ f("button", {
157
+ "aria-busy": b,
158
+ className: "basalt-hover-list mt-basalt-space-lg max-h-basalt-60 overflow-y-auto",
159
+ children: !b && G.map((t, n) => /* @__PURE__ */ f("button", {
159
160
  type: "button",
160
161
  role: "option",
162
+ "data-basalt-hover-item": "",
163
+ "data-hover-active": q === t.value,
161
164
  id: `${V}-option-${n}`,
162
165
  "data-option-index": n,
163
166
  tabIndex: -1,
164
167
  "aria-selected": H.includes(t.value),
168
+ "data-hover-selected": H.includes(t.value),
165
169
  "aria-disabled": t.disabled || void 0,
166
170
  disabled: t.disabled,
167
- className: e("flex w-full items-center gap-2 rounded-basalt-md px-2 py-2 text-left text-sm disabled:opacity-40", q === t.value && "bg-basalt-accent"),
171
+ className: e("flex w-full items-center gap-basalt-space-lg rounded-basalt-md px-basalt-space-lg py-basalt-space-lg text-left text-basalt-base disabled:opacity-40"),
168
172
  onPointerMove: (e) => {
169
173
  let n = z.current;
170
174
  (n?.x !== e.clientX || n.y !== e.clientY) && (z.current = {
171
175
  x: e.clientX,
172
176
  y: e.clientY
173
- }, t.disabled || F(t.value));
177
+ }, t.disabled || P(t.value));
174
178
  },
175
179
  onMouseDown: (e) => e.preventDefault(),
176
180
  onClick: () => {
177
- Q(t), I.current?.focus();
181
+ Q(t), F.current?.focus();
178
182
  },
179
183
  children: [
180
184
  t.leading,
@@ -184,21 +188,21 @@ function p({ label: p, options: m, value: h, defaultValue: g = [], onValueChange
184
188
  className: "block break-words",
185
189
  children: t.label
186
190
  }), t.description && /* @__PURE__ */ d("span", {
187
- className: "block text-xs text-basalt-muted-foreground",
191
+ className: "block text-basalt-sm text-basalt-muted-foreground",
188
192
  children: t.description
189
193
  })]
190
194
  }),
191
- H.includes(t.value) && /* @__PURE__ */ d(c, {
192
- className: "size-4 shrink-0",
195
+ H.includes(t.value) && /* @__PURE__ */ d(te, {
196
+ className: "size-basalt-icon-lg shrink-0",
193
197
  "aria-hidden": "true"
194
198
  })
195
199
  ]
196
200
  }, t.value))
197
201
  }),
198
- (x || G.length === 0) && /* @__PURE__ */ d("p", {
202
+ (b || G.length === 0) && /* @__PURE__ */ d("p", {
199
203
  role: "status",
200
- className: "px-2 py-4 text-sm text-basalt-muted-foreground",
201
- children: x ? E : T
204
+ className: "px-basalt-space-lg py-basalt-space-lg text-basalt-base text-basalt-muted-foreground",
205
+ children: b ? T : w
202
206
  })
203
207
  ]
204
208
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"multi-select.js","names":["MultiSelect","localValue","setLocalValue","useState","defaultValue","localQuery","setLocalQuery","defaultQuery","localOpen","setLocalOpen","defaultOpen","active","setActive","input","useRef","trigger","list","pointer","resetConfig","useEffect","owner","timers","reset","timer","event","config","form","id","useId","selected","value","search","query","expanded","open","disabled","visible","options","filterOptions","option","enabled","activeValue","activeIndex","countText","formatSelectionCount","changeOpen","next","onOpenChange","changeValue","onValueChange","toggle","item","removeLast","cn","BASALT_UI_CLASS","className","showChips","label","text","removeLabel","X","name","Popover","PopoverTrigger","Button","placeholder","ChevronDown","PopoverContent","Input","loading","searchPlaceholder","onQueryChange","index","previous","Check","loadingLabel","emptyLabel"],"sources":["../../src/components/multi-select.tsx"],"sourcesContent":["import { Check, ChevronDown, X } from \"lucide-react\";\nimport { type ReactNode, useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { Button } from \"./button\";\nimport { Input } from \"./input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\";\n\nexport interface MultiSelectOption {\n\t/** Stable identifier, independent of the display label. */\n\tvalue: string;\n\t/** Searchable and accessible display name. */\n\tlabel: string;\n\t/** Optional secondary searchable text. */\n\tdescription?: string;\n\t/** Caller-provided icon or avatar. */\n\tleading?: ReactNode;\n\t/** Prevent adding or removing this option. */\n\tdisabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n\t/** Accessible field and popup name. */\n\tlabel: string;\n\t/** Available options; retain selected options to preserve their display labels. */\n\toptions: readonly MultiSelectOption[];\n\t/** Controlled selected identifiers. */\n\tvalue?: readonly string[];\n\t/** Initial uncontrolled selection. @default [] */\n\tdefaultValue?: readonly string[];\n\t/** Selection requests; controlled values change only when accepted by the caller. */\n\tonValueChange?: (value: string[]) => void;\n\t/** Controlled search text; asynchronous requests belong to the caller. */\n\tquery?: string;\n\t/** Initial uncontrolled search text. @default \"\" */\n\tdefaultQuery?: string;\n\t/** Search text requests. */\n\tonQueryChange?: (query: string) => void;\n\t/** Controlled popup visibility. */\n\topen?: boolean;\n\t/** Initial uncontrolled popup visibility. @default false */\n\tdefaultOpen?: boolean;\n\t/** Popup visibility requests. */\n\tonOpenChange?: (open: boolean) => void;\n\t/** Use local label/description filtering; disable for server-filtered options. @default true */\n\tfilterOptions?: boolean;\n\t/** Loading content replaces the option list. @default false */\n\tloading?: boolean;\n\t/** Disables the trigger, chips, and native form entries. @default false */\n\tdisabled?: boolean;\n\t/** Repeated native form entry name, one entry per selected identifier. */\n\tname?: string;\n\t/** Optional external native form owner. Uncontrolled state respects uncancelled form resets. */\n\tform?: string;\n\t/** Empty selection prompt. @default \"Select options\" */\n\tplaceholder?: string;\n\t/** Search input prompt. @default \"Search options…\" */\n\tsearchPlaceholder?: string;\n\t/** Empty search result text. @default \"No options found.\" */\n\temptyLabel?: string;\n\t/** Loading announcement. @default \"Loading options…\" */\n\tloadingLabel?: string;\n\t/** Chip removal label prefix. @default \"Remove\" */\n\tremoveLabel?: string;\n\t/** Show removable selection chips; disable when a FilterBar renders its own chips. @default true */\n\tshowChips?: boolean;\n\t/** Selection count formatter, including localization. */\n\tformatSelectionCount?: (count: number) => string;\n\t/** Additional root classes. */\n\tclassName?: string;\n}\n\nexport function MultiSelect({\n\tlabel,\n\toptions,\n\tvalue,\n\tdefaultValue = [],\n\tonValueChange,\n\tquery,\n\tdefaultQuery = \"\",\n\tonQueryChange,\n\topen,\n\tdefaultOpen = false,\n\tonOpenChange,\n\tfilterOptions = true,\n\tloading = false,\n\tdisabled = false,\n\tname,\n\tform,\n\tplaceholder = \"Select options\",\n\tsearchPlaceholder = \"Search options…\",\n\temptyLabel = \"No options found.\",\n\tloadingLabel = \"Loading options…\",\n\tremoveLabel = \"Remove\",\n\tshowChips = true,\n\tformatSelectionCount,\n\tclassName,\n}: MultiSelectProps) {\n\tconst [localValue, setLocalValue] = useState(defaultValue);\n\tconst [localQuery, setLocalQuery] = useState(defaultQuery);\n\tconst [localOpen, setLocalOpen] = useState(defaultOpen);\n\tconst [active, setActive] = useState<string | null>(null);\n\tconst input = useRef<HTMLInputElement>(null);\n\tconst trigger = useRef<HTMLButtonElement>(null);\n\tconst list = useRef<HTMLDivElement>(null);\n\tconst pointer = useRef<{ x: number; y: number } | null>(null);\n\tconst resetConfig = useRef({ value, defaultValue, query, defaultQuery, open });\n\tuseEffect(() => {\n\t\tresetConfig.current = { value, defaultValue, query, defaultQuery, open };\n\t});\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: form changes the native owner of the same trigger and requires rebinding its reset listener.\n\tuseEffect(() => {\n\t\tconst owner = trigger.current?.form;\n\t\tif (!owner) return;\n\t\tconst timers = new Set<ReturnType<typeof setTimeout>>();\n\t\tfunction reset(event: Event) {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\ttimers.delete(timer);\n\t\t\t\tif (event.defaultPrevented) return;\n\t\t\t\tconst config = resetConfig.current;\n\t\t\t\tif (config.value === undefined) setLocalValue(config.defaultValue);\n\t\t\t\tif (config.query === undefined) setLocalQuery(config.defaultQuery);\n\t\t\t\tif (config.open === undefined) setLocalOpen(false);\n\t\t\t\tsetActive(null);\n\t\t\t}, 0);\n\t\t\ttimers.add(timer);\n\t\t}\n\t\towner.addEventListener(\"reset\", reset);\n\t\treturn () => {\n\t\t\tfor (const timer of timers) clearTimeout(timer);\n\t\t\towner.removeEventListener(\"reset\", reset);\n\t\t};\n\t}, [form]);\n\tconst id = useId();\n\tconst selected = value ?? localValue;\n\tconst search = query ?? localQuery;\n\tconst expanded = (open ?? localOpen) && !disabled;\n\tconst visible = options.filter(\n\t\t(option) =>\n\t\t\t!filterOptions ||\n\t\t\t`${option.label} ${option.description ?? \"\"}`\n\t\t\t\t.toLocaleLowerCase()\n\t\t\t\t.includes(search.trim().toLocaleLowerCase()),\n\t);\n\tconst enabled = visible.filter((option) => !option.disabled);\n\tconst activeValue = enabled.some((option) => option.value === active)\n\t\t? active\n\t\t: enabled[0]?.value;\n\tconst activeIndex = visible.findIndex((option) => option.value === activeValue);\n\tconst countText = formatSelectionCount\n\t\t? formatSelectionCount(selected.length)\n\t\t: `${selected.length} selected`;\n\n\tfunction changeOpen(next: boolean) {\n\t\tif (disabled) return;\n\t\tif (open === undefined) setLocalOpen(next);\n\t\tonOpenChange?.(next);\n\t}\n\tfunction changeValue(next: string[]) {\n\t\tif (disabled) return;\n\t\tif (value === undefined) setLocalValue(next);\n\t\tonValueChange?.(next);\n\t}\n\tfunction toggle(option: MultiSelectOption) {\n\t\tif (option.disabled) return;\n\t\tchangeValue(\n\t\t\tselected.includes(option.value)\n\t\t\t\t? selected.filter((item) => item !== option.value)\n\t\t\t\t: [...selected, option.value],\n\t\t);\n\t}\n\tfunction removeLast() {\n\t\tif (search) return;\n\t\tconst item = [...selected]\n\t\t\t.reverse()\n\t\t\t.find((value) => !options.find((option) => option.value === value)?.disabled);\n\t\tif (item !== undefined) changeValue(selected.filter((value) => value !== item));\n\t}\n\n\treturn (\n\t\t<div className={cn(BASALT_UI_CLASS, \"min-w-0 space-y-2\", className)}>\n\t\t\t{showChips && selected.length > 0 && (\n\t\t\t\t<div role=\"group\" className=\"flex flex-wrap gap-1.5\" aria-label={`${label}: ${countText}`}>\n\t\t\t\t\t{selected.map((item) => {\n\t\t\t\t\t\tconst option = options.find((option) => option.value === item);\n\t\t\t\t\t\tconst text = option?.label ?? item;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={item}\n\t\t\t\t\t\t\t\tclassName=\"inline-flex max-w-full items-center gap-1 rounded-basalt-md border border-basalt-border bg-basalt-accent py-0.5 pl-2 text-xs\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"min-w-0 break-words\">{text}</span>\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\tdisabled={disabled || option?.disabled}\n\t\t\t\t\t\t\t\t\taria-label={`${removeLabel} ${text}`}\n\t\t\t\t\t\t\t\t\tclassName=\"rounded p-1 focus-visible:outline-2 focus-visible:outline-basalt-ring disabled:opacity-50\"\n\t\t\t\t\t\t\t\t\tonClick={() => changeValue(selected.filter((value) => value !== item))}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<X className=\"size-3\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{!disabled &&\n\t\t\t\tname &&\n\t\t\t\tselected.map((item) => (\n\t\t\t\t\t<input key={item} type=\"hidden\" name={name} form={form} value={item} />\n\t\t\t\t))}\n\t\t\t<Popover open={expanded} onOpenChange={changeOpen}>\n\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tref={trigger}\n\t\t\t\t\t\tform={form}\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\taria-label={label}\n\t\t\t\t\t\taria-describedby={`${id}-count`}\n\t\t\t\t\t\tclassName=\"w-full justify-between\"\n\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\tif (event.nativeEvent.isComposing) return;\n\t\t\t\t\t\t\tif (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tchangeOpen(true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"Backspace\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tremoveLast();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"truncate\">{selected.length ? countText : placeholder}</span>\n\t\t\t\t\t\t<ChevronDown aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</PopoverTrigger>\n\t\t\t\t<PopoverContent\n\t\t\t\t\talign=\"start\"\n\t\t\t\t\tclassName=\"w-72 max-w-[calc(100vw-2rem)] p-2\"\n\t\t\t\t\taria-label={label}\n\t\t\t\t\tonOpenAutoFocus={(event) => {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tinput.current?.focus();\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tref={input}\n\t\t\t\t\t\trole=\"combobox\"\n\t\t\t\t\t\taria-label={`${label}: search`}\n\t\t\t\t\t\taria-expanded={expanded}\n\t\t\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\t\t\taria-controls={`${id}-list`}\n\t\t\t\t\t\taria-activedescendant={\n\t\t\t\t\t\t\t!loading && activeIndex >= 0 ? `${id}-option-${activeIndex}` : undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t\tplaceholder={searchPlaceholder}\n\t\t\t\t\t\tvalue={search}\n\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\tif (query === undefined) setLocalQuery(event.target.value);\n\t\t\t\t\t\t\tonQueryChange?.(event.target.value);\n\t\t\t\t\t\t\tsetActive(null);\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) return;\n\t\t\t\t\t\t\tif (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tif (!loading && enabled.length) {\n\t\t\t\t\t\t\t\t\tconst index = enabled.findIndex((option) => option.value === activeValue);\n\t\t\t\t\t\t\t\t\tconst next =\n\t\t\t\t\t\t\t\t\t\tenabled[\n\t\t\t\t\t\t\t\t\t\t\t(index + (event.key === \"ArrowDown\" ? 1 : -1) + enabled.length) %\n\t\t\t\t\t\t\t\t\t\t\t\tenabled.length\n\t\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t\tsetActive(next.value);\n\t\t\t\t\t\t\t\t\tlist.current\n\t\t\t\t\t\t\t\t\t\t?.querySelector<HTMLElement>(`[data-option-index=\"${visible.indexOf(next)}\"]`)\n\t\t\t\t\t\t\t\t\t\t?.scrollIntoView({ block: \"nearest\" });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (event.key === \"Enter\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tconst option = enabled.find((option) => option.value === activeValue);\n\t\t\t\t\t\t\t\tif (!loading && option) toggle(option);\n\t\t\t\t\t\t\t} else if (event.key === \"Backspace\" && !search) {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tremoveLast();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t\t<div\n\t\t\t\t\t\tref={list}\n\t\t\t\t\t\tid={`${id}-list`}\n\t\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\t\taria-label={label}\n\t\t\t\t\t\taria-multiselectable=\"true\"\n\t\t\t\t\t\taria-busy={loading}\n\t\t\t\t\t\tclassName=\"mt-2 max-h-60 overflow-y-auto\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{!loading &&\n\t\t\t\t\t\t\tvisible.map((option, 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\trole=\"option\"\n\t\t\t\t\t\t\t\t\tid={`${id}-option-${index}`}\n\t\t\t\t\t\t\t\t\tdata-option-index={index}\n\t\t\t\t\t\t\t\t\tkey={option.value}\n\t\t\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\t\t\taria-selected={selected.includes(option.value)}\n\t\t\t\t\t\t\t\t\taria-disabled={option.disabled || undefined}\n\t\t\t\t\t\t\t\t\tdisabled={option.disabled}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"flex w-full items-center gap-2 rounded-basalt-md px-2 py-2 text-left text-sm disabled:opacity-40\",\n\t\t\t\t\t\t\t\t\t\tactiveValue === option.value && \"bg-basalt-accent\",\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\tconst previous = pointer.current;\n\t\t\t\t\t\t\t\t\t\tif (previous?.x === event.clientX && previous.y === event.clientY) return;\n\t\t\t\t\t\t\t\t\t\tpointer.current = { x: event.clientX, y: event.clientY };\n\t\t\t\t\t\t\t\t\t\tif (!option.disabled) setActive(option.value);\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={() => {\n\t\t\t\t\t\t\t\t\t\ttoggle(option);\n\t\t\t\t\t\t\t\t\t\tinput.current?.focus();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{option.leading}\n\t\t\t\t\t\t\t\t\t<span className=\"min-w-0 flex-1\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"block break-words\">{option.label}</span>\n\t\t\t\t\t\t\t\t\t\t{option.description && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"block text-xs text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t{option.description}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{selected.includes(option.value) && (\n\t\t\t\t\t\t\t\t\t\t<Check className=\"size-4 shrink-0\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t\t{(loading || visible.length === 0) && (\n\t\t\t\t\t\t<p role=\"status\" className=\"px-2 py-4 text-sm text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t{loading ? loadingLabel : emptyLabel}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</PopoverContent>\n\t\t\t</Popover>\n\t\t\t<span id={`${id}-count`} className=\"sr-only\" aria-live=\"polite\">\n\t\t\t\t{countText}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"multi-select.js","names":["MultiSelect","localValue","setLocalValue","useState","defaultValue","localQuery","setLocalQuery","defaultQuery","localOpen","setLocalOpen","defaultOpen","active","setActive","input","useRef","trigger","list","highlightRef","useHoverHighlight","pointer","resetConfig","useEffect","owner","timers","reset","timer","event","config","form","id","useId","selected","value","search","query","expanded","open","disabled","visible","options","filterOptions","option","enabled","activeValue","activeIndex","countText","formatSelectionCount","changeOpen","next","onOpenChange","changeValue","onValueChange","toggle","item","removeLast","cn","BASALT_UI_CLASS","className","showChips","label","text","removeLabel","X","name","Popover","PopoverTrigger","Button","placeholder","ChevronDown","PopoverContent","Input","loading","searchPlaceholder","onQueryChange","index","previous","Check","loadingLabel","emptyLabel"],"sources":["../../src/components/multi-select.tsx"],"sourcesContent":["import { Check, ChevronDown, X } from \"lucide-react\";\nimport { type ReactNode, useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { useHoverHighlight } from \"../utils/use-hover-highlight\";\nimport { Button } from \"./button\";\nimport { Input } from \"./input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\";\n\nexport interface MultiSelectOption {\n\t/** Stable identifier, independent of the display label. */\n\tvalue: string;\n\t/** Searchable and accessible display name. */\n\tlabel: string;\n\t/** Optional secondary searchable text. */\n\tdescription?: string;\n\t/** Caller-provided icon or avatar. */\n\tleading?: ReactNode;\n\t/** Prevent adding or removing this option. */\n\tdisabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n\t/** Accessible field and popup name. */\n\tlabel: string;\n\t/** Available options; retain selected options to preserve their display labels. */\n\toptions: readonly MultiSelectOption[];\n\t/** Controlled selected identifiers. */\n\tvalue?: readonly string[];\n\t/** Initial uncontrolled selection. @default [] */\n\tdefaultValue?: readonly string[];\n\t/** Selection requests; controlled values change only when accepted by the caller. */\n\tonValueChange?: (value: string[]) => void;\n\t/** Controlled search text; asynchronous requests belong to the caller. */\n\tquery?: string;\n\t/** Initial uncontrolled search text. @default \"\" */\n\tdefaultQuery?: string;\n\t/** Search text requests. */\n\tonQueryChange?: (query: string) => void;\n\t/** Controlled popup visibility. */\n\topen?: boolean;\n\t/** Initial uncontrolled popup visibility. @default false */\n\tdefaultOpen?: boolean;\n\t/** Popup visibility requests. */\n\tonOpenChange?: (open: boolean) => void;\n\t/** Use local label/description filtering; disable for server-filtered options. @default true */\n\tfilterOptions?: boolean;\n\t/** Loading content replaces the option list. @default false */\n\tloading?: boolean;\n\t/** Disables the trigger, chips, and native form entries. @default false */\n\tdisabled?: boolean;\n\t/** Repeated native form entry name, one entry per selected identifier. */\n\tname?: string;\n\t/** Optional external native form owner. Uncontrolled state respects uncancelled form resets. */\n\tform?: string;\n\t/** Empty selection prompt. @default \"Select options\" */\n\tplaceholder?: string;\n\t/** Search input prompt. @default \"Search options…\" */\n\tsearchPlaceholder?: string;\n\t/** Empty search result text. @default \"No options found.\" */\n\temptyLabel?: string;\n\t/** Loading announcement. @default \"Loading options…\" */\n\tloadingLabel?: string;\n\t/** Chip removal label prefix. @default \"Remove\" */\n\tremoveLabel?: string;\n\t/** Show removable selection chips; disable when a FilterBar renders its own chips. @default true */\n\tshowChips?: boolean;\n\t/** Selection count formatter, including localization. */\n\tformatSelectionCount?: (count: number) => string;\n\t/** Additional root classes. */\n\tclassName?: string;\n}\n\nexport function MultiSelect({\n\tlabel,\n\toptions,\n\tvalue,\n\tdefaultValue = [],\n\tonValueChange,\n\tquery,\n\tdefaultQuery = \"\",\n\tonQueryChange,\n\topen,\n\tdefaultOpen = false,\n\tonOpenChange,\n\tfilterOptions = true,\n\tloading = false,\n\tdisabled = false,\n\tname,\n\tform,\n\tplaceholder = \"Select options\",\n\tsearchPlaceholder = \"Search options…\",\n\temptyLabel = \"No options found.\",\n\tloadingLabel = \"Loading options…\",\n\tremoveLabel = \"Remove\",\n\tshowChips = true,\n\tformatSelectionCount,\n\tclassName,\n}: MultiSelectProps) {\n\tconst [localValue, setLocalValue] = useState(defaultValue);\n\tconst [localQuery, setLocalQuery] = useState(defaultQuery);\n\tconst [localOpen, setLocalOpen] = useState(defaultOpen);\n\tconst [active, setActive] = useState<string | null>(null);\n\tconst input = useRef<HTMLInputElement>(null);\n\tconst trigger = useRef<HTMLButtonElement>(null);\n\tconst list = useRef<HTMLDivElement>(null);\n\tconst highlightRef = useHoverHighlight(list);\n\tconst pointer = useRef<{ x: number; y: number } | null>(null);\n\tconst resetConfig = useRef({ value, defaultValue, query, defaultQuery, open });\n\tuseEffect(() => {\n\t\tresetConfig.current = { value, defaultValue, query, defaultQuery, open };\n\t});\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: form changes the native owner of the same trigger and requires rebinding its reset listener.\n\tuseEffect(() => {\n\t\tconst owner = trigger.current?.form;\n\t\tif (!owner) return;\n\t\tconst timers = new Set<ReturnType<typeof setTimeout>>();\n\t\tfunction reset(event: Event) {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\ttimers.delete(timer);\n\t\t\t\tif (event.defaultPrevented) return;\n\t\t\t\tconst config = resetConfig.current;\n\t\t\t\tif (config.value === undefined) setLocalValue(config.defaultValue);\n\t\t\t\tif (config.query === undefined) setLocalQuery(config.defaultQuery);\n\t\t\t\tif (config.open === undefined) setLocalOpen(false);\n\t\t\t\tsetActive(null);\n\t\t\t}, 0);\n\t\t\ttimers.add(timer);\n\t\t}\n\t\towner.addEventListener(\"reset\", reset);\n\t\treturn () => {\n\t\t\tfor (const timer of timers) clearTimeout(timer);\n\t\t\towner.removeEventListener(\"reset\", reset);\n\t\t};\n\t}, [form]);\n\tconst id = useId();\n\tconst selected = value ?? localValue;\n\tconst search = query ?? localQuery;\n\tconst expanded = (open ?? localOpen) && !disabled;\n\tconst visible = options.filter(\n\t\t(option) =>\n\t\t\t!filterOptions ||\n\t\t\t`${option.label} ${option.description ?? \"\"}`\n\t\t\t\t.toLocaleLowerCase()\n\t\t\t\t.includes(search.trim().toLocaleLowerCase()),\n\t);\n\tconst enabled = visible.filter((option) => !option.disabled);\n\tconst activeValue = enabled.some((option) => option.value === active)\n\t\t? active\n\t\t: enabled[0]?.value;\n\tconst activeIndex = visible.findIndex((option) => option.value === activeValue);\n\tconst countText = formatSelectionCount\n\t\t? formatSelectionCount(selected.length)\n\t\t: `${selected.length} selected`;\n\n\tfunction changeOpen(next: boolean) {\n\t\tif (disabled) return;\n\t\tif (open === undefined) setLocalOpen(next);\n\t\tonOpenChange?.(next);\n\t}\n\tfunction changeValue(next: string[]) {\n\t\tif (disabled) return;\n\t\tif (value === undefined) setLocalValue(next);\n\t\tonValueChange?.(next);\n\t}\n\tfunction toggle(option: MultiSelectOption) {\n\t\tif (option.disabled) return;\n\t\tchangeValue(\n\t\t\tselected.includes(option.value)\n\t\t\t\t? selected.filter((item) => item !== option.value)\n\t\t\t\t: [...selected, option.value],\n\t\t);\n\t}\n\tfunction removeLast() {\n\t\tif (search) return;\n\t\tconst item = [...selected]\n\t\t\t.reverse()\n\t\t\t.find((value) => !options.find((option) => option.value === value)?.disabled);\n\t\tif (item !== undefined) changeValue(selected.filter((value) => value !== item));\n\t}\n\n\treturn (\n\t\t<div className={cn(BASALT_UI_CLASS, \"min-w-0 space-y-basalt-space-lg\", className)}>\n\t\t\t{showChips && selected.length > 0 && (\n\t\t\t\t<div\n\t\t\t\t\trole=\"group\"\n\t\t\t\t\tclassName=\"flex flex-wrap gap-basalt-space-md\"\n\t\t\t\t\taria-label={`${label}: ${countText}`}\n\t\t\t\t>\n\t\t\t\t\t{selected.map((item) => {\n\t\t\t\t\t\tconst option = options.find((option) => option.value === item);\n\t\t\t\t\t\tconst text = option?.label ?? item;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={item}\n\t\t\t\t\t\t\t\tclassName=\"inline-flex max-w-full items-center gap-basalt-space-sm rounded-basalt-md border border-basalt-border bg-basalt-selected py-basalt-space-xs pl-basalt-space-lg text-basalt-sm\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"min-w-0 break-words\">{text}</span>\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\tdisabled={disabled || option?.disabled}\n\t\t\t\t\t\t\t\t\taria-label={`${removeLabel} ${text}`}\n\t\t\t\t\t\t\t\t\tclassName=\"rounded-basalt-sm p-basalt-space-sm focus-visible:outline-2 focus-visible:outline-basalt-ring disabled:opacity-50\"\n\t\t\t\t\t\t\t\t\tonClick={() => changeValue(selected.filter((value) => value !== item))}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<X className=\"size-basalt-icon-sm\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t{!disabled &&\n\t\t\t\tname &&\n\t\t\t\tselected.map((item) => (\n\t\t\t\t\t<input key={item} type=\"hidden\" name={name} form={form} value={item} />\n\t\t\t\t))}\n\t\t\t<Popover open={expanded} onOpenChange={changeOpen}>\n\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tref={trigger}\n\t\t\t\t\t\tform={form}\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\t\taria-label={label}\n\t\t\t\t\t\taria-describedby={`${id}-count`}\n\t\t\t\t\t\tclassName=\"w-full justify-between\"\n\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\tif (event.nativeEvent.isComposing) return;\n\t\t\t\t\t\t\tif (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tchangeOpen(true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (event.key === \"Backspace\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tremoveLast();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"truncate\">{selected.length ? countText : placeholder}</span>\n\t\t\t\t\t\t<ChevronDown aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</PopoverTrigger>\n\t\t\t\t<PopoverContent\n\t\t\t\t\talign=\"start\"\n\t\t\t\t\tclassName=\"w-basalt-72 max-w-[calc(100vw-2rem)] p-basalt-space-lg\"\n\t\t\t\t\taria-label={label}\n\t\t\t\t\tonOpenAutoFocus={(event) => {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tinput.current?.focus();\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tref={input}\n\t\t\t\t\t\trole=\"combobox\"\n\t\t\t\t\t\taria-label={`${label}: search`}\n\t\t\t\t\t\taria-expanded={expanded}\n\t\t\t\t\t\taria-autocomplete=\"list\"\n\t\t\t\t\t\taria-controls={`${id}-list`}\n\t\t\t\t\t\taria-activedescendant={\n\t\t\t\t\t\t\t!loading && activeIndex >= 0 ? `${id}-option-${activeIndex}` : undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t\tplaceholder={searchPlaceholder}\n\t\t\t\t\t\tvalue={search}\n\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\tif (query === undefined) setLocalQuery(event.target.value);\n\t\t\t\t\t\t\tonQueryChange?.(event.target.value);\n\t\t\t\t\t\t\tsetActive(null);\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) return;\n\t\t\t\t\t\t\tif (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tif (!loading && enabled.length) {\n\t\t\t\t\t\t\t\t\tconst index = enabled.findIndex((option) => option.value === activeValue);\n\t\t\t\t\t\t\t\t\tconst next =\n\t\t\t\t\t\t\t\t\t\tenabled[\n\t\t\t\t\t\t\t\t\t\t\t(index + (event.key === \"ArrowDown\" ? 1 : -1) + enabled.length) %\n\t\t\t\t\t\t\t\t\t\t\t\tenabled.length\n\t\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t\tsetActive(next.value);\n\t\t\t\t\t\t\t\t\tlist.current\n\t\t\t\t\t\t\t\t\t\t?.querySelector<HTMLElement>(`[data-option-index=\"${visible.indexOf(next)}\"]`)\n\t\t\t\t\t\t\t\t\t\t?.scrollIntoView({ block: \"nearest\" });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (event.key === \"Enter\") {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tconst option = enabled.find((option) => option.value === activeValue);\n\t\t\t\t\t\t\t\tif (!loading && option) toggle(option);\n\t\t\t\t\t\t\t} else if (event.key === \"Backspace\" && !search) {\n\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\tremoveLast();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t\t<div\n\t\t\t\t\t\tref={highlightRef}\n\t\t\t\t\t\tid={`${id}-list`}\n\t\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\t\taria-label={label}\n\t\t\t\t\t\taria-multiselectable=\"true\"\n\t\t\t\t\t\taria-busy={loading}\n\t\t\t\t\t\tclassName=\"basalt-hover-list mt-basalt-space-lg max-h-basalt-60 overflow-y-auto\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{!loading &&\n\t\t\t\t\t\t\tvisible.map((option, 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\trole=\"option\"\n\t\t\t\t\t\t\t\t\tdata-basalt-hover-item=\"\"\n\t\t\t\t\t\t\t\t\tdata-hover-active={activeValue === option.value}\n\t\t\t\t\t\t\t\t\tid={`${id}-option-${index}`}\n\t\t\t\t\t\t\t\t\tdata-option-index={index}\n\t\t\t\t\t\t\t\t\tkey={option.value}\n\t\t\t\t\t\t\t\t\ttabIndex={-1}\n\t\t\t\t\t\t\t\t\taria-selected={selected.includes(option.value)}\n\t\t\t\t\t\t\t\t\tdata-hover-selected={selected.includes(option.value)}\n\t\t\t\t\t\t\t\t\taria-disabled={option.disabled || undefined}\n\t\t\t\t\t\t\t\t\tdisabled={option.disabled}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"flex w-full items-center gap-basalt-space-lg rounded-basalt-md px-basalt-space-lg py-basalt-space-lg text-left text-basalt-base disabled:opacity-40\",\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\tconst previous = pointer.current;\n\t\t\t\t\t\t\t\t\t\tif (previous?.x === event.clientX && previous.y === event.clientY) return;\n\t\t\t\t\t\t\t\t\t\tpointer.current = { x: event.clientX, y: event.clientY };\n\t\t\t\t\t\t\t\t\t\tif (!option.disabled) setActive(option.value);\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={() => {\n\t\t\t\t\t\t\t\t\t\ttoggle(option);\n\t\t\t\t\t\t\t\t\t\tinput.current?.focus();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{option.leading}\n\t\t\t\t\t\t\t\t\t<span className=\"min-w-0 flex-1\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"block break-words\">{option.label}</span>\n\t\t\t\t\t\t\t\t\t\t{option.description && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"block text-basalt-sm text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t{option.description}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{selected.includes(option.value) && (\n\t\t\t\t\t\t\t\t\t\t<Check className=\"size-basalt-icon-lg shrink-0\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t\t{(loading || visible.length === 0) && (\n\t\t\t\t\t\t<p\n\t\t\t\t\t\t\trole=\"status\"\n\t\t\t\t\t\t\tclassName=\"px-basalt-space-lg py-basalt-space-lg text-basalt-base text-basalt-muted-foreground\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{loading ? loadingLabel : emptyLabel}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</PopoverContent>\n\t\t\t</Popover>\n\t\t\t<span id={`${id}-count`} className=\"sr-only\" aria-live=\"polite\">\n\t\t\t\t{countText}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
@@ -53,7 +53,7 @@ export interface NavigationMenuListProps extends Omit<RadixNavigationMenuListPro
53
53
  */
54
54
  asChild?: RadixNavigationMenuListProps["asChild"];
55
55
  }
56
- export declare const NavigationMenuList: React.ForwardRefExoticComponent<NavigationMenuPrimitive.NavigationMenuListProps & React.RefAttributes<HTMLUListElement>>;
56
+ export declare function NavigationMenuList({ className, ref, ...props }: NavigationMenuListProps): React.JSX.Element;
57
57
  export interface NavigationMenuItemProps extends Omit<RadixNavigationMenuItemProps, "value" | "asChild"> {
58
58
  /**
59
59
  * Unique value that associates the item with sub-navigation state.
@@ -85,5 +85,5 @@ export interface NavigationMenuLinkProps extends Omit<RadixNavigationMenuLinkPro
85
85
  */
86
86
  asChild?: RadixNavigationMenuLinkProps["asChild"];
87
87
  }
88
- export declare function NavigationMenuLink({ className, ...props }: NavigationMenuLinkProps): React.JSX.Element;
88
+ export declare function NavigationMenuLink({ className, active, ...props }: NavigationMenuLinkProps): React.JSX.Element;
89
89
  export {};
@@ -1,16 +1,30 @@
1
1
  "use client";
2
2
  import { cn as e } from "../utils/cn.js";
3
- import { jsx as t } from "react/jsx-runtime";
4
- import * as n from "@radix-ui/react-navigation-menu";
3
+ import { useHoverHighlight as t } from "../utils/use-hover-highlight.js";
4
+ import { NAV_ITEM_CLASS as n, NAV_LIST_CLASS as r } from "../utils/navigation.js";
5
+ import { jsx as i } from "react/jsx-runtime";
6
+ import * as a from "@radix-ui/react-navigation-menu";
5
7
  //#region src/components/navigation-menu.tsx
6
- var r = n.Root, i = n.List, a = n.Item;
7
- function o({ className: r, ...i }) {
8
- return /* @__PURE__ */ t(n.Link, {
9
- className: e("rounded-basalt-md px-3 py-2 text-sm hover:bg-basalt-accent", r),
10
- ...i
8
+ var o = a.Root;
9
+ function s({ className: n, ref: o, ...s }) {
10
+ let c = t(o);
11
+ return /* @__PURE__ */ i(a.List, {
12
+ ref: c,
13
+ className: e(r, "data-[orientation=vertical]:flex-col", n),
14
+ ...s
15
+ });
16
+ }
17
+ var c = a.Item;
18
+ function l({ className: t, active: r, ...o }) {
19
+ return /* @__PURE__ */ i(a.Link, {
20
+ active: r,
21
+ "data-basalt-hover-item": "",
22
+ "data-hover-selected": r,
23
+ className: e(n, t),
24
+ ...o
11
25
  });
12
26
  }
13
27
  //#endregion
14
- export { r as NavigationMenu, a as NavigationMenuItem, o as NavigationMenuLink, i as NavigationMenuList };
28
+ export { o as NavigationMenu, c as NavigationMenuItem, l as NavigationMenuLink, s as NavigationMenuList };
15
29
 
16
30
  //# sourceMappingURL=navigation-menu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-menu.js","names":["NavigationMenuPrimitive","NavigationMenu","NavigationMenuList","NavigationMenuItem","NavigationMenuLink","props","cn","className"],"sources":["../../src/components/navigation-menu.tsx"],"sourcesContent":["import * as NavigationMenuPrimitive from \"@radix-ui/react-navigation-menu\";\nimport type * as React from \"react\";\nimport { cn } from \"../utils/cn\";\n\ntype RadixNavigationMenuProps = React.ComponentProps<typeof NavigationMenuPrimitive.Root>;\ntype RadixNavigationMenuListProps = React.ComponentProps<typeof NavigationMenuPrimitive.List>;\ntype RadixNavigationMenuItemProps = React.ComponentProps<typeof NavigationMenuPrimitive.Item>;\ntype RadixNavigationMenuLinkProps = React.ComponentProps<typeof NavigationMenuPrimitive.Link>;\n\nexport interface NavigationMenuProps\n\textends Omit<\n\t\tRadixNavigationMenuProps,\n\t\t| \"value\"\n\t\t| \"defaultValue\"\n\t\t| \"onValueChange\"\n\t\t| \"dir\"\n\t\t| \"orientation\"\n\t\t| \"delayDuration\"\n\t\t| \"skipDelayDuration\"\n\t\t| \"asChild\"\n\t> {\n\t/**\n\t * Controlled value of the currently active navigation item.\n\t */\n\tvalue?: RadixNavigationMenuProps[\"value\"];\n\t/**\n\t * Uncontrolled initial active navigation item value.\n\t */\n\tdefaultValue?: RadixNavigationMenuProps[\"defaultValue\"];\n\t/**\n\t * Callback invoked when active navigation item value changes.\n\t */\n\tonValueChange?: RadixNavigationMenuProps[\"onValueChange\"];\n\t/**\n\t * Reading direction of the navigation menu. Inherits from nearest DirectionProvider or ltr.\n\t * @default \"ltr\"\n\t */\n\tdir?: RadixNavigationMenuProps[\"dir\"];\n\t/**\n\t * Orientation of the navigation menu items and keyboard navigation axis.\n\t * @default \"horizontal\"\n\t */\n\torientation?: RadixNavigationMenuProps[\"orientation\"];\n\t/**\n\t * Duration in milliseconds from pointer entering trigger until menu opens.\n\t * @default 200\n\t */\n\tdelayDuration?: RadixNavigationMenuProps[\"delayDuration\"];\n\t/**\n\t * Duration in milliseconds to move between triggers without re-triggering delay.\n\t * @default 300\n\t */\n\tskipDelayDuration?: RadixNavigationMenuProps[\"skipDelayDuration\"];\n\t/**\n\t * Change the default rendered nav element to the child element, merging props and behavior.\n\t * NavigationMenu forwards ref to HTMLElement and inherits native nav attributes.\n\t * @default false\n\t */\n\tasChild?: RadixNavigationMenuProps[\"asChild\"];\n}\n\nexport const NavigationMenu = NavigationMenuPrimitive.Root;\n\nexport interface NavigationMenuListProps extends Omit<RadixNavigationMenuListProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered ul element to the child element, merging props and behavior.\n\t * NavigationMenuList forwards ref to HTMLUListElement and inherits native ul attributes.\n\t * @default false\n\t */\n\tasChild?: RadixNavigationMenuListProps[\"asChild\"];\n}\n\nexport const NavigationMenuList = NavigationMenuPrimitive.List;\n\nexport interface NavigationMenuItemProps\n\textends Omit<RadixNavigationMenuItemProps, \"value\" | \"asChild\"> {\n\t/**\n\t * Unique value that associates the item with sub-navigation state.\n\t */\n\tvalue?: RadixNavigationMenuItemProps[\"value\"];\n\t/**\n\t * Change the default rendered li element to the child element, merging props and behavior.\n\t * NavigationMenuItem forwards ref to HTMLLIElement and inherits native li attributes.\n\t * @default false\n\t */\n\tasChild?: RadixNavigationMenuItemProps[\"asChild\"];\n}\n\nexport const NavigationMenuItem = NavigationMenuPrimitive.Item;\n\nexport interface NavigationMenuLinkProps\n\textends Omit<RadixNavigationMenuLinkProps, \"active\" | \"onSelect\" | \"asChild\"> {\n\t/**\n\t * Whether the link represents the currently active destination.\n\t * Adds data-active attribute for styling.\n\t * @default false\n\t */\n\tactive?: RadixNavigationMenuLinkProps[\"active\"];\n\t/**\n\t * Event handler called when the navigation link is selected via pointer or keyboard.\n\t */\n\tonSelect?: RadixNavigationMenuLinkProps[\"onSelect\"];\n\t/**\n\t * Change the default rendered anchor element to the child element, merging props and behavior.\n\t * NavigationMenuLink forwards ref to HTMLAnchorElement and inherits native anchor attributes.\n\t * @default false\n\t */\n\tasChild?: RadixNavigationMenuLinkProps[\"asChild\"];\n}\n\nexport function NavigationMenuLink({ className, ...props }: NavigationMenuLinkProps) {\n\treturn (\n\t\t<NavigationMenuPrimitive.Link\n\t\t\tclassName={cn(\"rounded-basalt-md px-3 py-2 text-sm hover:bg-basalt-accent\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n"],"mappings":";;;;;AA6DA,IAAaC,IAAiBD,EAAwB,MAWzCE,IAAqBF,EAAwB,MAgB7CG,IAAqBH,EAAwB;AAsB1D,SAAgBI,EAAmB,EAAE,cAAW,GAAGC,KAAkC;CACpF,OACC,kBAACL,EAAwB,MAAzB;EACC,WAAWM,EAAG,8DAA8DC,CAAS;EACrF,GAAIF;CACJ,CAAA;AAEH"}
1
+ {"version":3,"file":"navigation-menu.js","names":["NavigationMenuPrimitive","NavigationMenu","NavigationMenuList","props","highlightRef","useHoverHighlight","ref","cn","NAV_LIST_CLASS","className","NavigationMenuItem","NavigationMenuLink","active","NAV_ITEM_CLASS"],"sources":["../../src/components/navigation-menu.tsx"],"sourcesContent":["import * as NavigationMenuPrimitive from \"@radix-ui/react-navigation-menu\";\nimport type * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { NAV_ITEM_CLASS, NAV_LIST_CLASS } from \"../utils/navigation\";\nimport { useHoverHighlight } from \"../utils/use-hover-highlight\";\n\ntype RadixNavigationMenuProps = React.ComponentProps<typeof NavigationMenuPrimitive.Root>;\ntype RadixNavigationMenuListProps = React.ComponentProps<typeof NavigationMenuPrimitive.List>;\ntype RadixNavigationMenuItemProps = React.ComponentProps<typeof NavigationMenuPrimitive.Item>;\ntype RadixNavigationMenuLinkProps = React.ComponentProps<typeof NavigationMenuPrimitive.Link>;\n\nexport interface NavigationMenuProps\n\textends Omit<\n\t\tRadixNavigationMenuProps,\n\t\t| \"value\"\n\t\t| \"defaultValue\"\n\t\t| \"onValueChange\"\n\t\t| \"dir\"\n\t\t| \"orientation\"\n\t\t| \"delayDuration\"\n\t\t| \"skipDelayDuration\"\n\t\t| \"asChild\"\n\t> {\n\t/**\n\t * Controlled value of the currently active navigation item.\n\t */\n\tvalue?: RadixNavigationMenuProps[\"value\"];\n\t/**\n\t * Uncontrolled initial active navigation item value.\n\t */\n\tdefaultValue?: RadixNavigationMenuProps[\"defaultValue\"];\n\t/**\n\t * Callback invoked when active navigation item value changes.\n\t */\n\tonValueChange?: RadixNavigationMenuProps[\"onValueChange\"];\n\t/**\n\t * Reading direction of the navigation menu. Inherits from nearest DirectionProvider or ltr.\n\t * @default \"ltr\"\n\t */\n\tdir?: RadixNavigationMenuProps[\"dir\"];\n\t/**\n\t * Orientation of the navigation menu items and keyboard navigation axis.\n\t * @default \"horizontal\"\n\t */\n\torientation?: RadixNavigationMenuProps[\"orientation\"];\n\t/**\n\t * Duration in milliseconds from pointer entering trigger until menu opens.\n\t * @default 200\n\t */\n\tdelayDuration?: RadixNavigationMenuProps[\"delayDuration\"];\n\t/**\n\t * Duration in milliseconds to move between triggers without re-triggering delay.\n\t * @default 300\n\t */\n\tskipDelayDuration?: RadixNavigationMenuProps[\"skipDelayDuration\"];\n\t/**\n\t * Change the default rendered nav element to the child element, merging props and behavior.\n\t * NavigationMenu forwards ref to HTMLElement and inherits native nav attributes.\n\t * @default false\n\t */\n\tasChild?: RadixNavigationMenuProps[\"asChild\"];\n}\n\nexport const NavigationMenu = NavigationMenuPrimitive.Root;\n\nexport interface NavigationMenuListProps extends Omit<RadixNavigationMenuListProps, \"asChild\"> {\n\t/**\n\t * Change the default rendered ul element to the child element, merging props and behavior.\n\t * NavigationMenuList forwards ref to HTMLUListElement and inherits native ul attributes.\n\t * @default false\n\t */\n\tasChild?: RadixNavigationMenuListProps[\"asChild\"];\n}\n\nexport function NavigationMenuList({ className, ref, ...props }: NavigationMenuListProps) {\n\tconst highlightRef = useHoverHighlight(ref);\n\treturn (\n\t\t<NavigationMenuPrimitive.List\n\t\t\tref={highlightRef}\n\t\t\tclassName={cn(NAV_LIST_CLASS, \"data-[orientation=vertical]:flex-col\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport interface NavigationMenuItemProps\n\textends Omit<RadixNavigationMenuItemProps, \"value\" | \"asChild\"> {\n\t/**\n\t * Unique value that associates the item with sub-navigation state.\n\t */\n\tvalue?: RadixNavigationMenuItemProps[\"value\"];\n\t/**\n\t * Change the default rendered li element to the child element, merging props and behavior.\n\t * NavigationMenuItem forwards ref to HTMLLIElement and inherits native li attributes.\n\t * @default false\n\t */\n\tasChild?: RadixNavigationMenuItemProps[\"asChild\"];\n}\n\nexport const NavigationMenuItem = NavigationMenuPrimitive.Item;\n\nexport interface NavigationMenuLinkProps\n\textends Omit<RadixNavigationMenuLinkProps, \"active\" | \"onSelect\" | \"asChild\"> {\n\t/**\n\t * Whether the link represents the currently active destination.\n\t * Adds data-active attribute for styling.\n\t * @default false\n\t */\n\tactive?: RadixNavigationMenuLinkProps[\"active\"];\n\t/**\n\t * Event handler called when the navigation link is selected via pointer or keyboard.\n\t */\n\tonSelect?: RadixNavigationMenuLinkProps[\"onSelect\"];\n\t/**\n\t * Change the default rendered anchor element to the child element, merging props and behavior.\n\t * NavigationMenuLink forwards ref to HTMLAnchorElement and inherits native anchor attributes.\n\t * @default false\n\t */\n\tasChild?: RadixNavigationMenuLinkProps[\"asChild\"];\n}\n\nexport function NavigationMenuLink({ className, active, ...props }: NavigationMenuLinkProps) {\n\treturn (\n\t\t<NavigationMenuPrimitive.Link\n\t\t\tactive={active}\n\t\t\tdata-basalt-hover-item=\"\"\n\t\t\tdata-hover-selected={active}\n\t\t\tclassName={cn(NAV_ITEM_CLASS, className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n"],"mappings":";;;;;;;AA+DA,IAAaC,IAAiBD,EAAwB;AAWtD,SAAgBE,EAAmB,EAAE,cAAW,QAAK,GAAGC,KAAkC;CACzF,IAAMC,IAAeC,EAAkBC,CAAG;CAC1C,OACC,kBAACN,EAAwB,MAAzB;EACC,KAAKI;EACL,WAAWG,EAAGC,GAAgB,wCAAwCC,CAAS;EAC/E,GAAIN;CACJ,CAAA;AAEH;AAgBA,IAAaO,IAAqBV,EAAwB;AAsB1D,SAAgBW,EAAmB,EAAE,cAAW,WAAQ,GAAGR,KAAkC;CAC5F,OACC,kBAACH,EAAwB,MAAzB;EACS;EACR,0BAAuB;EACvB,uBAAqBY;EACrB,WAAWL,EAAGM,GAAgBJ,CAAS;EACvC,GAAIN;CACJ,CAAA;AAEH"}
@@ -4,10 +4,10 @@ import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
4
4
  //#region src/components/overlay.ts
5
5
  var n = 4, r = 8, i = "z-50", a = "motion-reduce:animate-none motion-reduce:transition-none", o = "outline-hidden focus-visible:border-basalt-ring", s = "outline-hidden focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-basalt-ring", c = "outline-hidden focus-visible:ring-2 focus-visible:ring-basalt-ring focus-visible:ring-offset-2 focus-visible:ring-offset-basalt-background";
6
6
  function l(n) {
7
- return e(t, i, "overflow-hidden rounded-basalt-md border border-basalt-border bg-basalt-popover py-1.5 text-sm text-basalt-popover-foreground shadow-md", a, n);
7
+ return e(t, i, "basalt-floating overflow-hidden rounded-basalt-md border border-basalt-border bg-basalt-popover py-basalt-menu-inset text-basalt-base text-basalt-popover-foreground shadow-md", a, n);
8
8
  }
9
9
  function u(t) {
10
- return e("mx-1.5 flex w-[calc(100%-0.75rem)] cursor-default items-center rounded-basalt-sm px-2 py-1.5 text-left text-sm outline-hidden select-none", t);
10
+ return e("mx-basalt-menu-inset flex min-h-basalt-menu-row w-[calc(100%-var(--basalt-space-menu-inset)*2)] cursor-default items-center gap-basalt-row-gap rounded-basalt-sm px-basalt-menu-x py-basalt-menu-y text-left text-basalt-base leading-basalt-row outline-hidden select-none", t);
11
11
  }
12
12
  //#endregion
13
13
  export { o as FOCUS_BORDER, s as FOCUS_INSET, c as FOCUS_RING, r as MENU_GAP, n as OVERLAY_GAP, i as OVERLAY_LAYER, a as OVERLAY_MOTION, u as overlayItemClass, l as overlayPanelClass };
@@ -1 +1 @@
1
- {"version":3,"file":"overlay.js","names":["OVERLAY_GAP","MENU_GAP","OVERLAY_LAYER","OVERLAY_MOTION","FOCUS_BORDER","FOCUS_INSET","FOCUS_RING","overlayPanelClass","cn","BASALT_UI_CLASS","className","overlayItemClass"],"sources":["../../src/components/overlay.ts"],"sourcesContent":["import { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\n\n/** Gap between a field trigger and its list. Matches Kumo Select/Combobox. */\nexport const OVERLAY_GAP = 4;\n/** Gap between a menu trigger and its panel. Matches Kumo Dropdown/Popover. */\nexport const MENU_GAP = 8;\n/** Shared stacking layer for portals, menus, and chrome overlays. */\nexport const OVERLAY_LAYER = \"z-50\";\n/** Kill CSS animation and transition when the user prefers reduced motion. */\nexport const OVERLAY_MOTION = \"motion-reduce:animate-none motion-reduce:transition-none\";\n\n/** Recolor the existing 1px border. No extra ring, no layout shift. */\nexport const FOCUS_BORDER = \"outline-hidden focus-visible:border-basalt-ring\";\n/** Inset 1px theme ring for borderless controls. Outer size stays put. */\nexport const FOCUS_INSET =\n\t\"outline-hidden focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-basalt-ring\";\n/** Offset ring stays visible on primary fills (ring token equals primary). */\nexport const FOCUS_RING =\n\t\"outline-hidden focus-visible:ring-2 focus-visible:ring-basalt-ring focus-visible:ring-offset-2 focus-visible:ring-offset-basalt-background\";\n\nexport function overlayPanelClass(className?: string) {\n\treturn cn(\n\t\tBASALT_UI_CLASS,\n\t\tOVERLAY_LAYER,\n\t\t\"overflow-hidden rounded-basalt-md border border-basalt-border bg-basalt-popover py-1.5 text-sm text-basalt-popover-foreground shadow-md\",\n\t\tOVERLAY_MOTION,\n\t\tclassName,\n\t);\n}\n\nexport function overlayItemClass(className?: string) {\n\treturn cn(\n\t\t\"mx-1.5 flex w-[calc(100%-0.75rem)] cursor-default items-center rounded-basalt-sm px-2 py-1.5 text-left text-sm outline-hidden select-none\",\n\t\tclassName,\n\t);\n}\n"],"mappings":";;;;AAIA,IAAaA,IAAc,GAEdC,IAAW,GAEXC,IAAgB,QAEhBC,IAAiB,4DAGjBC,IAAe,mDAEfC,IACZ,+FAEYC,IACZ;AAED,SAAgBC,EAAkB,GAAoB;CACrD,OAAOC,EACNC,GACAP,GACA,2IACAC,GACAO,CACD;AACD;AAEA,SAAgBC,EAAiB,GAAoB;CACpD,OAAOH,EACN,6IACAE,CACD;AACD"}
1
+ {"version":3,"file":"overlay.js","names":["OVERLAY_GAP","MENU_GAP","OVERLAY_LAYER","OVERLAY_MOTION","FOCUS_BORDER","FOCUS_INSET","FOCUS_RING","overlayPanelClass","cn","BASALT_UI_CLASS","className","overlayItemClass"],"sources":["../../src/components/overlay.ts"],"sourcesContent":["import { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\n\n/** Gap between a field trigger and its list. Matches Kumo Select/Combobox. */\nexport const OVERLAY_GAP = 4;\n/** Gap between a menu trigger and its panel. Matches Kumo Dropdown/Popover. */\nexport const MENU_GAP = 8;\n/** Shared stacking layer for portals, menus, and chrome overlays. */\nexport const OVERLAY_LAYER = \"z-50\";\n/** Kill CSS animation and transition when the user prefers reduced motion. */\nexport const OVERLAY_MOTION = \"motion-reduce:animate-none motion-reduce:transition-none\";\n\n/** Recolor the existing 1px border. No extra ring, no layout shift. */\nexport const FOCUS_BORDER = \"outline-hidden focus-visible:border-basalt-ring\";\n/** Inset 1px theme ring for borderless controls. Outer size stays put. */\nexport const FOCUS_INSET =\n\t\"outline-hidden focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-basalt-ring\";\n/** Offset ring stays visible on primary fills (ring token equals primary). */\nexport const FOCUS_RING =\n\t\"outline-hidden focus-visible:ring-2 focus-visible:ring-basalt-ring focus-visible:ring-offset-2 focus-visible:ring-offset-basalt-background\";\n\nexport function overlayPanelClass(className?: string) {\n\treturn cn(\n\t\tBASALT_UI_CLASS,\n\t\tOVERLAY_LAYER,\n\t\t\"basalt-floating overflow-hidden rounded-basalt-md border border-basalt-border bg-basalt-popover py-basalt-menu-inset text-basalt-base text-basalt-popover-foreground shadow-md\",\n\t\tOVERLAY_MOTION,\n\t\tclassName,\n\t);\n}\n\nexport function overlayItemClass(className?: string) {\n\treturn cn(\n\t\t\"mx-basalt-menu-inset flex min-h-basalt-menu-row w-[calc(100%-var(--basalt-space-menu-inset)*2)] cursor-default items-center gap-basalt-row-gap rounded-basalt-sm px-basalt-menu-x py-basalt-menu-y text-left text-basalt-base leading-basalt-row outline-hidden select-none\",\n\t\tclassName,\n\t);\n}\n"],"mappings":";;;;AAIA,IAAaA,IAAc,GAEdC,IAAW,GAEXC,IAAgB,QAEhBC,IAAiB,4DAGjBC,IAAe,mDAEfC,IACZ,+FAEYC,IACZ;AAED,SAAgBC,EAAkB,GAAoB;CACrD,OAAOC,EACNC,GACAP,GACA,kLACAC,GACAO,CACD;AACD;AAEA,SAAgBC,EAAiB,GAAoB;CACpD,OAAOH,EACN,+QACAE,CACD;AACD"}
@@ -5,6 +5,8 @@ export interface PageHeaderBreadcrumb {
5
5
  icon?: React.ReactNode;
6
6
  }
7
7
  export interface PageHeaderProps {
8
+ /** Heading scale: workspace, dashboard, or reading-focused page. @default "md" */
9
+ size?: "md" | "lg" | "xl";
8
10
  /** The page title, rendered as the only heading. */
9
11
  title: React.ReactNode;
10
12
  /** Supporting text below the title. */
@@ -16,4 +18,4 @@ export interface PageHeaderProps {
16
18
  /** Own-row filters. Keep short filters in actions. */
17
19
  filters?: React.ReactNode;
18
20
  }
19
- export declare function PageHeader({ actions, breadcrumbs, description, filters, title }: PageHeaderProps): React.JSX.Element;
21
+ export declare function PageHeader({ actions, breadcrumbs, description, filters, size, title, }: PageHeaderProps): React.JSX.Element;