@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 +21 -0
- package/README.md +70 -0
- package/dist/index.d.ts +210 -0
- package/dist/index.js +597 -0
- package/dist/index.js.map +1 -0
- package/dist/native.d.ts +21 -0
- package/dist/native.js +109 -0
- package/dist/native.js.map +1 -0
- package/package.json +71 -0
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.
|
package/dist/index.d.ts
ADDED
|
@@ -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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"}
|
package/dist/native.d.ts
ADDED
|
@@ -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
|
+
}
|