@nadicodeai/ui 12.1.101 → 12.1.103

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 (567) hide show
  1. package/dist/components/accordion.d.ts +38 -12
  2. package/dist/components/accordion.d.ts.map +1 -1
  3. package/dist/components/accordion.js +36 -15
  4. package/dist/components/account-menu.d.ts +18 -18
  5. package/dist/components/account-menu.d.ts.map +1 -1
  6. package/dist/components/account-menu.js +17 -13
  7. package/dist/components/action-menu.d.ts +20 -19
  8. package/dist/components/action-menu.d.ts.map +1 -1
  9. package/dist/components/action-menu.js +22 -15
  10. package/dist/components/action-status.d.ts +14 -10
  11. package/dist/components/action-status.d.ts.map +1 -1
  12. package/dist/components/action-status.js +14 -10
  13. package/dist/components/activity-calendar.d.ts +3 -1
  14. package/dist/components/activity-calendar.d.ts.map +1 -1
  15. package/dist/components/activity-calendar.js +16 -2
  16. package/dist/components/activity-feed.d.ts +2 -2
  17. package/dist/components/activity-feed.d.ts.map +1 -1
  18. package/dist/components/activity-feed.js +14 -5
  19. package/dist/components/agent-card.d.ts +11 -5
  20. package/dist/components/agent-card.d.ts.map +1 -1
  21. package/dist/components/agent-card.js +13 -7
  22. package/dist/components/agent-list-row.js +1 -1
  23. package/dist/components/agent-orb-appearance.d.ts +7 -1
  24. package/dist/components/agent-orb-appearance.d.ts.map +1 -1
  25. package/dist/components/agent-orb-appearance.js +26 -0
  26. package/dist/components/agent-profile.d.ts +8 -7
  27. package/dist/components/agent-profile.d.ts.map +1 -1
  28. package/dist/components/agent-profile.js +6 -6
  29. package/dist/components/alert-dialog.d.ts +25 -16
  30. package/dist/components/alert-dialog.d.ts.map +1 -1
  31. package/dist/components/alert-dialog.js +30 -18
  32. package/dist/components/alert.d.ts +1 -1
  33. package/dist/components/avatar.d.ts +83 -17
  34. package/dist/components/avatar.d.ts.map +1 -1
  35. package/dist/components/avatar.js +89 -23
  36. package/dist/components/badge.d.ts +59 -8
  37. package/dist/components/badge.d.ts.map +1 -1
  38. package/dist/components/badge.js +57 -14
  39. package/dist/components/breadcrumb.d.ts +18 -7
  40. package/dist/components/breadcrumb.d.ts.map +1 -1
  41. package/dist/components/breadcrumb.js +37 -13
  42. package/dist/components/button-group.d.ts +12 -15
  43. package/dist/components/button-group.d.ts.map +1 -1
  44. package/dist/components/button-group.js +13 -17
  45. package/dist/components/button.d.ts +126 -23
  46. package/dist/components/button.d.ts.map +1 -1
  47. package/dist/components/button.js +164 -39
  48. package/dist/components/calendar.d.ts +26 -15
  49. package/dist/components/calendar.d.ts.map +1 -1
  50. package/dist/components/calendar.js +60 -46
  51. package/dist/components/card-row.d.ts +13 -5
  52. package/dist/components/card-row.d.ts.map +1 -1
  53. package/dist/components/card-row.js +24 -10
  54. package/dist/components/card.d.ts +6 -12
  55. package/dist/components/card.d.ts.map +1 -1
  56. package/dist/components/card.js +11 -16
  57. package/dist/components/carousel.d.ts +3 -3
  58. package/dist/components/carousel.d.ts.map +1 -1
  59. package/dist/components/carousel.js +7 -7
  60. package/dist/components/catalog-drawer.d.ts +7 -7
  61. package/dist/components/catalog-drawer.d.ts.map +1 -1
  62. package/dist/components/catalog-drawer.js +6 -6
  63. package/dist/components/chart-area-stacked.d.ts +1 -1
  64. package/dist/components/chart-area-stacked.d.ts.map +1 -1
  65. package/dist/components/chart-area-stacked.js +4 -4
  66. package/dist/components/chart-axis.d.ts +14 -1
  67. package/dist/components/chart-axis.d.ts.map +1 -1
  68. package/dist/components/chart-axis.js +11 -1
  69. package/dist/components/chart-bar-horizontal.d.ts +2 -2
  70. package/dist/components/chart-bar-horizontal.d.ts.map +1 -1
  71. package/dist/components/chart-bar-horizontal.js +14 -5
  72. package/dist/components/chart-bar-multiple.d.ts +1 -1
  73. package/dist/components/chart-bar-multiple.d.ts.map +1 -1
  74. package/dist/components/chart-bar-multiple.js +3 -3
  75. package/dist/components/chart-bar-stacked.d.ts +1 -1
  76. package/dist/components/chart-bar-stacked.d.ts.map +1 -1
  77. package/dist/components/chart-bar-stacked.js +3 -3
  78. package/dist/components/chart-card.d.ts +17 -6
  79. package/dist/components/chart-card.d.ts.map +1 -1
  80. package/dist/components/chart-card.js +11 -4
  81. package/dist/components/chart-date-range-control.d.ts +18 -9
  82. package/dist/components/chart-date-range-control.d.ts.map +1 -1
  83. package/dist/components/chart-date-range-control.js +43 -47
  84. package/dist/components/chart-sparkline.d.ts +4 -3
  85. package/dist/components/chart-sparkline.d.ts.map +1 -1
  86. package/dist/components/chart-sparkline.js +6 -5
  87. package/dist/components/check-counts.d.ts +44 -0
  88. package/dist/components/check-counts.d.ts.map +1 -0
  89. package/dist/components/check-counts.js +51 -0
  90. package/dist/components/checkbox.d.ts +39 -11
  91. package/dist/components/checkbox.d.ts.map +1 -1
  92. package/dist/components/checkbox.js +38 -11
  93. package/dist/components/choicebox.d.ts +96 -0
  94. package/dist/components/choicebox.d.ts.map +1 -0
  95. package/dist/components/choicebox.js +113 -0
  96. package/dist/components/clearable-input.d.ts +57 -0
  97. package/dist/components/clearable-input.d.ts.map +1 -0
  98. package/dist/components/clearable-input.js +58 -0
  99. package/dist/components/code-block.d.ts +77 -17
  100. package/dist/components/code-block.d.ts.map +1 -1
  101. package/dist/components/code-block.js +85 -15
  102. package/dist/components/collapsible.d.ts +27 -10
  103. package/dist/components/collapsible.d.ts.map +1 -1
  104. package/dist/components/collapsible.js +24 -9
  105. package/dist/components/collection-filters.d.ts +3 -1
  106. package/dist/components/collection-filters.d.ts.map +1 -1
  107. package/dist/components/collection-filters.js +7 -9
  108. package/dist/components/collection-page.d.ts +31 -10
  109. package/dist/components/collection-page.d.ts.map +1 -1
  110. package/dist/components/collection-page.js +14 -13
  111. package/dist/components/collection-search.d.ts +32 -10
  112. package/dist/components/collection-search.d.ts.map +1 -1
  113. package/dist/components/collection-search.js +24 -13
  114. package/dist/components/collection-sort.d.ts.map +1 -1
  115. package/dist/components/collection-sort.js +2 -2
  116. package/dist/components/collection-toolbar.d.ts +5 -2
  117. package/dist/components/collection-toolbar.d.ts.map +1 -1
  118. package/dist/components/collection-toolbar.js +7 -4
  119. package/dist/components/combobox.d.ts +97 -22
  120. package/dist/components/combobox.d.ts.map +1 -1
  121. package/dist/components/combobox.js +80 -28
  122. package/dist/components/command.d.ts +34 -21
  123. package/dist/components/command.d.ts.map +1 -1
  124. package/dist/components/command.js +33 -25
  125. package/dist/components/confirm-dialog.d.ts +23 -6
  126. package/dist/components/confirm-dialog.d.ts.map +1 -1
  127. package/dist/components/confirm-dialog.js +32 -13
  128. package/dist/components/context-menu.d.ts +14 -10
  129. package/dist/components/context-menu.d.ts.map +1 -1
  130. package/dist/components/context-menu.js +17 -12
  131. package/dist/components/copy-button.d.ts +5 -2
  132. package/dist/components/copy-button.d.ts.map +1 -1
  133. package/dist/components/copy-button.js +16 -8
  134. package/dist/components/data-table.d.ts +120 -54
  135. package/dist/components/data-table.d.ts.map +1 -1
  136. package/dist/components/data-table.js +163 -92
  137. package/dist/components/desktop-shell.d.ts +4 -2
  138. package/dist/components/desktop-shell.d.ts.map +1 -1
  139. package/dist/components/desktop-shell.js +3 -3
  140. package/dist/components/dialog.d.ts +63 -24
  141. package/dist/components/dialog.d.ts.map +1 -1
  142. package/dist/components/dialog.js +52 -31
  143. package/dist/components/disclosure-row.d.ts +1 -2
  144. package/dist/components/disclosure-row.d.ts.map +1 -1
  145. package/dist/components/disclosure-row.js +1 -2
  146. package/dist/components/disclosure.d.ts +1 -1
  147. package/dist/components/disclosure.js +1 -1
  148. package/dist/components/drawer.d.ts +33 -12
  149. package/dist/components/drawer.d.ts.map +1 -1
  150. package/dist/components/drawer.js +47 -35
  151. package/dist/components/dropdown-menu.d.ts +59 -14
  152. package/dist/components/dropdown-menu.d.ts.map +1 -1
  153. package/dist/components/dropdown-menu.js +58 -26
  154. package/dist/components/empty-state.d.ts +35 -12
  155. package/dist/components/empty-state.d.ts.map +1 -1
  156. package/dist/components/empty-state.js +35 -17
  157. package/dist/components/entry-scene.d.ts +4 -4
  158. package/dist/components/entry-scene.d.ts.map +1 -1
  159. package/dist/components/entry-scene.js +2 -2
  160. package/dist/components/field.d.ts +44 -14
  161. package/dist/components/field.d.ts.map +1 -1
  162. package/dist/components/field.js +49 -20
  163. package/dist/components/file-diff.d.ts.map +1 -1
  164. package/dist/components/file-diff.js +3 -4
  165. package/dist/components/form-dialog.d.ts +7 -7
  166. package/dist/components/form-dialog.d.ts.map +1 -1
  167. package/dist/components/form-dialog.js +13 -11
  168. package/dist/components/form-drawer.d.ts +10 -7
  169. package/dist/components/form-drawer.d.ts.map +1 -1
  170. package/dist/components/form-drawer.js +11 -10
  171. package/dist/components/gauge.d.ts +64 -0
  172. package/dist/components/gauge.d.ts.map +1 -0
  173. package/dist/components/gauge.js +73 -0
  174. package/dist/components/hover-card.d.ts +72 -14
  175. package/dist/components/hover-card.d.ts.map +1 -1
  176. package/dist/components/hover-card.js +51 -17
  177. package/dist/components/identity-mark.d.ts +8 -1
  178. package/dist/components/identity-mark.d.ts.map +1 -1
  179. package/dist/components/identity-mark.js +31 -5
  180. package/dist/components/input-group.d.ts +44 -21
  181. package/dist/components/input-group.d.ts.map +1 -1
  182. package/dist/components/input-group.js +74 -27
  183. package/dist/components/input.d.ts +64 -12
  184. package/dist/components/input.d.ts.map +1 -1
  185. package/dist/components/input.js +51 -15
  186. package/dist/components/item.d.ts +37 -15
  187. package/dist/components/item.d.ts.map +1 -1
  188. package/dist/components/item.js +41 -22
  189. package/dist/components/kbd.d.ts +49 -13
  190. package/dist/components/kbd.d.ts.map +1 -1
  191. package/dist/components/kbd.js +115 -16
  192. package/dist/components/kpi-card.d.ts +12 -4
  193. package/dist/components/kpi-card.d.ts.map +1 -1
  194. package/dist/components/kpi-card.js +13 -4
  195. package/dist/components/label.d.ts +17 -10
  196. package/dist/components/label.d.ts.map +1 -1
  197. package/dist/components/label.js +18 -11
  198. package/dist/components/library-shelf.d.ts +6 -6
  199. package/dist/components/library-shelf.d.ts.map +1 -1
  200. package/dist/components/library-shelf.js +5 -5
  201. package/dist/components/load-more-button.d.ts +35 -0
  202. package/dist/components/load-more-button.d.ts.map +1 -0
  203. package/dist/components/load-more-button.js +26 -0
  204. package/dist/components/menu-choice-row.d.ts +6 -8
  205. package/dist/components/menu-choice-row.d.ts.map +1 -1
  206. package/dist/components/menu-choice-row.js +8 -8
  207. package/dist/components/message-notice.d.ts +8 -13
  208. package/dist/components/message-notice.d.ts.map +1 -1
  209. package/dist/components/message-notice.js +25 -22
  210. package/dist/components/middle-truncate.d.ts +33 -0
  211. package/dist/components/middle-truncate.d.ts.map +1 -0
  212. package/dist/components/middle-truncate.js +72 -0
  213. package/dist/components/multi-select.d.ts +94 -0
  214. package/dist/components/multi-select.d.ts.map +1 -0
  215. package/dist/components/multi-select.js +132 -0
  216. package/dist/components/native-select.d.ts +37 -13
  217. package/dist/components/native-select.d.ts.map +1 -1
  218. package/dist/components/native-select.js +27 -11
  219. package/dist/components/notice-banner.d.ts +17 -10
  220. package/dist/components/notice-banner.d.ts.map +1 -1
  221. package/dist/components/notice-banner.js +12 -11
  222. package/dist/components/numbered-steps.d.ts +10 -4
  223. package/dist/components/numbered-steps.d.ts.map +1 -1
  224. package/dist/components/numbered-steps.js +9 -7
  225. package/dist/components/object-page.d.ts +40 -14
  226. package/dist/components/object-page.d.ts.map +1 -1
  227. package/dist/components/object-page.js +12 -10
  228. package/dist/components/object-workspace.d.ts +29 -10
  229. package/dist/components/object-workspace.d.ts.map +1 -1
  230. package/dist/components/object-workspace.js +18 -8
  231. package/dist/components/overview-page.d.ts +2 -2
  232. package/dist/components/overview-page.d.ts.map +1 -1
  233. package/dist/components/overview-page.js +8 -7
  234. package/dist/components/page-actions.d.ts +28 -8
  235. package/dist/components/page-actions.d.ts.map +1 -1
  236. package/dist/components/page-actions.js +22 -25
  237. package/dist/components/page-header.d.ts +11 -4
  238. package/dist/components/page-header.d.ts.map +1 -1
  239. package/dist/components/page-header.js +10 -5
  240. package/dist/components/pending-label.d.ts +6 -5
  241. package/dist/components/pending-label.d.ts.map +1 -1
  242. package/dist/components/pending-label.js +14 -6
  243. package/dist/components/picture-label.d.ts +2 -2
  244. package/dist/components/picture-label.d.ts.map +1 -1
  245. package/dist/components/picture-label.js +3 -3
  246. package/dist/components/portal-sidebar-frame.d.ts +9 -21
  247. package/dist/components/portal-sidebar-frame.d.ts.map +1 -1
  248. package/dist/components/portal-sidebar-frame.js +13 -27
  249. package/dist/components/progress-checklist.d.ts +46 -0
  250. package/dist/components/progress-checklist.d.ts.map +1 -0
  251. package/dist/components/progress-checklist.js +46 -0
  252. package/dist/components/progress.d.ts +48 -8
  253. package/dist/components/progress.d.ts.map +1 -1
  254. package/dist/components/progress.js +92 -16
  255. package/dist/components/prose.d.ts +19 -0
  256. package/dist/components/prose.d.ts.map +1 -0
  257. package/dist/components/prose.js +29 -0
  258. package/dist/components/radio-group.d.ts +51 -12
  259. package/dist/components/radio-group.d.ts.map +1 -1
  260. package/dist/components/radio-group.js +42 -12
  261. package/dist/components/reading-column.js +1 -1
  262. package/dist/components/record-identity.d.ts +6 -5
  263. package/dist/components/record-identity.d.ts.map +1 -1
  264. package/dist/components/record-identity.js +12 -3
  265. package/dist/components/record-list.d.ts +117 -27
  266. package/dist/components/record-list.d.ts.map +1 -1
  267. package/dist/components/record-list.js +185 -55
  268. package/dist/components/record-time.d.ts +20 -7
  269. package/dist/components/record-time.d.ts.map +1 -1
  270. package/dist/components/record-time.js +38 -19
  271. package/dist/components/resizable.d.ts +7 -5
  272. package/dist/components/resizable.d.ts.map +1 -1
  273. package/dist/components/resizable.js +6 -6
  274. package/dist/components/scope-switcher.d.ts +2 -0
  275. package/dist/components/scope-switcher.d.ts.map +1 -1
  276. package/dist/components/scope-switcher.js +2 -2
  277. package/dist/components/scroll-area.d.ts +47 -11
  278. package/dist/components/scroll-area.d.ts.map +1 -1
  279. package/dist/components/scroll-area.js +72 -14
  280. package/dist/components/segmented-control.d.ts +82 -0
  281. package/dist/components/segmented-control.d.ts.map +1 -0
  282. package/dist/components/segmented-control.js +90 -0
  283. package/dist/components/select.d.ts +55 -15
  284. package/dist/components/select.d.ts.map +1 -1
  285. package/dist/components/select.js +62 -19
  286. package/dist/components/separator.d.ts +12 -5
  287. package/dist/components/separator.d.ts.map +1 -1
  288. package/dist/components/separator.js +11 -6
  289. package/dist/components/settings-facts.d.ts +34 -14
  290. package/dist/components/settings-facts.d.ts.map +1 -1
  291. package/dist/components/settings-facts.js +21 -12
  292. package/dist/components/settings-page.d.ts +4 -4
  293. package/dist/components/settings-page.js +5 -5
  294. package/dist/components/settings-section.d.ts +160 -34
  295. package/dist/components/settings-section.d.ts.map +1 -1
  296. package/dist/components/settings-section.js +92 -39
  297. package/dist/components/sheet.d.ts +47 -16
  298. package/dist/components/sheet.d.ts.map +1 -1
  299. package/dist/components/sheet.js +40 -19
  300. package/dist/components/sidebar-sections.d.ts +16 -12
  301. package/dist/components/sidebar-sections.d.ts.map +1 -1
  302. package/dist/components/sidebar-sections.js +30 -15
  303. package/dist/components/sidebar.d.ts +117 -21
  304. package/dist/components/sidebar.d.ts.map +1 -1
  305. package/dist/components/sidebar.js +180 -55
  306. package/dist/components/skeleton.d.ts +54 -8
  307. package/dist/components/skeleton.d.ts.map +1 -1
  308. package/dist/components/skeleton.js +44 -9
  309. package/dist/components/slider.d.ts +53 -11
  310. package/dist/components/slider.d.ts.map +1 -1
  311. package/dist/components/slider.js +71 -17
  312. package/dist/components/snippet.d.ts +57 -0
  313. package/dist/components/snippet.d.ts.map +1 -0
  314. package/dist/components/snippet.js +60 -0
  315. package/dist/components/sonner.d.ts +13 -14
  316. package/dist/components/sonner.d.ts.map +1 -1
  317. package/dist/components/sonner.js +25 -22
  318. package/dist/components/status-badge.d.ts +37 -11
  319. package/dist/components/status-badge.d.ts.map +1 -1
  320. package/dist/components/status-badge.js +63 -15
  321. package/dist/components/status-dot.d.ts +42 -11
  322. package/dist/components/status-dot.d.ts.map +1 -1
  323. package/dist/components/status-dot.js +40 -11
  324. package/dist/components/switch.d.ts +43 -12
  325. package/dist/components/switch.d.ts.map +1 -1
  326. package/dist/components/switch.js +42 -12
  327. package/dist/components/table-card.d.ts +21 -12
  328. package/dist/components/table-card.d.ts.map +1 -1
  329. package/dist/components/table-card.js +17 -9
  330. package/dist/components/table-layouts.d.ts +40 -8
  331. package/dist/components/table-layouts.d.ts.map +1 -1
  332. package/dist/components/table-layouts.js +52 -8
  333. package/dist/components/table-value.d.ts +4 -3
  334. package/dist/components/table-value.d.ts.map +1 -1
  335. package/dist/components/table-value.js +6 -5
  336. package/dist/components/table.d.ts +62 -25
  337. package/dist/components/table.d.ts.map +1 -1
  338. package/dist/components/table.js +62 -26
  339. package/dist/components/tabs.d.ts +86 -17
  340. package/dist/components/tabs.d.ts.map +1 -1
  341. package/dist/components/tabs.js +93 -22
  342. package/dist/components/text-link.d.ts +4 -1
  343. package/dist/components/text-link.d.ts.map +1 -1
  344. package/dist/components/text-link.js +4 -1
  345. package/dist/components/text-with-copy-button.d.ts +30 -0
  346. package/dist/components/text-with-copy-button.d.ts.map +1 -0
  347. package/dist/components/text-with-copy-button.js +32 -0
  348. package/dist/components/textarea.d.ts +40 -10
  349. package/dist/components/textarea.d.ts.map +1 -1
  350. package/dist/components/textarea.js +50 -11
  351. package/dist/components/theme-mode-switcher.d.ts +20 -3
  352. package/dist/components/theme-mode-switcher.d.ts.map +1 -1
  353. package/dist/components/theme-mode-switcher.js +23 -6
  354. package/dist/components/toggle-group.d.ts +28 -8
  355. package/dist/components/toggle-group.d.ts.map +1 -1
  356. package/dist/components/toggle-group.js +12 -6
  357. package/dist/components/toggle.d.ts +6 -3
  358. package/dist/components/toggle.d.ts.map +1 -1
  359. package/dist/components/toggle.js +10 -7
  360. package/dist/components/tooltip.d.ts +43 -12
  361. package/dist/components/tooltip.d.ts.map +1 -1
  362. package/dist/components/tooltip.js +45 -16
  363. package/dist/components/trend-badge.js +1 -1
  364. package/dist/components/update-card.js +1 -1
  365. package/dist/eslint/index.js +82 -0
  366. package/dist/eslint/rules/badge-is-static-label.js +93 -0
  367. package/dist/eslint/rules/button-icon-slot.js +102 -0
  368. package/dist/eslint/rules/button-link-for-navigation.js +146 -0
  369. package/dist/eslint/rules/button-no-aria-label-with-text.js +75 -0
  370. package/dist/eslint/rules/button-type-roles-only-in-buttons.js +83 -0
  371. package/dist/eslint/rules/choice-control-has-a-name.js +98 -0
  372. package/dist/eslint/rules/choice-group-in-fieldset.js +86 -0
  373. package/dist/eslint/rules/command-shortcut-single-owner.js +118 -0
  374. package/dist/eslint/rules/confirm-dialog-words.js +63 -0
  375. package/dist/eslint/rules/data-table-one-unsized-column.js +96 -0
  376. package/dist/eslint/rules/destructive-action-placement.js +69 -0
  377. package/dist/eslint/rules/disabled-choice-says-why.js +71 -0
  378. package/dist/eslint/rules/ellipsis-opens-a-dialog.js +110 -0
  379. package/dist/eslint/rules/input-group-owners.js +155 -0
  380. package/dist/eslint/rules/kbd-children-no-modifier-text.js +128 -0
  381. package/dist/eslint/rules/literal-text.js +41 -0
  382. package/dist/eslint/rules/middle-truncate-placement.js +69 -0
  383. package/dist/eslint/rules/no-bare-button-label.js +55 -0
  384. package/dist/eslint/rules/no-control-in-read-only-surface.js +106 -0
  385. package/dist/eslint/rules/no-hand-built-busy.js +104 -0
  386. package/dist/eslint/rules/no-hand-built-theme-picker.js +144 -0
  387. package/dist/eslint/rules/no-hand-built-tile.js +41 -0
  388. package/dist/eslint/rules/no-hand-formatted-date.js +87 -0
  389. package/dist/eslint/rules/no-hand-stacked-avatars.js +67 -0
  390. package/dist/eslint/rules/no-nested-collapse.js +60 -0
  391. package/dist/eslint/rules/no-search-in-form.js +47 -0
  392. package/dist/eslint/rules/no-shadcn-appearance-override.js +2 -16
  393. package/dist/eslint/rules/no-shell-prompt-in-snippet.js +74 -0
  394. package/dist/eslint/rules/no-stock-error-words.js +56 -0
  395. package/dist/eslint/rules/no-tooltip-around-labelled-field.js +72 -0
  396. package/dist/eslint/rules/no-weight-class-on-strong-in-type-role.js +57 -0
  397. package/dist/eslint/rules/one-submenu-level.js +43 -0
  398. package/dist/eslint/rules/one-toast-per-flow.js +91 -0
  399. package/dist/eslint/rules/option-count.js +167 -0
  400. package/dist/eslint/rules/page-command-link-not-render.js +43 -0
  401. package/dist/eslint/rules/progress-needs-name.js +57 -0
  402. package/dist/eslint/rules/settings-limit-in-footer.js +94 -0
  403. package/dist/eslint/rules/skeleton-in-region.js +80 -0
  404. package/dist/eslint/rules/sortable-heading-uses-sort.js +79 -0
  405. package/dist/eslint/rules/table-cell-inset-owner.js +107 -0
  406. package/dist/eslint/rules/tabs-selection-is-controlled.js +47 -0
  407. package/dist/eslint/rules/tooltip-only-arrow.js +38 -0
  408. package/dist/eslint/rules/tooltip-trigger-accessible-name.js +114 -0
  409. package/dist/eslint/strict-design-system.js +21 -7
  410. package/dist/index.d.ts +13 -13
  411. package/dist/index.d.ts.map +1 -1
  412. package/dist/index.js +12 -13
  413. package/dist/internal/assistant-ui/attachment.d.ts.map +1 -1
  414. package/dist/internal/assistant-ui/attachment.js +2 -2
  415. package/dist/internal/assistant-ui/reasoning.d.ts +1 -1
  416. package/dist/internal/assistant-ui/reasoning.d.ts.map +1 -1
  417. package/dist/internal/assistant-ui/tool-fallback.js +1 -1
  418. package/dist/internal/assistant-ui/tool-group.d.ts +1 -1
  419. package/dist/internal/assistant-ui/tool-group.d.ts.map +1 -1
  420. package/dist/internal/assistant-ui/tooltip-icon-button.d.ts +2 -3
  421. package/dist/internal/assistant-ui/tooltip-icon-button.d.ts.map +1 -1
  422. package/dist/internal/assistant-ui/tooltip-icon-button.js +1 -1
  423. package/dist/internal/catalog-list.js +2 -2
  424. package/dist/internal/collection-note.d.ts +23 -0
  425. package/dist/internal/collection-note.d.ts.map +1 -0
  426. package/dist/internal/collection-note.js +17 -0
  427. package/dist/internal/collection-rail.d.ts +57 -0
  428. package/dist/internal/collection-rail.d.ts.map +1 -0
  429. package/dist/internal/collection-rail.js +33 -0
  430. package/dist/internal/company-logo.d.ts +18 -0
  431. package/dist/internal/company-logo.d.ts.map +1 -0
  432. package/dist/internal/company-logo.js +14 -0
  433. package/dist/internal/control-name.d.ts +12 -0
  434. package/dist/internal/control-name.d.ts.map +1 -0
  435. package/dist/internal/control-name.js +1 -0
  436. package/dist/internal/diff-line.d.ts +9 -0
  437. package/dist/internal/diff-line.d.ts.map +1 -0
  438. package/dist/internal/diff-line.js +8 -0
  439. package/dist/internal/empty.d.ts +8 -4
  440. package/dist/internal/empty.d.ts.map +1 -1
  441. package/dist/internal/empty.js +14 -8
  442. package/dist/internal/field-frame.d.ts +33 -0
  443. package/dist/internal/field-frame.d.ts.map +1 -0
  444. package/dist/internal/field-frame.js +20 -0
  445. package/dist/internal/final-focus.d.ts +8 -0
  446. package/dist/internal/final-focus.d.ts.map +1 -0
  447. package/dist/internal/final-focus.js +10 -0
  448. package/dist/internal/footer-strip.d.ts.map +1 -1
  449. package/dist/internal/footer-strip.js +3 -1
  450. package/dist/internal/form-submit.d.ts +9 -5
  451. package/dist/internal/form-submit.d.ts.map +1 -1
  452. package/dist/internal/form-submit.js +31 -2
  453. package/dist/internal/input-box.d.ts +26 -0
  454. package/dist/internal/input-box.d.ts.map +1 -0
  455. package/dist/internal/input-box.js +59 -0
  456. package/dist/internal/menu-surface.d.ts +10 -3
  457. package/dist/internal/menu-surface.d.ts.map +1 -1
  458. package/dist/internal/menu-surface.js +10 -3
  459. package/dist/internal/notice.d.ts +25 -0
  460. package/dist/internal/notice.d.ts.map +1 -0
  461. package/dist/internal/notice.js +33 -0
  462. package/dist/internal/overlay-stem.d.ts +5 -0
  463. package/dist/internal/overlay-stem.d.ts.map +1 -0
  464. package/dist/internal/overlay-stem.js +7 -0
  465. package/dist/internal/phone-sheet.d.ts +4 -0
  466. package/dist/internal/phone-sheet.d.ts.map +1 -0
  467. package/dist/internal/phone-sheet.js +2 -0
  468. package/dist/internal/present-content.d.ts +3 -0
  469. package/dist/internal/present-content.d.ts.map +1 -0
  470. package/dist/internal/present-content.js +1 -0
  471. package/dist/internal/record-menu-items.d.ts +4 -3
  472. package/dist/internal/record-menu-items.d.ts.map +1 -1
  473. package/dist/internal/record-menu-items.js +21 -6
  474. package/dist/internal/settings-save.d.ts +9 -2
  475. package/dist/internal/settings-save.d.ts.map +1 -1
  476. package/dist/internal/settings-save.js +9 -6
  477. package/dist/internal/table-compact-line.d.ts +38 -0
  478. package/dist/internal/table-compact-line.d.ts.map +1 -0
  479. package/dist/internal/table-compact-line.js +35 -0
  480. package/dist/internal/title-when-cut.d.ts +8 -0
  481. package/dist/internal/title-when-cut.d.ts.map +1 -0
  482. package/dist/internal/title-when-cut.js +22 -0
  483. package/dist/internal/unsaved-guard.d.ts +18 -0
  484. package/dist/internal/unsaved-guard.d.ts.map +1 -0
  485. package/dist/internal/unsaved-guard.js +31 -0
  486. package/dist/lib/merge-tokens.generated.js +2 -2
  487. package/dist/showcase/foundations.d.ts.map +1 -1
  488. package/dist/showcase/foundations.js +10 -2
  489. package/dist/showcase/specimens/actions.d.ts +2 -0
  490. package/dist/showcase/specimens/actions.d.ts.map +1 -1
  491. package/dist/showcase/specimens/actions.js +30 -5
  492. package/dist/showcase/specimens/charts.d.ts.map +1 -1
  493. package/dist/showcase/specimens/charts.js +22 -13
  494. package/dist/showcase/specimens/choices.d.ts +14 -3
  495. package/dist/showcase/specimens/choices.d.ts.map +1 -1
  496. package/dist/showcase/specimens/choices.js +93 -13
  497. package/dist/showcase/specimens/compositions.d.ts.map +1 -1
  498. package/dist/showcase/specimens/compositions.js +136 -23
  499. package/dist/showcase/specimens/data.d.ts +12 -7
  500. package/dist/showcase/specimens/data.d.ts.map +1 -1
  501. package/dist/showcase/specimens/data.js +139 -35
  502. package/dist/showcase/specimens/feedback.d.ts +2 -0
  503. package/dist/showcase/specimens/feedback.d.ts.map +1 -1
  504. package/dist/showcase/specimens/feedback.js +39 -15
  505. package/dist/showcase/specimens/index.d.ts +20 -12
  506. package/dist/showcase/specimens/index.d.ts.map +1 -1
  507. package/dist/showcase/specimens/inputs.d.ts +4 -2
  508. package/dist/showcase/specimens/inputs.d.ts.map +1 -1
  509. package/dist/showcase/specimens/inputs.js +62 -19
  510. package/dist/showcase/specimens/menus.d.ts +0 -1
  511. package/dist/showcase/specimens/menus.d.ts.map +1 -1
  512. package/dist/showcase/specimens/menus.js +47 -18
  513. package/dist/showcase/specimens/navigation.d.ts +6 -4
  514. package/dist/showcase/specimens/navigation.d.ts.map +1 -1
  515. package/dist/showcase/specimens/navigation.js +61 -32
  516. package/dist/showcase/specimens/other.d.ts +1 -3
  517. package/dist/showcase/specimens/other.d.ts.map +1 -1
  518. package/dist/showcase/specimens/other.js +17 -13
  519. package/dist/showcase/specimens/status.d.ts +4 -1
  520. package/dist/showcase/specimens/status.d.ts.map +1 -1
  521. package/dist/showcase/specimens/status.js +64 -11
  522. package/dist/showcase/usage.generated.d.ts +1 -1
  523. package/dist/showcase/usage.generated.d.ts.map +1 -1
  524. package/dist/showcase/usage.generated.js +637 -549
  525. package/dist/standalone/index.js +1 -1
  526. package/dist/styles/components.css +13 -13
  527. package/dist/styles/globals.css +25 -0
  528. package/package.json +2 -3
  529. package/dist/components/approval-row.d.ts +0 -34
  530. package/dist/components/approval-row.d.ts.map +0 -1
  531. package/dist/components/approval-row.js +0 -27
  532. package/dist/components/channel-view.d.ts +0 -112
  533. package/dist/components/channel-view.d.ts.map +0 -1
  534. package/dist/components/channel-view.js +0 -84
  535. package/dist/components/dashboard-shell.d.ts +0 -51
  536. package/dist/components/dashboard-shell.d.ts.map +0 -1
  537. package/dist/components/dashboard-shell.js +0 -89
  538. package/dist/components/direction.d.ts +0 -2
  539. package/dist/components/direction.d.ts.map +0 -1
  540. package/dist/components/direction.js +0 -1
  541. package/dist/components/expandable-block.d.ts +0 -20
  542. package/dist/components/expandable-block.d.ts.map +0 -1
  543. package/dist/components/expandable-block.js +0 -32
  544. package/dist/components/form.d.ts +0 -42
  545. package/dist/components/form.d.ts.map +0 -1
  546. package/dist/components/form.js +0 -91
  547. package/dist/components/label-system.d.ts +0 -17
  548. package/dist/components/label-system.d.ts.map +0 -1
  549. package/dist/components/label-system.js +0 -21
  550. package/dist/components/memory-item.d.ts +0 -21
  551. package/dist/components/memory-item.d.ts.map +0 -1
  552. package/dist/components/memory-item.js +0 -18
  553. package/dist/components/menubar.d.ts +0 -43
  554. package/dist/components/menubar.d.ts.map +0 -1
  555. package/dist/components/menubar.js +0 -69
  556. package/dist/components/navigation-menu.d.ts +0 -26
  557. package/dist/components/navigation-menu.d.ts.map +0 -1
  558. package/dist/components/navigation-menu.js +0 -45
  559. package/dist/components/pagination.d.ts +0 -33
  560. package/dist/components/pagination.d.ts.map +0 -1
  561. package/dist/components/pagination.js +0 -41
  562. package/dist/components/plugin-slot-boundary.d.ts +0 -42
  563. package/dist/components/plugin-slot-boundary.d.ts.map +0 -1
  564. package/dist/components/plugin-slot-boundary.js +0 -41
  565. package/dist/components/status-row.d.ts +0 -32
  566. package/dist/components/status-row.d.ts.map +0 -1
  567. package/dist/components/status-row.js +0 -35
@@ -1,35 +1,38 @@
1
1
  "use client";
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { OctagonAlert } from "lucide-react";
4
+ import { Alert } from "./alert.js";
5
+ import { NOTICE_EDGE, NoticeLink, NoticeMark, } from "../internal/notice.js";
3
6
  import { cn } from "../lib/utils.js";
4
- import { NoticeBanner } from "./notice-banner.js";
5
7
  import { PageCommandButton } from "./page-actions.js";
6
8
  import { bodyBeyondTitle } from "./product-message.js";
7
- function MessageActions({ message }) {
8
- if (message.actions.length === 0) {
9
- return null;
10
- }
11
- return (_jsx("div", { className: "flex flex-wrap items-center gap-2", "data-message-actions": true, children: message.actions.map((action) => (_jsx(PageCommandButton, { command: action, variant: "outline" }, action.label))) }));
12
- }
9
+ const TONE_TEXT = {
10
+ neutral: "text-muted-foreground",
11
+ info: "text-info-soft-foreground",
12
+ warning: "text-warning-soft-foreground",
13
+ destructive: "text-destructive-soft-foreground",
14
+ };
15
+ const LEAD = { action: "font-medium", section: "font-semibold" };
16
+ const closedSentence = (title) => (/[.!?:…]$/u.test(title) ? title : `${title}.`);
13
17
  /**
14
18
  * A catalogue message about one thing on the screen.
15
19
  *
16
20
  * @family Feedback
17
- * @when A catalogue message about one thing on the screen: under the control whose write failed
18
- * (`action`), or atop the section whose condition lasts (`section`).
19
- * @instead For a whole page or the account use `NoticeBanner`. For a finished action use a toast.
20
- * For a field's wrong value use `FieldError`. For a failed read use `EmptyState failed`.
21
- * @behaviour Tone `destructive` by default, `warning` or `info`. Its actions are outline buttons.
22
- * Long words wrap, nothing truncates. A successful retry removes it. A `section` message with no
23
- * action is drawn as the passage under a control, because a banner always carries its fix.
24
- * @words From the catalogue only. Title says what happened to which object; body only when it adds
25
- * something. Action labelled with its result: *Try again*, *Sign in*.
26
- * @access `action`: `role="alert"` (`status` for info). `section`: `role="status"`,
27
- * `aria-live="polite"`, atomic. Focus stays where the person was.
21
+ * @when "Display text that requires attention or provides additional information." (https://vercel.com/geist/note) "Use a Note for inline contextual feedback next to the field, card, or section it describes: a region change warning above a region picker, a rate-limit notice next to a usage gauge." (https://vercel.com/geist/note) "Choose `variant` by meaning: `error` for a problem the user must fix, `warning` for a consequence to acknowledge, `success` for a passed check, `secondary` for neutral information." (https://vercel.com/geist/note) Ours: a message beside the field, card or section it describes, under the control whose write failed (`action`), or atop the section whose condition lasts (`section`). The `tone` is the `variant`: `destructive` is `error`, `warning` is `warning`, `info` is `success` (a passed check or a state the product manages for the reader, drawn in the accent blue) and `neutral` is `secondary`; there is no violet or cyan.
22
+ * @instead "Pick `Banner` when the message is page-level or system-wide and needs a CTA, `Toast` for transient acknowledgments, `Modal` for destructive confirmations." (https://vercel.com/geist/note) Ours: `Banner` is `NoticeBanner`, `Toast` is `showMessageToast` and `Modal` is `ConfirmDialog`. For a field's wrong value use `FieldError`. For a failed read use `EmptyState failed`.
23
+ * @behaviour "A Note is persistent until the underlying state changes; don’t add an ad hoc dismiss control because it competes with the message." (https://vercel.com/geist/note) "One Note per concept. Stacking three Notes on a card means the page architecture, not the Note copy, is wrong." (https://vercel.com/geist/note) "The optional `NoteAction` holds a single inline CTA. Don’t pair it with a second button." (https://vercel.com/geist/note) Ours: tone `destructive` by default, `warning`, `info` or `neutral`. Colour sits on the line, the text and the mark; the ground stays clear, and the action stays neutral. `section` is the framed note, a line 36 px high with its mark and sentence on one row and the action at its trailing edge (under 768 px the action takes its own row, full width), with or without an action; `action` is the unframed passage, its mark and sentence on one row with the action link inside the sentence, after a space. The title and the body run on as one passage, the title closed by a full stop when it has none of its own, so the two never read as one sentence; the title leads in bold (semibold in a note, medium in a passage) only when a body follows it, and stands at regular weight alone. A message worded for a notice has one action, a small default button in a section and an underlined 14 px link in the passage, and at most a second one after it (Nadia's *Ask Nadia* after *Try again*), an outline button in a section and a second link in the passage; a third is not shown. Long words wrap, nothing truncates. A successful retry removes it. Required: the catalogue `message`, so the sentence and its mark are always drawn.
24
+ * @words "`NoteLabel` is a 1–2 word Title Case prefix that names the topic: `Region Change`, `Rate Limit`, `Plan Limit`. Cut hedges like `Heads Up`, `FYI`, and `Note`." (https://vercel.com/geist/note) "`NoteContent` is one sentence in active voice that names the impact: `Changing this region restarts all functions.`" (https://vercel.com/geist/note) "No `variant="info"` exists; omit `variant` for the default info icon or use `variant="secondary"` for neutral copy." (https://vercel.com/geist/note) "Single-fragment labels take no period; full descriptive sentences in the body do." (https://vercel.com/geist/note) Ours: words come from the catalogue only. The message's title stands in for `NoteLabel` and `NoteContent`, in sentence case, and `info` exists as Geist's blue `success`. The action is labelled with its result: *Try again*, *Sign in*. When the message carries an action, its body does not repeat it: the link is the step, so the body ends before "Try again".
25
+ * @access `action`: `role="alert"` (`status` for info and neutral). `section`: `role="status"`, `aria-live="polite"`, atomic. The mark is hidden. Focus stays where the person was.
28
26
  */
29
27
  export function MessageNotice({ message, placement, tone = "destructive", className, }) {
30
28
  const body = bodyBeyondTitle(message);
31
- if (placement === "section" && message.actions.length > 0) {
32
- return (_jsx(NoticeBanner, { action: _jsx(MessageActions, { message: message }), className: className, "data-message-code": message.code, description: body ?? undefined, liveRegion: "polite", title: _jsx("span", { className: "min-w-0 break-words", children: message.title }), tone: tone }));
29
+ const [action, secondAction] = message.actions;
30
+ const mark = _jsx(NoticeMark, { icon: tone === "destructive" ? OctagonAlert : undefined, tone: tone });
31
+ const sentence = (_jsxs("p", { className: "m-0 min-w-0 break-words", children: [_jsx("span", { className: body ? LEAD[placement] : undefined, children: body ? closedSentence(message.title) : message.title }), body ? ` ${body}` : null, placement === "action"
32
+ ? [action, secondAction].map((command) => command ? (_jsxs("span", { children: [" ", _jsx(NoticeLink, { command: command, place: "inline" })] }, command.label)) : null)
33
+ : null] }));
34
+ if (placement === "section") {
35
+ return (_jsxs(Alert, { "aria-atomic": "true", "aria-live": "polite", className: cn("flex min-h-9 flex-wrap items-center gap-3 bg-transparent px-3 py-1.5 text-body-sm", action && "rounded-md max-md:py-3 md:pr-2", NOTICE_EDGE[tone], TONE_TEXT[tone], className), "data-message-code": message.code, "data-message-notice": "section", "data-tone": tone, role: "status", variant: "default", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-start gap-3", children: [_jsx("span", { className: "flex h-5 shrink-0 items-center [&_svg]:size-3.5", children: mark }), sentence] }), action ? (_jsxs("div", { className: "flex shrink-0 gap-2 self-center max-md:basis-full max-md:*:flex-1", "data-notice-action": true, children: [_jsx(PageCommandButton, { command: action, size: "sm", variant: "default" }), secondAction ? (_jsx(PageCommandButton, { command: secondAction, size: "sm", variant: "outline" })) : null] })) : null] }));
33
36
  }
34
- return (_jsxs("div", { className: cn("flex min-w-0 flex-col gap-2 text-sm", className), "data-message-code": message.code, "data-message-notice": "action", role: tone === "info" ? "status" : "alert", children: [_jsxs("p", { className: cn("m-0 min-w-0 break-words", tone === "destructive" && "text-destructive", tone === "warning" && "text-warning-soft-foreground", tone === "info" && "text-muted-foreground"), children: [_jsx("span", { className: "block font-medium", children: message.title }), body ? (_jsxs(_Fragment, { children: [" ", _jsx("span", { className: "block", children: body })] })) : null] }), _jsx(MessageActions, { message: message })] }));
37
+ return (_jsxs("div", { className: cn("flex min-w-0 items-start gap-2 text-body-sm", TONE_TEXT[tone], className), "data-message-code": message.code, "data-message-notice": "action", role: tone === "info" || tone === "neutral" ? "status" : "alert", children: [_jsx("span", { className: "flex h-5 shrink-0 items-center [&_svg]:size-4", children: mark }), sentence] }));
35
38
  }
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ /**
3
+ * A technical string cut in the middle, so its start and its end both stay readable.
4
+ *
5
+ * @family Data
6
+ * @when “Use Middle Truncate for strings whose head and tail both carry information: file paths (`apps/…/page.tsx`), URLs, deployment IDs (`dpl_…abc123`), commit hashes, branch names with prefixes.” (https://vercel.com/geist/middle-truncate)
7
+ * Ours: an environment variable is one of these too.
8
+ * @instead “For prose, descriptions, and headings, use end-truncation with `…` instead; cutting the middle of a sentence destroys meaning.” (https://vercel.com/geist/middle-truncate)
9
+ * “For any truncated value the user might need verbatim, pair with a `Tooltip` showing the full string or a copy-on-click affordance.” (https://vercel.com/geist/middle-truncate)
10
+ * Ours: a value someone may need exactly is `TextWithCopyButton` or sits beside a `CopyButton`; while
11
+ * the middle is cut, the whole value is also the element's `title`.
12
+ * @behaviour “Middle Truncate renders a single ellipsis glyph (`…`) rather than three periods. This keeps monospace values, including environment variable keys, IDs, hashes, and paths, from reserving three character cells for the truncation marker.” (https://vercel.com/geist/middle-truncate)
13
+ * “The component listens to its container width, so layouts that change width on hover (expanding cards, animated rows) cause the truncation point to jitter. Lock the width during interaction.” (https://vercel.com/geist/middle-truncate)
14
+ * “Copying truncated text yields the full original string from the underlying value, not the visible ellipsis form. Confirm this stays true if wrapping with custom `onCopy`.” (https://vercel.com/geist/middle-truncate)
15
+ * “Don’t wrap Middle Truncate in another `text-overflow: ellipsis` container; the two strategies fight each other and the inner ellipsis wins inconsistently.” (https://vercel.com/geist/middle-truncate)
16
+ * “Keep the visible string long enough on small viewports that the head still identifies the resource (path segment, ID prefix).” (https://vercel.com/geist/middle-truncate)
17
+ * Ours: it fills the width its parent gives and shows the whole value when it fits. Too narrow, the
18
+ * last third of the value, at most 16 characters, stays whole. While the middle is cut, copying a
19
+ * selection made inside it puts the whole value on the clipboard; a value shown in full copies only
20
+ * what is selected. A custom `onCopy` runs first, and `preventDefault` in it keeps the value as
21
+ * selected.
22
+ * @words Ours: the value as the system writes it, unchanged; the ellipsis comes from the layout, never from
23
+ * the text.
24
+ * @access “Expose the full string to assistive tech via the wrapping element’s accessible name (the component already keeps the full value in the DOM for copy).” (https://vercel.com/geist/middle-truncate)
25
+ * “Avoid Middle Truncate inside focusable controls without an explicit `aria-label`; the ellipsis on its own gives screen readers nothing to announce.” (https://vercel.com/geist/middle-truncate)
26
+ * Ours: the whole value is the element's text, read in full; inside a link or a button, the control
27
+ * carries the `aria-label` that names the target.
28
+ */
29
+ declare function MiddleTruncate({ className, onCopy, value, ...props }: Omit<React.ComponentProps<"span">, "children"> & {
30
+ value: string;
31
+ }): React.JSX.Element;
32
+ export { MiddleTruncate };
33
+ //# sourceMappingURL=middle-truncate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middle-truncate.d.ts","sourceRoot":"","sources":["../../src/components/middle-truncate.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAmC/B;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,iBAAS,cAAc,CAAC,EACtB,SAAS,EACT,MAAM,EACN,KAAK,EACL,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,qBA0BpE;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,72 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/utils.js";
5
+ const TAIL_SHARE = 3;
6
+ const TAIL_MAX = 16;
7
+ function copiesWholeValue(root) {
8
+ const head = root.firstElementChild;
9
+ const selection = window.getSelection();
10
+ return (head !== null &&
11
+ head.scrollWidth > head.clientWidth &&
12
+ selection !== null &&
13
+ !selection.isCollapsed &&
14
+ root.contains(selection.anchorNode) &&
15
+ root.contains(selection.focusNode));
16
+ }
17
+ function useIsCut(root, value) {
18
+ const [cut, setCut] = React.useState(false);
19
+ React.useEffect(() => {
20
+ const element = root.current;
21
+ const head = element?.firstElementChild;
22
+ if (!element || !head || typeof ResizeObserver === "undefined")
23
+ return;
24
+ const measure = () => setCut(head.scrollWidth > head.clientWidth);
25
+ measure();
26
+ const observer = new ResizeObserver(measure);
27
+ observer.observe(element);
28
+ return () => observer.disconnect();
29
+ }, [root, value]);
30
+ return cut;
31
+ }
32
+ /**
33
+ * A technical string cut in the middle, so its start and its end both stay readable.
34
+ *
35
+ * @family Data
36
+ * @when “Use Middle Truncate for strings whose head and tail both carry information: file paths (`apps/…/page.tsx`), URLs, deployment IDs (`dpl_…abc123`), commit hashes, branch names with prefixes.” (https://vercel.com/geist/middle-truncate)
37
+ * Ours: an environment variable is one of these too.
38
+ * @instead “For prose, descriptions, and headings, use end-truncation with `…` instead; cutting the middle of a sentence destroys meaning.” (https://vercel.com/geist/middle-truncate)
39
+ * “For any truncated value the user might need verbatim, pair with a `Tooltip` showing the full string or a copy-on-click affordance.” (https://vercel.com/geist/middle-truncate)
40
+ * Ours: a value someone may need exactly is `TextWithCopyButton` or sits beside a `CopyButton`; while
41
+ * the middle is cut, the whole value is also the element's `title`.
42
+ * @behaviour “Middle Truncate renders a single ellipsis glyph (`…`) rather than three periods. This keeps monospace values, including environment variable keys, IDs, hashes, and paths, from reserving three character cells for the truncation marker.” (https://vercel.com/geist/middle-truncate)
43
+ * “The component listens to its container width, so layouts that change width on hover (expanding cards, animated rows) cause the truncation point to jitter. Lock the width during interaction.” (https://vercel.com/geist/middle-truncate)
44
+ * “Copying truncated text yields the full original string from the underlying value, not the visible ellipsis form. Confirm this stays true if wrapping with custom `onCopy`.” (https://vercel.com/geist/middle-truncate)
45
+ * “Don’t wrap Middle Truncate in another `text-overflow: ellipsis` container; the two strategies fight each other and the inner ellipsis wins inconsistently.” (https://vercel.com/geist/middle-truncate)
46
+ * “Keep the visible string long enough on small viewports that the head still identifies the resource (path segment, ID prefix).” (https://vercel.com/geist/middle-truncate)
47
+ * Ours: it fills the width its parent gives and shows the whole value when it fits. Too narrow, the
48
+ * last third of the value, at most 16 characters, stays whole. While the middle is cut, copying a
49
+ * selection made inside it puts the whole value on the clipboard; a value shown in full copies only
50
+ * what is selected. A custom `onCopy` runs first, and `preventDefault` in it keeps the value as
51
+ * selected.
52
+ * @words Ours: the value as the system writes it, unchanged; the ellipsis comes from the layout, never from
53
+ * the text.
54
+ * @access “Expose the full string to assistive tech via the wrapping element’s accessible name (the component already keeps the full value in the DOM for copy).” (https://vercel.com/geist/middle-truncate)
55
+ * “Avoid Middle Truncate inside focusable controls without an explicit `aria-label`; the ellipsis on its own gives screen readers nothing to announce.” (https://vercel.com/geist/middle-truncate)
56
+ * Ours: the whole value is the element's text, read in full; inside a link or a button, the control
57
+ * carries the `aria-label` that names the target.
58
+ */
59
+ function MiddleTruncate({ className, onCopy, value, ...props }) {
60
+ const root = React.useRef(null);
61
+ const cut = useIsCut(root, value);
62
+ const characters = Array.from(value);
63
+ const tailStart = characters.length - Math.min(Math.ceil(characters.length / TAIL_SHARE), TAIL_MAX);
64
+ return (_jsxs("span", { ref: root, "data-slot": "middle-truncate", title: cut ? value : undefined, className: cn("inline-flex max-w-full min-w-0 overflow-hidden", className), onCopy: (event) => {
65
+ onCopy?.(event);
66
+ if (event.defaultPrevented || !copiesWholeValue(event.currentTarget))
67
+ return;
68
+ event.clipboardData.setData("text/plain", value);
69
+ event.preventDefault();
70
+ }, ...props, children: [_jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-pre", children: characters.slice(0, tailStart).join("") }), _jsx("span", { className: "shrink-0 whitespace-pre", children: characters.slice(tailStart).join("") })] }));
71
+ }
72
+ export { MiddleTruncate };
@@ -0,0 +1,94 @@
1
+ import * as React from "react";
2
+ import { type FieldName } from "../internal/field-frame.js";
3
+ import { type FieldSize } from "../internal/input-box.js";
4
+ import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
5
+ type MultiSelectContextValue = {
6
+ selectedCount: number;
7
+ totalCount: number;
8
+ onSelectAll: () => void;
9
+ selectOnlyLabel: string;
10
+ selectAllLabel: string;
11
+ };
12
+ type MultiSelectProps = React.ComponentProps<typeof Popover> & MultiSelectContextValue;
13
+ /**
14
+ * A menu of checkbox rows that chooses several values from a known list, with the count in its
15
+ * trigger.
16
+ *
17
+ * @family Choices
18
+ * @when "A keyboard-navigable dropdown for selecting multiple items with advanced focus management."
19
+ * (https://vercel.com/geist/multi-select) "Pick `<MultiSelect>` when users pick more than one value
20
+ * from a known list (regions, scopes, tags)." (https://vercel.com/geist/multi-select) Ours: also the facets of a collection.
21
+ * @instead "For a single value from a short list, use `Select`."
22
+ * (https://vercel.com/geist/multi-select) "When typing to filter is more important than seeing
23
+ * every option at once, use `Combobox`." (https://vercel.com/geist/multi-select) "Skip
24
+ * `<MultiSelect>` for boolean settings; `Toggle` handles those."
25
+ * (https://vercel.com/geist/multi-select) Ours: `Combobox` takes several values with `multiple`; a
26
+ * yes-or-no setting is a `Switch`, never a one-row multi-select.
27
+ * @behaviour "Use controlled mode when state lives in the URL or syncs to the server so the trigger
28
+ * label and stored value stay in lockstep." (https://vercel.com/geist/multi-select) "Use controlled
29
+ * state to manage selections programmatically." (https://vercel.com/geist/multi-select) Ours: the
30
+ * caller always owns the selection and passes it back through each row, so the trigger and the
31
+ * stored value (the URL, the server) never disagree; the root takes the same `selectedCount` and
32
+ * `totalCount` the trigger's summary is built from. "Show the selected count in the trigger (`3
33
+ * regions selected`); show the single name when only one is picked."
34
+ * (https://vercel.com/geist/multi-select) The trigger is `Input`'s box at its default size, in
35
+ * regular weight, with the chevron at the end (it turns up while the list is open); it keeps the
36
+ * hover edge while the list is open and takes the page's outline for keyboard focus, as a button
37
+ * does. Everything is neutral: a checked box is ink, never blue, and an error turns only the
38
+ * trigger's ring red. `label` and `error` go on `MultiSelectTrigger`, and a string `label` requires
39
+ * `id`; a trigger with no name does not compile. The list opens 8 px under the trigger at least as
40
+ * wide as it, in an 8 px inset, with rows 28 px tall and no gap, and stops 16 px short of the
41
+ * screen's edge (at most 24rem tall, then it scrolls). A row is the checkbox in a 28 px square that
42
+ * fills on hover, then a button holding the row's name; pressing the name runs the button, pressing
43
+ * the box toggles the row. "**Checkbox focus + Enter/Space**: Toggle individual item"
44
+ * (https://vercel.com/geist/multi-select) "**Button focus + Enter/Space**: Smart selection (Select
45
+ * Only, Select All, or Toggle based on context)" (https://vercel.com/geist/multi-select) "**Hidden
46
+ * action labels**: Appear on hover/focus to show available actions"
47
+ * (https://vercel.com/geist/multi-select) Ours: the button's label reads *Select all* on a chosen
48
+ * row while some rows are unchosen, and *Select only* on every other row; it shows on hover and
49
+ * focus of the row, and always on touch. "Keep checkbox focus and button focus distinct so Up/Down
50
+ * navigates rows and Left/Right toggles between the row’s checkbox and action button."
51
+ * (https://vercel.com/geist/multi-select) "**Up/Down arrows**: Navigate between rows while
52
+ * maintaining checkbox/button focus state" (https://vercel.com/geist/multi-select) "**Left/Right
53
+ * arrows**: Switch between checkbox and button focus within the current row"
54
+ * (https://vercel.com/geist/multi-select) "**Tab**: Focus away from the menu (natural tab
55
+ * behavior)" (https://vercel.com/geist/multi-select) "**Enter/Space**: Execute actions based on
56
+ * current focus (checkbox toggle or button click)" (https://vercel.com/geist/multi-select) "Trap
57
+ * focus inside the menu while it’s open and return focus to the trigger on close."
58
+ * (https://vercel.com/geist/multi-select) Ours: focus moves into the list when it opens; Tab leaves
59
+ * it in order. "For empty filters, render `No {items} match "{query}"` rather than `No results`."
60
+ * (https://vercel.com/geist/multi-select) Ours: there is no filter box, so a list that needs one is
61
+ * a `Combobox` with `multiple`.
62
+ * @words The trigger says how many are chosen in the product's words: the name when exactly one is
63
+ * chosen, *No regions selected* when none. It is never empty. Rows are sentence case names.
64
+ * "Announce bulk actions (`Select All`, `Select Only`) through the visible button label so the
65
+ * action matches what the screen reader speaks." (https://vercel.com/geist/multi-select) Ours: the
66
+ * two button labels come from the caller, in sentence case.
67
+ * @access "Each row checkbox needs an `aria-label` that names the item (`Select us-east-1`); a bare
68
+ * `Select` is unanchored for screen readers." (https://vercel.com/geist/multi-select) Ours: each
69
+ * row's checkbox is named by the row's name; each button is named by its label and the row (*Select
70
+ * only us-east-1*). "The trigger button needs a stable accessible name even when zero items are
71
+ * selected; don’t rely on the placeholder alone." (https://vercel.com/geist/multi-select) Ours: the
72
+ * trigger is a button with `aria-expanded`, named by its visible label tied by `id`, or by
73
+ * `aria-label` or `aria-labelledby`; the summary describes it.
74
+ */
75
+ declare function MultiSelect({ selectedCount, totalCount, onSelectAll, selectOnlyLabel, selectAllLabel, ...props }: MultiSelectProps): React.JSX.Element;
76
+ declare function MultiSelectTrigger({ className, children, size, label, error, ...props }: Omit<React.ComponentProps<typeof PopoverTrigger>, "children"> & {
77
+ /** The summary of the choice: a count, a name, or that nothing is chosen. It describes the trigger. */
78
+ children: string;
79
+ size?: FieldSize;
80
+ /** The message under the box, in the product's words; it also marks the box invalid. */
81
+ error?: string;
82
+ } & FieldName): React.JSX.Element;
83
+ declare function MultiSelectContent({ className, align, sideOffset, onKeyDown, ...props }: React.ComponentProps<typeof PopoverContent>): React.JSX.Element;
84
+ declare function MultiSelectRow({ name, checked, onCheckedChange, onSelectOnly, disabled, className, }: {
85
+ name: string;
86
+ checked: boolean;
87
+ onCheckedChange: (checked: boolean) => void;
88
+ /** Chooses this row and clears the rest. */
89
+ onSelectOnly: () => void;
90
+ disabled?: boolean;
91
+ className?: string;
92
+ }): React.JSX.Element;
93
+ export { MultiSelect, MultiSelectContent, MultiSelectRow, MultiSelectTrigger };
94
+ //# sourceMappingURL=multi-select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-select.d.ts","sourceRoot":"","sources":["../../src/components/multi-select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG/E,KAAK,uBAAuB,GAAG;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAUF,KAAK,gBAAgB,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,OAAO,CAAC,GAAG,uBAAuB,CAAC;AAEvF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,iBAAS,WAAW,CAAC,EACnB,aAAa,EACb,UAAU,EACV,WAAW,EACX,eAAe,EACf,cAAc,EACd,GAAG,KAAK,EACT,EAAE,gBAAgB,qBAUlB;AAED,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,QAAQ,EACR,IAAgB,EAChB,KAAK,EACL,KAAK,EACL,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,EAAE,UAAU,CAAC,GAAG;IACjE,uGAAuG;IACvG,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,SAAS,qBAmCZ;AAwBD,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,KAAe,EACf,UAAc,EACd,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,qBAiB7C;AAED,iBAAS,cAAc,CAAC,EACtB,IAAI,EACJ,OAAO,EACP,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,SAAS,GACV,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,4CAA4C;IAC5C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,qBAuDA;AAED,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,132 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
5
+ import { cn } from "../lib/utils.js";
6
+ import { Button } from "./button.js";
7
+ import { Checkbox } from "./checkbox.js";
8
+ import { FieldFrame } from "../internal/field-frame.js";
9
+ import { fieldTriggerStates } from "../internal/input-box.js";
10
+ import { Label } from "./label.js";
11
+ import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
12
+ import { selectTriggerVariants } from "./select.js";
13
+ const MultiSelectContext = React.createContext(null);
14
+ function useMultiSelect() {
15
+ const context = React.useContext(MultiSelectContext);
16
+ if (!context)
17
+ throw new Error("MultiSelectRow must be inside <MultiSelect>");
18
+ return context;
19
+ }
20
+ /**
21
+ * A menu of checkbox rows that chooses several values from a known list, with the count in its
22
+ * trigger.
23
+ *
24
+ * @family Choices
25
+ * @when "A keyboard-navigable dropdown for selecting multiple items with advanced focus management."
26
+ * (https://vercel.com/geist/multi-select) "Pick `<MultiSelect>` when users pick more than one value
27
+ * from a known list (regions, scopes, tags)." (https://vercel.com/geist/multi-select) Ours: also the facets of a collection.
28
+ * @instead "For a single value from a short list, use `Select`."
29
+ * (https://vercel.com/geist/multi-select) "When typing to filter is more important than seeing
30
+ * every option at once, use `Combobox`." (https://vercel.com/geist/multi-select) "Skip
31
+ * `<MultiSelect>` for boolean settings; `Toggle` handles those."
32
+ * (https://vercel.com/geist/multi-select) Ours: `Combobox` takes several values with `multiple`; a
33
+ * yes-or-no setting is a `Switch`, never a one-row multi-select.
34
+ * @behaviour "Use controlled mode when state lives in the URL or syncs to the server so the trigger
35
+ * label and stored value stay in lockstep." (https://vercel.com/geist/multi-select) "Use controlled
36
+ * state to manage selections programmatically." (https://vercel.com/geist/multi-select) Ours: the
37
+ * caller always owns the selection and passes it back through each row, so the trigger and the
38
+ * stored value (the URL, the server) never disagree; the root takes the same `selectedCount` and
39
+ * `totalCount` the trigger's summary is built from. "Show the selected count in the trigger (`3
40
+ * regions selected`); show the single name when only one is picked."
41
+ * (https://vercel.com/geist/multi-select) The trigger is `Input`'s box at its default size, in
42
+ * regular weight, with the chevron at the end (it turns up while the list is open); it keeps the
43
+ * hover edge while the list is open and takes the page's outline for keyboard focus, as a button
44
+ * does. Everything is neutral: a checked box is ink, never blue, and an error turns only the
45
+ * trigger's ring red. `label` and `error` go on `MultiSelectTrigger`, and a string `label` requires
46
+ * `id`; a trigger with no name does not compile. The list opens 8 px under the trigger at least as
47
+ * wide as it, in an 8 px inset, with rows 28 px tall and no gap, and stops 16 px short of the
48
+ * screen's edge (at most 24rem tall, then it scrolls). A row is the checkbox in a 28 px square that
49
+ * fills on hover, then a button holding the row's name; pressing the name runs the button, pressing
50
+ * the box toggles the row. "**Checkbox focus + Enter/Space**: Toggle individual item"
51
+ * (https://vercel.com/geist/multi-select) "**Button focus + Enter/Space**: Smart selection (Select
52
+ * Only, Select All, or Toggle based on context)" (https://vercel.com/geist/multi-select) "**Hidden
53
+ * action labels**: Appear on hover/focus to show available actions"
54
+ * (https://vercel.com/geist/multi-select) Ours: the button's label reads *Select all* on a chosen
55
+ * row while some rows are unchosen, and *Select only* on every other row; it shows on hover and
56
+ * focus of the row, and always on touch. "Keep checkbox focus and button focus distinct so Up/Down
57
+ * navigates rows and Left/Right toggles between the row’s checkbox and action button."
58
+ * (https://vercel.com/geist/multi-select) "**Up/Down arrows**: Navigate between rows while
59
+ * maintaining checkbox/button focus state" (https://vercel.com/geist/multi-select) "**Left/Right
60
+ * arrows**: Switch between checkbox and button focus within the current row"
61
+ * (https://vercel.com/geist/multi-select) "**Tab**: Focus away from the menu (natural tab
62
+ * behavior)" (https://vercel.com/geist/multi-select) "**Enter/Space**: Execute actions based on
63
+ * current focus (checkbox toggle or button click)" (https://vercel.com/geist/multi-select) "Trap
64
+ * focus inside the menu while it’s open and return focus to the trigger on close."
65
+ * (https://vercel.com/geist/multi-select) Ours: focus moves into the list when it opens; Tab leaves
66
+ * it in order. "For empty filters, render `No {items} match "{query}"` rather than `No results`."
67
+ * (https://vercel.com/geist/multi-select) Ours: there is no filter box, so a list that needs one is
68
+ * a `Combobox` with `multiple`.
69
+ * @words The trigger says how many are chosen in the product's words: the name when exactly one is
70
+ * chosen, *No regions selected* when none. It is never empty. Rows are sentence case names.
71
+ * "Announce bulk actions (`Select All`, `Select Only`) through the visible button label so the
72
+ * action matches what the screen reader speaks." (https://vercel.com/geist/multi-select) Ours: the
73
+ * two button labels come from the caller, in sentence case.
74
+ * @access "Each row checkbox needs an `aria-label` that names the item (`Select us-east-1`); a bare
75
+ * `Select` is unanchored for screen readers." (https://vercel.com/geist/multi-select) Ours: each
76
+ * row's checkbox is named by the row's name; each button is named by its label and the row (*Select
77
+ * only us-east-1*). "The trigger button needs a stable accessible name even when zero items are
78
+ * selected; don’t rely on the placeholder alone." (https://vercel.com/geist/multi-select) Ours: the
79
+ * trigger is a button with `aria-expanded`, named by its visible label tied by `id`, or by
80
+ * `aria-label` or `aria-labelledby`; the summary describes it.
81
+ */
82
+ function MultiSelect({ selectedCount, totalCount, onSelectAll, selectOnlyLabel, selectAllLabel, ...props }) {
83
+ const value = React.useMemo(() => ({ selectedCount, totalCount, onSelectAll, selectOnlyLabel, selectAllLabel }), [selectedCount, totalCount, onSelectAll, selectOnlyLabel, selectAllLabel]);
84
+ return (_jsx(MultiSelectContext.Provider, { value: value, children: _jsx(Popover, { ...props }) }));
85
+ }
86
+ function MultiSelectTrigger({ className, children, size = "default", label, error, ...props }) {
87
+ const summaryId = React.useId();
88
+ return (_jsx(FieldFrame, { label: label, error: error, size: size, control: props, children: (aria) => (_jsxs(PopoverTrigger, { "data-slot": "multi-select-trigger", "data-size": size, "data-field-frame": "", className: cn(selectTriggerVariants({ size }), fieldTriggerStates, "group/multi-select-trigger pr-3.25 pl-2.5", size !== "lg" && "leading-5.25", className), ...props, ...aria, "aria-describedby": [aria["aria-describedby"], summaryId].filter(Boolean).join(" "), children: [_jsx("span", { id: summaryId, className: "flex-1 truncate px-1.5 text-left", children: children }), _jsx(ChevronDownIcon, { "aria-hidden": "true", className: "size-3.5 text-muted-foreground group-data-popup-open/multi-select-trigger:hidden" }), _jsx(ChevronUpIcon, { "aria-hidden": "true", className: "hidden size-3.5 text-muted-foreground group-data-popup-open/multi-select-trigger:block" })] })) }));
89
+ }
90
+ const ROW = "[data-slot=multi-select-row]";
91
+ function moveFocus(event) {
92
+ const target = event.target;
93
+ if (!(target instanceof HTMLElement))
94
+ return;
95
+ const column = target.closest("[data-multi-select]")?.dataset.multiSelect;
96
+ const row = target.closest(ROW);
97
+ if (!column || !row)
98
+ return;
99
+ const rows = [...event.currentTarget.querySelectorAll(ROW)];
100
+ const index = rows.indexOf(row);
101
+ const focusIn = (next, name) => {
102
+ const control = next?.querySelector(`[data-multi-select=${name}]`);
103
+ if (!control)
104
+ return;
105
+ event.preventDefault();
106
+ control.focus();
107
+ };
108
+ if (event.key === "ArrowDown")
109
+ focusIn(rows[index + 1], column);
110
+ else if (event.key === "ArrowUp")
111
+ focusIn(rows[index - 1], column);
112
+ else if (event.key === "ArrowRight" && column === "check")
113
+ focusIn(row, "action");
114
+ else if (event.key === "ArrowLeft" && column === "action")
115
+ focusIn(row, "check");
116
+ }
117
+ function MultiSelectContent({ className, align = "start", sideOffset = 8, onKeyDown, ...props }) {
118
+ return (_jsx(PopoverContent, { "data-slot": "multi-select-content", align: align, sideOffset: sideOffset, className: cn("max-h-[min(calc(var(--available-height)---spacing(4)),24rem)] w-auto min-w-(--anchor-width) gap-0 overflow-y-auto p-2", className), onKeyDown: (event) => {
119
+ onKeyDown?.(event);
120
+ if (!event.defaultPrevented)
121
+ moveFocus(event);
122
+ }, ...props }));
123
+ }
124
+ function MultiSelectRow({ name, checked, onCheckedChange, onSelectOnly, disabled, className, }) {
125
+ const { selectedCount, totalCount, onSelectAll, selectOnlyLabel, selectAllLabel } = useMultiSelect();
126
+ const id = React.useId();
127
+ const actionId = `${id}-action`;
128
+ const nameId = `${id}-name`;
129
+ const selectsAll = checked && selectedCount < totalCount;
130
+ return (_jsxs("div", { "data-slot": "multi-select-row", className: cn("group/multi-select-row flex min-h-touch-target items-center text-sm [@media(hover:hover)_and_(pointer:fine)]:min-h-7", className), children: [_jsx(Label, { className: "size-7 shrink-0 justify-center gap-0 rounded-xs-plus hover:bg-accent active:bg-border has-data-disabled:pointer-events-none", htmlFor: id, children: _jsx(Checkbox, { id: id, "aria-labelledby": nameId, checked: checked, "data-multi-select": "check", disabled: disabled, onCheckedChange: onCheckedChange }) }), totalCount > 1 ? (_jsxs(Button, { "aria-labelledby": `${actionId} ${nameId}`, className: "h-7 flex-1 justify-between gap-0 rounded-xs-plus p-1 text-sm font-normal hover:bg-accent focus-visible:bg-accent active:bg-border disabled:text-placeholder disabled:opacity-100", "data-multi-select": "action", disabled: disabled, onClick: selectsAll ? onSelectAll : onSelectOnly, size: "xs", variant: "ghost", children: [_jsx("span", { className: "min-w-0 truncate text-left", id: nameId, children: name }), _jsx("span", { className: "min-w-16 rounded-xs-plus px-1 py-0.5 text-right text-xs font-normal text-muted-foreground opacity-0 group-focus-within/multi-select-row:opacity-100 group-hover/multi-select-row:opacity-100 [@media(hover:none)]:opacity-100", id: actionId, children: selectsAll ? selectAllLabel : selectOnlyLabel })] })) : (_jsx(Label, { className: "flex-1 truncate px-1 text-sm leading-5", htmlFor: id, id: nameId, children: name }))] }));
131
+ }
132
+ export { MultiSelect, MultiSelectContent, MultiSelectRow, MultiSelectTrigger };
@@ -1,24 +1,48 @@
1
1
  import * as React from "react";
2
- type NativeSelectProps = Omit<React.ComponentProps<"select">, "size"> & {
3
- size?: "sm" | "default";
4
- };
2
+ import { type FieldName } from "../internal/field-frame.js";
3
+ import { type FieldSize } from "../internal/input-box.js";
4
+ type NativeSelectProps = Omit<React.ComponentProps<"select">, "prefix" | "size"> & {
5
+ size?: FieldSize;
6
+ /** A mark before the value, such as an icon. */
7
+ prefix?: React.ReactNode;
8
+ /** A mark at the end, in place of the chevron. */
9
+ suffix?: React.ReactNode;
10
+ /** One muted sentence under the box that says what to enter, read with the box. */
11
+ description?: string;
12
+ /** The message under the box, in the product's words; it also marks the box invalid. */
13
+ error?: string;
14
+ } & FieldName;
5
15
  /**
6
16
  * The platform's own select, drawn as our input box.
7
17
  *
8
18
  * @family Inputs
9
19
  * @when A long fixed list inside a native form, where the platform's own picker serves best: a time
10
20
  * zone, a country.
11
- * @instead Two or three options are radios or a segmented control. Up to about ten, drawn in our
12
- * style, is `Select`. A list people type into is `Combobox`.
13
- * @behaviour Same box as `Input`, the chevron 10 px from the edge in the muted grey. It opens the
14
- * system's list, with native keyboard and typing. As an uncontrolled field it keeps a saved value
15
- * instead of snapping back to the page's first default.
16
- * @words The label names the thing chosen (*Time zone*). A placeholder option is distinct from a
17
- * real one and never preselected as if chosen.
18
- * @access A real `select` with a visible label; option groups are `optgroup`. The chevron is
19
- * decorative.
21
+ * @instead Two or three options are radios or a segmented control. "Pick `<Select>` for short,
22
+ * fixed lists (under ~10 items) where typing adds nothing; switch to `Combobox` once filtering
23
+ * helps." (https://vercel.com/geist/select) Ours: `Select` draws the short list in our style.
24
+ * @behaviour Same box, sizes, ring, hover, focus halo and disabled as `Input`, filling its
25
+ * container, the chevron in the muted grey with room reserved for it, after an optional `prefix`
26
+ * mark (a `suffix` mark takes the chevron's place). Colour sits in one place: an error turns the
27
+ * ring red and fills the halo, and colours the message; the fill, the value and the label stay
28
+ * neutral. `label` and `error` draw the `Field` parts around the box, and a string `label` requires
29
+ * `id`; a box with no name does not compile. It opens the system's list, with native keyboard and
30
+ * typing. As an uncontrolled field it keeps a saved value instead of snapping back to the page's
31
+ * first default. "Validate on blur and pass a string to `error`; messages name the field and end in
32
+ * a period (`Select a framework.`)." (https://vercel.com/geist/select) Other form rules are
33
+ * `Field`'s.
34
+ * @words "Label is a short Title Case noun (`Framework`, `Region`); accept the prop directly on
35
+ * `<Select>`." (https://vercel.com/geist/select) "Options are Title Case when short and match
36
+ * canonical branding (`Next.js`, not `NextJS`); keep the same register across the list."
37
+ * (https://vercel.com/geist/select) Ours: the label names the thing chosen (*Time zone*) and
38
+ * options are sentence case. A placeholder option is distinct from a real one and never preselected
39
+ * as if chosen.
40
+ * @access A real `select` with a visible label tied by `id`, or `aria-label`. "Group options past
41
+ * ~10 items with native `<optgroup>`; Geist `<Select>` has no `.Group` static."
42
+ * (https://vercel.com/geist/select) Ours: option groups are `optgroup`. The chevron, the prefix and
43
+ * the suffix are decorative.
20
44
  */
21
- declare function NativeSelect({ className, size, ref, ...props }: NativeSelectProps): React.JSX.Element;
45
+ declare function NativeSelect({ className, size, prefix, suffix, label, description, error, ref, ...props }: NativeSelectProps): React.JSX.Element;
22
46
  declare function NativeSelectOption({ className, ...props }: React.ComponentProps<"option">): React.JSX.Element;
23
47
  declare function NativeSelectOptGroup({ className, ...props }: React.ComponentProps<"optgroup">): React.JSX.Element;
24
48
  export { NativeSelect, NativeSelectOptGroup, NativeSelectOption };
@@ -1 +1 @@
1
- {"version":3,"file":"native-select.d.ts","sourceRoot":"","sources":["../../src/components/native-select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,KAAK,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,GAAG;IACtE,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,IAAgB,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,EAAE,iBAAiB,qBAiEtF;AAED,iBAAS,kBAAkB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,qBAQlF;AAED,iBAAS,oBAAoB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,qBAQtF;AAED,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"native-select.d.ts","sourceRoot":"","sources":["../../src/components/native-select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAG7F,KAAK,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC,GAAG;IACjF,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,gDAAgD;IAChD,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,kDAAkD;IAClD,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,SAAS,CAAC;AAEd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,IAAgB,EAChB,MAAM,EACN,MAAM,EACN,KAAK,EACL,WAAW,EACX,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,iBAAiB,qBA6FnB;AAED,iBAAS,kBAAkB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,qBAQlF;AAED,iBAAS,oBAAoB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,qBAQtF;AAED,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,CAAC"}
@@ -2,6 +2,8 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../lib/utils.js";
5
+ import { FieldFrame } from "../internal/field-frame.js";
6
+ import { fieldDisabled, fieldRing, fieldStates } from "../internal/input-box.js";
5
7
  import { ChevronDownIcon } from "lucide-react";
6
8
  /**
7
9
  * The platform's own select, drawn as our input box.
@@ -9,17 +11,31 @@ import { ChevronDownIcon } from "lucide-react";
9
11
  * @family Inputs
10
12
  * @when A long fixed list inside a native form, where the platform's own picker serves best: a time
11
13
  * zone, a country.
12
- * @instead Two or three options are radios or a segmented control. Up to about ten, drawn in our
13
- * style, is `Select`. A list people type into is `Combobox`.
14
- * @behaviour Same box as `Input`, the chevron 10 px from the edge in the muted grey. It opens the
15
- * system's list, with native keyboard and typing. As an uncontrolled field it keeps a saved value
16
- * instead of snapping back to the page's first default.
17
- * @words The label names the thing chosen (*Time zone*). A placeholder option is distinct from a
18
- * real one and never preselected as if chosen.
19
- * @access A real `select` with a visible label; option groups are `optgroup`. The chevron is
20
- * decorative.
14
+ * @instead Two or three options are radios or a segmented control. "Pick `<Select>` for short,
15
+ * fixed lists (under ~10 items) where typing adds nothing; switch to `Combobox` once filtering
16
+ * helps." (https://vercel.com/geist/select) Ours: `Select` draws the short list in our style.
17
+ * @behaviour Same box, sizes, ring, hover, focus halo and disabled as `Input`, filling its
18
+ * container, the chevron in the muted grey with room reserved for it, after an optional `prefix`
19
+ * mark (a `suffix` mark takes the chevron's place). Colour sits in one place: an error turns the
20
+ * ring red and fills the halo, and colours the message; the fill, the value and the label stay
21
+ * neutral. `label` and `error` draw the `Field` parts around the box, and a string `label` requires
22
+ * `id`; a box with no name does not compile. It opens the system's list, with native keyboard and
23
+ * typing. As an uncontrolled field it keeps a saved value instead of snapping back to the page's
24
+ * first default. "Validate on blur and pass a string to `error`; messages name the field and end in
25
+ * a period (`Select a framework.`)." (https://vercel.com/geist/select) Other form rules are
26
+ * `Field`'s.
27
+ * @words "Label is a short Title Case noun (`Framework`, `Region`); accept the prop directly on
28
+ * `<Select>`." (https://vercel.com/geist/select) "Options are Title Case when short and match
29
+ * canonical branding (`Next.js`, not `NextJS`); keep the same register across the list."
30
+ * (https://vercel.com/geist/select) Ours: the label names the thing chosen (*Time zone*) and
31
+ * options are sentence case. A placeholder option is distinct from a real one and never preselected
32
+ * as if chosen.
33
+ * @access A real `select` with a visible label tied by `id`, or `aria-label`. "Group options past
34
+ * ~10 items with native `<optgroup>`; Geist `<Select>` has no `.Group` static."
35
+ * (https://vercel.com/geist/select) Ours: option groups are `optgroup`. The chevron, the prefix and
36
+ * the suffix are decorative.
21
37
  */
22
- function NativeSelect({ className, size = "default", ref, ...props }) {
38
+ function NativeSelect({ className, size = "default", prefix, suffix, label, description, error, ref, ...props }) {
23
39
  const select = React.useRef(null);
24
40
  const setRef = React.useCallback((node) => {
25
41
  select.current = node;
@@ -63,7 +79,7 @@ function NativeSelect({ className, size = "default", ref, ...props }) {
63
79
  option.selected = shown.includes(option.value);
64
80
  appliedDefault.current = defaultKey;
65
81
  }, [defaultKey]);
66
- return (_jsxs("div", { className: cn("group/native-select relative w-fit has-[select:disabled]:opacity-50", className), "data-slot": "native-select-wrapper", "data-size": size, children: [_jsx("select", { ref: setRef, "data-slot": "native-select", "data-size": size, className: "h-control-height min-h-touch-target w-full min-w-0 appearance-none rounded-sm border border-input bg-background py-1 pr-8 pl-control-padding-inline text-sm transition-colors select-none selection:bg-primary selection:text-primary-foreground placeholder:text-placeholder disabled:pointer-events-none disabled:cursor-not-allowed aria-invalid:border-destructive data-[size=sm]:h-nadicode-control-height-sm data-[size=sm]:py-0.5 [@media(hover:hover)_and_(pointer:fine)]:min-h-0", ...props }), _jsx(ChevronDownIcon, { className: "pointer-events-none absolute top-1/2 right-2.5 size-4 -translate-y-1/2 text-muted-foreground select-none", "aria-hidden": "true", "data-slot": "native-select-icon" })] }));
82
+ return (_jsx(FieldFrame, { label: label, description: description, error: error, size: size, control: props, children: (aria) => (_jsxs("div", { className: cn("group/native-select relative w-full", className), "data-slot": "native-select-wrapper", "data-size": size, children: [prefix ? (_jsx("span", { "data-slot": "native-select-prefix", "aria-hidden": "true", className: "pointer-events-none absolute top-1/2 left-control-padding-inline flex -translate-y-1/2 items-center text-muted-foreground [&_svg:not([class*='size-'])]:size-4", children: prefix })) : null, _jsx("select", { ref: setRef, "data-slot": "native-select", "data-size": size, "data-field-frame": "", className: cn("h-control-height min-h-touch-target w-full min-w-0 cursor-pointer appearance-none rounded-sm bg-(--nc-field,var(--background)) py-0 pr-9 pl-control-padding-inline text-sm select-none selection:bg-primary selection:text-primary-foreground data-[size=lg]:h-nadicode-control-height-lg data-[size=lg]:rounded-md data-[size=lg]:text-base data-[size=sm]:h-nadicode-control-height-sm [@media(hover:hover)_and_(pointer:fine)]:min-h-0", fieldRing, fieldStates, fieldDisabled, prefix ? "pl-10" : undefined), ...props, ...aria }), suffix ? (_jsx("span", { "data-slot": "native-select-suffix", "aria-hidden": "true", className: "pointer-events-none absolute top-1/2 right-3 flex -translate-y-1/2 items-center text-muted-foreground select-none [&_svg:not([class*='size-'])]:size-4", children: suffix })) : (_jsx(ChevronDownIcon, { className: "pointer-events-none absolute top-1/2 right-3 size-3.5 -translate-y-1/2 text-muted-foreground select-none", "aria-hidden": "true", "data-slot": "native-select-icon" }))] })) }));
67
83
  }
68
84
  function NativeSelectOption({ className, ...props }) {
69
85
  return (_jsx("option", { "data-slot": "native-select-option", className: cn("bg-[Canvas] text-[CanvasText]", className), ...props }));