@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.
Files changed (272) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +4 -4
  3. package/dist/actions/body/converters.d.ts +20 -0
  4. package/dist/actions/body/converters.js +57 -0
  5. package/dist/actions/body/converters.js.map +1 -0
  6. package/dist/actions/body/escapeHtml.d.ts +6 -0
  7. package/dist/actions/body/escapeHtml.js +14 -0
  8. package/dist/actions/body/escapeHtml.js.map +1 -0
  9. package/dist/actions/body/serializeBody.d.ts +50 -0
  10. package/dist/actions/body/serializeBody.js +78 -0
  11. package/dist/actions/body/serializeBody.js.map +1 -0
  12. package/dist/actions/body/serializeSlate.js +31 -0
  13. package/dist/actions/body/serializeSlate.js.map +1 -0
  14. package/dist/actions/body/wildcards.d.ts +10 -0
  15. package/dist/actions/body/wildcards.js +33 -0
  16. package/dist/actions/body/wildcards.js.map +1 -0
  17. package/dist/actions/builtin/confirmation.js +40 -15
  18. package/dist/actions/builtin/confirmation.js.map +1 -1
  19. package/dist/actions/builtin/emailTeam.js +19 -12
  20. package/dist/actions/builtin/emailTeam.js.map +1 -1
  21. package/dist/actions/builtin/index.d.ts +13 -1
  22. package/dist/actions/builtin/index.js +18 -7
  23. package/dist/actions/builtin/index.js.map +1 -1
  24. package/dist/actions/builtin/signedWebhook.js +6 -2
  25. package/dist/actions/builtin/signedWebhook.js.map +1 -1
  26. package/dist/actions/defineAction.d.ts +2 -1
  27. package/dist/actions/defineAction.js.map +1 -1
  28. package/dist/actions/dispatch.js +2 -1
  29. package/dist/actions/dispatch.js.map +1 -1
  30. package/dist/actions/runActions.js +10 -1
  31. package/dist/actions/runActions.js.map +1 -1
  32. package/dist/actions/task.js +7 -5
  33. package/dist/actions/task.js.map +1 -1
  34. package/dist/aggregation/resolveResultsRequest.d.ts +22 -6
  35. package/dist/aggregation/resolveResultsRequest.js +18 -7
  36. package/dist/aggregation/resolveResultsRequest.js.map +1 -1
  37. package/dist/calc/normalizeCalc.js +5 -17
  38. package/dist/calc/normalizeCalc.js.map +1 -1
  39. package/dist/calc/types.d.ts +8 -2
  40. package/dist/calc/types.js +22 -0
  41. package/dist/calc/types.js.map +1 -0
  42. package/dist/client/ByteSizeField.d.ts +21 -0
  43. package/dist/client/ByteSizeField.js +56 -0
  44. package/dist/client/ByteSizeField.js.map +1 -0
  45. package/dist/client/ConditionBuilder.js +2 -1
  46. package/dist/client/ConditionBuilder.js.map +1 -1
  47. package/dist/client/FieldNameSelect.d.ts +20 -0
  48. package/dist/client/FieldNameSelect.js +58 -0
  49. package/dist/client/FieldNameSelect.js.map +1 -0
  50. package/dist/client/FlowBuilder.d.ts +0 -1
  51. package/dist/client/FlowBuilder.js +102 -59
  52. package/dist/client/FlowBuilder.js.map +1 -1
  53. package/dist/client/FormConditionField.js +1 -4
  54. package/dist/client/FormConditionField.js.map +1 -1
  55. package/dist/client/admin.css +234 -0
  56. package/dist/client/flowAuthoring.js +35 -0
  57. package/dist/client/flowAuthoring.js.map +1 -0
  58. package/dist/client/synthesizeClientField.js +6 -2
  59. package/dist/client/synthesizeClientField.js.map +1 -1
  60. package/dist/client/toStaticLabel.js +13 -0
  61. package/dist/client/toStaticLabel.js.map +1 -0
  62. package/dist/collections/formSubmissions.js +49 -9
  63. package/dist/collections/formSubmissions.js.map +1 -1
  64. package/dist/collections/forms.js +251 -88
  65. package/dist/collections/forms.js.map +1 -1
  66. package/dist/conditions/conditionType.js +9 -2
  67. package/dist/conditions/conditionType.js.map +1 -1
  68. package/dist/conditions/normalizeConditions.js +2 -1
  69. package/dist/conditions/normalizeConditions.js.map +1 -1
  70. package/dist/consent/builtin/index.d.ts +11 -1
  71. package/dist/consent/builtin/index.js +15 -5
  72. package/dist/consent/builtin/index.js.map +1 -1
  73. package/dist/consent/builtin/static.js +7 -3
  74. package/dist/consent/builtin/static.js.map +1 -1
  75. package/dist/exports/client.d.ts +3 -1
  76. package/dist/exports/client.js +3 -1
  77. package/dist/exports/react.d.ts +15 -6
  78. package/dist/exports/react.js +10 -2
  79. package/dist/exports/rsc.d.ts +6 -1
  80. package/dist/exports/rsc.js +6 -1
  81. package/dist/exports/types.d.ts +4 -4
  82. package/dist/fields/buildFieldBlocks.js +20 -15
  83. package/dist/fields/buildFieldBlocks.js.map +1 -1
  84. package/dist/fields/builtin/calculation.js +115 -2
  85. package/dist/fields/builtin/calculation.js.map +1 -1
  86. package/dist/fields/builtin/consent.js +8 -4
  87. package/dist/fields/builtin/consent.js.map +1 -1
  88. package/dist/fields/builtin/date.js +29 -0
  89. package/dist/fields/builtin/date.js.map +1 -0
  90. package/dist/fields/builtin/file.d.ts +15 -0
  91. package/dist/fields/builtin/file.js +87 -7
  92. package/dist/fields/builtin/file.js.map +1 -1
  93. package/dist/fields/builtin/index.d.ts +24 -0
  94. package/dist/fields/builtin/index.js +29 -8
  95. package/dist/fields/builtin/index.js.map +1 -1
  96. package/dist/fields/builtin/message.js +26 -0
  97. package/dist/fields/builtin/message.js.map +1 -0
  98. package/dist/fields/builtin/repeater.js +7 -3
  99. package/dist/fields/builtin/repeater.js.map +1 -1
  100. package/dist/fields/builtin/select.js +7 -3
  101. package/dist/fields/builtin/select.js.map +1 -1
  102. package/dist/fields/fieldNamesOfType.js +25 -0
  103. package/dist/fields/fieldNamesOfType.js.map +1 -0
  104. package/dist/fields/localizedIf.d.ts +13 -0
  105. package/dist/fields/localizedIf.js +12 -0
  106. package/dist/fields/localizedIf.js.map +1 -0
  107. package/dist/fields/sharedConfig.js +82 -57
  108. package/dist/fields/sharedConfig.js.map +1 -1
  109. package/dist/fields/types.d.ts +7 -2
  110. package/dist/flow/normalizeFlow.js +7 -1
  111. package/dist/flow/normalizeFlow.js.map +1 -1
  112. package/dist/flow/stepLabel.js +15 -0
  113. package/dist/flow/stepLabel.js.map +1 -0
  114. package/dist/form/pollState.d.ts +22 -0
  115. package/dist/form/pollState.js +17 -0
  116. package/dist/form/pollState.js.map +1 -0
  117. package/dist/form/toFormDocument.d.ts +60 -0
  118. package/dist/form/toFormDocument.js +39 -0
  119. package/dist/form/toFormDocument.js.map +1 -0
  120. package/dist/form/types.d.ts +48 -0
  121. package/dist/index.d.ts +81 -13
  122. package/dist/index.js +36 -15
  123. package/dist/index.js.map +1 -1
  124. package/dist/plugin/registerCollections.js +17 -37
  125. package/dist/plugin/registerCollections.js.map +1 -1
  126. package/dist/plugin/uploadsCollection.d.ts +11 -0
  127. package/dist/plugin/uploadsCollection.js +44 -0
  128. package/dist/plugin/uploadsCollection.js.map +1 -0
  129. package/dist/poll/applyPollOptions.js +17 -0
  130. package/dist/poll/applyPollOptions.js.map +1 -0
  131. package/dist/poll/buildPollOptionSourceFields.js +53 -0
  132. package/dist/poll/buildPollOptionSourceFields.js.map +1 -0
  133. package/dist/poll/definePollOptionSource.d.ts +35 -0
  134. package/dist/poll/definePollOptionSource.js +6 -0
  135. package/dist/poll/definePollOptionSource.js.map +1 -0
  136. package/dist/poll/registry.d.ts +16 -0
  137. package/dist/poll/registry.js +16 -0
  138. package/dist/poll/registry.js.map +1 -0
  139. package/dist/poll/resolvePollOptions.d.ts +28 -0
  140. package/dist/poll/resolvePollOptions.js +64 -0
  141. package/dist/poll/resolvePollOptions.js.map +1 -0
  142. package/dist/poll/resolvePollOutcome.d.ts +21 -0
  143. package/dist/poll/resolvePollOutcome.js +36 -0
  144. package/dist/poll/resolvePollOutcome.js.map +1 -0
  145. package/dist/prefill/valuesFromSearchParams.js +1 -1
  146. package/dist/prefill/valuesFromSearchParams.js.map +1 -1
  147. package/dist/presentations/types.d.ts +3 -2
  148. package/dist/react/Form.d.ts +13 -31
  149. package/dist/react/Form.js +98 -85
  150. package/dist/react/Form.js.map +1 -1
  151. package/dist/react/FormContext.d.ts +35 -1
  152. package/dist/react/FormContext.js.map +1 -1
  153. package/dist/react/FormControls.d.ts +55 -0
  154. package/dist/react/FormControls.js +56 -0
  155. package/dist/react/FormControls.js.map +1 -0
  156. package/dist/react/FormResults.d.ts +6 -3
  157. package/dist/react/FormResults.js +37 -29
  158. package/dist/react/FormResults.js.map +1 -1
  159. package/dist/react/FormSteps.d.ts +17 -0
  160. package/dist/react/FormSteps.js +35 -0
  161. package/dist/react/FormSteps.js.map +1 -0
  162. package/dist/react/Poll.d.ts +17 -6
  163. package/dist/react/Poll.js +66 -18
  164. package/dist/react/Poll.js.map +1 -1
  165. package/dist/react/captcha/HcaptchaCaptcha.d.ts +28 -0
  166. package/dist/react/captcha/HcaptchaCaptcha.js +53 -0
  167. package/dist/react/captcha/HcaptchaCaptcha.js.map +1 -0
  168. package/dist/react/captcha/RecaptchaCaptcha.d.ts +41 -0
  169. package/dist/react/captcha/RecaptchaCaptcha.js +93 -0
  170. package/dist/react/captcha/RecaptchaCaptcha.js.map +1 -0
  171. package/dist/react/captcha/TurnstileCaptcha.d.ts +28 -0
  172. package/dist/react/captcha/TurnstileCaptcha.js +53 -0
  173. package/dist/react/captcha/TurnstileCaptcha.js.map +1 -0
  174. package/dist/react/captcha/types.d.ts +8 -0
  175. package/dist/react/captcha/useCaptchaScript.js +45 -0
  176. package/dist/react/captcha/useCaptchaScript.js.map +1 -0
  177. package/dist/react/captcha/vendors.js +8 -0
  178. package/dist/react/captcha/vendors.js.map +1 -0
  179. package/dist/react/fetchResults.d.ts +1 -1
  180. package/dist/react/fetchResults.js +1 -1
  181. package/dist/react/fetchResults.js.map +1 -1
  182. package/dist/react/primitives/Input.d.ts +1 -1
  183. package/dist/react/primitives/Input.js.map +1 -1
  184. package/dist/react/renderers/calculation.js +1 -1
  185. package/dist/react/renderers/checkbox.js +1 -1
  186. package/dist/react/renderers/consent.js +1 -1
  187. package/dist/react/renderers/date.js +37 -0
  188. package/dist/react/renderers/date.js.map +1 -0
  189. package/dist/react/renderers/email.js +1 -1
  190. package/dist/react/renderers/file.js +25 -8
  191. package/dist/react/renderers/file.js.map +1 -1
  192. package/dist/react/renderers/index.js +4 -0
  193. package/dist/react/renderers/index.js.map +1 -1
  194. package/dist/react/renderers/message.js +35 -0
  195. package/dist/react/renderers/message.js.map +1 -0
  196. package/dist/react/renderers/number.js +1 -1
  197. package/dist/react/renderers/repeater.js +1 -1
  198. package/dist/react/renderers/select.js +1 -1
  199. package/dist/react/renderers/text.js +1 -1
  200. package/dist/react/renderers/textarea.js +1 -1
  201. package/dist/react/state.d.ts +25 -1
  202. package/dist/react/state.js +14 -1
  203. package/dist/react/state.js.map +1 -1
  204. package/dist/react/uploadFile.d.ts +2 -2
  205. package/dist/react/uploadFile.js +1 -1
  206. package/dist/react/uploadFile.js.map +1 -1
  207. package/dist/spam/captcha.d.ts +3 -2
  208. package/dist/spam/captcha.js +3 -2
  209. package/dist/spam/captcha.js.map +1 -1
  210. package/dist/spam/clientIp.js +9 -0
  211. package/dist/spam/clientIp.js.map +1 -0
  212. package/dist/spam/identify.js +3 -6
  213. package/dist/spam/identify.js.map +1 -1
  214. package/dist/spam/providers/hcaptcha.d.ts +17 -0
  215. package/dist/spam/providers/hcaptcha.js +18 -0
  216. package/dist/spam/providers/hcaptcha.js.map +1 -0
  217. package/dist/spam/providers/recaptcha.d.ts +23 -0
  218. package/dist/spam/providers/recaptcha.js +23 -0
  219. package/dist/spam/providers/recaptcha.js.map +1 -0
  220. package/dist/spam/providers/siteverify.js +34 -0
  221. package/dist/spam/providers/siteverify.js.map +1 -0
  222. package/dist/spam/providers/turnstile.d.ts +17 -0
  223. package/dist/spam/providers/turnstile.js +18 -0
  224. package/dist/spam/providers/turnstile.js.map +1 -0
  225. package/dist/spam/spamGuard.js +2 -5
  226. package/dist/spam/spamGuard.js.map +1 -1
  227. package/dist/spam/types.d.ts +2 -2
  228. package/dist/submissions/runSubmission.js +70 -33
  229. package/dist/submissions/runSubmission.js.map +1 -1
  230. package/dist/submissions/validateSubmission.js +41 -7
  231. package/dist/submissions/validateSubmission.js.map +1 -1
  232. package/dist/submissions/votedCookie.d.ts +13 -0
  233. package/dist/submissions/votedCookie.js +27 -0
  234. package/dist/submissions/votedCookie.js.map +1 -0
  235. package/dist/translations/de.js +219 -0
  236. package/dist/translations/de.js.map +1 -0
  237. package/dist/translations/en.js +71 -10
  238. package/dist/translations/en.js.map +1 -1
  239. package/dist/translations/index.d.ts +2 -1
  240. package/dist/translations/index.js +6 -2
  241. package/dist/translations/index.js.map +1 -1
  242. package/dist/translations/keys.d.ts +69 -8
  243. package/dist/translations/keys.js +70 -9
  244. package/dist/translations/keys.js.map +1 -1
  245. package/dist/uploads/formatBytes.d.ts +9 -0
  246. package/dist/uploads/formatBytes.js +25 -0
  247. package/dist/uploads/formatBytes.js.map +1 -0
  248. package/dist/uploads/resolveFileRef.js +1 -1
  249. package/dist/uploads/resolveFileRef.js.map +1 -1
  250. package/dist/uploads/types.d.ts +1 -2
  251. package/dist/validation/builtin/index.d.ts +14 -0
  252. package/dist/validation/builtin/index.js +12 -1
  253. package/dist/validation/builtin/index.js.map +1 -1
  254. package/dist/validation/builtin/maxDate.js +23 -0
  255. package/dist/validation/builtin/maxDate.js.map +1 -0
  256. package/dist/validation/builtin/minDate.js +23 -0
  257. package/dist/validation/builtin/minDate.js.map +1 -0
  258. package/dist/validation/builtin/validateDateBound.js +18 -0
  259. package/dist/validation/builtin/validateDateBound.js.map +1 -0
  260. package/dist/validation/validateUrl.js +22 -0
  261. package/dist/validation/validateUrl.js.map +1 -0
  262. package/package.json +4 -2
  263. package/styles/form-builder.css +10 -190
  264. package/dist/collections/uploads.d.ts +0 -30
  265. package/dist/collections/uploads.js +0 -55
  266. package/dist/collections/uploads.js.map +0 -1
  267. package/dist/presentations/registry.d.ts +0 -15
  268. package/dist/presentations/registry.js +0 -15
  269. package/dist/presentations/registry.js.map +0 -1
  270. package/dist/react/toFormDocument.d.ts +0 -24
  271. package/dist/react/toFormDocument.js +0 -22
  272. 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":"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"}
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,5 +1,4 @@
1
1
  import { ConditionFieldType } from "../conditions/fieldTypes.js";
2
-
3
2
  //#region src/client/FlowBuilder.d.ts
4
3
  type FlowBuilderProps = {
5
4
  path?: string;
@@ -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 { useMemo, useRef, useState } from "react";
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, stepIds, conditionTypes, isFirst, isLast, onChange, onRemove, onMoveUp, onMoveDown }) => {
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: toOptions,
43
- value: toOptions.find((o) => o.value === transition.to) ?? void 0,
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, otherStepIds, fieldNames, conditionTypes, isExpanded, onToggle, onChange, onRemove, onMoveUp, onMoveDown, onAddAbove, onAddBelow }) => {
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: "— terminal —",
99
+ label: t(keys.flowNextTerminal),
100
100
  value: ""
101
- }, ...otherStepIds.map((id) => ({
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 = otherStepIds[0];
111
- if (!firstOtherStep) return;
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__ */ jsxs("span", {
158
+ children: [/* @__PURE__ */ jsx("span", {
161
159
  className: "fb-flow-step__index",
162
- children: ["Step ", stepIndex + 1]
163
- }), /* @__PURE__ */ jsx("span", {
164
- className: "fb-flow-step__id-preview",
165
- children: step.id.length > 0 ? step.id : /* @__PURE__ */ jsx("em", { children: "no id" })
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) => /* @__PURE__ */ jsx(CheckboxInput, {
233
- label: name,
234
- checked: step.fields.includes(name),
235
- onToggle: (e) => {
236
- const fields = e.target.checked ? [...step.fields, name] : step.fields.filter((f) => f !== name);
237
- onChange({
238
- ...step,
239
- fields
240
- });
241
- }
242
- }, name))
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
- stepIds: otherStepIds,
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 allStepIds = steps.map((s) => s.id).filter((id) => id.length > 0);
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 keys = keysRef.current;
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 = [...keys];
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), keys);
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 = keys[index];
335
- emit(steps.filter((_, i) => i !== index), keys.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 = [...keys];
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 = keys[index];
384
+ const key = stepKeys[index];
369
385
  if (key === void 0) return null;
370
- const otherStepIds = allStepIds.filter((id) => id !== step.id);
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
- otherStepIds,
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, {