@formadapter/html 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ludicrous
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # `@formadapter/html`
2
+
3
+ The accessible, unstyled native HTML adapter for FormAdapter. Use it as a
4
+ ready-to-render default or as the foundation for your own design system.
5
+
6
+ ```sh
7
+ bun add @formadapter/react @formadapter/html zod
8
+ ```
9
+
10
+ ## Set the adapter once
11
+
12
+ ```tsx
13
+ "use client";
14
+
15
+ import { HTMLProvider } from "@formadapter/html";
16
+
17
+ export function Providers({ children }: { children: React.ReactNode }) {
18
+ return <HTMLProvider>{children}</HTMLProvider>;
19
+ }
20
+ ```
21
+
22
+ Forms created with `createForm` from `@formadapter/react` use the nearest
23
+ provider. A nested provider replaces its parent adapter for that subtree.
24
+
25
+ For an HTML fallback that works without a provider, import the adapter-bound
26
+ factory instead. A nearest provider still overrides that fallback:
27
+
28
+ ```tsx
29
+ import { createForm } from "@formadapter/html";
30
+ import { z } from "zod";
31
+
32
+ const Contact = createForm(z.object({
33
+ email: z.email(),
34
+ message: z.string(),
35
+ })).configure({
36
+ fields: { message: { control: "textarea" } },
37
+ });
38
+
39
+ export function ContactForm() {
40
+ return <Contact.Form onSubmit={(values) => console.log(values)} />;
41
+ }
42
+ ```
43
+
44
+ The adapter covers every built-in control and every visible slot: nested
45
+ groups, arrays, error summaries, form messages, unsupported schema nodes, and
46
+ wizards. It emits semantic HTML and accessibility attributes without classes
47
+ or layout styles. Classes, styles, and safe native attributes configured on
48
+ fields pass through untouched.
49
+
50
+ ## Build your design system
51
+
52
+ Extend the complete adapter and replace only the pieces your system owns:
53
+
54
+ ```tsx
55
+ import { htmlAdapter } from "@formadapter/html";
56
+
57
+ export const productAdapter = htmlAdapter.extend({
58
+ name: "Product UI",
59
+ slots: { Button: ProductButton },
60
+ controls: { custom: { rating: RatingControl } },
61
+ });
62
+ ```
63
+
64
+ `extend` returns a new complete adapter and never mutates `htmlAdapter`.
65
+ Individual controls and slots are exported for direct reuse.
66
+
67
+ The framework-free control normalization helpers used by native adapters are
68
+ available from `@formadapter/html/native`. This keeps the root component API
69
+ small while giving custom native renderers one tested implementation for input
70
+ types, values, safe `controlProps`, and typed option serialization.
@@ -0,0 +1,210 @@
1
+ 'use client';
2
+ import { ArrayItemSlotProps, ArraySlotProps, ButtonSlotProps, ControlProps, CreateForm, ErrorSummarySlotProps, FieldSlotProps, FormAdapter, FormMessageSlotProps, FormSlotProps, GroupSlotProps, UnsupportedSlotProps, WizardSlotProps } from "@formadapter/react";
3
+ import { ReactNode } from "react";
4
+
5
+ //#region src/adapter.d.ts
6
+ declare const htmlAdapter: FormAdapter<Record<never, never>>;
7
+ //#endregion
8
+ //#region src/provider.d.ts
9
+ interface HTMLProviderProps {
10
+ readonly children: ReactNode;
11
+ }
12
+ /** Client boundary that keeps the function-rich adapter out of server props. */
13
+ declare function HTMLProvider({
14
+ children
15
+ }: HTMLProviderProps): ReactNode;
16
+ //#endregion
17
+ //#region src/controls/checkbox.d.ts
18
+ declare function Checkbox({
19
+ controlRef,
20
+ disabled,
21
+ field,
22
+ id,
23
+ inputProps,
24
+ name,
25
+ onBlur,
26
+ onValueChange,
27
+ readOnly,
28
+ required,
29
+ value
30
+ }: ControlProps): ReactNode;
31
+ //#endregion
32
+ //#region src/controls/file.d.ts
33
+ declare function File({
34
+ controlRef,
35
+ disabled,
36
+ field,
37
+ id,
38
+ inputProps,
39
+ name,
40
+ onBlur,
41
+ onValueChange,
42
+ readOnly,
43
+ required,
44
+ value
45
+ }: ControlProps): ReactNode;
46
+ //#endregion
47
+ //#region src/controls/input.d.ts
48
+ declare function Input({
49
+ controlRef,
50
+ disabled,
51
+ field,
52
+ id,
53
+ inputProps,
54
+ name,
55
+ onBlur,
56
+ onValueChange,
57
+ readOnly,
58
+ required,
59
+ value
60
+ }: ControlProps): ReactNode;
61
+ //#endregion
62
+ //#region src/controls/radio.d.ts
63
+ declare function Radio({
64
+ controlRef,
65
+ disabled,
66
+ field,
67
+ id,
68
+ inputProps,
69
+ name,
70
+ onBlur,
71
+ onValueChange,
72
+ readOnly,
73
+ required,
74
+ value
75
+ }: ControlProps): ReactNode;
76
+ //#endregion
77
+ //#region src/controls/select.d.ts
78
+ declare function Select({
79
+ controlRef,
80
+ disabled,
81
+ field,
82
+ id,
83
+ inputProps,
84
+ name,
85
+ onBlur,
86
+ onValueChange,
87
+ readOnly,
88
+ required,
89
+ value
90
+ }: ControlProps): ReactNode;
91
+ //#endregion
92
+ //#region src/controls/textarea.d.ts
93
+ declare function Textarea({
94
+ controlRef,
95
+ disabled,
96
+ field,
97
+ id,
98
+ inputProps,
99
+ name,
100
+ onBlur,
101
+ onValueChange,
102
+ readOnly,
103
+ required,
104
+ value
105
+ }: ControlProps): ReactNode;
106
+ //#endregion
107
+ //#region src/slots/array.d.ts
108
+ declare function Array({
109
+ actions,
110
+ children,
111
+ disabled,
112
+ error,
113
+ errorId,
114
+ field,
115
+ itemCount,
116
+ readOnly,
117
+ required,
118
+ ...props
119
+ }: ArraySlotProps): ReactNode;
120
+ //#endregion
121
+ //#region src/slots/array-item.d.ts
122
+ declare function ArrayItem({
123
+ actions,
124
+ children,
125
+ field,
126
+ index,
127
+ label,
128
+ ...props
129
+ }: ArrayItemSlotProps): ReactNode;
130
+ //#endregion
131
+ //#region src/slots/button.d.ts
132
+ declare function Button({
133
+ ariaLabel,
134
+ children,
135
+ disabled,
136
+ intent,
137
+ onClick,
138
+ pending,
139
+ type
140
+ }: ButtonSlotProps): ReactNode;
141
+ //#endregion
142
+ //#region src/slots/error-summary.d.ts
143
+ declare function ErrorSummary({
144
+ errors,
145
+ items,
146
+ onSelect,
147
+ title
148
+ }: ErrorSummarySlotProps): ReactNode;
149
+ //#endregion
150
+ //#region src/slots/field.d.ts
151
+ declare function Field({
152
+ children,
153
+ controlId,
154
+ descriptionId,
155
+ error,
156
+ errorId,
157
+ field,
158
+ invalid,
159
+ required,
160
+ validating,
161
+ ...props
162
+ }: FieldSlotProps): ReactNode;
163
+ //#endregion
164
+ //#region src/slots/form.d.ts
165
+ declare function Form({
166
+ children,
167
+ ...props
168
+ }: FormSlotProps): ReactNode;
169
+ //#endregion
170
+ //#region src/slots/form-message.d.ts
171
+ declare function FormMessage({
172
+ kind,
173
+ message
174
+ }: FormMessageSlotProps): ReactNode;
175
+ //#endregion
176
+ //#region src/slots/group.d.ts
177
+ declare function Group({
178
+ children,
179
+ disabled,
180
+ error,
181
+ errorId,
182
+ field,
183
+ readOnly,
184
+ required,
185
+ ...props
186
+ }: GroupSlotProps): ReactNode;
187
+ //#endregion
188
+ //#region src/slots/unsupported.d.ts
189
+ declare function Unsupported({
190
+ field,
191
+ reason
192
+ }: UnsupportedSlotProps): ReactNode;
193
+ //#endregion
194
+ //#region src/slots/wizard.d.ts
195
+ declare function Wizard({
196
+ children,
197
+ currentStep,
198
+ description,
199
+ navigation,
200
+ steps,
201
+ title,
202
+ totalSteps,
203
+ ...props
204
+ }: WizardSlotProps): ReactNode;
205
+ //#endregion
206
+ //#region src/index.d.ts
207
+ declare const createForm: CreateForm<typeof htmlAdapter>;
208
+ //#endregion
209
+ export { Array, ArrayItem, Button, Checkbox, ErrorSummary, Field, File, Form, FormMessage, Group, HTMLProvider, type HTMLProviderProps, Input, Radio, Select, Textarea, Unsupported, Wizard, createForm, htmlAdapter };
210
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,597 @@
1
+ 'use client';
2
+ import { FormAdapterProvider, createAdapter, createFormFactory } from "@formadapter/react";
3
+ import { optionForSerializedValue, serializeOptionValue } from "@formadapter/core";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { useCallback, useEffect, useId, useRef } from "react";
6
+ //#region src/controls/shared.ts
7
+ const RESERVED_CONTROL_PROPS = new Set([
8
+ "aria-describedby",
9
+ "aria-invalid",
10
+ "aria-label",
11
+ "checked",
12
+ "children",
13
+ "className",
14
+ "constructor",
15
+ "controlRef",
16
+ "dangerouslySetInnerHTML",
17
+ "defaultChecked",
18
+ "defaultValue",
19
+ "disabled",
20
+ "id",
21
+ "key",
22
+ "multiple",
23
+ "name",
24
+ "onBlur",
25
+ "onChange",
26
+ "onInput",
27
+ "__proto__",
28
+ "prototype",
29
+ "readOnly",
30
+ "ref",
31
+ "required",
32
+ "style",
33
+ "type",
34
+ "value"
35
+ ]);
36
+ function isRecord(value) {
37
+ return typeof value === "object" && value !== null && !Array.isArray(value);
38
+ }
39
+ /** Keeps runtime-owned form props authoritative while allowing native options. */
40
+ function nativeControlProps(field) {
41
+ const configured = field.config.controlProps;
42
+ if (!isRecord(configured)) return { props: {} };
43
+ const props = {};
44
+ for (const [key, value] of Object.entries(configured)) if (!RESERVED_CONTROL_PROPS.has(key) && value !== void 0) props[key] = value;
45
+ return {
46
+ ...typeof configured.className === "string" ? { className: configured.className } : {},
47
+ props,
48
+ ...isRecord(configured.style) ? { style: configured.style } : {}
49
+ };
50
+ }
51
+ function inputType(field) {
52
+ if (field.inputType) return field.inputType;
53
+ switch (field.control) {
54
+ case "date":
55
+ case "datetime-local":
56
+ case "email":
57
+ case "hidden":
58
+ case "number":
59
+ case "password":
60
+ case "range":
61
+ case "search":
62
+ case "tel":
63
+ case "text":
64
+ case "time":
65
+ case "url": return field.control;
66
+ }
67
+ if (field.dataType === "number" || field.dataType === "integer") return "number";
68
+ switch (field.constraints.format) {
69
+ case "date": return "date";
70
+ case "date-time": return "text";
71
+ case "email": return "email";
72
+ case "password": return "password";
73
+ case "tel": return "tel";
74
+ case "time": return "time";
75
+ case "uri":
76
+ case "url": return "url";
77
+ default: return "text";
78
+ }
79
+ }
80
+ function formatDate(value, type) {
81
+ const pad = (part) => String(part).padStart(2, "0");
82
+ const date = `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;
83
+ const time = `${pad(value.getHours())}:${pad(value.getMinutes())}`;
84
+ if (type === "date") return date;
85
+ if (type === "datetime-local") return `${date}T${time}`;
86
+ if (type === "time") return time;
87
+ return value.toISOString();
88
+ }
89
+ function inputValue(value, type = "text") {
90
+ if (value === void 0 || value === null) return "";
91
+ if (typeof value === "string" || typeof value === "number") return value;
92
+ if (value instanceof Date) return formatDate(value, type);
93
+ return String(value);
94
+ }
95
+ function changedInputValue(field, rawValue, valueAsNumber) {
96
+ if (rawValue === "") return "";
97
+ if (field.dataType === "number" || field.dataType === "integer") return Number.isNaN(valueAsNumber) ? rawValue : valueAsNumber;
98
+ return rawValue;
99
+ }
100
+ function serializedOptionValue(value) {
101
+ return serializeOptionValue(value);
102
+ }
103
+ function optionForValue(options, rawValue) {
104
+ return optionForSerializedValue(options, rawValue);
105
+ }
106
+ function selectedOptionValue(options, value) {
107
+ const selected = options.find((option) => Object.is(option.value, value));
108
+ return selected ? serializedOptionValue(selected.value) : "";
109
+ }
110
+ //#endregion
111
+ //#region src/controls/checkbox.tsx
112
+ function requiresCheckedValue(field) {
113
+ return typeof field.source === "object" && field.source !== null && field.source.const === true;
114
+ }
115
+ function Checkbox({ controlRef, disabled, field, id, inputProps, name, onBlur, onValueChange, readOnly, required, value }) {
116
+ const configured = nativeControlProps(field);
117
+ return /* @__PURE__ */ jsx("input", {
118
+ ...configured.props,
119
+ ...inputProps,
120
+ "aria-readonly": readOnly || void 0,
121
+ checked: value === true,
122
+ className: configured.className,
123
+ disabled: disabled || readOnly,
124
+ id,
125
+ name,
126
+ onBlur,
127
+ onChange: (event) => {
128
+ if (!readOnly) onValueChange(event.currentTarget.checked);
129
+ },
130
+ readOnly,
131
+ ref: controlRef,
132
+ required: required && requiresCheckedValue(field),
133
+ style: configured.style,
134
+ type: "checkbox",
135
+ value: "true"
136
+ });
137
+ }
138
+ //#endregion
139
+ //#region src/controls/file.tsx
140
+ function isFileMetadata(value) {
141
+ return typeof value === "object" && value !== null && typeof value.lastModified === "number" && typeof value.name === "string" && typeof value.size === "number" && typeof value.type === "string";
142
+ }
143
+ function controlledFiles(value) {
144
+ if (isFileMetadata(value)) return [value];
145
+ if (typeof FileList !== "undefined" && value instanceof FileList) return Array.from(value).filter(isFileMetadata);
146
+ if (!Array.isArray(value)) return [];
147
+ return value.filter(isFileMetadata);
148
+ }
149
+ function nativeFilesMatch(value, files) {
150
+ const controlled = controlledFiles(value);
151
+ if (controlled.length === 0 || files?.length !== controlled.length) return false;
152
+ return controlled.every((file, index) => {
153
+ const nativeFile = files.item(index);
154
+ return nativeFile !== null && (Object.is(file, nativeFile) || file.name === nativeFile.name && file.size === nativeFile.size && file.type === nativeFile.type && file.lastModified === nativeFile.lastModified);
155
+ });
156
+ }
157
+ function File({ controlRef, disabled, field, id, inputProps, name, onBlur, onValueChange, readOnly, required, value }) {
158
+ const configured = nativeControlProps(field);
159
+ const input = useRef(null);
160
+ const setControlRef = useCallback((instance) => {
161
+ input.current = instance;
162
+ controlRef(instance);
163
+ }, [controlRef]);
164
+ useEffect(() => {
165
+ const current = input.current;
166
+ if (current?.value && !nativeFilesMatch(value, current.files)) current.value = "";
167
+ }, [value]);
168
+ return /* @__PURE__ */ jsx("input", {
169
+ ...configured.props,
170
+ ...inputProps,
171
+ accept: field.constraints.accept ?? field.constraints.contentMediaType ?? configured.props.accept,
172
+ className: configured.className,
173
+ disabled: disabled || readOnly,
174
+ id,
175
+ multiple: field.config.multiple || field.constraints.multiple,
176
+ name,
177
+ onBlur,
178
+ onChange: (event) => {
179
+ const files = event.currentTarget.files;
180
+ onValueChange(field.config.multiple || field.constraints.multiple ? Array.from(files ?? []) : files?.[0] ?? "");
181
+ },
182
+ ref: setControlRef,
183
+ required,
184
+ style: configured.style,
185
+ type: "file"
186
+ });
187
+ }
188
+ //#endregion
189
+ //#region src/controls/input.tsx
190
+ function Input({ controlRef, disabled, field, id, inputProps, name, onBlur, onValueChange, readOnly, required, value }) {
191
+ const configured = nativeControlProps(field);
192
+ const type = inputType(field);
193
+ return /* @__PURE__ */ jsx("input", {
194
+ ...configured.props,
195
+ ...inputProps,
196
+ className: configured.className,
197
+ disabled: disabled || readOnly && type === "range",
198
+ id,
199
+ max: field.constraints.maximum,
200
+ maxLength: field.constraints.maxLength,
201
+ min: field.constraints.minimum,
202
+ minLength: field.constraints.minLength,
203
+ name,
204
+ onBlur,
205
+ onChange: (event) => {
206
+ if (readOnly) return;
207
+ onValueChange(changedInputValue(field, event.currentTarget.value, event.currentTarget.valueAsNumber));
208
+ },
209
+ pattern: field.constraints.pattern,
210
+ placeholder: field.config.placeholder ?? configured.props.placeholder,
211
+ readOnly,
212
+ ref: controlRef,
213
+ required,
214
+ step: field.constraints.multipleOf ?? (field.dataType === "integer" ? 1 : field.dataType === "number" ? "any" : void 0),
215
+ style: configured.style,
216
+ type,
217
+ value: inputValue(value, type)
218
+ });
219
+ }
220
+ //#endregion
221
+ //#region src/controls/radio.tsx
222
+ function Radio({ controlRef, disabled, field, id, inputProps, name, onBlur, onValueChange, readOnly, required, value }) {
223
+ const configured = nativeControlProps(field);
224
+ const options = field.options ?? [];
225
+ return /* @__PURE__ */ jsx("div", {
226
+ ...inputProps,
227
+ "aria-label": inputProps["aria-label"] ?? field.label,
228
+ "aria-readonly": readOnly || void 0,
229
+ "aria-required": required || void 0,
230
+ ref: options.length === 0 ? controlRef : void 0,
231
+ role: "radiogroup",
232
+ tabIndex: options.length === 0 ? -1 : void 0,
233
+ children: options.map((option, index) => {
234
+ const optionId = index === 0 ? id : `${id}-${index}`;
235
+ return /* @__PURE__ */ jsxs("label", {
236
+ htmlFor: optionId,
237
+ children: [/* @__PURE__ */ jsx("input", {
238
+ ...configured.props,
239
+ checked: Object.is(value, option.value),
240
+ className: configured.className,
241
+ disabled: disabled || readOnly,
242
+ id: optionId,
243
+ name,
244
+ onBlur,
245
+ onChange: () => {
246
+ if (!readOnly) onValueChange(option.value);
247
+ },
248
+ readOnly,
249
+ ref: index === 0 ? controlRef : void 0,
250
+ required,
251
+ style: configured.style,
252
+ type: "radio",
253
+ value: serializedOptionValue(option.value)
254
+ }), /* @__PURE__ */ jsx("span", { children: option.label })]
255
+ }, optionId);
256
+ })
257
+ });
258
+ }
259
+ //#endregion
260
+ //#region src/controls/select.tsx
261
+ function Select({ controlRef, disabled, field, id, inputProps, name, onBlur, onValueChange, readOnly, required, value }) {
262
+ const configured = nativeControlProps(field);
263
+ const options = field.options ?? [];
264
+ const placeholder = field.config.placeholder ?? "Select an option";
265
+ return /* @__PURE__ */ jsxs("select", {
266
+ ...configured.props,
267
+ ...inputProps,
268
+ "aria-readonly": readOnly || void 0,
269
+ className: configured.className,
270
+ disabled: disabled || readOnly,
271
+ id,
272
+ name,
273
+ onBlur,
274
+ onChange: (event) => {
275
+ if (readOnly) return;
276
+ const selected = optionForValue(options, event.currentTarget.value);
277
+ onValueChange(selected ? selected.value : "");
278
+ },
279
+ ref: controlRef,
280
+ required,
281
+ style: configured.style,
282
+ value: selectedOptionValue(options, value),
283
+ children: [/* @__PURE__ */ jsx("option", {
284
+ disabled: required,
285
+ value: "",
286
+ children: placeholder
287
+ }), options.map((option, index) => /* @__PURE__ */ jsx("option", {
288
+ value: serializedOptionValue(option.value),
289
+ children: option.label
290
+ }, `${serializedOptionValue(option.value)}-${index}`))]
291
+ });
292
+ }
293
+ //#endregion
294
+ //#region src/controls/textarea.tsx
295
+ function Textarea({ controlRef, disabled, field, id, inputProps, name, onBlur, onValueChange, readOnly, required, value }) {
296
+ const configured = nativeControlProps(field);
297
+ return /* @__PURE__ */ jsx("textarea", {
298
+ ...configured.props,
299
+ ...inputProps,
300
+ className: configured.className,
301
+ disabled,
302
+ id,
303
+ maxLength: field.constraints.maxLength,
304
+ minLength: field.constraints.minLength,
305
+ name,
306
+ onBlur,
307
+ onChange: (event) => {
308
+ if (!readOnly) onValueChange(event.currentTarget.value);
309
+ },
310
+ placeholder: field.config.placeholder ?? configured.props.placeholder,
311
+ readOnly,
312
+ ref: controlRef,
313
+ required,
314
+ style: configured.style,
315
+ value: inputValue(value)
316
+ });
317
+ }
318
+ //#endregion
319
+ //#region src/slots/array.tsx
320
+ function Array$1({ actions, children, disabled, error, errorId, field, itemCount, readOnly, required, ...props }) {
321
+ const descriptionId = useId();
322
+ const describedBy = [
323
+ props["aria-describedby"],
324
+ field.description ? descriptionId : void 0,
325
+ error ? errorId : void 0
326
+ ].filter(Boolean).join(" ") || void 0;
327
+ return /* @__PURE__ */ jsxs("fieldset", {
328
+ ...props,
329
+ "aria-describedby": describedBy,
330
+ "aria-disabled": disabled || void 0,
331
+ "aria-invalid": error ? true : void 0,
332
+ "data-field-path": field.path,
333
+ "data-invalid": error ? true : void 0,
334
+ "data-item-count": itemCount,
335
+ "data-readonly": readOnly || void 0,
336
+ children: [
337
+ /* @__PURE__ */ jsxs("legend", { children: [field.label, required ? /* @__PURE__ */ jsx("span", {
338
+ "aria-hidden": "true",
339
+ children: " *"
340
+ }) : null] }),
341
+ field.description ? /* @__PURE__ */ jsx("p", {
342
+ id: descriptionId,
343
+ children: field.description
344
+ }) : null,
345
+ children,
346
+ actions,
347
+ error ? /* @__PURE__ */ jsx("p", {
348
+ id: errorId,
349
+ role: "alert",
350
+ children: error
351
+ }) : null
352
+ ]
353
+ });
354
+ }
355
+ //#endregion
356
+ //#region src/slots/array-item.tsx
357
+ function ArrayItem({ actions, children, field, index, label, ...props }) {
358
+ const labelId = useId();
359
+ return /* @__PURE__ */ jsxs("div", {
360
+ ...props,
361
+ "aria-labelledby": props["aria-labelledby"] ?? labelId,
362
+ "data-array-path": field.path,
363
+ "data-item-index": index,
364
+ role: props.role ?? "group",
365
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("span", {
366
+ id: labelId,
367
+ children: label
368
+ }), actions] }), children]
369
+ });
370
+ }
371
+ //#endregion
372
+ //#region src/slots/button.tsx
373
+ function Button({ ariaLabel, children, disabled, intent, onClick, pending, type }) {
374
+ return /* @__PURE__ */ jsx("button", {
375
+ "aria-busy": pending || void 0,
376
+ "aria-label": ariaLabel,
377
+ "data-intent": intent,
378
+ disabled,
379
+ onClick,
380
+ type,
381
+ children
382
+ });
383
+ }
384
+ //#endregion
385
+ //#region src/slots/error-summary.tsx
386
+ function ErrorSummary({ errors, items, onSelect, title }) {
387
+ const resolvedItems = items ?? errors.map((message) => ({ message }));
388
+ if (resolvedItems.length === 0) return null;
389
+ return /* @__PURE__ */ jsxs("div", {
390
+ role: "alert",
391
+ children: [/* @__PURE__ */ jsx("strong", { children: title }), /* @__PURE__ */ jsx("ul", { children: resolvedItems.map((item, index) => {
392
+ const focusPath = item.focusPath;
393
+ return /* @__PURE__ */ jsx("li", { children: focusPath && onSelect ? /* @__PURE__ */ jsx("button", {
394
+ onClick: () => onSelect(focusPath),
395
+ type: "button",
396
+ children: item.message
397
+ }) : item.message }, `${item.path ?? "form"}-${item.message}-${index}`);
398
+ }) })]
399
+ });
400
+ }
401
+ //#endregion
402
+ //#region src/slots/field.tsx
403
+ function FieldLabel({ label, required }) {
404
+ return /* @__PURE__ */ jsxs("span", { children: [label, required ? /* @__PURE__ */ jsx("span", {
405
+ "aria-hidden": "true",
406
+ children: " *"
407
+ }) : null] });
408
+ }
409
+ function Field({ children, controlId, descriptionId, error, errorId, field, invalid, required, validating, ...props }) {
410
+ if (field.control === "hidden" || field.inputType === "hidden") return children;
411
+ const label = /* @__PURE__ */ jsx(FieldLabel, {
412
+ label: field.label,
413
+ required
414
+ });
415
+ return /* @__PURE__ */ jsxs("div", {
416
+ ...props,
417
+ "data-field-path": field.path,
418
+ "data-invalid": invalid || void 0,
419
+ "data-validating": validating || void 0,
420
+ children: [
421
+ field.control === "checkbox" ? /* @__PURE__ */ jsxs("label", {
422
+ htmlFor: controlId,
423
+ children: [children, label]
424
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [field.control === "radio" ? /* @__PURE__ */ jsx("div", { children: label }) : /* @__PURE__ */ jsx("label", {
425
+ htmlFor: controlId,
426
+ children: label
427
+ }), children] }),
428
+ field.description ? /* @__PURE__ */ jsx("p", {
429
+ id: descriptionId,
430
+ children: field.description
431
+ }) : null,
432
+ validating ? /* @__PURE__ */ jsx("output", {
433
+ "aria-live": "polite",
434
+ children: "Checking…"
435
+ }) : null,
436
+ error ? /* @__PURE__ */ jsx("p", {
437
+ id: errorId,
438
+ role: "alert",
439
+ children: error
440
+ }) : null
441
+ ]
442
+ });
443
+ }
444
+ //#endregion
445
+ //#region src/slots/form.tsx
446
+ function Form({ children, ...props }) {
447
+ return /* @__PURE__ */ jsx("form", {
448
+ ...props,
449
+ children
450
+ });
451
+ }
452
+ //#endregion
453
+ //#region src/slots/form-message.tsx
454
+ function FormMessage({ kind, message }) {
455
+ return /* @__PURE__ */ jsx("div", {
456
+ "data-kind": kind,
457
+ role: kind === "error" ? "alert" : "status",
458
+ children: message
459
+ });
460
+ }
461
+ //#endregion
462
+ //#region src/slots/group.tsx
463
+ function Group({ children, disabled, error, errorId, field, readOnly, required, ...props }) {
464
+ const descriptionId = useId();
465
+ const describedBy = [
466
+ props["aria-describedby"],
467
+ field.description ? descriptionId : void 0,
468
+ error ? errorId : void 0
469
+ ].filter(Boolean).join(" ") || void 0;
470
+ return /* @__PURE__ */ jsxs("fieldset", {
471
+ ...props,
472
+ "aria-describedby": describedBy,
473
+ "aria-disabled": disabled || void 0,
474
+ "aria-invalid": error ? true : void 0,
475
+ "data-field-path": field.path,
476
+ "data-invalid": error ? true : void 0,
477
+ "data-readonly": readOnly || void 0,
478
+ children: [
479
+ /* @__PURE__ */ jsxs("legend", { children: [field.label, required ? /* @__PURE__ */ jsx("span", {
480
+ "aria-hidden": "true",
481
+ children: " *"
482
+ }) : null] }),
483
+ field.description ? /* @__PURE__ */ jsx("p", {
484
+ id: descriptionId,
485
+ children: field.description
486
+ }) : null,
487
+ children,
488
+ error ? /* @__PURE__ */ jsx("p", {
489
+ id: errorId,
490
+ role: "alert",
491
+ children: error
492
+ }) : null
493
+ ]
494
+ });
495
+ }
496
+ //#endregion
497
+ //#region src/slots/unsupported.tsx
498
+ function Unsupported({ field, reason }) {
499
+ return /* @__PURE__ */ jsxs("div", {
500
+ role: "alert",
501
+ children: [/* @__PURE__ */ jsx("strong", { children: field.label }), /* @__PURE__ */ jsx("p", { children: reason })]
502
+ });
503
+ }
504
+ //#endregion
505
+ //#region src/slots/wizard.tsx
506
+ function Wizard({ children, currentStep, description, navigation, steps, title, totalSteps, ...props }) {
507
+ const headingId = useId();
508
+ const heading = useRef(null);
509
+ const previousStep = useRef(currentStep);
510
+ useEffect(() => {
511
+ if (previousStep.current !== currentStep) heading.current?.focus();
512
+ previousStep.current = currentStep;
513
+ }, [currentStep]);
514
+ return /* @__PURE__ */ jsxs("section", {
515
+ ...props,
516
+ "aria-labelledby": props["aria-labelledby"] ?? headingId,
517
+ "data-step-count": steps?.length,
518
+ children: [
519
+ /* @__PURE__ */ jsxs("p", {
520
+ "aria-live": "polite",
521
+ children: [
522
+ "Step ",
523
+ currentStep,
524
+ " of ",
525
+ totalSteps
526
+ ]
527
+ }),
528
+ /* @__PURE__ */ jsx("h2", {
529
+ id: headingId,
530
+ ref: heading,
531
+ tabIndex: -1,
532
+ children: title
533
+ }),
534
+ description ? /* @__PURE__ */ jsx("p", { children: description }) : null,
535
+ /* @__PURE__ */ jsx("progress", {
536
+ "aria-label": `Step ${currentStep} of ${totalSteps}`,
537
+ max: totalSteps,
538
+ value: currentStep
539
+ }),
540
+ steps && steps.length > 0 ? /* @__PURE__ */ jsx("ol", {
541
+ "aria-label": "Form steps",
542
+ children: steps.map((step) => /* @__PURE__ */ jsx("li", {
543
+ "aria-current": step.current ? "step" : void 0,
544
+ "data-completed": step.completed || void 0,
545
+ children: step.title
546
+ }, step.id))
547
+ }) : null,
548
+ /* @__PURE__ */ jsx("div", { children }),
549
+ /* @__PURE__ */ jsx("nav", {
550
+ "aria-label": "Wizard navigation",
551
+ children: navigation
552
+ })
553
+ ]
554
+ });
555
+ }
556
+ //#endregion
557
+ //#region src/adapter.ts
558
+ const htmlAdapter = createAdapter({
559
+ name: "HTML",
560
+ controls: {
561
+ checkbox: Checkbox,
562
+ custom: {},
563
+ file: File,
564
+ input: Input,
565
+ radio: Radio,
566
+ select: Select,
567
+ textarea: Textarea
568
+ },
569
+ slots: {
570
+ Array: Array$1,
571
+ ArrayItem,
572
+ Button,
573
+ ErrorSummary,
574
+ Field,
575
+ Form,
576
+ FormMessage,
577
+ Group,
578
+ Unsupported,
579
+ Wizard
580
+ }
581
+ });
582
+ //#endregion
583
+ //#region src/provider.tsx
584
+ /** Client boundary that keeps the function-rich adapter out of server props. */
585
+ function HTMLProvider({ children }) {
586
+ return /* @__PURE__ */ jsx(FormAdapterProvider, {
587
+ adapter: htmlAdapter,
588
+ children
589
+ });
590
+ }
591
+ //#endregion
592
+ //#region src/index.ts
593
+ const createForm = createFormFactory(htmlAdapter);
594
+ //#endregion
595
+ export { Array$1 as Array, ArrayItem, Button, Checkbox, ErrorSummary, Field, File, Form, FormMessage, Group, HTMLProvider, Input, Radio, Select, Textarea, Unsupported, Wizard, createForm, htmlAdapter };
596
+
597
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["Array"],"sources":["../src/controls/shared.ts","../src/controls/checkbox.tsx","../src/controls/file.tsx","../src/controls/input.tsx","../src/controls/radio.tsx","../src/controls/select.tsx","../src/controls/textarea.tsx","../src/slots/array.tsx","../src/slots/array-item.tsx","../src/slots/button.tsx","../src/slots/error-summary.tsx","../src/slots/field.tsx","../src/slots/form.tsx","../src/slots/form-message.tsx","../src/slots/group.tsx","../src/slots/unsupported.tsx","../src/slots/wizard.tsx","../src/adapter.ts","../src/provider.tsx","../src/index.ts"],"sourcesContent":["import type {\n CSSProperties,\n HTMLInputTypeAttribute,\n} from \"react\";\n\nimport type {\n FormOption,\n ScalarField,\n} from \"@formadapter/core\";\nimport {\n optionForSerializedValue,\n serializeOptionValue,\n} from \"@formadapter/core\";\n\ntype NativeScalarField = ScalarField<string, unknown>;\n\nconst RESERVED_CONTROL_PROPS = new Set([\n \"aria-describedby\",\n \"aria-invalid\",\n \"aria-label\",\n \"checked\",\n \"children\",\n \"className\",\n \"constructor\",\n \"controlRef\",\n \"dangerouslySetInnerHTML\",\n \"defaultChecked\",\n \"defaultValue\",\n \"disabled\",\n \"id\",\n \"key\",\n \"multiple\",\n \"name\",\n \"onBlur\",\n \"onChange\",\n \"onInput\",\n \"__proto__\",\n \"prototype\",\n \"readOnly\",\n \"ref\",\n \"required\",\n \"style\",\n \"type\",\n \"value\",\n]);\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport interface NativeControlProps<TProps extends object> {\n readonly className?: string | undefined;\n readonly props: TProps;\n readonly style?: CSSProperties | undefined;\n}\n\n/** Keeps runtime-owned form props authoritative while allowing native options. */\nexport function nativeControlProps<TProps extends object>(\n field: NativeScalarField,\n): NativeControlProps<TProps> {\n const configured = field.config.controlProps;\n if (!isRecord(configured)) return { props: {} as TProps };\n\n const props: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(configured)) {\n if (!RESERVED_CONTROL_PROPS.has(key) && value !== undefined) {\n props[key] = value;\n }\n }\n\n return {\n ...(typeof configured.className === \"string\"\n ? { className: configured.className }\n : {}),\n props: props as TProps,\n ...(isRecord(configured.style)\n ? { style: configured.style as CSSProperties }\n : {}),\n };\n}\n\nexport function inputType(field: NativeScalarField): HTMLInputTypeAttribute {\n if (field.inputType) return field.inputType;\n\n switch (field.control) {\n case \"date\":\n case \"datetime-local\":\n case \"email\":\n case \"hidden\":\n case \"number\":\n case \"password\":\n case \"range\":\n case \"search\":\n case \"tel\":\n case \"text\":\n case \"time\":\n case \"url\":\n return field.control;\n }\n\n if (field.dataType === \"number\" || field.dataType === \"integer\") {\n return \"number\";\n }\n\n switch (field.constraints.format) {\n case \"date\":\n return \"date\";\n case \"date-time\":\n return \"text\";\n case \"email\":\n return \"email\";\n case \"password\":\n return \"password\";\n case \"tel\":\n return \"tel\";\n case \"time\":\n return \"time\";\n case \"uri\":\n case \"url\":\n return \"url\";\n default:\n return \"text\";\n }\n}\n\nfunction formatDate(value: Date, type: HTMLInputTypeAttribute): string {\n const pad = (part: number): string => String(part).padStart(2, \"0\");\n const date = `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;\n const time = `${pad(value.getHours())}:${pad(value.getMinutes())}`;\n if (type === \"date\") return date;\n if (type === \"datetime-local\") return `${date}T${time}`;\n if (type === \"time\") return time;\n return value.toISOString();\n}\n\nexport function inputValue(\n value: unknown,\n type: HTMLInputTypeAttribute = \"text\",\n): string | number {\n if (value === undefined || value === null) return \"\";\n if (typeof value === \"string\" || typeof value === \"number\") return value;\n if (value instanceof Date) return formatDate(value, type);\n return String(value);\n}\n\nexport function changedInputValue(\n field: NativeScalarField,\n rawValue: string,\n valueAsNumber: number,\n): unknown {\n if (rawValue === \"\") return \"\";\n if (field.dataType === \"number\" || field.dataType === \"integer\") {\n return Number.isNaN(valueAsNumber) ? rawValue : valueAsNumber;\n }\n return rawValue;\n}\n\nexport function serializedOptionValue(value: FormOption[\"value\"]): string {\n return serializeOptionValue(value);\n}\n\nexport function optionForValue(\n options: readonly FormOption[],\n rawValue: string,\n): FormOption | undefined {\n return optionForSerializedValue(options, rawValue);\n}\n\nexport function selectedOptionValue(\n options: readonly FormOption[],\n value: unknown,\n): string {\n const selected = options.find((option) => Object.is(option.value, value));\n return selected ? serializedOptionValue(selected.value) : \"\";\n}\n","import type {\n InputHTMLAttributes,\n ReactNode,\n} from \"react\";\n\nimport type { ControlProps } from \"@formadapter/react\";\n\nimport { nativeControlProps } from \"./shared\";\n\nfunction requiresCheckedValue(field: ControlProps[\"field\"]): boolean {\n return typeof field.source === \"object\" &&\n field.source !== null &&\n field.source.const === true;\n}\n\nexport function Checkbox({\n controlRef,\n disabled,\n field,\n id,\n inputProps,\n name,\n onBlur,\n onValueChange,\n readOnly,\n required,\n value,\n}: ControlProps): ReactNode {\n const configured = nativeControlProps<InputHTMLAttributes<HTMLInputElement>>(\n field,\n );\n\n return (\n <input\n {...configured.props}\n {...inputProps}\n aria-readonly={readOnly || undefined}\n checked={value === true}\n className={configured.className}\n disabled={disabled || readOnly}\n id={id}\n name={name}\n onBlur={onBlur}\n onChange={(event) => {\n if (!readOnly) onValueChange(event.currentTarget.checked);\n }}\n readOnly={readOnly}\n ref={controlRef}\n required={required && requiresCheckedValue(field)}\n style={configured.style}\n type=\"checkbox\"\n value=\"true\"\n />\n );\n}\n","import {\n useCallback,\n useEffect,\n useRef,\n type InputHTMLAttributes,\n type ReactNode,\n} from \"react\";\n\nimport type { ControlProps } from \"@formadapter/react\";\n\nimport { nativeControlProps } from \"./shared\";\n\ninterface FileMetadata {\n readonly lastModified: number;\n readonly name: string;\n readonly size: number;\n readonly type: string;\n}\n\nfunction isFileMetadata(value: unknown): value is FileMetadata {\n return typeof value === \"object\" && value !== null &&\n typeof (value as Partial<FileMetadata>).lastModified === \"number\" &&\n typeof (value as Partial<FileMetadata>).name === \"string\" &&\n typeof (value as Partial<FileMetadata>).size === \"number\" &&\n typeof (value as Partial<FileMetadata>).type === \"string\";\n}\n\nfunction controlledFiles(value: unknown): readonly FileMetadata[] {\n if (isFileMetadata(value)) return [value];\n if (typeof FileList !== \"undefined\" && value instanceof FileList) {\n return Array.from(value).filter(isFileMetadata);\n }\n if (!Array.isArray(value)) return [];\n return value.filter(isFileMetadata);\n}\n\nfunction nativeFilesMatch(\n value: unknown,\n files: FileList | null,\n): boolean {\n const controlled = controlledFiles(value);\n if (controlled.length === 0 || files?.length !== controlled.length) {\n return false;\n }\n return controlled.every((file, index) => {\n const nativeFile = files.item(index);\n return nativeFile !== null &&\n (Object.is(file, nativeFile) ||\n (file.name === nativeFile.name &&\n file.size === nativeFile.size &&\n file.type === nativeFile.type &&\n file.lastModified === nativeFile.lastModified));\n });\n}\n\nexport function File({\n controlRef,\n disabled,\n field,\n id,\n inputProps,\n name,\n onBlur,\n onValueChange,\n readOnly,\n required,\n value,\n}: ControlProps): ReactNode {\n const configured = nativeControlProps<InputHTMLAttributes<HTMLInputElement>>(\n field,\n );\n const input = useRef<HTMLInputElement | null>(null);\n const setControlRef = useCallback((instance: HTMLInputElement | null) => {\n input.current = instance;\n controlRef(instance);\n }, [controlRef]);\n\n useEffect(() => {\n const current = input.current;\n if (current?.value && !nativeFilesMatch(value, current.files)) {\n // Browsers forbid assigning a non-empty selection. Clear stale native\n // text rather than displaying file A while form state contains file B.\n current.value = \"\";\n }\n }, [value]);\n\n return (\n <input\n {...configured.props}\n {...inputProps}\n accept={\n field.constraints.accept ??\n field.constraints.contentMediaType ??\n configured.props.accept\n }\n className={configured.className}\n disabled={disabled || readOnly}\n id={id}\n multiple={field.config.multiple || field.constraints.multiple}\n name={name}\n onBlur={onBlur}\n onChange={(event) => {\n const files = event.currentTarget.files;\n onValueChange(\n field.config.multiple || field.constraints.multiple\n ? Array.from(files ?? [])\n : files?.[0] ?? \"\",\n );\n }}\n ref={setControlRef}\n required={required}\n style={configured.style}\n type=\"file\"\n />\n );\n}\n","import type {\n InputHTMLAttributes,\n ReactNode,\n} from \"react\";\n\nimport type { ControlProps } from \"@formadapter/react\";\n\nimport {\n changedInputValue,\n inputType,\n inputValue,\n nativeControlProps,\n} from \"./shared\";\n\nexport function Input({\n controlRef,\n disabled,\n field,\n id,\n inputProps,\n name,\n onBlur,\n onValueChange,\n readOnly,\n required,\n value,\n}: ControlProps): ReactNode {\n const configured = nativeControlProps<InputHTMLAttributes<HTMLInputElement>>(\n field,\n );\n const type = inputType(field);\n\n return (\n <input\n {...configured.props}\n {...inputProps}\n className={configured.className}\n disabled={disabled || (readOnly && type === \"range\")}\n id={id}\n max={field.constraints.maximum}\n maxLength={field.constraints.maxLength}\n min={field.constraints.minimum}\n minLength={field.constraints.minLength}\n name={name}\n onBlur={onBlur}\n onChange={(event) => {\n if (readOnly) return;\n onValueChange(\n changedInputValue(\n field,\n event.currentTarget.value,\n event.currentTarget.valueAsNumber,\n ),\n );\n }}\n pattern={field.constraints.pattern}\n placeholder={field.config.placeholder ?? configured.props.placeholder}\n readOnly={readOnly}\n ref={controlRef}\n required={required}\n step={\n field.constraints.multipleOf ??\n (field.dataType === \"integer\"\n ? 1\n : field.dataType === \"number\"\n ? \"any\"\n : undefined)\n }\n style={configured.style}\n type={type}\n value={inputValue(value, type)}\n />\n );\n}\n","import type {\n InputHTMLAttributes,\n ReactNode,\n} from \"react\";\n\nimport type { ControlProps } from \"@formadapter/react\";\n\nimport {\n nativeControlProps,\n serializedOptionValue,\n} from \"./shared\";\n\nexport function Radio({\n controlRef,\n disabled,\n field,\n id,\n inputProps,\n name,\n onBlur,\n onValueChange,\n readOnly,\n required,\n value,\n}: ControlProps): ReactNode {\n const configured = nativeControlProps<InputHTMLAttributes<HTMLInputElement>>(\n field,\n );\n const options = field.options ?? [];\n\n return (\n <div\n {...inputProps}\n aria-label={inputProps[\"aria-label\"] ?? field.label}\n aria-readonly={readOnly || undefined}\n aria-required={required || undefined}\n ref={options.length === 0 ? controlRef : undefined}\n role=\"radiogroup\"\n tabIndex={options.length === 0 ? -1 : undefined}\n >\n {options.map((option, index) => {\n const optionId = index === 0 ? id : `${id}-${index}`;\n return (\n <label htmlFor={optionId} key={optionId}>\n <input\n {...configured.props}\n checked={Object.is(value, option.value)}\n className={configured.className}\n disabled={disabled || readOnly}\n id={optionId}\n name={name}\n onBlur={onBlur}\n onChange={() => {\n if (!readOnly) onValueChange(option.value);\n }}\n readOnly={readOnly}\n ref={index === 0 ? controlRef : undefined}\n required={required}\n style={configured.style}\n type=\"radio\"\n value={serializedOptionValue(option.value)}\n />\n <span>{option.label}</span>\n </label>\n );\n })}\n </div>\n );\n}\n","import type {\n ReactNode,\n SelectHTMLAttributes,\n} from \"react\";\n\nimport type { ControlProps } from \"@formadapter/react\";\n\nimport {\n nativeControlProps,\n optionForValue,\n selectedOptionValue,\n serializedOptionValue,\n} from \"./shared\";\n\nexport function Select({\n controlRef,\n disabled,\n field,\n id,\n inputProps,\n name,\n onBlur,\n onValueChange,\n readOnly,\n required,\n value,\n}: ControlProps): ReactNode {\n const configured =\n nativeControlProps<SelectHTMLAttributes<HTMLSelectElement>>(field);\n const options = field.options ?? [];\n const placeholder = field.config.placeholder ?? \"Select an option\";\n\n return (\n <select\n {...configured.props}\n {...inputProps}\n aria-readonly={readOnly || undefined}\n className={configured.className}\n disabled={disabled || readOnly}\n id={id}\n name={name}\n onBlur={onBlur}\n onChange={(event) => {\n if (readOnly) return;\n const selected = optionForValue(options, event.currentTarget.value);\n onValueChange(selected ? selected.value : \"\");\n }}\n ref={controlRef}\n required={required}\n style={configured.style}\n value={selectedOptionValue(options, value)}\n >\n <option disabled={required} value=\"\">\n {placeholder}\n </option>\n {options.map((option, index) => (\n <option\n key={`${serializedOptionValue(option.value)}-${index}`}\n value={serializedOptionValue(option.value)}\n >\n {option.label}\n </option>\n ))}\n </select>\n );\n}\n","import type {\n ReactNode,\n TextareaHTMLAttributes,\n} from \"react\";\n\nimport type { ControlProps } from \"@formadapter/react\";\n\nimport {\n inputValue,\n nativeControlProps,\n} from \"./shared\";\n\nexport function Textarea({\n controlRef,\n disabled,\n field,\n id,\n inputProps,\n name,\n onBlur,\n onValueChange,\n readOnly,\n required,\n value,\n}: ControlProps): ReactNode {\n const configured =\n nativeControlProps<TextareaHTMLAttributes<HTMLTextAreaElement>>(field);\n\n return (\n <textarea\n {...configured.props}\n {...inputProps}\n className={configured.className}\n disabled={disabled}\n id={id}\n maxLength={field.constraints.maxLength}\n minLength={field.constraints.minLength}\n name={name}\n onBlur={onBlur}\n onChange={(event) => {\n if (!readOnly) onValueChange(event.currentTarget.value);\n }}\n placeholder={field.config.placeholder ?? configured.props.placeholder}\n readOnly={readOnly}\n ref={controlRef}\n required={required}\n style={configured.style}\n value={inputValue(value)}\n />\n );\n}\n","import { useId, type ReactNode } from \"react\";\n\nimport type { ArraySlotProps } from \"@formadapter/react\";\n\nexport function Array({\n actions,\n children,\n disabled,\n error,\n errorId,\n field,\n itemCount,\n readOnly,\n required,\n ...props\n}: ArraySlotProps): ReactNode {\n const descriptionId = useId();\n const describedBy = [\n props[\"aria-describedby\"],\n field.description ? descriptionId : undefined,\n error ? errorId : undefined,\n ].filter(Boolean).join(\" \") || undefined;\n\n return (\n <fieldset\n {...props}\n aria-describedby={describedBy}\n aria-disabled={disabled || undefined}\n aria-invalid={error ? true : undefined}\n data-field-path={field.path}\n data-invalid={error ? true : undefined}\n data-item-count={itemCount}\n data-readonly={readOnly || undefined}\n >\n <legend>\n {field.label}\n {required ? <span aria-hidden=\"true\"> *</span> : null}\n </legend>\n {field.description ? <p id={descriptionId}>{field.description}</p> : null}\n {children}\n {actions}\n {error ? <p id={errorId} role=\"alert\">{error}</p> : null}\n </fieldset>\n );\n}\n","import { useId, type ReactNode } from \"react\";\n\nimport type { ArrayItemSlotProps } from \"@formadapter/react\";\n\nexport function ArrayItem({\n actions,\n children,\n field,\n index,\n label,\n ...props\n}: ArrayItemSlotProps): ReactNode {\n const labelId = useId();\n\n return (\n <div\n {...props}\n aria-labelledby={props[\"aria-labelledby\"] ?? labelId}\n data-array-path={field.path}\n data-item-index={index}\n role={props.role ?? \"group\"}\n >\n <div>\n <span id={labelId}>{label}</span>\n {actions}\n </div>\n {children}\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport type { ButtonSlotProps } from \"@formadapter/react\";\n\nexport function Button({\n ariaLabel,\n children,\n disabled,\n intent,\n onClick,\n pending,\n type,\n}: ButtonSlotProps): ReactNode {\n return (\n <button\n aria-busy={pending || undefined}\n aria-label={ariaLabel}\n data-intent={intent}\n disabled={disabled}\n onClick={onClick}\n type={type}\n >\n {children}\n </button>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport type { ErrorSummarySlotProps } from \"@formadapter/react\";\n\nexport function ErrorSummary({\n errors,\n items,\n onSelect,\n title,\n}: ErrorSummarySlotProps): ReactNode {\n const resolvedItems: NonNullable<ErrorSummarySlotProps[\"items\"]> =\n items ?? errors.map((message) => ({ message }));\n\n if (resolvedItems.length === 0) return null;\n\n return (\n <div role=\"alert\">\n <strong>{title}</strong>\n <ul>\n {resolvedItems.map((item, index) => {\n const focusPath = item.focusPath;\n return (\n <li key={`${item.path ?? \"form\"}-${item.message}-${index}`}>\n {focusPath && onSelect ? (\n <button onClick={() => onSelect(focusPath)} type=\"button\">\n {item.message}\n </button>\n ) : item.message}\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport type { FieldSlotProps } from \"@formadapter/react\";\n\ninterface FieldLabelProps {\n readonly label: string;\n readonly required: boolean;\n}\n\nfunction FieldLabel({ label, required }: FieldLabelProps): ReactNode {\n return (\n <span>\n {label}\n {required ? <span aria-hidden=\"true\"> *</span> : null}\n </span>\n );\n}\n\nexport function Field({\n children,\n controlId,\n descriptionId,\n error,\n errorId,\n field,\n invalid,\n required,\n validating,\n ...props\n}: FieldSlotProps): ReactNode {\n if (field.control === \"hidden\" || field.inputType === \"hidden\") {\n return children;\n }\n\n const label = <FieldLabel label={field.label} required={required} />;\n\n return (\n <div\n {...props}\n data-field-path={field.path}\n data-invalid={invalid || undefined}\n data-validating={validating || undefined}\n >\n {field.control === \"checkbox\" ? (\n <label htmlFor={controlId}>\n {children}\n {label}\n </label>\n ) : (\n <>\n {field.control === \"radio\" ? (\n <div>{label}</div>\n ) : (\n <label htmlFor={controlId}>{label}</label>\n )}\n {children}\n </>\n )}\n {field.description ? (\n <p id={descriptionId}>{field.description}</p>\n ) : null}\n {validating ? <output aria-live=\"polite\">Checking…</output> : null}\n {error ? <p id={errorId} role=\"alert\">{error}</p> : null}\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport type { FormSlotProps } from \"@formadapter/react\";\n\nexport function Form({ children, ...props }: FormSlotProps): ReactNode {\n return <form {...props}>{children}</form>;\n}\n","import type { ReactNode } from \"react\";\n\nimport type { FormMessageSlotProps } from \"@formadapter/react\";\n\nexport function FormMessage({\n kind,\n message,\n}: FormMessageSlotProps): ReactNode {\n return (\n <div\n data-kind={kind}\n role={kind === \"error\" ? \"alert\" : \"status\"}\n >\n {message}\n </div>\n );\n}\n","import { useId, type ReactNode } from \"react\";\n\nimport type { GroupSlotProps } from \"@formadapter/react\";\n\nexport function Group({\n children,\n disabled,\n error,\n errorId,\n field,\n readOnly,\n required,\n ...props\n}: GroupSlotProps): ReactNode {\n const descriptionId = useId();\n const describedBy = [\n props[\"aria-describedby\"],\n field.description ? descriptionId : undefined,\n error ? errorId : undefined,\n ].filter(Boolean).join(\" \") || undefined;\n\n return (\n <fieldset\n {...props}\n aria-describedby={describedBy}\n aria-disabled={disabled || undefined}\n aria-invalid={error ? true : undefined}\n data-field-path={field.path}\n data-invalid={error ? true : undefined}\n data-readonly={readOnly || undefined}\n >\n <legend>\n {field.label}\n {required ? <span aria-hidden=\"true\"> *</span> : null}\n </legend>\n {field.description ? <p id={descriptionId}>{field.description}</p> : null}\n {children}\n {error ? <p id={errorId} role=\"alert\">{error}</p> : null}\n </fieldset>\n );\n}\n","import type { ReactNode } from \"react\";\n\nimport type { UnsupportedSlotProps } from \"@formadapter/react\";\n\nexport function Unsupported({\n field,\n reason,\n}: UnsupportedSlotProps): ReactNode {\n return (\n <div role=\"alert\">\n <strong>{field.label}</strong>\n <p>{reason}</p>\n </div>\n );\n}\n","import {\n useEffect,\n useId,\n useRef,\n type ReactNode,\n} from \"react\";\n\nimport type { WizardSlotProps } from \"@formadapter/react\";\n\nexport function Wizard({\n children,\n currentStep,\n description,\n navigation,\n steps,\n title,\n totalSteps,\n ...props\n}: WizardSlotProps): ReactNode {\n const headingId = useId();\n const heading = useRef<HTMLHeadingElement | null>(null);\n const previousStep = useRef(currentStep);\n\n useEffect(() => {\n if (previousStep.current !== currentStep) heading.current?.focus();\n previousStep.current = currentStep;\n }, [currentStep]);\n\n return (\n <section\n {...props}\n aria-labelledby={props[\"aria-labelledby\"] ?? headingId}\n data-step-count={steps?.length}\n >\n <p aria-live=\"polite\">Step {currentStep} of {totalSteps}</p>\n <h2 id={headingId} ref={heading} tabIndex={-1}>{title}</h2>\n {description ? <p>{description}</p> : null}\n <progress\n aria-label={`Step ${currentStep} of ${totalSteps}`}\n max={totalSteps}\n value={currentStep}\n />\n {steps && steps.length > 0 ? (\n <ol aria-label=\"Form steps\">\n {steps.map((step) => (\n <li\n aria-current={step.current ? \"step\" : undefined}\n data-completed={step.completed || undefined}\n key={step.id}\n >\n {step.title}\n </li>\n ))}\n </ol>\n ) : null}\n <div>{children}</div>\n <nav aria-label=\"Wizard navigation\">{navigation}</nav>\n </section>\n );\n}\n","import {\n createAdapter,\n type FormAdapter,\n} from \"@formadapter/react\";\n\nimport { Checkbox } from \"./controls/checkbox\";\nimport { File } from \"./controls/file\";\nimport { Input } from \"./controls/input\";\nimport { Radio } from \"./controls/radio\";\nimport { Select } from \"./controls/select\";\nimport { Textarea } from \"./controls/textarea\";\nimport { Array } from \"./slots/array\";\nimport { ArrayItem } from \"./slots/array-item\";\nimport { Button } from \"./slots/button\";\nimport { ErrorSummary } from \"./slots/error-summary\";\nimport { Field } from \"./slots/field\";\nimport { Form } from \"./slots/form\";\nimport { FormMessage } from \"./slots/form-message\";\nimport { Group } from \"./slots/group\";\nimport { Unsupported } from \"./slots/unsupported\";\nimport { Wizard } from \"./slots/wizard\";\n\nexport const htmlAdapter: FormAdapter<Record<never, never>> = createAdapter({\n name: \"HTML\",\n controls: {\n checkbox: Checkbox,\n custom: {},\n file: File,\n input: Input,\n radio: Radio,\n select: Select,\n textarea: Textarea,\n },\n slots: {\n Array,\n ArrayItem,\n Button,\n ErrorSummary,\n Field,\n Form,\n FormMessage,\n Group,\n Unsupported,\n Wizard,\n },\n});\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { FormAdapterProvider } from \"@formadapter/react\";\n\nimport { htmlAdapter } from \"./adapter\";\n\nexport interface HTMLProviderProps {\n readonly children: ReactNode;\n}\n\n/** Client boundary that keeps the function-rich adapter out of server props. */\nexport function HTMLProvider({ children }: HTMLProviderProps): ReactNode {\n return (\n <FormAdapterProvider adapter={htmlAdapter}>\n {children}\n </FormAdapterProvider>\n );\n}\n","import {\n createFormFactory,\n type CreateForm,\n} from \"@formadapter/react\";\n\nimport { htmlAdapter } from \"./adapter\";\n\nexport { htmlAdapter } from \"./adapter\";\nexport { HTMLProvider } from \"./provider\";\nexport type { HTMLProviderProps } from \"./provider\";\nexport { Checkbox } from \"./controls/checkbox\";\nexport { File } from \"./controls/file\";\nexport { Input } from \"./controls/input\";\nexport { Radio } from \"./controls/radio\";\nexport { Select } from \"./controls/select\";\nexport { Textarea } from \"./controls/textarea\";\nexport { Array } from \"./slots/array\";\nexport { ArrayItem } from \"./slots/array-item\";\nexport { Button } from \"./slots/button\";\nexport { ErrorSummary } from \"./slots/error-summary\";\nexport { Field } from \"./slots/field\";\nexport { Form } from \"./slots/form\";\nexport { FormMessage } from \"./slots/form-message\";\nexport { Group } from \"./slots/group\";\nexport { Unsupported } from \"./slots/unsupported\";\nexport { Wizard } from \"./slots/wizard\";\n\nexport const createForm: CreateForm<typeof htmlAdapter> =\n createFormFactory(htmlAdapter);\n"],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { FormOption, ScalarField } from "@formadapter/core";
2
+ import { CSSProperties, HTMLInputTypeAttribute } from "react";
3
+
4
+ //#region src/controls/shared.d.ts
5
+ type NativeScalarField = ScalarField<string, unknown>;
6
+ interface NativeControlProps<TProps extends object> {
7
+ readonly className?: string | undefined;
8
+ readonly props: TProps;
9
+ readonly style?: CSSProperties | undefined;
10
+ }
11
+ /** Keeps runtime-owned form props authoritative while allowing native options. */
12
+ declare function nativeControlProps<TProps extends object>(field: NativeScalarField): NativeControlProps<TProps>;
13
+ declare function inputType(field: NativeScalarField): HTMLInputTypeAttribute;
14
+ declare function inputValue(value: unknown, type?: HTMLInputTypeAttribute): string | number;
15
+ declare function changedInputValue(field: NativeScalarField, rawValue: string, valueAsNumber: number): unknown;
16
+ declare function serializedOptionValue(value: FormOption["value"]): string;
17
+ declare function optionForValue(options: readonly FormOption[], rawValue: string): FormOption | undefined;
18
+ declare function selectedOptionValue(options: readonly FormOption[], value: unknown): string;
19
+ //#endregion
20
+ export { type NativeControlProps, changedInputValue, inputType, inputValue, nativeControlProps, optionForValue, selectedOptionValue, serializedOptionValue };
21
+ //# sourceMappingURL=native.d.ts.map
package/dist/native.js ADDED
@@ -0,0 +1,109 @@
1
+ import { optionForSerializedValue, serializeOptionValue } from "@formadapter/core";
2
+ //#region src/controls/shared.ts
3
+ const RESERVED_CONTROL_PROPS = new Set([
4
+ "aria-describedby",
5
+ "aria-invalid",
6
+ "aria-label",
7
+ "checked",
8
+ "children",
9
+ "className",
10
+ "constructor",
11
+ "controlRef",
12
+ "dangerouslySetInnerHTML",
13
+ "defaultChecked",
14
+ "defaultValue",
15
+ "disabled",
16
+ "id",
17
+ "key",
18
+ "multiple",
19
+ "name",
20
+ "onBlur",
21
+ "onChange",
22
+ "onInput",
23
+ "__proto__",
24
+ "prototype",
25
+ "readOnly",
26
+ "ref",
27
+ "required",
28
+ "style",
29
+ "type",
30
+ "value"
31
+ ]);
32
+ function isRecord(value) {
33
+ return typeof value === "object" && value !== null && !Array.isArray(value);
34
+ }
35
+ /** Keeps runtime-owned form props authoritative while allowing native options. */
36
+ function nativeControlProps(field) {
37
+ const configured = field.config.controlProps;
38
+ if (!isRecord(configured)) return { props: {} };
39
+ const props = {};
40
+ for (const [key, value] of Object.entries(configured)) if (!RESERVED_CONTROL_PROPS.has(key) && value !== void 0) props[key] = value;
41
+ return {
42
+ ...typeof configured.className === "string" ? { className: configured.className } : {},
43
+ props,
44
+ ...isRecord(configured.style) ? { style: configured.style } : {}
45
+ };
46
+ }
47
+ function inputType(field) {
48
+ if (field.inputType) return field.inputType;
49
+ switch (field.control) {
50
+ case "date":
51
+ case "datetime-local":
52
+ case "email":
53
+ case "hidden":
54
+ case "number":
55
+ case "password":
56
+ case "range":
57
+ case "search":
58
+ case "tel":
59
+ case "text":
60
+ case "time":
61
+ case "url": return field.control;
62
+ }
63
+ if (field.dataType === "number" || field.dataType === "integer") return "number";
64
+ switch (field.constraints.format) {
65
+ case "date": return "date";
66
+ case "date-time": return "text";
67
+ case "email": return "email";
68
+ case "password": return "password";
69
+ case "tel": return "tel";
70
+ case "time": return "time";
71
+ case "uri":
72
+ case "url": return "url";
73
+ default: return "text";
74
+ }
75
+ }
76
+ function formatDate(value, type) {
77
+ const pad = (part) => String(part).padStart(2, "0");
78
+ const date = `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;
79
+ const time = `${pad(value.getHours())}:${pad(value.getMinutes())}`;
80
+ if (type === "date") return date;
81
+ if (type === "datetime-local") return `${date}T${time}`;
82
+ if (type === "time") return time;
83
+ return value.toISOString();
84
+ }
85
+ function inputValue(value, type = "text") {
86
+ if (value === void 0 || value === null) return "";
87
+ if (typeof value === "string" || typeof value === "number") return value;
88
+ if (value instanceof Date) return formatDate(value, type);
89
+ return String(value);
90
+ }
91
+ function changedInputValue(field, rawValue, valueAsNumber) {
92
+ if (rawValue === "") return "";
93
+ if (field.dataType === "number" || field.dataType === "integer") return Number.isNaN(valueAsNumber) ? rawValue : valueAsNumber;
94
+ return rawValue;
95
+ }
96
+ function serializedOptionValue(value) {
97
+ return serializeOptionValue(value);
98
+ }
99
+ function optionForValue(options, rawValue) {
100
+ return optionForSerializedValue(options, rawValue);
101
+ }
102
+ function selectedOptionValue(options, value) {
103
+ const selected = options.find((option) => Object.is(option.value, value));
104
+ return selected ? serializedOptionValue(selected.value) : "";
105
+ }
106
+ //#endregion
107
+ export { changedInputValue, inputType, inputValue, nativeControlProps, optionForValue, selectedOptionValue, serializedOptionValue };
108
+
109
+ //# sourceMappingURL=native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.js","names":[],"sources":["../src/controls/shared.ts"],"sourcesContent":["import type {\n CSSProperties,\n HTMLInputTypeAttribute,\n} from \"react\";\n\nimport type {\n FormOption,\n ScalarField,\n} from \"@formadapter/core\";\nimport {\n optionForSerializedValue,\n serializeOptionValue,\n} from \"@formadapter/core\";\n\ntype NativeScalarField = ScalarField<string, unknown>;\n\nconst RESERVED_CONTROL_PROPS = new Set([\n \"aria-describedby\",\n \"aria-invalid\",\n \"aria-label\",\n \"checked\",\n \"children\",\n \"className\",\n \"constructor\",\n \"controlRef\",\n \"dangerouslySetInnerHTML\",\n \"defaultChecked\",\n \"defaultValue\",\n \"disabled\",\n \"id\",\n \"key\",\n \"multiple\",\n \"name\",\n \"onBlur\",\n \"onChange\",\n \"onInput\",\n \"__proto__\",\n \"prototype\",\n \"readOnly\",\n \"ref\",\n \"required\",\n \"style\",\n \"type\",\n \"value\",\n]);\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport interface NativeControlProps<TProps extends object> {\n readonly className?: string | undefined;\n readonly props: TProps;\n readonly style?: CSSProperties | undefined;\n}\n\n/** Keeps runtime-owned form props authoritative while allowing native options. */\nexport function nativeControlProps<TProps extends object>(\n field: NativeScalarField,\n): NativeControlProps<TProps> {\n const configured = field.config.controlProps;\n if (!isRecord(configured)) return { props: {} as TProps };\n\n const props: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(configured)) {\n if (!RESERVED_CONTROL_PROPS.has(key) && value !== undefined) {\n props[key] = value;\n }\n }\n\n return {\n ...(typeof configured.className === \"string\"\n ? { className: configured.className }\n : {}),\n props: props as TProps,\n ...(isRecord(configured.style)\n ? { style: configured.style as CSSProperties }\n : {}),\n };\n}\n\nexport function inputType(field: NativeScalarField): HTMLInputTypeAttribute {\n if (field.inputType) return field.inputType;\n\n switch (field.control) {\n case \"date\":\n case \"datetime-local\":\n case \"email\":\n case \"hidden\":\n case \"number\":\n case \"password\":\n case \"range\":\n case \"search\":\n case \"tel\":\n case \"text\":\n case \"time\":\n case \"url\":\n return field.control;\n }\n\n if (field.dataType === \"number\" || field.dataType === \"integer\") {\n return \"number\";\n }\n\n switch (field.constraints.format) {\n case \"date\":\n return \"date\";\n case \"date-time\":\n return \"text\";\n case \"email\":\n return \"email\";\n case \"password\":\n return \"password\";\n case \"tel\":\n return \"tel\";\n case \"time\":\n return \"time\";\n case \"uri\":\n case \"url\":\n return \"url\";\n default:\n return \"text\";\n }\n}\n\nfunction formatDate(value: Date, type: HTMLInputTypeAttribute): string {\n const pad = (part: number): string => String(part).padStart(2, \"0\");\n const date = `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;\n const time = `${pad(value.getHours())}:${pad(value.getMinutes())}`;\n if (type === \"date\") return date;\n if (type === \"datetime-local\") return `${date}T${time}`;\n if (type === \"time\") return time;\n return value.toISOString();\n}\n\nexport function inputValue(\n value: unknown,\n type: HTMLInputTypeAttribute = \"text\",\n): string | number {\n if (value === undefined || value === null) return \"\";\n if (typeof value === \"string\" || typeof value === \"number\") return value;\n if (value instanceof Date) return formatDate(value, type);\n return String(value);\n}\n\nexport function changedInputValue(\n field: NativeScalarField,\n rawValue: string,\n valueAsNumber: number,\n): unknown {\n if (rawValue === \"\") return \"\";\n if (field.dataType === \"number\" || field.dataType === \"integer\") {\n return Number.isNaN(valueAsNumber) ? rawValue : valueAsNumber;\n }\n return rawValue;\n}\n\nexport function serializedOptionValue(value: FormOption[\"value\"]): string {\n return serializeOptionValue(value);\n}\n\nexport function optionForValue(\n options: readonly FormOption[],\n rawValue: string,\n): FormOption | undefined {\n return optionForSerializedValue(options, rawValue);\n}\n\nexport function selectedOptionValue(\n options: readonly FormOption[],\n value: unknown,\n): string {\n const selected = options.find((option) => Object.is(option.value, value));\n return selected ? serializedOptionValue(selected.value) : \"\";\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@formadapter/html",
3
+ "version": "0.0.0",
4
+ "description": "Accessible, unstyled native HTML renderer for FormAdapter.",
5
+ "keywords": [
6
+ "forms",
7
+ "html",
8
+ "react",
9
+ "schema",
10
+ "typescript"
11
+ ],
12
+ "homepage": "https://formadapter.com",
13
+ "bugs": {
14
+ "url": "https://github.com/ludicroushq/formadapter/issues"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/ludicroushq/formadapter.git",
19
+ "directory": "packages/html"
20
+ },
21
+ "license": "MIT",
22
+ "author": "ludicrous",
23
+ "type": "module",
24
+ "sideEffects": false,
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "main": "./dist/index.js",
34
+ "module": "./dist/index.js",
35
+ "types": "./dist/index.d.ts",
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/index.js",
40
+ "default": "./dist/index.js"
41
+ },
42
+ "./native": {
43
+ "types": "./dist/native.d.ts",
44
+ "import": "./dist/native.js",
45
+ "default": "./dist/native.js"
46
+ },
47
+ "./package.json": "./package.json"
48
+ },
49
+ "dependencies": {
50
+ "@formadapter/core": "^0.0.0",
51
+ "@formadapter/react": "^0.0.0"
52
+ },
53
+ "peerDependencies": {
54
+ "react": "^19.0.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/react": "^19.2.17",
58
+ "@types/react-dom": "^19.2.3",
59
+ "react": "19.2.7",
60
+ "react-dom": "19.2.7",
61
+ "zod": "^4.4.3"
62
+ },
63
+ "scripts": {
64
+ "build": "tsdown",
65
+ "clean": "rm -rf dist tsconfig.tsbuildinfo .turbo",
66
+ "dev": "tsdown --watch",
67
+ "test": "vitest run --config vitest.config.ts",
68
+ "test:coverage": "vitest run --config vitest.config.ts --coverage",
69
+ "typecheck": "tsc --project tsconfig.json --noEmit"
70
+ }
71
+ }