@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
@@ -3,12 +3,86 @@ import { labelFor } from "../../translations/server.js";
3
3
  import { defineFormField } from "../defineFormField.js";
4
4
  //#region src/fields/builtin/file.ts
5
5
  const isFileRef = (value) => typeof value === "object" && value !== null && "filename" in value;
6
+ const MAX_SIZE_REF = "@10x-media/form-builder/client#ByteSizeField";
7
+ /**
8
+ * Curated MIME type choices for the file field's `mimeTypes` select. Values are raw MIME strings or
9
+ * trailing-`*` prefix wildcards, exactly the patterns `resolveFileRef` matches server-side; `type/*`
10
+ * values double as the file input's `accept` attribute (browsers ignore other prefix patterns, which
11
+ * is fine since the server stays authoritative). Every value must be 63 bytes or less: Payload's
12
+ * db-postgres adapter stores select options as a pg enum, and pg enum labels cap at 63 bytes.
13
+ */
14
+ const fileMimeTypeOptions = [
15
+ {
16
+ label: "Any image",
17
+ value: "image/*"
18
+ },
19
+ {
20
+ label: "PNG image",
21
+ value: "image/png"
22
+ },
23
+ {
24
+ label: "JPEG image",
25
+ value: "image/jpeg"
26
+ },
27
+ {
28
+ label: "WebP image",
29
+ value: "image/webp"
30
+ },
31
+ {
32
+ label: "GIF image",
33
+ value: "image/gif"
34
+ },
35
+ {
36
+ label: "SVG image",
37
+ value: "image/svg+xml"
38
+ },
39
+ {
40
+ label: "PDF",
41
+ value: "application/pdf"
42
+ },
43
+ {
44
+ label: "Plain text",
45
+ value: "text/plain"
46
+ },
47
+ {
48
+ label: "CSV",
49
+ value: "text/csv"
50
+ },
51
+ {
52
+ label: "ZIP archive",
53
+ value: "application/zip"
54
+ },
55
+ {
56
+ label: "Word document (.doc)",
57
+ value: "application/msword"
58
+ },
59
+ {
60
+ label: "Excel spreadsheet (.xls)",
61
+ value: "application/vnd.ms-excel"
62
+ },
63
+ {
64
+ label: "Office documents (docx, xlsx, pptx)",
65
+ value: "application/vnd.openxmlformats-officedocument.*"
66
+ },
67
+ {
68
+ label: "Any audio",
69
+ value: "audio/*"
70
+ },
71
+ {
72
+ label: "Any video",
73
+ value: "video/*"
74
+ }
75
+ ];
6
76
  /**
7
77
  * The `file` field type. Its stored value is a server-captured {@link FileRef}; the client only ever submits
8
78
  * the upload id, and `runSubmission` re-reads filename/mimeType/filesize from the upload doc at the trust
9
79
  * boundary (see `captureFileRef`). The intrinsic validator is intentionally a no-op: MIME/size/existence are
10
- * enforced authoritatively server-side against the stored doc, never against client metadata. `relationTo`,
11
- * `mimeTypes`, and `maxSize` persist on the block so the capture can enforce them.
80
+ * enforced authoritatively server-side against the stored doc, never against client metadata. `mimeTypes`
81
+ * and `maxSize` persist on the block so the capture can enforce them. `uploadsCollection` is not
82
+ * author-editable: a forms hook stamps the plugin-configured uploads collection slug onto it on every save
83
+ * so the client renderer knows where to POST; the server never trusts it at submit time (plugin config
84
+ * stays the source of truth). Named `uploadsCollection` because `collection` is a mongoose-reserved
85
+ * schema pathname.
12
86
  */
13
87
  const fileField = defineFormField({
14
88
  type: "file",
@@ -16,25 +90,31 @@ const fileField = defineFormField({
16
90
  value: "file",
17
91
  config: [
18
92
  {
19
- name: "relationTo",
93
+ name: "uploadsCollection",
20
94
  type: "text",
21
- label: labelFor(keys.fileConfigRelationTo)
95
+ admin: { hidden: true }
22
96
  },
23
97
  {
24
98
  name: "mimeTypes",
25
- type: "text",
99
+ type: "select",
26
100
  hasMany: true,
101
+ options: fileMimeTypeOptions,
27
102
  label: labelFor(keys.fileConfigMimeTypes)
28
103
  },
29
104
  {
30
105
  name: "maxSize",
31
106
  type: "number",
32
- label: labelFor(keys.fileConfigMaxSize)
107
+ min: 0,
108
+ label: labelFor(keys.fileConfigMaxSize),
109
+ admin: {
110
+ description: labelFor(keys.fileConfigMaxSizeDescription),
111
+ components: { Field: MAX_SIZE_REF }
112
+ }
33
113
  }
34
114
  ],
35
115
  format: ({ value }) => isFileRef(value) ? value.filename : ""
36
116
  });
37
117
  //#endregion
38
- export { fileField };
118
+ export { fileField, fileMimeTypeOptions };
39
119
 
40
120
  //# sourceMappingURL=file.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"file.js","names":[],"sources":["../../../src/fields/builtin/file.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport type { FileRef } from '../../uploads/types'\nimport { defineFormField } from '../defineFormField'\n\ntype FileConfig = {\n\trelationTo?: string\n\tmimeTypes?: string[]\n\tmaxSize?: number\n}\n\nconst isFileRef = (value: unknown): value is FileRef =>\n\ttypeof value === 'object' && value !== null && 'filename' in value\n\n/**\n * The `file` field type. Its stored value is a server-captured {@link FileRef}; the client only ever submits\n * the upload id, and `runSubmission` re-reads filename/mimeType/filesize from the upload doc at the trust\n * boundary (see `captureFileRef`). The intrinsic validator is intentionally a no-op: MIME/size/existence are\n * enforced authoritatively server-side against the stored doc, never against client metadata. `relationTo`,\n * `mimeTypes`, and `maxSize` persist on the block so the capture can enforce them.\n */\nexport const fileField = defineFormField<'file', FileConfig>({\n\ttype: 'file',\n\tlabel: keys.fieldTypeFile,\n\tvalue: 'file',\n\tconfig: [\n\t\t{ name: 'relationTo', type: 'text', label: labelFor(keys.fileConfigRelationTo) },\n\t\t{ name: 'mimeTypes', type: 'text', hasMany: true, label: labelFor(keys.fileConfigMimeTypes) },\n\t\t{ name: 'maxSize', type: 'number', label: labelFor(keys.fileConfigMaxSize) },\n\t],\n\tformat: ({ value }) => (isFileRef(value) ? value.filename : ''),\n})\n"],"mappings":";;;;AAWA,MAAM,aAAa,UAClB,OAAO,UAAU,YAAY,UAAU,QAAQ,cAAc;;;;;;;;AAS9D,MAAa,YAAY,gBAAoC;CAC5D,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,QAAQ;EACP;GAAE,MAAM;GAAc,MAAM;GAAQ,OAAO,SAAS,KAAK,oBAAoB;EAAE;EAC/E;GAAE,MAAM;GAAa,MAAM;GAAQ,SAAS;GAAM,OAAO,SAAS,KAAK,mBAAmB;EAAE;EAC5F;GAAE,MAAM;GAAW,MAAM;GAAU,OAAO,SAAS,KAAK,iBAAiB;EAAE;CAC5E;CACA,SAAS,EAAE,YAAa,UAAU,KAAK,IAAI,MAAM,WAAW;AAC7D,CAAC"}
1
+ {"version":3,"file":"file.js","names":[],"sources":["../../../src/fields/builtin/file.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport type { FileRef } from '../../uploads/types'\nimport { defineFormField } from '../defineFormField'\n\ntype FileConfig = {\n\tuploadsCollection?: string\n\tmimeTypes?: string[]\n\tmaxSize?: number\n}\n\nconst isFileRef = (value: unknown): value is FileRef =>\n\ttypeof value === 'object' && value !== null && 'filename' in value\n\nconst MAX_SIZE_REF = '@10x-media/form-builder/client#ByteSizeField'\n\n/**\n * Curated MIME type choices for the file field's `mimeTypes` select. Values are raw MIME strings or\n * trailing-`*` prefix wildcards, exactly the patterns `resolveFileRef` matches server-side; `type/*`\n * values double as the file input's `accept` attribute (browsers ignore other prefix patterns, which\n * is fine since the server stays authoritative). Every value must be 63 bytes or less: Payload's\n * db-postgres adapter stores select options as a pg enum, and pg enum labels cap at 63 bytes.\n */\nexport const fileMimeTypeOptions = [\n\t{ label: 'Any image', value: 'image/*' },\n\t{ label: 'PNG image', value: 'image/png' },\n\t{ label: 'JPEG image', value: 'image/jpeg' },\n\t{ label: 'WebP image', value: 'image/webp' },\n\t{ label: 'GIF image', value: 'image/gif' },\n\t{ label: 'SVG image', value: 'image/svg+xml' },\n\t{ label: 'PDF', value: 'application/pdf' },\n\t{ label: 'Plain text', value: 'text/plain' },\n\t{ label: 'CSV', value: 'text/csv' },\n\t{ label: 'ZIP archive', value: 'application/zip' },\n\t{ label: 'Word document (.doc)', value: 'application/msword' },\n\t{ label: 'Excel spreadsheet (.xls)', value: 'application/vnd.ms-excel' },\n\t{\n\t\tlabel: 'Office documents (docx, xlsx, pptx)',\n\t\tvalue: 'application/vnd.openxmlformats-officedocument.*',\n\t},\n\t{ label: 'Any audio', value: 'audio/*' },\n\t{ label: 'Any video', value: 'video/*' },\n]\n\n/**\n * The `file` field type. Its stored value is a server-captured {@link FileRef}; the client only ever submits\n * the upload id, and `runSubmission` re-reads filename/mimeType/filesize from the upload doc at the trust\n * boundary (see `captureFileRef`). The intrinsic validator is intentionally a no-op: MIME/size/existence are\n * enforced authoritatively server-side against the stored doc, never against client metadata. `mimeTypes`\n * and `maxSize` persist on the block so the capture can enforce them. `uploadsCollection` is not\n * author-editable: a forms hook stamps the plugin-configured uploads collection slug onto it on every save\n * so the client renderer knows where to POST; the server never trusts it at submit time (plugin config\n * stays the source of truth). Named `uploadsCollection` because `collection` is a mongoose-reserved\n * schema pathname.\n */\nexport const fileField = defineFormField<'file', FileConfig>({\n\ttype: 'file',\n\tlabel: keys.fieldTypeFile,\n\tvalue: 'file',\n\tconfig: [\n\t\t{ name: 'uploadsCollection', type: 'text', admin: { hidden: true } },\n\t\t{\n\t\t\tname: 'mimeTypes',\n\t\t\ttype: 'select',\n\t\t\thasMany: true,\n\t\t\toptions: fileMimeTypeOptions,\n\t\t\tlabel: labelFor(keys.fileConfigMimeTypes),\n\t\t},\n\t\t{\n\t\t\tname: 'maxSize',\n\t\t\ttype: 'number',\n\t\t\tmin: 0,\n\t\t\tlabel: labelFor(keys.fileConfigMaxSize),\n\t\t\tadmin: {\n\t\t\t\tdescription: labelFor(keys.fileConfigMaxSizeDescription),\n\t\t\t\tcomponents: { Field: MAX_SIZE_REF },\n\t\t\t},\n\t\t},\n\t],\n\tformat: ({ value }) => (isFileRef(value) ? value.filename : ''),\n})\n"],"mappings":";;;;AAWA,MAAM,aAAa,UAClB,OAAO,UAAU,YAAY,UAAU,QAAQ,cAAc;AAE9D,MAAM,eAAe;;;;;;;;AASrB,MAAa,sBAAsB;CAClC;EAAE,OAAO;EAAa,OAAO;CAAU;CACvC;EAAE,OAAO;EAAa,OAAO;CAAY;CACzC;EAAE,OAAO;EAAc,OAAO;CAAa;CAC3C;EAAE,OAAO;EAAc,OAAO;CAAa;CAC3C;EAAE,OAAO;EAAa,OAAO;CAAY;CACzC;EAAE,OAAO;EAAa,OAAO;CAAgB;CAC7C;EAAE,OAAO;EAAO,OAAO;CAAkB;CACzC;EAAE,OAAO;EAAc,OAAO;CAAa;CAC3C;EAAE,OAAO;EAAO,OAAO;CAAW;CAClC;EAAE,OAAO;EAAe,OAAO;CAAkB;CACjD;EAAE,OAAO;EAAwB,OAAO;CAAqB;CAC7D;EAAE,OAAO;EAA4B,OAAO;CAA2B;CACvE;EACC,OAAO;EACP,OAAO;CACR;CACA;EAAE,OAAO;EAAa,OAAO;CAAU;CACvC;EAAE,OAAO;EAAa,OAAO;CAAU;AACxC;;;;;;;;;;;;AAaA,MAAa,YAAY,gBAAoC;CAC5D,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,QAAQ;EACP;GAAE,MAAM;GAAqB,MAAM;GAAQ,OAAO,EAAE,QAAQ,KAAK;EAAE;EACnE;GACC,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;GACT,OAAO,SAAS,KAAK,mBAAmB;EACzC;EACA;GACC,MAAM;GACN,MAAM;GACN,KAAK;GACL,OAAO,SAAS,KAAK,iBAAiB;GACtC,OAAO;IACN,aAAa,SAAS,KAAK,4BAA4B;IACvD,YAAY,EAAE,OAAO,aAAa;GACnC;EACD;CACD;CACA,SAAS,EAAE,YAAa,UAAU,KAAK,IAAI,MAAM,WAAW;AAC7D,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { AnyFormFieldDefinition } from "../types.js";
2
+
3
+ //#region src/fields/builtin/index.d.ts
4
+ /**
5
+ * Built-in field definitions with content-bearing config fields (select option labels, consent
6
+ * statement, repeater add label) carrying `localized: true` when `localize` is true. Field types
7
+ * without content config are shared static definitions. Definitions are authored with precise
8
+ * value/config generics; the registry stores them erased (config is re-narrowed per matched type
9
+ * at execution), hence one cast per built-in, no `any`.
10
+ */
11
+ declare const buildDefaultFieldDefinitions: (localize: boolean) => AnyFormFieldDefinition[];
12
+ declare const defaultFieldDefinitions: AnyFormFieldDefinition[];
13
+ /**
14
+ * `defaultFieldDefinitions` indexed by `type`, so a single built-in can be spread and tweaked
15
+ * without hand-rolling a `.find()`, e.g.
16
+ * `fields: { text: { ...defaultFieldDefinitionsByType.text, validate: myValidate } }`. The
17
+ * `fields` plugin option replaces a built-in of the same `type`; `false` removes it, `true` keeps it.
18
+ * Prebuilt with content localization on; with `localizeContent: false`, derive overrides from
19
+ * `buildDefaultFieldDefinitions(false)` instead so the spread does not reintroduce `localized`.
20
+ */
21
+ declare const defaultFieldDefinitionsByType: Record<string, AnyFormFieldDefinition>;
22
+ //#endregion
23
+ export { buildDefaultFieldDefinitions, defaultFieldDefinitions, defaultFieldDefinitionsByType };
24
+ //# sourceMappingURL=index.d.ts.map
@@ -1,27 +1,48 @@
1
1
  import { calculationField } from "./calculation.js";
2
2
  import { checkboxField } from "./checkbox.js";
3
- import { consentField } from "./consent.js";
3
+ import { buildConsentField } from "./consent.js";
4
+ import { dateField } from "./date.js";
4
5
  import { emailField } from "./email.js";
5
6
  import { fileField } from "./file.js";
7
+ import { buildMessageField } from "./message.js";
6
8
  import { numberField } from "./number.js";
7
- import { repeaterField } from "./repeater.js";
8
- import { selectField } from "./select.js";
9
+ import { buildRepeaterField } from "./repeater.js";
10
+ import { buildSelectField } from "./select.js";
9
11
  import { textField } from "./text.js";
10
12
  import { textareaField } from "./textarea.js";
11
13
  //#region src/fields/builtin/index.ts
12
- const defaultFieldDefinitions = [
14
+ /**
15
+ * Built-in field definitions with content-bearing config fields (select option labels, consent
16
+ * statement, repeater add label) carrying `localized: true` when `localize` is true. Field types
17
+ * without content config are shared static definitions. Definitions are authored with precise
18
+ * value/config generics; the registry stores them erased (config is re-narrowed per matched type
19
+ * at execution), hence one cast per built-in, no `any`.
20
+ */
21
+ const buildDefaultFieldDefinitions = (localize) => [
13
22
  textField,
14
23
  textareaField,
15
24
  emailField,
16
25
  numberField,
17
- selectField,
26
+ buildSelectField(localize),
18
27
  checkboxField,
19
28
  calculationField,
20
- consentField,
29
+ buildConsentField(localize),
21
30
  fileField,
22
- repeaterField
31
+ buildRepeaterField(localize),
32
+ dateField,
33
+ buildMessageField(localize)
23
34
  ];
35
+ const defaultFieldDefinitions = buildDefaultFieldDefinitions(true);
36
+ /**
37
+ * `defaultFieldDefinitions` indexed by `type`, so a single built-in can be spread and tweaked
38
+ * without hand-rolling a `.find()`, e.g.
39
+ * `fields: { text: { ...defaultFieldDefinitionsByType.text, validate: myValidate } }`. The
40
+ * `fields` plugin option replaces a built-in of the same `type`; `false` removes it, `true` keeps it.
41
+ * Prebuilt with content localization on; with `localizeContent: false`, derive overrides from
42
+ * `buildDefaultFieldDefinitions(false)` instead so the spread does not reintroduce `localized`.
43
+ */
44
+ const defaultFieldDefinitionsByType = Object.fromEntries(defaultFieldDefinitions.map((definition) => [definition.type, definition]));
24
45
  //#endregion
25
- export { defaultFieldDefinitions };
46
+ export { buildDefaultFieldDefinitions, defaultFieldDefinitions, defaultFieldDefinitionsByType };
26
47
 
27
48
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/fields/builtin/index.ts"],"sourcesContent":["import type { AnyFormFieldDefinition } from '../types'\nimport { calculationField } from './calculation'\nimport { checkboxField } from './checkbox'\nimport { consentField } from './consent'\nimport { emailField } from './email'\nimport { fileField } from './file'\nimport { numberField } from './number'\nimport { repeaterField } from './repeater'\nimport { selectField } from './select'\nimport { textField } from './text'\nimport { textareaField } from './textarea'\n\n// Field types are authored with precise value/config generics; the registry stores them erased\n// (config is re-narrowed per matched type at execution). One cast per built-in, no `any`.\nexport const defaultFieldDefinitions: AnyFormFieldDefinition[] = [\n\ttextField as AnyFormFieldDefinition,\n\ttextareaField as AnyFormFieldDefinition,\n\temailField as AnyFormFieldDefinition,\n\tnumberField as AnyFormFieldDefinition,\n\tselectField as AnyFormFieldDefinition,\n\tcheckboxField as AnyFormFieldDefinition,\n\tcalculationField as AnyFormFieldDefinition,\n\tconsentField as AnyFormFieldDefinition,\n\tfileField as AnyFormFieldDefinition,\n\trepeaterField as AnyFormFieldDefinition,\n]\n"],"mappings":";;;;;;;;;;;AAcA,MAAa,0BAAoD;CAChE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/fields/builtin/index.ts"],"sourcesContent":["import type { AnyFormFieldDefinition } from '../types'\nimport { calculationField } from './calculation'\nimport { checkboxField } from './checkbox'\nimport { buildConsentField } from './consent'\nimport { dateField } from './date'\nimport { emailField } from './email'\nimport { fileField } from './file'\nimport { buildMessageField } from './message'\nimport { numberField } from './number'\nimport { buildRepeaterField } from './repeater'\nimport { buildSelectField } from './select'\nimport { textField } from './text'\nimport { textareaField } from './textarea'\n\n/**\n * Built-in field definitions with content-bearing config fields (select option labels, consent\n * statement, repeater add label) carrying `localized: true` when `localize` is true. Field types\n * without content config are shared static definitions. Definitions are authored with precise\n * value/config generics; the registry stores them erased (config is re-narrowed per matched type\n * at execution), hence one cast per built-in, no `any`.\n */\nexport const buildDefaultFieldDefinitions = (localize: boolean): AnyFormFieldDefinition[] => [\n\ttextField as AnyFormFieldDefinition,\n\ttextareaField as AnyFormFieldDefinition,\n\temailField as AnyFormFieldDefinition,\n\tnumberField as AnyFormFieldDefinition,\n\tbuildSelectField(localize) as AnyFormFieldDefinition,\n\tcheckboxField as AnyFormFieldDefinition,\n\tcalculationField as AnyFormFieldDefinition,\n\tbuildConsentField(localize) as AnyFormFieldDefinition,\n\tfileField as AnyFormFieldDefinition,\n\tbuildRepeaterField(localize) as AnyFormFieldDefinition,\n\tdateField as AnyFormFieldDefinition,\n\tbuildMessageField(localize) as AnyFormFieldDefinition,\n]\n\nexport const defaultFieldDefinitions: AnyFormFieldDefinition[] = buildDefaultFieldDefinitions(true)\n\n/**\n * `defaultFieldDefinitions` indexed by `type`, so a single built-in can be spread and tweaked\n * without hand-rolling a `.find()`, e.g.\n * `fields: { text: { ...defaultFieldDefinitionsByType.text, validate: myValidate } }`. The\n * `fields` plugin option replaces a built-in of the same `type`; `false` removes it, `true` keeps it.\n * Prebuilt with content localization on; with `localizeContent: false`, derive overrides from\n * `buildDefaultFieldDefinitions(false)` instead so the spread does not reintroduce `localized`.\n */\nexport const defaultFieldDefinitionsByType: Record<string, AnyFormFieldDefinition> =\n\tObject.fromEntries(defaultFieldDefinitions.map((definition) => [definition.type, definition]))\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,MAAa,gCAAgC,aAAgD;CAC5F;CACA;CACA;CACA;CACA,iBAAiB,QAAQ;CACzB;CACA;CACA,kBAAkB,QAAQ;CAC1B;CACA,mBAAmB,QAAQ;CAC3B;CACA,kBAAkB,QAAQ;AAC3B;AAEA,MAAa,0BAAoD,6BAA6B,IAAI;;;;;;;;;AAUlG,MAAa,gCACZ,OAAO,YAAY,wBAAwB,KAAK,eAAe,CAAC,WAAW,MAAM,UAAU,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { localizedIf } from "../localizedIf.js";
2
+ import { keys } from "../../translations/keys.js";
3
+ import { labelFor } from "../../translations/server.js";
4
+ import { defineFormField } from "../defineFormField.js";
5
+ //#region src/fields/builtin/message.ts
6
+ /**
7
+ * Display-only inline rich text rendered between fields. Value kind `'none'`: the engine never
8
+ * validates it and never writes it to submissions (its `name` exists only for block identity,
9
+ * conditions targeting, and React keys). `content` is authored copy and follows `localize`.
10
+ */
11
+ const buildMessageField = (localize) => defineFormField({
12
+ type: "message",
13
+ label: keys.fieldTypeMessage,
14
+ value: "none",
15
+ config: [{
16
+ name: "content",
17
+ type: "richText",
18
+ label: labelFor(keys.configContent),
19
+ ...localizedIf(localize)
20
+ }]
21
+ });
22
+ buildMessageField(true);
23
+ //#endregion
24
+ export { buildMessageField };
25
+
26
+ //# sourceMappingURL=message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.js","names":[],"sources":["../../../src/fields/builtin/message.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport { defineFormField } from '../defineFormField'\nimport { localizedIf } from '../localizedIf'\n\n/**\n * Display-only inline rich text rendered between fields. Value kind `'none'`: the engine never\n * validates it and never writes it to submissions (its `name` exists only for block identity,\n * conditions targeting, and React keys). `content` is authored copy and follows `localize`.\n */\nexport const buildMessageField = (localize: boolean) =>\n\tdefineFormField<'none'>({\n\t\ttype: 'message',\n\t\tlabel: keys.fieldTypeMessage,\n\t\tvalue: 'none',\n\t\tconfig: [\n\t\t\t{\n\t\t\t\tname: 'content',\n\t\t\t\ttype: 'richText',\n\t\t\t\tlabel: labelFor(keys.configContent),\n\t\t\t\t...localizedIf(localize),\n\t\t\t},\n\t\t],\n\t})\n\nexport const messageField = buildMessageField(true)\n"],"mappings":";;;;;;;;;;AAUA,MAAa,qBAAqB,aACjC,gBAAwB;CACvB,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,QAAQ,CACP;EACC,MAAM;EACN,MAAM;EACN,OAAO,SAAS,KAAK,aAAa;EAClC,GAAG,YAAY,QAAQ;CACxB,CACD;AACD,CAAC;AAE0B,kBAAkB,IAAI"}
@@ -1,8 +1,10 @@
1
+ import { localizedIf } from "../localizedIf.js";
1
2
  import { keys } from "../../translations/keys.js";
2
3
  import { labelFor } from "../../translations/server.js";
3
4
  import { defineFormField } from "../defineFormField.js";
4
5
  //#region src/fields/builtin/repeater.ts
5
- const repeaterField = defineFormField({
6
+ /** `addLabel` is the visitor-facing add-row button text and follows `localize`; row bounds never do. */
7
+ const buildRepeaterField = (localize) => defineFormField({
6
8
  type: "repeater",
7
9
  label: keys.fieldTypeRepeater,
8
10
  value: "repeater",
@@ -22,7 +24,8 @@ const repeaterField = defineFormField({
22
24
  {
23
25
  name: "addLabel",
24
26
  type: "text",
25
- label: labelFor(keys.configAddLabel)
27
+ label: labelFor(keys.configAddLabel),
28
+ ...localizedIf(localize)
26
29
  }
27
30
  ],
28
31
  validate: ({ value, config, t }) => {
@@ -38,7 +41,8 @@ const repeaterField = defineFormField({
38
41
  return t(keys.repeaterRowCount).replace("{count}", String(count));
39
42
  }
40
43
  });
44
+ buildRepeaterField(true);
41
45
  //#endregion
42
- export { repeaterField };
46
+ export { buildRepeaterField };
43
47
 
44
48
  //# sourceMappingURL=repeater.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"repeater.js","names":[],"sources":["../../../src/fields/builtin/repeater.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport { defineFormField } from '../defineFormField'\n\ntype RepeaterConfig = {\n\tminRows?: number\n\tmaxRows?: number\n\taddLabel?: string\n}\n\nexport const repeaterField = defineFormField<'repeater', RepeaterConfig>({\n\ttype: 'repeater',\n\tlabel: keys.fieldTypeRepeater,\n\tvalue: 'repeater',\n\tconfig: [\n\t\t{\n\t\t\tname: 'minRows',\n\t\t\ttype: 'number',\n\t\t\tmin: 0,\n\t\t\tlabel: labelFor(keys.configMinRows),\n\t\t},\n\t\t{\n\t\t\tname: 'maxRows',\n\t\t\ttype: 'number',\n\t\t\tmin: 1,\n\t\t\tlabel: labelFor(keys.configMaxRows),\n\t\t},\n\t\t{\n\t\t\tname: 'addLabel',\n\t\t\ttype: 'text',\n\t\t\tlabel: labelFor(keys.configAddLabel),\n\t\t},\n\t\t// subFields blocks field is injected by buildFieldBlocks after the main loop,\n\t\t// so all non-repeater blocks are available and repeater-in-repeater is excluded.\n\t],\n\tvalidate: ({ value, config, t }) => {\n\t\tconst rows = Array.isArray(value) ? value : []\n\t\tconst min = typeof config.minRows === 'number' ? config.minRows : 0\n\t\tif (rows.length < min) {\n\t\t\treturn t(keys.validationRepeaterMin).replace('{min}', String(min))\n\t\t}\n\t\tconst max = typeof config.maxRows === 'number' ? config.maxRows : undefined\n\t\tif (max != null && rows.length > max) {\n\t\t\treturn t(keys.validationRepeaterMax).replace('{max}', String(max))\n\t\t}\n\t\treturn true\n\t},\n\tformat: ({ value, t }) => {\n\t\tconst count = Array.isArray(value) ? value.length : 0\n\t\treturn t(keys.repeaterRowCount).replace('{count}', String(count))\n\t},\n})\n"],"mappings":";;;;AAUA,MAAa,gBAAgB,gBAA4C;CACxE,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,QAAQ;EACP;GACC,MAAM;GACN,MAAM;GACN,KAAK;GACL,OAAO,SAAS,KAAK,aAAa;EACnC;EACA;GACC,MAAM;GACN,MAAM;GACN,KAAK;GACL,OAAO,SAAS,KAAK,aAAa;EACnC;EACA;GACC,MAAM;GACN,MAAM;GACN,OAAO,SAAS,KAAK,cAAc;EACpC;CAGD;CACA,WAAW,EAAE,OAAO,QAAQ,QAAQ;EACnC,MAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;EAC7C,MAAM,MAAM,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;EAClE,IAAI,KAAK,SAAS,KACjB,OAAO,EAAE,KAAK,qBAAqB,EAAE,QAAQ,SAAS,OAAO,GAAG,CAAC;EAElE,MAAM,MAAM,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU,KAAA;EAClE,IAAI,OAAO,QAAQ,KAAK,SAAS,KAChC,OAAO,EAAE,KAAK,qBAAqB,EAAE,QAAQ,SAAS,OAAO,GAAG,CAAC;EAElE,OAAO;CACR;CACA,SAAS,EAAE,OAAO,QAAQ;EACzB,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS;EACpD,OAAO,EAAE,KAAK,gBAAgB,EAAE,QAAQ,WAAW,OAAO,KAAK,CAAC;CACjE;AACD,CAAC"}
1
+ {"version":3,"file":"repeater.js","names":[],"sources":["../../../src/fields/builtin/repeater.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport { defineFormField } from '../defineFormField'\nimport { localizedIf } from '../localizedIf'\n\ntype RepeaterConfig = {\n\tminRows?: number\n\tmaxRows?: number\n\taddLabel?: string\n}\n\n/** `addLabel` is the visitor-facing add-row button text and follows `localize`; row bounds never do. */\nexport const buildRepeaterField = (localize: boolean) =>\n\tdefineFormField<'repeater', RepeaterConfig>({\n\t\ttype: 'repeater',\n\t\tlabel: keys.fieldTypeRepeater,\n\t\tvalue: 'repeater',\n\t\tconfig: [\n\t\t\t{\n\t\t\t\tname: 'minRows',\n\t\t\t\ttype: 'number',\n\t\t\t\tmin: 0,\n\t\t\t\tlabel: labelFor(keys.configMinRows),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'maxRows',\n\t\t\t\ttype: 'number',\n\t\t\t\tmin: 1,\n\t\t\t\tlabel: labelFor(keys.configMaxRows),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'addLabel',\n\t\t\t\ttype: 'text',\n\t\t\t\tlabel: labelFor(keys.configAddLabel),\n\t\t\t\t...localizedIf(localize),\n\t\t\t},\n\t\t\t// subFields blocks field is injected by buildFieldBlocks after the main loop,\n\t\t\t// so all non-repeater blocks are available and repeater-in-repeater is excluded.\n\t\t],\n\t\tvalidate: ({ value, config, t }) => {\n\t\t\tconst rows = Array.isArray(value) ? value : []\n\t\t\tconst min = typeof config.minRows === 'number' ? config.minRows : 0\n\t\t\tif (rows.length < min) {\n\t\t\t\treturn t(keys.validationRepeaterMin).replace('{min}', String(min))\n\t\t\t}\n\t\t\tconst max = typeof config.maxRows === 'number' ? config.maxRows : undefined\n\t\t\tif (max != null && rows.length > max) {\n\t\t\t\treturn t(keys.validationRepeaterMax).replace('{max}', String(max))\n\t\t\t}\n\t\t\treturn true\n\t\t},\n\t\tformat: ({ value, t }) => {\n\t\t\tconst count = Array.isArray(value) ? value.length : 0\n\t\t\treturn t(keys.repeaterRowCount).replace('{count}', String(count))\n\t\t},\n\t})\n\nexport const repeaterField = buildRepeaterField(true)\n"],"mappings":";;;;;;AAYA,MAAa,sBAAsB,aAClC,gBAA4C;CAC3C,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,QAAQ;EACP;GACC,MAAM;GACN,MAAM;GACN,KAAK;GACL,OAAO,SAAS,KAAK,aAAa;EACnC;EACA;GACC,MAAM;GACN,MAAM;GACN,KAAK;GACL,OAAO,SAAS,KAAK,aAAa;EACnC;EACA;GACC,MAAM;GACN,MAAM;GACN,OAAO,SAAS,KAAK,cAAc;GACnC,GAAG,YAAY,QAAQ;EACxB;CAGD;CACA,WAAW,EAAE,OAAO,QAAQ,QAAQ;EACnC,MAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;EAC7C,MAAM,MAAM,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;EAClE,IAAI,KAAK,SAAS,KACjB,OAAO,EAAE,KAAK,qBAAqB,EAAE,QAAQ,SAAS,OAAO,GAAG,CAAC;EAElE,MAAM,MAAM,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU,KAAA;EAClE,IAAI,OAAO,QAAQ,KAAK,SAAS,KAChC,OAAO,EAAE,KAAK,qBAAqB,EAAE,QAAQ,SAAS,OAAO,GAAG,CAAC;EAElE,OAAO;CACR;CACA,SAAS,EAAE,OAAO,QAAQ;EACzB,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS;EACpD,OAAO,EAAE,KAAK,gBAAgB,EAAE,QAAQ,WAAW,OAAO,KAAK,CAAC;CACjE;AACD,CAAC;AAE2B,mBAAmB,IAAI"}
@@ -1,8 +1,10 @@
1
+ import { localizedIf } from "../localizedIf.js";
1
2
  import { keys } from "../../translations/keys.js";
2
3
  import { labelFor } from "../../translations/server.js";
3
4
  import { defineFormField } from "../defineFormField.js";
4
5
  //#region src/fields/builtin/select.ts
5
- const selectField = defineFormField({
6
+ /** Option `label` is author-facing content and follows `localize`; `value` is an identifier and never does. */
7
+ const buildSelectField = (localize) => defineFormField({
6
8
  type: "select",
7
9
  label: keys.fieldTypeSelect,
8
10
  value: "text",
@@ -19,7 +21,8 @@ const selectField = defineFormField({
19
21
  name: "label",
20
22
  type: "text",
21
23
  required: true,
22
- label: labelFor(keys.configOptionLabel)
24
+ label: labelFor(keys.configOptionLabel),
25
+ ...localizedIf(localize)
23
26
  }, {
24
27
  name: "value",
25
28
  type: "text",
@@ -38,7 +41,8 @@ const selectField = defineFormField({
38
41
  return optionLabels?.[value] ?? String(value);
39
42
  }
40
43
  });
44
+ buildSelectField(true);
41
45
  //#endregion
42
- export { selectField };
46
+ export { buildSelectField };
43
47
 
44
48
  //# sourceMappingURL=select.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","names":[],"sources":["../../../src/fields/builtin/select.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport { defineFormField } from '../defineFormField'\n\ntype SelectOption = { label: string; value: string }\ntype SelectConfig = { options?: SelectOption[] }\n\nexport const selectField = defineFormField<'text', SelectConfig>({\n\ttype: 'select',\n\tlabel: keys.fieldTypeSelect,\n\tvalue: 'text',\n\tconditionType: 'select',\n\tconfig: [\n\t\t{\n\t\t\tname: 'options',\n\t\t\ttype: 'array',\n\t\t\tlabel: labelFor(keys.configOptions),\n\t\t\tlabels: { singular: labelFor(keys.configOption), plural: labelFor(keys.configOptions) },\n\t\t\tfields: [\n\t\t\t\t{ name: 'label', type: 'text', required: true, label: labelFor(keys.configOptionLabel) },\n\t\t\t\t{ name: 'value', type: 'text', required: true, label: labelFor(keys.configOptionValue) },\n\t\t\t],\n\t\t},\n\t],\n\tvalidate: ({ value, config, t }) => {\n\t\tif (value == null || value === '') {\n\t\t\treturn true\n\t\t}\n\t\tconst options = config.options ?? []\n\t\tif (options.length === 0) {\n\t\t\treturn true\n\t\t}\n\t\treturn options.some((option) => option.value === value) ? true : t(keys.validationSelect)\n\t},\n\tformat: ({ value, optionLabels }) => {\n\t\tif (value == null || value === '') {\n\t\t\treturn ''\n\t\t}\n\t\treturn optionLabels?.[value] ?? String(value)\n\t},\n})\n"],"mappings":";;;;AAOA,MAAa,cAAc,gBAAsC;CAChE,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,eAAe;CACf,QAAQ,CACP;EACC,MAAM;EACN,MAAM;EACN,OAAO,SAAS,KAAK,aAAa;EAClC,QAAQ;GAAE,UAAU,SAAS,KAAK,YAAY;GAAG,QAAQ,SAAS,KAAK,aAAa;EAAE;EACtF,QAAQ,CACP;GAAE,MAAM;GAAS,MAAM;GAAQ,UAAU;GAAM,OAAO,SAAS,KAAK,iBAAiB;EAAE,GACvF;GAAE,MAAM;GAAS,MAAM;GAAQ,UAAU;GAAM,OAAO,SAAS,KAAK,iBAAiB;EAAE,CACxF;CACD,CACD;CACA,WAAW,EAAE,OAAO,QAAQ,QAAQ;EACnC,IAAI,SAAS,QAAQ,UAAU,IAC9B,OAAO;EAER,MAAM,UAAU,OAAO,WAAW,CAAC;EACnC,IAAI,QAAQ,WAAW,GACtB,OAAO;EAER,OAAO,QAAQ,MAAM,WAAW,OAAO,UAAU,KAAK,IAAI,OAAO,EAAE,KAAK,gBAAgB;CACzF;CACA,SAAS,EAAE,OAAO,mBAAmB;EACpC,IAAI,SAAS,QAAQ,UAAU,IAC9B,OAAO;EAER,OAAO,eAAe,UAAU,OAAO,KAAK;CAC7C;AACD,CAAC"}
1
+ {"version":3,"file":"select.js","names":[],"sources":["../../../src/fields/builtin/select.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport { defineFormField } from '../defineFormField'\nimport { localizedIf } from '../localizedIf'\n\ntype SelectOption = { label: string; value: string }\ntype SelectConfig = { options?: SelectOption[] }\n\n/** Option `label` is author-facing content and follows `localize`; `value` is an identifier and never does. */\nexport const buildSelectField = (localize: boolean) =>\n\tdefineFormField<'text', SelectConfig>({\n\t\ttype: 'select',\n\t\tlabel: keys.fieldTypeSelect,\n\t\tvalue: 'text',\n\t\tconditionType: 'select',\n\t\tconfig: [\n\t\t\t{\n\t\t\t\tname: 'options',\n\t\t\t\ttype: 'array',\n\t\t\t\tlabel: labelFor(keys.configOptions),\n\t\t\t\tlabels: { singular: labelFor(keys.configOption), plural: labelFor(keys.configOptions) },\n\t\t\t\tfields: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: 'label',\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\trequired: true,\n\t\t\t\t\t\tlabel: labelFor(keys.configOptionLabel),\n\t\t\t\t\t\t...localizedIf(localize),\n\t\t\t\t\t},\n\t\t\t\t\t{ name: 'value', type: 'text', required: true, label: labelFor(keys.configOptionValue) },\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t\tvalidate: ({ value, config, t }) => {\n\t\t\tif (value == null || value === '') {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst options = config.options ?? []\n\t\t\tif (options.length === 0) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn options.some((option) => option.value === value) ? true : t(keys.validationSelect)\n\t\t},\n\t\tformat: ({ value, optionLabels }) => {\n\t\t\tif (value == null || value === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn optionLabels?.[value] ?? String(value)\n\t\t},\n\t})\n\nexport const selectField = buildSelectField(true)\n"],"mappings":";;;;;;AASA,MAAa,oBAAoB,aAChC,gBAAsC;CACrC,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,eAAe;CACf,QAAQ,CACP;EACC,MAAM;EACN,MAAM;EACN,OAAO,SAAS,KAAK,aAAa;EAClC,QAAQ;GAAE,UAAU,SAAS,KAAK,YAAY;GAAG,QAAQ,SAAS,KAAK,aAAa;EAAE;EACtF,QAAQ,CACP;GACC,MAAM;GACN,MAAM;GACN,UAAU;GACV,OAAO,SAAS,KAAK,iBAAiB;GACtC,GAAG,YAAY,QAAQ;EACxB,GACA;GAAE,MAAM;GAAS,MAAM;GAAQ,UAAU;GAAM,OAAO,SAAS,KAAK,iBAAiB;EAAE,CACxF;CACD,CACD;CACA,WAAW,EAAE,OAAO,QAAQ,QAAQ;EACnC,IAAI,SAAS,QAAQ,UAAU,IAC9B,OAAO;EAER,MAAM,UAAU,OAAO,WAAW,CAAC;EACnC,IAAI,QAAQ,WAAW,GACtB,OAAO;EAER,OAAO,QAAQ,MAAM,WAAW,OAAO,UAAU,KAAK,IAAI,OAAO,EAAE,KAAK,gBAAgB;CACzF;CACA,SAAS,EAAE,OAAO,mBAAmB;EACpC,IAAI,SAAS,QAAQ,UAAU,IAC9B,OAAO;EAER,OAAO,eAAe,UAAU,OAAO,KAAK;CAC7C;AACD,CAAC;AAEyB,iBAAiB,IAAI"}
@@ -0,0 +1,25 @@
1
+ //#region src/fields/fieldNamesOfType.ts
2
+ /**
3
+ * Names of form `fields` blocks whose `blockType` is in `types`, trimmed and non-empty.
4
+ * Server-safe and defensive: `fields` may be missing, non-array, or contain garbled rows
5
+ * mid-edit. The single source of truth for which field names are selectable and valid as
6
+ * a field reference (e.g. the confirmation action's `toField`), so client options and
7
+ * server validation cannot drift apart.
8
+ */
9
+ const fieldNamesOfType = (fields, types) => {
10
+ if (!Array.isArray(fields)) return [];
11
+ const names = [];
12
+ for (const row of fields) {
13
+ if (!row || typeof row !== "object") continue;
14
+ const { blockType, name } = row;
15
+ if (typeof blockType !== "string" || !types.includes(blockType)) continue;
16
+ if (typeof name !== "string") continue;
17
+ const trimmed = name.trim();
18
+ if (trimmed.length > 0) names.push(trimmed);
19
+ }
20
+ return names;
21
+ };
22
+ //#endregion
23
+ export { fieldNamesOfType };
24
+
25
+ //# sourceMappingURL=fieldNamesOfType.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fieldNamesOfType.js","names":[],"sources":["../../src/fields/fieldNamesOfType.ts"],"sourcesContent":["/**\n * Names of form `fields` blocks whose `blockType` is in `types`, trimmed and non-empty.\n * Server-safe and defensive: `fields` may be missing, non-array, or contain garbled rows\n * mid-edit. The single source of truth for which field names are selectable and valid as\n * a field reference (e.g. the confirmation action's `toField`), so client options and\n * server validation cannot drift apart.\n */\nexport const fieldNamesOfType = (fields: unknown, types: readonly string[]): string[] => {\n\tif (!Array.isArray(fields)) {\n\t\treturn []\n\t}\n\tconst names: string[] = []\n\tfor (const row of fields) {\n\t\tif (!row || typeof row !== 'object') {\n\t\t\tcontinue\n\t\t}\n\t\tconst { blockType, name } = row as { blockType?: unknown; name?: unknown }\n\t\tif (typeof blockType !== 'string' || !types.includes(blockType)) {\n\t\t\tcontinue\n\t\t}\n\t\tif (typeof name !== 'string') {\n\t\t\tcontinue\n\t\t}\n\t\tconst trimmed = name.trim()\n\t\tif (trimmed.length > 0) {\n\t\t\tnames.push(trimmed)\n\t\t}\n\t}\n\treturn names\n}\n"],"mappings":";;;;;;;;AAOA,MAAa,oBAAoB,QAAiB,UAAuC;CACxF,IAAI,CAAC,MAAM,QAAQ,MAAM,GACxB,OAAO,CAAC;CAET,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,OAAO,QAAQ;EACzB,IAAI,CAAC,OAAO,OAAO,QAAQ,UAC1B;EAED,MAAM,EAAE,WAAW,SAAS;EAC5B,IAAI,OAAO,cAAc,YAAY,CAAC,MAAM,SAAS,SAAS,GAC7D;EAED,IAAI,OAAO,SAAS,UACnB;EAED,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,QAAQ,SAAS,GACpB,MAAM,KAAK,OAAO;CAEpB;CACA,OAAO;AACR"}
@@ -0,0 +1,13 @@
1
+ //#region src/fields/localizedIf.d.ts
2
+ /**
3
+ * Spreadable localization flag: `{ localized: true }` when `localize` is true, an empty object
4
+ * otherwise, so `...localizedIf(flag)` adds the key only when wanted. Payload itself strips
5
+ * `localized` from fields when the host config has no `localization`, so spreading `true` is
6
+ * always safe.
7
+ */
8
+ declare const localizedIf: (localize: boolean) => {
9
+ localized: true;
10
+ } | Record<string, never>;
11
+ //#endregion
12
+ export { localizedIf };
13
+ //# sourceMappingURL=localizedIf.d.ts.map
@@ -0,0 +1,12 @@
1
+ //#region src/fields/localizedIf.ts
2
+ /**
3
+ * Spreadable localization flag: `{ localized: true }` when `localize` is true, an empty object
4
+ * otherwise, so `...localizedIf(flag)` adds the key only when wanted. Payload itself strips
5
+ * `localized` from fields when the host config has no `localization`, so spreading `true` is
6
+ * always safe.
7
+ */
8
+ const localizedIf = (localize) => localize ? { localized: true } : {};
9
+ //#endregion
10
+ export { localizedIf };
11
+
12
+ //# sourceMappingURL=localizedIf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localizedIf.js","names":[],"sources":["../../src/fields/localizedIf.ts"],"sourcesContent":["/**\n * Spreadable localization flag: `{ localized: true }` when `localize` is true, an empty object\n * otherwise, so `...localizedIf(flag)` adds the key only when wanted. Payload itself strips\n * `localized` from fields when the host config has no `localization`, so spreading `true` is\n * always safe.\n */\nexport const localizedIf = (localize: boolean): { localized: true } | Record<string, never> =>\n\tlocalize ? { localized: true } : {}\n"],"mappings":";;;;;;;AAMA,MAAa,eAAe,aAC3B,WAAW,EAAE,WAAW,KAAK,IAAI,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { localizedIf } from "./localizedIf.js";
1
2
  import { keys } from "../translations/keys.js";
2
3
  import { labelFor } from "../translations/server.js";
3
4
  //#region src/fields/sharedConfig.ts
@@ -13,77 +14,101 @@ const conditionField = (name, labelKey, conditionTypes) => ({
13
14
  });
14
15
  /**
15
16
  * Config every field instance carries regardless of type. `name` is the machine key written into
16
- * submissions; `width` is stored for the layout grid. `visibleWhen`/`validateWhen` store a
17
- * canonical Payload `Where`, edited by the native condition builder. Field types add their own `config`
18
- * after these.
17
+ * submissions; `width` is stored for the layout grid. Field types add their own `config` after
18
+ * these inside the Field tab (see `fieldBlockTabs`). Content-bearing fields (`label`,
19
+ * `placeholder`, `description`) are localized unless `localize` is false; identifiers and
20
+ * behavior flags never are. The rows below are presentational only (unnamed, data paths stay
21
+ * flat); `required` sits last, under `description`, per the admin layout.
19
22
  */
20
- const sharedFieldConfig = (conditionTypes) => [
23
+ const sharedFieldConfig = (localize = true) => [
21
24
  {
22
- name: "name",
23
- type: "text",
24
- required: true,
25
- label: labelFor(keys.configName)
25
+ type: "row",
26
+ fields: [{
27
+ name: "name",
28
+ type: "text",
29
+ required: true,
30
+ label: labelFor(keys.configName),
31
+ admin: { width: "50%" }
32
+ }, {
33
+ name: "label",
34
+ type: "text",
35
+ label: labelFor(keys.configLabel),
36
+ admin: { width: "50%" },
37
+ ...localizedIf(localize)
38
+ }]
26
39
  },
27
40
  {
28
- name: "label",
29
- type: "text",
30
- label: labelFor(keys.configLabel)
31
- },
32
- {
33
- name: "required",
34
- type: "checkbox",
35
- label: labelFor(keys.configRequired)
36
- },
37
- {
38
- name: "width",
39
- type: "select",
40
- defaultValue: "full",
41
- label: labelFor(keys.configWidth),
42
- options: [
43
- {
44
- label: "Full",
45
- value: "full"
46
- },
47
- {
48
- label: "Half",
49
- value: "half"
41
+ type: "row",
42
+ fields: [{
43
+ name: "width",
44
+ type: "select",
45
+ required: true,
46
+ defaultValue: "full",
47
+ label: labelFor(keys.configWidth),
48
+ admin: {
49
+ width: "50%",
50
+ isClearable: false
50
51
  },
51
- {
52
- label: "Third",
53
- value: "third"
54
- },
55
- {
56
- label: "Two thirds",
57
- value: "twoThirds"
58
- }
59
- ]
60
- },
61
- {
62
- name: "placeholder",
63
- type: "text",
64
- label: labelFor(keys.configPlaceholder)
52
+ options: [
53
+ {
54
+ label: "Full",
55
+ value: "full"
56
+ },
57
+ {
58
+ label: "Half",
59
+ value: "half"
60
+ },
61
+ {
62
+ label: "Third",
63
+ value: "third"
64
+ },
65
+ {
66
+ label: "Two thirds",
67
+ value: "twoThirds"
68
+ }
69
+ ]
70
+ }, {
71
+ name: "placeholder",
72
+ type: "text",
73
+ label: labelFor(keys.configPlaceholder),
74
+ admin: { width: "50%" },
75
+ ...localizedIf(localize)
76
+ }]
65
77
  },
66
78
  {
67
79
  name: "description",
68
80
  type: "textarea",
69
- label: labelFor(keys.configDescription)
81
+ label: labelFor(keys.configDescription),
82
+ ...localizedIf(localize)
70
83
  },
71
84
  {
72
- type: "collapsible",
73
- label: labelFor(keys.configAdvanced),
74
- admin: { initCollapsed: true },
75
- fields: [
76
- conditionField("visibleWhen", keys.configVisibleWhen, conditionTypes),
77
- conditionField("validateWhen", keys.configValidateWhen, conditionTypes),
78
- {
85
+ name: "required",
86
+ type: "checkbox",
87
+ label: labelFor(keys.configRequired)
88
+ }
89
+ ];
90
+ const fieldBlockTabs = ({ conditionTypes, typeConfig, validations, localize = true }) => ({
91
+ type: "tabs",
92
+ tabs: [
93
+ {
94
+ label: labelFor(keys.tabField),
95
+ fields: [...sharedFieldConfig(localize), ...typeConfig]
96
+ },
97
+ {
98
+ label: labelFor(keys.tabValidation),
99
+ fields: [validations, conditionField("validateWhen", keys.configValidateWhen, conditionTypes)]
100
+ },
101
+ {
102
+ label: labelFor(keys.tabAdvanced),
103
+ fields: [conditionField("visibleWhen", keys.configVisibleWhen, conditionTypes), {
79
104
  name: "hidden",
80
105
  type: "checkbox",
81
106
  label: labelFor(keys.configHidden)
82
- }
83
- ]
84
- }
85
- ];
107
+ }]
108
+ }
109
+ ]
110
+ });
86
111
  //#endregion
87
- export { sharedFieldConfig };
112
+ export { fieldBlockTabs };
88
113
 
89
114
  //# sourceMappingURL=sharedConfig.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sharedConfig.js","names":[],"sources":["../../src/fields/sharedConfig.ts"],"sourcesContent":["import type { Field } from 'payload'\nimport type { ConditionFieldType } from '../conditions/fieldTypes'\nimport { keys } from '../translations/keys'\nimport { labelFor } from '../translations/server'\n\nconst CONDITION_FIELD_REF = '@10x-media/form-builder/client#FormConditionField'\n\nconst conditionField = (\n\tname: 'visibleWhen' | 'validateWhen',\n\tlabelKey: string,\n\tconditionTypes: Record<string, ConditionFieldType>\n): Field => ({\n\tname,\n\ttype: 'json',\n\tlabel: labelFor(labelKey),\n\tadmin: {\n\t\tcomponents: {\n\t\t\tField: { path: CONDITION_FIELD_REF, clientProps: { conditionTypes } },\n\t\t},\n\t},\n})\n\n/**\n * Config every field instance carries regardless of type. `name` is the machine key written into\n * submissions; `width` is stored for the layout grid. `visibleWhen`/`validateWhen` store a\n * canonical Payload `Where`, edited by the native condition builder. Field types add their own `config`\n * after these.\n */\nexport const sharedFieldConfig = (conditionTypes: Record<string, ConditionFieldType>): Field[] => [\n\t{ name: 'name', type: 'text', required: true, label: labelFor(keys.configName) },\n\t{ name: 'label', type: 'text', label: labelFor(keys.configLabel) },\n\t{ name: 'required', type: 'checkbox', label: labelFor(keys.configRequired) },\n\t{\n\t\tname: 'width',\n\t\ttype: 'select',\n\t\tdefaultValue: 'full',\n\t\tlabel: labelFor(keys.configWidth),\n\t\toptions: [\n\t\t\t{ label: 'Full', value: 'full' },\n\t\t\t{ label: 'Half', value: 'half' },\n\t\t\t{ label: 'Third', value: 'third' },\n\t\t\t{ label: 'Two thirds', value: 'twoThirds' },\n\t\t],\n\t},\n\t{ name: 'placeholder', type: 'text', label: labelFor(keys.configPlaceholder) },\n\t{ name: 'description', type: 'textarea', label: labelFor(keys.configDescription) },\n\t{\n\t\ttype: 'collapsible',\n\t\tlabel: labelFor(keys.configAdvanced),\n\t\tadmin: { initCollapsed: true },\n\t\tfields: [\n\t\t\tconditionField('visibleWhen', keys.configVisibleWhen, conditionTypes),\n\t\t\tconditionField('validateWhen', keys.configValidateWhen, conditionTypes),\n\t\t\t{ name: 'hidden', type: 'checkbox', label: labelFor(keys.configHidden) },\n\t\t],\n\t},\n]\n"],"mappings":";;;AAKA,MAAM,sBAAsB;AAE5B,MAAM,kBACL,MACA,UACA,oBACY;CACZ;CACA,MAAM;CACN,OAAO,SAAS,QAAQ;CACxB,OAAO,EACN,YAAY,EACX,OAAO;EAAE,MAAM;EAAqB,aAAa,EAAE,eAAe;CAAE,EACrE,EACD;AACD;;;;;;;AAQA,MAAa,qBAAqB,mBAAgE;CACjG;EAAE,MAAM;EAAQ,MAAM;EAAQ,UAAU;EAAM,OAAO,SAAS,KAAK,UAAU;CAAE;CAC/E;EAAE,MAAM;EAAS,MAAM;EAAQ,OAAO,SAAS,KAAK,WAAW;CAAE;CACjE;EAAE,MAAM;EAAY,MAAM;EAAY,OAAO,SAAS,KAAK,cAAc;CAAE;CAC3E;EACC,MAAM;EACN,MAAM;EACN,cAAc;EACd,OAAO,SAAS,KAAK,WAAW;EAChC,SAAS;GACR;IAAE,OAAO;IAAQ,OAAO;GAAO;GAC/B;IAAE,OAAO;IAAQ,OAAO;GAAO;GAC/B;IAAE,OAAO;IAAS,OAAO;GAAQ;GACjC;IAAE,OAAO;IAAc,OAAO;GAAY;EAC3C;CACD;CACA;EAAE,MAAM;EAAe,MAAM;EAAQ,OAAO,SAAS,KAAK,iBAAiB;CAAE;CAC7E;EAAE,MAAM;EAAe,MAAM;EAAY,OAAO,SAAS,KAAK,iBAAiB;CAAE;CACjF;EACC,MAAM;EACN,OAAO,SAAS,KAAK,cAAc;EACnC,OAAO,EAAE,eAAe,KAAK;EAC7B,QAAQ;GACP,eAAe,eAAe,KAAK,mBAAmB,cAAc;GACpE,eAAe,gBAAgB,KAAK,oBAAoB,cAAc;GACtE;IAAE,MAAM;IAAU,MAAM;IAAY,OAAO,SAAS,KAAK,YAAY;GAAE;EACxE;CACD;AACD"}
1
+ {"version":3,"file":"sharedConfig.js","names":[],"sources":["../../src/fields/sharedConfig.ts"],"sourcesContent":["import type { Field } from 'payload'\nimport type { ConditionFieldType } from '../conditions/fieldTypes'\nimport { keys } from '../translations/keys'\nimport { labelFor } from '../translations/server'\nimport { localizedIf } from './localizedIf'\n\nconst CONDITION_FIELD_REF = '@10x-media/form-builder/client#FormConditionField'\n\nconst conditionField = (\n\tname: 'visibleWhen' | 'validateWhen',\n\tlabelKey: string,\n\tconditionTypes: Record<string, ConditionFieldType>\n): Field => ({\n\tname,\n\ttype: 'json',\n\tlabel: labelFor(labelKey),\n\tadmin: {\n\t\tcomponents: {\n\t\t\tField: { path: CONDITION_FIELD_REF, clientProps: { conditionTypes } },\n\t\t},\n\t},\n})\n\n/**\n * Config every field instance carries regardless of type. `name` is the machine key written into\n * submissions; `width` is stored for the layout grid. Field types add their own `config` after\n * these inside the Field tab (see `fieldBlockTabs`). Content-bearing fields (`label`,\n * `placeholder`, `description`) are localized unless `localize` is false; identifiers and\n * behavior flags never are. The rows below are presentational only (unnamed, data paths stay\n * flat); `required` sits last, under `description`, per the admin layout.\n */\nexport const sharedFieldConfig = (localize = true): Field[] => [\n\t{\n\t\ttype: 'row',\n\t\tfields: [\n\t\t\t{\n\t\t\t\tname: 'name',\n\t\t\t\ttype: 'text',\n\t\t\t\trequired: true,\n\t\t\t\tlabel: labelFor(keys.configName),\n\t\t\t\tadmin: { width: '50%' },\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'label',\n\t\t\t\ttype: 'text',\n\t\t\t\tlabel: labelFor(keys.configLabel),\n\t\t\t\tadmin: { width: '50%' },\n\t\t\t\t...localizedIf(localize),\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\ttype: 'row',\n\t\tfields: [\n\t\t\t{\n\t\t\t\tname: 'width',\n\t\t\t\ttype: 'select',\n\t\t\t\trequired: true,\n\t\t\t\tdefaultValue: 'full',\n\t\t\t\tlabel: labelFor(keys.configWidth),\n\t\t\t\tadmin: { width: '50%', isClearable: false },\n\t\t\t\toptions: [\n\t\t\t\t\t{ label: 'Full', value: 'full' },\n\t\t\t\t\t{ label: 'Half', value: 'half' },\n\t\t\t\t\t{ label: 'Third', value: 'third' },\n\t\t\t\t\t{ label: 'Two thirds', value: 'twoThirds' },\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'placeholder',\n\t\t\t\ttype: 'text',\n\t\t\t\tlabel: labelFor(keys.configPlaceholder),\n\t\t\t\tadmin: { width: '50%' },\n\t\t\t\t...localizedIf(localize),\n\t\t\t},\n\t\t],\n\t},\n\t{\n\t\tname: 'description',\n\t\ttype: 'textarea',\n\t\tlabel: labelFor(keys.configDescription),\n\t\t...localizedIf(localize),\n\t},\n\t{ name: 'required', type: 'checkbox', label: labelFor(keys.configRequired) },\n]\n\n/**\n * The single field of every field block: unnamed tabs (presentational only, data paths stay flat).\n * Field tab holds the shared basics plus the type's own config; Validation holds the rule blocks and\n * `validateWhen`; Advanced holds `visibleWhen` and `hidden`. `visibleWhen`/`validateWhen` store a\n * canonical Payload `Where`, edited by the native condition builder.\n */\ntype FieldBlockTabsArgs = {\n\tconditionTypes: Record<string, ConditionFieldType>\n\ttypeConfig: Field[]\n\tvalidations: Field\n\tlocalize?: boolean\n}\n\nexport const fieldBlockTabs = ({\n\tconditionTypes,\n\ttypeConfig,\n\tvalidations,\n\tlocalize = true,\n}: FieldBlockTabsArgs): Field => ({\n\ttype: 'tabs',\n\ttabs: [\n\t\t{\n\t\t\tlabel: labelFor(keys.tabField),\n\t\t\tfields: [...sharedFieldConfig(localize), ...typeConfig],\n\t\t},\n\t\t{\n\t\t\tlabel: labelFor(keys.tabValidation),\n\t\t\tfields: [\n\t\t\t\tvalidations,\n\t\t\t\tconditionField('validateWhen', keys.configValidateWhen, conditionTypes),\n\t\t\t],\n\t\t},\n\t\t{\n\t\t\tlabel: labelFor(keys.tabAdvanced),\n\t\t\tfields: [\n\t\t\t\tconditionField('visibleWhen', keys.configVisibleWhen, conditionTypes),\n\t\t\t\t{ name: 'hidden', type: 'checkbox', label: labelFor(keys.configHidden) },\n\t\t\t],\n\t\t},\n\t],\n})\n"],"mappings":";;;;AAMA,MAAM,sBAAsB;AAE5B,MAAM,kBACL,MACA,UACA,oBACY;CACZ;CACA,MAAM;CACN,OAAO,SAAS,QAAQ;CACxB,OAAO,EACN,YAAY,EACX,OAAO;EAAE,MAAM;EAAqB,aAAa,EAAE,eAAe;CAAE,EACrE,EACD;AACD;;;;;;;;;AAUA,MAAa,qBAAqB,WAAW,SAAkB;CAC9D;EACC,MAAM;EACN,QAAQ,CACP;GACC,MAAM;GACN,MAAM;GACN,UAAU;GACV,OAAO,SAAS,KAAK,UAAU;GAC/B,OAAO,EAAE,OAAO,MAAM;EACvB,GACA;GACC,MAAM;GACN,MAAM;GACN,OAAO,SAAS,KAAK,WAAW;GAChC,OAAO,EAAE,OAAO,MAAM;GACtB,GAAG,YAAY,QAAQ;EACxB,CACD;CACD;CACA;EACC,MAAM;EACN,QAAQ,CACP;GACC,MAAM;GACN,MAAM;GACN,UAAU;GACV,cAAc;GACd,OAAO,SAAS,KAAK,WAAW;GAChC,OAAO;IAAE,OAAO;IAAO,aAAa;GAAM;GAC1C,SAAS;IACR;KAAE,OAAO;KAAQ,OAAO;IAAO;IAC/B;KAAE,OAAO;KAAQ,OAAO;IAAO;IAC/B;KAAE,OAAO;KAAS,OAAO;IAAQ;IACjC;KAAE,OAAO;KAAc,OAAO;IAAY;GAC3C;EACD,GACA;GACC,MAAM;GACN,MAAM;GACN,OAAO,SAAS,KAAK,iBAAiB;GACtC,OAAO,EAAE,OAAO,MAAM;GACtB,GAAG,YAAY,QAAQ;EACxB,CACD;CACD;CACA;EACC,MAAM;EACN,MAAM;EACN,OAAO,SAAS,KAAK,iBAAiB;EACtC,GAAG,YAAY,QAAQ;CACxB;CACA;EAAE,MAAM;EAAY,MAAM;EAAY,OAAO,SAAS,KAAK,cAAc;CAAE;AAC5E;AAeA,MAAa,kBAAkB,EAC9B,gBACA,YACA,aACA,WAAW,YACsB;CACjC,MAAM;CACN,MAAM;EACL;GACC,OAAO,SAAS,KAAK,QAAQ;GAC7B,QAAQ,CAAC,GAAG,kBAAkB,QAAQ,GAAG,GAAG,UAAU;EACvD;EACA;GACC,OAAO,SAAS,KAAK,aAAa;GAClC,QAAQ,CACP,aACA,eAAe,gBAAgB,KAAK,oBAAoB,cAAc,CACvE;EACD;EACA;GACC,OAAO,SAAS,KAAK,WAAW;GAChC,QAAQ,CACP,eAAe,eAAe,KAAK,mBAAmB,cAAc,GACpE;IAAE,MAAM;IAAU,MAAM;IAAY,OAAO,SAAS,KAAK,YAAY;GAAE,CACxE;EACD;CACD;AACD"}