@wealthx/shadcn 0.0.2 → 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 (188) hide show
  1. package/.turbo/turbo-build.log +135 -11
  2. package/CHANGELOG.md +6 -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 +4312 -90
  125. package/dist/index.mjs +459 -158
  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 +1 -1
  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/dist/index.d.mts +0 -27
  186. package/dist/index.d.ts +0 -27
  187. package/src/provider/ShadcnProvider.tsx +0 -89
  188. package/src/provider/index.ts +0 -2
@@ -0,0 +1,24 @@
1
+ import { type ReactElement } from "react"
2
+ import * as React from "react"
3
+ import { cn } from "@/lib/utils"
4
+
5
+ export type LabelProps = React.ComponentProps<"label">
6
+
7
+ function Label({
8
+ className,
9
+ ...props
10
+ }: LabelProps): ReactElement {
11
+ return (
12
+ // eslint-disable-next-line jsx-a11y/label-has-associated-control -- htmlFor is passed by the consumer
13
+ <label
14
+ className={cn(
15
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
16
+ className
17
+ )}
18
+ data-slot="label"
19
+ {...props}
20
+ />
21
+ )
22
+ }
23
+
24
+ export { Label }
@@ -0,0 +1,148 @@
1
+ import { type ReactElement } from "react"
2
+ import * as React from "react"
3
+ import {
4
+ ChevronLeftIcon,
5
+ ChevronRightIcon,
6
+ MoreHorizontalIcon,
7
+ } from "lucide-react"
8
+ import { cn } from "@/lib/utils"
9
+ import { buttonVariants, type Button } from "@/components/ui/button"
10
+
11
+ /**
12
+ * Pagination — WealthX Design System
13
+ * Figma: https://www.figma.com/design/9V9F0NGVsif8LGmEhVjOcT/Design-System---shadcn?node-id=73-1981
14
+ *
15
+ * Base: official shadcn pagination (npx shadcn\@latest add pagination)
16
+ * WealthX overrides: none — ghost/outline variants match design system
17
+ */
18
+
19
+ export type PaginationProps = React.ComponentProps<"nav">
20
+
21
+ function Pagination({ className, ...props }: PaginationProps): ReactElement {
22
+ return (
23
+ <nav
24
+ aria-label="pagination"
25
+ className={cn("mx-auto flex w-full justify-center", className)}
26
+ data-slot="pagination"
27
+ role="navigation"
28
+ {...props}
29
+ />
30
+ )
31
+ }
32
+
33
+ export type PaginationContentProps = React.ComponentProps<"ul">
34
+
35
+ function PaginationContent({
36
+ className,
37
+ ...props
38
+ }: PaginationContentProps): ReactElement {
39
+ return (
40
+ <ul
41
+ className={cn("flex flex-row items-center gap-1", className)}
42
+ data-slot="pagination-content"
43
+ {...props}
44
+ />
45
+ )
46
+ }
47
+
48
+ export type PaginationItemProps = React.ComponentProps<"li">
49
+
50
+ function PaginationItem({ ...props }: PaginationItemProps): ReactElement {
51
+ return <li data-slot="pagination-item" {...props} />
52
+ }
53
+
54
+ export type PaginationLinkProps = {
55
+ isActive?: boolean
56
+ } & Pick<React.ComponentProps<typeof Button>, "size"> &
57
+ React.ComponentProps<"a">
58
+
59
+ function PaginationLink({
60
+ className,
61
+ isActive,
62
+ size = "icon",
63
+ ...props
64
+ }: PaginationLinkProps): ReactElement {
65
+ return (
66
+ // eslint-disable-next-line jsx-a11y/anchor-has-content -- children passed via props spread
67
+ <a
68
+ aria-current={isActive ? "page" : undefined}
69
+ className={cn(
70
+ buttonVariants({
71
+ variant: isActive ? "outline" : "ghost",
72
+ size,
73
+ }),
74
+ className
75
+ )}
76
+ data-active={isActive}
77
+ data-slot="pagination-link"
78
+ {...props}
79
+ />
80
+ )
81
+ }
82
+
83
+ export type PaginationPreviousProps = React.ComponentProps<typeof PaginationLink>
84
+
85
+ function PaginationPrevious({
86
+ className,
87
+ ...props
88
+ }: PaginationPreviousProps): ReactElement {
89
+ return (
90
+ <PaginationLink
91
+ aria-label="Go to previous page"
92
+ className={cn("gap-1 px-2.5 sm:pl-2.5", className)}
93
+ size="default"
94
+ {...props}
95
+ >
96
+ <ChevronLeftIcon />
97
+ <span className="hidden sm:block">Previous</span>
98
+ </PaginationLink>
99
+ )
100
+ }
101
+
102
+ export type PaginationNextProps = React.ComponentProps<typeof PaginationLink>
103
+
104
+ function PaginationNext({
105
+ className,
106
+ ...props
107
+ }: PaginationNextProps): ReactElement {
108
+ return (
109
+ <PaginationLink
110
+ aria-label="Go to next page"
111
+ className={cn("gap-1 px-2.5 sm:pr-2.5", className)}
112
+ size="default"
113
+ {...props}
114
+ >
115
+ <span className="hidden sm:block">Next</span>
116
+ <ChevronRightIcon />
117
+ </PaginationLink>
118
+ )
119
+ }
120
+
121
+ export type PaginationEllipsisProps = React.ComponentProps<"span">
122
+
123
+ function PaginationEllipsis({
124
+ className,
125
+ ...props
126
+ }: PaginationEllipsisProps): ReactElement {
127
+ return (
128
+ <span
129
+ aria-hidden
130
+ className={cn("flex size-9 items-center justify-center", className)}
131
+ data-slot="pagination-ellipsis"
132
+ {...props}
133
+ >
134
+ <MoreHorizontalIcon className="size-4" />
135
+ <span className="sr-only">More pages</span>
136
+ </span>
137
+ )
138
+ }
139
+
140
+ export {
141
+ Pagination,
142
+ PaginationContent,
143
+ PaginationLink,
144
+ PaginationItem,
145
+ PaginationPrevious,
146
+ PaginationNext,
147
+ PaginationEllipsis,
148
+ }
@@ -0,0 +1,112 @@
1
+ import { type ReactElement } from "react"
2
+ import * as React from "react"
3
+ import { Popover as PopoverPrimitive } from "@base-ui/react/popover"
4
+ import { cn } from "@/lib/utils"
5
+ import { useThemeVars } from "@/lib/theme-provider"
6
+
7
+ export type PopoverProps = React.ComponentProps<typeof PopoverPrimitive.Root>
8
+
9
+ function Popover({
10
+ ...props
11
+ }: PopoverProps): ReactElement {
12
+ return <PopoverPrimitive.Root data-slot="popover" {...props} />
13
+ }
14
+
15
+ export type PopoverTriggerProps = React.ComponentProps<typeof PopoverPrimitive.Trigger>
16
+
17
+ function PopoverTrigger({
18
+ ...props
19
+ }: PopoverTriggerProps): ReactElement {
20
+ return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
21
+ }
22
+
23
+ export type PopoverContentProps = React.ComponentProps<typeof PopoverPrimitive.Popup> & {
24
+ align?: "start" | "center" | "end"
25
+ sideOffset?: number
26
+ }
27
+
28
+ function PopoverContent({
29
+ className,
30
+ align = "center",
31
+ sideOffset = 4,
32
+ style,
33
+ ...props
34
+ }: PopoverContentProps): ReactElement {
35
+ const themeVars = useThemeVars();
36
+ return (
37
+ <PopoverPrimitive.Portal>
38
+ <PopoverPrimitive.Positioner
39
+ align={align}
40
+ sideOffset={sideOffset}
41
+ >
42
+ <PopoverPrimitive.Popup
43
+ className={cn(
44
+ "z-50 w-72 border border-border bg-popover p-4 text-popover-foreground shadow-md outline-hidden 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",
45
+ className
46
+ )}
47
+ data-slot="popover-content"
48
+ style={{ ...themeVars, ...style } as React.CSSProperties}
49
+ {...props}
50
+ />
51
+ </PopoverPrimitive.Positioner>
52
+ </PopoverPrimitive.Portal>
53
+ )
54
+ }
55
+
56
+ export type PopoverAnchorProps = React.ComponentProps<"div">
57
+
58
+ function PopoverAnchor({
59
+ ...props
60
+ }: PopoverAnchorProps): ReactElement {
61
+ return <div data-slot="popover-anchor" {...props} />
62
+ }
63
+
64
+ export type PopoverHeaderProps = React.ComponentProps<"div">
65
+
66
+ function PopoverHeader({ className, ...props }: PopoverHeaderProps): ReactElement {
67
+ return (
68
+ <div
69
+ className={cn("flex flex-col gap-1 text-sm", className)}
70
+ data-slot="popover-header"
71
+ {...props}
72
+ />
73
+ )
74
+ }
75
+
76
+ export type PopoverTitleProps = React.ComponentProps<"h2">
77
+
78
+ function PopoverTitle({ className, ...props }: PopoverTitleProps): ReactElement {
79
+ return (
80
+ // eslint-disable-next-line jsx-a11y/heading-has-content -- children passed via props spread
81
+ <h2
82
+ className={cn("font-medium", className)}
83
+ data-slot="popover-title"
84
+ {...props}
85
+ />
86
+ )
87
+ }
88
+
89
+ export type PopoverDescriptionProps = React.ComponentProps<"p">
90
+
91
+ function PopoverDescription({
92
+ className,
93
+ ...props
94
+ }: PopoverDescriptionProps): ReactElement {
95
+ return (
96
+ <p
97
+ className={cn("text-muted-foreground", className)}
98
+ data-slot="popover-description"
99
+ {...props}
100
+ />
101
+ )
102
+ }
103
+
104
+ export {
105
+ Popover,
106
+ PopoverTrigger,
107
+ PopoverContent,
108
+ PopoverAnchor,
109
+ PopoverHeader,
110
+ PopoverTitle,
111
+ PopoverDescription,
112
+ }
@@ -0,0 +1,40 @@
1
+ import { type ReactElement } from "react"
2
+ import * as React from "react"
3
+ import { Progress as ProgressPrimitive } from "@base-ui/react/progress"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ /**
7
+ * Progress — WealthX Design System
8
+ * Figma: https://www.figma.com/design/9V9F0NGVsif8LGmEhVjOcT/Design-System---shadcn?node-id=73-1983
9
+ *
10
+ * Base: official shadcn progress (npx shadcn\@latest add progress)
11
+ * WealthX overrides: sharp corners (no rounded-full), track bg-muted instead of bg-primary/20
12
+ */
13
+ export type ProgressProps = React.ComponentProps<typeof ProgressPrimitive.Root>
14
+
15
+ function Progress({
16
+ className,
17
+ value,
18
+ ...props
19
+ }: ProgressProps): ReactElement {
20
+ return (
21
+ <ProgressPrimitive.Root
22
+ className={cn(
23
+ "relative h-2 w-full overflow-hidden bg-muted",
24
+ className
25
+ )}
26
+ data-slot="progress"
27
+ value={value}
28
+ {...props}
29
+ >
30
+ <ProgressPrimitive.Track className="h-full">
31
+ <ProgressPrimitive.Indicator
32
+ className="h-full bg-primary transition-all"
33
+ data-slot="progress-indicator"
34
+ />
35
+ </ProgressPrimitive.Track>
36
+ </ProgressPrimitive.Root>
37
+ )
38
+ }
39
+
40
+ export { Progress }
@@ -0,0 +1,129 @@
1
+ import { type ReactElement, type ComponentProps } from "react"
2
+ import { CircleIcon } from "lucide-react"
3
+ import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group"
4
+ import { Radio as RadioPrimitive } from "@base-ui/react/radio"
5
+ import { cn } from "@/lib/utils"
6
+
7
+ /**
8
+ * RadioGroup — WealthX Design System (shadcn base)
9
+ * Figma: https://www.figma.com/design/9V9F0NGVsif8LGmEhVjOcT/Design-System---shadcn?node-id=76-8893
10
+ *
11
+ * Base: official shadcn radio-group (npx shadcn\@latest add radio-group)
12
+ * WealthX additions: RadioGroupCard (card with radio + label/description)
13
+ *
14
+ * Tokens: circle fill=background (white), stroke=primary, checked fill=primary,
15
+ * indicator=background. Error overrides primary-destructive.
16
+ */
17
+
18
+ /* Shared radio circle base classes -- single source for Item & Card */
19
+ const CIRCLE_BASE =
20
+ "inline-flex items-center justify-center aspect-square size-4 shrink-0 rounded-full border border-primary bg-background"
21
+
22
+ /* Shared indicator dot */
23
+ const INDICATOR_DOT = "size-2 fill-background text-background"
24
+
25
+ export type RadioGroupProps = ComponentProps<typeof RadioGroupPrimitive>
26
+
27
+ function RadioGroup({
28
+ className,
29
+ ...props
30
+ }: RadioGroupProps): ReactElement {
31
+ return (
32
+ <RadioGroupPrimitive
33
+ className={cn("grid gap-3", className)}
34
+ data-slot="radio-group"
35
+ {...props}
36
+ />
37
+ )
38
+ }
39
+
40
+ export type RadioGroupItemProps = ComponentProps<typeof RadioPrimitive.Root>
41
+
42
+ function RadioGroupItem({
43
+ className,
44
+ ...props
45
+ }: RadioGroupItemProps): ReactElement {
46
+ return (
47
+ <RadioPrimitive.Root
48
+ className={cn(
49
+ CIRCLE_BASE,
50
+ "transition-all outline-none",
51
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
52
+ "disabled:cursor-not-allowed disabled:opacity-50",
53
+ "data-checked:bg-primary",
54
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
55
+ "aria-invalid:data-checked:border-destructive aria-invalid:data-checked:bg-destructive",
56
+ "dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
57
+ className
58
+ )}
59
+ data-slot="radio-group-item"
60
+ {...props}
61
+ >
62
+ <RadioPrimitive.Indicator
63
+ data-slot="radio-group-indicator"
64
+ >
65
+ <CircleIcon className={INDICATOR_DOT} />
66
+ </RadioPrimitive.Indicator>
67
+ </RadioPrimitive.Root>
68
+ )
69
+ }
70
+
71
+ /**
72
+ * RadioGroupCard -- card with radio left, label+description right.
73
+ * Figma: CardRadio (3039:5971)
74
+ * Checked: border-primary + bg-primary/5. Error: destructive variant.
75
+ */
76
+ export type RadioGroupCardProps = Omit<ComponentProps<typeof RadioPrimitive.Root>, "children"> & {
77
+ label: string
78
+ description?: string
79
+ error?: boolean
80
+ }
81
+
82
+ function RadioGroupCard({
83
+ className,
84
+ value,
85
+ disabled,
86
+ error,
87
+ label,
88
+ description,
89
+ ...props
90
+ }: RadioGroupCardProps): ReactElement {
91
+ return (
92
+ <RadioPrimitive.Root
93
+ aria-invalid={error || undefined}
94
+ className={cn(
95
+ "group/card flex items-center gap-3 border border-border p-4 font-sans transition-colors text-left cursor-pointer outline-none",
96
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
97
+ "disabled:cursor-not-allowed disabled:opacity-50",
98
+ "data-checked:border-primary data-checked:bg-primary/5",
99
+ "aria-invalid:border-destructive",
100
+ "aria-invalid:data-checked:border-destructive aria-invalid:data-checked:bg-destructive/5",
101
+ className
102
+ )}
103
+ data-slot="radio-group-card"
104
+ disabled={disabled}
105
+ value={value}
106
+ {...props}
107
+ >
108
+ <div
109
+ className={cn(
110
+ CIRCLE_BASE,
111
+ "transition-colors",
112
+ "group-data-[checked]/card:bg-primary",
113
+ "group-aria-invalid/card:border-destructive",
114
+ "group-aria-invalid/card:group-data-[checked]/card:border-destructive group-aria-invalid/card:group-data-[checked]/card:bg-destructive",
115
+ )}
116
+ >
117
+ <RadioPrimitive.Indicator>
118
+ <CircleIcon className={INDICATOR_DOT} />
119
+ </RadioPrimitive.Indicator>
120
+ </div>
121
+ <div className="flex flex-col gap-1">
122
+ <span className="text-sm font-medium leading-none">{label}</span>
123
+ {description ? <span className="text-sm text-muted-foreground">{description}</span> : null}
124
+ </div>
125
+ </RadioPrimitive.Root>
126
+ )
127
+ }
128
+
129
+ export { RadioGroup, RadioGroupItem, RadioGroupCard }
@@ -0,0 +1,201 @@
1
+ import { type ReactElement } from "react"
2
+ import * as React from "react"
3
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
4
+ import { Select as SelectPrimitive } from "@base-ui/react/select"
5
+ import { cn } from "@/lib/utils"
6
+ import { useThemeVars } from "@/lib/theme-provider"
7
+
8
+ export type SelectProps = React.ComponentProps<typeof SelectPrimitive.Root>
9
+
10
+ function Select({
11
+ ...props
12
+ }: SelectProps): ReactElement {
13
+ return <SelectPrimitive.Root data-slot="select" {...props} />
14
+ }
15
+
16
+ export type SelectGroupProps = React.ComponentProps<typeof SelectPrimitive.Group>
17
+
18
+ function SelectGroup({
19
+ ...props
20
+ }: SelectGroupProps): ReactElement {
21
+ return <SelectPrimitive.Group data-slot="select-group" {...props} />
22
+ }
23
+
24
+ export type SelectValueProps = React.ComponentProps<typeof SelectPrimitive.Value>
25
+
26
+ function SelectValue({
27
+ ...props
28
+ }: SelectValueProps): ReactElement {
29
+ return <SelectPrimitive.Value data-slot="select-value" {...props} />
30
+ }
31
+
32
+ export type SelectTriggerProps = React.ComponentProps<typeof SelectPrimitive.Trigger> & {
33
+ size?: "sm" | "default"
34
+ }
35
+
36
+ function SelectTrigger({
37
+ className,
38
+ size = "default",
39
+ children,
40
+ ...props
41
+ }: SelectTriggerProps): ReactElement {
42
+ return (
43
+ <SelectPrimitive.Trigger
44
+ className={cn(
45
+ "flex w-fit items-center justify-between gap-2 border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-primary/20 data-popup-open:border-primary data-popup-open:ring-[3px] data-popup-open:ring-primary/20 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
46
+ className
47
+ )}
48
+ data-size={size}
49
+ data-slot="select-trigger"
50
+ {...props}
51
+ >
52
+ {children}
53
+ <SelectPrimitive.Icon className="transition-transform duration-200 data-popup-open:rotate-180">
54
+ <ChevronDownIcon className="size-4 opacity-50" />
55
+ </SelectPrimitive.Icon>
56
+ </SelectPrimitive.Trigger>
57
+ )
58
+ }
59
+
60
+ export type SelectContentProps = React.ComponentProps<typeof SelectPrimitive.Popup>
61
+
62
+ function SelectContent({
63
+ className,
64
+ children,
65
+ style,
66
+ ...props
67
+ }: SelectContentProps): ReactElement {
68
+ const themeVars = useThemeVars();
69
+ return (
70
+ <SelectPrimitive.Portal>
71
+ <SelectPrimitive.Positioner align="start" alignItemWithTrigger={false} sideOffset={4}>
72
+ <SelectPrimitive.Popup
73
+ className={cn(
74
+ "relative z-50 max-h-[var(--available-height)] min-w-[8rem] overflow-x-hidden overflow-y-auto 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",
75
+ className
76
+ )}
77
+ data-slot="select-content"
78
+ style={{ ...themeVars, ...style } as React.CSSProperties}
79
+ {...props}
80
+ >
81
+ <SelectScrollUpButton />
82
+ {children}
83
+ <SelectScrollDownButton />
84
+ </SelectPrimitive.Popup>
85
+ </SelectPrimitive.Positioner>
86
+ </SelectPrimitive.Portal>
87
+ )
88
+ }
89
+
90
+ export type SelectLabelProps = React.ComponentProps<typeof SelectPrimitive.GroupLabel>
91
+
92
+ function SelectLabel({
93
+ className,
94
+ ...props
95
+ }: SelectLabelProps): ReactElement {
96
+ return (
97
+ <SelectPrimitive.GroupLabel
98
+ className={cn("px-2 py-1.5 text-xs font-semibold text-muted-foreground", className)}
99
+ data-slot="select-label"
100
+ {...props}
101
+ />
102
+ )
103
+ }
104
+
105
+ export type SelectItemProps = React.ComponentProps<typeof SelectPrimitive.Item>
106
+
107
+ function SelectItem({
108
+ className,
109
+ children,
110
+ ...props
111
+ }: SelectItemProps): ReactElement {
112
+ return (
113
+ <SelectPrimitive.Item
114
+ className={cn(
115
+ "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-highlighted:bg-primary/5 data-highlighted:text-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
116
+ className
117
+ )}
118
+ data-slot="select-item"
119
+ {...props}
120
+ >
121
+ <span
122
+ className="absolute right-2 flex size-3.5 items-center justify-center"
123
+ data-slot="select-item-indicator"
124
+ >
125
+ <SelectPrimitive.ItemIndicator>
126
+ <CheckIcon className="size-4" />
127
+ </SelectPrimitive.ItemIndicator>
128
+ </span>
129
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
130
+ </SelectPrimitive.Item>
131
+ )
132
+ }
133
+
134
+ export type SelectSeparatorProps = React.ComponentProps<"div">
135
+
136
+ function SelectSeparator({
137
+ className,
138
+ ...props
139
+ }: SelectSeparatorProps): ReactElement {
140
+ return (
141
+ <div
142
+ className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
143
+ data-slot="select-separator"
144
+ role="separator"
145
+ {...props}
146
+ />
147
+ )
148
+ }
149
+
150
+ export type SelectScrollUpButtonProps = React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>
151
+
152
+ function SelectScrollUpButton({
153
+ className,
154
+ ...props
155
+ }: SelectScrollUpButtonProps): ReactElement {
156
+ return (
157
+ <SelectPrimitive.ScrollUpArrow
158
+ className={cn(
159
+ "flex cursor-default items-center justify-center py-1",
160
+ className
161
+ )}
162
+ data-slot="select-scroll-up-button"
163
+ {...props}
164
+ >
165
+ <ChevronUpIcon className="size-4" />
166
+ </SelectPrimitive.ScrollUpArrow>
167
+ )
168
+ }
169
+
170
+ export type SelectScrollDownButtonProps = React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>
171
+
172
+ function SelectScrollDownButton({
173
+ className,
174
+ ...props
175
+ }: SelectScrollDownButtonProps): ReactElement {
176
+ return (
177
+ <SelectPrimitive.ScrollDownArrow
178
+ className={cn(
179
+ "flex cursor-default items-center justify-center py-1",
180
+ className
181
+ )}
182
+ data-slot="select-scroll-down-button"
183
+ {...props}
184
+ >
185
+ <ChevronDownIcon className="size-4" />
186
+ </SelectPrimitive.ScrollDownArrow>
187
+ )
188
+ }
189
+
190
+ export {
191
+ Select,
192
+ SelectContent,
193
+ SelectGroup,
194
+ SelectItem,
195
+ SelectLabel,
196
+ SelectScrollDownButton,
197
+ SelectScrollUpButton,
198
+ SelectSeparator,
199
+ SelectTrigger,
200
+ SelectValue,
201
+ }
@@ -0,0 +1,26 @@
1
+ import { type ReactElement } from "react"
2
+ import * as React from "react"
3
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ export type SeparatorProps = React.ComponentProps<typeof SeparatorPrimitive>
7
+
8
+ function Separator({
9
+ className,
10
+ orientation = "horizontal",
11
+ ...props
12
+ }: SeparatorProps): ReactElement {
13
+ return (
14
+ <SeparatorPrimitive
15
+ className={cn(
16
+ "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
17
+ className
18
+ )}
19
+ data-orientation={orientation}
20
+ data-slot="separator"
21
+ {...props}
22
+ />
23
+ )
24
+ }
25
+
26
+ export { Separator }