@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.
Files changed (114) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -7
  3. package/THIRD_PARTY_NOTICES.md +17 -0
  4. package/dist/index.js +1028 -638
  5. package/dist/index.js.map +1 -1
  6. package/package.json +30 -32
  7. package/src/animations.css +16 -434
  8. package/src/components/data-display/accordion.tsx +10 -7
  9. package/src/components/data-display/filter-chip.tsx +2 -2
  10. package/src/components/data-display/index.ts +5 -0
  11. package/src/components/data-display/metric-card.stories.tsx +48 -0
  12. package/src/components/data-display/metric-card.tsx +20 -0
  13. package/src/components/data-display/sort-controls.stories.tsx +45 -0
  14. package/src/components/data-display/sort-controls.tsx +99 -0
  15. package/src/components/data-display/sort-row.tsx +74 -0
  16. package/src/components/data-display/table.tsx +5 -5
  17. package/src/components/feedback/alert.tsx +4 -4
  18. package/src/components/feedback/empty-state.tsx +6 -6
  19. package/src/components/feedback/file-upload-status-badge.stories.tsx +28 -0
  20. package/src/components/feedback/file-upload-status-badge.tsx +33 -0
  21. package/src/components/feedback/file-upload-status.stories.tsx +57 -0
  22. package/src/components/feedback/file-upload-status.tsx +152 -0
  23. package/src/components/feedback/index.ts +9 -0
  24. package/src/components/feedback/progress-bar.stories.tsx +39 -0
  25. package/src/components/feedback/progress-bar.tsx +21 -0
  26. package/src/components/feedback/progress.stories.tsx +8 -6
  27. package/src/components/feedback/progress.tsx +1 -1
  28. package/src/components/feedback/skeleton.stories.tsx +36 -0
  29. package/src/components/feedback/skeleton.tsx +7 -0
  30. package/src/components/feedback/spinner.tsx +2 -2
  31. package/src/components/feedback/update-guard.stories.tsx +32 -0
  32. package/src/components/feedback/update-guard.tsx +280 -0
  33. package/src/components/forms/calendar.tsx +11 -11
  34. package/src/components/forms/date-month-select.stories.tsx +26 -0
  35. package/src/components/forms/date-month-select.tsx +70 -0
  36. package/src/components/forms/date-picker.tsx +3 -3
  37. package/src/components/forms/drop-zone.stories.tsx +56 -0
  38. package/src/components/forms/drop-zone.tsx +146 -0
  39. package/src/components/forms/entity-select.stories.tsx +65 -0
  40. package/src/components/forms/entity-select.tsx +79 -0
  41. package/src/components/forms/field.tsx +3 -3
  42. package/src/components/forms/group-by-select.stories.tsx +26 -0
  43. package/src/components/forms/group-by-select.tsx +47 -0
  44. package/src/components/forms/index.ts +10 -0
  45. package/src/components/forms/input.stories.tsx +4 -4
  46. package/src/components/forms/input.tsx +1 -1
  47. package/src/components/forms/passkey-management.stories.tsx +81 -0
  48. package/src/components/forms/passkey-management.tsx +138 -0
  49. package/src/components/forms/radio-group.tsx +14 -13
  50. package/src/components/forms/select.stories.tsx +4 -4
  51. package/src/components/forms/select.tsx +45 -53
  52. package/src/components/forms/slider.stories.tsx +5 -2
  53. package/src/components/forms/slider.tsx +27 -17
  54. package/src/components/forms/stepper.tsx +4 -4
  55. package/src/components/forms/switch.stories.tsx +1 -1
  56. package/src/components/forms/switch.tsx +2 -2
  57. package/src/components/forms/textarea.stories.tsx +2 -2
  58. package/src/components/forms/textarea.tsx +1 -1
  59. package/src/components/layout/index.ts +3 -0
  60. package/src/components/layout/scroll-area.stories.tsx +4 -4
  61. package/src/components/layout/scroll-area.tsx +23 -21
  62. package/src/components/layout/section-intro.stories.tsx +29 -0
  63. package/src/components/layout/section-intro.tsx +31 -0
  64. package/src/components/layout/surface-panel.stories.tsx +32 -0
  65. package/src/components/layout/surface-panel.tsx +15 -0
  66. package/src/components/navigation/app-navigation.stories.tsx +2 -2
  67. package/src/components/navigation/app-navigation.tsx +13 -13
  68. package/src/components/navigation/index.ts +4 -0
  69. package/src/components/navigation/pagination-controls.stories.tsx +41 -0
  70. package/src/components/navigation/pagination-controls.tsx +50 -0
  71. package/src/components/navigation/route-link.stories.tsx +30 -0
  72. package/src/components/navigation/route-link.tsx +42 -0
  73. package/src/components/navigation/tabs.stories.tsx +5 -5
  74. package/src/components/navigation/tabs.tsx +7 -7
  75. package/src/components/overlays/alert-dialog.tsx +21 -46
  76. package/src/components/overlays/command.tsx +51 -82
  77. package/src/components/overlays/dialog.tsx +5 -5
  78. package/src/components/overlays/dropdown-menu.stories.tsx +0 -5
  79. package/src/components/overlays/dropdown-menu.tsx +44 -142
  80. package/src/components/overlays/popover.stories.tsx +2 -2
  81. package/src/components/overlays/popover.tsx +34 -16
  82. package/src/components/overlays/sheet.stories.tsx +2 -2
  83. package/src/components/overlays/sheet.tsx +35 -17
  84. package/src/components/primitives/avatar.tsx +3 -3
  85. package/src/components/primitives/badge.tsx +7 -11
  86. package/src/components/primitives/button.tsx +41 -13
  87. package/src/components/primitives/card.stories.tsx +2 -2
  88. package/src/components/primitives/card.tsx +2 -2
  89. package/src/components/primitives/index.ts +2 -0
  90. package/src/components/primitives/label.stories.tsx +1 -1
  91. package/src/components/primitives/label.tsx +5 -6
  92. package/src/components/primitives/status-badge.stories.tsx +52 -0
  93. package/src/components/primitives/status-badge.tsx +33 -0
  94. package/src/constants/chart-colors.ts +5 -5
  95. package/src/hooks/index.ts +1 -0
  96. package/src/hooks/use-sort.ts +20 -0
  97. package/src/index.ts +30 -11
  98. package/src/styles.css +76 -1040
  99. package/src/tokens/color-systems.stories.tsx +9 -119
  100. package/src/tokens/foundations.stories.tsx +21 -113
  101. package/src/tokens/generated/dark.colors.generated.ts +56 -0
  102. package/src/tokens/generated/dark.css +54 -0
  103. package/src/tokens/generated/foundations.css +148 -0
  104. package/src/tokens/generated/foundations.generated.ts +443 -0
  105. package/src/tokens/generated/light.colors.generated.ts +56 -0
  106. package/src/tokens/generated/light.css +54 -0
  107. package/src/tokens/index.ts +74 -16
  108. package/src/tokens/layout.stories.tsx +3 -108
  109. package/src/tokens/colors.ts +0 -156
  110. package/src/tokens/motion.ts +0 -51
  111. package/src/tokens/radii.ts +0 -10
  112. package/src/tokens/shadows.ts +0 -34
  113. package/src/tokens/spacing.ts +0 -30
  114. package/src/tokens/typography.ts +0 -42
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import * as SheetPrimitive from "@radix-ui/react-dialog";
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
- const SheetTrigger = SheetPrimitive.Trigger;
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
- const SheetClose = SheetPrimitive.Close;
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.Overlay>,
19
- React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
36
+ React.ComponentRef<typeof SheetPrimitive.Backdrop>,
37
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Backdrop>
20
38
  >(({ className, ...props }, ref) => (
21
- <SheetPrimitive.Overlay
39
+ <SheetPrimitive.Backdrop
22
40
  className={cn(
23
- "overlay-backdrop 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 transition-opacity",
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 = SheetPrimitive.Overlay.displayName;
48
+ SheetOverlay.displayName = "SheetOverlay";
31
49
 
32
50
  const sheetClassName =
33
- "sheet-responsive sheet-edge-responsive bg-canvas text-primary border-subtle fixed inset-x-0 bottom-0 z-50 flex max-h-[90dvh] min-h-[30dvh] flex-col gap-6 rounded-t-xl border border-r-0 border-b-0 border-l-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";
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.Content> {}
53
+ interface SheetContentProps extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Popup> {}
36
54
 
37
55
  const SheetContent = React.forwardRef<
38
- React.ComponentRef<typeof SheetPrimitive.Content>,
56
+ React.ComponentRef<typeof SheetPrimitive.Popup>,
39
57
  SheetContentProps
40
58
  >(({ className, children, ...props }, ref) => (
41
59
  <SheetPortal>
42
60
  <SheetOverlay />
43
- <SheetPrimitive.Content ref={ref} className={cn(sheetClassName, className)} {...props}>
44
- <SheetPrimitive.Close className="sheet-glass-control text-secondary hover:bg-inset/70 hover:text-primary 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">
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.Content>
67
+ </SheetPrimitive.Popup>
50
68
  </SheetPortal>
51
69
  ));
52
- SheetContent.displayName = SheetPrimitive.Content.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("heading-2 text-primary", className)} {...props} />
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("body-2 text-secondary max-w-prose", className)}
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 * as AvatarPrimitive from "@radix-ui/react-avatar";
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-inset text-secondary absolute inset-0 flex items-center justify-center rounded-full",
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-inset text-secondary ring-background relative flex shrink-0 items-center justify-center rounded-full text-sm ring-2",
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-accent text-on-accent [a&]:[a&]: [a&]: [a&]:",
13
- secondary: "bg-panel text-primary [a&]:[a&]: [a&]: [a&]:",
14
- destructive: "bg-destructive text-on-destructive [a&]:[a&]: [a&]: [a&]:",
15
- outline: "border-default text-primary [a&]:[a&]: [a&]: [a&]:",
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-primary underline-offset-4 [a&]:[a&]:hover:underline [a&]: [a&]: [a&]:",
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> & { asChild?: boolean }) {
32
- const Comp = asChild ? Slot : "span";
33
-
29
+ }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
34
30
  return (
35
- <Comp
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 { Slot } from "@radix-ui/react-slot";
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-11 !cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-md border px-3 py-1.5 text-sm font-medium leading-5 transition-colors duration-150 focus-visible:ring-2 focus-visible:ring-focus focus-visible:ring-offset-2 focus-visible:outline-none disabled:pointer-events-none disabled:!cursor-default [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
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
- "border-transparent bg-accent text-on-accent hover:bg-accent-hover active:bg-accent-pressed disabled:border-subtle disabled:bg-panel disabled:text-disabled",
14
+ "bg-primary text-primary-foreground hover:bg-primary/90",
15
15
  destructive:
16
- "border-transparent bg-destructive text-on-destructive hover:bg-destructive-hover active:bg-destructive-pressed disabled:border-subtle disabled:bg-panel disabled:text-disabled",
16
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
17
17
  outline:
18
- "border-default bg-transparent text-primary hover:bg-surface-hover active:bg-surface-pressed disabled:border-subtle disabled:text-disabled",
18
+ "border-input bg-background hover:bg-accent hover:text-accent-foreground",
19
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",
20
+ "border-input bg-secondary text-secondary-foreground hover:bg-secondary/80",
21
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",
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
- const Comp = asChild ? Slot : "button";
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
- <Comp
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
- </Comp>
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="body-2 text-secondary">
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="body-2 text-secondary">Minimal card with only content.</p>
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-panel text-primary border-default flex flex-col rounded-md border",
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("body-4 text-secondary", className)}
39
+ className={cn("text-sm text-muted-foreground", className)}
40
40
  {...props}
41
41
  />
42
42
  );
@@ -11,3 +11,5 @@ export {
11
11
  CardTitle,
12
12
  } from "./card";
13
13
  export { Label } from "./label";
14
+ export { StatusBadge } from "./status-badge";
15
+ export type { StatusBadgeConfig, StatusBadgeProps } from "./status-badge";
@@ -38,7 +38,7 @@ export const Disabled: Story = {
38
38
  (locked)
39
39
  </Label>
40
40
  </div>
41
- <p className="text-secondary text-xs">
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-primary peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
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
- React.ComponentRef<typeof LabelPrimitive.Root>,
15
- React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
13
+ HTMLLabelElement,
14
+ React.LabelHTMLAttributes<HTMLLabelElement> & VariantProps<typeof labelVariants>
16
15
  >(({ className, ...props }, ref) => (
17
- <LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
16
+ <label ref={ref} className={cn(labelVariants(), className)} {...props} />
18
17
  ));
19
- Label.displayName = LabelPrimitive.Root.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-subtle)",
18
+ grid: "var(--color-border)",
19
19
 
20
- axis: "var(--color-text-tertiary)",
21
- label: "var(--color-text-secondary)",
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-text-primary)",
26
- border: "var(--color-border-default)",
25
+ text: "var(--color-foreground)",
26
+ border: "var(--color-border)",
27
27
  },
28
28
  } as const;
29
29
 
@@ -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
- COLOR_MODE_ATTRIBUTE,
14
- COLOR_SYSTEM_ATTRIBUTE,
13
+ animations,
15
14
  colors,
16
- colorSystems,
15
+ colorThemes,
17
16
  colorTokenNames,
18
- } from "./tokens/colors";
19
- export type { ColorMode, ColorSystem, ColorToken } from "./tokens/colors";
20
- export { durations, easingWeb, translateDistances } from "./tokens/motion";
21
- export { radii } from "./tokens/radii";
22
- export type { RadiusToken } from "./tokens/radii";
23
- export { shadowsNative, shadowsWeb } from "./tokens/shadows";
24
- export { contentWidths, spacing } from "./tokens/spacing";
25
- export type { ContentWidthToken, SpacingToken } from "./tokens/spacing";
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";