@10x-media/form-builder 0.1.0-beta.2 → 0.1.0-beta.4
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/CHANGELOG.md +60 -0
- package/README.md +4 -4
- package/dist/actions/body/converters.d.ts +20 -0
- package/dist/actions/body/converters.js +57 -0
- package/dist/actions/body/converters.js.map +1 -0
- package/dist/actions/body/escapeHtml.d.ts +6 -0
- package/dist/actions/body/escapeHtml.js +14 -0
- package/dist/actions/body/escapeHtml.js.map +1 -0
- package/dist/actions/body/serializeBody.d.ts +50 -0
- package/dist/actions/body/serializeBody.js +78 -0
- package/dist/actions/body/serializeBody.js.map +1 -0
- package/dist/actions/body/serializeSlate.js +31 -0
- package/dist/actions/body/serializeSlate.js.map +1 -0
- package/dist/actions/body/wildcards.d.ts +10 -0
- package/dist/actions/body/wildcards.js +33 -0
- package/dist/actions/body/wildcards.js.map +1 -0
- package/dist/actions/builtin/confirmation.js +40 -15
- package/dist/actions/builtin/confirmation.js.map +1 -1
- package/dist/actions/builtin/emailTeam.js +19 -12
- package/dist/actions/builtin/emailTeam.js.map +1 -1
- package/dist/actions/builtin/index.d.ts +13 -1
- package/dist/actions/builtin/index.js +18 -7
- package/dist/actions/builtin/index.js.map +1 -1
- package/dist/actions/builtin/signedWebhook.js +6 -2
- package/dist/actions/builtin/signedWebhook.js.map +1 -1
- package/dist/actions/defineAction.d.ts +2 -1
- package/dist/actions/defineAction.js.map +1 -1
- package/dist/actions/dispatch.js +2 -1
- package/dist/actions/dispatch.js.map +1 -1
- package/dist/actions/runActions.js +10 -1
- package/dist/actions/runActions.js.map +1 -1
- package/dist/actions/task.js +7 -5
- package/dist/actions/task.js.map +1 -1
- package/dist/aggregation/resolveResultsRequest.d.ts +22 -6
- package/dist/aggregation/resolveResultsRequest.js +18 -7
- package/dist/aggregation/resolveResultsRequest.js.map +1 -1
- package/dist/calc/normalizeCalc.js +5 -17
- package/dist/calc/normalizeCalc.js.map +1 -1
- package/dist/calc/types.d.ts +8 -2
- package/dist/calc/types.js +22 -0
- package/dist/calc/types.js.map +1 -0
- package/dist/client/ByteSizeField.d.ts +21 -0
- package/dist/client/ByteSizeField.js +56 -0
- package/dist/client/ByteSizeField.js.map +1 -0
- package/dist/client/ConditionBuilder.js +2 -1
- package/dist/client/ConditionBuilder.js.map +1 -1
- package/dist/client/FieldNameSelect.d.ts +20 -0
- package/dist/client/FieldNameSelect.js +58 -0
- package/dist/client/FieldNameSelect.js.map +1 -0
- package/dist/client/FlowBuilder.d.ts +0 -1
- package/dist/client/FlowBuilder.js +102 -59
- package/dist/client/FlowBuilder.js.map +1 -1
- package/dist/client/FormConditionField.js +1 -4
- package/dist/client/FormConditionField.js.map +1 -1
- package/dist/client/admin.css +234 -0
- package/dist/client/flowAuthoring.js +35 -0
- package/dist/client/flowAuthoring.js.map +1 -0
- package/dist/client/synthesizeClientField.js +6 -2
- package/dist/client/synthesizeClientField.js.map +1 -1
- package/dist/client/toStaticLabel.js +13 -0
- package/dist/client/toStaticLabel.js.map +1 -0
- package/dist/collections/formSubmissions.js +49 -9
- package/dist/collections/formSubmissions.js.map +1 -1
- package/dist/collections/forms.js +251 -88
- package/dist/collections/forms.js.map +1 -1
- package/dist/conditions/conditionType.js +9 -2
- package/dist/conditions/conditionType.js.map +1 -1
- package/dist/conditions/normalizeConditions.js +2 -1
- package/dist/conditions/normalizeConditions.js.map +1 -1
- package/dist/consent/builtin/index.d.ts +11 -1
- package/dist/consent/builtin/index.js +15 -5
- package/dist/consent/builtin/index.js.map +1 -1
- package/dist/consent/builtin/static.js +7 -3
- package/dist/consent/builtin/static.js.map +1 -1
- package/dist/exports/client.d.ts +3 -1
- package/dist/exports/client.js +3 -1
- package/dist/exports/react.d.ts +15 -6
- package/dist/exports/react.js +10 -2
- package/dist/exports/rsc.d.ts +6 -1
- package/dist/exports/rsc.js +6 -1
- package/dist/exports/types.d.ts +4 -4
- package/dist/fields/buildFieldBlocks.js +20 -15
- package/dist/fields/buildFieldBlocks.js.map +1 -1
- package/dist/fields/builtin/calculation.js +115 -2
- package/dist/fields/builtin/calculation.js.map +1 -1
- package/dist/fields/builtin/consent.js +8 -4
- package/dist/fields/builtin/consent.js.map +1 -1
- package/dist/fields/builtin/date.js +29 -0
- package/dist/fields/builtin/date.js.map +1 -0
- package/dist/fields/builtin/file.d.ts +15 -0
- package/dist/fields/builtin/file.js +87 -7
- package/dist/fields/builtin/file.js.map +1 -1
- package/dist/fields/builtin/index.d.ts +24 -0
- package/dist/fields/builtin/index.js +29 -8
- package/dist/fields/builtin/index.js.map +1 -1
- package/dist/fields/builtin/message.js +26 -0
- package/dist/fields/builtin/message.js.map +1 -0
- package/dist/fields/builtin/repeater.js +7 -3
- package/dist/fields/builtin/repeater.js.map +1 -1
- package/dist/fields/builtin/select.js +7 -3
- package/dist/fields/builtin/select.js.map +1 -1
- package/dist/fields/fieldNamesOfType.js +25 -0
- package/dist/fields/fieldNamesOfType.js.map +1 -0
- package/dist/fields/localizedIf.d.ts +13 -0
- package/dist/fields/localizedIf.js +12 -0
- package/dist/fields/localizedIf.js.map +1 -0
- package/dist/fields/sharedConfig.js +82 -57
- package/dist/fields/sharedConfig.js.map +1 -1
- package/dist/fields/types.d.ts +7 -2
- package/dist/flow/normalizeFlow.js +7 -1
- package/dist/flow/normalizeFlow.js.map +1 -1
- package/dist/flow/stepLabel.js +15 -0
- package/dist/flow/stepLabel.js.map +1 -0
- package/dist/form/pollState.d.ts +22 -0
- package/dist/form/pollState.js +17 -0
- package/dist/form/pollState.js.map +1 -0
- package/dist/form/toFormDocument.d.ts +60 -0
- package/dist/form/toFormDocument.js +39 -0
- package/dist/form/toFormDocument.js.map +1 -0
- package/dist/form/types.d.ts +48 -0
- package/dist/index.d.ts +81 -13
- package/dist/index.js +36 -15
- package/dist/index.js.map +1 -1
- package/dist/plugin/registerCollections.js +17 -37
- package/dist/plugin/registerCollections.js.map +1 -1
- package/dist/plugin/uploadsCollection.d.ts +11 -0
- package/dist/plugin/uploadsCollection.js +44 -0
- package/dist/plugin/uploadsCollection.js.map +1 -0
- package/dist/poll/applyPollOptions.js +17 -0
- package/dist/poll/applyPollOptions.js.map +1 -0
- package/dist/poll/buildPollOptionSourceFields.js +53 -0
- package/dist/poll/buildPollOptionSourceFields.js.map +1 -0
- package/dist/poll/definePollOptionSource.d.ts +35 -0
- package/dist/poll/definePollOptionSource.js +6 -0
- package/dist/poll/definePollOptionSource.js.map +1 -0
- package/dist/poll/registry.d.ts +16 -0
- package/dist/poll/registry.js +16 -0
- package/dist/poll/registry.js.map +1 -0
- package/dist/poll/resolvePollOptions.d.ts +28 -0
- package/dist/poll/resolvePollOptions.js +64 -0
- package/dist/poll/resolvePollOptions.js.map +1 -0
- package/dist/poll/resolvePollOutcome.d.ts +21 -0
- package/dist/poll/resolvePollOutcome.js +36 -0
- package/dist/poll/resolvePollOutcome.js.map +1 -0
- package/dist/prefill/valuesFromSearchParams.js +1 -1
- package/dist/prefill/valuesFromSearchParams.js.map +1 -1
- package/dist/presentations/types.d.ts +3 -2
- package/dist/react/Form.d.ts +13 -31
- package/dist/react/Form.js +98 -85
- package/dist/react/Form.js.map +1 -1
- package/dist/react/FormContext.d.ts +35 -1
- package/dist/react/FormContext.js.map +1 -1
- package/dist/react/FormControls.d.ts +55 -0
- package/dist/react/FormControls.js +56 -0
- package/dist/react/FormControls.js.map +1 -0
- package/dist/react/FormResults.d.ts +6 -3
- package/dist/react/FormResults.js +37 -29
- package/dist/react/FormResults.js.map +1 -1
- package/dist/react/FormSteps.d.ts +17 -0
- package/dist/react/FormSteps.js +35 -0
- package/dist/react/FormSteps.js.map +1 -0
- package/dist/react/Poll.d.ts +17 -6
- package/dist/react/Poll.js +66 -18
- package/dist/react/Poll.js.map +1 -1
- package/dist/react/captcha/HcaptchaCaptcha.d.ts +28 -0
- package/dist/react/captcha/HcaptchaCaptcha.js +53 -0
- package/dist/react/captcha/HcaptchaCaptcha.js.map +1 -0
- package/dist/react/captcha/RecaptchaCaptcha.d.ts +41 -0
- package/dist/react/captcha/RecaptchaCaptcha.js +93 -0
- package/dist/react/captcha/RecaptchaCaptcha.js.map +1 -0
- package/dist/react/captcha/TurnstileCaptcha.d.ts +28 -0
- package/dist/react/captcha/TurnstileCaptcha.js +53 -0
- package/dist/react/captcha/TurnstileCaptcha.js.map +1 -0
- package/dist/react/captcha/types.d.ts +8 -0
- package/dist/react/captcha/useCaptchaScript.js +45 -0
- package/dist/react/captcha/useCaptchaScript.js.map +1 -0
- package/dist/react/captcha/vendors.js +8 -0
- package/dist/react/captcha/vendors.js.map +1 -0
- package/dist/react/fetchResults.d.ts +1 -1
- package/dist/react/fetchResults.js +1 -1
- package/dist/react/fetchResults.js.map +1 -1
- package/dist/react/primitives/Input.d.ts +1 -1
- package/dist/react/primitives/Input.js.map +1 -1
- package/dist/react/renderers/calculation.js +1 -1
- package/dist/react/renderers/checkbox.js +1 -1
- package/dist/react/renderers/consent.js +1 -1
- package/dist/react/renderers/date.js +37 -0
- package/dist/react/renderers/date.js.map +1 -0
- package/dist/react/renderers/email.js +1 -1
- package/dist/react/renderers/file.js +25 -8
- package/dist/react/renderers/file.js.map +1 -1
- package/dist/react/renderers/index.js +4 -0
- package/dist/react/renderers/index.js.map +1 -1
- package/dist/react/renderers/message.js +35 -0
- package/dist/react/renderers/message.js.map +1 -0
- package/dist/react/renderers/number.js +1 -1
- package/dist/react/renderers/repeater.js +1 -1
- package/dist/react/renderers/select.js +1 -1
- package/dist/react/renderers/text.js +1 -1
- package/dist/react/renderers/textarea.js +1 -1
- package/dist/react/state.d.ts +25 -1
- package/dist/react/state.js +14 -1
- package/dist/react/state.js.map +1 -1
- package/dist/react/uploadFile.d.ts +2 -2
- package/dist/react/uploadFile.js +1 -1
- package/dist/react/uploadFile.js.map +1 -1
- package/dist/spam/captcha.d.ts +3 -2
- package/dist/spam/captcha.js +3 -2
- package/dist/spam/captcha.js.map +1 -1
- package/dist/spam/clientIp.js +9 -0
- package/dist/spam/clientIp.js.map +1 -0
- package/dist/spam/identify.js +3 -6
- package/dist/spam/identify.js.map +1 -1
- package/dist/spam/providers/hcaptcha.d.ts +17 -0
- package/dist/spam/providers/hcaptcha.js +18 -0
- package/dist/spam/providers/hcaptcha.js.map +1 -0
- package/dist/spam/providers/recaptcha.d.ts +23 -0
- package/dist/spam/providers/recaptcha.js +23 -0
- package/dist/spam/providers/recaptcha.js.map +1 -0
- package/dist/spam/providers/siteverify.js +34 -0
- package/dist/spam/providers/siteverify.js.map +1 -0
- package/dist/spam/providers/turnstile.d.ts +17 -0
- package/dist/spam/providers/turnstile.js +18 -0
- package/dist/spam/providers/turnstile.js.map +1 -0
- package/dist/spam/spamGuard.js +2 -5
- package/dist/spam/spamGuard.js.map +1 -1
- package/dist/spam/types.d.ts +2 -2
- package/dist/submissions/runSubmission.js +70 -33
- package/dist/submissions/runSubmission.js.map +1 -1
- package/dist/submissions/validateSubmission.js +41 -7
- package/dist/submissions/validateSubmission.js.map +1 -1
- package/dist/submissions/votedCookie.d.ts +13 -0
- package/dist/submissions/votedCookie.js +27 -0
- package/dist/submissions/votedCookie.js.map +1 -0
- package/dist/translations/de.js +219 -0
- package/dist/translations/de.js.map +1 -0
- package/dist/translations/en.js +71 -10
- package/dist/translations/en.js.map +1 -1
- package/dist/translations/index.d.ts +2 -1
- package/dist/translations/index.js +6 -2
- package/dist/translations/index.js.map +1 -1
- package/dist/translations/keys.d.ts +69 -8
- package/dist/translations/keys.js +70 -9
- package/dist/translations/keys.js.map +1 -1
- package/dist/uploads/formatBytes.d.ts +9 -0
- package/dist/uploads/formatBytes.js +25 -0
- package/dist/uploads/formatBytes.js.map +1 -0
- package/dist/uploads/resolveFileRef.js +1 -1
- package/dist/uploads/resolveFileRef.js.map +1 -1
- package/dist/uploads/types.d.ts +1 -2
- package/dist/validation/builtin/index.d.ts +14 -0
- package/dist/validation/builtin/index.js +12 -1
- package/dist/validation/builtin/index.js.map +1 -1
- package/dist/validation/builtin/maxDate.js +23 -0
- package/dist/validation/builtin/maxDate.js.map +1 -0
- package/dist/validation/builtin/minDate.js +23 -0
- package/dist/validation/builtin/minDate.js.map +1 -0
- package/dist/validation/builtin/validateDateBound.js +18 -0
- package/dist/validation/builtin/validateDateBound.js.map +1 -0
- package/dist/validation/validateUrl.js +22 -0
- package/dist/validation/validateUrl.js.map +1 -0
- package/package.json +4 -2
- package/styles/form-builder.css +10 -190
- package/dist/collections/uploads.d.ts +0 -30
- package/dist/collections/uploads.js +0 -55
- package/dist/collections/uploads.js.map +0 -1
- package/dist/presentations/registry.d.ts +0 -15
- package/dist/presentations/registry.js +0 -15
- package/dist/presentations/registry.js.map +0 -1
- package/dist/react/toFormDocument.d.ts +0 -24
- package/dist/react/toFormDocument.js +0 -22
- package/dist/react/toFormDocument.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valuesFromSearchParams.js","names":[],"sources":["../../src/prefill/valuesFromSearchParams.ts"],"sourcesContent":["import type { FieldTypeRegistry } from '../fields/registry'\nimport type { FormFieldValueKind } from '../fields/types'\nimport type { FormFieldInstance } from '../submissions/types'\n\nexport type PrefillOptions = {\n\t/** Map a query-param name to a field name. Unmapped params use their own name. */\n\tmap?: Record<string, string>\n\t/** If set, only these field names may be prefilled. */\n\tallow?: string[]\n\t/** These field names may never be prefilled. */\n\tdeny?: string[]\n}\n\nconst TRUTHY = new Set(['true', '1', 'on', 'yes'])\n\nconst coerce = (kind: FormFieldValueKind, raw: string, all: string[]): unknown => {\n\tswitch (kind) {\n\t\tcase 'number': {\n\t\t\tconst n = Number(raw)\n\t\t\treturn Number.isNaN(n) ? undefined : n\n\t\t}\n\t\tcase 'boolean':\n\t\t\treturn TRUTHY.has(raw.toLowerCase())\n\t\tcase 'text[]':\n\t\t\treturn all\n\t\tdefault:\n\t\t\treturn raw\n\t}\n}\n\n/**\n * Map URL/query params to typed initial values for KNOWN fields only, coerced by each field's value\n * kind. Unknown, denied, or un-allowed params are ignored; invalid numbers are dropped. Prefilled\n * values are never trusted -- they still validate on submit. Pass the result to `<Form initialValues>`.\n */\n// biome-ignore lint/complexity/useMaxParams: four positional args are the minimal surface (params, fields, registry, options)\nexport const valuesFromSearchParams = (\n\tparams: URLSearchParams | Record<string, string>,\n\tfields: FormFieldInstance[],\n\tregistry: FieldTypeRegistry,\n\toptions: PrefillOptions = {}\n): Record<string, unknown> => {\n\tconst isSearch = params instanceof URLSearchParams\n\tconst paramKeys = isSearch ? [...new Set(params.keys())] : Object.keys(params)\n\tconst byName = new Map(fields.map((field) => [field.name, field]))\n\tconst result: Record<string, unknown> = {}\n\n\tfor (const param of paramKeys) {\n\t\tconst fieldName = options.map?.[param] ?? param\n\n\t\tif (options.allow && !options.allow.includes(fieldName)) {\n\t\t\tcontinue\n\t\t}\n\t\tif (options.deny?.includes(fieldName)) {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst field = byName.get(fieldName)\n\t\tconst definition = field && registry.get(field.blockType)\n\t\tif (!field || !definition) {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst all = isSearch ? params.getAll(param) : [params[param] as string]\n\t\tconst value = coerce(definition.value, all[0] ?? '', all)\n\t\tif (value !== undefined) {\n\t\t\tresult[fieldName] = value\n\t\t}\n\t}\n\n\treturn result\n}\n"],"mappings":";AAaA,MAAM,SAAS,IAAI,IAAI;CAAC;CAAQ;CAAK;CAAM;AAAK,CAAC;AAEjD,MAAM,UAAU,MAA0B,KAAa,QAA2B;CACjF,QAAQ,MAAR;EACC,KAAK,UAAU;GACd,MAAM,IAAI,OAAO,GAAG;GACpB,OAAO,OAAO,MAAM,CAAC,IAAI,KAAA,IAAY;EACtC;EACA,KAAK,WACJ,OAAO,OAAO,IAAI,IAAI,YAAY,CAAC;EACpC,KAAK,UACJ,OAAO;EACR,SACC,OAAO;CACT;AACD;;;;;;AAQA,MAAa,0BACZ,QACA,QACA,UACA,UAA0B,CAAC,MACE;CAC7B,MAAM,WAAW,kBAAkB;CACnC,MAAM,YAAY,WAAW,CAAC,GAAG,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC,IAAI,OAAO,KAAK,MAAM;CAC7E,MAAM,SAAS,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;CACjE,MAAM,SAAkC,CAAC;CAEzC,KAAK,MAAM,SAAS,WAAW;EAC9B,MAAM,YAAY,QAAQ,MAAM,UAAU;EAE1C,IAAI,QAAQ,SAAS,CAAC,QAAQ,MAAM,SAAS,SAAS,GACrD;EAED,IAAI,QAAQ,MAAM,SAAS,SAAS,GACnC;EAGD,MAAM,QAAQ,OAAO,IAAI,SAAS;EAClC,MAAM,aAAa,SAAS,SAAS,IAAI,MAAM,SAAS;
|
|
1
|
+
{"version":3,"file":"valuesFromSearchParams.js","names":[],"sources":["../../src/prefill/valuesFromSearchParams.ts"],"sourcesContent":["import type { FieldTypeRegistry } from '../fields/registry'\nimport type { FormFieldValueKind } from '../fields/types'\nimport type { FormFieldInstance } from '../submissions/types'\n\nexport type PrefillOptions = {\n\t/** Map a query-param name to a field name. Unmapped params use their own name. */\n\tmap?: Record<string, string>\n\t/** If set, only these field names may be prefilled. */\n\tallow?: string[]\n\t/** These field names may never be prefilled. */\n\tdeny?: string[]\n}\n\nconst TRUTHY = new Set(['true', '1', 'on', 'yes'])\n\nconst coerce = (kind: FormFieldValueKind, raw: string, all: string[]): unknown => {\n\tswitch (kind) {\n\t\tcase 'number': {\n\t\t\tconst n = Number(raw)\n\t\t\treturn Number.isNaN(n) ? undefined : n\n\t\t}\n\t\tcase 'boolean':\n\t\t\treturn TRUTHY.has(raw.toLowerCase())\n\t\tcase 'text[]':\n\t\t\treturn all\n\t\tdefault:\n\t\t\treturn raw\n\t}\n}\n\n/**\n * Map URL/query params to typed initial values for KNOWN fields only, coerced by each field's value\n * kind. Unknown, denied, or un-allowed params are ignored; invalid numbers are dropped. Prefilled\n * values are never trusted -- they still validate on submit. Pass the result to `<Form initialValues>`.\n */\n// biome-ignore lint/complexity/useMaxParams: four positional args are the minimal surface (params, fields, registry, options)\nexport const valuesFromSearchParams = (\n\tparams: URLSearchParams | Record<string, string>,\n\tfields: FormFieldInstance[],\n\tregistry: FieldTypeRegistry,\n\toptions: PrefillOptions = {}\n): Record<string, unknown> => {\n\tconst isSearch = params instanceof URLSearchParams\n\tconst paramKeys = isSearch ? [...new Set(params.keys())] : Object.keys(params)\n\tconst byName = new Map(fields.map((field) => [field.name, field]))\n\tconst result: Record<string, unknown> = {}\n\n\tfor (const param of paramKeys) {\n\t\tconst fieldName = options.map?.[param] ?? param\n\n\t\tif (options.allow && !options.allow.includes(fieldName)) {\n\t\t\tcontinue\n\t\t}\n\t\tif (options.deny?.includes(fieldName)) {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst field = byName.get(fieldName)\n\t\tconst definition = field && registry.get(field.blockType)\n\t\t// 'none'-kind (display-only) fields carry no value, so a param under their name is ignored.\n\t\tif (!field || !definition || definition.value === 'none') {\n\t\t\tcontinue\n\t\t}\n\n\t\tconst all = isSearch ? params.getAll(param) : [params[param] as string]\n\t\tconst value = coerce(definition.value, all[0] ?? '', all)\n\t\tif (value !== undefined) {\n\t\t\tresult[fieldName] = value\n\t\t}\n\t}\n\n\treturn result\n}\n"],"mappings":";AAaA,MAAM,SAAS,IAAI,IAAI;CAAC;CAAQ;CAAK;CAAM;AAAK,CAAC;AAEjD,MAAM,UAAU,MAA0B,KAAa,QAA2B;CACjF,QAAQ,MAAR;EACC,KAAK,UAAU;GACd,MAAM,IAAI,OAAO,GAAG;GACpB,OAAO,OAAO,MAAM,CAAC,IAAI,KAAA,IAAY;EACtC;EACA,KAAK,WACJ,OAAO,OAAO,IAAI,IAAI,YAAY,CAAC;EACpC,KAAK,UACJ,OAAO;EACR,SACC,OAAO;CACT;AACD;;;;;;AAQA,MAAa,0BACZ,QACA,QACA,UACA,UAA0B,CAAC,MACE;CAC7B,MAAM,WAAW,kBAAkB;CACnC,MAAM,YAAY,WAAW,CAAC,GAAG,IAAI,IAAI,OAAO,KAAK,CAAC,CAAC,IAAI,OAAO,KAAK,MAAM;CAC7E,MAAM,SAAS,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;CACjE,MAAM,SAAkC,CAAC;CAEzC,KAAK,MAAM,SAAS,WAAW;EAC9B,MAAM,YAAY,QAAQ,MAAM,UAAU;EAE1C,IAAI,QAAQ,SAAS,CAAC,QAAQ,MAAM,SAAS,SAAS,GACrD;EAED,IAAI,QAAQ,MAAM,SAAS,SAAS,GACnC;EAGD,MAAM,QAAQ,OAAO,IAAI,SAAS;EAClC,MAAM,aAAa,SAAS,SAAS,IAAI,MAAM,SAAS;EAExD,IAAI,CAAC,SAAS,CAAC,cAAc,WAAW,UAAU,QACjD;EAGD,MAAM,MAAM,WAAW,OAAO,OAAO,KAAK,IAAI,CAAC,OAAO,MAAgB;EACtE,MAAM,QAAQ,OAAO,WAAW,OAAO,IAAI,MAAM,IAAI,GAAG;EACxD,IAAI,UAAU,KAAA,GACb,OAAO,aAAa;CAEtB;CAEA,OAAO;AACR"}
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
type PresentationSurface = 'inline' | 'page' | 'overlay';
|
|
4
4
|
type PresentationDensity = 'comfortable' | 'compact';
|
|
5
5
|
/**
|
|
6
|
-
* Serializable, framework-agnostic description of a presentation.
|
|
7
|
-
*
|
|
6
|
+
* Serializable, framework-agnostic description of a presentation. `presentations/defaults.ts`
|
|
7
|
+
* ships the built-ins the React presentation registry wraps with a `Wrapper` (see `./react`). No
|
|
8
|
+
* React here.
|
|
8
9
|
*/
|
|
9
10
|
type PresentationDescriptor = {
|
|
10
11
|
name: string; /** i18n key or literal shown in the admin select. */
|
package/dist/react/Form.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { AnyFormFieldDefinition } from "../fields/types.js";
|
|
2
|
-
import {
|
|
3
|
-
import { FormFlow } from "../flow/types.js";
|
|
2
|
+
import { FormDisplaySettings, FormDocument, FormPollSettings, FormResponseSettings } from "../form/types.js";
|
|
4
3
|
import { RendererTranslate } from "./contract.js";
|
|
5
4
|
import { FormEventSink } from "../events/types.js";
|
|
6
5
|
import { AnyValidationRuleDefinition } from "../validation/types.js";
|
|
6
|
+
import { BackButtonRenderProps, NextButtonRenderProps, SubmitButtonRenderProps } from "./FormControls.js";
|
|
7
7
|
import { FormPresentation } from "./presentation/types.js";
|
|
8
8
|
import { PresentationsConfig } from "./presentation/registry.js";
|
|
9
9
|
import { RenderersConfig } from "./registry.js";
|
|
@@ -11,29 +11,6 @@ import { SubmitHandler } from "./submitForm.js";
|
|
|
11
11
|
import { ReactNode } from "react";
|
|
12
12
|
|
|
13
13
|
//#region src/react/Form.d.ts
|
|
14
|
-
type FormDocument = {
|
|
15
|
-
id: number | string;
|
|
16
|
-
fields: FormFieldInstance[];
|
|
17
|
-
flow?: FormFlow; /** Stored presentation name; overridden by the `presentation` prop. */
|
|
18
|
-
defaultPresentation?: string;
|
|
19
|
-
};
|
|
20
|
-
/** Props passed to `renderSubmit`. */
|
|
21
|
-
type SubmitButtonRenderProps = {
|
|
22
|
-
label: string;
|
|
23
|
-
submitting: boolean;
|
|
24
|
-
};
|
|
25
|
-
/** Props passed to `renderNext`. */
|
|
26
|
-
type NextButtonRenderProps = {
|
|
27
|
-
label: string;
|
|
28
|
-
submitting: boolean;
|
|
29
|
-
onClick: () => void;
|
|
30
|
-
};
|
|
31
|
-
/** Props passed to `renderBack`. */
|
|
32
|
-
type BackButtonRenderProps = {
|
|
33
|
-
label: string;
|
|
34
|
-
submitting: boolean;
|
|
35
|
-
onClick: () => void;
|
|
36
|
-
};
|
|
37
14
|
type FormProps = {
|
|
38
15
|
form: FormDocument;
|
|
39
16
|
fieldTypes?: AnyFormFieldDefinition[];
|
|
@@ -46,15 +23,20 @@ type FormProps = {
|
|
|
46
23
|
events?: FormEventSink;
|
|
47
24
|
t?: RendererTranslate;
|
|
48
25
|
locale?: string;
|
|
49
|
-
layout?: boolean;
|
|
50
|
-
submitLabel?: string;
|
|
51
|
-
nextLabel?: string;
|
|
26
|
+
layout?: boolean; /** Submit button label. Precedence: this prop, then the form's `response.submitLabel`, then the translated default. */
|
|
27
|
+
submitLabel?: string; /** "Next" button label for multi-step forms. Defaults to the translated `'Next'`. */
|
|
28
|
+
nextLabel?: string; /** "Back" button label for multi-step forms. Defaults to the translated `'Back'`. */
|
|
52
29
|
backLabel?: string; /** Label for the overlay close control (modal/drawer). */
|
|
53
30
|
closeLabel?: string;
|
|
54
|
-
successMessage?: string; /** Active presentation: a name into the registry or an inline presentation.
|
|
31
|
+
successMessage?: string; /** Active presentation: a name into the registry or an inline presentation. Defaults to `'page'` when omitted. */
|
|
55
32
|
presentation?: string | FormPresentation; /** Per-render presentation overrides merged onto the defaults (add, replace, or `false` to remove). */
|
|
56
33
|
presentations?: PresentationsConfig; /** Invoked when an overlay presentation dismisses (close button, Escape, outside click, or `dismissOnSuccess`). */
|
|
57
|
-
onClose?: () => void;
|
|
34
|
+
onClose?: () => void;
|
|
35
|
+
/**
|
|
36
|
+
* Accessible name for an overlay surface (modal/drawer). Hosts choosing between a trigger
|
|
37
|
+
* label and the form's own display title should prefer `form.display?.title` when
|
|
38
|
+
* `form.display?.showTitle` is set, falling back to their own label otherwise.
|
|
39
|
+
*/
|
|
58
40
|
title?: string; /** Seed initial field values (e.g. from `valuesFromSearchParams`). Still validated on submit. */
|
|
59
41
|
initialValues?: Record<string, unknown>; /** Honeypot decoy (on by default). `false` removes it; `{ name }` matches a customized server `spam.honeypot.fieldName`. */
|
|
60
42
|
honeypot?: false | {
|
|
@@ -106,5 +88,5 @@ declare const Form: ({
|
|
|
106
88
|
backButtonClassName
|
|
107
89
|
}: FormProps) => import("react/jsx-runtime").JSX.Element;
|
|
108
90
|
//#endregion
|
|
109
|
-
export {
|
|
91
|
+
export { Form, FormProps };
|
|
110
92
|
//# sourceMappingURL=Form.d.ts.map
|
package/dist/react/Form.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { interpolate } from "../recall/interpolate.js";
|
|
3
|
+
import { keys } from "../translations/keys.js";
|
|
4
|
+
import { serializeBody } from "../actions/body/serializeBody.js";
|
|
3
5
|
import { noopEventSink } from "../events/noopSink.js";
|
|
4
6
|
import { CAPTCHA_TOKEN_KEY } from "../spam/constants.js";
|
|
5
|
-
import { defaultPresentationDescriptors } from "../presentations/defaults.js";
|
|
6
7
|
import { calcExpressionOf, computeCalcFields } from "../calc/computeCalcFields.js";
|
|
7
8
|
import { evaluateCondition } from "../conditions/evaluate.js";
|
|
8
9
|
import { en } from "../translations/en.js";
|
|
10
|
+
import { defaultPresentationDescriptors } from "../presentations/defaults.js";
|
|
9
11
|
import { buildRecallResolver } from "../recall/resolver.js";
|
|
10
12
|
import { firstStepId, isTerminalStepId, resolveNextStepId, stepFieldNames } from "../flow/engine.js";
|
|
11
13
|
import { cn } from "./cn.js";
|
|
12
14
|
import { makeTranslate } from "../translations/makeTranslate.js";
|
|
13
15
|
import { emitFormEvent } from "./events.js";
|
|
14
16
|
import { FormContext } from "./FormContext.js";
|
|
17
|
+
import { FormControls } from "./FormControls.js";
|
|
15
18
|
import { FormLayout, widthProps } from "./FormLayout.js";
|
|
16
19
|
import { Honeypot } from "./Honeypot.js";
|
|
17
20
|
import { defaultPresentations } from "./presentation/presentations.js";
|
|
@@ -21,11 +24,11 @@ import { resolveRenderers } from "./registry.js";
|
|
|
21
24
|
import { useField } from "./useField.js";
|
|
22
25
|
import { defaultRenderers } from "./renderers/index.js";
|
|
23
26
|
import { buildFieldTypeRegistry, buildValidationRuleRegistry, visibleFields } from "./resolveForm.js";
|
|
24
|
-
import { formReducer, initialFormState } from "./state.js";
|
|
27
|
+
import { formReducer, initialFormState, seedFieldValues } from "./state.js";
|
|
25
28
|
import { submitForm } from "./submitForm.js";
|
|
26
29
|
import { validateFieldValue } from "./validateField.js";
|
|
27
|
-
import { createElement, useCallback, useEffect, useId, useMemo, useReducer, useRef, useState } from "react";
|
|
28
30
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
31
|
+
import { createElement, useCallback, useEffect, useId, useMemo, useReducer, useRef, useState } from "react";
|
|
29
32
|
//#region src/react/Form.tsx
|
|
30
33
|
const isEmpty = (value) => value == null || value === "" || Array.isArray(value) && value.length === 0;
|
|
31
34
|
const FIELD_WIDTHS = new Set([
|
|
@@ -69,22 +72,34 @@ const CalcFieldHost = ({ field, renderer, value, locale, t }) => {
|
|
|
69
72
|
});
|
|
70
73
|
};
|
|
71
74
|
/** The headless form controller: state, progressive client validation, conditional visibility, submission, events. */
|
|
72
|
-
const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, events, t, locale = "en", layout, submitLabel
|
|
75
|
+
const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, events, t, locale = "en", layout, submitLabel, nextLabel, backLabel, closeLabel = "Close", successMessage = "Thank you.", presentation, presentations, onClose, title, initialValues, honeypot, captchaToken, children, className, renderSubmit, renderNext, renderBack, submitButtonClassName, nextButtonClassName, backButtonClassName }) => {
|
|
73
76
|
const honeypotName = honeypot === false ? null : honeypot?.name ?? "website";
|
|
74
77
|
const honeypotRef = useRef(null);
|
|
75
78
|
const registry = useMemo(() => buildFieldTypeRegistry(fieldTypes), [fieldTypes]);
|
|
76
79
|
const ruleRegistry = useMemo(() => buildValidationRuleRegistry(rules), [rules]);
|
|
77
80
|
const rendererRegistry = useMemo(() => resolveRenderers(defaultRenderers, renderers), [renderers]);
|
|
78
81
|
const presentationRegistry = useMemo(() => resolvePresentations(defaultPresentations, presentations), [presentations]);
|
|
79
|
-
const activePresentation = typeof presentation === "object" ? presentation : presentationRegistry.get(presentation ??
|
|
82
|
+
const activePresentation = typeof presentation === "object" ? presentation : presentationRegistry.get(presentation ?? "page") ?? presentationRegistry.get("page") ?? defaultPresentationDescriptors.page;
|
|
80
83
|
const fieldsByName = useMemo(() => new Map(form.fields.map((field) => [field.name, field])), [form.fields]);
|
|
81
84
|
const translate = useMemo(() => t ?? makeTranslate(en), [t]);
|
|
85
|
+
const docSubmitLabel = typeof form.response?.submitLabel === "string" && form.response.submitLabel.length > 0 ? form.response.submitLabel : void 0;
|
|
86
|
+
const labels = useMemo(() => ({
|
|
87
|
+
back: backLabel ?? translate(keys.formBack),
|
|
88
|
+
next: nextLabel ?? translate(keys.formNext),
|
|
89
|
+
submit: submitLabel ?? docSubmitLabel ?? translate(keys.formSubmit)
|
|
90
|
+
}), [
|
|
91
|
+
backLabel,
|
|
92
|
+
nextLabel,
|
|
93
|
+
submitLabel,
|
|
94
|
+
docSubmitLabel,
|
|
95
|
+
translate
|
|
96
|
+
]);
|
|
82
97
|
const sinkRef = useRef(noopEventSink);
|
|
83
98
|
sinkRef.current = events ?? noopEventSink;
|
|
84
99
|
const formIdRef = useRef("");
|
|
85
100
|
formIdRef.current = String(form.id);
|
|
86
101
|
const [state, rawDispatch] = useReducer(formReducer, form.fields, (fields) => initialFormState({
|
|
87
|
-
...
|
|
102
|
+
...seedFieldValues(fields),
|
|
88
103
|
...initialValues ?? {}
|
|
89
104
|
}));
|
|
90
105
|
const effectiveValues = useMemo(() => computeCalcFields(form.fields, state.values), [form.fields, state.values]);
|
|
@@ -163,10 +178,15 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
163
178
|
translate
|
|
164
179
|
]);
|
|
165
180
|
const visible = visibleFields(form.fields, effectiveValues);
|
|
181
|
+
/** Answered visible fields as submission values. Display-only ('none' kind) fields never contribute. */
|
|
182
|
+
const answeredValues = () => visibleFields(form.fields, effectiveValues).filter((field) => registry.get(field.blockType)?.value !== "none").filter((field) => !isEmpty(effectiveValues[field.name])).map((field) => ({
|
|
183
|
+
field: field.name,
|
|
184
|
+
value: effectiveValues[field.name]
|
|
185
|
+
}));
|
|
166
186
|
const stepVisible = (flow && currentStepId ? stepFieldNames(flow, currentStepId) : []).map((name) => visible.find((field) => field.name === name)).filter((field) => Boolean(field));
|
|
167
187
|
const goNext = async () => {
|
|
168
188
|
if (!flow || !currentStepId) return;
|
|
169
|
-
const results = await Promise.all(stepVisible.filter((field) => !calcExpressionOf(field)).filter((field) => evaluateCondition(field.validateWhen, effectiveValues)).map(async (field) => ({
|
|
189
|
+
const results = await Promise.all(stepVisible.filter((field) => !calcExpressionOf(field)).filter((field) => registry.get(field.blockType)?.value !== "none").filter((field) => evaluateCondition(field.validateWhen, effectiveValues)).map(async (field) => ({
|
|
170
190
|
field,
|
|
171
191
|
...await validateFieldValue({
|
|
172
192
|
field,
|
|
@@ -193,7 +213,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
193
213
|
if (result.errors.length > 0) hasError = true;
|
|
194
214
|
}
|
|
195
215
|
if (hasError) return;
|
|
196
|
-
const next = resolveNextStepId(flow, currentStepId,
|
|
216
|
+
const next = resolveNextStepId(flow, currentStepId, effectiveValues);
|
|
197
217
|
if (!next) return;
|
|
198
218
|
emitFormEvent(sinkRef.current, formIdRef.current, {
|
|
199
219
|
type: "step.completed",
|
|
@@ -238,7 +258,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
238
258
|
if (submittingRef.current) return;
|
|
239
259
|
submittingRef.current = true;
|
|
240
260
|
const visible = visibleFields(form.fields, effectiveValues);
|
|
241
|
-
const results = await Promise.all(visible.filter((field) => !calcExpressionOf(field)).filter((field) => evaluateCondition(field.validateWhen, effectiveValues)).map(async (field) => ({
|
|
261
|
+
const results = await Promise.all(visible.filter((field) => !calcExpressionOf(field)).filter((field) => registry.get(field.blockType)?.value !== "none").filter((field) => evaluateCondition(field.validateWhen, effectiveValues)).map(async (field) => ({
|
|
242
262
|
field,
|
|
243
263
|
...await validateFieldValue({
|
|
244
264
|
field,
|
|
@@ -297,10 +317,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
297
317
|
return;
|
|
298
318
|
}
|
|
299
319
|
rawDispatch({ type: "SUBMIT_START" });
|
|
300
|
-
const values =
|
|
301
|
-
field: field.name,
|
|
302
|
-
value: effectiveValues[field.name]
|
|
303
|
-
}));
|
|
320
|
+
const values = answeredValues();
|
|
304
321
|
if (honeypotName) {
|
|
305
322
|
const decoy = honeypotRef.current?.value ?? "";
|
|
306
323
|
if (decoy !== "") values.push({
|
|
@@ -330,6 +347,8 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
330
347
|
});
|
|
331
348
|
onSuccess?.(result.submissionId);
|
|
332
349
|
if (activePresentation.dismissOnSuccess) handleClose();
|
|
350
|
+
const redirectUrl = form.response?.type === "redirect" ? form.response.redirect?.url : void 0;
|
|
351
|
+
if (typeof redirectUrl === "string" && redirectUrl.length > 0 && typeof window !== "undefined") window.location.assign(redirectUrl);
|
|
333
352
|
} else {
|
|
334
353
|
if (result.fieldErrors) rawDispatch({
|
|
335
354
|
type: "SET_ALL_ISSUES",
|
|
@@ -344,32 +363,34 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
344
363
|
onError?.(message);
|
|
345
364
|
}
|
|
346
365
|
};
|
|
347
|
-
const step = flow ? {
|
|
348
|
-
flow,
|
|
349
|
-
currentStepId,
|
|
350
|
-
stepIndex: flow.steps.findIndex((s) => s.id === currentStepId),
|
|
351
|
-
stepCount: flow.steps.length,
|
|
352
|
-
isFirst: history.length === 0,
|
|
353
|
-
isTerminal: currentStepId ? isTerminalStepId(flow, currentStepId, state.values) : true,
|
|
354
|
-
goNext: () => {
|
|
355
|
-
goNext();
|
|
356
|
-
},
|
|
357
|
-
goBack
|
|
358
|
-
} : {
|
|
359
|
-
stepIndex: 0,
|
|
360
|
-
stepCount: 1,
|
|
361
|
-
isFirst: true,
|
|
362
|
-
isTerminal: true,
|
|
363
|
-
goNext: () => {},
|
|
364
|
-
goBack: () => {}
|
|
365
|
-
};
|
|
366
366
|
const contextValue = {
|
|
367
367
|
state,
|
|
368
368
|
dispatch,
|
|
369
369
|
validateField,
|
|
370
370
|
locale,
|
|
371
|
-
step
|
|
372
|
-
|
|
371
|
+
step: flow ? {
|
|
372
|
+
flow,
|
|
373
|
+
currentStepId,
|
|
374
|
+
stepIndex: flow.steps.findIndex((s) => s.id === currentStepId),
|
|
375
|
+
stepCount: flow.steps.length,
|
|
376
|
+
isFirst: history.length === 0,
|
|
377
|
+
isTerminal: currentStepId ? isTerminalStepId(flow, currentStepId, effectiveValues) : true,
|
|
378
|
+
goNext: () => {
|
|
379
|
+
goNext();
|
|
380
|
+
},
|
|
381
|
+
goBack
|
|
382
|
+
} : {
|
|
383
|
+
stepIndex: 0,
|
|
384
|
+
stepCount: 1,
|
|
385
|
+
isFirst: true,
|
|
386
|
+
isTerminal: true,
|
|
387
|
+
goNext: () => {},
|
|
388
|
+
goBack: () => {}
|
|
389
|
+
},
|
|
390
|
+
rendererRegistry,
|
|
391
|
+
labels,
|
|
392
|
+
t: translate,
|
|
393
|
+
effectiveValues
|
|
373
394
|
};
|
|
374
395
|
const PresentationWrapper = activePresentation.Wrapper;
|
|
375
396
|
const wrap = (content) => PresentationWrapper ? /* @__PURE__ */ jsx(PresentationWrapper, {
|
|
@@ -394,17 +415,35 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
394
415
|
}) : null, children]
|
|
395
416
|
}))
|
|
396
417
|
});
|
|
397
|
-
if (state.submitted)
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
418
|
+
if (state.submitted) {
|
|
419
|
+
const responseMessage = form.response?.type === "message" || form.response?.type == null ? form.response?.message : void 0;
|
|
420
|
+
const responseHtml = responseMessage ? serializeBody(responseMessage, {
|
|
421
|
+
values: answeredValues(),
|
|
422
|
+
descriptors: []
|
|
423
|
+
}) : void 0;
|
|
424
|
+
return /* @__PURE__ */ jsx(FormContext.Provider, {
|
|
425
|
+
value: contextValue,
|
|
426
|
+
children: wrap(responseHtml ? /* @__PURE__ */ jsx("div", {
|
|
427
|
+
role: "status",
|
|
428
|
+
className: cn("fb-form__success", className),
|
|
429
|
+
"data-fb-presentation": activePresentation.name,
|
|
430
|
+
"data-fb-density": activePresentation.density,
|
|
431
|
+
dangerouslySetInnerHTML: { __html: responseHtml }
|
|
432
|
+
}) : /* @__PURE__ */ jsx("p", {
|
|
433
|
+
role: "status",
|
|
434
|
+
className: cn("fb-form__success", className),
|
|
435
|
+
"data-fb-presentation": activePresentation.name,
|
|
436
|
+
"data-fb-density": activePresentation.density,
|
|
437
|
+
children: interpolate(successMessage, recall)
|
|
438
|
+
}))
|
|
439
|
+
});
|
|
440
|
+
}
|
|
407
441
|
const rendered = (flow ? stepVisible : visible).filter((field) => field.hidden !== true && field.calcDisplay !== false);
|
|
442
|
+
const displayTitleText = form.display?.showTitle && typeof form.display.title === "string" && form.display.title.length > 0 ? interpolate(form.display.title, recall) : void 0;
|
|
443
|
+
const displayIntroHtml = form.display?.intro ? serializeBody(form.display.intro, {
|
|
444
|
+
values: answeredValues(),
|
|
445
|
+
descriptors: []
|
|
446
|
+
}) : "";
|
|
408
447
|
return /* @__PURE__ */ jsx(FormContext.Provider, {
|
|
409
448
|
value: contextValue,
|
|
410
449
|
children: wrap(/* @__PURE__ */ jsxs("form", {
|
|
@@ -418,6 +457,14 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
418
457
|
name: honeypotName,
|
|
419
458
|
inputRef: honeypotRef
|
|
420
459
|
}) : null,
|
|
460
|
+
displayTitleText ? /* @__PURE__ */ jsx("h2", {
|
|
461
|
+
className: "fb-form__title",
|
|
462
|
+
children: displayTitleText
|
|
463
|
+
}) : null,
|
|
464
|
+
displayIntroHtml ? /* @__PURE__ */ jsx("div", {
|
|
465
|
+
className: "fb-form__intro",
|
|
466
|
+
dangerouslySetInnerHTML: { __html: displayIntroHtml }
|
|
467
|
+
}) : null,
|
|
421
468
|
/* @__PURE__ */ jsx(FormLayout, {
|
|
422
469
|
enabled: layout !== false,
|
|
423
470
|
children: rendered.map((field) => {
|
|
@@ -447,47 +494,13 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
447
494
|
className: "fb-form__submit-error",
|
|
448
495
|
children: state.submitError
|
|
449
496
|
}) : null,
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
type: "button",
|
|
458
|
-
className: backButtonClassName,
|
|
459
|
-
onClick: goBack,
|
|
460
|
-
disabled: state.submitting,
|
|
461
|
-
children: backLabel
|
|
462
|
-
}) : null, step.isTerminal ? renderSubmit ? renderSubmit({
|
|
463
|
-
label: submitLabel,
|
|
464
|
-
submitting: state.submitting
|
|
465
|
-
}) : /* @__PURE__ */ jsx("button", {
|
|
466
|
-
type: "submit",
|
|
467
|
-
className: submitButtonClassName,
|
|
468
|
-
disabled: state.submitting,
|
|
469
|
-
children: submitLabel
|
|
470
|
-
}) : renderNext ? renderNext({
|
|
471
|
-
label: nextLabel,
|
|
472
|
-
submitting: state.submitting,
|
|
473
|
-
onClick: () => void goNext()
|
|
474
|
-
}) : /* @__PURE__ */ jsx("button", {
|
|
475
|
-
type: "button",
|
|
476
|
-
className: nextButtonClassName,
|
|
477
|
-
disabled: state.submitting,
|
|
478
|
-
onClick: () => {
|
|
479
|
-
goNext();
|
|
480
|
-
},
|
|
481
|
-
children: nextLabel
|
|
482
|
-
})]
|
|
483
|
-
}) : renderSubmit ? renderSubmit({
|
|
484
|
-
label: submitLabel,
|
|
485
|
-
submitting: state.submitting
|
|
486
|
-
}) : /* @__PURE__ */ jsx("button", {
|
|
487
|
-
type: "submit",
|
|
488
|
-
className: submitButtonClassName,
|
|
489
|
-
disabled: state.submitting,
|
|
490
|
-
children: submitLabel
|
|
497
|
+
/* @__PURE__ */ jsx(FormControls, {
|
|
498
|
+
backButtonClassName,
|
|
499
|
+
nextButtonClassName,
|
|
500
|
+
submitButtonClassName,
|
|
501
|
+
renderBack,
|
|
502
|
+
renderNext,
|
|
503
|
+
renderSubmit
|
|
491
504
|
})
|
|
492
505
|
]
|
|
493
506
|
}))
|
package/dist/react/Form.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Form.js","names":[],"sources":["../../src/react/Form.tsx"],"sourcesContent":["'use client'\n\nimport {\n\tcreateElement,\n\ttype FormEvent as ReactFormEvent,\n\ttype ReactNode,\n\tuseCallback,\n\tuseEffect,\n\tuseId,\n\tuseMemo,\n\tuseReducer,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { calcExpressionOf, computeCalcFields } from '../calc/computeCalcFields'\nimport { evaluateCondition } from '../conditions/evaluate'\nimport { noopEventSink } from '../events/noopSink'\nimport type { FormEventSink } from '../events/types'\nimport type { AnyFormFieldDefinition } from '../fields/types'\nimport { firstStepId, isTerminalStepId, resolveNextStepId, stepFieldNames } from '../flow/engine'\nimport type { FormFlow } from '../flow/types'\nimport { defaultPresentationDescriptors } from '../presentations/defaults'\nimport { interpolate } from '../recall/interpolate'\nimport { buildRecallResolver } from '../recall/resolver'\nimport { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from '../spam/constants'\nimport type { FormFieldInstance, SubmissionValue } from '../submissions/types'\nimport { en } from '../translations/en'\nimport { makeTranslate } from '../translations/makeTranslate'\nimport type { AnyValidationRuleDefinition } from '../validation/types'\nimport { cn } from './cn'\nimport type { FieldRenderer, RendererTranslate } from './contract'\nimport { emitFormEvent } from './events'\nimport { FormContext, type FormStepInfo } from './FormContext'\nimport { type FieldWidth, FormLayout, widthProps } from './FormLayout'\nimport { Honeypot } from './Honeypot'\nimport { defaultPresentations } from './presentation/presentations'\nimport { type PresentationsConfig, resolvePresentations } from './presentation/registry'\nimport type { FormPresentation } from './presentation/types'\nimport { applyRecall } from './recall'\nimport { type RenderersConfig, resolveRenderers } from './registry'\nimport { defaultRenderers } from './renderers'\nimport { buildFieldTypeRegistry, buildValidationRuleRegistry, visibleFields } from './resolveForm'\nimport { type FieldErrors, type FormAction, formReducer, initialFormState } from './state'\nimport { type SubmitFormResult, type SubmitHandler, submitForm } from './submitForm'\nimport { useField } from './useField'\nimport { validateFieldValue } from './validateField'\n\nexport type FormDocument = {\n\tid: number | string\n\tfields: FormFieldInstance[]\n\tflow?: FormFlow\n\t/** Stored presentation name; overridden by the `presentation` prop. */\n\tdefaultPresentation?: string\n}\n\n/** Props passed to `renderSubmit`. */\nexport type SubmitButtonRenderProps = {\n\tlabel: string\n\tsubmitting: boolean\n}\n\n/** Props passed to `renderNext`. */\nexport type NextButtonRenderProps = {\n\tlabel: string\n\tsubmitting: boolean\n\tonClick: () => void\n}\n\n/** Props passed to `renderBack`. */\nexport type BackButtonRenderProps = {\n\tlabel: string\n\tsubmitting: boolean\n\tonClick: () => void\n}\n\nexport type FormProps = {\n\tform: FormDocument\n\tfieldTypes?: AnyFormFieldDefinition[]\n\trules?: AnyValidationRuleDefinition[]\n\trenderers?: RenderersConfig\n\tapiRoute?: string\n\tonSubmit?: SubmitHandler\n\tonSuccess?: (submissionId?: string) => void\n\tonError?: (message: string) => void\n\tevents?: FormEventSink\n\tt?: RendererTranslate\n\tlocale?: string\n\tlayout?: boolean\n\tsubmitLabel?: string\n\tnextLabel?: string\n\tbackLabel?: string\n\t/** Label for the overlay close control (modal/drawer). */\n\tcloseLabel?: string\n\tsuccessMessage?: string\n\t/** Active presentation: a name into the registry or an inline presentation. Overrides the form's stored default. */\n\tpresentation?: string | FormPresentation\n\t/** Per-render presentation overrides merged onto the defaults (add, replace, or `false` to remove). */\n\tpresentations?: PresentationsConfig\n\t/** Invoked when an overlay presentation dismisses (close button, Escape, outside click, or `dismissOnSuccess`). */\n\tonClose?: () => void\n\t/** Accessible name for an overlay surface. */\n\ttitle?: string\n\t/** Seed initial field values (e.g. from `valuesFromSearchParams`). Still validated on submit. */\n\tinitialValues?: Record<string, unknown>\n\t/** Honeypot decoy (on by default). `false` removes it; `{ name }` matches a customized server `spam.honeypot.fieldName`. */\n\thoneypot?: false | { name?: string }\n\t/** A token from your captcha widget; verified server-side when a captcha provider is configured. */\n\tcaptchaToken?: string\n\t/** Custom layout: render fields with `useField`/`useFormState` instead of the auto-rendered field loop. */\n\tchildren?: ReactNode\n\t/** Additional CSS class names applied to the root `<form>` element (and the success node). */\n\tclassName?: string\n\t/** Replace the default submit button entirely. Receives the resolved label and submitting state. */\n\trenderSubmit?: (props: SubmitButtonRenderProps) => ReactNode\n\t/** Replace the default \"Next\" button in multi-step forms. */\n\trenderNext?: (props: NextButtonRenderProps) => ReactNode\n\t/** Replace the default \"Back\" button in multi-step forms. */\n\trenderBack?: (props: BackButtonRenderProps) => ReactNode\n\t/** CSS class forwarded to the default submit `<button>`. Ignored when `renderSubmit` is provided. */\n\tsubmitButtonClassName?: string\n\t/** CSS class forwarded to the default \"Next\" `<button>`. Ignored when `renderNext` is provided. */\n\tnextButtonClassName?: string\n\t/** CSS class forwarded to the default \"Back\" `<button>`. Ignored when `renderBack` is provided. */\n\tbackButtonClassName?: string\n}\n\nconst isEmpty = (value: unknown): boolean =>\n\tvalue == null || value === '' || (Array.isArray(value) && value.length === 0)\n\nconst FIELD_WIDTHS = new Set<string>(['full', 'half', 'third', 'twoThirds'])\n\ntype FieldHostProps = {\n\tfield: FormFieldInstance\n\trenderer: FieldRenderer\n\tlocale: string\n\tt: RendererTranslate\n}\n\nconst FieldHost = ({ field, renderer, locale, t }: FieldHostProps) => {\n\tconst id = useId()\n\tconst { value, errors, warnings, setValue, onBlur } = useField(field.name)\n\treturn createElement(renderer, {\n\t\tfield,\n\t\tid,\n\t\tname: field.name,\n\t\tvalue,\n\t\tonChange: setValue,\n\t\tonBlur,\n\t\terrors,\n\t\twarnings,\n\t\trequired: Boolean(field.required),\n\t\tlocale,\n\t\tt,\n\t})\n}\n\ntype CalcFieldHostProps = FieldHostProps & { value: unknown }\n\n/** Hosts a derived (calc) field: read-only, value supplied from `effectiveValues`, never bound via `useField`. */\nconst CalcFieldHost = ({ field, renderer, value, locale, t }: CalcFieldHostProps) => {\n\tconst id = useId()\n\treturn createElement(renderer, {\n\t\tfield,\n\t\tid,\n\t\tname: field.name,\n\t\tvalue,\n\t\tonChange: () => {},\n\t\tonBlur: () => {},\n\t\terrors: [],\n\t\twarnings: [],\n\t\trequired: false,\n\t\tdisabled: true,\n\t\tlocale,\n\t\tt,\n\t})\n}\n\n/** The headless form controller: state, progressive client validation, conditional visibility, submission, events. */\nexport const Form = ({\n\tform,\n\tfieldTypes,\n\trules,\n\trenderers,\n\tapiRoute,\n\tonSubmit,\n\tonSuccess,\n\tonError,\n\tevents,\n\tt,\n\tlocale = 'en',\n\tlayout,\n\tsubmitLabel = 'Submit',\n\tnextLabel = 'Next',\n\tbackLabel = 'Back',\n\tcloseLabel = 'Close',\n\tsuccessMessage = 'Thank you.',\n\tpresentation,\n\tpresentations,\n\tonClose,\n\ttitle,\n\tinitialValues,\n\thoneypot,\n\tcaptchaToken,\n\tchildren,\n\tclassName,\n\trenderSubmit,\n\trenderNext,\n\trenderBack,\n\tsubmitButtonClassName,\n\tnextButtonClassName,\n\tbackButtonClassName,\n}: FormProps) => {\n\tconst honeypotName = honeypot === false ? null : (honeypot?.name ?? DEFAULT_HONEYPOT_FIELD)\n\tconst honeypotRef = useRef<HTMLInputElement>(null)\n\tconst registry = useMemo(() => buildFieldTypeRegistry(fieldTypes), [fieldTypes])\n\tconst ruleRegistry = useMemo(() => buildValidationRuleRegistry(rules), [rules])\n\tconst rendererRegistry = useMemo(() => resolveRenderers(defaultRenderers, renderers), [renderers])\n\tconst presentationRegistry = useMemo(\n\t\t() => resolvePresentations(defaultPresentations, presentations),\n\t\t[presentations]\n\t)\n\tconst activePresentation: FormPresentation =\n\t\ttypeof presentation === 'object'\n\t\t\t? presentation\n\t\t\t: (presentationRegistry.get(presentation ?? form.defaultPresentation ?? 'page') ??\n\t\t\t\tpresentationRegistry.get('page') ??\n\t\t\t\tdefaultPresentationDescriptors.page)\n\tconst fieldsByName = useMemo(\n\t\t() => new Map(form.fields.map((field) => [field.name, field])),\n\t\t[form.fields]\n\t)\n\tconst translate = useMemo<RendererTranslate>(() => t ?? makeTranslate(en), [t])\n\n\t// Latest-value refs so event emission and the mount/unmount effect tolerate an inline `events` prop or a changing form id.\n\tconst sinkRef = useRef<FormEventSink>(noopEventSink)\n\tsinkRef.current = events ?? noopEventSink\n\tconst formIdRef = useRef('')\n\tformIdRef.current = String(form.id)\n\n\tconst [state, rawDispatch] = useReducer(formReducer, form.fields, (fields) =>\n\t\tinitialFormState({\n\t\t\t...Object.fromEntries(fields.map((field) => [field.name, undefined])),\n\t\t\t...(initialValues ?? {}),\n\t\t})\n\t)\n\n\t// Authoritative values for derived (calc) fields, recomputed from user answers on every change. The\n\t// server recomputes these too at submit; the client copy drives the live calc renderer, recall, and submit.\n\tconst effectiveValues = useMemo(\n\t\t() => computeCalcFields(form.fields, state.values),\n\t\t[form.fields, state.values]\n\t)\n\n\tconst recall = useMemo(\n\t\t() =>\n\t\t\tbuildRecallResolver({\n\t\t\t\tfields: form.fields,\n\t\t\t\tvalues: effectiveValues,\n\t\t\t\tregistry,\n\t\t\t\tlocale,\n\t\t\t\tt: translate,\n\t\t\t}),\n\t\t[form.fields, effectiveValues, registry, locale, translate]\n\t)\n\n\t// Multi-step is active only when a flow declares two or more steps; otherwise this is an ordinary single-step form.\n\tconst flow = form.flow && form.flow.steps.length >= 2 ? form.flow : undefined\n\tconst [currentStepId, setCurrentStepId] = useState<string | undefined>(() =>\n\t\tflow ? firstStepId(flow) : undefined\n\t)\n\tconst [history, setHistory] = useState<string[]>([])\n\n\tconst startedRef = useRef(false)\n\tconst submittedRef = useRef(false)\n\tconst submittingRef = useRef(false)\n\tconst flowRef = useRef(flow)\n\tflowRef.current = flow\n\n\tconst dispatch = useCallback((action: FormAction) => {\n\t\tif (action.type === 'SET_VALUE' && !startedRef.current) {\n\t\t\tstartedRef.current = true\n\t\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'form.started' })\n\t\t}\n\t\trawDispatch(action)\n\t}, [])\n\n\tconst validateField = useCallback(\n\t\t(name: string, value: unknown) => {\n\t\t\tconst field = fieldsByName.get(name)\n\t\t\tif (!field) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst answers = { ...effectiveValues, [name]: value }\n\t\t\t// Mirror the server: a field whose `validateWhen` is unmet is not validated; clear any stale error.\n\t\t\tif (!evaluateCondition(field.validateWhen, answers)) {\n\t\t\t\trawDispatch({ type: 'SET_FIELD_ISSUES', name, errors: [], warnings: [] })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvoid validateFieldValue({\n\t\t\t\tfield,\n\t\t\t\tvalue,\n\t\t\t\tregistry,\n\t\t\t\truleRegistry,\n\t\t\t\tanswers,\n\t\t\t\tlocale,\n\t\t\t\tt: translate,\n\t\t\t}).then(({ errors, warnings }) => {\n\t\t\t\trawDispatch({ type: 'SET_FIELD_ISSUES', name, errors, warnings })\n\t\t\t\tconst [firstError] = errors\n\t\t\t\tif (firstError !== undefined) {\n\t\t\t\t\temitFormEvent(sinkRef.current, formIdRef.current, {\n\t\t\t\t\t\ttype: 'field.errored',\n\t\t\t\t\t\tfield: name,\n\t\t\t\t\t\tmessage: firstError,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t\t[fieldsByName, effectiveValues, registry, ruleRegistry, locale, translate]\n\t)\n\n\tconst visible = visibleFields(form.fields, effectiveValues)\n\tconst stepNames = flow && currentStepId ? stepFieldNames(flow, currentStepId) : []\n\tconst stepVisible: FormFieldInstance[] = stepNames\n\t\t.map((name) => visible.find((field) => field.name === name))\n\t\t.filter((field): field is FormFieldInstance => Boolean(field))\n\n\tconst goNext = async () => {\n\t\tif (!flow || !currentStepId) {\n\t\t\treturn\n\t\t}\n\t\tconst results = await Promise.all(\n\t\t\tstepVisible\n\t\t\t\t.filter((field) => !calcExpressionOf(field))\n\t\t\t\t.filter((field) => evaluateCondition(field.validateWhen, effectiveValues))\n\t\t\t\t.map(async (field) => ({\n\t\t\t\t\tfield,\n\t\t\t\t\t...(await validateFieldValue({\n\t\t\t\t\t\tfield,\n\t\t\t\t\t\tvalue: effectiveValues[field.name],\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\truleRegistry,\n\t\t\t\t\t\tanswers: effectiveValues,\n\t\t\t\t\t\tlocale,\n\t\t\t\t\t\tt: translate,\n\t\t\t\t\t})),\n\t\t\t\t}))\n\t\t)\n\t\tlet hasError = false\n\t\tfor (const result of results) {\n\t\t\trawDispatch({ type: 'TOUCH', name: result.field.name })\n\t\t\trawDispatch({\n\t\t\t\ttype: 'SET_FIELD_ISSUES',\n\t\t\t\tname: result.field.name,\n\t\t\t\terrors: result.errors,\n\t\t\t\twarnings: result.warnings,\n\t\t\t})\n\t\t\tif (result.errors.length > 0) {\n\t\t\t\thasError = true\n\t\t\t}\n\t\t}\n\t\tif (hasError) {\n\t\t\treturn\n\t\t}\n\t\tconst next = resolveNextStepId(flow, currentStepId, state.values)\n\t\tif (!next) {\n\t\t\treturn\n\t\t}\n\t\temitFormEvent(sinkRef.current, formIdRef.current, {\n\t\t\ttype: 'step.completed',\n\t\t\tstepId: currentStepId,\n\t\t})\n\t\tsetHistory((prev) => [...prev, currentStepId])\n\t\tsetCurrentStepId(next)\n\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'step.viewed', stepId: next })\n\t}\n\n\tconst goBack = () => {\n\t\tconst prev = history[history.length - 1]\n\t\tif (prev === undefined) {\n\t\t\treturn\n\t\t}\n\t\tsetHistory((entries) => entries.slice(0, -1))\n\t\tsetCurrentStepId(prev)\n\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'step.viewed', stepId: prev })\n\t}\n\n\tuseEffect(() => {\n\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'form.viewed' })\n\t\tconst mountFlow = flowRef.current\n\t\tif (mountFlow) {\n\t\t\tconst first = firstStepId(mountFlow)\n\t\t\tif (first) {\n\t\t\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'step.viewed', stepId: first })\n\t\t\t}\n\t\t}\n\t\treturn () => {\n\t\t\tif (!submittedRef.current && !submittingRef.current) {\n\t\t\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'form.abandoned' })\n\t\t\t}\n\t\t}\n\t}, [])\n\n\tconst handleClose = useCallback(() => {\n\t\tonClose?.()\n\t}, [onClose])\n\n\tconst handleSubmit = async (event: ReactFormEvent<HTMLFormElement>) => {\n\t\tevent.preventDefault()\n\t\t// Re-entrancy guard: claim the in-flight slot before the async validation window so a fast second\n\t\t// activation (double-click, Enter + click) cannot reach the transport and POST the submission twice.\n\t\tif (submittingRef.current) {\n\t\t\treturn\n\t\t}\n\t\tsubmittingRef.current = true\n\t\tconst visible = visibleFields(form.fields, effectiveValues)\n\t\tconst results = await Promise.all(\n\t\t\t// Calc fields carry no rules and have no input; they are always satisfied, so skip validating them.\n\t\t\t// A field whose `validateWhen` is unmet is skipped too, mirroring the server (no client/server divergence).\n\t\t\tvisible\n\t\t\t\t.filter((field) => !calcExpressionOf(field))\n\t\t\t\t.filter((field) => evaluateCondition(field.validateWhen, effectiveValues))\n\t\t\t\t.map(async (field) => ({\n\t\t\t\t\tfield,\n\t\t\t\t\t...(await validateFieldValue({\n\t\t\t\t\t\tfield,\n\t\t\t\t\t\tvalue: effectiveValues[field.name],\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\truleRegistry,\n\t\t\t\t\t\tanswers: effectiveValues,\n\t\t\t\t\t\tlocale,\n\t\t\t\t\t\tt: translate,\n\t\t\t\t\t})),\n\t\t\t\t}))\n\t\t)\n\t\tconst errors: FieldErrors = {}\n\t\tconst warnings: FieldErrors = {}\n\t\tfor (const result of results) {\n\t\t\tif (result.errors.length > 0) {\n\t\t\t\terrors[result.field.name] = result.errors\n\t\t\t\tconst [firstError] = result.errors\n\t\t\t\tif (firstError !== undefined) {\n\t\t\t\t\temitFormEvent(sinkRef.current, formIdRef.current, {\n\t\t\t\t\t\ttype: 'field.errored',\n\t\t\t\t\t\tfield: result.field.name,\n\t\t\t\t\t\tmessage: firstError,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (result.warnings.length > 0) {\n\t\t\t\twarnings[result.field.name] = result.warnings\n\t\t\t}\n\t\t}\n\n\t\t// Validate sub-fields within each visible repeater, mirroring the server's per-row pass.\n\t\t// Errors are stored under the composite key `fieldName[rowIndex].subFieldName` so the\n\t\t// repeater renderer can look them up from form state and display them inline.\n\t\tfor (const field of visible.filter((f) => f.blockType === 'repeater')) {\n\t\t\tconst rows = Array.isArray(effectiveValues[field.name])\n\t\t\t\t? (effectiveValues[field.name] as Array<Record<string, unknown>>)\n\t\t\t\t: []\n\t\t\tconst subFields = Array.isArray(field.subFields)\n\t\t\t\t? (field.subFields as FormFieldInstance[])\n\t\t\t\t: []\n\t\t\tfor (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {\n\t\t\t\tconst row = rows[rowIndex] ?? {}\n\t\t\t\tfor (const subField of subFields) {\n\t\t\t\t\tif (!evaluateCondition(subField.visibleWhen, row)) continue\n\t\t\t\t\tif (!evaluateCondition(subField.validateWhen, row)) continue\n\t\t\t\t\tconst subResult = await validateFieldValue({\n\t\t\t\t\t\tfield: subField,\n\t\t\t\t\t\tvalue: row[subField.name],\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\truleRegistry,\n\t\t\t\t\t\tanswers: row,\n\t\t\t\t\t\tlocale,\n\t\t\t\t\t\tt: translate,\n\t\t\t\t\t})\n\t\t\t\t\tconst compositeKey = `${field.name}[${rowIndex}].${subField.name}`\n\t\t\t\t\tif (subResult.errors.length > 0) errors[compositeKey] = subResult.errors\n\t\t\t\t\tif (subResult.warnings.length > 0) warnings[compositeKey] = subResult.warnings\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trawDispatch({ type: 'SET_ALL_ISSUES', errors, warnings })\n\t\tif (Object.keys(errors).length > 0) {\n\t\t\tsubmittingRef.current = false\n\t\t\treturn\n\t\t}\n\t\trawDispatch({ type: 'SUBMIT_START' })\n\t\tconst values: SubmissionValue[] = visible\n\t\t\t.filter((field) => !isEmpty(effectiveValues[field.name]))\n\t\t\t.map((field) => ({ field: field.name, value: effectiveValues[field.name] }))\n\t\tif (honeypotName) {\n\t\t\tconst decoy = honeypotRef.current?.value ?? ''\n\t\t\tif (decoy !== '') {\n\t\t\t\tvalues.push({ field: honeypotName, value: decoy })\n\t\t\t}\n\t\t}\n\t\tif (captchaToken) {\n\t\t\tvalues.push({ field: CAPTCHA_TOKEN_KEY, value: captchaToken })\n\t\t}\n\t\tconst result: SubmitFormResult = onSubmit\n\t\t\t? await onSubmit({ formId: form.id, values })\n\t\t\t: await submitForm({ formId: form.id, values, apiRoute })\n\t\tsubmittingRef.current = false\n\t\tif (result.ok) {\n\t\t\tsubmittedRef.current = true\n\t\t\trawDispatch({ type: 'SUBMIT_SUCCESS' })\n\t\t\temitFormEvent(sinkRef.current, formIdRef.current, {\n\t\t\t\ttype: 'submission.created',\n\t\t\t\tsubmissionId: result.submissionId,\n\t\t\t})\n\t\t\tonSuccess?.(result.submissionId)\n\t\t\tif (activePresentation.dismissOnSuccess) {\n\t\t\t\thandleClose()\n\t\t\t}\n\t\t} else {\n\t\t\tif (result.fieldErrors) {\n\t\t\t\trawDispatch({ type: 'SET_ALL_ISSUES', errors: result.fieldErrors, warnings: {} })\n\t\t\t}\n\t\t\tconst message = result.message ?? 'Submission failed'\n\t\t\trawDispatch({ type: 'SUBMIT_ERROR', message })\n\t\t\tonError?.(message)\n\t\t}\n\t}\n\n\tconst step: FormStepInfo = flow\n\t\t? {\n\t\t\t\tflow,\n\t\t\t\tcurrentStepId,\n\t\t\t\tstepIndex: flow.steps.findIndex((s) => s.id === currentStepId),\n\t\t\t\tstepCount: flow.steps.length,\n\t\t\t\tisFirst: history.length === 0,\n\t\t\t\tisTerminal: currentStepId ? isTerminalStepId(flow, currentStepId, state.values) : true,\n\t\t\t\tgoNext: () => {\n\t\t\t\t\tvoid goNext()\n\t\t\t\t},\n\t\t\t\tgoBack,\n\t\t\t}\n\t\t: {\n\t\t\t\tstepIndex: 0,\n\t\t\t\tstepCount: 1,\n\t\t\t\tisFirst: true,\n\t\t\t\tisTerminal: true,\n\t\t\t\tgoNext: () => {},\n\t\t\t\tgoBack: () => {},\n\t\t\t}\n\n\tconst contextValue = { state, dispatch, validateField, locale, step, rendererRegistry }\n\n\tconst PresentationWrapper = activePresentation.Wrapper\n\tconst wrap = (content: ReactNode): ReactNode =>\n\t\tPresentationWrapper ? (\n\t\t\t<PresentationWrapper\n\t\t\t\tpresentation={activePresentation}\n\t\t\t\topen\n\t\t\t\tonClose={handleClose}\n\t\t\t\ttitle={title}\n\t\t\t\tcloseLabel={closeLabel}\n\t\t\t>\n\t\t\t\t{content}\n\t\t\t</PresentationWrapper>\n\t\t) : (\n\t\t\tcontent\n\t\t)\n\n\tif (children !== undefined) {\n\t\treturn (\n\t\t\t<FormContext.Provider value={contextValue}>\n\t\t\t\t{wrap(\n\t\t\t\t\t<form\n\t\t\t\t\t\tclassName={cn('fb-form-root', className)}\n\t\t\t\t\t\tnoValidate\n\t\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\t\tdata-fb-presentation={activePresentation.name}\n\t\t\t\t\t\tdata-fb-density={activePresentation.density}\n\t\t\t\t\t>\n\t\t\t\t\t\t{honeypotName ? <Honeypot name={honeypotName} inputRef={honeypotRef} /> : null}\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</form>\n\t\t\t\t)}\n\t\t\t</FormContext.Provider>\n\t\t)\n\t}\n\n\tif (state.submitted) {\n\t\treturn (\n\t\t\t<FormContext.Provider value={contextValue}>\n\t\t\t\t{wrap(\n\t\t\t\t\t<p\n\t\t\t\t\t\trole=\"status\"\n\t\t\t\t\t\tclassName={cn('fb-form__success', className)}\n\t\t\t\t\t\tdata-fb-presentation={activePresentation.name}\n\t\t\t\t\t\tdata-fb-density={activePresentation.density}\n\t\t\t\t\t>\n\t\t\t\t\t\t{interpolate(successMessage, recall)}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</FormContext.Provider>\n\t\t)\n\t}\n\n\tconst rendered = (flow ? stepVisible : visible).filter(\n\t\t(field) => field.hidden !== true && field.calcDisplay !== false\n\t)\n\n\treturn (\n\t\t<FormContext.Provider value={contextValue}>\n\t\t\t{wrap(\n\t\t\t\t<form\n\t\t\t\t\tclassName={cn('fb-form-root', className)}\n\t\t\t\t\tnoValidate\n\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\tdata-fb-presentation={activePresentation.name}\n\t\t\t\t\tdata-fb-density={activePresentation.density}\n\t\t\t\t>\n\t\t\t\t\t{honeypotName ? <Honeypot name={honeypotName} inputRef={honeypotRef} /> : null}\n\t\t\t\t\t<FormLayout enabled={layout !== false}>\n\t\t\t\t\t\t{rendered.map((field) => {\n\t\t\t\t\t\t\tconst renderer = rendererRegistry.get(field.blockType)\n\t\t\t\t\t\t\tif (!renderer) {\n\t\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst width: FieldWidth | undefined =\n\t\t\t\t\t\t\t\ttypeof field.width === 'string' && FIELD_WIDTHS.has(field.width)\n\t\t\t\t\t\t\t\t\t? (field.width as FieldWidth)\n\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\tconst recalledField = applyRecall(field, recall)\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div key={field.name} {...widthProps(width)}>\n\t\t\t\t\t\t\t\t\t{calcExpressionOf(field) ? (\n\t\t\t\t\t\t\t\t\t\t<CalcFieldHost\n\t\t\t\t\t\t\t\t\t\t\tfield={recalledField}\n\t\t\t\t\t\t\t\t\t\t\trenderer={renderer}\n\t\t\t\t\t\t\t\t\t\t\tvalue={effectiveValues[field.name]}\n\t\t\t\t\t\t\t\t\t\t\tlocale={locale}\n\t\t\t\t\t\t\t\t\t\t\tt={translate}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t<FieldHost\n\t\t\t\t\t\t\t\t\t\t\tfield={recalledField}\n\t\t\t\t\t\t\t\t\t\t\trenderer={renderer}\n\t\t\t\t\t\t\t\t\t\t\tlocale={locale}\n\t\t\t\t\t\t\t\t\t\t\tt={translate}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t})}\n\t\t\t\t\t</FormLayout>\n\t\t\t\t\t{state.submitError ? (\n\t\t\t\t\t\t<p role=\"alert\" className=\"fb-form__submit-error\">\n\t\t\t\t\t\t\t{state.submitError}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{flow ? (\n\t\t\t\t\t\t<div className=\"fb-form__controls\">\n\t\t\t\t\t\t\t{!step.isFirst ? (\n\t\t\t\t\t\t\t\trenderBack ? (\n\t\t\t\t\t\t\t\t\trenderBack({ label: backLabel, submitting: state.submitting, onClick: goBack })\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tclassName={backButtonClassName}\n\t\t\t\t\t\t\t\t\t\tonClick={goBack}\n\t\t\t\t\t\t\t\t\t\tdisabled={state.submitting}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{backLabel}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t{step.isTerminal ? (\n\t\t\t\t\t\t\t\trenderSubmit ? (\n\t\t\t\t\t\t\t\t\trenderSubmit({ label: submitLabel, submitting: state.submitting })\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\t\t\tclassName={submitButtonClassName}\n\t\t\t\t\t\t\t\t\t\tdisabled={state.submitting}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{submitLabel}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t) : renderNext ? (\n\t\t\t\t\t\t\t\trenderNext({\n\t\t\t\t\t\t\t\t\tlabel: nextLabel,\n\t\t\t\t\t\t\t\t\tsubmitting: state.submitting,\n\t\t\t\t\t\t\t\t\tonClick: () => void goNext(),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tclassName={nextButtonClassName}\n\t\t\t\t\t\t\t\t\tdisabled={state.submitting}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tvoid goNext()\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{nextLabel}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : renderSubmit ? (\n\t\t\t\t\t\trenderSubmit({ label: submitLabel, submitting: state.submitting })\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<button type=\"submit\" className={submitButtonClassName} disabled={state.submitting}>\n\t\t\t\t\t\t\t{submitLabel}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t)}\n\t\t\t\t</form>\n\t\t\t)}\n\t\t</FormContext.Provider>\n\t)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Form.js","names":[],"sources":["../../src/react/Form.tsx"],"sourcesContent":["'use client'\n\nimport {\n\tcreateElement,\n\ttype FormEvent as ReactFormEvent,\n\ttype ReactNode,\n\tuseCallback,\n\tuseEffect,\n\tuseId,\n\tuseMemo,\n\tuseReducer,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { serializeBody } from '../actions/body/serializeBody'\nimport { calcExpressionOf, computeCalcFields } from '../calc/computeCalcFields'\nimport { evaluateCondition } from '../conditions/evaluate'\nimport { noopEventSink } from '../events/noopSink'\nimport type { FormEventSink } from '../events/types'\nimport type { AnyFormFieldDefinition } from '../fields/types'\nimport { firstStepId, isTerminalStepId, resolveNextStepId, stepFieldNames } from '../flow/engine'\nimport type {\n\tFormDisplaySettings,\n\tFormDocument,\n\tFormPollSettings,\n\tFormResponseSettings,\n} from '../form/types'\nimport {\n\tDEFAULT_PRESENTATION_NAME,\n\tdefaultPresentationDescriptors,\n} from '../presentations/defaults'\nimport { interpolate } from '../recall/interpolate'\nimport { buildRecallResolver } from '../recall/resolver'\nimport { CAPTCHA_TOKEN_KEY, DEFAULT_HONEYPOT_FIELD } from '../spam/constants'\nimport type { FormFieldInstance, SubmissionValue } from '../submissions/types'\nimport { en } from '../translations/en'\nimport { keys } from '../translations/keys'\nimport { makeTranslate } from '../translations/makeTranslate'\nimport type { AnyValidationRuleDefinition } from '../validation/types'\nimport { cn } from './cn'\nimport type { FieldRenderer, RendererTranslate } from './contract'\nimport { emitFormEvent } from './events'\nimport { FormContext, type FormContextValue, type FormStepInfo } from './FormContext'\nimport {\n\ttype BackButtonRenderProps,\n\tFormControls,\n\ttype NextButtonRenderProps,\n\ttype SubmitButtonRenderProps,\n} from './FormControls'\nimport { type FieldWidth, FormLayout, widthProps } from './FormLayout'\nimport { Honeypot } from './Honeypot'\nimport { defaultPresentations } from './presentation/presentations'\nimport { type PresentationsConfig, resolvePresentations } from './presentation/registry'\nimport type { FormPresentation } from './presentation/types'\nimport { applyRecall } from './recall'\nimport { type RenderersConfig, resolveRenderers } from './registry'\nimport { defaultRenderers } from './renderers'\nimport { buildFieldTypeRegistry, buildValidationRuleRegistry, visibleFields } from './resolveForm'\nimport {\n\ttype FieldErrors,\n\ttype FormAction,\n\tformReducer,\n\tinitialFormState,\n\tseedFieldValues,\n} from './state'\nimport { type SubmitFormResult, type SubmitHandler, submitForm } from './submitForm'\nimport { useField } from './useField'\nimport { validateFieldValue } from './validateField'\n\nexport type {\n\tBackButtonRenderProps,\n\tNextButtonRenderProps,\n\tSubmitButtonRenderProps,\n} from './FormControls'\n// FormResponseSettings, FormDisplaySettings, FormPollSettings, and FormDocument live in\n// `../form/types` (no 'use client') so server code (e.g. `toFormDocument` in a Server Component)\n// can use them without pulling in this client module. Re-exported here so `./react` and existing\n// `from './Form'` imports keep working unchanged.\nexport type { FormDisplaySettings, FormDocument, FormPollSettings, FormResponseSettings }\n\nexport type FormProps = {\n\tform: FormDocument\n\tfieldTypes?: AnyFormFieldDefinition[]\n\trules?: AnyValidationRuleDefinition[]\n\trenderers?: RenderersConfig\n\tapiRoute?: string\n\tonSubmit?: SubmitHandler\n\tonSuccess?: (submissionId?: string) => void\n\tonError?: (message: string) => void\n\tevents?: FormEventSink\n\tt?: RendererTranslate\n\tlocale?: string\n\tlayout?: boolean\n\t/** Submit button label. Precedence: this prop, then the form's `response.submitLabel`, then the translated default. */\n\tsubmitLabel?: string\n\t/** \"Next\" button label for multi-step forms. Defaults to the translated `'Next'`. */\n\tnextLabel?: string\n\t/** \"Back\" button label for multi-step forms. Defaults to the translated `'Back'`. */\n\tbackLabel?: string\n\t/** Label for the overlay close control (modal/drawer). */\n\tcloseLabel?: string\n\tsuccessMessage?: string\n\t/** Active presentation: a name into the registry or an inline presentation. Defaults to `'page'` when omitted. */\n\tpresentation?: string | FormPresentation\n\t/** Per-render presentation overrides merged onto the defaults (add, replace, or `false` to remove). */\n\tpresentations?: PresentationsConfig\n\t/** Invoked when an overlay presentation dismisses (close button, Escape, outside click, or `dismissOnSuccess`). */\n\tonClose?: () => void\n\t/**\n\t * Accessible name for an overlay surface (modal/drawer). Hosts choosing between a trigger\n\t * label and the form's own display title should prefer `form.display?.title` when\n\t * `form.display?.showTitle` is set, falling back to their own label otherwise.\n\t */\n\ttitle?: string\n\t/** Seed initial field values (e.g. from `valuesFromSearchParams`). Still validated on submit. */\n\tinitialValues?: Record<string, unknown>\n\t/** Honeypot decoy (on by default). `false` removes it; `{ name }` matches a customized server `spam.honeypot.fieldName`. */\n\thoneypot?: false | { name?: string }\n\t/** A token from your captcha widget; verified server-side when a captcha provider is configured. */\n\tcaptchaToken?: string\n\t/** Custom layout: render fields with `useField`/`useFormState` instead of the auto-rendered field loop. */\n\tchildren?: ReactNode\n\t/** Additional CSS class names applied to the root `<form>` element (and the success node). */\n\tclassName?: string\n\t/** Replace the default submit button entirely. Receives the resolved label and submitting state. */\n\trenderSubmit?: (props: SubmitButtonRenderProps) => ReactNode\n\t/** Replace the default \"Next\" button in multi-step forms. */\n\trenderNext?: (props: NextButtonRenderProps) => ReactNode\n\t/** Replace the default \"Back\" button in multi-step forms. */\n\trenderBack?: (props: BackButtonRenderProps) => ReactNode\n\t/** CSS class forwarded to the default submit `<button>`. Ignored when `renderSubmit` is provided. */\n\tsubmitButtonClassName?: string\n\t/** CSS class forwarded to the default \"Next\" `<button>`. Ignored when `renderNext` is provided. */\n\tnextButtonClassName?: string\n\t/** CSS class forwarded to the default \"Back\" `<button>`. Ignored when `renderBack` is provided. */\n\tbackButtonClassName?: string\n}\n\nconst isEmpty = (value: unknown): boolean =>\n\tvalue == null || value === '' || (Array.isArray(value) && value.length === 0)\n\nconst FIELD_WIDTHS = new Set<string>(['full', 'half', 'third', 'twoThirds'])\n\ntype FieldHostProps = {\n\tfield: FormFieldInstance\n\trenderer: FieldRenderer\n\tlocale: string\n\tt: RendererTranslate\n}\n\nconst FieldHost = ({ field, renderer, locale, t }: FieldHostProps) => {\n\tconst id = useId()\n\tconst { value, errors, warnings, setValue, onBlur } = useField(field.name)\n\treturn createElement(renderer, {\n\t\tfield,\n\t\tid,\n\t\tname: field.name,\n\t\tvalue,\n\t\tonChange: setValue,\n\t\tonBlur,\n\t\terrors,\n\t\twarnings,\n\t\trequired: Boolean(field.required),\n\t\tlocale,\n\t\tt,\n\t})\n}\n\ntype CalcFieldHostProps = FieldHostProps & { value: unknown }\n\n/** Hosts a derived (calc) field: read-only, value supplied from `effectiveValues`, never bound via `useField`. */\nconst CalcFieldHost = ({ field, renderer, value, locale, t }: CalcFieldHostProps) => {\n\tconst id = useId()\n\treturn createElement(renderer, {\n\t\tfield,\n\t\tid,\n\t\tname: field.name,\n\t\tvalue,\n\t\tonChange: () => {},\n\t\tonBlur: () => {},\n\t\terrors: [],\n\t\twarnings: [],\n\t\trequired: false,\n\t\tdisabled: true,\n\t\tlocale,\n\t\tt,\n\t})\n}\n\n/** The headless form controller: state, progressive client validation, conditional visibility, submission, events. */\nexport const Form = ({\n\tform,\n\tfieldTypes,\n\trules,\n\trenderers,\n\tapiRoute,\n\tonSubmit,\n\tonSuccess,\n\tonError,\n\tevents,\n\tt,\n\tlocale = 'en',\n\tlayout,\n\tsubmitLabel,\n\tnextLabel,\n\tbackLabel,\n\tcloseLabel = 'Close',\n\tsuccessMessage = 'Thank you.',\n\tpresentation,\n\tpresentations,\n\tonClose,\n\ttitle,\n\tinitialValues,\n\thoneypot,\n\tcaptchaToken,\n\tchildren,\n\tclassName,\n\trenderSubmit,\n\trenderNext,\n\trenderBack,\n\tsubmitButtonClassName,\n\tnextButtonClassName,\n\tbackButtonClassName,\n}: FormProps) => {\n\tconst honeypotName = honeypot === false ? null : (honeypot?.name ?? DEFAULT_HONEYPOT_FIELD)\n\tconst honeypotRef = useRef<HTMLInputElement>(null)\n\tconst registry = useMemo(() => buildFieldTypeRegistry(fieldTypes), [fieldTypes])\n\tconst ruleRegistry = useMemo(() => buildValidationRuleRegistry(rules), [rules])\n\tconst rendererRegistry = useMemo(() => resolveRenderers(defaultRenderers, renderers), [renderers])\n\tconst presentationRegistry = useMemo(\n\t\t() => resolvePresentations(defaultPresentations, presentations),\n\t\t[presentations]\n\t)\n\tconst activePresentation: FormPresentation =\n\t\ttypeof presentation === 'object'\n\t\t\t? presentation\n\t\t\t: (presentationRegistry.get(presentation ?? DEFAULT_PRESENTATION_NAME) ??\n\t\t\t\tpresentationRegistry.get(DEFAULT_PRESENTATION_NAME) ??\n\t\t\t\tdefaultPresentationDescriptors.page)\n\tconst fieldsByName = useMemo(\n\t\t() => new Map(form.fields.map((field) => [field.name, field])),\n\t\t[form.fields]\n\t)\n\tconst translate = useMemo<RendererTranslate>(() => t ?? makeTranslate(en), [t])\n\tconst docSubmitLabel =\n\t\ttypeof form.response?.submitLabel === 'string' && form.response.submitLabel.length > 0\n\t\t\t? form.response.submitLabel\n\t\t\t: undefined\n\tconst labels = useMemo(\n\t\t() => ({\n\t\t\tback: backLabel ?? translate(keys.formBack),\n\t\t\tnext: nextLabel ?? translate(keys.formNext),\n\t\t\tsubmit: submitLabel ?? docSubmitLabel ?? translate(keys.formSubmit),\n\t\t}),\n\t\t[backLabel, nextLabel, submitLabel, docSubmitLabel, translate]\n\t)\n\n\t// Latest-value refs so event emission and the mount/unmount effect tolerate an inline `events` prop or a changing form id.\n\tconst sinkRef = useRef<FormEventSink>(noopEventSink)\n\tsinkRef.current = events ?? noopEventSink\n\tconst formIdRef = useRef('')\n\tformIdRef.current = String(form.id)\n\n\tconst [state, rawDispatch] = useReducer(formReducer, form.fields, (fields) =>\n\t\tinitialFormState({\n\t\t\t...seedFieldValues(fields),\n\t\t\t...(initialValues ?? {}),\n\t\t})\n\t)\n\n\t// Authoritative values for derived (calc) fields, recomputed from user answers on every change. The\n\t// server recomputes these too at submit; the client copy drives the live calc renderer, recall, and submit.\n\tconst effectiveValues = useMemo(\n\t\t() => computeCalcFields(form.fields, state.values),\n\t\t[form.fields, state.values]\n\t)\n\n\tconst recall = useMemo(\n\t\t() =>\n\t\t\tbuildRecallResolver({\n\t\t\t\tfields: form.fields,\n\t\t\t\tvalues: effectiveValues,\n\t\t\t\tregistry,\n\t\t\t\tlocale,\n\t\t\t\tt: translate,\n\t\t\t}),\n\t\t[form.fields, effectiveValues, registry, locale, translate]\n\t)\n\n\t// Multi-step is active only when a flow declares two or more steps; otherwise this is an ordinary single-step form.\n\tconst flow = form.flow && form.flow.steps.length >= 2 ? form.flow : undefined\n\tconst [currentStepId, setCurrentStepId] = useState<string | undefined>(() =>\n\t\tflow ? firstStepId(flow) : undefined\n\t)\n\tconst [history, setHistory] = useState<string[]>([])\n\n\tconst startedRef = useRef(false)\n\tconst submittedRef = useRef(false)\n\tconst submittingRef = useRef(false)\n\tconst flowRef = useRef(flow)\n\tflowRef.current = flow\n\n\tconst dispatch = useCallback((action: FormAction) => {\n\t\tif (action.type === 'SET_VALUE' && !startedRef.current) {\n\t\t\tstartedRef.current = true\n\t\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'form.started' })\n\t\t}\n\t\trawDispatch(action)\n\t}, [])\n\n\tconst validateField = useCallback(\n\t\t(name: string, value: unknown) => {\n\t\t\tconst field = fieldsByName.get(name)\n\t\t\tif (!field) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst answers = { ...effectiveValues, [name]: value }\n\t\t\t// Mirror the server: a field whose `validateWhen` is unmet is not validated; clear any stale error.\n\t\t\tif (!evaluateCondition(field.validateWhen, answers)) {\n\t\t\t\trawDispatch({ type: 'SET_FIELD_ISSUES', name, errors: [], warnings: [] })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tvoid validateFieldValue({\n\t\t\t\tfield,\n\t\t\t\tvalue,\n\t\t\t\tregistry,\n\t\t\t\truleRegistry,\n\t\t\t\tanswers,\n\t\t\t\tlocale,\n\t\t\t\tt: translate,\n\t\t\t}).then(({ errors, warnings }) => {\n\t\t\t\trawDispatch({ type: 'SET_FIELD_ISSUES', name, errors, warnings })\n\t\t\t\tconst [firstError] = errors\n\t\t\t\tif (firstError !== undefined) {\n\t\t\t\t\temitFormEvent(sinkRef.current, formIdRef.current, {\n\t\t\t\t\t\ttype: 'field.errored',\n\t\t\t\t\t\tfield: name,\n\t\t\t\t\t\tmessage: firstError,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t\t[fieldsByName, effectiveValues, registry, ruleRegistry, locale, translate]\n\t)\n\n\tconst visible = visibleFields(form.fields, effectiveValues)\n\n\t/** Answered visible fields as submission values. Display-only ('none' kind) fields never contribute. */\n\tconst answeredValues = (): SubmissionValue[] =>\n\t\tvisibleFields(form.fields, effectiveValues)\n\t\t\t.filter((field) => registry.get(field.blockType)?.value !== 'none')\n\t\t\t.filter((field) => !isEmpty(effectiveValues[field.name]))\n\t\t\t.map((field) => ({ field: field.name, value: effectiveValues[field.name] }))\n\n\tconst stepNames = flow && currentStepId ? stepFieldNames(flow, currentStepId) : []\n\tconst stepVisible: FormFieldInstance[] = stepNames\n\t\t.map((name) => visible.find((field) => field.name === name))\n\t\t.filter((field): field is FormFieldInstance => Boolean(field))\n\n\tconst goNext = async () => {\n\t\tif (!flow || !currentStepId) {\n\t\t\treturn\n\t\t}\n\t\tconst results = await Promise.all(\n\t\t\tstepVisible\n\t\t\t\t.filter((field) => !calcExpressionOf(field))\n\t\t\t\t.filter((field) => registry.get(field.blockType)?.value !== 'none')\n\t\t\t\t.filter((field) => evaluateCondition(field.validateWhen, effectiveValues))\n\t\t\t\t.map(async (field) => ({\n\t\t\t\t\tfield,\n\t\t\t\t\t...(await validateFieldValue({\n\t\t\t\t\t\tfield,\n\t\t\t\t\t\tvalue: effectiveValues[field.name],\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\truleRegistry,\n\t\t\t\t\t\tanswers: effectiveValues,\n\t\t\t\t\t\tlocale,\n\t\t\t\t\t\tt: translate,\n\t\t\t\t\t})),\n\t\t\t\t}))\n\t\t)\n\t\tlet hasError = false\n\t\tfor (const result of results) {\n\t\t\trawDispatch({ type: 'TOUCH', name: result.field.name })\n\t\t\trawDispatch({\n\t\t\t\ttype: 'SET_FIELD_ISSUES',\n\t\t\t\tname: result.field.name,\n\t\t\t\terrors: result.errors,\n\t\t\t\twarnings: result.warnings,\n\t\t\t})\n\t\t\tif (result.errors.length > 0) {\n\t\t\t\thasError = true\n\t\t\t}\n\t\t}\n\t\tif (hasError) {\n\t\t\treturn\n\t\t}\n\t\tconst next = resolveNextStepId(flow, currentStepId, effectiveValues)\n\t\tif (!next) {\n\t\t\treturn\n\t\t}\n\t\temitFormEvent(sinkRef.current, formIdRef.current, {\n\t\t\ttype: 'step.completed',\n\t\t\tstepId: currentStepId,\n\t\t})\n\t\tsetHistory((prev) => [...prev, currentStepId])\n\t\tsetCurrentStepId(next)\n\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'step.viewed', stepId: next })\n\t}\n\n\tconst goBack = () => {\n\t\tconst prev = history[history.length - 1]\n\t\tif (prev === undefined) {\n\t\t\treturn\n\t\t}\n\t\tsetHistory((entries) => entries.slice(0, -1))\n\t\tsetCurrentStepId(prev)\n\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'step.viewed', stepId: prev })\n\t}\n\n\tuseEffect(() => {\n\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'form.viewed' })\n\t\tconst mountFlow = flowRef.current\n\t\tif (mountFlow) {\n\t\t\tconst first = firstStepId(mountFlow)\n\t\t\tif (first) {\n\t\t\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'step.viewed', stepId: first })\n\t\t\t}\n\t\t}\n\t\treturn () => {\n\t\t\tif (!submittedRef.current && !submittingRef.current) {\n\t\t\t\temitFormEvent(sinkRef.current, formIdRef.current, { type: 'form.abandoned' })\n\t\t\t}\n\t\t}\n\t}, [])\n\n\tconst handleClose = useCallback(() => {\n\t\tonClose?.()\n\t}, [onClose])\n\n\tconst handleSubmit = async (event: ReactFormEvent<HTMLFormElement>) => {\n\t\tevent.preventDefault()\n\t\t// Re-entrancy guard: claim the in-flight slot before the async validation window so a fast second\n\t\t// activation (double-click, Enter + click) cannot reach the transport and POST the submission twice.\n\t\tif (submittingRef.current) {\n\t\t\treturn\n\t\t}\n\t\tsubmittingRef.current = true\n\t\tconst visible = visibleFields(form.fields, effectiveValues)\n\t\tconst results = await Promise.all(\n\t\t\t// Calc fields carry no rules and have no input; they are always satisfied, so skip validating them.\n\t\t\t// Display-only ('none' kind, e.g. message) fields are skipped too, mirroring the server.\n\t\t\t// A field whose `validateWhen` is unmet is skipped too, mirroring the server (no client/server divergence).\n\t\t\tvisible\n\t\t\t\t.filter((field) => !calcExpressionOf(field))\n\t\t\t\t.filter((field) => registry.get(field.blockType)?.value !== 'none')\n\t\t\t\t.filter((field) => evaluateCondition(field.validateWhen, effectiveValues))\n\t\t\t\t.map(async (field) => ({\n\t\t\t\t\tfield,\n\t\t\t\t\t...(await validateFieldValue({\n\t\t\t\t\t\tfield,\n\t\t\t\t\t\tvalue: effectiveValues[field.name],\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\truleRegistry,\n\t\t\t\t\t\tanswers: effectiveValues,\n\t\t\t\t\t\tlocale,\n\t\t\t\t\t\tt: translate,\n\t\t\t\t\t})),\n\t\t\t\t}))\n\t\t)\n\t\tconst errors: FieldErrors = {}\n\t\tconst warnings: FieldErrors = {}\n\t\tfor (const result of results) {\n\t\t\tif (result.errors.length > 0) {\n\t\t\t\terrors[result.field.name] = result.errors\n\t\t\t\tconst [firstError] = result.errors\n\t\t\t\tif (firstError !== undefined) {\n\t\t\t\t\temitFormEvent(sinkRef.current, formIdRef.current, {\n\t\t\t\t\t\ttype: 'field.errored',\n\t\t\t\t\t\tfield: result.field.name,\n\t\t\t\t\t\tmessage: firstError,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (result.warnings.length > 0) {\n\t\t\t\twarnings[result.field.name] = result.warnings\n\t\t\t}\n\t\t}\n\n\t\t// Validate sub-fields within each visible repeater, mirroring the server's per-row pass.\n\t\t// Errors are stored under the composite key `fieldName[rowIndex].subFieldName` so the\n\t\t// repeater renderer can look them up from form state and display them inline.\n\t\tfor (const field of visible.filter((f) => f.blockType === 'repeater')) {\n\t\t\tconst rows = Array.isArray(effectiveValues[field.name])\n\t\t\t\t? (effectiveValues[field.name] as Array<Record<string, unknown>>)\n\t\t\t\t: []\n\t\t\tconst subFields = Array.isArray(field.subFields)\n\t\t\t\t? (field.subFields as FormFieldInstance[])\n\t\t\t\t: []\n\t\t\tfor (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {\n\t\t\t\tconst row = rows[rowIndex] ?? {}\n\t\t\t\tfor (const subField of subFields) {\n\t\t\t\t\tif (!evaluateCondition(subField.visibleWhen, row)) continue\n\t\t\t\t\tif (!evaluateCondition(subField.validateWhen, row)) continue\n\t\t\t\t\tconst subResult = await validateFieldValue({\n\t\t\t\t\t\tfield: subField,\n\t\t\t\t\t\tvalue: row[subField.name],\n\t\t\t\t\t\tregistry,\n\t\t\t\t\t\truleRegistry,\n\t\t\t\t\t\tanswers: row,\n\t\t\t\t\t\tlocale,\n\t\t\t\t\t\tt: translate,\n\t\t\t\t\t})\n\t\t\t\t\tconst compositeKey = `${field.name}[${rowIndex}].${subField.name}`\n\t\t\t\t\tif (subResult.errors.length > 0) errors[compositeKey] = subResult.errors\n\t\t\t\t\tif (subResult.warnings.length > 0) warnings[compositeKey] = subResult.warnings\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trawDispatch({ type: 'SET_ALL_ISSUES', errors, warnings })\n\t\tif (Object.keys(errors).length > 0) {\n\t\t\tsubmittingRef.current = false\n\t\t\treturn\n\t\t}\n\t\trawDispatch({ type: 'SUBMIT_START' })\n\t\tconst values: SubmissionValue[] = answeredValues()\n\t\tif (honeypotName) {\n\t\t\tconst decoy = honeypotRef.current?.value ?? ''\n\t\t\tif (decoy !== '') {\n\t\t\t\tvalues.push({ field: honeypotName, value: decoy })\n\t\t\t}\n\t\t}\n\t\tif (captchaToken) {\n\t\t\tvalues.push({ field: CAPTCHA_TOKEN_KEY, value: captchaToken })\n\t\t}\n\t\tconst result: SubmitFormResult = onSubmit\n\t\t\t? await onSubmit({ formId: form.id, values })\n\t\t\t: await submitForm({ formId: form.id, values, apiRoute })\n\t\tsubmittingRef.current = false\n\t\tif (result.ok) {\n\t\t\tsubmittedRef.current = true\n\t\t\trawDispatch({ type: 'SUBMIT_SUCCESS' })\n\t\t\temitFormEvent(sinkRef.current, formIdRef.current, {\n\t\t\t\ttype: 'submission.created',\n\t\t\t\tsubmissionId: result.submissionId,\n\t\t\t})\n\t\t\tonSuccess?.(result.submissionId)\n\t\t\tif (activePresentation.dismissOnSuccess) {\n\t\t\t\thandleClose()\n\t\t\t}\n\t\t\tconst redirectUrl =\n\t\t\t\tform.response?.type === 'redirect' ? form.response.redirect?.url : undefined\n\t\t\t// Part of submit handling, not rendering: fires on the custom-`children` path too.\n\t\t\t// Browser-only: no-op during SSR or in non-DOM test environments.\n\t\t\tif (\n\t\t\t\ttypeof redirectUrl === 'string' &&\n\t\t\t\tredirectUrl.length > 0 &&\n\t\t\t\ttypeof window !== 'undefined'\n\t\t\t) {\n\t\t\t\twindow.location.assign(redirectUrl)\n\t\t\t}\n\t\t} else {\n\t\t\tif (result.fieldErrors) {\n\t\t\t\trawDispatch({ type: 'SET_ALL_ISSUES', errors: result.fieldErrors, warnings: {} })\n\t\t\t}\n\t\t\tconst message = result.message ?? 'Submission failed'\n\t\t\trawDispatch({ type: 'SUBMIT_ERROR', message })\n\t\t\tonError?.(message)\n\t\t}\n\t}\n\n\tconst step: FormStepInfo = flow\n\t\t? {\n\t\t\t\tflow,\n\t\t\t\tcurrentStepId,\n\t\t\t\tstepIndex: flow.steps.findIndex((s) => s.id === currentStepId),\n\t\t\t\tstepCount: flow.steps.length,\n\t\t\t\tisFirst: history.length === 0,\n\t\t\t\tisTerminal: currentStepId ? isTerminalStepId(flow, currentStepId, effectiveValues) : true,\n\t\t\t\tgoNext: () => {\n\t\t\t\t\tvoid goNext()\n\t\t\t\t},\n\t\t\t\tgoBack,\n\t\t\t}\n\t\t: {\n\t\t\t\tstepIndex: 0,\n\t\t\t\tstepCount: 1,\n\t\t\t\tisFirst: true,\n\t\t\t\tisTerminal: true,\n\t\t\t\tgoNext: () => {},\n\t\t\t\tgoBack: () => {},\n\t\t\t}\n\n\tconst contextValue: FormContextValue = {\n\t\tstate,\n\t\tdispatch,\n\t\tvalidateField,\n\t\tlocale,\n\t\tstep,\n\t\trendererRegistry,\n\t\tlabels,\n\t\tt: translate,\n\t\teffectiveValues,\n\t}\n\n\tconst PresentationWrapper = activePresentation.Wrapper\n\tconst wrap = (content: ReactNode): ReactNode =>\n\t\tPresentationWrapper ? (\n\t\t\t<PresentationWrapper\n\t\t\t\tpresentation={activePresentation}\n\t\t\t\topen\n\t\t\t\tonClose={handleClose}\n\t\t\t\ttitle={title}\n\t\t\t\tcloseLabel={closeLabel}\n\t\t\t>\n\t\t\t\t{content}\n\t\t\t</PresentationWrapper>\n\t\t) : (\n\t\t\tcontent\n\t\t)\n\n\tif (children !== undefined) {\n\t\treturn (\n\t\t\t<FormContext.Provider value={contextValue}>\n\t\t\t\t{wrap(\n\t\t\t\t\t<form\n\t\t\t\t\t\tclassName={cn('fb-form-root', className)}\n\t\t\t\t\t\tnoValidate\n\t\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\t\tdata-fb-presentation={activePresentation.name}\n\t\t\t\t\t\tdata-fb-density={activePresentation.density}\n\t\t\t\t\t>\n\t\t\t\t\t\t{honeypotName ? <Honeypot name={honeypotName} inputRef={honeypotRef} /> : null}\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</form>\n\t\t\t\t)}\n\t\t\t</FormContext.Provider>\n\t\t)\n\t}\n\n\tif (state.submitted) {\n\t\tconst responseMessage =\n\t\t\tform.response?.type === 'message' || form.response?.type == null\n\t\t\t\t? form.response?.message\n\t\t\t\t: undefined\n\t\tconst responseHtml = responseMessage\n\t\t\t? serializeBody(responseMessage, { values: answeredValues(), descriptors: [] })\n\t\t\t: undefined\n\t\treturn (\n\t\t\t<FormContext.Provider value={contextValue}>\n\t\t\t\t{wrap(\n\t\t\t\t\tresponseHtml ? (\n\t\t\t\t\t\t// Safe to inject: serializeBody HTML-escapes all text (recall values included) and sanitizes link URLs.\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\trole=\"status\"\n\t\t\t\t\t\t\tclassName={cn('fb-form__success', className)}\n\t\t\t\t\t\t\tdata-fb-presentation={activePresentation.name}\n\t\t\t\t\t\t\tdata-fb-density={activePresentation.density}\n\t\t\t\t\t\t\t// biome-ignore lint/security/noDangerouslySetInnerHtml: HTML is produced by our escaping serializer, never raw user input\n\t\t\t\t\t\t\tdangerouslySetInnerHTML={{ __html: responseHtml }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p\n\t\t\t\t\t\t\trole=\"status\"\n\t\t\t\t\t\t\tclassName={cn('fb-form__success', className)}\n\t\t\t\t\t\t\tdata-fb-presentation={activePresentation.name}\n\t\t\t\t\t\t\tdata-fb-density={activePresentation.density}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{interpolate(successMessage, recall)}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)\n\t\t\t\t)}\n\t\t\t</FormContext.Provider>\n\t\t)\n\t}\n\n\tconst rendered = (flow ? stepVisible : visible).filter(\n\t\t(field) => field.hidden !== true && field.calcDisplay !== false\n\t)\n\n\tconst displayTitleText =\n\t\tform.display?.showTitle &&\n\t\ttypeof form.display.title === 'string' &&\n\t\tform.display.title.length > 0\n\t\t\t? interpolate(form.display.title, recall)\n\t\t\t: undefined\n\tconst displayIntroHtml = form.display?.intro\n\t\t? serializeBody(form.display.intro, { values: answeredValues(), descriptors: [] })\n\t\t: ''\n\n\treturn (\n\t\t<FormContext.Provider value={contextValue}>\n\t\t\t{wrap(\n\t\t\t\t<form\n\t\t\t\t\tclassName={cn('fb-form-root', className)}\n\t\t\t\t\tnoValidate\n\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\tdata-fb-presentation={activePresentation.name}\n\t\t\t\t\tdata-fb-density={activePresentation.density}\n\t\t\t\t>\n\t\t\t\t\t{honeypotName ? <Honeypot name={honeypotName} inputRef={honeypotRef} /> : null}\n\t\t\t\t\t{displayTitleText ? <h2 className=\"fb-form__title\">{displayTitleText}</h2> : null}\n\t\t\t\t\t{displayIntroHtml ? (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"fb-form__intro\"\n\t\t\t\t\t\t\t// Safe to inject: serializeBody HTML-escapes all text (recall values included) and sanitizes link URLs.\n\t\t\t\t\t\t\t// biome-ignore lint/security/noDangerouslySetInnerHtml: HTML is produced by our escaping serializer, never raw user input\n\t\t\t\t\t\t\tdangerouslySetInnerHTML={{ __html: displayIntroHtml }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<FormLayout enabled={layout !== false}>\n\t\t\t\t\t\t{rendered.map((field) => {\n\t\t\t\t\t\t\tconst renderer = rendererRegistry.get(field.blockType)\n\t\t\t\t\t\t\tif (!renderer) {\n\t\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst width: FieldWidth | undefined =\n\t\t\t\t\t\t\t\ttypeof field.width === 'string' && FIELD_WIDTHS.has(field.width)\n\t\t\t\t\t\t\t\t\t? (field.width as FieldWidth)\n\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\tconst recalledField = applyRecall(field, recall)\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div key={field.name} {...widthProps(width)}>\n\t\t\t\t\t\t\t\t\t{calcExpressionOf(field) ? (\n\t\t\t\t\t\t\t\t\t\t<CalcFieldHost\n\t\t\t\t\t\t\t\t\t\t\tfield={recalledField}\n\t\t\t\t\t\t\t\t\t\t\trenderer={renderer}\n\t\t\t\t\t\t\t\t\t\t\tvalue={effectiveValues[field.name]}\n\t\t\t\t\t\t\t\t\t\t\tlocale={locale}\n\t\t\t\t\t\t\t\t\t\t\tt={translate}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t<FieldHost\n\t\t\t\t\t\t\t\t\t\t\tfield={recalledField}\n\t\t\t\t\t\t\t\t\t\t\trenderer={renderer}\n\t\t\t\t\t\t\t\t\t\t\tlocale={locale}\n\t\t\t\t\t\t\t\t\t\t\tt={translate}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t})}\n\t\t\t\t\t</FormLayout>\n\t\t\t\t\t{state.submitError ? (\n\t\t\t\t\t\t<p role=\"alert\" className=\"fb-form__submit-error\">\n\t\t\t\t\t\t\t{state.submitError}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t) : null}\n\t\t\t\t\t<FormControls\n\t\t\t\t\t\tbackButtonClassName={backButtonClassName}\n\t\t\t\t\t\tnextButtonClassName={nextButtonClassName}\n\t\t\t\t\t\tsubmitButtonClassName={submitButtonClassName}\n\t\t\t\t\t\trenderBack={renderBack}\n\t\t\t\t\t\trenderNext={renderNext}\n\t\t\t\t\t\trenderSubmit={renderSubmit}\n\t\t\t\t\t/>\n\t\t\t\t</form>\n\t\t\t)}\n\t\t</FormContext.Provider>\n\t)\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { FormFlow } from "../flow/types.js";
|
|
2
|
+
import { RendererTranslate } from "./contract.js";
|
|
3
|
+
import { RendererRegistry } from "./registry.js";
|
|
4
|
+
import { FormAction, FormState } from "./state.js";
|
|
5
|
+
import { Dispatch } from "react";
|
|
6
|
+
|
|
2
7
|
//#region src/react/FormContext.d.ts
|
|
3
8
|
/** Multi-step navigation state. Defaults to a single terminal step when the form has no flow. */
|
|
4
9
|
type FormStepInfo = {
|
|
@@ -11,6 +16,35 @@ type FormStepInfo = {
|
|
|
11
16
|
goNext: () => void;
|
|
12
17
|
goBack: () => void;
|
|
13
18
|
};
|
|
19
|
+
/** Resolved chrome button labels. Precedence per label: the `<Form>` prop, then (submit only) the form's `response.submitLabel`, then the translated default. */
|
|
20
|
+
type FormControlLabels = {
|
|
21
|
+
back: string;
|
|
22
|
+
next: string;
|
|
23
|
+
submit: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The form controller's context, read via `useFormContext` (or the focused `useField`,
|
|
27
|
+
* `useFormState`, and `useFormStep` hooks). Available anywhere under `<Form>`, including
|
|
28
|
+
* custom `children` layouts and custom field renderers.
|
|
29
|
+
*/
|
|
30
|
+
type FormContextValue = {
|
|
31
|
+
/** Current form state: values, errors, warnings, touched, submitting, submitted, submitError. */state: FormState;
|
|
32
|
+
/**
|
|
33
|
+
* Dispatch a `FormAction` (see `./state` for the action union). Custom field layouts
|
|
34
|
+
* typically dispatch `TOUCH` or `SET_FIELD_ISSUES`; prefer `useField` for value binding,
|
|
35
|
+
* which wires `SET_VALUE` and validation for you.
|
|
36
|
+
*/
|
|
37
|
+
dispatch: Dispatch<FormAction>; /** Validate one field now (client mode) against the supplied value and store its issues. */
|
|
38
|
+
validateField: (name: string, value: unknown) => void; /** The locale passed to `<Form>` (defaults to `'en'`). */
|
|
39
|
+
locale: string; /** Multi-step navigation state and the `goNext`/`goBack` handlers. */
|
|
40
|
+
step: FormStepInfo; /** The active renderer registry, exposed so nested renderers (e.g. repeater) can look up sub-renderers. */
|
|
41
|
+
rendererRegistry: RendererRegistry; /** Resolved back/next/submit labels used by `<FormControls>` and available to custom chrome. */
|
|
42
|
+
labels: FormControlLabels; /** The active translator: the `<Form>` `t` prop, else the bundled English fallback. */
|
|
43
|
+
t: RendererTranslate; /** Calc-authoritative answers: `state.values` overlaid with derived calc values. Consumers fall back to `state.values` when absent. */
|
|
44
|
+
effectiveValues?: Record<string, unknown>;
|
|
45
|
+
};
|
|
46
|
+
/** Read the form controller context. Throws if used outside `<Form>`. */
|
|
47
|
+
declare const useFormContext: () => FormContextValue;
|
|
14
48
|
//#endregion
|
|
15
|
-
export { FormStepInfo };
|
|
49
|
+
export { FormContextValue, FormControlLabels, FormStepInfo, useFormContext };
|
|
16
50
|
//# sourceMappingURL=FormContext.d.ts.map
|