@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,38 @@
1
+ "use client";
2
+
3
+ import { Direction as DirectionPrimitive } from "radix-ui";
4
+ import type { ReactNode } from "react";
5
+
6
+ /**
7
+ * Tells the component set which way the writing runs.
8
+ *
9
+ * Every direction-dependent style in this library is written logically —
10
+ * `ps-`, `me-`, `text-end` — so the CSS follows `dir` on its own. The
11
+ * primitives underneath do not: several of them read direction from React
12
+ * context rather than from the document, and with no provider they assume
13
+ * left-to-right and set `dir="ltr"` on their own elements. The result is a page
14
+ * that mirrors correctly except for its menus, selects and sliders, which is
15
+ * worse than one that does not mirror at all, because it looks deliberate.
16
+ *
17
+ * So an RTL application needs both: `dir` on the document for the CSS, and this
18
+ * for the primitives.
19
+ *
20
+ * ```tsx
21
+ * <html dir="rtl" lang="ar">
22
+ * <body>
23
+ * <DirectionProvider dir="rtl">{children}</DirectionProvider>
24
+ * </body>
25
+ * </html>
26
+ * ```
27
+ *
28
+ * A left-to-right application needs neither. Nothing here is required to render
29
+ * English correctly, which is why it is easy to ship without it and never know.
30
+ */
31
+ export interface DirectionProviderProps {
32
+ dir: "ltr" | "rtl";
33
+ children?: ReactNode;
34
+ }
35
+
36
+ export function DirectionProvider({ dir, children }: DirectionProviderProps) {
37
+ return <DirectionPrimitive.Provider dir={dir}>{children}</DirectionPrimitive.Provider>;
38
+ }
@@ -0,0 +1 @@
1
+ export { DirectionProvider, type DirectionProviderProps } from "./direction";
@@ -0,0 +1,19 @@
1
+ import { defineMeta } from "@/registry/schema";
2
+
3
+ export const meta = defineMeta({
4
+ name: "direction",
5
+ title: "Direction Provider",
6
+ description:
7
+ "Tells the component primitives which way the writing runs, so menus and sliders mirror with the rest of the page.",
8
+ category: "foundation",
9
+ status: "stable",
10
+ dependencies: ["radix-ui"],
11
+ registryDependencies: [],
12
+ files: ["direction.tsx"],
13
+ a11y:
14
+ "Needed only for right-to-left languages, and needed in addition to `dir` on the document " +
15
+ "rather than instead of it. The styling here is written with logical properties and follows " +
16
+ "`dir` by itself; the primitives underneath read direction from React context and assume " +
17
+ "left-to-right without a provider, which mirrors a page correctly except for its menus, " +
18
+ "selects and sliders — worse than not mirroring at all, because it looks deliberate.",
19
+ });
@@ -0,0 +1,315 @@
1
+ "use client";
2
+
3
+ import {
4
+ useEffect,
5
+ useId,
6
+ useRef,
7
+ useState,
8
+ type ComponentPropsWithRef,
9
+ type ReactNode,
10
+ } from "react";
11
+
12
+ import { Button } from "@/components/button";
13
+ import { Spinner } from "@/components/spinner";
14
+ import { focusRing } from "@/lib/styles";
15
+ import { cn } from "@/lib/utils";
16
+
17
+ /**
18
+ * "Add this record at your DNS provider."
19
+ *
20
+ * Vercel, Resend, Cloudflare, Postmark, Google Workspace and every product
21
+ * that verifies a domain or routes its mail draws this card by hand, and
22
+ * they all learn the same three things the hard way.
23
+ *
24
+ * The parts are copied separately. A provider's form has a Name field, a
25
+ * Type field and a Value field, so one button that copies the whole line
26
+ * copies something nobody can paste anywhere.
27
+ *
28
+ * The Name field is a trap. Some providers want the host relative to the
29
+ * zone, `_dmarc`; some want the full name, `_dmarc.acme.com`; and some take
30
+ * the relative form and append the zone themselves, so a full name pasted
31
+ * in becomes `_dmarc.acme.com.acme.com` and the check fails for a reason
32
+ * nobody can see. So the host is shown both ways, with the sentence that
33
+ * explains which to use.
34
+ *
35
+ * A failed check says what was found. "Not verified" sends people back to
36
+ * their provider to stare at a record that is correct and has not
37
+ * propagated yet; "found v=spf1 -all, expected v=spf1 include:…" sends them
38
+ * to the typo. And nothing found at all is said as nothing found, with how
39
+ * long that can take, because it is not the same as wrong.
40
+ */
41
+
42
+ export type DnsRecordStatus = "pending" | "checking" | "verified" | "failed";
43
+
44
+ export interface DnsHostForms {
45
+ /** Relative to the zone: `_dmarc`, or `@` for the zone itself. */
46
+ relative: string;
47
+ /** Fully qualified: `_dmarc.acme.com`. */
48
+ full: string;
49
+ }
50
+
51
+ /**
52
+ * The host both ways a provider might want it. Accepts either form and a
53
+ * trailing dot, so the record can be declared however the backend has it.
54
+ */
55
+ export function dnsHostForms(name: string, zone: string): DnsHostForms {
56
+ const cleanZone = zone.replace(/\.$/, "");
57
+ const cleanName = name.replace(/\.$/, "");
58
+ if (cleanName === "@" || cleanName === "" || cleanName === cleanZone) {
59
+ return { relative: "@", full: cleanZone };
60
+ }
61
+ if (cleanName.endsWith(`.${cleanZone}`)) {
62
+ return { relative: cleanName.slice(0, -cleanZone.length - 1), full: cleanName };
63
+ }
64
+ return { relative: cleanName, full: `${cleanName}.${cleanZone}` };
65
+ }
66
+
67
+ export interface DnsRecordProps extends Omit<ComponentPropsWithRef<"section">, "children"> {
68
+ type: string;
69
+ /** The host, relative or fully qualified. */
70
+ name: string;
71
+ value: string;
72
+ /** The zone the record belongs to. Enables the host to be shown both ways. */
73
+ zone?: string;
74
+ ttl?: number | string;
75
+ priority?: number;
76
+ /** What the record is for, in the reader's language: "Proves you own acme.com". */
77
+ purpose?: string;
78
+ status?: DnsRecordStatus;
79
+ /** What the check found instead, when it failed. The useful part. */
80
+ found?: string[];
81
+ checkedAt?: Date;
82
+ locale?: string;
83
+ onCheck?: () => void;
84
+ children?: ReactNode;
85
+ }
86
+
87
+ type CopyState = "idle" | "copied" | "error";
88
+
89
+ export function DnsRecord({
90
+ className,
91
+ type,
92
+ name,
93
+ value,
94
+ zone,
95
+ ttl,
96
+ priority,
97
+ purpose,
98
+ status = "pending",
99
+ found,
100
+ checkedAt,
101
+ locale,
102
+ onCheck,
103
+ children,
104
+ ...props
105
+ }: DnsRecordProps) {
106
+ const id = useId();
107
+ const forms = zone ? dnsHostForms(name, zone) : null;
108
+ const host = forms ? forms.relative : name;
109
+
110
+ // Which part was copied, and whether it worked, so the button that was
111
+ // pressed is the one that says so.
112
+ const [copy, setCopy] = useState<{ part: string; state: CopyState } | null>(null);
113
+ const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
114
+ useEffect(
115
+ () => () => {
116
+ if (timer.current) clearTimeout(timer.current);
117
+ },
118
+ [],
119
+ );
120
+
121
+ const copyPart = (part: string, text: string) => {
122
+ const settle = (state: CopyState) => {
123
+ setCopy({ part, state });
124
+ if (timer.current) clearTimeout(timer.current);
125
+ timer.current = setTimeout(() => {
126
+ setCopy(null);
127
+ }, 2000);
128
+ };
129
+ Promise.resolve()
130
+ .then(() => navigator.clipboard.writeText(text))
131
+ .then(
132
+ () => {
133
+ settle("copied");
134
+ },
135
+ () => {
136
+ settle("error");
137
+ },
138
+ );
139
+ };
140
+
141
+ const statusText =
142
+ status === "verified"
143
+ ? "Verified"
144
+ : status === "checking"
145
+ ? "Checking…"
146
+ : status === "failed"
147
+ ? found && found.length > 0
148
+ ? "Not verified. Something else was found."
149
+ : "Not verified. Nothing found yet — DNS changes can take up to 48 hours to appear."
150
+ : "Not checked yet";
151
+
152
+ const checked =
153
+ checkedAt !== undefined
154
+ ? new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short" }).format(
155
+ checkedAt,
156
+ )
157
+ : null;
158
+
159
+ const parts: { key: string; label: string; text: string; note?: ReactNode }[] = [
160
+ { key: "type", label: "Type", text: type },
161
+ {
162
+ key: "name",
163
+ label: "Name",
164
+ text: host,
165
+ note: forms ? (
166
+ forms.relative === "@" ? (
167
+ <>
168
+ Some providers want <code className="font-mono">{forms.full}</code> or leave it
169
+ blank instead of <code className="font-mono">@</code>.
170
+ </>
171
+ ) : (
172
+ <>
173
+ Some providers want the full name, <code className="font-mono">{forms.full}</code>.
174
+ If the field already ends in <code className="font-mono">{zone}</code>, enter just
175
+ the part shown.
176
+ </>
177
+ )
178
+ ) : undefined,
179
+ },
180
+ {
181
+ key: "value",
182
+ label: "Value",
183
+ text: value,
184
+ note:
185
+ type.toUpperCase() === "TXT"
186
+ ? "Enter without quotes; your provider adds them if it needs to."
187
+ : undefined,
188
+ },
189
+ ...(priority !== undefined
190
+ ? [{ key: "priority", label: "Priority", text: String(priority) }]
191
+ : []),
192
+ ...(ttl !== undefined ? [{ key: "ttl", label: "TTL", text: String(ttl) }] : []),
193
+ ];
194
+
195
+ return (
196
+ <section
197
+ data-slot="dns-record"
198
+ data-status={status}
199
+ aria-labelledby={`${id}-heading`}
200
+ aria-busy={status === "checking" || undefined}
201
+ className={cn(
202
+ "flex flex-col gap-3 rounded-lg border border-border bg-card p-4",
203
+ className,
204
+ )}
205
+ {...props}
206
+ >
207
+ <div className="flex flex-wrap items-baseline justify-between gap-2">
208
+ <h3 id={`${id}-heading`} className="text-sm font-medium">
209
+ {type} record
210
+ {purpose ? (
211
+ <span className="font-normal text-muted-foreground"> · {purpose}</span>
212
+ ) : null}
213
+ </h3>
214
+ {/* Status as a word, and announced when it changes: a check was asked
215
+ for, and the answer should arrive where the asker is. */}
216
+ <p
217
+ data-slot="dns-record-status"
218
+ role="status"
219
+ aria-live="polite"
220
+ className={cn(
221
+ "flex items-center gap-1.5 text-xs",
222
+ status === "verified" && "text-success",
223
+ status === "failed" && "text-destructive",
224
+ (status === "pending" || status === "checking") && "text-muted-foreground",
225
+ )}
226
+ >
227
+ {status === "checking" ? <Spinner size="sm" /> : null}
228
+ {statusText}
229
+ {checked && status !== "checking" ? (
230
+ <span className="text-muted-foreground"> · checked {checked}</span>
231
+ ) : null}
232
+ </p>
233
+ </div>
234
+
235
+ {children}
236
+
237
+ <dl className="m-0 grid gap-x-4 gap-y-2 sm:grid-cols-[auto_1fr]">
238
+ {parts.map((part) => (
239
+ <div key={part.key} className="contents">
240
+ <dt className="text-xs font-medium text-muted-foreground sm:pt-1.5">
241
+ {part.label}
242
+ </dt>
243
+ <dd className="m-0 flex flex-col gap-1">
244
+ <div className="flex items-start gap-2">
245
+ <code
246
+ data-slot="dns-record-part"
247
+ data-part={part.key}
248
+ className="min-w-0 flex-1 rounded-md bg-muted px-2 py-1 font-mono text-xs break-all"
249
+ >
250
+ {part.text}
251
+ </code>
252
+ <button
253
+ type="button"
254
+ aria-label={`Copy ${part.label.toLowerCase()}`}
255
+ data-state={copy?.part === part.key ? copy.state : "idle"}
256
+ className={cn(
257
+ "shrink-0 rounded-md border border-input bg-background px-2 py-1 text-xs font-medium",
258
+ "hover:bg-accent hover:text-accent-foreground",
259
+ focusRing,
260
+ )}
261
+ onClick={() => {
262
+ copyPart(part.key, part.text);
263
+ }}
264
+ >
265
+ {copy?.part === part.key && copy.state === "copied" ? "Copied" : "Copy"}
266
+ </button>
267
+ </div>
268
+ {copy?.part === part.key && copy.state === "error" ? (
269
+ <p data-slot="dns-record-copy-error" className="text-xs text-destructive">
270
+ Could not copy. Select it and copy by hand.
271
+ </p>
272
+ ) : null}
273
+ {part.note ? <p className="text-xs text-muted-foreground">{part.note}</p> : null}
274
+ </dd>
275
+ </div>
276
+ ))}
277
+ </dl>
278
+
279
+ {status === "failed" && found && found.length > 0 ? (
280
+ <div
281
+ data-slot="dns-record-found"
282
+ className="rounded-md border border-destructive/40 bg-destructive/5 p-3 text-xs"
283
+ >
284
+ <p className="font-medium text-destructive">Found instead:</p>
285
+ <ul className="m-0 mt-1 list-none p-0">
286
+ {found.map((entry) => (
287
+ <li key={entry}>
288
+ <code className="font-mono break-all">{entry}</code>
289
+ </li>
290
+ ))}
291
+ </ul>
292
+ </div>
293
+ ) : null}
294
+
295
+ {/* A separate region from the verification status, so "Copied name"
296
+ never replaces or interrupts a check result that is being read.
297
+ Present from the start, so the first copy is heard. */}
298
+ <span role="status" aria-live="polite" className="sr-only">
299
+ {copy?.state === "copied"
300
+ ? `Copied ${copy.part}`
301
+ : copy?.state === "error"
302
+ ? `Could not copy ${copy.part}. Select it and copy by hand.`
303
+ : ""}
304
+ </span>
305
+
306
+ {onCheck ? (
307
+ <div className="flex items-center gap-2">
308
+ <Button variant="outline" size="sm" loading={status === "checking"} onClick={onCheck}>
309
+ {status === "verified" ? "Check again" : "Check now"}
310
+ </Button>
311
+ </div>
312
+ ) : null}
313
+ </section>
314
+ );
315
+ }
@@ -0,0 +1,7 @@
1
+ export {
2
+ DnsRecord,
3
+ dnsHostForms,
4
+ type DnsHostForms,
5
+ type DnsRecordProps,
6
+ type DnsRecordStatus,
7
+ } from "./dns-record";
@@ -0,0 +1,21 @@
1
+ import { defineMeta } from "@/registry/schema";
2
+
3
+ export const meta = defineMeta({
4
+ name: "dns-record",
5
+ title: "DNS Record",
6
+ description:
7
+ "Add this record at your provider: parts copied separately, checked with what was found.",
8
+ category: "data",
9
+ status: "stable",
10
+ dependencies: [],
11
+ registryDependencies: ["button", "spinner"],
12
+ files: ["dns-record.tsx"],
13
+ a11y:
14
+ "The card is a region named by the record's type and purpose. Each part is a definition " +
15
+ "with its own copy button, named by the part it copies, so five Copy buttons are five " +
16
+ "different buttons. Verification status is a word in a polite live region, so the answer to " +
17
+ "a check arrives where it was asked for, and the card is aria-busy while checking. Copy " +
18
+ "results are announced in a separate region, present from the start, so a copy never " +
19
+ "replaces a check result mid-sentence; failure is announced with what to do instead. What " +
20
+ "the check found is a list, not a colour.",
21
+ });
@@ -221,7 +221,7 @@ export function DrawerHeader({ className, ...props }: ComponentPropsWithRef<"div
221
221
  return (
222
222
  <div
223
223
  data-slot="drawer-header"
224
- className={cn("flex shrink-0 flex-col gap-1.5 px-6 pt-4 pb-2 text-left", className)}
224
+ className={cn("flex shrink-0 flex-col gap-1.5 px-6 pt-4 pb-2 text-start", className)}
225
225
  {...props}
226
226
  />
227
227
  );
@@ -3,6 +3,7 @@
3
3
  import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
4
4
  import type { ComponentPropsWithRef } from "react";
5
5
 
6
+ import { mirrorForDirection } from "@/lib/styles";
6
7
  import { cn } from "@/lib/utils";
7
8
 
8
9
  /**
@@ -80,7 +81,7 @@ export function DropdownMenuItem({
80
81
  data-variant={variant}
81
82
  className={cn(
82
83
  menuItem,
83
- inset && "pl-8",
84
+ inset && "ps-8",
84
85
  variant === "destructive" &&
85
86
  "text-destructive data-[highlighted]:bg-destructive/10 data-[highlighted]:text-destructive",
86
87
  className,
@@ -94,7 +95,7 @@ export function DropdownMenuItem({
94
95
  * aria-checked, so announcing it twice would be noise. */
95
96
  function ItemIndicator({ radio = false }: { radio?: boolean }) {
96
97
  return (
97
- <span className="pointer-events-none absolute left-2 grid size-4 place-items-center">
98
+ <span className="pointer-events-none absolute start-2 grid size-4 place-items-center">
98
99
  <DropdownMenuPrimitive.ItemIndicator>
99
100
  {radio ? (
100
101
  <span className="size-2 rounded-full bg-current" />
@@ -122,7 +123,7 @@ export function DropdownMenuCheckboxItem({
122
123
  return (
123
124
  <DropdownMenuPrimitive.CheckboxItem
124
125
  data-slot="dropdown-menu-checkbox-item"
125
- className={cn(menuItem, "pl-8", className)}
126
+ className={cn(menuItem, "ps-8", className)}
126
127
  {...props}
127
128
  >
128
129
  <ItemIndicator />
@@ -139,7 +140,7 @@ export function DropdownMenuRadioItem({
139
140
  return (
140
141
  <DropdownMenuPrimitive.RadioItem
141
142
  data-slot="dropdown-menu-radio-item"
142
- className={cn(menuItem, "pl-8", className)}
143
+ className={cn(menuItem, "ps-8", className)}
143
144
  {...props}
144
145
  >
145
146
  <ItemIndicator radio />
@@ -160,7 +161,7 @@ export function DropdownMenuLabel({ className, inset, ...props }: DropdownMenuLa
160
161
  data-slot="dropdown-menu-label"
161
162
  className={cn(
162
163
  "px-2 py-1.5 text-xs font-medium text-muted-foreground",
163
- inset && "pl-8",
164
+ inset && "ps-8",
164
165
  className,
165
166
  )}
166
167
  {...props}
@@ -188,7 +189,7 @@ export function DropdownMenuShortcut({ className, ...props }: ComponentPropsWith
188
189
  <span
189
190
  data-slot="dropdown-menu-shortcut"
190
191
  aria-hidden="true"
191
- className={cn("ml-auto text-2xs tracking-wide text-muted-foreground", className)}
192
+ className={cn("ms-auto text-2xs tracking-wide text-muted-foreground", className)}
192
193
  {...props}
193
194
  />
194
195
  );
@@ -209,11 +210,16 @@ export function DropdownMenuSubTrigger({
209
210
  return (
210
211
  <DropdownMenuPrimitive.SubTrigger
211
212
  data-slot="dropdown-menu-sub-trigger"
212
- className={cn(menuItem, "data-[state=open]:bg-accent", inset && "pl-8", className)}
213
+ className={cn(menuItem, "data-[state=open]:bg-accent", inset && "ps-8", className)}
213
214
  {...props}
214
215
  >
215
216
  {children}
216
- <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="ml-auto size-3.5">
217
+ <svg
218
+ viewBox="0 0 24 24"
219
+ fill="none"
220
+ aria-hidden="true"
221
+ className={cn("ms-auto size-3.5", mirrorForDirection)}
222
+ >
217
223
  <path
218
224
  d="m9 18 6-6-6-6"
219
225
  stroke="currentColor"
@@ -9,7 +9,7 @@ const inputVariants = cva(
9
9
  "flex w-full min-w-0 rounded-md border border-input bg-background text-foreground shadow-xs",
10
10
  "transition-[border-color,box-shadow] duration-[var(--duration-fast)] ease-[var(--ease-out-quint)]",
11
11
  "placeholder:text-muted-foreground",
12
- "file:mr-3 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground",
12
+ "file:me-3 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground",
13
13
  "focus-visible:border-ring",
14
14
  "disabled:cursor-not-allowed disabled:opacity-55",
15
15
  focusRingInset,
@@ -203,7 +203,7 @@ export function LogViewer({
203
203
  data-index={item.index}
204
204
  data-slot="log-viewer-row"
205
205
  data-level={line.level}
206
- className="absolute top-0 left-0 w-full"
206
+ className="absolute start-0 top-0 w-full"
207
207
  style={{ transform: `translateY(${String(item.start)}px)` }}
208
208
  >
209
209
  <LogViewerRow
@@ -296,7 +296,7 @@ export function LogViewerRow({ line, expanded = false, onToggle }: LogViewerRowP
296
296
  {expanded && line.fields ? (
297
297
  <dl
298
298
  data-slot="log-viewer-fields"
299
- className="mt-1 mb-1 ml-4 grid grid-cols-[auto_1fr] gap-x-3 gap-y-0.5 border-l border-border pl-3"
299
+ className="ms-4 mt-1 mb-1 grid grid-cols-[auto_1fr] gap-x-3 gap-y-0.5 border-s border-border ps-3"
300
300
  >
301
301
  {Object.entries(line.fields).map(([key, value]) => (
302
302
  <div key={key} className="contents">
@@ -141,7 +141,7 @@ export function Meter({
141
141
  data-slot="meter-segment"
142
142
  data-tone={segment.tone ?? "primary"}
143
143
  className={cn(
144
- "h-full first:rounded-l-full last:rounded-r-full",
144
+ "h-full first:rounded-s-full last:rounded-e-full",
145
145
  "transition-[width] duration-[var(--duration-slow)] ease-[var(--ease-out-quint)]",
146
146
  TONES[segment.tone ?? "primary"],
147
147
  )}
@@ -2,6 +2,7 @@ import { Slot } from "radix-ui";
2
2
  import type { ComponentPropsWithRef } from "react";
3
3
 
4
4
  import { buttonVariants } from "@/components/button";
5
+ import { mirrorForDirection } from "@/lib/styles";
5
6
  import { cn } from "@/lib/utils";
6
7
 
7
8
  /**
@@ -80,7 +81,12 @@ export function PaginationPrevious({ className, ...props }: PaginationLinkProps)
80
81
  className={cn("gap-1 px-2.5", className)}
81
82
  {...props}
82
83
  >
83
- <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="size-4">
84
+ <svg
85
+ viewBox="0 0 24 24"
86
+ fill="none"
87
+ aria-hidden="true"
88
+ className={cn("size-4", mirrorForDirection)}
89
+ >
84
90
  <path
85
91
  d="m15 18-6-6 6-6"
86
92
  stroke="currentColor"
@@ -103,7 +109,12 @@ export function PaginationNext({ className, ...props }: PaginationLinkProps) {
103
109
  {...props}
104
110
  >
105
111
  <span className="hidden sm:inline">Next</span>
106
- <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="size-4">
112
+ <svg
113
+ viewBox="0 0 24 24"
114
+ fill="none"
115
+ aria-hidden="true"
116
+ className={cn("size-4", mirrorForDirection)}
117
+ >
107
118
  <path
108
119
  d="m9 18 6-6-6-6"
109
120
  stroke="currentColor"
@@ -0,0 +1,14 @@
1
+ export { PermissionMatrix, type PermissionMatrixProps } from "./permission-matrix";
2
+ export {
3
+ cellState,
4
+ countGranted,
5
+ groupPermissions,
6
+ groupState,
7
+ inheritedFrom,
8
+ type CellState,
9
+ type Grants,
10
+ type GroupState,
11
+ type Permission,
12
+ type PermissionGroup,
13
+ type Role,
14
+ } from "./permission-model";
@@ -0,0 +1,23 @@
1
+ import { defineMeta } from "@/registry/schema";
2
+
3
+ export const meta = defineMeta({
4
+ name: "permission-matrix",
5
+ title: "Permission Matrix",
6
+ description:
7
+ "Roles across, permissions down, with inheritance, locked roles and one tab stop.",
8
+ category: "data",
9
+ status: "stable",
10
+ dependencies: [],
11
+ registryDependencies: ["checkbox"],
12
+ files: ["permission-model.ts", "permission-matrix.tsx"],
13
+ a11y:
14
+ 'A grid in the WAI-ARIA sense: role="grid" with one tab stop, arrow keys between cells, Home ' +
15
+ "and End along a row and Ctrl with them across the whole matrix — the right call for a " +
16
+ "matrix that is operated, where it was the wrong one for a diff that is read. Every checkbox " +
17
+ 'is named by both coordinates, "Delete projects for Editor", so a reader arriving by arrow ' +
18
+ "key knows where they are without re-reading the headers. An inherited grant is a checked " +
19
+ "box that cannot be changed here, marked aria-disabled rather than disabled so it stays in " +
20
+ "the keyboard path, with the role it came from as its description. A locked role says in " +
21
+ "each cell's description that nothing can be changed. Group toggles are tri-state, and the " +
22
+ "column header counts each role's grants in text.",
23
+ });