@dowel-ui/react 0.4.0 → 0.7.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 (335) hide show
  1. package/README.md +9 -5
  2. package/dist/blocks/admin-dashboard/meta.js +31 -0
  3. package/dist/blocks/admin-dashboard/meta.js.map +1 -0
  4. package/dist/blocks/agent-console/meta.js +27 -0
  5. package/dist/blocks/agent-console/meta.js.map +1 -0
  6. package/dist/blocks/ai-dashboard/meta.js +25 -0
  7. package/dist/blocks/ai-dashboard/meta.js.map +1 -0
  8. package/dist/blocks/ai-workspace/meta.js +33 -0
  9. package/dist/blocks/ai-workspace/meta.js.map +1 -0
  10. package/dist/blocks/analytics/meta.js +24 -0
  11. package/dist/blocks/analytics/meta.js.map +1 -0
  12. package/dist/blocks/billing/meta.js +25 -0
  13. package/dist/blocks/billing/meta.js.map +1 -0
  14. package/dist/blocks/command-center/meta.js +28 -0
  15. package/dist/blocks/command-center/meta.js.map +1 -0
  16. package/dist/blocks/crm/meta.js +32 -0
  17. package/dist/blocks/crm/meta.js.map +1 -0
  18. package/dist/blocks/onboarding/meta.js +23 -0
  19. package/dist/blocks/onboarding/meta.js.map +1 -0
  20. package/dist/components/accordion/accordion.js +1 -1
  21. package/dist/components/accordion/accordion.js.map +1 -1
  22. package/dist/components/activity-feed/activity-feed.js +1 -1
  23. package/dist/components/activity-feed/activity-feed.js.map +1 -1
  24. package/dist/components/ai-agent-plan/ai-agent-plan.js +1 -1
  25. package/dist/components/ai-agent-plan/ai-agent-plan.js.map +1 -1
  26. package/dist/components/ai-extraction-review/ai-extraction-review.d.ts +52 -0
  27. package/dist/components/ai-extraction-review/ai-extraction-review.d.ts.map +1 -0
  28. package/dist/components/ai-extraction-review/ai-extraction-review.js +249 -0
  29. package/dist/components/ai-extraction-review/ai-extraction-review.js.map +1 -0
  30. package/dist/components/ai-extraction-review/extraction-model.d.ts +94 -0
  31. package/dist/components/ai-extraction-review/extraction-model.d.ts.map +1 -0
  32. package/dist/components/ai-extraction-review/extraction-model.js +91 -0
  33. package/dist/components/ai-extraction-review/extraction-model.js.map +1 -0
  34. package/dist/components/ai-extraction-review/index.d.ts +3 -0
  35. package/dist/components/ai-extraction-review/index.js +3 -0
  36. package/dist/components/ai-extraction-review/meta.js +17 -0
  37. package/dist/components/ai-extraction-review/meta.js.map +1 -0
  38. package/dist/components/ai-prompt-input/ai-prompt-input.js +1 -1
  39. package/dist/components/ai-prompt-input/ai-prompt-input.js.map +1 -1
  40. package/dist/components/ai-reasoning/ai-reasoning.js +1 -1
  41. package/dist/components/ai-reasoning/ai-reasoning.js.map +1 -1
  42. package/dist/components/ai-response/ai-response.js +2 -2
  43. package/dist/components/ai-response/ai-response.js.map +1 -1
  44. package/dist/components/ai-suggested-value/ai-suggested-value.d.ts +72 -0
  45. package/dist/components/ai-suggested-value/ai-suggested-value.d.ts.map +1 -0
  46. package/dist/components/ai-suggested-value/ai-suggested-value.js +126 -0
  47. package/dist/components/ai-suggested-value/ai-suggested-value.js.map +1 -0
  48. package/dist/components/ai-suggested-value/index.d.ts +2 -0
  49. package/dist/components/ai-suggested-value/index.js +2 -0
  50. package/dist/components/ai-suggested-value/meta.js +17 -0
  51. package/dist/components/ai-suggested-value/meta.js.map +1 -0
  52. package/dist/components/ai-tool/ai-tool.js +2 -2
  53. package/dist/components/ai-tool/ai-tool.js.map +1 -1
  54. package/dist/components/breadcrumb/breadcrumb.d.ts +52 -0
  55. package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -0
  56. package/dist/components/breadcrumb/breadcrumb.js +141 -0
  57. package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
  58. package/dist/components/breadcrumb/index.d.ts +2 -0
  59. package/dist/components/breadcrumb/index.js +2 -0
  60. package/dist/components/breadcrumb/meta.js +17 -0
  61. package/dist/components/breadcrumb/meta.js.map +1 -0
  62. package/dist/components/calendar/calendar.js +2 -2
  63. package/dist/components/calendar/calendar.js.map +1 -1
  64. package/dist/components/code-block/code-block.js +1 -1
  65. package/dist/components/code-block/code-block.js.map +1 -1
  66. package/dist/components/collapsible/collapsible.d.ts +30 -0
  67. package/dist/components/collapsible/collapsible.d.ts.map +1 -0
  68. package/dist/components/collapsible/collapsible.js +38 -0
  69. package/dist/components/collapsible/collapsible.js.map +1 -0
  70. package/dist/components/collapsible/index.d.ts +2 -0
  71. package/dist/components/collapsible/index.js +2 -0
  72. package/dist/components/collapsible/meta.js +17 -0
  73. package/dist/components/collapsible/meta.js.map +1 -0
  74. package/dist/components/combobox/combobox.js +2 -2
  75. package/dist/components/combobox/combobox.js.map +1 -1
  76. package/dist/components/command/command.js +1 -1
  77. package/dist/components/command/command.js.map +1 -1
  78. package/dist/components/confirm-typed/confirm-typed.d.ts +47 -0
  79. package/dist/components/confirm-typed/confirm-typed.d.ts.map +1 -0
  80. package/dist/components/confirm-typed/confirm-typed.js +106 -0
  81. package/dist/components/confirm-typed/confirm-typed.js.map +1 -0
  82. package/dist/components/confirm-typed/index.d.ts +2 -0
  83. package/dist/components/confirm-typed/index.js +2 -0
  84. package/dist/components/confirm-typed/meta.js +17 -0
  85. package/dist/components/confirm-typed/meta.js.map +1 -0
  86. package/dist/components/cron-editor/cron-editor.d.ts +36 -0
  87. package/dist/components/cron-editor/cron-editor.d.ts.map +1 -0
  88. package/dist/components/cron-editor/cron-editor.js +493 -0
  89. package/dist/components/cron-editor/cron-editor.js.map +1 -0
  90. package/dist/components/cron-editor/cron-expression.d.ts +82 -0
  91. package/dist/components/cron-editor/cron-expression.d.ts.map +1 -0
  92. package/dist/components/cron-editor/cron-expression.js +352 -0
  93. package/dist/components/cron-editor/cron-expression.js.map +1 -0
  94. package/dist/components/cron-editor/index.d.ts +3 -0
  95. package/dist/components/cron-editor/index.js +3 -0
  96. package/dist/components/cron-editor/meta.js +17 -0
  97. package/dist/components/cron-editor/meta.js.map +1 -0
  98. package/dist/components/data-table/data-table.d.ts.map +1 -1
  99. package/dist/components/data-table/data-table.js +4 -1
  100. package/dist/components/data-table/data-table.js.map +1 -1
  101. package/dist/components/dialog/dialog.js +2 -2
  102. package/dist/components/dialog/dialog.js.map +1 -1
  103. package/dist/components/diff-viewer/diff-viewer.js +4 -4
  104. package/dist/components/diff-viewer/diff-viewer.js.map +1 -1
  105. package/dist/components/direction/direction.d.ts +35 -0
  106. package/dist/components/direction/direction.d.ts.map +1 -0
  107. package/dist/components/direction/direction.js +14 -0
  108. package/dist/components/direction/direction.js.map +1 -0
  109. package/dist/components/direction/index.d.ts +2 -0
  110. package/dist/components/direction/index.js +2 -0
  111. package/dist/components/direction/meta.js +17 -0
  112. package/dist/components/direction/meta.js.map +1 -0
  113. package/dist/components/dns-record/dns-record.d.ts +61 -0
  114. package/dist/components/dns-record/dns-record.d.ts.map +1 -0
  115. package/dist/components/dns-record/dns-record.js +227 -0
  116. package/dist/components/dns-record/dns-record.js.map +1 -0
  117. package/dist/components/dns-record/index.d.ts +2 -0
  118. package/dist/components/dns-record/index.js +2 -0
  119. package/dist/components/dns-record/meta.js +17 -0
  120. package/dist/components/dns-record/meta.js.map +1 -0
  121. package/dist/components/drawer/drawer.js +1 -1
  122. package/dist/components/drawer/drawer.js.map +1 -1
  123. package/dist/components/dropdown-menu/dropdown-menu.d.ts.map +1 -1
  124. package/dist/components/dropdown-menu/dropdown-menu.js +9 -8
  125. package/dist/components/dropdown-menu/dropdown-menu.js.map +1 -1
  126. package/dist/components/input/input.js +1 -1
  127. package/dist/components/input/input.js.map +1 -1
  128. package/dist/components/log-viewer/log-viewer.js +2 -2
  129. package/dist/components/log-viewer/log-viewer.js.map +1 -1
  130. package/dist/components/meter/meter.js +1 -1
  131. package/dist/components/meter/meter.js.map +1 -1
  132. package/dist/components/pagination/pagination.d.ts.map +1 -1
  133. package/dist/components/pagination/pagination.js +3 -2
  134. package/dist/components/pagination/pagination.js.map +1 -1
  135. package/dist/components/permission-matrix/index.d.ts +3 -0
  136. package/dist/components/permission-matrix/index.js +3 -0
  137. package/dist/components/permission-matrix/meta.js +17 -0
  138. package/dist/components/permission-matrix/meta.js.map +1 -0
  139. package/dist/components/permission-matrix/permission-matrix.d.ts +46 -0
  140. package/dist/components/permission-matrix/permission-matrix.d.ts.map +1 -0
  141. package/dist/components/permission-matrix/permission-matrix.js +199 -0
  142. package/dist/components/permission-matrix/permission-matrix.js.map +1 -0
  143. package/dist/components/permission-matrix/permission-model.d.ts +53 -0
  144. package/dist/components/permission-matrix/permission-model.d.ts.map +1 -0
  145. package/dist/components/permission-matrix/permission-model.js +64 -0
  146. package/dist/components/permission-matrix/permission-model.js.map +1 -0
  147. package/dist/components/record-diff/record-diff.js +4 -4
  148. package/dist/components/record-diff/record-diff.js.map +1 -1
  149. package/dist/components/secret-field/index.d.ts +2 -0
  150. package/dist/components/secret-field/index.js +2 -0
  151. package/dist/components/secret-field/meta.js +17 -0
  152. package/dist/components/secret-field/meta.js.map +1 -0
  153. package/dist/components/secret-field/secret-field.d.ts +58 -0
  154. package/dist/components/secret-field/secret-field.d.ts.map +1 -0
  155. package/dist/components/secret-field/secret-field.js +168 -0
  156. package/dist/components/secret-field/secret-field.js.map +1 -0
  157. package/dist/components/select/select.js +2 -2
  158. package/dist/components/select/select.js.map +1 -1
  159. package/dist/components/session-expiry/index.d.ts +2 -0
  160. package/dist/components/session-expiry/index.js +2 -0
  161. package/dist/components/session-expiry/meta.js +17 -0
  162. package/dist/components/session-expiry/meta.js.map +1 -0
  163. package/dist/components/session-expiry/session-expiry.d.ts +64 -0
  164. package/dist/components/session-expiry/session-expiry.d.ts.map +1 -0
  165. package/dist/components/session-expiry/session-expiry.js +185 -0
  166. package/dist/components/session-expiry/session-expiry.js.map +1 -0
  167. package/dist/components/sheet/sheet.d.ts.map +1 -1
  168. package/dist/components/sheet/sheet.js +2 -2
  169. package/dist/components/sheet/sheet.js.map +1 -1
  170. package/dist/components/shortcut-recorder/index.d.ts +3 -0
  171. package/dist/components/shortcut-recorder/index.js +3 -0
  172. package/dist/components/shortcut-recorder/meta.js +17 -0
  173. package/dist/components/shortcut-recorder/meta.js.map +1 -0
  174. package/dist/components/shortcut-recorder/shortcut-model.d.ts +67 -0
  175. package/dist/components/shortcut-recorder/shortcut-model.d.ts.map +1 -0
  176. package/dist/components/shortcut-recorder/shortcut-model.js +168 -0
  177. package/dist/components/shortcut-recorder/shortcut-model.js.map +1 -0
  178. package/dist/components/shortcut-recorder/shortcut-recorder.d.ts +47 -0
  179. package/dist/components/shortcut-recorder/shortcut-recorder.d.ts.map +1 -0
  180. package/dist/components/shortcut-recorder/shortcut-recorder.js +212 -0
  181. package/dist/components/shortcut-recorder/shortcut-recorder.js.map +1 -0
  182. package/dist/components/sidebar/index.d.ts +2 -0
  183. package/dist/components/sidebar/index.js +2 -0
  184. package/dist/components/sidebar/meta.js +17 -0
  185. package/dist/components/sidebar/meta.js.map +1 -0
  186. package/dist/components/sidebar/sidebar.d.ts +69 -0
  187. package/dist/components/sidebar/sidebar.d.ts.map +1 -0
  188. package/dist/components/sidebar/sidebar.js +256 -0
  189. package/dist/components/sidebar/sidebar.js.map +1 -0
  190. package/dist/components/sync-status/index.d.ts +2 -0
  191. package/dist/components/sync-status/index.js +2 -0
  192. package/dist/components/sync-status/meta.js +17 -0
  193. package/dist/components/sync-status/meta.js.map +1 -0
  194. package/dist/components/sync-status/sync-status.d.ts +65 -0
  195. package/dist/components/sync-status/sync-status.d.ts.map +1 -0
  196. package/dist/components/sync-status/sync-status.js +103 -0
  197. package/dist/components/sync-status/sync-status.js.map +1 -0
  198. package/dist/components/table/table.js +2 -2
  199. package/dist/components/table/table.js.map +1 -1
  200. package/dist/components/tags-input/tags-input.js +1 -1
  201. package/dist/components/tags-input/tags-input.js.map +1 -1
  202. package/dist/components/textarea/index.d.ts +2 -0
  203. package/dist/components/textarea/index.js +2 -0
  204. package/dist/components/textarea/meta.js +17 -0
  205. package/dist/components/textarea/meta.js.map +1 -0
  206. package/dist/components/textarea/textarea.d.ts +43 -0
  207. package/dist/components/textarea/textarea.d.ts.map +1 -0
  208. package/dist/components/textarea/textarea.js +119 -0
  209. package/dist/components/textarea/textarea.js.map +1 -0
  210. package/dist/components/time-range-picker/time-range-picker.js +3 -3
  211. package/dist/components/time-range-picker/time-range-picker.js.map +1 -1
  212. package/dist/components/toast/toast.d.ts.map +1 -1
  213. package/dist/components/toast/toast.js +2 -2
  214. package/dist/components/toast/toast.js.map +1 -1
  215. package/dist/index.d.ts +36 -2
  216. package/dist/index.js +22 -3
  217. package/dist/lib/styles.d.ts +16 -1
  218. package/dist/lib/styles.d.ts.map +1 -1
  219. package/dist/lib/styles.js +16 -1
  220. package/dist/lib/styles.js.map +1 -1
  221. package/dist/registry/blocks.d.ts.map +1 -1
  222. package/dist/registry/blocks.js +27 -9
  223. package/dist/registry/blocks.js.map +1 -1
  224. package/dist/registry/components.d.ts.map +1 -1
  225. package/dist/registry/components.js +84 -54
  226. package/dist/registry/components.js.map +1 -1
  227. package/dist/registry/schema.d.ts +8 -0
  228. package/dist/registry/schema.d.ts.map +1 -1
  229. package/dist/registry/schema.js.map +1 -1
  230. package/package.json +5 -4
  231. package/src/components/accordion/accordion.tsx +1 -1
  232. package/src/components/activity-feed/activity-feed.tsx +1 -1
  233. package/src/components/ai-agent-plan/ai-agent-plan.tsx +0 -0
  234. package/src/components/ai-extraction-review/ai-extraction-review.tsx +492 -0
  235. package/src/components/ai-extraction-review/extraction-model.ts +159 -0
  236. package/src/components/ai-extraction-review/index.ts +13 -0
  237. package/src/components/ai-extraction-review/meta.ts +24 -0
  238. package/src/components/ai-prompt-input/ai-prompt-input.tsx +1 -1
  239. package/src/components/ai-reasoning/ai-reasoning.tsx +1 -1
  240. package/src/components/ai-response/ai-response.tsx +2 -2
  241. package/src/components/ai-suggested-value/ai-suggested-value.tsx +295 -0
  242. package/src/components/ai-suggested-value/index.ts +6 -0
  243. package/src/components/ai-suggested-value/meta.ts +24 -0
  244. package/src/components/ai-tool/ai-tool.tsx +2 -2
  245. package/src/components/breadcrumb/breadcrumb.tsx +157 -0
  246. package/src/components/breadcrumb/index.ts +10 -0
  247. package/src/components/breadcrumb/meta.ts +19 -0
  248. package/src/components/calendar/calendar.tsx +2 -2
  249. package/src/components/code-block/code-block.tsx +1 -1
  250. package/src/components/collapsible/collapsible.tsx +63 -0
  251. package/src/components/collapsible/index.ts +6 -0
  252. package/src/components/collapsible/meta.ts +16 -0
  253. package/src/components/combobox/combobox.tsx +2 -2
  254. package/src/components/command/command.tsx +1 -1
  255. package/src/components/confirm-typed/confirm-typed.tsx +191 -0
  256. package/src/components/confirm-typed/index.ts +1 -0
  257. package/src/components/confirm-typed/meta.ts +22 -0
  258. package/src/components/cron-editor/cron-editor.tsx +676 -0
  259. package/src/components/cron-editor/cron-expression.ts +552 -0
  260. package/src/components/cron-editor/index.ts +23 -0
  261. package/src/components/cron-editor/meta.ts +23 -0
  262. package/src/components/data-table/data-table.tsx +14 -3
  263. package/src/components/dialog/dialog.tsx +2 -2
  264. package/src/components/diff-viewer/diff-viewer.tsx +4 -4
  265. package/src/components/direction/direction.tsx +38 -0
  266. package/src/components/direction/index.ts +1 -0
  267. package/src/components/direction/meta.ts +19 -0
  268. package/src/components/dns-record/dns-record.tsx +315 -0
  269. package/src/components/dns-record/index.ts +7 -0
  270. package/src/components/dns-record/meta.ts +21 -0
  271. package/src/components/drawer/drawer.tsx +1 -1
  272. package/src/components/dropdown-menu/dropdown-menu.tsx +14 -8
  273. package/src/components/input/input.tsx +1 -1
  274. package/src/components/log-viewer/log-viewer.tsx +2 -2
  275. package/src/components/meter/meter.tsx +1 -1
  276. package/src/components/pagination/pagination.tsx +13 -2
  277. package/src/components/permission-matrix/index.ts +14 -0
  278. package/src/components/permission-matrix/meta.ts +23 -0
  279. package/src/components/permission-matrix/permission-matrix.tsx +368 -0
  280. package/src/components/permission-matrix/permission-model.ts +120 -0
  281. package/src/components/record-diff/record-diff.tsx +4 -4
  282. package/src/components/secret-field/index.ts +6 -0
  283. package/src/components/secret-field/meta.ts +21 -0
  284. package/src/components/secret-field/secret-field.tsx +284 -0
  285. package/src/components/select/select.tsx +2 -2
  286. package/src/components/session-expiry/index.ts +9 -0
  287. package/src/components/session-expiry/meta.ts +23 -0
  288. package/src/components/session-expiry/session-expiry.tsx +321 -0
  289. package/src/components/sheet/sheet.tsx +8 -4
  290. package/src/components/shortcut-recorder/index.ts +19 -0
  291. package/src/components/shortcut-recorder/meta.ts +22 -0
  292. package/src/components/shortcut-recorder/shortcut-model.ts +204 -0
  293. package/src/components/shortcut-recorder/shortcut-recorder.tsx +336 -0
  294. package/src/components/sidebar/index.ts +19 -0
  295. package/src/components/sidebar/meta.ts +25 -0
  296. package/src/components/sidebar/sidebar.tsx +374 -0
  297. package/src/components/sync-status/index.ts +9 -0
  298. package/src/components/sync-status/meta.ts +21 -0
  299. package/src/components/sync-status/sync-status.tsx +217 -0
  300. package/src/components/table/table.tsx +3 -3
  301. package/src/components/tags-input/tags-input.tsx +1 -1
  302. package/src/components/textarea/index.ts +1 -0
  303. package/src/components/textarea/meta.ts +21 -0
  304. package/src/components/textarea/textarea.tsx +210 -0
  305. package/src/components/time-range-picker/time-range-picker.tsx +3 -3
  306. package/src/components/toast/toast.tsx +14 -6
  307. package/src/index.ts +15 -0
  308. package/src/lib/styles.ts +16 -0
  309. package/src/registry/blocks.ts +18 -0
  310. package/src/registry/components.ts +30 -0
  311. package/src/registry/schema.ts +8 -0
  312. package/src/blocks/admin-users/admin-users.tsx +0 -251
  313. package/src/blocks/admin-users/index.ts +0 -1
  314. package/src/blocks/admin-users/meta.ts +0 -27
  315. package/src/blocks/ai-chat/ai-chat.tsx +0 -280
  316. package/src/blocks/ai-chat/index.ts +0 -7
  317. package/src/blocks/ai-chat/meta.ts +0 -32
  318. package/src/blocks/dashboard/dashboard.tsx +0 -192
  319. package/src/blocks/dashboard/index.ts +0 -6
  320. package/src/blocks/dashboard/meta.ts +0 -18
  321. package/src/blocks/forgot-password/forgot-password.tsx +0 -158
  322. package/src/blocks/forgot-password/index.ts +0 -1
  323. package/src/blocks/forgot-password/meta.ts +0 -18
  324. package/src/blocks/login/index.ts +0 -1
  325. package/src/blocks/login/login.tsx +0 -178
  326. package/src/blocks/login/meta.ts +0 -18
  327. package/src/blocks/pricing/index.ts +0 -1
  328. package/src/blocks/pricing/meta.ts +0 -18
  329. package/src/blocks/pricing/pricing.tsx +0 -176
  330. package/src/blocks/settings/index.ts +0 -6
  331. package/src/blocks/settings/meta.ts +0 -29
  332. package/src/blocks/settings/settings.tsx +0 -317
  333. package/src/blocks/signup/index.ts +0 -1
  334. package/src/blocks/signup/meta.ts +0 -18
  335. package/src/blocks/signup/signup.tsx +0 -240
@@ -0,0 +1,157 @@
1
+ import { Slot } from "radix-ui";
2
+ import type { ComponentPropsWithRef } from "react";
3
+
4
+ import { focusRing, mirrorForDirection } from "@/lib/styles";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ /**
8
+ * Where this page sits in the hierarchy.
9
+ *
10
+ * Three things separate a breadcrumb that works from one that only looks like
11
+ * one, and all three are about the last item.
12
+ *
13
+ * It is not a link. A link to the page you are already on is a control that
14
+ * does nothing, and in a screen reader's list of links it is indistinguishable
15
+ * from the ones that go somewhere.
16
+ *
17
+ * It carries `aria-current="page"`. Bolding it says "you are here" to people
18
+ * who can see it and to nobody else.
19
+ *
20
+ * The separators are hidden. A slash between every item is read aloud as
21
+ * "slash" — "Home slash Projects slash Settings" — which is the punctuation of
22
+ * the design leaking into the content.
23
+ */
24
+
25
+ export function Breadcrumb({ className, ...props }: ComponentPropsWithRef<"nav">) {
26
+ return (
27
+ <nav
28
+ // Named, because a page can have more than one navigation landmark and
29
+ // "navigation" on its own does not say which this is.
30
+ aria-label="Breadcrumb"
31
+ data-slot="breadcrumb"
32
+ className={cn("min-w-0", className)}
33
+ {...props}
34
+ />
35
+ );
36
+ }
37
+
38
+ export function BreadcrumbList({ className, ...props }: ComponentPropsWithRef<"ol">) {
39
+ return (
40
+ <ol
41
+ data-slot="breadcrumb-list"
42
+ className={cn(
43
+ "flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground",
44
+ className,
45
+ )}
46
+ {...props}
47
+ />
48
+ );
49
+ }
50
+
51
+ export function BreadcrumbItem({ className, ...props }: ComponentPropsWithRef<"li">) {
52
+ return (
53
+ <li
54
+ data-slot="breadcrumb-item"
55
+ className={cn("inline-flex items-center gap-1.5", className)}
56
+ {...props}
57
+ />
58
+ );
59
+ }
60
+
61
+ export interface BreadcrumbLinkProps extends ComponentPropsWithRef<"a"> {
62
+ /** Render the child as the link, for a router's own component. */
63
+ asChild?: boolean;
64
+ }
65
+
66
+ export function BreadcrumbLink({ className, asChild = false, ...props }: BreadcrumbLinkProps) {
67
+ const Component = asChild ? Slot.Root : "a";
68
+
69
+ return (
70
+ <Component
71
+ data-slot="breadcrumb-link"
72
+ className={cn(
73
+ "rounded-sm transition-colors duration-[var(--duration-fast)]",
74
+ "hover:text-foreground",
75
+ focusRing,
76
+ className,
77
+ )}
78
+ {...props}
79
+ />
80
+ );
81
+ }
82
+
83
+ /**
84
+ * The page you are on.
85
+ *
86
+ * A span, not a link: there is nowhere to go. `aria-current="page"` is what
87
+ * says "you are here" to anything that is not looking at the font weight.
88
+ */
89
+ export function BreadcrumbPage({ className, ...props }: ComponentPropsWithRef<"span">) {
90
+ return (
91
+ <span
92
+ data-slot="breadcrumb-page"
93
+ aria-current="page"
94
+ className={cn("font-medium text-foreground", className)}
95
+ {...props}
96
+ />
97
+ );
98
+ }
99
+
100
+ /**
101
+ * The mark between items.
102
+ *
103
+ * Hidden from assistive technology and given no accessible name: the list
104
+ * structure already conveys the sequence, and reading "slash" between every
105
+ * item is the design's punctuation leaking into the content.
106
+ */
107
+ export function BreadcrumbSeparator({
108
+ className,
109
+ children,
110
+ ...props
111
+ }: ComponentPropsWithRef<"li">) {
112
+ return (
113
+ <li
114
+ data-slot="breadcrumb-separator"
115
+ role="presentation"
116
+ aria-hidden
117
+ className={cn("[&>svg]:size-3.5", className)}
118
+ {...props}
119
+ >
120
+ {children ?? (
121
+ <svg viewBox="0 0 24 24" fill="none" className={mirrorForDirection}>
122
+ <path
123
+ d="m9 18 6-6-6-6"
124
+ stroke="currentColor"
125
+ strokeWidth="2"
126
+ strokeLinecap="round"
127
+ strokeLinejoin="round"
128
+ />
129
+ </svg>
130
+ )}
131
+ </li>
132
+ );
133
+ }
134
+
135
+ /**
136
+ * Stands in for the middle of a long trail.
137
+ *
138
+ * Named rather than hidden, because unlike a separator it is *content*: it
139
+ * says levels have been left out. A bare "…" announces as nothing at all.
140
+ */
141
+ export function BreadcrumbEllipsis({ className, ...props }: ComponentPropsWithRef<"span">) {
142
+ return (
143
+ <span
144
+ data-slot="breadcrumb-ellipsis"
145
+ role="presentation"
146
+ className={cn("flex size-5 items-center justify-center", className)}
147
+ {...props}
148
+ >
149
+ <svg viewBox="0 0 24 24" fill="none" aria-hidden className="size-4">
150
+ <circle cx="5" cy="12" r="1.5" fill="currentColor" />
151
+ <circle cx="12" cy="12" r="1.5" fill="currentColor" />
152
+ <circle cx="19" cy="12" r="1.5" fill="currentColor" />
153
+ </svg>
154
+ <span className="sr-only">Intermediate levels</span>
155
+ </span>
156
+ );
157
+ }
@@ -0,0 +1,10 @@
1
+ export {
2
+ Breadcrumb,
3
+ BreadcrumbEllipsis,
4
+ BreadcrumbItem,
5
+ BreadcrumbLink,
6
+ BreadcrumbList,
7
+ BreadcrumbPage,
8
+ BreadcrumbSeparator,
9
+ type BreadcrumbLinkProps,
10
+ } from "./breadcrumb";
@@ -0,0 +1,19 @@
1
+ import { defineMeta } from "@/registry/schema";
2
+
3
+ export const meta = defineMeta({
4
+ name: "breadcrumb",
5
+ title: "Breadcrumb",
6
+ description: "Where the current page sits in the hierarchy, and how to get back up it.",
7
+ category: "navigation",
8
+ status: "stable",
9
+ dependencies: ["radix-ui"],
10
+ registryDependencies: [],
11
+ files: ["breadcrumb.tsx"],
12
+ a11y:
13
+ 'The current page is a span carrying aria-current="page", not a link: a link to the page ' +
14
+ "you are already on does nothing, and in a list of links it is indistinguishable from the " +
15
+ "ones that go somewhere. Separators are hidden from assistive technology, because the list " +
16
+ 'already conveys the sequence and reading "slash" between every item is the design\'s ' +
17
+ "punctuation leaking into the content. The ellipsis is the exception and is named, because " +
18
+ "it is content: it says levels have been left out.",
19
+ });
@@ -70,12 +70,12 @@ export function Calendar({
70
70
  ),
71
71
  // Range middles keep the muted surface so the two ends stay legible as
72
72
  // the actual selection.
73
- range_start: cn(defaults.range_start, "rounded-l-md bg-accent"),
73
+ range_start: cn(defaults.range_start, "rounded-s-md bg-accent"),
74
74
  range_middle: cn(
75
75
  defaults.range_middle,
76
76
  "bg-accent [&>button]:bg-transparent [&>button]:text-accent-foreground",
77
77
  ),
78
- range_end: cn(defaults.range_end, "rounded-r-md bg-accent"),
78
+ range_end: cn(defaults.range_end, "rounded-e-md bg-accent"),
79
79
  today: cn(defaults.today, "[&>button]:font-semibold [&>button]:text-primary"),
80
80
  outside: cn(defaults.outside, "text-muted-foreground opacity-50"),
81
81
  disabled: cn(defaults.disabled, "opacity-40"),
@@ -143,7 +143,7 @@ export function CopyButton({
143
143
  onClick={copy}
144
144
  aria-label={label}
145
145
  className={cn(
146
- "ml-auto grid size-6 shrink-0 place-items-center rounded text-muted-foreground",
146
+ "ms-auto grid size-6 shrink-0 place-items-center rounded text-muted-foreground",
147
147
  "transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-foreground",
148
148
  focusRing,
149
149
  className,
@@ -0,0 +1,63 @@
1
+ "use client";
2
+
3
+ import { Collapsible as CollapsiblePrimitive } from "radix-ui";
4
+ import type { ComponentPropsWithRef } from "react";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ /**
9
+ * A section that opens and closes.
10
+ *
11
+ * Unlike Accordion, there is no set: one region, one trigger, no notion of
12
+ * "only one open at a time". Reaching for an Accordion with a single item
13
+ * gives that item a `heading` role and a position in a list of one, both of
14
+ * which are noise.
15
+ *
16
+ * The height animation is driven by the primitive's own measured height rather
17
+ * than a guess, so content of any size opens smoothly, and it derives from
18
+ * `--duration-*` so the motion scale applies.
19
+ */
20
+
21
+ export type CollapsibleProps = ComponentPropsWithRef<typeof CollapsiblePrimitive.Root>;
22
+
23
+ /**
24
+ * Wrapped rather than re-exported.
25
+ *
26
+ * A bare `export const Collapsible = CollapsiblePrimitive.Root` leaves the
27
+ * inferred type pointing into the primitive's own package, which TypeScript
28
+ * cannot name in declaration output (TS2883). It also gives every other
29
+ * component's `data-slot` a hole where this one should be.
30
+ */
31
+ export function Collapsible({ className, ...props }: CollapsibleProps) {
32
+ return <CollapsiblePrimitive.Root data-slot="collapsible" className={className} {...props} />;
33
+ }
34
+
35
+ export function CollapsibleTrigger({
36
+ className,
37
+ ...props
38
+ }: ComponentPropsWithRef<typeof CollapsiblePrimitive.Trigger>) {
39
+ return (
40
+ <CollapsiblePrimitive.Trigger
41
+ data-slot="collapsible-trigger"
42
+ className={cn("outline-none", className)}
43
+ {...props}
44
+ />
45
+ );
46
+ }
47
+
48
+ export function CollapsibleContent({
49
+ className,
50
+ ...props
51
+ }: ComponentPropsWithRef<typeof CollapsiblePrimitive.Content>) {
52
+ return (
53
+ <CollapsiblePrimitive.Content
54
+ data-slot="collapsible-content"
55
+ className={cn(
56
+ "overflow-hidden",
57
+ "data-[state=closed]:animate-collapse-up data-[state=open]:animate-collapse-down",
58
+ className,
59
+ )}
60
+ {...props}
61
+ />
62
+ );
63
+ }
@@ -0,0 +1,6 @@
1
+ export {
2
+ Collapsible,
3
+ CollapsibleContent,
4
+ CollapsibleTrigger,
5
+ type CollapsibleProps,
6
+ } from "./collapsible";
@@ -0,0 +1,16 @@
1
+ import { defineMeta } from "@/registry/schema";
2
+
3
+ export const meta = defineMeta({
4
+ name: "collapsible",
5
+ title: "Collapsible",
6
+ description: "A single section that opens and closes, without an accordion's set semantics.",
7
+ category: "layout",
8
+ status: "stable",
9
+ dependencies: ["radix-ui"],
10
+ registryDependencies: [],
11
+ files: ["collapsible.tsx"],
12
+ a11y:
13
+ "The trigger carries aria-expanded and points at the region it controls, both from the " +
14
+ "primitive. Reach for this rather than an Accordion of one item: a single-item Accordion " +
15
+ "gives its trigger a heading role and a position in a list of one, neither of which is true.",
16
+ });
@@ -499,7 +499,7 @@ export function ComboboxItem({
499
499
  }
500
500
  }}
501
501
  className={cn(
502
- "relative flex cursor-default items-center gap-2 rounded-md py-1.5 pr-8 pl-2 text-sm outline-none select-none",
502
+ "relative flex cursor-default items-center gap-2 rounded-md py-1.5 ps-2 pe-8 text-sm outline-none select-none",
503
503
  "transition-colors duration-[var(--duration-instant)]",
504
504
  "data-[active]:bg-accent data-[active]:text-accent-foreground",
505
505
  "data-[disabled]:pointer-events-none data-[disabled]:opacity-55",
@@ -510,7 +510,7 @@ export function ComboboxItem({
510
510
  >
511
511
  {children ?? value}
512
512
  {selected ? (
513
- <span className="absolute right-2 grid size-4 place-items-center">
513
+ <span className="absolute end-2 grid size-4 place-items-center">
514
514
  <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="size-3.5">
515
515
  <path
516
516
  d="m5 13 4 4L19 7"
@@ -438,7 +438,7 @@ export function CommandShortcut({ className, ...props }: ComponentPropsWithRef<"
438
438
  <span
439
439
  data-slot="command-shortcut"
440
440
  aria-hidden="true"
441
- className={cn("ml-auto text-2xs tracking-wide text-muted-foreground", className)}
441
+ className={cn("ms-auto text-2xs tracking-wide text-muted-foreground", className)}
442
442
  {...props}
443
443
  />
444
444
  );
@@ -0,0 +1,191 @@
1
+ "use client";
2
+
3
+ import {
4
+ useId,
5
+ useRef,
6
+ useState,
7
+ type ComponentPropsWithRef,
8
+ type KeyboardEvent,
9
+ type ReactNode,
10
+ } from "react";
11
+
12
+ import { Button } from "@/components/button";
13
+ import { Input } from "@/components/input";
14
+ import { cn } from "@/lib/utils";
15
+
16
+ /**
17
+ * Type the name to confirm.
18
+ *
19
+ * The GitHub pattern for deleting a repository, and the one every product
20
+ * copies for the action that cannot be undone: the name of the thing has to
21
+ * pass through the reader's fingers before the button works. Trivial to
22
+ * build, absent from every component library, and usually built wrong in
23
+ * the one place it matters — what happens when the text does not match.
24
+ * The common version disables the button and says nothing, so a keyboard
25
+ * or screen reader user presses it, or presses Enter, and nothing happens
26
+ * at all.
27
+ *
28
+ * Here a mismatch is a thing that is said. The button stays reachable and
29
+ * pressing it, or Enter, before the text matches announces what was
30
+ * expected and marks the field invalid. The moment the text matches, that
31
+ * is announced too, once, so a reader knows the action has become
32
+ * available without watching the button change colour.
33
+ *
34
+ * Pasting is allowed. Blocking it is a popular piece of friction, and it
35
+ * punishes exactly the people who cannot type a long name easily — switch
36
+ * users, voice users, anyone with a tremor — while stopping nobody who can
37
+ * select-all and copy. The name is on screen either way; the point is that
38
+ * it passed through the reader's attention, not their keyboard.
39
+ */
40
+
41
+ export interface ConfirmTypedProps extends Omit<ComponentPropsWithRef<"div">, "children"> {
42
+ /** The text that must be typed, exactly. */
43
+ expected: string;
44
+ /** What confirming does, as the button reads: "Delete project". */
45
+ action: string;
46
+ onConfirm: () => void;
47
+ /** While the action runs. The button stays focusable and says so. */
48
+ pending?: boolean;
49
+ /** Match regardless of case. Off by default: a name is a name. */
50
+ caseSensitive?: boolean;
51
+ variant?: "destructive" | "primary";
52
+ /** The typed text. Controlled, if you want it. */
53
+ value?: string;
54
+ onValueChange?: (value: string) => void;
55
+ /** The consequence, in a sentence or two, before the field. */
56
+ children?: ReactNode;
57
+ }
58
+
59
+ export function ConfirmTyped({
60
+ className,
61
+ expected,
62
+ action,
63
+ onConfirm,
64
+ pending = false,
65
+ caseSensitive = true,
66
+ variant = "destructive",
67
+ value: valueProp,
68
+ onValueChange,
69
+ children,
70
+ ...props
71
+ }: ConfirmTypedProps) {
72
+ const id = useId();
73
+ const inputRef = useRef<HTMLInputElement>(null);
74
+
75
+ const [uncontrolled, setUncontrolled] = useState("");
76
+ const value = valueProp ?? uncontrolled;
77
+
78
+ const [attempted, setAttempted] = useState(false);
79
+ const [announcement, setAnnouncement] = useState("");
80
+
81
+ // Surrounding whitespace never decides it: a trailing space is not a
82
+ // different name, and a reader cannot see it to know why it failed.
83
+ const normalise = (text: string) => (caseSensitive ? text.trim() : text.trim().toLowerCase());
84
+ const matched = value.length > 0 && normalise(value) === normalise(expected);
85
+
86
+ // Announce the match once, on the transition. Adjusted during render so the
87
+ // announcement lands in the same commit as the state it describes.
88
+ const [wasMatched, setWasMatched] = useState(matched);
89
+ if (wasMatched !== matched) {
90
+ setWasMatched(matched);
91
+ if (matched) {
92
+ setAnnouncement(`Matches. ${action} is available.`);
93
+ setAttempted(false);
94
+ }
95
+ }
96
+
97
+ const setValue = (next: string) => {
98
+ setUncontrolled(next);
99
+ onValueChange?.(next);
100
+ };
101
+
102
+ const attempt = () => {
103
+ if (pending) return;
104
+ if (matched) {
105
+ onConfirm();
106
+ return;
107
+ }
108
+ // Said, not swallowed. A press that does nothing tells a keyboard user
109
+ // nothing; this one tells them what was expected.
110
+ setAttempted(true);
111
+ setAnnouncement(`Does not match. Type ${expected} exactly.`);
112
+ inputRef.current?.focus();
113
+ };
114
+
115
+ const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
116
+ if (event.key !== "Enter" || event.nativeEvent.isComposing) return;
117
+ event.preventDefault();
118
+ attempt();
119
+ };
120
+
121
+ const mismatch = attempted && !matched;
122
+ const hint = matched
123
+ ? "Matches."
124
+ : mismatch
125
+ ? `Does not match. Type ${expected} exactly.`
126
+ : caseSensitive
127
+ ? "Type it exactly as shown."
128
+ : "Type it as shown. Case does not matter.";
129
+
130
+ return (
131
+ <div
132
+ data-slot="confirm-typed"
133
+ data-state={matched ? "matched" : mismatch ? "mismatched" : "idle"}
134
+ className={cn("flex flex-col gap-2", className)}
135
+ {...props}
136
+ >
137
+ {children}
138
+
139
+ <label htmlFor={`${id}-input`} className="text-sm">
140
+ To confirm, type{" "}
141
+ <strong className="rounded bg-muted px-1 py-0.5 font-mono text-xs font-medium select-all">
142
+ {expected}
143
+ </strong>
144
+ </label>
145
+
146
+ <Input
147
+ ref={inputRef}
148
+ id={`${id}-input`}
149
+ value={value}
150
+ autoComplete="off"
151
+ autoCapitalize="off"
152
+ spellCheck={false}
153
+ aria-describedby={`${id}-hint`}
154
+ aria-invalid={mismatch || undefined}
155
+ onChange={(event) => {
156
+ setValue(event.target.value);
157
+ }}
158
+ onKeyDown={onKeyDown}
159
+ />
160
+
161
+ <p
162
+ id={`${id}-hint`}
163
+ data-slot="confirm-typed-hint"
164
+ className={cn(
165
+ "text-xs",
166
+ mismatch ? "text-destructive" : matched ? "text-success" : "text-muted-foreground",
167
+ )}
168
+ >
169
+ {hint}
170
+ </p>
171
+
172
+ {/* Present from the first render, so the first announcement is heard. */}
173
+ <span role="status" aria-live="polite" className="sr-only">
174
+ {announcement}
175
+ </span>
176
+
177
+ <Button
178
+ variant={variant}
179
+ loading={pending}
180
+ data-ready={matched || undefined}
181
+ aria-describedby={matched ? undefined : `${id}-hint`}
182
+ // Dimmed rather than disabled while the text does not match: it stays
183
+ // reachable, and pressing it says why nothing happened.
184
+ className={cn("self-start", !matched && !pending && "opacity-55")}
185
+ onClick={attempt}
186
+ >
187
+ {pending ? `${action}…` : action}
188
+ </Button>
189
+ </div>
190
+ );
191
+ }
@@ -0,0 +1 @@
1
+ export { ConfirmTyped, type ConfirmTypedProps } from "./confirm-typed";
@@ -0,0 +1,22 @@
1
+ import { defineMeta } from "@/registry/schema";
2
+
3
+ export const meta = defineMeta({
4
+ name: "confirm-typed",
5
+ title: "Confirm Typed",
6
+ description: "Type the name to confirm — and be told, not ignored, when it does not match.",
7
+ category: "form",
8
+ status: "stable",
9
+ dependencies: [],
10
+ registryDependencies: ["button", "input"],
11
+ files: ["confirm-typed.tsx"],
12
+ a11y:
13
+ "The button stays reachable while the text does not match, dimmed rather than disabled, and " +
14
+ "pressing it or Enter announces what was expected, marks the field invalid and returns focus " +
15
+ "to it — the common version disables the button and says nothing, so a keyboard user cannot " +
16
+ "tell whether anything happened. The match is announced once, on the transition, naming the " +
17
+ "action that became available, so nobody has to watch the button change colour. Typing " +
18
+ "itself stays silent, because a verdict on every keystroke is noise. The hint is the field's " +
19
+ "description and the button's, and the status region exists from the first render so the " +
20
+ "first announcement is heard. Pasting is allowed: blocking it punishes people who cannot " +
21
+ "type a long name easily and stops nobody who can select-all and copy.",
22
+ });