@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,128 @@
1
+ /*
2
+ * A shortcut hint is `<Kbd meta>K</Kbd>`: the modifier is a flag, the child is
3
+ * one key from `KBD_KEYS`, and the component draws the person's platform. A
4
+ * hand-written ⌘ or "Ctrl" is right on one platform and wrong on the others.
5
+ *
6
+ * Standard tool checked first: `no-restricted-syntax` can match a `Kbd` child
7
+ * by text, but not whether this `Kbd` is the package's (an import binding, so a
8
+ * renamed or local `Kbd` is judged correctly), and it would need the key list
9
+ * spelled into a regex. The list is read from the package's own `KBD_KEYS`
10
+ * array at lint time, so a key added there changes this rule's answer with no
11
+ * edit here.
12
+ */
13
+ import { readFileSync } from "node:fs";
14
+ import { createRequire } from "node:module";
15
+ import { fileURLToPath } from "node:url";
16
+
17
+ import { packageModules, partOf, renderedImports } from "./rendered-import.js";
18
+
19
+ const KBD_MODULE = "@nadicodeai/ui/components/kbd";
20
+ const OWNER_FILE = "packages/ui/src/components/kbd.tsx";
21
+
22
+ const KBD = new Map([["Kbd", packageModules("kbd").map((from) => ({ from, part: "Kbd" }))]]);
23
+
24
+ /*
25
+ * TypeScript is loaded when this rule first runs, not when the plugin loads:
26
+ * the plugin ships inside `@nadicodeai/ui`, which does not depend on the
27
+ * compiler, and a consumer who never enables this rule never asks for it.
28
+ */
29
+ let ts;
30
+ function typescript() {
31
+ ts ??= createRequire(import.meta.url)("typescript");
32
+ return ts;
33
+ }
34
+
35
+ function arrayLiteralOf(ts, expression) {
36
+ let node = expression;
37
+ while (
38
+ node &&
39
+ (ts.isAsExpression(node) ||
40
+ ts.isSatisfiesExpression(node) ||
41
+ ts.isParenthesizedExpression(node))
42
+ ) {
43
+ node = node.expression;
44
+ }
45
+ return node && ts.isArrayLiteralExpression(node) ? node : null;
46
+ }
47
+
48
+ function readKeys() {
49
+ const ts = typescript();
50
+ const file = fileURLToPath(import.meta.resolve(KBD_MODULE));
51
+ const source = ts.createSourceFile(
52
+ file,
53
+ readFileSync(file, "utf8"),
54
+ ts.ScriptTarget.Latest,
55
+ false,
56
+ file.endsWith(".js") ? ts.ScriptKind.JS : ts.ScriptKind.TSX,
57
+ );
58
+ for (const statement of source.statements) {
59
+ if (!ts.isVariableStatement(statement)) continue;
60
+ for (const declaration of statement.declarationList.declarations) {
61
+ if (!ts.isIdentifier(declaration.name) || declaration.name.text !== "KBD_KEYS") continue;
62
+ const array = arrayLiteralOf(ts, declaration.initializer);
63
+ if (!array) break;
64
+ return new Set(array.elements.map((element) => element.text));
65
+ }
66
+ }
67
+ throw new Error(`${file} has no KBD_KEYS array literal to read the keys from.`);
68
+ }
69
+
70
+ let keys;
71
+
72
+ /** Read once per lint process; the built module does not move under it. */
73
+ function kbdKeys() {
74
+ keys ??= readKeys();
75
+ return keys;
76
+ }
77
+
78
+ function writtenText(child) {
79
+ if (child.type === "JSXText") return child.value.trim() === "" ? null : child.value.trim();
80
+ if (child.type !== "JSXExpressionContainer") return null;
81
+ const { expression } = child;
82
+ // 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.
83
+ if (expression.type === "Literal" && typeof expression.value === "string") {
84
+ return expression.value;
85
+ }
86
+ if (expression.type === "TemplateLiteral" && expression.expressions.length === 0) {
87
+ return expression.quasis[0].value.cooked;
88
+ }
89
+ return null;
90
+ }
91
+
92
+ export const kbdChildrenNoModifierText = {
93
+ meta: {
94
+ type: "problem",
95
+ docs: {
96
+ description:
97
+ "A Kbd child is one key from KBD_KEYS with the modifier as a flag, and a shortcut is never a lowercase kbd element.",
98
+ },
99
+ schema: [],
100
+ messages: {
101
+ "hand-written-key":
102
+ "Write the modifier as a flag, <Kbd meta>K</Kbd>, and the child as one key from KBD_KEYS. A hand-written glyph shows the wrong key on Windows and Linux.",
103
+ },
104
+ },
105
+ create(context) {
106
+ const importOf = renderedImports(context);
107
+ const ownsTheElement = context.filename.replaceAll("\\", "/").endsWith(`/${OWNER_FILE}`);
108
+
109
+ return {
110
+ JSXElement(node) {
111
+ const name = node.openingElement.name;
112
+ if (name.type === "JSXIdentifier" && name.name === "kbd") {
113
+ if (!ownsTheElement) {
114
+ context.report({ node: node.openingElement, messageId: "hand-written-key" });
115
+ }
116
+ return;
117
+ }
118
+ if (partOf(importOf(name), KBD, context.filename) !== "Kbd") return;
119
+ for (const child of node.children) {
120
+ const text = writtenText(child);
121
+ if (text !== null && !kbdKeys().has(text)) {
122
+ context.report({ node: child, messageId: "hand-written-key" });
123
+ }
124
+ }
125
+ },
126
+ };
127
+ },
128
+ };
@@ -0,0 +1,41 @@
1
+ import { staticAttribute } from "./rendered-import.js";
2
+
3
+ /** The string a Literal or a template without expressions states, else null. */
4
+ export function literalText(node) {
5
+ if (!node) return null;
6
+ if (node.type === "Literal") {
7
+ // 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.
8
+ return typeof node.value === "string" ? node.value : null;
9
+ }
10
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0) {
11
+ return node.quasis[0].value.cooked;
12
+ }
13
+ return null;
14
+ }
15
+
16
+ /** The text a JSX element's children state when all of them are literal text, else null. */
17
+ export function childText(element) {
18
+ let text = "";
19
+ for (const child of element.children) {
20
+ if (child.type === "JSXText") text += child.value;
21
+ else if (child.type === "JSXExpressionContainer" && literalText(child.expression) !== null) {
22
+ text += literalText(child.expression);
23
+ } else return null;
24
+ }
25
+ const trimmed = text.replace(/\s+/g, " ").trim();
26
+ return trimmed === "" ? null : trimmed;
27
+ }
28
+
29
+ /** The key an object property names, else null. */
30
+ export function keyName(property) {
31
+ if (property.type !== "Property" || property.computed) return null;
32
+ if (property.key.type === "Identifier") return property.key.name;
33
+ return literalText(property.key);
34
+ }
35
+
36
+ /** An attribute's value when the source states it as a string, else null. */
37
+ export function stringAttribute(openingElement, name) {
38
+ const value = staticAttribute(openingElement, name);
39
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- staticAttribute returns true, a number or a string; only typeof picks the string.
40
+ return typeof value === "string" ? value : null;
41
+ }
@@ -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
+ };