@dowel-ui/react 0.5.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 (218) hide show
  1. package/README.md +1 -1
  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.js +1 -1
  27. package/dist/components/ai-extraction-review/ai-extraction-review.js.map +1 -1
  28. package/dist/components/ai-prompt-input/ai-prompt-input.js +1 -1
  29. package/dist/components/ai-prompt-input/ai-prompt-input.js.map +1 -1
  30. package/dist/components/ai-reasoning/ai-reasoning.js +1 -1
  31. package/dist/components/ai-reasoning/ai-reasoning.js.map +1 -1
  32. package/dist/components/ai-response/ai-response.js +2 -2
  33. package/dist/components/ai-response/ai-response.js.map +1 -1
  34. package/dist/components/ai-tool/ai-tool.js +2 -2
  35. package/dist/components/ai-tool/ai-tool.js.map +1 -1
  36. package/dist/components/alert/alert.d.ts +1 -1
  37. package/dist/components/avatar/avatar.d.ts +1 -1
  38. package/dist/components/badge/badge.d.ts +1 -1
  39. package/dist/components/breadcrumb/breadcrumb.d.ts +52 -0
  40. package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -0
  41. package/dist/components/breadcrumb/breadcrumb.js +141 -0
  42. package/dist/components/breadcrumb/breadcrumb.js.map +1 -0
  43. package/dist/components/breadcrumb/index.d.ts +2 -0
  44. package/dist/components/breadcrumb/index.js +2 -0
  45. package/dist/components/breadcrumb/meta.js +17 -0
  46. package/dist/components/breadcrumb/meta.js.map +1 -0
  47. package/dist/components/button/button.d.ts +1 -1
  48. package/dist/components/calendar/calendar.js +2 -2
  49. package/dist/components/calendar/calendar.js.map +1 -1
  50. package/dist/components/code-block/code-block.js +1 -1
  51. package/dist/components/code-block/code-block.js.map +1 -1
  52. package/dist/components/collapsible/collapsible.d.ts +30 -0
  53. package/dist/components/collapsible/collapsible.d.ts.map +1 -0
  54. package/dist/components/collapsible/collapsible.js +38 -0
  55. package/dist/components/collapsible/collapsible.js.map +1 -0
  56. package/dist/components/collapsible/index.d.ts +2 -0
  57. package/dist/components/collapsible/index.js +2 -0
  58. package/dist/components/collapsible/meta.js +17 -0
  59. package/dist/components/collapsible/meta.js.map +1 -0
  60. package/dist/components/combobox/combobox.js +2 -2
  61. package/dist/components/combobox/combobox.js.map +1 -1
  62. package/dist/components/command/command.js +1 -1
  63. package/dist/components/command/command.js.map +1 -1
  64. package/dist/components/data-table/data-table.d.ts.map +1 -1
  65. package/dist/components/data-table/data-table.js +4 -1
  66. package/dist/components/data-table/data-table.js.map +1 -1
  67. package/dist/components/dialog/dialog.js +2 -2
  68. package/dist/components/dialog/dialog.js.map +1 -1
  69. package/dist/components/diff-viewer/diff-viewer.js +4 -4
  70. package/dist/components/diff-viewer/diff-viewer.js.map +1 -1
  71. package/dist/components/direction/direction.d.ts +35 -0
  72. package/dist/components/direction/direction.d.ts.map +1 -0
  73. package/dist/components/direction/direction.js +14 -0
  74. package/dist/components/direction/direction.js.map +1 -0
  75. package/dist/components/direction/index.d.ts +2 -0
  76. package/dist/components/direction/index.js +2 -0
  77. package/dist/components/direction/meta.js +17 -0
  78. package/dist/components/direction/meta.js.map +1 -0
  79. package/dist/components/drawer/drawer.js +1 -1
  80. package/dist/components/drawer/drawer.js.map +1 -1
  81. package/dist/components/dropdown-menu/dropdown-menu.d.ts.map +1 -1
  82. package/dist/components/dropdown-menu/dropdown-menu.js +9 -8
  83. package/dist/components/dropdown-menu/dropdown-menu.js.map +1 -1
  84. package/dist/components/input/input.js +1 -1
  85. package/dist/components/input/input.js.map +1 -1
  86. package/dist/components/log-viewer/log-viewer.js +2 -2
  87. package/dist/components/log-viewer/log-viewer.js.map +1 -1
  88. package/dist/components/meter/meter.js +1 -1
  89. package/dist/components/meter/meter.js.map +1 -1
  90. package/dist/components/pagination/pagination.d.ts.map +1 -1
  91. package/dist/components/pagination/pagination.js +3 -2
  92. package/dist/components/pagination/pagination.js.map +1 -1
  93. package/dist/components/permission-matrix/permission-matrix.js +3 -3
  94. package/dist/components/permission-matrix/permission-matrix.js.map +1 -1
  95. package/dist/components/progress/progress.d.ts +1 -1
  96. package/dist/components/record-diff/record-diff.js +4 -4
  97. package/dist/components/record-diff/record-diff.js.map +1 -1
  98. package/dist/components/select/select.js +2 -2
  99. package/dist/components/select/select.js.map +1 -1
  100. package/dist/components/sheet/sheet.d.ts.map +1 -1
  101. package/dist/components/sheet/sheet.js +2 -2
  102. package/dist/components/sheet/sheet.js.map +1 -1
  103. package/dist/components/sidebar/index.d.ts +2 -0
  104. package/dist/components/sidebar/index.js +2 -0
  105. package/dist/components/sidebar/meta.js +17 -0
  106. package/dist/components/sidebar/meta.js.map +1 -0
  107. package/dist/components/sidebar/sidebar.d.ts +69 -0
  108. package/dist/components/sidebar/sidebar.d.ts.map +1 -0
  109. package/dist/components/sidebar/sidebar.js +256 -0
  110. package/dist/components/sidebar/sidebar.js.map +1 -0
  111. package/dist/components/spinner/spinner.d.ts +1 -1
  112. package/dist/components/table/table.js +2 -2
  113. package/dist/components/table/table.js.map +1 -1
  114. package/dist/components/tags-input/tags-input.js +1 -1
  115. package/dist/components/tags-input/tags-input.js.map +1 -1
  116. package/dist/components/textarea/index.d.ts +2 -0
  117. package/dist/components/textarea/index.js +2 -0
  118. package/dist/components/textarea/meta.js +17 -0
  119. package/dist/components/textarea/meta.js.map +1 -0
  120. package/dist/components/textarea/textarea.d.ts +43 -0
  121. package/dist/components/textarea/textarea.d.ts.map +1 -0
  122. package/dist/components/textarea/textarea.js +119 -0
  123. package/dist/components/textarea/textarea.js.map +1 -0
  124. package/dist/components/time-range-picker/time-range-picker.js +3 -3
  125. package/dist/components/time-range-picker/time-range-picker.js.map +1 -1
  126. package/dist/components/toast/toast.d.ts +1 -1
  127. package/dist/components/toast/toast.d.ts.map +1 -1
  128. package/dist/components/toast/toast.js +2 -2
  129. package/dist/components/toast/toast.js.map +1 -1
  130. package/dist/index.d.ts +12 -2
  131. package/dist/index.js +7 -2
  132. package/dist/lib/styles.d.ts +16 -1
  133. package/dist/lib/styles.d.ts.map +1 -1
  134. package/dist/lib/styles.js +16 -1
  135. package/dist/lib/styles.js.map +1 -1
  136. package/dist/registry/blocks.d.ts.map +1 -1
  137. package/dist/registry/blocks.js +27 -9
  138. package/dist/registry/blocks.js.map +1 -1
  139. package/dist/registry/components.d.ts.map +1 -1
  140. package/dist/registry/components.js +60 -50
  141. package/dist/registry/components.js.map +1 -1
  142. package/dist/registry/schema.d.ts +8 -0
  143. package/dist/registry/schema.d.ts.map +1 -1
  144. package/dist/registry/schema.js.map +1 -1
  145. package/package.json +5 -4
  146. package/src/components/accordion/accordion.tsx +1 -1
  147. package/src/components/activity-feed/activity-feed.tsx +1 -1
  148. package/src/components/ai-agent-plan/ai-agent-plan.tsx +0 -0
  149. package/src/components/ai-extraction-review/ai-extraction-review.tsx +1 -1
  150. package/src/components/ai-prompt-input/ai-prompt-input.tsx +1 -1
  151. package/src/components/ai-reasoning/ai-reasoning.tsx +1 -1
  152. package/src/components/ai-response/ai-response.tsx +2 -2
  153. package/src/components/ai-tool/ai-tool.tsx +2 -2
  154. package/src/components/breadcrumb/breadcrumb.tsx +157 -0
  155. package/src/components/breadcrumb/index.ts +10 -0
  156. package/src/components/breadcrumb/meta.ts +19 -0
  157. package/src/components/calendar/calendar.tsx +2 -2
  158. package/src/components/code-block/code-block.tsx +1 -1
  159. package/src/components/collapsible/collapsible.tsx +63 -0
  160. package/src/components/collapsible/index.ts +6 -0
  161. package/src/components/collapsible/meta.ts +16 -0
  162. package/src/components/combobox/combobox.tsx +2 -2
  163. package/src/components/command/command.tsx +1 -1
  164. package/src/components/data-table/data-table.tsx +14 -3
  165. package/src/components/dialog/dialog.tsx +2 -2
  166. package/src/components/diff-viewer/diff-viewer.tsx +4 -4
  167. package/src/components/direction/direction.tsx +38 -0
  168. package/src/components/direction/index.ts +1 -0
  169. package/src/components/direction/meta.ts +19 -0
  170. package/src/components/drawer/drawer.tsx +1 -1
  171. package/src/components/dropdown-menu/dropdown-menu.tsx +14 -8
  172. package/src/components/input/input.tsx +1 -1
  173. package/src/components/log-viewer/log-viewer.tsx +2 -2
  174. package/src/components/meter/meter.tsx +1 -1
  175. package/src/components/pagination/pagination.tsx +13 -2
  176. package/src/components/permission-matrix/permission-matrix.tsx +4 -4
  177. package/src/components/record-diff/record-diff.tsx +4 -4
  178. package/src/components/select/select.tsx +2 -2
  179. package/src/components/sheet/sheet.tsx +8 -4
  180. package/src/components/sidebar/index.ts +19 -0
  181. package/src/components/sidebar/meta.ts +25 -0
  182. package/src/components/sidebar/sidebar.tsx +374 -0
  183. package/src/components/table/table.tsx +3 -3
  184. package/src/components/tags-input/tags-input.tsx +1 -1
  185. package/src/components/textarea/index.ts +1 -0
  186. package/src/components/textarea/meta.ts +21 -0
  187. package/src/components/textarea/textarea.tsx +210 -0
  188. package/src/components/time-range-picker/time-range-picker.tsx +3 -3
  189. package/src/components/toast/toast.tsx +14 -6
  190. package/src/index.ts +5 -0
  191. package/src/lib/styles.ts +16 -0
  192. package/src/registry/blocks.ts +18 -0
  193. package/src/registry/components.ts +10 -0
  194. package/src/registry/schema.ts +8 -0
  195. package/src/blocks/admin-users/admin-users.tsx +0 -251
  196. package/src/blocks/admin-users/index.ts +0 -1
  197. package/src/blocks/admin-users/meta.ts +0 -27
  198. package/src/blocks/ai-chat/ai-chat.tsx +0 -280
  199. package/src/blocks/ai-chat/index.ts +0 -7
  200. package/src/blocks/ai-chat/meta.ts +0 -32
  201. package/src/blocks/dashboard/dashboard.tsx +0 -192
  202. package/src/blocks/dashboard/index.ts +0 -6
  203. package/src/blocks/dashboard/meta.ts +0 -18
  204. package/src/blocks/forgot-password/forgot-password.tsx +0 -158
  205. package/src/blocks/forgot-password/index.ts +0 -1
  206. package/src/blocks/forgot-password/meta.ts +0 -18
  207. package/src/blocks/login/index.ts +0 -1
  208. package/src/blocks/login/login.tsx +0 -178
  209. package/src/blocks/login/meta.ts +0 -18
  210. package/src/blocks/pricing/index.ts +0 -1
  211. package/src/blocks/pricing/meta.ts +0 -18
  212. package/src/blocks/pricing/pricing.tsx +0 -176
  213. package/src/blocks/settings/index.ts +0 -6
  214. package/src/blocks/settings/meta.ts +0 -29
  215. package/src/blocks/settings/settings.tsx +0 -317
  216. package/src/blocks/signup/index.ts +0 -1
  217. package/src/blocks/signup/meta.ts +0 -18
  218. package/src/blocks/signup/signup.tsx +0 -240
@@ -41,7 +41,7 @@ function SheetContent({ className, children, side, showCloseButton = true, ...pr
41
41
  children: [children, showCloseButton ? /* @__PURE__ */ jsx(Dialog.Close, {
42
42
  "data-slot": "sheet-close",
43
43
  "aria-label": "Close",
44
- className: cn("absolute top-4 right-4 grid size-7 place-items-center rounded-md text-muted-foreground", "transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-foreground", "[&_svg]:size-4", focusRing),
44
+ className: cn("absolute end-4 top-4 grid size-7 place-items-center rounded-md text-muted-foreground", "transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-foreground", "[&_svg]:size-4", focusRing),
45
45
  children: /* @__PURE__ */ jsx("svg", {
46
46
  viewBox: "0 0 24 24",
47
47
  fill: "none",
@@ -59,7 +59,7 @@ function SheetContent({ className, children, side, showCloseButton = true, ...pr
59
59
  function SheetHeader({ className, ...props }) {
60
60
  return /* @__PURE__ */ jsx("div", {
61
61
  "data-slot": "sheet-header",
62
- className: cn("flex flex-col gap-1.5 pr-8 text-left", className),
62
+ className: cn("flex flex-col gap-1.5 pe-8 text-start", className),
63
63
  ...props
64
64
  });
65
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sheet.js","names":["SheetPrimitive"],"sources":["../../../src/components/sheet/sheet.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Dialog as SheetPrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A panel that enters from an edge of the viewport.\n *\n * Built on the same modal primitive as Dialog, so focus management, Escape\n * handling and page inerting are identical; only the placement and motion\n * differ. Reach for Sheet when the content is a secondary surface — filters,\n * details, navigation — rather than a decision that must be answered.\n */\nexport const Sheet = SheetPrimitive.Root;\nexport const SheetTrigger = SheetPrimitive.Trigger;\nexport const SheetPortal = SheetPrimitive.Portal;\nexport const SheetClose = SheetPrimitive.Close;\n\nexport function SheetOverlay({\n className,\n ...props\n}: ComponentPropsWithRef<typeof SheetPrimitive.Overlay>) {\n return (\n <SheetPrimitive.Overlay\n data-slot=\"sheet-overlay\"\n className={cn(\n \"fixed inset-0 z-[var(--z-overlay)] bg-overlay\",\n \"data-[state=closed]:animate-overlay-out data-[state=open]:animate-overlay-in\",\n className,\n )}\n {...props}\n />\n );\n}\n\nconst sheetVariants = cva(\n cn(\n \"fixed z-[var(--z-drawer)] flex flex-col gap-4 bg-card p-6 text-card-foreground shadow-xl\",\n \"data-[state=closed]:animate-slide-out data-[state=open]:animate-slide-in\",\n ),\n {\n variants: {\n // --slide-x/--slide-y feed the shared slide keyframes, so all four sides\n // reuse one animation pair instead of eight bespoke keyframes.\n side: {\n top: \"inset-x-0 top-0 border-b [--slide-y:-100%]\",\n bottom: \"inset-x-0 bottom-0 border-t [--slide-y:100%]\",\n left: \"inset-y-0 left-0 h-full w-3/4 max-w-sm border-r [--slide-x:-100%]\",\n right: \"inset-y-0 right-0 h-full w-3/4 max-w-sm border-l [--slide-x:100%]\",\n },\n },\n defaultVariants: {\n side: \"right\",\n },\n },\n);\n\nexport interface SheetContentProps\n extends\n ComponentPropsWithRef<typeof SheetPrimitive.Content>,\n VariantProps<typeof sheetVariants> {\n showCloseButton?: boolean;\n}\n\nexport function SheetContent({\n className,\n children,\n side,\n showCloseButton = true,\n ...props\n}: SheetContentProps) {\n return (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Content\n data-slot=\"sheet-content\"\n className={cn(sheetVariants({ side }), className)}\n {...props}\n >\n {children}\n {showCloseButton ? (\n <SheetPrimitive.Close\n data-slot=\"sheet-close\"\n aria-label=\"Close\"\n className={cn(\n \"absolute top-4 right-4 grid size-7 place-items-center rounded-md text-muted-foreground\",\n \"transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-foreground\",\n \"[&_svg]:size-4\",\n focusRing,\n )}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"m6 6 12 12M18 6 6 18\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </SheetPrimitive.Close>\n ) : null}\n </SheetPrimitive.Content>\n </SheetPortal>\n );\n}\n\nexport function SheetHeader({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn(\"flex flex-col gap-1.5 pr-8 text-left\", className)}\n {...props}\n />\n );\n}\n\nexport function SheetFooter({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn(\n \"mt-auto flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function SheetTitle({\n className,\n ...props\n}: ComponentPropsWithRef<typeof SheetPrimitive.Title>) {\n return (\n <SheetPrimitive.Title\n data-slot=\"sheet-title\"\n className={cn(\"text-lg leading-tight font-semibold tracking-tight\", className)}\n {...props}\n />\n );\n}\n\nexport function SheetDescription({\n className,\n ...props\n}: ComponentPropsWithRef<typeof SheetPrimitive.Description>) {\n return (\n <SheetPrimitive.Description\n data-slot=\"sheet-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport { sheetVariants };\n"],"mappings":";;;;;;;;;;;;;;;AAiBA,MAAa,QAAQA,OAAe;AACpC,MAAa,eAAeA,OAAe;AAC3C,MAAa,cAAcA,OAAe;AAC1C,MAAa,aAAaA,OAAe;AAEzC,SAAgB,aAAa,EAC3B,WACA,GAAG,SACoD;CACvD,OACE,oBAACA,OAAe,SAAhB;EACE,aAAU;EACV,WAAW,GACT,iDACA,gFACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,MAAM,gBAAgB,IACpB,GACE,4FACA,0EACF,GACA;CACE,UAAU,EAGR,MAAM;EACJ,KAAK;EACL,QAAQ;EACR,MAAM;EACN,OAAO;CACT,EACF;CACA,iBAAiB,EACf,MAAM,QACR;AACF,CACF;AASA,SAAgB,aAAa,EAC3B,WACA,UACA,MACA,kBAAkB,MAClB,GAAG,SACiB;CACpB,OACE,qBAAC,aAAD,EAAA,UAAA,CACE,oBAAC,cAAD,CAAe,CAAA,GACf,qBAACA,OAAe,SAAhB;EACE,aAAU;EACV,WAAW,GAAG,cAAc,EAAE,KAAK,CAAC,GAAG,SAAS;EAChD,GAAI;EAHN,UAAA,CAKG,UACA,kBACC,oBAACA,OAAe,OAAhB;GACE,aAAU;GACV,cAAW;GACX,WAAW,GACT,0FACA,2FACA,kBACA,SACF;GAEA,UAAA,oBAAC,OAAD;IAAK,SAAQ;IAAY,MAAK;IAAO,eAAY;IAC/C,UAAA,oBAAC,QAAD;KACE,GAAE;KACF,QAAO;KACP,aAAY;KACZ,eAAc;IACf,CAAA;GACE,CAAA;EACe,CAAA,IACpB,IACkB;CACb,CAAA,CAAA,EAAA,CAAA;AAEjB;AAEA,SAAgB,YAAY,EAAE,WAAW,GAAG,SAAuC;CACjF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,wCAAwC,SAAS;EAC/D,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,YAAY,EAAE,WAAW,GAAG,SAAuC;CACjF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,kEACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,WAAW,EACzB,WACA,GAAG,SACkD;CACrD,OACE,oBAACA,OAAe,OAAhB;EACE,aAAU;EACV,WAAW,GAAG,sDAAsD,SAAS;EAC7E,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,iBAAiB,EAC/B,WACA,GAAG,SACwD;CAC3D,OACE,oBAACA,OAAe,aAAhB;EACE,aAAU;EACV,WAAW,GAAG,iCAAiC,SAAS;EACxD,GAAI;CACL,CAAA;AAEL"}
1
+ {"version":3,"file":"sheet.js","names":["SheetPrimitive"],"sources":["../../../src/components/sheet/sheet.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Dialog as SheetPrimitive } from \"radix-ui\";\nimport type { ComponentPropsWithRef } from \"react\";\n\nimport { focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A panel that enters from an edge of the viewport.\n *\n * Built on the same modal primitive as Dialog, so focus management, Escape\n * handling and page inerting are identical; only the placement and motion\n * differ. Reach for Sheet when the content is a secondary surface — filters,\n * details, navigation — rather than a decision that must be answered.\n */\nexport const Sheet = SheetPrimitive.Root;\nexport const SheetTrigger = SheetPrimitive.Trigger;\nexport const SheetPortal = SheetPrimitive.Portal;\nexport const SheetClose = SheetPrimitive.Close;\n\nexport function SheetOverlay({\n className,\n ...props\n}: ComponentPropsWithRef<typeof SheetPrimitive.Overlay>) {\n return (\n <SheetPrimitive.Overlay\n data-slot=\"sheet-overlay\"\n className={cn(\n \"fixed inset-0 z-[var(--z-overlay)] bg-overlay\",\n \"data-[state=closed]:animate-overlay-out data-[state=open]:animate-overlay-in\",\n className,\n )}\n {...props}\n />\n );\n}\n\nconst sheetVariants = cva(\n cn(\n \"fixed z-[var(--z-drawer)] flex flex-col gap-4 bg-card p-6 text-card-foreground shadow-xl\",\n \"data-[state=closed]:animate-slide-out data-[state=open]:animate-slide-in\",\n ),\n {\n variants: {\n // --slide-x/--slide-y feed the shared slide keyframes, so all four sides\n // reuse one animation pair instead of eight bespoke keyframes.\n side: {\n top: \"inset-x-0 top-0 border-b [--slide-y:-100%]\",\n bottom: \"inset-x-0 bottom-0 border-t [--slide-y:100%]\",\n // rtl-ok: the variant is named `left`, and a sheet asked for on the\n // left that opens on the right in Arabic is an API telling a lie. The\n // logical version of this is a `start`/`end` side, which is a rename\n // rather than a restyle.\n left: \"inset-y-0 left-0 h-full w-3/4 max-w-sm border-r [--slide-x:-100%]\", // rtl-ok: named side\n right: \"inset-y-0 right-0 h-full w-3/4 max-w-sm border-l [--slide-x:100%]\", // rtl-ok: named side\n },\n },\n defaultVariants: {\n side: \"right\",\n },\n },\n);\n\nexport interface SheetContentProps\n extends\n ComponentPropsWithRef<typeof SheetPrimitive.Content>,\n VariantProps<typeof sheetVariants> {\n showCloseButton?: boolean;\n}\n\nexport function SheetContent({\n className,\n children,\n side,\n showCloseButton = true,\n ...props\n}: SheetContentProps) {\n return (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Content\n data-slot=\"sheet-content\"\n className={cn(sheetVariants({ side }), className)}\n {...props}\n >\n {children}\n {showCloseButton ? (\n <SheetPrimitive.Close\n data-slot=\"sheet-close\"\n aria-label=\"Close\"\n className={cn(\n \"absolute end-4 top-4 grid size-7 place-items-center rounded-md text-muted-foreground\",\n \"transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-foreground\",\n \"[&_svg]:size-4\",\n focusRing,\n )}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"m6 6 12 12M18 6 6 18\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </SheetPrimitive.Close>\n ) : null}\n </SheetPrimitive.Content>\n </SheetPortal>\n );\n}\n\nexport function SheetHeader({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn(\"flex flex-col gap-1.5 pe-8 text-start\", className)}\n {...props}\n />\n );\n}\n\nexport function SheetFooter({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn(\n \"mt-auto flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function SheetTitle({\n className,\n ...props\n}: ComponentPropsWithRef<typeof SheetPrimitive.Title>) {\n return (\n <SheetPrimitive.Title\n data-slot=\"sheet-title\"\n className={cn(\"text-lg leading-tight font-semibold tracking-tight\", className)}\n {...props}\n />\n );\n}\n\nexport function SheetDescription({\n className,\n ...props\n}: ComponentPropsWithRef<typeof SheetPrimitive.Description>) {\n return (\n <SheetPrimitive.Description\n data-slot=\"sheet-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport { sheetVariants };\n"],"mappings":";;;;;;;;;;;;;;;AAiBA,MAAa,QAAQA,OAAe;AACpC,MAAa,eAAeA,OAAe;AAC3C,MAAa,cAAcA,OAAe;AAC1C,MAAa,aAAaA,OAAe;AAEzC,SAAgB,aAAa,EAC3B,WACA,GAAG,SACoD;CACvD,OACE,oBAACA,OAAe,SAAhB;EACE,aAAU;EACV,WAAW,GACT,iDACA,gFACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,MAAM,gBAAgB,IACpB,GACE,4FACA,0EACF,GACA;CACE,UAAU,EAGR,MAAM;EACJ,KAAK;EACL,QAAQ;EAKR,MAAM;EACN,OAAO;CACT,EACF;CACA,iBAAiB,EACf,MAAM,QACR;AACF,CACF;AASA,SAAgB,aAAa,EAC3B,WACA,UACA,MACA,kBAAkB,MAClB,GAAG,SACiB;CACpB,OACE,qBAAC,aAAD,EAAA,UAAA,CACE,oBAAC,cAAD,CAAe,CAAA,GACf,qBAACA,OAAe,SAAhB;EACE,aAAU;EACV,WAAW,GAAG,cAAc,EAAE,KAAK,CAAC,GAAG,SAAS;EAChD,GAAI;EAHN,UAAA,CAKG,UACA,kBACC,oBAACA,OAAe,OAAhB;GACE,aAAU;GACV,cAAW;GACX,WAAW,GACT,wFACA,2FACA,kBACA,SACF;GAEA,UAAA,oBAAC,OAAD;IAAK,SAAQ;IAAY,MAAK;IAAO,eAAY;IAC/C,UAAA,oBAAC,QAAD;KACE,GAAE;KACF,QAAO;KACP,aAAY;KACZ,eAAc;IACf,CAAA;GACE,CAAA;EACe,CAAA,IACpB,IACkB;CACb,CAAA,CAAA,EAAA,CAAA;AAEjB;AAEA,SAAgB,YAAY,EAAE,WAAW,GAAG,SAAuC;CACjF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,yCAAyC,SAAS;EAChE,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,YAAY,EAAE,WAAW,GAAG,SAAuC;CACjF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,kEACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,WAAW,EACzB,WACA,GAAG,SACkD;CACrD,OACE,oBAACA,OAAe,OAAhB;EACE,aAAU;EACV,WAAW,GAAG,sDAAsD,SAAS;EAC7E,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,iBAAiB,EAC/B,WACA,GAAG,SACwD;CAC3D,OACE,oBAACA,OAAe,aAAhB;EACE,aAAU;EACV,WAAW,GAAG,iCAAiC,SAAS;EACxD,GAAI;CACL,CAAA;AAEL"}
@@ -0,0 +1,2 @@
1
+ import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuButtonProps, SidebarMenuItem, SidebarMenuLabel, SidebarProps, SidebarProvider, SidebarProviderProps, SidebarTrigger, SidebarTriggerProps } from "./sidebar.js";
2
+ export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, type SidebarMenuButtonProps, SidebarMenuItem, SidebarMenuLabel, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarTrigger, type SidebarTriggerProps };
@@ -0,0 +1,2 @@
1
+ import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuLabel, SidebarProvider, SidebarTrigger } from "./sidebar.js";
2
+ export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuLabel, SidebarProvider, SidebarTrigger };
@@ -0,0 +1,17 @@
1
+ import { defineMeta } from "../../registry/schema.js";
2
+ //#region src/components/sidebar/meta.ts
3
+ const meta = defineMeta({
4
+ name: "sidebar",
5
+ title: "Sidebar",
6
+ description: "The application's own navigation: a collapsible rail on a wide screen, an overlay on a narrow one.",
7
+ category: "navigation",
8
+ status: "stable",
9
+ dependencies: ["radix-ui"],
10
+ registryDependencies: ["sheet"],
11
+ files: ["sidebar.tsx"],
12
+ a11y: "The navigation landmark must be named — a page has several, and three regions all called \"navigation\" is a list nobody can choose from. The active entry carries aria-current=\"page\" rather than only a background colour. On a narrow screen it is a Sheet, not a styled div: an overlay needs a focus trap and an Escape key, and without them the page behind stays reachable by Tab while the menu covers it. When the rail collapses, labels are visually hidden rather than removed — removing them leaves controls whose only content is an icon, which is how a collapsed sidebar becomes a column of links all announced as \"link\". The trigger says what pressing it will do, not what the state currently is. The overlay is mounted only on a narrow screen and has its own open state: a modal dialog that CSS hides is still modal, and mounting it always made every wide screen aria-hidden and unclickable whenever the rail was open."
13
+ });
14
+ //#endregion
15
+ export { meta };
16
+
17
+ //# sourceMappingURL=meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.js","names":[],"sources":["../../../src/components/sidebar/meta.ts"],"sourcesContent":["import { defineMeta } from \"@/registry/schema\";\n\nexport const meta = defineMeta({\n name: \"sidebar\",\n title: \"Sidebar\",\n description:\n \"The application's own navigation: a collapsible rail on a wide screen, an overlay on a narrow one.\",\n category: \"navigation\",\n status: \"stable\",\n dependencies: [\"radix-ui\"],\n registryDependencies: [\"sheet\"],\n files: [\"sidebar.tsx\"],\n a11y:\n \"The navigation landmark must be named — a page has several, and three regions all called \" +\n '\"navigation\" is a list nobody can choose from. The active entry carries aria-current=' +\n '\"page\" rather than only a background colour. On a narrow screen it is a Sheet, not a ' +\n \"styled div: an overlay needs a focus trap and an Escape key, and without them the page \" +\n \"behind stays reachable by Tab while the menu covers it. When the rail collapses, labels \" +\n \"are visually hidden rather than removed — removing them leaves controls whose only content \" +\n \"is an icon, which is how a collapsed sidebar becomes a column of links all announced as \" +\n '\"link\". The trigger says what pressing it will do, not what the state currently is. The ' +\n \"overlay is mounted only on a narrow screen and has its own open state: a modal dialog that \" +\n \"CSS hides is still modal, and mounting it always made every wide screen aria-hidden and \" +\n \"unclickable whenever the rail was open.\",\n});\n"],"mappings":";;AAEA,MAAa,OAAO,WAAW;CAC7B,MAAM;CACN,OAAO;CACP,aACE;CACF,UAAU;CACV,QAAQ;CACR,cAAc,CAAC,UAAU;CACzB,sBAAsB,CAAC,OAAO;CAC9B,OAAO,CAAC,aAAa;CACrB,MACE;AAWJ,CAAC"}
@@ -0,0 +1,69 @@
1
+ import { ComponentPropsWithRef, ReactNode } from "react";
2
+ //#region src/components/sidebar/sidebar.d.ts
3
+ interface SidebarProviderProps {
4
+ /** Controlled open state. */
5
+ open?: boolean;
6
+ defaultOpen?: boolean;
7
+ onOpenChange?: (open: boolean) => void;
8
+ children?: ReactNode;
9
+ }
10
+ declare function SidebarProvider({ open: openProp, defaultOpen, onOpenChange, children }: SidebarProviderProps): import("react").JSX.Element;
11
+ interface SidebarProps extends ComponentPropsWithRef<"aside"> {
12
+ /**
13
+ * Names the navigation landmark.
14
+ *
15
+ * Required, not optional. A page has more than one navigation region — this,
16
+ * a breadcrumb, a footer — and three landmarks all called "navigation" is a
17
+ * list a screen reader user cannot choose from.
18
+ */
19
+ label: string;
20
+ /** Title for the overlay on a narrow screen, where it is a dialog. */
21
+ mobileTitle?: string;
22
+ }
23
+ declare function Sidebar({ className, label, mobileTitle, children, ...props }: SidebarProps): import("react").JSX.Element;
24
+ declare function SidebarHeader({ className, ...props }: ComponentPropsWithRef<"div">): import("react").JSX.Element;
25
+ declare function SidebarContent({ className, ...props }: ComponentPropsWithRef<"div">): import("react").JSX.Element;
26
+ declare function SidebarFooter({ className, ...props }: ComponentPropsWithRef<"div">): import("react").JSX.Element;
27
+ declare function SidebarGroup({ className, ...props }: ComponentPropsWithRef<"div">): import("react").JSX.Element;
28
+ /**
29
+ * A heading for a group of links.
30
+ *
31
+ * Hidden rather than removed when the rail is collapsed, so the grouping
32
+ * survives for anyone not reading the layout visually.
33
+ */
34
+ declare function SidebarGroupLabel({ className, ...props }: ComponentPropsWithRef<"div">): import("react").JSX.Element;
35
+ declare function SidebarMenu({ className, ...props }: ComponentPropsWithRef<"ul">): import("react").JSX.Element;
36
+ declare function SidebarMenuItem({ className, ...props }: ComponentPropsWithRef<"li">): import("react").JSX.Element;
37
+ interface SidebarMenuButtonProps extends ComponentPropsWithRef<"a"> {
38
+ /** Render the child as the control, for a router's own link or a button. */
39
+ asChild?: boolean;
40
+ /** The page currently being shown. */
41
+ isActive?: boolean;
42
+ }
43
+ /**
44
+ * One entry in the navigation.
45
+ *
46
+ * `aria-current="page"` when active, not merely a background colour: which page
47
+ * you are on is information, and a highlight conveys it to exactly one kind of
48
+ * reader.
49
+ */
50
+ declare function SidebarMenuButton({ className, asChild, isActive, children, ...props }: SidebarMenuButtonProps): import("react").JSX.Element;
51
+ /**
52
+ * The text of a menu entry.
53
+ *
54
+ * Visually hidden rather than removed when the rail is collapsed. Removing it
55
+ * leaves a control whose only content is an icon, and an icon has no accessible
56
+ * name — which is how a collapsed sidebar becomes a column of buttons all
57
+ * announced as "link".
58
+ */
59
+ declare function SidebarMenuLabel({ className, ...props }: ComponentPropsWithRef<"span">): import("react").JSX.Element;
60
+ interface SidebarTriggerProps extends ComponentPropsWithRef<"button"> {
61
+ /** Overrides the label, which otherwise says what pressing it will do. */
62
+ label?: string;
63
+ }
64
+ declare function SidebarTrigger({ className, label, ...props }: SidebarTriggerProps): import("react").JSX.Element;
65
+ /** The region the sidebar sits beside. */
66
+ declare function SidebarInset({ className, ...props }: ComponentPropsWithRef<"main">): import("react").JSX.Element;
67
+ //#endregion
68
+ export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuButtonProps, SidebarMenuItem, SidebarMenuLabel, SidebarProps, SidebarProvider, SidebarProviderProps, SidebarTrigger, SidebarTriggerProps };
69
+ //# sourceMappingURL=sidebar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar.d.ts","names":[],"sources":["../../../src/components/sidebar/sidebar.tsx"],"mappings":";;UA6FiB;;EAEf;EACA;EACA,gBAAgB;EAChB,WAAW;;iBAGG,kBACd,MAAM,UACN,aACA,cACA,YACC,uCAAoB,IAAA;UAiCN,qBAAqB;;;;;;;;EAQpC;;EAEA;;iBAGc,UAAU,WAAW,OAAO,aAAa,aAAa,SAAS,+BAAY,IAAA;iBA6C3E,gBAAgB,cAAc,SAAS,+CAA4B,IAAA;iBAUnE,iBAAiB,cAAc,SAAS,+CAA4B,IAAA;iBAUpE,gBAAgB,cAAc,SAAS,+CAA4B,IAAA;iBAInE,eAAe,cAAc,SAAS,+CAA4B,IAAA;;;;;;;iBAUlE,oBAAoB,cAAc,SAAS,+CAA4B,IAAA;iBAkBvE,cAAc,cAAc,SAAS,8CAA2B,IAAA;iBAIhE,kBAAkB,cAAc,SAAS,8CAA2B,IAAA;UAInE,+BAA+B;;EAE9C;;EAEA;;;;;;;;;iBAUc,oBACd,WACA,SACA,UACA,aACG,SACF,yCAAsB,IAAA;;;;;;;;;iBAkCT,mBAAmB,cAAc,SAAS,gDAA6B,IAAA;UAYtE,4BAA4B;;EAE3C;;iBAGc,iBAAiB,WAAW,UAAU,SAAS,sCAAmB,IAAA;;iBAyClE,eAAe,cAAc,SAAS,gDAA6B,IAAA"}
@@ -0,0 +1,256 @@
1
+ "use client";
2
+ import { disabledStyles, focusRing } from "../../lib/styles.js";
3
+ import { cn } from "../../lib/utils.js";
4
+ import { Sheet, SheetContent, SheetTitle } from "../sheet/sheet.js";
5
+ import { Slot } from "radix-ui";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { createContext, useContext, useId, useMemo, useState, useSyncExternalStore } from "react";
8
+ //#region src/components/sidebar/sidebar.tsx
9
+ /**
10
+ * The application's own navigation.
11
+ *
12
+ * Two states, not one. On a wide screen it collapses to a rail of icons and
13
+ * stays in the page; on a narrow one it is an overlay, which needs a focus trap
14
+ * and an Escape key and is therefore a Sheet rather than a div with a
15
+ * transform. Building one behaviour and hiding it at a breakpoint gets the
16
+ * mobile case wrong every time — the page behind stays reachable by Tab while
17
+ * the menu covers it.
18
+ *
19
+ * The collapsed rail is where these usually fail. Hiding the label leaves a
20
+ * button whose only content is an icon, and an icon has no accessible name. The
21
+ * label here is not removed but visually hidden, so the control keeps its name
22
+ * whether or not the text is on screen.
23
+ *
24
+ * The overlay is mounted only on a narrow screen, and it has its own open
25
+ * state. Mounting it always and hiding it with CSS was the first version, and
26
+ * it was wrong in a way no visual check catches: a modal dialog that is
27
+ * `display: none` is still modal. It marks the rest of the page `aria-hidden`,
28
+ * traps focus in an element nothing can focus, and turns pointer events off on
29
+ * the body — so on a desktop the whole application was invisible to a screen
30
+ * reader and unclickable the moment the sidebar was open, which is its default.
31
+ */
32
+ /** Below Tailwind's `md` breakpoint, which is where the rail gives way to the overlay. */
33
+ const NARROW_QUERY = "(max-width: 767px)";
34
+ function subscribeToNarrow(callback) {
35
+ const query = window.matchMedia(NARROW_QUERY);
36
+ query.addEventListener("change", callback);
37
+ return () => {
38
+ query.removeEventListener("change", callback);
39
+ };
40
+ }
41
+ /**
42
+ * Whether the viewport is narrow enough for the overlay.
43
+ *
44
+ * The server snapshot says "wide": the rail renders in the HTML and CSS hides
45
+ * it below the breakpoint, so a narrow screen shows nothing wrong before
46
+ * hydration and the sheet mounts, closed, straight after.
47
+ */
48
+ function useIsNarrow() {
49
+ return useSyncExternalStore(subscribeToNarrow, () => window.matchMedia(NARROW_QUERY).matches, () => false);
50
+ }
51
+ const SidebarContext = createContext(null);
52
+ function useSidebar(component) {
53
+ const context = useContext(SidebarContext);
54
+ if (!context) throw new Error(`${component} must be rendered inside <SidebarProvider>.`);
55
+ return context;
56
+ }
57
+ function SidebarProvider({ open: openProp, defaultOpen = true, onOpenChange, children }) {
58
+ const [uncontrolled, setUncontrolled] = useState(defaultOpen);
59
+ const open = openProp ?? uncontrolled;
60
+ const [overlayOpen, setOverlayOpen] = useState(false);
61
+ const narrow = useIsNarrow();
62
+ const sidebarId = useId();
63
+ const value = useMemo(() => {
64
+ const setOpen = (next) => {
65
+ if (openProp === void 0) setUncontrolled(next);
66
+ onOpenChange?.(next);
67
+ };
68
+ return {
69
+ open,
70
+ setOpen,
71
+ narrow,
72
+ overlayOpen,
73
+ setOverlayOpen,
74
+ toggle: () => {
75
+ if (narrow) setOverlayOpen(!overlayOpen);
76
+ else setOpen(!open);
77
+ },
78
+ sidebarId
79
+ };
80
+ }, [
81
+ open,
82
+ openProp,
83
+ onOpenChange,
84
+ narrow,
85
+ overlayOpen,
86
+ sidebarId
87
+ ]);
88
+ return /* @__PURE__ */ jsx(SidebarContext.Provider, {
89
+ value,
90
+ children
91
+ });
92
+ }
93
+ function Sidebar({ className, label, mobileTitle, children, ...props }) {
94
+ const { open, narrow, overlayOpen, setOverlayOpen, sidebarId } = useSidebar("Sidebar");
95
+ return /* @__PURE__ */ jsxs(Fragment, { children: [narrow ? /* @__PURE__ */ jsx(Sheet, {
96
+ open: overlayOpen,
97
+ onOpenChange: setOverlayOpen,
98
+ children: /* @__PURE__ */ jsxs(SheetContent, {
99
+ side: "left",
100
+ className: "w-72 p-0",
101
+ children: [/* @__PURE__ */ jsx(SheetTitle, {
102
+ className: "sr-only",
103
+ children: mobileTitle ?? label
104
+ }), /* @__PURE__ */ jsx("nav", {
105
+ "aria-label": label,
106
+ className: "flex h-full flex-col gap-2 p-3",
107
+ children
108
+ })]
109
+ })
110
+ }) : null, /* @__PURE__ */ jsx("aside", {
111
+ id: sidebarId,
112
+ "data-slot": "sidebar",
113
+ "data-state": open ? "expanded" : "collapsed",
114
+ className: cn("hidden shrink-0 border-e border-border bg-background md:block", "transition-[width] duration-[var(--duration-normal)] ease-[var(--ease-out-quint)]", open ? "w-60" : "w-14", className),
115
+ ...props,
116
+ children: /* @__PURE__ */ jsx("nav", {
117
+ "aria-label": label,
118
+ className: "flex h-full flex-col gap-2 p-3",
119
+ children
120
+ })
121
+ })] });
122
+ }
123
+ function SidebarHeader({ className, ...props }) {
124
+ return /* @__PURE__ */ jsx("div", {
125
+ "data-slot": "sidebar-header",
126
+ className: cn("flex min-h-9 items-center gap-2 px-1", className),
127
+ ...props
128
+ });
129
+ }
130
+ function SidebarContent({ className, ...props }) {
131
+ return /* @__PURE__ */ jsx("div", {
132
+ "data-slot": "sidebar-content",
133
+ className: cn("flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto", className),
134
+ ...props
135
+ });
136
+ }
137
+ function SidebarFooter({ className, ...props }) {
138
+ return /* @__PURE__ */ jsx("div", {
139
+ "data-slot": "sidebar-footer",
140
+ className: cn("mt-auto", className),
141
+ ...props
142
+ });
143
+ }
144
+ function SidebarGroup({ className, ...props }) {
145
+ return /* @__PURE__ */ jsx("div", {
146
+ "data-slot": "sidebar-group",
147
+ className: cn("grid gap-1", className),
148
+ ...props
149
+ });
150
+ }
151
+ /**
152
+ * A heading for a group of links.
153
+ *
154
+ * Hidden rather than removed when the rail is collapsed, so the grouping
155
+ * survives for anyone not reading the layout visually.
156
+ */
157
+ function SidebarGroupLabel({ className, ...props }) {
158
+ const { open, narrow } = useSidebar("SidebarGroupLabel");
159
+ return /* @__PURE__ */ jsx("div", {
160
+ "data-slot": "sidebar-group-label",
161
+ className: cn("px-2 text-xs font-medium tracking-wide text-muted-foreground uppercase", !open && !narrow && "sr-only", className),
162
+ ...props
163
+ });
164
+ }
165
+ function SidebarMenu({ className, ...props }) {
166
+ return /* @__PURE__ */ jsx("ul", {
167
+ "data-slot": "sidebar-menu",
168
+ className: cn("grid gap-0.5", className),
169
+ ...props
170
+ });
171
+ }
172
+ function SidebarMenuItem({ className, ...props }) {
173
+ return /* @__PURE__ */ jsx("li", {
174
+ "data-slot": "sidebar-menu-item",
175
+ className: cn("min-w-0", className),
176
+ ...props
177
+ });
178
+ }
179
+ /**
180
+ * One entry in the navigation.
181
+ *
182
+ * `aria-current="page"` when active, not merely a background colour: which page
183
+ * you are on is information, and a highlight conveys it to exactly one kind of
184
+ * reader.
185
+ */
186
+ function SidebarMenuButton({ className, asChild = false, isActive = false, children, ...props }) {
187
+ const Component = asChild ? Slot.Root : "a";
188
+ return /* @__PURE__ */ jsx(Component, {
189
+ "data-slot": "sidebar-menu-button",
190
+ "data-active": isActive || void 0,
191
+ "aria-current": isActive ? "page" : void 0,
192
+ className: cn("flex h-9 w-full items-center gap-2.5 rounded-md px-2 text-sm", "transition-colors duration-[var(--duration-fast)]", "[&>svg]:size-4 [&>svg]:shrink-0", isActive ? "bg-accent font-medium text-accent-foreground" : "text-muted-foreground hover:bg-accent/60 hover:text-foreground", focusRing, disabledStyles, className),
193
+ ...props,
194
+ children
195
+ });
196
+ }
197
+ /**
198
+ * The text of a menu entry.
199
+ *
200
+ * Visually hidden rather than removed when the rail is collapsed. Removing it
201
+ * leaves a control whose only content is an icon, and an icon has no accessible
202
+ * name — which is how a collapsed sidebar becomes a column of buttons all
203
+ * announced as "link".
204
+ */
205
+ function SidebarMenuLabel({ className, ...props }) {
206
+ const { open, narrow } = useSidebar("SidebarMenuLabel");
207
+ return /* @__PURE__ */ jsx("span", {
208
+ "data-slot": "sidebar-menu-label",
209
+ className: cn("min-w-0 truncate", !open && !narrow && "sr-only", className),
210
+ ...props
211
+ });
212
+ }
213
+ function SidebarTrigger({ className, label, ...props }) {
214
+ const { open, narrow, overlayOpen, toggle, sidebarId } = useSidebar("SidebarTrigger");
215
+ return /* @__PURE__ */ jsx("button", {
216
+ type: "button",
217
+ "data-slot": "sidebar-trigger",
218
+ "aria-expanded": narrow ? overlayOpen : open,
219
+ "aria-controls": narrow ? void 0 : sidebarId,
220
+ "aria-label": label ?? (narrow ? overlayOpen ? "Close navigation" : "Open navigation" : open ? "Collapse navigation" : "Expand navigation"),
221
+ onClick: toggle,
222
+ className: cn("grid size-9 shrink-0 place-items-center rounded-md text-muted-foreground", "transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-foreground", focusRing, className),
223
+ ...props,
224
+ children: /* @__PURE__ */ jsxs("svg", {
225
+ viewBox: "0 0 24 24",
226
+ fill: "none",
227
+ "aria-hidden": true,
228
+ className: "size-4",
229
+ children: [/* @__PURE__ */ jsx("rect", {
230
+ x: "3",
231
+ y: "4",
232
+ width: "18",
233
+ height: "16",
234
+ rx: "2",
235
+ stroke: "currentColor",
236
+ strokeWidth: "2"
237
+ }), /* @__PURE__ */ jsx("path", {
238
+ d: "M9 4v16",
239
+ stroke: "currentColor",
240
+ strokeWidth: "2"
241
+ })]
242
+ })
243
+ });
244
+ }
245
+ /** The region the sidebar sits beside. */
246
+ function SidebarInset({ className, ...props }) {
247
+ return /* @__PURE__ */ jsx("main", {
248
+ "data-slot": "sidebar-inset",
249
+ className: cn("min-w-0 flex-1", className),
250
+ ...props
251
+ });
252
+ }
253
+ //#endregion
254
+ export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuLabel, SidebarProvider, SidebarTrigger };
255
+
256
+ //# sourceMappingURL=sidebar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar.js","names":[],"sources":["../../../src/components/sidebar/sidebar.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"radix-ui\";\nimport {\n createContext,\n useContext,\n useId,\n useMemo,\n useState,\n useSyncExternalStore,\n type ComponentPropsWithRef,\n type ReactNode,\n} from \"react\";\n\nimport { Sheet, SheetContent, SheetTitle } from \"@/components/sheet\";\nimport { disabledStyles, focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * The application's own navigation.\n *\n * Two states, not one. On a wide screen it collapses to a rail of icons and\n * stays in the page; on a narrow one it is an overlay, which needs a focus trap\n * and an Escape key and is therefore a Sheet rather than a div with a\n * transform. Building one behaviour and hiding it at a breakpoint gets the\n * mobile case wrong every time — the page behind stays reachable by Tab while\n * the menu covers it.\n *\n * The collapsed rail is where these usually fail. Hiding the label leaves a\n * button whose only content is an icon, and an icon has no accessible name. The\n * label here is not removed but visually hidden, so the control keeps its name\n * whether or not the text is on screen.\n *\n * The overlay is mounted only on a narrow screen, and it has its own open\n * state. Mounting it always and hiding it with CSS was the first version, and\n * it was wrong in a way no visual check catches: a modal dialog that is\n * `display: none` is still modal. It marks the rest of the page `aria-hidden`,\n * traps focus in an element nothing can focus, and turns pointer events off on\n * the body — so on a desktop the whole application was invisible to a screen\n * reader and unclickable the moment the sidebar was open, which is its default.\n */\n\n/** Below Tailwind's `md` breakpoint, which is where the rail gives way to the overlay. */\nconst NARROW_QUERY = \"(max-width: 767px)\";\n\nfunction subscribeToNarrow(callback: () => void): () => void {\n const query = window.matchMedia(NARROW_QUERY);\n query.addEventListener(\"change\", callback);\n return () => {\n query.removeEventListener(\"change\", callback);\n };\n}\n\n/**\n * Whether the viewport is narrow enough for the overlay.\n *\n * The server snapshot says \"wide\": the rail renders in the HTML and CSS hides\n * it below the breakpoint, so a narrow screen shows nothing wrong before\n * hydration and the sheet mounts, closed, straight after.\n */\nfunction useIsNarrow(): boolean {\n return useSyncExternalStore(\n subscribeToNarrow,\n () => window.matchMedia(NARROW_QUERY).matches,\n () => false,\n );\n}\n\ninterface SidebarContextValue {\n /** The rail is expanded rather than collapsed. */\n open: boolean;\n setOpen: (open: boolean) => void;\n /** The viewport is narrow, so the navigation is an overlay. */\n narrow: boolean;\n /** The overlay is showing. Only meaningful when `narrow`. */\n overlayOpen: boolean;\n setOverlayOpen: (open: boolean) => void;\n /** Whichever of the two states applies on this viewport. */\n toggle: () => void;\n /** Id of the sidebar element, for the trigger's aria-controls. */\n sidebarId: string;\n}\n\nconst SidebarContext = createContext<SidebarContextValue | null>(null);\n\nfunction useSidebar(component: string): SidebarContextValue {\n const context = useContext(SidebarContext);\n if (!context) {\n throw new Error(`${component} must be rendered inside <SidebarProvider>.`);\n }\n return context;\n}\n\nexport interface SidebarProviderProps {\n /** Controlled open state. */\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: ReactNode;\n}\n\nexport function SidebarProvider({\n open: openProp,\n defaultOpen = true,\n onOpenChange,\n children,\n}: SidebarProviderProps) {\n const [uncontrolled, setUncontrolled] = useState(defaultOpen);\n const open = openProp ?? uncontrolled;\n // Separate from `open`, and never true to begin with: \"expanded by default\"\n // is a fact about the rail. An overlay that covers the page on first load is\n // a menu nobody asked for.\n const [overlayOpen, setOverlayOpen] = useState(false);\n const narrow = useIsNarrow();\n const sidebarId = useId();\n\n const value = useMemo<SidebarContextValue>(() => {\n const setOpen = (next: boolean) => {\n if (openProp === undefined) setUncontrolled(next);\n onOpenChange?.(next);\n };\n\n return {\n open,\n setOpen,\n narrow,\n overlayOpen,\n setOverlayOpen,\n toggle: () => {\n if (narrow) setOverlayOpen(!overlayOpen);\n else setOpen(!open);\n },\n sidebarId,\n };\n }, [open, openProp, onOpenChange, narrow, overlayOpen, sidebarId]);\n\n return <SidebarContext.Provider value={value}>{children}</SidebarContext.Provider>;\n}\n\nexport interface SidebarProps extends ComponentPropsWithRef<\"aside\"> {\n /**\n * Names the navigation landmark.\n *\n * Required, not optional. A page has more than one navigation region — this,\n * a breadcrumb, a footer — and three landmarks all called \"navigation\" is a\n * list a screen reader user cannot choose from.\n */\n label: string;\n /** Title for the overlay on a narrow screen, where it is a dialog. */\n mobileTitle?: string;\n}\n\nexport function Sidebar({ className, label, mobileTitle, children, ...props }: SidebarProps) {\n const { open, narrow, overlayOpen, setOverlayOpen, sidebarId } = useSidebar(\"Sidebar\");\n\n return (\n <>\n {/*\n The overlay, on a narrow screen. A Sheet rather than a styled div: it\n traps focus, closes on Escape and marks the rest of the page inert,\n none of which a transform does — and without them the page behind stays\n reachable by Tab while the menu covers it.\n\n Mounted only when the screen is narrow. Those three behaviours are\n exactly why it must not exist on a wide one: see the note at the top.\n */}\n {narrow ? (\n <Sheet open={overlayOpen} onOpenChange={setOverlayOpen}>\n <SheetContent side=\"left\" className=\"w-72 p-0\">\n <SheetTitle className=\"sr-only\">{mobileTitle ?? label}</SheetTitle>\n <nav aria-label={label} className=\"flex h-full flex-col gap-2 p-3\">\n {children}\n </nav>\n </SheetContent>\n </Sheet>\n ) : null}\n\n <aside\n id={sidebarId}\n data-slot=\"sidebar\"\n data-state={open ? \"expanded\" : \"collapsed\"}\n className={cn(\n \"hidden shrink-0 border-e border-border bg-background md:block\",\n \"transition-[width] duration-[var(--duration-normal)] ease-[var(--ease-out-quint)]\",\n open ? \"w-60\" : \"w-14\",\n className,\n )}\n {...props}\n >\n <nav aria-label={label} className=\"flex h-full flex-col gap-2 p-3\">\n {children}\n </nav>\n </aside>\n </>\n );\n}\n\nexport function SidebarHeader({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"sidebar-header\"\n className={cn(\"flex min-h-9 items-center gap-2 px-1\", className)}\n {...props}\n />\n );\n}\n\nexport function SidebarContent({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return (\n <div\n data-slot=\"sidebar-content\"\n className={cn(\"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto\", className)}\n {...props}\n />\n );\n}\n\nexport function SidebarFooter({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return <div data-slot=\"sidebar-footer\" className={cn(\"mt-auto\", className)} {...props} />;\n}\n\nexport function SidebarGroup({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n return <div data-slot=\"sidebar-group\" className={cn(\"grid gap-1\", className)} {...props} />;\n}\n\n/**\n * A heading for a group of links.\n *\n * Hidden rather than removed when the rail is collapsed, so the grouping\n * survives for anyone not reading the layout visually.\n */\nexport function SidebarGroupLabel({ className, ...props }: ComponentPropsWithRef<\"div\">) {\n const { open, narrow } = useSidebar(\"SidebarGroupLabel\");\n\n return (\n <div\n data-slot=\"sidebar-group-label\"\n className={cn(\n \"px-2 text-xs font-medium tracking-wide text-muted-foreground uppercase\",\n // The rail's collapsed state is not the overlay's: a sheet is always\n // wide enough for its labels.\n !open && !narrow && \"sr-only\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function SidebarMenu({ className, ...props }: ComponentPropsWithRef<\"ul\">) {\n return <ul data-slot=\"sidebar-menu\" className={cn(\"grid gap-0.5\", className)} {...props} />;\n}\n\nexport function SidebarMenuItem({ className, ...props }: ComponentPropsWithRef<\"li\">) {\n return <li data-slot=\"sidebar-menu-item\" className={cn(\"min-w-0\", className)} {...props} />;\n}\n\nexport interface SidebarMenuButtonProps extends ComponentPropsWithRef<\"a\"> {\n /** Render the child as the control, for a router's own link or a button. */\n asChild?: boolean;\n /** The page currently being shown. */\n isActive?: boolean;\n}\n\n/**\n * One entry in the navigation.\n *\n * `aria-current=\"page\"` when active, not merely a background colour: which page\n * you are on is information, and a highlight conveys it to exactly one kind of\n * reader.\n */\nexport function SidebarMenuButton({\n className,\n asChild = false,\n isActive = false,\n children,\n ...props\n}: SidebarMenuButtonProps) {\n const Component = asChild ? Slot.Root : \"a\";\n\n return (\n <Component\n data-slot=\"sidebar-menu-button\"\n data-active={isActive || undefined}\n aria-current={isActive ? \"page\" : undefined}\n className={cn(\n \"flex h-9 w-full items-center gap-2.5 rounded-md px-2 text-sm\",\n \"transition-colors duration-[var(--duration-fast)]\",\n \"[&>svg]:size-4 [&>svg]:shrink-0\",\n isActive\n ? \"bg-accent font-medium text-accent-foreground\"\n : \"text-muted-foreground hover:bg-accent/60 hover:text-foreground\",\n focusRing,\n disabledStyles,\n className,\n )}\n {...props}\n >\n {children}\n </Component>\n );\n}\n\n/**\n * The text of a menu entry.\n *\n * Visually hidden rather than removed when the rail is collapsed. Removing it\n * leaves a control whose only content is an icon, and an icon has no accessible\n * name — which is how a collapsed sidebar becomes a column of buttons all\n * announced as \"link\".\n */\nexport function SidebarMenuLabel({ className, ...props }: ComponentPropsWithRef<\"span\">) {\n const { open, narrow } = useSidebar(\"SidebarMenuLabel\");\n\n return (\n <span\n data-slot=\"sidebar-menu-label\"\n className={cn(\"min-w-0 truncate\", !open && !narrow && \"sr-only\", className)}\n {...props}\n />\n );\n}\n\nexport interface SidebarTriggerProps extends ComponentPropsWithRef<\"button\"> {\n /** Overrides the label, which otherwise says what pressing it will do. */\n label?: string;\n}\n\nexport function SidebarTrigger({ className, label, ...props }: SidebarTriggerProps) {\n const { open, narrow, overlayOpen, toggle, sidebarId } = useSidebar(\"SidebarTrigger\");\n\n const expanded = narrow ? overlayOpen : open;\n const fallback = narrow\n ? overlayOpen\n ? \"Close navigation\"\n : \"Open navigation\"\n : open\n ? \"Collapse navigation\"\n : \"Expand navigation\";\n\n return (\n <button\n type=\"button\"\n data-slot=\"sidebar-trigger\"\n aria-expanded={expanded}\n // The rail is what this controls. The overlay is a dialog that names\n // itself, and pointing at an element CSS has hidden helps nobody.\n aria-controls={narrow ? undefined : sidebarId}\n // Says what pressing it does, not what the state currently is. \"Collapse\n // navigation\" while it is expanded is the useful half.\n aria-label={label ?? fallback}\n onClick={toggle}\n className={cn(\n \"grid size-9 shrink-0 place-items-center rounded-md text-muted-foreground\",\n \"transition-colors duration-[var(--duration-fast)] hover:bg-accent hover:text-foreground\",\n focusRing,\n className,\n )}\n {...props}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden className=\"size-4\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\" stroke=\"currentColor\" strokeWidth=\"2\" />\n <path d=\"M9 4v16\" stroke=\"currentColor\" strokeWidth=\"2\" />\n </svg>\n </button>\n );\n}\n\n/** The region the sidebar sits beside. */\nexport function SidebarInset({ className, ...props }: ComponentPropsWithRef<\"main\">) {\n return (\n <main data-slot=\"sidebar-inset\" className={cn(\"min-w-0 flex-1\", className)} {...props} />\n );\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { VariantProps } from "class-variance-authority";
2
2
  import { ComponentPropsWithRef } from "react";
3
3
  //#region src/components/spinner/spinner.d.ts
4
4
  declare const spinnerVariants: (props?: ({
5
- size?: "xs" | "sm" | "md" | "lg" | "xl" | null | undefined;
5
+ size?: "sm" | "md" | "xs" | "lg" | "xl" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  interface SpinnerProps extends Omit<ComponentPropsWithRef<"svg">, "children">, VariantProps<typeof spinnerVariants> {
8
8
  /**
@@ -48,14 +48,14 @@ function TableRow({ className, ...props }) {
48
48
  function TableHead({ className, ...props }) {
49
49
  return /* @__PURE__ */ jsx("th", {
50
50
  "data-slot": "table-head",
51
- className: cn("h-10 px-3 text-left align-middle text-xs font-medium whitespace-nowrap text-muted-foreground", "[&:has([role=checkbox])]:w-px [&:has([role=checkbox])]:pr-0", className),
51
+ className: cn("h-10 px-3 text-start align-middle text-xs font-medium whitespace-nowrap text-muted-foreground", "[&:has([role=checkbox])]:w-px [&:has([role=checkbox])]:pe-0", className),
52
52
  ...props
53
53
  });
54
54
  }
55
55
  function TableCell({ className, ...props }) {
56
56
  return /* @__PURE__ */ jsx("td", {
57
57
  "data-slot": "table-cell",
58
- className: cn("px-3 py-2.5 align-middle", "[&:has([role=checkbox])]:w-px [&:has([role=checkbox])]:pr-0", className),
58
+ className: cn("px-3 py-2.5 align-middle", "[&:has([role=checkbox])]:w-px [&:has([role=checkbox])]:pe-0", className),
59
59
  ...props
60
60
  });
61
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"table.js","names":[],"sources":["../../../src/components/table/table.tsx"],"sourcesContent":["import type { ComponentPropsWithRef } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Semantic table primitives.\n *\n * A real `<table>`, not a grid of divs: screen readers announce row and column\n * position, header association and table dimensions from the native elements,\n * and none of that can be recovered with ARIA after the fact.\n *\n * The wrapper scrolls horizontally and is focusable, because a table that\n * overflows its container is otherwise unreachable by keyboard.\n */\n/**\n * Arbitrary `data-*` attributes.\n *\n * TypeScript accepts `data-*` written directly on JSX, but not inside an object\n * literal typed as HTML attributes — so without this, `containerProps` could not\n * carry a test id or a styling hook.\n */\ntype DataAttributes = Record<`data-${string}`, string | number | boolean | undefined>;\n\nexport interface TableProps extends ComponentPropsWithRef<\"table\"> {\n /** Props for the scrolling wrapper around the table. */\n containerProps?: ComponentPropsWithRef<\"div\"> & DataAttributes;\n}\n\nexport function Table({ className, containerProps, ...props }: TableProps) {\n const { className: containerClassName, ...restContainer } = containerProps ?? {};\n\n return (\n <div\n data-slot=\"table-container\"\n // tabIndex 0 with a role and label makes an overflowing table scrollable\n // by keyboard. Without it, the columns past the edge are unreachable.\n tabIndex={0}\n role=\"region\"\n aria-label={props[\"aria-label\"] ?? \"Table\"}\n className={cn(\"relative w-full overflow-x-auto\", containerClassName)}\n {...restContainer}\n >\n <table\n data-slot=\"table\"\n className={cn(\"w-full caption-bottom border-collapse text-sm\", className)}\n {...props}\n />\n </div>\n );\n}\n\nexport function TableHeader({ className, ...props }: ComponentPropsWithRef<\"thead\">) {\n return (\n <thead\n data-slot=\"table-header\"\n className={cn(\"[&_tr]:border-b [&_tr]:border-border\", className)}\n {...props}\n />\n );\n}\n\nexport function TableBody({ className, ...props }: ComponentPropsWithRef<\"tbody\">) {\n return (\n <tbody\n data-slot=\"table-body\"\n className={cn(\"[&_tr:last-child]:border-0\", className)}\n {...props}\n />\n );\n}\n\nexport function TableFooter({ className, ...props }: ComponentPropsWithRef<\"tfoot\">) {\n return (\n <tfoot\n data-slot=\"table-footer\"\n className={cn(\"border-t border-border bg-muted/40 font-medium\", className)}\n {...props}\n />\n );\n}\n\nexport function TableRow({ className, ...props }: ComponentPropsWithRef<\"tr\">) {\n return (\n <tr\n data-slot=\"table-row\"\n className={cn(\n \"border-b border-border transition-colors duration-[var(--duration-instant)]\",\n \"hover:bg-muted/50 data-[state=selected]:bg-accent\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function TableHead({ className, ...props }: ComponentPropsWithRef<\"th\">) {\n return (\n <th\n data-slot=\"table-head\"\n className={cn(\n \"h-10 px-3 text-left align-middle text-xs font-medium whitespace-nowrap text-muted-foreground\",\n \"[&:has([role=checkbox])]:w-px [&:has([role=checkbox])]:pr-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function TableCell({ className, ...props }: ComponentPropsWithRef<\"td\">) {\n return (\n <td\n data-slot=\"table-cell\"\n className={cn(\n \"px-3 py-2.5 align-middle\",\n \"[&:has([role=checkbox])]:w-px [&:has([role=checkbox])]:pr-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * A caption describing the table.\n *\n * Rendered below the table but announced as its accessible name, which is why\n * it belongs here rather than as a heading above.\n */\nexport function TableCaption({ className, ...props }: ComponentPropsWithRef<\"caption\">) {\n return (\n <caption\n data-slot=\"table-caption\"\n className={cn(\"mt-4 text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n"],"mappings":";;;AA4BA,SAAgB,MAAM,EAAE,WAAW,gBAAgB,GAAG,SAAqB;CACzE,MAAM,EAAE,WAAW,oBAAoB,GAAG,kBAAkB,kBAAkB,CAAC;CAE/E,OACE,oBAAC,OAAD;EACE,aAAU;EAGV,UAAU;EACV,MAAK;EACL,cAAY,MAAM,iBAAiB;EACnC,WAAW,GAAG,mCAAmC,kBAAkB;EACnE,GAAI;EAEJ,UAAA,oBAAC,SAAD;GACE,aAAU;GACV,WAAW,GAAG,iDAAiD,SAAS;GACxE,GAAI;EACL,CAAA;CACE,CAAA;AAET;AAEA,SAAgB,YAAY,EAAE,WAAW,GAAG,SAAyC;CACnF,OACE,oBAAC,SAAD;EACE,aAAU;EACV,WAAW,GAAG,wCAAwC,SAAS;EAC/D,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,UAAU,EAAE,WAAW,GAAG,SAAyC;CACjF,OACE,oBAAC,SAAD;EACE,aAAU;EACV,WAAW,GAAG,8BAA8B,SAAS;EACrD,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,YAAY,EAAE,WAAW,GAAG,SAAyC;CACnF,OACE,oBAAC,SAAD;EACE,aAAU;EACV,WAAW,GAAG,kDAAkD,SAAS;EACzE,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,SAAS,EAAE,WAAW,GAAG,SAAsC;CAC7E,OACE,oBAAC,MAAD;EACE,aAAU;EACV,WAAW,GACT,+EACA,qDACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,UAAU,EAAE,WAAW,GAAG,SAAsC;CAC9E,OACE,oBAAC,MAAD;EACE,aAAU;EACV,WAAW,GACT,gGACA,+DACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,UAAU,EAAE,WAAW,GAAG,SAAsC;CAC9E,OACE,oBAAC,MAAD;EACE,aAAU;EACV,WAAW,GACT,4BACA,+DACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;;;;;;;AAQA,SAAgB,aAAa,EAAE,WAAW,GAAG,SAA2C;CACtF,OACE,oBAAC,WAAD;EACE,aAAU;EACV,WAAW,GAAG,sCAAsC,SAAS;EAC7D,GAAI;CACL,CAAA;AAEL"}
1
+ {"version":3,"file":"table.js","names":[],"sources":["../../../src/components/table/table.tsx"],"sourcesContent":["import type { ComponentPropsWithRef } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Semantic table primitives.\n *\n * A real `<table>`, not a grid of divs: screen readers announce row and column\n * position, header association and table dimensions from the native elements,\n * and none of that can be recovered with ARIA after the fact.\n *\n * The wrapper scrolls horizontally and is focusable, because a table that\n * overflows its container is otherwise unreachable by keyboard.\n */\n/**\n * Arbitrary `data-*` attributes.\n *\n * TypeScript accepts `data-*` written directly on JSX, but not inside an object\n * literal typed as HTML attributes — so without this, `containerProps` could not\n * carry a test id or a styling hook.\n */\ntype DataAttributes = Record<`data-${string}`, string | number | boolean | undefined>;\n\nexport interface TableProps extends ComponentPropsWithRef<\"table\"> {\n /** Props for the scrolling wrapper around the table. */\n containerProps?: ComponentPropsWithRef<\"div\"> & DataAttributes;\n}\n\nexport function Table({ className, containerProps, ...props }: TableProps) {\n const { className: containerClassName, ...restContainer } = containerProps ?? {};\n\n return (\n <div\n data-slot=\"table-container\"\n // tabIndex 0 with a role and label makes an overflowing table scrollable\n // by keyboard. Without it, the columns past the edge are unreachable.\n tabIndex={0}\n role=\"region\"\n aria-label={props[\"aria-label\"] ?? \"Table\"}\n className={cn(\"relative w-full overflow-x-auto\", containerClassName)}\n {...restContainer}\n >\n <table\n data-slot=\"table\"\n className={cn(\"w-full caption-bottom border-collapse text-sm\", className)}\n {...props}\n />\n </div>\n );\n}\n\nexport function TableHeader({ className, ...props }: ComponentPropsWithRef<\"thead\">) {\n return (\n <thead\n data-slot=\"table-header\"\n className={cn(\"[&_tr]:border-b [&_tr]:border-border\", className)}\n {...props}\n />\n );\n}\n\nexport function TableBody({ className, ...props }: ComponentPropsWithRef<\"tbody\">) {\n return (\n <tbody\n data-slot=\"table-body\"\n className={cn(\"[&_tr:last-child]:border-0\", className)}\n {...props}\n />\n );\n}\n\nexport function TableFooter({ className, ...props }: ComponentPropsWithRef<\"tfoot\">) {\n return (\n <tfoot\n data-slot=\"table-footer\"\n className={cn(\"border-t border-border bg-muted/40 font-medium\", className)}\n {...props}\n />\n );\n}\n\nexport function TableRow({ className, ...props }: ComponentPropsWithRef<\"tr\">) {\n return (\n <tr\n data-slot=\"table-row\"\n className={cn(\n \"border-b border-border transition-colors duration-[var(--duration-instant)]\",\n \"hover:bg-muted/50 data-[state=selected]:bg-accent\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function TableHead({ className, ...props }: ComponentPropsWithRef<\"th\">) {\n return (\n <th\n data-slot=\"table-head\"\n className={cn(\n \"h-10 px-3 text-start align-middle text-xs font-medium whitespace-nowrap text-muted-foreground\",\n \"[&:has([role=checkbox])]:w-px [&:has([role=checkbox])]:pe-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function TableCell({ className, ...props }: ComponentPropsWithRef<\"td\">) {\n return (\n <td\n data-slot=\"table-cell\"\n className={cn(\n \"px-3 py-2.5 align-middle\",\n \"[&:has([role=checkbox])]:w-px [&:has([role=checkbox])]:pe-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\n/**\n * A caption describing the table.\n *\n * Rendered below the table but announced as its accessible name, which is why\n * it belongs here rather than as a heading above.\n */\nexport function TableCaption({ className, ...props }: ComponentPropsWithRef<\"caption\">) {\n return (\n <caption\n data-slot=\"table-caption\"\n className={cn(\"mt-4 text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n"],"mappings":";;;AA4BA,SAAgB,MAAM,EAAE,WAAW,gBAAgB,GAAG,SAAqB;CACzE,MAAM,EAAE,WAAW,oBAAoB,GAAG,kBAAkB,kBAAkB,CAAC;CAE/E,OACE,oBAAC,OAAD;EACE,aAAU;EAGV,UAAU;EACV,MAAK;EACL,cAAY,MAAM,iBAAiB;EACnC,WAAW,GAAG,mCAAmC,kBAAkB;EACnE,GAAI;EAEJ,UAAA,oBAAC,SAAD;GACE,aAAU;GACV,WAAW,GAAG,iDAAiD,SAAS;GACxE,GAAI;EACL,CAAA;CACE,CAAA;AAET;AAEA,SAAgB,YAAY,EAAE,WAAW,GAAG,SAAyC;CACnF,OACE,oBAAC,SAAD;EACE,aAAU;EACV,WAAW,GAAG,wCAAwC,SAAS;EAC/D,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,UAAU,EAAE,WAAW,GAAG,SAAyC;CACjF,OACE,oBAAC,SAAD;EACE,aAAU;EACV,WAAW,GAAG,8BAA8B,SAAS;EACrD,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,YAAY,EAAE,WAAW,GAAG,SAAyC;CACnF,OACE,oBAAC,SAAD;EACE,aAAU;EACV,WAAW,GAAG,kDAAkD,SAAS;EACzE,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,SAAS,EAAE,WAAW,GAAG,SAAsC;CAC7E,OACE,oBAAC,MAAD;EACE,aAAU;EACV,WAAW,GACT,+EACA,qDACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,UAAU,EAAE,WAAW,GAAG,SAAsC;CAC9E,OACE,oBAAC,MAAD;EACE,aAAU;EACV,WAAW,GACT,iGACA,+DACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAgB,UAAU,EAAE,WAAW,GAAG,SAAsC;CAC9E,OACE,oBAAC,MAAD;EACE,aAAU;EACV,WAAW,GACT,4BACA,+DACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;;;;;;;AAQA,SAAgB,aAAa,EAAE,WAAW,GAAG,SAA2C;CACtF,OACE,oBAAC,WAAD;EACE,aAAU;EACV,WAAW,GAAG,sCAAsC,SAAS;EAC7D,GAAI;CACL,CAAA;AAEL"}
@@ -123,7 +123,7 @@ function TagsInput({ className, value, onValueChange, label, delimiters = [",",
123
123
  onClick: () => {
124
124
  removeAt(index);
125
125
  },
126
- className: cn("-mr-0.5 grid size-4 place-items-center rounded-full", "transition-colors hover:bg-foreground/10", focusRing, disabledStyles),
126
+ className: cn("-me-0.5 grid size-4 place-items-center rounded-full", "transition-colors hover:bg-foreground/10", focusRing, disabledStyles),
127
127
  children: /* @__PURE__ */ jsx("svg", {
128
128
  viewBox: "0 0 24 24",
129
129
  "aria-hidden": "true",
@@ -1 +1 @@
1
- {"version":3,"file":"tags-input.js","names":[],"sources":["../../../src/components/tags-input/tags-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useId,\n useRef,\n useState,\n type ClipboardEvent,\n type ComponentPropsWithRef,\n type KeyboardEvent,\n} from \"react\";\n\nimport { disabledStyles, focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A field whose value is a list of short strings: invited emails, allowed\n * domains, labels, stop sequences.\n *\n * The behaviour that makes this worth shipping is what happens to input that\n * fails validation. Every implementation surveyed either refuses to create the\n * token, or creates it and silently throws it away — and both leave the reader\n * staring at a field that did not do what they asked, with nothing to correct.\n * Here an invalid entry becomes a token like any other, marked invalid and\n * carrying its reason, so it can be seen, read out, and fixed by removing it.\n * `value` holds every token including the invalid ones; the consumer runs the\n * same `validate` to decide whether the field is submittable.\n *\n * Deliberately no suggestion list. A token field with an anchored listbox is a\n * multi-select combobox, and this library already has 544 lines of hand-rolled\n * combobox ARIA. A second copy of that would be the expensive kind of\n * duplication — the kind that drifts. When multi-select is wanted, it belongs in\n * Combobox.\n */\n\n/** Returns `true` for a valid tag, or a reason it is not. */\nexport type TagValidator = (tag: string, existing: string[]) => true | string;\n\nexport interface TagsInputProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"onChange\" | \"children\"\n> {\n value: string[];\n onValueChange: (tags: string[]) => void;\n /** Names the field. Required — an unlabelled list of tokens is unreadable. */\n label: string;\n /** Characters that commit the token being typed, besides Enter. */\n delimiters?: string[];\n /**\n * Decides whether a tag is acceptable. An invalid tag is still added, marked,\n * and given its reason, rather than being refused or dropped.\n */\n validate?: TagValidator;\n /** Rejects a tag already present. Rejections are announced, never silent. */\n allowDuplicates?: boolean;\n /** Refuses further tags once reached, and says so. */\n max?: number;\n placeholder?: string;\n disabled?: boolean;\n inputProps?: Omit<ComponentPropsWithRef<\"input\">, \"value\" | \"onChange\" | \"disabled\">;\n}\n\n/** Splits pasted or typed text on any delimiter, dropping empty fragments. */\nfunction splitOn(text: string, delimiters: string[]): string[] {\n if (delimiters.length === 0) return [text];\n const pattern = new RegExp(`[${delimiters.map((d) => `\\\\${d}`).join(\"\")}]`);\n return text\n .split(pattern)\n .map((part) => part.trim())\n .filter((part) => part.length > 0);\n}\n\nexport function TagsInput({\n className,\n value,\n onValueChange,\n label,\n delimiters = [\",\", \";\"],\n validate,\n allowDuplicates = false,\n max,\n placeholder,\n disabled = false,\n inputProps,\n ...props\n}: TagsInputProps) {\n const [draft, setDraft] = useState(\"\");\n const [announcement, setAnnouncement] = useState(\"\");\n const inputRef = useRef<HTMLInputElement | null>(null);\n const labelId = useId();\n const inputId = useId();\n const hintId = useId();\n\n const atLimit = max !== undefined && value.length >= max;\n\n function reasonFor(tag: string): string | null {\n if (!validate) return null;\n const result = validate(tag, value);\n return result === true ? null : result;\n }\n\n /** Adds tags, reporting anything refused rather than dropping it in silence. */\n function commit(candidates: string[]) {\n const accepted: string[] = [];\n const refused: string[] = [];\n\n // Split defensively rather than trusting the caller. A draft can arrive\n // holding a delimiter from autofill, an IME commit, or a programmatic set,\n // and a token with a comma inside it is never what was meant.\n for (const candidate of candidates.flatMap((part) => splitOn(part, delimiters))) {\n const tag = candidate.trim();\n if (tag.length === 0) continue;\n\n if (!allowDuplicates && [...value, ...accepted].includes(tag)) {\n refused.push(`${tag} is already added`);\n continue;\n }\n if (max !== undefined && value.length + accepted.length >= max) {\n refused.push(`${tag} not added, limit of ${String(max)} reached`);\n continue;\n }\n // An invalid tag is still added. It is the reader's text and they need to\n // see it to fix it; refusing silently is how these fields lose input.\n accepted.push(tag);\n }\n\n if (accepted.length > 0) onValueChange([...value, ...accepted]);\n\n const parts: string[] = [];\n if (accepted.length > 0) {\n const invalid = accepted.filter((tag) => reasonFor(tag) !== null);\n parts.push(`Added ${accepted.join(\", \")}`);\n if (invalid.length > 0) parts.push(`${String(invalid.length)} need attention`);\n }\n parts.push(...refused);\n setAnnouncement(parts.join(\". \"));\n }\n\n function removeAt(index: number) {\n const removed = value[index];\n onValueChange(value.filter((_, position) => position !== index));\n setAnnouncement(removed === undefined ? \"\" : `Removed ${removed}`);\n inputRef.current?.focus();\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n if (event.key === \"Enter\") {\n // Never let a token commit submit the form as well.\n event.preventDefault();\n if (draft.trim().length > 0) {\n commit([draft]);\n setDraft(\"\");\n }\n return;\n }\n\n if (delimiters.includes(event.key)) {\n event.preventDefault();\n if (draft.trim().length > 0) {\n commit([draft]);\n setDraft(\"\");\n }\n return;\n }\n\n // Backspace on an empty field removes the last token — the expected\n // shortcut, and the only way to correct without reaching for the mouse.\n if (event.key === \"Backspace\" && draft.length === 0 && value.length > 0) {\n event.preventDefault();\n removeAt(value.length - 1);\n }\n }\n\n function handlePaste(event: ClipboardEvent<HTMLInputElement>) {\n const text = event.clipboardData.getData(\"text\");\n const parts = splitOn(text, delimiters);\n // Only intercept a paste that actually contains several tags; a single\n // value should behave like an ordinary paste and stay editable.\n if (parts.length <= 1) return;\n\n event.preventDefault();\n commit(parts);\n setDraft(\"\");\n }\n\n return (\n <div data-slot=\"tags-input\" className={cn(\"flex flex-col gap-1.5\", className)} {...props}>\n <label id={labelId} htmlFor={inputId} className=\"w-fit text-sm font-medium\">\n {label}\n </label>\n\n {/* A group rather than a listbox: these are committed values, not options\n being chosen from, and calling it a listbox would promise navigation\n semantics that do not exist here. */}\n <div\n role=\"group\"\n aria-labelledby={labelId}\n data-disabled={disabled || undefined}\n className={cn(\n \"flex min-h-10 w-full flex-wrap items-center gap-1.5 rounded-md border border-input bg-background px-2 py-1.5\",\n \"focus-within:ring-2 focus-within:ring-ring/55\",\n disabled && \"pointer-events-none opacity-55\",\n )}\n >\n {value.length > 0 ? (\n <ul className=\"contents\">\n {value.map((tag, index) => {\n const reason = reasonFor(tag);\n return (\n <li\n key={`${tag}-${String(index)}`}\n data-slot=\"tags-input-tag\"\n data-invalid={reason !== null || undefined}\n className={cn(\n \"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium\",\n reason === null\n ? \"border-transparent bg-secondary text-secondary-foreground\"\n : \"border-destructive/40 bg-destructive/10 text-destructive\",\n )}\n >\n {tag}\n {/* The reason travels with the token, so it is announced when\n the token is, rather than as a detached error elsewhere. */}\n {reason === null ? null : (\n <span className=\"sr-only\">, invalid: {reason}</span>\n )}\n <button\n type=\"button\"\n data-slot=\"tags-input-remove\"\n disabled={disabled}\n aria-label={`Remove ${tag}`}\n onClick={() => {\n removeAt(index);\n }}\n className={cn(\n \"-mr-0.5 grid size-4 place-items-center rounded-full\",\n \"transition-colors hover:bg-foreground/10\",\n focusRing,\n disabledStyles,\n )}\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" className=\"size-2.5\">\n <path\n d=\"M6 6l12 12M18 6L6 18\"\n stroke=\"currentColor\"\n strokeWidth=\"3\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n </li>\n );\n })}\n </ul>\n ) : null}\n\n <input\n ref={inputRef}\n type=\"text\"\n id={inputId}\n data-slot=\"tags-input-field\"\n value={draft}\n disabled={disabled}\n placeholder={value.length === 0 ? placeholder : undefined}\n aria-describedby={hintId}\n onChange={(event) => {\n setDraft(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n onBlur={() => {\n // Committing on blur stops a typed-but-uncommitted value being lost\n // when the reader tabs away thinking they are done.\n if (draft.trim().length > 0) {\n commit([draft]);\n setDraft(\"\");\n }\n }}\n className={cn(\n \"min-w-24 flex-1 self-stretch bg-transparent text-sm outline-none\",\n \"placeholder:text-muted-foreground\",\n )}\n {...inputProps}\n />\n </div>\n\n <span id={hintId} className=\"text-xs text-muted-foreground\">\n {atLimit\n ? `Limit of ${String(max ?? 0)} reached`\n : `Press Enter${delimiters.length > 0 ? ` or ${delimiters.join(\" \")}` : \"\"} to add`}\n </span>\n\n {/* Additions, removals and refusals all land here. A refusal that is only\n visible as \"nothing happened\" is the failure this component exists to\n avoid. */}\n <span aria-live=\"polite\" className=\"sr-only\">\n {announcement}\n </span>\n </div>\n );\n}\n"],"mappings":";;;;;;;AA8DA,SAAS,QAAQ,MAAc,YAAgC;CAC7D,IAAI,WAAW,WAAW,GAAG,OAAO,CAAC,IAAI;CACzC,MAAM,UAAU,IAAI,OAAO,IAAI,WAAW,KAAK,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,EAAE;CAC1E,OAAO,KACJ,MAAM,OAAO,CAAC,CACd,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,QAAQ,SAAS,KAAK,SAAS,CAAC;AACrC;AAEA,SAAgB,UAAU,EACxB,WACA,OACA,eACA,OACA,aAAa,CAAC,KAAK,GAAG,GACtB,UACA,kBAAkB,OAClB,KACA,aACA,WAAW,OACX,YACA,GAAG,SACc;CACjB,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CACrC,MAAM,CAAC,cAAc,mBAAmB,SAAS,EAAE;CACnD,MAAM,WAAW,OAAgC,IAAI;CACrD,MAAM,UAAU,MAAM;CACtB,MAAM,UAAU,MAAM;CACtB,MAAM,SAAS,MAAM;CAErB,MAAM,UAAU,QAAQ,KAAA,KAAa,MAAM,UAAU;CAErD,SAAS,UAAU,KAA4B;EAC7C,IAAI,CAAC,UAAU,OAAO;EACtB,MAAM,SAAS,SAAS,KAAK,KAAK;EAClC,OAAO,WAAW,OAAO,OAAO;CAClC;;CAGA,SAAS,OAAO,YAAsB;EACpC,MAAM,WAAqB,CAAC;EAC5B,MAAM,UAAoB,CAAC;EAK3B,KAAK,MAAM,aAAa,WAAW,SAAS,SAAS,QAAQ,MAAM,UAAU,CAAC,GAAG;GAC/E,MAAM,MAAM,UAAU,KAAK;GAC3B,IAAI,IAAI,WAAW,GAAG;GAEtB,IAAI,CAAC,mBAAmB,CAAC,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC,SAAS,GAAG,GAAG;IAC7D,QAAQ,KAAK,GAAG,IAAI,kBAAkB;IACtC;GACF;GACA,IAAI,QAAQ,KAAA,KAAa,MAAM,SAAS,SAAS,UAAU,KAAK;IAC9D,QAAQ,KAAK,GAAG,IAAI,uBAAuB,OAAO,GAAG,EAAE,SAAS;IAChE;GACF;GAGA,SAAS,KAAK,GAAG;EACnB;EAEA,IAAI,SAAS,SAAS,GAAG,cAAc,CAAC,GAAG,OAAO,GAAG,QAAQ,CAAC;EAE9D,MAAM,QAAkB,CAAC;EACzB,IAAI,SAAS,SAAS,GAAG;GACvB,MAAM,UAAU,SAAS,QAAQ,QAAQ,UAAU,GAAG,MAAM,IAAI;GAChE,MAAM,KAAK,SAAS,SAAS,KAAK,IAAI,GAAG;GACzC,IAAI,QAAQ,SAAS,GAAG,MAAM,KAAK,GAAG,OAAO,QAAQ,MAAM,EAAE,gBAAgB;EAC/E;EACA,MAAM,KAAK,GAAG,OAAO;EACrB,gBAAgB,MAAM,KAAK,IAAI,CAAC;CAClC;CAEA,SAAS,SAAS,OAAe;EAC/B,MAAM,UAAU,MAAM;EACtB,cAAc,MAAM,QAAQ,GAAG,aAAa,aAAa,KAAK,CAAC;EAC/D,gBAAgB,YAAY,KAAA,IAAY,KAAK,WAAW,SAAS;EACjE,SAAS,SAAS,MAAM;CAC1B;CAEA,SAAS,cAAc,OAAwC;EAC7D,IAAI,MAAM,QAAQ,SAAS;GAEzB,MAAM,eAAe;GACrB,IAAI,MAAM,KAAK,CAAC,CAAC,SAAS,GAAG;IAC3B,OAAO,CAAC,KAAK,CAAC;IACd,SAAS,EAAE;GACb;GACA;EACF;EAEA,IAAI,WAAW,SAAS,MAAM,GAAG,GAAG;GAClC,MAAM,eAAe;GACrB,IAAI,MAAM,KAAK,CAAC,CAAC,SAAS,GAAG;IAC3B,OAAO,CAAC,KAAK,CAAC;IACd,SAAS,EAAE;GACb;GACA;EACF;EAIA,IAAI,MAAM,QAAQ,eAAe,MAAM,WAAW,KAAK,MAAM,SAAS,GAAG;GACvE,MAAM,eAAe;GACrB,SAAS,MAAM,SAAS,CAAC;EAC3B;CACF;CAEA,SAAS,YAAY,OAAyC;EAE5D,MAAM,QAAQ,QADD,MAAM,cAAc,QAAQ,MAChB,GAAG,UAAU;EAGtC,IAAI,MAAM,UAAU,GAAG;EAEvB,MAAM,eAAe;EACrB,OAAO,KAAK;EACZ,SAAS,EAAE;CACb;CAEA,OACE,qBAAC,OAAD;EAAK,aAAU;EAAa,WAAW,GAAG,yBAAyB,SAAS;EAAG,GAAI;EAAnF,UAAA;GACE,oBAAC,SAAD;IAAO,IAAI;IAAS,SAAS;IAAS,WAAU;IAC7C,UAAA;GACI,CAAA;GAKP,qBAAC,OAAD;IACE,MAAK;IACL,mBAAiB;IACjB,iBAAe,YAAY,KAAA;IAC3B,WAAW,GACT,gHACA,iDACA,YAAY,gCACd;IARF,UAAA,CAUG,MAAM,SAAS,IACd,oBAAC,MAAD;KAAI,WAAU;KACX,UAAA,MAAM,KAAK,KAAK,UAAU;MACzB,MAAM,SAAS,UAAU,GAAG;MAC5B,OACE,qBAAC,MAAD;OAEE,aAAU;OACV,gBAAc,WAAW,QAAQ,KAAA;OACjC,WAAW,GACT,sFACA,WAAW,OACP,8DACA,0DACN;OATF,UAAA;QAWG;QAGA,WAAW,OAAO,OACjB,qBAAC,QAAD;SAAM,WAAU;SAAhB,UAAA,CAA0B,eAAY,MAAa;;QAErD,oBAAC,UAAD;SACE,MAAK;SACL,aAAU;SACA;SACV,cAAY,UAAU;SACtB,eAAe;UACb,SAAS,KAAK;SAChB;SACA,WAAW,GACT,uDACA,4CACA,WACA,cACF;SAEA,UAAA,oBAAC,OAAD;UAAK,SAAQ;UAAY,eAAY;UAAO,WAAU;UACpD,UAAA,oBAAC,QAAD;WACE,GAAE;WACF,QAAO;WACP,aAAY;WACZ,eAAc;UACf,CAAA;SACE,CAAA;QACC,CAAA;OACN;MAxCG,GAAA,GAAG,IAAI,GAAG,OAAO,KAAK,GAwCzB;KAER,CAAC;IACC,CAAA,IACF,MAEJ,oBAAC,SAAD;KACE,KAAK;KACL,MAAK;KACL,IAAI;KACJ,aAAU;KACV,OAAO;KACG;KACV,aAAa,MAAM,WAAW,IAAI,cAAc,KAAA;KAChD,oBAAkB;KAClB,WAAW,UAAU;MACnB,SAAS,MAAM,OAAO,KAAK;KAC7B;KACA,WAAW;KACX,SAAS;KACT,cAAc;MAGZ,IAAI,MAAM,KAAK,CAAC,CAAC,SAAS,GAAG;OAC3B,OAAO,CAAC,KAAK,CAAC;OACd,SAAS,EAAE;MACb;KACF;KACA,WAAW,GACT,oEACA,mCACF;KACA,GAAI;IACL,CAAA,CACE;;GAEL,oBAAC,QAAD;IAAM,IAAI;IAAQ,WAAU;IACzB,UAAA,UACG,YAAY,OAAO,OAAO,CAAC,EAAE,YAC7B,cAAc,WAAW,SAAS,IAAI,OAAO,WAAW,KAAK,GAAG,MAAM,GAAG;GACzE,CAAA;GAKN,oBAAC,QAAD;IAAM,aAAU;IAAS,WAAU;IAChC,UAAA;GACG,CAAA;EACH;;AAET"}
1
+ {"version":3,"file":"tags-input.js","names":[],"sources":["../../../src/components/tags-input/tags-input.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useId,\n useRef,\n useState,\n type ClipboardEvent,\n type ComponentPropsWithRef,\n type KeyboardEvent,\n} from \"react\";\n\nimport { disabledStyles, focusRing } from \"@/lib/styles\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * A field whose value is a list of short strings: invited emails, allowed\n * domains, labels, stop sequences.\n *\n * The behaviour that makes this worth shipping is what happens to input that\n * fails validation. Every implementation surveyed either refuses to create the\n * token, or creates it and silently throws it away — and both leave the reader\n * staring at a field that did not do what they asked, with nothing to correct.\n * Here an invalid entry becomes a token like any other, marked invalid and\n * carrying its reason, so it can be seen, read out, and fixed by removing it.\n * `value` holds every token including the invalid ones; the consumer runs the\n * same `validate` to decide whether the field is submittable.\n *\n * Deliberately no suggestion list. A token field with an anchored listbox is a\n * multi-select combobox, and this library already has 544 lines of hand-rolled\n * combobox ARIA. A second copy of that would be the expensive kind of\n * duplication — the kind that drifts. When multi-select is wanted, it belongs in\n * Combobox.\n */\n\n/** Returns `true` for a valid tag, or a reason it is not. */\nexport type TagValidator = (tag: string, existing: string[]) => true | string;\n\nexport interface TagsInputProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"onChange\" | \"children\"\n> {\n value: string[];\n onValueChange: (tags: string[]) => void;\n /** Names the field. Required — an unlabelled list of tokens is unreadable. */\n label: string;\n /** Characters that commit the token being typed, besides Enter. */\n delimiters?: string[];\n /**\n * Decides whether a tag is acceptable. An invalid tag is still added, marked,\n * and given its reason, rather than being refused or dropped.\n */\n validate?: TagValidator;\n /** Rejects a tag already present. Rejections are announced, never silent. */\n allowDuplicates?: boolean;\n /** Refuses further tags once reached, and says so. */\n max?: number;\n placeholder?: string;\n disabled?: boolean;\n inputProps?: Omit<ComponentPropsWithRef<\"input\">, \"value\" | \"onChange\" | \"disabled\">;\n}\n\n/** Splits pasted or typed text on any delimiter, dropping empty fragments. */\nfunction splitOn(text: string, delimiters: string[]): string[] {\n if (delimiters.length === 0) return [text];\n const pattern = new RegExp(`[${delimiters.map((d) => `\\\\${d}`).join(\"\")}]`);\n return text\n .split(pattern)\n .map((part) => part.trim())\n .filter((part) => part.length > 0);\n}\n\nexport function TagsInput({\n className,\n value,\n onValueChange,\n label,\n delimiters = [\",\", \";\"],\n validate,\n allowDuplicates = false,\n max,\n placeholder,\n disabled = false,\n inputProps,\n ...props\n}: TagsInputProps) {\n const [draft, setDraft] = useState(\"\");\n const [announcement, setAnnouncement] = useState(\"\");\n const inputRef = useRef<HTMLInputElement | null>(null);\n const labelId = useId();\n const inputId = useId();\n const hintId = useId();\n\n const atLimit = max !== undefined && value.length >= max;\n\n function reasonFor(tag: string): string | null {\n if (!validate) return null;\n const result = validate(tag, value);\n return result === true ? null : result;\n }\n\n /** Adds tags, reporting anything refused rather than dropping it in silence. */\n function commit(candidates: string[]) {\n const accepted: string[] = [];\n const refused: string[] = [];\n\n // Split defensively rather than trusting the caller. A draft can arrive\n // holding a delimiter from autofill, an IME commit, or a programmatic set,\n // and a token with a comma inside it is never what was meant.\n for (const candidate of candidates.flatMap((part) => splitOn(part, delimiters))) {\n const tag = candidate.trim();\n if (tag.length === 0) continue;\n\n if (!allowDuplicates && [...value, ...accepted].includes(tag)) {\n refused.push(`${tag} is already added`);\n continue;\n }\n if (max !== undefined && value.length + accepted.length >= max) {\n refused.push(`${tag} not added, limit of ${String(max)} reached`);\n continue;\n }\n // An invalid tag is still added. It is the reader's text and they need to\n // see it to fix it; refusing silently is how these fields lose input.\n accepted.push(tag);\n }\n\n if (accepted.length > 0) onValueChange([...value, ...accepted]);\n\n const parts: string[] = [];\n if (accepted.length > 0) {\n const invalid = accepted.filter((tag) => reasonFor(tag) !== null);\n parts.push(`Added ${accepted.join(\", \")}`);\n if (invalid.length > 0) parts.push(`${String(invalid.length)} need attention`);\n }\n parts.push(...refused);\n setAnnouncement(parts.join(\". \"));\n }\n\n function removeAt(index: number) {\n const removed = value[index];\n onValueChange(value.filter((_, position) => position !== index));\n setAnnouncement(removed === undefined ? \"\" : `Removed ${removed}`);\n inputRef.current?.focus();\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n if (event.key === \"Enter\") {\n // Never let a token commit submit the form as well.\n event.preventDefault();\n if (draft.trim().length > 0) {\n commit([draft]);\n setDraft(\"\");\n }\n return;\n }\n\n if (delimiters.includes(event.key)) {\n event.preventDefault();\n if (draft.trim().length > 0) {\n commit([draft]);\n setDraft(\"\");\n }\n return;\n }\n\n // Backspace on an empty field removes the last token — the expected\n // shortcut, and the only way to correct without reaching for the mouse.\n if (event.key === \"Backspace\" && draft.length === 0 && value.length > 0) {\n event.preventDefault();\n removeAt(value.length - 1);\n }\n }\n\n function handlePaste(event: ClipboardEvent<HTMLInputElement>) {\n const text = event.clipboardData.getData(\"text\");\n const parts = splitOn(text, delimiters);\n // Only intercept a paste that actually contains several tags; a single\n // value should behave like an ordinary paste and stay editable.\n if (parts.length <= 1) return;\n\n event.preventDefault();\n commit(parts);\n setDraft(\"\");\n }\n\n return (\n <div data-slot=\"tags-input\" className={cn(\"flex flex-col gap-1.5\", className)} {...props}>\n <label id={labelId} htmlFor={inputId} className=\"w-fit text-sm font-medium\">\n {label}\n </label>\n\n {/* A group rather than a listbox: these are committed values, not options\n being chosen from, and calling it a listbox would promise navigation\n semantics that do not exist here. */}\n <div\n role=\"group\"\n aria-labelledby={labelId}\n data-disabled={disabled || undefined}\n className={cn(\n \"flex min-h-10 w-full flex-wrap items-center gap-1.5 rounded-md border border-input bg-background px-2 py-1.5\",\n \"focus-within:ring-2 focus-within:ring-ring/55\",\n disabled && \"pointer-events-none opacity-55\",\n )}\n >\n {value.length > 0 ? (\n <ul className=\"contents\">\n {value.map((tag, index) => {\n const reason = reasonFor(tag);\n return (\n <li\n key={`${tag}-${String(index)}`}\n data-slot=\"tags-input-tag\"\n data-invalid={reason !== null || undefined}\n className={cn(\n \"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium\",\n reason === null\n ? \"border-transparent bg-secondary text-secondary-foreground\"\n : \"border-destructive/40 bg-destructive/10 text-destructive\",\n )}\n >\n {tag}\n {/* The reason travels with the token, so it is announced when\n the token is, rather than as a detached error elsewhere. */}\n {reason === null ? null : (\n <span className=\"sr-only\">, invalid: {reason}</span>\n )}\n <button\n type=\"button\"\n data-slot=\"tags-input-remove\"\n disabled={disabled}\n aria-label={`Remove ${tag}`}\n onClick={() => {\n removeAt(index);\n }}\n className={cn(\n \"-me-0.5 grid size-4 place-items-center rounded-full\",\n \"transition-colors hover:bg-foreground/10\",\n focusRing,\n disabledStyles,\n )}\n >\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" className=\"size-2.5\">\n <path\n d=\"M6 6l12 12M18 6L6 18\"\n stroke=\"currentColor\"\n strokeWidth=\"3\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n </li>\n );\n })}\n </ul>\n ) : null}\n\n <input\n ref={inputRef}\n type=\"text\"\n id={inputId}\n data-slot=\"tags-input-field\"\n value={draft}\n disabled={disabled}\n placeholder={value.length === 0 ? placeholder : undefined}\n aria-describedby={hintId}\n onChange={(event) => {\n setDraft(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n onBlur={() => {\n // Committing on blur stops a typed-but-uncommitted value being lost\n // when the reader tabs away thinking they are done.\n if (draft.trim().length > 0) {\n commit([draft]);\n setDraft(\"\");\n }\n }}\n className={cn(\n \"min-w-24 flex-1 self-stretch bg-transparent text-sm outline-none\",\n \"placeholder:text-muted-foreground\",\n )}\n {...inputProps}\n />\n </div>\n\n <span id={hintId} className=\"text-xs text-muted-foreground\">\n {atLimit\n ? `Limit of ${String(max ?? 0)} reached`\n : `Press Enter${delimiters.length > 0 ? ` or ${delimiters.join(\" \")}` : \"\"} to add`}\n </span>\n\n {/* Additions, removals and refusals all land here. A refusal that is only\n visible as \"nothing happened\" is the failure this component exists to\n avoid. */}\n <span aria-live=\"polite\" className=\"sr-only\">\n {announcement}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Textarea, TextareaProps, textareaVariants } from "./textarea.js";
2
+ export { Textarea, type TextareaProps, textareaVariants };
@@ -0,0 +1,2 @@
1
+ import { Textarea, textareaVariants } from "./textarea.js";
2
+ export { Textarea, textareaVariants };