@luanthnh/cntt-ui 0.1.5

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 (255) hide show
  1. package/.storybook/globals.d.ts +1 -0
  2. package/.storybook/main.ts +29 -0
  3. package/.storybook/preview.ts +32 -0
  4. package/README.md +86 -0
  5. package/assets/fonts/Montserrat-Black.eot +0 -0
  6. package/assets/fonts/Montserrat-Black.ttf +0 -0
  7. package/assets/fonts/Montserrat-Black.woff +0 -0
  8. package/assets/fonts/Montserrat-Black.woff2 +0 -0
  9. package/assets/fonts/Montserrat-BlackItalic.eot +0 -0
  10. package/assets/fonts/Montserrat-BlackItalic.ttf +0 -0
  11. package/assets/fonts/Montserrat-BlackItalic.woff +0 -0
  12. package/assets/fonts/Montserrat-BlackItalic.woff2 +0 -0
  13. package/assets/fonts/Montserrat-Bold.eot +0 -0
  14. package/assets/fonts/Montserrat-Bold.ttf +0 -0
  15. package/assets/fonts/Montserrat-Bold.woff +0 -0
  16. package/assets/fonts/Montserrat-Bold.woff2 +0 -0
  17. package/assets/fonts/Montserrat-BoldItalic.eot +0 -0
  18. package/assets/fonts/Montserrat-BoldItalic.ttf +0 -0
  19. package/assets/fonts/Montserrat-BoldItalic.woff +0 -0
  20. package/assets/fonts/Montserrat-BoldItalic.woff2 +0 -0
  21. package/assets/fonts/Montserrat-ExtraBold.eot +0 -0
  22. package/assets/fonts/Montserrat-ExtraBold.ttf +0 -0
  23. package/assets/fonts/Montserrat-ExtraBold.woff +0 -0
  24. package/assets/fonts/Montserrat-ExtraBold.woff2 +0 -0
  25. package/assets/fonts/Montserrat-ExtraBoldItalic.eot +0 -0
  26. package/assets/fonts/Montserrat-ExtraBoldItalic.ttf +0 -0
  27. package/assets/fonts/Montserrat-ExtraBoldItalic.woff +0 -0
  28. package/assets/fonts/Montserrat-ExtraBoldItalic.woff2 +0 -0
  29. package/assets/fonts/Montserrat-ExtraLight.eot +0 -0
  30. package/assets/fonts/Montserrat-ExtraLight.ttf +0 -0
  31. package/assets/fonts/Montserrat-ExtraLight.woff +0 -0
  32. package/assets/fonts/Montserrat-ExtraLight.woff2 +0 -0
  33. package/assets/fonts/Montserrat-ExtraLightItalic.eot +0 -0
  34. package/assets/fonts/Montserrat-ExtraLightItalic.ttf +0 -0
  35. package/assets/fonts/Montserrat-ExtraLightItalic.woff +0 -0
  36. package/assets/fonts/Montserrat-ExtraLightItalic.woff2 +0 -0
  37. package/assets/fonts/Montserrat-Italic.eot +0 -0
  38. package/assets/fonts/Montserrat-Italic.ttf +0 -0
  39. package/assets/fonts/Montserrat-Italic.woff +0 -0
  40. package/assets/fonts/Montserrat-Italic.woff2 +0 -0
  41. package/assets/fonts/Montserrat-Light.eot +0 -0
  42. package/assets/fonts/Montserrat-Light.ttf +0 -0
  43. package/assets/fonts/Montserrat-Light.woff +0 -0
  44. package/assets/fonts/Montserrat-Light.woff2 +0 -0
  45. package/assets/fonts/Montserrat-LightItalic.eot +0 -0
  46. package/assets/fonts/Montserrat-LightItalic.ttf +0 -0
  47. package/assets/fonts/Montserrat-LightItalic.woff +0 -0
  48. package/assets/fonts/Montserrat-LightItalic.woff2 +0 -0
  49. package/assets/fonts/Montserrat-Medium.eot +0 -0
  50. package/assets/fonts/Montserrat-Medium.ttf +0 -0
  51. package/assets/fonts/Montserrat-Medium.woff +0 -0
  52. package/assets/fonts/Montserrat-Medium.woff2 +0 -0
  53. package/assets/fonts/Montserrat-MediumItalic.eot +0 -0
  54. package/assets/fonts/Montserrat-MediumItalic.ttf +0 -0
  55. package/assets/fonts/Montserrat-MediumItalic.woff +0 -0
  56. package/assets/fonts/Montserrat-MediumItalic.woff2 +0 -0
  57. package/assets/fonts/Montserrat-Regular.eot +0 -0
  58. package/assets/fonts/Montserrat-Regular.ttf +0 -0
  59. package/assets/fonts/Montserrat-Regular.woff +0 -0
  60. package/assets/fonts/Montserrat-Regular.woff2 +0 -0
  61. package/assets/fonts/Montserrat-SemiBold.eot +0 -0
  62. package/assets/fonts/Montserrat-SemiBold.ttf +0 -0
  63. package/assets/fonts/Montserrat-SemiBold.woff +0 -0
  64. package/assets/fonts/Montserrat-SemiBold.woff2 +0 -0
  65. package/assets/fonts/Montserrat-SemiBoldItalic.eot +0 -0
  66. package/assets/fonts/Montserrat-SemiBoldItalic.ttf +0 -0
  67. package/assets/fonts/Montserrat-SemiBoldItalic.woff +0 -0
  68. package/assets/fonts/Montserrat-SemiBoldItalic.woff2 +0 -0
  69. package/assets/fonts/Montserrat-Thin.eot +0 -0
  70. package/assets/fonts/Montserrat-Thin.ttf +0 -0
  71. package/assets/fonts/Montserrat-Thin.woff +0 -0
  72. package/assets/fonts/Montserrat-Thin.woff2 +0 -0
  73. package/assets/fonts/Montserrat-ThinItalic.eot +0 -0
  74. package/assets/fonts/Montserrat-ThinItalic.ttf +0 -0
  75. package/assets/fonts/Montserrat-ThinItalic.woff +0 -0
  76. package/assets/fonts/Montserrat-ThinItalic.woff2 +0 -0
  77. package/assets/fonts/Montserrat-Variable.eot +0 -0
  78. package/assets/fonts/Montserrat-Variable.ttf +0 -0
  79. package/assets/fonts/Montserrat-Variable.woff +0 -0
  80. package/assets/fonts/Montserrat-Variable.woff2 +0 -0
  81. package/assets/fonts/Montserrat-VariableItalic.eot +0 -0
  82. package/assets/fonts/Montserrat-VariableItalic.ttf +0 -0
  83. package/assets/fonts/Montserrat-VariableItalic.woff +0 -0
  84. package/assets/fonts/Montserrat-VariableItalic.woff2 +0 -0
  85. package/assets/icons/arrow-left.svg +1 -0
  86. package/assets/icons/file.svg +1 -0
  87. package/assets/icons/globe.svg +1 -0
  88. package/assets/icons/logo-line.svg +1 -0
  89. package/assets/icons/next.svg +1 -0
  90. package/assets/icons/panel-left-expand.svg +1 -0
  91. package/assets/icons/placeholder.svg +57 -0
  92. package/assets/icons/vercel.svg +1 -0
  93. package/assets/icons/window.svg +1 -0
  94. package/assets/lotties/error-404.json +19642 -0
  95. package/assets/lotties/error.json +2414 -0
  96. package/assets/lotties/loader.json +305 -0
  97. package/components/Welcome.mdx +74 -0
  98. package/components/lenis/index.tsx +48 -0
  99. package/components/motion/auto-height.tsx +56 -0
  100. package/components/motion/cursor.tsx +108 -0
  101. package/components/motion/highlight.tsx +605 -0
  102. package/components/motion/number-ticker.tsx +55 -0
  103. package/components/motion/slot.tsx +106 -0
  104. package/components/motion/waves.tsx +417 -0
  105. package/components/primitives/tabs.tsx +174 -0
  106. package/components/ui/Accordion/index.stories.tsx +39 -0
  107. package/components/ui/Accordion/index.tsx +170 -0
  108. package/components/ui/Alert/index.stories.tsx +39 -0
  109. package/components/ui/Alert/index.tsx +60 -0
  110. package/components/ui/AlertDialog/index.stories.tsx +47 -0
  111. package/components/ui/AlertDialog/index.tsx +172 -0
  112. package/components/ui/AspectRatio/index.stories.tsx +40 -0
  113. package/components/ui/AspectRatio/index.tsx +9 -0
  114. package/components/ui/Avatar/index.stories.tsx +39 -0
  115. package/components/ui/Avatar/index.tsx +44 -0
  116. package/components/ui/Badge/index.stories.tsx +64 -0
  117. package/components/ui/Badge/index.tsx +46 -0
  118. package/components/ui/Breadcrumb/index.stories.tsx +64 -0
  119. package/components/ui/Breadcrumb/index.tsx +102 -0
  120. package/components/ui/Button/index.stories.tsx +232 -0
  121. package/components/ui/Button/index.tsx +114 -0
  122. package/components/ui/Calendar/index.stories.tsx +20 -0
  123. package/components/ui/Calendar/index.tsx +149 -0
  124. package/components/ui/Card/index.stories.tsx +39 -0
  125. package/components/ui/Card/index.tsx +65 -0
  126. package/components/ui/Carousel/index.stories.tsx +37 -0
  127. package/components/ui/Carousel/index.tsx +242 -0
  128. package/components/ui/Chart/index.stories.tsx +53 -0
  129. package/components/ui/Chart/index.tsx +322 -0
  130. package/components/ui/Checkbox/index.stories.tsx +56 -0
  131. package/components/ui/Checkbox/index.tsx +167 -0
  132. package/components/ui/CircleProcess/index.stories.tsx +29 -0
  133. package/components/ui/CircleProcess/index.tsx +50 -0
  134. package/components/ui/Collapsible/index.stories.tsx +33 -0
  135. package/components/ui/Collapsible/index.tsx +124 -0
  136. package/components/ui/Command/index.stories.tsx +65 -0
  137. package/components/ui/Command/index.tsx +161 -0
  138. package/components/ui/Container/index.stories.tsx +22 -0
  139. package/components/ui/Container/index.tsx +30 -0
  140. package/components/ui/ContextMenu/index.stories.tsx +51 -0
  141. package/components/ui/ContextMenu/index.tsx +224 -0
  142. package/components/ui/Dialog/index.stories.tsx +44 -0
  143. package/components/ui/Dialog/index.tsx +156 -0
  144. package/components/ui/Drawer/index.stories.tsx +54 -0
  145. package/components/ui/Drawer/index.tsx +124 -0
  146. package/components/ui/DropdownMenu/index.stories.tsx +83 -0
  147. package/components/ui/DropdownMenu/index.tsx +231 -0
  148. package/components/ui/Dropzone/index.stories.tsx +18 -0
  149. package/components/ui/Dropzone/index.tsx +47 -0
  150. package/components/ui/Form/date-field.tsx +77 -0
  151. package/components/ui/Form/index.stories.tsx +67 -0
  152. package/components/ui/Form/index.tsx +188 -0
  153. package/components/ui/Form/select-field.tsx +55 -0
  154. package/components/ui/Form/text-area-field.tsx +37 -0
  155. package/components/ui/Form/text-field.tsx +72 -0
  156. package/components/ui/HStack/index.stories.tsx +48 -0
  157. package/components/ui/HStack/index.tsx +73 -0
  158. package/components/ui/HoverCard/index.stories.tsx +38 -0
  159. package/components/ui/HoverCard/index.tsx +38 -0
  160. package/components/ui/Icons/index.stories.tsx +27 -0
  161. package/components/ui/Icons/index.tsx +33 -0
  162. package/components/ui/ImageWithFallback/index.stories.tsx +32 -0
  163. package/components/ui/ImageWithFallback/index.tsx +34 -0
  164. package/components/ui/Input/index.stories.tsx +47 -0
  165. package/components/ui/Input/index.tsx +21 -0
  166. package/components/ui/InputOtp/index.stories.tsx +35 -0
  167. package/components/ui/InputOtp/index.tsx +70 -0
  168. package/components/ui/Label/index.stories.tsx +18 -0
  169. package/components/ui/Label/index.tsx +21 -0
  170. package/components/ui/Marquee/index.stories.tsx +71 -0
  171. package/components/ui/Marquee/index.tsx +65 -0
  172. package/components/ui/Menubar/index.stories.tsx +116 -0
  173. package/components/ui/Menubar/index.tsx +252 -0
  174. package/components/ui/NavigationMenu/index.stories.tsx +112 -0
  175. package/components/ui/NavigationMenu/index.tsx +185 -0
  176. package/components/ui/NoData/index.stories.tsx +24 -0
  177. package/components/ui/NoData/index.tsx +19 -0
  178. package/components/ui/Pagination/index.stories.tsx +53 -0
  179. package/components/ui/Pagination/index.tsx +114 -0
  180. package/components/ui/Popover/index.stories.tsx +31 -0
  181. package/components/ui/Popover/index.tsx +42 -0
  182. package/components/ui/Progress/index.stories.tsx +35 -0
  183. package/components/ui/Progress/index.tsx +28 -0
  184. package/components/ui/RadioGroup/index.stories.tsx +28 -0
  185. package/components/ui/RadioGroup/index.tsx +45 -0
  186. package/components/ui/Resizable/index.stories.tsx +44 -0
  187. package/components/ui/Resizable/index.tsx +54 -0
  188. package/components/ui/ScrollArea/index.stories.tsx +31 -0
  189. package/components/ui/ScrollArea/index.tsx +56 -0
  190. package/components/ui/Select/index.stories.tsx +64 -0
  191. package/components/ui/Select/index.tsx +170 -0
  192. package/components/ui/Separator/index.stories.tsx +31 -0
  193. package/components/ui/Separator/index.tsx +28 -0
  194. package/components/ui/Sheet/index.stories.tsx +45 -0
  195. package/components/ui/Sheet/index.tsx +130 -0
  196. package/components/ui/Sidebar/index.stories.tsx +82 -0
  197. package/components/ui/Sidebar/index.tsx +676 -0
  198. package/components/ui/Skeleton/index.stories.tsx +36 -0
  199. package/components/ui/Skeleton/index.tsx +13 -0
  200. package/components/ui/Slider/index.stories.tsx +48 -0
  201. package/components/ui/Slider/index.tsx +82 -0
  202. package/components/ui/Slot/index.stories.tsx +29 -0
  203. package/components/ui/Slot/index.tsx +106 -0
  204. package/components/ui/Sonner/index.stories.tsx +36 -0
  205. package/components/ui/Sonner/index.tsx +31 -0
  206. package/components/ui/Switch/index.stories.tsx +33 -0
  207. package/components/ui/Switch/index.tsx +28 -0
  208. package/components/ui/Table/index.stories.tsx +74 -0
  209. package/components/ui/Table/index.tsx +95 -0
  210. package/components/ui/Tabs/index.stories.tsx +38 -0
  211. package/components/ui/Tabs/index.tsx +78 -0
  212. package/components/ui/Text/index.stories.tsx +53 -0
  213. package/components/ui/Text/index.tsx +138 -0
  214. package/components/ui/Textarea/index.stories.tsx +25 -0
  215. package/components/ui/Textarea/index.tsx +18 -0
  216. package/components/ui/Toggle/index.stories.tsx +52 -0
  217. package/components/ui/Toggle/index.tsx +46 -0
  218. package/components/ui/ToggleGroup/index.stories.tsx +52 -0
  219. package/components/ui/ToggleGroup/index.tsx +69 -0
  220. package/components/ui/Tooltip/index.stories.tsx +29 -0
  221. package/components/ui/Tooltip/index.tsx +35 -0
  222. package/components/ui/VStack/index.stories.tsx +45 -0
  223. package/components/ui/VStack/index.tsx +69 -0
  224. package/components/ui/colors.stories.tsx +148 -0
  225. package/dist/arrow-left-46B4CAEY.svg +1 -0
  226. package/dist/file-4IXBJF4J.svg +1 -0
  227. package/dist/globe-KVAXBN2U.svg +1 -0
  228. package/dist/index.cjs +6001 -0
  229. package/dist/index.cjs.map +1 -0
  230. package/dist/index.d.cts +693 -0
  231. package/dist/index.d.ts +693 -0
  232. package/dist/index.js +5714 -0
  233. package/dist/index.js.map +1 -0
  234. package/dist/logo-line-QLUD5DAV.svg +1 -0
  235. package/dist/next-HOXZBJQP.svg +1 -0
  236. package/dist/panel-left-expand-SIPFBG4J.svg +1 -0
  237. package/dist/placeholder-H3V4XYVI.svg +57 -0
  238. package/dist/vercel-KFYFHF3A.svg +1 -0
  239. package/dist/window-JNUL4Q2E.svg +1 -0
  240. package/eslint.config.js +10 -0
  241. package/globals.css +994 -0
  242. package/hooks/index.ts +3 -0
  243. package/hooks/use-auto-height.tsx +99 -0
  244. package/hooks/use-controlled-state.tsx +32 -0
  245. package/hooks/use-mobile.ts +19 -0
  246. package/index.ts +58 -0
  247. package/lib/get-strict-context.ts +15 -0
  248. package/lib/utils.ts +10 -0
  249. package/package.json +107 -0
  250. package/scripts/generate-exports.ts +32 -0
  251. package/tsconfig.json +12 -0
  252. package/tsconfig.tsbuildinfo +1 -0
  253. package/tsup.config.ts +11 -0
  254. package/types/svg.d.ts +10 -0
  255. package/vercel.json +5 -0
@@ -0,0 +1,44 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { Button } from '@/components/ui/Button';
4
+
5
+ import {
6
+ Dialog,
7
+ DialogContent,
8
+ DialogDescription,
9
+ DialogFooter,
10
+ DialogHeader,
11
+ DialogTitle,
12
+ DialogTrigger,
13
+ } from './index';
14
+
15
+ const meta: Meta<typeof Dialog> = {
16
+ title: 'UI/Dialog',
17
+ component: Dialog,
18
+ tags: ['autodocs'],
19
+ };
20
+
21
+ export default meta;
22
+ type Story = StoryObj<typeof Dialog>;
23
+
24
+ export const Default: Story = {
25
+ render: (args) => (
26
+ <Dialog {...args}>
27
+ <DialogTrigger asChild>
28
+ <Button variant="outline">Open Dialog</Button>
29
+ </DialogTrigger>
30
+ <DialogContent>
31
+ <DialogHeader>
32
+ <DialogTitle>Are you absolutely sure?</DialogTitle>
33
+ <DialogDescription>
34
+ This action cannot be undone. This will permanently delete your account and remove your
35
+ data from our servers.
36
+ </DialogDescription>
37
+ </DialogHeader>
38
+ <DialogFooter>
39
+ <Button type="submit">Confirm</Button>
40
+ </DialogFooter>
41
+ </DialogContent>
42
+ </Dialog>
43
+ ),
44
+ };
@@ -0,0 +1,156 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
5
+ import { useLenis } from 'lenis/react';
6
+ import { XIcon } from 'lucide-react';
7
+
8
+ import { cn } from '@/lib/utils';
9
+ import { useControlledState } from '@/hooks/use-controlled-state';
10
+
11
+ function Dialog({
12
+ open: openProp,
13
+ defaultOpen = false,
14
+ onOpenChange,
15
+ ...props
16
+ }: React.ComponentProps<typeof DialogPrimitive.Root> & {
17
+ open?: boolean;
18
+ defaultOpen?: boolean;
19
+ onOpenChange?: (open: boolean) => void;
20
+ }) {
21
+ const [open, setOpen] = useControlledState<boolean>({
22
+ value: openProp,
23
+ defaultValue: defaultOpen,
24
+ onChange: onOpenChange,
25
+ });
26
+
27
+ const lenis = useLenis();
28
+
29
+ React.useEffect(() => {
30
+ if (open) {
31
+ lenis?.stop();
32
+ } else {
33
+ lenis?.start();
34
+ }
35
+ }, [open, lenis]);
36
+
37
+ return <DialogPrimitive.Root data-slot="dialog" {...props} open={open} onOpenChange={setOpen} />;
38
+ }
39
+
40
+ function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
41
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
42
+ }
43
+
44
+ function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
45
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
46
+ }
47
+
48
+ function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
49
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
50
+ }
51
+
52
+ function DialogOverlay({
53
+ className,
54
+ ...props
55
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
56
+ return (
57
+ <DialogPrimitive.Overlay
58
+ data-slot="dialog-overlay"
59
+ className={cn(
60
+ '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',
61
+ className,
62
+ )}
63
+ {...props}
64
+ />
65
+ );
66
+ }
67
+
68
+ function DialogContent({
69
+ className,
70
+ children,
71
+ showCloseButton = true,
72
+ ...props
73
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
74
+ showCloseButton?: boolean;
75
+ }) {
76
+ return (
77
+ <DialogPortal data-slot="dialog-portal">
78
+ <DialogOverlay />
79
+ <DialogPrimitive.Content
80
+ data-slot="dialog-content"
81
+ className={cn(
82
+ '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-2xl border p-6 shadow-lg duration-200 sm:max-w-lg',
83
+ className,
84
+ )}
85
+ {...props}
86
+ >
87
+ {children}
88
+ {showCloseButton && (
89
+ <DialogPrimitive.Close
90
+ data-slot="dialog-close"
91
+ className="ring-offset-background 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"
92
+ >
93
+ <XIcon />
94
+ <span className="sr-only">Close</span>
95
+ </DialogPrimitive.Close>
96
+ )}
97
+ </DialogPrimitive.Content>
98
+ </DialogPortal>
99
+ );
100
+ }
101
+
102
+ function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
103
+ return (
104
+ <div
105
+ data-slot="dialog-header"
106
+ className={cn('flex flex-col gap-2 text-center sm:text-left', className)}
107
+ {...props}
108
+ />
109
+ );
110
+ }
111
+
112
+ function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
113
+ return (
114
+ <div
115
+ data-slot="dialog-footer"
116
+ className={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
117
+ {...props}
118
+ />
119
+ );
120
+ }
121
+
122
+ function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
123
+ return (
124
+ <DialogPrimitive.Title
125
+ data-slot="dialog-title"
126
+ className={cn('text-lg leading-none font-semibold', className)}
127
+ {...props}
128
+ />
129
+ );
130
+ }
131
+
132
+ function DialogDescription({
133
+ className,
134
+ ...props
135
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
136
+ return (
137
+ <DialogPrimitive.Description
138
+ data-slot="dialog-description"
139
+ className={cn('text-muted-foreground text-sm', className)}
140
+ {...props}
141
+ />
142
+ );
143
+ }
144
+
145
+ export {
146
+ Dialog,
147
+ DialogClose,
148
+ DialogContent,
149
+ DialogDescription,
150
+ DialogFooter,
151
+ DialogHeader,
152
+ DialogOverlay,
153
+ DialogPortal,
154
+ DialogTitle,
155
+ DialogTrigger,
156
+ };
@@ -0,0 +1,54 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { Button } from '../Button';
4
+ import {
5
+ Drawer,
6
+ DrawerClose,
7
+ DrawerContent,
8
+ DrawerDescription,
9
+ DrawerFooter,
10
+ DrawerHeader,
11
+ DrawerTitle,
12
+ DrawerTrigger,
13
+ } from './index';
14
+
15
+ const meta: Meta<typeof Drawer> = {
16
+ title: 'UI/Drawer',
17
+ component: Drawer,
18
+ tags: ['autodocs'],
19
+ };
20
+
21
+ export default meta;
22
+ type Story = StoryObj<typeof Drawer>;
23
+
24
+ export const Default: Story = {
25
+ render: (args) => (
26
+ <Drawer {...args}>
27
+ <DrawerTrigger asChild>
28
+ <Button variant="outline">Open Drawer</Button>
29
+ </DrawerTrigger>
30
+ <DrawerContent>
31
+ <div className="mx-auto w-full max-w-sm">
32
+ <DrawerHeader>
33
+ <DrawerTitle>Move Goal</DrawerTitle>
34
+ <DrawerDescription>Set your daily activity goal.</DrawerDescription>
35
+ </DrawerHeader>
36
+ <div className="p-4 pb-0">
37
+ <div className="flex items-center justify-center space-x-2">
38
+ <div className="flex-1 text-center">
39
+ <div className="text-7xl font-bold tracking-tighter">350</div>
40
+ <div className="text-muted-foreground text-[0.70rem] uppercase">Calories/day</div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ <DrawerFooter>
45
+ <Button>Submit</Button>
46
+ <DrawerClose asChild>
47
+ <Button variant="outline">Cancel</Button>
48
+ </DrawerClose>
49
+ </DrawerFooter>
50
+ </div>
51
+ </DrawerContent>
52
+ </Drawer>
53
+ ),
54
+ };
@@ -0,0 +1,124 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { Drawer as DrawerPrimitive } from 'vaul';
5
+
6
+ import { cn } from '@/lib/utils';
7
+
8
+ function Drawer({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
9
+ return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
10
+ }
11
+
12
+ function DrawerTrigger({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
13
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
14
+ }
15
+
16
+ function DrawerPortal({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
17
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
18
+ }
19
+
20
+ function DrawerClose({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
21
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
22
+ }
23
+
24
+ function DrawerOverlay({
25
+ className,
26
+ ...props
27
+ }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
28
+ return (
29
+ <DrawerPrimitive.Overlay
30
+ data-slot="drawer-overlay"
31
+ className={cn(
32
+ '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',
33
+ className,
34
+ )}
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ function DrawerContent({
41
+ className,
42
+ children,
43
+ ...props
44
+ }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
45
+ return (
46
+ <DrawerPortal data-slot="drawer-portal">
47
+ <DrawerOverlay />
48
+ <DrawerPrimitive.Content
49
+ data-slot="drawer-content"
50
+ className={cn(
51
+ 'group/drawer-content bg-background fixed z-50 flex h-auto flex-col',
52
+ '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',
53
+ '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',
54
+ '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',
55
+ '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',
56
+ className,
57
+ )}
58
+ {...props}
59
+ >
60
+ <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" />
61
+ {children}
62
+ </DrawerPrimitive.Content>
63
+ </DrawerPortal>
64
+ );
65
+ }
66
+
67
+ function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
68
+ return (
69
+ <div
70
+ data-slot="drawer-header"
71
+ className={cn(
72
+ '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',
73
+ className,
74
+ )}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) {
81
+ return (
82
+ <div
83
+ data-slot="drawer-footer"
84
+ className={cn('mt-auto flex flex-col gap-2 p-4', className)}
85
+ {...props}
86
+ />
87
+ );
88
+ }
89
+
90
+ function DrawerTitle({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
91
+ return (
92
+ <DrawerPrimitive.Title
93
+ data-slot="drawer-title"
94
+ className={cn('text-foreground font-semibold', className)}
95
+ {...props}
96
+ />
97
+ );
98
+ }
99
+
100
+ function DrawerDescription({
101
+ className,
102
+ ...props
103
+ }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
104
+ return (
105
+ <DrawerPrimitive.Description
106
+ data-slot="drawer-description"
107
+ className={cn('text-muted-foreground text-sm', className)}
108
+ {...props}
109
+ />
110
+ );
111
+ }
112
+
113
+ export {
114
+ Drawer,
115
+ DrawerPortal,
116
+ DrawerOverlay,
117
+ DrawerTrigger,
118
+ DrawerClose,
119
+ DrawerContent,
120
+ DrawerHeader,
121
+ DrawerFooter,
122
+ DrawerTitle,
123
+ DrawerDescription,
124
+ };
@@ -0,0 +1,83 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { Button } from '../Button';
4
+ import {
5
+ DropdownMenu,
6
+ DropdownMenuContent,
7
+ DropdownMenuGroup,
8
+ DropdownMenuItem,
9
+ DropdownMenuLabel,
10
+ DropdownMenuSeparator,
11
+ DropdownMenuShortcut,
12
+ DropdownMenuSub,
13
+ DropdownMenuSubContent,
14
+ DropdownMenuSubTrigger,
15
+ DropdownMenuTrigger,
16
+ } from './index';
17
+
18
+ const meta: Meta<typeof DropdownMenu> = {
19
+ title: 'UI/DropdownMenu',
20
+ component: DropdownMenu,
21
+ tags: ['autodocs'],
22
+ };
23
+
24
+ export default meta;
25
+ type Story = StoryObj<typeof DropdownMenu>;
26
+
27
+ export const Default: Story = {
28
+ render: (args) => (
29
+ <DropdownMenu {...args}>
30
+ <DropdownMenuTrigger asChild>
31
+ <Button variant="outline">Open Menu</Button>
32
+ </DropdownMenuTrigger>
33
+ <DropdownMenuContent className="w-56">
34
+ <DropdownMenuLabel>My Account</DropdownMenuLabel>
35
+ <DropdownMenuSeparator />
36
+ <DropdownMenuGroup>
37
+ <DropdownMenuItem>
38
+ Profile
39
+ <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
40
+ </DropdownMenuItem>
41
+ <DropdownMenuItem>
42
+ Billing
43
+ <DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
44
+ </DropdownMenuItem>
45
+ <DropdownMenuItem>
46
+ Settings
47
+ <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
48
+ </DropdownMenuItem>
49
+ <DropdownMenuItem>
50
+ Keyboard shortcuts
51
+ <DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
52
+ </DropdownMenuItem>
53
+ </DropdownMenuGroup>
54
+ <DropdownMenuSeparator />
55
+ <DropdownMenuGroup>
56
+ <DropdownMenuItem>Team</DropdownMenuItem>
57
+ <DropdownMenuSub>
58
+ <DropdownMenuSubTrigger>Invite users</DropdownMenuSubTrigger>
59
+ <DropdownMenuSubContent>
60
+ <DropdownMenuItem>Email</DropdownMenuItem>
61
+ <DropdownMenuItem>Message</DropdownMenuItem>
62
+ <DropdownMenuSeparator />
63
+ <DropdownMenuItem>More...</DropdownMenuItem>
64
+ </DropdownMenuSubContent>
65
+ </DropdownMenuSub>
66
+ <DropdownMenuItem>
67
+ New Team
68
+ <DropdownMenuShortcut>⌘+T</DropdownMenuShortcut>
69
+ </DropdownMenuItem>
70
+ </DropdownMenuGroup>
71
+ <DropdownMenuSeparator />
72
+ <DropdownMenuItem>GitHub</DropdownMenuItem>
73
+ <DropdownMenuItem>Support</DropdownMenuItem>
74
+ <DropdownMenuItem disabled>API</DropdownMenuItem>
75
+ <DropdownMenuSeparator />
76
+ <DropdownMenuItem variant="destructive">
77
+ Log out
78
+ <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
79
+ </DropdownMenuItem>
80
+ </DropdownMenuContent>
81
+ </DropdownMenu>
82
+ ),
83
+ };
@@ -0,0 +1,231 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
5
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
6
+
7
+ import { cn } from '@/lib/utils';
8
+
9
+ function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
10
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
11
+ }
12
+
13
+ function DropdownMenuPortal({
14
+ ...props
15
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
16
+ return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
17
+ }
18
+
19
+ const DropdownMenuTrigger = React.forwardRef<
20
+ React.ElementRef<typeof DropdownMenuPrimitive.Trigger>,
21
+ React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>
22
+ >(({ ...props }, ref) => {
23
+ return <DropdownMenuPrimitive.Trigger ref={ref} data-slot="dropdown-menu-trigger" {...props} />;
24
+ });
25
+
26
+ DropdownMenuTrigger.displayName = 'DropdownMenuTrigger';
27
+
28
+ function DropdownMenuContent({
29
+ className,
30
+ sideOffset = 4,
31
+ ...props
32
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
33
+ return (
34
+ <DropdownMenuPrimitive.Portal>
35
+ <DropdownMenuPrimitive.Content
36
+ data-slot="dropdown-menu-content"
37
+ sideOffset={sideOffset}
38
+ className={cn(
39
+ 'bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-2xl border border-gray-100 p-1 shadow-sm dark:border-gray-800',
40
+ className,
41
+ )}
42
+ {...props}
43
+ />
44
+ </DropdownMenuPrimitive.Portal>
45
+ );
46
+ }
47
+
48
+ function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
49
+ return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
50
+ }
51
+
52
+ function DropdownMenuItem({
53
+ className,
54
+ inset,
55
+ variant = 'default',
56
+ ...props
57
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
58
+ inset?: boolean;
59
+ variant?: 'default' | 'destructive';
60
+ }) {
61
+ return (
62
+ <DropdownMenuPrimitive.Item
63
+ data-slot="dropdown-menu-item"
64
+ data-inset={inset}
65
+ data-variant={variant}
66
+ className={cn(
67
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-pointer items-center gap-2 rounded-xl px-2 py-1.5 text-base !ring-offset-0 outline-hidden !outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
68
+ className,
69
+ )}
70
+ {...props}
71
+ />
72
+ );
73
+ }
74
+
75
+ function DropdownMenuCheckboxItem({
76
+ className,
77
+ children,
78
+ checked,
79
+ ...props
80
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
81
+ return (
82
+ <DropdownMenuPrimitive.CheckboxItem
83
+ data-slot="dropdown-menu-checkbox-item"
84
+ className={cn(
85
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-pointer items-center gap-2 rounded-xl py-1.5 pr-2 pl-8 text-base !ring-offset-0 outline-hidden !outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
86
+ className,
87
+ )}
88
+ checked={checked}
89
+ {...props}
90
+ >
91
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
92
+ <DropdownMenuPrimitive.ItemIndicator>
93
+ <CheckIcon className="size-4" />
94
+ </DropdownMenuPrimitive.ItemIndicator>
95
+ </span>
96
+ {children}
97
+ </DropdownMenuPrimitive.CheckboxItem>
98
+ );
99
+ }
100
+
101
+ function DropdownMenuRadioGroup({
102
+ ...props
103
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
104
+ return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;
105
+ }
106
+
107
+ function DropdownMenuRadioItem({
108
+ className,
109
+ children,
110
+ ...props
111
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
112
+ return (
113
+ <DropdownMenuPrimitive.RadioItem
114
+ data-slot="dropdown-menu-radio-item"
115
+ className={cn(
116
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-pointer items-center gap-2 rounded-xl py-1.5 pr-2 pl-8 text-base !ring-offset-0 outline-hidden !outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
117
+ className,
118
+ )}
119
+ {...props}
120
+ >
121
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
122
+ <DropdownMenuPrimitive.ItemIndicator>
123
+ <CircleIcon className="size-2 fill-current" />
124
+ </DropdownMenuPrimitive.ItemIndicator>
125
+ </span>
126
+ {children}
127
+ </DropdownMenuPrimitive.RadioItem>
128
+ );
129
+ }
130
+
131
+ function DropdownMenuLabel({
132
+ className,
133
+ inset,
134
+ ...props
135
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
136
+ inset?: boolean;
137
+ }) {
138
+ return (
139
+ <DropdownMenuPrimitive.Label
140
+ data-slot="dropdown-menu-label"
141
+ data-inset={inset}
142
+ className={cn('px-2 py-1.5 text-base font-medium data-[inset]:pl-8', className)}
143
+ {...props}
144
+ />
145
+ );
146
+ }
147
+
148
+ function DropdownMenuSeparator({
149
+ className,
150
+ ...props
151
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
152
+ return (
153
+ <DropdownMenuPrimitive.Separator
154
+ data-slot="dropdown-menu-separator"
155
+ className={cn('-mx-1 my-1 h-px bg-gray-100 dark:bg-gray-800', className)}
156
+ {...props}
157
+ />
158
+ );
159
+ }
160
+
161
+ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
162
+ return (
163
+ <span
164
+ data-slot="dropdown-menu-shortcut"
165
+ className={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)}
166
+ {...props}
167
+ />
168
+ );
169
+ }
170
+
171
+ function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
172
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
173
+ }
174
+
175
+ function DropdownMenuSubTrigger({
176
+ className,
177
+ inset,
178
+ children,
179
+ ...props
180
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
181
+ inset?: boolean;
182
+ }) {
183
+ return (
184
+ <DropdownMenuPrimitive.SubTrigger
185
+ data-slot="dropdown-menu-sub-trigger"
186
+ data-inset={inset}
187
+ className={cn(
188
+ 'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-xl px-2 py-1.5 text-base outline-hidden select-none data-[inset]:pl-8',
189
+ className,
190
+ )}
191
+ {...props}
192
+ >
193
+ {children}
194
+ <ChevronRightIcon className="ml-auto size-4" />
195
+ </DropdownMenuPrimitive.SubTrigger>
196
+ );
197
+ }
198
+
199
+ function DropdownMenuSubContent({
200
+ className,
201
+ ...props
202
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
203
+ return (
204
+ <DropdownMenuPrimitive.SubContent
205
+ data-slot="dropdown-menu-sub-content"
206
+ className={cn(
207
+ 'bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-2xl border border-gray-100 p-1 shadow-lg !ring-0 !ring-offset-0 !outline-none dark:border-gray-800',
208
+ className,
209
+ )}
210
+ {...props}
211
+ />
212
+ );
213
+ }
214
+
215
+ export {
216
+ DropdownMenu,
217
+ DropdownMenuPortal,
218
+ DropdownMenuTrigger,
219
+ DropdownMenuContent,
220
+ DropdownMenuGroup,
221
+ DropdownMenuLabel,
222
+ DropdownMenuItem,
223
+ DropdownMenuCheckboxItem,
224
+ DropdownMenuRadioGroup,
225
+ DropdownMenuRadioItem,
226
+ DropdownMenuSeparator,
227
+ DropdownMenuShortcut,
228
+ DropdownMenuSub,
229
+ DropdownMenuSubTrigger,
230
+ DropdownMenuSubContent,
231
+ };