@nadicodeai/ui 12.1.102 → 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 (562) 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.js +2 -2
  5. package/dist/components/action-menu.d.ts +20 -19
  6. package/dist/components/action-menu.d.ts.map +1 -1
  7. package/dist/components/action-menu.js +22 -15
  8. package/dist/components/action-status.d.ts +14 -10
  9. package/dist/components/action-status.d.ts.map +1 -1
  10. package/dist/components/action-status.js +14 -10
  11. package/dist/components/activity-calendar.d.ts +3 -1
  12. package/dist/components/activity-calendar.d.ts.map +1 -1
  13. package/dist/components/activity-calendar.js +16 -2
  14. package/dist/components/activity-feed.d.ts +2 -2
  15. package/dist/components/activity-feed.d.ts.map +1 -1
  16. package/dist/components/activity-feed.js +14 -5
  17. package/dist/components/agent-card.d.ts +11 -5
  18. package/dist/components/agent-card.d.ts.map +1 -1
  19. package/dist/components/agent-card.js +13 -7
  20. package/dist/components/agent-list-row.js +1 -1
  21. package/dist/components/agent-orb-appearance.d.ts +7 -1
  22. package/dist/components/agent-orb-appearance.d.ts.map +1 -1
  23. package/dist/components/agent-orb-appearance.js +26 -0
  24. package/dist/components/agent-profile.d.ts +8 -7
  25. package/dist/components/agent-profile.d.ts.map +1 -1
  26. package/dist/components/agent-profile.js +6 -6
  27. package/dist/components/alert-dialog.d.ts +25 -16
  28. package/dist/components/alert-dialog.d.ts.map +1 -1
  29. package/dist/components/alert-dialog.js +30 -18
  30. package/dist/components/alert.d.ts +1 -1
  31. package/dist/components/avatar.d.ts +83 -17
  32. package/dist/components/avatar.d.ts.map +1 -1
  33. package/dist/components/avatar.js +89 -23
  34. package/dist/components/badge.d.ts +59 -8
  35. package/dist/components/badge.d.ts.map +1 -1
  36. package/dist/components/badge.js +57 -14
  37. package/dist/components/breadcrumb.d.ts +18 -7
  38. package/dist/components/breadcrumb.d.ts.map +1 -1
  39. package/dist/components/breadcrumb.js +37 -13
  40. package/dist/components/button-group.d.ts +12 -15
  41. package/dist/components/button-group.d.ts.map +1 -1
  42. package/dist/components/button-group.js +13 -17
  43. package/dist/components/button.d.ts +126 -23
  44. package/dist/components/button.d.ts.map +1 -1
  45. package/dist/components/button.js +164 -39
  46. package/dist/components/calendar.d.ts +26 -15
  47. package/dist/components/calendar.d.ts.map +1 -1
  48. package/dist/components/calendar.js +60 -46
  49. package/dist/components/card-row.d.ts +13 -5
  50. package/dist/components/card-row.d.ts.map +1 -1
  51. package/dist/components/card-row.js +24 -10
  52. package/dist/components/card.d.ts +6 -12
  53. package/dist/components/card.d.ts.map +1 -1
  54. package/dist/components/card.js +11 -16
  55. package/dist/components/carousel.d.ts +3 -3
  56. package/dist/components/carousel.d.ts.map +1 -1
  57. package/dist/components/carousel.js +7 -7
  58. package/dist/components/catalog-drawer.d.ts +7 -7
  59. package/dist/components/catalog-drawer.d.ts.map +1 -1
  60. package/dist/components/catalog-drawer.js +6 -6
  61. package/dist/components/chart-area-stacked.d.ts +1 -1
  62. package/dist/components/chart-area-stacked.d.ts.map +1 -1
  63. package/dist/components/chart-area-stacked.js +4 -4
  64. package/dist/components/chart-axis.d.ts +14 -1
  65. package/dist/components/chart-axis.d.ts.map +1 -1
  66. package/dist/components/chart-axis.js +11 -1
  67. package/dist/components/chart-bar-horizontal.d.ts +2 -2
  68. package/dist/components/chart-bar-horizontal.d.ts.map +1 -1
  69. package/dist/components/chart-bar-horizontal.js +14 -5
  70. package/dist/components/chart-bar-multiple.d.ts +1 -1
  71. package/dist/components/chart-bar-multiple.d.ts.map +1 -1
  72. package/dist/components/chart-bar-multiple.js +3 -3
  73. package/dist/components/chart-bar-stacked.d.ts +1 -1
  74. package/dist/components/chart-bar-stacked.d.ts.map +1 -1
  75. package/dist/components/chart-bar-stacked.js +3 -3
  76. package/dist/components/chart-card.d.ts +17 -6
  77. package/dist/components/chart-card.d.ts.map +1 -1
  78. package/dist/components/chart-card.js +11 -4
  79. package/dist/components/chart-date-range-control.d.ts +18 -9
  80. package/dist/components/chart-date-range-control.d.ts.map +1 -1
  81. package/dist/components/chart-date-range-control.js +43 -47
  82. package/dist/components/chart-sparkline.d.ts +4 -3
  83. package/dist/components/chart-sparkline.d.ts.map +1 -1
  84. package/dist/components/chart-sparkline.js +6 -5
  85. package/dist/components/check-counts.d.ts +44 -0
  86. package/dist/components/check-counts.d.ts.map +1 -0
  87. package/dist/components/check-counts.js +51 -0
  88. package/dist/components/checkbox.d.ts +39 -11
  89. package/dist/components/checkbox.d.ts.map +1 -1
  90. package/dist/components/checkbox.js +38 -11
  91. package/dist/components/choicebox.d.ts +96 -0
  92. package/dist/components/choicebox.d.ts.map +1 -0
  93. package/dist/components/choicebox.js +113 -0
  94. package/dist/components/clearable-input.d.ts +57 -0
  95. package/dist/components/clearable-input.d.ts.map +1 -0
  96. package/dist/components/clearable-input.js +58 -0
  97. package/dist/components/code-block.d.ts +77 -17
  98. package/dist/components/code-block.d.ts.map +1 -1
  99. package/dist/components/code-block.js +85 -15
  100. package/dist/components/collapsible.d.ts +27 -10
  101. package/dist/components/collapsible.d.ts.map +1 -1
  102. package/dist/components/collapsible.js +24 -9
  103. package/dist/components/collection-filters.d.ts +3 -1
  104. package/dist/components/collection-filters.d.ts.map +1 -1
  105. package/dist/components/collection-filters.js +7 -9
  106. package/dist/components/collection-page.d.ts +31 -10
  107. package/dist/components/collection-page.d.ts.map +1 -1
  108. package/dist/components/collection-page.js +14 -13
  109. package/dist/components/collection-search.d.ts +32 -10
  110. package/dist/components/collection-search.d.ts.map +1 -1
  111. package/dist/components/collection-search.js +24 -13
  112. package/dist/components/collection-sort.d.ts.map +1 -1
  113. package/dist/components/collection-sort.js +2 -2
  114. package/dist/components/collection-toolbar.d.ts +5 -2
  115. package/dist/components/collection-toolbar.d.ts.map +1 -1
  116. package/dist/components/collection-toolbar.js +7 -4
  117. package/dist/components/combobox.d.ts +97 -22
  118. package/dist/components/combobox.d.ts.map +1 -1
  119. package/dist/components/combobox.js +80 -28
  120. package/dist/components/command.d.ts +34 -21
  121. package/dist/components/command.d.ts.map +1 -1
  122. package/dist/components/command.js +33 -25
  123. package/dist/components/confirm-dialog.d.ts +23 -6
  124. package/dist/components/confirm-dialog.d.ts.map +1 -1
  125. package/dist/components/confirm-dialog.js +32 -13
  126. package/dist/components/context-menu.d.ts +14 -10
  127. package/dist/components/context-menu.d.ts.map +1 -1
  128. package/dist/components/context-menu.js +17 -12
  129. package/dist/components/copy-button.d.ts +5 -2
  130. package/dist/components/copy-button.d.ts.map +1 -1
  131. package/dist/components/copy-button.js +16 -8
  132. package/dist/components/data-table.d.ts +120 -54
  133. package/dist/components/data-table.d.ts.map +1 -1
  134. package/dist/components/data-table.js +163 -92
  135. package/dist/components/desktop-shell.d.ts +4 -2
  136. package/dist/components/desktop-shell.d.ts.map +1 -1
  137. package/dist/components/desktop-shell.js +3 -3
  138. package/dist/components/dialog.d.ts +63 -24
  139. package/dist/components/dialog.d.ts.map +1 -1
  140. package/dist/components/dialog.js +52 -31
  141. package/dist/components/disclosure-row.d.ts +1 -2
  142. package/dist/components/disclosure-row.d.ts.map +1 -1
  143. package/dist/components/disclosure-row.js +1 -2
  144. package/dist/components/disclosure.d.ts +1 -1
  145. package/dist/components/disclosure.js +1 -1
  146. package/dist/components/drawer.d.ts +33 -12
  147. package/dist/components/drawer.d.ts.map +1 -1
  148. package/dist/components/drawer.js +47 -35
  149. package/dist/components/dropdown-menu.d.ts +59 -14
  150. package/dist/components/dropdown-menu.d.ts.map +1 -1
  151. package/dist/components/dropdown-menu.js +58 -26
  152. package/dist/components/empty-state.d.ts +35 -12
  153. package/dist/components/empty-state.d.ts.map +1 -1
  154. package/dist/components/empty-state.js +35 -17
  155. package/dist/components/entry-scene.d.ts +4 -4
  156. package/dist/components/entry-scene.d.ts.map +1 -1
  157. package/dist/components/entry-scene.js +2 -2
  158. package/dist/components/field.d.ts +44 -14
  159. package/dist/components/field.d.ts.map +1 -1
  160. package/dist/components/field.js +49 -20
  161. package/dist/components/file-diff.d.ts.map +1 -1
  162. package/dist/components/file-diff.js +3 -4
  163. package/dist/components/form-dialog.d.ts +7 -7
  164. package/dist/components/form-dialog.d.ts.map +1 -1
  165. package/dist/components/form-dialog.js +13 -11
  166. package/dist/components/form-drawer.d.ts +10 -7
  167. package/dist/components/form-drawer.d.ts.map +1 -1
  168. package/dist/components/form-drawer.js +11 -10
  169. package/dist/components/gauge.d.ts +64 -0
  170. package/dist/components/gauge.d.ts.map +1 -0
  171. package/dist/components/gauge.js +73 -0
  172. package/dist/components/hover-card.d.ts +72 -14
  173. package/dist/components/hover-card.d.ts.map +1 -1
  174. package/dist/components/hover-card.js +51 -17
  175. package/dist/components/identity-mark.d.ts +8 -1
  176. package/dist/components/identity-mark.d.ts.map +1 -1
  177. package/dist/components/identity-mark.js +31 -5
  178. package/dist/components/input-group.d.ts +44 -21
  179. package/dist/components/input-group.d.ts.map +1 -1
  180. package/dist/components/input-group.js +74 -27
  181. package/dist/components/input.d.ts +64 -12
  182. package/dist/components/input.d.ts.map +1 -1
  183. package/dist/components/input.js +51 -15
  184. package/dist/components/item.d.ts +37 -15
  185. package/dist/components/item.d.ts.map +1 -1
  186. package/dist/components/item.js +41 -22
  187. package/dist/components/kbd.d.ts +49 -13
  188. package/dist/components/kbd.d.ts.map +1 -1
  189. package/dist/components/kbd.js +115 -16
  190. package/dist/components/kpi-card.d.ts +12 -4
  191. package/dist/components/kpi-card.d.ts.map +1 -1
  192. package/dist/components/kpi-card.js +13 -4
  193. package/dist/components/label.d.ts +17 -10
  194. package/dist/components/label.d.ts.map +1 -1
  195. package/dist/components/label.js +18 -11
  196. package/dist/components/library-shelf.d.ts +6 -6
  197. package/dist/components/library-shelf.d.ts.map +1 -1
  198. package/dist/components/library-shelf.js +5 -5
  199. package/dist/components/load-more-button.d.ts +35 -0
  200. package/dist/components/load-more-button.d.ts.map +1 -0
  201. package/dist/components/load-more-button.js +26 -0
  202. package/dist/components/message-notice.d.ts +8 -13
  203. package/dist/components/message-notice.d.ts.map +1 -1
  204. package/dist/components/message-notice.js +25 -22
  205. package/dist/components/middle-truncate.d.ts +33 -0
  206. package/dist/components/middle-truncate.d.ts.map +1 -0
  207. package/dist/components/middle-truncate.js +72 -0
  208. package/dist/components/multi-select.d.ts +94 -0
  209. package/dist/components/multi-select.d.ts.map +1 -0
  210. package/dist/components/multi-select.js +132 -0
  211. package/dist/components/native-select.d.ts +37 -13
  212. package/dist/components/native-select.d.ts.map +1 -1
  213. package/dist/components/native-select.js +27 -11
  214. package/dist/components/notice-banner.d.ts +17 -10
  215. package/dist/components/notice-banner.d.ts.map +1 -1
  216. package/dist/components/notice-banner.js +12 -11
  217. package/dist/components/numbered-steps.d.ts +10 -4
  218. package/dist/components/numbered-steps.d.ts.map +1 -1
  219. package/dist/components/numbered-steps.js +9 -7
  220. package/dist/components/object-page.d.ts +40 -14
  221. package/dist/components/object-page.d.ts.map +1 -1
  222. package/dist/components/object-page.js +12 -10
  223. package/dist/components/object-workspace.d.ts +29 -10
  224. package/dist/components/object-workspace.d.ts.map +1 -1
  225. package/dist/components/object-workspace.js +18 -8
  226. package/dist/components/overview-page.d.ts +2 -2
  227. package/dist/components/overview-page.d.ts.map +1 -1
  228. package/dist/components/overview-page.js +8 -7
  229. package/dist/components/page-actions.d.ts +28 -8
  230. package/dist/components/page-actions.d.ts.map +1 -1
  231. package/dist/components/page-actions.js +22 -25
  232. package/dist/components/page-header.d.ts +11 -4
  233. package/dist/components/page-header.d.ts.map +1 -1
  234. package/dist/components/page-header.js +10 -5
  235. package/dist/components/pending-label.d.ts +6 -5
  236. package/dist/components/pending-label.d.ts.map +1 -1
  237. package/dist/components/pending-label.js +14 -6
  238. package/dist/components/picture-label.d.ts +2 -2
  239. package/dist/components/picture-label.d.ts.map +1 -1
  240. package/dist/components/picture-label.js +3 -3
  241. package/dist/components/portal-sidebar-frame.d.ts +9 -21
  242. package/dist/components/portal-sidebar-frame.d.ts.map +1 -1
  243. package/dist/components/portal-sidebar-frame.js +13 -27
  244. package/dist/components/progress-checklist.d.ts +46 -0
  245. package/dist/components/progress-checklist.d.ts.map +1 -0
  246. package/dist/components/progress-checklist.js +46 -0
  247. package/dist/components/progress.d.ts +48 -8
  248. package/dist/components/progress.d.ts.map +1 -1
  249. package/dist/components/progress.js +92 -16
  250. package/dist/components/prose.d.ts +19 -0
  251. package/dist/components/prose.d.ts.map +1 -0
  252. package/dist/components/prose.js +29 -0
  253. package/dist/components/radio-group.d.ts +51 -12
  254. package/dist/components/radio-group.d.ts.map +1 -1
  255. package/dist/components/radio-group.js +42 -12
  256. package/dist/components/reading-column.js +1 -1
  257. package/dist/components/record-identity.d.ts +6 -5
  258. package/dist/components/record-identity.d.ts.map +1 -1
  259. package/dist/components/record-identity.js +12 -3
  260. package/dist/components/record-list.d.ts +117 -27
  261. package/dist/components/record-list.d.ts.map +1 -1
  262. package/dist/components/record-list.js +185 -55
  263. package/dist/components/record-time.d.ts +20 -7
  264. package/dist/components/record-time.d.ts.map +1 -1
  265. package/dist/components/record-time.js +38 -19
  266. package/dist/components/resizable.d.ts +7 -5
  267. package/dist/components/resizable.d.ts.map +1 -1
  268. package/dist/components/resizable.js +6 -6
  269. package/dist/components/scope-switcher.d.ts +2 -0
  270. package/dist/components/scope-switcher.d.ts.map +1 -1
  271. package/dist/components/scope-switcher.js +2 -2
  272. package/dist/components/scroll-area.d.ts +47 -11
  273. package/dist/components/scroll-area.d.ts.map +1 -1
  274. package/dist/components/scroll-area.js +72 -14
  275. package/dist/components/segmented-control.d.ts +82 -0
  276. package/dist/components/segmented-control.d.ts.map +1 -0
  277. package/dist/components/segmented-control.js +90 -0
  278. package/dist/components/select.d.ts +55 -15
  279. package/dist/components/select.d.ts.map +1 -1
  280. package/dist/components/select.js +62 -19
  281. package/dist/components/separator.d.ts +12 -5
  282. package/dist/components/separator.d.ts.map +1 -1
  283. package/dist/components/separator.js +11 -6
  284. package/dist/components/settings-facts.d.ts +34 -14
  285. package/dist/components/settings-facts.d.ts.map +1 -1
  286. package/dist/components/settings-facts.js +21 -12
  287. package/dist/components/settings-page.d.ts +4 -4
  288. package/dist/components/settings-page.js +5 -5
  289. package/dist/components/settings-section.d.ts +160 -34
  290. package/dist/components/settings-section.d.ts.map +1 -1
  291. package/dist/components/settings-section.js +92 -39
  292. package/dist/components/sheet.d.ts +47 -16
  293. package/dist/components/sheet.d.ts.map +1 -1
  294. package/dist/components/sheet.js +40 -19
  295. package/dist/components/sidebar-sections.d.ts +16 -12
  296. package/dist/components/sidebar-sections.d.ts.map +1 -1
  297. package/dist/components/sidebar-sections.js +30 -15
  298. package/dist/components/sidebar.d.ts +117 -21
  299. package/dist/components/sidebar.d.ts.map +1 -1
  300. package/dist/components/sidebar.js +180 -55
  301. package/dist/components/skeleton.d.ts +54 -8
  302. package/dist/components/skeleton.d.ts.map +1 -1
  303. package/dist/components/skeleton.js +44 -9
  304. package/dist/components/slider.d.ts +53 -11
  305. package/dist/components/slider.d.ts.map +1 -1
  306. package/dist/components/slider.js +71 -17
  307. package/dist/components/snippet.d.ts +57 -0
  308. package/dist/components/snippet.d.ts.map +1 -0
  309. package/dist/components/snippet.js +60 -0
  310. package/dist/components/sonner.d.ts +13 -14
  311. package/dist/components/sonner.d.ts.map +1 -1
  312. package/dist/components/sonner.js +25 -22
  313. package/dist/components/status-badge.d.ts +37 -11
  314. package/dist/components/status-badge.d.ts.map +1 -1
  315. package/dist/components/status-badge.js +63 -15
  316. package/dist/components/status-dot.d.ts +42 -11
  317. package/dist/components/status-dot.d.ts.map +1 -1
  318. package/dist/components/status-dot.js +40 -11
  319. package/dist/components/switch.d.ts +43 -12
  320. package/dist/components/switch.d.ts.map +1 -1
  321. package/dist/components/switch.js +42 -12
  322. package/dist/components/table-card.d.ts +21 -12
  323. package/dist/components/table-card.d.ts.map +1 -1
  324. package/dist/components/table-card.js +17 -9
  325. package/dist/components/table-layouts.d.ts +40 -8
  326. package/dist/components/table-layouts.d.ts.map +1 -1
  327. package/dist/components/table-layouts.js +52 -8
  328. package/dist/components/table-value.d.ts +4 -3
  329. package/dist/components/table-value.d.ts.map +1 -1
  330. package/dist/components/table-value.js +6 -5
  331. package/dist/components/table.d.ts +62 -25
  332. package/dist/components/table.d.ts.map +1 -1
  333. package/dist/components/table.js +62 -26
  334. package/dist/components/tabs.d.ts +86 -17
  335. package/dist/components/tabs.d.ts.map +1 -1
  336. package/dist/components/tabs.js +93 -22
  337. package/dist/components/text-link.d.ts +4 -1
  338. package/dist/components/text-link.d.ts.map +1 -1
  339. package/dist/components/text-link.js +4 -1
  340. package/dist/components/text-with-copy-button.d.ts +30 -0
  341. package/dist/components/text-with-copy-button.d.ts.map +1 -0
  342. package/dist/components/text-with-copy-button.js +32 -0
  343. package/dist/components/textarea.d.ts +40 -10
  344. package/dist/components/textarea.d.ts.map +1 -1
  345. package/dist/components/textarea.js +50 -11
  346. package/dist/components/theme-mode-switcher.d.ts +18 -1
  347. package/dist/components/theme-mode-switcher.d.ts.map +1 -1
  348. package/dist/components/theme-mode-switcher.js +20 -2
  349. package/dist/components/toggle-group.d.ts +28 -8
  350. package/dist/components/toggle-group.d.ts.map +1 -1
  351. package/dist/components/toggle-group.js +12 -6
  352. package/dist/components/toggle.d.ts +6 -3
  353. package/dist/components/toggle.d.ts.map +1 -1
  354. package/dist/components/toggle.js +10 -7
  355. package/dist/components/tooltip.d.ts +43 -12
  356. package/dist/components/tooltip.d.ts.map +1 -1
  357. package/dist/components/tooltip.js +45 -16
  358. package/dist/components/trend-badge.js +1 -1
  359. package/dist/components/update-card.js +1 -1
  360. package/dist/eslint/index.js +82 -0
  361. package/dist/eslint/rules/badge-is-static-label.js +93 -0
  362. package/dist/eslint/rules/button-icon-slot.js +102 -0
  363. package/dist/eslint/rules/button-link-for-navigation.js +146 -0
  364. package/dist/eslint/rules/button-no-aria-label-with-text.js +75 -0
  365. package/dist/eslint/rules/button-type-roles-only-in-buttons.js +83 -0
  366. package/dist/eslint/rules/choice-control-has-a-name.js +98 -0
  367. package/dist/eslint/rules/choice-group-in-fieldset.js +86 -0
  368. package/dist/eslint/rules/command-shortcut-single-owner.js +118 -0
  369. package/dist/eslint/rules/confirm-dialog-words.js +63 -0
  370. package/dist/eslint/rules/data-table-one-unsized-column.js +96 -0
  371. package/dist/eslint/rules/destructive-action-placement.js +69 -0
  372. package/dist/eslint/rules/disabled-choice-says-why.js +71 -0
  373. package/dist/eslint/rules/ellipsis-opens-a-dialog.js +110 -0
  374. package/dist/eslint/rules/input-group-owners.js +155 -0
  375. package/dist/eslint/rules/kbd-children-no-modifier-text.js +128 -0
  376. package/dist/eslint/rules/literal-text.js +41 -0
  377. package/dist/eslint/rules/middle-truncate-placement.js +69 -0
  378. package/dist/eslint/rules/no-bare-button-label.js +55 -0
  379. package/dist/eslint/rules/no-control-in-read-only-surface.js +106 -0
  380. package/dist/eslint/rules/no-hand-built-busy.js +104 -0
  381. package/dist/eslint/rules/no-hand-built-theme-picker.js +144 -0
  382. package/dist/eslint/rules/no-hand-built-tile.js +41 -0
  383. package/dist/eslint/rules/no-hand-formatted-date.js +87 -0
  384. package/dist/eslint/rules/no-hand-stacked-avatars.js +67 -0
  385. package/dist/eslint/rules/no-nested-collapse.js +60 -0
  386. package/dist/eslint/rules/no-search-in-form.js +47 -0
  387. package/dist/eslint/rules/no-shadcn-appearance-override.js +2 -16
  388. package/dist/eslint/rules/no-shell-prompt-in-snippet.js +74 -0
  389. package/dist/eslint/rules/no-stock-error-words.js +56 -0
  390. package/dist/eslint/rules/no-tooltip-around-labelled-field.js +72 -0
  391. package/dist/eslint/rules/no-weight-class-on-strong-in-type-role.js +57 -0
  392. package/dist/eslint/rules/one-submenu-level.js +43 -0
  393. package/dist/eslint/rules/one-toast-per-flow.js +91 -0
  394. package/dist/eslint/rules/option-count.js +167 -0
  395. package/dist/eslint/rules/page-command-link-not-render.js +43 -0
  396. package/dist/eslint/rules/progress-needs-name.js +57 -0
  397. package/dist/eslint/rules/settings-limit-in-footer.js +94 -0
  398. package/dist/eslint/rules/skeleton-in-region.js +80 -0
  399. package/dist/eslint/rules/sortable-heading-uses-sort.js +79 -0
  400. package/dist/eslint/rules/table-cell-inset-owner.js +107 -0
  401. package/dist/eslint/rules/tabs-selection-is-controlled.js +47 -0
  402. package/dist/eslint/rules/tooltip-only-arrow.js +38 -0
  403. package/dist/eslint/rules/tooltip-trigger-accessible-name.js +114 -0
  404. package/dist/eslint/strict-design-system.js +21 -7
  405. package/dist/index.d.ts +13 -13
  406. package/dist/index.d.ts.map +1 -1
  407. package/dist/index.js +12 -13
  408. package/dist/internal/assistant-ui/attachment.d.ts.map +1 -1
  409. package/dist/internal/assistant-ui/attachment.js +2 -2
  410. package/dist/internal/assistant-ui/reasoning.d.ts +1 -1
  411. package/dist/internal/assistant-ui/reasoning.d.ts.map +1 -1
  412. package/dist/internal/assistant-ui/tool-fallback.js +1 -1
  413. package/dist/internal/assistant-ui/tool-group.d.ts +1 -1
  414. package/dist/internal/assistant-ui/tool-group.d.ts.map +1 -1
  415. package/dist/internal/assistant-ui/tooltip-icon-button.d.ts +2 -3
  416. package/dist/internal/assistant-ui/tooltip-icon-button.d.ts.map +1 -1
  417. package/dist/internal/assistant-ui/tooltip-icon-button.js +1 -1
  418. package/dist/internal/catalog-list.js +2 -2
  419. package/dist/internal/collection-note.d.ts +23 -0
  420. package/dist/internal/collection-note.d.ts.map +1 -0
  421. package/dist/internal/collection-note.js +17 -0
  422. package/dist/internal/collection-rail.d.ts +57 -0
  423. package/dist/internal/collection-rail.d.ts.map +1 -0
  424. package/dist/internal/collection-rail.js +33 -0
  425. package/dist/internal/company-logo.d.ts +18 -0
  426. package/dist/internal/company-logo.d.ts.map +1 -0
  427. package/dist/internal/company-logo.js +14 -0
  428. package/dist/internal/control-name.d.ts +12 -0
  429. package/dist/internal/control-name.d.ts.map +1 -0
  430. package/dist/internal/control-name.js +1 -0
  431. package/dist/internal/diff-line.d.ts +9 -0
  432. package/dist/internal/diff-line.d.ts.map +1 -0
  433. package/dist/internal/diff-line.js +8 -0
  434. package/dist/internal/empty.d.ts +8 -4
  435. package/dist/internal/empty.d.ts.map +1 -1
  436. package/dist/internal/empty.js +14 -8
  437. package/dist/internal/field-frame.d.ts +33 -0
  438. package/dist/internal/field-frame.d.ts.map +1 -0
  439. package/dist/internal/field-frame.js +20 -0
  440. package/dist/internal/final-focus.d.ts +8 -0
  441. package/dist/internal/final-focus.d.ts.map +1 -0
  442. package/dist/internal/final-focus.js +10 -0
  443. package/dist/internal/footer-strip.d.ts.map +1 -1
  444. package/dist/internal/footer-strip.js +3 -1
  445. package/dist/internal/form-submit.d.ts +9 -5
  446. package/dist/internal/form-submit.d.ts.map +1 -1
  447. package/dist/internal/form-submit.js +31 -2
  448. package/dist/internal/input-box.d.ts +26 -0
  449. package/dist/internal/input-box.d.ts.map +1 -0
  450. package/dist/internal/input-box.js +59 -0
  451. package/dist/internal/menu-surface.d.ts +10 -3
  452. package/dist/internal/menu-surface.d.ts.map +1 -1
  453. package/dist/internal/menu-surface.js +10 -3
  454. package/dist/internal/notice.d.ts +25 -0
  455. package/dist/internal/notice.d.ts.map +1 -0
  456. package/dist/internal/notice.js +33 -0
  457. package/dist/internal/overlay-stem.d.ts +5 -0
  458. package/dist/internal/overlay-stem.d.ts.map +1 -0
  459. package/dist/internal/overlay-stem.js +7 -0
  460. package/dist/internal/phone-sheet.d.ts +4 -0
  461. package/dist/internal/phone-sheet.d.ts.map +1 -0
  462. package/dist/internal/phone-sheet.js +2 -0
  463. package/dist/internal/present-content.d.ts +3 -0
  464. package/dist/internal/present-content.d.ts.map +1 -0
  465. package/dist/internal/present-content.js +1 -0
  466. package/dist/internal/record-menu-items.d.ts +4 -3
  467. package/dist/internal/record-menu-items.d.ts.map +1 -1
  468. package/dist/internal/record-menu-items.js +21 -6
  469. package/dist/internal/settings-save.d.ts +9 -2
  470. package/dist/internal/settings-save.d.ts.map +1 -1
  471. package/dist/internal/settings-save.js +9 -6
  472. package/dist/internal/table-compact-line.d.ts +38 -0
  473. package/dist/internal/table-compact-line.d.ts.map +1 -0
  474. package/dist/internal/table-compact-line.js +35 -0
  475. package/dist/internal/title-when-cut.d.ts +8 -0
  476. package/dist/internal/title-when-cut.d.ts.map +1 -0
  477. package/dist/internal/title-when-cut.js +22 -0
  478. package/dist/internal/unsaved-guard.d.ts +18 -0
  479. package/dist/internal/unsaved-guard.d.ts.map +1 -0
  480. package/dist/internal/unsaved-guard.js +31 -0
  481. package/dist/lib/merge-tokens.generated.js +2 -2
  482. package/dist/showcase/foundations.d.ts.map +1 -1
  483. package/dist/showcase/foundations.js +10 -2
  484. package/dist/showcase/specimens/actions.d.ts +2 -0
  485. package/dist/showcase/specimens/actions.d.ts.map +1 -1
  486. package/dist/showcase/specimens/actions.js +30 -5
  487. package/dist/showcase/specimens/charts.d.ts.map +1 -1
  488. package/dist/showcase/specimens/charts.js +22 -13
  489. package/dist/showcase/specimens/choices.d.ts +14 -3
  490. package/dist/showcase/specimens/choices.d.ts.map +1 -1
  491. package/dist/showcase/specimens/choices.js +93 -13
  492. package/dist/showcase/specimens/compositions.d.ts.map +1 -1
  493. package/dist/showcase/specimens/compositions.js +136 -23
  494. package/dist/showcase/specimens/data.d.ts +12 -7
  495. package/dist/showcase/specimens/data.d.ts.map +1 -1
  496. package/dist/showcase/specimens/data.js +139 -35
  497. package/dist/showcase/specimens/feedback.d.ts +2 -0
  498. package/dist/showcase/specimens/feedback.d.ts.map +1 -1
  499. package/dist/showcase/specimens/feedback.js +39 -15
  500. package/dist/showcase/specimens/index.d.ts +20 -12
  501. package/dist/showcase/specimens/index.d.ts.map +1 -1
  502. package/dist/showcase/specimens/inputs.d.ts +4 -2
  503. package/dist/showcase/specimens/inputs.d.ts.map +1 -1
  504. package/dist/showcase/specimens/inputs.js +62 -19
  505. package/dist/showcase/specimens/menus.d.ts +0 -1
  506. package/dist/showcase/specimens/menus.d.ts.map +1 -1
  507. package/dist/showcase/specimens/menus.js +47 -17
  508. package/dist/showcase/specimens/navigation.d.ts +6 -4
  509. package/dist/showcase/specimens/navigation.d.ts.map +1 -1
  510. package/dist/showcase/specimens/navigation.js +61 -32
  511. package/dist/showcase/specimens/other.d.ts +1 -3
  512. package/dist/showcase/specimens/other.d.ts.map +1 -1
  513. package/dist/showcase/specimens/other.js +17 -13
  514. package/dist/showcase/specimens/status.d.ts +4 -1
  515. package/dist/showcase/specimens/status.d.ts.map +1 -1
  516. package/dist/showcase/specimens/status.js +64 -11
  517. package/dist/showcase/usage.generated.d.ts +1 -1
  518. package/dist/showcase/usage.generated.d.ts.map +1 -1
  519. package/dist/showcase/usage.generated.js +632 -544
  520. package/dist/standalone/index.js +1 -1
  521. package/dist/styles/components.css +13 -13
  522. package/dist/styles/globals.css +25 -0
  523. package/package.json +2 -3
  524. package/dist/components/approval-row.d.ts +0 -34
  525. package/dist/components/approval-row.d.ts.map +0 -1
  526. package/dist/components/approval-row.js +0 -27
  527. package/dist/components/channel-view.d.ts +0 -112
  528. package/dist/components/channel-view.d.ts.map +0 -1
  529. package/dist/components/channel-view.js +0 -84
  530. package/dist/components/dashboard-shell.d.ts +0 -51
  531. package/dist/components/dashboard-shell.d.ts.map +0 -1
  532. package/dist/components/dashboard-shell.js +0 -89
  533. package/dist/components/direction.d.ts +0 -2
  534. package/dist/components/direction.d.ts.map +0 -1
  535. package/dist/components/direction.js +0 -1
  536. package/dist/components/expandable-block.d.ts +0 -20
  537. package/dist/components/expandable-block.d.ts.map +0 -1
  538. package/dist/components/expandable-block.js +0 -32
  539. package/dist/components/form.d.ts +0 -42
  540. package/dist/components/form.d.ts.map +0 -1
  541. package/dist/components/form.js +0 -91
  542. package/dist/components/label-system.d.ts +0 -17
  543. package/dist/components/label-system.d.ts.map +0 -1
  544. package/dist/components/label-system.js +0 -21
  545. package/dist/components/memory-item.d.ts +0 -21
  546. package/dist/components/memory-item.d.ts.map +0 -1
  547. package/dist/components/memory-item.js +0 -18
  548. package/dist/components/menubar.d.ts +0 -43
  549. package/dist/components/menubar.d.ts.map +0 -1
  550. package/dist/components/menubar.js +0 -69
  551. package/dist/components/navigation-menu.d.ts +0 -26
  552. package/dist/components/navigation-menu.d.ts.map +0 -1
  553. package/dist/components/navigation-menu.js +0 -45
  554. package/dist/components/pagination.d.ts +0 -33
  555. package/dist/components/pagination.d.ts.map +0 -1
  556. package/dist/components/pagination.js +0 -41
  557. package/dist/components/plugin-slot-boundary.d.ts +0 -42
  558. package/dist/components/plugin-slot-boundary.d.ts.map +0 -1
  559. package/dist/components/plugin-slot-boundary.js +0 -41
  560. package/dist/components/status-row.d.ts +0 -32
  561. package/dist/components/status-row.d.ts.map +0 -1
  562. package/dist/components/status-row.js +0 -35
@@ -0,0 +1,69 @@
1
+ /*
2
+ * Standard tool checked first: a `no-restricted-syntax` descendant selector can
3
+ * match `MiddleTruncate` under an element by name, but not that the name is the
4
+ * design system's import, that the class sits in a `cn(...)` call or a
5
+ * same-file table, nor that the nearest control carries an `aria-label`
6
+ * without a second selector per spelling.
7
+ */
8
+ import { openingElementClassUtilities } from "./jsx-class-utility.js";
9
+ import { renderedImports } from "./rendered-import.js";
10
+
11
+ const FROM_DESIGN_SYSTEM = /^(@nadicodeai\/ui(\/|$)|@\/|\.)/;
12
+ const CONTROLS = new Set(["Button", "ButtonLink", "TextLink"]);
13
+ const HOST_CONTROLS = new Set(["a", "button"]);
14
+ const NAMING_ATTRIBUTES = new Set(["aria-label", "aria-labelledby"]);
15
+ const TRUNCATING = /^(truncate|text-ellipsis|overflow-ellipsis|line-clamp-(?!none$).+)$/;
16
+
17
+ const withoutVariants = (utility) => utility.split(":").pop().replace(/^!/, "");
18
+
19
+ const namesItself = (openingElement) =>
20
+ openingElement.attributes.some(
21
+ (attribute) => attribute.type === "JSXAttribute" && NAMING_ATTRIBUTES.has(attribute.name.name),
22
+ );
23
+
24
+ export const middleTruncatePlacement = {
25
+ meta: {
26
+ type: "problem",
27
+ docs: {
28
+ description: "MiddleTruncate cuts its own ellipsis and a control around it carries the name.",
29
+ },
30
+ schema: [],
31
+ messages: {
32
+ "cuts-its-own-ellipsis":
33
+ "MiddleTruncate cuts its own ellipsis: take it out of the element that also truncates. Inside a control, give the control an aria-label that names its target, since the cut value is not a name.",
34
+ },
35
+ },
36
+ create(context) {
37
+ const importOf = renderedImports(context);
38
+ const designSystemExport = (elementName) => {
39
+ const imported = importOf(elementName);
40
+ return imported && FROM_DESIGN_SYSTEM.test(imported.source) ? imported.name : null;
41
+ };
42
+ const isControl = ({ name }) =>
43
+ CONTROLS.has(designSystemExport(name)) ||
44
+ (name.type === "JSXIdentifier" && HOST_CONTROLS.has(name.name) && !importOf(name));
45
+ const truncates = (openingElement) =>
46
+ openingElementClassUtilities(openingElement, context).some((utility) =>
47
+ TRUNCATING.test(withoutVariants(utility)),
48
+ );
49
+
50
+ return {
51
+ JSXElement(node) {
52
+ if (designSystemExport(node.openingElement.name) !== "MiddleTruncate") return;
53
+ let nearestControl = null;
54
+ for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
55
+ if (ancestor.type !== "JSXElement") continue;
56
+ const { openingElement } = ancestor;
57
+ if (truncates(openingElement)) {
58
+ context.report({ node: node.openingElement, messageId: "cuts-its-own-ellipsis" });
59
+ return;
60
+ }
61
+ if (!nearestControl && isControl(openingElement)) nearestControl = openingElement;
62
+ }
63
+ if (nearestControl && !namesItself(nearestControl)) {
64
+ context.report({ node: node.openingElement, messageId: "cuts-its-own-ellipsis" });
65
+ }
66
+ },
67
+ };
68
+ },
69
+ };
@@ -0,0 +1,55 @@
1
+ /*
2
+ * Standard tool checked first: `no-restricted-syntax` can match a `Button`
3
+ * whose text is "OK", but not whether that `Button` is the package's (an
4
+ * import binding, so a renamed or local one is judged correctly), nor read the
5
+ * same word off a form's `submit={{ label }}`.
6
+ */
7
+ import { keyName, childText, literalText } from "./literal-text.js";
8
+ import { renderedImports } from "./rendered-import.js";
9
+
10
+ const BUTTONS = new Set(["Button", "ButtonLink"]);
11
+ const BARE = /^(?:submit|ok|okay|confirm)[.!…]*$/i;
12
+ const FROM_DESIGN_SYSTEM = /^(@nadicodeai\/ui(\/|$)|@\/|\.)/;
13
+
14
+ export const noBareButtonLabel = {
15
+ meta: {
16
+ type: "problem",
17
+ docs: {
18
+ description:
19
+ "A button says its result as a verb and a noun, never a bare Submit, OK or Confirm.",
20
+ },
21
+ schema: [],
22
+ messages: {
23
+ "bare-label":
24
+ 'A button says its result: "Create customer", "Save changes", "Remove mailbox". "{{label}}" says nothing about what happens.',
25
+ },
26
+ },
27
+ create(context) {
28
+ const importOf = renderedImports(context);
29
+
30
+ function report(node, label) {
31
+ context.report({ node, messageId: "bare-label", data: { label } });
32
+ }
33
+
34
+ return {
35
+ JSXElement(node) {
36
+ const imported = importOf(node.openingElement.name);
37
+ if (!imported || !BUTTONS.has(imported.name) || !FROM_DESIGN_SYSTEM.test(imported.source)) {
38
+ return;
39
+ }
40
+ const label = childText(node);
41
+ if (label !== null && BARE.test(label)) report(node.openingElement, label);
42
+ },
43
+ JSXAttribute(node) {
44
+ if (node.name.name !== "submit" || node.value?.type !== "JSXExpressionContainer") return;
45
+ const { expression } = node.value;
46
+ if (expression.type !== "ObjectExpression") return;
47
+ for (const property of expression.properties) {
48
+ if (keyName(property) !== "label") continue;
49
+ const label = literalText(property.value);
50
+ if (label !== null && BARE.test(label.trim())) report(property.value, label.trim());
51
+ }
52
+ },
53
+ };
54
+ },
55
+ };
@@ -0,0 +1,106 @@
1
+ /*
2
+ * Standard tool checked first: a `no-restricted-syntax` selector can match a
3
+ * `Button` below `TooltipContent`, but not follow a renamed import to the part,
4
+ * count the links across a HoverCardContent's children and its summary's
5
+ * `action`.
6
+ */
7
+ import { renderedImports, walk } from "./rendered-import.js";
8
+
9
+ const OVERLAYS = new Set(["Tooltip", "HoverCard"]);
10
+ const CONTROLS = new Set(["Button", "ButtonLink", "TextLink", "Link", "a"]);
11
+ const NATIVE_CONTROLS = new Set(["input", "select", "textarea"]);
12
+ const HOVER_CARD_SUMMARY = "HoverCardSummary";
13
+
14
+ const hasAttribute = (openingElement, attributeName) =>
15
+ openingElement.attributes.some(
16
+ (attribute) => attribute.type === "JSXAttribute" && attribute.name.name === attributeName,
17
+ );
18
+
19
+ export const noControlInReadOnlySurface = {
20
+ meta: {
21
+ type: "problem",
22
+ docs: {
23
+ description:
24
+ "Tooltip text and a Choicebox tile are for reading; a HoverCard holds at most one link.",
25
+ },
26
+ schema: [],
27
+ messages: {
28
+ "read-only-surface":
29
+ "Tooltip text and a Choicebox tile are for reading: move the control outside. A HoverCard holds at most one link, through HoverCardSummary's `action`, and never a second Tooltip or HoverCard; for more, open a Popover, a menu or the record's page.",
30
+ },
31
+ },
32
+ create(context) {
33
+ const importOf = renderedImports(context);
34
+ const { visitorKeys } = context.sourceCode;
35
+
36
+ const nameOf = (element) => {
37
+ const { name } = element.openingElement;
38
+ if (name.type !== "JSXIdentifier" && name.type !== "JSXMemberExpression") return null;
39
+ if (name.type === "JSXIdentifier" && /^[a-z]/.test(name.name)) return name.name;
40
+ const imported = importOf(name);
41
+ if (!imported) return null;
42
+ return imported.name === "default" ? name.name : imported.name;
43
+ };
44
+
45
+ const isInteractive = (element, name) =>
46
+ CONTROLS.has(name) ||
47
+ NATIVE_CONTROLS.has(name) ||
48
+ hasAttribute(element.openingElement, "onClick") ||
49
+ hasAttribute(element.openingElement, "tabIndex");
50
+
51
+ function visitChildren(container, visit) {
52
+ for (const child of container.children) {
53
+ if (visit(child) === false) continue;
54
+ walk(child, visitorKeys, visit);
55
+ }
56
+ }
57
+
58
+ function checkTooltipContent(container) {
59
+ visitChildren(container, (child) => {
60
+ if (child.type !== "JSXElement") return true;
61
+ const name = nameOf(child);
62
+ if (!OVERLAYS.has(name) && !isInteractive(child, name)) return true;
63
+ context.report({ node: child.openingElement, messageId: "read-only-surface" });
64
+ return false;
65
+ });
66
+ }
67
+
68
+ function checkChoiceboxItem(container) {
69
+ visitChildren(container, (child) => {
70
+ if (child.type !== "JSXElement" || !isInteractive(child, nameOf(child))) return true;
71
+ context.report({ node: child.openingElement, messageId: "read-only-surface" });
72
+ return false;
73
+ });
74
+ }
75
+
76
+ function checkHoverCardContent(container) {
77
+ let links = 0;
78
+ visitChildren(container, (child) => {
79
+ if (child.type !== "JSXElement") return true;
80
+ const name = nameOf(child);
81
+ const { openingElement } = child;
82
+ if (OVERLAYS.has(name)) {
83
+ context.report({ node: openingElement, messageId: "read-only-surface" });
84
+ return false;
85
+ }
86
+ const isLink =
87
+ CONTROLS.has(name) ||
88
+ hasAttribute(openingElement, "onClick") ||
89
+ (name === HOVER_CARD_SUMMARY && hasAttribute(openingElement, "action"));
90
+ if (!isLink) return true;
91
+ links += 1;
92
+ if (links > 1) context.report({ node: openingElement, messageId: "read-only-surface" });
93
+ return true;
94
+ });
95
+ }
96
+
97
+ return {
98
+ JSXElement(node) {
99
+ const name = nameOf(node);
100
+ if (name === "TooltipContent") checkTooltipContent(node);
101
+ else if (name === "ChoiceboxGroupItem") checkChoiceboxItem(node);
102
+ else if (name === "HoverCardContent") checkHoverCardContent(node);
103
+ },
104
+ };
105
+ },
106
+ };
@@ -0,0 +1,104 @@
1
+ /*
2
+ * Standard tool checked first: `no-restricted-imports` refuses an import by
3
+ * module, but not the same export by module and file area (PendingLabel is
4
+ * allowed inside packages/ui), and `no-restricted-syntax` cannot join a
5
+ * PendingLabel element to the Button it sits under through a renamed import.
6
+ */
7
+ import { openingElementClassUtilities } from "./jsx-class-utility.js";
8
+ import { PACKAGE_BARREL, renderedImports } from "./rendered-import.js";
9
+
10
+ const PENDING_LABEL = "PendingLabel";
11
+ const PENDING_LABEL_MODULES = new Set([
12
+ PACKAGE_BARREL,
13
+ `${PACKAGE_BARREL}/components/pending-label`,
14
+ ]);
15
+ const BUTTONS = new Set(["Button", "ButtonLink"]);
16
+ const SPINNER_ICONS = new Set([
17
+ "Loader",
18
+ "LoaderIcon",
19
+ "Loader2",
20
+ "Loader2Icon",
21
+ "LoaderCircle",
22
+ "LoaderCircleIcon",
23
+ "LoaderPinwheel",
24
+ ]);
25
+ const ICON_MODULE = "lucide-react";
26
+ const SPIN = "animate-spin";
27
+ const FUNCTIONS = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
28
+
29
+ const PACKAGE_PATH = "/packages/ui/";
30
+ const PORTAL_PATH = "/apps/portal/";
31
+
32
+ const PENDING_LABEL_ONLY = new Set([PENDING_LABEL]);
33
+
34
+ const SOURCE_SIDE = { ImportSpecifier: "imported", ExportSpecifier: "local" };
35
+
36
+ const sourceName = (specifier) => {
37
+ const named = specifier[SOURCE_SIDE[specifier.type]];
38
+ return named?.name ?? named?.value;
39
+ };
40
+
41
+ const isSpinToken = (utility) => utility.split(":").pop().replace(/^!/, "") === SPIN;
42
+
43
+ function insideAButton(element, importOf) {
44
+ for (let current = element.parent; current; current = current.parent) {
45
+ if (FUNCTIONS.has(current.type)) return false;
46
+ if (current.type === "JSXElement" && BUTTONS.has(importOf(current.openingElement.name)?.name)) {
47
+ return true;
48
+ }
49
+ }
50
+ return false;
51
+ }
52
+
53
+ export const noHandBuiltBusy = {
54
+ meta: {
55
+ type: "problem",
56
+ docs: {
57
+ description:
58
+ "A working button takes loading and loadingLabel; no product hand-builds a busy label or a spinner.",
59
+ },
60
+ schema: [],
61
+ messages: {
62
+ "hand-built-busy":
63
+ "A working button takes loading and loadingLabel: it keeps its place and focus, says its loadingLabel and shows no spinner. A wait past a second is WaitingLine.",
64
+ },
65
+ },
66
+ create(context) {
67
+ const filename = context.filename.replaceAll("\\", "/");
68
+ const inPackage = filename.includes(PACKAGE_PATH);
69
+ const inPortal = filename.includes(PORTAL_PATH);
70
+ const importOf = renderedImports(context);
71
+
72
+ const report = (node) => context.report({ node, messageId: "hand-built-busy" });
73
+
74
+ function bannedFrom(source) {
75
+ if (source === ICON_MODULE) return SPINNER_ICONS;
76
+ if (!inPackage && PENDING_LABEL_MODULES.has(source)) return PENDING_LABEL_ONLY;
77
+ return null;
78
+ }
79
+
80
+ function checkImport(node) {
81
+ if (node.importKind === "type" || node.exportKind === "type") return;
82
+ const source = node.source?.value;
83
+ const bannedNames = bannedFrom(source);
84
+ if (!bannedNames) return;
85
+ for (const specifier of node.specifiers) {
86
+ if (specifier.importKind === "type" || specifier.exportKind === "type") continue;
87
+ if (bannedNames.has(sourceName(specifier))) report(specifier);
88
+ }
89
+ }
90
+
91
+ return {
92
+ ImportDeclaration: checkImport,
93
+ ExportNamedDeclaration: checkImport,
94
+ JSXElement(node) {
95
+ if (importOf(node.openingElement.name)?.name !== PENDING_LABEL) return;
96
+ if (insideAButton(node, importOf)) report(node.openingElement);
97
+ },
98
+ JSXOpeningElement(node) {
99
+ if (inPortal) return;
100
+ if (openingElementClassUtilities(node, context).some(isSpinToken)) report(node);
101
+ },
102
+ };
103
+ },
104
+ };
@@ -0,0 +1,144 @@
1
+ /*
2
+ * Standard tool checked first: a `no-restricted-syntax` selector can match a
3
+ * `Sun` element under a `RadioGroup`, but not the same icon named through a
4
+ * renamed import, nor an option table declared in the same file, and it cannot
5
+ * join the icons of two sibling Buttons.
6
+ */
7
+ import { renderedImports, walk } from "./rendered-import.js";
8
+
9
+ const ICON_MODULE = "lucide-react";
10
+ const PICKERS = new Set(["RadioGroup", "ToggleGroup", "SegmentedControl", "Switch"]);
11
+ const BUTTONS = new Set(["Button", "ButtonLink"]);
12
+ const THEME_ICONS = new Map([
13
+ ["Sun", "light"],
14
+ ["SunIcon", "light"],
15
+ ["Moon", "dark"],
16
+ ["MoonIcon", "dark"],
17
+ ["Monitor", "system"],
18
+ ["MonitorIcon", "system"],
19
+ ]);
20
+ const THEME_VALUES = new Set(["light", "dark"]);
21
+
22
+ const keyName = (property) => property.key.name ?? property.key.value;
23
+
24
+ function isThemeValue(literal) {
25
+ const { parent: container } = literal;
26
+ const parent = container.type === "JSXExpressionContainer" ? container.parent : container;
27
+ const isItemValue =
28
+ (parent.type === "JSXAttribute" && parent.name.name === "value") ||
29
+ (parent.type === "Property" && parent.value === literal && keyName(parent) === "value") ||
30
+ parent.type === "ArrayExpression" ||
31
+ parent.type === "CallExpression";
32
+ return isItemValue && THEME_VALUES.has(literal.value);
33
+ }
34
+
35
+ const isPropertyName = (identifier) => {
36
+ const { parent } = identifier;
37
+ return (
38
+ (parent.type === "Property" && parent.key === identifier && !parent.computed) ||
39
+ (parent.type === "MemberExpression" && parent.property === identifier && !parent.computed)
40
+ );
41
+ };
42
+
43
+ export const noHandBuiltThemePicker = {
44
+ meta: {
45
+ type: "problem",
46
+ docs: {
47
+ description:
48
+ "The appearance is chosen with ThemeModeSwitcher or ThemeModeMenu, never a hand-built picker.",
49
+ },
50
+ schema: [],
51
+ messages: {
52
+ "hand-built-theme-picker":
53
+ "The appearance is chosen with ThemeModeSwitcher on a settings page or ThemeModeMenu in the account menu; do not rebuild it.",
54
+ },
55
+ },
56
+ create(context) {
57
+ const { sourceCode } = context;
58
+ const { visitorKeys } = sourceCode;
59
+ const importOf = renderedImports(context);
60
+
61
+ const iconsByLocalName = new Map();
62
+ for (const statement of sourceCode.ast.body) {
63
+ if (statement.type !== "ImportDeclaration" || statement.importKind === "type") continue;
64
+ if (statement.source.value !== ICON_MODULE) continue;
65
+ for (const specifier of statement.specifiers) {
66
+ if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type") continue;
67
+ const kind = THEME_ICONS.get(specifier.imported.name ?? specifier.imported.value);
68
+ if (kind) iconsByLocalName.set(specifier.local.name, kind);
69
+ }
70
+ }
71
+
72
+ const sameFileConstant = (identifier) => {
73
+ for (let scope = sourceCode.getScope(identifier); scope; scope = scope.upper) {
74
+ const variable = scope.set.get(identifier.name);
75
+ if (!variable) continue;
76
+ const declarator = variable.defs[0]?.node;
77
+ const isConstant =
78
+ declarator?.type === "VariableDeclarator" && declarator.parent.kind === "const";
79
+ return isConstant ? declarator.init : null;
80
+ }
81
+ return null;
82
+ };
83
+
84
+ function collect(root, held, followNames) {
85
+ const visit = (child) => {
86
+ if (child.type === "JSXOpeningElement") {
87
+ const imported = importOf(child.name);
88
+ const kind = imported?.source === ICON_MODULE ? THEME_ICONS.get(imported.name) : null;
89
+ if (kind) held.icons.add(kind);
90
+ } else if (child.type === "Identifier" && !isPropertyName(child)) {
91
+ const kind = iconsByLocalName.get(child.name);
92
+ if (kind) held.icons.add(kind);
93
+ else if (followNames) {
94
+ const init = sameFileConstant(child);
95
+ if (init) collect(init, held, false);
96
+ }
97
+ } else if (child.type === "Literal" && isThemeValue(child)) {
98
+ held.values.add(child.value);
99
+ }
100
+ return true;
101
+ };
102
+ visit(root);
103
+ walk(root, visitorKeys, visit);
104
+ }
105
+
106
+ const holdsAThemeChoice = ({ icons, values }, needsBoth) =>
107
+ needsBoth ? icons.size >= 2 || values.size === 2 : icons.size >= 1 || values.size === 2;
108
+
109
+ const heldBy = (nodes) => {
110
+ const held = { icons: new Set(), values: new Set() };
111
+ for (const node of nodes) collect(node, held, true);
112
+ return held;
113
+ };
114
+
115
+ const buttonsIn = (parent) =>
116
+ parent.children.flatMap((child) => {
117
+ if (child.type === "JSXFragment") return buttonsIn(child);
118
+ const isButton =
119
+ child.type === "JSXElement" && BUTTONS.has(importOf(child.openingElement.name)?.name);
120
+ return isButton ? [child] : [];
121
+ });
122
+
123
+ const report = (node) => context.report({ node, messageId: "hand-built-theme-picker" });
124
+
125
+ return {
126
+ JSXElement(node) {
127
+ if (PICKERS.has(importOf(node.openingElement.name)?.name)) {
128
+ const held = heldBy([node]);
129
+ if (holdsAThemeChoice(held, false)) report(node.openingElement);
130
+ return;
131
+ }
132
+ const buttons = buttonsIn(node);
133
+ if (buttons.length < 2) return;
134
+ if (holdsAThemeChoice(heldBy(buttons), true)) report(node.openingElement);
135
+ },
136
+ JSXFragment(node) {
137
+ if (node.parent.type === "JSXElement" || node.parent.type === "JSXFragment") return;
138
+ const buttons = buttonsIn(node);
139
+ if (buttons.length < 2) return;
140
+ if (holdsAThemeChoice(heldBy(buttons), true)) report(node);
141
+ },
142
+ };
143
+ },
144
+ };
@@ -0,0 +1,41 @@
1
+ import { findBelow, renderedImports, walk } from "./rendered-import.js";
2
+
3
+ const FIELD_LABEL = "FieldLabel";
4
+ const FIELD = "Field";
5
+ const CONTROLS = new Set(["RadioGroupItem", "Checkbox"]);
6
+
7
+ export const noHandBuiltTile = {
8
+ meta: {
9
+ type: "problem",
10
+ docs: {
11
+ description:
12
+ "A bordered selectable tile is a ChoiceboxGroup, never a FieldLabel around a Field with a radio or a checkbox.",
13
+ },
14
+ schema: [],
15
+ messages: {
16
+ "hand-built-tile":
17
+ "A bordered selectable tile is a ChoiceboxGroup with ChoiceboxGroupItem. Options without a description are a plain RadioGroup with a Label for each.",
18
+ },
19
+ },
20
+ create(context) {
21
+ const importOf = renderedImports(context);
22
+ const { visitorKeys } = context.sourceCode;
23
+ const isRendered = (name) => (node) =>
24
+ node.type === "JSXElement" && name(importOf(node.openingElement.name)?.name);
25
+ const isField = isRendered((name) => name === FIELD);
26
+ const isControl = isRendered((name) => CONTROLS.has(name));
27
+
28
+ return {
29
+ JSXElement(node) {
30
+ if (importOf(node.openingElement.name)?.name !== FIELD_LABEL) return;
31
+ let handBuilt = false;
32
+ walk(node, visitorKeys, (child) => {
33
+ if (!isField(child) || !findBelow(child, visitorKeys, isControl)) return true;
34
+ handBuilt = true;
35
+ return "stop";
36
+ });
37
+ if (handBuilt) context.report({ node: node.openingElement, messageId: "hand-built-tile" });
38
+ },
39
+ };
40
+ },
41
+ };
@@ -0,0 +1,87 @@
1
+ /*
2
+ * Standard tool checked first: `no-restricted-syntax` can match a member named
3
+ * `toLocaleDateString` or `new Intl.DateTimeFormat`, but `toLocaleString` is
4
+ * also how a number is written, so it is a date only when its options object
5
+ * names a date or time part, which a selector cannot read. It reports only a
6
+ * date written into markup (inside a JSX expression of the same function): a
7
+ * helper that returns a chart label, a period name or an email's text has no
8
+ * element to put a RecordTime in.
9
+ */
10
+ const ALWAYS_DATES = new Set(["toLocaleDateString", "toLocaleTimeString"]);
11
+ const DATE_PARTS = new Set([
12
+ "dateStyle",
13
+ "timeStyle",
14
+ "weekday",
15
+ "era",
16
+ "year",
17
+ "month",
18
+ "day",
19
+ "dayPeriod",
20
+ "hour",
21
+ "minute",
22
+ "second",
23
+ "fractionalSecondDigits",
24
+ "timeZoneName",
25
+ "hour12",
26
+ "hourCycle",
27
+ ]);
28
+
29
+ const isIntlDateTimeFormat = (callee) =>
30
+ callee.type === "MemberExpression" &&
31
+ !callee.computed &&
32
+ callee.object.type === "Identifier" &&
33
+ callee.object.name === "Intl" &&
34
+ callee.property.name === "DateTimeFormat";
35
+
36
+ const namesADatePart = (options) =>
37
+ options?.type === "ObjectExpression" &&
38
+ options.properties.some(
39
+ (property) =>
40
+ property.type === "Property" &&
41
+ !property.computed &&
42
+ property.key.type === "Identifier" &&
43
+ DATE_PARTS.has(property.key.name),
44
+ );
45
+
46
+ const FUNCTIONS = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
47
+
48
+ function writtenIntoMarkup(node) {
49
+ for (let current = node.parent; current; current = current.parent) {
50
+ if (current.type === "JSXExpressionContainer") return true;
51
+ if (FUNCTIONS.has(current.type)) return false;
52
+ }
53
+ return false;
54
+ }
55
+
56
+ export const noHandFormattedDate = {
57
+ meta: {
58
+ type: "problem",
59
+ docs: {
60
+ description: "An app shows a moment through RecordTime, never a hand-formatted date.",
61
+ },
62
+ schema: [],
63
+ messages: {
64
+ "hand-formatted-date":
65
+ "Show a moment with RecordTime from @nadicodeai/ui/components/record-time: it reads relative within a week, then the date, in the viewer's zone and language, with the exact time in a card. A date formatted by hand drifts from the other screens and shows the server's zone.",
66
+ },
67
+ },
68
+ create(context) {
69
+ function check(node) {
70
+ if (!writtenIntoMarkup(node)) return;
71
+ const { callee } = node;
72
+ if (isIntlDateTimeFormat(callee)) {
73
+ context.report({ node, messageId: "hand-formatted-date" });
74
+ return;
75
+ }
76
+ if (callee.type !== "MemberExpression" || callee.computed) return;
77
+ const name = callee.property.name;
78
+ if (
79
+ ALWAYS_DATES.has(name) ||
80
+ (name === "toLocaleString" && namesADatePart(node.arguments[1]))
81
+ ) {
82
+ context.report({ node, messageId: "hand-formatted-date" });
83
+ }
84
+ }
85
+ return { CallExpression: check, NewExpression: check };
86
+ },
87
+ };
@@ -0,0 +1,67 @@
1
+ import { openingElementClassUtilities } from "./jsx-class-utility.js";
2
+ import { renderedImports } from "./rendered-import.js";
3
+
4
+ const AVATAR = "Avatar";
5
+ const OVERLAP = /^-(?:space-x-|ml-|ms-|mx-)/;
6
+
7
+ const hasOverlapClass = (node, context) =>
8
+ openingElementClassUtilities(node, context).some((utility) =>
9
+ OVERLAP.test(utility.slice(utility.lastIndexOf(":") + 1).replace(/^!|!$/g, "")),
10
+ );
11
+
12
+ function returnedElement(callback) {
13
+ if (callback?.type !== "ArrowFunctionExpression" && callback?.type !== "FunctionExpression") {
14
+ return null;
15
+ }
16
+ if (callback.body.type !== "BlockStatement") return callback.body;
17
+ return callback.body.body.find((statement) => statement.type === "ReturnStatement")?.argument;
18
+ }
19
+
20
+ export const noHandStackedAvatars = {
21
+ meta: {
22
+ type: "problem",
23
+ docs: {
24
+ description:
25
+ "Stacked avatars are one AvatarGroup, never sibling Avatars or an overlap written on a parent or an Avatar.",
26
+ },
27
+ schema: [],
28
+ messages: {
29
+ "hand-stacked-avatars":
30
+ "Stacked avatars are one <AvatarGroup members={...} label={...} />, which owns the overlap, the order, the +N count and the group's label.",
31
+ },
32
+ },
33
+ create(context) {
34
+ const importOf = renderedImports(context);
35
+ const reported = new WeakSet();
36
+ const isAvatar = (node) =>
37
+ node?.type === "JSXElement" && importOf(node.openingElement.name)?.name === AVATAR;
38
+
39
+ function report(avatar) {
40
+ if (reported.has(avatar)) return;
41
+ reported.add(avatar);
42
+ context.report({ node: avatar.openingElement, messageId: "hand-stacked-avatars" });
43
+ }
44
+
45
+ return {
46
+ JSXElement(node) {
47
+ if (isAvatar(node) && hasOverlapClass(node.openingElement, context)) report(node);
48
+ const avatars = node.children.filter(isAvatar);
49
+ if (avatars.length >= 2) report(avatars[1]);
50
+ },
51
+ JSXFragment(node) {
52
+ const avatars = node.children.filter(isAvatar);
53
+ if (avatars.length >= 2) report(avatars[1]);
54
+ },
55
+ CallExpression(node) {
56
+ const { callee, parent } = node;
57
+ const isMap =
58
+ callee.type === "MemberExpression" && !callee.computed && callee.property.name === "map";
59
+ if (!isMap) return;
60
+ if (parent.type !== "JSXExpressionContainer" || parent.parent.type !== "JSXElement") return;
61
+ if (!hasOverlapClass(parent.parent.openingElement, context)) return;
62
+ const returned = returnedElement(node.arguments[0]);
63
+ if (isAvatar(returned)) report(returned);
64
+ },
65
+ };
66
+ },
67
+ };