@wealthx/shadcn 0.0.2 → 1.0.1
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.
- package/.turbo/turbo-build.log +135 -11
- package/CHANGELOG.md +12 -0
- package/CHANGES.md +345 -0
- package/README.md +128 -0
- package/dist/chunk-2WZVSBAY.mjs +232 -0
- package/dist/chunk-2Y7YJKPE.mjs +47 -0
- package/dist/chunk-3U7SD3MS.mjs +55 -0
- package/dist/chunk-3VQNJ235.mjs +114 -0
- package/dist/chunk-55CEW76V.mjs +35 -0
- package/dist/chunk-6AFMNC42.mjs +146 -0
- package/dist/chunk-6OJF6XRN.mjs +117 -0
- package/dist/chunk-7LDIMXGM.mjs +181 -0
- package/dist/chunk-AMJ23O53.mjs +122 -0
- package/dist/chunk-BBJBJSXQ.mjs +44 -0
- package/dist/chunk-BGP2N52Z.mjs +126 -0
- package/dist/chunk-BMFN37JH.mjs +41 -0
- package/dist/chunk-CGOKTPXU.mjs +79 -0
- package/dist/chunk-CZ3BW5GL.mjs +81 -0
- package/dist/chunk-DBHJ5KC3.mjs +55 -0
- package/dist/chunk-DDPA2XXS.mjs +97 -0
- package/dist/chunk-DS2AMHN2.mjs +30 -0
- package/dist/chunk-E3K6O4FZ.mjs +57 -0
- package/dist/chunk-FWCSY2DS.mjs +37 -0
- package/dist/chunk-GPRJQ24C.mjs +28 -0
- package/dist/chunk-HS7TFG7V.mjs +24 -0
- package/dist/chunk-HUVTPUV2.mjs +256 -0
- package/dist/chunk-IAOOZCUY.mjs +90 -0
- package/dist/chunk-JF4PHPD5.mjs +111 -0
- package/dist/chunk-JU2RUWHF.mjs +123 -0
- package/dist/chunk-KKHTJNMM.mjs +86 -0
- package/dist/chunk-MJIEMGRD.mjs +266 -0
- package/dist/chunk-MKFL5MNH.mjs +372 -0
- package/dist/chunk-MQ72DIBH.mjs +105 -0
- package/dist/chunk-NGYG2EA6.mjs +148 -0
- package/dist/chunk-NWZ46DJL.mjs +213 -0
- package/dist/chunk-OXQQNQZI.mjs +75 -0
- package/dist/chunk-PMKODV6M.mjs +161 -0
- package/dist/chunk-QOJ2DQD6.mjs +57 -0
- package/dist/chunk-RL772EH7.mjs +126 -0
- package/dist/chunk-SLWCCURD.mjs +99 -0
- package/dist/chunk-V7CNWJT3.mjs +10 -0
- package/dist/chunk-VG6UF6UT.mjs +68 -0
- package/dist/chunk-VYMHBV6D.mjs +123 -0
- package/dist/chunk-VZ2NR7L3.mjs +195 -0
- package/dist/chunk-YN5SYTOO.mjs +117 -0
- package/dist/chunk-Z3MK2KKZ.mjs +83 -0
- package/dist/chunk-ZN2QKLF6.mjs +187 -0
- package/dist/chunk-ZZV5JVNW.mjs +34 -0
- package/dist/components/ui/accordion.js +142 -0
- package/dist/components/ui/accordion.mjs +14 -0
- package/dist/components/ui/alert-dialog.js +413 -0
- package/dist/components/ui/alert-dialog.mjs +34 -0
- package/dist/components/ui/alert.js +134 -0
- package/dist/components/ui/alert.mjs +12 -0
- package/dist/components/ui/avatar.js +173 -0
- package/dist/components/ui/avatar.mjs +18 -0
- package/dist/components/ui/badge.js +163 -0
- package/dist/components/ui/badge.mjs +11 -0
- package/dist/components/ui/button.js +198 -0
- package/dist/components/ui/button.mjs +11 -0
- package/dist/components/ui/calendar.js +408 -0
- package/dist/components/ui/calendar.mjs +12 -0
- package/dist/components/ui/card.js +156 -0
- package/dist/components/ui/card.mjs +20 -0
- package/dist/components/ui/checkbox.js +166 -0
- package/dist/components/ui/checkbox.mjs +11 -0
- package/dist/components/ui/chip.js +199 -0
- package/dist/components/ui/chip.mjs +10 -0
- package/dist/components/ui/data-table.js +925 -0
- package/dist/components/ui/data-table.mjs +29 -0
- package/dist/components/ui/date-picker.js +561 -0
- package/dist/components/ui/date-picker.mjs +15 -0
- package/dist/components/ui/dialog.js +378 -0
- package/dist/components/ui/dialog.mjs +30 -0
- package/dist/components/ui/drawer.js +213 -0
- package/dist/components/ui/drawer.mjs +28 -0
- package/dist/components/ui/dropdown-menu.js +338 -0
- package/dist/components/ui/dropdown-menu.mjs +38 -0
- package/dist/components/ui/empty.js +173 -0
- package/dist/components/ui/empty.mjs +18 -0
- package/dist/components/ui/field.js +359 -0
- package/dist/components/ui/field.mjs +28 -0
- package/dist/components/ui/input-group.js +406 -0
- package/dist/components/ui/input-group.mjs +22 -0
- package/dist/components/ui/input-otp.js +149 -0
- package/dist/components/ui/input-otp.mjs +14 -0
- package/dist/components/ui/input.js +81 -0
- package/dist/components/ui/input.mjs +8 -0
- package/dist/components/ui/label.js +85 -0
- package/dist/components/ui/label.mjs +8 -0
- package/dist/components/ui/pagination.js +333 -0
- package/dist/components/ui/pagination.mjs +22 -0
- package/dist/components/ui/popover.js +167 -0
- package/dist/components/ui/popover.mjs +22 -0
- package/dist/components/ui/progress.js +97 -0
- package/dist/components/ui/progress.mjs +8 -0
- package/dist/components/ui/radio-group.js +178 -0
- package/dist/components/ui/radio-group.mjs +12 -0
- package/dist/components/ui/select.js +262 -0
- package/dist/components/ui/select.mjs +28 -0
- package/dist/components/ui/separator.js +86 -0
- package/dist/components/ui/separator.mjs +8 -0
- package/dist/components/ui/sheet.js +227 -0
- package/dist/components/ui/sheet.mjs +26 -0
- package/dist/components/ui/skeleton.js +75 -0
- package/dist/components/ui/skeleton.mjs +8 -0
- package/dist/components/ui/sonner.js +86 -0
- package/dist/components/ui/sonner.mjs +7 -0
- package/dist/components/ui/spinner.js +93 -0
- package/dist/components/ui/spinner.mjs +10 -0
- package/dist/components/ui/switch.js +178 -0
- package/dist/components/ui/switch.mjs +11 -0
- package/dist/components/ui/table.js +184 -0
- package/dist/components/ui/table.mjs +22 -0
- package/dist/components/ui/tabs.js +181 -0
- package/dist/components/ui/tabs.mjs +16 -0
- package/dist/components/ui/textarea.js +79 -0
- package/dist/components/ui/textarea.mjs +8 -0
- package/dist/components/ui/toggle-group.js +184 -0
- package/dist/components/ui/toggle-group.mjs +12 -0
- package/dist/components/ui/toggle.js +108 -0
- package/dist/components/ui/toggle.mjs +11 -0
- package/dist/components/ui/tooltip.js +140 -0
- package/dist/components/ui/tooltip.mjs +16 -0
- package/dist/index.js +4312 -90
- package/dist/index.mjs +459 -158
- package/dist/lib/colors.js +84 -0
- package/dist/lib/colors.mjs +13 -0
- package/dist/lib/theme-provider.js +150 -0
- package/dist/lib/theme-provider.mjs +13 -0
- package/dist/lib/typography.js +157 -0
- package/dist/lib/typography.mjs +25 -0
- package/dist/lib/utils.js +34 -0
- package/dist/lib/utils.mjs +7 -0
- package/dist/styles.css +1 -1
- package/package.json +228 -11
- package/scripts/build-css.ts +15 -9
- package/src/components/index.tsx +443 -0
- package/src/components/ui/accordion.tsx +99 -0
- package/src/components/ui/alert-dialog.tsx +239 -0
- package/src/components/ui/alert.tsx +81 -0
- package/src/components/ui/avatar.tsx +130 -0
- package/src/components/ui/badge.tsx +57 -0
- package/src/components/ui/button.tsx +69 -37
- package/src/components/ui/calendar.tsx +252 -0
- package/src/components/ui/card.tsx +106 -0
- package/src/components/ui/checkbox.tsx +111 -0
- package/src/components/ui/chip.tsx +65 -0
- package/src/components/ui/data-table.tsx +490 -0
- package/src/components/ui/date-picker.tsx +133 -0
- package/src/components/ui/dialog.tsx +195 -0
- package/src/components/ui/drawer.tsx +169 -0
- package/src/components/ui/dropdown-menu.tsx +315 -0
- package/src/components/ui/empty.tsx +128 -0
- package/src/components/ui/field.tsx +273 -0
- package/src/components/ui/input-group.tsx +190 -0
- package/src/components/ui/input-otp.tsx +90 -0
- package/src/components/ui/input.tsx +28 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/pagination.tsx +148 -0
- package/src/components/ui/popover.tsx +112 -0
- package/src/components/ui/progress.tsx +40 -0
- package/src/components/ui/radio-group.tsx +129 -0
- package/src/components/ui/select.tsx +201 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/sheet.tsx +182 -0
- package/src/components/ui/skeleton.tsx +22 -0
- package/src/components/ui/sonner.tsx +48 -0
- package/src/components/ui/spinner.tsx +41 -0
- package/src/components/ui/switch.tsx +126 -0
- package/src/components/ui/table.tsx +143 -0
- package/src/components/ui/tabs.tsx +119 -0
- package/src/components/ui/textarea.tsx +28 -0
- package/src/components/ui/toggle-group.tsx +94 -0
- package/src/components/ui/toggle.tsx +59 -0
- package/src/components/ui/tooltip.tsx +80 -0
- package/src/index.ts +15 -3
- package/src/lib/colors.ts +74 -0
- package/src/lib/slot.tsx +68 -0
- package/src/lib/theme-provider.tsx +134 -0
- package/src/lib/typography.ts +153 -0
- package/src/lib/utils.ts +1 -1
- package/src/styles/globals.css +377 -107
- package/src/styles/styles-css.ts +1 -1
- package/tsup.config.ts +48 -2
- package/dist/index.d.mts +0 -27
- package/dist/index.d.ts +0 -27
- package/src/provider/ShadcnProvider.tsx +0 -89
- package/src/provider/index.ts +0 -2
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Textarea — WealthX DS overrides (shadcn base)
|
|
3
|
+
*
|
|
4
|
+
* Changes from shadcn default:
|
|
5
|
+
* - `shadow-xs` removed — flat panels per Figma
|
|
6
|
+
* - `font-sans` added — consistent typography token
|
|
7
|
+
*/
|
|
8
|
+
import { type ReactElement } from "react"
|
|
9
|
+
import * as React from "react"
|
|
10
|
+
import { cn } from "@/lib/utils"
|
|
11
|
+
|
|
12
|
+
export type TextareaProps = React.ComponentProps<"textarea">
|
|
13
|
+
|
|
14
|
+
function Textarea({ className, ...props }: TextareaProps): ReactElement {
|
|
15
|
+
return (
|
|
16
|
+
<textarea
|
|
17
|
+
className={cn(
|
|
18
|
+
// WealthX: removed shadow-xs (flat panels), added font-sans
|
|
19
|
+
"flex field-sizing-content min-h-16 w-full border border-input bg-transparent px-3 py-2 text-base font-sans transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-primary/20 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
|
|
20
|
+
className
|
|
21
|
+
)}
|
|
22
|
+
data-slot="textarea"
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { Textarea }
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { type ReactElement } from "react"
|
|
4
|
+
import * as React from "react"
|
|
5
|
+
import { type VariantProps } from "class-variance-authority"
|
|
6
|
+
import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group"
|
|
7
|
+
import { Toggle as ToggleComponent } from "@base-ui/react/toggle"
|
|
8
|
+
import { cn } from "@/lib/utils"
|
|
9
|
+
import { toggleVariants } from "@/components/ui/toggle"
|
|
10
|
+
|
|
11
|
+
const ToggleGroupContext = React.createContext<
|
|
12
|
+
VariantProps<typeof toggleVariants> & {
|
|
13
|
+
spacing?: number
|
|
14
|
+
}
|
|
15
|
+
>({
|
|
16
|
+
size: "default",
|
|
17
|
+
variant: "default",
|
|
18
|
+
spacing: 0,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
export type ToggleGroupProps = React.ComponentProps<typeof ToggleGroupPrimitive> &
|
|
22
|
+
VariantProps<typeof toggleVariants> & {
|
|
23
|
+
spacing?: number
|
|
24
|
+
/** shadcn-compatible alias: "single" -- multiple=false, "multiple" -- multiple=true */
|
|
25
|
+
type?: "single" | "multiple"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function ToggleGroup({
|
|
29
|
+
className,
|
|
30
|
+
variant,
|
|
31
|
+
size,
|
|
32
|
+
spacing = 0,
|
|
33
|
+
type,
|
|
34
|
+
children,
|
|
35
|
+
...props
|
|
36
|
+
}: ToggleGroupProps): ReactElement {
|
|
37
|
+
return (
|
|
38
|
+
<ToggleGroupPrimitive
|
|
39
|
+
className={cn(
|
|
40
|
+
// WealthX: removed rounded-md (sharp corners) and shadow-xs (flat panels)
|
|
41
|
+
"group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))]",
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
data-size={size}
|
|
45
|
+
data-slot="toggle-group"
|
|
46
|
+
data-spacing={spacing}
|
|
47
|
+
data-variant={variant}
|
|
48
|
+
multiple={type === "multiple"}
|
|
49
|
+
style={{ "--gap": spacing } as React.CSSProperties}
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
<ToggleGroupContext.Provider value={{ variant, size, spacing }}>
|
|
53
|
+
{children}
|
|
54
|
+
</ToggleGroupContext.Provider>
|
|
55
|
+
</ToggleGroupPrimitive>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type ToggleGroupItemProps = React.ComponentProps<typeof ToggleComponent> &
|
|
60
|
+
VariantProps<typeof toggleVariants>
|
|
61
|
+
|
|
62
|
+
function ToggleGroupItem({
|
|
63
|
+
className,
|
|
64
|
+
children,
|
|
65
|
+
variant,
|
|
66
|
+
size,
|
|
67
|
+
...props
|
|
68
|
+
}: ToggleGroupItemProps): ReactElement {
|
|
69
|
+
const context = React.useContext(ToggleGroupContext)
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<ToggleComponent
|
|
73
|
+
className={cn(
|
|
74
|
+
toggleVariants({
|
|
75
|
+
variant: context.variant || variant,
|
|
76
|
+
size: context.size || size,
|
|
77
|
+
}),
|
|
78
|
+
"w-auto min-w-0 shrink-0 focus:z-10 focus-visible:z-10",
|
|
79
|
+
// WealthX: no rounded corners on grouped items
|
|
80
|
+
"data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l",
|
|
81
|
+
className
|
|
82
|
+
)}
|
|
83
|
+
data-size={context.size || size}
|
|
84
|
+
data-slot="toggle-group-item"
|
|
85
|
+
data-spacing={context.spacing}
|
|
86
|
+
data-variant={context.variant || variant}
|
|
87
|
+
{...props}
|
|
88
|
+
>
|
|
89
|
+
{children}
|
|
90
|
+
</ToggleComponent>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { ToggleGroup, ToggleGroupItem }
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { type ReactElement } from "react"
|
|
4
|
+
import * as React from "react"
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
6
|
+
import { Toggle as ToggleComponent } from "@base-ui/react/toggle"
|
|
7
|
+
import { cn } from "@/lib/utils"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Toggle — WealthX DS overrides (shadcn base)
|
|
11
|
+
*
|
|
12
|
+
* Changes from shadcn default:
|
|
13
|
+
* - `rounded-md` removed — sharp corners per WealthX DS
|
|
14
|
+
* - `shadow-xs` removed from outline variant — flat panels
|
|
15
|
+
* - Pressed/on state uses `bg-primary/10` + `inset-ring-primary` via Figma tokens
|
|
16
|
+
* (inset-ring instead of border-primary so indicator is independent of border-width,
|
|
17
|
+
* works correctly inside ToggleGroup where border-l is collapsed for connected items)
|
|
18
|
+
*/
|
|
19
|
+
const toggleVariants = cva(
|
|
20
|
+
"inline-flex items-center justify-center gap-2 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-pressed:bg-primary/10 data-pressed:inset-ring data-pressed:inset-ring-primary data-pressed:text-foreground data-pressed:hover:bg-primary/10 data-pressed:hover:text-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
21
|
+
{
|
|
22
|
+
variants: {
|
|
23
|
+
variant: {
|
|
24
|
+
default: "bg-transparent hover:bg-muted hover:text-muted-foreground",
|
|
25
|
+
outline:
|
|
26
|
+
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
|
|
27
|
+
},
|
|
28
|
+
size: {
|
|
29
|
+
default: "h-9 min-w-9 px-2",
|
|
30
|
+
sm: "h-8 min-w-8 px-1.5",
|
|
31
|
+
lg: "h-10 min-w-10 px-2.5",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: {
|
|
35
|
+
variant: "default",
|
|
36
|
+
size: "default",
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
export type ToggleProps = React.ComponentProps<typeof ToggleComponent> &
|
|
42
|
+
VariantProps<typeof toggleVariants>
|
|
43
|
+
|
|
44
|
+
function Toggle({
|
|
45
|
+
className,
|
|
46
|
+
variant,
|
|
47
|
+
size,
|
|
48
|
+
...props
|
|
49
|
+
}: ToggleProps): ReactElement {
|
|
50
|
+
return (
|
|
51
|
+
<ToggleComponent
|
|
52
|
+
className={cn(toggleVariants({ variant, size, className }))}
|
|
53
|
+
data-slot="toggle"
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { Toggle, toggleVariants }
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type ReactElement } from "react"
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
import { useThemeVars } from "@/lib/theme-provider"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Tooltip — WealthX Design System
|
|
9
|
+
* Figma: https://www.figma.com/design/9V9F0NGVsif8LGmEhVjOcT/Design-System---shadcn?node-id=73-2904
|
|
10
|
+
*
|
|
11
|
+
* Base: official shadcn tooltip (npx shadcn\@latest add tooltip)
|
|
12
|
+
* WealthX overrides: bg-brand-secondary, text-brand-secondary-foreground, sharp corners (no rounded-md)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export type TooltipProviderProps = React.ComponentProps<typeof TooltipPrimitive.Provider>
|
|
16
|
+
|
|
17
|
+
function TooltipProvider({
|
|
18
|
+
delay = 0,
|
|
19
|
+
...props
|
|
20
|
+
}: TooltipProviderProps): ReactElement {
|
|
21
|
+
return (
|
|
22
|
+
<TooltipPrimitive.Provider
|
|
23
|
+
data-slot="tooltip-provider"
|
|
24
|
+
delay={delay}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type TooltipProps = React.ComponentProps<typeof TooltipPrimitive.Root>
|
|
31
|
+
|
|
32
|
+
function Tooltip({
|
|
33
|
+
...props
|
|
34
|
+
}: TooltipProps): ReactElement {
|
|
35
|
+
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type TooltipTriggerProps = React.ComponentProps<typeof TooltipPrimitive.Trigger>
|
|
39
|
+
|
|
40
|
+
function TooltipTrigger({
|
|
41
|
+
...props
|
|
42
|
+
}: TooltipTriggerProps): ReactElement {
|
|
43
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type TooltipContentProps = React.ComponentProps<typeof TooltipPrimitive.Popup> & {
|
|
47
|
+
sideOffset?: number
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function TooltipContent({
|
|
51
|
+
className,
|
|
52
|
+
sideOffset = 8,
|
|
53
|
+
children,
|
|
54
|
+
style,
|
|
55
|
+
...props
|
|
56
|
+
}: TooltipContentProps): ReactElement {
|
|
57
|
+
const themeVars = useThemeVars();
|
|
58
|
+
return (
|
|
59
|
+
<TooltipPrimitive.Portal>
|
|
60
|
+
<TooltipPrimitive.Positioner sideOffset={sideOffset}>
|
|
61
|
+
<TooltipPrimitive.Popup
|
|
62
|
+
className={cn(
|
|
63
|
+
"z-50 w-fit animate-in bg-brand-secondary px-3 py-1.5 text-xs text-balance text-brand-secondary-foreground fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95 data-ending-style:fill-mode-forwards",
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
data-slot="tooltip-content"
|
|
67
|
+
style={{ ...themeVars, ...style } as React.CSSProperties}
|
|
68
|
+
{...props}
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</TooltipPrimitive.Popup>
|
|
72
|
+
<TooltipPrimitive.Arrow
|
|
73
|
+
className="z-50 size-2.5 rotate-45 bg-brand-secondary"
|
|
74
|
+
/>
|
|
75
|
+
</TooltipPrimitive.Positioner>
|
|
76
|
+
</TooltipPrimitive.Portal>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
3
|
-
|
|
1
|
+
export { cn } from "./lib/utils";
|
|
2
|
+
export {
|
|
3
|
+
TYPOGRAPHY,
|
|
4
|
+
TYPOGRAPHY_DISPLAY,
|
|
5
|
+
TYPOGRAPHY_HEADING,
|
|
6
|
+
TYPOGRAPHY_BODY,
|
|
7
|
+
TYPOGRAPHY_LABEL,
|
|
8
|
+
TYPOGRAPHY_UTILITY,
|
|
9
|
+
TYPOGRAPHY_RESPONSIVE,
|
|
10
|
+
FONT_FAMILY_SANS,
|
|
11
|
+
getTypographyCssVars,
|
|
12
|
+
getResponsiveTypographyCssVars,
|
|
13
|
+
} from "./lib/typography";
|
|
14
|
+
export type { TypographyStyle } from "./lib/typography";
|
|
15
|
+
export * from "./components";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color utilities for WealthX white-label theming.
|
|
3
|
+
* Ported from \@wealthx/ui theme/utils.ts — zero MUI dependencies.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// WCAG contrast text colors
|
|
7
|
+
const CONTRAST_DARK = "#040D13";
|
|
8
|
+
const CONTRAST_LIGHT = "#FFFFFF";
|
|
9
|
+
|
|
10
|
+
export function hexToRgb(hex: string): [number, number, number] {
|
|
11
|
+
const normalized = hex.replace(/^#/, "");
|
|
12
|
+
if (normalized.length === 3) {
|
|
13
|
+
const r = parseInt(normalized[0] + normalized[0], 16);
|
|
14
|
+
const g = parseInt(normalized[1] + normalized[1], 16);
|
|
15
|
+
const b = parseInt(normalized[2] + normalized[2], 16);
|
|
16
|
+
return [r, g, b];
|
|
17
|
+
}
|
|
18
|
+
if (normalized.length === 6 || normalized.length === 8) {
|
|
19
|
+
const r = parseInt(normalized.slice(0, 2), 16);
|
|
20
|
+
const g = parseInt(normalized.slice(2, 4), 16);
|
|
21
|
+
const b = parseInt(normalized.slice(4, 6), 16);
|
|
22
|
+
return [r, g, b];
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`Invalid hex color: ${hex}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function linearizeSrgb(c: number): number {
|
|
28
|
+
const n = c / 255;
|
|
29
|
+
return n <= 0.03928 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** WCAG relative luminance (0 = black, 1 = white) */
|
|
33
|
+
export function getLuminance(hex: string): number {
|
|
34
|
+
const [r, g, b] = hexToRgb(hex);
|
|
35
|
+
return (
|
|
36
|
+
0.2126 * linearizeSrgb(r) +
|
|
37
|
+
0.7152 * linearizeSrgb(g) +
|
|
38
|
+
0.0722 * linearizeSrgb(b)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Returns dark or light text color based on WCAG contrast ratio */
|
|
43
|
+
export function getContrastText(backgroundColor: string): string {
|
|
44
|
+
const luminance = getLuminance(backgroundColor);
|
|
45
|
+
return luminance > 0.179 ? CONTRAST_DARK : CONTRAST_LIGHT;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Convert hex to oklch() CSS string */
|
|
49
|
+
export function hexToOklch(hex: string): string {
|
|
50
|
+
const [r, g, b] = hexToRgb(hex);
|
|
51
|
+
const rl = linearizeSrgb(r);
|
|
52
|
+
const gl = linearizeSrgb(g);
|
|
53
|
+
const bl = linearizeSrgb(b);
|
|
54
|
+
|
|
55
|
+
// sRGB linear → OKLab
|
|
56
|
+
const l = 0.4122214708 * rl + 0.5363325363 * gl + 0.0514459929 * bl;
|
|
57
|
+
const m = 0.2119034982 * rl + 0.6806995451 * gl + 0.1073969566 * bl;
|
|
58
|
+
const s = 0.0883024619 * rl + 0.2817188376 * gl + 0.6299787005 * bl;
|
|
59
|
+
|
|
60
|
+
const l_ = l > 0 ? Math.cbrt(l) : 0;
|
|
61
|
+
const m_ = m > 0 ? Math.cbrt(m) : 0;
|
|
62
|
+
const s_ = s > 0 ? Math.cbrt(s) : 0;
|
|
63
|
+
|
|
64
|
+
const L = 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_;
|
|
65
|
+
const a = 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_;
|
|
66
|
+
const bv = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_;
|
|
67
|
+
|
|
68
|
+
// OKLab → OKLch
|
|
69
|
+
const C = Math.sqrt(a * a + bv * bv);
|
|
70
|
+
const h = ((Math.atan2(bv, a) * 180) / Math.PI + 360) % 360;
|
|
71
|
+
|
|
72
|
+
if (C < 0.001) return `oklch(${L.toFixed(3)} 0 0)`;
|
|
73
|
+
return `oklch(${L.toFixed(3)} ${C.toFixed(3)} ${h.toFixed(1)})`;
|
|
74
|
+
}
|
package/src/lib/slot.tsx
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
function mergeRefs<T>(
|
|
4
|
+
...refs: (React.Ref<T> | undefined | null)[]
|
|
5
|
+
): (value: T | null) => void {
|
|
6
|
+
return (value: T | null): void => {
|
|
7
|
+
for (const ref of refs) {
|
|
8
|
+
if (typeof ref === "function") ref(value);
|
|
9
|
+
else if (ref !== null)
|
|
10
|
+
(ref as React.MutableRefObject<T | null>).current = value;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface SlotProps extends React.HTMLAttributes<HTMLElement> {
|
|
16
|
+
children?: React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const Slot = React.forwardRef<HTMLElement, SlotProps>(
|
|
20
|
+
({ children, ...props }, forwardedRef) => {
|
|
21
|
+
const child = React.Children.only(children);
|
|
22
|
+
if (!React.isValidElement(child)) return null;
|
|
23
|
+
|
|
24
|
+
const childProps = child.props as Record<string, unknown>;
|
|
25
|
+
const merged: Record<string, unknown> = { ...props };
|
|
26
|
+
|
|
27
|
+
for (const key of Object.keys(childProps)) {
|
|
28
|
+
if (key === "className") {
|
|
29
|
+
merged.className = [props.className, childProps.className]
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join(" ");
|
|
32
|
+
} else if (key === "style") {
|
|
33
|
+
merged.style = {
|
|
34
|
+
...(props.style as object),
|
|
35
|
+
...(childProps.style as object),
|
|
36
|
+
};
|
|
37
|
+
} else if (
|
|
38
|
+
key.startsWith("on") &&
|
|
39
|
+
typeof childProps[key] === "function"
|
|
40
|
+
) {
|
|
41
|
+
const parentHandler = (props as Record<string, unknown>)[key];
|
|
42
|
+
if (typeof parentHandler === "function") {
|
|
43
|
+
merged[key] = (...args: unknown[]) => {
|
|
44
|
+
(childProps[key] as (...a: unknown[]) => unknown)(...args);
|
|
45
|
+
(parentHandler as (...a: unknown[]) => unknown)(...args);
|
|
46
|
+
};
|
|
47
|
+
} else {
|
|
48
|
+
merged[key] = childProps[key];
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
merged[key] = childProps[key];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const childRef = (child as unknown as { ref?: React.Ref<HTMLElement> }).ref;
|
|
56
|
+
merged.ref = forwardedRef
|
|
57
|
+
? mergeRefs(forwardedRef, childRef)
|
|
58
|
+
: childRef;
|
|
59
|
+
|
|
60
|
+
return React.cloneElement(
|
|
61
|
+
child,
|
|
62
|
+
merged as React.Attributes & Record<string, unknown>,
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
Slot.displayName = "Slot";
|
|
67
|
+
|
|
68
|
+
export { Slot };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext, useMemo } from "react";
|
|
4
|
+
import { hexToOklch, getContrastText } from "./colors";
|
|
5
|
+
|
|
6
|
+
export interface ThemeProviderProps {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
/** Tenant primary brand color (hex). Default: WealthX green */
|
|
9
|
+
primary?: string;
|
|
10
|
+
/** Tenant secondary brand color (hex). Default: WealthX dark navy */
|
|
11
|
+
secondary?: string;
|
|
12
|
+
/** Tenant font family override. Default: Figtree */
|
|
13
|
+
fontFamily?: string;
|
|
14
|
+
/** Disable CSS variable injection (e.g. for SSR with static CSS) */
|
|
15
|
+
injectCssVariables?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* React context that stores the computed theme CSS variables.
|
|
20
|
+
*
|
|
21
|
+
* Portal-rendered components (Dialog, AlertDialog, Popover, Tooltip, etc.)
|
|
22
|
+
* escape the DOM subtree of the ThemeProvider wrapper \<div\>, so they lose
|
|
23
|
+
* the scoped CSS custom properties set via inline styles. Components that
|
|
24
|
+
* render inside a portal consume this context and re-apply the variables
|
|
25
|
+
* on their outermost portal element, restoring the theme cascade.
|
|
26
|
+
*/
|
|
27
|
+
const ThemeVarsContext = createContext<Record<string, string>>({});
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns the theme CSS variables from the nearest ThemeProvider.
|
|
31
|
+
* Portal-based components use this to re-apply scoped theme vars
|
|
32
|
+
* on content rendered outside the ThemeProvider's DOM subtree.
|
|
33
|
+
*/
|
|
34
|
+
export function useThemeVars(): Record<string, string> {
|
|
35
|
+
return useContext(ThemeVarsContext);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const DEFAULT_PRIMARY = "#33FF99";
|
|
39
|
+
const DEFAULT_SECONDARY = "#162029";
|
|
40
|
+
const DEFAULT_FONT =
|
|
41
|
+
'"Figtree", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* WealthX ThemeProvider — injects tenant brand colors as scoped CSS variables.
|
|
45
|
+
*
|
|
46
|
+
* How it works:
|
|
47
|
+
* 1. Takes primary/secondary hex colors from tenant config
|
|
48
|
+
* 2. Converts to oklch() (matching globals.css format)
|
|
49
|
+
* 3. Auto-computes WCAG contrast text for each via WCAG luminance
|
|
50
|
+
* 4. Sets CSS custom properties as inline styles on a wrapper \<div\>,
|
|
51
|
+
* scoped to this subtree (inherited by children via CSS cascade).
|
|
52
|
+
* This allows multiple ThemeProviders on the same page with different colors.
|
|
53
|
+
*
|
|
54
|
+
* Usage:
|
|
55
|
+
* \<ThemeProvider primary="#FF6600" secondary="#1A1A2E"\>
|
|
56
|
+
* \<App /\>
|
|
57
|
+
* \</ThemeProvider\>
|
|
58
|
+
*/
|
|
59
|
+
export function ThemeProvider({
|
|
60
|
+
children,
|
|
61
|
+
primary = DEFAULT_PRIMARY,
|
|
62
|
+
secondary = DEFAULT_SECONDARY,
|
|
63
|
+
fontFamily = DEFAULT_FONT,
|
|
64
|
+
injectCssVariables = true,
|
|
65
|
+
}: ThemeProviderProps): React.ReactElement {
|
|
66
|
+
const vars = useMemo(() => {
|
|
67
|
+
if (!injectCssVariables) return {} as Record<string, string>;
|
|
68
|
+
|
|
69
|
+
const primaryOklch = hexToOklch(primary);
|
|
70
|
+
const primaryFgOklch = hexToOklch(getContrastText(primary));
|
|
71
|
+
const secondaryOklch = hexToOklch(secondary);
|
|
72
|
+
const secondaryFgOklch = hexToOklch(getContrastText(secondary));
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
// Brand primary — used by buttons, links, focus rings, active states
|
|
76
|
+
"--primary": primaryOklch,
|
|
77
|
+
"--primary-foreground": primaryFgOklch,
|
|
78
|
+
"--ring": primaryOklch,
|
|
79
|
+
|
|
80
|
+
// Sidebar uses primary for active navigation
|
|
81
|
+
"--sidebar-primary": primaryOklch,
|
|
82
|
+
"--sidebar-primary-foreground": primaryFgOklch,
|
|
83
|
+
|
|
84
|
+
// Brand secondary — tenant navy used by Secondary button variant
|
|
85
|
+
// NOTE: --secondary is reserved for paper/surface (#F5F8FA), so we use --brand-secondary
|
|
86
|
+
"--brand-secondary": secondaryOklch,
|
|
87
|
+
"--brand-secondary-foreground": secondaryFgOklch,
|
|
88
|
+
|
|
89
|
+
// Font family — applies to all components + typography utility classes
|
|
90
|
+
"--font-sans": fontFamily,
|
|
91
|
+
"--font-family-sans": fontFamily,
|
|
92
|
+
|
|
93
|
+
// Legacy compat (used by existing WealthX apps)
|
|
94
|
+
"--theme-primary": primary,
|
|
95
|
+
"--theme-secondary": secondary,
|
|
96
|
+
};
|
|
97
|
+
}, [primary, secondary, fontFamily, injectCssVariables]);
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<ThemeVarsContext.Provider value={vars}>
|
|
101
|
+
<div data-wealthx-theme style={vars as React.CSSProperties}>
|
|
102
|
+
{children}
|
|
103
|
+
</div>
|
|
104
|
+
</ThemeVarsContext.Provider>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Build CSS variables object for SSR or static contexts.
|
|
110
|
+
* Returns a flat Record\<string, string\> that can be serialized to a \<style\> tag.
|
|
111
|
+
*/
|
|
112
|
+
export function buildCssVariables(options: {
|
|
113
|
+
primary?: string;
|
|
114
|
+
secondary?: string;
|
|
115
|
+
fontFamily?: string;
|
|
116
|
+
}): Record<string, string> {
|
|
117
|
+
const primary = options.primary ?? DEFAULT_PRIMARY;
|
|
118
|
+
const secondary = options.secondary ?? DEFAULT_SECONDARY;
|
|
119
|
+
const fontFamily = options.fontFamily ?? DEFAULT_FONT;
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
"--primary": hexToOklch(primary),
|
|
123
|
+
"--primary-foreground": hexToOklch(getContrastText(primary)),
|
|
124
|
+
"--ring": hexToOklch(primary),
|
|
125
|
+
"--sidebar-primary": hexToOklch(primary),
|
|
126
|
+
"--sidebar-primary-foreground": hexToOklch(getContrastText(primary)),
|
|
127
|
+
"--brand-secondary": hexToOklch(secondary),
|
|
128
|
+
"--brand-secondary-foreground": hexToOklch(getContrastText(secondary)),
|
|
129
|
+
"--font-sans": fontFamily,
|
|
130
|
+
"--font-family-sans": fontFamily,
|
|
131
|
+
"--theme-primary": primary,
|
|
132
|
+
"--theme-secondary": secondary,
|
|
133
|
+
};
|
|
134
|
+
}
|