@aviala-design/spiral 2.9.0 → 3.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/dist/chunk-CXGFGGGI.cjs +174 -0
- package/dist/chunk-CXGFGGGI.cjs.map +1 -0
- package/dist/chunk-XVABQUTX.js +174 -0
- package/dist/chunk-XVABQUTX.js.map +1 -0
- package/dist/component-changelogs.json +137 -0
- package/dist/form-field-context-XYh-G1KL.d.cts +7 -0
- package/dist/form-field-context-XYh-G1KL.d.ts +7 -0
- package/dist/form.cjs +187 -0
- package/dist/form.cjs.map +1 -0
- package/dist/form.d.cts +85 -0
- package/dist/form.d.ts +85 -0
- package/dist/form.js +187 -0
- package/dist/form.js.map +1 -0
- package/dist/index.cjs +4520 -3592
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -99
- package/dist/index.d.ts +14 -99
- package/dist/index.js +3292 -2432
- package/dist/index.js.map +1 -1
- package/dist/props.json +15 -12
- package/dist/styles.css +240 -65
- package/package.json +17 -4
package/dist/form.cjs
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
var _chunkCXGFGGGIcjs = require('./chunk-CXGFGGGI.cjs');
|
|
9
|
+
|
|
10
|
+
// src/components/form-field.tsx
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
var _icons = require('@aviala-design/icons');
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
var _react = require('react');
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
var _reacthookform = require('react-hook-form');
|
|
24
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
25
|
+
var FormFieldLayout = _react.forwardRef.call(void 0,
|
|
26
|
+
({
|
|
27
|
+
className,
|
|
28
|
+
label,
|
|
29
|
+
htmlFor,
|
|
30
|
+
description,
|
|
31
|
+
error,
|
|
32
|
+
info,
|
|
33
|
+
alert,
|
|
34
|
+
required,
|
|
35
|
+
direction = "vertical",
|
|
36
|
+
children,
|
|
37
|
+
...props
|
|
38
|
+
}, ref) => {
|
|
39
|
+
const reactId = _react.useId.call(void 0, );
|
|
40
|
+
const labelId = `${reactId}-label`;
|
|
41
|
+
const hasLabel = label != null && label !== "";
|
|
42
|
+
const invalid = Boolean(error);
|
|
43
|
+
const controlContext = _react.useMemo.call(void 0, () => ({ invalid }), [invalid]);
|
|
44
|
+
const labelNode = hasLabel || description ? htmlFor ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
45
|
+
"label",
|
|
46
|
+
{
|
|
47
|
+
htmlFor,
|
|
48
|
+
className: "aviala-form__label aviala-form__label--control",
|
|
49
|
+
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
50
|
+
_chunkCXGFGGGIcjs.Typeface,
|
|
51
|
+
{
|
|
52
|
+
id: labelId,
|
|
53
|
+
content: "textCaption",
|
|
54
|
+
primary: hasLabel ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
55
|
+
label,
|
|
56
|
+
required ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "aviala-form__required", "aria-hidden": true, children: "*" }) : null
|
|
57
|
+
] }) : void 0,
|
|
58
|
+
secondary: description
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
63
|
+
_chunkCXGFGGGIcjs.Typeface,
|
|
64
|
+
{
|
|
65
|
+
id: labelId,
|
|
66
|
+
className: "aviala-form__label",
|
|
67
|
+
content: "textCaption",
|
|
68
|
+
primary: hasLabel ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
69
|
+
label,
|
|
70
|
+
required ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "aviala-form__required", "aria-hidden": true, children: "*" }) : null
|
|
71
|
+
] }) : void 0,
|
|
72
|
+
secondary: description
|
|
73
|
+
}
|
|
74
|
+
) : null;
|
|
75
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
76
|
+
"div",
|
|
77
|
+
{
|
|
78
|
+
ref,
|
|
79
|
+
className: _chunkCXGFGGGIcjs.cn.call(void 0, "aviala-form", className),
|
|
80
|
+
"data-direction": direction,
|
|
81
|
+
"aria-labelledby": hasLabel ? labelId : void 0,
|
|
82
|
+
...props,
|
|
83
|
+
children: [
|
|
84
|
+
labelNode,
|
|
85
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "aviala-form__content", children: [
|
|
86
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkCXGFGGGIcjs.FormFieldControlContext.Provider, { value: controlContext, children }),
|
|
87
|
+
error ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
88
|
+
"div",
|
|
89
|
+
{
|
|
90
|
+
className: "aviala-form__message aviala-form__message--error",
|
|
91
|
+
role: "alert",
|
|
92
|
+
children: [
|
|
93
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
94
|
+
_icons.SymbolWrongCircle,
|
|
95
|
+
{
|
|
96
|
+
className: "aviala-form__message-icon",
|
|
97
|
+
width: 14,
|
|
98
|
+
height: 14,
|
|
99
|
+
"aria-hidden": true
|
|
100
|
+
}
|
|
101
|
+
),
|
|
102
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
103
|
+
_chunkCXGFGGGIcjs.Typography,
|
|
104
|
+
{
|
|
105
|
+
level: "caption",
|
|
106
|
+
as: "p",
|
|
107
|
+
className: "aviala-form__message-text",
|
|
108
|
+
children: error
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
) : null,
|
|
114
|
+
info ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "aviala-form__message aviala-form__message--info", children: [
|
|
115
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
116
|
+
_icons.SymbolInformationCircle,
|
|
117
|
+
{
|
|
118
|
+
className: "aviala-form__message-icon",
|
|
119
|
+
width: 14,
|
|
120
|
+
height: 14,
|
|
121
|
+
"aria-hidden": true
|
|
122
|
+
}
|
|
123
|
+
),
|
|
124
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
125
|
+
_chunkCXGFGGGIcjs.Typography,
|
|
126
|
+
{
|
|
127
|
+
level: "caption",
|
|
128
|
+
as: "p",
|
|
129
|
+
className: "aviala-form__message-text",
|
|
130
|
+
children: info
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
] }) : null,
|
|
134
|
+
alert ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "aviala-form__alert", children: alert }) : null
|
|
135
|
+
] })
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
FormFieldLayout.displayName = "FormFieldLayout";
|
|
142
|
+
var FormFieldControlled = _react.forwardRef.call(void 0,
|
|
143
|
+
({
|
|
144
|
+
control,
|
|
145
|
+
name,
|
|
146
|
+
rules,
|
|
147
|
+
defaultValue,
|
|
148
|
+
shouldUnregister,
|
|
149
|
+
disabled,
|
|
150
|
+
render,
|
|
151
|
+
error,
|
|
152
|
+
...layoutProps
|
|
153
|
+
}, ref) => {
|
|
154
|
+
const id = _react.useId.call(void 0, );
|
|
155
|
+
const { field, fieldState, formState } = _reacthookform.useController.call(void 0, {
|
|
156
|
+
name,
|
|
157
|
+
control,
|
|
158
|
+
rules,
|
|
159
|
+
defaultValue,
|
|
160
|
+
shouldUnregister,
|
|
161
|
+
disabled
|
|
162
|
+
});
|
|
163
|
+
const message = error !== void 0 ? error : _nullishCoalesce(_optionalChain([fieldState, 'access', _ => _.error, 'optionalAccess', _2 => _2.message]), () => ( null));
|
|
164
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FormFieldLayout, { ref, ...layoutProps, htmlFor: id, error: message, children: render({ field, fieldState, formState, id }) });
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
FormFieldControlled.displayName = "FormFieldControlled";
|
|
168
|
+
function isControlledProps(props) {
|
|
169
|
+
return "render" in props && typeof props.render === "function" && "name" in props;
|
|
170
|
+
}
|
|
171
|
+
var FormFieldRoot = _react.forwardRef.call(void 0,
|
|
172
|
+
(props, ref) => {
|
|
173
|
+
if (isControlledProps(props)) {
|
|
174
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FormFieldControlled, { ref, ...props });
|
|
175
|
+
}
|
|
176
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, FormFieldLayout, { ref, ...props });
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
var FormField = FormFieldRoot;
|
|
180
|
+
FormField.displayName = "FormField";
|
|
181
|
+
var Form = _reacthookform.FormProvider;
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
exports.Form = Form; exports.FormField = FormField; exports.useResolvedControlError = _chunkCXGFGGGIcjs.useResolvedControlError;
|
|
187
|
+
//# sourceMappingURL=form.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/spiral-2/spiral-2/packages/ui/dist/form.cjs","../src/components/form-field.tsx"],"names":[],"mappings":"AAAA,6rBAAY;AACZ;AACE;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;ACTA;AACE;AACA;AAAA,6CACK;AACP;AACE;AACA;AACA;AAAA,8BAKK;AACP;AACE;AACA;AAAA,gDAQK;AAsGW,+CAAA;AAlClB,IAAM,gBAAA,EAAkB,+BAAA;AAAA,EACtB,CACE;AAAA,IACE,SAAA;AAAA,IACA,KAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA;AAAA,IACA,KAAA;AAAA,IACA,IAAA;AAAA,IACA,KAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA,EAAY,UAAA;AAAA,IACZ,QAAA;AAAA,IACA,GAAG;AAAA,EACL,CAAA,EACA,GAAA,EAAA,GACG;AACH,IAAA,MAAM,QAAA,EAAU,0BAAA,CAAM;AACtB,IAAA,MAAM,QAAA,EAAU,CAAA,EAAA;AACC,IAAA;AACD,IAAA;AACV,IAAA;AACA,IAAA;AAGC,MAAA;AAAA,MAAA;AACC,QAAA;AACU,QAAA;AAEV,QAAA;AAAC,UAAA;AAAA,UAAA;AACK,YAAA;AACI,YAAA;AAEN,YAAA;AAEK,cAAA;AACA,cAAA;AAMD,YAAA;AAEN,YAAA;AAAW,UAAA;AACb,QAAA;AAAA,MAAA;AAGF,IAAA;AAAC,MAAA;AAAA,MAAA;AACK,QAAA;AACM,QAAA;AACF,QAAA;AAEN,QAAA;AAEK,UAAA;AAEC,UAAA;AAKF,QAAA;AAEK,QAAA;AAAA,MAAA;AAGb,IAAA;AAGJ,IAAA;AAAC,MAAA;AAAA,MAAA;AACC,QAAA;AACW,QAAA;AACX,QAAA;AACA,QAAA;AACI,QAAA;AAEH,QAAA;AAAA,UAAA;AACD,0BAAA;AACE,4BAAA;AAIE,YAAA;AAAC,cAAA;AAAA,cAAA;AACC,gBAAA;AACA,gBAAA;AAEA,gBAAA;AAAA,kCAAA;AAAC,oBAAA;AAAA,oBAAA;AAAA,sBAAA;AACW,sBAAA;AACH,sBAAA;AACC,sBAAA;AACG,oBAAA;AACb,kBAAA;AACA,kCAAA;AAAC,oBAAA;AAAA,oBAAA;AAAA,sBAAA;AACO,sBAAA;AACH,sBAAA;AACO,sBAAA;AAET,oBAAA;AACH,kBAAA;AAAA,gBAAA;AAAA,cAAA;AAEA,YAAA;AAEF,YAAA;AACE,8BAAA;AAAC,gBAAA;AAAA,gBAAA;AACC,kBAAA;AACA,kBAAA;AACA,kBAAA;AACA,kBAAA;AAAW,gBAAA;AACb,cAAA;AACA,8BAAA;AAAC,gBAAA;AAAA,gBAAA;AACC,kBAAA;AACA,kBAAA;AACA,kBAAA;AAEC,kBAAA;AAAA,gBAAA;AACH,cAAA;AAEA,YAAA;AACK,YAAA;AACX,UAAA;AAAA,QAAA;AAAA,MAAA;AACF,IAAA;AAEJ,EAAA;AACF;AACgB;AAEV;AAKF,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACG,IAAA;AAGF,EAAA;AACc,IAAA;AACF,IAAA;AACb,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACD,IAAA;AAEC,IAAA;AAGA,IAAA;AAIJ,EAAA;AACF;AACoB;AAEX;AAIO,EAAA;AAEhB;AAEsB;AACJ,EAAA;AACV,IAAA;AACK,MAAA;AACT,IAAA;AACO,IAAA;AACT,EAAA;AACF;AA6ByB;AASf;AAGU;ADpIC;AACA;AACA;AACA;AACA","file":"/home/runner/work/spiral-2/spiral-2/packages/ui/dist/form.cjs","sourcesContent":[null,"import {\n SymbolInformationCircle,\n SymbolWrongCircle,\n} from \"@aviala-design/icons\";\nimport {\n forwardRef,\n useId,\n useMemo,\n type HTMLAttributes,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport {\n FormProvider,\n useController,\n type Control,\n type ControllerFieldState,\n type ControllerRenderProps,\n type FieldPath,\n type FieldValues,\n type UseControllerProps,\n type UseFormStateReturn,\n} from \"react-hook-form\";\nimport { cn } from \"../lib/utils\";\nimport { FormFieldControlContext } from \"./form-field-context\";\nimport { Typeface } from \"./typeface\";\nimport { Typography } from \"./typography\";\n\n/** Figma Components → System Composition → Form (527:57461 / 527:57641) */\nexport type FormFieldDirection = \"vertical\" | \"horizontal\";\n\nexport { useResolvedControlError } from \"./form-field-context\";\n\n/** Layout mode — the original props-driven wrapper */\nexport type FormFieldLayoutProps = HTMLAttributes<HTMLDivElement> & {\n label?: ReactNode;\n htmlFor?: string;\n description?: ReactNode;\n error?: ReactNode;\n info?: ReactNode;\n /** Optional Alert / Feedback under the control */\n alert?: ReactNode;\n required?: boolean;\n /** Figma `Direction` — vertical stacks label above control; horizontal places label beside */\n direction?: FormFieldDirection;\n children?: ReactNode;\n};\n\n/** Render arguments passed to `render` in react-hook-form mode */\nexport type FormFieldRenderProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = {\n field: ControllerRenderProps<TFieldValues, TName>;\n fieldState: ControllerFieldState;\n formState: UseFormStateReturn<TFieldValues>;\n /**\n * Auto-generated control id. The field label is already wired to it via\n * `htmlFor` — pass it to the underlying input (`<Input id={id} />`).\n */\n id: string;\n};\n\n/**\n * react-hook-form mode — bind the field to a form via `name` (+ optional\n * `control`; falls back to the surrounding `<Form>` context). Validation\n * errors from the resolver (zod etc.) are rendered automatically unless the\n * `error` prop overrides them. Nested controls inherit the invalid state via\n * context unless they set an explicit `error` prop.\n */\nexport type FormFieldControlledProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = Omit<FormFieldLayoutProps, \"children\" | \"htmlFor\"> &\n Pick<\n UseControllerProps<TFieldValues, TName>,\n \"rules\" | \"defaultValue\" | \"shouldUnregister\" | \"disabled\"\n > & {\n name: TName;\n control?: Control<TFieldValues>;\n render: (props: FormFieldRenderProps<TFieldValues, TName>) => ReactNode;\n /** Overrides the validation error message shown under the control */\n error?: ReactNode;\n };\n\nexport type FormFieldProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = FormFieldLayoutProps | FormFieldControlledProps<TFieldValues, TName>;\n\nconst FormFieldLayout = forwardRef<HTMLDivElement, FormFieldLayoutProps>(\n (\n {\n className,\n label,\n htmlFor,\n description,\n error,\n info,\n alert,\n required,\n direction = \"vertical\",\n children,\n ...props\n },\n ref\n ) => {\n const reactId = useId();\n const labelId = `${reactId}-label`;\n const hasLabel = label != null && label !== \"\";\n const invalid = Boolean(error);\n const controlContext = useMemo(() => ({ invalid }), [invalid]);\n const labelNode =\n hasLabel || description ? (\n htmlFor ? (\n <label\n htmlFor={htmlFor}\n className=\"aviala-form__label aviala-form__label--control\"\n >\n <Typeface\n id={labelId}\n content=\"textCaption\"\n primary={\n hasLabel ? (\n <>\n {label}\n {required ? (\n <span className=\"aviala-form__required\" aria-hidden>\n *\n </span>\n ) : null}\n </>\n ) : undefined\n }\n secondary={description}\n />\n </label>\n ) : (\n <Typeface\n id={labelId}\n className=\"aviala-form__label\"\n content=\"textCaption\"\n primary={\n hasLabel ? (\n <>\n {label}\n {required ? (\n <span className=\"aviala-form__required\" aria-hidden>\n *\n </span>\n ) : null}\n </>\n ) : undefined\n }\n secondary={description}\n />\n )\n ) : null;\n\n return (\n <div\n ref={ref}\n className={cn(\"aviala-form\", className)}\n data-direction={direction}\n aria-labelledby={hasLabel ? labelId : undefined}\n {...props}\n >\n {labelNode}\n <div className=\"aviala-form__content\">\n <FormFieldControlContext.Provider value={controlContext}>\n {children}\n </FormFieldControlContext.Provider>\n {error ? (\n <div\n className=\"aviala-form__message aviala-form__message--error\"\n role=\"alert\"\n >\n <SymbolWrongCircle\n className=\"aviala-form__message-icon\"\n width={14}\n height={14}\n aria-hidden\n />\n <Typography\n level=\"caption\"\n as=\"p\"\n className=\"aviala-form__message-text\"\n >\n {error}\n </Typography>\n </div>\n ) : null}\n {info ? (\n <div className=\"aviala-form__message aviala-form__message--info\">\n <SymbolInformationCircle\n className=\"aviala-form__message-icon\"\n width={14}\n height={14}\n aria-hidden\n />\n <Typography\n level=\"caption\"\n as=\"p\"\n className=\"aviala-form__message-text\"\n >\n {info}\n </Typography>\n </div>\n ) : null}\n {alert ? <div className=\"aviala-form__alert\">{alert}</div> : null}\n </div>\n </div>\n );\n }\n);\nFormFieldLayout.displayName = \"FormFieldLayout\";\n\nconst FormFieldControlled = forwardRef<\n HTMLDivElement,\n FormFieldControlledProps\n>(\n (\n {\n control,\n name,\n rules,\n defaultValue,\n shouldUnregister,\n disabled,\n render,\n error,\n ...layoutProps\n },\n ref\n ) => {\n const id = useId();\n const { field, fieldState, formState } = useController({\n name,\n control,\n rules,\n defaultValue,\n shouldUnregister,\n disabled,\n });\n const message =\n error !== undefined ? error : (fieldState.error?.message ?? null);\n\n return (\n <FormFieldLayout ref={ref} {...layoutProps} htmlFor={id} error={message}>\n {render({ field, fieldState, formState, id })}\n </FormFieldLayout>\n );\n }\n);\nFormFieldControlled.displayName = \"FormFieldControlled\";\n\nfunction isControlledProps(\n props: FormFieldProps\n): props is FormFieldControlledProps {\n return (\n \"render\" in props && typeof props.render === \"function\" && \"name\" in props\n );\n}\n\nconst FormFieldRoot = forwardRef<HTMLDivElement, FormFieldProps>(\n (props, ref) => {\n if (isControlledProps(props)) {\n return <FormFieldControlled ref={ref} {...props} />;\n }\n return <FormFieldLayout ref={ref} {...props} />;\n }\n);\n\n/**\n * FormField — layout wrapper around a form control, or a react-hook-form\n * bound field when given `name` + `render` (shadcn-style).\n *\n * When an error tip is shown, nested Spiral controls that support `error`\n * (Input, Textarea, SelectTrigger, …) inherit the invalid state unless they\n * set an explicit `error` prop.\n *\n * @example Layout mode\n * <FormField label=\"Email\" htmlFor=\"email\" error=\"Required\">\n * <Input id=\"email\" />\n * </FormField>\n *\n * @example react-hook-form + zod mode\n * <Form {...form}>\n * <form onSubmit={form.handleSubmit(onSubmit)}>\n * <FormField\n * control={form.control}\n * name=\"email\"\n * label=\"Email\"\n * render={({ field, id }) => (\n * <Input id={id} {...field} />\n * )}\n * />\n * </form>\n * </Form>\n */\nexport const FormField = FormFieldRoot as {\n <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n >(\n props: FormFieldProps<TFieldValues, TName> & { ref?: Ref<HTMLDivElement> }\n ): ReactElement | null;\n displayName?: string;\n};\nFormField.displayName = \"FormField\";\n\n/** Alias of react-hook-form's FormProvider — wrap your form with it (shadcn-style `<Form {...form}>`) */\nexport const Form = FormProvider;\n"]}
|
package/dist/form.d.cts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as react_hook_form from 'react-hook-form';
|
|
2
|
+
import { FieldValues, FieldPath, UseControllerProps, Control, ControllerRenderProps, ControllerFieldState, UseFormStateReturn } from 'react-hook-form';
|
|
3
|
+
import { HTMLAttributes, ReactNode, Ref, ReactElement } from 'react';
|
|
4
|
+
export { u as useResolvedControlError } from './form-field-context-XYh-G1KL.cjs';
|
|
5
|
+
|
|
6
|
+
/** Figma Components → System Composition → Form (527:57461 / 527:57641) */
|
|
7
|
+
type FormFieldDirection = "vertical" | "horizontal";
|
|
8
|
+
|
|
9
|
+
/** Layout mode — the original props-driven wrapper */
|
|
10
|
+
type FormFieldLayoutProps = HTMLAttributes<HTMLDivElement> & {
|
|
11
|
+
label?: ReactNode;
|
|
12
|
+
htmlFor?: string;
|
|
13
|
+
description?: ReactNode;
|
|
14
|
+
error?: ReactNode;
|
|
15
|
+
info?: ReactNode;
|
|
16
|
+
/** Optional Alert / Feedback under the control */
|
|
17
|
+
alert?: ReactNode;
|
|
18
|
+
required?: boolean;
|
|
19
|
+
/** Figma `Direction` — vertical stacks label above control; horizontal places label beside */
|
|
20
|
+
direction?: FormFieldDirection;
|
|
21
|
+
children?: ReactNode;
|
|
22
|
+
};
|
|
23
|
+
/** Render arguments passed to `render` in react-hook-form mode */
|
|
24
|
+
type FormFieldRenderProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = {
|
|
25
|
+
field: ControllerRenderProps<TFieldValues, TName>;
|
|
26
|
+
fieldState: ControllerFieldState;
|
|
27
|
+
formState: UseFormStateReturn<TFieldValues>;
|
|
28
|
+
/**
|
|
29
|
+
* Auto-generated control id. The field label is already wired to it via
|
|
30
|
+
* `htmlFor` — pass it to the underlying input (`<Input id={id} />`).
|
|
31
|
+
*/
|
|
32
|
+
id: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* react-hook-form mode — bind the field to a form via `name` (+ optional
|
|
36
|
+
* `control`; falls back to the surrounding `<Form>` context). Validation
|
|
37
|
+
* errors from the resolver (zod etc.) are rendered automatically unless the
|
|
38
|
+
* `error` prop overrides them. Nested controls inherit the invalid state via
|
|
39
|
+
* context unless they set an explicit `error` prop.
|
|
40
|
+
*/
|
|
41
|
+
type FormFieldControlledProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = Omit<FormFieldLayoutProps, "children" | "htmlFor"> & Pick<UseControllerProps<TFieldValues, TName>, "rules" | "defaultValue" | "shouldUnregister" | "disabled"> & {
|
|
42
|
+
name: TName;
|
|
43
|
+
control?: Control<TFieldValues>;
|
|
44
|
+
render: (props: FormFieldRenderProps<TFieldValues, TName>) => ReactNode;
|
|
45
|
+
/** Overrides the validation error message shown under the control */
|
|
46
|
+
error?: ReactNode;
|
|
47
|
+
};
|
|
48
|
+
type FormFieldProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = FormFieldLayoutProps | FormFieldControlledProps<TFieldValues, TName>;
|
|
49
|
+
/**
|
|
50
|
+
* FormField — layout wrapper around a form control, or a react-hook-form
|
|
51
|
+
* bound field when given `name` + `render` (shadcn-style).
|
|
52
|
+
*
|
|
53
|
+
* When an error tip is shown, nested Spiral controls that support `error`
|
|
54
|
+
* (Input, Textarea, SelectTrigger, …) inherit the invalid state unless they
|
|
55
|
+
* set an explicit `error` prop.
|
|
56
|
+
*
|
|
57
|
+
* @example Layout mode
|
|
58
|
+
* <FormField label="Email" htmlFor="email" error="Required">
|
|
59
|
+
* <Input id="email" />
|
|
60
|
+
* </FormField>
|
|
61
|
+
*
|
|
62
|
+
* @example react-hook-form + zod mode
|
|
63
|
+
* <Form {...form}>
|
|
64
|
+
* <form onSubmit={form.handleSubmit(onSubmit)}>
|
|
65
|
+
* <FormField
|
|
66
|
+
* control={form.control}
|
|
67
|
+
* name="email"
|
|
68
|
+
* label="Email"
|
|
69
|
+
* render={({ field, id }) => (
|
|
70
|
+
* <Input id={id} {...field} />
|
|
71
|
+
* )}
|
|
72
|
+
* />
|
|
73
|
+
* </form>
|
|
74
|
+
* </Form>
|
|
75
|
+
*/
|
|
76
|
+
declare const FormField: {
|
|
77
|
+
<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>(props: FormFieldProps<TFieldValues, TName> & {
|
|
78
|
+
ref?: Ref<HTMLDivElement>;
|
|
79
|
+
}): ReactElement | null;
|
|
80
|
+
displayName?: string;
|
|
81
|
+
};
|
|
82
|
+
/** Alias of react-hook-form's FormProvider — wrap your form with it (shadcn-style `<Form {...form}>`) */
|
|
83
|
+
declare const Form: <TFieldValues extends FieldValues, TContext = any, TTransformedValues = TFieldValues>({ children, watch, getValues, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe, }: react_hook_form.FormProviderProps<TFieldValues, TContext, TTransformedValues>) => React.JSX.Element;
|
|
84
|
+
|
|
85
|
+
export { Form, FormField, type FormFieldControlledProps, type FormFieldDirection, type FormFieldLayoutProps, type FormFieldProps, type FormFieldRenderProps };
|
package/dist/form.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as react_hook_form from 'react-hook-form';
|
|
2
|
+
import { FieldValues, FieldPath, UseControllerProps, Control, ControllerRenderProps, ControllerFieldState, UseFormStateReturn } from 'react-hook-form';
|
|
3
|
+
import { HTMLAttributes, ReactNode, Ref, ReactElement } from 'react';
|
|
4
|
+
export { u as useResolvedControlError } from './form-field-context-XYh-G1KL.js';
|
|
5
|
+
|
|
6
|
+
/** Figma Components → System Composition → Form (527:57461 / 527:57641) */
|
|
7
|
+
type FormFieldDirection = "vertical" | "horizontal";
|
|
8
|
+
|
|
9
|
+
/** Layout mode — the original props-driven wrapper */
|
|
10
|
+
type FormFieldLayoutProps = HTMLAttributes<HTMLDivElement> & {
|
|
11
|
+
label?: ReactNode;
|
|
12
|
+
htmlFor?: string;
|
|
13
|
+
description?: ReactNode;
|
|
14
|
+
error?: ReactNode;
|
|
15
|
+
info?: ReactNode;
|
|
16
|
+
/** Optional Alert / Feedback under the control */
|
|
17
|
+
alert?: ReactNode;
|
|
18
|
+
required?: boolean;
|
|
19
|
+
/** Figma `Direction` — vertical stacks label above control; horizontal places label beside */
|
|
20
|
+
direction?: FormFieldDirection;
|
|
21
|
+
children?: ReactNode;
|
|
22
|
+
};
|
|
23
|
+
/** Render arguments passed to `render` in react-hook-form mode */
|
|
24
|
+
type FormFieldRenderProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = {
|
|
25
|
+
field: ControllerRenderProps<TFieldValues, TName>;
|
|
26
|
+
fieldState: ControllerFieldState;
|
|
27
|
+
formState: UseFormStateReturn<TFieldValues>;
|
|
28
|
+
/**
|
|
29
|
+
* Auto-generated control id. The field label is already wired to it via
|
|
30
|
+
* `htmlFor` — pass it to the underlying input (`<Input id={id} />`).
|
|
31
|
+
*/
|
|
32
|
+
id: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* react-hook-form mode — bind the field to a form via `name` (+ optional
|
|
36
|
+
* `control`; falls back to the surrounding `<Form>` context). Validation
|
|
37
|
+
* errors from the resolver (zod etc.) are rendered automatically unless the
|
|
38
|
+
* `error` prop overrides them. Nested controls inherit the invalid state via
|
|
39
|
+
* context unless they set an explicit `error` prop.
|
|
40
|
+
*/
|
|
41
|
+
type FormFieldControlledProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = Omit<FormFieldLayoutProps, "children" | "htmlFor"> & Pick<UseControllerProps<TFieldValues, TName>, "rules" | "defaultValue" | "shouldUnregister" | "disabled"> & {
|
|
42
|
+
name: TName;
|
|
43
|
+
control?: Control<TFieldValues>;
|
|
44
|
+
render: (props: FormFieldRenderProps<TFieldValues, TName>) => ReactNode;
|
|
45
|
+
/** Overrides the validation error message shown under the control */
|
|
46
|
+
error?: ReactNode;
|
|
47
|
+
};
|
|
48
|
+
type FormFieldProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> = FormFieldLayoutProps | FormFieldControlledProps<TFieldValues, TName>;
|
|
49
|
+
/**
|
|
50
|
+
* FormField — layout wrapper around a form control, or a react-hook-form
|
|
51
|
+
* bound field when given `name` + `render` (shadcn-style).
|
|
52
|
+
*
|
|
53
|
+
* When an error tip is shown, nested Spiral controls that support `error`
|
|
54
|
+
* (Input, Textarea, SelectTrigger, …) inherit the invalid state unless they
|
|
55
|
+
* set an explicit `error` prop.
|
|
56
|
+
*
|
|
57
|
+
* @example Layout mode
|
|
58
|
+
* <FormField label="Email" htmlFor="email" error="Required">
|
|
59
|
+
* <Input id="email" />
|
|
60
|
+
* </FormField>
|
|
61
|
+
*
|
|
62
|
+
* @example react-hook-form + zod mode
|
|
63
|
+
* <Form {...form}>
|
|
64
|
+
* <form onSubmit={form.handleSubmit(onSubmit)}>
|
|
65
|
+
* <FormField
|
|
66
|
+
* control={form.control}
|
|
67
|
+
* name="email"
|
|
68
|
+
* label="Email"
|
|
69
|
+
* render={({ field, id }) => (
|
|
70
|
+
* <Input id={id} {...field} />
|
|
71
|
+
* )}
|
|
72
|
+
* />
|
|
73
|
+
* </form>
|
|
74
|
+
* </Form>
|
|
75
|
+
*/
|
|
76
|
+
declare const FormField: {
|
|
77
|
+
<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>(props: FormFieldProps<TFieldValues, TName> & {
|
|
78
|
+
ref?: Ref<HTMLDivElement>;
|
|
79
|
+
}): ReactElement | null;
|
|
80
|
+
displayName?: string;
|
|
81
|
+
};
|
|
82
|
+
/** Alias of react-hook-form's FormProvider — wrap your form with it (shadcn-style `<Form {...form}>`) */
|
|
83
|
+
declare const Form: <TFieldValues extends FieldValues, TContext = any, TTransformedValues = TFieldValues>({ children, watch, getValues, getFieldState, setError, clearErrors, setValue, setValues, trigger, formState, resetField, reset, resetDefaultValues, handleSubmit, unregister, control, register, setFocus, subscribe, }: react_hook_form.FormProviderProps<TFieldValues, TContext, TTransformedValues>) => React.JSX.Element;
|
|
84
|
+
|
|
85
|
+
export { Form, FormField, type FormFieldControlledProps, type FormFieldDirection, type FormFieldLayoutProps, type FormFieldProps, type FormFieldRenderProps };
|
package/dist/form.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
FormFieldControlContext,
|
|
4
|
+
Typeface,
|
|
5
|
+
Typography,
|
|
6
|
+
cn,
|
|
7
|
+
useResolvedControlError
|
|
8
|
+
} from "./chunk-XVABQUTX.js";
|
|
9
|
+
|
|
10
|
+
// src/components/form-field.tsx
|
|
11
|
+
import {
|
|
12
|
+
SymbolInformationCircle,
|
|
13
|
+
SymbolWrongCircle
|
|
14
|
+
} from "@aviala-design/icons";
|
|
15
|
+
import {
|
|
16
|
+
forwardRef,
|
|
17
|
+
useId,
|
|
18
|
+
useMemo
|
|
19
|
+
} from "react";
|
|
20
|
+
import {
|
|
21
|
+
FormProvider,
|
|
22
|
+
useController
|
|
23
|
+
} from "react-hook-form";
|
|
24
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
25
|
+
var FormFieldLayout = forwardRef(
|
|
26
|
+
({
|
|
27
|
+
className,
|
|
28
|
+
label,
|
|
29
|
+
htmlFor,
|
|
30
|
+
description,
|
|
31
|
+
error,
|
|
32
|
+
info,
|
|
33
|
+
alert,
|
|
34
|
+
required,
|
|
35
|
+
direction = "vertical",
|
|
36
|
+
children,
|
|
37
|
+
...props
|
|
38
|
+
}, ref) => {
|
|
39
|
+
const reactId = useId();
|
|
40
|
+
const labelId = `${reactId}-label`;
|
|
41
|
+
const hasLabel = label != null && label !== "";
|
|
42
|
+
const invalid = Boolean(error);
|
|
43
|
+
const controlContext = useMemo(() => ({ invalid }), [invalid]);
|
|
44
|
+
const labelNode = hasLabel || description ? htmlFor ? /* @__PURE__ */ jsx(
|
|
45
|
+
"label",
|
|
46
|
+
{
|
|
47
|
+
htmlFor,
|
|
48
|
+
className: "aviala-form__label aviala-form__label--control",
|
|
49
|
+
children: /* @__PURE__ */ jsx(
|
|
50
|
+
Typeface,
|
|
51
|
+
{
|
|
52
|
+
id: labelId,
|
|
53
|
+
content: "textCaption",
|
|
54
|
+
primary: hasLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
55
|
+
label,
|
|
56
|
+
required ? /* @__PURE__ */ jsx("span", { className: "aviala-form__required", "aria-hidden": true, children: "*" }) : null
|
|
57
|
+
] }) : void 0,
|
|
58
|
+
secondary: description
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
) : /* @__PURE__ */ jsx(
|
|
63
|
+
Typeface,
|
|
64
|
+
{
|
|
65
|
+
id: labelId,
|
|
66
|
+
className: "aviala-form__label",
|
|
67
|
+
content: "textCaption",
|
|
68
|
+
primary: hasLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
69
|
+
label,
|
|
70
|
+
required ? /* @__PURE__ */ jsx("span", { className: "aviala-form__required", "aria-hidden": true, children: "*" }) : null
|
|
71
|
+
] }) : void 0,
|
|
72
|
+
secondary: description
|
|
73
|
+
}
|
|
74
|
+
) : null;
|
|
75
|
+
return /* @__PURE__ */ jsxs(
|
|
76
|
+
"div",
|
|
77
|
+
{
|
|
78
|
+
ref,
|
|
79
|
+
className: cn("aviala-form", className),
|
|
80
|
+
"data-direction": direction,
|
|
81
|
+
"aria-labelledby": hasLabel ? labelId : void 0,
|
|
82
|
+
...props,
|
|
83
|
+
children: [
|
|
84
|
+
labelNode,
|
|
85
|
+
/* @__PURE__ */ jsxs("div", { className: "aviala-form__content", children: [
|
|
86
|
+
/* @__PURE__ */ jsx(FormFieldControlContext.Provider, { value: controlContext, children }),
|
|
87
|
+
error ? /* @__PURE__ */ jsxs(
|
|
88
|
+
"div",
|
|
89
|
+
{
|
|
90
|
+
className: "aviala-form__message aviala-form__message--error",
|
|
91
|
+
role: "alert",
|
|
92
|
+
children: [
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
|
+
SymbolWrongCircle,
|
|
95
|
+
{
|
|
96
|
+
className: "aviala-form__message-icon",
|
|
97
|
+
width: 14,
|
|
98
|
+
height: 14,
|
|
99
|
+
"aria-hidden": true
|
|
100
|
+
}
|
|
101
|
+
),
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
Typography,
|
|
104
|
+
{
|
|
105
|
+
level: "caption",
|
|
106
|
+
as: "p",
|
|
107
|
+
className: "aviala-form__message-text",
|
|
108
|
+
children: error
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
) : null,
|
|
114
|
+
info ? /* @__PURE__ */ jsxs("div", { className: "aviala-form__message aviala-form__message--info", children: [
|
|
115
|
+
/* @__PURE__ */ jsx(
|
|
116
|
+
SymbolInformationCircle,
|
|
117
|
+
{
|
|
118
|
+
className: "aviala-form__message-icon",
|
|
119
|
+
width: 14,
|
|
120
|
+
height: 14,
|
|
121
|
+
"aria-hidden": true
|
|
122
|
+
}
|
|
123
|
+
),
|
|
124
|
+
/* @__PURE__ */ jsx(
|
|
125
|
+
Typography,
|
|
126
|
+
{
|
|
127
|
+
level: "caption",
|
|
128
|
+
as: "p",
|
|
129
|
+
className: "aviala-form__message-text",
|
|
130
|
+
children: info
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
] }) : null,
|
|
134
|
+
alert ? /* @__PURE__ */ jsx("div", { className: "aviala-form__alert", children: alert }) : null
|
|
135
|
+
] })
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
FormFieldLayout.displayName = "FormFieldLayout";
|
|
142
|
+
var FormFieldControlled = forwardRef(
|
|
143
|
+
({
|
|
144
|
+
control,
|
|
145
|
+
name,
|
|
146
|
+
rules,
|
|
147
|
+
defaultValue,
|
|
148
|
+
shouldUnregister,
|
|
149
|
+
disabled,
|
|
150
|
+
render,
|
|
151
|
+
error,
|
|
152
|
+
...layoutProps
|
|
153
|
+
}, ref) => {
|
|
154
|
+
const id = useId();
|
|
155
|
+
const { field, fieldState, formState } = useController({
|
|
156
|
+
name,
|
|
157
|
+
control,
|
|
158
|
+
rules,
|
|
159
|
+
defaultValue,
|
|
160
|
+
shouldUnregister,
|
|
161
|
+
disabled
|
|
162
|
+
});
|
|
163
|
+
const message = error !== void 0 ? error : fieldState.error?.message ?? null;
|
|
164
|
+
return /* @__PURE__ */ jsx(FormFieldLayout, { ref, ...layoutProps, htmlFor: id, error: message, children: render({ field, fieldState, formState, id }) });
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
FormFieldControlled.displayName = "FormFieldControlled";
|
|
168
|
+
function isControlledProps(props) {
|
|
169
|
+
return "render" in props && typeof props.render === "function" && "name" in props;
|
|
170
|
+
}
|
|
171
|
+
var FormFieldRoot = forwardRef(
|
|
172
|
+
(props, ref) => {
|
|
173
|
+
if (isControlledProps(props)) {
|
|
174
|
+
return /* @__PURE__ */ jsx(FormFieldControlled, { ref, ...props });
|
|
175
|
+
}
|
|
176
|
+
return /* @__PURE__ */ jsx(FormFieldLayout, { ref, ...props });
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
var FormField = FormFieldRoot;
|
|
180
|
+
FormField.displayName = "FormField";
|
|
181
|
+
var Form = FormProvider;
|
|
182
|
+
export {
|
|
183
|
+
Form,
|
|
184
|
+
FormField,
|
|
185
|
+
useResolvedControlError
|
|
186
|
+
};
|
|
187
|
+
//# sourceMappingURL=form.js.map
|
package/dist/form.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/form-field.tsx"],"sourcesContent":["import {\n SymbolInformationCircle,\n SymbolWrongCircle,\n} from \"@aviala-design/icons\";\nimport {\n forwardRef,\n useId,\n useMemo,\n type HTMLAttributes,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport {\n FormProvider,\n useController,\n type Control,\n type ControllerFieldState,\n type ControllerRenderProps,\n type FieldPath,\n type FieldValues,\n type UseControllerProps,\n type UseFormStateReturn,\n} from \"react-hook-form\";\nimport { cn } from \"../lib/utils\";\nimport { FormFieldControlContext } from \"./form-field-context\";\nimport { Typeface } from \"./typeface\";\nimport { Typography } from \"./typography\";\n\n/** Figma Components → System Composition → Form (527:57461 / 527:57641) */\nexport type FormFieldDirection = \"vertical\" | \"horizontal\";\n\nexport { useResolvedControlError } from \"./form-field-context\";\n\n/** Layout mode — the original props-driven wrapper */\nexport type FormFieldLayoutProps = HTMLAttributes<HTMLDivElement> & {\n label?: ReactNode;\n htmlFor?: string;\n description?: ReactNode;\n error?: ReactNode;\n info?: ReactNode;\n /** Optional Alert / Feedback under the control */\n alert?: ReactNode;\n required?: boolean;\n /** Figma `Direction` — vertical stacks label above control; horizontal places label beside */\n direction?: FormFieldDirection;\n children?: ReactNode;\n};\n\n/** Render arguments passed to `render` in react-hook-form mode */\nexport type FormFieldRenderProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = {\n field: ControllerRenderProps<TFieldValues, TName>;\n fieldState: ControllerFieldState;\n formState: UseFormStateReturn<TFieldValues>;\n /**\n * Auto-generated control id. The field label is already wired to it via\n * `htmlFor` — pass it to the underlying input (`<Input id={id} />`).\n */\n id: string;\n};\n\n/**\n * react-hook-form mode — bind the field to a form via `name` (+ optional\n * `control`; falls back to the surrounding `<Form>` context). Validation\n * errors from the resolver (zod etc.) are rendered automatically unless the\n * `error` prop overrides them. Nested controls inherit the invalid state via\n * context unless they set an explicit `error` prop.\n */\nexport type FormFieldControlledProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = Omit<FormFieldLayoutProps, \"children\" | \"htmlFor\"> &\n Pick<\n UseControllerProps<TFieldValues, TName>,\n \"rules\" | \"defaultValue\" | \"shouldUnregister\" | \"disabled\"\n > & {\n name: TName;\n control?: Control<TFieldValues>;\n render: (props: FormFieldRenderProps<TFieldValues, TName>) => ReactNode;\n /** Overrides the validation error message shown under the control */\n error?: ReactNode;\n };\n\nexport type FormFieldProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = FormFieldLayoutProps | FormFieldControlledProps<TFieldValues, TName>;\n\nconst FormFieldLayout = forwardRef<HTMLDivElement, FormFieldLayoutProps>(\n (\n {\n className,\n label,\n htmlFor,\n description,\n error,\n info,\n alert,\n required,\n direction = \"vertical\",\n children,\n ...props\n },\n ref\n ) => {\n const reactId = useId();\n const labelId = `${reactId}-label`;\n const hasLabel = label != null && label !== \"\";\n const invalid = Boolean(error);\n const controlContext = useMemo(() => ({ invalid }), [invalid]);\n const labelNode =\n hasLabel || description ? (\n htmlFor ? (\n <label\n htmlFor={htmlFor}\n className=\"aviala-form__label aviala-form__label--control\"\n >\n <Typeface\n id={labelId}\n content=\"textCaption\"\n primary={\n hasLabel ? (\n <>\n {label}\n {required ? (\n <span className=\"aviala-form__required\" aria-hidden>\n *\n </span>\n ) : null}\n </>\n ) : undefined\n }\n secondary={description}\n />\n </label>\n ) : (\n <Typeface\n id={labelId}\n className=\"aviala-form__label\"\n content=\"textCaption\"\n primary={\n hasLabel ? (\n <>\n {label}\n {required ? (\n <span className=\"aviala-form__required\" aria-hidden>\n *\n </span>\n ) : null}\n </>\n ) : undefined\n }\n secondary={description}\n />\n )\n ) : null;\n\n return (\n <div\n ref={ref}\n className={cn(\"aviala-form\", className)}\n data-direction={direction}\n aria-labelledby={hasLabel ? labelId : undefined}\n {...props}\n >\n {labelNode}\n <div className=\"aviala-form__content\">\n <FormFieldControlContext.Provider value={controlContext}>\n {children}\n </FormFieldControlContext.Provider>\n {error ? (\n <div\n className=\"aviala-form__message aviala-form__message--error\"\n role=\"alert\"\n >\n <SymbolWrongCircle\n className=\"aviala-form__message-icon\"\n width={14}\n height={14}\n aria-hidden\n />\n <Typography\n level=\"caption\"\n as=\"p\"\n className=\"aviala-form__message-text\"\n >\n {error}\n </Typography>\n </div>\n ) : null}\n {info ? (\n <div className=\"aviala-form__message aviala-form__message--info\">\n <SymbolInformationCircle\n className=\"aviala-form__message-icon\"\n width={14}\n height={14}\n aria-hidden\n />\n <Typography\n level=\"caption\"\n as=\"p\"\n className=\"aviala-form__message-text\"\n >\n {info}\n </Typography>\n </div>\n ) : null}\n {alert ? <div className=\"aviala-form__alert\">{alert}</div> : null}\n </div>\n </div>\n );\n }\n);\nFormFieldLayout.displayName = \"FormFieldLayout\";\n\nconst FormFieldControlled = forwardRef<\n HTMLDivElement,\n FormFieldControlledProps\n>(\n (\n {\n control,\n name,\n rules,\n defaultValue,\n shouldUnregister,\n disabled,\n render,\n error,\n ...layoutProps\n },\n ref\n ) => {\n const id = useId();\n const { field, fieldState, formState } = useController({\n name,\n control,\n rules,\n defaultValue,\n shouldUnregister,\n disabled,\n });\n const message =\n error !== undefined ? error : (fieldState.error?.message ?? null);\n\n return (\n <FormFieldLayout ref={ref} {...layoutProps} htmlFor={id} error={message}>\n {render({ field, fieldState, formState, id })}\n </FormFieldLayout>\n );\n }\n);\nFormFieldControlled.displayName = \"FormFieldControlled\";\n\nfunction isControlledProps(\n props: FormFieldProps\n): props is FormFieldControlledProps {\n return (\n \"render\" in props && typeof props.render === \"function\" && \"name\" in props\n );\n}\n\nconst FormFieldRoot = forwardRef<HTMLDivElement, FormFieldProps>(\n (props, ref) => {\n if (isControlledProps(props)) {\n return <FormFieldControlled ref={ref} {...props} />;\n }\n return <FormFieldLayout ref={ref} {...props} />;\n }\n);\n\n/**\n * FormField — layout wrapper around a form control, or a react-hook-form\n * bound field when given `name` + `render` (shadcn-style).\n *\n * When an error tip is shown, nested Spiral controls that support `error`\n * (Input, Textarea, SelectTrigger, …) inherit the invalid state unless they\n * set an explicit `error` prop.\n *\n * @example Layout mode\n * <FormField label=\"Email\" htmlFor=\"email\" error=\"Required\">\n * <Input id=\"email\" />\n * </FormField>\n *\n * @example react-hook-form + zod mode\n * <Form {...form}>\n * <form onSubmit={form.handleSubmit(onSubmit)}>\n * <FormField\n * control={form.control}\n * name=\"email\"\n * label=\"Email\"\n * render={({ field, id }) => (\n * <Input id={id} {...field} />\n * )}\n * />\n * </form>\n * </Form>\n */\nexport const FormField = FormFieldRoot as {\n <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n >(\n props: FormFieldProps<TFieldValues, TName> & { ref?: Ref<HTMLDivElement> }\n ): ReactElement | null;\n displayName?: string;\n};\nFormField.displayName = \"FormField\";\n\n/** Alias of react-hook-form's FormProvider — wrap your form with it (shadcn-style `<Form {...form}>`) */\nexport const Form = FormProvider;\n"],"mappings":"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","names":[]}
|