@amplifyup/forms 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,100 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # @amplifyup/forms
2
+
3
+ Headless React forms for [Amplify UP](https://amplifyup.ai). Build a form in Amplify UP, place it on a page, and this package renders its fields, validates input, submits it, and tracks the submit. It ships no styles: you bring the look, the package handles the behaviour.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @amplifyup/forms
9
+ ```
10
+
11
+ It needs `@amplifyup/sdk` set up on your site with your tracking ID (see the SDK README).
12
+
13
+ ## Register
14
+
15
+ Run your usual Amplify UP push so the platform knows your site can render forms:
16
+
17
+ ```bash
18
+ npx amplifyup push
19
+ ```
20
+
21
+ `Form` then shows up in Composer as **Form** (`@amplifyup/forms:Form`). Your component registry picks the package up from your `package.json`; there is nothing else to configure.
22
+
23
+ ## Use
24
+
25
+ Placed on a page in Composer, `Form` renders on its own. You can also render it yourself:
26
+
27
+ ```tsx
28
+ import { Form } from '@amplifyup/forms';
29
+
30
+ export const ContactSection = () => (
31
+ <Form
32
+ formId="your-form-id"
33
+ onSuccess={(result) => console.log('submitted', result)}
34
+ onError={(event) => console.warn(event.kind)}
35
+ />
36
+ );
37
+ ```
38
+
39
+ What `Form` does:
40
+
41
+ 1. Loads the published form and renders its title, intro, headings and fields: text, email, phone, number, date, textarea, dropdown (select), single choice (radio), checkboxes, checkbox, consent and hidden. Hidden fields set to a UTM parameter pick up its value from the page address.
42
+ 2. Splits the form into steps where it has page breaks, with Next and Back buttons. Each step is checked before moving on.
43
+ 3. Checks required fields and email, phone, number, date and choice values before submitting, and shows errors next to each field.
44
+ 4. Submits, then shows the form's success message or redirects, whichever the form is set to.
45
+ 5. Shows errors returned by Amplify UP against the right fields.
46
+ 6. Tracks a `Form Submit` event with `formId`, `formKey` and `formVersion`, once per successful submit.
47
+
48
+ In Composer previews the form renders but never submits.
49
+
50
+ ## Swap any part
51
+
52
+ Pass `components` to replace any renderer. Submit, validation and tracking keep working.
53
+
54
+ ```tsx
55
+ import { Form, type FieldControlProps } from '@amplifyup/forms';
56
+
57
+ const BrandInput = ({ id, field, value, onChange, error }: FieldControlProps) => (
58
+ <input
59
+ id={id}
60
+ className={error ? 'input input--error' : 'input'}
61
+ placeholder={field.placeholder}
62
+ value={String(value)}
63
+ onChange={(event) => onChange(event.target.value)}
64
+ />
65
+ );
66
+
67
+ <Form formId="your-form-id" components={{ Input: BrandInput }} />;
68
+ ```
69
+
70
+ | Renderer | Props |
71
+ |---|---|
72
+ | `Field` | `id`, `field`, `value`, `onChange`, `error`, `children` (wraps label + control + error) |
73
+ | `Input`, `Textarea`, `Select`, `Radio`, `Checkboxes`, `Checkbox` | `id`, `field`, `value`, `onChange`, `error`. `Checkboxes` values are a string array; `Checkbox` also renders consent fields |
74
+ | `Button` | `label`, `submitting`, `disabled` (Submit, or Next before the last step) |
75
+ | `Back` | `label`, `onClick` |
76
+ | `Text` | `kind` (`title`, `intro`, `step`, `heading`, `paragraph`), `text` |
77
+ | `Error` | `message`, `field?`, `id?` |
78
+ | `Success` | `message` |
79
+
80
+ ## Styling hooks
81
+
82
+ The default renderers are plain HTML with these classes:
83
+
84
+ | Class | Element |
85
+ |---|---|
86
+ | `aup-form` | `<form>` |
87
+ | `aup-form__field` | Wrapper per field (`data-invalid="true"` when it has an error) |
88
+ | `aup-form__field--half` | Added to half-width fields (`data-width="half"`); lay them out side by side yourself |
89
+ | `aup-form__label` | Label |
90
+ | `aup-form__input` | Input, textarea, select, radio and checkbox |
91
+ | `aup-form__choices`, `aup-form__choice` | Radio and checkboxes group, and each option |
92
+ | `aup-form__title`, `aup-form__intro`, `aup-form__step`, `aup-form__heading`, `aup-form__paragraph` | Text |
93
+ | `aup-form__back` | Back button |
94
+ | `aup-form__error` | Field or form error |
95
+ | `aup-form__submit` | Submit button |
96
+ | `aup-form__success` | Success message |
97
+
98
+ ## License
99
+
100
+ MIT
@@ -0,0 +1,25 @@
1
+ {
2
+ "components": [
3
+ {
4
+ "component_id": "@amplifyup/forms:Form",
5
+ "component_name": "Form",
6
+ "component_type": "form",
7
+ "description": "A form built in Amplify UP. Renders its fields and handles submission.",
8
+ "export_name": "Form",
9
+ "fields": [
10
+ {
11
+ "name": "formId",
12
+ "label": "Form",
13
+ "role": "config",
14
+ "kind": "string"
15
+ }
16
+ ],
17
+ "resources": [],
18
+ "slots": [],
19
+ "trackable_events": [
20
+ "Form Submit"
21
+ ],
22
+ "data_attribute": "data-component=\"@amplifyup/forms:Form\""
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,85 @@
1
+ import { ReactNode, ComponentType, FC } from 'react';
2
+ import { FormField, FormValue, FormOnSuccess } from '@amplifyup/sdk';
3
+
4
+ type FieldControlProps = {
5
+ /** id for the control, so a label can point at it. */
6
+ id: string;
7
+ field: FormField;
8
+ value: FormValue;
9
+ onChange: (value: FormValue) => void;
10
+ error?: string;
11
+ };
12
+ type FieldWrapperProps = FieldControlProps & {
13
+ children: ReactNode;
14
+ };
15
+ type SubmitButtonProps = {
16
+ label: string;
17
+ submitting: boolean;
18
+ disabled: boolean;
19
+ };
20
+ type BackButtonProps = {
21
+ label: string;
22
+ onClick: () => void;
23
+ };
24
+ /** Form title and intro, step titles, and the headings and paragraphs placed between fields. */
25
+ type TextKind = 'title' | 'intro' | 'step' | 'heading' | 'paragraph';
26
+ type TextProps = {
27
+ kind: TextKind;
28
+ text: string;
29
+ };
30
+ type ErrorMessageProps = {
31
+ /** Field errors: the id the control's aria-describedby points at. */
32
+ id?: string;
33
+ message: string;
34
+ /** Set for a field error; absent for a form-level error. */
35
+ field?: FormField;
36
+ };
37
+ type SuccessMessageProps = {
38
+ message: string;
39
+ };
40
+ /** Renderers a site can swap. Submit, validation and events stay in Form. */
41
+ type FormComponents = {
42
+ Field: ComponentType<FieldWrapperProps>;
43
+ Input: ComponentType<FieldControlProps>;
44
+ Textarea: ComponentType<FieldControlProps>;
45
+ Select: ComponentType<FieldControlProps>;
46
+ /** One choice from a list (`radio`). */
47
+ Radio: ComponentType<FieldControlProps>;
48
+ /** Any number of choices (`checkboxes`); the value is a string array. */
49
+ Checkboxes: ComponentType<FieldControlProps>;
50
+ /** `checkbox` and `consent`. */
51
+ Checkbox: ComponentType<FieldControlProps>;
52
+ /** Submit, or Next on every step but the last. */
53
+ Button: ComponentType<SubmitButtonProps>;
54
+ /** Back to the previous step. */
55
+ Back: ComponentType<BackButtonProps>;
56
+ Text: ComponentType<TextProps>;
57
+ Error: ComponentType<ErrorMessageProps>;
58
+ Success: ComponentType<SuccessMessageProps>;
59
+ };
60
+ type FormErrorEvent = {
61
+ kind: 'load';
62
+ error: unknown;
63
+ } | {
64
+ kind: 'submit';
65
+ error: unknown;
66
+ } | {
67
+ kind: 'invalid';
68
+ errors: Record<string, string>;
69
+ };
70
+ type FormProps = {
71
+ /** Id of a published Amplify UP form. */
72
+ formId?: string;
73
+ /** Set when Amplify UP renders the form from a page; `formId` wins when both are given. */
74
+ fields?: Record<string, unknown>;
75
+ components?: Partial<FormComponents>;
76
+ onSuccess?: (result: FormOnSuccess) => void;
77
+ onError?: (event: FormErrorEvent) => void;
78
+ };
79
+
80
+ /** Renders and submits a form published in Amplify UP. */
81
+ declare const Form: FC<FormProps>;
82
+
83
+ declare const defaultComponents: FormComponents;
84
+
85
+ export { type BackButtonProps, type ErrorMessageProps, type FieldControlProps, type FieldWrapperProps, Form, type FormComponents, type FormErrorEvent, type FormProps, type SubmitButtonProps, type SuccessMessageProps, type TextKind, type TextProps, defaultComponents };
package/dist/index.js ADDED
@@ -0,0 +1,496 @@
1
+ 'use client';
2
+ import { useMemo, useRef, useState, useEffect } from 'react';
3
+ import { isComposerPreview, getFormDefinition, submitForm, track } from '@amplifyup/sdk';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+ import { isFieldEnvelope } from '@amplifyup/sdk/react';
6
+
7
+ // src/Form.tsx
8
+ var INPUT_TYPES = {
9
+ text: "text",
10
+ email: "email",
11
+ phone: "tel",
12
+ number: "number",
13
+ date: "date"
14
+ };
15
+ var INLINE_LABEL_TYPES = /* @__PURE__ */ new Set(["checkbox", "consent"]);
16
+ var GROUP_TYPES = /* @__PURE__ */ new Set(["radio", "checkboxes"]);
17
+ var labelId = (id) => `${id}-label`;
18
+ var errorId = (id) => `${id}-error`;
19
+ var describedBy = (id, error) => error ? errorId(id) : void 0;
20
+ var DefaultField = ({ id, field, error, children }) => {
21
+ const half = field.width === "half";
22
+ const showLabel = field.label && !INLINE_LABEL_TYPES.has(field.type);
23
+ return /* @__PURE__ */ jsxs(
24
+ "div",
25
+ {
26
+ className: half ? "aup-form__field aup-form__field--half" : "aup-form__field",
27
+ "data-field": field.key,
28
+ "data-width": half ? "half" : void 0,
29
+ "data-invalid": error ? "true" : void 0,
30
+ children: [
31
+ showLabel && GROUP_TYPES.has(field.type) ? /* @__PURE__ */ jsx("span", { className: "aup-form__label", id: labelId(id), children: field.label }) : showLabel ? /* @__PURE__ */ jsx("label", { className: "aup-form__label", htmlFor: id, children: field.label }) : null,
32
+ children
33
+ ]
34
+ }
35
+ );
36
+ };
37
+ var DefaultInput = ({ id, field, value, onChange, error }) => /* @__PURE__ */ jsx(
38
+ "input",
39
+ {
40
+ className: "aup-form__input",
41
+ id,
42
+ name: field.key,
43
+ type: INPUT_TYPES[field.type] ?? "text",
44
+ placeholder: field.placeholder,
45
+ required: field.required,
46
+ value: String(value),
47
+ "aria-invalid": error ? true : void 0,
48
+ "aria-describedby": describedBy(id, error),
49
+ onChange: (event) => onChange(event.target.value)
50
+ }
51
+ );
52
+ var DefaultTextarea = ({ id, field, value, onChange, error }) => /* @__PURE__ */ jsx(
53
+ "textarea",
54
+ {
55
+ className: "aup-form__input aup-form__textarea",
56
+ id,
57
+ name: field.key,
58
+ placeholder: field.placeholder,
59
+ required: field.required,
60
+ value: String(value),
61
+ "aria-invalid": error ? true : void 0,
62
+ "aria-describedby": describedBy(id, error),
63
+ onChange: (event) => onChange(event.target.value)
64
+ }
65
+ );
66
+ var DefaultSelect = ({ id, field, value, onChange, error }) => /* @__PURE__ */ jsxs(
67
+ "select",
68
+ {
69
+ className: "aup-form__input aup-form__select",
70
+ id,
71
+ name: field.key,
72
+ required: field.required,
73
+ value: String(value),
74
+ "aria-invalid": error ? true : void 0,
75
+ "aria-describedby": describedBy(id, error),
76
+ onChange: (event) => onChange(event.target.value),
77
+ children: [
78
+ /* @__PURE__ */ jsx("option", { value: "", children: field.placeholder ?? "" }),
79
+ (field.options ?? []).map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
80
+ ]
81
+ }
82
+ );
83
+ var DefaultCheckbox = ({ id, field, value, onChange, error }) => /* @__PURE__ */ jsxs("label", { className: "aup-form__checkbox", htmlFor: id, children: [
84
+ /* @__PURE__ */ jsx(
85
+ "input",
86
+ {
87
+ className: "aup-form__input",
88
+ id,
89
+ name: field.key,
90
+ type: "checkbox",
91
+ required: field.required,
92
+ checked: value === true,
93
+ "aria-invalid": error ? true : void 0,
94
+ "aria-describedby": describedBy(id, error),
95
+ onChange: (event) => onChange(event.target.checked)
96
+ }
97
+ ),
98
+ field.label ? /* @__PURE__ */ jsx("span", { className: "aup-form__label", children: field.label }) : null
99
+ ] });
100
+ var DefaultRadio = ({ id, field, value, onChange, error }) => /* @__PURE__ */ jsx(
101
+ "div",
102
+ {
103
+ className: "aup-form__choices",
104
+ role: "radiogroup",
105
+ "aria-labelledby": field.label ? labelId(id) : void 0,
106
+ "aria-invalid": error ? true : void 0,
107
+ "aria-describedby": describedBy(id, error),
108
+ children: (field.options ?? []).map((option, index) => /* @__PURE__ */ jsxs("label", { className: "aup-form__choice", htmlFor: `${id}-${index}`, children: [
109
+ /* @__PURE__ */ jsx(
110
+ "input",
111
+ {
112
+ className: "aup-form__input",
113
+ id: `${id}-${index}`,
114
+ name: field.key,
115
+ type: "radio",
116
+ value: option.value,
117
+ checked: value === option.value,
118
+ onChange: () => onChange(option.value)
119
+ }
120
+ ),
121
+ /* @__PURE__ */ jsx("span", { children: option.label })
122
+ ] }, option.value))
123
+ }
124
+ );
125
+ var DefaultCheckboxes = ({ id, field, value, onChange, error }) => {
126
+ const chosen = Array.isArray(value) ? value : [];
127
+ const toggle = (optionValue, checked) => onChange(checked ? [...chosen, optionValue] : chosen.filter((item) => item !== optionValue));
128
+ return /* @__PURE__ */ jsx(
129
+ "div",
130
+ {
131
+ className: "aup-form__choices",
132
+ role: "group",
133
+ "aria-labelledby": field.label ? labelId(id) : void 0,
134
+ "aria-invalid": error ? true : void 0,
135
+ "aria-describedby": describedBy(id, error),
136
+ children: (field.options ?? []).map((option, index) => /* @__PURE__ */ jsxs("label", { className: "aup-form__choice", htmlFor: `${id}-${index}`, children: [
137
+ /* @__PURE__ */ jsx(
138
+ "input",
139
+ {
140
+ className: "aup-form__input",
141
+ id: `${id}-${index}`,
142
+ name: field.key,
143
+ type: "checkbox",
144
+ value: option.value,
145
+ checked: chosen.includes(option.value),
146
+ onChange: (event) => toggle(option.value, event.target.checked)
147
+ }
148
+ ),
149
+ /* @__PURE__ */ jsx("span", { children: option.label })
150
+ ] }, option.value))
151
+ }
152
+ );
153
+ };
154
+ var DefaultBack = ({ label, onClick }) => /* @__PURE__ */ jsx("button", { className: "aup-form__back", type: "button", onClick, children: label });
155
+ var TEXT_TAGS = { title: "h2", step: "h3", heading: "h3", intro: "p", paragraph: "p" };
156
+ var DefaultText = ({ kind, text }) => {
157
+ const Tag = TEXT_TAGS[kind];
158
+ return /* @__PURE__ */ jsx(Tag, { className: `aup-form__${kind}`, children: text });
159
+ };
160
+ var DefaultButton = ({ label, submitting, disabled }) => /* @__PURE__ */ jsx("button", { className: "aup-form__submit", type: "submit", disabled, "aria-busy": submitting || void 0, children: label });
161
+ var DefaultError = ({ id, message }) => /* @__PURE__ */ jsx("p", { className: "aup-form__error", id, role: "alert", children: message });
162
+ var DefaultSuccess = ({ message }) => /* @__PURE__ */ jsx("div", { className: "aup-form__success", role: "status", children: message });
163
+ var defaultComponents = {
164
+ Field: DefaultField,
165
+ Input: DefaultInput,
166
+ Textarea: DefaultTextarea,
167
+ Select: DefaultSelect,
168
+ Radio: DefaultRadio,
169
+ Checkboxes: DefaultCheckboxes,
170
+ Checkbox: DefaultCheckbox,
171
+ Button: DefaultButton,
172
+ Back: DefaultBack,
173
+ Text: DefaultText,
174
+ Error: DefaultError,
175
+ Success: DefaultSuccess
176
+ };
177
+ var SUPPORTED_TYPES = /* @__PURE__ */ new Set([
178
+ "text",
179
+ "email",
180
+ "phone",
181
+ "number",
182
+ "date",
183
+ "textarea",
184
+ "select",
185
+ "radio",
186
+ "checkboxes",
187
+ "checkbox",
188
+ "consent",
189
+ "hidden"
190
+ ]);
191
+ var warnedTypes = /* @__PURE__ */ new Set();
192
+ var isProduction = () => typeof process !== "undefined" && process.env?.NODE_ENV === "production";
193
+ var warnUnknownType = (field) => {
194
+ if (isProduction() || warnedTypes.has(field.type)) return;
195
+ warnedTypes.add(field.type);
196
+ console.warn(`[Amplify UP] Form field "${field.key}" has unsupported type "${field.type}" and was not rendered.`);
197
+ };
198
+ var controlFor = (field, components) => {
199
+ if (field.type === "textarea") return components.Textarea;
200
+ if (field.type === "select") return components.Select;
201
+ if (field.type === "radio") return components.Radio;
202
+ if (field.type === "checkboxes") return components.Checkboxes;
203
+ if (field.type === "checkbox" || field.type === "consent") return components.Checkbox;
204
+ return components.Input;
205
+ };
206
+ var FormFieldRow = ({
207
+ formId,
208
+ field,
209
+ value,
210
+ error,
211
+ components,
212
+ onChange
213
+ }) => {
214
+ if (!SUPPORTED_TYPES.has(field.type)) {
215
+ warnUnknownType(field);
216
+ return null;
217
+ }
218
+ if (field.type === "hidden") {
219
+ return /* @__PURE__ */ jsx("input", { type: "hidden", name: field.key, value: String(value) });
220
+ }
221
+ const id = `aup-form-${formId}-${field.key}`;
222
+ const controlProps = {
223
+ id,
224
+ field,
225
+ value,
226
+ error,
227
+ onChange: (next) => onChange(field.key, next)
228
+ };
229
+ const Control = controlFor(field, components);
230
+ const { Field, Error: ErrorMessage } = components;
231
+ return /* @__PURE__ */ jsxs(Field, { ...controlProps, children: [
232
+ /* @__PURE__ */ jsx(Control, { ...controlProps }),
233
+ error ? /* @__PURE__ */ jsx(ErrorMessage, { id: errorId(id), message: error, field }) : null
234
+ ] });
235
+ };
236
+ var fieldString = (fields, name) => {
237
+ const field = fields?.[name];
238
+ if (!isFieldEnvelope(field)) return void 0;
239
+ return typeof field.value === "string" && field.value.trim() ? field.value.trim() : void 0;
240
+ };
241
+
242
+ // src/steps.ts
243
+ var formSteps = (definition) => definition.steps?.length ? definition.steps : [{ items: definition.fields.map((field) => ({ kind: "field", key: field.key })) }];
244
+ var visibleFieldsOf = (step, fieldsByKey) => step.items.flatMap((item) => {
245
+ if (item.kind !== "field") return [];
246
+ const field = fieldsByKey.get(item.key);
247
+ return field && field.type !== "hidden" ? [field] : [];
248
+ });
249
+ var firstStepWithError = (steps, fieldsByKey, errors) => {
250
+ const index = steps.findIndex((step) => visibleFieldsOf(step, fieldsByKey).some((field) => errors[field.key]));
251
+ return index < 0 ? steps.length - 1 : index;
252
+ };
253
+ var useFormDefinition = (formId) => {
254
+ const [state, setState] = useState(formId ? { status: "loading" } : { status: "missing" });
255
+ useEffect(() => {
256
+ if (!formId) {
257
+ setState({ status: "missing" });
258
+ return;
259
+ }
260
+ let active = true;
261
+ setState({ status: "loading" });
262
+ getFormDefinition(formId).then((definition) => {
263
+ if (active) setState({ status: "ready", definition });
264
+ }).catch((error) => {
265
+ if (active) setState({ status: "failed", error });
266
+ });
267
+ return () => {
268
+ active = false;
269
+ };
270
+ }, [formId]);
271
+ return state;
272
+ };
273
+
274
+ // src/validate.ts
275
+ var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
276
+ var PHONE = /^\+?[0-9\s().-]{7,}$/;
277
+ var DATE = /^\d{4}-\d{2}-\d{2}$/;
278
+ var BOOLEAN_TYPES = /* @__PURE__ */ new Set(["checkbox", "consent"]);
279
+ var isBlank = (value) => value === void 0 || value === false || Array.isArray(value) && value.length === 0 || typeof value === "string" && value.trim() === "";
280
+ var allowed = (field) => new Set((field.options ?? []).map((option) => option.value));
281
+ var textError = (field, value) => {
282
+ const trimmed = value.trim();
283
+ if (field.type === "email" && !EMAIL.test(trimmed)) return "Enter a valid email address.";
284
+ if (field.type === "phone" && !PHONE.test(trimmed)) return "Enter a valid phone number.";
285
+ if (field.type === "number" && !Number.isFinite(Number(trimmed))) return "Enter a number.";
286
+ if (field.type === "date" && !DATE.test(trimmed)) return "Enter a date.";
287
+ if ((field.type === "select" || field.type === "radio") && !allowed(field).has(value)) return "Choose an option.";
288
+ return null;
289
+ };
290
+ var fieldError = (field, value) => {
291
+ if (isBlank(value)) {
292
+ if (!field.required) return null;
293
+ return field.type === "consent" ? "Please tick this box to continue." : "This field is required.";
294
+ }
295
+ if (Array.isArray(value)) {
296
+ const options = allowed(field);
297
+ return value.every((item) => options.has(item)) ? null : "Choose from the options.";
298
+ }
299
+ return typeof value === "string" ? textError(field, value) : null;
300
+ };
301
+ var validateValues = (fields, values) => {
302
+ const errors = {};
303
+ for (const field of fields) {
304
+ if (field.type === "hidden") continue;
305
+ const error = fieldError(field, values[field.key]);
306
+ if (error) errors[field.key] = error;
307
+ }
308
+ return errors;
309
+ };
310
+ var fromQuery = (field) => {
311
+ if (!field.fromQuery || typeof window === "undefined") return null;
312
+ return new URLSearchParams(window.location.search).get(field.fromQuery);
313
+ };
314
+ var initialValue = (field) => {
315
+ if (BOOLEAN_TYPES.has(field.type)) return false;
316
+ if (field.type === "checkboxes") return [];
317
+ return fromQuery(field) ?? field.defaultValue ?? "";
318
+ };
319
+ var initialValues = (fields) => Object.fromEntries(fields.map((field) => [field.key, initialValue(field)]));
320
+ var LOAD_FAILED = "This form isn\u2019t available right now.";
321
+ var SUBMIT_FAILED = "Something went wrong. Please try again.";
322
+ var CHOOSE_FORM = "Choose a form to show here.";
323
+ var NEXT_LABEL = "Next";
324
+ var BACK_LABEL = "Back";
325
+ var HONEYPOT_STYLE = {
326
+ position: "absolute",
327
+ left: "-10000px",
328
+ width: "1px",
329
+ height: "1px",
330
+ overflow: "hidden"
331
+ };
332
+ var LoadedForm = ({ formId, definition, components, onSuccess, onError }) => {
333
+ const [values, setValues] = useState(() => initialValues(definition.fields));
334
+ const [honeypot, setHoneypot] = useState("");
335
+ const [errors, setErrors] = useState({});
336
+ const [formError, setFormError] = useState(null);
337
+ const [successMessage, setSuccessMessage] = useState(null);
338
+ const [submitting, setSubmitting] = useState(false);
339
+ const [stepIndex, setStepIndex] = useState(0);
340
+ const inFlight = useRef(false);
341
+ const preview = useMemo(() => isComposerPreview(), []);
342
+ const steps = useMemo(() => formSteps(definition), [definition]);
343
+ const fieldsByKey = useMemo(() => new Map(definition.fields.map((field) => [field.key, field])), [definition]);
344
+ const hiddenFields = definition.fields.filter((field) => field.type === "hidden");
345
+ const step = steps[stepIndex] ?? steps[0];
346
+ const isLastStep = stepIndex >= steps.length - 1;
347
+ const setValue = (key, value) => {
348
+ setValues((current) => ({ ...current, [key]: value }));
349
+ setErrors(({ [key]: _cleared, ...rest }) => rest);
350
+ };
351
+ const applySuccess = (result) => {
352
+ track("Form Submit", {
353
+ formId: definition.id,
354
+ formKey: definition.key,
355
+ formVersion: definition.version
356
+ });
357
+ if (result.type === "redirect") {
358
+ window.location.assign(result.url);
359
+ } else {
360
+ setSuccessMessage(result.message);
361
+ }
362
+ onSuccess?.(result);
363
+ };
364
+ const goToStep = (index) => {
365
+ setStepIndex(index);
366
+ setFormError(null);
367
+ };
368
+ const handleSubmit = async (event) => {
369
+ event.preventDefault();
370
+ if (!isLastStep) {
371
+ const stepErrors = validateValues(visibleFieldsOf(step, fieldsByKey), values);
372
+ setErrors(stepErrors);
373
+ if (Object.keys(stepErrors).length === 0) goToStep(stepIndex + 1);
374
+ return;
375
+ }
376
+ if (preview || inFlight.current) return;
377
+ const clientErrors = validateValues(definition.fields, values);
378
+ setErrors(clientErrors);
379
+ setFormError(null);
380
+ if (Object.keys(clientErrors).length > 0) {
381
+ setStepIndex(firstStepWithError(steps, fieldsByKey, clientErrors));
382
+ return;
383
+ }
384
+ inFlight.current = true;
385
+ setSubmitting(true);
386
+ try {
387
+ const result = await submitForm(formId, { ...values, [definition.honeypot.name]: honeypot });
388
+ if (result.ok) {
389
+ applySuccess(result.onSuccess);
390
+ } else {
391
+ setErrors(result.errors);
392
+ setStepIndex(firstStepWithError(steps, fieldsByKey, result.errors));
393
+ onError?.({ kind: "invalid", errors: result.errors });
394
+ }
395
+ } catch (error) {
396
+ setFormError(SUBMIT_FAILED);
397
+ onError?.({ kind: "submit", error });
398
+ } finally {
399
+ inFlight.current = false;
400
+ setSubmitting(false);
401
+ }
402
+ };
403
+ const { Back, Button, Error: ErrorMessage, Success, Text } = components;
404
+ if (successMessage !== null) {
405
+ return /* @__PURE__ */ jsx(Success, { message: successMessage });
406
+ }
407
+ return /* @__PURE__ */ jsxs(
408
+ "form",
409
+ {
410
+ className: "aup-form",
411
+ "data-form-key": definition.key,
412
+ "data-step": steps.length > 1 ? stepIndex + 1 : void 0,
413
+ noValidate: true,
414
+ onSubmit: handleSubmit,
415
+ children: [
416
+ definition.title ? /* @__PURE__ */ jsx(Text, { kind: "title", text: definition.title }) : null,
417
+ definition.intro ? /* @__PURE__ */ jsx(Text, { kind: "intro", text: definition.intro }) : null,
418
+ step.title ? /* @__PURE__ */ jsx(Text, { kind: "step", text: step.title }) : null,
419
+ step.items.map((item, index) => {
420
+ if (item.kind !== "field") return /* @__PURE__ */ jsx(Text, { kind: item.kind, text: item.text }, `text-${index}`);
421
+ const field = fieldsByKey.get(item.key);
422
+ if (!field || field.type === "hidden") return null;
423
+ return /* @__PURE__ */ jsx(
424
+ FormFieldRow,
425
+ {
426
+ formId,
427
+ field,
428
+ value: values[field.key] ?? "",
429
+ error: errors[field.key],
430
+ components,
431
+ onChange: setValue
432
+ },
433
+ field.key
434
+ );
435
+ }),
436
+ hiddenFields.map((field) => /* @__PURE__ */ jsx(
437
+ FormFieldRow,
438
+ {
439
+ formId,
440
+ field,
441
+ value: values[field.key] ?? "",
442
+ components,
443
+ onChange: setValue
444
+ },
445
+ field.key
446
+ )),
447
+ /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: HONEYPOT_STYLE, children: /* @__PURE__ */ jsx(
448
+ "input",
449
+ {
450
+ type: "text",
451
+ name: definition.honeypot.name,
452
+ tabIndex: -1,
453
+ autoComplete: "off",
454
+ value: honeypot,
455
+ onChange: (event) => setHoneypot(event.target.value)
456
+ }
457
+ ) }),
458
+ formError ? /* @__PURE__ */ jsx(ErrorMessage, { message: formError }) : null,
459
+ stepIndex > 0 ? /* @__PURE__ */ jsx(Back, { label: BACK_LABEL, onClick: () => goToStep(stepIndex - 1) }) : null,
460
+ isLastStep ? /* @__PURE__ */ jsx(Button, { label: definition.submit.label, submitting, disabled: submitting || preview }) : /* @__PURE__ */ jsx(Button, { label: NEXT_LABEL, submitting: false, disabled: false })
461
+ ]
462
+ }
463
+ );
464
+ };
465
+ var Form = ({ formId, fields, components, onSuccess, onError }) => {
466
+ const resolvedId = formId ?? fieldString(fields, "formId");
467
+ const state = useFormDefinition(resolvedId);
468
+ const merged = useMemo(() => ({ ...defaultComponents, ...components }), [components]);
469
+ const reported = useRef(null);
470
+ if (state.status === "missing") {
471
+ return isComposerPreview() ? /* @__PURE__ */ jsx(merged.Error, { message: CHOOSE_FORM }) : null;
472
+ }
473
+ if (state.status === "loading" || !resolvedId) return null;
474
+ if (state.status === "failed") {
475
+ if (reported.current !== state.error) {
476
+ reported.current = state.error;
477
+ onError?.({ kind: "load", error: state.error });
478
+ }
479
+ return /* @__PURE__ */ jsx(merged.Error, { message: LOAD_FAILED });
480
+ }
481
+ return /* @__PURE__ */ jsx(
482
+ LoadedForm,
483
+ {
484
+ formId: resolvedId,
485
+ definition: state.definition,
486
+ components: merged,
487
+ onSuccess,
488
+ onError
489
+ },
490
+ `${state.definition.id}:${state.definition.version}`
491
+ );
492
+ };
493
+
494
+ export { Form, defaultComponents };
495
+ //# sourceMappingURL=index.js.map
496
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/defaults.tsx","../src/FormFieldRow.tsx","../src/registry-props.ts","../src/steps.ts","../src/use-form-definition.ts","../src/validate.ts","../src/Form.tsx"],"names":["jsx","jsxs","useState"],"mappings":"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","file":"index.js","sourcesContent":["import type { FC } from 'react';\r\nimport type {\r\n BackButtonProps,\r\n ErrorMessageProps,\r\n FieldControlProps,\r\n FieldWrapperProps,\r\n FormComponents,\r\n SubmitButtonProps,\r\n SuccessMessageProps,\r\n TextProps,\r\n} from './types';\r\n\r\nconst INPUT_TYPES: Record<string, string> = {\r\n text: 'text',\r\n email: 'email',\r\n phone: 'tel',\r\n number: 'number',\r\n date: 'date',\r\n};\r\n\r\n/** Checkbox-style fields carry their label beside the box, not above it. */\r\nconst INLINE_LABEL_TYPES = new Set(['checkbox', 'consent']);\r\n\r\n/** Choice groups are labelled by their legend-like label, not a `for` target. */\r\nconst GROUP_TYPES = new Set(['radio', 'checkboxes']);\r\n\r\nexport const labelId = (id: string) => `${id}-label`;\r\n\r\nexport const errorId = (id: string) => `${id}-error`;\r\n\r\nconst describedBy = (id: string, error?: string) => (error ? errorId(id) : undefined);\r\n\r\nconst DefaultField: FC<FieldWrapperProps> = ({ id, field, error, children }) => {\r\n const half = field.width === 'half';\r\n const showLabel = field.label && !INLINE_LABEL_TYPES.has(field.type);\r\n\r\n return (\r\n <div\r\n className={half ? 'aup-form__field aup-form__field--half' : 'aup-form__field'}\r\n data-field={field.key}\r\n data-width={half ? 'half' : undefined}\r\n data-invalid={error ? 'true' : undefined}\r\n >\r\n {showLabel && GROUP_TYPES.has(field.type) ? (\r\n <span className=\"aup-form__label\" id={labelId(id)}>\r\n {field.label}\r\n </span>\r\n ) : showLabel ? (\r\n <label className=\"aup-form__label\" htmlFor={id}>\r\n {field.label}\r\n </label>\r\n ) : null}\r\n {children}\r\n </div>\r\n );\r\n};\r\n\r\nconst DefaultInput: FC<FieldControlProps> = ({ id, field, value, onChange, error }) => (\r\n <input\r\n className=\"aup-form__input\"\r\n id={id}\r\n name={field.key}\r\n type={INPUT_TYPES[field.type] ?? 'text'}\r\n placeholder={field.placeholder}\r\n required={field.required}\r\n value={String(value)}\r\n aria-invalid={error ? true : undefined}\r\n aria-describedby={describedBy(id, error)}\r\n onChange={(event) => onChange(event.target.value)}\r\n />\r\n);\r\n\r\nconst DefaultTextarea: FC<FieldControlProps> = ({ id, field, value, onChange, error }) => (\r\n <textarea\r\n className=\"aup-form__input aup-form__textarea\"\r\n id={id}\r\n name={field.key}\r\n placeholder={field.placeholder}\r\n required={field.required}\r\n value={String(value)}\r\n aria-invalid={error ? true : undefined}\r\n aria-describedby={describedBy(id, error)}\r\n onChange={(event) => onChange(event.target.value)}\r\n />\r\n);\r\n\r\nconst DefaultSelect: FC<FieldControlProps> = ({ id, field, value, onChange, error }) => (\r\n <select\r\n className=\"aup-form__input aup-form__select\"\r\n id={id}\r\n name={field.key}\r\n required={field.required}\r\n value={String(value)}\r\n aria-invalid={error ? true : undefined}\r\n aria-describedby={describedBy(id, error)}\r\n onChange={(event) => onChange(event.target.value)}\r\n >\r\n <option value=\"\">{field.placeholder ?? ''}</option>\r\n {(field.options ?? []).map((option) => (\r\n <option key={option.value} value={option.value}>\r\n {option.label}\r\n </option>\r\n ))}\r\n </select>\r\n);\r\n\r\nconst DefaultCheckbox: FC<FieldControlProps> = ({ id, field, value, onChange, error }) => (\r\n <label className=\"aup-form__checkbox\" htmlFor={id}>\r\n <input\r\n className=\"aup-form__input\"\r\n id={id}\r\n name={field.key}\r\n type=\"checkbox\"\r\n required={field.required}\r\n checked={value === true}\r\n aria-invalid={error ? true : undefined}\r\n aria-describedby={describedBy(id, error)}\r\n onChange={(event) => onChange(event.target.checked)}\r\n />\r\n {field.label ? <span className=\"aup-form__label\">{field.label}</span> : null}\r\n </label>\r\n);\r\n\r\nconst DefaultRadio: FC<FieldControlProps> = ({ id, field, value, onChange, error }) => (\r\n <div\r\n className=\"aup-form__choices\"\r\n role=\"radiogroup\"\r\n aria-labelledby={field.label ? labelId(id) : undefined}\r\n aria-invalid={error ? true : undefined}\r\n aria-describedby={describedBy(id, error)}\r\n >\r\n {(field.options ?? []).map((option, index) => (\r\n <label key={option.value} className=\"aup-form__choice\" htmlFor={`${id}-${index}`}>\r\n <input\r\n className=\"aup-form__input\"\r\n id={`${id}-${index}`}\r\n name={field.key}\r\n type=\"radio\"\r\n value={option.value}\r\n checked={value === option.value}\r\n onChange={() => onChange(option.value)}\r\n />\r\n <span>{option.label}</span>\r\n </label>\r\n ))}\r\n </div>\r\n);\r\n\r\nconst DefaultCheckboxes: FC<FieldControlProps> = ({ id, field, value, onChange, error }) => {\r\n const chosen = Array.isArray(value) ? value : [];\r\n const toggle = (optionValue: string, checked: boolean) =>\r\n onChange(checked ? [...chosen, optionValue] : chosen.filter((item) => item !== optionValue));\r\n\r\n return (\r\n <div\r\n className=\"aup-form__choices\"\r\n role=\"group\"\r\n aria-labelledby={field.label ? labelId(id) : undefined}\r\n aria-invalid={error ? true : undefined}\r\n aria-describedby={describedBy(id, error)}\r\n >\r\n {(field.options ?? []).map((option, index) => (\r\n <label key={option.value} className=\"aup-form__choice\" htmlFor={`${id}-${index}`}>\r\n <input\r\n className=\"aup-form__input\"\r\n id={`${id}-${index}`}\r\n name={field.key}\r\n type=\"checkbox\"\r\n value={option.value}\r\n checked={chosen.includes(option.value)}\r\n onChange={(event) => toggle(option.value, event.target.checked)}\r\n />\r\n <span>{option.label}</span>\r\n </label>\r\n ))}\r\n </div>\r\n );\r\n};\r\n\r\nconst DefaultBack: FC<BackButtonProps> = ({ label, onClick }) => (\r\n <button className=\"aup-form__back\" type=\"button\" onClick={onClick}>\r\n {label}\r\n </button>\r\n);\r\n\r\nconst TEXT_TAGS = { title: 'h2', step: 'h3', heading: 'h3', intro: 'p', paragraph: 'p' } as const;\r\n\r\nconst DefaultText: FC<TextProps> = ({ kind, text }) => {\r\n const Tag = TEXT_TAGS[kind];\r\n return <Tag className={`aup-form__${kind}`}>{text}</Tag>;\r\n};\r\n\r\nconst DefaultButton: FC<SubmitButtonProps> = ({ label, submitting, disabled }) => (\r\n <button className=\"aup-form__submit\" type=\"submit\" disabled={disabled} aria-busy={submitting || undefined}>\r\n {label}\r\n </button>\r\n);\r\n\r\nconst DefaultError: FC<ErrorMessageProps> = ({ id, message }) => (\r\n <p className=\"aup-form__error\" id={id} role=\"alert\">\r\n {message}\r\n </p>\r\n);\r\n\r\nconst DefaultSuccess: FC<SuccessMessageProps> = ({ message }) => (\r\n <div className=\"aup-form__success\" role=\"status\">\r\n {message}\r\n </div>\r\n);\r\n\r\nexport const defaultComponents: FormComponents = {\r\n Field: DefaultField,\r\n Input: DefaultInput,\r\n Textarea: DefaultTextarea,\r\n Select: DefaultSelect,\r\n Radio: DefaultRadio,\r\n Checkboxes: DefaultCheckboxes,\r\n Checkbox: DefaultCheckbox,\r\n Button: DefaultButton,\r\n Back: DefaultBack,\r\n Text: DefaultText,\r\n Error: DefaultError,\r\n Success: DefaultSuccess,\r\n};\r\n","import type { FC } from 'react';\r\nimport type { FormField, FormValue } from '@amplifyup/sdk';\r\nimport { errorId } from './defaults';\r\nimport type { FieldControlProps, FormComponents } from './types';\r\n\r\nconst SUPPORTED_TYPES = new Set([\r\n 'text',\r\n 'email',\r\n 'phone',\r\n 'number',\r\n 'date',\r\n 'textarea',\r\n 'select',\r\n 'radio',\r\n 'checkboxes',\r\n 'checkbox',\r\n 'consent',\r\n 'hidden',\r\n]);\r\n\r\nconst warnedTypes = new Set<string>();\r\n\r\nconst isProduction = () => typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';\r\n\r\nconst warnUnknownType = (field: FormField) => {\r\n if (isProduction() || warnedTypes.has(field.type)) return;\r\n\r\n warnedTypes.add(field.type);\r\n console.warn(`[Amplify UP] Form field \"${field.key}\" has unsupported type \"${field.type}\" and was not rendered.`);\r\n};\r\n\r\nconst controlFor = (field: FormField, components: FormComponents) => {\r\n if (field.type === 'textarea') return components.Textarea;\r\n if (field.type === 'select') return components.Select;\r\n if (field.type === 'radio') return components.Radio;\r\n if (field.type === 'checkboxes') return components.Checkboxes;\r\n if (field.type === 'checkbox' || field.type === 'consent') return components.Checkbox;\r\n return components.Input;\r\n};\r\n\r\ntype FormFieldRowProps = {\r\n formId: string;\r\n field: FormField;\r\n value: FormValue;\r\n error?: string;\r\n components: FormComponents;\r\n onChange: (key: string, value: FormValue) => void;\r\n};\r\n\r\nexport const FormFieldRow: FC<FormFieldRowProps> = ({\r\n formId,\r\n field,\r\n value,\r\n error,\r\n components,\r\n onChange,\r\n}) => {\r\n if (!SUPPORTED_TYPES.has(field.type)) {\r\n warnUnknownType(field);\r\n return null;\r\n }\r\n\r\n if (field.type === 'hidden') {\r\n return <input type=\"hidden\" name={field.key} value={String(value)} />;\r\n }\r\n\r\n const id = `aup-form-${formId}-${field.key}`;\r\n const controlProps: FieldControlProps = {\r\n id,\r\n field,\r\n value,\r\n error,\r\n onChange: (next) => onChange(field.key, next),\r\n };\r\n\r\n const Control = controlFor(field, components);\r\n const { Field, Error: ErrorMessage } = components;\r\n\r\n return (\r\n <Field {...controlProps}>\r\n <Control {...controlProps} />\r\n {error ? <ErrorMessage id={errorId(id)} message={error} field={field} /> : null}\r\n </Field>\r\n );\r\n};\r\n","import { isFieldEnvelope } from '@amplifyup/sdk/react';\n\n/** A string setting from the field envelope Amplify UP passes to registry components. */\nexport const fieldString = (fields: Record<string, unknown> | undefined, name: string): string | undefined => {\n const field = fields?.[name];\n if (!isFieldEnvelope(field)) return undefined;\n\n return typeof field.value === 'string' && field.value.trim() ? field.value.trim() : undefined;\n};\n","import type { FormDefinition, FormField } from '@amplifyup/sdk';\r\n\r\ntype Step = NonNullable<FormDefinition['steps']>[number];\r\n\r\n/** The form's steps; a definition without them is one step with every field in order. */\r\nexport const formSteps = (definition: FormDefinition): Step[] =>\r\n definition.steps?.length\r\n ? definition.steps\r\n : [{ items: definition.fields.map((field) => ({ kind: 'field' as const, key: field.key })) }];\r\n\r\n/** Fields shown on a step, hidden ones excluded. */\r\nexport const visibleFieldsOf = (step: Step, fieldsByKey: Map<string, FormField>): FormField[] =>\r\n step.items.flatMap((item) => {\r\n if (item.kind !== 'field') return [];\r\n const field = fieldsByKey.get(item.key);\r\n return field && field.type !== 'hidden' ? [field] : [];\r\n });\r\n\r\n/** First step with an error, so a rejected submit lands where the visitor can fix it. */\r\nexport const firstStepWithError = (\r\n steps: Step[],\r\n fieldsByKey: Map<string, FormField>,\r\n errors: Record<string, string>\r\n): number => {\r\n const index = steps.findIndex((step) => visibleFieldsOf(step, fieldsByKey).some((field) => errors[field.key]));\r\n return index < 0 ? steps.length - 1 : index;\r\n};\r\n","import { useEffect, useState } from 'react';\nimport { getFormDefinition, type FormDefinition } from '@amplifyup/sdk';\n\nexport type DefinitionState =\n | { status: 'missing' }\n | { status: 'loading' }\n | { status: 'ready'; definition: FormDefinition }\n | { status: 'failed'; error: unknown };\n\n/** Loads the published definition for a form; reloads when formId changes. */\nexport const useFormDefinition = (formId: string | undefined): DefinitionState => {\n const [state, setState] = useState<DefinitionState>(formId ? { status: 'loading' } : { status: 'missing' });\n\n useEffect(() => {\n if (!formId) {\n setState({ status: 'missing' });\n return;\n }\n\n let active = true;\n setState({ status: 'loading' });\n\n getFormDefinition(formId)\n .then((definition) => {\n if (active) setState({ status: 'ready', definition });\n })\n .catch((error: unknown) => {\n if (active) setState({ status: 'failed', error });\n });\n\n return () => {\n active = false;\n };\n }, [formId]);\n\n return state;\n};\n","import type { FormField, FormValue } from '@amplifyup/sdk';\r\n\r\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\r\nconst PHONE = /^\\+?[0-9\\s().-]{7,}$/;\r\nconst DATE = /^\\d{4}-\\d{2}-\\d{2}$/;\r\n\r\nconst BOOLEAN_TYPES = new Set(['checkbox', 'consent']);\r\n\r\nconst isBlank = (value: FormValue | undefined): boolean =>\r\n value === undefined ||\r\n value === false ||\r\n (Array.isArray(value) && value.length === 0) ||\r\n (typeof value === 'string' && value.trim() === '');\r\n\r\nconst allowed = (field: FormField): Set<string> => new Set((field.options ?? []).map((option) => option.value));\r\n\r\nconst textError = (field: FormField, value: string): string | null => {\r\n const trimmed = value.trim();\r\n\r\n if (field.type === 'email' && !EMAIL.test(trimmed)) return 'Enter a valid email address.';\r\n if (field.type === 'phone' && !PHONE.test(trimmed)) return 'Enter a valid phone number.';\r\n if (field.type === 'number' && !Number.isFinite(Number(trimmed))) return 'Enter a number.';\r\n if (field.type === 'date' && !DATE.test(trimmed)) return 'Enter a date.';\r\n if ((field.type === 'select' || field.type === 'radio') && !allowed(field).has(value)) return 'Choose an option.';\r\n return null;\r\n};\r\n\r\nconst fieldError = (field: FormField, value: FormValue | undefined): string | null => {\r\n if (isBlank(value)) {\r\n if (!field.required) return null;\r\n return field.type === 'consent' ? 'Please tick this box to continue.' : 'This field is required.';\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n const options = allowed(field);\r\n return value.every((item) => options.has(item)) ? null : 'Choose from the options.';\r\n }\r\n return typeof value === 'string' ? textError(field, value) : null;\r\n};\r\n\r\n/** Client-side feedback only; the server's validation is authoritative. */\r\nexport const validateValues = (\r\n fields: FormField[],\r\n values: Record<string, FormValue>\r\n): Record<string, string> => {\r\n const errors: Record<string, string> = {};\r\n\r\n for (const field of fields) {\r\n if (field.type === 'hidden') continue;\r\n const error = fieldError(field, values[field.key]);\r\n if (error) errors[field.key] = error;\r\n }\r\n\r\n return errors;\r\n};\r\n\r\n/** A UTM value from the current page address, when the page has one. */\r\nconst fromQuery = (field: FormField): string | null => {\r\n if (!field.fromQuery || typeof window === 'undefined') return null;\r\n return new URLSearchParams(window.location.search).get(field.fromQuery);\r\n};\r\n\r\nconst initialValue = (field: FormField): FormValue => {\r\n if (BOOLEAN_TYPES.has(field.type)) return false;\r\n if (field.type === 'checkboxes') return [];\r\n return fromQuery(field) ?? field.defaultValue ?? '';\r\n};\r\n\r\nexport const initialValues = (fields: FormField[]): Record<string, FormValue> =>\r\n Object.fromEntries(fields.map((field) => [field.key, initialValue(field)]));\r\n","import { useMemo, useRef, useState, type CSSProperties, type FC, type FormEvent } from 'react';\r\nimport {\r\n isComposerPreview,\r\n submitForm,\r\n track,\r\n type FormDefinition,\r\n type FormOnSuccess,\r\n type FormValue,\r\n} from '@amplifyup/sdk';\r\nimport { defaultComponents } from './defaults';\r\nimport { FormFieldRow } from './FormFieldRow';\r\nimport { fieldString } from './registry-props';\r\nimport { firstStepWithError, formSteps, visibleFieldsOf } from './steps';\r\nimport type { FormComponents, FormProps } from './types';\r\nimport { useFormDefinition } from './use-form-definition';\r\nimport { initialValues, validateValues } from './validate';\r\n\r\nconst LOAD_FAILED = 'This form isn’t available right now.';\r\nconst SUBMIT_FAILED = 'Something went wrong. Please try again.';\r\nconst CHOOSE_FORM = 'Choose a form to show here.';\r\nconst NEXT_LABEL = 'Next';\r\nconst BACK_LABEL = 'Back';\r\n\r\n/** Off-screen and out of the tab order, so only bots fill it in. */\r\nconst HONEYPOT_STYLE: CSSProperties = {\r\n position: 'absolute',\r\n left: '-10000px',\r\n width: '1px',\r\n height: '1px',\r\n overflow: 'hidden',\r\n};\r\n\r\ntype LoadedFormProps = Omit<FormProps, 'components' | 'fields' | 'formId'> & {\r\n formId: string;\r\n definition: FormDefinition;\r\n components: FormComponents;\r\n};\r\n\r\nconst LoadedForm: FC<LoadedFormProps> = ({ formId, definition, components, onSuccess, onError }) => {\r\n const [values, setValues] = useState(() => initialValues(definition.fields));\r\n const [honeypot, setHoneypot] = useState('');\r\n const [errors, setErrors] = useState<Record<string, string>>({});\r\n const [formError, setFormError] = useState<string | null>(null);\r\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\r\n const [submitting, setSubmitting] = useState(false);\r\n const [stepIndex, setStepIndex] = useState(0);\r\n const inFlight = useRef(false);\r\n const preview = useMemo(() => isComposerPreview(), []);\r\n\r\n const steps = useMemo(() => formSteps(definition), [definition]);\r\n const fieldsByKey = useMemo(() => new Map(definition.fields.map((field) => [field.key, field])), [definition]);\r\n const hiddenFields = definition.fields.filter((field) => field.type === 'hidden');\r\n const step = steps[stepIndex] ?? steps[0]!;\r\n const isLastStep = stepIndex >= steps.length - 1;\r\n\r\n const setValue = (key: string, value: FormValue) => {\r\n setValues((current) => ({ ...current, [key]: value }));\r\n setErrors(({ [key]: _cleared, ...rest }) => rest);\r\n };\r\n\r\n const applySuccess = (result: FormOnSuccess) => {\r\n track('Form Submit', {\r\n formId: definition.id,\r\n formKey: definition.key,\r\n formVersion: definition.version,\r\n });\r\n\r\n if (result.type === 'redirect') {\r\n window.location.assign(result.url);\r\n } else {\r\n setSuccessMessage(result.message);\r\n }\r\n onSuccess?.(result);\r\n };\r\n\r\n const goToStep = (index: number) => {\r\n setStepIndex(index);\r\n setFormError(null);\r\n };\r\n\r\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\r\n event.preventDefault();\r\n\r\n if (!isLastStep) {\r\n const stepErrors = validateValues(visibleFieldsOf(step, fieldsByKey), values);\r\n setErrors(stepErrors);\r\n if (Object.keys(stepErrors).length === 0) goToStep(stepIndex + 1);\r\n return;\r\n }\r\n if (preview || inFlight.current) return;\r\n\r\n const clientErrors = validateValues(definition.fields, values);\r\n setErrors(clientErrors);\r\n setFormError(null);\r\n if (Object.keys(clientErrors).length > 0) {\r\n setStepIndex(firstStepWithError(steps, fieldsByKey, clientErrors));\r\n return;\r\n }\r\n\r\n inFlight.current = true;\r\n setSubmitting(true);\r\n\r\n try {\r\n const result = await submitForm(formId, { ...values, [definition.honeypot.name]: honeypot });\r\n if (result.ok) {\r\n applySuccess(result.onSuccess);\r\n } else {\r\n setErrors(result.errors);\r\n setStepIndex(firstStepWithError(steps, fieldsByKey, result.errors));\r\n onError?.({ kind: 'invalid', errors: result.errors });\r\n }\r\n } catch (error) {\r\n setFormError(SUBMIT_FAILED);\r\n onError?.({ kind: 'submit', error });\r\n } finally {\r\n inFlight.current = false;\r\n setSubmitting(false);\r\n }\r\n };\r\n\r\n const { Back, Button, Error: ErrorMessage, Success, Text } = components;\r\n\r\n if (successMessage !== null) {\r\n return <Success message={successMessage} />;\r\n }\r\n\r\n return (\r\n <form\r\n className=\"aup-form\"\r\n data-form-key={definition.key}\r\n data-step={steps.length > 1 ? stepIndex + 1 : undefined}\r\n noValidate\r\n onSubmit={handleSubmit}\r\n >\r\n {definition.title ? <Text kind=\"title\" text={definition.title} /> : null}\r\n {definition.intro ? <Text kind=\"intro\" text={definition.intro} /> : null}\r\n {step.title ? <Text kind=\"step\" text={step.title} /> : null}\r\n\r\n {step.items.map((item, index) => {\r\n if (item.kind !== 'field') return <Text key={`text-${index}`} kind={item.kind} text={item.text} />;\r\n\r\n const field = fieldsByKey.get(item.key);\r\n if (!field || field.type === 'hidden') return null;\r\n return (\r\n <FormFieldRow\r\n key={field.key}\r\n formId={formId}\r\n field={field}\r\n value={values[field.key] ?? ''}\r\n error={errors[field.key]}\r\n components={components}\r\n onChange={setValue}\r\n />\r\n );\r\n })}\r\n\r\n {hiddenFields.map((field) => (\r\n <FormFieldRow\r\n key={field.key}\r\n formId={formId}\r\n field={field}\r\n value={values[field.key] ?? ''}\r\n components={components}\r\n onChange={setValue}\r\n />\r\n ))}\r\n\r\n <div aria-hidden=\"true\" style={HONEYPOT_STYLE}>\r\n <input\r\n type=\"text\"\r\n name={definition.honeypot.name}\r\n tabIndex={-1}\r\n autoComplete=\"off\"\r\n value={honeypot}\r\n onChange={(event) => setHoneypot(event.target.value)}\r\n />\r\n </div>\r\n\r\n {formError ? <ErrorMessage message={formError} /> : null}\r\n {stepIndex > 0 ? <Back label={BACK_LABEL} onClick={() => goToStep(stepIndex - 1)} /> : null}\r\n {isLastStep ? (\r\n <Button label={definition.submit.label} submitting={submitting} disabled={submitting || preview} />\r\n ) : (\r\n <Button label={NEXT_LABEL} submitting={false} disabled={false} />\r\n )}\r\n </form>\r\n );\r\n};\r\n\r\n/** Renders and submits a form published in Amplify UP. */\r\nexport const Form: FC<FormProps> = ({ formId, fields, components, onSuccess, onError }) => {\r\n const resolvedId = formId ?? fieldString(fields, 'formId');\r\n const state = useFormDefinition(resolvedId);\r\n const merged = useMemo(() => ({ ...defaultComponents, ...components }), [components]);\r\n const reported = useRef<unknown>(null);\r\n\r\n if (state.status === 'missing') {\r\n return isComposerPreview() ? <merged.Error message={CHOOSE_FORM} /> : null;\r\n }\r\n\r\n if (state.status === 'loading' || !resolvedId) return null;\r\n\r\n if (state.status === 'failed') {\r\n if (reported.current !== state.error) {\r\n reported.current = state.error;\r\n onError?.({ kind: 'load', error: state.error });\r\n }\r\n return <merged.Error message={LOAD_FAILED} />;\r\n }\r\n\r\n return (\r\n <LoadedForm\r\n key={`${state.definition.id}:${state.definition.version}`}\r\n formId={resolvedId}\r\n definition={state.definition}\r\n components={merged}\r\n onSuccess={onSuccess}\r\n onError={onError}\r\n />\r\n );\r\n};\r\n"]}
@@ -0,0 +1,5 @@
1
+ import * as _amplifyup_component_types from '@amplifyup/component-types';
2
+
3
+ declare const manifest: _amplifyup_component_types.PackageManifest;
4
+
5
+ export { manifest };
@@ -0,0 +1,29 @@
1
+ import { defineManifest, packageComponentId } from '@amplifyup/component-types';
2
+
3
+ // src/manifest.ts
4
+ var manifest = defineManifest({
5
+ components: [
6
+ {
7
+ component_id: packageComponentId("@amplifyup/forms", "Form"),
8
+ component_name: "Form",
9
+ component_type: "form",
10
+ description: "A form built in Amplify UP. Renders its fields and handles submission.",
11
+ export_name: "Form",
12
+ fields: [
13
+ {
14
+ name: "formId",
15
+ label: "Form",
16
+ role: "config",
17
+ kind: "string"
18
+ }
19
+ ],
20
+ resources: [],
21
+ slots: [],
22
+ trackable_events: ["Form Submit"]
23
+ }
24
+ ]
25
+ });
26
+
27
+ export { manifest };
28
+ //# sourceMappingURL=manifest.js.map
29
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/manifest.ts"],"names":[],"mappings":";;;AAEO,IAAM,WAAW,cAAA,CAAe;AAAA,EACrC,UAAA,EAAY;AAAA,IACV;AAAA,MACE,YAAA,EAAc,kBAAA,CAAmB,kBAAA,EAAoB,MAAM,CAAA;AAAA,MAC3D,cAAA,EAAgB,MAAA;AAAA,MAChB,cAAA,EAAgB,MAAA;AAAA,MAChB,WAAA,EAAa,wEAAA;AAAA,MACb,WAAA,EAAa,MAAA;AAAA,MACb,MAAA,EAAQ;AAAA,QACN;AAAA,UACE,IAAA,EAAM,QAAA;AAAA,UACN,KAAA,EAAO,MAAA;AAAA,UACP,IAAA,EAAM,QAAA;AAAA,UACN,IAAA,EAAM;AAAA;AACR,OACF;AAAA,MACA,WAAW,EAAC;AAAA,MACZ,OAAO,EAAC;AAAA,MACR,gBAAA,EAAkB,CAAC,aAAa;AAAA;AAClC;AAEJ,CAAC","file":"manifest.js","sourcesContent":["import { defineManifest, packageComponentId } from '@amplifyup/component-types';\n\nexport const manifest = defineManifest({\n components: [\n {\n component_id: packageComponentId('@amplifyup/forms', 'Form'),\n component_name: 'Form',\n component_type: 'form',\n description: 'A form built in Amplify UP. Renders its fields and handles submission.',\n export_name: 'Form',\n fields: [\n {\n name: 'formId',\n label: 'Form',\n role: 'config',\n kind: 'string',\n },\n ],\n resources: [],\n slots: [],\n trackable_events: ['Form Submit'],\n },\n ],\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,67 @@
1
1
  {
2
2
  "name": "@amplifyup/forms",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Headless Amplify UP forms for React: render and submit forms built in Amplify UP",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "sideEffects": false,
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./manifest": {
17
+ "types": "./dist/manifest.d.ts",
18
+ "import": "./dist/manifest.js",
19
+ "default": "./dist/manifest.js"
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "amplifyup": {
24
+ "manifest": "./dist/amplifyup.manifest.json"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "scripts": {
33
+ "build": "tsup && node ./scripts/finish-build.mjs",
34
+ "dev": "tsup --watch --onSuccess \"node ./scripts/finish-build.mjs\"",
35
+ "lint": "node -e \"console.log('@amplifyup/forms: lint ok'); process.exit(0)\"",
36
+ "clean": "node -e \"const fs=require('fs');const p=require('path').join(__dirname,'dist');if(fs.existsSync(p))fs.rmSync(p,{recursive:true,force:true});\"",
37
+ "typecheck": "tsc --noEmit",
38
+ "test": "vitest run"
39
+ },
40
+ "keywords": [
41
+ "amplifyup",
42
+ "forms",
43
+ "react",
44
+ "headless"
45
+ ],
46
+ "author": "AmplifyUp",
47
+ "license": "MIT",
48
+ "homepage": "https://amplifyup.ai",
49
+ "dependencies": {
50
+ "@amplifyup/component-types": "^0.1.0"
51
+ },
52
+ "peerDependencies": {
53
+ "@amplifyup/sdk": "^0.3.0",
54
+ "react": "^18.0.0 || ^19.0.0"
55
+ },
56
+ "devDependencies": {
57
+ "@amplifyup/sdk": "*",
58
+ "@testing-library/react": "^16.3.0",
59
+ "@types/react": "^18.3.0",
60
+ "jsdom": "^30.0.1",
61
+ "react": "^18.3.1",
62
+ "react-dom": "^18.3.1",
63
+ "tsup": "^8.0.1",
64
+ "typescript": "^5",
65
+ "vitest": "^3.2.7"
66
+ }
67
+ }