@10x-media/form-builder 0.1.0-beta.5 → 0.1.0-beta.7
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 +102 -0
- package/dist/actions/buildActionBlocks.js +12 -9
- package/dist/actions/buildActionBlocks.js.map +1 -1
- package/dist/actions/builtin/confirmation.js +27 -79
- package/dist/actions/builtin/confirmation.js.map +1 -1
- package/dist/actions/builtin/emailAction.d.ts +17 -0
- package/dist/actions/builtin/emailAction.js +83 -0
- package/dist/actions/builtin/emailAction.js.map +1 -0
- package/dist/actions/builtin/emailTeam.js +14 -84
- package/dist/actions/builtin/emailTeam.js.map +1 -1
- package/dist/actions/builtin/index.d.ts +9 -11
- package/dist/actions/builtin/index.js +11 -11
- package/dist/actions/builtin/index.js.map +1 -1
- package/dist/actions/defineAction.d.ts +2 -2
- package/dist/actions/defineAction.js.map +1 -1
- package/dist/actions/emailRecipients.d.ts +12 -0
- package/dist/actions/emailRecipients.js +116 -0
- package/dist/actions/emailRecipients.js.map +1 -0
- package/dist/actions/registry.d.ts +2 -1
- package/dist/actions/registry.js +2 -6
- package/dist/actions/registry.js.map +1 -1
- package/dist/actions/task.js +5 -3
- package/dist/actions/task.js.map +1 -1
- package/dist/calc/evaluate.js +1 -2
- package/dist/calc/evaluate.js.map +1 -1
- package/dist/calc/normalizeCalc.js +1 -2
- package/dist/calc/normalizeCalc.js.map +1 -1
- package/dist/calc/types.js.map +1 -1
- package/dist/client/ConsentBlockLabel.d.ts +15 -0
- package/dist/client/ConsentBlockLabel.js +71 -0
- package/dist/client/ConsentBlockLabel.js.map +1 -0
- package/dist/client/ConsentSourceRowLabel.d.ts +9 -0
- package/dist/client/ConsentSourceRowLabel.js +21 -0
- package/dist/client/ConsentSourceRowLabel.js.map +1 -0
- package/dist/client/EndpointOptionsSelect.d.ts +1 -0
- package/dist/client/EndpointOptionsSelect.js +4 -1
- package/dist/client/EndpointOptionsSelect.js.map +1 -1
- package/dist/client/FieldNameSelect.d.ts +1 -0
- package/dist/client/FieldNameSelect.js +4 -1
- package/dist/client/FieldNameSelect.js.map +1 -1
- package/dist/client/RecipientsSelect.d.ts +31 -0
- package/dist/client/RecipientsSelect.js +174 -0
- package/dist/client/RecipientsSelect.js.map +1 -0
- package/dist/client/admin.css +5 -0
- package/dist/collections/buttonFields.js +1 -1
- package/dist/collections/formSubmissions.js +4 -2
- package/dist/collections/formSubmissions.js.map +1 -1
- package/dist/collections/forms.js +46 -99
- package/dist/collections/forms.js.map +1 -1
- package/dist/collections/formsEndpoints.js +101 -0
- package/dist/collections/formsEndpoints.js.map +1 -0
- package/dist/conditions/evaluate.js +1 -1
- package/dist/conditions/evaluate.js.map +1 -1
- package/dist/conditions/fieldTypes.d.ts +3 -1
- package/dist/conditions/fieldTypes.js +4 -1
- package/dist/conditions/fieldTypes.js.map +1 -1
- package/dist/conditions/normalizeConditions.js +18 -5
- package/dist/conditions/normalizeConditions.js.map +1 -1
- package/dist/consent/captureConsent.d.ts +21 -5
- package/dist/consent/captureConsent.js +31 -7
- package/dist/consent/captureConsent.js.map +1 -1
- package/dist/consent/consentSourcesField.d.ts +8 -0
- package/dist/consent/consentSourcesField.js +13 -2
- package/dist/consent/consentSourcesField.js.map +1 -1
- package/dist/consent/resolveConsentEntries.d.ts +1 -1
- package/dist/consent/resolveConsentEntries.js +1 -4
- package/dist/consent/resolveConsentEntries.js.map +1 -1
- package/dist/consent/resolveConsentSourcesRequest.js +0 -1
- package/dist/consent/resolveConsentSourcesRequest.js.map +1 -1
- package/dist/consent/resolveConsentStatements.d.ts +1 -1
- package/dist/consent/resolveConsentStatements.js.map +1 -1
- package/dist/consent/types.d.ts +13 -1
- package/dist/email/departments.d.ts +13 -9
- package/dist/email/departments.js +7 -50
- package/dist/email/departments.js.map +1 -1
- package/dist/email/departmentsField.d.ts +4 -0
- package/dist/email/departmentsField.js +5 -1
- package/dist/email/departmentsField.js.map +1 -1
- package/dist/exports/client.d.ts +4 -1
- package/dist/exports/client.js +4 -1
- package/dist/exports/react.d.ts +3 -2
- package/dist/exports/react.js +4 -3
- package/dist/exports/rsc.js +1 -1
- package/dist/exports/types.d.ts +1 -1
- package/dist/fields/buildFieldBlocks.js +4 -4
- package/dist/fields/buildFieldBlocks.js.map +1 -1
- package/dist/fields/builtin/consent.js +6 -1
- package/dist/fields/builtin/consent.js.map +1 -1
- package/dist/fields/builtin/email.js +1 -1
- package/dist/fields/builtin/email.js.map +1 -1
- package/dist/fields/builtin/file.js +9 -3
- package/dist/fields/builtin/file.js.map +1 -1
- package/dist/fields/builtin/index.d.ts +1 -1
- package/dist/fields/builtin/index.js +3 -3
- package/dist/fields/builtin/index.js.map +1 -1
- package/dist/fields/builtin/message.js +1 -1
- package/dist/fields/builtin/repeater.js +1 -1
- package/dist/fields/builtin/select.js +1 -1
- package/dist/fields/registry.d.ts +2 -1
- package/dist/fields/registry.js +2 -6
- package/dist/fields/registry.js.map +1 -1
- package/dist/fields/sharedConfig.js +1 -1
- package/dist/fields/types.d.ts +9 -2
- package/dist/flow/normalizeFlow.js +7 -3
- package/dist/flow/normalizeFlow.js.map +1 -1
- package/dist/form/toFormDocument.d.ts +20 -3
- package/dist/form/toFormDocument.js +16 -4
- package/dist/form/toFormDocument.js.map +1 -1
- package/dist/form/types.d.ts +5 -5
- package/dist/index.d.ts +5 -193
- package/dist/index.js +18 -8
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +228 -0
- package/dist/plugin/applyRegistryConfig.d.ts +11 -0
- package/dist/plugin/applyRegistryConfig.js +20 -0
- package/dist/plugin/applyRegistryConfig.js.map +1 -0
- package/dist/plugin/registerCollections.js +3 -1
- package/dist/plugin/registerCollections.js.map +1 -1
- package/dist/plugin/uploadsCollection.d.ts +3 -0
- package/dist/plugin/uploadsCollection.js +10 -1
- package/dist/plugin/uploadsCollection.js.map +1 -1
- package/dist/poll/buildPollOptionSourceFields.js +2 -2
- package/dist/poll/buildPollOptionSourceFields.js.map +1 -1
- package/dist/poll/definePollOptionSource.d.ts +2 -2
- package/dist/poll/definePollOptionSource.js.map +1 -1
- package/dist/poll/pollTypeRegistry.js +2 -4
- package/dist/poll/pollTypeRegistry.js.map +1 -1
- package/dist/poll/registry.d.ts +2 -1
- package/dist/poll/registry.js +2 -6
- package/dist/poll/registry.js.map +1 -1
- package/dist/poll/resolvePollOptionsRequest.js +0 -1
- package/dist/poll/resolvePollOptionsRequest.js.map +1 -1
- package/dist/prefill/valuesFromSearchParams.js +2 -7
- package/dist/prefill/valuesFromSearchParams.js.map +1 -1
- package/dist/react/Form.d.ts +3 -1
- package/dist/react/Form.js +93 -155
- package/dist/react/Form.js.map +1 -1
- package/dist/react/FormContext.d.ts +5 -1
- package/dist/react/FormContext.js.map +1 -1
- package/dist/react/FormFields.d.ts +18 -0
- package/dist/react/FormFields.js +114 -0
- package/dist/react/FormFields.js.map +1 -0
- package/dist/react/FormLayout.d.ts +4 -2
- package/dist/react/FormLayout.js +3 -2
- package/dist/react/FormLayout.js.map +1 -1
- package/dist/react/Poll.js +13 -3
- package/dist/react/Poll.js.map +1 -1
- package/dist/react/presentation/DialogSurface.js +1 -4
- package/dist/react/presentation/DialogSurface.js.map +1 -1
- package/dist/react/renderers/calculation.js +1 -3
- package/dist/react/renderers/calculation.js.map +1 -1
- package/dist/react/renderers/checkbox.js +1 -3
- package/dist/react/renderers/checkbox.js.map +1 -1
- package/dist/react/renderers/consent.js +2 -3
- package/dist/react/renderers/consent.js.map +1 -1
- package/dist/react/renderers/date.js +1 -3
- package/dist/react/renderers/date.js.map +1 -1
- package/dist/react/renderers/email.js +1 -3
- package/dist/react/renderers/email.js.map +1 -1
- package/dist/react/renderers/file.js +6 -7
- package/dist/react/renderers/file.js.map +1 -1
- package/dist/react/renderers/number.js +1 -3
- package/dist/react/renderers/number.js.map +1 -1
- package/dist/react/renderers/region.js +1 -3
- package/dist/react/renderers/region.js.map +1 -1
- package/dist/react/renderers/repeater.js +20 -4
- package/dist/react/renderers/repeater.js.map +1 -1
- package/dist/react/renderers/select.js +1 -3
- package/dist/react/renderers/select.js.map +1 -1
- package/dist/react/renderers/text.js +1 -3
- package/dist/react/renderers/text.js.map +1 -1
- package/dist/react/renderers/textarea.js +1 -3
- package/dist/react/renderers/textarea.js.map +1 -1
- package/dist/spam/constants.js +7 -1
- package/dist/spam/constants.js.map +1 -1
- package/dist/spam/resolveSpam.js +2 -1
- package/dist/spam/resolveSpam.js.map +1 -1
- package/dist/spam/spamGuard.js +22 -6
- package/dist/spam/spamGuard.js.map +1 -1
- package/dist/spam/types.d.ts +17 -2
- package/dist/submissions/SubmissionAnswers.js.map +1 -1
- package/dist/submissions/SubmissionAnswersClient.js +33 -26
- package/dist/submissions/SubmissionAnswersClient.js.map +1 -1
- package/dist/submissions/coerceBoolean.js +14 -0
- package/dist/submissions/coerceBoolean.js.map +1 -0
- package/dist/submissions/runSubmission.js +9 -8
- package/dist/submissions/runSubmission.js.map +1 -1
- package/dist/submissions/validateSubmission.js +4 -2
- package/dist/submissions/validateSubmission.js.map +1 -1
- package/dist/translations/de.js +14 -2
- package/dist/translations/de.js.map +1 -1
- package/dist/translations/en.js +14 -2
- package/dist/translations/en.js.map +1 -1
- package/dist/translations/keys.d.ts +14 -2
- package/dist/translations/keys.js +14 -2
- package/dist/translations/keys.js.map +1 -1
- package/dist/translations/server.js +8 -1
- package/dist/translations/server.js.map +1 -1
- package/dist/uploads/captureFileRef.d.ts +10 -3
- package/dist/uploads/captureFileRef.js +18 -9
- package/dist/uploads/captureFileRef.js.map +1 -1
- package/dist/uploads/resolveFileRef.js +2 -1
- package/dist/uploads/resolveFileRef.js.map +1 -1
- package/dist/validation/builtin/email.js +1 -1
- package/dist/validation/builtin/email.js.map +1 -1
- package/dist/validation/builtin/notAlreadySubmitted.js +1 -0
- package/dist/validation/builtin/notAlreadySubmitted.js.map +1 -1
- package/dist/validation/emailPattern.js +10 -0
- package/dist/validation/emailPattern.js.map +1 -0
- package/dist/validation/registry.d.ts +2 -1
- package/dist/validation/registry.js +2 -9
- package/dist/validation/registry.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildPollOptionSourceFields.js","names":[],"sources":["../../src/poll/buildPollOptionSourceFields.ts"],"sourcesContent":["import type { Field } from 'payload'\nimport { keys } from '../translations/keys'\nimport {
|
|
1
|
+
{"version":3,"file":"buildPollOptionSourceFields.js","names":[],"sources":["../../src/poll/buildPollOptionSourceFields.ts"],"sourcesContent":["import type { Field } from 'payload'\nimport { keys } from '../translations/keys'\nimport { labelForKey, resolveDefinitionLabel } from '../translations/server'\nimport type { PollOptionSourceRegistry } from './registry'\n\n/**\n * Builds the `optionSource` select and `sourceConfig` group for the forms poll group; returns\n * nothing when no sources are registered, so the fields only exist for hosts that opted in.\n * Fields inside `sourceConfig` are annotated with `admin.condition` so only the fields of the\n * currently selected source type are visible; like consent's `sourceConfig`, field names within\n * the group must be unique across all registered sources. Unlike consent's `source` select there\n * is no default: hand-authored options remain the baseline and a source is an explicit opt-in per\n * form (the select stays clearable).\n */\nexport const buildPollOptionSourceFields = (registry: PollOptionSourceRegistry): Field[] => {\n\tconst sources = [...registry.values()]\n\tif (sources.length === 0) {\n\t\treturn []\n\t}\n\n\tconst sourceConfigFields: Field[] = sources.flatMap((source) =>\n\t\t(source.config ?? []).map((field) => {\n\t\t\tconst existingAdmin =\n\t\t\t\ttypeof field.admin === 'object' && field.admin !== null ? field.admin : {}\n\t\t\treturn {\n\t\t\t\t...field,\n\t\t\t\tadmin: {\n\t\t\t\t\t...existingAdmin,\n\t\t\t\t\tcondition: (data: unknown) =>\n\t\t\t\t\t\t(data as { poll?: { optionSource?: string } })?.poll?.optionSource === source.type,\n\t\t\t\t},\n\t\t\t} as Field\n\t\t})\n\t)\n\n\treturn [\n\t\t{\n\t\t\tname: 'optionSource',\n\t\t\ttype: 'select',\n\t\t\tlabel: labelForKey(keys.pollOptionSource),\n\t\t\tadmin: {\n\t\t\t\tdescription: labelForKey(keys.pollOptionSourceDescription),\n\t\t\t},\n\t\t\toptions: sources.map((source) => ({\n\t\t\t\tlabel: resolveDefinitionLabel(source.label),\n\t\t\t\tvalue: source.type,\n\t\t\t})),\n\t\t},\n\t\t{\n\t\t\tname: 'sourceConfig',\n\t\t\ttype: 'group',\n\t\t\tlabel: labelForKey(keys.pollSourceConfig),\n\t\t\tadmin: {\n\t\t\t\tcondition: (_data, siblingData) => Boolean(siblingData?.optionSource),\n\t\t\t},\n\t\t\tfields:\n\t\t\t\tsourceConfigFields.length > 0\n\t\t\t\t\t? sourceConfigFields\n\t\t\t\t\t: [{ name: '_placeholder', type: 'text', admin: { hidden: true } }],\n\t\t},\n\t]\n}\n"],"mappings":";;;;;;;;;;;;AAcA,MAAa,+BAA+B,aAAgD;CAC3F,MAAM,UAAU,CAAC,GAAG,SAAS,OAAO,CAAC;CACrC,IAAI,QAAQ,WAAW,GACtB,OAAO,CAAC;CAGT,MAAM,qBAA8B,QAAQ,SAAS,YACnD,OAAO,UAAU,CAAC,GAAG,KAAK,UAAU;EACpC,MAAM,gBACL,OAAO,MAAM,UAAU,YAAY,MAAM,UAAU,OAAO,MAAM,QAAQ,CAAC;EAC1E,OAAO;GACN,GAAG;GACH,OAAO;IACN,GAAG;IACH,YAAY,SACV,MAA+C,MAAM,iBAAiB,OAAO;GAChF;EACD;CACD,CAAC,CACF;CAEA,OAAO,CACN;EACC,MAAM;EACN,MAAM;EACN,OAAO,YAAY,KAAK,gBAAgB;EACxC,OAAO,EACN,aAAa,YAAY,KAAK,2BAA2B,EAC1D;EACA,SAAS,QAAQ,KAAK,YAAY;GACjC,OAAO,uBAAuB,OAAO,KAAK;GAC1C,OAAO,OAAO;EACf,EAAE;CACH,GACA;EACC,MAAM;EACN,MAAM;EACN,OAAO,YAAY,KAAK,gBAAgB;EACxC,OAAO,EACN,YAAY,OAAO,gBAAgB,QAAQ,aAAa,YAAY,EACrE;EACA,QACC,mBAAmB,SAAS,IACzB,qBACA,CAAC;GAAE,MAAM;GAAgB,MAAM;GAAQ,OAAO,EAAE,QAAQ,KAAK;EAAE,CAAC;CACrE,CACD;AACD"}
|
|
@@ -22,8 +22,8 @@ type PollOptionResolveArgs<TConfig extends Record<string, unknown> = Record<stri
|
|
|
22
22
|
* the resolved values are the only accepted answers (fail closed).
|
|
23
23
|
*/
|
|
24
24
|
type PollOptionSource<TConfig extends Record<string, unknown> = Record<string, unknown>> = {
|
|
25
|
-
type: string;
|
|
26
|
-
label: string
|
|
25
|
+
type: string; /** i18n-key or literal (resolved like a field label), or a per-locale record. */
|
|
26
|
+
label: string | Record<string, string>;
|
|
27
27
|
config?: Field[];
|
|
28
28
|
resolve: (args: PollOptionResolveArgs<TConfig>) => Promise<PollOption[]> | PollOption[];
|
|
29
29
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definePollOptionSource.js","names":[],"sources":["../../src/poll/definePollOptionSource.ts"],"sourcesContent":["import type { Field, Payload, PayloadRequest } from 'payload'\n\n/** One selectable poll choice. `value` is the host's stable identifier contract: it is stored in submissions and later matched against `outcome.winningValues`, so it must never change once votes exist. */\nexport type PollOption = { label: string; value: string }\n\nexport type PollOptionResolveArgs<\n\tTConfig extends Record<string, unknown> = Record<string, unknown>,\n> = {\n\tconfig: TConfig\n\tform: { id: number | string; title?: string }\n\tpayload: Payload\n\treq?: PayloadRequest\n}\n\n/**\n * A poll option source type, authored once by the host: `config` is the admin `Field[]` an author\n * fills per form (e.g. an event id); `resolve` maps that config to the current options from host\n * domain data. Called at render time (via `resolvePollOptions`) and again at submission time, where\n * the resolved values are the only accepted answers (fail closed).\n */\nexport type PollOptionSource<TConfig extends Record<string, unknown> = Record<string, unknown>> = {\n\ttype: string\n\tlabel: string
|
|
1
|
+
{"version":3,"file":"definePollOptionSource.js","names":[],"sources":["../../src/poll/definePollOptionSource.ts"],"sourcesContent":["import type { Field, Payload, PayloadRequest } from 'payload'\n\n/** One selectable poll choice. `value` is the host's stable identifier contract: it is stored in submissions and later matched against `outcome.winningValues`, so it must never change once votes exist. */\nexport type PollOption = { label: string; value: string }\n\nexport type PollOptionResolveArgs<\n\tTConfig extends Record<string, unknown> = Record<string, unknown>,\n> = {\n\tconfig: TConfig\n\tform: { id: number | string; title?: string }\n\tpayload: Payload\n\treq?: PayloadRequest\n}\n\n/**\n * A poll option source type, authored once by the host: `config` is the admin `Field[]` an author\n * fills per form (e.g. an event id); `resolve` maps that config to the current options from host\n * domain data. Called at render time (via `resolvePollOptions`) and again at submission time, where\n * the resolved values are the only accepted answers (fail closed).\n */\nexport type PollOptionSource<TConfig extends Record<string, unknown> = Record<string, unknown>> = {\n\ttype: string\n\t/** i18n-key or literal (resolved like a field label), or a per-locale record. */\n\tlabel: string | Record<string, string>\n\tconfig?: Field[]\n\tresolve: (args: PollOptionResolveArgs<TConfig>) => Promise<PollOption[]> | PollOption[]\n\t/**\n\t * Optional outcome resolver: return the winning option value(s) from domain data (e.g. the\n\t * recorded race result), a `string[]` when the result is a tie, or `undefined` while the outcome\n\t * is not yet decidable. Backs the built-in `source` poll type, so a form whose `poll.type` is\n\t * `source` auto-resolves through it (via the scheduled close job, a results-read, or an explicit\n\t * `resolvePollOutcome({ payload, formId })`); every returned value is validated against the poll's\n\t * resolved options before it is recorded, so each must be one of the values `resolve` yields.\n\t */\n\tresolveOutcome?: (\n\t\targs: PollOptionResolveArgs<TConfig>\n\t) => Promise<string | string[] | undefined> | string | string[] | undefined\n}\n\n/** Erased shape stored in the registry; config re-narrows per matched type at resolution. */\nexport type AnyPollOptionSource = PollOptionSource<Record<string, unknown>>\n\nexport const definePollOptionSource = <\n\tTConfig extends Record<string, unknown> = Record<string, unknown>,\n>(\n\tsource: PollOptionSource<TConfig>\n): PollOptionSource<TConfig> => source\n"],"mappings":";AA0CA,MAAa,0BAGZ,WAC+B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { keys } from "../translations/keys.js";
|
|
2
|
+
import { applyRegistryConfig } from "../plugin/applyRegistryConfig.js";
|
|
2
3
|
import { customStateOf, stashCustomState } from "../plugin/customState.js";
|
|
3
4
|
import { definePollType } from "./definePollType.js";
|
|
4
5
|
import { mostVotedStrategy } from "./mostVoted.js";
|
|
@@ -56,10 +57,7 @@ const resolvePollTypes = (config) => {
|
|
|
56
57
|
[sourceStrategy.type, sourceStrategy]
|
|
57
58
|
]);
|
|
58
59
|
if (Array.isArray(config)) for (const strategy of config) registry.set(strategy.type, strategy);
|
|
59
|
-
else if (config)
|
|
60
|
-
...strategy,
|
|
61
|
-
type
|
|
62
|
-
});
|
|
60
|
+
else if (config) applyRegistryConfig(registry, config);
|
|
63
61
|
return registry;
|
|
64
62
|
};
|
|
65
63
|
/** Store the resolved registry on the config so `resolvePollOutcome` can reach it via `payload.config`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pollTypeRegistry.js","names":[],"sources":["../../src/poll/pollTypeRegistry.ts"],"sourcesContent":["import type { Payload } from 'payload'\nimport { customStateOf, stashCustomState } from '../plugin/customState'\nimport { keys } from '../translations/keys'\nimport { definePollType, type PollOutcomeStrategy } from './definePollType'\nimport { mostVotedStrategy } from './mostVoted'\nimport { pollOptionSourcesOf } from './resolvePollOptions'\n\nexport type PollTypeRegistry = Map<string, PollOutcomeStrategy>\n\n/**\n * Host-supplied outcome strategies: a keyed record (the key forces `type`, like the field-type\n * registry) or a plain array. There are no boolean toggles because the three built-ins are always\n * registered; a host entry with a built-in's `type` replaces it.\n */\nexport type PollTypesConfig = Record<string, PollOutcomeStrategy> | PollOutcomeStrategy[]\n\n/** The default: an admin records `winningValues` by hand, so there is nothing to auto-resolve. */\nexport const manualStrategy: PollOutcomeStrategy = definePollType({\n\ttype: 'manual',\n\tlabel: keys.pollTypeManual,\n\tresolveOutcome: () => undefined,\n})\n\n/** One winner set as a deduped array of non-empty strings, or `undefined` when the source yields none. */\nconst normalizeSourceOutcome = (value: string | string[] | undefined): string[] | undefined => {\n\tif (value === undefined) {\n\t\treturn undefined\n\t}\n\tconst cleaned = [\n\t\t...new Set(\n\t\t\t(Array.isArray(value) ? value : [value]).filter(\n\t\t\t\t(entry): entry is string => typeof entry === 'string' && entry.length > 0\n\t\t\t)\n\t\t),\n\t]\n\treturn cleaned.length > 0 ? cleaned : undefined\n}\n\n/**\n * Delegate to the poll's configured option source `resolveOutcome` (the same domain resolver that\n * feeds explicit `resolvePollOutcome` auto mode before this task shipped). Returns `undefined` when\n * the poll has no source, the source is unregistered, or it does not implement `resolveOutcome`, so a\n * misconfigured poll never throws from the close job or the read fallback; a source that resolves to\n * no decision (its `resolveOutcome` returns `undefined`) also yields `undefined`.\n */\nexport const sourceStrategy: PollOutcomeStrategy = definePollType({\n\ttype: 'source',\n\tlabel: keys.pollTypeSource,\n\tresolveOutcome: async ({ payload, req, form, poll }) => {\n\t\tconst optionSource =\n\t\t\ttypeof poll.optionSource === 'string' && poll.optionSource.length > 0\n\t\t\t\t? poll.optionSource\n\t\t\t\t: undefined\n\t\tif (!optionSource) {\n\t\t\treturn undefined\n\t\t}\n\t\tconst source = pollOptionSourcesOf(payload).get(optionSource)\n\t\tif (!source?.resolveOutcome) {\n\t\t\treturn undefined\n\t\t}\n\t\tconst config =\n\t\t\tpoll.sourceConfig != null && typeof poll.sourceConfig === 'object'\n\t\t\t\t? (poll.sourceConfig as Record<string, unknown>)\n\t\t\t\t: {}\n\t\treturn normalizeSourceOutcome(\n\t\t\tawait source.resolveOutcome({\n\t\t\t\tconfig,\n\t\t\t\tform: { id: form.id, title: typeof form.title === 'string' ? form.title : undefined },\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t})\n\t\t)\n\t},\n})\n\n/**\n * The active poll-type registry: the three always-registered built-ins (`manual`, `mostVoted`,\n * `source`) plus any host strategies from the plugin `poll.types` option. A host entry keyed by (or\n * carrying a `type` of) a built-in slug replaces that built-in. Mirrors the field-type registry: a\n * record key forces the strategy's `type` so a keyed override cannot rename the slot.\n */\nexport const resolvePollTypes = (config?: PollTypesConfig): PollTypeRegistry => {\n\tconst registry: PollTypeRegistry = new Map([\n\t\t[manualStrategy.type, manualStrategy],\n\t\t[mostVotedStrategy.type, mostVotedStrategy],\n\t\t[sourceStrategy.type, sourceStrategy],\n\t])\n\tif (Array.isArray(config)) {\n\t\tfor (const strategy of config) {\n\t\t\tregistry.set(strategy.type, strategy)\n\t\t}\n\t} else if (config) {\n\t\
|
|
1
|
+
{"version":3,"file":"pollTypeRegistry.js","names":[],"sources":["../../src/poll/pollTypeRegistry.ts"],"sourcesContent":["import type { Payload } from 'payload'\nimport { applyRegistryConfig } from '../plugin/applyRegistryConfig'\nimport { customStateOf, stashCustomState } from '../plugin/customState'\nimport { keys } from '../translations/keys'\nimport { definePollType, type PollOutcomeStrategy } from './definePollType'\nimport { mostVotedStrategy } from './mostVoted'\nimport { pollOptionSourcesOf } from './resolvePollOptions'\n\nexport type PollTypeRegistry = Map<string, PollOutcomeStrategy>\n\n/**\n * Host-supplied outcome strategies: a keyed record (the key forces `type`, like the field-type\n * registry) or a plain array. There are no boolean toggles because the three built-ins are always\n * registered; a host entry with a built-in's `type` replaces it.\n */\nexport type PollTypesConfig = Record<string, PollOutcomeStrategy> | PollOutcomeStrategy[]\n\n/** The default: an admin records `winningValues` by hand, so there is nothing to auto-resolve. */\nexport const manualStrategy: PollOutcomeStrategy = definePollType({\n\ttype: 'manual',\n\tlabel: keys.pollTypeManual,\n\tresolveOutcome: () => undefined,\n})\n\n/** One winner set as a deduped array of non-empty strings, or `undefined` when the source yields none. */\nconst normalizeSourceOutcome = (value: string | string[] | undefined): string[] | undefined => {\n\tif (value === undefined) {\n\t\treturn undefined\n\t}\n\tconst cleaned = [\n\t\t...new Set(\n\t\t\t(Array.isArray(value) ? value : [value]).filter(\n\t\t\t\t(entry): entry is string => typeof entry === 'string' && entry.length > 0\n\t\t\t)\n\t\t),\n\t]\n\treturn cleaned.length > 0 ? cleaned : undefined\n}\n\n/**\n * Delegate to the poll's configured option source `resolveOutcome` (the same domain resolver that\n * feeds explicit `resolvePollOutcome` auto mode before this task shipped). Returns `undefined` when\n * the poll has no source, the source is unregistered, or it does not implement `resolveOutcome`, so a\n * misconfigured poll never throws from the close job or the read fallback; a source that resolves to\n * no decision (its `resolveOutcome` returns `undefined`) also yields `undefined`.\n */\nexport const sourceStrategy: PollOutcomeStrategy = definePollType({\n\ttype: 'source',\n\tlabel: keys.pollTypeSource,\n\tresolveOutcome: async ({ payload, req, form, poll }) => {\n\t\tconst optionSource =\n\t\t\ttypeof poll.optionSource === 'string' && poll.optionSource.length > 0\n\t\t\t\t? poll.optionSource\n\t\t\t\t: undefined\n\t\tif (!optionSource) {\n\t\t\treturn undefined\n\t\t}\n\t\tconst source = pollOptionSourcesOf(payload).get(optionSource)\n\t\tif (!source?.resolveOutcome) {\n\t\t\treturn undefined\n\t\t}\n\t\tconst config =\n\t\t\tpoll.sourceConfig != null && typeof poll.sourceConfig === 'object'\n\t\t\t\t? (poll.sourceConfig as Record<string, unknown>)\n\t\t\t\t: {}\n\t\treturn normalizeSourceOutcome(\n\t\t\tawait source.resolveOutcome({\n\t\t\t\tconfig,\n\t\t\t\tform: { id: form.id, title: typeof form.title === 'string' ? form.title : undefined },\n\t\t\t\tpayload,\n\t\t\t\treq,\n\t\t\t})\n\t\t)\n\t},\n})\n\n/**\n * The active poll-type registry: the three always-registered built-ins (`manual`, `mostVoted`,\n * `source`) plus any host strategies from the plugin `poll.types` option. A host entry keyed by (or\n * carrying a `type` of) a built-in slug replaces that built-in. Mirrors the field-type registry: a\n * record key forces the strategy's `type` so a keyed override cannot rename the slot.\n */\nexport const resolvePollTypes = (config?: PollTypesConfig): PollTypeRegistry => {\n\tconst registry: PollTypeRegistry = new Map([\n\t\t[manualStrategy.type, manualStrategy],\n\t\t[mostVotedStrategy.type, mostVotedStrategy],\n\t\t[sourceStrategy.type, sourceStrategy],\n\t])\n\tif (Array.isArray(config)) {\n\t\tfor (const strategy of config) {\n\t\t\tregistry.set(strategy.type, strategy)\n\t\t}\n\t} else if (config) {\n\t\t// A keyed record shares the sibling registries' merge (the key forces `type`). There are no\n\t\t// boolean toggles here because the three built-ins are always registered, so those branches\n\t\t// are inert for this always-strategy config.\n\t\tapplyRegistryConfig(registry, config)\n\t}\n\treturn registry\n}\n\ntype PollTypesCustomState = { pollTypes: PollTypeRegistry }\n\n/** Store the resolved registry on the config so `resolvePollOutcome` can reach it via `payload.config`. */\nexport const stashPollTypes = (\n\tcustom: Record<string, unknown> | undefined,\n\tregistry: PollTypeRegistry\n): Record<string, unknown> =>\n\tstashCustomState<PollTypesCustomState>(custom, { pollTypes: registry })\n\nexport const pollTypesOf = (payload: Payload): PollTypeRegistry =>\n\tcustomStateOf<PollTypesCustomState>(payload).pollTypes ?? new Map()\n"],"mappings":";;;;;;;;AAkBA,MAAa,iBAAsC,eAAe;CACjE,MAAM;CACN,OAAO,KAAK;CACZ,sBAAsB,KAAA;AACvB,CAAC;;AAGD,MAAM,0BAA0B,UAA+D;CAC9F,IAAI,UAAU,KAAA,GACb;CAED,MAAM,UAAU,CACf,GAAG,IAAI,KACL,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAG,QACvC,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CACzE,CACD,CACD;CACA,OAAO,QAAQ,SAAS,IAAI,UAAU,KAAA;AACvC;;;;;;;;AASA,MAAa,iBAAsC,eAAe;CACjE,MAAM;CACN,OAAO,KAAK;CACZ,gBAAgB,OAAO,EAAE,SAAS,KAAK,MAAM,WAAW;EACvD,MAAM,eACL,OAAO,KAAK,iBAAiB,YAAY,KAAK,aAAa,SAAS,IACjE,KAAK,eACL,KAAA;EACJ,IAAI,CAAC,cACJ;EAED,MAAM,SAAS,oBAAoB,OAAO,EAAE,IAAI,YAAY;EAC5D,IAAI,CAAC,QAAQ,gBACZ;EAED,MAAM,SACL,KAAK,gBAAgB,QAAQ,OAAO,KAAK,iBAAiB,WACtD,KAAK,eACN,CAAC;EACL,OAAO,uBACN,MAAM,OAAO,eAAe;GAC3B;GACA,MAAM;IAAE,IAAI,KAAK;IAAI,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAA;GAAU;GACpF;GACA;EACD,CAAC,CACF;CACD;AACD,CAAC;;;;;;;AAQD,MAAa,oBAAoB,WAA+C;CAC/E,MAAM,WAA6B,IAAI,IAAI;EAC1C,CAAC,eAAe,MAAM,cAAc;EACpC,CAAC,kBAAkB,MAAM,iBAAiB;EAC1C,CAAC,eAAe,MAAM,cAAc;CACrC,CAAC;CACD,IAAI,MAAM,QAAQ,MAAM,GACvB,KAAK,MAAM,YAAY,QACtB,SAAS,IAAI,SAAS,MAAM,QAAQ;MAE/B,IAAI,QAIV,oBAAoB,UAAU,MAAM;CAErC,OAAO;AACR;;AAKA,MAAa,kBACZ,QACA,aAEA,iBAAuC,QAAQ,EAAE,WAAW,SAAS,CAAC;AAEvE,MAAa,eAAe,YAC3B,cAAoC,OAAO,EAAE,6BAAa,IAAI,IAAI"}
|
package/dist/poll/registry.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { AnyPollOptionSource } from "./definePollOptionSource.js";
|
|
2
|
+
import { RegistryConfig } from "../plugin/applyRegistryConfig.js";
|
|
2
3
|
|
|
3
4
|
//#region src/poll/registry.d.ts
|
|
4
5
|
type PollOptionSourceRegistry = Map<string, AnyPollOptionSource>;
|
|
5
6
|
/** `false` removes a source, `true` keeps it (no-op: there are no built-ins), a definition adds or replaces one. */
|
|
6
7
|
type PollOptionSourceOption = boolean | AnyPollOptionSource;
|
|
7
|
-
type PollOptionSourcesConfig =
|
|
8
|
+
type PollOptionSourcesConfig = RegistryConfig<AnyPollOptionSource>;
|
|
8
9
|
/**
|
|
9
10
|
* Resolve the active poll option-source registry from a consumer map. Unlike the consent and
|
|
10
11
|
* action registries there are no built-in defaults: the registry starts empty and only host
|
package/dist/poll/registry.js
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
+
import { applyRegistryConfig } from "../plugin/applyRegistryConfig.js";
|
|
1
2
|
//#region src/poll/registry.ts
|
|
2
3
|
/**
|
|
3
4
|
* Resolve the active poll option-source registry from a consumer map. Unlike the consent and
|
|
4
5
|
* action registries there are no built-in defaults: the registry starts empty and only host
|
|
5
6
|
* definitions populate it, so `true`/`false` entries are accepted but change nothing.
|
|
6
7
|
*/
|
|
7
|
-
const resolvePollOptionSources = (config = {}) =>
|
|
8
|
-
const registry = /* @__PURE__ */ new Map();
|
|
9
|
-
for (const [type, option] of Object.entries(config)) if (option === false) registry.delete(type);
|
|
10
|
-
else if (option === true) {} else registry.set(type, option);
|
|
11
|
-
return registry;
|
|
12
|
-
};
|
|
8
|
+
const resolvePollOptionSources = (config = {}) => applyRegistryConfig(/* @__PURE__ */ new Map(), config);
|
|
13
9
|
//#endregion
|
|
14
10
|
export { resolvePollOptionSources };
|
|
15
11
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","names":[],"sources":["../../src/poll/registry.ts"],"sourcesContent":["import type { AnyPollOptionSource } from './definePollOptionSource'\n\nexport type PollOptionSourceRegistry = Map<string, AnyPollOptionSource>\n\n/** `false` removes a source, `true` keeps it (no-op: there are no built-ins), a definition adds or replaces one. */\nexport type PollOptionSourceOption = boolean | AnyPollOptionSource\n\nexport type PollOptionSourcesConfig =
|
|
1
|
+
{"version":3,"file":"registry.js","names":[],"sources":["../../src/poll/registry.ts"],"sourcesContent":["import { applyRegistryConfig, type RegistryConfig } from '../plugin/applyRegistryConfig'\nimport type { AnyPollOptionSource } from './definePollOptionSource'\n\nexport type PollOptionSourceRegistry = Map<string, AnyPollOptionSource>\n\n/** `false` removes a source, `true` keeps it (no-op: there are no built-ins), a definition adds or replaces one. */\nexport type PollOptionSourceOption = boolean | AnyPollOptionSource\n\nexport type PollOptionSourcesConfig = RegistryConfig<AnyPollOptionSource>\n\n/**\n * Resolve the active poll option-source registry from a consumer map. Unlike the consent and\n * action registries there are no built-in defaults: the registry starts empty and only host\n * definitions populate it, so `true`/`false` entries are accepted but change nothing.\n */\nexport const resolvePollOptionSources = (\n\tconfig: PollOptionSourcesConfig = {}\n): PollOptionSourceRegistry => applyRegistryConfig(new Map<string, AnyPollOptionSource>(), config)\n"],"mappings":";;;;;;;AAeA,MAAa,4BACZ,SAAkC,CAAC,MACL,oCAAoB,IAAI,IAAiC,GAAG,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolvePollOptionsRequest.js","names":[],"sources":["../../src/poll/resolvePollOptionsRequest.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { FORMS_SLUG } from '../collections/forms'\nimport type { PollOption } from './definePollOptionSource'\nimport { resolveEffectivePollOptions } from './effectivePollOptions'\n\nexport type ResolvePollOptionsRequestArgs = {\n\tpayload: Payload\n\tformId: number | string | undefined\n\t/** Whether the caller is authenticated (an admin/user). */\n\tisAuthed: boolean\n\treq?: PayloadRequest\n}\n\nexport type ResolvePollOptionsRequestResult = {\n\tstatus: number\n\tbody: { options: PollOption[] } | { errors: { message: string }[] }\n}\n\n/**\n * Authorize and resolve the `GET /:id/poll-options` request backing the admin winner select:\n * authenticated callers get the poll's effective options (source-resolved or authored, exactly the\n * set outcome validation accepts), anonymous callers are always refused. Statuses mirror the\n * results endpoint: 400 missing id, 403 unauthenticated, 404 unknown form, 503 when the option\n * source fails (fail closed).\n */\nexport const resolvePollOptionsRequest = async (\n\targs: ResolvePollOptionsRequestArgs\n): Promise<ResolvePollOptionsRequestResult> => {\n\tconst { payload, formId, isAuthed, req } = args\n\tif (!isAuthed) {\n\t\treturn { status: 403, body: { errors: [{ message: 'Forbidden' }] } }\n\t}\n\tif (formId == null) {\n\t\treturn { status: 400, body: { errors: [{ message: 'Missing form id' }] } }\n\t}\n\tconst form = await payload\n\t\t.findByID({ collection: FORMS_SLUG, id: formId, depth: 0,
|
|
1
|
+
{"version":3,"file":"resolvePollOptionsRequest.js","names":[],"sources":["../../src/poll/resolvePollOptionsRequest.ts"],"sourcesContent":["import type { Payload, PayloadRequest } from 'payload'\nimport { FORMS_SLUG } from '../collections/forms'\nimport type { PollOption } from './definePollOptionSource'\nimport { resolveEffectivePollOptions } from './effectivePollOptions'\n\nexport type ResolvePollOptionsRequestArgs = {\n\tpayload: Payload\n\tformId: number | string | undefined\n\t/** Whether the caller is authenticated (an admin/user). */\n\tisAuthed: boolean\n\treq?: PayloadRequest\n}\n\nexport type ResolvePollOptionsRequestResult = {\n\tstatus: number\n\tbody: { options: PollOption[] } | { errors: { message: string }[] }\n}\n\n/**\n * Authorize and resolve the `GET /:id/poll-options` request backing the admin winner select:\n * authenticated callers get the poll's effective options (source-resolved or authored, exactly the\n * set outcome validation accepts), anonymous callers are always refused. Statuses mirror the\n * results endpoint: 400 missing id, 403 unauthenticated, 404 unknown form, 503 when the option\n * source fails (fail closed).\n */\nexport const resolvePollOptionsRequest = async (\n\targs: ResolvePollOptionsRequestArgs\n): Promise<ResolvePollOptionsRequestResult> => {\n\tconst { payload, formId, isAuthed, req } = args\n\tif (!isAuthed) {\n\t\treturn { status: 403, body: { errors: [{ message: 'Forbidden' }] } }\n\t}\n\tif (formId == null) {\n\t\treturn { status: 400, body: { errors: [{ message: 'Missing form id' }] } }\n\t}\n\t// Load under the caller's own read access (no overrideAccess): a tenant-scoped `forms.access.read`\n\t// then refuses a cross-tenant form id (null -> 404), so an authed user cannot read another tenant's\n\t// poll options.\n\tconst form = await payload\n\t\t.findByID({ collection: FORMS_SLUG, id: formId, depth: 0, req })\n\t\t.catch(() => null)\n\tif (!form) {\n\t\treturn { status: 404, body: { errors: [{ message: 'Not found' }] } }\n\t}\n\ttry {\n\t\tconst options = await resolveEffectivePollOptions({ payload, req, form })\n\t\treturn { status: 200, body: { options } }\n\t} catch {\n\t\treturn { status: 503, body: { errors: [{ message: 'Poll options unavailable' }] } }\n\t}\n}\n"],"mappings":";;;;;;;;;;AAyBA,MAAa,4BAA4B,OACxC,SAC8C;CAC9C,MAAM,EAAE,SAAS,QAAQ,UAAU,QAAQ;CAC3C,IAAI,CAAC,UACJ,OAAO;EAAE,QAAQ;EAAK,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,YAAY,CAAC,EAAE;CAAE;CAEpE,IAAI,UAAU,MACb,OAAO;EAAE,QAAQ;EAAK,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,kBAAkB,CAAC,EAAE;CAAE;CAK1E,MAAM,OAAO,MAAM,QACjB,SAAS;EAAE,YAAY;EAAY,IAAI;EAAQ,OAAO;EAAG;CAAI,CAAC,EAC9D,YAAY,IAAI;CAClB,IAAI,CAAC,MACJ,OAAO;EAAE,QAAQ;EAAK,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,YAAY,CAAC,EAAE;CAAE;CAEpE,IAAI;EAEH,OAAO;GAAE,QAAQ;GAAK,MAAM,EAAE,SAAA,MADR,4BAA4B;IAAE;IAAS;IAAK;GAAK,CAAC,EAClC;EAAE;CACzC,QAAQ;EACP,OAAO;GAAE,QAAQ;GAAK,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS,2BAA2B,CAAC,EAAE;EAAE;CACnF;AACD"}
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
import { isNamedField } from "../fields/fieldKey.js";
|
|
2
|
+
import { isTruthyString } from "../submissions/coerceBoolean.js";
|
|
2
3
|
//#region src/prefill/valuesFromSearchParams.ts
|
|
3
|
-
const TRUTHY = new Set([
|
|
4
|
-
"true",
|
|
5
|
-
"1",
|
|
6
|
-
"on",
|
|
7
|
-
"yes"
|
|
8
|
-
]);
|
|
9
4
|
const coerce = (kind, raw, all) => {
|
|
10
5
|
switch (kind) {
|
|
11
6
|
case "number": {
|
|
12
7
|
const n = Number(raw);
|
|
13
8
|
return Number.isNaN(n) ? void 0 : n;
|
|
14
9
|
}
|
|
15
|
-
case "boolean": return
|
|
10
|
+
case "boolean": return isTruthyString(raw);
|
|
16
11
|
case "text[]": return all;
|
|
17
12
|
default: return raw;
|
|
18
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valuesFromSearchParams.js","names":[],"sources":["../../src/prefill/valuesFromSearchParams.ts"],"sourcesContent":["import { isNamedField } from '../fields/fieldKey'\nimport 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
|
|
1
|
+
{"version":3,"file":"valuesFromSearchParams.js","names":[],"sources":["../../src/prefill/valuesFromSearchParams.ts"],"sourcesContent":["import { isNamedField } from '../fields/fieldKey'\nimport type { FieldTypeRegistry } from '../fields/registry'\nimport type { FormFieldValueKind } from '../fields/types'\nimport { isTruthyString } from '../submissions/coerceBoolean'\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 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 isTruthyString(raw)\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.filter(isNamedField).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":";;;AAeA,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,eAAe,GAAG;EAC1B,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,OAAO,YAAY,EAAE,KAAK,UAAU,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;CACtF,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"}
|
package/dist/react/Form.d.ts
CHANGED
|
@@ -43,7 +43,8 @@ type FormProps = {
|
|
|
43
43
|
name?: string;
|
|
44
44
|
}; /** A token from your captcha widget; verified server-side when a captcha provider is configured. */
|
|
45
45
|
captchaToken?: string; /** Custom layout: render fields with `useField`/`useFormState` instead of the auto-rendered field loop. */
|
|
46
|
-
children?: ReactNode; /**
|
|
46
|
+
children?: ReactNode; /** Chrome rendered inside the form, above the fields, in default mode (e.g. `<FormSteps />`). */
|
|
47
|
+
header?: ReactNode; /** Additional CSS class names applied to the root `<form>` element (and the success node). */
|
|
47
48
|
className?: string; /** Replace the default submit button entirely. Receives the resolved label and submitting state. */
|
|
48
49
|
renderSubmit?: (props: SubmitButtonRenderProps) => ReactNode; /** Replace the default "Next" button in multi-step forms. */
|
|
49
50
|
renderNext?: (props: NextButtonRenderProps) => ReactNode; /** Replace the default "Back" button in multi-step forms. */
|
|
@@ -79,6 +80,7 @@ declare const Form: ({
|
|
|
79
80
|
honeypot,
|
|
80
81
|
captchaToken,
|
|
81
82
|
children,
|
|
83
|
+
header,
|
|
82
84
|
className,
|
|
83
85
|
renderSubmit,
|
|
84
86
|
renderNext,
|
package/dist/react/Form.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { interpolate } from "../recall/interpolate.js";
|
|
3
2
|
import { keys } from "../translations/keys.js";
|
|
3
|
+
import { interpolate } from "../recall/interpolate.js";
|
|
4
4
|
import { serializeBody } from "../actions/body/serializeBody.js";
|
|
5
5
|
import { noopEventSink } from "../events/noopSink.js";
|
|
6
|
-
import { CAPTCHA_TOKEN_KEY } from "../spam/constants.js";
|
|
6
|
+
import { CAPTCHA_TOKEN_KEY, HONEYPOT_VALUE_KEY } from "../spam/constants.js";
|
|
7
7
|
import { fieldKey, isNamedField } from "../fields/fieldKey.js";
|
|
8
8
|
import { calcExpressionOf, computeCalcFields } from "../calc/computeCalcFields.js";
|
|
9
9
|
import { evaluateCondition } from "../conditions/evaluate.js";
|
|
@@ -16,82 +16,24 @@ import { makeTranslate } from "../translations/makeTranslate.js";
|
|
|
16
16
|
import { emitFormEvent } from "./events.js";
|
|
17
17
|
import { FormContext } from "./FormContext.js";
|
|
18
18
|
import { FormControls } from "./FormControls.js";
|
|
19
|
-
import {
|
|
19
|
+
import { FormFields } from "./FormFields.js";
|
|
20
20
|
import { Honeypot } from "./Honeypot.js";
|
|
21
21
|
import { defaultPresentations } from "./presentation/presentations.js";
|
|
22
22
|
import { resolvePresentations } from "./presentation/registry.js";
|
|
23
|
-
import { applyRecall } from "./recall.js";
|
|
24
23
|
import { resolveRenderers } from "./registry.js";
|
|
25
|
-
import { useField } from "./useField.js";
|
|
26
24
|
import { defaultRenderers } from "./renderers/index.js";
|
|
27
25
|
import { buildFieldTypeRegistry, buildValidationRuleRegistry, visibleFields } from "./resolveForm.js";
|
|
28
26
|
import { formReducer, initialFormState, seedFieldValues } from "./state.js";
|
|
29
27
|
import { submitForm } from "./submitForm.js";
|
|
30
28
|
import { validateFieldValue } from "./validateField.js";
|
|
31
29
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
32
|
-
import {
|
|
30
|
+
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
33
31
|
//#region src/react/Form.tsx
|
|
34
32
|
const isEmpty = (value) => value == null || value === "" || Array.isArray(value) && value.length === 0;
|
|
35
33
|
/** A stored button label counts only when it is a non-empty string; anything else falls through. */
|
|
36
34
|
const storedLabel = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
|
|
37
|
-
const FIELD_WIDTHS = new Set([
|
|
38
|
-
"full",
|
|
39
|
-
"half",
|
|
40
|
-
"third",
|
|
41
|
-
"twoThirds"
|
|
42
|
-
]);
|
|
43
|
-
const FieldHost = ({ field, renderer, locale, t }) => {
|
|
44
|
-
const id = useId();
|
|
45
|
-
const { value, errors, setValue, onBlur } = useField(field.name);
|
|
46
|
-
return createElement(renderer, {
|
|
47
|
-
field,
|
|
48
|
-
id,
|
|
49
|
-
name: field.name,
|
|
50
|
-
value,
|
|
51
|
-
onChange: setValue,
|
|
52
|
-
onBlur,
|
|
53
|
-
errors,
|
|
54
|
-
required: Boolean(field.required),
|
|
55
|
-
locale,
|
|
56
|
-
t
|
|
57
|
-
});
|
|
58
|
-
};
|
|
59
|
-
/** Hosts a derived (calc) field: read-only, value supplied from `effectiveValues`, never bound via `useField`. */
|
|
60
|
-
const CalcFieldHost = ({ field, renderer, value, locale, t }) => {
|
|
61
|
-
return createElement(renderer, {
|
|
62
|
-
field,
|
|
63
|
-
id: useId(),
|
|
64
|
-
name: field.name,
|
|
65
|
-
value,
|
|
66
|
-
onChange: () => {},
|
|
67
|
-
onBlur: () => {},
|
|
68
|
-
errors: [],
|
|
69
|
-
required: false,
|
|
70
|
-
disabled: true,
|
|
71
|
-
locale,
|
|
72
|
-
t
|
|
73
|
-
});
|
|
74
|
-
};
|
|
75
|
-
/**
|
|
76
|
-
* Hosts a nameless (bare) display block, e.g. a message: no `useField` binding at all. The
|
|
77
|
-
* renderer reads only the instance and the form context; `name` is the row key for consistency.
|
|
78
|
-
*/
|
|
79
|
-
const StaticFieldHost = ({ field, renderer, locale, t }) => {
|
|
80
|
-
return createElement(renderer, {
|
|
81
|
-
field,
|
|
82
|
-
id: useId(),
|
|
83
|
-
name: fieldKey(field),
|
|
84
|
-
value: void 0,
|
|
85
|
-
onChange: () => {},
|
|
86
|
-
onBlur: () => {},
|
|
87
|
-
errors: [],
|
|
88
|
-
required: false,
|
|
89
|
-
locale,
|
|
90
|
-
t
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
35
|
/** The headless form controller: state, progressive client validation, conditional visibility, submission, events. */
|
|
94
|
-
const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, events, t, locale = "en", layout, submitLabel, nextLabel, prevLabel, closeLabel
|
|
36
|
+
const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSuccess, onError, events, t, locale = "en", layout, submitLabel, nextLabel, prevLabel, closeLabel, successMessage, presentation, presentations, onClose, title, initialValues, honeypot, captchaToken, children, header, className, renderSubmit, renderNext, renderBack, submitButtonClassName, nextButtonClassName, backButtonClassName }) => {
|
|
95
37
|
const honeypotName = honeypot === false ? null : honeypot?.name ?? "website";
|
|
96
38
|
const honeypotRef = useRef(null);
|
|
97
39
|
const registry = useMemo(() => buildFieldTypeRegistry(fieldTypes), [fieldTypes]);
|
|
@@ -101,6 +43,8 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
101
43
|
const activePresentation = typeof presentation === "object" ? presentation : presentationRegistry.get(presentation ?? "page") ?? presentationRegistry.get("page") ?? defaultPresentationDescriptors.page;
|
|
102
44
|
const fieldsByName = useMemo(() => new Map(form.fields.filter(isNamedField).map((field) => [field.name, field])), [form.fields]);
|
|
103
45
|
const translate = useMemo(() => t ?? makeTranslate(en), [t]);
|
|
46
|
+
const resolvedCloseLabel = closeLabel ?? translate(keys.formClose);
|
|
47
|
+
const resolvedSuccessMessage = successMessage ?? translate(keys.formSuccess);
|
|
104
48
|
const docButtons = form.buttons;
|
|
105
49
|
const labels = useMemo(() => ({
|
|
106
50
|
prev: prevLabel ?? storedLabel(docButtons?.prevLabel) ?? translate(keys.formBack),
|
|
@@ -141,6 +85,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
141
85
|
const startedRef = useRef(false);
|
|
142
86
|
const submittedRef = useRef(false);
|
|
143
87
|
const submittingRef = useRef(false);
|
|
88
|
+
const advancingRef = useRef(false);
|
|
144
89
|
const flowRef = useRef(flow);
|
|
145
90
|
flowRef.current = flow;
|
|
146
91
|
const dispatch = useCallback((action) => {
|
|
@@ -211,46 +156,86 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
211
156
|
value: recall(field.name)
|
|
212
157
|
}));
|
|
213
158
|
const stepVisible = (flow && currentStepId ? stepFieldNames(flow, currentStepId) : []).map((key) => visible.find((field) => fieldKey(field) === key)).filter((field) => Boolean(field));
|
|
159
|
+
const validateRepeaterSubFields = async (repeaters) => {
|
|
160
|
+
const errors = {};
|
|
161
|
+
for (const field of repeaters.filter((f) => f.blockType === "repeater" && isNamedField(f))) {
|
|
162
|
+
const rows = Array.isArray(effectiveValues[field.name]) ? effectiveValues[field.name] : [];
|
|
163
|
+
const subFields = (Array.isArray(field.subFields) ? field.subFields : []).filter(isNamedField);
|
|
164
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
165
|
+
const row = rows[rowIndex] ?? {};
|
|
166
|
+
for (const subField of subFields) {
|
|
167
|
+
if (!evaluateCondition(subField.visibleWhen, row)) continue;
|
|
168
|
+
if (!evaluateCondition(subField.validateWhen, row)) continue;
|
|
169
|
+
const subResult = await validateFieldValue({
|
|
170
|
+
field: subField,
|
|
171
|
+
value: row[subField.name],
|
|
172
|
+
registry,
|
|
173
|
+
ruleRegistry,
|
|
174
|
+
answers: row,
|
|
175
|
+
locale,
|
|
176
|
+
t: translate
|
|
177
|
+
});
|
|
178
|
+
const compositeKey = `${field.name}[${rowIndex}].${subField.name}`;
|
|
179
|
+
if (subResult.errors.length > 0) errors[compositeKey] = subResult.errors;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return errors;
|
|
184
|
+
};
|
|
214
185
|
const goNext = async () => {
|
|
215
|
-
if (!flow || !currentStepId) return;
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
186
|
+
if (!flow || !currentStepId || advancingRef.current) return;
|
|
187
|
+
advancingRef.current = true;
|
|
188
|
+
try {
|
|
189
|
+
const results = await Promise.all(stepVisible.filter((field) => !calcExpressionOf(field)).filter((field) => registry.get(field.blockType)?.value !== "none").filter(isNamedField).filter((field) => evaluateCondition(field.validateWhen, effectiveValues)).map(async (field) => ({
|
|
219
190
|
field,
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
191
|
+
...await validateFieldValue({
|
|
192
|
+
field,
|
|
193
|
+
value: effectiveValues[field.name],
|
|
194
|
+
registry,
|
|
195
|
+
ruleRegistry,
|
|
196
|
+
answers: effectiveValues,
|
|
197
|
+
locale,
|
|
198
|
+
t: translate
|
|
199
|
+
})
|
|
200
|
+
})));
|
|
201
|
+
let hasError = false;
|
|
202
|
+
for (const result of results) {
|
|
203
|
+
rawDispatch({
|
|
204
|
+
type: "TOUCH",
|
|
205
|
+
name: result.field.name
|
|
206
|
+
});
|
|
207
|
+
rawDispatch({
|
|
208
|
+
type: "SET_FIELD_ISSUES",
|
|
209
|
+
name: result.field.name,
|
|
210
|
+
errors: result.errors
|
|
211
|
+
});
|
|
212
|
+
if (result.errors.length > 0) hasError = true;
|
|
213
|
+
}
|
|
214
|
+
const repeaterErrors = await validateRepeaterSubFields(stepVisible);
|
|
215
|
+
for (const [compositeKey, errs] of Object.entries(repeaterErrors)) {
|
|
216
|
+
rawDispatch({
|
|
217
|
+
type: "SET_FIELD_ISSUES",
|
|
218
|
+
name: compositeKey,
|
|
219
|
+
errors: errs
|
|
220
|
+
});
|
|
221
|
+
hasError = true;
|
|
222
|
+
}
|
|
223
|
+
if (hasError) return;
|
|
224
|
+
const next = resolveNextStepId(flow, currentStepId, effectiveValues);
|
|
225
|
+
if (!next) return;
|
|
226
|
+
emitFormEvent(sinkRef.current, formIdRef.current, {
|
|
227
|
+
type: "step.completed",
|
|
228
|
+
stepId: currentStepId
|
|
233
229
|
});
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
230
|
+
setHistory((prev) => [...prev, currentStepId]);
|
|
231
|
+
setCurrentStepId(next);
|
|
232
|
+
emitFormEvent(sinkRef.current, formIdRef.current, {
|
|
233
|
+
type: "step.viewed",
|
|
234
|
+
stepId: next
|
|
238
235
|
});
|
|
239
|
-
|
|
236
|
+
} finally {
|
|
237
|
+
advancingRef.current = false;
|
|
240
238
|
}
|
|
241
|
-
if (hasError) return;
|
|
242
|
-
const next = resolveNextStepId(flow, currentStepId, effectiveValues);
|
|
243
|
-
if (!next) return;
|
|
244
|
-
emitFormEvent(sinkRef.current, formIdRef.current, {
|
|
245
|
-
type: "step.completed",
|
|
246
|
-
stepId: currentStepId
|
|
247
|
-
});
|
|
248
|
-
setHistory((prev) => [...prev, currentStepId]);
|
|
249
|
-
setCurrentStepId(next);
|
|
250
|
-
emitFormEvent(sinkRef.current, formIdRef.current, {
|
|
251
|
-
type: "step.viewed",
|
|
252
|
-
stepId: next
|
|
253
|
-
});
|
|
254
239
|
};
|
|
255
240
|
const goBack = () => {
|
|
256
241
|
const prev = history[history.length - 1];
|
|
@@ -306,28 +291,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
306
291
|
message: firstError
|
|
307
292
|
});
|
|
308
293
|
}
|
|
309
|
-
|
|
310
|
-
const rows = Array.isArray(effectiveValues[field.name]) ? effectiveValues[field.name] : [];
|
|
311
|
-
const subFields = (Array.isArray(field.subFields) ? field.subFields : []).filter(isNamedField);
|
|
312
|
-
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
313
|
-
const row = rows[rowIndex] ?? {};
|
|
314
|
-
for (const subField of subFields) {
|
|
315
|
-
if (!evaluateCondition(subField.visibleWhen, row)) continue;
|
|
316
|
-
if (!evaluateCondition(subField.validateWhen, row)) continue;
|
|
317
|
-
const subResult = await validateFieldValue({
|
|
318
|
-
field: subField,
|
|
319
|
-
value: row[subField.name],
|
|
320
|
-
registry,
|
|
321
|
-
ruleRegistry,
|
|
322
|
-
answers: row,
|
|
323
|
-
locale,
|
|
324
|
-
t: translate
|
|
325
|
-
});
|
|
326
|
-
const compositeKey = `${field.name}[${rowIndex}].${subField.name}`;
|
|
327
|
-
if (subResult.errors.length > 0) errors[compositeKey] = subResult.errors;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
294
|
+
Object.assign(errors, await validateRepeaterSubFields(visible));
|
|
331
295
|
rawDispatch({
|
|
332
296
|
type: "SET_ALL_ISSUES",
|
|
333
297
|
errors
|
|
@@ -341,7 +305,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
341
305
|
if (honeypotName) {
|
|
342
306
|
const decoy = honeypotRef.current?.value ?? "";
|
|
343
307
|
if (decoy !== "") values.push({
|
|
344
|
-
field:
|
|
308
|
+
field: HONEYPOT_VALUE_KEY,
|
|
345
309
|
value: decoy
|
|
346
310
|
});
|
|
347
311
|
}
|
|
@@ -374,7 +338,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
374
338
|
type: "SET_ALL_ISSUES",
|
|
375
339
|
errors: result.fieldErrors
|
|
376
340
|
});
|
|
377
|
-
const message = result.message ??
|
|
341
|
+
const message = result.message ?? translate(keys.formSubmitFailed);
|
|
378
342
|
rawDispatch({
|
|
379
343
|
type: "SUBMIT_ERROR",
|
|
380
344
|
message
|
|
@@ -410,7 +374,9 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
410
374
|
rendererRegistry,
|
|
411
375
|
labels,
|
|
412
376
|
t: translate,
|
|
413
|
-
effectiveValues
|
|
377
|
+
effectiveValues,
|
|
378
|
+
recall,
|
|
379
|
+
renderedFields: (flow ? stepVisible : visible).filter((field) => field.hidden !== true && field.calcDisplay !== false)
|
|
414
380
|
};
|
|
415
381
|
const PresentationWrapper = activePresentation.Wrapper;
|
|
416
382
|
const wrap = (content) => PresentationWrapper ? /* @__PURE__ */ jsx(PresentationWrapper, {
|
|
@@ -418,7 +384,7 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
418
384
|
open: true,
|
|
419
385
|
onClose: handleClose,
|
|
420
386
|
title,
|
|
421
|
-
closeLabel,
|
|
387
|
+
closeLabel: resolvedCloseLabel,
|
|
422
388
|
children: content
|
|
423
389
|
}) : content;
|
|
424
390
|
if (children !== void 0) return /* @__PURE__ */ jsx(FormContext.Provider, {
|
|
@@ -454,11 +420,10 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
454
420
|
className: cn("fb-form__success", className),
|
|
455
421
|
"data-fb-presentation": activePresentation.name,
|
|
456
422
|
"data-fb-density": activePresentation.density,
|
|
457
|
-
children: interpolate(
|
|
423
|
+
children: interpolate(resolvedSuccessMessage, recall)
|
|
458
424
|
}))
|
|
459
425
|
});
|
|
460
426
|
}
|
|
461
|
-
const rendered = (flow ? stepVisible : visible).filter((field) => field.hidden !== true && field.calcDisplay !== false);
|
|
462
427
|
return /* @__PURE__ */ jsx(FormContext.Provider, {
|
|
463
428
|
value: contextValue,
|
|
464
429
|
children: wrap(/* @__PURE__ */ jsxs("form", {
|
|
@@ -472,35 +437,8 @@ const Form = ({ form, fieldTypes, rules, renderers, apiRoute, onSubmit, onSucces
|
|
|
472
437
|
name: honeypotName,
|
|
473
438
|
inputRef: honeypotRef
|
|
474
439
|
}) : null,
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
children: rendered.map((field) => {
|
|
478
|
-
const renderer = rendererRegistry.get(field.blockType);
|
|
479
|
-
if (!renderer) return null;
|
|
480
|
-
const width = typeof field.width === "string" && FIELD_WIDTHS.has(field.width) ? field.width : void 0;
|
|
481
|
-
const recalledField = applyRecall(field, recall);
|
|
482
|
-
return /* @__PURE__ */ jsx("div", {
|
|
483
|
-
...widthProps(width),
|
|
484
|
-
children: !isNamedField(recalledField) ? /* @__PURE__ */ jsx(StaticFieldHost, {
|
|
485
|
-
field: recalledField,
|
|
486
|
-
renderer,
|
|
487
|
-
locale,
|
|
488
|
-
t: translate
|
|
489
|
-
}) : calcExpressionOf(recalledField) ? /* @__PURE__ */ jsx(CalcFieldHost, {
|
|
490
|
-
field: recalledField,
|
|
491
|
-
renderer,
|
|
492
|
-
value: effectiveValues[recalledField.name],
|
|
493
|
-
locale,
|
|
494
|
-
t: translate
|
|
495
|
-
}) : /* @__PURE__ */ jsx(FieldHost, {
|
|
496
|
-
field: recalledField,
|
|
497
|
-
renderer,
|
|
498
|
-
locale,
|
|
499
|
-
t: translate
|
|
500
|
-
})
|
|
501
|
-
}, fieldKey(field));
|
|
502
|
-
})
|
|
503
|
-
}),
|
|
440
|
+
header,
|
|
441
|
+
/* @__PURE__ */ jsx(FormFields, { layout }),
|
|
504
442
|
state.submitError ? /* @__PURE__ */ jsx("p", {
|
|
505
443
|
role: "alert",
|
|
506
444
|
className: "fb-form__submit-error",
|