@10x-media/form-builder 0.1.0-beta.2 → 0.1.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/README.md +4 -4
- package/dist/actions/body/converters.d.ts +20 -0
- package/dist/actions/body/converters.js +57 -0
- package/dist/actions/body/converters.js.map +1 -0
- package/dist/actions/body/escapeHtml.d.ts +6 -0
- package/dist/actions/body/escapeHtml.js +14 -0
- package/dist/actions/body/escapeHtml.js.map +1 -0
- package/dist/actions/body/serializeBody.d.ts +50 -0
- package/dist/actions/body/serializeBody.js +78 -0
- package/dist/actions/body/serializeBody.js.map +1 -0
- package/dist/actions/body/serializeSlate.js +31 -0
- package/dist/actions/body/serializeSlate.js.map +1 -0
- package/dist/actions/body/wildcards.d.ts +10 -0
- package/dist/actions/body/wildcards.js +33 -0
- package/dist/actions/body/wildcards.js.map +1 -0
- package/dist/actions/builtin/confirmation.js +40 -15
- package/dist/actions/builtin/confirmation.js.map +1 -1
- package/dist/actions/builtin/emailTeam.js +19 -12
- package/dist/actions/builtin/emailTeam.js.map +1 -1
- package/dist/actions/builtin/index.d.ts +13 -1
- package/dist/actions/builtin/index.js +18 -7
- package/dist/actions/builtin/index.js.map +1 -1
- package/dist/actions/builtin/signedWebhook.js +6 -2
- package/dist/actions/builtin/signedWebhook.js.map +1 -1
- package/dist/actions/defineAction.d.ts +2 -1
- package/dist/actions/defineAction.js.map +1 -1
- package/dist/actions/dispatch.js +2 -1
- package/dist/actions/dispatch.js.map +1 -1
- package/dist/actions/runActions.js +10 -1
- package/dist/actions/runActions.js.map +1 -1
- package/dist/actions/task.js +7 -5
- package/dist/actions/task.js.map +1 -1
- package/dist/aggregation/resolveResultsRequest.d.ts +22 -6
- package/dist/aggregation/resolveResultsRequest.js +18 -7
- package/dist/aggregation/resolveResultsRequest.js.map +1 -1
- package/dist/calc/normalizeCalc.js +5 -17
- package/dist/calc/normalizeCalc.js.map +1 -1
- package/dist/calc/types.d.ts +8 -2
- package/dist/calc/types.js +22 -0
- package/dist/calc/types.js.map +1 -0
- package/dist/client/ByteSizeField.d.ts +21 -0
- package/dist/client/ByteSizeField.js +56 -0
- package/dist/client/ByteSizeField.js.map +1 -0
- package/dist/client/ConditionBuilder.js +2 -1
- package/dist/client/ConditionBuilder.js.map +1 -1
- package/dist/client/FieldNameSelect.d.ts +20 -0
- package/dist/client/FieldNameSelect.js +58 -0
- package/dist/client/FieldNameSelect.js.map +1 -0
- package/dist/client/FlowBuilder.d.ts +0 -1
- package/dist/client/FlowBuilder.js +102 -59
- package/dist/client/FlowBuilder.js.map +1 -1
- package/dist/client/FormConditionField.js +1 -4
- package/dist/client/FormConditionField.js.map +1 -1
- package/dist/client/admin.css +234 -0
- package/dist/client/flowAuthoring.js +35 -0
- package/dist/client/flowAuthoring.js.map +1 -0
- package/dist/client/synthesizeClientField.js +6 -2
- package/dist/client/synthesizeClientField.js.map +1 -1
- package/dist/client/toStaticLabel.js +13 -0
- package/dist/client/toStaticLabel.js.map +1 -0
- package/dist/collections/formSubmissions.js +49 -9
- package/dist/collections/formSubmissions.js.map +1 -1
- package/dist/collections/forms.js +251 -88
- package/dist/collections/forms.js.map +1 -1
- package/dist/conditions/conditionType.js +9 -2
- package/dist/conditions/conditionType.js.map +1 -1
- package/dist/conditions/normalizeConditions.js +2 -1
- package/dist/conditions/normalizeConditions.js.map +1 -1
- package/dist/consent/builtin/index.d.ts +11 -1
- package/dist/consent/builtin/index.js +15 -5
- package/dist/consent/builtin/index.js.map +1 -1
- package/dist/consent/builtin/static.js +7 -3
- package/dist/consent/builtin/static.js.map +1 -1
- package/dist/exports/client.d.ts +3 -1
- package/dist/exports/client.js +3 -1
- package/dist/exports/react.d.ts +15 -6
- package/dist/exports/react.js +10 -2
- package/dist/exports/rsc.d.ts +6 -1
- package/dist/exports/rsc.js +6 -1
- package/dist/exports/types.d.ts +4 -4
- package/dist/fields/buildFieldBlocks.js +20 -15
- package/dist/fields/buildFieldBlocks.js.map +1 -1
- package/dist/fields/builtin/calculation.js +115 -2
- package/dist/fields/builtin/calculation.js.map +1 -1
- package/dist/fields/builtin/consent.js +8 -4
- package/dist/fields/builtin/consent.js.map +1 -1
- package/dist/fields/builtin/date.js +29 -0
- package/dist/fields/builtin/date.js.map +1 -0
- package/dist/fields/builtin/file.d.ts +15 -0
- package/dist/fields/builtin/file.js +87 -7
- package/dist/fields/builtin/file.js.map +1 -1
- package/dist/fields/builtin/index.d.ts +24 -0
- package/dist/fields/builtin/index.js +29 -8
- package/dist/fields/builtin/index.js.map +1 -1
- package/dist/fields/builtin/message.js +26 -0
- package/dist/fields/builtin/message.js.map +1 -0
- package/dist/fields/builtin/repeater.js +7 -3
- package/dist/fields/builtin/repeater.js.map +1 -1
- package/dist/fields/builtin/select.js +7 -3
- package/dist/fields/builtin/select.js.map +1 -1
- package/dist/fields/fieldNamesOfType.js +25 -0
- package/dist/fields/fieldNamesOfType.js.map +1 -0
- package/dist/fields/localizedIf.d.ts +13 -0
- package/dist/fields/localizedIf.js +12 -0
- package/dist/fields/localizedIf.js.map +1 -0
- package/dist/fields/sharedConfig.js +82 -57
- package/dist/fields/sharedConfig.js.map +1 -1
- package/dist/fields/types.d.ts +7 -2
- package/dist/flow/normalizeFlow.js +7 -1
- package/dist/flow/normalizeFlow.js.map +1 -1
- package/dist/flow/stepLabel.js +15 -0
- package/dist/flow/stepLabel.js.map +1 -0
- package/dist/form/pollState.d.ts +22 -0
- package/dist/form/pollState.js +17 -0
- package/dist/form/pollState.js.map +1 -0
- package/dist/form/toFormDocument.d.ts +60 -0
- package/dist/form/toFormDocument.js +39 -0
- package/dist/form/toFormDocument.js.map +1 -0
- package/dist/form/types.d.ts +48 -0
- package/dist/index.d.ts +81 -13
- package/dist/index.js +36 -15
- package/dist/index.js.map +1 -1
- package/dist/plugin/registerCollections.js +17 -37
- package/dist/plugin/registerCollections.js.map +1 -1
- package/dist/plugin/uploadsCollection.d.ts +11 -0
- package/dist/plugin/uploadsCollection.js +44 -0
- package/dist/plugin/uploadsCollection.js.map +1 -0
- package/dist/poll/applyPollOptions.js +17 -0
- package/dist/poll/applyPollOptions.js.map +1 -0
- package/dist/poll/buildPollOptionSourceFields.js +53 -0
- package/dist/poll/buildPollOptionSourceFields.js.map +1 -0
- package/dist/poll/definePollOptionSource.d.ts +35 -0
- package/dist/poll/definePollOptionSource.js +6 -0
- package/dist/poll/definePollOptionSource.js.map +1 -0
- package/dist/poll/registry.d.ts +16 -0
- package/dist/poll/registry.js +16 -0
- package/dist/poll/registry.js.map +1 -0
- package/dist/poll/resolvePollOptions.d.ts +28 -0
- package/dist/poll/resolvePollOptions.js +64 -0
- package/dist/poll/resolvePollOptions.js.map +1 -0
- package/dist/poll/resolvePollOutcome.d.ts +21 -0
- package/dist/poll/resolvePollOutcome.js +36 -0
- package/dist/poll/resolvePollOutcome.js.map +1 -0
- package/dist/prefill/valuesFromSearchParams.js +1 -1
- package/dist/prefill/valuesFromSearchParams.js.map +1 -1
- package/dist/presentations/types.d.ts +3 -2
- package/dist/react/Form.d.ts +13 -31
- package/dist/react/Form.js +98 -85
- package/dist/react/Form.js.map +1 -1
- package/dist/react/FormContext.d.ts +35 -1
- package/dist/react/FormContext.js.map +1 -1
- package/dist/react/FormControls.d.ts +55 -0
- package/dist/react/FormControls.js +56 -0
- package/dist/react/FormControls.js.map +1 -0
- package/dist/react/FormResults.d.ts +6 -3
- package/dist/react/FormResults.js +37 -29
- package/dist/react/FormResults.js.map +1 -1
- package/dist/react/FormSteps.d.ts +17 -0
- package/dist/react/FormSteps.js +35 -0
- package/dist/react/FormSteps.js.map +1 -0
- package/dist/react/Poll.d.ts +17 -6
- package/dist/react/Poll.js +66 -18
- package/dist/react/Poll.js.map +1 -1
- package/dist/react/captcha/HcaptchaCaptcha.d.ts +28 -0
- package/dist/react/captcha/HcaptchaCaptcha.js +53 -0
- package/dist/react/captcha/HcaptchaCaptcha.js.map +1 -0
- package/dist/react/captcha/RecaptchaCaptcha.d.ts +41 -0
- package/dist/react/captcha/RecaptchaCaptcha.js +93 -0
- package/dist/react/captcha/RecaptchaCaptcha.js.map +1 -0
- package/dist/react/captcha/TurnstileCaptcha.d.ts +28 -0
- package/dist/react/captcha/TurnstileCaptcha.js +53 -0
- package/dist/react/captcha/TurnstileCaptcha.js.map +1 -0
- package/dist/react/captcha/types.d.ts +8 -0
- package/dist/react/captcha/useCaptchaScript.js +45 -0
- package/dist/react/captcha/useCaptchaScript.js.map +1 -0
- package/dist/react/captcha/vendors.js +8 -0
- package/dist/react/captcha/vendors.js.map +1 -0
- package/dist/react/fetchResults.d.ts +1 -1
- package/dist/react/fetchResults.js +1 -1
- package/dist/react/fetchResults.js.map +1 -1
- package/dist/react/primitives/Input.d.ts +1 -1
- package/dist/react/primitives/Input.js.map +1 -1
- package/dist/react/renderers/calculation.js +1 -1
- package/dist/react/renderers/checkbox.js +1 -1
- package/dist/react/renderers/consent.js +1 -1
- package/dist/react/renderers/date.js +37 -0
- package/dist/react/renderers/date.js.map +1 -0
- package/dist/react/renderers/email.js +1 -1
- package/dist/react/renderers/file.js +25 -8
- package/dist/react/renderers/file.js.map +1 -1
- package/dist/react/renderers/index.js +4 -0
- package/dist/react/renderers/index.js.map +1 -1
- package/dist/react/renderers/message.js +35 -0
- package/dist/react/renderers/message.js.map +1 -0
- package/dist/react/renderers/number.js +1 -1
- package/dist/react/renderers/repeater.js +1 -1
- package/dist/react/renderers/select.js +1 -1
- package/dist/react/renderers/text.js +1 -1
- package/dist/react/renderers/textarea.js +1 -1
- package/dist/react/state.d.ts +25 -1
- package/dist/react/state.js +14 -1
- package/dist/react/state.js.map +1 -1
- package/dist/react/uploadFile.d.ts +2 -2
- package/dist/react/uploadFile.js +1 -1
- package/dist/react/uploadFile.js.map +1 -1
- package/dist/spam/captcha.d.ts +3 -2
- package/dist/spam/captcha.js +3 -2
- package/dist/spam/captcha.js.map +1 -1
- package/dist/spam/clientIp.js +9 -0
- package/dist/spam/clientIp.js.map +1 -0
- package/dist/spam/identify.js +3 -6
- package/dist/spam/identify.js.map +1 -1
- package/dist/spam/providers/hcaptcha.d.ts +17 -0
- package/dist/spam/providers/hcaptcha.js +18 -0
- package/dist/spam/providers/hcaptcha.js.map +1 -0
- package/dist/spam/providers/recaptcha.d.ts +23 -0
- package/dist/spam/providers/recaptcha.js +23 -0
- package/dist/spam/providers/recaptcha.js.map +1 -0
- package/dist/spam/providers/siteverify.js +34 -0
- package/dist/spam/providers/siteverify.js.map +1 -0
- package/dist/spam/providers/turnstile.d.ts +17 -0
- package/dist/spam/providers/turnstile.js +18 -0
- package/dist/spam/providers/turnstile.js.map +1 -0
- package/dist/spam/spamGuard.js +2 -5
- package/dist/spam/spamGuard.js.map +1 -1
- package/dist/spam/types.d.ts +2 -2
- package/dist/submissions/runSubmission.js +70 -33
- package/dist/submissions/runSubmission.js.map +1 -1
- package/dist/submissions/validateSubmission.js +41 -7
- package/dist/submissions/validateSubmission.js.map +1 -1
- package/dist/submissions/votedCookie.d.ts +13 -0
- package/dist/submissions/votedCookie.js +27 -0
- package/dist/submissions/votedCookie.js.map +1 -0
- package/dist/translations/de.js +219 -0
- package/dist/translations/de.js.map +1 -0
- package/dist/translations/en.js +71 -10
- package/dist/translations/en.js.map +1 -1
- package/dist/translations/index.d.ts +2 -1
- package/dist/translations/index.js +6 -2
- package/dist/translations/index.js.map +1 -1
- package/dist/translations/keys.d.ts +69 -8
- package/dist/translations/keys.js +70 -9
- package/dist/translations/keys.js.map +1 -1
- package/dist/uploads/formatBytes.d.ts +9 -0
- package/dist/uploads/formatBytes.js +25 -0
- package/dist/uploads/formatBytes.js.map +1 -0
- package/dist/uploads/resolveFileRef.js +1 -1
- package/dist/uploads/resolveFileRef.js.map +1 -1
- package/dist/uploads/types.d.ts +1 -2
- package/dist/validation/builtin/index.d.ts +14 -0
- package/dist/validation/builtin/index.js +12 -1
- package/dist/validation/builtin/index.js.map +1 -1
- package/dist/validation/builtin/maxDate.js +23 -0
- package/dist/validation/builtin/maxDate.js.map +1 -0
- package/dist/validation/builtin/minDate.js +23 -0
- package/dist/validation/builtin/minDate.js.map +1 -0
- package/dist/validation/builtin/validateDateBound.js +18 -0
- package/dist/validation/builtin/validateDateBound.js.map +1 -0
- package/dist/validation/validateUrl.js +22 -0
- package/dist/validation/validateUrl.js.map +1 -0
- package/package.json +4 -2
- package/styles/form-builder.css +10 -190
- package/dist/collections/uploads.d.ts +0 -30
- package/dist/collections/uploads.js +0 -55
- package/dist/collections/uploads.js.map +0 -1
- package/dist/presentations/registry.d.ts +0 -15
- package/dist/presentations/registry.js +0 -15
- package/dist/presentations/registry.js.map +0 -1
- package/dist/react/toFormDocument.d.ts +0 -24
- package/dist/react/toFormDocument.js +0 -22
- package/dist/react/toFormDocument.js.map +0 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/client/ByteSizeField.d.ts
|
|
2
|
+
/** Props: standard number field client props; label and description arrive resolved on `field`. */
|
|
3
|
+
type ByteSizeFieldProps = {
|
|
4
|
+
path?: string;
|
|
5
|
+
field?: {
|
|
6
|
+
label?: unknown;
|
|
7
|
+
admin?: {
|
|
8
|
+
description?: unknown;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
label?: unknown;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The `Field` mounted on a byte-count number config field (the file field's `maxSize`): a plain
|
|
15
|
+
* number input that live-previews the value as a human-readable size ('= 2.5 MB') so authors are
|
|
16
|
+
* not left decoding raw bytes. Stores the raw byte number unchanged.
|
|
17
|
+
*/
|
|
18
|
+
declare const ByteSizeField: (props: ByteSizeFieldProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { ByteSizeField };
|
|
21
|
+
//# sourceMappingURL=ByteSizeField.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { formatBytes } from "../uploads/formatBytes.js";
|
|
3
|
+
import { toStaticLabel } from "./toStaticLabel.js";
|
|
4
|
+
import { FieldDescription, FieldLabel, useField } from "@payloadcms/ui";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/client/ByteSizeField.tsx
|
|
7
|
+
/**
|
|
8
|
+
* The `Field` mounted on a byte-count number config field (the file field's `maxSize`): a plain
|
|
9
|
+
* number input that live-previews the value as a human-readable size ('= 2.5 MB') so authors are
|
|
10
|
+
* not left decoding raw bytes. Stores the raw byte number unchanged.
|
|
11
|
+
*/
|
|
12
|
+
const ByteSizeField = (props) => {
|
|
13
|
+
const { path, setValue, value } = useField({ path: props.path });
|
|
14
|
+
const label = toStaticLabel(props.field?.label ?? props.label);
|
|
15
|
+
const description = toStaticLabel(props.field?.admin?.description);
|
|
16
|
+
const handleChange = (event) => {
|
|
17
|
+
if (event.target.value === "") {
|
|
18
|
+
setValue(null);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const parsed = Number(event.target.value);
|
|
22
|
+
if (Number.isFinite(parsed)) setValue(parsed);
|
|
23
|
+
};
|
|
24
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
25
|
+
className: "field-type number",
|
|
26
|
+
style: { marginBlockEnd: "1rem" },
|
|
27
|
+
children: [/* @__PURE__ */ jsx(FieldLabel, {
|
|
28
|
+
label,
|
|
29
|
+
path
|
|
30
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
31
|
+
className: "field-type__wrap",
|
|
32
|
+
children: [
|
|
33
|
+
/* @__PURE__ */ jsx("input", {
|
|
34
|
+
id: `field-${path.replace(/\./g, "__")}`,
|
|
35
|
+
min: 0,
|
|
36
|
+
name: path,
|
|
37
|
+
onChange: handleChange,
|
|
38
|
+
type: "number",
|
|
39
|
+
value: typeof value === "number" ? value : ""
|
|
40
|
+
}),
|
|
41
|
+
typeof value === "number" && value > 0 ? /* @__PURE__ */ jsx("div", {
|
|
42
|
+
className: "field-description",
|
|
43
|
+
children: `= ${formatBytes(value)}`
|
|
44
|
+
}) : null,
|
|
45
|
+
/* @__PURE__ */ jsx(FieldDescription, {
|
|
46
|
+
description,
|
|
47
|
+
path
|
|
48
|
+
})
|
|
49
|
+
]
|
|
50
|
+
})]
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
export { ByteSizeField };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=ByteSizeField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ByteSizeField.js","names":[],"sources":["../../src/client/ByteSizeField.tsx"],"sourcesContent":["'use client'\n\nimport { FieldDescription, FieldLabel, useField } from '@payloadcms/ui'\nimport type { ChangeEvent } from 'react'\nimport { formatBytes } from '../uploads/formatBytes'\nimport { toStaticLabel } from './toStaticLabel'\n\n/** Props: standard number field client props; label and description arrive resolved on `field`. */\nexport type ByteSizeFieldProps = {\n\tpath?: string\n\tfield?: { label?: unknown; admin?: { description?: unknown } }\n\tlabel?: unknown\n}\n\n/**\n * The `Field` mounted on a byte-count number config field (the file field's `maxSize`): a plain\n * number input that live-previews the value as a human-readable size ('= 2.5 MB') so authors are\n * not left decoding raw bytes. Stores the raw byte number unchanged.\n */\nexport const ByteSizeField = (props: ByteSizeFieldProps) => {\n\tconst { path, setValue, value } = useField<number>({ path: props.path })\n\tconst label = toStaticLabel(props.field?.label ?? props.label)\n\tconst description = toStaticLabel(props.field?.admin?.description)\n\n\tconst handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n\t\tif (event.target.value === '') {\n\t\t\tsetValue(null)\n\t\t\treturn\n\t\t}\n\t\tconst parsed = Number(event.target.value)\n\t\tif (Number.isFinite(parsed)) {\n\t\t\tsetValue(parsed)\n\t\t}\n\t}\n\n\treturn (\n\t\t<div className=\"field-type number\" style={{ marginBlockEnd: '1rem' }}>\n\t\t\t<FieldLabel label={label} path={path} />\n\t\t\t<div className=\"field-type__wrap\">\n\t\t\t\t<input\n\t\t\t\t\tid={`field-${path.replace(/\\./g, '__')}`}\n\t\t\t\t\tmin={0}\n\t\t\t\t\tname={path}\n\t\t\t\t\tonChange={handleChange}\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tvalue={typeof value === 'number' ? value : ''}\n\t\t\t\t/>\n\t\t\t\t{typeof value === 'number' && value > 0 ? (\n\t\t\t\t\t<div className=\"field-description\">{`= ${formatBytes(value)}`}</div>\n\t\t\t\t) : null}\n\t\t\t\t<FieldDescription description={description} path={path} />\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n"],"mappings":";;;;;;;;;;;AAmBA,MAAa,iBAAiB,UAA8B;CAC3D,MAAM,EAAE,MAAM,UAAU,UAAU,SAAiB,EAAE,MAAM,MAAM,KAAK,CAAC;CACvE,MAAM,QAAQ,cAAc,MAAM,OAAO,SAAS,MAAM,KAAK;CAC7D,MAAM,cAAc,cAAc,MAAM,OAAO,OAAO,WAAW;CAEjE,MAAM,gBAAgB,UAAyC;EAC9D,IAAI,MAAM,OAAO,UAAU,IAAI;GAC9B,SAAS,IAAI;GACb;EACD;EACA,MAAM,SAAS,OAAO,MAAM,OAAO,KAAK;EACxC,IAAI,OAAO,SAAS,MAAM,GACzB,SAAS,MAAM;CAEjB;CAEA,OACC,qBAAC,OAAD;EAAK,WAAU;EAAoB,OAAO,EAAE,gBAAgB,OAAO;YAAnE,CACC,oBAAC,YAAD;GAAmB;GAAa;EAAO,CAAA,GACvC,qBAAC,OAAD;GAAK,WAAU;aAAf;IACC,oBAAC,SAAD;KACC,IAAI,SAAS,KAAK,QAAQ,OAAO,IAAI;KACrC,KAAK;KACL,MAAM;KACN,UAAU;KACV,MAAK;KACL,OAAO,OAAO,UAAU,WAAW,QAAQ;IAC3C,CAAA;IACA,OAAO,UAAU,YAAY,QAAQ,IACrC,oBAAC,OAAD;KAAK,WAAU;eAAqB,KAAK,YAAY,KAAK;IAAS,CAAA,IAChE;IACJ,oBAAC,kBAAD;KAA+B;KAAmB;IAAO,CAAA;GACrD;IACD;;AAEP"}
|
|
@@ -6,8 +6,9 @@ import { operandFromRow } from "./synthesizeClientField.js";
|
|
|
6
6
|
import { ConditionRow } from "./ConditionRow.js";
|
|
7
7
|
import { reduceFieldsToValues, transformWhereQuery } from "payload/shared";
|
|
8
8
|
import { Button, useFormFields } from "@payloadcms/ui";
|
|
9
|
-
import { useMemo } from "react";
|
|
10
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { useMemo } from "react";
|
|
11
|
+
import "./admin.css";
|
|
11
12
|
//#region src/client/ConditionBuilder.tsx
|
|
12
13
|
const toRow = (constraint) => {
|
|
13
14
|
const field = Object.keys(constraint)[0];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConditionBuilder.js","names":["useTranslation"],"sources":["../../src/client/ConditionBuilder.tsx"],"sourcesContent":["'use client'\n\nimport { Button, useFormFields } from '@payloadcms/ui'\nimport type { Operator, Where } from 'payload'\nimport { reduceFieldsToValues, transformWhereQuery } from 'payload/shared'\nimport { useMemo } from 'react'\nimport { type ConditionFieldType, firstOperatorFor } from '../conditions/fieldTypes'\nimport { keys } from '../translations/keys'\nimport { useTranslation } from '../translations/useTranslation'\nimport { ConditionRow, type RowCondition } from './ConditionRow'\nimport { type ConditionOperand, type FieldRow, operandFromRow } from './synthesizeClientField'\n\nexport type ConditionBuilderProps = {\n\tvalue?: Where\n\tonChange: (next: Where) => void\n\tconditionTypes: Record<string, ConditionFieldType>\n\t/** This field's own machine name, excluded from the operand list (a field cannot condition on itself). */\n\tselfName?: string\n}\n\nconst toRow = (constraint: Record<string, unknown>): RowCondition | null => {\n\tconst field = Object.keys(constraint)[0]\n\tif (!field) {\n\t\treturn null\n\t}\n\tconst ops = constraint[field] as Record<string, unknown>\n\tconst operator = Object.keys(ops)[0] as Operator | undefined\n\tif (!operator) {\n\t\treturn null\n\t}\n\treturn { field, operator, value: ops[operator] }\n}\n\nconst fromRow = (row: RowCondition): Where => ({ [row.field]: { [row.operator]: row.value } })\n\nconst readGroups = (value: Where | undefined): RowCondition[][] => {\n\tif (!value || Object.keys(value).length === 0) {\n\t\treturn []\n\t}\n\tconst canonical = transformWhereQuery(value)\n\tconst or = Array.isArray(canonical.or) ? canonical.or : []\n\treturn or.map((group) => {\n\t\tconst and = Array.isArray((group as Where).and) ? ((group as Where).and as Where[]) : []\n\t\treturn and\n\t\t\t.map((constraint) => toRow(constraint as Record<string, unknown>))\n\t\t\t.filter((row): row is RowCondition => row !== null)\n\t})\n}\n\nconst writeGroups = (groups: RowCondition[][]): Where => ({\n\tor: groups.filter((group) => group.length > 0).map((group) => ({ and: group.map(fromRow) })),\n})\n\nconst operandsFromData = (\n\tdata: Record<string, unknown>,\n\tconditionTypes: Record<string, ConditionFieldType>,\n\tselfName: string | undefined\n): ConditionOperand[] => {\n\tconst rows = Array.isArray(data.fields) ? (data.fields as FieldRow[]) : []\n\treturn rows\n\t\t.map((row) => operandFromRow(row, conditionTypes))\n\t\t.filter((operand): operand is ConditionOperand => operand !== null && operand.name !== selfName)\n}\n\nexport const ConditionBuilder = ({\n\tvalue,\n\tonChange,\n\tconditionTypes,\n\tselfName,\n}: ConditionBuilderProps) => {\n\tconst { t } = useTranslation()\n\t// Serialize the operand list so the builder re-renders only when operands actually change (a field\n\t// added, renamed, retyped, or its options edited), not on every unrelated form-state update.\n\tconst operandsJson = useFormFields(([fields]) =>\n\t\tJSON.stringify(operandsFromData(reduceFieldsToValues(fields, true), conditionTypes, selfName))\n\t)\n\tconst operands = useMemo(() => JSON.parse(operandsJson) as ConditionOperand[], [operandsJson])\n\n\tconst groups = readGroups(value)\n\tconst emit = (next: RowCondition[][]) => onChange(writeGroups(next))\n\n\tconst addCondition = (groupIndex: number) => {\n\t\tconst first = operands[0]\n\t\tif (!first) {\n\t\t\treturn\n\t\t}\n\t\tconst row: RowCondition = {\n\t\t\tfield: first.name,\n\t\t\toperator: firstOperatorFor(first.conditionType),\n\t\t\tvalue: undefined,\n\t\t}\n\t\temit(\n\t\t\tgroups.length === 0\n\t\t\t\t? [[row]]\n\t\t\t\t: groups.map((group, index) => (index === groupIndex ? [...group, row] : group))\n\t\t)\n\t}\n\n\tconst addOrGroup = () => {\n\t\tconst first = operands[0]\n\t\tif (!first) {\n\t\t\treturn\n\t\t}\n\t\temit([\n\t\t\t...groups,\n\t\t\t[{ field: first.name, operator: firstOperatorFor(first.conditionType), value: undefined }],\n\t\t])\n\t}\n\n\tconst updateRow = (groupIndex: number, rowIndex: number, row: RowCondition) =>\n\t\temit(\n\t\t\tgroups.map((group, index) =>\n\t\t\t\tindex === groupIndex\n\t\t\t\t\t? group.map((existing, position) => (position === rowIndex ? row : existing))\n\t\t\t\t\t: group\n\t\t\t)\n\t\t)\n\n\tconst removeRow = (groupIndex: number, rowIndex: number) =>\n\t\temit(\n\t\t\tgroups\n\t\t\t\t.map((group, index) =>\n\t\t\t\t\tindex === groupIndex ? group.filter((_, position) => position !== rowIndex) : group\n\t\t\t\t)\n\t\t\t\t.filter((group) => group.length > 0)\n\t\t)\n\n\tif (operands.length === 0) {\n\t\treturn <p className=\"fb-condition-builder__hint\">{t(keys.conditionNoFields)}</p>\n\t}\n\n\treturn (\n\t\t<div className=\"fb-condition-builder\">\n\t\t\t{groups.length === 0 ? (\n\t\t\t\t<p className=\"fb-condition-builder__hint\">{t(keys.conditionEmpty)}</p>\n\t\t\t) : (\n\t\t\t\tgroups.map((group, groupIndex) => (\n\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: groups are positional and never reordered\n\t\t\t\t\t<div key={groupIndex} className=\"fb-condition-builder__group\">\n\t\t\t\t\t\t{groupIndex > 0 ? (\n\t\t\t\t\t\t\t<div className=\"fb-condition-builder__or-label\">{t(keys.conditionOr)}</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{group.map((row, rowIndex) => (\n\t\t\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: rows are positional and never reordered\n\t\t\t\t\t\t\t<div key={rowIndex} className=\"fb-condition-builder__and-row\">\n\t\t\t\t\t\t\t\t{rowIndex > 0 ? (\n\t\t\t\t\t\t\t\t\t<span className=\"fb-condition-builder__and-label\">{t(keys.conditionAnd)}</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<ConditionRow\n\t\t\t\t\t\t\t\t\tcondition={row}\n\t\t\t\t\t\t\t\t\toperands={operands}\n\t\t\t\t\t\t\t\t\tonChange={(next) => updateRow(groupIndex, rowIndex, next)}\n\t\t\t\t\t\t\t\t\tonRemove={() => removeRow(groupIndex, rowIndex)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t<div className=\"fb-condition-builder__add-condition\">\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tbuttonStyle=\"icon-label\"\n\t\t\t\t\t\t\t\ticon=\"plus\"\n\t\t\t\t\t\t\t\ticonStyle=\"with-border\"\n\t\t\t\t\t\t\t\ticonPosition=\"left\"\n\t\t\t\t\t\t\t\tonClick={() => addCondition(groupIndex)}\n\t\t\t\t\t\t\t\tmargin={false}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{t(keys.conditionAddCondition)}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t))\n\t\t\t)}\n\t\t\t<div className=\"fb-condition-builder__actions\">\n\t\t\t\t<Button\n\t\t\t\t\tbuttonStyle=\"icon-label\"\n\t\t\t\t\ticon=\"plus\"\n\t\t\t\t\ticonStyle=\"with-border\"\n\t\t\t\t\ticonPosition=\"left\"\n\t\t\t\t\tonClick={() => (groups.length === 0 ? addCondition(0) : addOrGroup())}\n\t\t\t\t\tmargin={false}\n\t\t\t\t>\n\t\t\t\t\t{groups.length === 0 ? t(keys.conditionAddCondition) : t(keys.conditionAddOr)}\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ConditionBuilder.js","names":["useTranslation"],"sources":["../../src/client/ConditionBuilder.tsx"],"sourcesContent":["'use client'\n\nimport { Button, useFormFields } from '@payloadcms/ui'\nimport type { Operator, Where } from 'payload'\nimport { reduceFieldsToValues, transformWhereQuery } from 'payload/shared'\nimport { useMemo } from 'react'\nimport { type ConditionFieldType, firstOperatorFor } from '../conditions/fieldTypes'\nimport { keys } from '../translations/keys'\nimport { useTranslation } from '../translations/useTranslation'\nimport { ConditionRow, type RowCondition } from './ConditionRow'\nimport { type ConditionOperand, type FieldRow, operandFromRow } from './synthesizeClientField'\nimport './admin.css'\n\nexport type ConditionBuilderProps = {\n\tvalue?: Where\n\tonChange: (next: Where) => void\n\tconditionTypes: Record<string, ConditionFieldType>\n\t/** This field's own machine name, excluded from the operand list (a field cannot condition on itself). */\n\tselfName?: string\n}\n\nconst toRow = (constraint: Record<string, unknown>): RowCondition | null => {\n\tconst field = Object.keys(constraint)[0]\n\tif (!field) {\n\t\treturn null\n\t}\n\tconst ops = constraint[field] as Record<string, unknown>\n\tconst operator = Object.keys(ops)[0] as Operator | undefined\n\tif (!operator) {\n\t\treturn null\n\t}\n\treturn { field, operator, value: ops[operator] }\n}\n\nconst fromRow = (row: RowCondition): Where => ({ [row.field]: { [row.operator]: row.value } })\n\nconst readGroups = (value: Where | undefined): RowCondition[][] => {\n\tif (!value || Object.keys(value).length === 0) {\n\t\treturn []\n\t}\n\tconst canonical = transformWhereQuery(value)\n\tconst or = Array.isArray(canonical.or) ? canonical.or : []\n\treturn or.map((group) => {\n\t\tconst and = Array.isArray((group as Where).and) ? ((group as Where).and as Where[]) : []\n\t\treturn and\n\t\t\t.map((constraint) => toRow(constraint as Record<string, unknown>))\n\t\t\t.filter((row): row is RowCondition => row !== null)\n\t})\n}\n\nconst writeGroups = (groups: RowCondition[][]): Where => ({\n\tor: groups.filter((group) => group.length > 0).map((group) => ({ and: group.map(fromRow) })),\n})\n\nconst operandsFromData = (\n\tdata: Record<string, unknown>,\n\tconditionTypes: Record<string, ConditionFieldType>,\n\tselfName: string | undefined\n): ConditionOperand[] => {\n\tconst rows = Array.isArray(data.fields) ? (data.fields as FieldRow[]) : []\n\treturn rows\n\t\t.map((row) => operandFromRow(row, conditionTypes))\n\t\t.filter((operand): operand is ConditionOperand => operand !== null && operand.name !== selfName)\n}\n\nexport const ConditionBuilder = ({\n\tvalue,\n\tonChange,\n\tconditionTypes,\n\tselfName,\n}: ConditionBuilderProps) => {\n\tconst { t } = useTranslation()\n\t// Serialize the operand list so the builder re-renders only when operands actually change (a field\n\t// added, renamed, retyped, or its options edited), not on every unrelated form-state update.\n\tconst operandsJson = useFormFields(([fields]) =>\n\t\tJSON.stringify(operandsFromData(reduceFieldsToValues(fields, true), conditionTypes, selfName))\n\t)\n\tconst operands = useMemo(() => JSON.parse(operandsJson) as ConditionOperand[], [operandsJson])\n\n\tconst groups = readGroups(value)\n\tconst emit = (next: RowCondition[][]) => onChange(writeGroups(next))\n\n\tconst addCondition = (groupIndex: number) => {\n\t\tconst first = operands[0]\n\t\tif (!first) {\n\t\t\treturn\n\t\t}\n\t\tconst row: RowCondition = {\n\t\t\tfield: first.name,\n\t\t\toperator: firstOperatorFor(first.conditionType),\n\t\t\tvalue: undefined,\n\t\t}\n\t\temit(\n\t\t\tgroups.length === 0\n\t\t\t\t? [[row]]\n\t\t\t\t: groups.map((group, index) => (index === groupIndex ? [...group, row] : group))\n\t\t)\n\t}\n\n\tconst addOrGroup = () => {\n\t\tconst first = operands[0]\n\t\tif (!first) {\n\t\t\treturn\n\t\t}\n\t\temit([\n\t\t\t...groups,\n\t\t\t[{ field: first.name, operator: firstOperatorFor(first.conditionType), value: undefined }],\n\t\t])\n\t}\n\n\tconst updateRow = (groupIndex: number, rowIndex: number, row: RowCondition) =>\n\t\temit(\n\t\t\tgroups.map((group, index) =>\n\t\t\t\tindex === groupIndex\n\t\t\t\t\t? group.map((existing, position) => (position === rowIndex ? row : existing))\n\t\t\t\t\t: group\n\t\t\t)\n\t\t)\n\n\tconst removeRow = (groupIndex: number, rowIndex: number) =>\n\t\temit(\n\t\t\tgroups\n\t\t\t\t.map((group, index) =>\n\t\t\t\t\tindex === groupIndex ? group.filter((_, position) => position !== rowIndex) : group\n\t\t\t\t)\n\t\t\t\t.filter((group) => group.length > 0)\n\t\t)\n\n\tif (operands.length === 0) {\n\t\treturn <p className=\"fb-condition-builder__hint\">{t(keys.conditionNoFields)}</p>\n\t}\n\n\treturn (\n\t\t<div className=\"fb-condition-builder\">\n\t\t\t{groups.length === 0 ? (\n\t\t\t\t<p className=\"fb-condition-builder__hint\">{t(keys.conditionEmpty)}</p>\n\t\t\t) : (\n\t\t\t\tgroups.map((group, groupIndex) => (\n\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: groups are positional and never reordered\n\t\t\t\t\t<div key={groupIndex} className=\"fb-condition-builder__group\">\n\t\t\t\t\t\t{groupIndex > 0 ? (\n\t\t\t\t\t\t\t<div className=\"fb-condition-builder__or-label\">{t(keys.conditionOr)}</div>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{group.map((row, rowIndex) => (\n\t\t\t\t\t\t\t// biome-ignore lint/suspicious/noArrayIndexKey: rows are positional and never reordered\n\t\t\t\t\t\t\t<div key={rowIndex} className=\"fb-condition-builder__and-row\">\n\t\t\t\t\t\t\t\t{rowIndex > 0 ? (\n\t\t\t\t\t\t\t\t\t<span className=\"fb-condition-builder__and-label\">{t(keys.conditionAnd)}</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t<ConditionRow\n\t\t\t\t\t\t\t\t\tcondition={row}\n\t\t\t\t\t\t\t\t\toperands={operands}\n\t\t\t\t\t\t\t\t\tonChange={(next) => updateRow(groupIndex, rowIndex, next)}\n\t\t\t\t\t\t\t\t\tonRemove={() => removeRow(groupIndex, rowIndex)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t<div className=\"fb-condition-builder__add-condition\">\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tbuttonStyle=\"icon-label\"\n\t\t\t\t\t\t\t\ticon=\"plus\"\n\t\t\t\t\t\t\t\ticonStyle=\"with-border\"\n\t\t\t\t\t\t\t\ticonPosition=\"left\"\n\t\t\t\t\t\t\t\tonClick={() => addCondition(groupIndex)}\n\t\t\t\t\t\t\t\tmargin={false}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{t(keys.conditionAddCondition)}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t))\n\t\t\t)}\n\t\t\t<div className=\"fb-condition-builder__actions\">\n\t\t\t\t<Button\n\t\t\t\t\tbuttonStyle=\"icon-label\"\n\t\t\t\t\ticon=\"plus\"\n\t\t\t\t\ticonStyle=\"with-border\"\n\t\t\t\t\ticonPosition=\"left\"\n\t\t\t\t\tonClick={() => (groups.length === 0 ? addCondition(0) : addOrGroup())}\n\t\t\t\t\tmargin={false}\n\t\t\t\t>\n\t\t\t\t\t{groups.length === 0 ? t(keys.conditionAddCondition) : t(keys.conditionAddOr)}\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/client/FieldNameSelect.d.ts
|
|
2
|
+
/** Props: standard JSON/text field client props plus the `types` allow-list we pass via clientProps. */
|
|
3
|
+
type FieldNameSelectProps = {
|
|
4
|
+
path?: string;
|
|
5
|
+
field?: {
|
|
6
|
+
label?: unknown;
|
|
7
|
+
};
|
|
8
|
+
label?: unknown; /** Sibling `fields` blocks whose `blockType` is in this list populate the select's options. */
|
|
9
|
+
types: string[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The `Field` mounted on a text config field (e.g. an action's `toField`) that should be authored
|
|
13
|
+
* by picking from the form's own fields rather than free-typing a name. Options come from the
|
|
14
|
+
* sibling `fields` blocks array, filtered to `types`; the stored value is kept selectable even if
|
|
15
|
+
* it no longer matches any field, so switching to this component never silently drops data.
|
|
16
|
+
*/
|
|
17
|
+
declare const FieldNameSelect: (props: FieldNameSelectProps) => import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { FieldNameSelect };
|
|
20
|
+
//# sourceMappingURL=FieldNameSelect.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { fieldNamesOfType } from "../fields/fieldNamesOfType.js";
|
|
3
|
+
import { toStaticLabel } from "./toStaticLabel.js";
|
|
4
|
+
import { reduceFieldsToValues } from "payload/shared";
|
|
5
|
+
import { FieldLabel, ReactSelect, useField, useFormFields } from "@payloadcms/ui";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { useMemo } from "react";
|
|
8
|
+
//#region src/client/FieldNameSelect.tsx
|
|
9
|
+
const optionsFromData = (data, types) => {
|
|
10
|
+
const rows = Array.isArray(data.fields) ? data.fields : [];
|
|
11
|
+
const labels = /* @__PURE__ */ new Map();
|
|
12
|
+
for (const row of rows) {
|
|
13
|
+
if (!row || typeof row !== "object") continue;
|
|
14
|
+
const { name, label } = row;
|
|
15
|
+
if (typeof name === "string" && typeof label === "string" && label.length > 0) labels.set(name.trim(), label);
|
|
16
|
+
}
|
|
17
|
+
return fieldNamesOfType(data.fields, types).map((name) => ({
|
|
18
|
+
label: labels.get(name) ?? name,
|
|
19
|
+
value: name
|
|
20
|
+
}));
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The `Field` mounted on a text config field (e.g. an action's `toField`) that should be authored
|
|
24
|
+
* by picking from the form's own fields rather than free-typing a name. Options come from the
|
|
25
|
+
* sibling `fields` blocks array, filtered to `types`; the stored value is kept selectable even if
|
|
26
|
+
* it no longer matches any field, so switching to this component never silently drops data.
|
|
27
|
+
*/
|
|
28
|
+
const FieldNameSelect = (props) => {
|
|
29
|
+
const { path, setValue, value } = useField({ path: props.path });
|
|
30
|
+
const label = toStaticLabel(props.field?.label ?? props.label);
|
|
31
|
+
const optionsJson = useFormFields(([fields]) => JSON.stringify(optionsFromData(reduceFieldsToValues(fields, true), props.types)));
|
|
32
|
+
const options = useMemo(() => JSON.parse(optionsJson), [optionsJson]);
|
|
33
|
+
const allOptions = value && !options.some((option) => option.value === value) ? [...options, {
|
|
34
|
+
label: value,
|
|
35
|
+
value
|
|
36
|
+
}] : options;
|
|
37
|
+
const handleChange = (selected) => {
|
|
38
|
+
const chosen = Array.isArray(selected) ? selected[0] : selected;
|
|
39
|
+
setValue(chosen ? chosen.value : "");
|
|
40
|
+
};
|
|
41
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
42
|
+
className: "field-type",
|
|
43
|
+
style: { marginBlockEnd: "1rem" },
|
|
44
|
+
children: [/* @__PURE__ */ jsx(FieldLabel, {
|
|
45
|
+
label,
|
|
46
|
+
path
|
|
47
|
+
}), /* @__PURE__ */ jsx(ReactSelect, {
|
|
48
|
+
options: allOptions,
|
|
49
|
+
value: allOptions.find((option) => option.value === value) ?? void 0,
|
|
50
|
+
isClearable: true,
|
|
51
|
+
onChange: handleChange
|
|
52
|
+
})]
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
//#endregion
|
|
56
|
+
export { FieldNameSelect };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=FieldNameSelect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FieldNameSelect.js","names":[],"sources":["../../src/client/FieldNameSelect.tsx"],"sourcesContent":["'use client'\n\nimport {\n\tFieldLabel,\n\tReactSelect,\n\ttype ReactSelectOption,\n\tuseField,\n\tuseFormFields,\n} from '@payloadcms/ui'\nimport { reduceFieldsToValues } from 'payload/shared'\nimport { useMemo } from 'react'\nimport { fieldNamesOfType } from '../fields/fieldNamesOfType'\nimport type { FieldRow } from './synthesizeClientField'\nimport { toStaticLabel } from './toStaticLabel'\n\n/** Props: standard JSON/text field client props plus the `types` allow-list we pass via clientProps. */\nexport type FieldNameSelectProps = {\n\tpath?: string\n\tfield?: { label?: unknown }\n\tlabel?: unknown\n\t/** Sibling `fields` blocks whose `blockType` is in this list populate the select's options. */\n\ttypes: string[]\n}\n\nconst optionsFromData = (data: Record<string, unknown>, types: string[]): 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\treturn fieldNamesOfType(data.fields, types).map((name) => ({\n\t\tlabel: labels.get(name) ?? name,\n\t\tvalue: name,\n\t}))\n}\n\n/**\n * The `Field` mounted on a text config field (e.g. an action's `toField`) that should be authored\n * by picking from the form's own fields rather than free-typing a name. Options come from the\n * sibling `fields` blocks array, filtered to `types`; the stored value is kept selectable even if\n * it no longer matches any field, so switching to this component never silently drops data.\n */\nexport const FieldNameSelect = (props: FieldNameSelectProps) => {\n\tconst { path, setValue, value } = useField<string>({ path: props.path })\n\tconst label = toStaticLabel(props.field?.label ?? props.label)\n\n\tconst optionsJson = useFormFields(([fields]) =>\n\t\tJSON.stringify(optionsFromData(reduceFieldsToValues(fields, true), props.types))\n\t)\n\tconst options = useMemo(() => JSON.parse(optionsJson) as ReactSelectOption[], [optionsJson])\n\n\tconst allOptions =\n\t\tvalue && !options.some((option) => option.value === value)\n\t\t\t? [...options, { label: value, value }]\n\t\t\t: options\n\n\tconst handleChange = (selected: ReactSelectOption | ReactSelectOption[] | null) => {\n\t\tconst chosen = Array.isArray(selected) ? selected[0] : selected\n\t\tsetValue(chosen ? (chosen.value as string) : '')\n\t}\n\n\treturn (\n\t\t<div className=\"field-type\" style={{ marginBlockEnd: '1rem' }}>\n\t\t\t<FieldLabel label={label} path={path} />\n\t\t\t<ReactSelect\n\t\t\t\toptions={allOptions}\n\t\t\t\tvalue={allOptions.find((option) => option.value === value) ?? undefined}\n\t\t\t\tisClearable\n\t\t\t\tonChange={handleChange}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n"],"mappings":";;;;;;;;AAwBA,MAAM,mBAAmB,MAA+B,UAAyC;CAChG,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;CACA,OAAO,iBAAiB,KAAK,QAAQ,KAAK,EAAE,KAAK,UAAU;EAC1D,OAAO,OAAO,IAAI,IAAI,KAAK;EAC3B,OAAO;CACR,EAAE;AACH;;;;;;;AAQA,MAAa,mBAAmB,UAAgC;CAC/D,MAAM,EAAE,MAAM,UAAU,UAAU,SAAiB,EAAE,MAAM,MAAM,KAAK,CAAC;CACvE,MAAM,QAAQ,cAAc,MAAM,OAAO,SAAS,MAAM,KAAK;CAE7D,MAAM,cAAc,eAAe,CAAC,YACnC,KAAK,UAAU,gBAAgB,qBAAqB,QAAQ,IAAI,GAAG,MAAM,KAAK,CAAC,CAChF;CACA,MAAM,UAAU,cAAc,KAAK,MAAM,WAAW,GAA0B,CAAC,WAAW,CAAC;CAE3F,MAAM,aACL,SAAS,CAAC,QAAQ,MAAM,WAAW,OAAO,UAAU,KAAK,IACtD,CAAC,GAAG,SAAS;EAAE,OAAO;EAAO;CAAM,CAAC,IACpC;CAEJ,MAAM,gBAAgB,aAA6D;EAClF,MAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI,SAAS,KAAK;EACvD,SAAS,SAAU,OAAO,QAAmB,EAAE;CAChD;CAEA,OACC,qBAAC,OAAD;EAAK,WAAU;EAAa,OAAO,EAAE,gBAAgB,OAAO;YAA5D,CACC,oBAAC,YAAD;GAAmB;GAAa;EAAO,CAAA,GACvC,oBAAC,aAAD;GACC,SAAS;GACT,OAAO,WAAW,MAAM,WAAW,OAAO,UAAU,KAAK,KAAK,KAAA;GAC9D,aAAA;GACA,UAAU;EACV,CAAA,CACG;;AAEP"}
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { keys } from "../translations/keys.js";
|
|
3
|
+
import { resolveMessage } from "../validation/message.js";
|
|
4
|
+
import { toStaticLabel } from "./toStaticLabel.js";
|
|
5
|
+
import { useTranslation as useTranslation$1 } from "../translations/useTranslation.js";
|
|
2
6
|
import { ConditionBuilder } from "./ConditionBuilder.js";
|
|
7
|
+
import { stepLabel } from "../flow/stepLabel.js";
|
|
8
|
+
import { assignFieldToStep, fieldHolders, removeFieldFromStep, unassignedFields } from "./flowAuthoring.js";
|
|
3
9
|
import { reduceFieldsToValues } from "payload/shared";
|
|
4
10
|
import { Button, CheckboxInput, Collapsible, FieldLabel, ReactSelect, TextInput, useField, useFormFields } from "@payloadcms/ui";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { useMemo, useRef, useState } from "react";
|
|
5
13
|
import { Popup, PopupList } from "@payloadcms/ui/elements/Popup";
|
|
6
14
|
import { MoreIcon } from "@payloadcms/ui/icons/More";
|
|
7
|
-
import
|
|
8
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
import "./admin.css";
|
|
9
16
|
//#region src/client/FlowBuilder.tsx
|
|
10
|
-
const toStaticLabel = (label) => {
|
|
11
|
-
if (typeof label === "string") return label;
|
|
12
|
-
if (label && typeof label === "object") return label;
|
|
13
|
-
};
|
|
14
17
|
const extractFieldNames = (data) => {
|
|
15
18
|
return (Array.isArray(data.fields) ? data.fields : []).map((row) => typeof row.name === "string" ? row.name.trim() : null).filter((name) => name !== null && name.length > 0);
|
|
16
19
|
};
|
|
17
|
-
const TransitionRow = ({ transition,
|
|
18
|
-
const toOptions = stepIds.map((id) => ({
|
|
19
|
-
label: id,
|
|
20
|
-
value: id
|
|
21
|
-
}));
|
|
20
|
+
const TransitionRow = ({ transition, stepOptions, conditionTypes, isFirst, isLast, onChange, onRemove, onMoveUp, onMoveDown }) => {
|
|
22
21
|
const handleGotoChange = (selected) => {
|
|
23
22
|
const chosen = Array.isArray(selected) ? selected[0] : selected;
|
|
24
23
|
if (chosen) onChange({
|
|
@@ -39,8 +38,8 @@ const TransitionRow = ({ transition, stepIds, conditionTypes, isFirst, isLast, o
|
|
|
39
38
|
/* @__PURE__ */ jsx("div", {
|
|
40
39
|
className: "fb-flow-transition__goto",
|
|
41
40
|
children: /* @__PURE__ */ jsx(ReactSelect, {
|
|
42
|
-
options:
|
|
43
|
-
value:
|
|
41
|
+
options: stepOptions,
|
|
42
|
+
value: stepOptions.find((o) => o.value === transition.to) ?? void 0,
|
|
44
43
|
placeholder: "Select step…",
|
|
45
44
|
isClearable: false,
|
|
46
45
|
onChange: handleGotoChange
|
|
@@ -94,21 +93,19 @@ const TransitionRow = ({ transition, stepIds, conditionTypes, isFirst, isLast, o
|
|
|
94
93
|
]
|
|
95
94
|
});
|
|
96
95
|
};
|
|
97
|
-
const StepCard = ({ step, stepKey, stepIndex, stepCount,
|
|
96
|
+
const StepCard = ({ step, stepKey, stepIndex, stepCount, otherStepOptions, holders, stepLabels, fieldNames, conditionTypes, isExpanded, onToggle, onChange, onToggleField, onRemove, onMoveUp, onMoveDown, onAddAbove, onAddBelow }) => {
|
|
97
|
+
const { t } = useTranslation$1();
|
|
98
98
|
const nextOptions = [{
|
|
99
|
-
label:
|
|
99
|
+
label: t(keys.flowNextTerminal),
|
|
100
100
|
value: ""
|
|
101
|
-
}, ...
|
|
102
|
-
label: id,
|
|
103
|
-
value: id
|
|
104
|
-
}))];
|
|
101
|
+
}, ...otherStepOptions];
|
|
105
102
|
const transitions = step.transitions ?? [];
|
|
106
103
|
const tKeysRef = useRef([]);
|
|
107
104
|
if (tKeysRef.current.length !== transitions.length) tKeysRef.current = transitions.map((_, i) => tKeysRef.current[i] ?? crypto.randomUUID());
|
|
108
105
|
const tKeys = tKeysRef.current;
|
|
109
106
|
const addTransition = () => {
|
|
110
|
-
const firstOtherStep =
|
|
111
|
-
if (
|
|
107
|
+
const firstOtherStep = otherStepOptions[0]?.value;
|
|
108
|
+
if (firstOtherStep === void 0) return;
|
|
112
109
|
tKeysRef.current = [...tKeys, crypto.randomUUID()];
|
|
113
110
|
onChange({
|
|
114
111
|
...step,
|
|
@@ -154,16 +151,17 @@ const StepCard = ({ step, stepKey, stepIndex, stepCount, otherStepIds, fieldName
|
|
|
154
151
|
next
|
|
155
152
|
});
|
|
156
153
|
};
|
|
154
|
+
const title = step.title?.trim() ?? "";
|
|
157
155
|
return /* @__PURE__ */ jsx(Collapsible, {
|
|
158
156
|
header: /* @__PURE__ */ jsxs("div", {
|
|
159
157
|
className: "fb-flow-step__header-content",
|
|
160
|
-
children: [/* @__PURE__ */
|
|
158
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
161
159
|
className: "fb-flow-step__index",
|
|
162
|
-
children:
|
|
163
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
164
|
-
className: "fb-flow-
|
|
165
|
-
children:
|
|
166
|
-
})]
|
|
160
|
+
children: resolveMessage(t(keys.flowStepFallbackTitle), { n: stepIndex + 1 })
|
|
161
|
+
}), title.length > 0 ? /* @__PURE__ */ jsx("span", {
|
|
162
|
+
className: "fb-flow-step__title",
|
|
163
|
+
children: title
|
|
164
|
+
}) : null]
|
|
167
165
|
}),
|
|
168
166
|
actions: /* @__PURE__ */ jsx(Popup, {
|
|
169
167
|
button: /* @__PURE__ */ jsx(MoreIcon, {}),
|
|
@@ -202,16 +200,6 @@ const StepCard = ({ step, stepKey, stepIndex, stepCount, otherStepIds, fieldName
|
|
|
202
200
|
children: /* @__PURE__ */ jsxs("div", {
|
|
203
201
|
className: "fb-flow-step__body",
|
|
204
202
|
children: [
|
|
205
|
-
/* @__PURE__ */ jsx(TextInput, {
|
|
206
|
-
path: `fb-flow-step-${stepKey}-id`,
|
|
207
|
-
label: "Step ID",
|
|
208
|
-
value: step.id,
|
|
209
|
-
placeholder: "e.g. intro",
|
|
210
|
-
onChange: (e) => onChange({
|
|
211
|
-
...step,
|
|
212
|
-
id: e.target.value
|
|
213
|
-
})
|
|
214
|
-
}),
|
|
215
203
|
/* @__PURE__ */ jsx(TextInput, {
|
|
216
204
|
path: `fb-flow-step-${stepKey}-title`,
|
|
217
205
|
label: "Title",
|
|
@@ -229,17 +217,25 @@ const StepCard = ({ step, stepKey, stepIndex, stepCount, otherStepIds, fieldName
|
|
|
229
217
|
children: "No fields defined on the form yet."
|
|
230
218
|
}) : /* @__PURE__ */ jsx("div", {
|
|
231
219
|
className: "fb-flow-step__field-picker",
|
|
232
|
-
children: fieldNames.map((name) =>
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
fields
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
220
|
+
children: fieldNames.map((name) => {
|
|
221
|
+
const holder = holders.get(name);
|
|
222
|
+
const inOtherStep = holder !== void 0 && holder !== stepIndex;
|
|
223
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
224
|
+
className: "fb-flow-step__field-option",
|
|
225
|
+
children: [/* @__PURE__ */ jsx(CheckboxInput, {
|
|
226
|
+
label: name,
|
|
227
|
+
checked: step.fields.includes(name) || inOtherStep,
|
|
228
|
+
readOnly: inOtherStep,
|
|
229
|
+
onToggle: (e) => {
|
|
230
|
+
if (inOtherStep) return;
|
|
231
|
+
onToggleField(name, e.target.checked);
|
|
232
|
+
}
|
|
233
|
+
}), inOtherStep ? /* @__PURE__ */ jsx("span", {
|
|
234
|
+
className: "fb-flow-step__field-hint",
|
|
235
|
+
children: resolveMessage(t(keys.flowFieldInStep), { step: stepLabels[holder] ?? "" })
|
|
236
|
+
}) : null]
|
|
237
|
+
}, name);
|
|
238
|
+
})
|
|
243
239
|
})]
|
|
244
240
|
}),
|
|
245
241
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -266,7 +262,7 @@ const StepCard = ({ step, stepKey, stepIndex, stepCount, otherStepIds, fieldName
|
|
|
266
262
|
if (tKey === void 0) return null;
|
|
267
263
|
return /* @__PURE__ */ jsx(TransitionRow, {
|
|
268
264
|
transition,
|
|
269
|
-
|
|
265
|
+
stepOptions: otherStepOptions,
|
|
270
266
|
conditionTypes,
|
|
271
267
|
isFirst: tIndex === 0,
|
|
272
268
|
isLast: tIndex === transitions.length - 1,
|
|
@@ -293,15 +289,23 @@ const StepCard = ({ step, stepKey, stepIndex, stepCount, otherStepIds, fieldName
|
|
|
293
289
|
};
|
|
294
290
|
const FlowBuilder = (props) => {
|
|
295
291
|
const { setValue, value } = useField();
|
|
292
|
+
const { t } = useTranslation$1();
|
|
296
293
|
const label = toStaticLabel(props.field?.label ?? props.label);
|
|
297
294
|
const [expandedKeys, setExpandedKeys] = useState(/* @__PURE__ */ new Set());
|
|
298
295
|
const fieldNamesJson = useFormFields(([fields]) => JSON.stringify(extractFieldNames(reduceFieldsToValues(fields, true))));
|
|
299
296
|
const fieldNames = useMemo(() => JSON.parse(fieldNamesJson), [fieldNamesJson]);
|
|
300
297
|
const steps = value?.steps ?? [];
|
|
301
|
-
const
|
|
298
|
+
const fallbackTitle = t(keys.flowStepFallbackTitle);
|
|
299
|
+
const stepLabels = steps.map((step, index) => stepLabel(step, index, fallbackTitle));
|
|
300
|
+
const stepOptions = steps.map((step, index) => ({
|
|
301
|
+
label: stepLabels[index] ?? "",
|
|
302
|
+
value: step.id
|
|
303
|
+
})).filter((option) => option.value.length > 0);
|
|
304
|
+
const holders = fieldHolders(steps);
|
|
305
|
+
const unassigned = unassignedFields(fieldNames, steps);
|
|
302
306
|
const keysRef = useRef([]);
|
|
303
307
|
if (keysRef.current.length !== steps.length) keysRef.current = steps.map((_, i) => keysRef.current[i] ?? crypto.randomUUID());
|
|
304
|
-
const
|
|
308
|
+
const stepKeys = keysRef.current;
|
|
305
309
|
const toggleExpanded = (key) => {
|
|
306
310
|
setExpandedKeys((prev) => {
|
|
307
311
|
const next = new Set(prev);
|
|
@@ -321,18 +325,26 @@ const FlowBuilder = (props) => {
|
|
|
321
325
|
id: crypto.randomUUID(),
|
|
322
326
|
fields: []
|
|
323
327
|
});
|
|
324
|
-
const nextKeys = [...
|
|
328
|
+
const nextKeys = [...stepKeys];
|
|
325
329
|
nextKeys.splice(at, 0, key);
|
|
326
330
|
emit(nextSteps, nextKeys);
|
|
327
331
|
setExpandedKeys((prev) => new Set(prev).add(key));
|
|
328
332
|
};
|
|
329
333
|
const addStep = () => insertStep(steps.length);
|
|
330
334
|
const updateStep = (index, next) => {
|
|
331
|
-
emit(steps.map((s, i) => i === index ? next : s),
|
|
335
|
+
emit(steps.map((s, i) => i === index ? next : s), stepKeys);
|
|
336
|
+
};
|
|
337
|
+
const toggleField = (stepIndex, name, checked) => {
|
|
338
|
+
emit(checked ? assignFieldToStep(steps, stepIndex, name) : removeFieldFromStep(steps, stepIndex, name), stepKeys);
|
|
339
|
+
};
|
|
340
|
+
const assignUnassignedField = (name, stepId) => {
|
|
341
|
+
const targetIndex = steps.findIndex((s) => s.id === stepId);
|
|
342
|
+
if (targetIndex === -1) return;
|
|
343
|
+
emit(assignFieldToStep(steps, targetIndex, name), stepKeys);
|
|
332
344
|
};
|
|
333
345
|
const removeStep = (index) => {
|
|
334
|
-
const removedKey =
|
|
335
|
-
emit(steps.filter((_, i) => i !== index),
|
|
346
|
+
const removedKey = stepKeys[index];
|
|
347
|
+
emit(steps.filter((_, i) => i !== index), stepKeys.filter((_, i) => i !== index));
|
|
336
348
|
if (removedKey !== void 0) setExpandedKeys((prev) => {
|
|
337
349
|
const next = new Set(prev);
|
|
338
350
|
next.delete(removedKey);
|
|
@@ -344,7 +356,7 @@ const FlowBuilder = (props) => {
|
|
|
344
356
|
const [movedStep] = nextSteps.splice(from, 1);
|
|
345
357
|
if (movedStep === void 0) return;
|
|
346
358
|
nextSteps.splice(to, 0, movedStep);
|
|
347
|
-
const nextKeys = [...
|
|
359
|
+
const nextKeys = [...stepKeys];
|
|
348
360
|
const [movedKey] = nextKeys.splice(from, 1);
|
|
349
361
|
if (movedKey !== void 0) nextKeys.splice(to, 0, movedKey);
|
|
350
362
|
emit(nextSteps, nextKeys);
|
|
@@ -359,26 +371,33 @@ const FlowBuilder = (props) => {
|
|
|
359
371
|
children: /* @__PURE__ */ jsx("h3", { children: typeof label === "string" ? label : "Flow" })
|
|
360
372
|
})
|
|
361
373
|
}),
|
|
374
|
+
/* @__PURE__ */ jsx("p", {
|
|
375
|
+
className: "fb-flow-builder__description",
|
|
376
|
+
children: t(keys.flowDescription)
|
|
377
|
+
}),
|
|
362
378
|
steps.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
363
379
|
className: "fb-flow-builder__hint",
|
|
364
380
|
children: "No steps defined. Add at least two steps to enable multi-page flow routing."
|
|
365
381
|
}) : /* @__PURE__ */ jsx("div", {
|
|
366
382
|
className: "fb-flow-builder__steps",
|
|
367
383
|
children: steps.map((step, index) => {
|
|
368
|
-
const key =
|
|
384
|
+
const key = stepKeys[index];
|
|
369
385
|
if (key === void 0) return null;
|
|
370
|
-
const
|
|
386
|
+
const otherStepOptions = stepOptions.filter((option) => option.value !== step.id);
|
|
371
387
|
return /* @__PURE__ */ jsx(StepCard, {
|
|
372
388
|
stepKey: key,
|
|
373
389
|
step,
|
|
374
390
|
stepIndex: index,
|
|
375
391
|
stepCount: steps.length,
|
|
376
|
-
|
|
392
|
+
otherStepOptions,
|
|
393
|
+
holders,
|
|
394
|
+
stepLabels,
|
|
377
395
|
fieldNames,
|
|
378
396
|
conditionTypes: props.conditionTypes,
|
|
379
397
|
isExpanded: expandedKeys.has(key),
|
|
380
398
|
onToggle: () => toggleExpanded(key),
|
|
381
399
|
onChange: (next) => updateStep(index, next),
|
|
400
|
+
onToggleField: (name, checked) => toggleField(index, name, checked),
|
|
382
401
|
onRemove: () => removeStep(index),
|
|
383
402
|
onMoveUp: () => moveStep(index, index - 1),
|
|
384
403
|
onMoveDown: () => moveStep(index, index + 1),
|
|
@@ -387,6 +406,30 @@ const FlowBuilder = (props) => {
|
|
|
387
406
|
}, key);
|
|
388
407
|
})
|
|
389
408
|
}),
|
|
409
|
+
steps.length > 0 && unassigned.length > 0 ? /* @__PURE__ */ jsxs("div", {
|
|
410
|
+
className: "fb-flow-unassigned",
|
|
411
|
+
children: [/* @__PURE__ */ jsx(FieldLabel, { label: t(keys.flowUnassigned) }), /* @__PURE__ */ jsx("div", {
|
|
412
|
+
className: "fb-flow-unassigned__rows",
|
|
413
|
+
children: unassigned.map((name) => /* @__PURE__ */ jsxs("div", {
|
|
414
|
+
className: "fb-flow-unassigned__row",
|
|
415
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
416
|
+
className: "fb-flow-unassigned__name",
|
|
417
|
+
children: name
|
|
418
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
419
|
+
className: "fb-flow-unassigned__select",
|
|
420
|
+
children: /* @__PURE__ */ jsx(ReactSelect, {
|
|
421
|
+
options: stepOptions,
|
|
422
|
+
placeholder: t(keys.flowAssignToStep),
|
|
423
|
+
isClearable: false,
|
|
424
|
+
onChange: (selected) => {
|
|
425
|
+
const chosen = Array.isArray(selected) ? selected[0] : selected;
|
|
426
|
+
if (chosen) assignUnassignedField(name, chosen.value);
|
|
427
|
+
}
|
|
428
|
+
})
|
|
429
|
+
})]
|
|
430
|
+
}, name))
|
|
431
|
+
})]
|
|
432
|
+
}) : null,
|
|
390
433
|
/* @__PURE__ */ jsx("div", {
|
|
391
434
|
className: "fb-flow-builder__actions",
|
|
392
435
|
children: /* @__PURE__ */ jsx(Button, {
|