@inspirare/design-system 0.0.22 → 0.0.24

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 (297) hide show
  1. package/README.md +82 -61
  2. package/dist/book-logo-filled.svg +55 -0
  3. package/dist/components/ui/accessibility-improvements.js +26 -23
  4. package/dist/components/ui/accordion.js +34 -24
  5. package/dist/components/ui/alert-dialog.js +91 -57
  6. package/dist/components/ui/alert.js +38 -26
  7. package/dist/components/ui/aspect-ratio.js +11 -3
  8. package/dist/components/ui/avatar.js +30 -8
  9. package/dist/components/ui/badge.js +20 -14
  10. package/dist/components/ui/breadcrumb.js +69 -56
  11. package/dist/components/ui/button-group.js +17 -13
  12. package/dist/components/ui/button.js +19 -15
  13. package/dist/components/ui/calendar.js +63 -59
  14. package/dist/components/ui/card.js +11 -9
  15. package/dist/components/ui/carousel.js +70 -75
  16. package/dist/components/ui/chart.js +75 -72
  17. package/dist/components/ui/checkbox.js +4 -5
  18. package/dist/components/ui/collapsible.js +12 -14
  19. package/dist/components/ui/color-picker.js +60 -80
  20. package/dist/components/ui/combobox.js +153 -33
  21. package/dist/components/ui/command.js +50 -48
  22. package/dist/components/ui/context-menu.js +129 -68
  23. package/dist/components/ui/dialog.js +65 -60
  24. package/dist/components/ui/drawer.js +81 -40
  25. package/dist/components/ui/dropdown-menu.js +100 -87
  26. package/dist/components/ui/empty.js +7 -6
  27. package/dist/components/ui/field.js +44 -47
  28. package/dist/components/ui/form.js +22 -23
  29. package/dist/components/ui/hover-card.js +14 -11
  30. package/dist/components/ui/input-group.js +16 -13
  31. package/dist/components/ui/input-otp.js +37 -32
  32. package/dist/components/ui/input.js +9 -7
  33. package/dist/components/ui/item.js +42 -36
  34. package/dist/components/ui/kbd.js +2 -1
  35. package/dist/components/ui/label.js +6 -7
  36. package/dist/components/ui/menubar.js +124 -93
  37. package/dist/components/ui/navigation-menu.js +79 -54
  38. package/dist/components/ui/pagination.js +78 -58
  39. package/dist/components/ui/popover.js +35 -16
  40. package/dist/components/ui/progress.js +37 -13
  41. package/dist/components/ui/radio-group.js +14 -14
  42. package/dist/components/ui/resizable.js +24 -15
  43. package/dist/components/ui/scroll-area.js +29 -11
  44. package/dist/components/ui/select.js +98 -56
  45. package/dist/components/ui/separator.js +5 -7
  46. package/dist/components/ui/sheet.js +47 -42
  47. package/dist/components/ui/sidebar.js +279 -248
  48. package/dist/components/ui/skeleton.js +4 -40
  49. package/dist/components/ui/slider.js +27 -29
  50. package/dist/components/ui/sonner.js +27 -10
  51. package/dist/components/ui/spinner.js +2 -0
  52. package/dist/components/ui/switch.js +3 -4
  53. package/dist/components/ui/table.js +6 -6
  54. package/dist/components/ui/tabs.js +45 -22
  55. package/dist/components/ui/textarea.js +3 -2
  56. package/dist/components/ui/theme-toggle.js +38 -41
  57. package/dist/components/ui/toggle-group.js +30 -21
  58. package/dist/components/ui/toggle.js +8 -9
  59. package/dist/components/ui/tooltip.js +28 -23
  60. package/dist/index.css +1 -1
  61. package/dist/index.d.ts +446 -468
  62. package/dist/index.js +58 -58
  63. package/package.json +42 -41
  64. package/src/App.tsx +28 -34
  65. package/src/components/branding/assets/book-logo-filled.svg +55 -0
  66. package/src/components/branding/logo/logo.tsx +56 -0
  67. package/src/components/ui/accessibility-improvements.stories.tsx +18 -0
  68. package/src/components/ui/accessibility-improvements.test.tsx +13 -0
  69. package/src/components/ui/accessibility-improvements.tsx +16 -6
  70. package/src/components/ui/accordion.stories.tsx +19 -0
  71. package/src/components/ui/accordion.test.tsx +14 -0
  72. package/src/components/ui/accordion.tsx +28 -20
  73. package/src/components/ui/alert-dialog.stories.tsx +19 -0
  74. package/src/components/ui/alert-dialog.test.tsx +14 -0
  75. package/src/components/ui/alert-dialog.tsx +159 -123
  76. package/src/components/ui/alert.stories.tsx +19 -0
  77. package/src/components/ui/alert.test.tsx +14 -0
  78. package/src/components/ui/alert.tsx +60 -41
  79. package/src/components/ui/aspect-ratio.stories.tsx +19 -0
  80. package/src/components/ui/aspect-ratio.test.tsx +14 -0
  81. package/src/components/ui/aspect-ratio.tsx +19 -2
  82. package/src/components/ui/attachment.stories.tsx +19 -0
  83. package/src/components/ui/attachment.test.tsx +14 -0
  84. package/src/components/ui/attachment.tsx +209 -0
  85. package/src/components/ui/avatar.stories.tsx +19 -0
  86. package/src/components/ui/avatar.test.tsx +14 -0
  87. package/src/components/ui/avatar.tsx +73 -17
  88. package/src/components/ui/badge.stories.tsx +19 -0
  89. package/src/components/ui/badge.test.tsx +14 -0
  90. package/src/components/ui/badge.tsx +33 -25
  91. package/src/components/ui/breadcrumb.stories.tsx +19 -0
  92. package/src/components/ui/breadcrumb.test.tsx +14 -0
  93. package/src/components/ui/breadcrumb.tsx +98 -86
  94. package/src/components/ui/bubble.stories.tsx +19 -0
  95. package/src/components/ui/bubble.test.tsx +14 -0
  96. package/src/components/ui/bubble.tsx +130 -0
  97. package/src/components/ui/button-group.stories.tsx +19 -0
  98. package/src/components/ui/button-group.test.tsx +14 -0
  99. package/src/components/ui/button-group.tsx +26 -20
  100. package/src/components/ui/button.stories.tsx +19 -0
  101. package/src/components/ui/button.test.tsx +14 -0
  102. package/src/components/ui/button.tsx +30 -31
  103. package/src/components/ui/calendar.stories.tsx +19 -0
  104. package/src/components/ui/calendar.test.tsx +14 -0
  105. package/src/components/ui/calendar.tsx +28 -26
  106. package/src/components/ui/card.stories.tsx +19 -0
  107. package/src/components/ui/card.test.tsx +14 -0
  108. package/src/components/ui/card.tsx +30 -17
  109. package/src/components/ui/carousel.stories.tsx +19 -0
  110. package/src/components/ui/carousel.test.tsx +14 -0
  111. package/src/components/ui/carousel.tsx +115 -135
  112. package/src/components/ui/chart.stories.tsx +18 -0
  113. package/src/components/ui/chart.test.tsx +13 -0
  114. package/src/components/ui/chart.tsx +220 -216
  115. package/src/components/ui/checkbox.stories.tsx +19 -0
  116. package/src/components/ui/checkbox.test.tsx +14 -0
  117. package/src/components/ui/checkbox.tsx +7 -10
  118. package/src/components/ui/collapsible.stories.tsx +19 -0
  119. package/src/components/ui/collapsible.test.tsx +14 -0
  120. package/src/components/ui/collapsible.tsx +11 -26
  121. package/src/components/ui/color-picker.stories.tsx +19 -0
  122. package/src/components/ui/color-picker.test.tsx +14 -0
  123. package/src/components/ui/color-picker.tsx +36 -26
  124. package/src/components/ui/combobox.stories.tsx +19 -0
  125. package/src/components/ui/combobox.test.tsx +14 -0
  126. package/src/components/ui/combobox.tsx +281 -96
  127. package/src/components/ui/command.stories.tsx +19 -0
  128. package/src/components/ui/command.test.tsx +14 -0
  129. package/src/components/ui/command.tsx +59 -47
  130. package/src/components/ui/context-menu.stories.tsx +19 -0
  131. package/src/components/ui/context-menu.test.tsx +14 -0
  132. package/src/components/ui/context-menu.tsx +231 -160
  133. package/src/components/ui/dialog.stories.tsx +19 -0
  134. package/src/components/ui/dialog.test.tsx +14 -0
  135. package/src/components/ui/dialog.tsx +81 -72
  136. package/src/components/ui/direction.stories.tsx +19 -0
  137. package/src/components/ui/direction.test.tsx +14 -0
  138. package/src/components/ui/direction.tsx +6 -0
  139. package/src/components/ui/drawer.stories.tsx +19 -0
  140. package/src/components/ui/drawer.test.tsx +14 -0
  141. package/src/components/ui/drawer.tsx +143 -64
  142. package/src/components/ui/dropdown-menu.stories.tsx +19 -0
  143. package/src/components/ui/dropdown-menu.test.tsx +14 -0
  144. package/src/components/ui/dropdown-menu.tsx +199 -153
  145. package/src/components/ui/empty.stories.tsx +18 -0
  146. package/src/components/ui/empty.test.tsx +13 -0
  147. package/src/components/ui/empty.tsx +11 -9
  148. package/src/components/ui/field.stories.tsx +18 -0
  149. package/src/components/ui/field.test.tsx +13 -0
  150. package/src/components/ui/field.tsx +26 -32
  151. package/src/components/ui/form.stories.tsx +22 -0
  152. package/src/components/ui/form.test.tsx +15 -0
  153. package/src/components/ui/form.tsx +1 -2
  154. package/src/components/ui/hover-card.stories.tsx +19 -0
  155. package/src/components/ui/hover-card.test.tsx +14 -0
  156. package/src/components/ui/hover-card.tsx +34 -32
  157. package/src/components/ui/input-group.stories.tsx +18 -0
  158. package/src/components/ui/input-group.test.tsx +13 -0
  159. package/src/components/ui/input-group.tsx +21 -28
  160. package/src/components/ui/input-otp.stories.tsx +19 -0
  161. package/src/components/ui/input-otp.test.tsx +14 -0
  162. package/src/components/ui/input-otp.tsx +58 -42
  163. package/src/components/ui/input.stories.tsx +19 -0
  164. package/src/components/ui/input.test.tsx +14 -0
  165. package/src/components/ui/input.tsx +9 -8
  166. package/src/components/ui/item.stories.tsx +18 -0
  167. package/src/components/ui/item.test.tsx +13 -0
  168. package/src/components/ui/item.tsx +38 -28
  169. package/src/components/ui/kbd.stories.tsx +18 -0
  170. package/src/components/ui/kbd.test.tsx +13 -0
  171. package/src/components/ui/kbd.tsx +3 -3
  172. package/src/components/ui/label.stories.tsx +18 -0
  173. package/src/components/ui/label.test.tsx +13 -0
  174. package/src/components/ui/label.tsx +7 -11
  175. package/src/components/ui/marker.stories.tsx +19 -0
  176. package/src/components/ui/marker.test.tsx +14 -0
  177. package/src/components/ui/marker.tsx +73 -0
  178. package/src/components/ui/menubar.stories.tsx +19 -0
  179. package/src/components/ui/menubar.test.tsx +14 -0
  180. package/src/components/ui/menubar.tsx +226 -202
  181. package/src/components/ui/message-scroller.stories.tsx +19 -0
  182. package/src/components/ui/message-scroller.test.tsx +14 -0
  183. package/src/components/ui/message-scroller.tsx +131 -0
  184. package/src/components/ui/message.stories.tsx +19 -0
  185. package/src/components/ui/message.test.tsx +14 -0
  186. package/src/components/ui/message.tsx +94 -0
  187. package/src/components/ui/native-select.stories.tsx +18 -0
  188. package/src/components/ui/native-select.test.tsx +13 -0
  189. package/src/components/ui/native-select.tsx +63 -0
  190. package/src/components/ui/navigation-menu.stories.tsx +19 -0
  191. package/src/components/ui/navigation-menu.test.tsx +14 -0
  192. package/src/components/ui/navigation-menu.tsx +145 -103
  193. package/src/components/ui/pagination.stories.tsx +19 -0
  194. package/src/components/ui/pagination.test.tsx +14 -0
  195. package/src/components/ui/pagination.tsx +114 -83
  196. package/src/components/ui/popover.stories.tsx +19 -0
  197. package/src/components/ui/popover.test.tsx +14 -0
  198. package/src/components/ui/popover.tsx +68 -33
  199. package/src/components/ui/progress.stories.tsx +19 -0
  200. package/src/components/ui/progress.test.tsx +14 -0
  201. package/src/components/ui/progress.tsx +67 -15
  202. package/src/components/ui/questionnaire.stories.tsx +19 -0
  203. package/src/components/ui/questionnaire.test.tsx +14 -0
  204. package/src/components/ui/questionnaire.tsx +324 -0
  205. package/src/components/ui/radio-group.stories.tsx +19 -0
  206. package/src/components/ui/radio-group.test.tsx +14 -0
  207. package/src/components/ui/radio-group.tsx +15 -20
  208. package/src/components/ui/resizable.stories.tsx +19 -0
  209. package/src/components/ui/resizable.test.tsx +14 -0
  210. package/src/components/ui/resizable.tsx +37 -30
  211. package/src/components/ui/scroll-area.stories.tsx +19 -0
  212. package/src/components/ui/scroll-area.test.tsx +14 -0
  213. package/src/components/ui/scroll-area.tsx +41 -19
  214. package/src/components/ui/select.stories.tsx +19 -0
  215. package/src/components/ui/select.test.tsx +14 -0
  216. package/src/components/ui/select.tsx +145 -78
  217. package/src/components/ui/separator.stories.tsx +19 -0
  218. package/src/components/ui/separator.test.tsx +14 -0
  219. package/src/components/ui/separator.tsx +8 -11
  220. package/src/components/ui/sheet.stories.tsx +19 -0
  221. package/src/components/ui/sheet.test.tsx +14 -0
  222. package/src/components/ui/sheet.tsx +56 -71
  223. package/src/components/ui/sidebar.stories.tsx +18 -0
  224. package/src/components/ui/sidebar.test.tsx +13 -0
  225. package/src/components/ui/sidebar.tsx +469 -513
  226. package/src/components/ui/skeleton.stories.tsx +19 -0
  227. package/src/components/ui/skeleton.test.tsx +14 -0
  228. package/src/components/ui/skeleton.tsx +8 -114
  229. package/src/components/ui/slider.stories.tsx +19 -0
  230. package/src/components/ui/slider.test.tsx +14 -0
  231. package/src/components/ui/slider.tsx +31 -40
  232. package/src/components/ui/sonner.stories.tsx +19 -0
  233. package/src/components/ui/sonner.test.tsx +14 -0
  234. package/src/components/ui/sonner.tsx +39 -7
  235. package/src/components/ui/spinner.stories.tsx +18 -0
  236. package/src/components/ui/spinner.test.tsx +13 -0
  237. package/src/components/ui/spinner.tsx +3 -7
  238. package/src/components/ui/switch.stories.tsx +19 -0
  239. package/src/components/ui/switch.test.tsx +14 -0
  240. package/src/components/ui/switch.tsx +4 -7
  241. package/src/components/ui/table.stories.tsx +19 -0
  242. package/src/components/ui/table.test.tsx +14 -0
  243. package/src/components/ui/table.tsx +17 -17
  244. package/src/components/ui/tabs.stories.tsx +19 -0
  245. package/src/components/ui/tabs.test.tsx +14 -0
  246. package/src/components/ui/tabs.tsx +71 -31
  247. package/src/components/ui/textarea.stories.tsx +19 -0
  248. package/src/components/ui/textarea.test.tsx +14 -0
  249. package/src/components/ui/textarea.tsx +7 -5
  250. package/src/components/ui/theme-toggle.stories.tsx +18 -0
  251. package/src/components/ui/theme-toggle.test.tsx +13 -0
  252. package/src/components/ui/theme-toggle.tsx +35 -37
  253. package/src/components/ui/toggle-group.stories.tsx +19 -0
  254. package/src/components/ui/toggle-group.test.tsx +14 -0
  255. package/src/components/ui/toggle-group.tsx +41 -25
  256. package/src/components/ui/toggle.stories.tsx +19 -0
  257. package/src/components/ui/toggle.test.tsx +14 -0
  258. package/src/components/ui/toggle.tsx +17 -19
  259. package/src/components/ui/tooltip.stories.tsx +19 -0
  260. package/src/components/ui/tooltip.test.tsx +14 -0
  261. package/src/components/ui/tooltip.tsx +53 -44
  262. package/src/demo/AIDemo.tsx +50 -0
  263. package/src/demo/ButtonsDemo.tsx +41 -18
  264. package/src/demo/CalendarDemo.tsx +1 -1
  265. package/src/demo/FeedbackDemo.tsx +4 -6
  266. package/src/demo/FormsDemo.tsx +34 -11
  267. package/src/demo/NavigationDemo.tsx +38 -13
  268. package/src/demo/OverlaysDemo.tsx +22 -36
  269. package/src/demo/update-demos.js +82 -0
  270. package/src/index.css +66 -46
  271. package/src/lib/utils.ts +3 -3
  272. package/src/stories/Button.stories.ts +54 -0
  273. package/src/stories/Button.tsx +36 -0
  274. package/src/stories/Configure.mdx +388 -0
  275. package/src/stories/Header.stories.ts +34 -0
  276. package/src/stories/Header.tsx +55 -0
  277. package/src/stories/Page.stories.ts +33 -0
  278. package/src/stories/Page.tsx +73 -0
  279. package/src/stories/assets/accessibility.png +0 -0
  280. package/src/stories/assets/accessibility.svg +1 -0
  281. package/src/stories/assets/addon-library.png +0 -0
  282. package/src/stories/assets/assets.png +0 -0
  283. package/src/stories/assets/avif-test-image.avif +0 -0
  284. package/src/stories/assets/context.png +0 -0
  285. package/src/stories/assets/discord.svg +1 -0
  286. package/src/stories/assets/docs.png +0 -0
  287. package/src/stories/assets/figma-plugin.png +0 -0
  288. package/src/stories/assets/github.svg +1 -0
  289. package/src/stories/assets/share.png +0 -0
  290. package/src/stories/assets/styling.png +0 -0
  291. package/src/stories/assets/testing.png +0 -0
  292. package/src/stories/assets/theming.png +0 -0
  293. package/src/stories/assets/tutorials.svg +1 -0
  294. package/src/stories/assets/youtube.svg +1 -0
  295. package/src/stories/button.css +30 -0
  296. package/src/stories/header.css +32 -0
  297. package/src/stories/page.css +68 -0
@@ -1,80 +1,64 @@
1
- "use client";
1
+ "use client"
2
2
 
3
- import * as DialogPrimitive from "@radix-ui/react-dialog";
4
- import { XIcon } from "lucide-react";
5
- import * as React from "react";
3
+ import * as React from "react"
4
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
6
5
 
7
- import { cn } from "@/lib/utils";
6
+ import { cn } from "@/lib/utils"
7
+ import { Button } from "@/components/ui/button"
8
+ import { XIcon } from "lucide-react"
8
9
 
9
- function Dialog({
10
- ...props
11
- }: React.ComponentProps<typeof DialogPrimitive.Root>) {
12
- return <DialogPrimitive.Root data-slot="dialog" {...props} />;
10
+ function Dialog({ ...props }: DialogPrimitive.Root.Props) {
11
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
13
12
  }
14
13
 
15
- function DialogTrigger({
16
- className,
17
- ...props
18
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
19
- return (
20
- <DialogPrimitive.Trigger
21
- data-slot="dialog-trigger"
22
- className={cn("cursor-pointer", className)}
23
- {...props}
24
- />
25
- );
26
- }
14
+ const DialogTrigger = React.forwardRef<
15
+ React.ElementRef<"button">,
16
+ DialogPrimitive.Trigger.Props
17
+ >(({ ...props }, ref) => {
18
+ return <DialogPrimitive.Trigger ref={ref} data-slot="dialog-trigger" {...props} />
19
+ })
20
+ DialogTrigger.displayName = "DialogTrigger"
27
21
 
28
- function DialogPortal({
29
- ...props
30
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
31
- return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
22
+ function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
23
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
32
24
  }
33
25
 
34
- function DialogClose({
35
- className,
36
- ...props
37
- }: React.ComponentProps<typeof DialogPrimitive.Close>) {
38
- return (
39
- <DialogPrimitive.Close
40
- data-slot="dialog-close"
41
- className={cn("cursor-pointer", className)}
42
- {...props}
43
- />
44
- );
45
- }
26
+ const DialogClose = React.forwardRef<
27
+ React.ElementRef<"button">,
28
+ DialogPrimitive.Close.Props
29
+ >(({ ...props }, ref) => {
30
+ return <DialogPrimitive.Close ref={ref} data-slot="dialog-close" {...props} />
31
+ })
32
+ DialogClose.displayName = "DialogClose"
46
33
 
47
34
  function DialogOverlay({
48
35
  className,
49
36
  ...props
50
- }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
37
+ }: DialogPrimitive.Backdrop.Props) {
51
38
  return (
52
- <DialogPrimitive.Overlay
39
+ <DialogPrimitive.Backdrop
53
40
  data-slot="dialog-overlay"
54
41
  className={cn(
55
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
42
+ "fixed inset-0 isolate z-50 bg-black/80 backdrop-blur-sm duration-200 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
56
43
  className
57
44
  )}
58
45
  {...props}
59
46
  />
60
- );
47
+ )
61
48
  }
62
49
 
63
- function DialogContent({
64
- className,
65
- children,
66
- showCloseButton = true,
67
- ...props
68
- }: React.ComponentProps<typeof DialogPrimitive.Content> & {
69
- showCloseButton?: boolean;
70
- }) {
50
+ const DialogContent = React.forwardRef<
51
+ React.ElementRef<"div">,
52
+ DialogPrimitive.Popup.Props & { showCloseButton?: boolean }
53
+ >(({ className, children, showCloseButton = true, ...props }, ref) => {
71
54
  return (
72
- <DialogPortal data-slot="dialog-portal">
55
+ <DialogPortal>
73
56
  <DialogOverlay />
74
- <DialogPrimitive.Content
57
+ <DialogPrimitive.Popup
58
+ ref={ref}
75
59
  data-slot="dialog-content"
76
60
  className={cn(
77
- "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-border p-6 shadow-lg duration-200 sm:max-w-xl md:max-w-2xl lg:max-w-3xl",
61
+ "fixed top-1/2 left-1/2 z-50 grid w-[90vw] max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 rounded-2xl bg-popover/90 backdrop-blur-xl p-6 text-sm text-popover-foreground shadow-2xl ring-1 ring-border duration-200 outline-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
78
62
  className
79
63
  )}
80
64
  {...props}
@@ -83,64 +67,89 @@ function DialogContent({
83
67
  {showCloseButton && (
84
68
  <DialogPrimitive.Close
85
69
  data-slot="dialog-close"
86
- className="ring-offset-background cursor-pointer focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
70
+ render={
71
+ <Button
72
+ variant="ghost"
73
+ className="absolute top-4 right-4 rounded-full"
74
+ size="icon-sm"
75
+ />
76
+ }
87
77
  >
88
78
  <XIcon />
89
79
  <span className="sr-only">Close</span>
90
80
  </DialogPrimitive.Close>
91
81
  )}
92
- </DialogPrimitive.Content>
82
+ </DialogPrimitive.Popup>
93
83
  </DialogPortal>
94
- );
95
- }
84
+ )
85
+ })
86
+ DialogContent.displayName = "DialogContent"
96
87
 
97
88
  function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
98
89
  return (
99
90
  <div
100
91
  data-slot="dialog-header"
101
- className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
92
+ className={cn("flex flex-col gap-2", className)}
102
93
  {...props}
103
94
  />
104
- );
95
+ )
105
96
  }
106
97
 
107
- function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
98
+ function DialogFooter({
99
+ className,
100
+ showCloseButton = false,
101
+ children,
102
+ ...props
103
+ }: React.ComponentProps<"div"> & {
104
+ showCloseButton?: boolean
105
+ }) {
108
106
  return (
109
107
  <div
110
108
  data-slot="dialog-footer"
111
109
  className={cn(
112
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
110
+ "flex flex-col-reverse gap-2 pt-2 sm:flex-row sm:justify-end",
113
111
  className
114
112
  )}
115
113
  {...props}
116
- />
117
- );
114
+ >
115
+ {children}
116
+ {showCloseButton && (
117
+ <DialogPrimitive.Close render={<Button variant="outline" />}>
118
+ Close
119
+ </DialogPrimitive.Close>
120
+ )}
121
+ </div>
122
+ )
118
123
  }
119
124
 
120
- function DialogTitle({
121
- className,
122
- ...props
123
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
125
+ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
124
126
  return (
125
127
  <DialogPrimitive.Title
126
128
  data-slot="dialog-title"
127
- className={cn("text-lg leading-none font-semibold", className)}
129
+ className={cn(
130
+ "font-heading text-base leading-none font-medium",
131
+ className
132
+ )}
128
133
  {...props}
129
134
  />
130
- );
135
+ )
131
136
  }
132
137
 
133
138
  function DialogDescription({
134
139
  className,
135
140
  ...props
136
- }: React.ComponentProps<typeof DialogPrimitive.Description>) {
141
+ }: DialogPrimitive.Description.Props) {
137
142
  return (
138
143
  <DialogPrimitive.Description
139
144
  data-slot="dialog-description"
140
- className={cn("text-muted-foreground text-sm", className)}
145
+ render={<div />}
146
+ className={cn(
147
+ "text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
148
+ className
149
+ )}
141
150
  {...props}
142
151
  />
143
- );
152
+ )
144
153
  }
145
154
 
146
155
  export {
@@ -154,4 +163,4 @@ export {
154
163
  DialogPortal,
155
164
  DialogTitle,
156
165
  DialogTrigger,
157
- };
166
+ }
@@ -0,0 +1,19 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { DirectionProvider } from "./direction";
3
+
4
+ const meta = {
5
+ title: 'Design System/Direction',
6
+ parameters: { layout: 'centered' },
7
+ tags: ['autodocs'],
8
+ } satisfies Meta;
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Default: Story = {
14
+ render: () => (
15
+ <div className="p-4 w-full h-full flex items-center justify-center">
16
+ <DirectionProvider direction="ltr"><div>LTR Content</div></DirectionProvider>
17
+ </div>
18
+ )
19
+ };
@@ -0,0 +1,14 @@
1
+ import { render } from '@testing-library/react';
2
+ import { axe } from 'jest-axe';
3
+ import { DirectionProvider } from "./direction";
4
+
5
+ describe('Direction Accessibility', () => {
6
+ it('should have no accessibility violations in basic render', async () => {
7
+ const Wrapper = () => (
8
+ <DirectionProvider direction="ltr"><div>LTR Content</div></DirectionProvider>
9
+ );
10
+ const { container } = render(<Wrapper />);
11
+ const results = await axe(container);
12
+ expect(results).toHaveNoViolations();
13
+ });
14
+ });
@@ -0,0 +1,6 @@
1
+ "use client"
2
+
3
+ export {
4
+ DirectionProvider,
5
+ useDirection,
6
+ } from "@base-ui/react/direction-provider"
@@ -0,0 +1,19 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { Drawer, DrawerTrigger, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, DrawerFooter, DrawerClose } from "./drawer"; import { Button } from "./button";
3
+
4
+ const meta = {
5
+ title: 'Design System/Drawer',
6
+ parameters: { layout: 'centered' },
7
+ tags: ['autodocs'],
8
+ } satisfies Meta;
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Default: Story = {
14
+ render: () => (
15
+ <div className="p-4 w-full h-full flex items-center justify-center">
16
+ <Drawer><DrawerTrigger className="px-4 py-2 border rounded">Open Drawer</DrawerTrigger><DrawerContent><DrawerHeader><DrawerTitle>Are you absolutely sure?</DrawerTitle><DrawerDescription>This action cannot be undone.</DrawerDescription></DrawerHeader><DrawerFooter><Button>Submit</Button><DrawerClose><Button variant="outline">Cancel</Button></DrawerClose></DrawerFooter></DrawerContent></Drawer>
17
+ </div>
18
+ )
19
+ };
@@ -0,0 +1,14 @@
1
+ import { render } from '@testing-library/react';
2
+ import { axe } from 'jest-axe';
3
+ import { Drawer, DrawerTrigger, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, DrawerFooter, DrawerClose } from "./drawer"; import { Button } from "./button";
4
+
5
+ describe('Drawer Accessibility', () => {
6
+ it('should have no accessibility violations in basic render', async () => {
7
+ const Wrapper = () => (
8
+ <Drawer><DrawerTrigger className="px-4 py-2 border rounded">Open Drawer</DrawerTrigger><DrawerContent><DrawerHeader><DrawerTitle>Are you absolutely sure?</DrawerTitle><DrawerDescription>This action cannot be undone.</DrawerDescription></DrawerHeader><DrawerFooter><Button>Submit</Button><DrawerClose><Button variant="outline">Cancel</Button></DrawerClose></DrawerFooter></DrawerContent></Drawer>
9
+ );
10
+ const { container } = render(<Wrapper />);
11
+ const results = await axe(container);
12
+ expect(results).toHaveNoViolations();
13
+ });
14
+ });
@@ -1,89 +1,167 @@
1
- "use client";
1
+ "use client"
2
2
 
3
- import * as React from "react";
4
- import { Drawer as DrawerPrimitive } from "vaul";
3
+ import * as React from "react"
4
+ import { Drawer as DrawerPrimitive } from "@base-ui/react/drawer"
5
5
 
6
- import { cn } from "@/lib/utils";
6
+ import { cn } from "@/lib/utils"
7
7
 
8
- function Drawer({
9
- ...props
10
- }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
11
- return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
8
+ type DrawerContextProps = {
9
+ hasSnapPoints: boolean
10
+ modal: DrawerPrimitive.Root.Props["modal"]
11
+ showSwipeHandle: boolean
12
+ swipeDirection: NonNullable<DrawerPrimitive.Root.Props["swipeDirection"]>
12
13
  }
13
14
 
14
- function DrawerTrigger({
15
- className,
15
+ const DrawerContext = React.createContext<DrawerContextProps | null>(null)
16
+
17
+ function useDrawer() {
18
+ const context = React.useContext(DrawerContext)
19
+
20
+ if (!context) {
21
+ throw new Error("useDrawer must be used within a Drawer.")
22
+ }
23
+
24
+ return context
25
+ }
26
+
27
+ function Drawer({
28
+ modal = true,
29
+ showSwipeHandle = false,
30
+ snapPoints,
31
+ swipeDirection = "down",
16
32
  ...props
17
- }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
33
+ }: DrawerPrimitive.Root.Props & {
34
+ showSwipeHandle?: boolean
35
+ }) {
36
+ const hasSnapPoints = snapPoints != null && snapPoints.length > 0
37
+ const contextValue = React.useMemo(
38
+ () => ({ hasSnapPoints, modal, showSwipeHandle, swipeDirection }),
39
+ [hasSnapPoints, modal, showSwipeHandle, swipeDirection]
40
+ )
41
+
18
42
  return (
19
- <DrawerPrimitive.Trigger
20
- data-slot="drawer-trigger"
21
- className={cn("cursor-pointer", className)}
22
- {...props}
23
- />
24
- );
43
+ <DrawerContext.Provider value={contextValue}>
44
+ <DrawerPrimitive.Root
45
+ data-slot="drawer"
46
+ modal={modal}
47
+ snapPoints={snapPoints}
48
+ swipeDirection={swipeDirection}
49
+ {...props}
50
+ />
51
+ </DrawerContext.Provider>
52
+ )
25
53
  }
26
54
 
27
- function DrawerPortal({
28
- ...props
29
- }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
30
- return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
55
+ function DrawerTrigger({ ...props }: DrawerPrimitive.Trigger.Props) {
56
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />
57
+ }
58
+
59
+ function DrawerPortal({ ...props }: DrawerPrimitive.Portal.Props) {
60
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />
61
+ }
62
+
63
+ function DrawerClose({ ...props }: DrawerPrimitive.Close.Props) {
64
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />
31
65
  }
32
66
 
33
- function DrawerClose({
67
+ function DrawerOverlay({
34
68
  className,
35
69
  ...props
36
- }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
70
+ }: DrawerPrimitive.Backdrop.Props) {
37
71
  return (
38
- <DrawerPrimitive.Close
39
- data-slot="drawer-close"
40
- className={cn("cursor-pointer", className)}
72
+ <DrawerPrimitive.Backdrop
73
+ data-slot="drawer-overlay"
74
+ className={cn(
75
+ "fixed inset-0 z-50 min-h-dvh bg-black/10 opacity-[max(var(--drawer-overlay-min-opacity,0),calc(1-var(--drawer-swipe-progress)))] transition-opacity duration-450 ease-[cubic-bezier(0.32,0.72,0,1)] select-none data-ending-style:pointer-events-none data-ending-style:opacity-0 data-ending-style:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-snap-points:[--drawer-overlay-min-opacity:0.5] data-starting-style:opacity-0 data-swiping:duration-0 supports-backdrop-filter:backdrop-blur-xs supports-[-webkit-touch-callout:none]:absolute",
76
+ className
77
+ )}
41
78
  {...props}
42
79
  />
43
- );
80
+ )
44
81
  }
45
82
 
46
- function DrawerOverlay({
83
+ function DrawerSwipeHandle({
47
84
  className,
48
85
  ...props
49
- }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
86
+ }: React.ComponentProps<"div">) {
50
87
  return (
51
- <DrawerPrimitive.Overlay
52
- data-slot="drawer-overlay"
88
+ <div
89
+ data-slot="drawer-swipe-handle"
90
+ aria-hidden="true"
53
91
  className={cn(
54
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
92
+ "relative z-10 flex shrink-0 cursor-grab transition-opacity duration-200 group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100 group-data-[swipe-axis=x]/drawer-popup:h-full group-data-[swipe-axis=x]/drawer-popup:w-3 group-data-[swipe-axis=x]/drawer-popup:items-center group-data-[swipe-axis=y]/drawer-popup:h-3 group-data-[swipe-axis=y]/drawer-popup:w-full group-data-[swipe-axis=y]/drawer-popup:justify-center group-data-[swipe-direction=down]/drawer-popup:items-end group-data-[swipe-direction=left]/drawer-popup:order-last group-data-[swipe-direction=left]/drawer-popup:justify-start group-data-[swipe-direction=right]/drawer-popup:justify-end group-data-[swipe-direction=up]/drawer-popup:order-last group-data-[swipe-direction=up]/drawer-popup:items-start after:block after:shrink-0 after:rounded-full after:bg-muted group-data-[swipe-axis=x]/drawer-popup:after:h-24 group-data-[swipe-axis=x]/drawer-popup:after:w-1 group-data-[swipe-axis=y]/drawer-popup:after:h-1 group-data-[swipe-axis=y]/drawer-popup:after:w-24 active:cursor-grabbing",
55
93
  className
56
94
  )}
57
95
  {...props}
58
96
  />
59
- );
97
+ )
60
98
  }
61
99
 
62
100
  function DrawerContent({
63
101
  className,
64
102
  children,
65
103
  ...props
66
- }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
104
+ }: DrawerPrimitive.Popup.Props) {
105
+ const { hasSnapPoints, modal, showSwipeHandle, swipeDirection } = useDrawer()
106
+ const swipeAxis =
107
+ swipeDirection === "down" || swipeDirection === "up" ? "y" : "x"
108
+
67
109
  return (
68
110
  <DrawerPortal data-slot="drawer-portal">
69
- <DrawerOverlay />
70
- <DrawerPrimitive.Content
71
- data-slot="drawer-content"
72
- className={cn(
73
- "group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
74
- "data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
75
- "data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
76
- "data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
77
- "data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
78
- className
79
- )}
80
- {...props}
111
+ {modal === true && (
112
+ <DrawerOverlay data-snap-points={hasSnapPoints ? "" : undefined} />
113
+ )}
114
+ <DrawerPrimitive.Viewport
115
+ data-slot="drawer-viewport"
116
+ data-modal={modal}
117
+ className="pointer-events-none fixed inset-0 z-50 select-none data-[modal=true]:pointer-events-auto"
81
118
  >
82
- <div className="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
83
- {children}
84
- </DrawerPrimitive.Content>
119
+ <DrawerPrimitive.Popup
120
+ data-slot="drawer-popup"
121
+ data-swipe-axis={swipeAxis}
122
+ data-snap-points={hasSnapPoints ? "" : undefined}
123
+ className={cn(
124
+ // Base.
125
+ "group/drawer-popup pointer-events-auto fixed z-50 m-(--drawer-inset,0px) flex h-(--drawer-content-height) max-h-(--drawer-content-max-height,none) min-h-0 w-(--drawer-content-width,auto) transform-[translate3d(var(--translate-x,0px),var(--translate-y,0px),0)_scale(var(--stack-scale))] flex-col bg-popover text-sm text-popover-foreground transition-[transform,height,opacity,filter] duration-450 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-transform outline-none select-none [interpolate-size:allow-keywords] data-[swipe-direction=down]:rounded-t-xl data-[swipe-direction=down]:border-t data-[swipe-direction=left]:rounded-r-xl data-[swipe-direction=left]:border-r data-[swipe-direction=right]:rounded-l-xl data-[swipe-direction=right]:border-l data-[swipe-direction=up]:rounded-b-xl data-[swipe-direction=up]:border-b",
126
+ // Nested.
127
+ "data-nested-drawer-open:overflow-hidden data-nested-drawer-open:brightness-95",
128
+ // Bleed.
129
+ "after:pointer-events-none after:absolute after:bg-(--drawer-bleed-background,var(--color-popover)) data-[swipe-axis=x]:after:inset-y-0 data-[swipe-axis=x]:after:w-(--bleed) data-[swipe-axis=y]:after:inset-x-0 data-[swipe-axis=y]:after:h-(--bleed) data-[swipe-direction=down]:after:top-full data-[swipe-direction=left]:after:right-full data-[swipe-direction=right]:after:left-full data-[swipe-direction=up]:after:bottom-full",
130
+ // Sizing.
131
+ "[--drawer-content-height:var(--drawer-height,auto)] data-[swipe-axis=x]:[--drawer-content-width:75%] data-[swipe-axis=y]:[--drawer-content-max-height:calc(100dvh-6rem)] data-[swipe-axis=y]:data-snap-points:[--drawer-content-height:100dvh] data-[swipe-axis=x]:sm:[--drawer-content-width:24rem]",
132
+ // Stack.
133
+ "[--bleed:3rem] [--peek:1rem] [--stack-height:var(--drawer-frontmost-height,var(--drawer-height,0px))] [--stack-peek-offset:max(0px,calc((var(--nested-drawers)-var(--stack-progress))*var(--peek)))] [--stack-progress:clamp(0,var(--drawer-swipe-progress),1)] [--stack-scale-base:max(0,calc(1-(var(--nested-drawers)*var(--stack-step))))] [--stack-scale:clamp(0,calc(var(--stack-scale-base)+(var(--stack-step)*var(--stack-progress))),1)] [--stack-shrink:calc(1-var(--stack-scale))] [--stack-step:0.05]",
134
+ // Transitions.
135
+ "data-ending-style:transform-(--closed-transform) data-ending-style:opacity-[0.9999] data-ending-style:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-nested-drawer-swiping:duration-0 data-ending-style:data-nested-drawer-swiping:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-starting-style:transform-(--closed-transform) data-swiping:duration-0 data-ending-style:data-swiping:duration-[calc(var(--drawer-swipe-strength)*400ms)]",
136
+ // Axis: y.
137
+ "data-[swipe-axis=y]:inset-x-0 data-[swipe-axis=y]:data-nested-drawer-open:h-(--stack-height)",
138
+ // Axis: x.
139
+ "data-[swipe-axis=x]:inset-y-0 data-[swipe-axis=x]:flex-row",
140
+ // Direction: down.
141
+ "data-[swipe-direction=down]:bottom-0 data-[swipe-direction=down]:origin-bottom data-[swipe-direction=down]:[--closed-transform:translate3d(0,calc(100%+var(--drawer-inset,0px)+2px),0)] data-[swipe-direction=down]:[--translate-y:calc(var(--drawer-snap-point-offset,0px)+var(--drawer-swipe-movement-y)-var(--stack-peek-offset)-(var(--stack-shrink)*var(--stack-height)))]",
142
+ // Direction: up.
143
+ "data-[swipe-direction=up]:top-0 data-[swipe-direction=up]:origin-top data-[swipe-direction=up]:[--closed-transform:translate3d(0,calc(-100%-var(--drawer-inset,0px)-2px),0)] data-[swipe-direction=up]:[--translate-y:calc(var(--drawer-snap-point-offset,0px)+var(--drawer-swipe-movement-y)+var(--stack-peek-offset)+(var(--stack-shrink)*var(--stack-height)))]",
144
+ // Direction: left.
145
+ "data-[swipe-direction=left]:left-0 data-[swipe-direction=left]:origin-left data-[swipe-direction=left]:[--closed-transform:translate3d(calc(-100%-var(--drawer-inset,0px)-2px),0,0)] data-[swipe-direction=left]:[--translate-x:calc(var(--drawer-swipe-movement-x)+var(--stack-peek-offset)+(var(--stack-shrink)*100%))]",
146
+ // Direction: right.
147
+ "data-[swipe-direction=right]:right-0 data-[swipe-direction=right]:origin-right data-[swipe-direction=right]:[--closed-transform:translate3d(calc(100%+var(--drawer-inset,0px)+2px),0,0)] data-[swipe-direction=right]:[--translate-x:calc(var(--drawer-swipe-movement-x)-var(--stack-peek-offset)-(var(--stack-shrink)*100%))]",
148
+ className
149
+ )}
150
+ {...props}
151
+ >
152
+ {showSwipeHandle && <DrawerSwipeHandle />}
153
+ <DrawerPrimitive.Content
154
+ data-slot="drawer-content"
155
+ className={cn(
156
+ "flex min-h-0 flex-1 flex-col overflow-hidden overscroll-contain rounded-[inherit] transition-opacity duration-300 ease-[cubic-bezier(0.45,1.005,0,1.005)] select-text group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100 group-data-swiping/drawer-popup:select-none"
157
+ )}
158
+ >
159
+ {children}
160
+ </DrawerPrimitive.Content>
161
+ </DrawerPrimitive.Popup>
162
+ </DrawerPrimitive.Viewport>
85
163
  </DrawerPortal>
86
- );
164
+ )
87
165
  }
88
166
 
89
167
  function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
@@ -91,54 +169,55 @@ function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
91
169
  <div
92
170
  data-slot="drawer-header"
93
171
  className={cn(
94
- "flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left",
172
+ "flex shrink-0 flex-col gap-0.5 p-4 pb-0 group-data-[swipe-axis=y]/drawer-popup:text-center md:gap-0.5 md:text-left",
95
173
  className
96
174
  )}
97
175
  {...props}
98
176
  />
99
- );
177
+ )
100
178
  }
101
179
 
102
180
  function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
103
181
  return (
104
182
  <div
105
183
  data-slot="drawer-footer"
106
- className={cn("mt-auto flex flex-col gap-2 p-4", className)}
184
+ className={cn("mt-auto flex shrink-0 flex-col gap-2 p-4 pt-0", className)}
107
185
  {...props}
108
186
  />
109
- );
187
+ )
110
188
  }
111
189
 
112
- function DrawerTitle({
113
- className,
114
- ...props
115
- }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
190
+ function DrawerTitle({ className, ...props }: DrawerPrimitive.Title.Props) {
116
191
  return (
117
192
  <DrawerPrimitive.Title
118
193
  data-slot="drawer-title"
119
- className={cn("text-foreground font-semibold", className)}
194
+ className={cn(
195
+ "font-heading text-base font-medium text-foreground",
196
+ className
197
+ )}
120
198
  {...props}
121
199
  />
122
- );
200
+ )
123
201
  }
124
202
 
125
203
  function DrawerDescription({
126
204
  className,
127
205
  ...props
128
- }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
206
+ }: DrawerPrimitive.Description.Props) {
129
207
  return (
130
208
  <DrawerPrimitive.Description
131
209
  data-slot="drawer-description"
132
- className={cn("text-muted-foreground text-sm", className)}
210
+ className={cn("text-sm text-balance text-muted-foreground", className)}
133
211
  {...props}
134
212
  />
135
- );
213
+ )
136
214
  }
137
215
 
138
216
  export {
139
217
  Drawer,
140
218
  DrawerPortal,
141
219
  DrawerOverlay,
220
+ DrawerSwipeHandle,
142
221
  DrawerTrigger,
143
222
  DrawerClose,
144
223
  DrawerContent,
@@ -146,4 +225,4 @@ export {
146
225
  DrawerFooter,
147
226
  DrawerTitle,
148
227
  DrawerDescription,
149
- };
228
+ }
@@ -0,0 +1,19 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup } from "./dropdown-menu";
3
+
4
+ const meta = {
5
+ title: 'Design System/DropdownMenu',
6
+ parameters: { layout: 'centered' },
7
+ tags: ['autodocs'],
8
+ } satisfies Meta;
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Default: Story = {
14
+ render: () => (
15
+ <div className="p-4 w-full h-full flex items-center justify-center">
16
+ <DropdownMenu><DropdownMenuTrigger className="px-4 py-2 border rounded">Open</DropdownMenuTrigger><DropdownMenuContent><DropdownMenuLabel>My Account</DropdownMenuLabel><DropdownMenuSeparator /><DropdownMenuGroup><DropdownMenuItem>Profile</DropdownMenuItem><DropdownMenuItem>Billing</DropdownMenuItem></DropdownMenuGroup></DropdownMenuContent></DropdownMenu>
17
+ </div>
18
+ )
19
+ };
@@ -0,0 +1,14 @@
1
+ import { render } from '@testing-library/react';
2
+ import { axe } from 'jest-axe';
3
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup } from "./dropdown-menu";
4
+
5
+ describe('DropdownMenu Accessibility', () => {
6
+ it('should have no accessibility violations in basic render', async () => {
7
+ const Wrapper = () => (
8
+ <DropdownMenu><DropdownMenuTrigger className="px-4 py-2 border rounded">Open</DropdownMenuTrigger><DropdownMenuContent><DropdownMenuLabel>My Account</DropdownMenuLabel><DropdownMenuSeparator /><DropdownMenuGroup><DropdownMenuItem>Profile</DropdownMenuItem><DropdownMenuItem>Billing</DropdownMenuItem></DropdownMenuGroup></DropdownMenuContent></DropdownMenu>
9
+ );
10
+ const { container } = render(<Wrapper />);
11
+ const results = await axe(container);
12
+ expect(results).toHaveNoViolations();
13
+ });
14
+ });