@ponti-studios/ui 0.1.2 → 0.4.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 +988 -640
- package/dist/index.js.map +1 -1
- package/package.json +41 -41
- package/src/animations.css +42 -383
- package/src/components/data-display/accordion.tsx +22 -10
- package/src/components/data-display/filter-chip.stories.tsx +38 -0
- package/src/components/data-display/filter-chip.tsx +10 -4
- 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 -9
- package/src/components/feedback/alert.tsx +4 -4
- package/src/components/feedback/empty-state.tsx +16 -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 +3 -3
- 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 +14 -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 +47 -48
- package/src/components/forms/slider.stories.tsx +5 -2
- package/src/components/forms/slider.tsx +39 -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 +8 -8
- package/src/components/overlays/alert-dialog.tsx +22 -25
- package/src/components/overlays/command.tsx +98 -26
- 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 +49 -31
- package/src/components/overlays/popover.stories.tsx +2 -2
- package/src/components/overlays/popover.tsx +43 -14
- package/src/components/overlays/sheet.stories.tsx +2 -2
- package/src/components/overlays/sheet.tsx +51 -17
- package/src/components/primitives/avatar.tsx +9 -5
- package/src/components/primitives/badge.tsx +11 -12
- package/src/components/primitives/button.tsx +49 -23
- package/src/components/primitives/card.stories.tsx +2 -2
- package/src/components/primitives/card.tsx +2 -5
- 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 +7 -7
- 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 +100 -974
- package/src/tokens/chart-colors.stories.tsx +192 -0
- package/src/tokens/color-systems.stories.tsx +20 -112
- package/src/tokens/foundations.stories.tsx +58 -100
- package/src/tokens/generated/dark.colors.generated.ts +29 -0
- package/src/tokens/generated/dark.css +29 -0
- package/src/tokens/generated/foundations.css +148 -0
- package/src/tokens/generated/foundations.generated.ts +451 -0
- package/src/tokens/generated/light.colors.generated.ts +29 -0
- package/src/tokens/generated/light.css +29 -0
- package/src/tokens/index.ts +79 -16
- package/src/tokens/layout.stories.tsx +23 -91
- 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,78 @@ import { cn } from "../../lib/utils";
|
|
|
8
8
|
|
|
9
9
|
const Sheet = SheetPrimitive.Root;
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
function SheetTrigger({
|
|
12
|
+
asChild,
|
|
13
|
+
children,
|
|
14
|
+
...props
|
|
15
|
+
}: React.ComponentProps<typeof SheetPrimitive.Trigger> & { asChild?: boolean }) {
|
|
16
|
+
return (
|
|
17
|
+
<SheetPrimitive.Trigger
|
|
18
|
+
{...(asChild && React.isValidElement(children)
|
|
19
|
+
? { render: children as React.ReactElement }
|
|
20
|
+
: {})}
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
{!asChild && children}
|
|
24
|
+
</SheetPrimitive.Trigger>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
12
27
|
|
|
13
|
-
|
|
28
|
+
function SheetClose({
|
|
29
|
+
asChild,
|
|
30
|
+
children,
|
|
31
|
+
...props
|
|
32
|
+
}: React.ComponentProps<typeof SheetPrimitive.Close> & { asChild?: boolean }) {
|
|
33
|
+
return (
|
|
34
|
+
<SheetPrimitive.Close
|
|
35
|
+
{...(asChild && React.isValidElement(children)
|
|
36
|
+
? { render: children as React.ReactElement }
|
|
37
|
+
: {})}
|
|
38
|
+
{...props}
|
|
39
|
+
>
|
|
40
|
+
{!asChild && children}
|
|
41
|
+
</SheetPrimitive.Close>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
14
44
|
|
|
15
45
|
const SheetPortal = SheetPrimitive.Portal;
|
|
16
46
|
|
|
17
47
|
const SheetOverlay = React.forwardRef<
|
|
18
|
-
React.ComponentRef<typeof SheetPrimitive.
|
|
19
|
-
React.ComponentPropsWithoutRef<typeof SheetPrimitive.
|
|
48
|
+
React.ComponentRef<typeof SheetPrimitive.Backdrop>,
|
|
49
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Backdrop>
|
|
20
50
|
>(({ className, ...props }, ref) => (
|
|
21
|
-
<SheetPrimitive.
|
|
51
|
+
<SheetPrimitive.Backdrop
|
|
22
52
|
className={cn(
|
|
23
|
-
"
|
|
53
|
+
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/80 transition-opacity",
|
|
24
54
|
className,
|
|
25
55
|
)}
|
|
26
56
|
{...props}
|
|
27
57
|
ref={ref}
|
|
28
58
|
/>
|
|
29
59
|
));
|
|
30
|
-
SheetOverlay.displayName =
|
|
60
|
+
SheetOverlay.displayName = "SheetOverlay";
|
|
31
61
|
|
|
32
62
|
const sheetClassName =
|
|
33
|
-
"
|
|
63
|
+
"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
64
|
|
|
35
|
-
interface SheetContentProps extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.
|
|
65
|
+
interface SheetContentProps extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Popup> {}
|
|
36
66
|
|
|
37
67
|
const SheetContent = React.forwardRef<
|
|
38
|
-
React.ComponentRef<typeof SheetPrimitive.
|
|
68
|
+
React.ComponentRef<typeof SheetPrimitive.Popup>,
|
|
39
69
|
SheetContentProps
|
|
40
70
|
>(({ className, children, ...props }, ref) => (
|
|
41
71
|
<SheetPortal>
|
|
42
72
|
<SheetOverlay />
|
|
43
|
-
<SheetPrimitive.
|
|
44
|
-
<SheetPrimitive.Close className="
|
|
73
|
+
<SheetPrimitive.Popup ref={ref} className={cn(sheetClassName, className)} {...props}>
|
|
74
|
+
<SheetPrimitive.Close className="text-muted-foreground hover:bg-muted hover:text-muted-foreground focus-visible:outline-ring absolute top-3 right-3 inline-flex size-9 items-center justify-center rounded-md border border-transparent transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none md:top-4 md:right-4">
|
|
45
75
|
<X className="size-4.5" />
|
|
46
76
|
<span className="sr-only">Close</span>
|
|
47
77
|
</SheetPrimitive.Close>
|
|
48
78
|
{children}
|
|
49
|
-
</SheetPrimitive.
|
|
79
|
+
</SheetPrimitive.Popup>
|
|
50
80
|
</SheetPortal>
|
|
51
81
|
));
|
|
52
|
-
SheetContent.displayName =
|
|
82
|
+
SheetContent.displayName = "SheetContent";
|
|
53
83
|
|
|
54
84
|
const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
55
85
|
<div className={cn("flex flex-col gap-2 text-left", className)} {...props} />
|
|
@@ -68,7 +98,11 @@ const SheetTitle = React.forwardRef<
|
|
|
68
98
|
React.ComponentRef<typeof SheetPrimitive.Title>,
|
|
69
99
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
|
70
100
|
>(({ className, ...props }, ref) => (
|
|
71
|
-
<SheetPrimitive.Title
|
|
101
|
+
<SheetPrimitive.Title
|
|
102
|
+
ref={ref}
|
|
103
|
+
className={cn("text-lg font-semibold tracking-tight", className)}
|
|
104
|
+
{...props}
|
|
105
|
+
/>
|
|
72
106
|
));
|
|
73
107
|
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
|
74
108
|
|
|
@@ -78,7 +112,7 @@ const SheetDescription = React.forwardRef<
|
|
|
78
112
|
>(({ className, ...props }, ref) => (
|
|
79
113
|
<SheetPrimitive.Description
|
|
80
114
|
ref={ref}
|
|
81
|
-
className={cn("
|
|
115
|
+
className={cn("text-muted-foreground max-w-prose text-sm", className)}
|
|
82
116
|
{...props}
|
|
83
117
|
/>
|
|
84
118
|
));
|
|
@@ -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
|
|
|
@@ -46,7 +46,11 @@ function Avatar({
|
|
|
46
46
|
<AvatarContext.Provider value={{ size }}>
|
|
47
47
|
<AvatarPrimitive.Root
|
|
48
48
|
data-slot="avatar"
|
|
49
|
-
className={cn(
|
|
49
|
+
className={cn(
|
|
50
|
+
"relative inline-flex shrink-0 rounded-full select-none",
|
|
51
|
+
avatarSize[size],
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
50
54
|
{...props}
|
|
51
55
|
>
|
|
52
56
|
{children}
|
|
@@ -54,7 +58,7 @@ function Avatar({
|
|
|
54
58
|
<span
|
|
55
59
|
data-slot="avatar-badge"
|
|
56
60
|
className={cn(
|
|
57
|
-
"bg-
|
|
61
|
+
"bg-primary ring-background absolute right-0 bottom-0 z-10 rounded-full ring-2",
|
|
58
62
|
badgeDotSize[size],
|
|
59
63
|
)}
|
|
60
64
|
/>
|
|
@@ -84,7 +88,7 @@ function AvatarFallback({
|
|
|
84
88
|
<AvatarPrimitive.Fallback
|
|
85
89
|
data-slot="avatar-fallback"
|
|
86
90
|
className={cn(
|
|
87
|
-
"bg-
|
|
91
|
+
"bg-muted text-muted-foreground absolute inset-0 flex items-center justify-center rounded-full",
|
|
88
92
|
fallbackText[size],
|
|
89
93
|
className,
|
|
90
94
|
)}
|
|
@@ -119,7 +123,7 @@ function AvatarGroupCount({ className, ...props }: React.ComponentProps<"div">)
|
|
|
119
123
|
<div
|
|
120
124
|
data-slot="avatar-group-count"
|
|
121
125
|
className={cn(
|
|
122
|
-
"bg-
|
|
126
|
+
"bg-muted text-muted-foreground ring-background relative flex shrink-0 items-center justify-center rounded-full text-sm ring-2",
|
|
123
127
|
avatarSize[size],
|
|
124
128
|
groupCountSvg[size],
|
|
125
129
|
className,
|
|
@@ -1,20 +1,22 @@
|
|
|
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
|
|
|
5
4
|
import { cn } from "../../lib/utils";
|
|
6
5
|
|
|
7
6
|
const badgeVariants = cva(
|
|
8
|
-
"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden whitespace-nowrap rounded border border-transparent px-1.5 py-px text-xs font-medium [&>svg]:size-3 [&>svg]:pointer-events-none aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
7
|
+
"state-settle inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden whitespace-nowrap rounded border border-transparent px-1.5 py-px text-xs font-medium [&>svg]:size-3 [&>svg]:pointer-events-none aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
|
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-muted",
|
|
16
15
|
ghost: "[a&]:[a&]: [a&]: [a&]:",
|
|
17
|
-
link: "text-
|
|
16
|
+
link: "text-foreground underline-offset-4 [a&]:hover:underline",
|
|
17
|
+
/** Quiet metadata marker for real reference IDs (case/appointment/feed
|
|
18
|
+
* numbers) — never for decorative sequence markers. */
|
|
19
|
+
ref: "ref-tag border-transparent bg-transparent px-0 py-0 font-normal",
|
|
18
20
|
},
|
|
19
21
|
},
|
|
20
22
|
defaultVariants: {
|
|
@@ -26,13 +28,10 @@ const badgeVariants = cva(
|
|
|
26
28
|
function Badge({
|
|
27
29
|
className,
|
|
28
30
|
variant = "default",
|
|
29
|
-
asChild = false,
|
|
30
31
|
...props
|
|
31
|
-
}: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants>
|
|
32
|
-
const Comp = asChild ? Slot : "span";
|
|
33
|
-
|
|
32
|
+
}: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
|
|
34
33
|
return (
|
|
35
|
-
<
|
|
34
|
+
<span
|
|
36
35
|
data-slot="badge"
|
|
37
36
|
data-variant={variant}
|
|
38
37
|
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,28 +6,22 @@ import { cn } from "../../lib/utils";
|
|
|
6
6
|
import { Spinner } from "../feedback/spinner";
|
|
7
7
|
|
|
8
8
|
const buttonVariants = cva(
|
|
9
|
-
"inline-flex
|
|
9
|
+
"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md border border-transparent font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
10
10
|
{
|
|
11
11
|
variants: {
|
|
12
12
|
variant: {
|
|
13
|
-
default:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
secondary:
|
|
20
|
-
"border-default bg-panel text-primary hover:bg-surface-hover active:bg-surface-pressed disabled:border-subtle disabled:bg-panel disabled:text-disabled",
|
|
21
|
-
ghost:
|
|
22
|
-
"border-transparent bg-transparent text-secondary hover:bg-surface-hover hover:text-primary active:bg-surface-pressed disabled:text-disabled",
|
|
23
|
-
link: "border-transparent bg-transparent px-0 text-accent hover:text-accent-hover disabled:text-disabled underline-offset-4 hover:underline",
|
|
13
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
14
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
15
|
+
outline: "border-input bg-background hover:bg-muted hover:text-muted-foreground",
|
|
16
|
+
secondary: "border-input bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
17
|
+
ghost: "border-transparent bg-transparent hover:bg-muted hover:text-muted-foreground",
|
|
18
|
+
link: "border-transparent bg-transparent px-0 text-primary underline-offset-4 hover:underline",
|
|
24
19
|
},
|
|
25
20
|
size: {
|
|
26
|
-
sm: "min-h-
|
|
27
|
-
md: "min-h-
|
|
28
|
-
lg: "min-h-
|
|
29
|
-
|
|
30
|
-
icon: "size-11 rounded-md p-0",
|
|
21
|
+
sm: "min-h-8 px-2.5 text-xs",
|
|
22
|
+
md: "min-h-9 px-3 text-sm",
|
|
23
|
+
lg: "min-h-10 px-4 text-base",
|
|
24
|
+
icon: "size-9 rounded-md p-0",
|
|
31
25
|
},
|
|
32
26
|
},
|
|
33
27
|
defaultVariants: {
|
|
@@ -60,9 +54,40 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
60
54
|
},
|
|
61
55
|
ref,
|
|
62
56
|
) => {
|
|
63
|
-
|
|
57
|
+
if (asChild && React.isValidElement(children)) {
|
|
58
|
+
const child = children as React.ReactElement<{ children?: React.ReactNode }>;
|
|
59
|
+
const renderedChild = React.cloneElement(child, {
|
|
60
|
+
children: isLoading ? (
|
|
61
|
+
<>
|
|
62
|
+
<span className="opacity-0" aria-hidden="true">
|
|
63
|
+
{child.props.children}
|
|
64
|
+
</span>
|
|
65
|
+
<span className="absolute inset-0 inline-flex items-center justify-center">
|
|
66
|
+
<Spinner size="sm" aria-hidden="true" />
|
|
67
|
+
</span>
|
|
68
|
+
<span className="sr-only">{loadingLabel}</span>
|
|
69
|
+
</>
|
|
70
|
+
) : (
|
|
71
|
+
child.props.children
|
|
72
|
+
),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<BaseButton
|
|
77
|
+
{...props}
|
|
78
|
+
render={renderedChild}
|
|
79
|
+
nativeButton={false}
|
|
80
|
+
className={cn(buttonVariants({ variant, size, className }), isLoading && "relative")}
|
|
81
|
+
disabled={disabled || isLoading}
|
|
82
|
+
aria-busy={isLoading}
|
|
83
|
+
aria-label={isLoading ? loadingLabel : ariaLabel}
|
|
84
|
+
ref={ref}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
64
88
|
return (
|
|
65
|
-
<
|
|
89
|
+
<button
|
|
90
|
+
{...props}
|
|
66
91
|
className={cn(buttonVariants({ variant, size, className }), isLoading && "relative")}
|
|
67
92
|
style={{
|
|
68
93
|
...props.style,
|
|
@@ -73,11 +98,12 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
73
98
|
disabled={disabled || isLoading}
|
|
74
99
|
aria-busy={isLoading}
|
|
75
100
|
aria-label={isLoading ? loadingLabel : ariaLabel}
|
|
76
|
-
{...props}
|
|
77
101
|
>
|
|
78
102
|
{isLoading ? (
|
|
79
103
|
<>
|
|
80
|
-
<span className="opacity-0">
|
|
104
|
+
<span className="opacity-0" aria-hidden="true">
|
|
105
|
+
{children}
|
|
106
|
+
</span>
|
|
81
107
|
<span className="absolute inset-0 inline-flex items-center justify-center">
|
|
82
108
|
<Spinner size="sm" aria-hidden="true" />
|
|
83
109
|
</span>
|
|
@@ -86,7 +112,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
86
112
|
) : (
|
|
87
113
|
children
|
|
88
114
|
)}
|
|
89
|
-
</
|
|
115
|
+
</button>
|
|
90
116
|
);
|
|
91
117
|
},
|
|
92
118
|
);
|
|
@@ -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-muted-foreground text-sm">
|
|
25
25
|
Card body content goes here. It can be any React node.
|
|
26
26
|
</p>
|
|
27
27
|
</CardContent>
|
|
@@ -47,7 +47,7 @@ export const Minimal: Story = {
|
|
|
47
47
|
render: () => (
|
|
48
48
|
<Card className="w-80">
|
|
49
49
|
<CardContent>
|
|
50
|
-
<p className="
|
|
50
|
+
<p className="text-muted-foreground text-sm">Minimal card with only content.</p>
|
|
51
51
|
</CardContent>
|
|
52
52
|
</Card>
|
|
53
53
|
),
|
|
@@ -6,10 +6,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
6
6
|
return (
|
|
7
7
|
<div
|
|
8
8
|
data-slot="card"
|
|
9
|
-
className={cn(
|
|
10
|
-
"bg-panel text-primary border-default flex flex-col rounded-md border",
|
|
11
|
-
className,
|
|
12
|
-
)}
|
|
9
|
+
className={cn("bg-card text-card-foreground flex flex-col rounded-xl border", className)}
|
|
13
10
|
{...props}
|
|
14
11
|
/>
|
|
15
12
|
);
|
|
@@ -36,7 +33,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
36
33
|
return (
|
|
37
34
|
<div
|
|
38
35
|
data-slot="card-description"
|
|
39
|
-
className={cn("
|
|
36
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
40
37
|
{...props}
|
|
41
38
|
/>
|
|
42
39
|
);
|
|
@@ -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
|
+
}
|
|
@@ -14,16 +14,16 @@ export const CHART_COLORS = {
|
|
|
14
14
|
negative: "var(--color-chart-negative)",
|
|
15
15
|
neutral: "var(--color-chart-neutral)",
|
|
16
16
|
|
|
17
|
-
background: "var(--color-
|
|
18
|
-
grid: "var(--color-border
|
|
17
|
+
background: "var(--color-background)",
|
|
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
|
-
background: "var(--color-
|
|
25
|
-
text: "var(--color-
|
|
26
|
-
border: "var(--color-border
|
|
24
|
+
background: "var(--color-card)",
|
|
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";
|