@zedtech/zedkit-ui 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/dialog.tsx ADDED
@@ -0,0 +1,102 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
5
+ import { X } from "lucide-react";
6
+ import { cn } from "./utils.js";
7
+
8
+ const Dialog = DialogPrimitive.Root;
9
+ const DialogTrigger = DialogPrimitive.Trigger;
10
+ const DialogClose = DialogPrimitive.Close;
11
+ const DialogPortal = DialogPrimitive.Portal;
12
+
13
+ const DialogOverlay = React.forwardRef<
14
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
15
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
16
+ >(({ className, ...props }, ref) => (
17
+ <DialogPrimitive.Overlay
18
+ ref={ref}
19
+ className={cn("fixed inset-0 z-50 bg-overlay/80", className)}
20
+ {...props}
21
+ />
22
+ ));
23
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
24
+
25
+ const DialogContent = React.forwardRef<
26
+ React.ElementRef<typeof DialogPrimitive.Content>,
27
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
28
+ >(({ className, children, ...props }, ref) => (
29
+ <DialogPortal>
30
+ <DialogOverlay />
31
+ <DialogPrimitive.Content
32
+ ref={ref}
33
+ className={cn(
34
+ "fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg sm:rounded-lg",
35
+ className,
36
+ )}
37
+ {...props}
38
+ >
39
+ {children}
40
+ <DialogPrimitive.Close className="absolute top-4 right-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-none disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
41
+ <X className="h-4 w-4" />
42
+ <span className="sr-only">Close</span>
43
+ </DialogPrimitive.Close>
44
+ </DialogPrimitive.Content>
45
+ </DialogPortal>
46
+ ));
47
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
48
+
49
+ function DialogHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
50
+ return (
51
+ <div
52
+ className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)}
53
+ {...props}
54
+ />
55
+ );
56
+ }
57
+
58
+ function DialogFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
59
+ return (
60
+ <div
61
+ className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
62
+ {...props}
63
+ />
64
+ );
65
+ }
66
+
67
+ const DialogTitle = React.forwardRef<
68
+ React.ElementRef<typeof DialogPrimitive.Title>,
69
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
70
+ >(({ className, ...props }, ref) => (
71
+ <DialogPrimitive.Title
72
+ ref={ref}
73
+ className={cn("text-lg leading-none font-semibold tracking-tight", className)}
74
+ {...props}
75
+ />
76
+ ));
77
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
78
+
79
+ const DialogDescription = React.forwardRef<
80
+ React.ElementRef<typeof DialogPrimitive.Description>,
81
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
82
+ >(({ className, ...props }, ref) => (
83
+ <DialogPrimitive.Description
84
+ ref={ref}
85
+ className={cn("text-sm text-muted-foreground", className)}
86
+ {...props}
87
+ />
88
+ ));
89
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
90
+
91
+ export {
92
+ Dialog,
93
+ DialogClose,
94
+ DialogContent,
95
+ DialogDescription,
96
+ DialogFooter,
97
+ DialogHeader,
98
+ DialogOverlay,
99
+ DialogPortal,
100
+ DialogTitle,
101
+ DialogTrigger,
102
+ };
package/src/field.tsx ADDED
@@ -0,0 +1,230 @@
1
+ "use client";
2
+
3
+ import type * as React from "react";
4
+ import { useMemo } from "react";
5
+ import { cva, type VariantProps } from "class-variance-authority";
6
+
7
+ import { cn } from "./utils.js";
8
+ import { Label } from "./label.js";
9
+ import { Separator } from "./separator.js";
10
+
11
+ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
12
+ return (
13
+ <fieldset
14
+ data-slot="field-set"
15
+ className={cn(
16
+ "flex flex-col gap-6",
17
+ "has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
18
+ className,
19
+ )}
20
+ {...props}
21
+ />
22
+ );
23
+ }
24
+
25
+ function FieldLegend({
26
+ className,
27
+ variant = "legend",
28
+ ...props
29
+ }: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
30
+ return (
31
+ <legend
32
+ data-slot="field-legend"
33
+ data-variant={variant}
34
+ className={cn(
35
+ "mb-3 font-medium",
36
+ "data-[variant=legend]:text-base",
37
+ "data-[variant=label]:text-sm",
38
+ className,
39
+ )}
40
+ {...props}
41
+ />
42
+ );
43
+ }
44
+
45
+ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
46
+ return (
47
+ <div
48
+ data-slot="field-group"
49
+ className={cn(
50
+ "group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
51
+ className,
52
+ )}
53
+ {...props}
54
+ />
55
+ );
56
+ }
57
+
58
+ const fieldVariants = cva("group/field flex w-full gap-3 data-[invalid=true]:text-destructive", {
59
+ variants: {
60
+ orientation: {
61
+ vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"],
62
+ horizontal: [
63
+ "flex-row items-center",
64
+ "[&>[data-slot=field-label]]:flex-auto",
65
+ "has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
66
+ ],
67
+ responsive: [
68
+ "flex-col @md/field-group:flex-row @md/field-group:items-center [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto",
69
+ "@md/field-group:[&>[data-slot=field-label]]:flex-auto",
70
+ "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
71
+ ],
72
+ },
73
+ },
74
+ defaultVariants: {
75
+ orientation: "vertical",
76
+ },
77
+ });
78
+
79
+ function Field({
80
+ className,
81
+ orientation = "vertical",
82
+ ...props
83
+ }: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
84
+ return (
85
+ <div
86
+ role="group"
87
+ data-slot="field"
88
+ data-orientation={orientation}
89
+ className={cn(fieldVariants({ orientation }), className)}
90
+ {...props}
91
+ />
92
+ );
93
+ }
94
+
95
+ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
96
+ return (
97
+ <div
98
+ data-slot="field-content"
99
+ className={cn("group/field-content flex flex-1 flex-col gap-1.5 leading-snug", className)}
100
+ {...props}
101
+ />
102
+ );
103
+ }
104
+
105
+ function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
106
+ return (
107
+ <Label
108
+ data-slot="field-label"
109
+ className={cn(
110
+ "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
111
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>[data-slot=field]]:p-4",
112
+ "has-data-[state=checked]:border-primary has-data-[state=checked]:bg-primary/5 dark:has-data-[state=checked]:bg-primary/10",
113
+ className,
114
+ )}
115
+ {...props}
116
+ />
117
+ );
118
+ }
119
+
120
+ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
121
+ return (
122
+ <div
123
+ data-slot="field-label"
124
+ className={cn(
125
+ "flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50",
126
+ className,
127
+ )}
128
+ {...props}
129
+ />
130
+ );
131
+ }
132
+
133
+ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
134
+ return (
135
+ <p
136
+ data-slot="field-description"
137
+ className={cn(
138
+ "text-sm leading-normal font-normal text-muted-foreground group-has-[[data-orientation=horizontal]]/field:text-balance",
139
+ "last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5",
140
+ "[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
141
+ className,
142
+ )}
143
+ {...props}
144
+ />
145
+ );
146
+ }
147
+
148
+ function FieldSeparator({
149
+ children,
150
+ className,
151
+ ...props
152
+ }: React.ComponentProps<"div"> & {
153
+ children?: React.ReactNode;
154
+ }) {
155
+ return (
156
+ <div
157
+ data-slot="field-separator"
158
+ data-content={!!children}
159
+ className={cn("relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2", className)}
160
+ {...props}
161
+ >
162
+ <Separator className="absolute inset-0 top-1/2" />
163
+ {children && (
164
+ <span
165
+ className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
166
+ data-slot="field-separator-content"
167
+ >
168
+ {children}
169
+ </span>
170
+ )}
171
+ </div>
172
+ );
173
+ }
174
+
175
+ function FieldError({
176
+ className,
177
+ children,
178
+ errors,
179
+ ...props
180
+ }: React.ComponentProps<"div"> & {
181
+ errors?: Array<{ message?: string } | undefined>;
182
+ }) {
183
+ const content = useMemo(() => {
184
+ if (children) {
185
+ return children;
186
+ }
187
+
188
+ if (!errors) {
189
+ return null;
190
+ }
191
+
192
+ if (errors?.length === 1 && errors[0]?.message) {
193
+ return errors[0].message;
194
+ }
195
+
196
+ return (
197
+ <ul className="ml-4 flex list-disc flex-col gap-1">
198
+ {errors.map((error, index) => error?.message && <li key={index}>{error.message}</li>)}
199
+ </ul>
200
+ );
201
+ }, [children, errors]);
202
+
203
+ if (!content) {
204
+ return null;
205
+ }
206
+
207
+ return (
208
+ <div
209
+ role="alert"
210
+ data-slot="field-error"
211
+ className={cn("text-sm font-normal text-destructive", className)}
212
+ {...props}
213
+ >
214
+ {content}
215
+ </div>
216
+ );
217
+ }
218
+
219
+ export {
220
+ Field,
221
+ FieldLabel,
222
+ FieldDescription,
223
+ FieldError,
224
+ FieldGroup,
225
+ FieldLegend,
226
+ FieldSeparator,
227
+ FieldSet,
228
+ FieldContent,
229
+ FieldTitle,
230
+ };
@@ -0,0 +1,29 @@
1
+ "use client";
2
+
3
+ import type * as React from "react";
4
+ import { useFormContext } from "react-hook-form";
5
+
6
+ import { Button, type ButtonProps } from "./button.js";
7
+
8
+ export type FormSubmitProps = Omit<ButtonProps, "type"> & {
9
+ /**
10
+ * Label shown while the form submits. Defaults to `children`.
11
+ * Both come from the caller (message catalog), never hard-coded.
12
+ */
13
+ pendingLabel?: React.ReactNode;
14
+ };
15
+
16
+ /**
17
+ * Submit button wired to the enclosing form: disabled while the
18
+ * submission is in flight, with an optional pending label.
19
+ * Must render inside `<Form>` (uses the form context).
20
+ */
21
+ export function FormSubmit({ pendingLabel, children, disabled, ...props }: FormSubmitProps) {
22
+ const { formState } = useFormContext();
23
+ const pending = formState.isSubmitting;
24
+ return (
25
+ <Button type="submit" disabled={disabled === true || pending} {...props}>
26
+ {pending && pendingLabel !== undefined ? pendingLabel : children}
27
+ </Button>
28
+ );
29
+ }
package/src/form.tsx ADDED
@@ -0,0 +1,157 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as LabelPrimitive from "@radix-ui/react-label";
5
+ import { Slot } from "@radix-ui/react-slot";
6
+ import {
7
+ Controller,
8
+ FormProvider,
9
+ useFormContext,
10
+ type ControllerProps,
11
+ type FieldPath,
12
+ type FieldValues,
13
+ } from "react-hook-form";
14
+
15
+ import { cn } from "./utils.js";
16
+ import { Label } from "./label.js";
17
+
18
+ const Form = FormProvider;
19
+
20
+ type FormFieldContextValue<
21
+ TFieldValues extends FieldValues = FieldValues,
22
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
23
+ > = {
24
+ name: TName;
25
+ };
26
+
27
+ const FormFieldContext = React.createContext<FormFieldContextValue | null>(null);
28
+
29
+ const FormField = <
30
+ TFieldValues extends FieldValues = FieldValues,
31
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
32
+ >({
33
+ ...props
34
+ }: ControllerProps<TFieldValues, TName>) => {
35
+ return (
36
+ <FormFieldContext.Provider value={{ name: props.name }}>
37
+ <Controller {...props} />
38
+ </FormFieldContext.Provider>
39
+ );
40
+ };
41
+
42
+ const useFormField = () => {
43
+ const fieldContext = React.useContext(FormFieldContext);
44
+ const itemContext = React.useContext(FormItemContext);
45
+ const { getFieldState, formState } = useFormContext();
46
+
47
+ if (!fieldContext) {
48
+ throw new Error("useFormField should be used within <FormField>");
49
+ }
50
+
51
+ if (!itemContext) {
52
+ throw new Error("useFormField should be used within <FormItem>");
53
+ }
54
+
55
+ const fieldState = getFieldState(fieldContext.name, formState);
56
+
57
+ const { id } = itemContext;
58
+
59
+ return {
60
+ id,
61
+ name: fieldContext.name,
62
+ formItemId: `${id}-form-item`,
63
+ formDescriptionId: `${id}-form-item-description`,
64
+ formMessageId: `${id}-form-item-message`,
65
+ ...fieldState,
66
+ };
67
+ };
68
+
69
+ type FormItemContextValue = {
70
+ id: string;
71
+ };
72
+
73
+ const FormItemContext = React.createContext<FormItemContextValue | null>(null);
74
+
75
+ const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
76
+ ({ className, ...props }, ref) => {
77
+ const id = React.useId();
78
+
79
+ return (
80
+ <FormItemContext.Provider value={{ id }}>
81
+ <div ref={ref} className={cn("space-y-2", className)} {...props} />
82
+ </FormItemContext.Provider>
83
+ );
84
+ },
85
+ );
86
+ FormItem.displayName = "FormItem";
87
+
88
+ const FormLabel = React.forwardRef<
89
+ React.ElementRef<typeof LabelPrimitive.Root>,
90
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
91
+ >(({ className, ...props }, ref) => {
92
+ const { error, formItemId } = useFormField();
93
+
94
+ return (
95
+ <Label
96
+ ref={ref}
97
+ className={cn(error && "text-destructive", className)}
98
+ htmlFor={formItemId}
99
+ {...props}
100
+ />
101
+ );
102
+ });
103
+ FormLabel.displayName = "FormLabel";
104
+
105
+ const FormControl = React.forwardRef<
106
+ React.ElementRef<typeof Slot>,
107
+ React.ComponentPropsWithoutRef<typeof Slot>
108
+ >(({ ...props }, ref) => {
109
+ const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
110
+
111
+ return (
112
+ <Slot
113
+ ref={ref}
114
+ id={formItemId}
115
+ aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
116
+ aria-invalid={!!error}
117
+ {...props}
118
+ />
119
+ );
120
+ });
121
+ FormControl.displayName = "FormControl";
122
+
123
+ const FormDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
124
+ ({ className, ...props }, ref) => {
125
+ const { formDescriptionId } = useFormField();
126
+
127
+ return (
128
+ <p ref={ref} id={formDescriptionId} className={cn("text-[0.8rem] text-muted-foreground", className)} {...props} />
129
+ );
130
+ },
131
+ );
132
+ FormDescription.displayName = "FormDescription";
133
+
134
+ const FormMessage = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
135
+ ({ className, children, ...props }, ref) => {
136
+ const { error, formMessageId } = useFormField();
137
+ const body = error ? String(error?.message ?? "") : children;
138
+
139
+ if (!body) {
140
+ return null;
141
+ }
142
+
143
+ return (
144
+ <p
145
+ ref={ref}
146
+ id={formMessageId}
147
+ className={cn("text-[0.8rem] font-medium text-destructive", className)}
148
+ {...props}
149
+ >
150
+ {body}
151
+ </p>
152
+ );
153
+ },
154
+ );
155
+ FormMessage.displayName = "FormMessage";
156
+
157
+ export { useFormField, Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField };
package/src/index.ts ADDED
@@ -0,0 +1,95 @@
1
+ export { Alert, AlertDescription, AlertTitle } from "./alert.js";
2
+ export { AppShell, type AppShellProps } from "./app-shell.js";
3
+ export { Badge, badgeVariants } from "./badge.js";
4
+ export { Button, buttonVariants, type ButtonProps } from "./button.js";
5
+ export { ConfirmDialog, type ConfirmDialogProps } from "./confirm-dialog.js";
6
+ export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./card.js";
7
+ export { Checkbox } from "./checkbox.js";
8
+ export {
9
+ DataTable,
10
+ createDataTableColumns,
11
+ type DataTableColumn,
12
+ type DataTableProps,
13
+ } from "./data-table.js";
14
+ export {
15
+ Dialog,
16
+ DialogClose,
17
+ DialogContent,
18
+ DialogDescription,
19
+ DialogFooter,
20
+ DialogHeader,
21
+ DialogOverlay,
22
+ DialogPortal,
23
+ DialogTitle,
24
+ DialogTrigger,
25
+ } from "./dialog.js";
26
+ export { DetailPanel, type DetailItem, type DetailPanelProps } from "./detail-panel.js";
27
+ export {
28
+ Field,
29
+ FieldContent,
30
+ FieldDescription,
31
+ FieldError,
32
+ FieldGroup,
33
+ FieldLabel,
34
+ FieldLegend,
35
+ FieldSeparator,
36
+ FieldSet,
37
+ FieldTitle,
38
+ } from "./field.js";
39
+ export {
40
+ Form,
41
+ FormControl,
42
+ FormDescription,
43
+ FormField,
44
+ FormItem,
45
+ FormLabel,
46
+ FormMessage,
47
+ useFormField,
48
+ } from "./form.js";
49
+ export { FormSubmit, type FormSubmitProps } from "./form-submit.js";
50
+ export { useValidatedForm } from "./use-validated-form.js";
51
+ export { Input } from "./input.js";
52
+ export { Label } from "./label.js";
53
+ export { Section, Stack, stackVariants, type SectionProps, type StackProps } from "./layout.js";
54
+ export { LivePulse, type LivePulseProps } from "./live-pulse.js";
55
+ export { OptionCards, type OptionCard, type OptionCardsProps } from "./option-cards.js";
56
+ export { PageHeader, type PageHeaderProps } from "./page-header.js";
57
+ export { SecretField, type SecretFieldProps } from "./secret-field.js";
58
+ export {
59
+ Select,
60
+ SelectContent,
61
+ SelectGroup,
62
+ SelectItem,
63
+ SelectLabel,
64
+ SelectScrollDownButton,
65
+ SelectScrollUpButton,
66
+ SelectSeparator,
67
+ SelectTrigger,
68
+ SelectValue,
69
+ } from "./select.js";
70
+ export { Separator } from "./separator.js";
71
+ export { Skeleton } from "./skeleton.js";
72
+ export {
73
+ Table,
74
+ TableBody,
75
+ TableCaption,
76
+ TableCell,
77
+ TableFooter,
78
+ TableHead,
79
+ TableHeader,
80
+ TableRow,
81
+ } from "./table.js";
82
+ export { WizardSteps, type WizardStepsProps } from "./wizard-steps.js";
83
+ export { Switch } from "./switch.js";
84
+ export { Tabs, TabsContent, TabsList, TabsTrigger } from "./tabs.js";
85
+ export { Textarea } from "./textarea.js";
86
+ export { Toaster } from "./sonner.js";
87
+ export {
88
+ Heading,
89
+ headingVariants,
90
+ Text,
91
+ textVariants,
92
+ type HeadingProps,
93
+ type TextProps,
94
+ } from "./typography.js";
95
+ export { cn } from "./utils.js";
package/src/input.tsx ADDED
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+ import { cn } from "./utils.js";
3
+
4
+ const Input = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
5
+ ({ className, type, ...props }, ref) => {
6
+ return (
7
+ <input
8
+ type={type}
9
+ className={cn(
10
+ "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
11
+ className,
12
+ )}
13
+ ref={ref}
14
+ {...props}
15
+ />
16
+ );
17
+ },
18
+ );
19
+ Input.displayName = "Input";
20
+
21
+ export { Input };
package/src/label.tsx ADDED
@@ -0,0 +1,20 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as LabelPrimitive from "@radix-ui/react-label";
5
+ import { cva, type VariantProps } from "class-variance-authority";
6
+ import { cn } from "./utils.js";
7
+
8
+ const labelVariants = cva(
9
+ "text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
10
+ );
11
+
12
+ const Label = React.forwardRef<
13
+ React.ElementRef<typeof LabelPrimitive.Root>,
14
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
15
+ >(({ className, ...props }, ref) => (
16
+ <LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
17
+ ));
18
+ Label.displayName = LabelPrimitive.Root.displayName;
19
+
20
+ export { Label };