@ponti-studios/ui 0.3.0 → 0.4.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/README.md +14 -0
- package/package.json +45 -24
- package/src/animations.css +92 -15
- package/src/components/data-display/accordion.tsx +15 -6
- package/src/components/data-display/filter-chip.stories.tsx +38 -0
- package/src/components/data-display/filter-chip.tsx +9 -3
- package/src/components/data-display/sort-controls.tsx +1 -1
- package/src/components/data-display/table.tsx +3 -7
- package/src/components/feedback/empty-state.tsx +16 -6
- package/src/components/feedback/file-upload-status-badge.tsx +1 -1
- package/src/components/feedback/progress.tsx +2 -2
- package/src/components/feedback/update-guard.tsx +1 -1
- package/src/components/forms/calendar.tsx +6 -3
- package/src/components/forms/field.tsx +3 -3
- package/src/components/forms/input.tsx +1 -1
- package/src/components/forms/passkey-management.tsx +2 -2
- package/src/components/forms/radio-group.tsx +1 -1
- package/src/components/forms/select.tsx +11 -4
- package/src/components/forms/slider.tsx +16 -4
- package/src/components/forms/stepper.tsx +1 -1
- package/src/components/forms/textarea.tsx +1 -1
- package/src/components/layout/section-intro.tsx +1 -1
- package/src/components/navigation/app-navigation.tsx +4 -4
- package/src/components/navigation/pagination-controls.tsx +2 -2
- package/src/components/navigation/tabs.tsx +1 -1
- package/src/components/overlays/alert-dialog.tsx +30 -8
- package/src/components/overlays/command.tsx +123 -20
- package/src/components/overlays/dialog.tsx +4 -4
- package/src/components/overlays/dropdown-menu.tsx +137 -21
- package/src/components/overlays/popover.tsx +14 -3
- package/src/components/overlays/sheet.tsx +24 -8
- package/src/components/primitives/avatar.tsx +6 -2
- package/src/components/primitives/badge.tsx +5 -2
- package/src/components/primitives/button.tsx +16 -18
- package/src/components/primitives/card.stories.tsx +2 -2
- package/src/components/primitives/card.tsx +2 -5
- package/src/constants/chart-colors.ts +2 -2
- package/src/index.ts +1 -1
- package/src/native/index.ts +11 -0
- package/src/native/make-styles.ts +32 -0
- package/src/native/shadows.ts +38 -0
- package/src/native/use-theme-colors.ts +19 -0
- package/src/styles.css +154 -66
- package/src/tokens/chart-colors.stories.tsx +189 -0
- package/src/tokens/color-systems.stories.tsx +21 -3
- package/src/tokens/css-vars.ts +15 -0
- package/src/tokens/foundations.stories.tsx +66 -16
- package/src/tokens/generated/dark.colors.generated.ts +24 -50
- package/src/tokens/generated/dark.css +24 -48
- package/src/tokens/generated/foundations.css +2 -2
- package/src/tokens/generated/foundations.generated.ts +14 -6
- package/src/tokens/generated/light.colors.generated.ts +24 -50
- package/src/tokens/generated/light.css +24 -48
- package/src/tokens/index.ts +12 -15
- package/src/tokens/layout.stories.tsx +40 -3
- package/src/tokens.css +3 -0
- package/dist/index.js +0 -2508
- package/dist/index.js.map +0 -1
|
@@ -12,11 +12,17 @@ function DropdownMenuPortal(props: React.ComponentProps<typeof DropdownMenuPrimi
|
|
|
12
12
|
return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
function DropdownMenuTrigger({
|
|
15
|
+
function DropdownMenuTrigger({
|
|
16
|
+
asChild,
|
|
17
|
+
children,
|
|
18
|
+
...props
|
|
19
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger> & { asChild?: boolean }) {
|
|
16
20
|
return (
|
|
17
21
|
<DropdownMenuPrimitive.Trigger
|
|
18
22
|
data-slot="dropdown-menu-trigger"
|
|
19
|
-
{...(asChild && React.isValidElement(children)
|
|
23
|
+
{...(asChild && React.isValidElement(children)
|
|
24
|
+
? { render: children as React.ReactElement }
|
|
25
|
+
: {})}
|
|
20
26
|
{...props}
|
|
21
27
|
>
|
|
22
28
|
{!asChild && children}
|
|
@@ -24,7 +30,12 @@ function DropdownMenuTrigger({ asChild, children, ...props }: React.ComponentPro
|
|
|
24
30
|
);
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
function DropdownMenuContent({
|
|
33
|
+
function DropdownMenuContent({
|
|
34
|
+
className,
|
|
35
|
+
sideOffset = 4,
|
|
36
|
+
children,
|
|
37
|
+
...props
|
|
38
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Popup> & { sideOffset?: number }) {
|
|
28
39
|
return (
|
|
29
40
|
<DropdownMenuPrimitive.Portal>
|
|
30
41
|
<DropdownMenuPrimitive.Positioner sideOffset={sideOffset}>
|
|
@@ -47,54 +58,159 @@ function DropdownMenuGroup(props: React.ComponentProps<typeof DropdownMenuPrimit
|
|
|
47
58
|
return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
|
|
48
59
|
}
|
|
49
60
|
|
|
50
|
-
function DropdownMenuLabel({
|
|
51
|
-
|
|
61
|
+
function DropdownMenuLabel({
|
|
62
|
+
className,
|
|
63
|
+
inset,
|
|
64
|
+
...props
|
|
65
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.GroupLabel> & { inset?: boolean }) {
|
|
66
|
+
return (
|
|
67
|
+
<DropdownMenuPrimitive.GroupLabel
|
|
68
|
+
data-slot="dropdown-menu-label"
|
|
69
|
+
data-inset={inset}
|
|
70
|
+
className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
52
74
|
}
|
|
53
75
|
|
|
54
|
-
function DropdownMenuItem({
|
|
55
|
-
|
|
76
|
+
function DropdownMenuItem({
|
|
77
|
+
className,
|
|
78
|
+
inset,
|
|
79
|
+
variant = "default",
|
|
80
|
+
...props
|
|
81
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
82
|
+
inset?: boolean;
|
|
83
|
+
variant?: "default" | "destructive";
|
|
84
|
+
}) {
|
|
85
|
+
return (
|
|
86
|
+
<DropdownMenuPrimitive.Item
|
|
87
|
+
data-slot="dropdown-menu-item"
|
|
88
|
+
data-inset={inset}
|
|
89
|
+
data-variant={variant}
|
|
90
|
+
className={cn(
|
|
91
|
+
"data-highlighted:bg-muted data-highlighted:text-muted-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/10 relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
92
|
+
className,
|
|
93
|
+
)}
|
|
94
|
+
{...props}
|
|
95
|
+
/>
|
|
96
|
+
);
|
|
56
97
|
}
|
|
57
98
|
|
|
58
|
-
function DropdownMenuCheckboxItem({
|
|
99
|
+
function DropdownMenuCheckboxItem({
|
|
100
|
+
className,
|
|
101
|
+
children,
|
|
102
|
+
checked,
|
|
103
|
+
...props
|
|
104
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
|
59
105
|
return (
|
|
60
|
-
<DropdownMenuPrimitive.CheckboxItem
|
|
61
|
-
|
|
106
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
107
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
108
|
+
className={cn(
|
|
109
|
+
"data-highlighted:bg-muted data-highlighted:text-muted-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
110
|
+
className,
|
|
111
|
+
)}
|
|
112
|
+
checked={checked}
|
|
113
|
+
{...props}
|
|
114
|
+
>
|
|
115
|
+
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
|
116
|
+
<DropdownMenuPrimitive.CheckboxItemIndicator>
|
|
117
|
+
<CheckIcon className="size-4" />
|
|
118
|
+
</DropdownMenuPrimitive.CheckboxItemIndicator>
|
|
119
|
+
</span>
|
|
62
120
|
{children}
|
|
63
121
|
</DropdownMenuPrimitive.CheckboxItem>
|
|
64
122
|
);
|
|
65
123
|
}
|
|
66
124
|
|
|
67
|
-
function DropdownMenuRadioGroup(
|
|
125
|
+
function DropdownMenuRadioGroup(
|
|
126
|
+
props: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>,
|
|
127
|
+
) {
|
|
68
128
|
return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;
|
|
69
129
|
}
|
|
70
130
|
|
|
71
|
-
function DropdownMenuRadioItem({
|
|
131
|
+
function DropdownMenuRadioItem({
|
|
132
|
+
className,
|
|
133
|
+
children,
|
|
134
|
+
...props
|
|
135
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
|
72
136
|
return (
|
|
73
|
-
<DropdownMenuPrimitive.RadioItem
|
|
74
|
-
|
|
137
|
+
<DropdownMenuPrimitive.RadioItem
|
|
138
|
+
data-slot="dropdown-menu-radio-item"
|
|
139
|
+
className={cn(
|
|
140
|
+
"data-highlighted:bg-muted data-highlighted:text-muted-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
141
|
+
className,
|
|
142
|
+
)}
|
|
143
|
+
{...props}
|
|
144
|
+
>
|
|
145
|
+
<span className="absolute left-2 flex size-3.5 items-center justify-center">
|
|
146
|
+
<DropdownMenuPrimitive.RadioItemIndicator>
|
|
147
|
+
<CircleIcon className="size-2 fill-current" />
|
|
148
|
+
</DropdownMenuPrimitive.RadioItemIndicator>
|
|
149
|
+
</span>
|
|
75
150
|
{children}
|
|
76
151
|
</DropdownMenuPrimitive.RadioItem>
|
|
77
152
|
);
|
|
78
153
|
}
|
|
79
154
|
|
|
80
|
-
function DropdownMenuSeparator({
|
|
81
|
-
|
|
155
|
+
function DropdownMenuSeparator({
|
|
156
|
+
className,
|
|
157
|
+
...props
|
|
158
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
|
159
|
+
return (
|
|
160
|
+
<DropdownMenuPrimitive.Separator
|
|
161
|
+
data-slot="dropdown-menu-separator"
|
|
162
|
+
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
163
|
+
{...props}
|
|
164
|
+
/>
|
|
165
|
+
);
|
|
82
166
|
}
|
|
83
167
|
|
|
84
168
|
function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
|
|
85
|
-
return
|
|
169
|
+
return (
|
|
170
|
+
<span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />
|
|
171
|
+
);
|
|
86
172
|
}
|
|
87
173
|
|
|
88
174
|
function DropdownMenuSub(props: React.ComponentProps<typeof DropdownMenuPrimitive.SubmenuRoot>) {
|
|
89
175
|
return <DropdownMenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />;
|
|
90
176
|
}
|
|
91
177
|
|
|
92
|
-
function DropdownMenuSubTrigger({
|
|
93
|
-
|
|
178
|
+
function DropdownMenuSubTrigger({
|
|
179
|
+
className,
|
|
180
|
+
inset,
|
|
181
|
+
children,
|
|
182
|
+
...props
|
|
183
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubmenuTrigger> & { inset?: boolean }) {
|
|
184
|
+
return (
|
|
185
|
+
<DropdownMenuPrimitive.SubmenuTrigger
|
|
186
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
187
|
+
data-inset={inset}
|
|
188
|
+
className={cn(
|
|
189
|
+
"data-highlighted:bg-muted data-highlighted:text-muted-foreground data-open:bg-muted data-open:text-muted-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8",
|
|
190
|
+
className,
|
|
191
|
+
)}
|
|
192
|
+
{...props}
|
|
193
|
+
>
|
|
194
|
+
{children}
|
|
195
|
+
<ChevronRightIcon className="ml-auto size-4" />
|
|
196
|
+
</DropdownMenuPrimitive.SubmenuTrigger>
|
|
197
|
+
);
|
|
94
198
|
}
|
|
95
199
|
|
|
96
|
-
function DropdownMenuSubContent({
|
|
97
|
-
|
|
200
|
+
function DropdownMenuSubContent({
|
|
201
|
+
className,
|
|
202
|
+
...props
|
|
203
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Popup>) {
|
|
204
|
+
return (
|
|
205
|
+
<DropdownMenuPrimitive.Popup
|
|
206
|
+
data-slot="dropdown-menu-sub-content"
|
|
207
|
+
className={cn(
|
|
208
|
+
"bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg outline-none",
|
|
209
|
+
className,
|
|
210
|
+
)}
|
|
211
|
+
{...props}
|
|
212
|
+
/>
|
|
213
|
+
);
|
|
98
214
|
}
|
|
99
215
|
|
|
100
216
|
export {
|
|
@@ -7,11 +7,17 @@ function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root
|
|
|
7
7
|
return <PopoverPrimitive.Root data-slot="popover" {...props} />;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
function PopoverTrigger({
|
|
10
|
+
function PopoverTrigger({
|
|
11
|
+
asChild,
|
|
12
|
+
children,
|
|
13
|
+
...props
|
|
14
|
+
}: React.ComponentProps<typeof PopoverPrimitive.Trigger> & { asChild?: boolean }) {
|
|
11
15
|
return (
|
|
12
16
|
<PopoverPrimitive.Trigger
|
|
13
17
|
data-slot="popover-trigger"
|
|
14
|
-
{...(asChild && React.isValidElement(children)
|
|
18
|
+
{...(asChild && React.isValidElement(children)
|
|
19
|
+
? { render: children as React.ReactElement }
|
|
20
|
+
: {})}
|
|
15
21
|
{...props}
|
|
16
22
|
>
|
|
17
23
|
{!asChild && children}
|
|
@@ -35,7 +41,12 @@ function PopoverContent({
|
|
|
35
41
|
}) {
|
|
36
42
|
return (
|
|
37
43
|
<PopoverPrimitive.Portal>
|
|
38
|
-
<PopoverPrimitive.Positioner
|
|
44
|
+
<PopoverPrimitive.Positioner
|
|
45
|
+
align={align}
|
|
46
|
+
side={side}
|
|
47
|
+
sideOffset={sideOffset}
|
|
48
|
+
collisionAvoidance={avoidCollisions ? undefined : { side: "none", align: "none" }}
|
|
49
|
+
>
|
|
39
50
|
<PopoverPrimitive.Popup
|
|
40
51
|
data-slot="popover-content"
|
|
41
52
|
className={cn(
|
|
@@ -8,10 +8,16 @@ import { cn } from "../../lib/utils";
|
|
|
8
8
|
|
|
9
9
|
const Sheet = SheetPrimitive.Root;
|
|
10
10
|
|
|
11
|
-
function SheetTrigger({
|
|
11
|
+
function SheetTrigger({
|
|
12
|
+
asChild,
|
|
13
|
+
children,
|
|
14
|
+
...props
|
|
15
|
+
}: React.ComponentProps<typeof SheetPrimitive.Trigger> & { asChild?: boolean }) {
|
|
12
16
|
return (
|
|
13
17
|
<SheetPrimitive.Trigger
|
|
14
|
-
{...(asChild && React.isValidElement(children)
|
|
18
|
+
{...(asChild && React.isValidElement(children)
|
|
19
|
+
? { render: children as React.ReactElement }
|
|
20
|
+
: {})}
|
|
15
21
|
{...props}
|
|
16
22
|
>
|
|
17
23
|
{!asChild && children}
|
|
@@ -19,10 +25,16 @@ function SheetTrigger({ asChild, children, ...props }: React.ComponentProps<type
|
|
|
19
25
|
);
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
function SheetClose({
|
|
28
|
+
function SheetClose({
|
|
29
|
+
asChild,
|
|
30
|
+
children,
|
|
31
|
+
...props
|
|
32
|
+
}: React.ComponentProps<typeof SheetPrimitive.Close> & { asChild?: boolean }) {
|
|
23
33
|
return (
|
|
24
34
|
<SheetPrimitive.Close
|
|
25
|
-
{...(asChild && React.isValidElement(children)
|
|
35
|
+
{...(asChild && React.isValidElement(children)
|
|
36
|
+
? { render: children as React.ReactElement }
|
|
37
|
+
: {})}
|
|
26
38
|
{...props}
|
|
27
39
|
>
|
|
28
40
|
{!asChild && children}
|
|
@@ -38,7 +50,7 @@ const SheetOverlay = React.forwardRef<
|
|
|
38
50
|
>(({ className, ...props }, ref) => (
|
|
39
51
|
<SheetPrimitive.Backdrop
|
|
40
52
|
className={cn(
|
|
41
|
-
"
|
|
53
|
+
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/80 transition-opacity",
|
|
42
54
|
className,
|
|
43
55
|
)}
|
|
44
56
|
{...props}
|
|
@@ -59,7 +71,7 @@ const SheetContent = React.forwardRef<
|
|
|
59
71
|
<SheetPortal>
|
|
60
72
|
<SheetOverlay />
|
|
61
73
|
<SheetPrimitive.Popup ref={ref} className={cn(sheetClassName, className)} {...props}>
|
|
62
|
-
<SheetPrimitive.Close className="text-muted-foreground hover:bg-
|
|
74
|
+
<SheetPrimitive.Close className="text-muted-foreground hover:bg-muted hover:text-muted-foreground focus-visible:outline-ring absolute top-3 right-3 inline-flex size-9 items-center justify-center rounded-md border border-transparent transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none md:top-4 md:right-4">
|
|
63
75
|
<X className="size-4.5" />
|
|
64
76
|
<span className="sr-only">Close</span>
|
|
65
77
|
</SheetPrimitive.Close>
|
|
@@ -86,7 +98,11 @@ const SheetTitle = React.forwardRef<
|
|
|
86
98
|
React.ComponentRef<typeof SheetPrimitive.Title>,
|
|
87
99
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
|
88
100
|
>(({ className, ...props }, ref) => (
|
|
89
|
-
<SheetPrimitive.Title
|
|
101
|
+
<SheetPrimitive.Title
|
|
102
|
+
ref={ref}
|
|
103
|
+
className={cn("text-lg font-semibold tracking-tight", className)}
|
|
104
|
+
{...props}
|
|
105
|
+
/>
|
|
90
106
|
));
|
|
91
107
|
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
|
92
108
|
|
|
@@ -96,7 +112,7 @@ const SheetDescription = React.forwardRef<
|
|
|
96
112
|
>(({ className, ...props }, ref) => (
|
|
97
113
|
<SheetPrimitive.Description
|
|
98
114
|
ref={ref}
|
|
99
|
-
className={cn("max-w-prose text-sm
|
|
115
|
+
className={cn("text-muted-foreground max-w-prose text-sm", className)}
|
|
100
116
|
{...props}
|
|
101
117
|
/>
|
|
102
118
|
));
|
|
@@ -46,7 +46,11 @@ function Avatar({
|
|
|
46
46
|
<AvatarContext.Provider value={{ size }}>
|
|
47
47
|
<AvatarPrimitive.Root
|
|
48
48
|
data-slot="avatar"
|
|
49
|
-
className={cn(
|
|
49
|
+
className={cn(
|
|
50
|
+
"relative inline-flex shrink-0 rounded-full select-none",
|
|
51
|
+
avatarSize[size],
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
50
54
|
{...props}
|
|
51
55
|
>
|
|
52
56
|
{children}
|
|
@@ -54,7 +58,7 @@ function Avatar({
|
|
|
54
58
|
<span
|
|
55
59
|
data-slot="avatar-badge"
|
|
56
60
|
className={cn(
|
|
57
|
-
"bg-
|
|
61
|
+
"bg-primary ring-background absolute right-0 bottom-0 z-10 rounded-full ring-2",
|
|
58
62
|
badgeDotSize[size],
|
|
59
63
|
)}
|
|
60
64
|
/>
|
|
@@ -4,16 +4,19 @@ import * as React from "react";
|
|
|
4
4
|
import { cn } from "../../lib/utils";
|
|
5
5
|
|
|
6
6
|
const badgeVariants = cva(
|
|
7
|
-
"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden whitespace-nowrap rounded border border-transparent px-1.5 py-px text-xs font-medium [&>svg]:size-3 [&>svg]:pointer-events-none aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
7
|
+
"state-settle inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden whitespace-nowrap rounded border border-transparent px-1.5 py-px text-xs font-medium [&>svg]:size-3 [&>svg]:pointer-events-none aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
8
8
|
{
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
11
11
|
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
12
12
|
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/80",
|
|
13
13
|
destructive: "bg-destructive text-destructive-foreground [a&]:hover:bg-destructive/90",
|
|
14
|
-
outline: "border-border text-foreground [a&]:hover:bg-
|
|
14
|
+
outline: "border-border text-foreground [a&]:hover:bg-muted",
|
|
15
15
|
ghost: "[a&]:[a&]: [a&]: [a&]:",
|
|
16
16
|
link: "text-foreground underline-offset-4 [a&]:hover:underline",
|
|
17
|
+
/** Quiet metadata marker for real reference IDs (case/appointment/feed
|
|
18
|
+
* numbers) — never for decorative sequence markers. */
|
|
19
|
+
ref: "ref-tag border-transparent bg-transparent px-0 py-0 font-normal",
|
|
17
20
|
},
|
|
18
21
|
},
|
|
19
22
|
defaultVariants: {
|
|
@@ -6,28 +6,22 @@ import { cn } from "../../lib/utils";
|
|
|
6
6
|
import { Spinner } from "../feedback/spinner";
|
|
7
7
|
|
|
8
8
|
const buttonVariants = cva(
|
|
9
|
-
"inline-flex
|
|
9
|
+
"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md border border-transparent font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
10
10
|
{
|
|
11
11
|
variants: {
|
|
12
12
|
variant: {
|
|
13
|
-
default:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
19
|
-
secondary:
|
|
20
|
-
"border-input bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
21
|
-
ghost:
|
|
22
|
-
"border-transparent bg-transparent hover:bg-accent hover:text-accent-foreground",
|
|
13
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
14
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
15
|
+
outline: "border-input bg-background hover:bg-muted hover:text-muted-foreground",
|
|
16
|
+
secondary: "border-input bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
17
|
+
ghost: "border-transparent bg-transparent hover:bg-muted hover:text-muted-foreground",
|
|
23
18
|
link: "border-transparent bg-transparent px-0 text-primary underline-offset-4 hover:underline",
|
|
24
19
|
},
|
|
25
20
|
size: {
|
|
26
|
-
sm: "min-h-
|
|
27
|
-
md: "min-h-
|
|
28
|
-
lg: "min-h-
|
|
29
|
-
|
|
30
|
-
icon: "size-11 rounded-md p-0",
|
|
21
|
+
sm: "min-h-8 px-2.5 text-xs",
|
|
22
|
+
md: "min-h-9 px-3 text-sm",
|
|
23
|
+
lg: "min-h-10 px-4 text-base",
|
|
24
|
+
icon: "size-9 rounded-md p-0",
|
|
31
25
|
},
|
|
32
26
|
},
|
|
33
27
|
defaultVariants: {
|
|
@@ -65,7 +59,9 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
65
59
|
const renderedChild = React.cloneElement(child, {
|
|
66
60
|
children: isLoading ? (
|
|
67
61
|
<>
|
|
68
|
-
<span className="opacity-0" aria-hidden="true">
|
|
62
|
+
<span className="opacity-0" aria-hidden="true">
|
|
63
|
+
{child.props.children}
|
|
64
|
+
</span>
|
|
69
65
|
<span className="absolute inset-0 inline-flex items-center justify-center">
|
|
70
66
|
<Spinner size="sm" aria-hidden="true" />
|
|
71
67
|
</span>
|
|
@@ -105,7 +101,9 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
105
101
|
>
|
|
106
102
|
{isLoading ? (
|
|
107
103
|
<>
|
|
108
|
-
<span className="opacity-0" aria-hidden="true">
|
|
104
|
+
<span className="opacity-0" aria-hidden="true">
|
|
105
|
+
{children}
|
|
106
|
+
</span>
|
|
109
107
|
<span className="absolute inset-0 inline-flex items-center justify-center">
|
|
110
108
|
<Spinner size="sm" aria-hidden="true" />
|
|
111
109
|
</span>
|
|
@@ -21,7 +21,7 @@ export const Default: Story = {
|
|
|
21
21
|
<CardDescription>A short description of the card's content.</CardDescription>
|
|
22
22
|
</CardHeader>
|
|
23
23
|
<CardContent>
|
|
24
|
-
<p className="text-
|
|
24
|
+
<p className="text-muted-foreground text-sm">
|
|
25
25
|
Card body content goes here. It can be any React node.
|
|
26
26
|
</p>
|
|
27
27
|
</CardContent>
|
|
@@ -47,7 +47,7 @@ export const Minimal: Story = {
|
|
|
47
47
|
render: () => (
|
|
48
48
|
<Card className="w-80">
|
|
49
49
|
<CardContent>
|
|
50
|
-
<p className="text-
|
|
50
|
+
<p className="text-muted-foreground text-sm">Minimal card with only content.</p>
|
|
51
51
|
</CardContent>
|
|
52
52
|
</Card>
|
|
53
53
|
),
|
|
@@ -6,10 +6,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
6
6
|
return (
|
|
7
7
|
<div
|
|
8
8
|
data-slot="card"
|
|
9
|
-
className={cn(
|
|
10
|
-
"bg-card text-card-foreground flex flex-col rounded-xl border",
|
|
11
|
-
className,
|
|
12
|
-
)}
|
|
9
|
+
className={cn("bg-card text-card-foreground flex flex-col rounded-xl border", className)}
|
|
13
10
|
{...props}
|
|
14
11
|
/>
|
|
15
12
|
);
|
|
@@ -36,7 +33,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
36
33
|
return (
|
|
37
34
|
<div
|
|
38
35
|
data-slot="card-description"
|
|
39
|
-
className={cn("text-
|
|
36
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
40
37
|
{...props}
|
|
41
38
|
/>
|
|
42
39
|
);
|
|
@@ -14,14 +14,14 @@ export const CHART_COLORS = {
|
|
|
14
14
|
negative: "var(--color-chart-negative)",
|
|
15
15
|
neutral: "var(--color-chart-neutral)",
|
|
16
16
|
|
|
17
|
-
background: "var(--color-
|
|
17
|
+
background: "var(--color-background)",
|
|
18
18
|
grid: "var(--color-border)",
|
|
19
19
|
|
|
20
20
|
axis: "var(--color-muted-foreground)",
|
|
21
21
|
label: "var(--color-muted-foreground)",
|
|
22
22
|
|
|
23
23
|
tooltip: {
|
|
24
|
-
background: "var(--color-
|
|
24
|
+
background: "var(--color-card)",
|
|
25
25
|
text: "var(--color-foreground)",
|
|
26
26
|
border: "var(--color-border)",
|
|
27
27
|
},
|
package/src/index.ts
CHANGED
|
@@ -9,9 +9,9 @@ export { normalizeOtp, OTP_LENGTH } from "./lib/auth";
|
|
|
9
9
|
export { copyToClipboard } from "./lib/clipboard";
|
|
10
10
|
export { createMemoryStorage } from "./lib/create-memory-storage";
|
|
11
11
|
export { cn } from "./lib/utils";
|
|
12
|
+
export { colors } from "./tokens/css-vars";
|
|
12
13
|
export {
|
|
13
14
|
animations,
|
|
14
|
-
colors,
|
|
15
15
|
colorThemes,
|
|
16
16
|
colorTokenNames,
|
|
17
17
|
containers,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native entry point.
|
|
3
|
+
*
|
|
4
|
+
* Kept as its own subpath (`@ponti-studios/ui/native`) because it is the only
|
|
5
|
+
* part of the package that imports `react-native`. Nothing here may import web
|
|
6
|
+
* code, so web consumers never pull React Native into their graph and native
|
|
7
|
+
* consumers never pull in react-dom.
|
|
8
|
+
*/
|
|
9
|
+
export { createMakeStyles, type NamedStyles } from "./make-styles";
|
|
10
|
+
export { nativeShadows, type NativeShadowLayer } from "./shadows";
|
|
11
|
+
export { useColorMode, useThemeColors } from "./use-theme-colors";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ImageStyle, TextStyle, ViewStyle } from "react-native";
|
|
2
|
+
|
|
3
|
+
export type NamedStyles = Record<string, ViewStyle | TextStyle | ImageStyle>;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds a `makeStyles` helper bound to a consuming app's own theme hook.
|
|
7
|
+
*
|
|
8
|
+
* The theme *shape* stays owned by the app — this package supplies only the
|
|
9
|
+
* mechanism — so `Theme` is a type parameter rather than a type exported from
|
|
10
|
+
* here. An app calls this once with its `useTheme` and uses the result
|
|
11
|
+
* everywhere:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* export const makeStyles = createMakeStyles(useTheme);
|
|
15
|
+
*
|
|
16
|
+
* const useStyles = makeStyles((theme) => ({
|
|
17
|
+
* card: { backgroundColor: theme.colors.card },
|
|
18
|
+
* }));
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* The returned `useStyles` is a hook: it calls `useTheme` on every render, so
|
|
22
|
+
* styles re-resolve when the color mode changes. Styles are returned as plain
|
|
23
|
+
* objects rather than passed through `StyleSheet.create`, since they are
|
|
24
|
+
* rebuilt per render and React Native accepts plain style objects directly.
|
|
25
|
+
*/
|
|
26
|
+
export function createMakeStyles<Theme>(useTheme: () => Theme) {
|
|
27
|
+
return function makeStyles<T extends NamedStyles>(factory: (theme: Theme) => T & NamedStyles) {
|
|
28
|
+
return function useStyles(): T {
|
|
29
|
+
return factory(useTheme());
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { shadows, type ShadowToken } from "../tokens";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One layer of a shadow in the shape React Native's `boxShadow` expects.
|
|
5
|
+
*
|
|
6
|
+
* The shared shadow tokens are authored for CSS, where each dimension carries
|
|
7
|
+
* its own unit. React Native takes bare numbers (density-independent pixels),
|
|
8
|
+
* so the unit is dropped here rather than at every call site.
|
|
9
|
+
*/
|
|
10
|
+
export interface NativeShadowLayer {
|
|
11
|
+
color: string;
|
|
12
|
+
offsetX: number;
|
|
13
|
+
offsetY: number;
|
|
14
|
+
blurRadius: number;
|
|
15
|
+
spreadDistance: number;
|
|
16
|
+
inset: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The shared shadow scale translated to React Native's `boxShadow` shape.
|
|
21
|
+
*
|
|
22
|
+
* Every token maps to an array of layers, including `none`, which is a single
|
|
23
|
+
* fully transparent layer rather than an empty array — that keeps the value
|
|
24
|
+
* assignable wherever a real shadow is expected.
|
|
25
|
+
*/
|
|
26
|
+
export const nativeShadows = Object.fromEntries(
|
|
27
|
+
Object.entries(shadows).map(([name, layers]) => [
|
|
28
|
+
name,
|
|
29
|
+
layers.map((layer) => ({
|
|
30
|
+
color: layer.color,
|
|
31
|
+
offsetX: layer.offsetX.value,
|
|
32
|
+
offsetY: layer.offsetY.value,
|
|
33
|
+
blurRadius: layer.blur.value,
|
|
34
|
+
spreadDistance: layer.spread.value,
|
|
35
|
+
inset: false,
|
|
36
|
+
})),
|
|
37
|
+
]),
|
|
38
|
+
) as Record<ShadowToken, NativeShadowLayer[]>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useColorScheme } from "react-native";
|
|
2
|
+
|
|
3
|
+
import { colorThemes, type ColorMode, type ColorTheme } from "../tokens";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolves the active color mode from the OS appearance setting.
|
|
7
|
+
*
|
|
8
|
+
* React Native's `useColorScheme` reports `null` before the appearance module
|
|
9
|
+
* has settled, so anything that isn't explicitly "dark" resolves to light —
|
|
10
|
+
* callers always receive a concrete mode rather than having to handle null.
|
|
11
|
+
*/
|
|
12
|
+
export function useColorMode(): ColorMode {
|
|
13
|
+
return useColorScheme() === "dark" ? "dark" : "light";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The full color token set for the active OS appearance. */
|
|
17
|
+
export function useThemeColors(): ColorTheme {
|
|
18
|
+
return colorThemes[useColorMode()];
|
|
19
|
+
}
|