@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.
Files changed (92) hide show
  1. package/README.md +223 -0
  2. package/docs/README.md +83 -0
  3. package/docs/button.md +119 -0
  4. package/docs/charts.md +325 -0
  5. package/docs/data-display-code-panel.md +120 -0
  6. package/docs/feedback-alert-panel.md +117 -0
  7. package/docs/feedback-skeleton.md +54 -0
  8. package/docs/forms-form-field.md +490 -0
  9. package/docs/forms-input-primitives.md +311 -0
  10. package/docs/forms-select-primitives.md +362 -0
  11. package/docs/graphs.md +250 -0
  12. package/docs/layout-display-primitives.md +211 -0
  13. package/docs/overlays.md +414 -0
  14. package/docs/table-data-table.md +331 -0
  15. package/docs/typography-roles.md +182 -0
  16. package/docs/widget-frame-recipes.md +273 -0
  17. package/package.json +161 -0
  18. package/src/category-paths.ts +20 -0
  19. package/src/charts/chart-state-panel.tsx +103 -0
  20. package/src/charts/chart.tsx +287 -0
  21. package/src/charts/index.ts +21 -0
  22. package/src/data-display/code-panel.tsx +67 -0
  23. package/src/data-display/data-table.tsx +180 -0
  24. package/src/data-display/description-list.tsx +24 -0
  25. package/src/data-display/index.ts +34 -0
  26. package/src/data-display/table-layout.ts +12 -0
  27. package/src/data-display/table.tsx +131 -0
  28. package/src/feedback/alert-panel-semantics.ts +75 -0
  29. package/src/feedback/alert-panel.tsx +187 -0
  30. package/src/feedback/index.ts +21 -0
  31. package/src/feedback/skeleton.tsx +16 -0
  32. package/src/forms/index.ts +73 -0
  33. package/src/forms/package-checkbox.tsx +50 -0
  34. package/src/forms/package-enum-select.tsx +185 -0
  35. package/src/forms/package-file-input.tsx +25 -0
  36. package/src/forms/package-form-field.tsx +202 -0
  37. package/src/forms/package-input.tsx +25 -0
  38. package/src/forms/package-native-select.tsx +25 -0
  39. package/src/forms/package-select.tsx +209 -0
  40. package/src/forms/package-textarea.tsx +35 -0
  41. package/src/forms/select-icons.tsx +35 -0
  42. package/src/graphs/graph-edge-path.ts +241 -0
  43. package/src/graphs/graph-edge.tsx +130 -0
  44. package/src/graphs/graph-interactive-example.tsx +187 -0
  45. package/src/graphs/graph-node-button.tsx +51 -0
  46. package/src/graphs/graph-node-handle-badge.tsx +108 -0
  47. package/src/graphs/graph-node-handle.ts +28 -0
  48. package/src/graphs/graph-node-shell.tsx +111 -0
  49. package/src/graphs/graph-node-state-indicator.tsx +47 -0
  50. package/src/graphs/graph-node-state.ts +111 -0
  51. package/src/graphs/graph-viewport-surface.tsx +27 -0
  52. package/src/graphs/index.ts +41 -0
  53. package/src/icons/index.ts +4 -0
  54. package/src/index.ts +209 -0
  55. package/src/layout/action-row.tsx +53 -0
  56. package/src/layout/index.ts +9 -0
  57. package/src/layout/surface-panel.tsx +91 -0
  58. package/src/navigation/index.ts +4 -0
  59. package/src/overlays/collapsible.tsx +22 -0
  60. package/src/overlays/dialog.tsx +148 -0
  61. package/src/overlays/index.ts +31 -0
  62. package/src/overlays/overlay-layout.ts +9 -0
  63. package/src/overlays/popover.tsx +29 -0
  64. package/src/overlays/scroll-area.tsx +78 -0
  65. package/src/primitives/button-link.tsx +22 -0
  66. package/src/primitives/button.tsx +258 -0
  67. package/src/primitives/icon-button-shell.tsx +49 -0
  68. package/src/primitives/index.ts +31 -0
  69. package/src/primitives/package-text.tsx +29 -0
  70. package/src/primitives/typography-roles.ts +12 -0
  71. package/src/primitives/typography.tsx +165 -0
  72. package/src/recipes/index.ts +61 -0
  73. package/src/recipes/widget-frame-content.tsx +114 -0
  74. package/src/recipes/widget-frame-disclosure.tsx +161 -0
  75. package/src/recipes/widget-frame-layout.ts +40 -0
  76. package/src/recipes/widget-frame-skeleton.tsx +16 -0
  77. package/src/recipes/widget-frame-states.tsx +99 -0
  78. package/src/recipes/widget-frame-typography.ts +19 -0
  79. package/src/recipes/widget-frame.tsx +84 -0
  80. package/src/styles/color-palette-presets.css +167 -0
  81. package/src/styles/color-role-tokens.css +163 -0
  82. package/src/styles/layout-role-tokens.css +27 -0
  83. package/src/styles/text-color-role-tokens.css +29 -0
  84. package/src/styles/typography-role-tokens.css +72 -0
  85. package/src/styles/typography-role-utilities.css +73 -0
  86. package/src/styles/typography-roles.css +53 -0
  87. package/src/styles.css +32 -0
  88. package/src/testing/index.ts +13 -0
  89. package/src/testing/render.tsx +16 -0
  90. package/src/tokens/index.ts +4 -0
  91. package/src/utilities/cn.ts +5 -0
  92. 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
+ });