@wealthx/shadcn 0.0.1 → 1.0.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 (186) hide show
  1. package/.turbo/turbo-build.log +160 -0
  2. package/CHANGELOG.md +13 -0
  3. package/CHANGES.md +345 -0
  4. package/dist/chunk-2WZVSBAY.mjs +232 -0
  5. package/dist/chunk-2Y7YJKPE.mjs +47 -0
  6. package/dist/chunk-3U7SD3MS.mjs +55 -0
  7. package/dist/chunk-3VQNJ235.mjs +114 -0
  8. package/dist/chunk-55CEW76V.mjs +35 -0
  9. package/dist/chunk-6AFMNC42.mjs +146 -0
  10. package/dist/chunk-6OJF6XRN.mjs +117 -0
  11. package/dist/chunk-7LDIMXGM.mjs +181 -0
  12. package/dist/chunk-AMJ23O53.mjs +122 -0
  13. package/dist/chunk-BBJBJSXQ.mjs +44 -0
  14. package/dist/chunk-BGP2N52Z.mjs +126 -0
  15. package/dist/chunk-BMFN37JH.mjs +41 -0
  16. package/dist/chunk-CGOKTPXU.mjs +79 -0
  17. package/dist/chunk-CZ3BW5GL.mjs +81 -0
  18. package/dist/chunk-DBHJ5KC3.mjs +55 -0
  19. package/dist/chunk-DDPA2XXS.mjs +97 -0
  20. package/dist/chunk-DS2AMHN2.mjs +30 -0
  21. package/dist/chunk-E3K6O4FZ.mjs +57 -0
  22. package/dist/chunk-FWCSY2DS.mjs +37 -0
  23. package/dist/chunk-GPRJQ24C.mjs +28 -0
  24. package/dist/chunk-HS7TFG7V.mjs +24 -0
  25. package/dist/chunk-HUVTPUV2.mjs +256 -0
  26. package/dist/chunk-IAOOZCUY.mjs +90 -0
  27. package/dist/chunk-JF4PHPD5.mjs +111 -0
  28. package/dist/chunk-JU2RUWHF.mjs +123 -0
  29. package/dist/chunk-KKHTJNMM.mjs +86 -0
  30. package/dist/chunk-MJIEMGRD.mjs +266 -0
  31. package/dist/chunk-MKFL5MNH.mjs +372 -0
  32. package/dist/chunk-MQ72DIBH.mjs +105 -0
  33. package/dist/chunk-NGYG2EA6.mjs +148 -0
  34. package/dist/chunk-NWZ46DJL.mjs +213 -0
  35. package/dist/chunk-OXQQNQZI.mjs +75 -0
  36. package/dist/chunk-PMKODV6M.mjs +161 -0
  37. package/dist/chunk-QOJ2DQD6.mjs +57 -0
  38. package/dist/chunk-RL772EH7.mjs +126 -0
  39. package/dist/chunk-SLWCCURD.mjs +99 -0
  40. package/dist/chunk-V7CNWJT3.mjs +10 -0
  41. package/dist/chunk-VG6UF6UT.mjs +68 -0
  42. package/dist/chunk-VYMHBV6D.mjs +123 -0
  43. package/dist/chunk-VZ2NR7L3.mjs +195 -0
  44. package/dist/chunk-YN5SYTOO.mjs +117 -0
  45. package/dist/chunk-Z3MK2KKZ.mjs +83 -0
  46. package/dist/chunk-ZN2QKLF6.mjs +187 -0
  47. package/dist/chunk-ZZV5JVNW.mjs +34 -0
  48. package/dist/components/ui/accordion.js +142 -0
  49. package/dist/components/ui/accordion.mjs +14 -0
  50. package/dist/components/ui/alert-dialog.js +413 -0
  51. package/dist/components/ui/alert-dialog.mjs +34 -0
  52. package/dist/components/ui/alert.js +134 -0
  53. package/dist/components/ui/alert.mjs +12 -0
  54. package/dist/components/ui/avatar.js +173 -0
  55. package/dist/components/ui/avatar.mjs +18 -0
  56. package/dist/components/ui/badge.js +163 -0
  57. package/dist/components/ui/badge.mjs +11 -0
  58. package/dist/components/ui/button.js +198 -0
  59. package/dist/components/ui/button.mjs +11 -0
  60. package/dist/components/ui/calendar.js +408 -0
  61. package/dist/components/ui/calendar.mjs +12 -0
  62. package/dist/components/ui/card.js +156 -0
  63. package/dist/components/ui/card.mjs +20 -0
  64. package/dist/components/ui/checkbox.js +166 -0
  65. package/dist/components/ui/checkbox.mjs +11 -0
  66. package/dist/components/ui/chip.js +199 -0
  67. package/dist/components/ui/chip.mjs +10 -0
  68. package/dist/components/ui/data-table.js +925 -0
  69. package/dist/components/ui/data-table.mjs +29 -0
  70. package/dist/components/ui/date-picker.js +561 -0
  71. package/dist/components/ui/date-picker.mjs +15 -0
  72. package/dist/components/ui/dialog.js +378 -0
  73. package/dist/components/ui/dialog.mjs +30 -0
  74. package/dist/components/ui/drawer.js +213 -0
  75. package/dist/components/ui/drawer.mjs +28 -0
  76. package/dist/components/ui/dropdown-menu.js +338 -0
  77. package/dist/components/ui/dropdown-menu.mjs +38 -0
  78. package/dist/components/ui/empty.js +173 -0
  79. package/dist/components/ui/empty.mjs +18 -0
  80. package/dist/components/ui/field.js +359 -0
  81. package/dist/components/ui/field.mjs +28 -0
  82. package/dist/components/ui/input-group.js +406 -0
  83. package/dist/components/ui/input-group.mjs +22 -0
  84. package/dist/components/ui/input-otp.js +149 -0
  85. package/dist/components/ui/input-otp.mjs +14 -0
  86. package/dist/components/ui/input.js +81 -0
  87. package/dist/components/ui/input.mjs +8 -0
  88. package/dist/components/ui/label.js +85 -0
  89. package/dist/components/ui/label.mjs +8 -0
  90. package/dist/components/ui/pagination.js +333 -0
  91. package/dist/components/ui/pagination.mjs +22 -0
  92. package/dist/components/ui/popover.js +167 -0
  93. package/dist/components/ui/popover.mjs +22 -0
  94. package/dist/components/ui/progress.js +97 -0
  95. package/dist/components/ui/progress.mjs +8 -0
  96. package/dist/components/ui/radio-group.js +178 -0
  97. package/dist/components/ui/radio-group.mjs +12 -0
  98. package/dist/components/ui/select.js +262 -0
  99. package/dist/components/ui/select.mjs +28 -0
  100. package/dist/components/ui/separator.js +86 -0
  101. package/dist/components/ui/separator.mjs +8 -0
  102. package/dist/components/ui/sheet.js +227 -0
  103. package/dist/components/ui/sheet.mjs +26 -0
  104. package/dist/components/ui/skeleton.js +75 -0
  105. package/dist/components/ui/skeleton.mjs +8 -0
  106. package/dist/components/ui/sonner.js +86 -0
  107. package/dist/components/ui/sonner.mjs +7 -0
  108. package/dist/components/ui/spinner.js +93 -0
  109. package/dist/components/ui/spinner.mjs +10 -0
  110. package/dist/components/ui/switch.js +178 -0
  111. package/dist/components/ui/switch.mjs +11 -0
  112. package/dist/components/ui/table.js +184 -0
  113. package/dist/components/ui/table.mjs +22 -0
  114. package/dist/components/ui/tabs.js +181 -0
  115. package/dist/components/ui/tabs.mjs +16 -0
  116. package/dist/components/ui/textarea.js +79 -0
  117. package/dist/components/ui/textarea.mjs +8 -0
  118. package/dist/components/ui/toggle-group.js +184 -0
  119. package/dist/components/ui/toggle-group.mjs +12 -0
  120. package/dist/components/ui/toggle.js +108 -0
  121. package/dist/components/ui/toggle.mjs +11 -0
  122. package/dist/components/ui/tooltip.js +140 -0
  123. package/dist/components/ui/tooltip.mjs +16 -0
  124. package/dist/index.js +4409 -0
  125. package/dist/index.mjs +462 -0
  126. package/dist/lib/colors.js +84 -0
  127. package/dist/lib/colors.mjs +13 -0
  128. package/dist/lib/theme-provider.js +150 -0
  129. package/dist/lib/theme-provider.mjs +13 -0
  130. package/dist/lib/typography.js +157 -0
  131. package/dist/lib/typography.mjs +25 -0
  132. package/dist/lib/utils.js +34 -0
  133. package/dist/lib/utils.mjs +7 -0
  134. package/dist/styles.css +2 -0
  135. package/package.json +228 -11
  136. package/scripts/build-css.ts +15 -9
  137. package/src/components/index.tsx +443 -0
  138. package/src/components/ui/accordion.tsx +99 -0
  139. package/src/components/ui/alert-dialog.tsx +239 -0
  140. package/src/components/ui/alert.tsx +81 -0
  141. package/src/components/ui/avatar.tsx +130 -0
  142. package/src/components/ui/badge.tsx +57 -0
  143. package/src/components/ui/button.tsx +69 -37
  144. package/src/components/ui/calendar.tsx +252 -0
  145. package/src/components/ui/card.tsx +106 -0
  146. package/src/components/ui/checkbox.tsx +111 -0
  147. package/src/components/ui/chip.tsx +65 -0
  148. package/src/components/ui/data-table.tsx +490 -0
  149. package/src/components/ui/date-picker.tsx +133 -0
  150. package/src/components/ui/dialog.tsx +195 -0
  151. package/src/components/ui/drawer.tsx +169 -0
  152. package/src/components/ui/dropdown-menu.tsx +315 -0
  153. package/src/components/ui/empty.tsx +128 -0
  154. package/src/components/ui/field.tsx +273 -0
  155. package/src/components/ui/input-group.tsx +190 -0
  156. package/src/components/ui/input-otp.tsx +90 -0
  157. package/src/components/ui/input.tsx +28 -0
  158. package/src/components/ui/label.tsx +24 -0
  159. package/src/components/ui/pagination.tsx +148 -0
  160. package/src/components/ui/popover.tsx +112 -0
  161. package/src/components/ui/progress.tsx +40 -0
  162. package/src/components/ui/radio-group.tsx +129 -0
  163. package/src/components/ui/select.tsx +201 -0
  164. package/src/components/ui/separator.tsx +26 -0
  165. package/src/components/ui/sheet.tsx +182 -0
  166. package/src/components/ui/skeleton.tsx +22 -0
  167. package/src/components/ui/sonner.tsx +48 -0
  168. package/src/components/ui/spinner.tsx +41 -0
  169. package/src/components/ui/switch.tsx +126 -0
  170. package/src/components/ui/table.tsx +143 -0
  171. package/src/components/ui/tabs.tsx +119 -0
  172. package/src/components/ui/textarea.tsx +28 -0
  173. package/src/components/ui/toggle-group.tsx +94 -0
  174. package/src/components/ui/toggle.tsx +59 -0
  175. package/src/components/ui/tooltip.tsx +80 -0
  176. package/src/index.ts +15 -3
  177. package/src/lib/colors.ts +74 -0
  178. package/src/lib/slot.tsx +68 -0
  179. package/src/lib/theme-provider.tsx +134 -0
  180. package/src/lib/typography.ts +153 -0
  181. package/src/lib/utils.ts +1 -1
  182. package/src/styles/globals.css +377 -107
  183. package/src/styles/styles-css.ts +1 -1
  184. package/tsup.config.ts +48 -2
  185. package/src/provider/ShadcnProvider.tsx +0 -89
  186. package/src/provider/index.ts +0 -2
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Dialog — WealthX DS overrides (shadcn / \@base-ui dialog)
3
+ *
4
+ * Changes from shadcn default:
5
+ * - DialogOverlay: `bg-black/50` → `bg-foreground/50` — DS foreground token (matches Figma)
6
+ * - DialogContent: `rounded-lg` removed — sharp corners per Figma
7
+ * - DialogContent: `shadow-lg` removed — flat panels per Figma
8
+ * - DialogHeader: `text-center` removed — always left-aligned per DS
9
+ * - DialogFooter: `border-t border-border pt-4` — separator above footer
10
+ * - Close button: no radius, `hover:bg-foreground/5`, `focus:ring-border`
11
+ */
12
+ import { type ReactElement } from "react"
13
+ import * as React from "react"
14
+ import { XIcon } from "lucide-react"
15
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
16
+ import { cn } from "@/lib/utils"
17
+ import { useThemeVars } from "@/lib/theme-provider"
18
+ import { buttonVariants } from "@/components/ui/button"
19
+
20
+ export type DialogProps = React.ComponentProps<typeof DialogPrimitive.Root>
21
+
22
+ function Dialog({
23
+ ...props
24
+ }: DialogProps): ReactElement {
25
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
26
+ }
27
+
28
+ export type DialogTriggerProps = React.ComponentProps<typeof DialogPrimitive.Trigger>
29
+
30
+ function DialogTrigger({
31
+ ...props
32
+ }: DialogTriggerProps): ReactElement {
33
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
34
+ }
35
+
36
+ export type DialogPortalProps = React.ComponentProps<typeof DialogPrimitive.Portal>
37
+
38
+ function DialogPortal({
39
+ ...props
40
+ }: DialogPortalProps): ReactElement {
41
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
42
+ }
43
+
44
+ export type DialogCloseProps = React.ComponentProps<typeof DialogPrimitive.Close>
45
+
46
+ function DialogClose({
47
+ ...props
48
+ }: DialogCloseProps): ReactElement {
49
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
50
+ }
51
+
52
+ export type DialogOverlayProps = React.ComponentProps<typeof DialogPrimitive.Backdrop>
53
+
54
+ function DialogOverlay({
55
+ className,
56
+ ...props
57
+ }: DialogOverlayProps): ReactElement {
58
+ return (
59
+ <DialogPrimitive.Backdrop
60
+ className={cn(
61
+ // WealthX: foreground/50 scrim (matches Figma — foreground token at 50% opacity)
62
+ "fixed inset-0 z-50 bg-foreground/50 data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:fill-mode-forwards data-open:animate-in data-open:fade-in-0",
63
+ className
64
+ )}
65
+ data-slot="dialog-overlay"
66
+ {...props}
67
+ />
68
+ )
69
+ }
70
+
71
+ export type DialogContentProps = React.ComponentProps<typeof DialogPrimitive.Popup> & {
72
+ showCloseButton?: boolean
73
+ }
74
+
75
+ function DialogContent({
76
+ className,
77
+ children,
78
+ showCloseButton = true,
79
+ style,
80
+ ...props
81
+ }: DialogContentProps): ReactElement {
82
+ const themeVars = useThemeVars();
83
+ return (
84
+ <DialogPortal>
85
+ <DialogOverlay />
86
+ <DialogPrimitive.Popup
87
+ className={cn(
88
+ // WealthX: removed rounded-lg (sharp corners), shadow-lg (flat panels), foreground/50 scrim via DialogOverlay
89
+ "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 duration-200 outline-none data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95 data-ending-style:fill-mode-forwards data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 sm:max-w-lg",
90
+ className
91
+ )}
92
+ data-slot="dialog-content"
93
+ style={{ ...themeVars, ...style } as React.CSSProperties}
94
+ {...props}
95
+ >
96
+ {children}
97
+ {showCloseButton ? <DialogPrimitive.Close
98
+ className="absolute top-4 right-4 transition-colors hover:bg-foreground/5 focus:outline-hidden focus:ring-2 focus:ring-border focus:ring-offset-0 disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
99
+ // WealthX: removed rounded-xs (sharp), replaced opacity fade with hover:bg-foreground/5,
100
+ // focus ring uses border color (subtle, matches Figma DialogClose focus state)
101
+ data-slot="dialog-close"
102
+ >
103
+ <XIcon />
104
+ <span className="sr-only">Close</span>
105
+ </DialogPrimitive.Close> : null}
106
+ </DialogPrimitive.Popup>
107
+ </DialogPortal>
108
+ )
109
+ }
110
+
111
+ export type DialogHeaderProps = React.ComponentProps<"div">
112
+
113
+ function DialogHeader({ className, ...props }: DialogHeaderProps): ReactElement {
114
+ return (
115
+ <div
116
+ // WealthX: always left-aligned (no sm:text-left — no centered variant)
117
+ className={cn("flex flex-col gap-1.5", className)}
118
+ data-slot="dialog-header"
119
+ {...props}
120
+ />
121
+ )
122
+ }
123
+
124
+ export type DialogFooterProps = React.ComponentProps<"div"> & {
125
+ showCloseButton?: boolean
126
+ }
127
+
128
+ function DialogFooter({
129
+ className,
130
+ showCloseButton = false,
131
+ children,
132
+ ...props
133
+ }: DialogFooterProps): ReactElement {
134
+ return (
135
+ <div
136
+ className={cn(
137
+ // WealthX: always row layout, right-aligned, separator above footer
138
+ "flex flex-row justify-end gap-2 border-t border-border pt-4",
139
+ className
140
+ )}
141
+ data-slot="dialog-footer"
142
+ {...props}
143
+ >
144
+ {children}
145
+ {showCloseButton ? <DialogPrimitive.Close
146
+ className={cn(buttonVariants({ variant: "outline" }))}
147
+ >
148
+ Close
149
+ </DialogPrimitive.Close> : null}
150
+ </div>
151
+ )
152
+ }
153
+
154
+ export type DialogTitleProps = React.ComponentProps<typeof DialogPrimitive.Title>
155
+
156
+ function DialogTitle({
157
+ className,
158
+ ...props
159
+ }: DialogTitleProps): ReactElement {
160
+ return (
161
+ <DialogPrimitive.Title
162
+ className={cn("text-lg leading-none font-semibold", className)}
163
+ data-slot="dialog-title"
164
+ {...props}
165
+ />
166
+ )
167
+ }
168
+
169
+ export type DialogDescriptionProps = React.ComponentProps<typeof DialogPrimitive.Description>
170
+
171
+ function DialogDescription({
172
+ className,
173
+ ...props
174
+ }: DialogDescriptionProps): ReactElement {
175
+ return (
176
+ <DialogPrimitive.Description
177
+ className={cn("text-sm text-muted-foreground", className)}
178
+ data-slot="dialog-description"
179
+ {...props}
180
+ />
181
+ )
182
+ }
183
+
184
+ export {
185
+ Dialog,
186
+ DialogClose,
187
+ DialogContent,
188
+ DialogDescription,
189
+ DialogFooter,
190
+ DialogHeader,
191
+ DialogOverlay,
192
+ DialogPortal,
193
+ DialogTitle,
194
+ DialogTrigger,
195
+ }
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Drawer — WealthX DS overrides (vaul/shadcn base)
3
+ *
4
+ * Changes from shadcn default:
5
+ * - DrawerOverlay: `bg-black/50` → `bg-foreground/50` — uses DS foreground token (matches Figma overlay)
6
+ * - DrawerContent: removed `rounded-b-lg` / `rounded-t-lg` — sharp corners per Figma (no radius)
7
+ * - DrawerContent: handle bar also shown for `direction=top` (not just bottom)
8
+ * - DrawerHeader: removed `text-center` for top/bottom — always left-aligned per DS rule
9
+ * - DrawerTitle: added `text-lg` — matches Figma Text-lg/Semibold
10
+ */
11
+ import { type ReactElement } from "react"
12
+ import * as React from "react"
13
+ import { Drawer as DrawerPrimitive } from "vaul"
14
+ import { cn } from "@/lib/utils"
15
+ import { useThemeVars } from "@/lib/theme-provider"
16
+
17
+ export type DrawerProps = React.ComponentProps<typeof DrawerPrimitive.Root>
18
+
19
+ function Drawer({
20
+ ...props
21
+ }: DrawerProps): ReactElement {
22
+ return <DrawerPrimitive.Root data-slot="drawer" {...props} />
23
+ }
24
+
25
+ export type DrawerTriggerProps = React.ComponentProps<typeof DrawerPrimitive.Trigger>
26
+
27
+ function DrawerTrigger({
28
+ ...props
29
+ }: DrawerTriggerProps): ReactElement {
30
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />
31
+ }
32
+
33
+ export type DrawerPortalProps = React.ComponentProps<typeof DrawerPrimitive.Portal>
34
+
35
+ function DrawerPortal({
36
+ ...props
37
+ }: DrawerPortalProps): ReactElement {
38
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />
39
+ }
40
+
41
+ export type DrawerCloseProps = React.ComponentProps<typeof DrawerPrimitive.Close>
42
+
43
+ function DrawerClose({
44
+ ...props
45
+ }: DrawerCloseProps): ReactElement {
46
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />
47
+ }
48
+
49
+ export type DrawerOverlayProps = React.ComponentProps<typeof DrawerPrimitive.Overlay>
50
+
51
+ function DrawerOverlay({
52
+ className,
53
+ ...props
54
+ }: DrawerOverlayProps): ReactElement {
55
+ return (
56
+ <DrawerPrimitive.Overlay
57
+ className={cn(
58
+ // WealthX: foreground/50 scrim — matches Figma overlay token
59
+ "fixed inset-0 z-50 bg-foreground/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:fill-mode-forwards data-[state=open]:animate-in data-[state=open]:fade-in-0",
60
+ className
61
+ )}
62
+ data-slot="drawer-overlay"
63
+ {...props}
64
+ />
65
+ )
66
+ }
67
+
68
+ export type DrawerContentProps = React.ComponentProps<typeof DrawerPrimitive.Content>
69
+
70
+ function DrawerContent({
71
+ className,
72
+ children,
73
+ style,
74
+ ...props
75
+ }: DrawerContentProps): ReactElement {
76
+ const themeVars = useThemeVars();
77
+ return (
78
+ <DrawerPortal data-slot="drawer-portal">
79
+ <DrawerOverlay />
80
+ <DrawerPrimitive.Content
81
+ className={cn(
82
+ "group/drawer-content fixed z-50 flex h-auto flex-col bg-background",
83
+ // WealthX: no rounded corners (Figma: sharp edges)
84
+ "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]:border-b",
85
+ "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]:border-t",
86
+ "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",
87
+ "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",
88
+ className
89
+ )}
90
+ data-slot="drawer-content"
91
+ style={{ ...themeVars, ...style } as React.CSSProperties}
92
+ {...props}
93
+ >
94
+ {/* WealthX: handle shown for both top and bottom directions */}
95
+ <div className="mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block group-data-[vaul-drawer-direction=top]/drawer-content:block" />
96
+ {children}
97
+ </DrawerPrimitive.Content>
98
+ </DrawerPortal>
99
+ )
100
+ }
101
+
102
+ export type DrawerHeaderProps = React.ComponentProps<"div">
103
+
104
+ function DrawerHeader({ className, ...props }: DrawerHeaderProps): ReactElement {
105
+ return (
106
+ <div
107
+ // WealthX: always left-aligned — no text-center for any direction
108
+ className={cn("flex flex-col gap-1 p-4", className)}
109
+ data-slot="drawer-header"
110
+ {...props}
111
+ />
112
+ )
113
+ }
114
+
115
+ export type DrawerFooterProps = React.ComponentProps<"div">
116
+
117
+ function DrawerFooter({ className, ...props }: DrawerFooterProps): ReactElement {
118
+ return (
119
+ <div
120
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
121
+ data-slot="drawer-footer"
122
+ {...props}
123
+ />
124
+ )
125
+ }
126
+
127
+ export type DrawerTitleProps = React.ComponentProps<typeof DrawerPrimitive.Title>
128
+
129
+ function DrawerTitle({
130
+ className,
131
+ ...props
132
+ }: DrawerTitleProps): ReactElement {
133
+ return (
134
+ <DrawerPrimitive.Title
135
+ // WealthX: text-lg matches Figma Text-lg/Semibold
136
+ className={cn("text-lg font-semibold text-foreground", className)}
137
+ data-slot="drawer-title"
138
+ {...props}
139
+ />
140
+ )
141
+ }
142
+
143
+ export type DrawerDescriptionProps = React.ComponentProps<typeof DrawerPrimitive.Description>
144
+
145
+ function DrawerDescription({
146
+ className,
147
+ ...props
148
+ }: DrawerDescriptionProps): ReactElement {
149
+ return (
150
+ <DrawerPrimitive.Description
151
+ className={cn("text-sm text-muted-foreground", className)}
152
+ data-slot="drawer-description"
153
+ {...props}
154
+ />
155
+ )
156
+ }
157
+
158
+ export {
159
+ Drawer,
160
+ DrawerPortal,
161
+ DrawerOverlay,
162
+ DrawerTrigger,
163
+ DrawerClose,
164
+ DrawerContent,
165
+ DrawerHeader,
166
+ DrawerFooter,
167
+ DrawerTitle,
168
+ DrawerDescription,
169
+ }
@@ -0,0 +1,315 @@
1
+ /**
2
+ * DropdownMenu — shadcn/WealthX
3
+ * Base: npx shadcn\@latest add dropdown-menu
4
+ * Figma: https://www.figma.com/design/9V9F0NGVsif8LGmEhVjOcT/Design-System---shadcn?node-id=73-229
5
+ *
6
+ * WealthX overrides:
7
+ * - DropdownMenuContent: rounded-md → rounded-none (sharp corners)
8
+ * - DropdownMenuItem: rounded-sm → rounded-none
9
+ * - DropdownMenuSubTrigger: rounded-sm → rounded-none
10
+ * - DropdownMenuCheckboxItem: rounded-sm → rounded-none; added data-checked:bg-primary/10
11
+ * - DropdownMenuRadioItem: rounded-sm → rounded-none; added data-checked:bg-primary/10
12
+ * - DropdownMenuSubContent: rounded-md → rounded-none
13
+ */
14
+ import { type ReactElement } from "react"
15
+ import * as React from "react"
16
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
17
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu"
18
+ import { cn } from "@/lib/utils"
19
+ import { useThemeVars } from "@/lib/theme-provider"
20
+
21
+ export type DropdownMenuProps = React.ComponentProps<typeof MenuPrimitive.Root>
22
+
23
+ function DropdownMenu({
24
+ ...props
25
+ }: DropdownMenuProps): ReactElement {
26
+ return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
27
+ }
28
+
29
+ export type DropdownMenuPortalProps = React.ComponentProps<typeof MenuPrimitive.Portal>
30
+
31
+ function DropdownMenuPortal({
32
+ ...props
33
+ }: DropdownMenuPortalProps): ReactElement {
34
+ return (
35
+ <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
36
+ )
37
+ }
38
+
39
+ export type DropdownMenuTriggerProps = React.ComponentProps<typeof MenuPrimitive.Trigger>
40
+
41
+ function DropdownMenuTrigger({
42
+ ...props
43
+ }: DropdownMenuTriggerProps): ReactElement {
44
+ return (
45
+ <MenuPrimitive.Trigger
46
+ data-slot="dropdown-menu-trigger"
47
+ {...props}
48
+ />
49
+ )
50
+ }
51
+
52
+ export type DropdownMenuContentProps = React.ComponentProps<typeof MenuPrimitive.Popup> & {
53
+ sideOffset?: number
54
+ }
55
+
56
+ function DropdownMenuContent({
57
+ className,
58
+ sideOffset = 4,
59
+ style,
60
+ ...props
61
+ }: DropdownMenuContentProps): ReactElement {
62
+ const themeVars = useThemeVars();
63
+ return (
64
+ <MenuPrimitive.Portal>
65
+ <MenuPrimitive.Positioner sideOffset={sideOffset}>
66
+ <MenuPrimitive.Popup
67
+ className={cn(
68
+ "z-50 min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-none border bg-popover p-1 text-popover-foreground shadow-md 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 data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95 data-ending-style:fill-mode-forwards data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95",
69
+ className
70
+ )}
71
+ data-slot="dropdown-menu-content"
72
+ style={{ ...themeVars, ...style } as React.CSSProperties}
73
+ {...props}
74
+ />
75
+ </MenuPrimitive.Positioner>
76
+ </MenuPrimitive.Portal>
77
+ )
78
+ }
79
+
80
+ export type DropdownMenuGroupProps = React.ComponentProps<typeof MenuPrimitive.Group>
81
+
82
+ function DropdownMenuGroup({
83
+ ...props
84
+ }: DropdownMenuGroupProps): ReactElement {
85
+ return (
86
+ <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
87
+ )
88
+ }
89
+
90
+ export type DropdownMenuItemProps = React.ComponentProps<typeof MenuPrimitive.Item> & {
91
+ inset?: boolean
92
+ variant?: "default" | "destructive"
93
+ }
94
+
95
+ function DropdownMenuItem({
96
+ className,
97
+ inset,
98
+ variant = "default",
99
+ ...props
100
+ }: DropdownMenuItemProps): ReactElement {
101
+ return (
102
+ <MenuPrimitive.Item
103
+ className={cn(
104
+ "relative flex cursor-default items-center gap-2 rounded-none px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",
105
+ className
106
+ )}
107
+ data-inset={inset}
108
+ data-slot="dropdown-menu-item"
109
+ data-variant={variant}
110
+ {...props}
111
+ />
112
+ )
113
+ }
114
+
115
+ export type DropdownMenuCheckboxItemProps = React.ComponentProps<typeof MenuPrimitive.CheckboxItem>
116
+
117
+ function DropdownMenuCheckboxItem({
118
+ className,
119
+ children,
120
+ checked,
121
+ ...props
122
+ }: DropdownMenuCheckboxItemProps): ReactElement {
123
+ return (
124
+ <MenuPrimitive.CheckboxItem
125
+ checked={checked}
126
+ className={cn(
127
+ "relative flex cursor-default items-center gap-2 rounded-none py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 data-checked:bg-primary/10 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
128
+ className
129
+ )}
130
+ data-slot="dropdown-menu-checkbox-item"
131
+ {...props}
132
+ >
133
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
134
+ <MenuPrimitive.CheckboxItemIndicator>
135
+ <CheckIcon className="size-4" />
136
+ </MenuPrimitive.CheckboxItemIndicator>
137
+ </span>
138
+ {children}
139
+ </MenuPrimitive.CheckboxItem>
140
+ )
141
+ }
142
+
143
+ export type DropdownMenuRadioGroupProps = React.ComponentProps<typeof MenuPrimitive.RadioGroup>
144
+
145
+ function DropdownMenuRadioGroup({
146
+ ...props
147
+ }: DropdownMenuRadioGroupProps): ReactElement {
148
+ return (
149
+ <MenuPrimitive.RadioGroup
150
+ data-slot="dropdown-menu-radio-group"
151
+ {...props}
152
+ />
153
+ )
154
+ }
155
+
156
+ export type DropdownMenuRadioItemProps = React.ComponentProps<typeof MenuPrimitive.RadioItem>
157
+
158
+ function DropdownMenuRadioItem({
159
+ className,
160
+ children,
161
+ ...props
162
+ }: DropdownMenuRadioItemProps): ReactElement {
163
+ return (
164
+ <MenuPrimitive.RadioItem
165
+ className={cn(
166
+ "relative flex cursor-default items-center gap-2 rounded-none py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 data-checked:bg-primary/10 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
167
+ className
168
+ )}
169
+ data-slot="dropdown-menu-radio-item"
170
+ {...props}
171
+ >
172
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
173
+ <MenuPrimitive.RadioItemIndicator>
174
+ <CircleIcon className="size-2 fill-current" />
175
+ </MenuPrimitive.RadioItemIndicator>
176
+ </span>
177
+ {children}
178
+ </MenuPrimitive.RadioItem>
179
+ )
180
+ }
181
+
182
+ export type DropdownMenuLabelProps = React.ComponentProps<typeof MenuPrimitive.GroupLabel> & {
183
+ inset?: boolean
184
+ }
185
+
186
+ function DropdownMenuLabel({
187
+ className,
188
+ inset,
189
+ ...props
190
+ }: DropdownMenuLabelProps): ReactElement {
191
+ return (
192
+ <MenuPrimitive.GroupLabel
193
+ className={cn(
194
+ "px-2 py-1.5 text-sm font-medium data-inset:pl-8",
195
+ className
196
+ )}
197
+ data-inset={inset}
198
+ data-slot="dropdown-menu-label"
199
+ {...props}
200
+ />
201
+ )
202
+ }
203
+
204
+ export type DropdownMenuSeparatorProps = React.ComponentProps<typeof MenuPrimitive.Separator>
205
+
206
+ function DropdownMenuSeparator({
207
+ className,
208
+ ...props
209
+ }: DropdownMenuSeparatorProps): ReactElement {
210
+ return (
211
+ <MenuPrimitive.Separator
212
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
213
+ data-slot="dropdown-menu-separator"
214
+ {...props}
215
+ />
216
+ )
217
+ }
218
+
219
+ export type DropdownMenuShortcutProps = React.ComponentProps<"span">
220
+
221
+ function DropdownMenuShortcut({
222
+ className,
223
+ ...props
224
+ }: DropdownMenuShortcutProps): ReactElement {
225
+ return (
226
+ <span
227
+ className={cn(
228
+ "ml-auto text-xs tracking-widest text-muted-foreground",
229
+ className
230
+ )}
231
+ data-slot="dropdown-menu-shortcut"
232
+ {...props}
233
+ />
234
+ )
235
+ }
236
+
237
+ export type DropdownMenuSubProps = React.ComponentProps<typeof MenuPrimitive.SubmenuRoot>
238
+
239
+ function DropdownMenuSub({
240
+ ...props
241
+ }: DropdownMenuSubProps): ReactElement {
242
+ return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
243
+ }
244
+
245
+ export type DropdownMenuSubTriggerProps = React.ComponentProps<typeof MenuPrimitive.SubmenuTrigger> & {
246
+ inset?: boolean
247
+ }
248
+
249
+ function DropdownMenuSubTrigger({
250
+ className,
251
+ inset,
252
+ children,
253
+ ...props
254
+ }: DropdownMenuSubTriggerProps): ReactElement {
255
+ return (
256
+ <MenuPrimitive.SubmenuTrigger
257
+ className={cn(
258
+ "flex cursor-default items-center gap-2 rounded-none px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-8 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
259
+ className
260
+ )}
261
+ data-inset={inset}
262
+ data-slot="dropdown-menu-sub-trigger"
263
+ {...props}
264
+ >
265
+ {children}
266
+ <ChevronRightIcon className="ml-auto size-4" />
267
+ </MenuPrimitive.SubmenuTrigger>
268
+ )
269
+ }
270
+
271
+ export type DropdownMenuSubContentProps = React.ComponentProps<typeof MenuPrimitive.Popup> & {
272
+ sideOffset?: number
273
+ }
274
+
275
+ function DropdownMenuSubContent({
276
+ className,
277
+ sideOffset = 0,
278
+ style,
279
+ ...props
280
+ }: DropdownMenuSubContentProps): ReactElement {
281
+ const themeVars = useThemeVars();
282
+ return (
283
+ <MenuPrimitive.Portal>
284
+ <MenuPrimitive.Positioner sideOffset={sideOffset}>
285
+ <MenuPrimitive.Popup
286
+ className={cn(
287
+ "z-50 min-w-[8rem] overflow-hidden rounded-none border bg-popover p-1 text-popover-foreground shadow-lg 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 data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95 data-ending-style:fill-mode-forwards data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95",
288
+ className
289
+ )}
290
+ data-slot="dropdown-menu-sub-content"
291
+ style={{ ...themeVars, ...style } as React.CSSProperties}
292
+ {...props}
293
+ />
294
+ </MenuPrimitive.Positioner>
295
+ </MenuPrimitive.Portal>
296
+ )
297
+ }
298
+
299
+ export {
300
+ DropdownMenu,
301
+ DropdownMenuPortal,
302
+ DropdownMenuTrigger,
303
+ DropdownMenuContent,
304
+ DropdownMenuGroup,
305
+ DropdownMenuLabel,
306
+ DropdownMenuItem,
307
+ DropdownMenuCheckboxItem,
308
+ DropdownMenuRadioGroup,
309
+ DropdownMenuRadioItem,
310
+ DropdownMenuSeparator,
311
+ DropdownMenuShortcut,
312
+ DropdownMenuSub,
313
+ DropdownMenuSubTrigger,
314
+ DropdownMenuSubContent,
315
+ }