@brightweblabs/ui 1.0.1 → 1.1.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 +18 -15
- package/src/components/action.tsx +2 -1
- package/src/components/alert-dialog.tsx +2 -2
- package/src/components/badge.tsx +3 -1
- package/src/components/breadcrumb.tsx +4 -2
- package/src/components/button-variants.ts +8 -8
- package/src/components/calendar.tsx +2 -2
- package/src/components/chart.tsx +3 -3
- package/src/components/checkbox.tsx +10 -0
- package/src/components/dropdown-menu.tsx +2 -2
- package/src/components/empty-state.tsx +2 -2
- package/src/components/input.tsx +1 -1
- package/src/components/kpi-breakdown-bar.tsx +2 -2
- package/src/components/password-input.tsx +11 -2
- package/src/components/phone-input.tsx +95 -20
- package/src/components/sheet.tsx +4 -4
- package/src/components/skeleton-table.tsx +2 -2
- package/src/components/skeleton.tsx +2 -2
- package/src/components/sonner.tsx +10 -10
- package/src/components/stat-tile.tsx +1 -1
- package/src/components/status-pill.tsx +2 -0
- package/src/components/table-pagination.tsx +10 -8
- package/src/components/tooltip.tsx +1 -1
- package/src/index.ts +2 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightweblabs/ui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"files": [
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"./calendar": "./src/components/calendar.tsx",
|
|
30
30
|
"./card": "./src/components/card.tsx",
|
|
31
31
|
"./chart": "./src/components/chart.tsx",
|
|
32
|
+
"./checkbox": "./src/components/checkbox.tsx",
|
|
32
33
|
"./dropdown-menu": "./src/components/dropdown-menu.tsx",
|
|
33
34
|
"./empty-state": "./src/components/empty-state.tsx",
|
|
34
35
|
"./field": "./src/components/field.tsx",
|
|
@@ -58,30 +59,32 @@
|
|
|
58
59
|
"./tooltip": "./src/components/tooltip.tsx"
|
|
59
60
|
},
|
|
60
61
|
"dependencies": {
|
|
61
|
-
"@radix-ui/react-alert-dialog": "^1.1.
|
|
62
|
-
"@radix-ui/react-avatar": "^1.
|
|
63
|
-
"@radix-ui/react-dialog": "^1.1.
|
|
64
|
-
"@radix-ui/react-dropdown-menu": "^2.1.
|
|
65
|
-
"@radix-ui/react-label": "^2.1.
|
|
66
|
-
"@radix-ui/react-popover": "^1.1.
|
|
67
|
-
"@radix-ui/react-separator": "^1.1.
|
|
68
|
-
"@radix-ui/react-slot": "^1.
|
|
69
|
-
"@radix-ui/react-tooltip": "^1.2.
|
|
62
|
+
"@radix-ui/react-alert-dialog": "^1.1.21",
|
|
63
|
+
"@radix-ui/react-avatar": "^1.2.4",
|
|
64
|
+
"@radix-ui/react-dialog": "^1.1.21",
|
|
65
|
+
"@radix-ui/react-dropdown-menu": "^2.1.22",
|
|
66
|
+
"@radix-ui/react-label": "^2.1.13",
|
|
67
|
+
"@radix-ui/react-popover": "^1.1.21",
|
|
68
|
+
"@radix-ui/react-separator": "^1.1.13",
|
|
69
|
+
"@radix-ui/react-slot": "^1.3.1",
|
|
70
|
+
"@radix-ui/react-tooltip": "^1.2.14",
|
|
70
71
|
"class-variance-authority": "^0.7.1",
|
|
71
72
|
"clsx": "^2.1.1",
|
|
72
|
-
"
|
|
73
|
-
"radix-ui": "^1.4.3",
|
|
74
|
-
"react-day-picker": "^9.13.2",
|
|
73
|
+
"react-day-picker": "^9.14.0",
|
|
75
74
|
"react-international-phone": "^4.8.0",
|
|
76
75
|
"recharts": "^2.15.4",
|
|
77
76
|
"sonner": "^2.0.7",
|
|
78
|
-
"tailwind-merge": "^3.
|
|
79
|
-
"@brightweblabs/theme": "0.
|
|
77
|
+
"tailwind-merge": "^3.6.0",
|
|
78
|
+
"@brightweblabs/theme": "0.4.0"
|
|
80
79
|
},
|
|
81
80
|
"peerDependencies": {
|
|
82
81
|
"lucide-react": "^1.8.0",
|
|
83
82
|
"next": "^16.0.0",
|
|
84
83
|
"react": "^19.0.0",
|
|
85
84
|
"react-dom": "^19.0.0"
|
|
85
|
+
},
|
|
86
|
+
"devDependencies": {
|
|
87
|
+
"react": "19.2.4",
|
|
88
|
+
"react-dom": "19.2.4"
|
|
86
89
|
}
|
|
87
90
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { ButtonHTMLAttributes } from "react";
|
|
2
2
|
|
|
3
|
+
import { buttonVariants } from "./button-variants";
|
|
3
4
|
import { cn } from "../lib/utils";
|
|
4
5
|
|
|
5
6
|
export const actionClassName =
|
|
6
|
-
"
|
|
7
|
+
buttonVariants({ variant: "brand", size: "sm", className: "gap-1.5 rounded-full px-3.5 text-ui-meta [&_svg]:size-3.5" });
|
|
7
8
|
|
|
8
9
|
export type ActionButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
|
|
9
10
|
|
|
@@ -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-[color:
|
|
33
|
+
"fixed inset-0 z-50 bg-[color:var(--surface-overlay-strong)] 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
|
-
"fixed left-1/2 top-1/2 z-50 grid w-full max-w-[
|
|
51
|
+
"fixed left-1/2 top-1/2 z-50 grid w-full max-w-[var(--dialog-width)] -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-[var(--shadow-dialog)] 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}
|
package/src/components/badge.tsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import * as React from "react";
|
|
2
4
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
5
|
import { Slot } from "@radix-ui/react-slot";
|
|
@@ -16,7 +18,7 @@ const badgeVariants = cva(
|
|
|
16
18
|
outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
17
19
|
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
18
20
|
link: "text-primary underline-offset-4 [a&]:hover:underline",
|
|
19
|
-
tint: "text-current [background-color:
|
|
21
|
+
tint: "text-current [background-color:var(--surface-badge-tint)]",
|
|
20
22
|
},
|
|
21
23
|
size: {
|
|
22
24
|
default: "gap-1 px-2 py-0.5 text-xs font-semibold",
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import * as React from "react"
|
|
2
4
|
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
|
3
|
-
import { Slot } from "radix-ui"
|
|
5
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
4
6
|
|
|
5
7
|
import { cn } from "../lib/utils"
|
|
6
8
|
|
|
@@ -38,7 +40,7 @@ function BreadcrumbLink({
|
|
|
38
40
|
}: React.ComponentProps<"a"> & {
|
|
39
41
|
asChild?: boolean
|
|
40
42
|
}) {
|
|
41
|
-
const Comp = asChild ? Slot
|
|
43
|
+
const Comp = asChild ? Slot : "a"
|
|
42
44
|
|
|
43
45
|
return (
|
|
44
46
|
<Comp
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
2
|
|
|
3
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-
|
|
4
|
+
"inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius-control)] text-ui-body !font-semibold transition-[color,background-color,border-color,box-shadow,transform,filter,text-decoration-color] motion-reduce:transition-none outline-none disabled:pointer-events-none disabled:opacity-50 focus-visible:border-current focus-visible:ring-[3px] focus-visible:ring-current 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
5
|
{
|
|
6
6
|
variants: {
|
|
7
7
|
variant: {
|
|
8
8
|
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
9
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-[
|
|
10
|
+
"relative isolate h-auto overflow-hidden !rounded-none px-0 py-0 font-display !font-bold text-[length:var(--text-ui-body)] leading-[var(--type-leading-160)] 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-current focus-visible:ring-offset-2",
|
|
11
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-[
|
|
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-[var(--type-leading-166)] text-[color:var(--foreground-accent-link)] no-underline hover:underline",
|
|
13
13
|
destructive:
|
|
14
|
-
"bg-destructive text-destructive-foreground hover:
|
|
14
|
+
"bg-destructive text-destructive-foreground hover:brightness-[1.05]",
|
|
15
15
|
brand:
|
|
16
|
-
"bg-[color:var(--brand
|
|
16
|
+
"bg-[color:var(--surface-button-brand)] text-[color:var(--accent-foreground)] !font-extrabold shadow-[var(--shadow-accent-control)] hover:brightness-[1.05]",
|
|
17
17
|
soft:
|
|
18
|
-
"border border-[color:var(--border)] bg-[color:
|
|
18
|
+
"border border-[color:var(--border)] bg-[color:var(--surface-button-soft)] text-foreground hover:border-[color:var(--border-button-soft-hover)] hover:bg-[color:var(--surface-button-soft-hover)]",
|
|
19
19
|
outline:
|
|
20
|
-
"border bg-background shadow-xs hover:bg-
|
|
20
|
+
"border bg-background shadow-xs hover:bg-[color:var(--surface-button-soft-hover)] hover:text-foreground dark:bg-input/30 dark:hover:bg-[color:var(--surface-button-soft-hover)]",
|
|
21
21
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
22
|
-
ghost: "hover:bg-
|
|
22
|
+
ghost: "hover:bg-[color:var(--surface-button-soft-hover)] hover:text-foreground",
|
|
23
23
|
link: "text-foreground underline-offset-4 transition-colors hover:text-primary hover:underline",
|
|
24
24
|
},
|
|
25
25
|
size: {
|
|
@@ -92,7 +92,7 @@ function Calendar({
|
|
|
92
92
|
table: "w-full border-collapse",
|
|
93
93
|
weekdays: cn("flex", defaultClassNames.weekdays),
|
|
94
94
|
weekday: cn(
|
|
95
|
-
"text-muted-foreground rounded-md flex-1 font-normal text-[
|
|
95
|
+
"text-muted-foreground rounded-md flex-1 font-normal text-[length:var(--text-ui-calendar)] select-none",
|
|
96
96
|
defaultClassNames.weekday
|
|
97
97
|
),
|
|
98
98
|
week: cn("flex w-full mt-2", defaultClassNames.week),
|
|
@@ -101,7 +101,7 @@ function Calendar({
|
|
|
101
101
|
defaultClassNames.week_number_header
|
|
102
102
|
),
|
|
103
103
|
week_number: cn(
|
|
104
|
-
"text-[
|
|
104
|
+
"text-[length:var(--text-ui-calendar)] select-none text-muted-foreground",
|
|
105
105
|
defaultClassNames.week_number
|
|
106
106
|
),
|
|
107
107
|
day: cn(
|
package/src/components/chart.tsx
CHANGED
|
@@ -197,7 +197,7 @@ function ChartTooltipContent({
|
|
|
197
197
|
return (
|
|
198
198
|
<div
|
|
199
199
|
className={cn(
|
|
200
|
-
"border-border/50 bg-background grid min-w-[
|
|
200
|
+
"border-border/50 bg-background grid min-w-[var(--chart-tooltip-min-width)] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
|
|
201
201
|
className
|
|
202
202
|
)}
|
|
203
203
|
>
|
|
@@ -228,7 +228,7 @@ function ChartTooltipContent({
|
|
|
228
228
|
!hideIndicator && (
|
|
229
229
|
<div
|
|
230
230
|
className={cn(
|
|
231
|
-
"shrink-0 rounded-[
|
|
231
|
+
"shrink-0 rounded-[var(--radius-swatch)] border-(--color-border) bg-(--color-bg)",
|
|
232
232
|
{
|
|
233
233
|
"h-2.5 w-2.5": indicator === "dot",
|
|
234
234
|
"w-1": indicator === "line",
|
|
@@ -318,7 +318,7 @@ function ChartLegendContent({
|
|
|
318
318
|
<itemConfig.icon />
|
|
319
319
|
) : (
|
|
320
320
|
<div
|
|
321
|
-
className="h-2 w-2 shrink-0 rounded-[
|
|
321
|
+
className="h-2 w-2 shrink-0 rounded-[var(--radius-swatch)]"
|
|
322
322
|
style={{
|
|
323
323
|
backgroundColor: item.color,
|
|
324
324
|
}}
|
|
@@ -42,7 +42,7 @@ function DropdownMenuContent({
|
|
|
42
42
|
data-slot="dropdown-menu-content"
|
|
43
43
|
sideOffset={sideOffset}
|
|
44
44
|
className={cn(
|
|
45
|
-
"bg-popover text-popover-foreground 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 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 z-[1300] max-h-(--radix-dropdown-menu-content-available-height) min-w-[
|
|
45
|
+
"bg-popover text-popover-foreground 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 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 z-[1300] max-h-(--radix-dropdown-menu-content-available-height) min-w-[var(--menu-min-width)] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
|
46
46
|
className
|
|
47
47
|
)}
|
|
48
48
|
{...props}
|
|
@@ -230,7 +230,7 @@ function DropdownMenuSubContent({
|
|
|
230
230
|
<DropdownMenuPrimitive.SubContent
|
|
231
231
|
data-slot="dropdown-menu-sub-content"
|
|
232
232
|
className={cn(
|
|
233
|
-
"bg-popover text-popover-foreground 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 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 z-[1300] min-w-[
|
|
233
|
+
"bg-popover text-popover-foreground 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 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 z-[1300] min-w-[var(--menu-min-width)] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
|
234
234
|
className
|
|
235
235
|
)}
|
|
236
236
|
{...props}
|
|
@@ -17,8 +17,8 @@ export function EmptyState({ icon: Icon, title, hint, action, className, ...prop
|
|
|
17
17
|
<Icon className="size-5 text-muted-foreground" aria-hidden="true" />
|
|
18
18
|
</div>
|
|
19
19
|
<div>
|
|
20
|
-
<p className="text-ui-body font-semibold">{title}</p>
|
|
21
|
-
{hint ? <p className="mt-1 text-ui-meta">{hint}</p> : null}
|
|
20
|
+
<p className="text-ui-body font-semibold text-foreground">{title}</p>
|
|
21
|
+
{hint ? <p className="mt-1 text-ui-meta text-muted-foreground">{hint}</p> : null}
|
|
22
22
|
</div>
|
|
23
23
|
{action ? <div className="mt-1">{action}</div> : null}
|
|
24
24
|
</div>
|
package/src/components/input.tsx
CHANGED
|
@@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|
|
8
8
|
type={type}
|
|
9
9
|
data-slot="input"
|
|
10
10
|
className={cn(
|
|
11
|
-
"file:text-foreground placeholder:text-foreground
|
|
11
|
+
"file:text-foreground placeholder:text-foreground-muted-accessible selection:bg-primary selection:text-primary-foreground h-11 w-full min-w-0 rounded-xl border border-foreground/15 bg-foreground/5 px-3 py-1 text-base text-foreground shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-input file:text-sm file:font-semibold disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
12
|
"focus:border-primary focus:ring-primary/40 focus:ring-[3px]",
|
|
13
13
|
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
14
14
|
className,
|
|
@@ -42,7 +42,7 @@ export function KpiBreakdownBar({ items, toneTokens, className }: KpiBreakdownBa
|
|
|
42
42
|
</TooltipTrigger>
|
|
43
43
|
<TooltipContent>
|
|
44
44
|
<span className="flex items-center gap-2">
|
|
45
|
-
<span className="size-2 rounded-[
|
|
45
|
+
<span className="size-2 rounded-[var(--radius-swatch)]" style={tokenStyle(token)} />
|
|
46
46
|
<span>{item.label}</span>
|
|
47
47
|
<span className="font-bold tabular-nums">{item.value}</span>
|
|
48
48
|
</span>
|
|
@@ -58,7 +58,7 @@ export function KpiBreakdownBar({ items, toneTokens, className }: KpiBreakdownBa
|
|
|
58
58
|
return (
|
|
59
59
|
<div key={`${item.tone}:${item.label}`} className="flex items-center justify-between gap-2 text-ui-meta">
|
|
60
60
|
<span className="flex min-w-0 items-center gap-2 text-muted-foreground">
|
|
61
|
-
<span className="size-1.5 shrink-0 rounded-[
|
|
61
|
+
<span className="size-1.5 shrink-0 rounded-[var(--radius-swatch)]" style={token ? tokenStyle(token) : undefined} />
|
|
62
62
|
<span className="truncate">{item.label}</span>
|
|
63
63
|
</span>
|
|
64
64
|
<span className="font-mono font-bold tabular-nums text-foreground">{item.value}</span>
|
|
@@ -8,10 +8,19 @@ import { Input } from "./input";
|
|
|
8
8
|
|
|
9
9
|
interface PasswordInputProps extends Omit<React.ComponentProps<"input">, "type"> {
|
|
10
10
|
showToggle?: boolean;
|
|
11
|
+
showPasswordLabel?: string;
|
|
12
|
+
hidePasswordLabel?: string;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
|
|
14
|
-
({
|
|
16
|
+
({
|
|
17
|
+
className,
|
|
18
|
+
disabled,
|
|
19
|
+
showToggle = true,
|
|
20
|
+
showPasswordLabel = "Show password",
|
|
21
|
+
hidePasswordLabel = "Hide password",
|
|
22
|
+
...props
|
|
23
|
+
}, ref) => {
|
|
15
24
|
const [showPassword, setShowPassword] = React.useState(false);
|
|
16
25
|
return (
|
|
17
26
|
<div className="relative w-full">
|
|
@@ -22,7 +31,7 @@ const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
|
|
|
22
31
|
onClick={() => setShowPassword((current) => !current)}
|
|
23
32
|
className="absolute right-2 top-1/2 flex size-8 -translate-y-1/2 items-center justify-center rounded-[var(--radius)] text-foreground/40 transition-colors hover:text-foreground/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 disabled:pointer-events-none disabled:opacity-50"
|
|
24
33
|
disabled={disabled}
|
|
25
|
-
aria-label={showPassword ?
|
|
34
|
+
aria-label={showPassword ? hidePasswordLabel : showPasswordLabel}
|
|
26
35
|
>
|
|
27
36
|
{showPassword ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
|
|
28
37
|
</button>
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { useEffect, useId, useRef, useState, type InputHTMLAttributes, type KeyboardEvent } from "react";
|
|
4
4
|
import { defaultCountries, parseCountry, usePhoneInput } from "react-international-phone";
|
|
5
5
|
import type { CountryIso2 } from "react-international-phone";
|
|
6
6
|
|
|
7
7
|
import { cn } from "../lib/utils";
|
|
8
8
|
|
|
9
|
-
interface PhoneInputProps {
|
|
9
|
+
interface PhoneInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "className" | "defaultValue" | "onChange" | "type" | "value"> {
|
|
10
10
|
value: string;
|
|
11
11
|
onChange: (e164: string) => void;
|
|
12
|
-
onBlur?: () => void;
|
|
13
12
|
defaultCountry?: CountryIso2;
|
|
14
|
-
disabled?: boolean;
|
|
15
13
|
className?: string;
|
|
16
14
|
}
|
|
17
15
|
|
|
@@ -25,12 +23,15 @@ function flagEmoji(iso2: string) {
|
|
|
25
23
|
return iso2.toUpperCase().split("").map((character) => String.fromCodePoint(0x1f1e6 - 0x41 + character.charCodeAt(0))).join("");
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
export function PhoneInput({ value, onChange,
|
|
26
|
+
export function PhoneInput({ value, onChange, defaultCountry = "pt", disabled = false, className, placeholder = "912 345 678", ...inputProps }: PhoneInputProps) {
|
|
29
27
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
30
28
|
const triggerRef = useRef<HTMLButtonElement | null>(null);
|
|
31
29
|
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
|
30
|
+
const optionRefs = useRef<Array<HTMLButtonElement | null>>([]);
|
|
31
|
+
const countryListId = useId();
|
|
32
32
|
const [open, setOpen] = useState(false);
|
|
33
33
|
const [search, setSearch] = useState("");
|
|
34
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
34
35
|
|
|
35
36
|
const { inputValue, handlePhoneValueChange, country, setCountry } = usePhoneInput({
|
|
36
37
|
defaultCountry,
|
|
@@ -52,19 +53,76 @@ export function PhoneInput({ value, onChange, onBlur, defaultCountry = "pt", dis
|
|
|
52
53
|
return () => document.removeEventListener("mousedown", handleOutsideClick);
|
|
53
54
|
}, [open]);
|
|
54
55
|
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (open) optionRefs.current[activeIndex]?.scrollIntoView({ block: "nearest" });
|
|
58
|
+
}, [activeIndex, open]);
|
|
59
|
+
|
|
55
60
|
const filteredCountries = search
|
|
56
61
|
? orderedCountries.filter((candidate) => candidate.name.toLowerCase().includes(search.toLowerCase()) || `+${candidate.dialCode}`.includes(search))
|
|
57
62
|
: orderedCountries;
|
|
58
63
|
|
|
64
|
+
const closePicker = (restoreFocus = false) => {
|
|
65
|
+
setOpen(false);
|
|
66
|
+
setSearch("");
|
|
67
|
+
if (restoreFocus) triggerRef.current?.focus();
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const openPicker = (direction: "first" | "last" | "selected" = "selected") => {
|
|
71
|
+
const selectedIndex = orderedCountries.findIndex((candidate) => candidate.iso2 === country.iso2);
|
|
72
|
+
setSearch("");
|
|
73
|
+
setActiveIndex(direction === "first" ? 0 : direction === "last" ? orderedCountries.length - 1 : Math.max(0, selectedIndex));
|
|
74
|
+
setOpen(true);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const selectCountry = (index: number) => {
|
|
78
|
+
const candidate = filteredCountries[index];
|
|
79
|
+
if (!candidate) return;
|
|
80
|
+
setCountry(candidate.iso2);
|
|
81
|
+
setOpen(false);
|
|
82
|
+
setSearch("");
|
|
83
|
+
inputRef.current?.focus();
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const handlePickerKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
87
|
+
if (event.key === "Escape") {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
closePicker(true);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
const direction = event.key === "ArrowDown" ? 1 : -1;
|
|
95
|
+
setActiveIndex((current) => filteredCountries.length === 0
|
|
96
|
+
? 0
|
|
97
|
+
: (current + direction + filteredCountries.length) % filteredCountries.length);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (event.key === "Enter") {
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
selectCountry(activeIndex);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
59
106
|
return (
|
|
60
107
|
<div className={cn("relative flex items-center", className)}>
|
|
61
108
|
<button
|
|
62
109
|
ref={triggerRef}
|
|
63
110
|
type="button"
|
|
64
111
|
disabled={disabled}
|
|
65
|
-
onClick={() =>
|
|
112
|
+
onClick={() => open ? closePicker() : openPicker()}
|
|
113
|
+
onKeyDown={(event) => {
|
|
114
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
115
|
+
event.preventDefault();
|
|
116
|
+
openPicker(event.key === "ArrowDown" ? "first" : "last");
|
|
117
|
+
} else if (event.key === "Escape" && open) {
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
closePicker(true);
|
|
120
|
+
}
|
|
121
|
+
}}
|
|
66
122
|
className="flex h-7 items-center gap-1 border-0 bg-transparent pr-1.5 text-sm disabled:pointer-events-none disabled:opacity-100"
|
|
123
|
+
aria-controls={countryListId}
|
|
67
124
|
aria-expanded={open}
|
|
125
|
+
aria-haspopup="listbox"
|
|
68
126
|
aria-label="Choose country code"
|
|
69
127
|
>
|
|
70
128
|
<span className="text-base leading-none">{flagEmoji(country.iso2)}</span>
|
|
@@ -74,39 +132,56 @@ export function PhoneInput({ value, onChange, onBlur, defaultCountry = "pt", dis
|
|
|
74
132
|
</button>
|
|
75
133
|
|
|
76
134
|
{open ? (
|
|
77
|
-
<div ref={dropdownRef} className="absolute left-0 top-full z-50 mt-1.5 w-56 rounded-[var(--radius-card)] border border-[color:var(--hairline-strong)] bg-popover text-popover-foreground shadow-[
|
|
135
|
+
<div ref={dropdownRef} className="absolute left-0 top-full z-50 mt-1.5 w-56 rounded-[var(--radius-card)] border border-[color:var(--hairline-strong)] bg-popover text-popover-foreground shadow-[var(--shadow-phone-dropdown)]">
|
|
78
136
|
<div className="border-b border-[color:var(--hairline)] px-2.5 py-2">
|
|
79
|
-
<input
|
|
137
|
+
<input
|
|
138
|
+
autoFocus
|
|
139
|
+
role="combobox"
|
|
140
|
+
aria-autocomplete="list"
|
|
141
|
+
aria-controls={countryListId}
|
|
142
|
+
aria-expanded="true"
|
|
143
|
+
aria-label="Search countries"
|
|
144
|
+
aria-activedescendant={filteredCountries[activeIndex] ? `${countryListId}-${filteredCountries[activeIndex].iso2}` : undefined}
|
|
145
|
+
value={search}
|
|
146
|
+
onChange={(event) => {
|
|
147
|
+
setSearch(event.target.value);
|
|
148
|
+
setActiveIndex(0);
|
|
149
|
+
}}
|
|
150
|
+
onKeyDown={handlePickerKeyDown}
|
|
151
|
+
placeholder="Search…"
|
|
152
|
+
className="w-full border-0 bg-transparent text-ui-meta text-foreground outline-none placeholder:text-foreground-muted-accessible"
|
|
153
|
+
/>
|
|
80
154
|
</div>
|
|
81
|
-
<div className="max-h-48 overflow-y-auto rounded-b-[var(--radius-card)] p-1">
|
|
82
|
-
{filteredCountries.map((candidate) => (
|
|
155
|
+
<div id={countryListId} role="listbox" aria-label="Country calling codes" className="max-h-48 overflow-y-auto rounded-b-[var(--radius-card)] p-1">
|
|
156
|
+
{filteredCountries.map((candidate, index) => (
|
|
83
157
|
<button
|
|
84
158
|
key={candidate.iso2}
|
|
159
|
+
id={`${countryListId}-${candidate.iso2}`}
|
|
160
|
+
ref={(node) => { optionRefs.current[index] = node; }}
|
|
85
161
|
type="button"
|
|
162
|
+
role="option"
|
|
163
|
+
aria-selected={country.iso2 === candidate.iso2}
|
|
164
|
+
tabIndex={-1}
|
|
86
165
|
onMouseDown={(event) => event.preventDefault()}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
setOpen(false);
|
|
90
|
-
setSearch("");
|
|
91
|
-
inputRef.current?.focus();
|
|
92
|
-
}}
|
|
166
|
+
onMouseEnter={() => setActiveIndex(index)}
|
|
167
|
+
onClick={() => selectCountry(index)}
|
|
93
168
|
className={cn(
|
|
94
169
|
"flex w-full items-center gap-2 rounded-[var(--radius)] px-2 py-1 text-left transition-colors hover:bg-[color:var(--elevate-2)]",
|
|
95
|
-
country.iso2 === candidate.iso2 && "bg-[color:var(--elevate-3)] text-[color:var(--brand-accent)]",
|
|
170
|
+
(country.iso2 === candidate.iso2 || activeIndex === index) && "bg-[color:var(--elevate-3)] text-[color:var(--brand-accent)]",
|
|
96
171
|
)}
|
|
97
172
|
>
|
|
98
173
|
<span className="text-sm leading-none">{flagEmoji(candidate.iso2)}</span>
|
|
99
174
|
<span className="flex-1 truncate text-ui-label normal-case tracking-normal text-foreground">{candidate.name}</span>
|
|
100
|
-
<span className="shrink-0 text-ui-micro text-foreground
|
|
175
|
+
<span className="shrink-0 text-ui-micro text-foreground-muted-accessible">+{candidate.dialCode}</span>
|
|
101
176
|
</button>
|
|
102
177
|
))}
|
|
103
|
-
{filteredCountries.length === 0 ? <p className="px-2 py-2 text-ui-meta text-foreground
|
|
178
|
+
{filteredCountries.length === 0 ? <p className="px-2 py-2 text-ui-meta text-foreground-muted-accessible">No results</p> : null}
|
|
104
179
|
</div>
|
|
105
180
|
</div>
|
|
106
181
|
) : null}
|
|
107
182
|
|
|
108
183
|
<span className="h-4 w-px shrink-0 bg-foreground/15" />
|
|
109
|
-
<input ref={inputRef} value={inputValue} onChange={handlePhoneValueChange}
|
|
184
|
+
<input {...inputProps} ref={inputRef} value={inputValue} onChange={handlePhoneValueChange} disabled={disabled} placeholder={placeholder} type="tel" className="h-7 flex-1 border-0 bg-transparent pl-2 text-sm text-foreground outline-none placeholder:text-foreground-muted-accessible disabled:pointer-events-none disabled:opacity-100" />
|
|
110
185
|
</div>
|
|
111
186
|
);
|
|
112
187
|
}
|
package/src/components/sheet.tsx
CHANGED
|
@@ -65,7 +65,7 @@ function SheetOverlay({
|
|
|
65
65
|
<SheetPrimitive.Overlay
|
|
66
66
|
data-slot="sheet-overlay"
|
|
67
67
|
className={cn(
|
|
68
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-[color:
|
|
68
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-[color:var(--surface-overlay)] motion-reduce:animate-none motion-reduce:transition-none",
|
|
69
69
|
className
|
|
70
70
|
)}
|
|
71
71
|
{...props}
|
|
@@ -89,11 +89,11 @@ function SheetContent({
|
|
|
89
89
|
<SheetPrimitive.Content
|
|
90
90
|
data-slot="sheet-content"
|
|
91
91
|
className={cn(
|
|
92
|
-
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
|
92
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 motion-reduce:animate-none motion-reduce:transform-none motion-reduce:transition-none",
|
|
93
93
|
side === "right" &&
|
|
94
|
-
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-[
|
|
94
|
+
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-[var(--sheet-width)]",
|
|
95
95
|
side === "left" &&
|
|
96
|
-
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-[
|
|
96
|
+
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-[var(--sheet-width)]",
|
|
97
97
|
side === "top" &&
|
|
98
98
|
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
|
99
99
|
side === "bottom" &&
|
|
@@ -12,11 +12,11 @@ export function TableRowsSkeleton({ rows = 6, columns }: {
|
|
|
12
12
|
{columns.map((kind, columnIndex) => (
|
|
13
13
|
<TableCell key={columnIndex}>
|
|
14
14
|
{kind === "chip" ? (
|
|
15
|
-
<Skeleton rounded="
|
|
15
|
+
<Skeleton rounded="var(--radius-pill)" className="h-5 w-14" />
|
|
16
16
|
) : kind === "action" ? (
|
|
17
17
|
<Skeleton rounded="var(--radius)" className="ml-auto size-7" />
|
|
18
18
|
) : (
|
|
19
|
-
<Skeleton rounded="
|
|
19
|
+
<Skeleton rounded="var(--radius-pill)" className="h-[var(--skeleton-line-height)] w-[72%]" />
|
|
20
20
|
)}
|
|
21
21
|
</TableCell>
|
|
22
22
|
))}
|
|
@@ -9,7 +9,7 @@ export function Skeleton({ className = "", rounded = "var(--radius)", style }: {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function SkeletonLine({ w = "100%", className = "" }: { w?: string; className?: string }) {
|
|
12
|
-
return <Skeleton rounded="
|
|
12
|
+
return <Skeleton rounded="var(--radius-pill)" className={`h-[var(--skeleton-line-height)] ${className}`} style={{ width: w }} />;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function SkeletonText({ lines = 3, className = "" }: { lines?: number; className?: string }) {
|
|
@@ -32,7 +32,7 @@ export function SkeletonCard({ lines = 3, meta = true, className = "", bodyClass
|
|
|
32
32
|
return (
|
|
33
33
|
<div className={`flex flex-col gap-4 rounded-[var(--radius-card)] border border-border bg-card p-5 shadow-[0_1px_2px_var(--hairline)] ${className}`}>
|
|
34
34
|
<div className="flex flex-col gap-2">
|
|
35
|
-
<SkeletonLine w="45%" className="h-[
|
|
35
|
+
<SkeletonLine w="45%" className="h-[var(--skeleton-line-height-lg)]" />
|
|
36
36
|
{meta ? <SkeletonLine w="28%" /> : null}
|
|
37
37
|
</div>
|
|
38
38
|
<SkeletonText lines={lines} className={bodyClassName} />
|
|
@@ -25,18 +25,18 @@ const Toaster = ({ ...props }: ToasterProps) => {
|
|
|
25
25
|
"--normal-bg": "var(--popover)",
|
|
26
26
|
"--normal-text": "var(--popover-foreground)",
|
|
27
27
|
"--normal-border": "var(--border)",
|
|
28
|
-
"--success-bg": "
|
|
28
|
+
"--success-bg": "var(--toast-success-bg)",
|
|
29
29
|
"--success-text": "var(--primary)",
|
|
30
|
-
"--success-border": "
|
|
31
|
-
"--warning-bg": "
|
|
32
|
-
"--warning-text": "
|
|
33
|
-
"--warning-border": "
|
|
34
|
-
"--error-bg": "
|
|
30
|
+
"--success-border": "var(--toast-success-border)",
|
|
31
|
+
"--warning-bg": "var(--toast-warning-bg)",
|
|
32
|
+
"--warning-text": "var(--toast-warning-text)",
|
|
33
|
+
"--warning-border": "var(--toast-warning-border)",
|
|
34
|
+
"--error-bg": "var(--toast-error-bg)",
|
|
35
35
|
"--error-text": "var(--destructive)",
|
|
36
|
-
"--error-border": "
|
|
37
|
-
"--info-bg": "
|
|
38
|
-
"--info-text": "
|
|
39
|
-
"--info-border": "
|
|
36
|
+
"--error-border": "var(--toast-error-border)",
|
|
37
|
+
"--info-bg": "var(--toast-info-bg)",
|
|
38
|
+
"--info-text": "var(--toast-info-text)",
|
|
39
|
+
"--info-border": "var(--toast-info-border)",
|
|
40
40
|
"--border-radius": "var(--radius)",
|
|
41
41
|
} as React.CSSProperties
|
|
42
42
|
}
|
|
@@ -9,7 +9,7 @@ export type StatTileProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
9
9
|
export function StatTile({ label, children, className, ...props }: StatTileProps) {
|
|
10
10
|
return (
|
|
11
11
|
<div className={cn("stat-cell", className)} {...props}>
|
|
12
|
-
<p className="text-ui-label">{label}</p>
|
|
12
|
+
<p className="text-ui-label text-muted-foreground">{label}</p>
|
|
13
13
|
<div className="mt-3">{children}</div>
|
|
14
14
|
</div>
|
|
15
15
|
);
|
|
@@ -37,11 +37,11 @@ export function TablePagination({
|
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
return (
|
|
40
|
-
<div className={cn("flex min-w-0 flex-col gap-2 border-t border-
|
|
40
|
+
<div className={cn("flex min-w-0 flex-col gap-2 border-t border-[color:var(--border)] px-4 py-2 md:flex-row md:items-center md:justify-between", className)}>
|
|
41
41
|
{summary ? (
|
|
42
|
-
<p className="min-w-0 truncate text-
|
|
43
|
-
<span className="font-semibold text-foreground">{pageLabel(safePage, safeTotalPages)}</span>
|
|
44
|
-
<span className="px-1.5 text-border">·</span>
|
|
42
|
+
<p className="min-w-0 truncate portal-meta leading-tight text-[color:var(--muted-foreground)]">
|
|
43
|
+
<span className="font-semibold text-[color:var(--foreground)]">{pageLabel(safePage, safeTotalPages)}</span>
|
|
44
|
+
<span className="px-1.5 text-[color:var(--border)]">·</span>
|
|
45
45
|
{summary}
|
|
46
46
|
</p>
|
|
47
47
|
) : null}
|
|
@@ -57,7 +57,7 @@ export function TablePagination({
|
|
|
57
57
|
event.preventDefault();
|
|
58
58
|
changePage(safePage - 1);
|
|
59
59
|
}}
|
|
60
|
-
className={cn("size-8 rounded-full border border-transparent text-muted-foreground hover:border-
|
|
60
|
+
className={cn("size-8 rounded-full border border-transparent text-[color:var(--muted-foreground)] hover:border-[color:var(--border)] hover:bg-[color:var(--muted)] hover:text-[color:var(--foreground)]", safePage === 1 && "pointer-events-none opacity-45")}
|
|
61
61
|
>
|
|
62
62
|
<ChevronLeft className="size-4" />
|
|
63
63
|
</PaginationLink>
|
|
@@ -73,8 +73,10 @@ export function TablePagination({
|
|
|
73
73
|
changePage(item);
|
|
74
74
|
}}
|
|
75
75
|
className={cn(
|
|
76
|
-
"size-8 rounded-full text-ui-label
|
|
77
|
-
item === safePage
|
|
76
|
+
"size-8 rounded-full text-[length:var(--text-ui-label)] font-semibold",
|
|
77
|
+
item === safePage
|
|
78
|
+
? "border-[color:var(--border-pagination-active)] bg-[color:var(--surface-pagination-active)] text-[color:var(--foreground)]"
|
|
79
|
+
: "text-[color:var(--muted-foreground)] hover:bg-[color:var(--muted)] hover:text-[color:var(--foreground)]",
|
|
78
80
|
)}
|
|
79
81
|
>
|
|
80
82
|
{item}
|
|
@@ -95,7 +97,7 @@ export function TablePagination({
|
|
|
95
97
|
event.preventDefault();
|
|
96
98
|
changePage(safePage + 1);
|
|
97
99
|
}}
|
|
98
|
-
className={cn("size-8 rounded-full border border-transparent text-muted-foreground hover:border-
|
|
100
|
+
className={cn("size-8 rounded-full border border-transparent text-[color:var(--muted-foreground)] hover:border-[color:var(--border)] hover:bg-[color:var(--muted)] hover:text-[color:var(--foreground)]", safePage === safeTotalPages && "pointer-events-none opacity-45")}
|
|
99
101
|
>
|
|
100
102
|
<ChevronRight className="size-4" />
|
|
101
103
|
</PaginationLink>
|
|
@@ -41,7 +41,7 @@ function TooltipContent({
|
|
|
41
41
|
data-slot="tooltip-content"
|
|
42
42
|
sideOffset={sideOffset}
|
|
43
43
|
className={cn(
|
|
44
|
-
"z-[1300] overflow-hidden rounded-[var(--radius-card)] border border-[color:var(--hairline-strong)] bg-[color:
|
|
44
|
+
"z-[1300] overflow-hidden rounded-[var(--radius-card)] border border-[color:var(--hairline-strong)] bg-[color:var(--surface-tooltip)] px-2.5 py-1.5 text-ui-meta !font-semibold text-popover-foreground shadow-[var(--shadow-tooltip)] backdrop-blur-xl data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
|
|
45
45
|
className
|
|
46
46
|
)}
|
|
47
47
|
{...props}
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,8 @@ export * from "./components/card";
|
|
|
29
29
|
export * from "./components/chart";
|
|
30
30
|
export * from "./components/breadcrumb";
|
|
31
31
|
export * from "./components/calendar";
|
|
32
|
+
export { Checkbox } from "./components/checkbox";
|
|
33
|
+
export type { CheckboxProps } from "./components/checkbox";
|
|
32
34
|
export { Label } from "./components/label";
|
|
33
35
|
export * from "./components/pagination";
|
|
34
36
|
export * from "./components/popover";
|