-
Notifications
You must be signed in to change notification settings - Fork 45
/
Label.tsx
120 lines (103 loc) · 3.44 KB
/
Label.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label";
import { Slot } from "@radix-ui/react-slot";
import { cn, isElementWithChildren, isReactElement } from "../../helpers/utils";
import { Tooltip } from "../Tooltip";
/* ---------------------------------- Types --------------------------------- */
export type LabelElement = React.ElementRef<typeof LabelPrimitive.Root>;
export type LabelProps = React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & {
/** Tooltip text to display when hovering over the label */
tooltip?: React.ReactNode;
/** Additional description text, shown next to the primary label */
description?: React.ReactNode;
/** Indicates if the label is associated with a required field */
required?: boolean;
/** Indicates if the label is associated with a disabled field */
disabled?: boolean;
};
/* -------------------------------- Component ------------------------------- */
const LabelWedges = React.forwardRef<LabelElement, LabelProps>(
(
{
asChild = false,
children,
className,
description,
disabled,
required = false,
tooltip,
...otherProps
},
ref
) => {
const useAsChild = asChild && isReactElement(children);
const innerContent = useAsChild ? (
React.cloneElement(children, {
children: (
<>
{isElementWithChildren(children) && children.props.children}
{required && <span className="text-destructive">*</span>}
</>
),
})
) : (
<>
{children ? <span>{children}</span> : null}
{description ? (
<span className={cn("font-normal text-surface-500", disabled && "text-current")}>
{description}
</span>
) : null}
{required ? <span className="font-normal text-destructive">*</span> : null}
</>
);
if (!children && !tooltip && !description) {
return null;
}
return (
<div className="wg-label inline-flex shrink-0 items-center gap-1 text-surface-900 wg-antialiased">
<LabelPrimitive.Root
ref={ref}
asChild={useAsChild}
className={cn(
"wg-label inline-flex cursor-pointer items-center gap-1 text-sm font-medium leading-6",
disabled && "pointer-events-none text-surface-300",
className
)}
{...otherProps}
>
{innerContent}
</LabelPrimitive.Root>
{tooltip ? <Tooltip content={tooltip} /> : null}
</div>
);
}
);
const HelperText = React.forwardRef<
HTMLSpanElement,
React.HTMLAttributes<HTMLSpanElement> & { error?: boolean; disabled?: boolean }
>(({ children, error, disabled, className, ...otherProps }, ref) => {
const HelperTextComponent = children && isReactElement(children) ? Slot : "span";
const ariaInvalid = otherProps["aria-invalid"];
return children ? (
<HelperTextComponent
ref={ref}
className={cn(
"wg-label__helper text-start text-sm leading-6 text-surface-500 wg-antialiased",
(ariaInvalid ?? error) && "text-destructive",
disabled && "text-surface-300",
className
)}
role={ariaInvalid ? "alert" : undefined}
{...otherProps}
>
{children}
</HelperTextComponent>
) : null;
});
LabelWedges.displayName = "Label";
HelperText.displayName = "HelperText";
const Label = Object.assign(LabelWedges, {
Helper: HelperText,
});
export default Label;