@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,182 @@
1
+ /**
2
+ * Sheet — WealthX DS overrides (shadcn / base-ui dialog)
3
+ *
4
+ * Changes from shadcn default:
5
+ * - SheetOverlay: `bg-black/50` -- `bg-foreground/50` — DS foreground token (matches Figma)
6
+ * - SheetContent: `shadow-lg` removed — flat panels per Figma
7
+ * - SheetTitle: added `text-lg` — matches Figma Text-lg/Semibold
8
+ * - Close button: matches DialogClose — no radius, `hover:bg-foreground/5`, `focus:ring-border`
9
+ */
10
+ import { type ReactElement } from "react"
11
+ import * as React from "react"
12
+ import { XIcon } from "lucide-react"
13
+ import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"
14
+ import { cn } from "@/lib/utils"
15
+ import { useThemeVars } from "@/lib/theme-provider"
16
+
17
+ // ---------------------------------------------------------------------------
18
+ // Side-specific panel classes
19
+ // ---------------------------------------------------------------------------
20
+
21
+ const SIDE_CLASSES = {
22
+ right: "inset-y-0 right-0 h-full w-3/4 border-l data-ending-style:slide-out-to-right data-open:slide-in-from-right sm:max-w-sm",
23
+ left: "inset-y-0 left-0 h-full w-3/4 border-r data-ending-style:slide-out-to-left data-open:slide-in-from-left sm:max-w-sm",
24
+ top: "inset-x-0 top-0 h-auto border-b data-ending-style:slide-out-to-top data-open:slide-in-from-top",
25
+ bottom: "inset-x-0 bottom-0 h-auto border-t data-ending-style:slide-out-to-bottom data-open:slide-in-from-bottom",
26
+ } satisfies Record<string, string>
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Components
30
+ // ---------------------------------------------------------------------------
31
+
32
+ export type SheetProps = React.ComponentProps<typeof SheetPrimitive.Root>
33
+
34
+ function Sheet({ ...props }: SheetProps): ReactElement {
35
+ return <SheetPrimitive.Root data-slot="sheet" {...props} />
36
+ }
37
+
38
+ export type SheetTriggerProps = React.ComponentProps<typeof SheetPrimitive.Trigger>
39
+
40
+ function SheetTrigger({
41
+ ...props
42
+ }: SheetTriggerProps): ReactElement {
43
+ return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
44
+ }
45
+
46
+ export type SheetCloseProps = React.ComponentProps<typeof SheetPrimitive.Close>
47
+
48
+ function SheetClose({
49
+ ...props
50
+ }: SheetCloseProps): ReactElement {
51
+ return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
52
+ }
53
+
54
+ export type SheetPortalProps = React.ComponentProps<typeof SheetPrimitive.Portal>
55
+
56
+ function SheetPortal({
57
+ ...props
58
+ }: SheetPortalProps): ReactElement {
59
+ return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
60
+ }
61
+
62
+ function SheetOverlay({
63
+ className,
64
+ ...props
65
+ }: React.ComponentProps<typeof SheetPrimitive.Backdrop>): ReactElement {
66
+ return (
67
+ <SheetPrimitive.Backdrop
68
+ className={cn(
69
+ "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",
70
+ className
71
+ )}
72
+ data-slot="sheet-overlay"
73
+ {...props}
74
+ />
75
+ )
76
+ }
77
+
78
+ export type SheetContentProps = React.ComponentProps<typeof SheetPrimitive.Popup> & {
79
+ side?: keyof typeof SIDE_CLASSES
80
+ showCloseButton?: boolean
81
+ }
82
+
83
+ function SheetContent({
84
+ className,
85
+ children,
86
+ side = "right",
87
+ showCloseButton = true,
88
+ style,
89
+ ...props
90
+ }: SheetContentProps): ReactElement {
91
+ const themeVars = useThemeVars();
92
+ return (
93
+ <SheetPortal>
94
+ <SheetOverlay />
95
+ <SheetPrimitive.Popup
96
+ className={cn(
97
+ "fixed z-50 flex flex-col gap-4 bg-background transition ease-in-out data-ending-style:animate-out data-ending-style:duration-300 data-ending-style:fill-mode-forwards data-open:animate-in data-open:duration-500",
98
+ SIDE_CLASSES[side],
99
+ className
100
+ )}
101
+ data-slot="sheet-content"
102
+ style={{ ...themeVars, ...style } as React.CSSProperties}
103
+ {...props}
104
+ >
105
+ {children}
106
+ {showCloseButton ? <SheetPrimitive.Close
107
+ 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"
108
+ data-slot="sheet-icon-close"
109
+ >
110
+ <XIcon />
111
+ <span className="sr-only">Close</span>
112
+ </SheetPrimitive.Close> : null}
113
+ </SheetPrimitive.Popup>
114
+ </SheetPortal>
115
+ )
116
+ }
117
+
118
+ export type SheetHeaderProps = React.ComponentProps<"div">
119
+
120
+ function SheetHeader({ className, ...props }: SheetHeaderProps): ReactElement {
121
+ return (
122
+ <div
123
+ className={cn("flex flex-col gap-1.5 p-4", className)}
124
+ data-slot="sheet-header"
125
+ {...props}
126
+ />
127
+ )
128
+ }
129
+
130
+ export type SheetFooterProps = React.ComponentProps<"div">
131
+
132
+ function SheetFooter({ className, ...props }: SheetFooterProps): ReactElement {
133
+ return (
134
+ <div
135
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
136
+ data-slot="sheet-footer"
137
+ {...props}
138
+ />
139
+ )
140
+ }
141
+
142
+ export type SheetTitleProps = React.ComponentProps<typeof SheetPrimitive.Title>
143
+
144
+ function SheetTitle({
145
+ className,
146
+ ...props
147
+ }: SheetTitleProps): ReactElement {
148
+ return (
149
+ <SheetPrimitive.Title
150
+ className={cn("text-lg font-semibold text-foreground", className)}
151
+ data-slot="sheet-title"
152
+ {...props}
153
+ />
154
+ )
155
+ }
156
+
157
+ export type SheetDescriptionProps = React.ComponentProps<typeof SheetPrimitive.Description>
158
+
159
+ function SheetDescription({
160
+ className,
161
+ ...props
162
+ }: SheetDescriptionProps): ReactElement {
163
+ return (
164
+ <SheetPrimitive.Description
165
+ className={cn("text-sm text-muted-foreground", className)}
166
+ data-slot="sheet-description"
167
+ {...props}
168
+ />
169
+ )
170
+ }
171
+
172
+ export {
173
+ Sheet,
174
+ SheetClose,
175
+ SheetContent,
176
+ SheetDescription,
177
+ SheetFooter,
178
+ SheetHeader,
179
+ SheetPortal,
180
+ SheetTitle,
181
+ SheetTrigger,
182
+ }
@@ -0,0 +1,22 @@
1
+ import { type ReactElement, type ComponentProps } from "react"
2
+ import { cn } from "@/lib/utils"
3
+
4
+ /**
5
+ * Skeleton — WealthX Design System (shadcn base)
6
+ *
7
+ * Base: official shadcn skeleton (npx shadcn\@latest add skeleton)
8
+ * WealthX override: removed rounded-md (sharp corners per design system)
9
+ */
10
+ export type SkeletonProps = ComponentProps<"div">
11
+
12
+ function Skeleton({ className, ...props }: SkeletonProps): ReactElement {
13
+ return (
14
+ <div
15
+ className={cn("animate-pulse bg-muted", className)}
16
+ data-slot="skeleton"
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+
22
+ export { Skeleton }
@@ -0,0 +1,48 @@
1
+ import { type ReactElement } from "react"
2
+ import React from "react"
3
+ import {
4
+ CircleCheckIcon,
5
+ InfoIcon,
6
+ Loader2Icon,
7
+ OctagonXIcon,
8
+ TriangleAlertIcon,
9
+ } from "lucide-react"
10
+ import { Toaster as Sonner, type ToasterProps } from "sonner"
11
+
12
+ /**
13
+ * Toaster — WealthX Design System (shadcn base)
14
+ * Figma: https://www.figma.com/design/9V9F0NGVsif8LGmEhVjOcT/Design-System---shadcn?node-id=3094-7557
15
+ *
16
+ * Variants: Default | Success | Info | Warning | Error | Loading
17
+ * Left accent strip (4px) applied to all status types via globals.css data-type selectors.
18
+ * WealthX uses a custom ThemeProvider (not next-themes), so `theme` defaults to "light".
19
+ */
20
+ function Toaster({ theme = "light", ...props }: ToasterProps): ReactElement {
21
+ return <Sonner
22
+ className="toaster group"
23
+ icons={{
24
+ success: <CircleCheckIcon className="size-4" />,
25
+ info: <InfoIcon className="size-4" />,
26
+ warning: <TriangleAlertIcon className="size-4" />,
27
+ error: <OctagonXIcon className="size-4" />,
28
+ loading: <Loader2Icon className="size-4 animate-spin" />,
29
+ }}
30
+ style={
31
+ {
32
+ "--normal-bg": "var(--popover)",
33
+ "--normal-text": "var(--popover-foreground)",
34
+ "--normal-border": "var(--border)",
35
+ "--border-radius": "var(--radius)",
36
+ } as React.CSSProperties
37
+ }
38
+ theme={theme}
39
+ toastOptions={{
40
+ classNames: {
41
+ toast: "font-sans",
42
+ },
43
+ }}
44
+ {...props}
45
+ />
46
+ }
47
+
48
+ export { Toaster }
@@ -0,0 +1,41 @@
1
+ import { type ReactElement, type ComponentProps } from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+ import { LoaderCircle } from "lucide-react"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ /**
7
+ * Spinner — WealthX Design System
8
+ * Figma: https://www.figma.com/design/9V9F0NGVsif8LGmEhVjOcT/Design-System---shadcn?node-id=1196-1174
9
+ *
10
+ * Built on lucide-react LoaderCircle + animate-spin.
11
+ * Color inherits from parent via currentColor — use text-* to style.
12
+ * Sizes: sm (12px) | default (16px) | lg (24px) | xl (32px)
13
+ */
14
+ const spinnerVariants = cva("animate-spin shrink-0", {
15
+ variants: {
16
+ size: {
17
+ sm: "size-3",
18
+ default: "size-4",
19
+ lg: "size-6",
20
+ xl: "size-8",
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ size: "default",
25
+ },
26
+ })
27
+
28
+ export type SpinnerProps = ComponentProps<"svg"> & VariantProps<typeof spinnerVariants>
29
+
30
+ function Spinner({ className, size, ...props }: SpinnerProps): ReactElement {
31
+ return (
32
+ <LoaderCircle
33
+ aria-hidden="true"
34
+ className={cn(spinnerVariants({ size }), className)}
35
+ data-slot="spinner"
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ export { Spinner, spinnerVariants }
@@ -0,0 +1,126 @@
1
+ "use client"
2
+
3
+ import { type ReactElement, useState, type ComponentProps } from "react"
4
+ import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
5
+ import { cn } from "@/lib/utils"
6
+
7
+ /**
8
+ * Switch — shadcn/WealthX
9
+ * Base: npx shadcn\@latest add switch (latest)
10
+ * Figma: Design-System---shadcn?node-id=3041-6197
11
+ *
12
+ * WealthX additions: aria-invalid error state, SwitchCard with position variants.
13
+ * Tokens: unchecked=input, checked=primary, error=destructive, thumb=background.
14
+ */
15
+ export type SwitchProps = ComponentProps<typeof SwitchPrimitive.Root> & {
16
+ size?: "sm" | "default"
17
+ }
18
+
19
+ function Switch({
20
+ className,
21
+ size = "default",
22
+ ...props
23
+ }: SwitchProps): ReactElement {
24
+ return (
25
+ <SwitchPrimitive.Root
26
+ className={cn(
27
+ "peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none",
28
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
29
+ "disabled:cursor-not-allowed disabled:opacity-50",
30
+ "data-[size=default]:h-[1.15rem] data-[size=default]:w-8",
31
+ "data-[size=sm]:h-3.5 data-[size=sm]:w-6",
32
+ "data-checked:bg-primary data-unchecked:bg-input",
33
+ "aria-invalid:data-checked:bg-destructive aria-invalid:border-destructive",
34
+ "dark:data-unchecked:bg-input/80",
35
+ className
36
+ )}
37
+ data-size={size}
38
+ data-slot="switch"
39
+ {...props}
40
+ >
41
+ <SwitchPrimitive.Thumb
42
+ className={cn(
43
+ "pointer-events-none block rounded-full bg-background ring-0 transition-transform",
44
+ "group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3",
45
+ "data-checked:translate-x-[calc(100%-2px)] data-unchecked:translate-x-0",
46
+ "dark:data-checked:bg-primary-foreground dark:data-unchecked:bg-foreground"
47
+ )}
48
+ data-slot="switch-thumb"
49
+ />
50
+ </SwitchPrimitive.Root>
51
+ )
52
+ }
53
+
54
+ /**
55
+ * SwitchCard -- card wrapper with label + description + switch toggle.
56
+ * Figma: SwitchCard (3041:6237)
57
+ */
58
+ export type SwitchCardProps = Omit<ComponentProps<typeof SwitchPrimitive.Root>, "children"> & {
59
+ label: string
60
+ description?: string
61
+ error?: boolean
62
+ switchPosition?: "left" | "right"
63
+ size?: "sm" | "default"
64
+ }
65
+
66
+ function SwitchCard({
67
+ className,
68
+ checked,
69
+ defaultChecked,
70
+ onCheckedChange,
71
+ disabled,
72
+ error,
73
+ label,
74
+ description,
75
+ switchPosition = "right",
76
+ size,
77
+ ...props
78
+ }: SwitchCardProps): ReactElement {
79
+ const [internalChecked, setInternalChecked] = useState(defaultChecked ?? false)
80
+ const isChecked = checked ?? internalChecked
81
+
82
+ const switchElement = (
83
+ <Switch
84
+ aria-invalid={error || undefined}
85
+ checked={isChecked as boolean}
86
+ className="shrink-0"
87
+ disabled={disabled}
88
+ onCheckedChange={(value, event) => {
89
+ setInternalChecked(value)
90
+ onCheckedChange?.(value, event)
91
+ }}
92
+ size={size}
93
+ {...props}
94
+ />
95
+ )
96
+
97
+ const contentElement = (
98
+ <div className="flex flex-col gap-1">
99
+ <span className="text-sm font-medium leading-none">{label}</span>
100
+ {description ? <span className="text-sm text-muted-foreground">{description}</span> : null}
101
+ </div>
102
+ )
103
+
104
+ return (
105
+ <label
106
+ className={cn(
107
+ "flex items-center gap-3 border border-border p-4 font-sans transition-colors cursor-pointer",
108
+ switchPosition === "right" && "justify-between",
109
+ disabled && "cursor-not-allowed opacity-50",
110
+ error
111
+ ? ["border-destructive", isChecked && "bg-destructive/5"]
112
+ : isChecked && "border-primary bg-primary/5",
113
+ className
114
+ )}
115
+ data-slot="switch-card"
116
+ >
117
+ {switchPosition === "left" ? (
118
+ <>{switchElement}{contentElement}</>
119
+ ) : (
120
+ <>{contentElement}{switchElement}</>
121
+ )}
122
+ </label>
123
+ )
124
+ }
125
+
126
+ export { Switch, SwitchCard }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Table — WealthX DS overrides (shadcn base)
3
+ *
4
+ * Changes from shadcn default:
5
+ * - TableHead: `text-foreground` -- `text-muted-foreground` — header labels are secondary text
6
+ * - TableHead: `px-2` -- `px-4` — wider horizontal padding per Figma spacing
7
+ * - TableCell: `p-2` -- `px-4 py-3` — consistent cell padding per Figma
8
+ * - TableRow: `transition-colors` -- `transition-[background-color,opacity]` — scoped animation
9
+ * - TableRow: `data-[state=selected]:bg-muted` -- `data-[state=selected]:bg-primary/10` — selected tint matches Figma (primary + 10% opacity)
10
+ * - TableFooter: inherits same cell padding via className
11
+ */
12
+ import { type ReactElement } from "react"
13
+ import * as React from "react"
14
+ import { cn } from "@/lib/utils"
15
+
16
+ export type TableProps = React.ComponentProps<"table">
17
+
18
+ function Table({ className, ...props }: TableProps): ReactElement {
19
+ return (
20
+ <div
21
+ className="relative w-full overflow-x-auto"
22
+ data-slot="table-container"
23
+ >
24
+ <table
25
+ className={cn("w-full caption-bottom text-sm", className)}
26
+ data-slot="table"
27
+ {...props}
28
+ />
29
+ </div>
30
+ )
31
+ }
32
+
33
+ export type TableHeaderProps = React.ComponentProps<"thead">
34
+
35
+ function TableHeader({ className, ...props }: TableHeaderProps): ReactElement {
36
+ return (
37
+ <thead
38
+ className={cn("[&_tr]:border-b", className)}
39
+ data-slot="table-header"
40
+ {...props}
41
+ />
42
+ )
43
+ }
44
+
45
+ export type TableBodyProps = React.ComponentProps<"tbody">
46
+
47
+ function TableBody({ className, ...props }: TableBodyProps): ReactElement {
48
+ return (
49
+ <tbody
50
+ className={cn("[&_tr:last-child]:border-0", className)}
51
+ data-slot="table-body"
52
+ {...props}
53
+ />
54
+ )
55
+ }
56
+
57
+ export type TableFooterProps = React.ComponentProps<"tfoot">
58
+
59
+ function TableFooter({ className, ...props }: TableFooterProps): ReactElement {
60
+ return (
61
+ <tfoot
62
+ className={cn(
63
+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
64
+ className
65
+ )}
66
+ data-slot="table-footer"
67
+ {...props}
68
+ />
69
+ )
70
+ }
71
+
72
+ export type TableRowProps = React.ComponentProps<"tr">
73
+
74
+ function TableRow({ className, ...props }: TableRowProps): ReactElement {
75
+ return (
76
+ <tr
77
+ className={cn(
78
+ "border-b transition-[background-color,opacity] hover:bg-muted/50 data-[state=selected]:bg-primary/10",
79
+ className
80
+ )}
81
+ data-slot="table-row"
82
+ {...props}
83
+ />
84
+ )
85
+ }
86
+
87
+ export type TableHeadProps = React.ComponentProps<"th">
88
+
89
+ function TableHead({ className, ...props }: TableHeadProps): ReactElement {
90
+ return (
91
+ <th
92
+ className={cn(
93
+ "h-10 px-4 text-left align-middle font-medium whitespace-nowrap text-muted-foreground",
94
+ "[&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
95
+ className
96
+ )}
97
+ data-slot="table-head"
98
+ {...props}
99
+ />
100
+ )
101
+ }
102
+
103
+ export type TableCellProps = React.ComponentProps<"td">
104
+
105
+ function TableCell({ className, ...props }: TableCellProps): ReactElement {
106
+ return (
107
+ <td
108
+ className={cn(
109
+ "px-4 py-3 align-middle whitespace-nowrap",
110
+ "[&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
111
+ className
112
+ )}
113
+ data-slot="table-cell"
114
+ {...props}
115
+ />
116
+ )
117
+ }
118
+
119
+ export type TableCaptionProps = React.ComponentProps<"caption">
120
+
121
+ function TableCaption({
122
+ className,
123
+ ...props
124
+ }: TableCaptionProps): ReactElement {
125
+ return (
126
+ <caption
127
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
128
+ data-slot="table-caption"
129
+ {...props}
130
+ />
131
+ )
132
+ }
133
+
134
+ export {
135
+ Table,
136
+ TableHeader,
137
+ TableBody,
138
+ TableFooter,
139
+ TableHead,
140
+ TableRow,
141
+ TableCell,
142
+ TableCaption,
143
+ }
@@ -0,0 +1,119 @@
1
+ import { type ReactElement } from "react"
2
+ import * as React from "react"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+ import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"
5
+ import { cn } from "@/lib/utils"
6
+
7
+ export type TabsProps = React.ComponentProps<typeof TabsPrimitive.Root> & {
8
+ orientation?: "horizontal" | "vertical"
9
+ }
10
+
11
+ function Tabs({
12
+ className,
13
+ orientation = "horizontal",
14
+ ...props
15
+ }: TabsProps): ReactElement {
16
+ return (
17
+ <TabsPrimitive.Root
18
+ className={cn(
19
+ "group/tabs flex gap-2 data-[orientation=horizontal]:flex-col",
20
+ className
21
+ )}
22
+ data-orientation={orientation}
23
+ data-slot="tabs"
24
+ {...props}
25
+ />
26
+ )
27
+ }
28
+
29
+ const tabsListVariants = cva(
30
+ "group/tabs-list inline-flex w-fit items-center justify-center gap-1 rounded-none p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col",
31
+ {
32
+ variants: {
33
+ variant: {
34
+ default: "bg-muted",
35
+ line: "bg-transparent p-0",
36
+ },
37
+ },
38
+ defaultVariants: {
39
+ variant: "default",
40
+ },
41
+ }
42
+ )
43
+
44
+ export type TabsListProps = React.ComponentProps<typeof TabsPrimitive.List> &
45
+ VariantProps<typeof tabsListVariants>
46
+
47
+ function TabsList({
48
+ className,
49
+ variant = "default",
50
+ ...props
51
+ }: TabsListProps): ReactElement {
52
+ return (
53
+ <TabsPrimitive.List
54
+ className={cn(tabsListVariants({ variant }), className)}
55
+ data-slot="tabs-list"
56
+ data-variant={variant}
57
+ {...props}
58
+ />
59
+ )
60
+ }
61
+
62
+ export type TabsTriggerProps = React.ComponentProps<typeof TabsPrimitive.Tab>
63
+
64
+ function TabsTrigger({
65
+ className,
66
+ ...props
67
+ }: TabsTriggerProps): ReactElement {
68
+ return (
69
+ <TabsPrimitive.Tab
70
+ className={cn(
71
+ // Base layout & typography
72
+ "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 whitespace-nowrap",
73
+ "rounded-none border border-transparent px-1.5 py-1 text-sm font-medium",
74
+ "text-muted-foreground transition-all",
75
+ // Vertical orientation
76
+ "group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start",
77
+ // Hover & focus
78
+ "hover:text-foreground",
79
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring",
80
+ // Disabled
81
+ "disabled:pointer-events-none disabled:opacity-50",
82
+ // Active state — Default variant: primary/10 bg + subtle shadow
83
+ // Base UI uses data-active attribute for the active/selected tab
84
+ "data-active:bg-primary/10 data-active:text-foreground",
85
+ "group-data-[variant=default]/tabs-list:data-active:shadow-sm",
86
+ // Active state — Line variant: suppress bg & shadow
87
+ "group-data-[variant=line]/tabs-list:data-active:bg-transparent",
88
+ "group-data-[variant=line]/tabs-list:data-active:shadow-none",
89
+ // Indicator pseudo-element (visible only for Line variant, active state)
90
+ "after:absolute after:bg-primary after:opacity-0 after:transition-opacity",
91
+ "group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5",
92
+ "group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5",
93
+ "group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
94
+ // SVG icons
95
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
96
+ className
97
+ )}
98
+ data-slot="tabs-trigger"
99
+ {...props}
100
+ />
101
+ )
102
+ }
103
+
104
+ export type TabsContentProps = React.ComponentProps<typeof TabsPrimitive.Panel>
105
+
106
+ function TabsContent({
107
+ className,
108
+ ...props
109
+ }: TabsContentProps): ReactElement {
110
+ return (
111
+ <TabsPrimitive.Panel
112
+ className={cn("flex-1 outline-none", className)}
113
+ data-slot="tabs-content"
114
+ {...props}
115
+ />
116
+ )
117
+ }
118
+
119
+ export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }