@salesforce/webapp-template-feature-react-chart-experimental 1.44.0 → 1.45.1

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 (20) hide show
  1. package/dist/CHANGELOG.md +16 -0
  2. package/dist/force-app/main/default/webapplications/feature-react-chart/package-lock.json +1183 -402
  3. package/dist/force-app/main/default/webapplications/feature-react-chart/package.json +1 -3
  4. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/alert.tsx +17 -13
  5. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/button.tsx +35 -22
  6. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/card.tsx +27 -12
  7. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/dialog.tsx +143 -0
  8. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/field.tsx +157 -46
  9. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/index.ts +1 -0
  10. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/input.tsx +3 -3
  11. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/label.tsx +2 -2
  12. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/pagination.tsx +87 -74
  13. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/select.tsx +156 -124
  14. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/separator.tsx +26 -0
  15. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/skeleton.tsx +1 -0
  16. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/spinner.tsx +5 -16
  17. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/table.tsx +68 -95
  18. package/dist/force-app/main/default/webapplications/feature-react-chart/src/components/ui/tabs.tsx +47 -84
  19. package/dist/package.json +1 -1
  20. package/package.json +3 -3
@@ -14,15 +14,13 @@
14
14
  "graphql:schema": "node scripts/get-graphql-schema.mjs"
15
15
  },
16
16
  "dependencies": {
17
- "@radix-ui/react-label": "^2.1.8",
18
- "@radix-ui/react-select": "^2.2.6",
19
- "@radix-ui/react-slot": "^1.2.4",
20
17
  "@salesforce/sdk-data": "^1.11.2",
21
18
  "@salesforce/webapp-experimental": "*",
22
19
  "@tailwindcss/vite": "^4.1.17",
23
20
  "class-variance-authority": "^0.7.1",
24
21
  "clsx": "^2.1.1",
25
22
  "lucide-react": "^0.562.0",
23
+ "radix-ui": "^1.4.3",
26
24
  "react": "^19.2.0",
27
25
  "react-dom": "^19.2.0",
28
26
  "react-router": "^7.10.1",
@@ -1,15 +1,16 @@
1
+ import * as React from "react";
1
2
  import { cva, type VariantProps } from "class-variance-authority";
2
3
 
3
4
  import { cn } from "../../lib/utils";
4
5
 
5
6
  const alertVariants = cva(
6
- "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
7
+ "grid gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 w-full relative group/alert",
7
8
  {
8
9
  variants: {
9
10
  variant: {
10
11
  default: "bg-card text-card-foreground",
11
12
  destructive:
12
- "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
13
+ "text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
13
14
  },
14
15
  },
15
16
  defaultVariants: {
@@ -21,29 +22,26 @@ const alertVariants = cva(
21
22
  function Alert({
22
23
  className,
23
24
  variant,
24
- role,
25
25
  ...props
26
26
  }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
27
27
  return (
28
28
  <div
29
29
  data-slot="alert"
30
- role={role ?? (variant === "destructive" ? "alert" : "status")}
30
+ role="alert"
31
31
  className={cn(alertVariants({ variant }), className)}
32
32
  {...props}
33
33
  />
34
34
  );
35
35
  }
36
36
 
37
- // Maintenance/A11y: Added 'asChild' pattern or a simple 'as' prop
38
- // to allow rendering semantic headings (h1-h6).
39
- interface AlertTitleProps extends React.ComponentProps<"div"> {
40
- as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "div" | "p";
41
- }
42
- function AlertTitle({ className, as: Component = "div", ...props }: AlertTitleProps) {
37
+ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
43
38
  return (
44
39
  <div
45
40
  data-slot="alert-title"
46
- className={cn("col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", className)}
41
+ className={cn(
42
+ "font-medium group-has-[>svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
43
+ className,
44
+ )}
47
45
  {...props}
48
46
  />
49
47
  );
@@ -54,7 +52,7 @@ function AlertDescription({ className, ...props }: React.ComponentProps<"div">)
54
52
  <div
55
53
  data-slot="alert-description"
56
54
  className={cn(
57
- "col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
55
+ "text-muted-foreground text-sm text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
58
56
  className,
59
57
  )}
60
58
  {...props}
@@ -62,4 +60,10 @@ function AlertDescription({ className, ...props }: React.ComponentProps<"div">)
62
60
  );
63
61
  }
64
62
 
65
- export { Alert, AlertTitle, AlertDescription };
63
+ function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
64
+ return (
65
+ <div data-slot="alert-action" className={cn("absolute top-2 right-2", className)} {...props} />
66
+ );
67
+ }
68
+
69
+ export { Alert, AlertTitle, AlertDescription, AlertAction };
@@ -1,28 +1,37 @@
1
1
  import * as React from "react";
2
- import { Slot } from "@radix-ui/react-slot";
3
2
  import { cva, type VariantProps } from "class-variance-authority";
3
+ import { Slot } from "radix-ui";
4
4
 
5
5
  import { cn } from "../../lib/utils";
6
6
 
7
7
  const buttonVariants = cva(
8
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive cursor-pointer",
8
+ "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
9
9
  {
10
10
  variants: {
11
11
  variant: {
12
- default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
13
- destructive:
14
- "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
12
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
15
13
  outline:
16
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
17
- secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
18
- ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
14
+ "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground",
15
+ secondary:
16
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
17
+ ghost:
18
+ "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
19
+ destructive:
20
+ "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
19
21
  link: "text-primary underline-offset-4 hover:underline",
20
22
  },
21
23
  size: {
22
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
23
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
24
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
25
- icon: "size-9",
24
+ default:
25
+ "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
26
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
27
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
28
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
29
+ icon: "size-8",
30
+ "icon-xs":
31
+ "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
32
+ "icon-sm":
33
+ "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
34
+ "icon-lg": "size-9",
26
35
  },
27
36
  },
28
37
  defaultVariants: {
@@ -32,23 +41,27 @@ const buttonVariants = cva(
32
41
  },
33
42
  );
34
43
 
35
- const Button = React.forwardRef<
36
- HTMLButtonElement,
37
- React.ComponentProps<"button"> &
38
- VariantProps<typeof buttonVariants> & {
39
- asChild?: boolean;
40
- }
41
- >(function Button({ className, variant, size, asChild = false, ...props }, ref) {
42
- const Comp = asChild ? Slot : "button";
44
+ function Button({
45
+ className,
46
+ variant = "default",
47
+ size = "default",
48
+ asChild = false,
49
+ ...props
50
+ }: React.ComponentProps<"button"> &
51
+ VariantProps<typeof buttonVariants> & {
52
+ asChild?: boolean;
53
+ }) {
54
+ const Comp = asChild ? Slot.Root : "button";
43
55
 
44
56
  return (
45
57
  <Comp
46
- ref={ref}
47
58
  data-slot="button"
59
+ data-variant={variant}
60
+ data-size={size}
48
61
  className={cn(buttonVariants({ variant, size, className }))}
49
62
  {...props}
50
63
  />
51
64
  );
52
- });
65
+ }
53
66
 
54
67
  export { Button, buttonVariants };
@@ -1,11 +1,18 @@
1
+ import * as React from "react";
2
+
1
3
  import { cn } from "../../lib/utils";
2
4
 
3
- function Card({ className, ...props }: React.ComponentProps<"div">) {
5
+ function Card({
6
+ className,
7
+ size = "default",
8
+ ...props
9
+ }: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
4
10
  return (
5
11
  <div
6
12
  data-slot="card"
13
+ data-size={size}
7
14
  className={cn(
8
- "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
15
+ "ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col",
9
16
  className,
10
17
  )}
11
18
  {...props}
@@ -18,7 +25,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
18
25
  <div
19
26
  data-slot="card-header"
20
27
  className={cn(
21
- "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
28
+ "gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
22
29
  className,
23
30
  )}
24
31
  {...props}
@@ -26,15 +33,14 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
26
33
  );
27
34
  }
28
35
 
29
- function CardTitle({
30
- className,
31
- as: Component = "h3",
32
- ...props
33
- }: React.ComponentProps<"div"> & { as?: any }) {
36
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
34
37
  return (
35
- <Component
38
+ <div
36
39
  data-slot="card-title"
37
- className={cn("leading-none font-semibold tracking-tight", className)}
40
+ className={cn(
41
+ "text-base leading-snug font-medium group-data-[size=sm]/card:text-sm",
42
+ className,
43
+ )}
38
44
  {...props}
39
45
  />
40
46
  );
@@ -61,14 +67,23 @@ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
61
67
  }
62
68
 
63
69
  function CardContent({ className, ...props }: React.ComponentProps<"div">) {
64
- return <div data-slot="card-content" className={cn("px-6", className)} {...props} />;
70
+ return (
71
+ <div
72
+ data-slot="card-content"
73
+ className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
74
+ {...props}
75
+ />
76
+ );
65
77
  }
66
78
 
67
79
  function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
68
80
  return (
69
81
  <div
70
82
  data-slot="card-footer"
71
- className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
83
+ className={cn(
84
+ "bg-muted/50 rounded-b-xl border-t p-4 group-data-[size=sm]/card:p-3 flex items-center",
85
+ className,
86
+ )}
72
87
  {...props}
73
88
  />
74
89
  );
@@ -0,0 +1,143 @@
1
+ import * as React from "react";
2
+ import { Dialog as DialogPrimitive } from "radix-ui";
3
+
4
+ import { cn } from "../../lib/utils";
5
+ import { Button } from "./button";
6
+ import { XIcon } from "lucide-react";
7
+
8
+ function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
9
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
10
+ }
11
+
12
+ function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
13
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
14
+ }
15
+
16
+ function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
17
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
18
+ }
19
+
20
+ function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
21
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
22
+ }
23
+
24
+ function DialogOverlay({
25
+ className,
26
+ ...props
27
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
28
+ return (
29
+ <DialogPrimitive.Overlay
30
+ data-slot="dialog-overlay"
31
+ className={cn(
32
+ "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50",
33
+ className,
34
+ )}
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ function DialogContent({
41
+ className,
42
+ children,
43
+ showCloseButton = true,
44
+ ...props
45
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
46
+ showCloseButton?: boolean;
47
+ }) {
48
+ return (
49
+ <DialogPortal>
50
+ <DialogOverlay />
51
+ <DialogPrimitive.Content
52
+ data-slot="dialog-content"
53
+ className={cn(
54
+ "bg-background data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 rounded-xl p-4 text-sm ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none",
55
+ className,
56
+ )}
57
+ {...props}
58
+ >
59
+ {children}
60
+ {showCloseButton && (
61
+ <DialogPrimitive.Close data-slot="dialog-close" asChild>
62
+ <Button variant="ghost" className="absolute top-2 right-2" size="icon-sm">
63
+ <XIcon />
64
+ <span className="sr-only">Close</span>
65
+ </Button>
66
+ </DialogPrimitive.Close>
67
+ )}
68
+ </DialogPrimitive.Content>
69
+ </DialogPortal>
70
+ );
71
+ }
72
+
73
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
74
+ return (
75
+ <div data-slot="dialog-header" className={cn("gap-2 flex flex-col", className)} {...props} />
76
+ );
77
+ }
78
+
79
+ function DialogFooter({
80
+ className,
81
+ showCloseButton = false,
82
+ children,
83
+ ...props
84
+ }: React.ComponentProps<"div"> & {
85
+ showCloseButton?: boolean;
86
+ }) {
87
+ return (
88
+ <div
89
+ data-slot="dialog-footer"
90
+ className={cn(
91
+ "bg-muted/50 -mx-4 -mb-4 rounded-b-xl border-t p-4 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
92
+ className,
93
+ )}
94
+ {...props}
95
+ >
96
+ {children}
97
+ {showCloseButton && (
98
+ <DialogPrimitive.Close asChild>
99
+ <Button variant="outline">Close</Button>
100
+ </DialogPrimitive.Close>
101
+ )}
102
+ </div>
103
+ );
104
+ }
105
+
106
+ function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
107
+ return (
108
+ <DialogPrimitive.Title
109
+ data-slot="dialog-title"
110
+ className={cn("text-base leading-none font-medium", className)}
111
+ {...props}
112
+ />
113
+ );
114
+ }
115
+
116
+ function DialogDescription({
117
+ className,
118
+ ...props
119
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
120
+ return (
121
+ <DialogPrimitive.Description
122
+ data-slot="dialog-description"
123
+ className={cn(
124
+ "text-muted-foreground *:[a]:hover:text-foreground text-sm *:[a]:underline *:[a]:underline-offset-3",
125
+ className,
126
+ )}
127
+ {...props}
128
+ />
129
+ );
130
+ }
131
+
132
+ export {
133
+ Dialog,
134
+ DialogClose,
135
+ DialogContent,
136
+ DialogDescription,
137
+ DialogFooter,
138
+ DialogHeader,
139
+ DialogOverlay,
140
+ DialogPortal,
141
+ DialogTitle,
142
+ DialogTrigger,
143
+ };
@@ -1,22 +1,102 @@
1
- import * as React from "react";
1
+ import { useMemo } from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
2
3
 
3
4
  import { cn } from "../../lib/utils";
4
5
  import { Label } from "./label";
6
+ import { Separator } from "./separator";
7
+
8
+ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
9
+ return (
10
+ <fieldset
11
+ data-slot="field-set"
12
+ className={cn(
13
+ "gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col",
14
+ className,
15
+ )}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ function FieldLegend({
22
+ className,
23
+ variant = "legend",
24
+ ...props
25
+ }: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
26
+ return (
27
+ <legend
28
+ data-slot="field-legend"
29
+ data-variant={variant}
30
+ className={cn(
31
+ "mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
32
+ className,
33
+ )}
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
40
+ return (
41
+ <div
42
+ data-slot="field-group"
43
+ className={cn(
44
+ "gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4 group/field-group @container/field-group flex w-full flex-col",
45
+ className,
46
+ )}
47
+ {...props}
48
+ />
49
+ );
50
+ }
51
+
52
+ const fieldVariants = cva("data-[invalid=true]:text-destructive gap-2 group/field flex w-full", {
53
+ variants: {
54
+ orientation: {
55
+ vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
56
+ horizontal:
57
+ "flex-row items-center *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
58
+ responsive:
59
+ "flex-col *:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:*:data-[slot=field-label]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
60
+ },
61
+ },
62
+ defaultVariants: {
63
+ orientation: "vertical",
64
+ },
65
+ });
5
66
 
6
67
  function Field({
7
68
  className,
8
69
  orientation = "vertical",
9
70
  ...props
10
- }: React.ComponentProps<"div"> & {
11
- orientation?: "horizontal" | "vertical";
12
- }) {
71
+ }: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
13
72
  return (
14
73
  <div
74
+ role="group"
15
75
  data-slot="field"
16
76
  data-orientation={orientation}
77
+ className={cn(fieldVariants({ orientation }), className)}
78
+ {...props}
79
+ />
80
+ );
81
+ }
82
+
83
+ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
84
+ return (
85
+ <div
86
+ data-slot="field-content"
87
+ className={cn("gap-0.5 group/field-content flex flex-1 flex-col leading-snug", className)}
88
+ {...props}
89
+ />
90
+ );
91
+ }
92
+
93
+ function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
94
+ return (
95
+ <Label
96
+ data-slot="field-label"
17
97
  className={cn(
18
- "grid gap-2",
19
- orientation === "horizontal" && "flex flex-wrap items-center gap-x-3 gap-y-1.5",
98
+ "has-data-checked:bg-primary/5 has-data-checked:border-primary/30 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10 gap-2 group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 group/field-label peer/field-label flex w-fit leading-snug",
99
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
20
100
  className,
21
101
  )}
22
102
  {...props}
@@ -24,12 +104,12 @@ function Field({
24
104
  );
25
105
  }
26
106
 
27
- function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
107
+ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
28
108
  return (
29
- <Label
109
+ <div
30
110
  data-slot="field-label"
31
111
  className={cn(
32
- "data-[disabled]:opacity-50 group-data-[disabled]:opacity-50 peer-disabled:opacity-50",
112
+ "gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50 flex w-fit items-center leading-snug",
33
113
  className,
34
114
  )}
35
115
  {...props}
@@ -41,71 +121,102 @@ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
41
121
  return (
42
122
  <p
43
123
  data-slot="field-description"
44
- className={cn("text-muted-foreground text-sm", className)}
124
+ className={cn(
125
+ "text-muted-foreground text-left text-sm [[data-variant=legend]+&]:-mt-1.5 leading-normal font-normal group-has-data-horizontal/field:text-balance",
126
+ "last:mt-0 nth-last-2:-mt-1",
127
+ "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
128
+ className,
129
+ )}
45
130
  {...props}
46
131
  />
47
132
  );
48
133
  }
49
134
 
50
- function FieldError({
135
+ function FieldSeparator({
136
+ children,
51
137
  className,
52
- errors,
53
138
  ...props
54
- }: React.ComponentProps<"p"> & { errors?: (Error | undefined | null)[] }) {
55
- const errorMessages = errors?.filter(Boolean).map((e) => e?.message);
56
-
57
- if (!errorMessages?.length) {
58
- return null;
59
- }
60
-
139
+ }: React.ComponentProps<"div"> & {
140
+ children?: React.ReactNode;
141
+ }) {
61
142
  return (
62
- <p
63
- data-slot="field-error"
64
- className={cn("text-destructive text-sm", className)}
65
- aria-live="polite"
143
+ <div
144
+ data-slot="field-separator"
145
+ data-content={!!children}
146
+ className={cn(
147
+ "-my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2 relative",
148
+ className,
149
+ )}
66
150
  {...props}
67
151
  >
68
- {errorMessages.join(", ")}
69
- </p>
152
+ <Separator className="absolute inset-0 top-1/2" />
153
+ {children && (
154
+ <span
155
+ className="text-muted-foreground px-2 bg-background relative mx-auto block w-fit"
156
+ data-slot="field-separator-content"
157
+ >
158
+ {children}
159
+ </span>
160
+ )}
161
+ </div>
70
162
  );
71
163
  }
72
164
 
73
- function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
74
- return <div data-slot="field-group" className={cn("grid gap-6", className)} {...props} />;
75
- }
165
+ function FieldError({
166
+ className,
167
+ children,
168
+ errors,
169
+ ...props
170
+ }: React.ComponentProps<"div"> & {
171
+ errors?: Array<{ message?: string } | undefined>;
172
+ }) {
173
+ const content = useMemo(() => {
174
+ if (children) {
175
+ return children;
176
+ }
76
177
 
77
- function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
78
- return <fieldset data-slot="field-set" className={cn("grid gap-4", className)} {...props} />;
79
- }
178
+ if (!errors?.length) {
179
+ return null;
180
+ }
80
181
 
81
- function FieldLegend({ className, ...props }: React.ComponentProps<"legend">) {
82
- return (
83
- <legend
84
- data-slot="field-legend"
85
- className={cn("text-foreground font-medium leading-none", className)}
86
- {...props}
87
- />
88
- );
89
- }
182
+ const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()];
183
+
184
+ if (uniqueErrors?.length == 1) {
185
+ return uniqueErrors[0]?.message;
186
+ }
187
+
188
+ return (
189
+ <ul className="ml-4 flex list-disc flex-col gap-1">
190
+ {uniqueErrors.map((error, index) => error?.message && <li key={index}>{error.message}</li>)}
191
+ </ul>
192
+ );
193
+ }, [children, errors]);
194
+
195
+ if (!content) {
196
+ return null;
197
+ }
90
198
 
91
- function FieldSeparator({ className, ...props }: React.ComponentProps<"div">) {
92
199
  return (
93
200
  <div
94
- data-slot="field-separator"
95
- role="separator"
96
- className={cn("bg-border -mx-6 h-px", className)}
201
+ role="alert"
202
+ data-slot="field-error"
203
+ className={cn("text-destructive text-sm font-normal", className)}
97
204
  {...props}
98
- />
205
+ >
206
+ {content}
207
+ </div>
99
208
  );
100
209
  }
101
210
 
102
211
  export {
103
212
  Field,
213
+ FieldLabel,
104
214
  FieldDescription,
105
215
  FieldError,
106
216
  FieldGroup,
107
- FieldLabel,
108
217
  FieldLegend,
109
218
  FieldSeparator,
110
219
  FieldSet,
220
+ FieldContent,
221
+ FieldTitle,
111
222
  };
@@ -69,3 +69,4 @@ export {
69
69
  PaginationNext,
70
70
  PaginationPrevious,
71
71
  } from "./pagination";
72
+ export { Separator } from "./separator";