@10x-media/form-builder 0.1.0-beta.4 → 0.1.0-beta.6
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 +112 -0
- package/README.md +3 -3
- package/dist/actions/body/serializeBody.d.ts +8 -5
- package/dist/actions/body/serializeBody.js.map +1 -1
- package/dist/actions/body/textOfBody.d.ts +13 -0
- package/dist/actions/body/textOfBody.js +35 -0
- package/dist/actions/body/textOfBody.js.map +1 -0
- package/dist/actions/builtin/confirmation.js +81 -48
- package/dist/actions/builtin/confirmation.js.map +1 -1
- package/dist/actions/builtin/emailTeam.js +67 -38
- package/dist/actions/builtin/emailTeam.js.map +1 -1
- package/dist/actions/builtin/index.d.ts +9 -3
- package/dist/actions/builtin/index.js +8 -5
- package/dist/actions/builtin/index.js.map +1 -1
- package/dist/actions/defineAction.d.ts +1 -1
- package/dist/actions/emailRecipients.d.ts +12 -0
- package/dist/actions/emailRecipients.js +72 -0
- package/dist/actions/emailRecipients.js.map +1 -0
- package/dist/actions/fromAddresses.d.ts +21 -0
- package/dist/actions/fromAddresses.js +74 -0
- package/dist/actions/fromAddresses.js.map +1 -0
- package/dist/actions/task.js +9 -8
- package/dist/actions/task.js.map +1 -1
- package/dist/aggregation/aggregateResponses.d.ts +9 -0
- package/dist/aggregation/aggregateResponses.js +8 -14
- package/dist/aggregation/aggregateResponses.js.map +1 -1
- package/dist/aggregation/resolveResultsRequest.d.ts +19 -6
- package/dist/aggregation/resolveResultsRequest.js +58 -12
- package/dist/aggregation/resolveResultsRequest.js.map +1 -1
- package/dist/calc/computeCalcFields.js +2 -1
- package/dist/calc/computeCalcFields.js.map +1 -1
- package/dist/client/ByteSizeField.d.ts +8 -0
- package/dist/client/ByteSizeField.js +3 -1
- package/dist/client/ByteSizeField.js.map +1 -1
- package/dist/client/ClosePollButton.d.ts +15 -0
- package/dist/client/ClosePollButton.js +80 -0
- package/dist/client/ClosePollButton.js.map +1 -0
- package/dist/client/CondensedArray.d.ts +15 -0
- package/dist/client/CondensedArray.js +97 -0
- package/dist/client/CondensedArray.js.map +1 -0
- 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 +46 -0
- package/dist/client/EndpointOptionsSelect.js +106 -0
- package/dist/client/EndpointOptionsSelect.js.map +1 -0
- package/dist/client/FieldBlockLabel.d.ts +18 -0
- package/dist/client/FieldBlockLabel.js +34 -0
- package/dist/client/FieldBlockLabel.js.map +1 -0
- package/dist/client/FieldNameSelect.d.ts +21 -4
- package/dist/client/FieldNameSelect.js +29 -15
- package/dist/client/FieldNameSelect.js.map +1 -1
- package/dist/client/FlowBuilder.d.ts +2 -1
- package/dist/client/FlowBuilder.js +63 -57
- package/dist/client/FlowBuilder.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/RuleDescription.d.ts +21 -0
- package/dist/client/RuleDescription.js +19 -0
- package/dist/client/RuleDescription.js.map +1 -0
- package/dist/client/admin.css +41 -0
- package/dist/client/endpointOptions.js +29 -0
- package/dist/client/endpointOptions.js.map +1 -0
- package/dist/client/flowAuthoring.js +114 -7
- package/dist/client/flowAuthoring.js.map +1 -1
- package/dist/client/synthesizeClientField.js +15 -8
- package/dist/client/synthesizeClientField.js.map +1 -1
- package/dist/collections/buttonFields.d.ts +34 -0
- package/dist/collections/buttonFields.js +35 -0
- package/dist/collections/buttonFields.js.map +1 -0
- package/dist/collections/cells/FieldCountCell.d.ts +15 -0
- package/dist/collections/cells/FieldCountCell.js +18 -0
- package/dist/collections/cells/FieldCountCell.js.map +1 -0
- package/dist/collections/cells/FlowStepsCell.d.ts +14 -0
- package/dist/collections/cells/FlowStepsCell.js +17 -0
- package/dist/collections/cells/FlowStepsCell.js.map +1 -0
- package/dist/collections/formSubmissions.js +23 -17
- package/dist/collections/formSubmissions.js.map +1 -1
- package/dist/collections/forms.js +323 -134
- package/dist/collections/forms.js.map +1 -1
- package/dist/collections/redirectFields.d.ts +29 -0
- package/dist/consent/applyConsentStatements.d.ts +14 -0
- package/dist/consent/applyConsentStatements.js +17 -0
- package/dist/consent/applyConsentStatements.js.map +1 -0
- package/dist/consent/captureConsent.d.ts +31 -11
- package/dist/consent/captureConsent.js +53 -13
- package/dist/consent/captureConsent.js.map +1 -1
- package/dist/consent/consentSourcesField.d.ts +62 -0
- package/dist/consent/consentSourcesField.js +86 -0
- package/dist/consent/consentSourcesField.js.map +1 -0
- package/dist/consent/resolveConsentEntries.d.ts +24 -0
- package/dist/consent/resolveConsentEntries.js +43 -0
- package/dist/consent/resolveConsentEntries.js.map +1 -0
- package/dist/consent/resolveConsentSourcesRequest.d.ts +37 -0
- package/dist/consent/resolveConsentSourcesRequest.js +55 -0
- package/dist/consent/resolveConsentSourcesRequest.js.map +1 -0
- package/dist/consent/resolveConsentStatements.d.ts +45 -0
- package/dist/consent/resolveConsentStatements.js +44 -0
- package/dist/consent/resolveConsentStatements.js.map +1 -0
- package/dist/consent/resolvePublishedVersionRef.d.ts +17 -11
- package/dist/consent/resolvePublishedVersionRef.js +13 -10
- package/dist/consent/resolvePublishedVersionRef.js.map +1 -1
- package/dist/consent/types.d.ts +53 -0
- package/dist/email/departments.d.ts +59 -0
- package/dist/email/departments.js +87 -0
- package/dist/email/departments.js.map +1 -0
- package/dist/email/departmentsField.d.ts +53 -0
- package/dist/email/departmentsField.js +73 -0
- package/dist/email/departmentsField.js.map +1 -0
- package/dist/exports/client.d.ts +11 -1
- package/dist/exports/client.js +11 -1
- package/dist/exports/react.d.ts +8 -3
- package/dist/exports/react.js +6 -2
- package/dist/exports/rsc.d.ts +3 -1
- package/dist/exports/rsc.js +4 -2
- package/dist/exports/types.d.ts +16 -8
- package/dist/fields/buildFieldBlocks.js +28 -13
- package/dist/fields/buildFieldBlocks.js.map +1 -1
- package/dist/fields/builtin/calculation.js +7 -0
- package/dist/fields/builtin/calculation.js.map +1 -1
- package/dist/fields/builtin/checkbox.js +8 -0
- package/dist/fields/builtin/checkbox.js.map +1 -1
- package/dist/fields/builtin/consent.js +32 -21
- package/dist/fields/builtin/consent.js.map +1 -1
- package/dist/fields/builtin/country.d.ts +13 -0
- package/dist/fields/builtin/country.js +30 -0
- package/dist/fields/builtin/country.js.map +1 -0
- package/dist/fields/builtin/date.js +2 -0
- package/dist/fields/builtin/date.js.map +1 -1
- package/dist/fields/builtin/file.js +14 -7
- package/dist/fields/builtin/file.js.map +1 -1
- package/dist/fields/builtin/index.d.ts +8 -6
- package/dist/fields/builtin/index.js +12 -11
- package/dist/fields/builtin/index.js.map +1 -1
- package/dist/fields/builtin/message.js +8 -4
- package/dist/fields/builtin/message.js.map +1 -1
- package/dist/fields/builtin/repeater.js +1 -0
- package/dist/fields/builtin/repeater.js.map +1 -1
- package/dist/fields/builtin/select.js +1 -0
- package/dist/fields/builtin/select.js.map +1 -1
- package/dist/fields/builtin/state.d.ts +13 -0
- package/dist/fields/builtin/state.js +30 -0
- package/dist/fields/builtin/state.js.map +1 -0
- package/dist/fields/data/regions.d.ts +21 -0
- package/dist/fields/data/regions.js +1238 -0
- package/dist/fields/data/regions.js.map +1 -0
- package/dist/fields/fieldKey.d.ts +15 -0
- package/dist/fields/fieldKey.js +16 -0
- package/dist/fields/fieldKey.js.map +1 -0
- package/dist/fields/fieldNamesOfType.js +13 -8
- package/dist/fields/fieldNamesOfType.js.map +1 -1
- package/dist/fields/instanceOptions.js +20 -0
- package/dist/fields/instanceOptions.js.map +1 -0
- package/dist/fields/registry.d.ts +1 -0
- package/dist/fields/registry.js +21 -2
- package/dist/fields/registry.js.map +1 -1
- package/dist/fields/sharedConfig.js +46 -9
- package/dist/fields/sharedConfig.js.map +1 -1
- package/dist/fields/types.d.ts +63 -5
- package/dist/flow/engine.d.ts +6 -4
- package/dist/flow/engine.js +11 -6
- package/dist/flow/engine.js.map +1 -1
- package/dist/flow/normalizeFlow.js +16 -7
- package/dist/flow/normalizeFlow.js.map +1 -1
- package/dist/flow/types.d.ts +12 -3
- package/dist/flow/types.js +11 -0
- package/dist/flow/types.js.map +1 -0
- package/dist/form/pollState.d.ts +4 -4
- package/dist/form/pollState.js.map +1 -1
- package/dist/form/toFormDocument.d.ts +54 -12
- package/dist/form/toFormDocument.js +56 -8
- package/dist/form/toFormDocument.js.map +1 -1
- package/dist/form/types.d.ts +31 -14
- package/dist/index.d.ts +153 -29
- package/dist/index.js +47 -15
- package/dist/index.js.map +1 -1
- package/dist/plugin/customState.js +26 -0
- package/dist/plugin/customState.js.map +1 -0
- package/dist/plugin/registerCollections.js +24 -13
- package/dist/plugin/registerCollections.js.map +1 -1
- package/dist/plugin/uploadsCollection.d.ts +5 -0
- package/dist/plugin/uploadsCollection.js +10 -1
- package/dist/plugin/uploadsCollection.js.map +1 -1
- package/dist/poll/applyPollOptions.js +3 -2
- package/dist/poll/applyPollOptions.js.map +1 -1
- package/dist/poll/buildPollOptionSourceFields.js +2 -5
- package/dist/poll/buildPollOptionSourceFields.js.map +1 -1
- package/dist/poll/closeJob.d.ts +52 -0
- package/dist/poll/closeJob.js +119 -0
- package/dist/poll/closeJob.js.map +1 -0
- package/dist/poll/definePollOptionSource.d.ts +10 -1
- package/dist/poll/definePollOptionSource.js.map +1 -1
- package/dist/poll/definePollType.d.ts +34 -0
- package/dist/poll/definePollType.js +6 -0
- package/dist/poll/definePollType.js.map +1 -0
- package/dist/poll/effectivePollOptions.d.ts +38 -0
- package/dist/poll/effectivePollOptions.js +64 -0
- package/dist/poll/effectivePollOptions.js.map +1 -0
- package/dist/poll/mostVoted.d.ts +21 -0
- package/dist/poll/mostVoted.js +32 -0
- package/dist/poll/mostVoted.js.map +1 -0
- package/dist/poll/outcomeBeforeChange.js +91 -0
- package/dist/poll/outcomeBeforeChange.js.map +1 -0
- package/dist/poll/outcomeFields.d.ts +38 -0
- package/dist/poll/outcomeFields.js +52 -0
- package/dist/poll/outcomeFields.js.map +1 -0
- package/dist/poll/pollTypeRegistry.d.ts +34 -0
- package/dist/poll/pollTypeRegistry.js +71 -0
- package/dist/poll/pollTypeRegistry.js.map +1 -0
- package/dist/poll/resolvePollCloseRequest.js +105 -0
- package/dist/poll/resolvePollCloseRequest.js.map +1 -0
- package/dist/poll/resolvePollOptions.d.ts +2 -1
- package/dist/poll/resolvePollOptions.js +5 -17
- package/dist/poll/resolvePollOptions.js.map +1 -1
- package/dist/poll/resolvePollOptionsRequest.js +51 -0
- package/dist/poll/resolvePollOptionsRequest.js.map +1 -0
- package/dist/poll/resolvePollOutcome.d.ts +22 -9
- package/dist/poll/resolvePollOutcome.js +54 -14
- package/dist/poll/resolvePollOutcome.js.map +1 -1
- package/dist/poll/resultsField.js +21 -0
- package/dist/poll/resultsField.js.map +1 -0
- package/dist/prefill/valuesFromSearchParams.js +2 -1
- package/dist/prefill/valuesFromSearchParams.js.map +1 -1
- package/dist/react/Form.d.ts +8 -8
- package/dist/react/Form.js +74 -62
- package/dist/react/Form.js.map +1 -1
- package/dist/react/FormContext.d.ts +6 -4
- package/dist/react/FormContext.js.map +1 -1
- package/dist/react/FormControls.d.ts +3 -3
- package/dist/react/FormControls.js +3 -3
- package/dist/react/FormControls.js.map +1 -1
- package/dist/react/FormResults.d.ts +5 -5
- package/dist/react/FormResults.js +6 -6
- package/dist/react/FormResults.js.map +1 -1
- package/dist/react/Poll.d.ts +2 -2
- package/dist/react/Poll.js +5 -5
- package/dist/react/Poll.js.map +1 -1
- package/dist/react/contract.d.ts +2 -3
- package/dist/react/contract.js.map +1 -1
- package/dist/react/primitives/Checkbox.d.ts +4 -2
- package/dist/react/primitives/Checkbox.js +2 -1
- package/dist/react/primitives/Checkbox.js.map +1 -1
- package/dist/react/primitives/FieldShell.d.ts +2 -4
- package/dist/react/primitives/FieldShell.js +12 -19
- package/dist/react/primitives/FieldShell.js.map +1 -1
- package/dist/react/renderers/calculation.js +1 -2
- package/dist/react/renderers/calculation.js.map +1 -1
- package/dist/react/renderers/checkbox.js +1 -2
- package/dist/react/renderers/checkbox.js.map +1 -1
- package/dist/react/renderers/consent.js +78 -34
- 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 -2
- package/dist/react/renderers/email.js.map +1 -1
- package/dist/react/renderers/file.js +1 -2
- package/dist/react/renderers/file.js.map +1 -1
- package/dist/react/renderers/index.js +3 -0
- package/dist/react/renderers/index.js.map +1 -1
- package/dist/react/renderers/number.js +1 -2
- package/dist/react/renderers/number.js.map +1 -1
- package/dist/react/renderers/region.js +45 -0
- package/dist/react/renderers/region.js.map +1 -0
- package/dist/react/renderers/repeater.js +7 -10
- package/dist/react/renderers/repeater.js.map +1 -1
- package/dist/react/renderers/select.js +1 -2
- package/dist/react/renderers/select.js.map +1 -1
- package/dist/react/renderers/text.js +1 -2
- package/dist/react/renderers/text.js.map +1 -1
- package/dist/react/renderers/textarea.js +1 -2
- package/dist/react/renderers/textarea.js.map +1 -1
- package/dist/react/state.d.ts +0 -3
- package/dist/react/state.js +7 -11
- package/dist/react/state.js.map +1 -1
- package/dist/react/useField.d.ts +0 -1
- package/dist/react/useField.js +0 -1
- package/dist/react/useField.js.map +1 -1
- package/dist/react/useFormState.d.ts +1 -1
- package/dist/react/useFormState.js +1 -1
- package/dist/react/useFormState.js.map +1 -1
- package/dist/react/validateField.js +3 -6
- package/dist/react/validateField.js.map +1 -1
- package/dist/recall/interpolate.d.ts +4 -3
- package/dist/recall/interpolate.js +5 -4
- package/dist/recall/interpolate.js.map +1 -1
- package/dist/recall/resolver.d.ts +1 -1
- package/dist/recall/resolver.js +14 -2
- package/dist/recall/resolver.js.map +1 -1
- package/dist/spam/captcha.d.ts +1 -1
- package/dist/spam/captcha.js +1 -1
- package/dist/spam/captcha.js.map +1 -1
- package/dist/submissions/SubmissionAnswers.js +6 -2
- package/dist/submissions/SubmissionAnswers.js.map +1 -1
- package/dist/submissions/SubmissionAnswersClient.js +100 -64
- package/dist/submissions/SubmissionAnswersClient.js.map +1 -1
- package/dist/submissions/createSubmission.d.ts +36 -0
- package/dist/submissions/createSubmission.js +26 -0
- package/dist/submissions/createSubmission.js.map +1 -0
- package/dist/submissions/runSubmission.d.ts +10 -0
- package/dist/submissions/runSubmission.js +31 -19
- package/dist/submissions/runSubmission.js.map +1 -1
- package/dist/submissions/types.d.ts +11 -4
- package/dist/submissions/validateSubmission.js +39 -23
- package/dist/submissions/validateSubmission.js.map +1 -1
- package/dist/submissions/votedCookie.js +3 -3
- package/dist/submissions/votedCookie.js.map +1 -1
- package/dist/translations/de.js +104 -26
- package/dist/translations/de.js.map +1 -1
- package/dist/translations/en.js +105 -27
- package/dist/translations/en.js.map +1 -1
- package/dist/translations/keys.d.ts +99 -21
- package/dist/translations/keys.js +100 -22
- package/dist/translations/keys.js.map +1 -1
- package/dist/uploads/resolveFileRef.js +2 -1
- package/dist/uploads/resolveFileRef.js.map +1 -1
- package/dist/validation/buildRuleBlocks.js +18 -17
- package/dist/validation/buildRuleBlocks.js.map +1 -1
- package/dist/validation/builtin/email.js +1 -0
- package/dist/validation/builtin/email.js.map +1 -1
- package/dist/validation/builtin/matchesField.js +3 -7
- package/dist/validation/builtin/matchesField.js.map +1 -1
- package/dist/validation/builtin/max.js +1 -0
- package/dist/validation/builtin/max.js.map +1 -1
- package/dist/validation/builtin/maxDate.js +1 -0
- package/dist/validation/builtin/maxDate.js.map +1 -1
- package/dist/validation/builtin/maxLength.js +1 -0
- package/dist/validation/builtin/maxLength.js.map +1 -1
- package/dist/validation/builtin/min.js +1 -0
- package/dist/validation/builtin/min.js.map +1 -1
- package/dist/validation/builtin/minDate.js +1 -0
- package/dist/validation/builtin/minDate.js.map +1 -1
- package/dist/validation/builtin/minLength.js +1 -0
- package/dist/validation/builtin/minLength.js.map +1 -1
- package/dist/validation/builtin/notAlreadySubmitted.js +1 -0
- package/dist/validation/builtin/notAlreadySubmitted.js.map +1 -1
- package/dist/validation/builtin/oneOf.js +1 -0
- package/dist/validation/builtin/oneOf.js.map +1 -1
- package/dist/validation/builtin/pattern.js +11 -2
- package/dist/validation/builtin/pattern.js.map +1 -1
- package/dist/validation/builtin/url.js +1 -0
- package/dist/validation/builtin/url.js.map +1 -1
- package/dist/validation/builtin/validateRegexParams.js +44 -0
- package/dist/validation/builtin/validateRegexParams.js.map +1 -0
- package/dist/validation/fieldTargetParam.d.ts +22 -0
- package/dist/validation/fieldTargetParam.js +38 -0
- package/dist/validation/fieldTargetParam.js.map +1 -0
- package/dist/validation/runValidation.js +8 -29
- package/dist/validation/runValidation.js.map +1 -1
- package/dist/validation/types.d.ts +5 -11
- package/package.json +6 -6
- package/dist/consent/buildConsentSourceConfig.js +0 -49
- package/dist/consent/buildConsentSourceConfig.js.map +0 -1
- package/dist/consent/builtin/index.d.ts +0 -17
- package/dist/consent/builtin/index.js +0 -21
- package/dist/consent/builtin/index.js.map +0 -1
- package/dist/consent/builtin/pageReference.js +0 -75
- package/dist/consent/builtin/pageReference.js.map +0 -1
- package/dist/consent/builtin/static.js +0 -48
- package/dist/consent/builtin/static.js.map +0 -1
- package/dist/consent/defineConsentSource.d.ts +0 -35
- package/dist/consent/defineConsentSource.js +0 -6
- package/dist/consent/defineConsentSource.js.map +0 -1
- package/dist/consent/registry.d.ts +0 -16
- package/dist/consent/registry.js +0 -16
- package/dist/consent/registry.js.map +0 -1
- package/dist/consent/resolveConsentLinks.d.ts +0 -19
- package/dist/consent/resolveConsentLinks.js +0 -26
- package/dist/consent/resolveConsentLinks.js.map +0 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { fieldNames, fieldNamesOfType } from "../fields/fieldNamesOfType.js";
|
|
3
|
+
import { keys } from "../translations/keys.js";
|
|
4
|
+
import { isPlausibleEmail } from "../actions/emailRecipients.js";
|
|
5
|
+
import { useTranslation as useTranslation$1 } from "../translations/useTranslation.js";
|
|
6
|
+
import { toStaticLabel } from "./toStaticLabel.js";
|
|
7
|
+
import { buildEndpointOptionsUrl, parseEndpointOptions } from "./endpointOptions.js";
|
|
8
|
+
import { reduceFieldsToValues } from "payload/shared";
|
|
9
|
+
import { FieldDescription, FieldError, FieldLabel, ReactSelect, RenderCustomComponent, useConfig, useDocumentInfo, useField, useFormFields } from "@payloadcms/ui";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { useEffect, useMemo, useState } from "react";
|
|
12
|
+
//#region src/client/RecipientsSelect.tsx
|
|
13
|
+
const tokenOptionsFromData = (data, types) => {
|
|
14
|
+
const rows = Array.isArray(data.fields) ? data.fields : [];
|
|
15
|
+
const labels = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const row of rows) {
|
|
17
|
+
if (!row || typeof row !== "object") continue;
|
|
18
|
+
const { name, label } = row;
|
|
19
|
+
if (typeof name === "string" && typeof label === "string" && label.length > 0) labels.set(name.trim(), label);
|
|
20
|
+
}
|
|
21
|
+
return (types && types.length > 0 ? fieldNamesOfType(data.fields, types) : fieldNames(data.fields)).map((name) => ({
|
|
22
|
+
label: labels.get(name) ?? name,
|
|
23
|
+
value: `{{${name}}}`
|
|
24
|
+
}));
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* A creatable, multi-value recipient field: pick a department (preset options from `endpoint`), pick a
|
|
28
|
+
* form-field token (`{{field}}`, offered when `fieldTokens`), or type any email and press Enter
|
|
29
|
+
* (`allowCustom`). Renders as native Payload badges (drag-reorderable), stores a `string[]`, honors
|
|
30
|
+
* `admin.width` and the standard field props. Unknown or legacy stored values stay selectable.
|
|
31
|
+
*/
|
|
32
|
+
const RecipientsSelect = (props) => {
|
|
33
|
+
const allowCustom = props.allowCustom !== false;
|
|
34
|
+
const fieldTokens = props.fieldTokens !== false;
|
|
35
|
+
const { customComponents: { Description, Error: ErrorComponent, Label } = {}, disabled, path, setValue, showError, value } = useField({ path: props.path });
|
|
36
|
+
const { t } = useTranslation$1();
|
|
37
|
+
const label = toStaticLabel(props.field?.label ?? props.label);
|
|
38
|
+
const description = props.descriptionKey ? t(props.descriptionKey) : toStaticLabel(props.field?.admin?.description);
|
|
39
|
+
const { id, collectionSlug } = useDocumentInfo();
|
|
40
|
+
const { config } = useConfig();
|
|
41
|
+
const apiRoute = config.routes.api;
|
|
42
|
+
const readOnly = props.readOnly === true || disabled === true;
|
|
43
|
+
const [state, setState] = useState({ status: "idle" });
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!props.endpoint || id == null || !collectionSlug) return;
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
const url = buildEndpointOptionsUrl({
|
|
48
|
+
apiRoute,
|
|
49
|
+
collectionSlug,
|
|
50
|
+
id,
|
|
51
|
+
endpoint: props.endpoint
|
|
52
|
+
});
|
|
53
|
+
setState({ status: "loading" });
|
|
54
|
+
fetch(url, {
|
|
55
|
+
credentials: "include",
|
|
56
|
+
headers: { Accept: "application/json" },
|
|
57
|
+
signal: controller.signal
|
|
58
|
+
}).then(async (response) => {
|
|
59
|
+
if (!response.ok) throw new Error(`Options request failed with ${response.status}`);
|
|
60
|
+
setState({
|
|
61
|
+
status: "loaded",
|
|
62
|
+
options: parseEndpointOptions(await response.json())
|
|
63
|
+
});
|
|
64
|
+
}).catch(() => {
|
|
65
|
+
if (!controller.signal.aborted) setState({ status: "error" });
|
|
66
|
+
});
|
|
67
|
+
return () => controller.abort();
|
|
68
|
+
}, [
|
|
69
|
+
apiRoute,
|
|
70
|
+
collectionSlug,
|
|
71
|
+
id,
|
|
72
|
+
props.endpoint
|
|
73
|
+
]);
|
|
74
|
+
const presetOptions = state.status === "loaded" ? state.options : [];
|
|
75
|
+
const tokenJson = useFormFields(([fields]) => fieldTokens ? JSON.stringify(tokenOptionsFromData(reduceFieldsToValues(fields, true), props.tokenFieldTypes)) : "[]");
|
|
76
|
+
const tokenOptions = useMemo(() => JSON.parse(tokenJson), [tokenJson]);
|
|
77
|
+
const stored = Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : typeof value === "string" && value.length > 0 ? [value] : [];
|
|
78
|
+
const byValue = useMemo(() => {
|
|
79
|
+
const map = /* @__PURE__ */ new Map();
|
|
80
|
+
for (const option of [...presetOptions, ...tokenOptions]) map.set(option.value, option);
|
|
81
|
+
return map;
|
|
82
|
+
}, [presetOptions, tokenOptions]);
|
|
83
|
+
const selected = stored.map((entry) => byValue.get(entry) ?? {
|
|
84
|
+
label: entry,
|
|
85
|
+
value: entry
|
|
86
|
+
});
|
|
87
|
+
const groupedOptions = useMemo(() => {
|
|
88
|
+
const groups = [];
|
|
89
|
+
if (presetOptions.length > 0) groups.push({
|
|
90
|
+
label: t(keys.recipientsGroupDepartments),
|
|
91
|
+
options: presetOptions
|
|
92
|
+
});
|
|
93
|
+
if (tokenOptions.length > 0) groups.push({
|
|
94
|
+
label: t(keys.recipientsGroupFields),
|
|
95
|
+
options: tokenOptions
|
|
96
|
+
});
|
|
97
|
+
return groups;
|
|
98
|
+
}, [
|
|
99
|
+
presetOptions,
|
|
100
|
+
tokenOptions,
|
|
101
|
+
t
|
|
102
|
+
]);
|
|
103
|
+
const handleChange = (selection) => {
|
|
104
|
+
if (readOnly) return;
|
|
105
|
+
const entries = (Array.isArray(selection) ? selection : selection ? [selection] : []).map((option) => String(option.value).trim()).filter((entry) => entry.length > 0 && (isPlausibleEmail(entry) || byValue.has(entry)));
|
|
106
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
107
|
+
for (const entry of entries) {
|
|
108
|
+
const key = entry.toLowerCase();
|
|
109
|
+
if (!deduped.has(key)) deduped.set(key, entry);
|
|
110
|
+
}
|
|
111
|
+
setValue(Array.from(deduped.values()));
|
|
112
|
+
};
|
|
113
|
+
const fieldStyle = props.field?.admin?.width ? { "--field-width": props.field.admin.width } : void 0;
|
|
114
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
115
|
+
className: [
|
|
116
|
+
"field-type",
|
|
117
|
+
showError && "error",
|
|
118
|
+
readOnly && "read-only"
|
|
119
|
+
].filter(Boolean).join(" "),
|
|
120
|
+
id: path ? `field-${path.replace(/\./g, "__")}` : void 0,
|
|
121
|
+
style: {
|
|
122
|
+
marginBlockEnd: "1rem",
|
|
123
|
+
...fieldStyle
|
|
124
|
+
},
|
|
125
|
+
children: [
|
|
126
|
+
/* @__PURE__ */ jsx(RenderCustomComponent, {
|
|
127
|
+
CustomComponent: Label,
|
|
128
|
+
Fallback: /* @__PURE__ */ jsx(FieldLabel, {
|
|
129
|
+
label,
|
|
130
|
+
path,
|
|
131
|
+
required: props.field?.required
|
|
132
|
+
})
|
|
133
|
+
}),
|
|
134
|
+
/* @__PURE__ */ jsxs("div", {
|
|
135
|
+
className: "field-type__wrap",
|
|
136
|
+
children: [/* @__PURE__ */ jsx(RenderCustomComponent, {
|
|
137
|
+
CustomComponent: ErrorComponent,
|
|
138
|
+
Fallback: /* @__PURE__ */ jsx(FieldError, {
|
|
139
|
+
path,
|
|
140
|
+
showError
|
|
141
|
+
})
|
|
142
|
+
}), /* @__PURE__ */ jsx(ReactSelect, {
|
|
143
|
+
options: groupedOptions,
|
|
144
|
+
value: selected,
|
|
145
|
+
isMulti: true,
|
|
146
|
+
isSortable: true,
|
|
147
|
+
isCreatable: allowCustom,
|
|
148
|
+
isClearable: !readOnly,
|
|
149
|
+
disabled: readOnly,
|
|
150
|
+
isLoading: state.status === "loading",
|
|
151
|
+
showError,
|
|
152
|
+
placeholder: state.status === "loading" ? t(keys.endpointOptionsLoading) : void 0,
|
|
153
|
+
onChange: handleChange,
|
|
154
|
+
filterOption: (option, rawInput) => {
|
|
155
|
+
if (!option) return allowCustom && isPlausibleEmail(rawInput);
|
|
156
|
+
const query = rawInput.toLowerCase();
|
|
157
|
+
return `${option.label} ${option.value}`.toLowerCase().includes(query);
|
|
158
|
+
}
|
|
159
|
+
})]
|
|
160
|
+
}),
|
|
161
|
+
/* @__PURE__ */ jsx(RenderCustomComponent, {
|
|
162
|
+
CustomComponent: Description,
|
|
163
|
+
Fallback: /* @__PURE__ */ jsx(FieldDescription, {
|
|
164
|
+
description: state.status === "error" ? t(keys.endpointOptionsError) : description,
|
|
165
|
+
path
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
]
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
//#endregion
|
|
172
|
+
export { RecipientsSelect };
|
|
173
|
+
|
|
174
|
+
//# sourceMappingURL=RecipientsSelect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RecipientsSelect.js","names":["useTranslation"],"sources":["../../src/client/RecipientsSelect.tsx"],"sourcesContent":["'use client'\n\nimport {\n\tFieldDescription,\n\tFieldError,\n\tFieldLabel,\n\tReactSelect,\n\ttype ReactSelectOption,\n\tRenderCustomComponent,\n\tuseConfig,\n\tuseDocumentInfo,\n\tuseField,\n\tuseFormFields,\n} from '@payloadcms/ui'\nimport { reduceFieldsToValues } from 'payload/shared'\nimport { type CSSProperties, useEffect, useMemo, useState } from 'react'\nimport { isPlausibleEmail } from '../actions/emailRecipients'\nimport { fieldNames, fieldNamesOfType } from '../fields/fieldNamesOfType'\nimport { keys, type TranslationKey } from '../translations/keys'\nimport { useTranslation } from '../translations/useTranslation'\nimport {\n\tbuildEndpointOptionsUrl,\n\ttype EndpointOption,\n\tparseEndpointOptions,\n} from './endpointOptions'\nimport type { FieldRow } from './synthesizeClientField'\nimport { toStaticLabel } from './toStaticLabel'\n\nexport type RecipientsSelectProps = {\n\tpath?: string\n\tfield?: { label?: unknown; required?: boolean; admin?: { description?: unknown; width?: string } }\n\tlabel?: unknown\n\treadOnly?: boolean\n\t/** Endpoint subpath supplying preset address options (e.g. `'departments'`); omit for none. */\n\tendpoint?: string\n\t/** Allow free-typed emails (default true). */\n\tallowCustom?: boolean\n\t/** Offer the form's own fields as recipient tokens (default true). */\n\tfieldTokens?: boolean\n\t/** Field types eligible as tokens (default `['email']`). */\n\ttokenFieldTypes?: string[]\n\tdescriptionKey?: TranslationKey\n}\n\ntype FetchState =\n\t| { status: 'error' | 'idle' | 'loading' }\n\t| { status: 'loaded'; options: EndpointOption[] }\n\nconst tokenOptionsFromData = (\n\tdata: Record<string, unknown>,\n\ttypes?: string[]\n): ReactSelectOption[] => {\n\tconst rows = Array.isArray(data.fields) ? (data.fields as unknown[]) : []\n\tconst labels = new Map<string, string>()\n\tfor (const row of rows) {\n\t\tif (!row || typeof row !== 'object') {\n\t\t\tcontinue\n\t\t}\n\t\tconst { name, label } = row as FieldRow\n\t\tif (typeof name === 'string' && typeof label === 'string' && label.length > 0) {\n\t\t\tlabels.set(name.trim(), label)\n\t\t}\n\t}\n\tconst names =\n\t\ttypes && types.length > 0 ? fieldNamesOfType(data.fields, types) : fieldNames(data.fields)\n\treturn names.map((name) => ({ label: labels.get(name) ?? name, value: `{{${name}}}` }))\n}\n\n/**\n * A creatable, multi-value recipient field: pick a department (preset options from `endpoint`), pick a\n * form-field token (`{{field}}`, offered when `fieldTokens`), or type any email and press Enter\n * (`allowCustom`). Renders as native Payload badges (drag-reorderable), stores a `string[]`, honors\n * `admin.width` and the standard field props. Unknown or legacy stored values stay selectable.\n */\nexport const RecipientsSelect = (props: RecipientsSelectProps) => {\n\tconst allowCustom = props.allowCustom !== false\n\tconst fieldTokens = props.fieldTokens !== false\n\tconst {\n\t\tcustomComponents: { Description, Error: ErrorComponent, Label } = {},\n\t\tdisabled,\n\t\tpath,\n\t\tsetValue,\n\t\tshowError,\n\t\tvalue,\n\t} = useField<string[] | string>({ path: props.path })\n\tconst { t } = useTranslation()\n\tconst label = toStaticLabel(props.field?.label ?? props.label)\n\tconst description = props.descriptionKey\n\t\t? t(props.descriptionKey)\n\t\t: toStaticLabel(props.field?.admin?.description)\n\tconst { id, collectionSlug } = useDocumentInfo()\n\tconst { config } = useConfig()\n\tconst apiRoute = config.routes.api\n\tconst readOnly = props.readOnly === true || disabled === true\n\tconst [state, setState] = useState<FetchState>({ status: 'idle' })\n\n\tuseEffect(() => {\n\t\tif (!props.endpoint || id == null || !collectionSlug) {\n\t\t\treturn\n\t\t}\n\t\tconst controller = new AbortController()\n\t\tconst url = buildEndpointOptionsUrl({ apiRoute, collectionSlug, id, endpoint: props.endpoint })\n\t\tsetState({ status: 'loading' })\n\t\tfetch(url, {\n\t\t\tcredentials: 'include',\n\t\t\theaders: { Accept: 'application/json' },\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\t\t.then(async (response) => {\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`Options request failed with ${response.status}`)\n\t\t\t\t}\n\t\t\t\tsetState({ status: 'loaded', options: parseEndpointOptions(await response.json()) })\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetState({ status: 'error' })\n\t\t\t\t}\n\t\t\t})\n\t\treturn () => controller.abort()\n\t}, [apiRoute, collectionSlug, id, props.endpoint])\n\n\tconst presetOptions: ReactSelectOption[] = state.status === 'loaded' ? state.options : []\n\n\tconst tokenJson = useFormFields(([fields]) =>\n\t\tfieldTokens\n\t\t\t? JSON.stringify(\n\t\t\t\t\ttokenOptionsFromData(reduceFieldsToValues(fields, true), props.tokenFieldTypes)\n\t\t\t\t)\n\t\t\t: '[]'\n\t)\n\tconst tokenOptions = useMemo(() => JSON.parse(tokenJson) as ReactSelectOption[], [tokenJson])\n\n\tconst stored: string[] = Array.isArray(value)\n\t\t? value.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0)\n\t\t: typeof value === 'string' && value.length > 0\n\t\t\t? [value]\n\t\t\t: []\n\n\tconst byValue = useMemo(() => {\n\t\tconst map = new Map<string, ReactSelectOption>()\n\t\tfor (const option of [...presetOptions, ...tokenOptions]) {\n\t\t\tmap.set(option.value as string, option)\n\t\t}\n\t\treturn map\n\t}, [presetOptions, tokenOptions])\n\n\tconst selected = stored.map((entry) => byValue.get(entry) ?? { label: entry, value: entry })\n\n\tconst groupedOptions = useMemo(() => {\n\t\tconst groups: { label: string; options: ReactSelectOption[] }[] = []\n\t\tif (presetOptions.length > 0) {\n\t\t\tgroups.push({ label: t(keys.recipientsGroupDepartments), options: presetOptions })\n\t\t}\n\t\tif (tokenOptions.length > 0) {\n\t\t\tgroups.push({ label: t(keys.recipientsGroupFields), options: tokenOptions })\n\t\t}\n\t\treturn groups\n\t}, [presetOptions, tokenOptions, t])\n\n\tconst handleChange = (selection: ReactSelectOption | ReactSelectOption[] | null) => {\n\t\tif (readOnly) {\n\t\t\treturn\n\t\t}\n\t\tconst chosen = Array.isArray(selection) ? selection : selection ? [selection] : []\n\t\tconst entries = chosen\n\t\t\t.map((option) => String(option.value).trim())\n\t\t\t.filter((entry) => entry.length > 0 && (isPlausibleEmail(entry) || byValue.has(entry)))\n\t\tconst deduped = new Map<string, string>()\n\t\tfor (const entry of entries) {\n\t\t\tconst key = entry.toLowerCase()\n\t\t\tif (!deduped.has(key)) {\n\t\t\t\tdeduped.set(key, entry)\n\t\t\t}\n\t\t}\n\t\tsetValue(Array.from(deduped.values()))\n\t}\n\n\tconst fieldStyle = props.field?.admin?.width\n\t\t? ({ '--field-width': props.field.admin.width } as CSSProperties)\n\t\t: undefined\n\n\treturn (\n\t\t<div\n\t\t\tclassName={['field-type', showError && 'error', readOnly && 'read-only']\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(' ')}\n\t\t\tid={path ? `field-${path.replace(/\\./g, '__')}` : undefined}\n\t\t\tstyle={{ marginBlockEnd: '1rem', ...fieldStyle }}\n\t\t>\n\t\t\t<RenderCustomComponent\n\t\t\t\tCustomComponent={Label}\n\t\t\t\tFallback={<FieldLabel label={label} path={path} required={props.field?.required} />}\n\t\t\t/>\n\t\t\t<div className=\"field-type__wrap\">\n\t\t\t\t<RenderCustomComponent\n\t\t\t\t\tCustomComponent={ErrorComponent}\n\t\t\t\t\tFallback={<FieldError path={path} showError={showError} />}\n\t\t\t\t/>\n\t\t\t\t<ReactSelect\n\t\t\t\t\toptions={groupedOptions}\n\t\t\t\t\tvalue={selected}\n\t\t\t\t\tisMulti\n\t\t\t\t\tisSortable\n\t\t\t\t\tisCreatable={allowCustom}\n\t\t\t\t\tisClearable={!readOnly}\n\t\t\t\t\tdisabled={readOnly}\n\t\t\t\t\tisLoading={state.status === 'loading'}\n\t\t\t\t\tshowError={showError}\n\t\t\t\t\tplaceholder={state.status === 'loading' ? t(keys.endpointOptionsLoading) : undefined}\n\t\t\t\t\tonChange={handleChange}\n\t\t\t\t\tfilterOption={(option, rawInput) => {\n\t\t\t\t\t\tif (!option) {\n\t\t\t\t\t\t\treturn allowCustom && isPlausibleEmail(rawInput)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst query = rawInput.toLowerCase()\n\t\t\t\t\t\treturn `${option.label} ${option.value}`.toLowerCase().includes(query)\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t\t<RenderCustomComponent\n\t\t\t\tCustomComponent={Description}\n\t\t\t\tFallback={\n\t\t\t\t\t<FieldDescription\n\t\t\t\t\t\tdescription={state.status === 'error' ? t(keys.endpointOptionsError) : description}\n\t\t\t\t\t\tpath={path}\n\t\t\t\t\t/>\n\t\t\t\t}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n"],"mappings":";;;;;;;;;;;;AAgDA,MAAM,wBACL,MACA,UACyB;CACzB,MAAM,OAAO,MAAM,QAAQ,KAAK,MAAM,IAAK,KAAK,SAAuB,CAAC;CACxE,MAAM,yBAAS,IAAI,IAAoB;CACvC,KAAK,MAAM,OAAO,MAAM;EACvB,IAAI,CAAC,OAAO,OAAO,QAAQ,UAC1B;EAED,MAAM,EAAE,MAAM,UAAU;EACxB,IAAI,OAAO,SAAS,YAAY,OAAO,UAAU,YAAY,MAAM,SAAS,GAC3E,OAAO,IAAI,KAAK,KAAK,GAAG,KAAK;CAE/B;CAGA,QADC,SAAS,MAAM,SAAS,IAAI,iBAAiB,KAAK,QAAQ,KAAK,IAAI,WAAW,KAAK,MAAM,GAC7E,KAAK,UAAU;EAAE,OAAO,OAAO,IAAI,IAAI,KAAK;EAAM,OAAO,KAAK,KAAK;CAAI,EAAE;AACvF;;;;;;;AAQA,MAAa,oBAAoB,UAAiC;CACjE,MAAM,cAAc,MAAM,gBAAgB;CAC1C,MAAM,cAAc,MAAM,gBAAgB;CAC1C,MAAM,EACL,kBAAkB,EAAE,aAAa,OAAO,gBAAgB,UAAU,CAAC,GACnE,UACA,MACA,UACA,WACA,UACG,SAA4B,EAAE,MAAM,MAAM,KAAK,CAAC;CACpD,MAAM,EAAE,MAAMA,iBAAe;CAC7B,MAAM,QAAQ,cAAc,MAAM,OAAO,SAAS,MAAM,KAAK;CAC7D,MAAM,cAAc,MAAM,iBACvB,EAAE,MAAM,cAAc,IACtB,cAAc,MAAM,OAAO,OAAO,WAAW;CAChD,MAAM,EAAE,IAAI,mBAAmB,gBAAgB;CAC/C,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,WAAW,OAAO,OAAO;CAC/B,MAAM,WAAW,MAAM,aAAa,QAAQ,aAAa;CACzD,MAAM,CAAC,OAAO,YAAY,SAAqB,EAAE,QAAQ,OAAO,CAAC;CAEjE,gBAAgB;EACf,IAAI,CAAC,MAAM,YAAY,MAAM,QAAQ,CAAC,gBACrC;EAED,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,MAAM,wBAAwB;GAAE;GAAU;GAAgB;GAAI,UAAU,MAAM;EAAS,CAAC;EAC9F,SAAS,EAAE,QAAQ,UAAU,CAAC;EAC9B,MAAM,KAAK;GACV,aAAa;GACb,SAAS,EAAE,QAAQ,mBAAmB;GACtC,QAAQ,WAAW;EACpB,CAAC,EACC,KAAK,OAAO,aAAa;GACzB,IAAI,CAAC,SAAS,IACb,MAAM,IAAI,MAAM,+BAA+B,SAAS,QAAQ;GAEjE,SAAS;IAAE,QAAQ;IAAU,SAAS,qBAAqB,MAAM,SAAS,KAAK,CAAC;GAAE,CAAC;EACpF,CAAC,EACA,YAAY;GACZ,IAAI,CAAC,WAAW,OAAO,SACtB,SAAS,EAAE,QAAQ,QAAQ,CAAC;EAE9B,CAAC;EACF,aAAa,WAAW,MAAM;CAC/B,GAAG;EAAC;EAAU;EAAgB;EAAI,MAAM;CAAQ,CAAC;CAEjD,MAAM,gBAAqC,MAAM,WAAW,WAAW,MAAM,UAAU,CAAC;CAExF,MAAM,YAAY,eAAe,CAAC,YACjC,cACG,KAAK,UACL,qBAAqB,qBAAqB,QAAQ,IAAI,GAAG,MAAM,eAAe,CAC/E,IACC,IACJ;CACA,MAAM,eAAe,cAAc,KAAK,MAAM,SAAS,GAA0B,CAAC,SAAS,CAAC;CAE5F,MAAM,SAAmB,MAAM,QAAQ,KAAK,IACzC,MAAM,QAAQ,UAA2B,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC,IACtF,OAAO,UAAU,YAAY,MAAM,SAAS,IAC3C,CAAC,KAAK,IACN,CAAC;CAEL,MAAM,UAAU,cAAc;EAC7B,MAAM,sBAAM,IAAI,IAA+B;EAC/C,KAAK,MAAM,UAAU,CAAC,GAAG,eAAe,GAAG,YAAY,GACtD,IAAI,IAAI,OAAO,OAAiB,MAAM;EAEvC,OAAO;CACR,GAAG,CAAC,eAAe,YAAY,CAAC;CAEhC,MAAM,WAAW,OAAO,KAAK,UAAU,QAAQ,IAAI,KAAK,KAAK;EAAE,OAAO;EAAO,OAAO;CAAM,CAAC;CAE3F,MAAM,iBAAiB,cAAc;EACpC,MAAM,SAA4D,CAAC;EACnE,IAAI,cAAc,SAAS,GAC1B,OAAO,KAAK;GAAE,OAAO,EAAE,KAAK,0BAA0B;GAAG,SAAS;EAAc,CAAC;EAElF,IAAI,aAAa,SAAS,GACzB,OAAO,KAAK;GAAE,OAAO,EAAE,KAAK,qBAAqB;GAAG,SAAS;EAAa,CAAC;EAE5E,OAAO;CACR,GAAG;EAAC;EAAe;EAAc;CAAC,CAAC;CAEnC,MAAM,gBAAgB,cAA8D;EACnF,IAAI,UACH;EAGD,MAAM,WADS,MAAM,QAAQ,SAAS,IAAI,YAAY,YAAY,CAAC,SAAS,IAAI,CAAC,GAE/E,KAAK,WAAW,OAAO,OAAO,KAAK,EAAE,KAAK,CAAC,EAC3C,QAAQ,UAAU,MAAM,SAAS,MAAM,iBAAiB,KAAK,KAAK,QAAQ,IAAI,KAAK,EAAE;EACvF,MAAM,0BAAU,IAAI,IAAoB;EACxC,KAAK,MAAM,SAAS,SAAS;GAC5B,MAAM,MAAM,MAAM,YAAY;GAC9B,IAAI,CAAC,QAAQ,IAAI,GAAG,GACnB,QAAQ,IAAI,KAAK,KAAK;EAExB;EACA,SAAS,MAAM,KAAK,QAAQ,OAAO,CAAC,CAAC;CACtC;CAEA,MAAM,aAAa,MAAM,OAAO,OAAO,QACnC,EAAE,iBAAiB,MAAM,MAAM,MAAM,MAAM,IAC5C,KAAA;CAEH,OACC,qBAAC,OAAD;EACC,WAAW;GAAC;GAAc,aAAa;GAAS,YAAY;EAAW,EACrE,OAAO,OAAO,EACd,KAAK,GAAG;EACV,IAAI,OAAO,SAAS,KAAK,QAAQ,OAAO,IAAI,MAAM,KAAA;EAClD,OAAO;GAAE,gBAAgB;GAAQ,GAAG;EAAW;YALhD;GAOC,oBAAC,uBAAD;IACC,iBAAiB;IACjB,UAAU,oBAAC,YAAD;KAAmB;KAAa;KAAM,UAAU,MAAM,OAAO;IAAW,CAAA;GAClF,CAAA;GACD,qBAAC,OAAD;IAAK,WAAU;cAAf,CACC,oBAAC,uBAAD;KACC,iBAAiB;KACjB,UAAU,oBAAC,YAAD;MAAkB;MAAiB;KAAY,CAAA;IACzD,CAAA,GACD,oBAAC,aAAD;KACC,SAAS;KACT,OAAO;KACP,SAAA;KACA,YAAA;KACA,aAAa;KACb,aAAa,CAAC;KACd,UAAU;KACV,WAAW,MAAM,WAAW;KACjB;KACX,aAAa,MAAM,WAAW,YAAY,EAAE,KAAK,sBAAsB,IAAI,KAAA;KAC3E,UAAU;KACV,eAAe,QAAQ,aAAa;MACnC,IAAI,CAAC,QACJ,OAAO,eAAe,iBAAiB,QAAQ;MAEhD,MAAM,QAAQ,SAAS,YAAY;MACnC,OAAO,GAAG,OAAO,MAAM,GAAG,OAAO,QAAQ,YAAY,EAAE,SAAS,KAAK;KACtE;IACA,CAAA,CACG;;GACL,oBAAC,uBAAD;IACC,iBAAiB;IACjB,UACC,oBAAC,kBAAD;KACC,aAAa,MAAM,WAAW,UAAU,EAAE,KAAK,oBAAoB,IAAI;KACjE;IACN,CAAA;GAEF,CAAA;EACG;;AAEP"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { TranslationKey } from "../translations/keys.js";
|
|
2
|
+
|
|
3
|
+
//#region src/client/RuleDescription.d.ts
|
|
4
|
+
type RuleDescriptionProps = {
|
|
5
|
+
/**
|
|
6
|
+
* The rule's plain-language description as a translation key, resolved client-side. Payload has no
|
|
7
|
+
* native `Block` description slot and drops function `admin.description`, so the key travels as a
|
|
8
|
+
* clientProp and resolves here.
|
|
9
|
+
*/
|
|
10
|
+
descriptionKey: TranslationKey;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Leading `field-description` paragraph at the top of a validation rule's config block, explaining in
|
|
14
|
+
* simple terms what makes the rule fail. Mounted as a `ui` field by `buildRuleBlocks`.
|
|
15
|
+
*/
|
|
16
|
+
declare const RuleDescription: ({
|
|
17
|
+
descriptionKey
|
|
18
|
+
}: RuleDescriptionProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { RuleDescription };
|
|
21
|
+
//# sourceMappingURL=RuleDescription.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useTranslation } from "../translations/useTranslation.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/client/RuleDescription.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Leading `field-description` paragraph at the top of a validation rule's config block, explaining in
|
|
7
|
+
* simple terms what makes the rule fail. Mounted as a `ui` field by `buildRuleBlocks`.
|
|
8
|
+
*/
|
|
9
|
+
const RuleDescription = ({ descriptionKey }) => {
|
|
10
|
+
const { t } = useTranslation();
|
|
11
|
+
return /* @__PURE__ */ jsx("p", {
|
|
12
|
+
className: "field-description",
|
|
13
|
+
children: t(descriptionKey)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { RuleDescription };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=RuleDescription.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RuleDescription.js","names":[],"sources":["../../src/client/RuleDescription.tsx"],"sourcesContent":["'use client'\n\nimport type { TranslationKey } from '../translations/keys'\nimport { useTranslation } from '../translations/useTranslation'\n\nexport type RuleDescriptionProps = {\n\t/**\n\t * The rule's plain-language description as a translation key, resolved client-side. Payload has no\n\t * native `Block` description slot and drops function `admin.description`, so the key travels as a\n\t * clientProp and resolves here.\n\t */\n\tdescriptionKey: TranslationKey\n}\n\n/**\n * Leading `field-description` paragraph at the top of a validation rule's config block, explaining in\n * simple terms what makes the rule fail. Mounted as a `ui` field by `buildRuleBlocks`.\n */\nexport const RuleDescription = ({ descriptionKey }: RuleDescriptionProps) => {\n\tconst { t } = useTranslation()\n\treturn <p className=\"field-description\">{t(descriptionKey)}</p>\n}\n"],"mappings":";;;;;;;;AAkBA,MAAa,mBAAmB,EAAE,qBAA2C;CAC5E,MAAM,EAAE,MAAM,eAAe;CAC7B,OAAO,oBAAC,KAAD;EAAG,WAAU;YAAqB,EAAE,cAAc;CAAK,CAAA;AAC/D"}
|
package/dist/client/admin.css
CHANGED
|
@@ -232,3 +232,44 @@
|
|
|
232
232
|
flex-shrink: 0;
|
|
233
233
|
align-self: center;
|
|
234
234
|
}
|
|
235
|
+
|
|
236
|
+
/* Condensed array (host-placed department emails): flat [label, email] rows, no per-row collapsible.
|
|
237
|
+
* The 50/50 split reuses Payload's own --field-width variable, set from each subfield's admin.width. */
|
|
238
|
+
.fb-condensed-array {
|
|
239
|
+
display: flex;
|
|
240
|
+
flex-direction: column;
|
|
241
|
+
gap: 0.5rem;
|
|
242
|
+
margin-block-end: 1rem;
|
|
243
|
+
}
|
|
244
|
+
.fb-condensed-array__rows {
|
|
245
|
+
display: flex;
|
|
246
|
+
flex-direction: column;
|
|
247
|
+
gap: 0.5rem;
|
|
248
|
+
}
|
|
249
|
+
.fb-condensed-array__row {
|
|
250
|
+
display: flex;
|
|
251
|
+
align-items: flex-end;
|
|
252
|
+
gap: 0.5rem;
|
|
253
|
+
}
|
|
254
|
+
.fb-condensed-array__fields {
|
|
255
|
+
flex: 1;
|
|
256
|
+
min-width: 0;
|
|
257
|
+
display: flex;
|
|
258
|
+
flex-wrap: wrap;
|
|
259
|
+
column-gap: 0.75rem;
|
|
260
|
+
}
|
|
261
|
+
.fb-condensed-array__fields > .field-type {
|
|
262
|
+
flex: 0 1 calc(var(--field-width, 50%) - 0.375rem);
|
|
263
|
+
min-width: 0;
|
|
264
|
+
}
|
|
265
|
+
.fb-condensed-array__remove {
|
|
266
|
+
flex-shrink: 0;
|
|
267
|
+
display: flex;
|
|
268
|
+
align-items: center;
|
|
269
|
+
/* Payload input height, so with the row's flex-end the button centers on the input, not the
|
|
270
|
+
* taller label+input stack. */
|
|
271
|
+
height: 40px;
|
|
272
|
+
}
|
|
273
|
+
.fb-condensed-array__actions {
|
|
274
|
+
margin-block-start: 0.25rem;
|
|
275
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { formatAdminURL } from "payload/shared";
|
|
2
|
+
//#region src/client/endpointOptions.ts
|
|
3
|
+
/**
|
|
4
|
+
* URL for a document-scoped collection endpoint (`{api}/{collection}/{id}/{endpoint}`), built the
|
|
5
|
+
* way Payload's own admin components build fetch URLs: `formatAdminURL` over `config.routes.api`,
|
|
6
|
+
* which handles a Next `basePath` and returns a same-origin relative URL so the admin cookie rides
|
|
7
|
+
* along without CORS concerns.
|
|
8
|
+
*/
|
|
9
|
+
const buildEndpointOptionsUrl = (args) => formatAdminURL({
|
|
10
|
+
apiRoute: args.apiRoute,
|
|
11
|
+
path: `/${args.collectionSlug}/${encodeURIComponent(String(args.id))}/${args.endpoint.replace(/^\/+/, "")}`
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* Narrow an endpoint response to `{ options }`. Entries without a string value are dropped and
|
|
15
|
+
* labels fall back to the value; a body without an options array throws so the select surfaces its
|
|
16
|
+
* error state instead of silently rendering empty.
|
|
17
|
+
*/
|
|
18
|
+
const parseEndpointOptions = (body) => {
|
|
19
|
+
const options = body != null && typeof body === "object" ? body.options : void 0;
|
|
20
|
+
if (!Array.isArray(options)) throw new Error("Malformed options response: expected { options: [] }");
|
|
21
|
+
return options.filter((option) => option != null && typeof option === "object" && typeof option.value === "string").map((option) => ({
|
|
22
|
+
value: option.value,
|
|
23
|
+
label: typeof option.label === "string" && option.label.length > 0 ? option.label : option.value
|
|
24
|
+
}));
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { buildEndpointOptionsUrl, parseEndpointOptions };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=endpointOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpointOptions.js","names":[],"sources":["../../src/client/endpointOptions.ts"],"sourcesContent":["import { formatAdminURL } from 'payload/shared'\n\nexport type EndpointOption = { label: string; value: string }\n\n/**\n * URL for a document-scoped collection endpoint (`{api}/{collection}/{id}/{endpoint}`), built the\n * way Payload's own admin components build fetch URLs: `formatAdminURL` over `config.routes.api`,\n * which handles a Next `basePath` and returns a same-origin relative URL so the admin cookie rides\n * along without CORS concerns.\n */\nexport const buildEndpointOptionsUrl = (args: {\n\tapiRoute: string\n\tcollectionSlug: string\n\tid: number | string\n\tendpoint: string\n}): string =>\n\tformatAdminURL({\n\t\tapiRoute: args.apiRoute,\n\t\tpath: `/${args.collectionSlug}/${encodeURIComponent(String(args.id))}/${args.endpoint.replace(/^\\/+/, '')}`,\n\t})\n\n/**\n * Narrow an endpoint response to `{ options }`. Entries without a string value are dropped and\n * labels fall back to the value; a body without an options array throws so the select surfaces its\n * error state instead of silently rendering empty.\n */\nexport const parseEndpointOptions = (body: unknown): EndpointOption[] => {\n\tconst options =\n\t\tbody != null && typeof body === 'object' ? (body as { options?: unknown }).options : undefined\n\tif (!Array.isArray(options)) {\n\t\tthrow new Error('Malformed options response: expected { options: [] }')\n\t}\n\treturn options\n\t\t.filter(\n\t\t\t(option): option is { label?: unknown; value: string } =>\n\t\t\t\toption != null && typeof option === 'object' && typeof option.value === 'string'\n\t\t)\n\t\t.map((option) => ({\n\t\t\tvalue: option.value,\n\t\t\tlabel:\n\t\t\t\ttypeof option.label === 'string' && option.label.length > 0 ? option.label : option.value,\n\t\t}))\n}\n"],"mappings":";;;;;;;;AAUA,MAAa,2BAA2B,SAMvC,eAAe;CACd,UAAU,KAAK;CACf,MAAM,IAAI,KAAK,eAAe,GAAG,mBAAmB,OAAO,KAAK,EAAE,CAAC,EAAE,GAAG,KAAK,SAAS,QAAQ,QAAQ,EAAE;AACzG,CAAC;;;;;;AAOF,MAAa,wBAAwB,SAAoC;CACxE,MAAM,UACL,QAAQ,QAAQ,OAAO,SAAS,WAAY,KAA+B,UAAU,KAAA;CACtF,IAAI,CAAC,MAAM,QAAQ,OAAO,GACzB,MAAM,IAAI,MAAM,sDAAsD;CAEvE,OAAO,QACL,QACC,WACA,UAAU,QAAQ,OAAO,WAAW,YAAY,OAAO,OAAO,UAAU,QAC1E,EACC,KAAK,YAAY;EACjB,OAAO,OAAO;EACd,OACC,OAAO,OAAO,UAAU,YAAY,OAAO,MAAM,SAAS,IAAI,OAAO,QAAQ,OAAO;CACtF,EAAE;AACJ"}
|
|
@@ -1,6 +1,92 @@
|
|
|
1
|
+
import { END_OF_FORM } from "../flow/types.js";
|
|
1
2
|
import "../flow/stepLabel.js";
|
|
2
3
|
//#region src/client/flowAuthoring.ts
|
|
3
|
-
/**
|
|
4
|
+
/** Project a step's stored `next` onto its select option value ('' = sequential fall-through). */
|
|
5
|
+
const nextToSelectValue = (next) => next === null ? END_OF_FORM : next ?? "";
|
|
6
|
+
/** Parse a select option value back into a stored `next` (undefined = sequential fall-through). */
|
|
7
|
+
const nextFromSelectValue = (value) => value === "__end__" ? null : value.length > 0 ? value : void 0;
|
|
8
|
+
/**
|
|
9
|
+
* One option per step, labeled like its step card. A step whose id is empty is dropped: the id is
|
|
10
|
+
* the option's value, so such a step cannot be routed to and an empty value would collide with the
|
|
11
|
+
* next select's sequential option.
|
|
12
|
+
*/
|
|
13
|
+
const stepSelectOptions = (steps, labels) => steps.map((step, index) => ({
|
|
14
|
+
label: labels[index] ?? "",
|
|
15
|
+
value: step.id
|
|
16
|
+
})).filter((option) => option.value.length > 0);
|
|
17
|
+
/** Every step but `stepId`, so a step's own next/transition selects cannot route it to itself. */
|
|
18
|
+
const otherStepSelectOptions = (options, stepId) => options.filter((option) => option.value !== stepId);
|
|
19
|
+
/**
|
|
20
|
+
* The `next` select's options: sequential fall-through first (the default, and what an absent
|
|
21
|
+
* `next` maps to), then explicit end of form, then every other step. Values round-trip through
|
|
22
|
+
* `nextFromSelectValue`.
|
|
23
|
+
*/
|
|
24
|
+
const nextSelectOptions = (args) => [
|
|
25
|
+
{
|
|
26
|
+
label: args.sequentialLabel,
|
|
27
|
+
value: ""
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
label: args.terminalLabel,
|
|
31
|
+
value: END_OF_FORM
|
|
32
|
+
},
|
|
33
|
+
...args.otherSteps
|
|
34
|
+
];
|
|
35
|
+
const isRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
|
|
36
|
+
/** Chars kept before truncating a `messageSnippet` result, plus a trailing ellipsis. */
|
|
37
|
+
const MESSAGE_SNIPPET_LENGTH = 40;
|
|
38
|
+
/**
|
|
39
|
+
* Short plain-text preview of a bare message block's richText content, for the flow step list.
|
|
40
|
+
* Reads only the shallow lexical shape (`root.children[].children[].text`), which is all a short
|
|
41
|
+
* authored message needs; a legacy Slate body or other non-lexical shape yields `undefined`, same
|
|
42
|
+
* as empty content, so the caller falls back to the block type's display label.
|
|
43
|
+
*/
|
|
44
|
+
const messageSnippet = (content) => {
|
|
45
|
+
const root = isRecord(content) ? content.root : void 0;
|
|
46
|
+
const text = (isRecord(root) && Array.isArray(root.children) ? root.children : []).map((block) => {
|
|
47
|
+
return (isRecord(block) && Array.isArray(block.children) ? block.children : []).map((leaf) => isRecord(leaf) && typeof leaf.text === "string" ? leaf.text : "").join("");
|
|
48
|
+
}).join(" ").replace(/\s+/g, " ").trim();
|
|
49
|
+
if (text.length === 0) return;
|
|
50
|
+
return text.length > MESSAGE_SNIPPET_LENGTH ? `${text.slice(0, MESSAGE_SNIPPET_LENGTH).trimEnd()}…` : text;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Project the form's field rows into flow entries. Named rows are keyed by their trimmed name and
|
|
54
|
+
* labeled with their configured `label`, falling back to the name when unset. Nameless rows of a
|
|
55
|
+
* bare type are keyed by their block row id and labeled with a snippet of their content when they
|
|
56
|
+
* have any (see `messageSnippet`), else the type's display label (numbered in form order when the
|
|
57
|
+
* type occurs more than once). Rows with neither a usable name nor a bare-type row id are not
|
|
58
|
+
* addressable and are skipped.
|
|
59
|
+
*/
|
|
60
|
+
const flowFieldEntries = (rows, bareTypeLabels) => {
|
|
61
|
+
const isBareRow = (row) => bareTypeLabels[row.blockType] !== void 0 && row.id != null;
|
|
62
|
+
const nameOf = (row) => typeof row.name === "string" ? row.name.trim() : "";
|
|
63
|
+
const bareTotals = /* @__PURE__ */ new Map();
|
|
64
|
+
for (const row of rows) if (nameOf(row).length === 0 && isBareRow(row)) bareTotals.set(row.blockType, (bareTotals.get(row.blockType) ?? 0) + 1);
|
|
65
|
+
const bareSeen = /* @__PURE__ */ new Map();
|
|
66
|
+
const entries = [];
|
|
67
|
+
for (const row of rows) {
|
|
68
|
+
const name = nameOf(row);
|
|
69
|
+
if (name.length > 0) {
|
|
70
|
+
const label = typeof row.label === "string" && row.label.trim().length > 0 ? row.label : name;
|
|
71
|
+
entries.push({
|
|
72
|
+
key: name,
|
|
73
|
+
label
|
|
74
|
+
});
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (!isBareRow(row)) continue;
|
|
78
|
+
const typeLabel = bareTypeLabels[row.blockType] ?? row.blockType;
|
|
79
|
+
const n = (bareSeen.get(row.blockType) ?? 0) + 1;
|
|
80
|
+
bareSeen.set(row.blockType, n);
|
|
81
|
+
const fallback = (bareTotals.get(row.blockType) ?? 0) > 1 ? `${typeLabel} ${n}` : typeLabel;
|
|
82
|
+
entries.push({
|
|
83
|
+
key: String(row.id),
|
|
84
|
+
label: messageSnippet(row.content) ?? fallback
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return entries;
|
|
88
|
+
};
|
|
89
|
+
/** Field key to index of the step holding it (first occurrence wins). */
|
|
4
90
|
const fieldHolders = (steps) => {
|
|
5
91
|
const holders = /* @__PURE__ */ new Map();
|
|
6
92
|
steps.forEach((step, index) => {
|
|
@@ -8,12 +94,12 @@ const fieldHolders = (steps) => {
|
|
|
8
94
|
});
|
|
9
95
|
return holders;
|
|
10
96
|
};
|
|
11
|
-
/**
|
|
12
|
-
const
|
|
97
|
+
/** Entries not assigned to any step, in form order. */
|
|
98
|
+
const unassignedEntries = (entries, steps) => {
|
|
13
99
|
const assigned = new Set(steps.flatMap((step) => step.fields));
|
|
14
|
-
return
|
|
100
|
+
return entries.filter((entry) => !assigned.has(entry.key));
|
|
15
101
|
};
|
|
16
|
-
/** Assign a field to the step at `targetIndex`, stripping it from every other step. */
|
|
102
|
+
/** Assign a field key to the step at `targetIndex`, stripping it from every other step. */
|
|
17
103
|
const assignFieldToStep = (steps, targetIndex, field) => steps.map((step, index) => {
|
|
18
104
|
if (index === targetIndex) return step.fields.includes(field) ? step : {
|
|
19
105
|
...step,
|
|
@@ -24,12 +110,33 @@ const assignFieldToStep = (steps, targetIndex, field) => steps.map((step, index)
|
|
|
24
110
|
fields: step.fields.filter((f) => f !== field)
|
|
25
111
|
} : step;
|
|
26
112
|
});
|
|
27
|
-
/** Remove a field from the step at `targetIndex`, leaving other steps untouched. */
|
|
113
|
+
/** Remove a field key from the step at `targetIndex`, leaving other steps untouched. */
|
|
28
114
|
const removeFieldFromStep = (steps, targetIndex, field) => steps.map((step, index) => index === targetIndex && step.fields.includes(field) ? {
|
|
29
115
|
...step,
|
|
30
116
|
fields: step.fields.filter((f) => f !== field)
|
|
31
117
|
} : step);
|
|
118
|
+
/**
|
|
119
|
+
* Remove the step at `index` and every reference to it: another step's `next` pointing at it
|
|
120
|
+
* returns to absent (sequential fall-through), and transitions targeting it are dropped. Without
|
|
121
|
+
* the cascade a dangling `next` would silently route into whatever step follows it in the array.
|
|
122
|
+
*/
|
|
123
|
+
const removeStepCascade = (steps, index) => {
|
|
124
|
+
const removed = steps[index];
|
|
125
|
+
const rest = steps.filter((_, i) => i !== index);
|
|
126
|
+
if (!removed) return rest;
|
|
127
|
+
return rest.map((step) => {
|
|
128
|
+
const keptTransitions = (step.transitions ?? []).filter((t) => t.to !== removed.id);
|
|
129
|
+
const dropsNext = step.next === removed.id;
|
|
130
|
+
const dropsTransitions = keptTransitions.length !== (step.transitions?.length ?? 0);
|
|
131
|
+
if (!dropsNext && !dropsTransitions) return step;
|
|
132
|
+
const { next, transitions, ...base } = step;
|
|
133
|
+
const cleaned = { ...base };
|
|
134
|
+
if (!dropsNext && next !== void 0) cleaned.next = next;
|
|
135
|
+
if (keptTransitions.length > 0) cleaned.transitions = keptTransitions;
|
|
136
|
+
return cleaned;
|
|
137
|
+
});
|
|
138
|
+
};
|
|
32
139
|
//#endregion
|
|
33
|
-
export { assignFieldToStep, fieldHolders, removeFieldFromStep,
|
|
140
|
+
export { assignFieldToStep, fieldHolders, flowFieldEntries, nextFromSelectValue, nextSelectOptions, nextToSelectValue, otherStepSelectOptions, removeFieldFromStep, removeStepCascade, stepSelectOptions, unassignedEntries };
|
|
34
141
|
|
|
35
142
|
//# sourceMappingURL=flowAuthoring.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flowAuthoring.js","names":[],"sources":["../../src/client/flowAuthoring.ts"],"sourcesContent":["import type { FlowStep } from '../flow/types'\n\nexport { stepLabel } from '../flow/stepLabel'\n\n/** Field name to index of the step holding it (first occurrence wins). */\nexport const fieldHolders = (steps: Pick<FlowStep, 'fields'>[]): Map<string, number> => {\n\tconst holders = new Map<string, number>()\n\tsteps.forEach((step, index) => {\n\t\tfor (const field of step.fields) {\n\t\t\tif (!holders.has(field)) holders.set(field, index)\n\t\t}\n\t})\n\treturn holders\n}\n\n/** Form fields not assigned to any step, in form order. */\nexport const unassignedFields = (\n\tfieldNames: string[],\n\tsteps: Pick<FlowStep, 'fields'>[]\n): string[] => {\n\tconst assigned = new Set(steps.flatMap((step) => step.fields))\n\treturn fieldNames.filter((name) => !assigned.has(name))\n}\n\n/** Assign a field to the step at `targetIndex`, stripping it from every other step. */\nexport const assignFieldToStep = (\n\tsteps: FlowStep[],\n\ttargetIndex: number,\n\tfield: string\n): FlowStep[] =>\n\tsteps.map((step, index) => {\n\t\tif (index === targetIndex) {\n\t\t\treturn step.fields.includes(field) ? step : { ...step, fields: [...step.fields, field] }\n\t\t}\n\t\treturn step.fields.includes(field)\n\t\t\t? { ...step, fields: step.fields.filter((f) => f !== field) }\n\t\t\t: step\n\t})\n\n/** Remove a field from the step at `targetIndex`, leaving other steps untouched. */\nexport const removeFieldFromStep = (\n\tsteps: FlowStep[],\n\ttargetIndex: number,\n\tfield: string\n): FlowStep[] =>\n\tsteps.map((step, index) =>\n\t\tindex === targetIndex && step.fields.includes(field)\n\t\t\t? { ...step, fields: step.fields.filter((f) => f !== field) }\n\t\t\t: step\n\t)\n"],"mappings":";;;AAKA,MAAa,gBAAgB,UAA2D;CACvF,MAAM,0BAAU,IAAI,IAAoB;CACxC,MAAM,SAAS,MAAM,UAAU;EAC9B,KAAK,MAAM,SAAS,KAAK,QACxB,IAAI,CAAC,QAAQ,IAAI,KAAK,GAAG,QAAQ,IAAI,OAAO,KAAK;CAEnD,CAAC;CACD,OAAO;AACR;;AAGA,MAAa,oBACZ,YACA,UACc;CACd,MAAM,WAAW,IAAI,IAAI,MAAM,SAAS,SAAS,KAAK,MAAM,CAAC;CAC7D,OAAO,WAAW,QAAQ,SAAS,CAAC,SAAS,IAAI,IAAI,CAAC;AACvD;;AAGA,MAAa,qBACZ,OACA,aACA,UAEA,MAAM,KAAK,MAAM,UAAU;CAC1B,IAAI,UAAU,aACb,OAAO,KAAK,OAAO,SAAS,KAAK,IAAI,OAAO;EAAE,GAAG;EAAM,QAAQ,CAAC,GAAG,KAAK,QAAQ,KAAK;CAAE;CAExF,OAAO,KAAK,OAAO,SAAS,KAAK,IAC9B;EAAE,GAAG;EAAM,QAAQ,KAAK,OAAO,QAAQ,MAAM,MAAM,KAAK;CAAE,IAC1D;AACJ,CAAC;;AAGF,MAAa,uBACZ,OACA,aACA,UAEA,MAAM,KAAK,MAAM,UAChB,UAAU,eAAe,KAAK,OAAO,SAAS,KAAK,IAChD;CAAE,GAAG;CAAM,QAAQ,KAAK,OAAO,QAAQ,MAAM,MAAM,KAAK;AAAE,IAC1D,IACJ"}
|
|
1
|
+
{"version":3,"file":"flowAuthoring.js","names":[],"sources":["../../src/client/flowAuthoring.ts"],"sourcesContent":["import { END_OF_FORM, type FlowStep } from '../flow/types'\nimport type { FieldRow } from './synthesizeClientField'\n\nexport { stepLabel } from '../flow/stepLabel'\nexport { END_OF_FORM }\n\n/** Project a step's stored `next` onto its select option value ('' = sequential fall-through). */\nexport const nextToSelectValue = (next: FlowStep['next']): string =>\n\tnext === null ? END_OF_FORM : (next ?? '')\n\n/** Parse a select option value back into a stored `next` (undefined = sequential fall-through). */\nexport const nextFromSelectValue = (value: string): FlowStep['next'] =>\n\tvalue === END_OF_FORM ? null : value.length > 0 ? value : undefined\n\n/** A form field as the flow builder addresses it: its stable key plus a display label. */\nexport type FlowFieldEntry = { key: string; label: string }\n\n/** A flow builder select option. Structurally a `ReactSelectOption`, without the client-only import. */\nexport type FlowSelectOption = { label: string; value: string }\n\n/**\n * One option per step, labeled like its step card. A step whose id is empty is dropped: the id is\n * the option's value, so such a step cannot be routed to and an empty value would collide with the\n * next select's sequential option.\n */\nexport const stepSelectOptions = (\n\tsteps: Pick<FlowStep, 'id'>[],\n\tlabels: string[]\n): FlowSelectOption[] =>\n\tsteps\n\t\t.map((step, index) => ({ label: labels[index] ?? '', value: step.id }))\n\t\t.filter((option) => option.value.length > 0)\n\n/** Every step but `stepId`, so a step's own next/transition selects cannot route it to itself. */\nexport const otherStepSelectOptions = (\n\toptions: FlowSelectOption[],\n\tstepId: string\n): FlowSelectOption[] => options.filter((option) => option.value !== stepId)\n\n/**\n * The `next` select's options: sequential fall-through first (the default, and what an absent\n * `next` maps to), then explicit end of form, then every other step. Values round-trip through\n * `nextFromSelectValue`.\n */\nexport const nextSelectOptions = (args: {\n\tsequentialLabel: string\n\tterminalLabel: string\n\totherSteps: FlowSelectOption[]\n}): FlowSelectOption[] => [\n\t{ label: args.sequentialLabel, value: '' },\n\t{ label: args.terminalLabel, value: END_OF_FORM },\n\t...args.otherSteps,\n]\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n\tvalue != null && typeof value === 'object' && !Array.isArray(value)\n\n/** Chars kept before truncating a `messageSnippet` result, plus a trailing ellipsis. */\nconst MESSAGE_SNIPPET_LENGTH = 40\n\n/**\n * Short plain-text preview of a bare message block's richText content, for the flow step list.\n * Reads only the shallow lexical shape (`root.children[].children[].text`), which is all a short\n * authored message needs; a legacy Slate body or other non-lexical shape yields `undefined`, same\n * as empty content, so the caller falls back to the block type's display label.\n */\nexport const messageSnippet = (content: unknown): string | undefined => {\n\tconst root = isRecord(content) ? content.root : undefined\n\tconst blockNodes = isRecord(root) && Array.isArray(root.children) ? root.children : []\n\tconst text = blockNodes\n\t\t.map((block) => {\n\t\t\tconst leaves = isRecord(block) && Array.isArray(block.children) ? block.children : []\n\t\t\treturn leaves\n\t\t\t\t.map((leaf) => (isRecord(leaf) && typeof leaf.text === 'string' ? leaf.text : ''))\n\t\t\t\t.join('')\n\t\t})\n\t\t.join(' ')\n\t\t.replace(/\\s+/g, ' ')\n\t\t.trim()\n\tif (text.length === 0) {\n\t\treturn undefined\n\t}\n\treturn text.length > MESSAGE_SNIPPET_LENGTH\n\t\t? `${text.slice(0, MESSAGE_SNIPPET_LENGTH).trimEnd()}…`\n\t\t: text\n}\n\n/**\n * Project the form's field rows into flow entries. Named rows are keyed by their trimmed name and\n * labeled with their configured `label`, falling back to the name when unset. Nameless rows of a\n * bare type are keyed by their block row id and labeled with a snippet of their content when they\n * have any (see `messageSnippet`), else the type's display label (numbered in form order when the\n * type occurs more than once). Rows with neither a usable name nor a bare-type row id are not\n * addressable and are skipped.\n */\nexport const flowFieldEntries = (\n\trows: FieldRow[],\n\tbareTypeLabels: Record<string, string>\n): FlowFieldEntry[] => {\n\tconst isBareRow = (row: FieldRow): boolean =>\n\t\tbareTypeLabels[row.blockType] !== undefined && row.id != null\n\tconst nameOf = (row: FieldRow): string => (typeof row.name === 'string' ? row.name.trim() : '')\n\n\tconst bareTotals = new Map<string, number>()\n\tfor (const row of rows) {\n\t\tif (nameOf(row).length === 0 && isBareRow(row)) {\n\t\t\tbareTotals.set(row.blockType, (bareTotals.get(row.blockType) ?? 0) + 1)\n\t\t}\n\t}\n\n\tconst bareSeen = new Map<string, number>()\n\tconst entries: FlowFieldEntry[] = []\n\tfor (const row of rows) {\n\t\tconst name = nameOf(row)\n\t\tif (name.length > 0) {\n\t\t\tconst label = typeof row.label === 'string' && row.label.trim().length > 0 ? row.label : name\n\t\t\tentries.push({ key: name, label })\n\t\t\tcontinue\n\t\t}\n\t\tif (!isBareRow(row)) {\n\t\t\tcontinue\n\t\t}\n\t\tconst typeLabel = bareTypeLabels[row.blockType] ?? row.blockType\n\t\tconst n = (bareSeen.get(row.blockType) ?? 0) + 1\n\t\tbareSeen.set(row.blockType, n)\n\t\tconst fallback = (bareTotals.get(row.blockType) ?? 0) > 1 ? `${typeLabel} ${n}` : typeLabel\n\t\tentries.push({ key: String(row.id), label: messageSnippet(row.content) ?? fallback })\n\t}\n\treturn entries\n}\n\n/** Field key to index of the step holding it (first occurrence wins). */\nexport const fieldHolders = (steps: Pick<FlowStep, 'fields'>[]): Map<string, number> => {\n\tconst holders = new Map<string, number>()\n\tsteps.forEach((step, index) => {\n\t\tfor (const field of step.fields) {\n\t\t\tif (!holders.has(field)) holders.set(field, index)\n\t\t}\n\t})\n\treturn holders\n}\n\n/** Entries not assigned to any step, in form order. */\nexport const unassignedEntries = (\n\tentries: FlowFieldEntry[],\n\tsteps: Pick<FlowStep, 'fields'>[]\n): FlowFieldEntry[] => {\n\tconst assigned = new Set(steps.flatMap((step) => step.fields))\n\treturn entries.filter((entry) => !assigned.has(entry.key))\n}\n\n/** Assign a field key to the step at `targetIndex`, stripping it from every other step. */\nexport const assignFieldToStep = (\n\tsteps: FlowStep[],\n\ttargetIndex: number,\n\tfield: string\n): FlowStep[] =>\n\tsteps.map((step, index) => {\n\t\tif (index === targetIndex) {\n\t\t\treturn step.fields.includes(field) ? step : { ...step, fields: [...step.fields, field] }\n\t\t}\n\t\treturn step.fields.includes(field)\n\t\t\t? { ...step, fields: step.fields.filter((f) => f !== field) }\n\t\t\t: step\n\t})\n\n/** Remove a field key from the step at `targetIndex`, leaving other steps untouched. */\nexport const removeFieldFromStep = (\n\tsteps: FlowStep[],\n\ttargetIndex: number,\n\tfield: string\n): FlowStep[] =>\n\tsteps.map((step, index) =>\n\t\tindex === targetIndex && step.fields.includes(field)\n\t\t\t? { ...step, fields: step.fields.filter((f) => f !== field) }\n\t\t\t: step\n\t)\n\n/**\n * Remove the step at `index` and every reference to it: another step's `next` pointing at it\n * returns to absent (sequential fall-through), and transitions targeting it are dropped. Without\n * the cascade a dangling `next` would silently route into whatever step follows it in the array.\n */\nexport const removeStepCascade = (steps: FlowStep[], index: number): FlowStep[] => {\n\tconst removed = steps[index]\n\tconst rest = steps.filter((_, i) => i !== index)\n\tif (!removed) {\n\t\treturn rest\n\t}\n\treturn rest.map((step) => {\n\t\tconst keptTransitions = (step.transitions ?? []).filter((t) => t.to !== removed.id)\n\t\tconst dropsNext = step.next === removed.id\n\t\tconst dropsTransitions = keptTransitions.length !== (step.transitions?.length ?? 0)\n\t\tif (!dropsNext && !dropsTransitions) {\n\t\t\treturn step\n\t\t}\n\t\tconst { next, transitions, ...base } = step\n\t\tconst cleaned: FlowStep = { ...base }\n\t\tif (!dropsNext && next !== undefined) cleaned.next = next\n\t\tif (keptTransitions.length > 0) cleaned.transitions = keptTransitions\n\t\treturn cleaned\n\t})\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fixedFieldOptions } from "../fields/data/regions.js";
|
|
1
2
|
//#region src/client/synthesizeClientField.ts
|
|
2
3
|
/**
|
|
3
4
|
* Project a stored field row into a condition operand, or `null` when the row has no usable name or
|
|
@@ -12,37 +13,43 @@ const operandFromRow = (row, conditionTypes) => {
|
|
|
12
13
|
name,
|
|
13
14
|
label: typeof row.label === "string" && row.label.length > 0 ? row.label : name,
|
|
14
15
|
conditionType,
|
|
15
|
-
options: Array.isArray(row.options) ? row.options :
|
|
16
|
+
options: Array.isArray(row.options) ? row.options : fixedFieldOptions(row.blockType)
|
|
16
17
|
};
|
|
17
18
|
};
|
|
18
19
|
/**
|
|
19
20
|
* Build the minimal `*FieldClient` a Payload leaf condition input requires for a given operand. Casts
|
|
20
21
|
* are localized to these four synths and nowhere else: `FieldBaseClient` requires only `name`, and each
|
|
21
22
|
* leaf is `FieldBaseClient & Pick<…, 'type' | …>` where every picked member but `type` (and `select`'s
|
|
22
|
-
* required `options`) is optional, so the literals are structurally complete.
|
|
23
|
-
* `admin
|
|
24
|
-
*
|
|
23
|
+
* required `options`) is optional, so the literals are structurally complete. Every synth carries an
|
|
24
|
+
* `admin: {}` because Payload's `Select` and `Date` leaf inputs read `field.admin.placeholder`
|
|
25
|
+
* unguarded and throw on a missing `admin`; the empty object also leaves `admin.date` unset, so
|
|
26
|
+
* `DateCondition` still falls back to its default date format. Each synth returns a single concrete
|
|
27
|
+
* client type so leaf `field` props need no narrowing.
|
|
25
28
|
*/
|
|
26
29
|
const textClientField = (operand) => ({
|
|
27
30
|
name: operand.name,
|
|
28
31
|
label: operand.label,
|
|
29
|
-
type: "text"
|
|
32
|
+
type: "text",
|
|
33
|
+
admin: {}
|
|
30
34
|
});
|
|
31
35
|
const numberClientField = (operand) => ({
|
|
32
36
|
name: operand.name,
|
|
33
37
|
label: operand.label,
|
|
34
|
-
type: "number"
|
|
38
|
+
type: "number",
|
|
39
|
+
admin: {}
|
|
35
40
|
});
|
|
36
41
|
const dateClientField = (operand) => ({
|
|
37
42
|
name: operand.name,
|
|
38
43
|
label: operand.label,
|
|
39
|
-
type: "date"
|
|
44
|
+
type: "date",
|
|
45
|
+
admin: {}
|
|
40
46
|
});
|
|
41
47
|
const selectClientField = (operand) => ({
|
|
42
48
|
name: operand.name,
|
|
43
49
|
label: operand.label,
|
|
44
50
|
type: "select",
|
|
45
|
-
options: operand.options ?? []
|
|
51
|
+
options: operand.options ?? [],
|
|
52
|
+
admin: {}
|
|
46
53
|
});
|
|
47
54
|
//#endregion
|
|
48
55
|
export { dateClientField, numberClientField, operandFromRow, selectClientField, textClientField };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"synthesizeClientField.js","names":[],"sources":["../../src/client/synthesizeClientField.ts"],"sourcesContent":["import type {\n\tDateFieldClient,\n\tNumberFieldClient,\n\tOption,\n\tSelectFieldClient,\n\tTextFieldClient,\n} from 'payload'\nimport type { ConditionFieldType } from '../conditions/fieldTypes'\n\n/** A field row as stored in the form's `fields` blocks array (only the keys the builder reads). */\nexport type FieldRow = {\n\tblockType: string\n\tname?: string\n\tlabel?: string\n\toptions?: Option[]\n\t[key: string]: unknown\n}\n\n/** A condition operand: a selectable form field the builder can compare against. */\nexport type ConditionOperand = {\n\tname: string\n\tlabel: string\n\tconditionType: ConditionFieldType\n\toptions?: Option[]\n}\n\n/**\n * Project a stored field row into a condition operand, or `null` when the row has no usable name or\n * its type is absent from the condition-type map (not conditionable, e.g. a display-only message).\n */\nexport const operandFromRow = (\n\trow: FieldRow,\n\tconditionTypes: Record<string, ConditionFieldType>\n): ConditionOperand | null => {\n\tconst name = typeof row.name === 'string' ? row.name.trim() : ''\n\tif (name.length === 0) {\n\t\treturn null\n\t}\n\tconst conditionType = conditionTypes[row.blockType]\n\tif (!conditionType) {\n\t\treturn null\n\t}\n\treturn {\n\t\tname,\n\t\tlabel: typeof row.label === 'string' && row.label.length > 0 ? row.label : name,\n\t\tconditionType,\n\t\toptions: Array.isArray(row.options) ? row.options :
|
|
1
|
+
{"version":3,"file":"synthesizeClientField.js","names":[],"sources":["../../src/client/synthesizeClientField.ts"],"sourcesContent":["import type {\n\tDateFieldClient,\n\tNumberFieldClient,\n\tOption,\n\tSelectFieldClient,\n\tTextFieldClient,\n} from 'payload'\nimport type { ConditionFieldType } from '../conditions/fieldTypes'\nimport { fixedFieldOptions } from '../fields/data/regions'\n\n/** A field row as stored in the form's `fields` blocks array (only the keys the builder reads). */\nexport type FieldRow = {\n\tblockType: string\n\tname?: string\n\tlabel?: string\n\toptions?: Option[]\n\t/** A bare message block's richText body; see `messageSnippet`. */\n\tcontent?: unknown\n\t[key: string]: unknown\n}\n\n/** A condition operand: a selectable form field the builder can compare against. */\nexport type ConditionOperand = {\n\tname: string\n\tlabel: string\n\tconditionType: ConditionFieldType\n\toptions?: Option[]\n}\n\n/**\n * Project a stored field row into a condition operand, or `null` when the row has no usable name or\n * its type is absent from the condition-type map (not conditionable, e.g. a display-only message).\n */\nexport const operandFromRow = (\n\trow: FieldRow,\n\tconditionTypes: Record<string, ConditionFieldType>\n): ConditionOperand | null => {\n\tconst name = typeof row.name === 'string' ? row.name.trim() : ''\n\tif (name.length === 0) {\n\t\treturn null\n\t}\n\tconst conditionType = conditionTypes[row.blockType]\n\tif (!conditionType) {\n\t\treturn null\n\t}\n\treturn {\n\t\tname,\n\t\tlabel: typeof row.label === 'string' && row.label.length > 0 ? row.label : name,\n\t\tconditionType,\n\t\t// Country/state author no options but are `select`-typed conditions, so offer their fixed set.\n\t\toptions: Array.isArray(row.options) ? row.options : fixedFieldOptions(row.blockType),\n\t}\n}\n\n/**\n * Build the minimal `*FieldClient` a Payload leaf condition input requires for a given operand. Casts\n * are localized to these four synths and nowhere else: `FieldBaseClient` requires only `name`, and each\n * leaf is `FieldBaseClient & Pick<…, 'type' | …>` where every picked member but `type` (and `select`'s\n * required `options`) is optional, so the literals are structurally complete. Every synth carries an\n * `admin: {}` because Payload's `Select` and `Date` leaf inputs read `field.admin.placeholder`\n * unguarded and throw on a missing `admin`; the empty object also leaves `admin.date` unset, so\n * `DateCondition` still falls back to its default date format. Each synth returns a single concrete\n * client type so leaf `field` props need no narrowing.\n */\nexport const textClientField = (operand: ConditionOperand): TextFieldClient =>\n\t({ name: operand.name, label: operand.label, type: 'text', admin: {} }) as TextFieldClient\n\nexport const numberClientField = (operand: ConditionOperand): NumberFieldClient =>\n\t({ name: operand.name, label: operand.label, type: 'number', admin: {} }) as NumberFieldClient\n\nexport const dateClientField = (operand: ConditionOperand): DateFieldClient =>\n\t({ name: operand.name, label: operand.label, type: 'date', admin: {} }) as DateFieldClient\n\nexport const selectClientField = (operand: ConditionOperand): SelectFieldClient =>\n\t({\n\t\tname: operand.name,\n\t\tlabel: operand.label,\n\t\ttype: 'select',\n\t\toptions: operand.options ?? [],\n\t\tadmin: {},\n\t}) as SelectFieldClient\n"],"mappings":";;;;;;AAiCA,MAAa,kBACZ,KACA,mBAC6B;CAC7B,MAAM,OAAO,OAAO,IAAI,SAAS,WAAW,IAAI,KAAK,KAAK,IAAI;CAC9D,IAAI,KAAK,WAAW,GACnB,OAAO;CAER,MAAM,gBAAgB,eAAe,IAAI;CACzC,IAAI,CAAC,eACJ,OAAO;CAER,OAAO;EACN;EACA,OAAO,OAAO,IAAI,UAAU,YAAY,IAAI,MAAM,SAAS,IAAI,IAAI,QAAQ;EAC3E;EAEA,SAAS,MAAM,QAAQ,IAAI,OAAO,IAAI,IAAI,UAAU,kBAAkB,IAAI,SAAS;CACpF;AACD;;;;;;;;;;;AAYA,MAAa,mBAAmB,aAC9B;CAAE,MAAM,QAAQ;CAAM,OAAO,QAAQ;CAAO,MAAM;CAAQ,OAAO,CAAC;AAAE;AAEtE,MAAa,qBAAqB,aAChC;CAAE,MAAM,QAAQ;CAAM,OAAO,QAAQ;CAAO,MAAM;CAAU,OAAO,CAAC;AAAE;AAExE,MAAa,mBAAmB,aAC9B;CAAE,MAAM,QAAQ;CAAM,OAAO,QAAQ;CAAO,MAAM;CAAQ,OAAO,CAAC;AAAE;AAEtE,MAAa,qBAAqB,aAChC;CACA,MAAM,QAAQ;CACd,OAAO,QAAQ;CACf,MAAM;CACN,SAAS,QAAQ,WAAW,CAAC;CAC7B,OAAO,CAAC;AACT"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Field } from "payload";
|
|
2
|
+
|
|
3
|
+
//#region src/collections/buttonFields.d.ts
|
|
4
|
+
/** The default button-label fields, keyed by button. What the `buttons.fields` seam receives. */
|
|
5
|
+
type DefaultButtonFields = {
|
|
6
|
+
submit: Field;
|
|
7
|
+
prev: Field;
|
|
8
|
+
next: Field;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Composes the three form-level button fields. Receives the defaults with the content localization
|
|
12
|
+
* flag already applied and returns the field for each slot; the collection places `submit` at the
|
|
13
|
+
* bottom of the Fields tab and `prev`/`next` in a row on the Flow tab. Any slot may return the
|
|
14
|
+
* default, a replacement, or the default wrapped in a `row` alongside a host field (e.g. an icon
|
|
15
|
+
* select), so adjacent fields ride along in the same slot.
|
|
16
|
+
*/
|
|
17
|
+
type ButtonFieldsOverride = (args: {
|
|
18
|
+
defaultFields: DefaultButtonFields;
|
|
19
|
+
}) => DefaultButtonFields;
|
|
20
|
+
/** The plugin `buttons` option: form-level button labels and the group's composition seam. */
|
|
21
|
+
type ButtonsOption = {
|
|
22
|
+
fields?: ButtonFieldsOverride;
|
|
23
|
+
};
|
|
24
|
+
/** The default submit button label field (`buttons.submitLabel`), localized when `localize` is true. */
|
|
25
|
+
declare const buildSubmitLabelField: (localize: boolean) => Field;
|
|
26
|
+
/** The default "Next" button label field (`buttons.nextLabel`); applies to multi-step forms. */
|
|
27
|
+
declare const buildNextLabelField: (localize: boolean) => Field;
|
|
28
|
+
/** The default "Previous" button label field (`buttons.prevLabel`); applies to multi-step forms. */
|
|
29
|
+
declare const buildPrevLabelField: (localize: boolean) => Field;
|
|
30
|
+
/** All three defaults with the localization flag applied: exactly what the seam is handed. */
|
|
31
|
+
declare const buildDefaultButtonFields: (localize: boolean) => DefaultButtonFields;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { ButtonFieldsOverride, ButtonsOption, DefaultButtonFields, buildDefaultButtonFields, buildNextLabelField, buildPrevLabelField, buildSubmitLabelField };
|
|
34
|
+
//# sourceMappingURL=buttonFields.d.ts.map
|