@brightweblabs/ui 0.4.0 → 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.
- package/package.json +43 -7
- package/src/components/action.tsx +12 -0
- package/src/components/activity-message.tsx +1 -1
- package/src/components/alert-dialog.tsx +4 -4
- package/src/components/avatar.tsx +80 -0
- package/src/components/badge.tsx +20 -30
- package/src/components/button-variants.ts +47 -0
- package/src/components/button.tsx +102 -53
- package/src/components/calendar.tsx +5 -4
- package/src/components/chart.tsx +4 -4
- package/src/components/dropdown-menu.tsx +5 -5
- package/src/components/empty-state.tsx +26 -0
- package/src/components/field.tsx +2 -2
- package/src/components/initials-avatar.tsx +30 -0
- package/src/components/input.tsx +1 -1
- package/src/components/kpi-breakdown-bar.tsx +71 -0
- package/src/components/label.tsx +1 -1
- package/src/components/pagination.tsx +2 -3
- package/src/components/password-input.tsx +10 -43
- package/src/components/password-strength.tsx +9 -10
- package/src/components/phone-input.tsx +114 -0
- package/src/components/popover.tsx +1 -1
- package/src/components/role-badge.tsx +34 -0
- package/src/components/search-field.tsx +63 -0
- package/src/components/section-heading.tsx +28 -0
- package/src/components/sheet.tsx +34 -5
- package/src/components/skeleton-table.tsx +27 -0
- package/src/components/skeleton.tsx +45 -0
- package/src/components/sonner.tsx +2 -1
- package/src/components/stat-tile.tsx +30 -0
- package/src/components/status-pill.tsx +37 -0
- package/src/components/surface-card.tsx +11 -0
- package/src/components/table-pagination.tsx +107 -0
- package/src/components/table.tsx +7 -4
- package/src/components/theme-provider.tsx +130 -0
- package/src/components/tooltip.tsx +2 -2
- package/src/index.ts +22 -1
- package/src/lib/patterns.ts +52 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightweblabs/ui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"files": [
|
|
@@ -17,35 +17,71 @@
|
|
|
17
17
|
},
|
|
18
18
|
"exports": {
|
|
19
19
|
".": "./src/index.ts",
|
|
20
|
+
"./action": "./src/components/action.tsx",
|
|
20
21
|
"./activity-format": "./src/lib/activity-format.ts",
|
|
21
22
|
"./activity-message": "./src/components/activity-message.tsx",
|
|
22
23
|
"./alert-dialog": "./src/components/alert-dialog.tsx",
|
|
24
|
+
"./avatar": "./src/components/avatar.tsx",
|
|
23
25
|
"./badge": "./src/components/badge.tsx",
|
|
26
|
+
"./breadcrumb": "./src/components/breadcrumb.tsx",
|
|
24
27
|
"./button": "./src/components/button.tsx",
|
|
28
|
+
"./button-variants": "./src/components/button-variants.ts",
|
|
29
|
+
"./calendar": "./src/components/calendar.tsx",
|
|
30
|
+
"./card": "./src/components/card.tsx",
|
|
31
|
+
"./chart": "./src/components/chart.tsx",
|
|
25
32
|
"./dropdown-menu": "./src/components/dropdown-menu.tsx",
|
|
33
|
+
"./empty-state": "./src/components/empty-state.tsx",
|
|
34
|
+
"./field": "./src/components/field.tsx",
|
|
26
35
|
"./input": "./src/components/input.tsx",
|
|
36
|
+
"./initials-avatar": "./src/components/initials-avatar.tsx",
|
|
37
|
+
"./kpi-breakdown-bar": "./src/components/kpi-breakdown-bar.tsx",
|
|
38
|
+
"./label": "./src/components/label.tsx",
|
|
39
|
+
"./pagination": "./src/components/pagination.tsx",
|
|
40
|
+
"./password-input": "./src/components/password-input.tsx",
|
|
41
|
+
"./password-strength": "./src/components/password-strength.tsx",
|
|
42
|
+
"./phone-input": "./src/components/phone-input.tsx",
|
|
43
|
+
"./popover": "./src/components/popover.tsx",
|
|
44
|
+
"./role-badge": "./src/components/role-badge.tsx",
|
|
45
|
+
"./search-field": "./src/components/search-field.tsx",
|
|
46
|
+
"./section-heading": "./src/components/section-heading.tsx",
|
|
47
|
+
"./separator": "./src/components/separator.tsx",
|
|
48
|
+
"./sheet": "./src/components/sheet.tsx",
|
|
49
|
+
"./skeleton": "./src/components/skeleton.tsx",
|
|
50
|
+
"./skeleton-table": "./src/components/skeleton-table.tsx",
|
|
51
|
+
"./stat-tile": "./src/components/stat-tile.tsx",
|
|
52
|
+
"./status-pill": "./src/components/status-pill.tsx",
|
|
53
|
+
"./surface-card": "./src/components/surface-card.tsx",
|
|
54
|
+
"./sonner": "./src/components/sonner.tsx",
|
|
27
55
|
"./table": "./src/components/table.tsx",
|
|
56
|
+
"./table-pagination": "./src/components/table-pagination.tsx",
|
|
57
|
+
"./theme-provider": "./src/components/theme-provider.tsx",
|
|
28
58
|
"./tooltip": "./src/components/tooltip.tsx"
|
|
29
59
|
},
|
|
30
60
|
"dependencies": {
|
|
61
|
+
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
62
|
+
"@radix-ui/react-avatar": "^1.1.10",
|
|
31
63
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
32
64
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
33
65
|
"@radix-ui/react-label": "^2.1.8",
|
|
34
66
|
"@radix-ui/react-popover": "^1.1.15",
|
|
35
67
|
"@radix-ui/react-separator": "^1.1.8",
|
|
36
68
|
"@radix-ui/react-slot": "^1.2.4",
|
|
69
|
+
"@radix-ui/react-tooltip": "^1.2.8",
|
|
37
70
|
"class-variance-authority": "^0.7.1",
|
|
38
71
|
"clsx": "^2.1.1",
|
|
39
72
|
"date-fns": "^4.1.0",
|
|
40
|
-
"lucide-react": "^0.577.0",
|
|
41
|
-
"next": "16.1.6",
|
|
42
|
-
"next-themes": "^0.4.6",
|
|
43
73
|
"radix-ui": "^1.4.3",
|
|
44
|
-
"react": "19.2.4",
|
|
45
74
|
"react-day-picker": "^9.13.2",
|
|
46
|
-
"react-
|
|
75
|
+
"react-international-phone": "^4.8.0",
|
|
47
76
|
"recharts": "^2.15.4",
|
|
48
77
|
"sonner": "^2.0.7",
|
|
49
|
-
"tailwind-merge": "^3.4.0"
|
|
78
|
+
"tailwind-merge": "^3.4.0",
|
|
79
|
+
"@brightweblabs/theme": "0.2.0"
|
|
80
|
+
},
|
|
81
|
+
"peerDependencies": {
|
|
82
|
+
"lucide-react": "^1.8.0",
|
|
83
|
+
"next": "^16.0.0",
|
|
84
|
+
"react": "^19.0.0",
|
|
85
|
+
"react-dom": "^19.0.0"
|
|
50
86
|
}
|
|
51
87
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ButtonHTMLAttributes } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
|
|
5
|
+
export const actionClassName =
|
|
6
|
+
"inline-flex shrink-0 items-center justify-center gap-1.5 rounded-full border border-[color:var(--brand-accent)] bg-[color:var(--brand-accent)] px-3.5 py-1.5 text-ui-meta font-semibold text-[color:var(--accent-foreground)] transition-colors hover:bg-[color:var(--brand-accent-soft)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-3.5";
|
|
7
|
+
|
|
8
|
+
export type ActionButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
|
|
9
|
+
|
|
10
|
+
export function ActionButton({ className, type = "button", ...props }: ActionButtonProps) {
|
|
11
|
+
return <button type={type} className={cn(actionClassName, className)} {...props} />;
|
|
12
|
+
}
|
|
@@ -14,7 +14,7 @@ export function ActivityMessage({ segs }: { segs: MsgSeg[] }) {
|
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
16
|
return (
|
|
17
|
-
<span key={index} className="font-
|
|
17
|
+
<span key={index} className="font-semibold text-[color:var(--foreground)]">
|
|
18
18
|
{seg.v}
|
|
19
19
|
</span>
|
|
20
20
|
);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
|
-
import
|
|
4
|
+
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
|
5
5
|
|
|
6
6
|
import { cn } from "../lib/utils"
|
|
7
|
-
import { buttonVariants } from "./button"
|
|
7
|
+
import { buttonVariants } from "./button-variants"
|
|
8
8
|
|
|
9
9
|
function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
|
|
10
10
|
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
|
|
@@ -30,7 +30,7 @@ function AlertDialogOverlay({
|
|
|
30
30
|
<AlertDialogPrimitive.Overlay
|
|
31
31
|
data-slot="alert-dialog-overlay"
|
|
32
32
|
className={cn(
|
|
33
|
-
"fixed inset-0 z-50 bg-
|
|
33
|
+
"fixed inset-0 z-50 bg-[color:color-mix(in_srgb,var(--foreground)_55%,transparent)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
34
34
|
className
|
|
35
35
|
)}
|
|
36
36
|
{...props}
|
|
@@ -48,7 +48,7 @@ function AlertDialogContent({
|
|
|
48
48
|
<AlertDialogPrimitive.Content
|
|
49
49
|
data-slot="alert-dialog-content"
|
|
50
50
|
className={cn(
|
|
51
|
-
"
|
|
51
|
+
"fixed left-1/2 top-1/2 z-50 grid w-full max-w-[28rem] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-[var(--radius-panel)] border border-[color:var(--hairline-strong)] bg-background p-6 shadow-[0_24px_64px_var(--elevate-3)] duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
52
52
|
className
|
|
53
53
|
)}
|
|
54
54
|
{...props}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
5
|
+
|
|
6
|
+
import { cn } from "../lib/utils";
|
|
7
|
+
|
|
8
|
+
function Avatar({ className, size = "default", ...props }: React.ComponentProps<typeof AvatarPrimitive.Root> & {
|
|
9
|
+
size?: "default" | "sm" | "lg";
|
|
10
|
+
}) {
|
|
11
|
+
return (
|
|
12
|
+
<AvatarPrimitive.Root
|
|
13
|
+
data-slot="avatar"
|
|
14
|
+
data-size={size}
|
|
15
|
+
className={cn(
|
|
16
|
+
"group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
|
|
17
|
+
className,
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function AvatarImage({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
|
25
|
+
return <AvatarPrimitive.Image data-slot="avatar-image" className={cn("aspect-square size-full", className)} {...props} />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function AvatarFallback({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
|
29
|
+
return (
|
|
30
|
+
<AvatarPrimitive.Fallback
|
|
31
|
+
data-slot="avatar-fallback"
|
|
32
|
+
className={cn(
|
|
33
|
+
"flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
|
|
34
|
+
className,
|
|
35
|
+
)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
|
|
42
|
+
return (
|
|
43
|
+
<span
|
|
44
|
+
data-slot="avatar-badge"
|
|
45
|
+
className={cn(
|
|
46
|
+
"absolute bottom-0 right-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground ring-2 ring-background select-none",
|
|
47
|
+
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
|
48
|
+
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
|
49
|
+
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
data-slot="avatar-group"
|
|
61
|
+
className={cn("group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background", className)}
|
|
62
|
+
{...props}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function AvatarGroupCount({ className, ...props }: React.ComponentProps<"div">) {
|
|
68
|
+
return (
|
|
69
|
+
<div
|
|
70
|
+
data-slot="avatar-group-count"
|
|
71
|
+
className={cn(
|
|
72
|
+
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
|
|
73
|
+
className,
|
|
74
|
+
)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroup, AvatarGroupCount };
|
package/src/components/badge.tsx
CHANGED
|
@@ -1,48 +1,38 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
-
import { Slot } from "radix-ui"
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
4
4
|
|
|
5
|
-
import { cn } from "../lib/utils"
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
6
|
|
|
7
7
|
const badgeVariants = cva(
|
|
8
|
-
"inline-flex items-center justify-center rounded-full border border-transparent
|
|
8
|
+
"inline-flex w-fit shrink-0 items-center justify-center overflow-hidden whitespace-nowrap rounded-full border border-transparent transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
12
12
|
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
13
|
-
secondary:
|
|
14
|
-
"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
13
|
+
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
15
14
|
destructive:
|
|
16
|
-
"bg-destructive text-
|
|
17
|
-
outline:
|
|
18
|
-
"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
15
|
+
"bg-destructive text-destructive-foreground [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
16
|
+
outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
19
17
|
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
20
18
|
link: "text-primary underline-offset-4 [a&]:hover:underline",
|
|
19
|
+
tint: "text-current [background-color:color-mix(in_srgb,currentColor_14%,transparent)]",
|
|
20
|
+
},
|
|
21
|
+
size: {
|
|
22
|
+
default: "gap-1 px-2 py-0.5 text-xs font-semibold",
|
|
23
|
+
sm: "gap-1.5 px-xs py-0.5 text-[length:var(--text-ui-micro)] font-semibold leading-none",
|
|
21
24
|
},
|
|
22
25
|
},
|
|
23
26
|
defaultVariants: {
|
|
24
27
|
variant: "default",
|
|
28
|
+
size: "default",
|
|
25
29
|
},
|
|
26
|
-
}
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
function Badge({
|
|
30
|
-
className,
|
|
31
|
-
variant = "default",
|
|
32
|
-
asChild = false,
|
|
33
|
-
...props
|
|
34
|
-
}: React.ComponentProps<"span"> &
|
|
35
|
-
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
|
36
|
-
const Comp = asChild ? Slot.Root : "span"
|
|
30
|
+
},
|
|
31
|
+
);
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
data-variant={variant}
|
|
42
|
-
className={cn(badgeVariants({ variant }), className)}
|
|
43
|
-
{...props}
|
|
44
|
-
/>
|
|
45
|
-
)
|
|
33
|
+
function Badge({ className, variant = "default", size = "default", asChild = false, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
|
34
|
+
const Comp = asChild ? Slot : "span";
|
|
35
|
+
return <Comp data-slot="badge" data-variant={variant} data-size={size} className={cn(badgeVariants({ variant, size }), className)} {...props} />;
|
|
46
36
|
}
|
|
47
37
|
|
|
48
|
-
export { Badge, badgeVariants }
|
|
38
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
|
|
3
|
+
const buttonVariants = cva(
|
|
4
|
+
"inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius-control)] text-ui-body !font-semibold transition-all outline-none disabled:pointer-events-none disabled:opacity-50 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
5
|
+
{
|
|
6
|
+
variants: {
|
|
7
|
+
variant: {
|
|
8
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
9
|
+
accent:
|
|
10
|
+
"relative isolate h-auto overflow-hidden !rounded-none px-0 py-0 font-display !font-bold text-[length:var(--text-ui-body)] leading-[1.6] text-foreground [padding:var(--space-button-y)_var(--space-button-x)] hover:text-foreground focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-[color:var(--brand-accent)] focus-visible:ring-offset-2",
|
|
11
|
+
accentLink:
|
|
12
|
+
"h-auto min-h-0 gap-[var(--space-sm)] !rounded-none px-0 py-0 font-display !font-bold text-[length:var(--text-ui-subhead)] leading-[1.66] text-[color:var(--brand-accent)] no-underline hover:opacity-70 hover:no-underline",
|
|
13
|
+
destructive:
|
|
14
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
15
|
+
brand:
|
|
16
|
+
"bg-[color:var(--brand-accent)] text-[color:var(--accent-foreground)] !font-extrabold shadow-[0_4px_14px_color-mix(in_srgb,var(--brand-accent)_26%,transparent)] hover:brightness-[1.05]",
|
|
17
|
+
soft:
|
|
18
|
+
"border border-[color:var(--border)] bg-[color:color-mix(in_srgb,var(--foreground)_3.5%,var(--card))] text-foreground hover:border-[color:color-mix(in_srgb,var(--foreground)_16%,transparent)] hover:bg-[color:color-mix(in_srgb,var(--foreground)_7%,var(--card))]",
|
|
19
|
+
outline:
|
|
20
|
+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:hover:bg-input/50",
|
|
21
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
22
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
23
|
+
link: "text-foreground underline-offset-4 transition-colors hover:text-primary hover:underline",
|
|
24
|
+
},
|
|
25
|
+
size: {
|
|
26
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
27
|
+
xs: "h-6 gap-1 rounded-[var(--radius-control)] px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
28
|
+
sm: "h-8 gap-1.5 rounded-[var(--radius-control)] px-3 has-[>svg]:px-2.5",
|
|
29
|
+
lg: "h-10 rounded-[var(--radius-control)] px-6 has-[>svg]:px-4",
|
|
30
|
+
icon: "size-9",
|
|
31
|
+
"icon-xs": "size-6 rounded-[var(--radius-control)] [&_svg:not([class*='size-'])]:size-3",
|
|
32
|
+
"icon-sm": "size-8",
|
|
33
|
+
"icon-lg": "size-10",
|
|
34
|
+
link: "h-auto min-h-0 px-0 py-0",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: {
|
|
38
|
+
variant: "default",
|
|
39
|
+
size: "default",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
type ButtonVariantProps = VariantProps<typeof buttonVariants>;
|
|
45
|
+
|
|
46
|
+
export { buttonVariants };
|
|
47
|
+
export type { ButtonVariantProps };
|
|
@@ -1,64 +1,113 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import * as React from "react";
|
|
4
|
+
import Link from "next/link";
|
|
2
5
|
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
6
|
|
|
5
7
|
import { cn } from "../lib/utils";
|
|
8
|
+
import { buttonVariants, type ButtonVariantProps } from "./button-variants";
|
|
9
|
+
|
|
10
|
+
type ButtonProps = React.ComponentProps<"button"> &
|
|
11
|
+
ButtonVariantProps & {
|
|
12
|
+
asChild?: boolean;
|
|
13
|
+
href?: string;
|
|
14
|
+
prefetch?: React.ComponentProps<typeof Link>["prefetch"];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
18
|
+
({ className, variant = "default", size = "default", asChild = false, href, prefetch, ...props }, ref) => {
|
|
19
|
+
const { onClick, onMouseEnter, onMouseLeave, onFocus, onBlur, children, ...restProps } = props;
|
|
20
|
+
const hasCornerMotion = !asChild && variant === "accent";
|
|
21
|
+
const [isActive, setIsActive] = React.useState(false);
|
|
22
|
+
const borderSize = hasCornerMotion && isActive ? "100%" : "30%";
|
|
23
|
+
const resolvedClassName = cn(buttonVariants({ variant, size, className }));
|
|
6
24
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
{
|
|
10
|
-
variants: {
|
|
11
|
-
variant: {
|
|
12
|
-
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
13
|
-
destructive:
|
|
14
|
-
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
15
|
-
outline:
|
|
16
|
-
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
17
|
-
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
18
|
-
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
19
|
-
link: "text-foreground underline-offset-4 transition-colors hover:text-primary hover:underline",
|
|
25
|
+
const activate = () => hasCornerMotion && setIsActive(true);
|
|
26
|
+
const deactivate = () => hasCornerMotion && setIsActive(false);
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
const content = hasCornerMotion ? (
|
|
29
|
+
<>
|
|
30
|
+
<span className="relative z-2">{children}</span>
|
|
31
|
+
<span
|
|
32
|
+
aria-hidden="true"
|
|
33
|
+
className="pointer-events-none absolute right-0 top-0 border-r border-t border-[color:var(--brand-accent)] transition-[width,height] duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none"
|
|
34
|
+
style={{ width: borderSize, height: borderSize }}
|
|
35
|
+
/>
|
|
36
|
+
<span
|
|
37
|
+
aria-hidden="true"
|
|
38
|
+
className="pointer-events-none absolute bottom-0 left-0 border-b border-l border-[color:var(--brand-accent)] transition-[width,height] duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none"
|
|
39
|
+
style={{ width: borderSize, height: borderSize }}
|
|
40
|
+
/>
|
|
41
|
+
</>
|
|
42
|
+
) : children;
|
|
43
|
+
|
|
44
|
+
if (!asChild && href) {
|
|
45
|
+
return (
|
|
46
|
+
<Link
|
|
47
|
+
href={href}
|
|
48
|
+
data-slot="button"
|
|
49
|
+
data-variant={variant}
|
|
50
|
+
data-size={size}
|
|
51
|
+
className={resolvedClassName}
|
|
52
|
+
prefetch={prefetch}
|
|
53
|
+
onClick={onClick as React.MouseEventHandler<HTMLAnchorElement> | undefined}
|
|
54
|
+
onMouseEnter={(event) => {
|
|
55
|
+
activate();
|
|
56
|
+
onMouseEnter?.(event as unknown as React.MouseEvent<HTMLButtonElement>);
|
|
57
|
+
}}
|
|
58
|
+
onMouseLeave={(event) => {
|
|
59
|
+
deactivate();
|
|
60
|
+
onMouseLeave?.(event as unknown as React.MouseEvent<HTMLButtonElement>);
|
|
61
|
+
}}
|
|
62
|
+
onFocus={(event) => {
|
|
63
|
+
activate();
|
|
64
|
+
onFocus?.(event as unknown as React.FocusEvent<HTMLButtonElement>);
|
|
65
|
+
}}
|
|
66
|
+
onBlur={(event) => {
|
|
67
|
+
deactivate();
|
|
68
|
+
onBlur?.(event as unknown as React.FocusEvent<HTMLButtonElement>);
|
|
69
|
+
}}
|
|
70
|
+
{...(restProps as Omit<React.ComponentProps<typeof Link>, "href" | "prefetch">)}
|
|
71
|
+
>
|
|
72
|
+
{content}
|
|
73
|
+
</Link>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const Comp = asChild ? Slot : "button";
|
|
78
|
+
return (
|
|
79
|
+
<Comp
|
|
80
|
+
ref={ref}
|
|
81
|
+
data-slot="button"
|
|
82
|
+
data-variant={variant}
|
|
83
|
+
data-size={size}
|
|
84
|
+
className={resolvedClassName}
|
|
85
|
+
onClick={onClick}
|
|
86
|
+
onMouseEnter={(event) => {
|
|
87
|
+
activate();
|
|
88
|
+
onMouseEnter?.(event as unknown as React.MouseEvent<HTMLButtonElement>);
|
|
89
|
+
}}
|
|
90
|
+
onMouseLeave={(event) => {
|
|
91
|
+
deactivate();
|
|
92
|
+
onMouseLeave?.(event as unknown as React.MouseEvent<HTMLButtonElement>);
|
|
93
|
+
}}
|
|
94
|
+
onFocus={(event) => {
|
|
95
|
+
activate();
|
|
96
|
+
onFocus?.(event as unknown as React.FocusEvent<HTMLButtonElement>);
|
|
97
|
+
}}
|
|
98
|
+
onBlur={(event) => {
|
|
99
|
+
deactivate();
|
|
100
|
+
onBlur?.(event as unknown as React.FocusEvent<HTMLButtonElement>);
|
|
101
|
+
}}
|
|
102
|
+
{...restProps}
|
|
103
|
+
>
|
|
104
|
+
{content}
|
|
105
|
+
</Comp>
|
|
106
|
+
);
|
|
38
107
|
},
|
|
39
108
|
);
|
|
40
109
|
|
|
41
|
-
|
|
42
|
-
className,
|
|
43
|
-
variant = "default",
|
|
44
|
-
size = "default",
|
|
45
|
-
asChild = false,
|
|
46
|
-
...props
|
|
47
|
-
}: React.ComponentProps<"button"> &
|
|
48
|
-
VariantProps<typeof buttonVariants> & {
|
|
49
|
-
asChild?: boolean;
|
|
50
|
-
}) {
|
|
51
|
-
const Comp = asChild ? Slot : "button";
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<Comp
|
|
55
|
-
data-slot="button"
|
|
56
|
-
data-variant={variant}
|
|
57
|
-
data-size={size}
|
|
58
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
59
|
-
{...props}
|
|
60
|
-
/>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
110
|
+
Button.displayName = "Button";
|
|
63
111
|
|
|
64
112
|
export { Button, buttonVariants };
|
|
113
|
+
export type { ButtonProps, ButtonVariantProps };
|
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
} from "react-day-picker"
|
|
14
14
|
|
|
15
15
|
import { cn } from "../lib/utils"
|
|
16
|
-
import { Button
|
|
16
|
+
import { Button } from "./button"
|
|
17
|
+
import { buttonVariants, type ButtonVariantProps } from "./button-variants"
|
|
17
18
|
|
|
18
19
|
function Calendar({
|
|
19
20
|
className,
|
|
@@ -25,7 +26,7 @@ function Calendar({
|
|
|
25
26
|
components,
|
|
26
27
|
...props
|
|
27
28
|
}: React.ComponentProps<typeof DayPicker> & {
|
|
28
|
-
buttonVariant?:
|
|
29
|
+
buttonVariant?: ButtonVariantProps["variant"]
|
|
29
30
|
}) {
|
|
30
31
|
const defaultClassNames = getDefaultClassNames()
|
|
31
32
|
|
|
@@ -70,7 +71,7 @@ function Calendar({
|
|
|
70
71
|
defaultClassNames.month_caption
|
|
71
72
|
),
|
|
72
73
|
dropdowns: cn(
|
|
73
|
-
"w-full flex items-center text-sm font-
|
|
74
|
+
"w-full flex items-center text-sm font-semibold justify-center h-(--cell-size) gap-1.5",
|
|
74
75
|
defaultClassNames.dropdowns
|
|
75
76
|
),
|
|
76
77
|
dropdown_root: cn(
|
|
@@ -82,7 +83,7 @@ function Calendar({
|
|
|
82
83
|
defaultClassNames.dropdown
|
|
83
84
|
),
|
|
84
85
|
caption_label: cn(
|
|
85
|
-
"select-none font-
|
|
86
|
+
"select-none font-semibold",
|
|
86
87
|
captionLayout === "label"
|
|
87
88
|
? "text-sm"
|
|
88
89
|
: "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5",
|
package/src/components/chart.tsx
CHANGED
|
@@ -79,7 +79,7 @@ function ChartContainer({
|
|
|
79
79
|
data-slot="chart"
|
|
80
80
|
data-chart={chartId}
|
|
81
81
|
className={cn(
|
|
82
|
-
"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='
|
|
82
|
+
"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='var(--border)']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='var(--border)']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='var(--border)']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='var(--card)']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='var(--card)']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
|
|
83
83
|
className
|
|
84
84
|
)}
|
|
85
85
|
{...props}
|
|
@@ -167,7 +167,7 @@ function ChartTooltipContent({
|
|
|
167
167
|
|
|
168
168
|
if (labelFormatter) {
|
|
169
169
|
return (
|
|
170
|
-
<div className={cn("font-
|
|
170
|
+
<div className={cn("font-semibold", labelClassName)}>
|
|
171
171
|
{labelFormatter(value, payload)}
|
|
172
172
|
</div>
|
|
173
173
|
)
|
|
@@ -177,7 +177,7 @@ function ChartTooltipContent({
|
|
|
177
177
|
return null
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
return <div className={cn("font-
|
|
180
|
+
return <div className={cn("font-semibold", labelClassName)}>{value}</div>
|
|
181
181
|
}, [
|
|
182
182
|
label,
|
|
183
183
|
labelFormatter,
|
|
@@ -259,7 +259,7 @@ function ChartTooltipContent({
|
|
|
259
259
|
</span>
|
|
260
260
|
</div>
|
|
261
261
|
{item.value && (
|
|
262
|
-
<span className="
|
|
262
|
+
<span className="font-mono font-semibold tabular-nums text-foreground">
|
|
263
263
|
{item.value.toLocaleString()}
|
|
264
264
|
</span>
|
|
265
265
|
)}
|
|
@@ -74,7 +74,7 @@ function DropdownMenuItem({
|
|
|
74
74
|
data-inset={inset}
|
|
75
75
|
data-variant={variant}
|
|
76
76
|
className={cn(
|
|
77
|
-
"focus:bg-accent focus:text-
|
|
77
|
+
"focus:bg-accent/15 focus:text-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-foreground/70 focus:[&_svg:not([class*='text-'])]:text-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
78
78
|
className
|
|
79
79
|
)}
|
|
80
80
|
{...props}
|
|
@@ -92,7 +92,7 @@ function DropdownMenuCheckboxItem({
|
|
|
92
92
|
<DropdownMenuPrimitive.CheckboxItem
|
|
93
93
|
data-slot="dropdown-menu-checkbox-item"
|
|
94
94
|
className={cn(
|
|
95
|
-
"focus:bg-accent focus:text-
|
|
95
|
+
"focus:bg-accent/15 focus:text-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
96
96
|
className
|
|
97
97
|
)}
|
|
98
98
|
checked={checked}
|
|
@@ -128,7 +128,7 @@ function DropdownMenuRadioItem({
|
|
|
128
128
|
<DropdownMenuPrimitive.RadioItem
|
|
129
129
|
data-slot="dropdown-menu-radio-item"
|
|
130
130
|
className={cn(
|
|
131
|
-
"focus:bg-accent focus:text-
|
|
131
|
+
"focus:bg-accent/15 focus:text-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
132
132
|
className
|
|
133
133
|
)}
|
|
134
134
|
{...props}
|
|
@@ -155,7 +155,7 @@ function DropdownMenuLabel({
|
|
|
155
155
|
data-slot="dropdown-menu-label"
|
|
156
156
|
data-inset={inset}
|
|
157
157
|
className={cn(
|
|
158
|
-
"px-2 py-1.5 text-sm font-
|
|
158
|
+
"px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8",
|
|
159
159
|
className
|
|
160
160
|
)}
|
|
161
161
|
{...props}
|
|
@@ -211,7 +211,7 @@ function DropdownMenuSubTrigger({
|
|
|
211
211
|
data-slot="dropdown-menu-sub-trigger"
|
|
212
212
|
data-inset={inset}
|
|
213
213
|
className={cn(
|
|
214
|
-
"focus:bg-accent focus:text-
|
|
214
|
+
"focus:bg-accent/15 focus:text-foreground data-[state=open]:bg-accent/15 data-[state=open]:text-foreground [&_svg:not([class*='text-'])]:text-foreground/70 focus:[&_svg:not([class*='text-'])]:text-foreground data-[state=open]:[&_svg:not([class*='text-'])]:text-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
215
215
|
className
|
|
216
216
|
)}
|
|
217
217
|
{...props}
|