@you-agent-factory/components 0.0.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/README.md +223 -0
- package/docs/README.md +83 -0
- package/docs/button.md +119 -0
- package/docs/charts.md +325 -0
- package/docs/data-display-code-panel.md +120 -0
- package/docs/feedback-alert-panel.md +117 -0
- package/docs/feedback-skeleton.md +54 -0
- package/docs/forms-form-field.md +490 -0
- package/docs/forms-input-primitives.md +311 -0
- package/docs/forms-select-primitives.md +362 -0
- package/docs/graphs.md +250 -0
- package/docs/layout-display-primitives.md +211 -0
- package/docs/overlays.md +414 -0
- package/docs/table-data-table.md +331 -0
- package/docs/typography-roles.md +182 -0
- package/docs/widget-frame-recipes.md +273 -0
- package/package.json +161 -0
- package/src/category-paths.ts +20 -0
- package/src/charts/chart-state-panel.tsx +103 -0
- package/src/charts/chart.tsx +287 -0
- package/src/charts/index.ts +21 -0
- package/src/data-display/code-panel.tsx +67 -0
- package/src/data-display/data-table.tsx +180 -0
- package/src/data-display/description-list.tsx +24 -0
- package/src/data-display/index.ts +34 -0
- package/src/data-display/table-layout.ts +12 -0
- package/src/data-display/table.tsx +131 -0
- package/src/feedback/alert-panel-semantics.ts +75 -0
- package/src/feedback/alert-panel.tsx +187 -0
- package/src/feedback/index.ts +21 -0
- package/src/feedback/skeleton.tsx +16 -0
- package/src/forms/index.ts +73 -0
- package/src/forms/package-checkbox.tsx +50 -0
- package/src/forms/package-enum-select.tsx +185 -0
- package/src/forms/package-file-input.tsx +25 -0
- package/src/forms/package-form-field.tsx +202 -0
- package/src/forms/package-input.tsx +25 -0
- package/src/forms/package-native-select.tsx +25 -0
- package/src/forms/package-select.tsx +209 -0
- package/src/forms/package-textarea.tsx +35 -0
- package/src/forms/select-icons.tsx +35 -0
- package/src/graphs/graph-edge-path.ts +241 -0
- package/src/graphs/graph-edge.tsx +130 -0
- package/src/graphs/graph-interactive-example.tsx +187 -0
- package/src/graphs/graph-node-button.tsx +51 -0
- package/src/graphs/graph-node-handle-badge.tsx +108 -0
- package/src/graphs/graph-node-handle.ts +28 -0
- package/src/graphs/graph-node-shell.tsx +111 -0
- package/src/graphs/graph-node-state-indicator.tsx +47 -0
- package/src/graphs/graph-node-state.ts +111 -0
- package/src/graphs/graph-viewport-surface.tsx +27 -0
- package/src/graphs/index.ts +41 -0
- package/src/icons/index.ts +4 -0
- package/src/index.ts +209 -0
- package/src/layout/action-row.tsx +53 -0
- package/src/layout/index.ts +9 -0
- package/src/layout/surface-panel.tsx +91 -0
- package/src/navigation/index.ts +4 -0
- package/src/overlays/collapsible.tsx +22 -0
- package/src/overlays/dialog.tsx +148 -0
- package/src/overlays/index.ts +31 -0
- package/src/overlays/overlay-layout.ts +9 -0
- package/src/overlays/popover.tsx +29 -0
- package/src/overlays/scroll-area.tsx +78 -0
- package/src/primitives/button-link.tsx +22 -0
- package/src/primitives/button.tsx +258 -0
- package/src/primitives/icon-button-shell.tsx +49 -0
- package/src/primitives/index.ts +31 -0
- package/src/primitives/package-text.tsx +29 -0
- package/src/primitives/typography-roles.ts +12 -0
- package/src/primitives/typography.tsx +165 -0
- package/src/recipes/index.ts +61 -0
- package/src/recipes/widget-frame-content.tsx +114 -0
- package/src/recipes/widget-frame-disclosure.tsx +161 -0
- package/src/recipes/widget-frame-layout.ts +40 -0
- package/src/recipes/widget-frame-skeleton.tsx +16 -0
- package/src/recipes/widget-frame-states.tsx +99 -0
- package/src/recipes/widget-frame-typography.ts +19 -0
- package/src/recipes/widget-frame.tsx +84 -0
- package/src/styles/color-palette-presets.css +167 -0
- package/src/styles/color-role-tokens.css +163 -0
- package/src/styles/layout-role-tokens.css +27 -0
- package/src/styles/text-color-role-tokens.css +29 -0
- package/src/styles/typography-role-tokens.css +72 -0
- package/src/styles/typography-role-utilities.css +73 -0
- package/src/styles/typography-roles.css +53 -0
- package/src/styles.css +32 -0
- package/src/testing/index.ts +13 -0
- package/src/testing/render.tsx +16 -0
- package/src/tokens/index.ts +4 -0
- package/src/utilities/cn.ts +5 -0
- package/src/utilities/index.ts +6 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { type ComponentProps, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Select,
|
|
5
|
+
SELECT_EMPTY_STATE_VALUE,
|
|
6
|
+
SelectContent,
|
|
7
|
+
SelectEmpty,
|
|
8
|
+
SelectItem,
|
|
9
|
+
SelectTrigger,
|
|
10
|
+
SelectValue,
|
|
11
|
+
} from "./package-select";
|
|
12
|
+
|
|
13
|
+
export const ENUM_SELECT_EMPTY_VALUE = "__enum-select-empty__";
|
|
14
|
+
|
|
15
|
+
export interface EnumSelectOption {
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
label: string;
|
|
18
|
+
value: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type EnumSelectAriaProps = Pick<
|
|
22
|
+
ComponentProps<typeof SelectTrigger>,
|
|
23
|
+
"aria-describedby" | "aria-invalid" | "aria-label" | "aria-labelledby"
|
|
24
|
+
>;
|
|
25
|
+
|
|
26
|
+
export interface EnumSelectProps extends EnumSelectAriaProps {
|
|
27
|
+
className?: string;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
emptyOptionsLabel?: string;
|
|
30
|
+
id: string;
|
|
31
|
+
loading?: boolean;
|
|
32
|
+
loadingLabel?: string;
|
|
33
|
+
onValueChange: (value: string) => void;
|
|
34
|
+
options: readonly EnumSelectOption[];
|
|
35
|
+
placeholder?: string;
|
|
36
|
+
value: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function EnumSelect({
|
|
40
|
+
className,
|
|
41
|
+
disabled,
|
|
42
|
+
emptyOptionsLabel = "No options available",
|
|
43
|
+
id,
|
|
44
|
+
loading = false,
|
|
45
|
+
loadingLabel = "Loading options...",
|
|
46
|
+
onValueChange,
|
|
47
|
+
options,
|
|
48
|
+
placeholder,
|
|
49
|
+
value,
|
|
50
|
+
...aria
|
|
51
|
+
}: EnumSelectProps) {
|
|
52
|
+
const isEmpty = !loading && options.length === 0;
|
|
53
|
+
const resolvedPlaceholder = loading ? loadingLabel : placeholder;
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Select
|
|
57
|
+
disabled={disabled || loading}
|
|
58
|
+
onValueChange={onValueChange}
|
|
59
|
+
value={loading ? SELECT_EMPTY_STATE_VALUE : value}
|
|
60
|
+
>
|
|
61
|
+
<SelectTrigger
|
|
62
|
+
aria-busy={loading || undefined}
|
|
63
|
+
className={className}
|
|
64
|
+
id={id}
|
|
65
|
+
{...aria}
|
|
66
|
+
>
|
|
67
|
+
<SelectValue placeholder={resolvedPlaceholder} />
|
|
68
|
+
</SelectTrigger>
|
|
69
|
+
<SelectContent>
|
|
70
|
+
{loading ? (
|
|
71
|
+
<SelectEmpty>{loadingLabel}</SelectEmpty>
|
|
72
|
+
) : isEmpty ? (
|
|
73
|
+
<SelectEmpty>{emptyOptionsLabel}</SelectEmpty>
|
|
74
|
+
) : (
|
|
75
|
+
options.map((option) => (
|
|
76
|
+
<SelectItem
|
|
77
|
+
disabled={option.disabled}
|
|
78
|
+
key={option.value}
|
|
79
|
+
value={option.value}
|
|
80
|
+
>
|
|
81
|
+
{option.label}
|
|
82
|
+
</SelectItem>
|
|
83
|
+
))
|
|
84
|
+
)}
|
|
85
|
+
</SelectContent>
|
|
86
|
+
</Select>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface OptionalEnumSelectProps extends EnumSelectAriaProps {
|
|
91
|
+
className?: string;
|
|
92
|
+
disabled?: boolean;
|
|
93
|
+
emptyOptionLabel: string;
|
|
94
|
+
id: string;
|
|
95
|
+
onValueChange: (value: string | null) => void;
|
|
96
|
+
options: readonly EnumSelectOption[];
|
|
97
|
+
value: string | null | undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function OptionalEnumSelect({
|
|
101
|
+
className,
|
|
102
|
+
disabled,
|
|
103
|
+
emptyOptionLabel,
|
|
104
|
+
id,
|
|
105
|
+
onValueChange,
|
|
106
|
+
options,
|
|
107
|
+
value,
|
|
108
|
+
...aria
|
|
109
|
+
}: OptionalEnumSelectProps) {
|
|
110
|
+
return (
|
|
111
|
+
<Select
|
|
112
|
+
disabled={disabled}
|
|
113
|
+
onValueChange={(nextValue) =>
|
|
114
|
+
onValueChange(nextValue === ENUM_SELECT_EMPTY_VALUE ? null : nextValue)
|
|
115
|
+
}
|
|
116
|
+
value={value ?? ENUM_SELECT_EMPTY_VALUE}
|
|
117
|
+
>
|
|
118
|
+
<SelectTrigger className={className} id={id} {...aria}>
|
|
119
|
+
<SelectValue placeholder={emptyOptionLabel} />
|
|
120
|
+
</SelectTrigger>
|
|
121
|
+
<SelectContent>
|
|
122
|
+
<SelectItem value={ENUM_SELECT_EMPTY_VALUE}>
|
|
123
|
+
{emptyOptionLabel}
|
|
124
|
+
</SelectItem>
|
|
125
|
+
{options.map((option) => (
|
|
126
|
+
<SelectItem
|
|
127
|
+
disabled={option.disabled}
|
|
128
|
+
key={option.value}
|
|
129
|
+
value={option.value}
|
|
130
|
+
>
|
|
131
|
+
{option.label}
|
|
132
|
+
</SelectItem>
|
|
133
|
+
))}
|
|
134
|
+
</SelectContent>
|
|
135
|
+
</Select>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface ResetEnumSelectProps extends EnumSelectAriaProps {
|
|
140
|
+
className?: string;
|
|
141
|
+
disabled?: boolean;
|
|
142
|
+
id: string;
|
|
143
|
+
onValueChange: (value: string) => void;
|
|
144
|
+
options: readonly EnumSelectOption[];
|
|
145
|
+
placeholder: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function ResetEnumSelect({
|
|
149
|
+
className,
|
|
150
|
+
disabled,
|
|
151
|
+
id,
|
|
152
|
+
onValueChange,
|
|
153
|
+
options,
|
|
154
|
+
placeholder,
|
|
155
|
+
...aria
|
|
156
|
+
}: ResetEnumSelectProps) {
|
|
157
|
+
const [instanceKey, setInstanceKey] = useState(0);
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<Select
|
|
161
|
+
key={instanceKey}
|
|
162
|
+
disabled={disabled}
|
|
163
|
+
onValueChange={(nextValue) => {
|
|
164
|
+
onValueChange(nextValue);
|
|
165
|
+
setInstanceKey((current) => current + 1);
|
|
166
|
+
}}
|
|
167
|
+
value={undefined}
|
|
168
|
+
>
|
|
169
|
+
<SelectTrigger className={className} id={id} {...aria}>
|
|
170
|
+
<SelectValue placeholder={placeholder} />
|
|
171
|
+
</SelectTrigger>
|
|
172
|
+
<SelectContent>
|
|
173
|
+
{options.map((option) => (
|
|
174
|
+
<SelectItem
|
|
175
|
+
disabled={option.disabled}
|
|
176
|
+
key={option.value}
|
|
177
|
+
value={option.value}
|
|
178
|
+
>
|
|
179
|
+
{option.label}
|
|
180
|
+
</SelectItem>
|
|
181
|
+
))}
|
|
182
|
+
</SelectContent>
|
|
183
|
+
</Select>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { forwardRef, type InputHTMLAttributes } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../utilities/cn";
|
|
4
|
+
|
|
5
|
+
export type PackageFileInputProps = Omit<
|
|
6
|
+
InputHTMLAttributes<HTMLInputElement>,
|
|
7
|
+
"type"
|
|
8
|
+
>;
|
|
9
|
+
|
|
10
|
+
const FILE_INPUT_CLASS =
|
|
11
|
+
"block w-full rounded-xl px-3 py-3 text-sm text-on-surface-variant outline-none focus-visible:ring-2 focus-visible:ring-af-focus-ring file:mr-3 file:rounded-lg file:border-0 file:bg-surface-container-high file:px-3 file:py-2 file:text-sm file:font-semibold file:text-on-surface hover:bg-af-overlay aria-invalid:border-af-danger-border aria-invalid:ring-2 aria-invalid:ring-af-danger-border disabled:cursor-not-allowed disabled:bg-surface-container-low disabled:text-on-surface-disabled";
|
|
12
|
+
|
|
13
|
+
export const PackageFileInput = forwardRef<
|
|
14
|
+
HTMLInputElement,
|
|
15
|
+
PackageFileInputProps
|
|
16
|
+
>(function PackageFileInput({ className, ...props }, ref) {
|
|
17
|
+
return (
|
|
18
|
+
<input
|
|
19
|
+
className={cn(FILE_INPUT_CLASS, className)}
|
|
20
|
+
ref={ref}
|
|
21
|
+
type="file"
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
});
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ElementType,
|
|
3
|
+
forwardRef,
|
|
4
|
+
type FieldsetHTMLAttributes,
|
|
5
|
+
type HTMLAttributes,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
} from "react";
|
|
8
|
+
|
|
9
|
+
import { cn } from "../utilities/cn";
|
|
10
|
+
|
|
11
|
+
export type FormFieldMessageIds = {
|
|
12
|
+
descriptionId?: string;
|
|
13
|
+
helperId?: string;
|
|
14
|
+
warningId?: string;
|
|
15
|
+
errorId?: string;
|
|
16
|
+
successId?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function buildFormFieldAriaDescribedBy(
|
|
20
|
+
messageIds: FormFieldMessageIds,
|
|
21
|
+
): string | undefined {
|
|
22
|
+
const ids = [
|
|
23
|
+
messageIds.descriptionId,
|
|
24
|
+
messageIds.helperId,
|
|
25
|
+
messageIds.warningId,
|
|
26
|
+
messageIds.errorId,
|
|
27
|
+
messageIds.successId,
|
|
28
|
+
].filter((id): id is string => Boolean(id));
|
|
29
|
+
|
|
30
|
+
return ids.length > 0 ? ids.join(" ") : undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type FormFieldProps = HTMLAttributes<HTMLDivElement>;
|
|
34
|
+
|
|
35
|
+
export const FormField = forwardRef<HTMLDivElement, FormFieldProps>(
|
|
36
|
+
function FormField({ className, ...props }, ref) {
|
|
37
|
+
return <div className={cn("space-y-2", className)} ref={ref} {...props} />;
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export type FormLabelProps = HTMLAttributes<HTMLElement> & {
|
|
42
|
+
as?: ElementType;
|
|
43
|
+
children?: ReactNode;
|
|
44
|
+
htmlFor?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const FORM_LABEL_CLASS = "block text-sm font-semibold text-on-surface";
|
|
48
|
+
|
|
49
|
+
export const FormLabel = forwardRef<HTMLElement, FormLabelProps>(
|
|
50
|
+
function FormLabel({ as: Component = "label", className, ...props }, ref) {
|
|
51
|
+
return (
|
|
52
|
+
<Component
|
|
53
|
+
className={cn(FORM_LABEL_CLASS, className)}
|
|
54
|
+
ref={ref}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
export interface FormDescriptionProps extends HTMLAttributes<HTMLElement> {
|
|
62
|
+
as?: ElementType;
|
|
63
|
+
variant?: "body" | "supporting";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const FORM_BODY_TEXT_CLASS = "m-0 text-body-medium text-on-surface";
|
|
67
|
+
const FORM_SUPPORTING_TEXT_CLASS =
|
|
68
|
+
"m-0 text-body-small text-on-surface-variant";
|
|
69
|
+
|
|
70
|
+
export const FormDescription = forwardRef<HTMLElement, FormDescriptionProps>(
|
|
71
|
+
function FormDescription(
|
|
72
|
+
{ as: Component = "p", className, variant = "supporting", ...props },
|
|
73
|
+
ref,
|
|
74
|
+
) {
|
|
75
|
+
return (
|
|
76
|
+
<Component
|
|
77
|
+
className={cn(
|
|
78
|
+
variant === "body" ? FORM_BODY_TEXT_CLASS : FORM_SUPPORTING_TEXT_CLASS,
|
|
79
|
+
className,
|
|
80
|
+
)}
|
|
81
|
+
ref={ref}
|
|
82
|
+
{...props}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
export interface FormHelperTextProps extends HTMLAttributes<HTMLElement> {
|
|
89
|
+
as?: ElementType;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const FormHelperText = forwardRef<HTMLElement, FormHelperTextProps>(
|
|
93
|
+
function FormHelperText(
|
|
94
|
+
{ as: Component = "p", className, ...props },
|
|
95
|
+
ref,
|
|
96
|
+
) {
|
|
97
|
+
return (
|
|
98
|
+
<Component
|
|
99
|
+
className={cn(FORM_SUPPORTING_TEXT_CLASS, className)}
|
|
100
|
+
ref={ref}
|
|
101
|
+
{...props}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
export interface FormErrorProps extends HTMLAttributes<HTMLElement> {
|
|
108
|
+
as?: ElementType;
|
|
109
|
+
role?: "alert" | "status";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const FormError = forwardRef<HTMLElement, FormErrorProps>(
|
|
113
|
+
function FormError(
|
|
114
|
+
{ as: Component = "p", className, role = "alert", ...props },
|
|
115
|
+
ref,
|
|
116
|
+
) {
|
|
117
|
+
return (
|
|
118
|
+
<Component
|
|
119
|
+
className={cn(
|
|
120
|
+
"m-0 text-body-small font-medium text-on-error-container",
|
|
121
|
+
className,
|
|
122
|
+
)}
|
|
123
|
+
ref={ref}
|
|
124
|
+
role={role}
|
|
125
|
+
{...props}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
},
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
export interface FormWarningProps extends HTMLAttributes<HTMLElement> {
|
|
132
|
+
as?: ElementType;
|
|
133
|
+
role?: "alert" | "status";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const FormWarning = forwardRef<HTMLElement, FormWarningProps>(
|
|
137
|
+
function FormWarning(
|
|
138
|
+
{ as: Component = "p", className, role, ...props },
|
|
139
|
+
ref,
|
|
140
|
+
) {
|
|
141
|
+
return (
|
|
142
|
+
<Component
|
|
143
|
+
className={cn(
|
|
144
|
+
"m-0 text-body-small font-medium text-on-warning-container",
|
|
145
|
+
className,
|
|
146
|
+
)}
|
|
147
|
+
ref={ref}
|
|
148
|
+
role={role}
|
|
149
|
+
{...props}
|
|
150
|
+
/>
|
|
151
|
+
);
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
export interface FormSuccessProps extends HTMLAttributes<HTMLElement> {
|
|
156
|
+
as?: ElementType;
|
|
157
|
+
role?: "alert" | "status";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const FormSuccess = forwardRef<HTMLElement, FormSuccessProps>(
|
|
161
|
+
function FormSuccess(
|
|
162
|
+
{ as: Component = "p", className, role = "status", ...props },
|
|
163
|
+
ref,
|
|
164
|
+
) {
|
|
165
|
+
return (
|
|
166
|
+
<Component
|
|
167
|
+
className={cn(
|
|
168
|
+
"m-0 text-body-small font-medium text-on-success-container",
|
|
169
|
+
className,
|
|
170
|
+
)}
|
|
171
|
+
ref={ref}
|
|
172
|
+
role={role}
|
|
173
|
+
{...props}
|
|
174
|
+
/>
|
|
175
|
+
);
|
|
176
|
+
},
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
export type FormFieldGroupProps = FieldsetHTMLAttributes<HTMLFieldSetElement>;
|
|
180
|
+
|
|
181
|
+
export const FormFieldGroup = forwardRef<HTMLFieldSetElement, FormFieldGroupProps>(
|
|
182
|
+
function FormFieldGroup({ className, ...props }, ref) {
|
|
183
|
+
return (
|
|
184
|
+
<fieldset
|
|
185
|
+
className={cn("m-0 space-y-2 border-0 p-0", className)}
|
|
186
|
+
ref={ref}
|
|
187
|
+
{...props}
|
|
188
|
+
/>
|
|
189
|
+
);
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
export type FormFieldGroupLabelProps = HTMLAttributes<HTMLLegendElement>;
|
|
194
|
+
|
|
195
|
+
export const FormFieldGroupLabel = forwardRef<
|
|
196
|
+
HTMLLegendElement,
|
|
197
|
+
FormFieldGroupLabelProps
|
|
198
|
+
>(function FormFieldGroupLabel({ className, ...props }, ref) {
|
|
199
|
+
return (
|
|
200
|
+
<legend className={cn(FORM_LABEL_CLASS, "px-0", className)} ref={ref} {...props} />
|
|
201
|
+
);
|
|
202
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { forwardRef, type InputHTMLAttributes } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../utilities/cn";
|
|
4
|
+
|
|
5
|
+
export type PackageInputProps = InputHTMLAttributes<HTMLInputElement>;
|
|
6
|
+
|
|
7
|
+
const INPUT_CLASS =
|
|
8
|
+
"flex min-h-11 w-full rounded-xl border border-outline bg-surface-container-high px-3 py-2.5 text-sm text-on-surface outline-none transition-colors placeholder:text-on-surface-disabled focus-visible:border-outline-variant focus-visible:ring-2 focus-visible:ring-af-focus-ring aria-invalid:border-af-danger-border aria-invalid:ring-2 aria-invalid:ring-af-danger-border disabled:cursor-not-allowed disabled:border-outline disabled:bg-surface-container-low disabled:text-on-surface-disabled";
|
|
9
|
+
|
|
10
|
+
export function inputVariants({ className }: { className?: string } = {}) {
|
|
11
|
+
return cn(INPUT_CLASS, className);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const PackageInput = forwardRef<HTMLInputElement, PackageInputProps>(
|
|
15
|
+
function PackageInput({ className, type = "text", ...props }, ref) {
|
|
16
|
+
return (
|
|
17
|
+
<input
|
|
18
|
+
className={inputVariants({ className })}
|
|
19
|
+
ref={ref}
|
|
20
|
+
type={type}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
},
|
|
25
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { forwardRef, type SelectHTMLAttributes } from "react";
|
|
2
|
+
|
|
3
|
+
import { inputVariants } from "./package-input";
|
|
4
|
+
import { SelectChevronDownIcon } from "./select-icons";
|
|
5
|
+
|
|
6
|
+
export type NativeSelectProps = SelectHTMLAttributes<HTMLSelectElement>;
|
|
7
|
+
|
|
8
|
+
export const NativeSelect = forwardRef<HTMLSelectElement, NativeSelectProps>(
|
|
9
|
+
function NativeSelect({ children, className, ...props }, ref) {
|
|
10
|
+
return (
|
|
11
|
+
<div className="relative">
|
|
12
|
+
<select
|
|
13
|
+
className={inputVariants({
|
|
14
|
+
className: `appearance-none pr-10 ${className ?? ""}`,
|
|
15
|
+
})}
|
|
16
|
+
ref={ref}
|
|
17
|
+
{...props}
|
|
18
|
+
>
|
|
19
|
+
{children}
|
|
20
|
+
</select>
|
|
21
|
+
<SelectChevronDownIcon className="pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-af-text-subtle" />
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
},
|
|
25
|
+
);
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
2
|
+
import {
|
|
3
|
+
type ComponentProps,
|
|
4
|
+
type ComponentRef,
|
|
5
|
+
forwardRef,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
} from "react";
|
|
8
|
+
|
|
9
|
+
import { cn } from "../utilities/cn";
|
|
10
|
+
import { inputVariants } from "./package-input";
|
|
11
|
+
import { SelectCheckIcon, SelectChevronDownIcon } from "./select-icons";
|
|
12
|
+
|
|
13
|
+
export const Select = SelectPrimitive.Root;
|
|
14
|
+
export const SelectGroup = SelectPrimitive.Group;
|
|
15
|
+
export const SelectValue = SelectPrimitive.Value;
|
|
16
|
+
|
|
17
|
+
export type SelectTriggerProps = ComponentProps<typeof SelectPrimitive.Trigger>;
|
|
18
|
+
|
|
19
|
+
export const SelectTrigger = forwardRef<
|
|
20
|
+
ComponentRef<typeof SelectPrimitive.Trigger>,
|
|
21
|
+
SelectTriggerProps
|
|
22
|
+
>(function SelectTrigger({ className, children, ...props }, ref) {
|
|
23
|
+
return (
|
|
24
|
+
<SelectPrimitive.Trigger
|
|
25
|
+
className={cn(
|
|
26
|
+
inputVariants({
|
|
27
|
+
className:
|
|
28
|
+
"flex items-center justify-between gap-2 text-left data-[placeholder]:text-on-surface-disabled [&>span]:line-clamp-1",
|
|
29
|
+
}),
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
ref={ref}
|
|
33
|
+
{...props}
|
|
34
|
+
>
|
|
35
|
+
{children}
|
|
36
|
+
<SelectPrimitive.Icon asChild>
|
|
37
|
+
<SelectChevronDownIcon className="h-4 w-4 shrink-0 text-af-text-subtle" />
|
|
38
|
+
</SelectPrimitive.Icon>
|
|
39
|
+
</SelectPrimitive.Trigger>
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export type SelectContentProps = ComponentProps<typeof SelectPrimitive.Content>;
|
|
44
|
+
|
|
45
|
+
// tailwind-exception: intrinsic-sizing
|
|
46
|
+
const SELECT_CONTENT_POPPER_CLASS = "min-w-[var(--radix-select-trigger-width)]";
|
|
47
|
+
// tailwind-exception: intrinsic-sizing
|
|
48
|
+
const SELECT_VIEWPORT_POPPER_CLASS =
|
|
49
|
+
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]";
|
|
50
|
+
|
|
51
|
+
export function SelectContent({
|
|
52
|
+
children,
|
|
53
|
+
className,
|
|
54
|
+
onCloseAutoFocus,
|
|
55
|
+
position = "popper",
|
|
56
|
+
...props
|
|
57
|
+
}: SelectContentProps) {
|
|
58
|
+
return (
|
|
59
|
+
<SelectPrimitive.Portal>
|
|
60
|
+
<SelectPrimitive.Content
|
|
61
|
+
className={cn(
|
|
62
|
+
"relative z-50 max-h-72 overflow-hidden rounded-xl border border-outline bg-surface-container-high text-on-surface shadow-af-panel data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
63
|
+
position === "popper" && SELECT_CONTENT_POPPER_CLASS,
|
|
64
|
+
position === "popper" &&
|
|
65
|
+
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
66
|
+
className,
|
|
67
|
+
)}
|
|
68
|
+
onCloseAutoFocus={onCloseAutoFocus}
|
|
69
|
+
position={position}
|
|
70
|
+
{...props}
|
|
71
|
+
>
|
|
72
|
+
<SelectPrimitive.Viewport
|
|
73
|
+
className={cn(
|
|
74
|
+
"p-1",
|
|
75
|
+
position === "popper" && SELECT_VIEWPORT_POPPER_CLASS,
|
|
76
|
+
)}
|
|
77
|
+
>
|
|
78
|
+
{children}
|
|
79
|
+
</SelectPrimitive.Viewport>
|
|
80
|
+
</SelectPrimitive.Content>
|
|
81
|
+
</SelectPrimitive.Portal>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type SelectLabelProps = ComponentProps<typeof SelectPrimitive.Label>;
|
|
86
|
+
|
|
87
|
+
export function SelectLabel({ className, ...props }: SelectLabelProps) {
|
|
88
|
+
return (
|
|
89
|
+
<SelectPrimitive.Label
|
|
90
|
+
className={cn(
|
|
91
|
+
"px-2 py-1.5 text-xs font-semibold uppercase tracking-[0.08em] text-on-surface-subtle",
|
|
92
|
+
className,
|
|
93
|
+
)}
|
|
94
|
+
{...props}
|
|
95
|
+
/>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const SELECT_EMPTY_STATE_VALUE = "__select-empty-state__";
|
|
100
|
+
|
|
101
|
+
export type SelectItemProps = ComponentProps<typeof SelectPrimitive.Item>;
|
|
102
|
+
|
|
103
|
+
export const SelectItem = forwardRef<
|
|
104
|
+
ComponentRef<typeof SelectPrimitive.Item>,
|
|
105
|
+
SelectItemProps
|
|
106
|
+
>(function SelectItem({ children, className, ...props }, ref) {
|
|
107
|
+
return (
|
|
108
|
+
<SelectPrimitive.Item
|
|
109
|
+
className={cn(
|
|
110
|
+
"relative flex w-full min-w-0 cursor-default select-none items-center rounded-lg py-2 pl-8 pr-2 text-sm outline-none focus:bg-surface-container-highest data-[disabled]:pointer-events-none data-[disabled]:text-on-surface-disabled data-[highlighted]:bg-surface-container-highest",
|
|
111
|
+
className,
|
|
112
|
+
)}
|
|
113
|
+
ref={ref}
|
|
114
|
+
{...props}
|
|
115
|
+
>
|
|
116
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
117
|
+
<SelectPrimitive.ItemIndicator>
|
|
118
|
+
<SelectCheckIcon className="h-4 w-4" />
|
|
119
|
+
</SelectPrimitive.ItemIndicator>
|
|
120
|
+
</span>
|
|
121
|
+
<SelectPrimitive.ItemText className="line-clamp-2 break-words">
|
|
122
|
+
{children}
|
|
123
|
+
</SelectPrimitive.ItemText>
|
|
124
|
+
</SelectPrimitive.Item>
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
export type SelectEmptyProps = {
|
|
129
|
+
children: ReactNode;
|
|
130
|
+
className?: string;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export function SelectEmpty({ children, className }: SelectEmptyProps) {
|
|
134
|
+
return (
|
|
135
|
+
<SelectPrimitive.Item
|
|
136
|
+
className={cn(
|
|
137
|
+
"relative flex w-full min-w-0 cursor-default select-none items-center rounded-lg px-2 py-2 text-sm text-on-surface-disabled data-[disabled]:pointer-events-none data-[disabled]:text-on-surface-disabled",
|
|
138
|
+
className,
|
|
139
|
+
)}
|
|
140
|
+
disabled
|
|
141
|
+
value={SELECT_EMPTY_STATE_VALUE}
|
|
142
|
+
>
|
|
143
|
+
<SelectPrimitive.ItemText className="line-clamp-2 break-words">
|
|
144
|
+
{children}
|
|
145
|
+
</SelectPrimitive.ItemText>
|
|
146
|
+
</SelectPrimitive.Item>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export type SelectSeparatorProps = ComponentProps<
|
|
151
|
+
typeof SelectPrimitive.Separator
|
|
152
|
+
>;
|
|
153
|
+
|
|
154
|
+
export function SelectSeparator({ className, ...props }: SelectSeparatorProps) {
|
|
155
|
+
return (
|
|
156
|
+
<SelectPrimitive.Separator
|
|
157
|
+
className={cn("-mx-1 my-1 h-px bg-outline", className)}
|
|
158
|
+
{...props}
|
|
159
|
+
/>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface SelectFieldProps {
|
|
164
|
+
children: ReactNode;
|
|
165
|
+
className?: string;
|
|
166
|
+
description?: ReactNode;
|
|
167
|
+
descriptionId?: string;
|
|
168
|
+
error?: ReactNode;
|
|
169
|
+
errorId?: string;
|
|
170
|
+
inputId: string;
|
|
171
|
+
label: ReactNode;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function SelectField({
|
|
175
|
+
children,
|
|
176
|
+
className,
|
|
177
|
+
description,
|
|
178
|
+
descriptionId,
|
|
179
|
+
error,
|
|
180
|
+
errorId,
|
|
181
|
+
inputId,
|
|
182
|
+
label,
|
|
183
|
+
}: SelectFieldProps) {
|
|
184
|
+
return (
|
|
185
|
+
<div className={cn("grid gap-2", className)}>
|
|
186
|
+
<label
|
|
187
|
+
className="text-xs font-bold uppercase tracking-[0.08em] text-af-text-subtle"
|
|
188
|
+
htmlFor={inputId}
|
|
189
|
+
>
|
|
190
|
+
{label}
|
|
191
|
+
</label>
|
|
192
|
+
{children}
|
|
193
|
+
{description ? (
|
|
194
|
+
<p className="m-0 text-sm text-af-text-muted" id={descriptionId}>
|
|
195
|
+
{description}
|
|
196
|
+
</p>
|
|
197
|
+
) : null}
|
|
198
|
+
{error ? (
|
|
199
|
+
<p
|
|
200
|
+
className="m-0 text-sm font-medium text-on-error-container"
|
|
201
|
+
id={errorId}
|
|
202
|
+
role="alert"
|
|
203
|
+
>
|
|
204
|
+
{error}
|
|
205
|
+
</p>
|
|
206
|
+
) : null}
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { forwardRef, type TextareaHTMLAttributes } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../utilities/cn";
|
|
4
|
+
import { inputVariants } from "./package-input";
|
|
5
|
+
|
|
6
|
+
const TEXTAREA_PLAIN_CLASS =
|
|
7
|
+
"m-0 w-full resize-none border-0 bg-transparent p-0 text-sm leading-6 text-on-surface outline-none";
|
|
8
|
+
|
|
9
|
+
const TEXTAREA_FIELD_CLASS =
|
|
10
|
+
"min-h-28 max-h-52 resize-none overflow-y-auto py-3";
|
|
11
|
+
|
|
12
|
+
export type PackageTextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
|
13
|
+
variant?: "field" | "plain";
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function textareaVariants({ className }: { className?: string } = {}) {
|
|
17
|
+
return inputVariants({ className: cn(TEXTAREA_FIELD_CLASS, className) });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const PackageTextarea = forwardRef<
|
|
21
|
+
HTMLTextAreaElement,
|
|
22
|
+
PackageTextareaProps
|
|
23
|
+
>(function PackageTextarea({ className, variant = "field", ...props }, ref) {
|
|
24
|
+
return (
|
|
25
|
+
<textarea
|
|
26
|
+
className={
|
|
27
|
+
variant === "plain"
|
|
28
|
+
? cn(TEXTAREA_PLAIN_CLASS, className)
|
|
29
|
+
: textareaVariants({ className })
|
|
30
|
+
}
|
|
31
|
+
ref={ref}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
});
|