@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
@@ -0,0 +1,118 @@
1
+ /*
2
+ * Standard tool checked first: a `no-restricted-syntax` selector can match the
3
+ * string `"mod+k"`, but not a handler that reads `key` and `metaKey` as two
4
+ * separate checks (`(e.metaKey || e.ctrlKey) && e.key === "k"`), because the
5
+ * two reads only mean a shortcut together, in the same function; and it cannot
6
+ * allow the files that own Find.
7
+ */
8
+
9
+ const SHORTCUT_STRING = /^(?:mod|meta|cmd|ctrl)\+k$/i;
10
+ const MODIFIER_PROPERTIES = new Set(["metaKey", "ctrlKey"]);
11
+ const CASE_METHODS = new Set(["toLowerCase", "toUpperCase"]);
12
+ const KEY_VALUES = { key: new Set(["k", "K"]), code: new Set(["KeyK"]) };
13
+
14
+ const inFiles = (filename, files) => {
15
+ const path = filename.replaceAll("\\", "/");
16
+ return files.some((file) => path === file || path.endsWith(`/${file}`));
17
+ };
18
+
19
+ const unwrapChain = (node) => (node.type === "ChainExpression" ? node.expression : node);
20
+
21
+ const propertyName = (member) =>
22
+ member.type === "MemberExpression" && !member.computed ? member.property.name : null;
23
+
24
+ function keyRead(node) {
25
+ const read = unwrapChain(node);
26
+ if (read.type === "CallExpression") {
27
+ const callee = unwrapChain(read.callee);
28
+ if (propertyName(callee) && CASE_METHODS.has(propertyName(callee))) {
29
+ return keyRead(callee.object);
30
+ }
31
+ return null;
32
+ }
33
+ const name = propertyName(read);
34
+ return name && name in KEY_VALUES ? name : null;
35
+ }
36
+
37
+ const isLetterK = (read, literal) =>
38
+ literal.type === "Literal" &&
39
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- ESTree types Literal.value as string | number | boolean | RegExp | bigint | null; only typeof picks the string.
40
+ typeof literal.value === "string" &&
41
+ KEY_VALUES[read].has(literal.value);
42
+
43
+ const COMPARISONS = new Set(["===", "==", "!==", "!="]);
44
+
45
+ export const commandShortcutSingleOwner = {
46
+ meta: {
47
+ type: "problem",
48
+ docs: {
49
+ description: "Only the files that own Find bind the command-and-K shortcut.",
50
+ },
51
+ schema: [
52
+ {
53
+ type: "object",
54
+ properties: {
55
+ owners: { type: "array", items: { type: "string" }, uniqueItems: true },
56
+ },
57
+ additionalProperties: false,
58
+ },
59
+ ],
60
+ defaultOptions: [{ owners: [] }],
61
+ messages: {
62
+ "find-shortcut": "⌘K and Ctrl+K belong to Find. Bind an in-page field to another key.",
63
+ },
64
+ },
65
+ create(context) {
66
+ if (inFiles(context.filename, context.options[0].owners)) return {};
67
+
68
+ const frames = [{ letterKs: [], readsModifier: false }];
69
+ const frame = () => frames.at(-1);
70
+
71
+ const enter = () => frames.push({ letterKs: [], readsModifier: false });
72
+ const leave = () => {
73
+ const { letterKs, readsModifier } = frames.pop();
74
+ if (!readsModifier) return;
75
+ for (const node of letterKs) context.report({ node, messageId: "find-shortcut" });
76
+ };
77
+
78
+ const checkString = (node, value) => {
79
+ if (SHORTCUT_STRING.test(value.trim())) context.report({ node, messageId: "find-shortcut" });
80
+ };
81
+
82
+ const noteLetterK = (node, read, other) => {
83
+ const name = keyRead(read);
84
+ if (name && isLetterK(name, other)) frame().letterKs.push(node);
85
+ };
86
+
87
+ return {
88
+ FunctionDeclaration: enter,
89
+ FunctionExpression: enter,
90
+ ArrowFunctionExpression: enter,
91
+ "FunctionDeclaration:exit": leave,
92
+ "FunctionExpression:exit": leave,
93
+ "ArrowFunctionExpression:exit": leave,
94
+ "Program:exit"() {
95
+ leave();
96
+ },
97
+ MemberExpression(node) {
98
+ if (MODIFIER_PROPERTIES.has(propertyName(node))) frame().readsModifier = true;
99
+ },
100
+ BinaryExpression(node) {
101
+ if (!COMPARISONS.has(node.operator)) return;
102
+ noteLetterK(node, node.left, node.right);
103
+ noteLetterK(node, node.right, node.left);
104
+ },
105
+ SwitchCase(node) {
106
+ if (node.test) noteLetterK(node, node.parent.discriminant, node.test);
107
+ },
108
+ Literal(node) {
109
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- ESTree types Literal.value as string | number | boolean | RegExp | bigint | null; only typeof picks the string.
110
+ if (typeof node.value === "string") checkString(node, node.value);
111
+ },
112
+ TemplateElement(node) {
113
+ if (node.parent.type === "TemplateLiteral" && node.parent.expressions.length === 0)
114
+ checkString(node, node.value.cooked ?? "");
115
+ },
116
+ };
117
+ },
118
+ };
@@ -0,0 +1,63 @@
1
+ /*
2
+ * Standard tool checked first: `no-restricted-syntax` reads one attribute at a
3
+ * time, and this check compares two of them on the same element (the title's
4
+ * verb with the confirm label's), and must know the element is the package's
5
+ * `ConfirmDialog` or the Portal's wrapper of it, not a local component.
6
+ */
7
+ import { stringAttribute } from "./literal-text.js";
8
+ import { renderedImports, writtenName } from "./rendered-import.js";
9
+
10
+ const DIALOGS = new Set(["ConfirmDialog", "ConfirmedActionForm"]);
11
+ const QUESTION_OPENING = /^(?:are you sure|do you (?:really )?want|would you like|should)\b/i;
12
+
13
+ const words = (text) => text.trim().split(/\s+/).filter(Boolean);
14
+
15
+ export const confirmDialogWords = {
16
+ meta: {
17
+ type: "problem",
18
+ docs: {
19
+ description:
20
+ "A ConfirmDialog's title is a statement, and its confirm label repeats the title's verb with the noun.",
21
+ },
22
+ schema: [],
23
+ messages: {
24
+ "title-is-a-question":
25
+ 'The title of a confirmation is a statement that names the record, "Remove pec@acme.it", never a question.',
26
+ "confirm-label-bare":
27
+ 'The confirm label repeats the verb and the noun, "Remove mailbox"; "{{label}}" alone does not say what is confirmed.',
28
+ "confirm-verb-differs":
29
+ 'The confirm label repeats the title\'s verb: the title says "{{title}}", so the label starts with "{{verb}}", not "{{label}}".',
30
+ },
31
+ },
32
+ create(context) {
33
+ const importOf = renderedImports(context);
34
+
35
+ return {
36
+ JSXOpeningElement(node) {
37
+ if (!importOf(node.name) || !DIALOGS.has(writtenName(node.name))) return;
38
+ const titleText = stringAttribute(node, "title");
39
+ const labelText = stringAttribute(node, "confirmLabel");
40
+ if (
41
+ titleText !== null &&
42
+ (titleText.trim().endsWith("?") || QUESTION_OPENING.test(titleText.trim()))
43
+ ) {
44
+ context.report({ node, messageId: "title-is-a-question" });
45
+ }
46
+ if (labelText === null) return;
47
+ const labelWords = words(labelText);
48
+ if (labelWords.length < 2) {
49
+ context.report({ node, messageId: "confirm-label-bare", data: { label: labelText } });
50
+ return;
51
+ }
52
+ const verb = titleText === null ? undefined : words(titleText)[0];
53
+ if (verb !== undefined && verb.toLowerCase() !== labelWords[0].toLowerCase()) {
54
+ context.report({
55
+ node,
56
+ messageId: "confirm-verb-differs",
57
+ data: { title: titleText, verb, label: labelText },
58
+ });
59
+ }
60
+ },
61
+ };
62
+ },
63
+ };
@@ -0,0 +1,96 @@
1
+ /*
2
+ * Standard tool checked first: a `no-restricted-syntax` selector matches one
3
+ * node, but this reads a whole `columns` array (how many of its objects lack a
4
+ * `size`, whether one sets `meta.wraps`, whether a spread or a call makes the
5
+ * list unknowable) and follows a variable to its array, which no selector can.
6
+ */
7
+ import { renderedImports } from "./rendered-import.js";
8
+
9
+ const COMMANDS_COLUMN_ID = "actions";
10
+
11
+ const WRAPPERS = new Set(["TSAsExpression", "TSSatisfiesExpression", "TSNonNullExpression"]);
12
+
13
+ function unwrap(node) {
14
+ let current = node;
15
+ while (WRAPPERS.has(current.type)) current = current.expression;
16
+ return current;
17
+ }
18
+
19
+ const keyName = (property) => {
20
+ if (property.computed) return null;
21
+ return property.key.type === "Identifier" ? property.key.name : property.key.value;
22
+ };
23
+
24
+ const propertyOf = (column, name) =>
25
+ column.properties.find((property) => property.type === "Property" && keyName(property) === name);
26
+
27
+ function setsWraps(column) {
28
+ const meta = propertyOf(column, "meta")?.value;
29
+ if (meta?.type !== "ObjectExpression") return false;
30
+ const wraps = propertyOf(meta, "wraps")?.value;
31
+ return wraps !== undefined && !(wraps.type === "Literal" && wraps.value === false);
32
+ }
33
+
34
+ const isCommandsColumn = (column) => propertyOf(column, "id")?.value.value === COMMANDS_COLUMN_ID;
35
+
36
+ const isKnownColumn = (element) =>
37
+ element?.type === "ObjectExpression" &&
38
+ element.properties.every((property) => property.type === "Property");
39
+
40
+ function arrayOf(context, expression) {
41
+ const node = unwrap(expression);
42
+ if (node.type === "ArrayExpression") return node;
43
+ if (node.type !== "Identifier") return null;
44
+ for (let scope = context.sourceCode.getScope(node); scope; scope = scope.upper) {
45
+ const variable = scope.set.get(node.name);
46
+ if (!variable) continue;
47
+ const [definition] = variable.defs;
48
+ const initialisedOnce =
49
+ variable.defs.length === 1 && variable.references.filter((use) => use.isWrite()).length === 1;
50
+ if (!initialisedOnce || definition.node.type !== "VariableDeclarator") return null;
51
+ const init = definition.node.init ? unwrap(definition.node.init) : null;
52
+ return init?.type === "ArrayExpression" ? init : null;
53
+ }
54
+ return null;
55
+ }
56
+
57
+ function breaksTheRule(array) {
58
+ if (!array.elements.every(isKnownColumn)) return false;
59
+ if (array.elements.some(setsWraps)) return false;
60
+ const unsized = array.elements.filter((column) => !propertyOf(column, "size"));
61
+ if (unsized.length > 1) return true;
62
+ const content = array.elements.filter((column) => !isCommandsColumn(column));
63
+ return content.length > 0 && unsized.length === 0;
64
+ }
65
+
66
+ export const dataTableOneUnsizedColumn = {
67
+ meta: {
68
+ type: "problem",
69
+ docs: {
70
+ description:
71
+ "A DataTable's columns all have a size in pixels except the one that takes the spare width.",
72
+ },
73
+ schema: [],
74
+ messages: {
75
+ "one-unsized-column":
76
+ "Give every column a `size` in pixels except the one that takes the spare width. Columns without a size share what is left, so two of them make the widths depend on the data.",
77
+ },
78
+ },
79
+ create(context) {
80
+ const importOf = renderedImports(context);
81
+ return {
82
+ JSXOpeningElement(node) {
83
+ if (importOf(node.name)?.name !== "DataTable") return;
84
+ const attribute = node.attributes.find(
85
+ (candidate) => candidate.type === "JSXAttribute" && candidate.name.name === "columns",
86
+ );
87
+ const value = attribute?.value;
88
+ if (value?.type !== "JSXExpressionContainer") return;
89
+ const array = arrayOf(context, value.expression);
90
+ if (array && breaksTheRule(array)) {
91
+ context.report({ node: value.expression, messageId: "one-unsized-column" });
92
+ }
93
+ },
94
+ };
95
+ },
96
+ };
@@ -0,0 +1,69 @@
1
+ /*
2
+ * Standard tool checked first: a `no-restricted-syntax` descendant selector can
3
+ * match a `Button` below `SheetContent`, but not that the button is the design
4
+ * system's, nor follow a renamed import, nor read `variant="destructive"` off
5
+ * the opening element or ignore a match that only sits in a `render` prop.
6
+ */
7
+ import { renderedImports, staticAttribute } from "./rendered-import.js";
8
+
9
+ const BUTTONS = new Set(["Button", "ButtonLink"]);
10
+ const CONFIRMATIONS = new Set(["ConfirmDialog", "AlertDialog", "ConfirmActionDialog"]);
11
+ const CLOSED_SURFACES = new Set([
12
+ "SheetContent",
13
+ "DrawerContent",
14
+ "FormDrawer",
15
+ "CatalogDrawer",
16
+ "AccordionContent",
17
+ "CollapsibleContent",
18
+ "Disclosure",
19
+ "HoverCardContent",
20
+ ]);
21
+
22
+ const FROM_DESIGN_SYSTEM = /^(@nadicodeai\/ui(\/|$)|@\/|\.)/;
23
+
24
+ export const destructiveActionPlacement = {
25
+ meta: {
26
+ type: "problem",
27
+ docs: {
28
+ description:
29
+ "A destructive action is confirmed in a ConfirmDialog opened from the page or a row's menu, never inside a sheet, drawer, hover card or collapsed section.",
30
+ },
31
+ schema: [],
32
+ messages: {
33
+ "destructive-placement":
34
+ "A destructive action is confirmed in a ConfirmDialog opened from the page or the row's menu: only a modal dims the whole page. Do not place it in a sheet, a drawer, a hover card or a section that starts closed.",
35
+ },
36
+ },
37
+ create(context) {
38
+ const importOf = renderedImports(context);
39
+ const exportOf = (element) => {
40
+ const imported = importOf(element.openingElement.name);
41
+ return imported && FROM_DESIGN_SYSTEM.test(imported.source) ? imported.name : null;
42
+ };
43
+
44
+ const isDestructive = (element) => {
45
+ const name = exportOf(element);
46
+ if (CONFIRMATIONS.has(name)) return true;
47
+ return (
48
+ BUTTONS.has(name) && staticAttribute(element.openingElement, "variant") === "destructive"
49
+ );
50
+ };
51
+
52
+ function insideClosedSurface(element) {
53
+ let child = element;
54
+ for (let node = element.parent; node; child = node, node = node.parent) {
55
+ if (node.type !== "JSXElement" || !node.children.includes(child)) continue;
56
+ if (CLOSED_SURFACES.has(exportOf(node))) return true;
57
+ }
58
+ return false;
59
+ }
60
+
61
+ return {
62
+ JSXElement(node) {
63
+ if (isDestructive(node) && insideClosedSurface(node)) {
64
+ context.report({ node: node.openingElement, messageId: "destructive-placement" });
65
+ }
66
+ },
67
+ };
68
+ },
69
+ };
@@ -0,0 +1,71 @@
1
+ /*
2
+ * Standard tool checked first: a `no-restricted-syntax` selector can match a
3
+ * `disabled` attribute on a `Checkbox`, but not follow a renamed import to the
4
+ * component, nor read the other attributes of the same element (aria-describedby,
5
+ * a spread) or tell a wait (`disabled={pending}`) from a reason.
6
+ */
7
+ import { PACKAGE_BARREL, partOf, renderedImports } from "./rendered-import.js";
8
+
9
+ const MODULES = [
10
+ ["Checkbox", "checkbox"],
11
+ ["Switch", "switch"],
12
+ ["RadioGroupItem", "radio-group"],
13
+ ];
14
+
15
+ const CHOICE = new Map(
16
+ MODULES.map(([part, module]) => [
17
+ part,
18
+ [`@nadicodeai/ui/components/${module}`, PACKAGE_BARREL].map((from) => ({ from, part })),
19
+ ]),
20
+ );
21
+
22
+ const WAIT = /^(?:is)?(?:pending|saving|submitting|busy)$/i;
23
+
24
+ const unwrapChain = (node) => (node.type === "ChainExpression" ? node.expression : node);
25
+
26
+ function isWait(attribute) {
27
+ if (attribute.value?.type !== "JSXExpressionContainer") return false;
28
+ const read = unwrapChain(attribute.value.expression);
29
+ if (read.type === "Identifier") return WAIT.test(read.name);
30
+ if (read.type !== "MemberExpression" || read.computed) return false;
31
+ return WAIT.test(read.property.name);
32
+ }
33
+
34
+ function isEnabledLiteral(attribute) {
35
+ const expression = attribute.value?.expression;
36
+ return expression?.type === "Literal" && expression.value === false;
37
+ }
38
+
39
+ const attributeNamed = (openingElement, name) =>
40
+ openingElement.attributes.find(
41
+ (attribute) => attribute.type === "JSXAttribute" && attribute.name.name === name,
42
+ );
43
+
44
+ export const disabledChoiceSaysWhy = {
45
+ meta: {
46
+ type: "problem",
47
+ docs: {
48
+ description:
49
+ "A disabled Checkbox, Switch or RadioGroupItem points aria-describedby at the line that names the reason.",
50
+ },
51
+ schema: [],
52
+ messages: {
53
+ "disabled-without-reason":
54
+ "Say why this is disabled: point aria-describedby at the line that names the reason, or use ChoiceboxGroupItem's disabledReason.",
55
+ },
56
+ },
57
+ create(context) {
58
+ const importOf = renderedImports(context);
59
+
60
+ return {
61
+ JSXOpeningElement(node) {
62
+ if (!partOf(importOf(node.name), CHOICE, context.filename)) return;
63
+ const disabled = attributeNamed(node, "disabled");
64
+ if (!disabled || isEnabledLiteral(disabled) || isWait(disabled)) return;
65
+ if (attributeNamed(node, "aria-describedby")) return;
66
+ if (node.attributes.some((attribute) => attribute.type === "JSXSpreadAttribute")) return;
67
+ context.report({ node: disabled, messageId: "disabled-without-reason" });
68
+ },
69
+ };
70
+ },
71
+ };
@@ -0,0 +1,110 @@
1
+ /*
2
+ * Standard tool checked first: `no-restricted-syntax` can match a label that
3
+ * ends in an ellipsis, but not what its command does: whether the handler
4
+ * opens something (a call that opens or passes `true`), runs, or navigates.
5
+ */
6
+ import { childText, keyName, literalText } from "./literal-text.js";
7
+ import { renderedImports } from "./rendered-import.js";
8
+
9
+ const ENDS_IN_ELLIPSIS = /(?:…|\.\.\.)\s*$/;
10
+ const COMMAND_KEYS = new Set(["onClick", "onSelect", "link", "render", "icon"]);
11
+ const HANDLER_KEYS = new Set(["onClick", "onSelect"]);
12
+ const MENU_ROWS = new Set([
13
+ "DropdownMenuItem",
14
+ "DropdownMenuCommand",
15
+ "ContextMenuItem",
16
+ "MenubarItem",
17
+ ]);
18
+ const MENU_LINKS = new Set(["DropdownMenuLink", "ContextMenuLink"]);
19
+ const FROM_DESIGN_SYSTEM = /^(@nadicodeai\/ui(\/|$)|@\/|\.)/;
20
+ const OPENING_CALL = /^(?:open|show)(?:[A-Z]|$)/;
21
+ const FUNCTIONS = new Set(["ArrowFunctionExpression", "FunctionExpression"]);
22
+
23
+ const calleeName = (callee) => {
24
+ if (callee.type === "Identifier") return callee.name;
25
+ if (callee.type === "MemberExpression" && !callee.computed) return callee.property.name;
26
+ return null;
27
+ };
28
+
29
+ function opensSomething(node, visitorKeys) {
30
+ const calls = [];
31
+ const stack = [node.body];
32
+ while (stack.length > 0) {
33
+ const current = stack.pop();
34
+ if (current.type === "CallExpression") calls.push(current);
35
+ for (const key of visitorKeys[current.type] ?? []) {
36
+ const value = current[key];
37
+ for (const child of Array.isArray(value) ? value : [value]) {
38
+ if (child?.type) stack.push(child);
39
+ }
40
+ }
41
+ }
42
+ return calls.some(
43
+ (call) =>
44
+ OPENING_CALL.test(calleeName(call.callee) ?? "") ||
45
+ call.arguments.some((argument) => argument.type === "Literal" && argument.value === true),
46
+ );
47
+ }
48
+
49
+ export const ellipsisOpensADialog = {
50
+ meta: {
51
+ type: "problem",
52
+ docs: {
53
+ description: "A command's label ends in an ellipsis only when the command opens a dialog.",
54
+ },
55
+ schema: [],
56
+ messages: {
57
+ "ellipsis-without-dialog":
58
+ 'An ellipsis says "this asks for more before it acts". "{{label}}" navigates or runs at once, so drop the ellipsis; keep it only on a command that opens a dialog.',
59
+ },
60
+ },
61
+ create(context) {
62
+ const importOf = renderedImports(context);
63
+ const { visitorKeys } = context.sourceCode;
64
+
65
+ function reportsNoDialog(handler) {
66
+ return (
67
+ handler !== undefined &&
68
+ FUNCTIONS.has(handler.type) &&
69
+ !opensSomething(handler, visitorKeys)
70
+ );
71
+ }
72
+
73
+ return {
74
+ ObjectExpression(node) {
75
+ const keys = new Map(node.properties.map((property) => [keyName(property), property]));
76
+ const label = literalText(keys.get("label")?.value);
77
+ if (label === null || !ENDS_IN_ELLIPSIS.test(label)) return;
78
+ if (![...keys.keys()].some((key) => COMMAND_KEYS.has(key))) return;
79
+ if (keys.has("render")) return;
80
+ const handlerProperty = [...keys].find(([key]) => HANDLER_KEYS.has(key))?.[1];
81
+ const navigates = keys.has("link");
82
+ if (navigates || reportsNoDialog(handlerProperty?.value)) {
83
+ context.report({
84
+ node: keys.get("label").value,
85
+ messageId: "ellipsis-without-dialog",
86
+ data: { label },
87
+ });
88
+ }
89
+ },
90
+ JSXElement(node) {
91
+ const imported = importOf(node.openingElement.name);
92
+ if (!imported || !FROM_DESIGN_SYSTEM.test(imported.source)) return;
93
+ const row = MENU_ROWS.has(imported.name);
94
+ if (!row && !MENU_LINKS.has(imported.name)) return;
95
+ const label = childText(node);
96
+ if (label === null || !ENDS_IN_ELLIPSIS.test(label)) return;
97
+ const handler = node.openingElement.attributes.find(
98
+ (attribute) => attribute.type === "JSXAttribute" && HANDLER_KEYS.has(attribute.name.name),
99
+ )?.value?.expression;
100
+ if (!row || reportsNoDialog(handler)) {
101
+ context.report({
102
+ node: node.openingElement,
103
+ messageId: "ellipsis-without-dialog",
104
+ data: { label },
105
+ });
106
+ }
107
+ },
108
+ };
109
+ },
110
+ };
@@ -0,0 +1,155 @@
1
+ /*
2
+ * Standard tool checked first: a `no-restricted-syntax` selector can match an
3
+ * `InputGroupAddon` holding `<Search/>`, but not an element joined to its import
4
+ * (a renamed or namespaced one), nor the shape of a whole group: only addons of
5
+ * fixed text or one icon around one input, a group with another button, a
6
+ * block addon or a textarea allowed. That needs the group's children together.
7
+ */
8
+ import {
9
+ PACKAGE_BARREL,
10
+ findBelow,
11
+ partOf,
12
+ renderedImports,
13
+ staticAttribute,
14
+ walk,
15
+ } from "./rendered-import.js";
16
+
17
+ const GROUP_PARTS = [
18
+ "InputGroup",
19
+ "InputGroupAddon",
20
+ "InputGroupButton",
21
+ "InputGroupInput",
22
+ "InputGroupText",
23
+ "InputGroupTextarea",
24
+ ];
25
+
26
+ const GROUP_MODULES = [
27
+ "@nadicodeai/ui/components/input-group",
28
+ PACKAGE_BARREL,
29
+ "@/components/input-group",
30
+ ];
31
+
32
+ const PARTS = new Map(
33
+ GROUP_PARTS.map((part) => [part, GROUP_MODULES.map((from) => ({ from, part }))]),
34
+ );
35
+
36
+ const ICON_MODULES = new Set(["lucide-react", "@nadicodeai/ui/components/house-icons"]);
37
+ const SEARCH_ICONS = new Set(["Search", "SearchIcon"]);
38
+ const CLEAR_ICONS = new Set(["X", "XIcon"]);
39
+ const INLINE_ALIGNS = new Set(["inline-start", "inline-end"]);
40
+
41
+ const significant = (element) =>
42
+ element.children.filter(
43
+ (child) =>
44
+ !(child.type === "JSXText" && child.value.trim() === "") &&
45
+ !(child.type === "JSXExpressionContainer" && child.expression.type === "JSXEmptyExpression"),
46
+ );
47
+
48
+ const isFixedText = (child) =>
49
+ child.type === "JSXText" ||
50
+ (child.type === "JSXExpressionContainer" && child.expression.type === "Literal");
51
+
52
+ export const inputGroupOwners = {
53
+ meta: {
54
+ type: "problem",
55
+ docs: {
56
+ description:
57
+ "A search box, a box that clears and fixed text or an icon in a box are drawn by the component that owns them, not by hand from InputGroup parts.",
58
+ },
59
+ schema: [],
60
+ messages: {
61
+ "hand-built-field":
62
+ "Use the component that owns this field: CollectionSearch for a collection's search, ClearableInput for a box that clears, Input's prefix and suffix for fixed text or an icon in the box.",
63
+ },
64
+ },
65
+ create(context) {
66
+ const importOf = renderedImports(context);
67
+ const { visitorKeys } = context.sourceCode;
68
+
69
+ const partName = (element) =>
70
+ partOf(importOf(element.openingElement.name), PARTS, context.filename);
71
+
72
+ const iconName = (element) => {
73
+ const imported = importOf(element.openingElement.name);
74
+ return imported && ICON_MODULES.has(imported.source) ? imported.name : null;
75
+ };
76
+
77
+ const holdsIcon = (element, names) =>
78
+ findBelow(
79
+ element,
80
+ visitorKeys,
81
+ (node) => node.type === "JSXElement" && names.has(iconName(node)),
82
+ ) !== null;
83
+
84
+ function holdsTextOrOneIcon(element) {
85
+ const content = significant(element);
86
+ if (content.length === 0) return false;
87
+ if (content.length === 1 && content[0].type === "JSXElement") {
88
+ const [only] = content;
89
+ if (iconName(only)) return true;
90
+ return partName(only) === "InputGroupText" && holdsTextOrOneIcon(only);
91
+ }
92
+ return content.every(isFixedText);
93
+ }
94
+
95
+ function readGroup(group) {
96
+ const facts = {
97
+ inputs: 0,
98
+ hasTextarea: false,
99
+ hasBlockAddon: false,
100
+ hasSearchAddon: false,
101
+ hasClearButton: false,
102
+ hasOtherButton: false,
103
+ };
104
+ walk(group, visitorKeys, (node) => {
105
+ if (node.type !== "JSXElement") return true;
106
+ const part = partName(node);
107
+ if (part === "InputGroup") return false;
108
+ if (part === "InputGroupInput") facts.inputs += 1;
109
+ if (part === "InputGroupTextarea") facts.hasTextarea = true;
110
+ if (part === "InputGroupAddon") {
111
+ const { openingElement } = node;
112
+ const hasAlign = openingElement.attributes.some(
113
+ (attribute) => attribute.type === "JSXAttribute" && attribute.name.name === "align",
114
+ );
115
+ if (hasAlign && !INLINE_ALIGNS.has(staticAttribute(openingElement, "align"))) {
116
+ facts.hasBlockAddon = true;
117
+ }
118
+ if (holdsIcon(node, SEARCH_ICONS)) facts.hasSearchAddon = true;
119
+ }
120
+ if (part === "InputGroupButton") {
121
+ if (holdsIcon(node, CLEAR_ICONS)) facts.hasClearButton = true;
122
+ else facts.hasOtherButton = true;
123
+ }
124
+ return true;
125
+ });
126
+ return facts;
127
+ }
128
+
129
+ function isFixedAdornments(group) {
130
+ const children = significant(group);
131
+ if (!children.every((child) => child.type === "JSXElement")) return false;
132
+ const parts = children.map(partName);
133
+ const addons = children.filter((_, index) => parts[index] === "InputGroupAddon");
134
+ const inputs = parts.filter((part) => part === "InputGroupInput");
135
+ return (
136
+ inputs.length === 1 &&
137
+ addons.length > 0 &&
138
+ addons.length + inputs.length === children.length &&
139
+ addons.every(holdsTextOrOneIcon)
140
+ );
141
+ }
142
+
143
+ return {
144
+ JSXElement(node) {
145
+ if (partName(node) !== "InputGroup") return;
146
+ const facts = readGroup(node);
147
+ if (facts.hasTextarea || facts.hasBlockAddon || facts.hasOtherButton) return;
148
+ const isSearch = facts.inputs > 0 && facts.hasSearchAddon;
149
+ if (isSearch || facts.hasClearButton || isFixedAdornments(node)) {
150
+ context.report({ node: node.openingElement, messageId: "hand-built-field" });
151
+ }
152
+ },
153
+ };
154
+ },
155
+ };