@ponti-studios/ui 0.1.2 → 0.3.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/LICENSE +21 -0
- package/README.md +7 -7
- package/THIRD_PARTY_NOTICES.md +17 -0
- package/dist/index.js +1028 -638
- package/dist/index.js.map +1 -1
- package/package.json +30 -32
- package/src/animations.css +16 -434
- package/src/components/data-display/accordion.tsx +10 -7
- package/src/components/data-display/filter-chip.tsx +2 -2
- package/src/components/data-display/index.ts +5 -0
- package/src/components/data-display/metric-card.stories.tsx +48 -0
- package/src/components/data-display/metric-card.tsx +20 -0
- package/src/components/data-display/sort-controls.stories.tsx +45 -0
- package/src/components/data-display/sort-controls.tsx +99 -0
- package/src/components/data-display/sort-row.tsx +74 -0
- package/src/components/data-display/table.tsx +5 -5
- package/src/components/feedback/alert.tsx +4 -4
- package/src/components/feedback/empty-state.tsx +6 -6
- package/src/components/feedback/file-upload-status-badge.stories.tsx +28 -0
- package/src/components/feedback/file-upload-status-badge.tsx +33 -0
- package/src/components/feedback/file-upload-status.stories.tsx +57 -0
- package/src/components/feedback/file-upload-status.tsx +152 -0
- package/src/components/feedback/index.ts +9 -0
- package/src/components/feedback/progress-bar.stories.tsx +39 -0
- package/src/components/feedback/progress-bar.tsx +21 -0
- package/src/components/feedback/progress.stories.tsx +8 -6
- package/src/components/feedback/progress.tsx +1 -1
- package/src/components/feedback/skeleton.stories.tsx +36 -0
- package/src/components/feedback/skeleton.tsx +7 -0
- package/src/components/feedback/spinner.tsx +2 -2
- package/src/components/feedback/update-guard.stories.tsx +32 -0
- package/src/components/feedback/update-guard.tsx +280 -0
- package/src/components/forms/calendar.tsx +11 -11
- package/src/components/forms/date-month-select.stories.tsx +26 -0
- package/src/components/forms/date-month-select.tsx +70 -0
- package/src/components/forms/date-picker.tsx +3 -3
- package/src/components/forms/drop-zone.stories.tsx +56 -0
- package/src/components/forms/drop-zone.tsx +146 -0
- package/src/components/forms/entity-select.stories.tsx +65 -0
- package/src/components/forms/entity-select.tsx +79 -0
- package/src/components/forms/field.tsx +3 -3
- package/src/components/forms/group-by-select.stories.tsx +26 -0
- package/src/components/forms/group-by-select.tsx +47 -0
- package/src/components/forms/index.ts +10 -0
- package/src/components/forms/input.stories.tsx +4 -4
- package/src/components/forms/input.tsx +1 -1
- package/src/components/forms/passkey-management.stories.tsx +81 -0
- package/src/components/forms/passkey-management.tsx +138 -0
- package/src/components/forms/radio-group.tsx +14 -13
- package/src/components/forms/select.stories.tsx +4 -4
- package/src/components/forms/select.tsx +45 -53
- package/src/components/forms/slider.stories.tsx +5 -2
- package/src/components/forms/slider.tsx +27 -17
- package/src/components/forms/stepper.tsx +4 -4
- package/src/components/forms/switch.stories.tsx +1 -1
- package/src/components/forms/switch.tsx +2 -2
- package/src/components/forms/textarea.stories.tsx +2 -2
- package/src/components/forms/textarea.tsx +1 -1
- package/src/components/layout/index.ts +3 -0
- package/src/components/layout/scroll-area.stories.tsx +4 -4
- package/src/components/layout/scroll-area.tsx +23 -21
- package/src/components/layout/section-intro.stories.tsx +29 -0
- package/src/components/layout/section-intro.tsx +31 -0
- package/src/components/layout/surface-panel.stories.tsx +32 -0
- package/src/components/layout/surface-panel.tsx +15 -0
- package/src/components/navigation/app-navigation.stories.tsx +2 -2
- package/src/components/navigation/app-navigation.tsx +13 -13
- package/src/components/navigation/index.ts +4 -0
- package/src/components/navigation/pagination-controls.stories.tsx +41 -0
- package/src/components/navigation/pagination-controls.tsx +50 -0
- package/src/components/navigation/route-link.stories.tsx +30 -0
- package/src/components/navigation/route-link.tsx +42 -0
- package/src/components/navigation/tabs.stories.tsx +5 -5
- package/src/components/navigation/tabs.tsx +7 -7
- package/src/components/overlays/alert-dialog.tsx +21 -46
- package/src/components/overlays/command.tsx +51 -82
- package/src/components/overlays/dialog.tsx +5 -5
- package/src/components/overlays/dropdown-menu.stories.tsx +0 -5
- package/src/components/overlays/dropdown-menu.tsx +44 -142
- package/src/components/overlays/popover.stories.tsx +2 -2
- package/src/components/overlays/popover.tsx +34 -16
- package/src/components/overlays/sheet.stories.tsx +2 -2
- package/src/components/overlays/sheet.tsx +35 -17
- package/src/components/primitives/avatar.tsx +3 -3
- package/src/components/primitives/badge.tsx +7 -11
- package/src/components/primitives/button.tsx +41 -13
- package/src/components/primitives/card.stories.tsx +2 -2
- package/src/components/primitives/card.tsx +2 -2
- package/src/components/primitives/index.ts +2 -0
- package/src/components/primitives/label.stories.tsx +1 -1
- package/src/components/primitives/label.tsx +5 -6
- package/src/components/primitives/status-badge.stories.tsx +52 -0
- package/src/components/primitives/status-badge.tsx +33 -0
- package/src/constants/chart-colors.ts +5 -5
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-sort.ts +20 -0
- package/src/index.ts +30 -11
- package/src/styles.css +76 -1040
- package/src/tokens/color-systems.stories.tsx +9 -119
- package/src/tokens/foundations.stories.tsx +21 -113
- package/src/tokens/generated/dark.colors.generated.ts +56 -0
- package/src/tokens/generated/dark.css +54 -0
- package/src/tokens/generated/foundations.css +148 -0
- package/src/tokens/generated/foundations.generated.ts +443 -0
- package/src/tokens/generated/light.colors.generated.ts +56 -0
- package/src/tokens/generated/light.css +54 -0
- package/src/tokens/index.ts +74 -16
- package/src/tokens/layout.stories.tsx +3 -108
- package/src/tokens/colors.ts +0 -156
- package/src/tokens/motion.ts +0 -51
- package/src/tokens/radii.ts +0 -10
- package/src/tokens/shadows.ts +0 -34
- package/src/tokens/spacing.ts +0 -30
- package/src/tokens/typography.ts +0 -42
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { Dialog as SheetPrimitive } from "@base-ui/react/dialog";
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
|
|
@@ -8,48 +8,66 @@ import { cn } from "../../lib/utils";
|
|
|
8
8
|
|
|
9
9
|
const Sheet = SheetPrimitive.Root;
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
function SheetTrigger({ asChild, children, ...props }: React.ComponentProps<typeof SheetPrimitive.Trigger> & { asChild?: boolean }) {
|
|
12
|
+
return (
|
|
13
|
+
<SheetPrimitive.Trigger
|
|
14
|
+
{...(asChild && React.isValidElement(children) ? { render: children as React.ReactElement } : {})}
|
|
15
|
+
{...props}
|
|
16
|
+
>
|
|
17
|
+
{!asChild && children}
|
|
18
|
+
</SheetPrimitive.Trigger>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
function SheetClose({ asChild, children, ...props }: React.ComponentProps<typeof SheetPrimitive.Close> & { asChild?: boolean }) {
|
|
23
|
+
return (
|
|
24
|
+
<SheetPrimitive.Close
|
|
25
|
+
{...(asChild && React.isValidElement(children) ? { render: children as React.ReactElement } : {})}
|
|
26
|
+
{...props}
|
|
27
|
+
>
|
|
28
|
+
{!asChild && children}
|
|
29
|
+
</SheetPrimitive.Close>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
14
32
|
|
|
15
33
|
const SheetPortal = SheetPrimitive.Portal;
|
|
16
34
|
|
|
17
35
|
const SheetOverlay = React.forwardRef<
|
|
18
|
-
React.ComponentRef<typeof SheetPrimitive.
|
|
19
|
-
React.ComponentPropsWithoutRef<typeof SheetPrimitive.
|
|
36
|
+
React.ComponentRef<typeof SheetPrimitive.Backdrop>,
|
|
37
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Backdrop>
|
|
20
38
|
>(({ className, ...props }, ref) => (
|
|
21
|
-
<SheetPrimitive.
|
|
39
|
+
<SheetPrimitive.Backdrop
|
|
22
40
|
className={cn(
|
|
23
|
-
"
|
|
41
|
+
"bg-black/80 data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 transition-opacity",
|
|
24
42
|
className,
|
|
25
43
|
)}
|
|
26
44
|
{...props}
|
|
27
45
|
ref={ref}
|
|
28
46
|
/>
|
|
29
47
|
));
|
|
30
|
-
SheetOverlay.displayName =
|
|
48
|
+
SheetOverlay.displayName = "SheetOverlay";
|
|
31
49
|
|
|
32
50
|
const sheetClassName =
|
|
33
|
-
"
|
|
51
|
+
"bg-background text-foreground data-open:animate-in data-closed:animate-out fixed inset-x-0 bottom-0 z-50 flex max-h-[90dvh] min-h-[30dvh] flex-col gap-6 rounded-t-xl border border-b-0 border-l-0 border-r-0 p-4 pb-[max(1rem,env(safe-area-inset-bottom))] md:inset-y-0 md:right-0 md:bottom-auto md:left-auto md:h-full md:w-3/4 md:max-w-sm md:min-h-0 md:rounded-t-none md:rounded-l-xl md:border-t-0 md:border-r-0 md:border-b-0 md:border-l md:p-6 md:pb-6";
|
|
34
52
|
|
|
35
|
-
interface SheetContentProps extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.
|
|
53
|
+
interface SheetContentProps extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Popup> {}
|
|
36
54
|
|
|
37
55
|
const SheetContent = React.forwardRef<
|
|
38
|
-
React.ComponentRef<typeof SheetPrimitive.
|
|
56
|
+
React.ComponentRef<typeof SheetPrimitive.Popup>,
|
|
39
57
|
SheetContentProps
|
|
40
58
|
>(({ className, children, ...props }, ref) => (
|
|
41
59
|
<SheetPortal>
|
|
42
60
|
<SheetOverlay />
|
|
43
|
-
<SheetPrimitive.
|
|
44
|
-
<SheetPrimitive.Close className="
|
|
61
|
+
<SheetPrimitive.Popup ref={ref} className={cn(sheetClassName, className)} {...props}>
|
|
62
|
+
<SheetPrimitive.Close className="text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-ring absolute top-3 right-3 inline-flex size-11 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">
|
|
45
63
|
<X className="size-4.5" />
|
|
46
64
|
<span className="sr-only">Close</span>
|
|
47
65
|
</SheetPrimitive.Close>
|
|
48
66
|
{children}
|
|
49
|
-
</SheetPrimitive.
|
|
67
|
+
</SheetPrimitive.Popup>
|
|
50
68
|
</SheetPortal>
|
|
51
69
|
));
|
|
52
|
-
SheetContent.displayName =
|
|
70
|
+
SheetContent.displayName = "SheetContent";
|
|
53
71
|
|
|
54
72
|
const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
55
73
|
<div className={cn("flex flex-col gap-2 text-left", className)} {...props} />
|
|
@@ -68,7 +86,7 @@ const SheetTitle = React.forwardRef<
|
|
|
68
86
|
React.ComponentRef<typeof SheetPrimitive.Title>,
|
|
69
87
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
|
70
88
|
>(({ className, ...props }, ref) => (
|
|
71
|
-
<SheetPrimitive.Title ref={ref} className={cn("
|
|
89
|
+
<SheetPrimitive.Title ref={ref} className={cn("text-lg font-semibold tracking-tight", className)} {...props} />
|
|
72
90
|
));
|
|
73
91
|
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
|
74
92
|
|
|
@@ -78,7 +96,7 @@ const SheetDescription = React.forwardRef<
|
|
|
78
96
|
>(({ className, ...props }, ref) => (
|
|
79
97
|
<SheetPrimitive.Description
|
|
80
98
|
ref={ref}
|
|
81
|
-
className={cn("
|
|
99
|
+
className={cn("max-w-prose text-sm text-muted-foreground", className)}
|
|
82
100
|
{...props}
|
|
83
101
|
/>
|
|
84
102
|
));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar";
|
|
2
2
|
import type { ClassValue } from "clsx";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
@@ -84,7 +84,7 @@ function AvatarFallback({
|
|
|
84
84
|
<AvatarPrimitive.Fallback
|
|
85
85
|
data-slot="avatar-fallback"
|
|
86
86
|
className={cn(
|
|
87
|
-
"bg-
|
|
87
|
+
"bg-muted text-muted-foreground absolute inset-0 flex items-center justify-center rounded-full",
|
|
88
88
|
fallbackText[size],
|
|
89
89
|
className,
|
|
90
90
|
)}
|
|
@@ -119,7 +119,7 @@ function AvatarGroupCount({ className, ...props }: React.ComponentProps<"div">)
|
|
|
119
119
|
<div
|
|
120
120
|
data-slot="avatar-group-count"
|
|
121
121
|
className={cn(
|
|
122
|
-
"bg-
|
|
122
|
+
"bg-muted text-muted-foreground ring-background relative flex shrink-0 items-center justify-center rounded-full text-sm ring-2",
|
|
123
123
|
avatarSize[size],
|
|
124
124
|
groupCountSvg[size],
|
|
125
125
|
className,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
2
1
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
2
|
import * as React from "react";
|
|
4
3
|
|
|
@@ -9,12 +8,12 @@ const badgeVariants = cva(
|
|
|
9
8
|
{
|
|
10
9
|
variants: {
|
|
11
10
|
variant: {
|
|
12
|
-
default: "bg-
|
|
13
|
-
secondary: "bg-
|
|
14
|
-
destructive: "bg-destructive text-
|
|
15
|
-
outline: "border-
|
|
11
|
+
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
12
|
+
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/80",
|
|
13
|
+
destructive: "bg-destructive text-destructive-foreground [a&]:hover:bg-destructive/90",
|
|
14
|
+
outline: "border-border text-foreground [a&]:hover:bg-accent",
|
|
16
15
|
ghost: "[a&]:[a&]: [a&]: [a&]:",
|
|
17
|
-
link: "text-
|
|
16
|
+
link: "text-foreground underline-offset-4 [a&]:hover:underline",
|
|
18
17
|
},
|
|
19
18
|
},
|
|
20
19
|
defaultVariants: {
|
|
@@ -26,13 +25,10 @@ const badgeVariants = cva(
|
|
|
26
25
|
function Badge({
|
|
27
26
|
className,
|
|
28
27
|
variant = "default",
|
|
29
|
-
asChild = false,
|
|
30
28
|
...props
|
|
31
|
-
}: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants>
|
|
32
|
-
const Comp = asChild ? Slot : "span";
|
|
33
|
-
|
|
29
|
+
}: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
|
|
34
30
|
return (
|
|
35
|
-
<
|
|
31
|
+
<span
|
|
36
32
|
data-slot="badge"
|
|
37
33
|
data-variant={variant}
|
|
38
34
|
className={cn(badgeVariants({ variant }), className)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Button as BaseButton } from "@base-ui/react/button";
|
|
2
2
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
@@ -6,21 +6,21 @@ import { cn } from "../../lib/utils";
|
|
|
6
6
|
import { Spinner } from "../feedback/spinner";
|
|
7
7
|
|
|
8
8
|
const buttonVariants = cva(
|
|
9
|
-
"inline-flex min-h-
|
|
9
|
+
"inline-flex min-h-9 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md border border-transparent px-4 py-2 text-sm 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
13
|
default:
|
|
14
|
-
"
|
|
14
|
+
"bg-primary text-primary-foreground hover:bg-primary/90",
|
|
15
15
|
destructive:
|
|
16
|
-
"
|
|
16
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
17
17
|
outline:
|
|
18
|
-
"border-
|
|
18
|
+
"border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
19
19
|
secondary:
|
|
20
|
-
"border-
|
|
20
|
+
"border-input bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
21
21
|
ghost:
|
|
22
|
-
"border-transparent bg-transparent
|
|
23
|
-
link: "border-transparent bg-transparent px-0 text-
|
|
22
|
+
"border-transparent bg-transparent hover:bg-accent hover:text-accent-foreground",
|
|
23
|
+
link: "border-transparent bg-transparent px-0 text-primary underline-offset-4 hover:underline",
|
|
24
24
|
},
|
|
25
25
|
size: {
|
|
26
26
|
sm: "min-h-11 px-2.5 text-sm",
|
|
@@ -60,9 +60,38 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
60
60
|
},
|
|
61
61
|
ref,
|
|
62
62
|
) => {
|
|
63
|
-
|
|
63
|
+
if (asChild && React.isValidElement(children)) {
|
|
64
|
+
const child = children as React.ReactElement<{ children?: React.ReactNode }>;
|
|
65
|
+
const renderedChild = React.cloneElement(child, {
|
|
66
|
+
children: isLoading ? (
|
|
67
|
+
<>
|
|
68
|
+
<span className="opacity-0" aria-hidden="true">{child.props.children}</span>
|
|
69
|
+
<span className="absolute inset-0 inline-flex items-center justify-center">
|
|
70
|
+
<Spinner size="sm" aria-hidden="true" />
|
|
71
|
+
</span>
|
|
72
|
+
<span className="sr-only">{loadingLabel}</span>
|
|
73
|
+
</>
|
|
74
|
+
) : (
|
|
75
|
+
child.props.children
|
|
76
|
+
),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<BaseButton
|
|
81
|
+
{...props}
|
|
82
|
+
render={renderedChild}
|
|
83
|
+
nativeButton={false}
|
|
84
|
+
className={cn(buttonVariants({ variant, size, className }), isLoading && "relative")}
|
|
85
|
+
disabled={disabled || isLoading}
|
|
86
|
+
aria-busy={isLoading}
|
|
87
|
+
aria-label={isLoading ? loadingLabel : ariaLabel}
|
|
88
|
+
ref={ref}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
64
92
|
return (
|
|
65
|
-
<
|
|
93
|
+
<button
|
|
94
|
+
{...props}
|
|
66
95
|
className={cn(buttonVariants({ variant, size, className }), isLoading && "relative")}
|
|
67
96
|
style={{
|
|
68
97
|
...props.style,
|
|
@@ -73,11 +102,10 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
73
102
|
disabled={disabled || isLoading}
|
|
74
103
|
aria-busy={isLoading}
|
|
75
104
|
aria-label={isLoading ? loadingLabel : ariaLabel}
|
|
76
|
-
{...props}
|
|
77
105
|
>
|
|
78
106
|
{isLoading ? (
|
|
79
107
|
<>
|
|
80
|
-
<span className="opacity-0">{children}</span>
|
|
108
|
+
<span className="opacity-0" aria-hidden="true">{children}</span>
|
|
81
109
|
<span className="absolute inset-0 inline-flex items-center justify-center">
|
|
82
110
|
<Spinner size="sm" aria-hidden="true" />
|
|
83
111
|
</span>
|
|
@@ -86,7 +114,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
86
114
|
) : (
|
|
87
115
|
children
|
|
88
116
|
)}
|
|
89
|
-
</
|
|
117
|
+
</button>
|
|
90
118
|
);
|
|
91
119
|
},
|
|
92
120
|
);
|
|
@@ -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="
|
|
24
|
+
<p className="text-sm text-muted-foreground">
|
|
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="
|
|
50
|
+
<p className="text-sm text-muted-foreground">Minimal card with only content.</p>
|
|
51
51
|
</CardContent>
|
|
52
52
|
</Card>
|
|
53
53
|
),
|
|
@@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
7
7
|
<div
|
|
8
8
|
data-slot="card"
|
|
9
9
|
className={cn(
|
|
10
|
-
"bg-
|
|
10
|
+
"bg-card text-card-foreground flex flex-col rounded-xl border",
|
|
11
11
|
className,
|
|
12
12
|
)}
|
|
13
13
|
{...props}
|
|
@@ -36,7 +36,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
36
36
|
return (
|
|
37
37
|
<div
|
|
38
38
|
data-slot="card-description"
|
|
39
|
-
className={cn("
|
|
39
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
40
40
|
{...props}
|
|
41
41
|
/>
|
|
42
42
|
);
|
|
@@ -38,7 +38,7 @@ export const Disabled: Story = {
|
|
|
38
38
|
(locked)
|
|
39
39
|
</Label>
|
|
40
40
|
</div>
|
|
41
|
-
<p className="text-
|
|
41
|
+
<p className="text-muted-foreground text-xs">
|
|
42
42
|
The <code className="font-mono">peer-disabled</code> variant mutes the label when its paired
|
|
43
43
|
input is disabled.
|
|
44
44
|
</p>
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
4
3
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
4
|
import * as React from "react";
|
|
6
5
|
|
|
7
6
|
import { cn } from "../../lib/utils";
|
|
8
7
|
|
|
9
8
|
const labelVariants = cva(
|
|
10
|
-
"text-sm font-medium leading-none text-
|
|
9
|
+
"text-sm font-medium leading-none text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
11
10
|
);
|
|
12
11
|
|
|
13
12
|
const Label = React.forwardRef<
|
|
14
|
-
|
|
15
|
-
React.
|
|
13
|
+
HTMLLabelElement,
|
|
14
|
+
React.LabelHTMLAttributes<HTMLLabelElement> & VariantProps<typeof labelVariants>
|
|
16
15
|
>(({ className, ...props }, ref) => (
|
|
17
|
-
<
|
|
16
|
+
<label ref={ref} className={cn(labelVariants(), className)} {...props} />
|
|
18
17
|
));
|
|
19
|
-
Label.displayName =
|
|
18
|
+
Label.displayName = "Label";
|
|
20
19
|
|
|
21
20
|
export { Label };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { CheckCircle, Clock, XCircle } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import { StatusBadge, type StatusBadgeConfig } from "./status-badge";
|
|
5
|
+
|
|
6
|
+
type ConnectionStatus = "active" | "error" | "pending" | "revoked";
|
|
7
|
+
|
|
8
|
+
const connectionStatusConfig: Record<ConnectionStatus, StatusBadgeConfig> = {
|
|
9
|
+
active: { label: "Active", variant: "default", icon: <CheckCircle className="mr-1 size-3" /> },
|
|
10
|
+
error: { label: "Error", variant: "destructive", icon: <XCircle className="mr-1 size-3" /> },
|
|
11
|
+
pending: { label: "Pending", variant: "secondary", icon: <Clock className="mr-1 size-3" /> },
|
|
12
|
+
revoked: { label: "Revoked", variant: "outline" },
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const meta = {
|
|
16
|
+
title: "Primitives/StatusBadge",
|
|
17
|
+
component: StatusBadge<ConnectionStatus>,
|
|
18
|
+
tags: ["autodocs"],
|
|
19
|
+
} satisfies Meta<typeof StatusBadge<ConnectionStatus>>;
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
type Story = StoryObj<typeof meta>;
|
|
23
|
+
|
|
24
|
+
export const Default: Story = {
|
|
25
|
+
args: {
|
|
26
|
+
status: "active",
|
|
27
|
+
config: connectionStatusConfig,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const AllStatuses: Story = {
|
|
32
|
+
args: {
|
|
33
|
+
status: "active",
|
|
34
|
+
config: connectionStatusConfig,
|
|
35
|
+
},
|
|
36
|
+
render: () => (
|
|
37
|
+
<div className="flex flex-wrap gap-2">
|
|
38
|
+
<StatusBadge status="active" config={connectionStatusConfig} />
|
|
39
|
+
<StatusBadge status="error" config={connectionStatusConfig} />
|
|
40
|
+
<StatusBadge status="pending" config={connectionStatusConfig} />
|
|
41
|
+
<StatusBadge status="revoked" config={connectionStatusConfig} />
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const WithFallback: Story = {
|
|
47
|
+
args: {
|
|
48
|
+
status: null,
|
|
49
|
+
config: connectionStatusConfig,
|
|
50
|
+
fallback: { label: "Unknown", variant: "outline" },
|
|
51
|
+
},
|
|
52
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import { Badge, type badgeVariants } from "./badge";
|
|
4
|
+
import type { VariantProps } from "class-variance-authority";
|
|
5
|
+
|
|
6
|
+
export interface StatusBadgeConfig {
|
|
7
|
+
label: string;
|
|
8
|
+
variant?: VariantProps<typeof badgeVariants>["variant"];
|
|
9
|
+
className?: string;
|
|
10
|
+
icon?: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface StatusBadgeProps<T extends string> {
|
|
14
|
+
status: T | null | undefined;
|
|
15
|
+
config: Record<T, StatusBadgeConfig>;
|
|
16
|
+
fallback?: StatusBadgeConfig;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Renders a `Badge` from a status → { label, variant, icon } config map. */
|
|
20
|
+
export function StatusBadge<T extends string>({ status, config, fallback }: StatusBadgeProps<T>) {
|
|
21
|
+
const entry = (status != null ? config[status] : undefined) ?? fallback;
|
|
22
|
+
|
|
23
|
+
if (!entry) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Badge variant={entry.variant ?? "outline"} className={entry.className}>
|
|
29
|
+
{entry.icon}
|
|
30
|
+
{entry.label}
|
|
31
|
+
</Badge>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -15,15 +15,15 @@ export const CHART_COLORS = {
|
|
|
15
15
|
neutral: "var(--color-chart-neutral)",
|
|
16
16
|
|
|
17
17
|
background: "var(--color-surface-canvas)",
|
|
18
|
-
grid: "var(--color-border
|
|
18
|
+
grid: "var(--color-border)",
|
|
19
19
|
|
|
20
|
-
axis: "var(--color-
|
|
21
|
-
label: "var(--color-
|
|
20
|
+
axis: "var(--color-muted-foreground)",
|
|
21
|
+
label: "var(--color-muted-foreground)",
|
|
22
22
|
|
|
23
23
|
tooltip: {
|
|
24
24
|
background: "var(--color-surface-panel)",
|
|
25
|
-
text: "var(--color-
|
|
26
|
-
border: "var(--color-border
|
|
25
|
+
text: "var(--color-foreground)",
|
|
26
|
+
border: "var(--color-border)",
|
|
27
27
|
},
|
|
28
28
|
} as const;
|
|
29
29
|
|
package/src/hooks/index.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { useDebouncedValue } from "./use-debounced-value";
|
|
|
3
3
|
export { useFilterState } from "./use-filter-state";
|
|
4
4
|
export { useMediaQuery } from "./use-media-query";
|
|
5
5
|
export { useIsMobile } from "./use-mobile";
|
|
6
|
+
export { useSort } from "./use-sort";
|
|
6
7
|
export type { SortField, SortDirection, SortOption } from "./sort.types";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
|
|
3
|
+
import type { SortOption } from "./sort.types";
|
|
4
|
+
|
|
5
|
+
interface UseSortOptions {
|
|
6
|
+
initialSortOptions?: SortOption[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function useSort(options: UseSortOptions = {}) {
|
|
10
|
+
const [sortOptions, setSortOptions] = useState<SortOption[]>(options.initialSortOptions ?? []);
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
sortOptions,
|
|
14
|
+
addSortOption: (opt: SortOption) => setSortOptions((prev) => [...prev, opt]),
|
|
15
|
+
updateSortOption: (index: number, opt: SortOption) =>
|
|
16
|
+
setSortOptions((prev) => prev.map((o, i) => (i === index ? opt : o))),
|
|
17
|
+
removeSortOption: (index: number) =>
|
|
18
|
+
setSortOptions((prev) => prev.filter((_, i) => i !== index)),
|
|
19
|
+
};
|
|
20
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -10,16 +10,35 @@ export { copyToClipboard } from "./lib/clipboard";
|
|
|
10
10
|
export { createMemoryStorage } from "./lib/create-memory-storage";
|
|
11
11
|
export { cn } from "./lib/utils";
|
|
12
12
|
export {
|
|
13
|
-
|
|
14
|
-
COLOR_SYSTEM_ATTRIBUTE,
|
|
13
|
+
animations,
|
|
15
14
|
colors,
|
|
16
|
-
|
|
15
|
+
colorThemes,
|
|
17
16
|
colorTokenNames,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
containers,
|
|
18
|
+
easing,
|
|
19
|
+
fontFamilies,
|
|
20
|
+
fontFamiliesNative,
|
|
21
|
+
fontWeights,
|
|
22
|
+
leading,
|
|
23
|
+
radii,
|
|
24
|
+
shadows,
|
|
25
|
+
spacing,
|
|
26
|
+
textLineHeights,
|
|
27
|
+
textSizes,
|
|
28
|
+
tracking,
|
|
29
|
+
transitionDurations,
|
|
30
|
+
breakpoints,
|
|
31
|
+
zIndices,
|
|
32
|
+
} from "./tokens/index";
|
|
33
|
+
export type {
|
|
34
|
+
ColorMode,
|
|
35
|
+
ColorTheme,
|
|
36
|
+
ColorToken,
|
|
37
|
+
FontWeightToken,
|
|
38
|
+
LeadingToken,
|
|
39
|
+
RadiusToken,
|
|
40
|
+
ShadowToken,
|
|
41
|
+
SpacingToken,
|
|
42
|
+
TextSizeToken,
|
|
43
|
+
TrackingToken,
|
|
44
|
+
} from "./tokens/index";
|