@webjsdev/ui 0.3.17 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/README.md +66 -7
  2. package/THIRD_PARTY_NOTICES.md +349 -0
  3. package/package.json +4 -2
  4. package/packages/registry/blocks/_shared/placeholder.svg +1 -0
  5. package/packages/registry/blocks/charts/chart-area-axes.ts +72 -0
  6. package/packages/registry/blocks/charts/chart-area-default.ts +64 -0
  7. package/packages/registry/blocks/charts/chart-area-gradient.ts +82 -0
  8. package/packages/registry/blocks/charts/chart-area-icons.ts +77 -0
  9. package/packages/registry/blocks/charts/chart-area-interactive.ts +204 -0
  10. package/packages/registry/blocks/charts/chart-area-legend.ts +72 -0
  11. package/packages/registry/blocks/charts/chart-area-linear.ts +64 -0
  12. package/packages/registry/blocks/charts/chart-area-stacked-expand.ts +77 -0
  13. package/packages/registry/blocks/charts/chart-area-stacked.ts +71 -0
  14. package/packages/registry/blocks/charts/chart-area-step.ts +68 -0
  15. package/packages/registry/blocks/charts/chart-bar-active.ts +93 -0
  16. package/packages/registry/blocks/charts/chart-bar-default.ts +60 -0
  17. package/packages/registry/blocks/charts/chart-bar-horizontal.ts +62 -0
  18. package/packages/registry/blocks/charts/chart-bar-interactive.ts +185 -0
  19. package/packages/registry/blocks/charts/chart-bar-label-custom.ts +78 -0
  20. package/packages/registry/blocks/charts/chart-bar-label.ts +66 -0
  21. package/packages/registry/blocks/charts/chart-bar-mixed.ts +87 -0
  22. package/packages/registry/blocks/charts/chart-bar-multiple.ts +67 -0
  23. package/packages/registry/blocks/charts/chart-bar-negative.ts +61 -0
  24. package/packages/registry/blocks/charts/chart-bar-stacked.ts +68 -0
  25. package/packages/registry/blocks/charts/chart-line-default.ts +61 -0
  26. package/packages/registry/blocks/charts/chart-line-dots-colors.ts +86 -0
  27. package/packages/registry/blocks/charts/chart-line-dots-custom.ts +75 -0
  28. package/packages/registry/blocks/charts/chart-line-dots.ts +65 -0
  29. package/packages/registry/blocks/charts/chart-line-interactive.ts +185 -0
  30. package/packages/registry/blocks/charts/chart-line-label-custom.ts +94 -0
  31. package/packages/registry/blocks/charts/chart-line-label.ts +73 -0
  32. package/packages/registry/blocks/charts/chart-line-linear.ts +61 -0
  33. package/packages/registry/blocks/charts/chart-line-multiple.ts +71 -0
  34. package/packages/registry/blocks/charts/chart-line-step.ts +61 -0
  35. package/packages/registry/blocks/charts/chart-pie-donut-active.ts +86 -0
  36. package/packages/registry/blocks/charts/chart-pie-donut-text.ts +89 -0
  37. package/packages/registry/blocks/charts/chart-pie-donut.ts +77 -0
  38. package/packages/registry/blocks/charts/chart-pie-interactive.ts +120 -0
  39. package/packages/registry/blocks/charts/chart-pie-label-custom.ts +82 -0
  40. package/packages/registry/blocks/charts/chart-pie-label-list.ts +86 -0
  41. package/packages/registry/blocks/charts/chart-pie-label.ts +77 -0
  42. package/packages/registry/blocks/charts/chart-pie-legend.ts +79 -0
  43. package/packages/registry/blocks/charts/chart-pie-separator-none.ts +77 -0
  44. package/packages/registry/blocks/charts/chart-pie-simple.ts +77 -0
  45. package/packages/registry/blocks/charts/chart-pie-stacked.ts +99 -0
  46. package/packages/registry/blocks/charts/chart-radar-default.ts +61 -0
  47. package/packages/registry/blocks/charts/chart-radar-dots.ts +61 -0
  48. package/packages/registry/blocks/charts/chart-radar-grid-circle-fill.ts +61 -0
  49. package/packages/registry/blocks/charts/chart-radar-grid-circle-no-lines.ts +61 -0
  50. package/packages/registry/blocks/charts/chart-radar-grid-circle.ts +61 -0
  51. package/packages/registry/blocks/charts/chart-radar-grid-custom.ts +61 -0
  52. package/packages/registry/blocks/charts/chart-radar-grid-fill.ts +61 -0
  53. package/packages/registry/blocks/charts/chart-radar-grid-none.ts +60 -0
  54. package/packages/registry/blocks/charts/chart-radar-icons.ts +76 -0
  55. package/packages/registry/blocks/charts/chart-radar-label-custom.ts +77 -0
  56. package/packages/registry/blocks/charts/chart-radar-legend.ts +70 -0
  57. package/packages/registry/blocks/charts/chart-radar-lines-only.ts +68 -0
  58. package/packages/registry/blocks/charts/chart-radar-multiple.ts +68 -0
  59. package/packages/registry/blocks/charts/chart-radar-radius.ts +68 -0
  60. package/packages/registry/blocks/charts/chart-radial-grid.ts +80 -0
  61. package/packages/registry/blocks/charts/chart-radial-label.ts +85 -0
  62. package/packages/registry/blocks/charts/chart-radial-shape.ts +81 -0
  63. package/packages/registry/blocks/charts/chart-radial-simple.ts +79 -0
  64. package/packages/registry/blocks/charts/chart-radial-stacked.ts +69 -0
  65. package/packages/registry/blocks/charts/chart-radial-text.ts +82 -0
  66. package/packages/registry/blocks/charts/chart-tooltip-advanced.ts +84 -0
  67. package/packages/registry/blocks/charts/chart-tooltip-default.ts +62 -0
  68. package/packages/registry/blocks/charts/chart-tooltip-formatter.ts +75 -0
  69. package/packages/registry/blocks/charts/chart-tooltip-icons.ts +68 -0
  70. package/packages/registry/blocks/charts/chart-tooltip-indicator-line.ts +62 -0
  71. package/packages/registry/blocks/charts/chart-tooltip-indicator-none.ts +62 -0
  72. package/packages/registry/blocks/charts/chart-tooltip-label-custom.ts +65 -0
  73. package/packages/registry/blocks/charts/chart-tooltip-label-formatter.ts +66 -0
  74. package/packages/registry/blocks/charts/chart-tooltip-label-none.ts +62 -0
  75. package/packages/registry/blocks/dashboard-01/components/app-sidebar.ts +108 -0
  76. package/packages/registry/blocks/dashboard-01/components/chart-area-interactive.ts +276 -0
  77. package/packages/registry/blocks/dashboard-01/components/dashboard-data.ts +89 -0
  78. package/packages/registry/blocks/dashboard-01/components/data-table.ts +637 -0
  79. package/packages/registry/blocks/dashboard-01/components/nav-documents.ts +65 -0
  80. package/packages/registry/blocks/dashboard-01/components/nav-main.ts +61 -0
  81. package/packages/registry/blocks/dashboard-01/components/nav-secondary.ts +40 -0
  82. package/packages/registry/blocks/dashboard-01/components/nav-user.ts +66 -0
  83. package/packages/registry/blocks/dashboard-01/components/section-cards.ts +59 -0
  84. package/packages/registry/blocks/dashboard-01/components/site-header.ts +52 -0
  85. package/packages/registry/blocks/dashboard-01/page.ts +39 -0
  86. package/packages/registry/blocks/login-01/components/login-form.ts +70 -0
  87. package/packages/registry/blocks/login-01/page.ts +19 -0
  88. package/packages/registry/blocks/login-02/components/login-form.ts +68 -0
  89. package/packages/registry/blocks/login-02/page.ts +36 -0
  90. package/packages/registry/blocks/login-03/components/login-form.ts +89 -0
  91. package/packages/registry/blocks/login-03/page.ts +28 -0
  92. package/packages/registry/blocks/login-04/components/login-form.ts +91 -0
  93. package/packages/registry/blocks/login-04/page.ts +20 -0
  94. package/packages/registry/blocks/login-05/components/login-form.ts +75 -0
  95. package/packages/registry/blocks/login-05/page.ts +19 -0
  96. package/packages/registry/blocks/preview/cards/activate-agent-dialog.ts +77 -0
  97. package/packages/registry/blocks/preview/cards/analytics-card.ts +64 -0
  98. package/packages/registry/blocks/preview/cards/anomaly-alert.ts +35 -0
  99. package/packages/registry/blocks/preview/cards/bar-chart-card.ts +91 -0
  100. package/packages/registry/blocks/preview/cards/book-appointment.ts +51 -0
  101. package/packages/registry/blocks/preview/cards/codespaces-card.ts +172 -0
  102. package/packages/registry/blocks/preview/cards/contributions-activity.ts +63 -0
  103. package/packages/registry/blocks/preview/cards/contributors.ts +51 -0
  104. package/packages/registry/blocks/preview/cards/environment-variables.ts +47 -0
  105. package/packages/registry/blocks/preview/cards/feedback-form.ts +61 -0
  106. package/packages/registry/blocks/preview/cards/file-upload.ts +51 -0
  107. package/packages/registry/blocks/preview/cards/github-profile.ts +66 -0
  108. package/packages/registry/blocks/preview/cards/icon-preview-grid.ts +47 -0
  109. package/packages/registry/blocks/preview/cards/invite-team.ts +78 -0
  110. package/packages/registry/blocks/preview/cards/invoice.ts +91 -0
  111. package/packages/registry/blocks/preview/cards/live-waveform.ts +330 -0
  112. package/packages/registry/blocks/preview/cards/no-team-members.ts +61 -0
  113. package/packages/registry/blocks/preview/cards/not-found.ts +46 -0
  114. package/packages/registry/blocks/preview/cards/observability-card.ts +44 -0
  115. package/packages/registry/blocks/preview/cards/pie-chart-card.ts +84 -0
  116. package/packages/registry/blocks/preview/cards/report-bug.ts +71 -0
  117. package/packages/registry/blocks/preview/cards/shipping-address.ts +79 -0
  118. package/packages/registry/blocks/preview/cards/shortcuts.ts +57 -0
  119. package/packages/registry/blocks/preview/cards/skeleton-loading.ts +37 -0
  120. package/packages/registry/blocks/preview/cards/sleep-report.ts +85 -0
  121. package/packages/registry/blocks/preview/cards/style-overview.ts +64 -0
  122. package/packages/registry/blocks/preview/cards/typography-specimen.ts +107 -0
  123. package/packages/registry/blocks/preview/cards/ui-elements.ts +164 -0
  124. package/packages/registry/blocks/preview/cards/usage-card.ts +63 -0
  125. package/packages/registry/blocks/preview/cards/visitors.ts +71 -0
  126. package/packages/registry/blocks/preview/cards/weekly-fitness-summary.ts +54 -0
  127. package/packages/registry/blocks/preview/page.ts +103 -0
  128. package/packages/registry/blocks/preview-02/cards/account-access.ts +70 -0
  129. package/packages/registry/blocks/preview-02/cards/card-overview.ts +75 -0
  130. package/packages/registry/blocks/preview-02/cards/claimable-balance.ts +54 -0
  131. package/packages/registry/blocks/preview-02/cards/contribution-history.ts +80 -0
  132. package/packages/registry/blocks/preview-02/cards/cover-art.ts +41 -0
  133. package/packages/registry/blocks/preview-02/cards/dividend-income.ts +76 -0
  134. package/packages/registry/blocks/preview-02/cards/empty-connect-bank.ts +38 -0
  135. package/packages/registry/blocks/preview-02/cards/empty-distribute-track.ts +38 -0
  136. package/packages/registry/blocks/preview-02/cards/empty-explore-catalog.ts +39 -0
  137. package/packages/registry/blocks/preview-02/cards/faq.ts +79 -0
  138. package/packages/registry/blocks/preview-02/cards/front-door.ts +40 -0
  139. package/packages/registry/blocks/preview-02/cards/index-investing.ts +31 -0
  140. package/packages/registry/blocks/preview-02/cards/kitchen-island.ts +90 -0
  141. package/packages/registry/blocks/preview-02/cards/loading-card.ts +34 -0
  142. package/packages/registry/blocks/preview-02/cards/new-milestone.ts +53 -0
  143. package/packages/registry/blocks/preview-02/cards/notification-settings.ts +126 -0
  144. package/packages/registry/blocks/preview-02/cards/payments.ts +88 -0
  145. package/packages/registry/blocks/preview-02/cards/payout-threshold.ts +92 -0
  146. package/packages/registry/blocks/preview-02/cards/power-usage.ts +84 -0
  147. package/packages/registry/blocks/preview-02/cards/preferences.ts +89 -0
  148. package/packages/registry/blocks/preview-02/cards/qr-connect.ts +54 -0
  149. package/packages/registry/blocks/preview-02/cards/receiving-method.ts +89 -0
  150. package/packages/registry/blocks/preview-02/cards/recent-transactions.ts +101 -0
  151. package/packages/registry/blocks/preview-02/cards/release-catalog.ts +73 -0
  152. package/packages/registry/blocks/preview-02/cards/roller-shades.ts +110 -0
  153. package/packages/registry/blocks/preview-02/cards/savings-progress.ts +68 -0
  154. package/packages/registry/blocks/preview-02/cards/savings-targets.ts +132 -0
  155. package/packages/registry/blocks/preview-02/cards/sidebar-nav.ts +88 -0
  156. package/packages/registry/blocks/preview-02/cards/social-links.ts +57 -0
  157. package/packages/registry/blocks/preview-02/cards/stock-performance.ts +106 -0
  158. package/packages/registry/blocks/preview-02/cards/syncing-state.ts +37 -0
  159. package/packages/registry/blocks/preview-02/cards/transfer-funds.ts +111 -0
  160. package/packages/registry/blocks/preview-02/cards/upcoming-payments.ts +69 -0
  161. package/packages/registry/blocks/preview-02/page.ts +111 -0
  162. package/packages/registry/blocks/preview-03/cards/files-chat.ts +75 -0
  163. package/packages/registry/blocks/preview-03/cards/group-chat.ts +114 -0
  164. package/packages/registry/blocks/preview-03/cards/reasoning-chat.ts +88 -0
  165. package/packages/registry/blocks/preview-03/cards/simple-chat.ts +53 -0
  166. package/packages/registry/blocks/preview-03/components/chat-card.ts +126 -0
  167. package/packages/registry/blocks/preview-03/components/chat-player.ts +174 -0
  168. package/packages/registry/blocks/preview-03/components/part-file.ts +77 -0
  169. package/packages/registry/blocks/preview-03/components/part-reasoning.ts +25 -0
  170. package/packages/registry/blocks/preview-03/components/part-source.ts +48 -0
  171. package/packages/registry/blocks/preview-03/components/part-text.ts +69 -0
  172. package/packages/registry/blocks/preview-03/components/part-tool.ts +71 -0
  173. package/packages/registry/blocks/preview-03/components/prompt-input.ts +86 -0
  174. package/packages/registry/blocks/preview-03/page.ts +39 -0
  175. package/packages/registry/blocks/sidebar-01/components/app-sidebar.ts +113 -0
  176. package/packages/registry/blocks/sidebar-01/components/search-form.ts +33 -0
  177. package/packages/registry/blocks/sidebar-01/components/version-switcher.ts +50 -0
  178. package/packages/registry/blocks/sidebar-01/page.ts +58 -0
  179. package/packages/registry/blocks/sidebar-02/components/app-sidebar.ts +131 -0
  180. package/packages/registry/blocks/sidebar-02/components/search-form.ts +33 -0
  181. package/packages/registry/blocks/sidebar-02/components/version-switcher.ts +50 -0
  182. package/packages/registry/blocks/sidebar-02/page.ts +53 -0
  183. package/packages/registry/blocks/sidebar-03/components/app-sidebar.ts +134 -0
  184. package/packages/registry/blocks/sidebar-03/page.ts +60 -0
  185. package/packages/registry/blocks/sidebar-04/components/app-sidebar.ts +136 -0
  186. package/packages/registry/blocks/sidebar-04/page.ts +58 -0
  187. package/packages/registry/blocks/sidebar-05/components/app-sidebar.ts +147 -0
  188. package/packages/registry/blocks/sidebar-05/components/search-form.ts +33 -0
  189. package/packages/registry/blocks/sidebar-05/page.ts +58 -0
  190. package/packages/registry/blocks/sidebar-06/components/app-sidebar.ts +105 -0
  191. package/packages/registry/blocks/sidebar-06/components/nav-main.ts +56 -0
  192. package/packages/registry/blocks/sidebar-06/components/sidebar-opt-in-form.ts +44 -0
  193. package/packages/registry/blocks/sidebar-06/page.ts +57 -0
  194. package/packages/registry/blocks/sidebar-07/components/app-sidebar.ts +108 -0
  195. package/packages/registry/blocks/sidebar-07/components/nav-main.ts +61 -0
  196. package/packages/registry/blocks/sidebar-07/components/nav-projects.ts +65 -0
  197. package/packages/registry/blocks/sidebar-07/components/nav-user.ts +70 -0
  198. package/packages/registry/blocks/sidebar-07/components/team-switcher.ts +66 -0
  199. package/packages/registry/blocks/sidebar-07/page.ts +59 -0
  200. package/packages/registry/blocks/sidebar-08/components/app-sidebar.ts +122 -0
  201. package/packages/registry/blocks/sidebar-08/components/nav-main.ts +68 -0
  202. package/packages/registry/blocks/sidebar-08/components/nav-projects.ts +65 -0
  203. package/packages/registry/blocks/sidebar-08/components/nav-secondary.ts +40 -0
  204. package/packages/registry/blocks/sidebar-08/components/nav-user.ts +70 -0
  205. package/packages/registry/blocks/sidebar-08/page.ts +59 -0
  206. package/packages/registry/blocks/sidebar-09/components/app-sidebar.ts +213 -0
  207. package/packages/registry/blocks/sidebar-09/components/nav-user.ts +70 -0
  208. package/packages/registry/blocks/sidebar-09/page.ts +52 -0
  209. package/packages/registry/blocks/sidebar-10/components/app-sidebar.ts +125 -0
  210. package/packages/registry/blocks/sidebar-10/components/nav-actions.ts +84 -0
  211. package/packages/registry/blocks/sidebar-10/components/nav-favorites.ts +66 -0
  212. package/packages/registry/blocks/sidebar-10/components/nav-main.ts +37 -0
  213. package/packages/registry/blocks/sidebar-10/components/nav-secondary.ts +43 -0
  214. package/packages/registry/blocks/sidebar-10/components/nav-workspaces.ts +78 -0
  215. package/packages/registry/blocks/sidebar-10/components/team-switcher.ts +63 -0
  216. package/packages/registry/blocks/sidebar-10/page.ts +46 -0
  217. package/packages/registry/blocks/sidebar-11/components/app-sidebar.ts +114 -0
  218. package/packages/registry/blocks/sidebar-11/page.ts +63 -0
  219. package/packages/registry/blocks/sidebar-12/components/app-sidebar.ts +63 -0
  220. package/packages/registry/blocks/sidebar-12/components/calendars.ts +53 -0
  221. package/packages/registry/blocks/sidebar-12/components/date-picker.ts +26 -0
  222. package/packages/registry/blocks/sidebar-12/components/nav-user.ts +70 -0
  223. package/packages/registry/blocks/sidebar-12/page.ts +43 -0
  224. package/packages/registry/blocks/sidebar-13/components/settings-dialog.ts +114 -0
  225. package/packages/registry/blocks/sidebar-13/page.ts +21 -0
  226. package/packages/registry/blocks/sidebar-14/components/app-sidebar.ts +120 -0
  227. package/packages/registry/blocks/sidebar-14/page.ts +55 -0
  228. package/packages/registry/blocks/sidebar-15/components/calendars.ts +53 -0
  229. package/packages/registry/blocks/sidebar-15/components/date-picker.ts +26 -0
  230. package/packages/registry/blocks/sidebar-15/components/nav-favorites.ts +66 -0
  231. package/packages/registry/blocks/sidebar-15/components/nav-main.ts +35 -0
  232. package/packages/registry/blocks/sidebar-15/components/nav-secondary.ts +43 -0
  233. package/packages/registry/blocks/sidebar-15/components/nav-user.ts +70 -0
  234. package/packages/registry/blocks/sidebar-15/components/nav-workspaces.ts +75 -0
  235. package/packages/registry/blocks/sidebar-15/components/sidebar-left.ts +126 -0
  236. package/packages/registry/blocks/sidebar-15/components/sidebar-right.ts +62 -0
  237. package/packages/registry/blocks/sidebar-15/components/team-switcher.ts +63 -0
  238. package/packages/registry/blocks/sidebar-15/page.ts +47 -0
  239. package/packages/registry/blocks/sidebar-16/components/app-sidebar.ts +124 -0
  240. package/packages/registry/blocks/sidebar-16/components/nav-main.ts +67 -0
  241. package/packages/registry/blocks/sidebar-16/components/nav-projects.ts +65 -0
  242. package/packages/registry/blocks/sidebar-16/components/nav-secondary.ts +40 -0
  243. package/packages/registry/blocks/sidebar-16/components/nav-user.ts +70 -0
  244. package/packages/registry/blocks/sidebar-16/components/search-form.ts +27 -0
  245. package/packages/registry/blocks/sidebar-16/components/site-header.ts +42 -0
  246. package/packages/registry/blocks/sidebar-16/page.ts +38 -0
  247. package/packages/registry/blocks/signup-01/components/signup-form.ts +78 -0
  248. package/packages/registry/blocks/signup-01/page.ts +19 -0
  249. package/packages/registry/blocks/signup-02/components/signup-form.ts +78 -0
  250. package/packages/registry/blocks/signup-02/page.ts +36 -0
  251. package/packages/registry/blocks/signup-03/components/signup-form.ts +79 -0
  252. package/packages/registry/blocks/signup-03/page.ts +28 -0
  253. package/packages/registry/blocks/signup-04/components/signup-form.ts +100 -0
  254. package/packages/registry/blocks/signup-04/page.ts +20 -0
  255. package/packages/registry/blocks/signup-05/components/signup-form.ts +75 -0
  256. package/packages/registry/blocks/signup-05/page.ts +20 -0
  257. package/packages/registry/components/accordion.ts +20 -7
  258. package/packages/registry/components/alert-dialog.ts +58 -13
  259. package/packages/registry/components/alert.ts +11 -6
  260. package/packages/registry/components/attachment.ts +119 -0
  261. package/packages/registry/components/avatar.ts +17 -8
  262. package/packages/registry/components/badge.ts +16 -9
  263. package/packages/registry/components/breadcrumb.ts +6 -6
  264. package/packages/registry/components/bubble.ts +95 -0
  265. package/packages/registry/components/button-group.ts +73 -0
  266. package/packages/registry/components/button.ts +35 -25
  267. package/packages/registry/components/calendar.ts +566 -0
  268. package/packages/registry/components/card.ts +8 -8
  269. package/packages/registry/components/carousel.ts +352 -0
  270. package/packages/registry/components/chart-element.ts +547 -0
  271. package/packages/registry/components/chart-geometry.ts +457 -0
  272. package/packages/registry/components/chart.ts +932 -0
  273. package/packages/registry/components/checkbox.ts +22 -84
  274. package/packages/registry/components/code-block.ts +191 -0
  275. package/packages/registry/components/collapsible.ts +5 -2
  276. package/packages/registry/components/combobox.ts +802 -0
  277. package/packages/registry/components/command.ts +618 -0
  278. package/packages/registry/components/context-menu.ts +302 -0
  279. package/packages/registry/components/dialog.ts +156 -45
  280. package/packages/registry/components/direction.ts +43 -0
  281. package/packages/registry/components/drawer.ts +226 -0
  282. package/packages/registry/components/dropdown-menu.ts +262 -83
  283. package/packages/registry/components/empty.ts +8 -8
  284. package/packages/registry/components/field.ts +20 -18
  285. package/packages/registry/components/floating.ts +124 -0
  286. package/packages/registry/components/hover-card.ts +19 -8
  287. package/packages/registry/components/input-group.ts +118 -0
  288. package/packages/registry/components/input-otp.ts +365 -0
  289. package/packages/registry/components/input.ts +3 -3
  290. package/packages/registry/components/item.ts +134 -0
  291. package/packages/registry/components/kbd.ts +2 -2
  292. package/packages/registry/components/label.ts +1 -1
  293. package/packages/registry/components/marker.ts +63 -0
  294. package/packages/registry/components/menubar.ts +388 -0
  295. package/packages/registry/components/message-scroller.ts +470 -0
  296. package/packages/registry/components/message.ts +69 -0
  297. package/packages/registry/components/native-select.ts +5 -5
  298. package/packages/registry/components/navigation-menu.ts +564 -0
  299. package/packages/registry/components/pagination.ts +5 -5
  300. package/packages/registry/components/popover.ts +30 -84
  301. package/packages/registry/components/progress.ts +7 -3
  302. package/packages/registry/components/questionnaire.ts +840 -0
  303. package/packages/registry/components/radio-group.ts +18 -73
  304. package/packages/registry/components/resizable.ts +330 -0
  305. package/packages/registry/components/scroll-area.ts +46 -0
  306. package/packages/registry/components/select.ts +650 -0
  307. package/packages/registry/components/separator.ts +1 -1
  308. package/packages/registry/components/sheet.ts +128 -0
  309. package/packages/registry/components/sidebar.ts +457 -0
  310. package/packages/registry/components/skeleton.ts +1 -1
  311. package/packages/registry/components/slider.ts +333 -0
  312. package/packages/registry/components/sonner.ts +74 -7
  313. package/packages/registry/components/spinner.ts +7 -4
  314. package/packages/registry/components/switch.ts +117 -9
  315. package/packages/registry/components/table.ts +11 -9
  316. package/packages/registry/components/tabs.ts +76 -25
  317. package/packages/registry/components/textarea.ts +1 -1
  318. package/packages/registry/components/toggle-group.ts +30 -11
  319. package/packages/registry/components/toggle.ts +20 -13
  320. package/packages/registry/components/tooltip.ts +68 -7
  321. package/packages/registry/components/trace.ts +75 -0
  322. package/packages/registry/lib/icons-hugeicons.ts +955 -0
  323. package/packages/registry/lib/icons-phosphor.ts +955 -0
  324. package/packages/registry/lib/icons-remixicon.ts +955 -0
  325. package/packages/registry/lib/icons-tabler.ts +955 -0
  326. package/packages/registry/lib/icons.ts +768 -0
  327. package/packages/registry/lib/utils.ts +49 -4
  328. package/packages/registry/registry.json +4056 -72
  329. package/packages/registry/styles/shadcn-tailwind.css +654 -0
  330. package/packages/registry/styles/style-luma.css +1987 -0
  331. package/packages/registry/styles/style-lyra.css +1965 -0
  332. package/packages/registry/styles/style-maia.css +1987 -0
  333. package/packages/registry/styles/style-mira.css +1992 -0
  334. package/packages/registry/styles/style-new-york.css +1597 -0
  335. package/packages/registry/styles/style-nova.css +1990 -0
  336. package/packages/registry/styles/style-rhea.css +1987 -0
  337. package/packages/registry/styles/style-sera.css +1977 -0
  338. package/packages/registry/styles/style-vega.css +1990 -0
  339. package/packages/registry/styles/supplement.json +54 -0
  340. package/packages/registry/styles/tw-animate.css +6 -0
  341. package/packages/registry/themes/base-colors.js +8 -2
  342. package/packages/registry/themes/fonts.js +120 -0
  343. package/packages/registry/themes/index.css +2 -0
  344. package/packages/registry/themes/items.js +61 -0
  345. package/packages/registry/themes/preset.js +302 -0
  346. package/packages/registry/themes/themes.js +1193 -0
  347. package/src/commands/add.js +236 -12
  348. package/src/commands/diff.js +34 -20
  349. package/src/commands/info.js +7 -0
  350. package/src/commands/init.js +125 -8
  351. package/src/commands/list.js +43 -11
  352. package/src/commands/view.js +33 -5
  353. package/src/registry/extract.js +192 -2
  354. package/src/registry/icons.js +31 -0
  355. package/src/registry/local.js +15 -1
  356. package/src/registry/schema.js +13 -0
  357. package/src/registry/style-bake.js +506 -0
  358. package/src/utils/theme.js +110 -21
@@ -0,0 +1,650 @@
1
+ /**
2
+ * Select: a styled single-choice picker that ENHANCES a native `<select>`.
3
+ * Tier-2. You author an ordinary `<select>` with its options inside
4
+ * `<ui-select>`. Without JavaScript that select is what the page shows and
5
+ * what the form submits. With JavaScript the element hides it (visually only:
6
+ * it still submits, validates and resets with its form) and draws shadcn's
7
+ * trigger and listbox in its place, writing every choice back into it.
8
+ *
9
+ * shadcn parity:
10
+ * Select → <ui-select placeholder size position align content-class>
11
+ * SelectTrigger (size) → drawn by the element (`size="sm" | "default"`)
12
+ * SelectTrigger children → a <template data-select-trigger> (see below)
13
+ * SelectValue placeholder → the `placeholder` attribute, or a placeholder <option>
14
+ * SelectContent → drawn by the element (`position="item-aligned"`, the
15
+ * default, opens over the trigger on the chosen option;
16
+ * `"popper"` opens below it, at `align="start"` (the
17
+ * default), `"center"` or `"end"`)
18
+ * SelectContent className → the `content-class` attribute
19
+ * SelectGroup + SelectLabel→ <optgroup label="...">
20
+ * SelectItem → <option value="...">
21
+ * SelectItem children → a <template> inside the <option> (see below)
22
+ * SelectItem className → the <option>'s class
23
+ * SelectSeparator → <hr> between options
24
+ * SelectScrollUp/DownButton→ drawn when the list overflows
25
+ * onValueChange → `ui-value-change` ({ detail: { value } }); the
26
+ * native select also fires `input` and `change`
27
+ *
28
+ * The options stay native elements because the HTML parser drops a custom
29
+ * element inside a `<select>`. A placeholder option is one with `value=""`
30
+ * that is `hidden` or `disabled`: it is not listed, and its text is the
31
+ * placeholder unless the `placeholder` attribute names one.
32
+ *
33
+ * An item shows its option's label (the `label` attribute, else its text),
34
+ * and so does the trigger once it is chosen. For an item with more in it,
35
+ * shadcn's SelectItem children such as a name over a muted value, put a
36
+ * `<template>` inside the `<option>`: the list renders the template's content
37
+ * in that item, and the trigger and the native select keep the label. The
38
+ * parser keeps a `<template>` inside an `<option>`, and it adds no text to the
39
+ * option, so the no-JavaScript select shows the label alone. A class on the
40
+ * `<option>` lands on its item, as SelectItem's `className` does.
41
+ *
42
+ * For a trigger with more than the value in it, shadcn's SelectTrigger
43
+ * children ("Format: " beside the chosen format), put a
44
+ * `<template data-select-trigger>` inside `<ui-select>`, beside the
45
+ * select. The trigger renders its content, and fills an element in it marked
46
+ * `data-slot="select-value"` with the chosen option's label (or the
47
+ * placeholder). That content is `aria-hidden`: the trigger still carries the
48
+ * label alone as its text for assistive technology, so the select is
49
+ * announced by its label and its value, as before. With JavaScript off the
50
+ * template is inert and the native select shows as it always did.
51
+ *
52
+ * Keyboard (as Radix's): on the trigger, ArrowDown / ArrowUp, Enter or Space
53
+ * open the list on the selected option, and typing selects the first option
54
+ * starting with what was typed, as a native select does. In the open list,
55
+ * ArrowDown / ArrowUp / Home / End move, typing jumps, Enter or Space choose,
56
+ * Escape closes without a change, and Tab chooses the focused option and
57
+ * moves on (the WAI-ARIA select-only combobox pattern). Every close returns
58
+ * focus to the trigger.
59
+ *
60
+ * A11y (handled by the element, plus your part): the trigger is a
61
+ * `role="combobox"` button with `aria-haspopup="listbox"`, `aria-expanded` and
62
+ * `aria-controls`, named by the select's `<label>`s (a label click focuses the
63
+ * trigger) or its `aria-label`, and it carries the select's
64
+ * `aria-describedby` and `aria-invalid`. The list is a `role="listbox"` of
65
+ * `role="option"` items with `aria-selected`, an `<optgroup>` is a named
66
+ * `role="group"`, and a disabled option is skipped. When a `required` select
67
+ * fails validation the trigger takes `aria-invalid="true"` and focus. Your
68
+ * part: a real `<label for>` on the select, and the field's error text wired
69
+ * through `aria-describedby` on the select as for any control.
70
+ *
71
+ * The native select stands in for the trigger, so the classes written on it
72
+ * are the trigger's classes (shadcn's `className` on SelectTrigger): the drawn
73
+ * trigger takes them, and falls back to `selectTriggerClass()` when the select
74
+ * carries none. `class=${cn(selectTriggerClass(), 'w-full')}` on the select
75
+ * gives a full-width trigger with JavaScript on and off alike.
76
+ *
77
+ * Style the native select for the no-JavaScript paint: `selectTriggerClass()`
78
+ * gives it the trigger's look with the browser's own arrow.
79
+ *
80
+ * @example
81
+ * ```html
82
+ * <label for="fruit" class=${labelClass()}>Fruit</label>
83
+ * <ui-select placeholder="Select a fruit">
84
+ * <select id="fruit" name="fruit" class=${selectTriggerClass()}>
85
+ * <option value="" disabled selected hidden>Select a fruit</option>
86
+ * <optgroup label="Fruits">
87
+ * <option value="apple">Apple</option>
88
+ * <option value="banana">Banana</option>
89
+ * <option value="grapes" disabled>Grapes</option>
90
+ * </optgroup>
91
+ * <hr>
92
+ * <option value="pineapple">Pineapple</option>
93
+ * </select>
94
+ * </ui-select>
95
+ * ```
96
+ */
97
+ import { WebComponent, html, prop, signal, nothing } from '@webjsdev/core';
98
+ import { templateContent, unsafeHTML } from '@webjsdev/core/directives';
99
+ import { cn, ensureId } from '../lib/utils.ts';
100
+ import { positionFloating } from './floating.ts';
101
+
102
+ /** The trigger's classes: the element's drawn trigger, and the native select's no-JavaScript look. */
103
+ export const selectTriggerClass = (): string =>
104
+ 'cn-select-trigger flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0';
105
+ /** The trigger's chevron. */
106
+ export const selectTriggerIconClass = (): string =>
107
+ 'cn-select-trigger-icon pointer-events-none';
108
+ /** The listbox panel's classes (the element applies them). */
109
+ export const selectContentClass = (): string =>
110
+ 'cn-select-content z-50 max-h-(--radix-select-content-available-height) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto data-[align-trigger=true]:animate-none not-open:hidden';
111
+ /** The listbox's inner viewport (the element applies them). */
112
+ export const selectViewportClass = (): string =>
113
+ 'cn-select-viewport data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)';
114
+ /** A group's classes (the element applies them). */
115
+ export const selectGroupClass = (): string => 'cn-select-group';
116
+ /** A group label's classes (the element applies them). */
117
+ export const selectLabelClass = (): string => 'cn-select-label';
118
+ /** An option's classes (the element applies them). */
119
+ export const selectItemClass = (): string =>
120
+ 'cn-select-item relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0';
121
+ /** The selected option's check mark slot. */
122
+ export const selectItemIndicatorClass = (): string => 'cn-select-item-indicator';
123
+ /** The selected option's check mark. */
124
+ export const selectItemIndicatorIconClass = (): string =>
125
+ 'cn-select-item-indicator-icon pointer-events-none';
126
+ /** A separator's classes (the element applies them). */
127
+ export const selectSeparatorClass = (): string =>
128
+ 'cn-select-separator pointer-events-none';
129
+ /** The scroll-up affordance, shown when the list overflows above. */
130
+ export const selectScrollUpButtonClass = (): string =>
131
+ 'cn-select-scroll-up-button sticky top-0 z-10 bg-inherit';
132
+ /** The scroll-down affordance, shown when the list overflows below. */
133
+ export const selectScrollDownButtonClass = (): string =>
134
+ 'cn-select-scroll-down-button sticky bottom-0 z-10 bg-inherit';
135
+
136
+ type Entry =
137
+ | { kind: 'option'; index: number; value: string; label: string; disabled: boolean; content: HTMLTemplateElement | null; cls: string }
138
+ | { kind: 'group'; label: string; options: Entry[] }
139
+ | { kind: 'separator' };
140
+
141
+ // The visual-only hide Radix uses for its form control: still focusable for a
142
+ // validation bubble, still submitted and reset with the form.
143
+ const HIDDEN_CONTROL =
144
+ 'position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;opacity:0;pointer-events:none;overflow:hidden;';
145
+
146
+ let uid = 0;
147
+
148
+ export class UiSelect extends WebComponent({
149
+ placeholder: String,
150
+ size: prop<'default' | 'sm'>(String),
151
+ position: prop<'popper' | 'item-aligned'>(String),
152
+ // Where a `position="popper"` list sits along the trigger (Radix's align).
153
+ align: prop<'start' | 'center' | 'end'>(String),
154
+ // Classes for the list panel, shadcn's SelectContent `className`.
155
+ contentClass: String,
156
+ open: prop(Boolean, { reflect: true }),
157
+ }) {
158
+ // Set once the element has taken over from the native select.
159
+ readonly #enhanced = signal(false);
160
+ // A failed constraint check, until the value is valid again.
161
+ readonly #invalid = signal(false);
162
+ _id = '';
163
+ _observer: MutationObserver | null = null;
164
+ _waiting: MutationObserver | null = null;
165
+ _form: HTMLFormElement | null = null;
166
+ _savedStyle: string | null = null;
167
+ _focusLast = false;
168
+ _typeBuffer = '';
169
+ _typeTimer = 0;
170
+ _scrollTimer = 0;
171
+
172
+ constructor() {
173
+ super();
174
+ this.placeholder = '';
175
+ this.size = 'default';
176
+ this.position = 'item-aligned';
177
+ this.open = false;
178
+ }
179
+
180
+ /** The native select this element enhances. */
181
+ get select(): HTMLSelectElement | null {
182
+ return this.querySelector('select');
183
+ }
184
+
185
+ /** The chosen value, as the native select holds it. */
186
+ get value(): string {
187
+ return this.select?.value ?? '';
188
+ }
189
+
190
+ set value(v: string) {
191
+ const select = this.select;
192
+ if (!select) return;
193
+ select.value = v;
194
+ this.requestUpdate();
195
+ }
196
+
197
+ connectedCallback(): void {
198
+ super.connectedCallback?.();
199
+ this._id ||= `ui-select-${++uid}`;
200
+ // A client render can connect the element before appending the authored
201
+ // select, so wait for it to arrive.
202
+ queueMicrotask(() => {
203
+ if (this._enhance() || this._waiting) return;
204
+ this._waiting = new MutationObserver(() => {
205
+ if (this._enhance()) { this._waiting?.disconnect(); this._waiting = null; }
206
+ });
207
+ this._waiting.observe(this, { childList: true, subtree: true });
208
+ });
209
+ }
210
+
211
+ disconnectedCallback(): void {
212
+ this._waiting?.disconnect();
213
+ this._waiting = null;
214
+ this._observer?.disconnect();
215
+ this._observer = null;
216
+ const select = this.select;
217
+ if (select) {
218
+ select.removeEventListener('change', this._onNativeChange);
219
+ select.removeEventListener('focus', this._onNativeFocus);
220
+ select.removeEventListener('invalid', this._onInvalid);
221
+ if (this._savedStyle !== null) select.setAttribute('style', this._savedStyle);
222
+ else select.removeAttribute('style');
223
+ select.removeAttribute('aria-hidden');
224
+ select.removeAttribute('tabindex');
225
+ }
226
+ this._form?.removeEventListener('reset', this._onReset);
227
+ document.removeEventListener('pointerdown', this._onDocPointerDown, true);
228
+ clearInterval(this._scrollTimer);
229
+ super.disconnectedCallback?.();
230
+ }
231
+
232
+ /** Take over from the native select. True once it has (or already had). */
233
+ _enhance(): boolean {
234
+ if (this.#enhanced.get()) return true;
235
+ const select = this.select;
236
+ if (!select) return false;
237
+ this._savedStyle = select.getAttribute('style');
238
+ select.setAttribute('style', (this._savedStyle ?? '') + HIDDEN_CONTROL);
239
+ select.setAttribute('aria-hidden', 'true');
240
+ select.tabIndex = -1;
241
+ select.addEventListener('change', this._onNativeChange);
242
+ select.addEventListener('focus', this._onNativeFocus);
243
+ select.addEventListener('invalid', this._onInvalid);
244
+ this._form = select.form;
245
+ this._form?.addEventListener('reset', this._onReset);
246
+ this._observer = new MutationObserver(() => { this.requestUpdate(); });
247
+ this._observer.observe(select, { childList: true, subtree: true, attributes: true, characterData: true });
248
+ this.#enhanced.set(true);
249
+ return true;
250
+ }
251
+
252
+ // The select changed under the element: an option edit, a form reset, or a
253
+ // script setting its value.
254
+ _onNativeChange = (): void => {
255
+ if (this.#invalid.get() && this.select?.checkValidity()) this.#invalid.set(false);
256
+ this.requestUpdate();
257
+ };
258
+
259
+ // A reset restores the value AFTER its event, so read it a tick later.
260
+ _onReset = (): void => {
261
+ queueMicrotask(() => { this.#invalid.set(false); this.requestUpdate(); });
262
+ };
263
+
264
+ // The hidden select takes focus from its label and from a failed submit;
265
+ // either way the user means the trigger.
266
+ _onNativeFocus = (): void => {
267
+ this._trigger()?.focus();
268
+ };
269
+
270
+ _onInvalid = (): void => {
271
+ this.#invalid.set(true);
272
+ this._trigger()?.focus();
273
+ };
274
+
275
+ _trigger(): HTMLButtonElement | null {
276
+ return this.querySelector('[data-slot="select-trigger"]');
277
+ }
278
+
279
+ _panel(): HTMLElement | null {
280
+ return this.querySelector('[data-slot="select-content"]');
281
+ }
282
+
283
+ _optionEls(): HTMLElement[] {
284
+ return Array.from(this.querySelectorAll<HTMLElement>('[data-slot="select-item"]'));
285
+ }
286
+
287
+ _enabledOptionEls(): HTMLElement[] {
288
+ return this._optionEls().filter((o) => !o.hasAttribute('data-disabled'));
289
+ }
290
+
291
+ static _isPlaceholder(o: HTMLOptionElement): boolean {
292
+ return o.value === '' && (!!o.hidden || o.disabled);
293
+ }
294
+
295
+ _entries(): Entry[] {
296
+ const select = this.select;
297
+ if (!select) return [];
298
+ const options = Array.from(select.options);
299
+ const toOption = (o: HTMLOptionElement): Entry | null =>
300
+ UiSelect._isPlaceholder(o)
301
+ ? null
302
+ : {
303
+ kind: 'option',
304
+ index: options.indexOf(o),
305
+ value: o.value,
306
+ label: o.label || o.text,
307
+ disabled: o.disabled || !!(o.parentElement as HTMLOptGroupElement | null)?.disabled,
308
+ content: Array.from(o.children).find((c): c is HTMLTemplateElement => c instanceof HTMLTemplateElement) ?? null,
309
+ cls: o.getAttribute('class') ?? '',
310
+ };
311
+ const out: Entry[] = [];
312
+ for (const child of Array.from(select.children)) {
313
+ if (child instanceof HTMLOptionElement) {
314
+ const e = toOption(child);
315
+ if (e) out.push(e);
316
+ } else if (child instanceof HTMLOptGroupElement) {
317
+ const opts = Array.from(child.children)
318
+ .filter((c): c is HTMLOptionElement => c instanceof HTMLOptionElement)
319
+ .map(toOption)
320
+ .filter((e): e is Entry => !!e);
321
+ out.push({ kind: 'group', label: child.label, options: opts });
322
+ } else if (child.tagName === 'HR') {
323
+ out.push({ kind: 'separator' });
324
+ }
325
+ }
326
+ return out;
327
+ }
328
+
329
+ _placeholderText(): string {
330
+ if (this.placeholder) return this.placeholder;
331
+ const select = this.select;
332
+ const p = select ? Array.from(select.options).find((o) => UiSelect._isPlaceholder(o)) : undefined;
333
+ return p?.text ?? '';
334
+ }
335
+
336
+ // The trigger, as shadcn's SelectTrigger, is the box that sits in the
337
+ // layout (a flex item a `w-full max-w-48` sizes against the row, a field's
338
+ // column), so neither the host nor the wrapper draws a box of its own. The
339
+ // host's is turned off through its style, which the server serves, unlike
340
+ // a class the element changes.
341
+ render() {
342
+ if (typeof this.getAttribute === 'function' && !this.getAttribute('style')) this.setAttribute('style', 'display: contents');
343
+ if (!this.#enhanced.get()) {
344
+ return html`<div data-slot="select" class="contents"><slot></slot></div>`;
345
+ }
346
+ const select = this.select;
347
+ const current = select && select.selectedIndex >= 0 ? select.options[select.selectedIndex] : null;
348
+ const shown = current && !UiSelect._isPlaceholder(current) ? current : null;
349
+ const labels = select?.labels ? Array.from(select.labels).map((l) => ensureId(l, 'ui-select-label')) : [];
350
+ const invalid = this.#invalid.get() || select?.getAttribute('aria-invalid') === 'true';
351
+ const listId = `${this._id}-listbox`;
352
+ const renderEntry = (e: Entry): unknown => {
353
+ if (e.kind === 'separator') {
354
+ return html`<div data-slot="select-separator" aria-hidden="true" class=${selectSeparatorClass()}></div>`;
355
+ }
356
+ if (e.kind === 'group') {
357
+ // An <optgroup> with no label is shadcn's SelectGroup with no
358
+ // SelectLabel: the group's spacing around its items, and no heading.
359
+ if (!e.label) {
360
+ return html`<div data-slot="select-group" role="group" class=${selectGroupClass()}>${e.options.map(renderEntry)}</div>`;
361
+ }
362
+ const labelId = `${this._id}-group-${e.label.replace(/\W+/g, '-')}`;
363
+ return html`<div data-slot="select-group" role="group" aria-labelledby=${labelId} class=${selectGroupClass()}><div
364
+ data-slot="select-label" id=${labelId} class=${selectLabelClass()}>${e.label}</div>${e.options.map(renderEntry)}</div>`;
365
+ }
366
+ const selected = !!shown && shown.index === e.index;
367
+ return html`<div
368
+ data-slot="select-item"
369
+ role="option"
370
+ id=${`${this._id}-option-${e.index}`}
371
+ tabindex="-1"
372
+ data-value=${e.value}
373
+ aria-selected=${selected ? 'true' : 'false'}
374
+ data-state=${selected ? 'checked' : 'unchecked'}
375
+ aria-disabled=${e.disabled ? 'true' : nothing}
376
+ ?data-disabled=${e.disabled}
377
+ class=${e.cls ? cn(selectItemClass(), e.cls) : selectItemClass()}
378
+ @click=${() => this._choose(e.index)}
379
+ @pointermove=${this._onOptionPointerMove}
380
+ ><span data-slot="select-item-indicator" class=${selectItemIndicatorClass()}>${selected
381
+ ? html`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class=${selectItemIndicatorIconClass()}><path d="M20 6 9 17l-5-5"></path></svg>`
382
+ : ''}</span><span>${e.content ? templateContent(e.content) : e.label}</span></div>`;
383
+ };
384
+ return html`<div data-slot="select" class="contents"><slot></slot><button
385
+ type="button"
386
+ data-slot="select-trigger"
387
+ role="combobox"
388
+ data-size=${this.size || 'default'}
389
+ ?data-placeholder=${!shown}
390
+ aria-haspopup="listbox"
391
+ aria-expanded=${this.open ? 'true' : 'false'}
392
+ aria-controls=${listId}
393
+ aria-labelledby=${labels.length ? labels.join(' ') : nothing}
394
+ aria-label=${!labels.length && select?.getAttribute('aria-label') ? select.getAttribute('aria-label') : nothing}
395
+ aria-describedby=${select?.getAttribute('aria-describedby') || nothing}
396
+ aria-invalid=${invalid ? 'true' : nothing}
397
+ aria-required=${select?.required ? 'true' : nothing}
398
+ ?disabled=${!!select?.disabled}
399
+ class=${select?.getAttribute('class') || selectTriggerClass()}
400
+ @click=${this._onTriggerClick}
401
+ @keydown=${this._onTriggerKeyDown}
402
+ >${this._triggerBody(shown ? shown.label || shown.text : this._placeholderText())}<svg
403
+ xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"
404
+ class=${selectTriggerIconClass()}
405
+ ><path d="m6 9 6 6 6-6"></path></svg></button><div
406
+ data-slot="select-content"
407
+ id=${listId}
408
+ role="listbox"
409
+ popover="manual"
410
+ aria-labelledby=${labels.length ? labels.join(' ') : nothing}
411
+ data-state=${this.open ? 'open' : 'closed'}
412
+ data-position=${this.position || 'item-aligned'}
413
+ data-align=${this.position === 'popper' ? this.align || 'start' : nothing}
414
+ class=${this.contentClass ? cn(selectContentClass(), this.contentClass) : selectContentClass()}
415
+ @keydown=${this._onListKeyDown}
416
+ @pointerdown=${this._onListPointerDown}
417
+ @scroll=${this._syncScrollButtons}
418
+ ><div data-slot="select-scroll-up-button" aria-hidden="true" hidden class=${selectScrollUpButtonClass()}
419
+ @pointerenter=${() => this._autoScroll(-1)} @pointerleave=${this._stopScroll}
420
+ ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"></path></svg></div><div
421
+ data-slot="select-viewport" data-position=${this.position || 'item-aligned'} class=${selectViewportClass()}
422
+ >${this._entries().map(renderEntry)}</div><div data-slot="select-scroll-down-button" aria-hidden="true" hidden class=${selectScrollDownButtonClass()}
423
+ @pointerenter=${() => this._autoScroll(1)} @pointerleave=${this._stopScroll}
424
+ ><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"></path></svg></div></div></div>`;
425
+ }
426
+
427
+ /** The author's trigger content, if any: `<template data-select-trigger>`. */
428
+ _triggerTemplate(): HTMLTemplateElement | null {
429
+ return this.querySelector<HTMLTemplateElement>('template[data-select-trigger]');
430
+ }
431
+
432
+ // The trigger's content: the value alone, or the author's template with the
433
+ // value written into its `data-slot="select-value"` element, hidden from
434
+ // assistive technology beside the value as plain text, so the trigger still
435
+ // reads as its label. The template is the author's own markup from the page
436
+ // and the value goes in as text, so the serialized copy is safe to render.
437
+ _triggerBody(text: string): unknown {
438
+ const tpl = this._triggerTemplate();
439
+ if (!tpl) return html`<span data-slot="select-value">${text}</span>`;
440
+ const box = document.createElement('div');
441
+ box.append(tpl.content.cloneNode(true));
442
+ const slot = box.querySelector('[data-slot="select-value"]');
443
+ if (slot) slot.textContent = text;
444
+ return html`<span data-slot="select-trigger-content" aria-hidden="true" class="contents">${unsafeHTML(box.innerHTML)}</span><span class="sr-only">${text}</span>`;
445
+ }
446
+
447
+ updated(changedProperties: Map<string, unknown>): void {
448
+ super.updated?.(changedProperties);
449
+ if (!changedProperties.has('open') || changedProperties.get('open') === undefined) return;
450
+ queueMicrotask(() => (this.open ? this._afterOpen() : this._afterClose()));
451
+ }
452
+
453
+ /** Open the list. */
454
+ show(): void {
455
+ if (this.select?.disabled) return;
456
+ this.open = true;
457
+ }
458
+
459
+ /** Close the list, returning focus to the trigger if it was in the list. */
460
+ hide(): void {
461
+ const panel = this._panel();
462
+ const active = document.activeElement;
463
+ // Focus leaves the panel BEFORE it hides, or the engine drops it to <body>.
464
+ if (panel && active && panel.contains(active)) this._trigger()?.focus();
465
+ this.open = false;
466
+ }
467
+
468
+ _afterOpen(): void {
469
+ const panel = this._panel() as (HTMLElement & { showPopover?: () => void }) | null;
470
+ const trigger = this._trigger();
471
+ if (!panel || !trigger) return;
472
+ if (typeof panel.showPopover === 'function' && !panel.matches(':popover-open')) panel.showPopover();
473
+ this._reposition();
474
+ document.addEventListener('pointerdown', this._onDocPointerDown, true);
475
+ window.addEventListener('resize', this._reposition);
476
+ const enabled = this._enabledOptionEls();
477
+ const selected = enabled.find((o) => o.getAttribute('aria-selected') === 'true');
478
+ const target = selected ?? (this._focusLast ? enabled[enabled.length - 1] : enabled[0]);
479
+ this._focusLast = false;
480
+ target?.focus({ preventScroll: true });
481
+ target?.scrollIntoView({ block: 'nearest' });
482
+ this._syncScrollButtons();
483
+ }
484
+
485
+ _afterClose(): void {
486
+ const panel = this._panel() as (HTMLElement & { hidePopover?: () => void }) | null;
487
+ if (panel && typeof panel.hidePopover === 'function' && panel.matches(':popover-open')) panel.hidePopover();
488
+ document.removeEventListener('pointerdown', this._onDocPointerDown, true);
489
+ window.removeEventListener('resize', this._reposition);
490
+ this._stopScroll();
491
+ }
492
+
493
+ _reposition = (): void => {
494
+ const panel = this._panel();
495
+ const trigger = this._trigger();
496
+ if (!panel || !trigger) return;
497
+ const t = trigger.getBoundingClientRect();
498
+ panel.style.setProperty('--radix-select-trigger-width', `${t.width}px`);
499
+ panel.style.setProperty('--radix-select-trigger-height', `${t.height}px`);
500
+ const room = Math.max(window.innerHeight - t.bottom, t.top) - 12;
501
+ panel.style.setProperty('--radix-select-content-available-height', `${Math.max(room, 120)}px`);
502
+ panel.style.minWidth = `${t.width}px`;
503
+ if (this.position === 'popper') {
504
+ positionFloating(trigger, panel, { side: 'bottom', align: this.align || 'start', sideOffset: 4 });
505
+ return;
506
+ }
507
+ // item-aligned: open OVER the trigger, the chosen option's text lying on
508
+ // the trigger's value text, as Radix does, kept inside the viewport.
509
+ const option =
510
+ this._optionEls().find((o) => o.getAttribute('aria-selected') === 'true') ??
511
+ this._enabledOptionEls()[0];
512
+ const valueEl = trigger.querySelector<HTMLElement>('[data-slot="select-value"]');
513
+ if (!option || !valueEl) {
514
+ positionFloating(trigger, panel, { side: 'bottom', align: 'start', sideOffset: 4 });
515
+ return;
516
+ }
517
+ const p = panel.getBoundingClientRect();
518
+ const o = option.getBoundingClientRect();
519
+ const text = (option.lastElementChild as HTMLElement | null)?.getBoundingClientRect() ?? o;
520
+ const v = valueEl.getBoundingClientRect();
521
+ const margin = 10;
522
+ let top = t.top + t.height / 2 - (o.top - p.top + o.height / 2);
523
+ let left = v.left - (text.left - p.left);
524
+ top = Math.max(margin, Math.min(top, window.innerHeight - p.height - margin));
525
+ left = Math.max(margin, Math.min(left, window.innerWidth - p.width - margin));
526
+ panel.style.top = `${top}px`;
527
+ panel.style.left = `${left}px`;
528
+ panel.setAttribute('data-side', 'none');
529
+ };
530
+
531
+ /** Choose the option at `index` in the native select, then close. */
532
+ _choose(index: number): void {
533
+ const select = this.select;
534
+ if (!select) return;
535
+ const option = select.options[index];
536
+ if (!option || option.disabled) return;
537
+ const changed = select.selectedIndex !== index;
538
+ if (changed) {
539
+ select.selectedIndex = index;
540
+ select.dispatchEvent(new Event('input', { bubbles: true }));
541
+ select.dispatchEvent(new Event('change', { bubbles: true }));
542
+ this.dispatchEvent(new CustomEvent('ui-value-change', { bubbles: true, detail: { value: select.value } }));
543
+ }
544
+ this.requestUpdate();
545
+ if (this.open) this.hide();
546
+ }
547
+
548
+ _indexOf(optionEl: Element | null): number {
549
+ const id = optionEl?.id ?? '';
550
+ const m = /-option-(\d+)$/.exec(id);
551
+ return m ? Number(m[1]) : -1;
552
+ }
553
+
554
+ _onTriggerClick = (): void => {
555
+ if (this.open) this.hide();
556
+ else this.show();
557
+ };
558
+
559
+ _onTriggerKeyDown = (e: KeyboardEvent): void => {
560
+ if (this.open) return;
561
+ if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
562
+ e.preventDefault();
563
+ this._focusLast = e.key === 'ArrowUp';
564
+ this.show();
565
+ return;
566
+ }
567
+ // Typing on a closed select picks, as the native control does.
568
+ if (e.key.length === 1 && e.key !== ' ' && !e.metaKey && !e.ctrlKey && !e.altKey) {
569
+ const match = this._typeahead(e.key);
570
+ if (match) this._choose(this._indexOf(match));
571
+ }
572
+ };
573
+
574
+ _onListKeyDown = (e: KeyboardEvent): void => {
575
+ const enabled = this._enabledOptionEls();
576
+ const active = (document.activeElement as HTMLElement | null)?.closest('[data-slot="select-item"]') as HTMLElement | null;
577
+ const i = active ? enabled.indexOf(active) : -1;
578
+ const focus = (el: HTMLElement | undefined) => {
579
+ if (!el) return;
580
+ el.focus({ preventScroll: true });
581
+ el.scrollIntoView({ block: 'nearest' });
582
+ };
583
+ if (e.key === 'ArrowDown') { e.preventDefault(); focus(enabled[Math.min(i + 1, enabled.length - 1)]); }
584
+ else if (e.key === 'ArrowUp') { e.preventDefault(); focus(enabled[Math.max(i - 1, 0)]); }
585
+ else if (e.key === 'Home') { e.preventDefault(); focus(enabled[0]); }
586
+ else if (e.key === 'End') { e.preventDefault(); focus(enabled[enabled.length - 1]); }
587
+ else if (e.key === 'Escape') { e.preventDefault(); this.hide(); }
588
+ else if (e.key === 'Enter' || (e.key === ' ' && !this._typeBuffer)) {
589
+ e.preventDefault();
590
+ if (active) this._choose(this._indexOf(active));
591
+ } else if (e.key === 'Tab') {
592
+ // Choose and move on: focus goes back to the trigger here, then the
593
+ // browser's own Tab advances from it.
594
+ if (active) this._choose(this._indexOf(active));
595
+ else this.hide();
596
+ } else if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
597
+ const match = this._typeahead(e.key);
598
+ if (match) { e.preventDefault(); focus(match); }
599
+ }
600
+ };
601
+
602
+ _typeahead(key: string): HTMLElement | null {
603
+ this._typeBuffer = (this._typeBuffer + key).toLowerCase();
604
+ clearTimeout(this._typeTimer);
605
+ this._typeTimer = window.setTimeout(() => { this._typeBuffer = ''; }, 500);
606
+ const buffer = this._typeBuffer;
607
+ return this._enabledOptionEls().find((o) => (o.textContent ?? '').trim().toLowerCase().startsWith(buffer)) ?? null;
608
+ }
609
+
610
+ // Pointing at an option focuses it, which is what highlights it.
611
+ _onOptionPointerMove = (e: PointerEvent): void => {
612
+ const option = e.currentTarget as HTMLElement;
613
+ if (option.hasAttribute('data-disabled') || document.activeElement === option) return;
614
+ option.focus({ preventScroll: true });
615
+ };
616
+
617
+ // A press inside the list must not blur the focused option first.
618
+ _onListPointerDown = (e: PointerEvent): void => {
619
+ e.preventDefault();
620
+ };
621
+
622
+ _onDocPointerDown = (e: Event): void => {
623
+ if (e.composedPath().includes(this)) return;
624
+ this.hide();
625
+ };
626
+
627
+ _syncScrollButtons = (): void => {
628
+ const panel = this._panel();
629
+ if (!panel) return;
630
+ const up = panel.querySelector<HTMLElement>('[data-slot="select-scroll-up-button"]');
631
+ const down = panel.querySelector<HTMLElement>('[data-slot="select-scroll-down-button"]');
632
+ if (up) up.hidden = panel.scrollTop <= 0;
633
+ if (down) down.hidden = panel.scrollTop + panel.clientHeight >= panel.scrollHeight - 1;
634
+ };
635
+
636
+ _autoScroll(dir: number): void {
637
+ this._stopScroll();
638
+ this._scrollTimer = window.setInterval(() => {
639
+ const panel = this._panel();
640
+ if (!panel) return;
641
+ panel.scrollTop += dir * 16;
642
+ this._syncScrollButtons();
643
+ }, 30);
644
+ }
645
+
646
+ _stopScroll = (): void => {
647
+ clearInterval(this._scrollTimer);
648
+ };
649
+ }
650
+ UiSelect.register('ui-select');
@@ -25,7 +25,7 @@
25
25
  import { cn } from '../lib/utils.ts';
26
26
 
27
27
  const BASE =
28
- 'shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px';
28
+ 'cn-separator shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch';
29
29
 
30
30
  export type SeparatorOrientation = 'horizontal' | 'vertical';
31
31