@10x-media/form-builder 0.1.0-beta.10 → 0.1.0-beta.12

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 (182) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/actions/assertNoBlockCollision.js +18 -0
  3. package/dist/actions/assertNoBlockCollision.js.map +1 -0
  4. package/dist/actions/builtin/confirmation.js +2 -1
  5. package/dist/actions/builtin/confirmation.js.map +1 -1
  6. package/dist/actions/builtin/emailAction.d.ts +3 -1
  7. package/dist/actions/builtin/emailAction.js +43 -12
  8. package/dist/actions/builtin/emailAction.js.map +1 -1
  9. package/dist/actions/builtin/emailTeam.js +11 -5
  10. package/dist/actions/builtin/emailTeam.js.map +1 -1
  11. package/dist/actions/defineAction.d.ts +3 -1
  12. package/dist/actions/defineAction.js.map +1 -1
  13. package/dist/actions/dispatch.js +1 -1
  14. package/dist/actions/dispatch.js.map +1 -1
  15. package/dist/actions/emailRecipients.js +34 -7
  16. package/dist/actions/emailRecipients.js.map +1 -1
  17. package/dist/actions/recipientSources.d.ts +34 -0
  18. package/dist/actions/recipientSources.js +11 -0
  19. package/dist/actions/recipientSources.js.map +1 -0
  20. package/dist/actions/task.js +20 -0
  21. package/dist/actions/task.js.map +1 -1
  22. package/dist/aggregation/resolveResultsRequest.d.ts +6 -0
  23. package/dist/aggregation/resolveResultsRequest.js +32 -3
  24. package/dist/aggregation/resolveResultsRequest.js.map +1 -1
  25. package/dist/calc/computeCalcFields.d.ts +5 -3
  26. package/dist/calc/computeCalcFields.js +4 -3
  27. package/dist/calc/computeCalcFields.js.map +1 -1
  28. package/dist/calc/evaluate.d.ts +26 -3
  29. package/dist/calc/evaluate.js +33 -11
  30. package/dist/calc/evaluate.js.map +1 -1
  31. package/dist/calc/formatCalc.d.ts +11 -0
  32. package/dist/calc/formatCalc.js +35 -0
  33. package/dist/calc/formatCalc.js.map +1 -0
  34. package/dist/calc/formatCalcValue.d.ts +17 -0
  35. package/dist/calc/formatCalcValue.js +17 -0
  36. package/dist/calc/formatCalcValue.js.map +1 -0
  37. package/dist/calc/normalizeCalc.d.ts +17 -3
  38. package/dist/calc/normalizeCalc.js +39 -17
  39. package/dist/calc/normalizeCalc.js.map +1 -1
  40. package/dist/calc/registry.d.ts +43 -0
  41. package/dist/calc/registry.js +27 -0
  42. package/dist/calc/registry.js.map +1 -0
  43. package/dist/calc/resolveCalcContext.d.ts +36 -0
  44. package/dist/calc/resolveCalcContext.js +87 -0
  45. package/dist/calc/resolveCalcContext.js.map +1 -0
  46. package/dist/calc/types.d.ts +18 -3
  47. package/dist/calc/types.js +2 -1
  48. package/dist/calc/types.js.map +1 -1
  49. package/dist/client/CalcExpressionBuilder.d.ts +20 -0
  50. package/dist/client/CalcExpressionBuilder.js +890 -0
  51. package/dist/client/CalcExpressionBuilder.js.map +1 -0
  52. package/dist/client/RecipientsSelect.d.ts +5 -1
  53. package/dist/client/RecipientsSelect.js +22 -3
  54. package/dist/client/RecipientsSelect.js.map +1 -1
  55. package/dist/client/admin.css +282 -0
  56. package/dist/collections/formSubmissions.js +34 -16
  57. package/dist/collections/formSubmissions.js.map +1 -1
  58. package/dist/collections/forms.js +59 -27
  59. package/dist/collections/forms.js.map +1 -1
  60. package/dist/collections/formsEndpoints.js +4 -2
  61. package/dist/collections/formsEndpoints.js.map +1 -1
  62. package/dist/collections/settingsFields.d.ts +31 -0
  63. package/dist/collections/settingsFields.js +45 -0
  64. package/dist/collections/settingsFields.js.map +1 -0
  65. package/dist/context/formContext.d.ts +30 -0
  66. package/dist/context/formContext.js +54 -0
  67. package/dist/context/formContext.js.map +1 -0
  68. package/dist/exports/client.d.ts +2 -1
  69. package/dist/exports/client.js +2 -1
  70. package/dist/exports/react.d.ts +7 -3
  71. package/dist/exports/react.js +6 -2
  72. package/dist/exports/rsc.d.ts +2 -1
  73. package/dist/exports/rsc.js +3 -2
  74. package/dist/exports/types.d.ts +1 -1
  75. package/dist/fields/buildFieldBlocks.js +2 -1
  76. package/dist/fields/buildFieldBlocks.js.map +1 -1
  77. package/dist/fields/builtin/calculation.d.ts +17 -0
  78. package/dist/fields/builtin/calculation.js +170 -115
  79. package/dist/fields/builtin/calculation.js.map +1 -1
  80. package/dist/fields/builtin/checkbox.js +15 -0
  81. package/dist/fields/builtin/checkbox.js.map +1 -1
  82. package/dist/fields/builtin/email.js +7 -0
  83. package/dist/fields/builtin/email.js.map +1 -1
  84. package/dist/fields/builtin/index.d.ts +6 -2
  85. package/dist/fields/builtin/index.js +9 -4
  86. package/dist/fields/builtin/index.js.map +1 -1
  87. package/dist/fields/builtin/select.js +20 -1
  88. package/dist/fields/builtin/select.js.map +1 -1
  89. package/dist/fields/builtin/text.js +7 -0
  90. package/dist/fields/builtin/text.js.map +1 -1
  91. package/dist/fields/instanceOptions.js +1 -1
  92. package/dist/fields/instanceOptions.js.map +1 -1
  93. package/dist/fields/sharedConfig.js +15 -11
  94. package/dist/fields/sharedConfig.js.map +1 -1
  95. package/dist/fields/types.d.ts +8 -2
  96. package/dist/form/toFormDocument.d.ts +7 -0
  97. package/dist/form/toFormDocument.js +2 -1
  98. package/dist/form/toFormDocument.js.map +1 -1
  99. package/dist/form/types.d.ts +7 -0
  100. package/dist/index.d.ts +14 -4
  101. package/dist/index.js +40 -9
  102. package/dist/index.js.map +1 -1
  103. package/dist/options.d.ts +42 -0
  104. package/dist/plugin/registerCollections.js +29 -19
  105. package/dist/plugin/registerCollections.js.map +1 -1
  106. package/dist/poll/closeJob.d.ts +4 -3
  107. package/dist/poll/closeJob.js +6 -4
  108. package/dist/poll/closeJob.js.map +1 -1
  109. package/dist/poll/pollTypeRegistry.js +1 -1
  110. package/dist/poll/resolvePollCloseRequest.js +2 -1
  111. package/dist/poll/resolvePollCloseRequest.js.map +1 -1
  112. package/dist/poll/resolvePollOutcome.d.ts +6 -0
  113. package/dist/poll/resolvePollOutcome.js +17 -3
  114. package/dist/poll/resolvePollOutcome.js.map +1 -1
  115. package/dist/poll/votes/aggregateFromVotes.d.ts +27 -0
  116. package/dist/poll/votes/aggregateFromVotes.js +53 -0
  117. package/dist/poll/votes/aggregateFromVotes.js.map +1 -0
  118. package/dist/poll/votes/bumpPollVote.js +72 -0
  119. package/dist/poll/votes/bumpPollVote.js.map +1 -0
  120. package/dist/poll/votes/recountPollVotes.d.ts +20 -0
  121. package/dist/poll/votes/recountPollVotes.js +63 -0
  122. package/dist/poll/votes/recountPollVotes.js.map +1 -0
  123. package/dist/poll/votes/voteTallyHook.js +58 -0
  124. package/dist/poll/votes/voteTallyHook.js.map +1 -0
  125. package/dist/poll/votes/votesCollection.d.ts +19 -0
  126. package/dist/poll/votes/votesCollection.js +89 -0
  127. package/dist/poll/votes/votesCollection.js.map +1 -0
  128. package/dist/react/Form.d.ts +15 -1
  129. package/dist/react/Form.js +24 -3
  130. package/dist/react/Form.js.map +1 -1
  131. package/dist/react/FormContext.d.ts +8 -1
  132. package/dist/react/FormContext.js.map +1 -1
  133. package/dist/react/primitives/Checkbox.d.ts +4 -2
  134. package/dist/react/primitives/Checkbox.js +3 -2
  135. package/dist/react/primitives/Checkbox.js.map +1 -1
  136. package/dist/react/primitives/ChoiceGroup.d.ts +40 -0
  137. package/dist/react/primitives/ChoiceGroup.js +33 -0
  138. package/dist/react/primitives/ChoiceGroup.js.map +1 -0
  139. package/dist/react/primitives/FieldShell.d.ts +11 -1
  140. package/dist/react/primitives/FieldShell.js +43 -33
  141. package/dist/react/primitives/FieldShell.js.map +1 -1
  142. package/dist/react/primitives/Input.d.ts +3 -1
  143. package/dist/react/primitives/Input.js +2 -1
  144. package/dist/react/primitives/Input.js.map +1 -1
  145. package/dist/react/renderers/calculation.js +6 -1
  146. package/dist/react/renderers/calculation.js.map +1 -1
  147. package/dist/react/renderers/checkbox.js +2 -1
  148. package/dist/react/renderers/checkbox.js.map +1 -1
  149. package/dist/react/renderers/email.js +3 -1
  150. package/dist/react/renderers/email.js.map +1 -1
  151. package/dist/react/renderers/select.js +20 -2
  152. package/dist/react/renderers/select.js.map +1 -1
  153. package/dist/react/renderers/text.js +3 -1
  154. package/dist/react/renderers/text.js.map +1 -1
  155. package/dist/react/useCalcValues.d.ts +10 -0
  156. package/dist/react/useCalcValues.js +13 -0
  157. package/dist/react/useCalcValues.js.map +1 -0
  158. package/dist/recall/resolver.js +1 -1
  159. package/dist/recall/resolver.js.map +1 -1
  160. package/dist/spam/constants.js +7 -1
  161. package/dist/spam/constants.js.map +1 -1
  162. package/dist/submissions/formIdOf.js +13 -0
  163. package/dist/submissions/formIdOf.js.map +1 -0
  164. package/dist/submissions/runSubmission.js +3 -3
  165. package/dist/submissions/runSubmission.js.map +1 -1
  166. package/dist/submissions/validateSubmission.js +26 -2
  167. package/dist/submissions/validateSubmission.js.map +1 -1
  168. package/dist/submissions/verifyContext.js +35 -0
  169. package/dist/submissions/verifyContext.js.map +1 -0
  170. package/dist/translations/de.js +62 -3
  171. package/dist/translations/de.js.map +1 -1
  172. package/dist/translations/en.js +62 -3
  173. package/dist/translations/en.js.map +1 -1
  174. package/dist/translations/keys.d.ts +60 -1
  175. package/dist/translations/keys.js +60 -1
  176. package/dist/translations/keys.js.map +1 -1
  177. package/dist/validation/builtin/index.js +2 -0
  178. package/dist/validation/builtin/index.js.map +1 -1
  179. package/dist/validation/builtin/integer.js +15 -0
  180. package/dist/validation/builtin/integer.js.map +1 -0
  181. package/package.json +11 -5
  182. package/styles/form-builder.css +84 -0
@@ -1,144 +1,199 @@
1
1
  import { keys } from "../../translations/keys.js";
2
2
  import { asTranslate, labelFor } from "../../translations/server.js";
3
+ import { localizedIf } from "../localizedIf.js";
3
4
  import { CALC_FNS, CALC_OPS } from "../../calc/types.js";
5
+ import { formatCalcValue } from "../../calc/formatCalcValue.js";
4
6
  import { normalizeCalc } from "../../calc/normalizeCalc.js";
5
7
  import { defineFormField } from "../defineFormField.js";
6
8
  //#region src/fields/builtin/calculation.ts
7
9
  /**
8
10
  * Editor-facing JSON schema for `CalcExpression` (src/calc/types.ts), used by the admin JSON
9
- * field's Monaco editor for autocomplete and inline diagnostics. The real validity gate is
10
- * `validateExpression`/`normalizeCalc`; this schema is a convenience, not the source of truth.
11
+ * field's Monaco editor for autocomplete and inline diagnostics. Built per boot so registered
12
+ * extension names (custom functions, source keys) autocomplete too. The real validity gate is
13
+ * `buildValidateExpression`/`normalizeCalc`; this schema is a convenience, not the source of truth.
11
14
  */
12
- const calcExpressionSchema = {
13
- uri: "https://10xmedia.de/schemas/form-builder/calc-expression.json",
14
- fileMatch: ["https://10xmedia.de/schemas/form-builder/calc-expression.json"],
15
- schema: {
16
- $ref: "#/definitions/expr",
17
- definitions: {
18
- expr: { oneOf: [
19
- { $ref: "#/definitions/lit" },
20
- { $ref: "#/definitions/ref" },
21
- { $ref: "#/definitions/op" },
22
- { $ref: "#/definitions/neg" },
23
- { $ref: "#/definitions/fn" },
24
- { $ref: "#/definitions/weight" }
25
- ] },
26
- lit: {
27
- type: "object",
28
- properties: {
29
- type: { enum: ["lit"] },
30
- value: { type: "number" }
15
+ const buildCalcExpressionSchema = (allowed) => {
16
+ const sourceKeys = [...allowed?.sources ?? []];
17
+ const fnNames = [...CALC_FNS, ...allowed?.functions ?? []];
18
+ return {
19
+ uri: "https://10xmedia.de/schemas/form-builder/calc-expression.json",
20
+ fileMatch: ["https://10xmedia.de/schemas/form-builder/calc-expression.json"],
21
+ schema: {
22
+ $ref: "#/definitions/expr",
23
+ definitions: {
24
+ expr: { oneOf: [
25
+ { $ref: "#/definitions/lit" },
26
+ { $ref: "#/definitions/ref" },
27
+ { $ref: "#/definitions/op" },
28
+ { $ref: "#/definitions/neg" },
29
+ { $ref: "#/definitions/fn" },
30
+ { $ref: "#/definitions/weight" },
31
+ ...sourceKeys.length > 0 ? [{ $ref: "#/definitions/source" }] : []
32
+ ] },
33
+ lit: {
34
+ type: "object",
35
+ properties: {
36
+ type: { enum: ["lit"] },
37
+ value: { type: "number" }
38
+ },
39
+ required: ["type", "value"],
40
+ additionalProperties: false
31
41
  },
32
- required: ["type", "value"],
33
- additionalProperties: false
34
- },
35
- ref: {
36
- type: "object",
37
- properties: {
38
- type: { enum: ["ref"] },
39
- field: { type: "string" }
40
- },
41
- required: ["type", "field"],
42
- additionalProperties: false
43
- },
44
- op: {
45
- type: "object",
46
- properties: {
47
- type: { enum: ["op"] },
48
- op: { enum: [...CALC_OPS] },
49
- left: { $ref: "#/definitions/expr" },
50
- right: { $ref: "#/definitions/expr" }
42
+ ref: {
43
+ type: "object",
44
+ properties: {
45
+ type: { enum: ["ref"] },
46
+ field: { type: "string" }
47
+ },
48
+ required: ["type", "field"],
49
+ additionalProperties: false
51
50
  },
52
- required: [
53
- "type",
54
- "op",
55
- "left",
56
- "right"
57
- ],
58
- additionalProperties: false
59
- },
60
- neg: {
61
- type: "object",
62
- properties: {
63
- type: { enum: ["neg"] },
64
- operand: { $ref: "#/definitions/expr" }
51
+ op: {
52
+ type: "object",
53
+ properties: {
54
+ type: { enum: ["op"] },
55
+ op: { enum: [...CALC_OPS] },
56
+ left: { $ref: "#/definitions/expr" },
57
+ right: { $ref: "#/definitions/expr" }
58
+ },
59
+ required: [
60
+ "type",
61
+ "op",
62
+ "left",
63
+ "right"
64
+ ],
65
+ additionalProperties: false
65
66
  },
66
- required: ["type", "operand"],
67
- additionalProperties: false
68
- },
69
- fn: {
70
- type: "object",
71
- properties: {
72
- type: { enum: ["fn"] },
73
- fn: { enum: [...CALC_FNS] },
74
- args: {
75
- type: "array",
76
- items: { $ref: "#/definitions/expr" }
77
- }
67
+ neg: {
68
+ type: "object",
69
+ properties: {
70
+ type: { enum: ["neg"] },
71
+ operand: { $ref: "#/definitions/expr" }
72
+ },
73
+ required: ["type", "operand"],
74
+ additionalProperties: false
78
75
  },
79
- required: [
80
- "type",
81
- "fn",
82
- "args"
83
- ],
84
- additionalProperties: false
85
- },
86
- weight: {
87
- type: "object",
88
- properties: {
89
- type: { enum: ["weight"] },
90
- field: { type: "string" },
91
- weights: {
92
- type: "object",
93
- additionalProperties: { type: "number" }
94
- }
76
+ fn: {
77
+ type: "object",
78
+ properties: {
79
+ type: { enum: ["fn"] },
80
+ fn: { enum: fnNames },
81
+ args: {
82
+ type: "array",
83
+ items: { $ref: "#/definitions/expr" }
84
+ }
85
+ },
86
+ required: [
87
+ "type",
88
+ "fn",
89
+ "args"
90
+ ],
91
+ additionalProperties: false
95
92
  },
96
- required: [
97
- "type",
98
- "field",
99
- "weights"
100
- ],
101
- additionalProperties: false
93
+ ...sourceKeys.length > 0 ? { source: {
94
+ type: "object",
95
+ properties: {
96
+ type: { enum: ["source"] },
97
+ source: { enum: sourceKeys }
98
+ },
99
+ required: ["type", "source"],
100
+ additionalProperties: false
101
+ } } : {},
102
+ weight: {
103
+ type: "object",
104
+ properties: {
105
+ type: { enum: ["weight"] },
106
+ field: { type: "string" },
107
+ weights: {
108
+ type: "object",
109
+ additionalProperties: { type: "number" }
110
+ },
111
+ ...sourceKeys.length > 0 ? { source: { enum: sourceKeys } } : {}
112
+ },
113
+ required: sourceKeys.length > 0 ? ["type", "field"] : [
114
+ "type",
115
+ "field",
116
+ "weights"
117
+ ],
118
+ additionalProperties: false
119
+ }
102
120
  }
103
121
  }
104
- }
122
+ };
105
123
  };
106
124
  /**
107
- * Validates the calculation field's `expression`: unset/empty is fine, otherwise it must
108
- * normalize into a well-formed `CalcExpression` tree. Exported for unit testing.
125
+ * Validates the calculation field's `expression` against the registered extension names: unset/empty
126
+ * is fine, otherwise it must normalize into a well-formed `CalcExpression` tree.
109
127
  */
110
- const validateExpression = (value, { req }) => {
128
+ const buildValidateExpression = (allowed) => (value, { req }) => {
111
129
  if (value == null || value === "") return true;
112
- return normalizeCalc(value) !== void 0 ? true : asTranslate(req.t)(keys.validationCalcExpressionInvalid);
130
+ return normalizeCalc(value, allowed) !== void 0 ? true : asTranslate(req.t)(keys.validationCalcExpressionInvalid);
113
131
  };
114
132
  /**
115
133
  * A calculation is derived and read-only: it renders disabled with no text affordance (no
116
134
  * `placeholder`), and it is never validated (`runSubmission` stores its computed value and skips
117
135
  * `runValidation` entirely), so an authored `required` could never fire. Both are omitted rather
118
- * than offered as settings that do nothing.
136
+ * than offered as settings that do nothing. `allowed` threads the registered calc extension names
137
+ * (plugin option `calc`) into the expression validate and the editor schema; `extras.sources`
138
+ * carries serializable source metadata to the builder component as clientProps (custom function
139
+ * names travel too, so the client-side normalize accepts every expression the server accepts).
119
140
  */
120
- const calculationField = defineFormField({
121
- type: "calculation",
122
- label: keys.fieldTypeCalculation,
123
- value: "number",
124
- omitShared: ["placeholder", "required"],
125
- config: [{
126
- name: "expression",
127
- type: "json",
128
- label: labelFor(keys.configExpression),
129
- admin: { description: labelFor(keys.configExpressionDescription) },
130
- jsonSchema: calcExpressionSchema,
131
- typescriptSchema: [() => ({ type: "object" })],
132
- validate: validateExpression
133
- }, {
134
- name: "calcDisplay",
135
- type: "checkbox",
136
- defaultValue: true,
137
- label: labelFor(keys.configCalcDisplay)
138
- }],
139
- format: ({ value }) => value == null ? "" : String(value)
140
- });
141
+ const buildCalculationField = (allowed, extras) => {
142
+ const sources = extras?.sources ?? [];
143
+ const functions = [...allowed?.functions ?? []];
144
+ const builderRef = "@10x-media/form-builder/client#CalcExpressionBuilder";
145
+ return defineFormField({
146
+ type: "calculation",
147
+ label: keys.fieldTypeCalculation,
148
+ value: "number",
149
+ omitShared: ["placeholder", "required"],
150
+ config: [
151
+ {
152
+ name: "expression",
153
+ type: "json",
154
+ label: labelFor(keys.configExpression),
155
+ admin: { components: { Field: sources.length > 0 || functions.length > 0 ? {
156
+ path: builderRef,
157
+ clientProps: {
158
+ sources,
159
+ functions
160
+ }
161
+ } : builderRef } },
162
+ jsonSchema: buildCalcExpressionSchema(allowed),
163
+ typescriptSchema: [() => ({ type: "object" })],
164
+ validate: buildValidateExpression(allowed)
165
+ },
166
+ {
167
+ name: "calcDisplay",
168
+ type: "checkbox",
169
+ defaultValue: true,
170
+ label: labelFor(keys.configCalcDisplay)
171
+ },
172
+ {
173
+ name: "decimals",
174
+ type: "number",
175
+ min: 0,
176
+ max: 6,
177
+ label: labelFor(keys.calcConfigDecimals)
178
+ },
179
+ {
180
+ name: "prefix",
181
+ type: "text",
182
+ label: labelFor(keys.calcConfigPrefix),
183
+ ...localizedIf(extras?.localize === true)
184
+ },
185
+ {
186
+ name: "suffix",
187
+ type: "text",
188
+ label: labelFor(keys.calcConfigSuffix),
189
+ ...localizedIf(extras?.localize === true)
190
+ }
191
+ ],
192
+ format: ({ value, config }) => value == null ? "" : formatCalcValue(value, config)
193
+ });
194
+ };
195
+ buildCalculationField();
141
196
  //#endregion
142
- export { calculationField };
197
+ export { buildCalculationField };
143
198
 
144
199
  //# sourceMappingURL=calculation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"calculation.js","names":[],"sources":["../../../src/fields/builtin/calculation.ts"],"sourcesContent":["import type { JSONField, PayloadRequest } from 'payload'\nimport { normalizeCalc } from '../../calc/normalizeCalc'\nimport { CALC_FNS, CALC_OPS } from '../../calc/types'\nimport { keys } from '../../translations/keys'\nimport { asTranslate, labelFor } from '../../translations/server'\nimport { defineFormField } from '../defineFormField'\n\ntype CalcExpressionSchema = NonNullable<JSONField['jsonSchema']>\n\n/**\n * Editor-facing JSON schema for `CalcExpression` (src/calc/types.ts), used by the admin JSON\n * field's Monaco editor for autocomplete and inline diagnostics. The real validity gate is\n * `validateExpression`/`normalizeCalc`; this schema is a convenience, not the source of truth.\n */\nconst calcExpressionSchema: CalcExpressionSchema = {\n\turi: 'https://10xmedia.de/schemas/form-builder/calc-expression.json',\n\tfileMatch: ['https://10xmedia.de/schemas/form-builder/calc-expression.json'],\n\tschema: {\n\t\t$ref: '#/definitions/expr',\n\t\tdefinitions: {\n\t\t\texpr: {\n\t\t\t\toneOf: [\n\t\t\t\t\t{ $ref: '#/definitions/lit' },\n\t\t\t\t\t{ $ref: '#/definitions/ref' },\n\t\t\t\t\t{ $ref: '#/definitions/op' },\n\t\t\t\t\t{ $ref: '#/definitions/neg' },\n\t\t\t\t\t{ $ref: '#/definitions/fn' },\n\t\t\t\t\t{ $ref: '#/definitions/weight' },\n\t\t\t\t],\n\t\t\t},\n\t\t\tlit: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: { type: { enum: ['lit'] }, value: { type: 'number' } },\n\t\t\t\trequired: ['type', 'value'],\n\t\t\t\tadditionalProperties: false,\n\t\t\t},\n\t\t\tref: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: { type: { enum: ['ref'] }, field: { type: 'string' } },\n\t\t\t\trequired: ['type', 'field'],\n\t\t\t\tadditionalProperties: false,\n\t\t\t},\n\t\t\top: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {\n\t\t\t\t\ttype: { enum: ['op'] },\n\t\t\t\t\top: { enum: [...CALC_OPS] },\n\t\t\t\t\tleft: { $ref: '#/definitions/expr' },\n\t\t\t\t\tright: { $ref: '#/definitions/expr' },\n\t\t\t\t},\n\t\t\t\trequired: ['type', 'op', 'left', 'right'],\n\t\t\t\tadditionalProperties: false,\n\t\t\t},\n\t\t\tneg: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: { type: { enum: ['neg'] }, operand: { $ref: '#/definitions/expr' } },\n\t\t\t\trequired: ['type', 'operand'],\n\t\t\t\tadditionalProperties: false,\n\t\t\t},\n\t\t\tfn: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {\n\t\t\t\t\ttype: { enum: ['fn'] },\n\t\t\t\t\tfn: { enum: [...CALC_FNS] },\n\t\t\t\t\targs: { type: 'array', items: { $ref: '#/definitions/expr' } },\n\t\t\t\t},\n\t\t\t\trequired: ['type', 'fn', 'args'],\n\t\t\t\tadditionalProperties: false,\n\t\t\t},\n\t\t\tweight: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {\n\t\t\t\t\ttype: { enum: ['weight'] },\n\t\t\t\t\tfield: { type: 'string' },\n\t\t\t\t\tweights: { type: 'object', additionalProperties: { type: 'number' } },\n\t\t\t\t},\n\t\t\t\trequired: ['type', 'field', 'weights'],\n\t\t\t\tadditionalProperties: false,\n\t\t\t},\n\t\t},\n\t},\n}\n\n/**\n * Validates the calculation field's `expression`: unset/empty is fine, otherwise it must\n * normalize into a well-formed `CalcExpression` tree. Exported for unit testing.\n */\nexport const validateExpression = (\n\tvalue: unknown,\n\t{ req }: { data?: unknown; req: PayloadRequest }\n): string | true => {\n\tif (value == null || value === '') {\n\t\treturn true\n\t}\n\treturn normalizeCalc(value) !== undefined\n\t\t? true\n\t\t: asTranslate(req.t)(keys.validationCalcExpressionInvalid)\n}\n\n/**\n * A calculation is derived and read-only: it renders disabled with no text affordance (no\n * `placeholder`), and it is never validated (`runSubmission` stores its computed value and skips\n * `runValidation` entirely), so an authored `required` could never fire. Both are omitted rather\n * than offered as settings that do nothing.\n */\nexport const calculationField = defineFormField<'number'>({\n\ttype: 'calculation',\n\tlabel: keys.fieldTypeCalculation,\n\tvalue: 'number',\n\tomitShared: ['placeholder', 'required'],\n\tconfig: [\n\t\t{\n\t\t\tname: 'expression',\n\t\t\ttype: 'json',\n\t\t\tlabel: labelFor(keys.configExpression),\n\t\t\tadmin: { description: labelFor(keys.configExpressionDescription) },\n\t\t\tjsonSchema: calcExpressionSchema,\n\t\t\t// generate:types embeds jsonSchema.schema into the config-level schema, where its\n\t\t\t// document-root-relative $refs cannot resolve; hand type generation a flat shape instead.\n\t\t\ttypescriptSchema: [() => ({ type: 'object' })],\n\t\t\tvalidate: validateExpression,\n\t\t},\n\t\t{\n\t\t\tname: 'calcDisplay',\n\t\t\ttype: 'checkbox',\n\t\t\tdefaultValue: true,\n\t\t\tlabel: labelFor(keys.configCalcDisplay),\n\t\t},\n\t],\n\tformat: ({ value }) => (value == null ? '' : String(value)),\n})\n"],"mappings":";;;;;;;;;;;AAcA,MAAM,uBAA6C;CAClD,KAAK;CACL,WAAW,CAAC,+DAA+D;CAC3E,QAAQ;EACP,MAAM;EACN,aAAa;GACZ,MAAM,EACL,OAAO;IACN,EAAE,MAAM,oBAAoB;IAC5B,EAAE,MAAM,oBAAoB;IAC5B,EAAE,MAAM,mBAAmB;IAC3B,EAAE,MAAM,oBAAoB;IAC5B,EAAE,MAAM,mBAAmB;IAC3B,EAAE,MAAM,uBAAuB;GAChC,EACD;GACA,KAAK;IACJ,MAAM;IACN,YAAY;KAAE,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE;KAAG,OAAO,EAAE,MAAM,SAAS;IAAE;IACjE,UAAU,CAAC,QAAQ,OAAO;IAC1B,sBAAsB;GACvB;GACA,KAAK;IACJ,MAAM;IACN,YAAY;KAAE,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE;KAAG,OAAO,EAAE,MAAM,SAAS;IAAE;IACjE,UAAU,CAAC,QAAQ,OAAO;IAC1B,sBAAsB;GACvB;GACA,IAAI;IACH,MAAM;IACN,YAAY;KACX,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE;KACrB,IAAI,EAAE,MAAM,CAAC,GAAG,QAAQ,EAAE;KAC1B,MAAM,EAAE,MAAM,qBAAqB;KACnC,OAAO,EAAE,MAAM,qBAAqB;IACrC;IACA,UAAU;KAAC;KAAQ;KAAM;KAAQ;IAAO;IACxC,sBAAsB;GACvB;GACA,KAAK;IACJ,MAAM;IACN,YAAY;KAAE,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE;KAAG,SAAS,EAAE,MAAM,qBAAqB;IAAE;IAC/E,UAAU,CAAC,QAAQ,SAAS;IAC5B,sBAAsB;GACvB;GACA,IAAI;IACH,MAAM;IACN,YAAY;KACX,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE;KACrB,IAAI,EAAE,MAAM,CAAC,GAAG,QAAQ,EAAE;KAC1B,MAAM;MAAE,MAAM;MAAS,OAAO,EAAE,MAAM,qBAAqB;KAAE;IAC9D;IACA,UAAU;KAAC;KAAQ;KAAM;IAAM;IAC/B,sBAAsB;GACvB;GACA,QAAQ;IACP,MAAM;IACN,YAAY;KACX,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;KACzB,OAAO,EAAE,MAAM,SAAS;KACxB,SAAS;MAAE,MAAM;MAAU,sBAAsB,EAAE,MAAM,SAAS;KAAE;IACrE;IACA,UAAU;KAAC;KAAQ;KAAS;IAAS;IACrC,sBAAsB;GACvB;EACD;CACD;AACD;;;;;AAMA,MAAa,sBACZ,OACA,EAAE,UACiB;CACnB,IAAI,SAAS,QAAQ,UAAU,IAC9B,OAAO;CAER,OAAO,cAAc,KAAK,MAAM,KAAA,IAC7B,OACA,YAAY,IAAI,CAAC,EAAE,KAAK,+BAA+B;AAC3D;;;;;;;AAQA,MAAa,mBAAmB,gBAA0B;CACzD,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,YAAY,CAAC,eAAe,UAAU;CACtC,QAAQ,CACP;EACC,MAAM;EACN,MAAM;EACN,OAAO,SAAS,KAAK,gBAAgB;EACrC,OAAO,EAAE,aAAa,SAAS,KAAK,2BAA2B,EAAE;EACjE,YAAY;EAGZ,kBAAkB,QAAQ,EAAE,MAAM,SAAS,EAAE;EAC7C,UAAU;CACX,GACA;EACC,MAAM;EACN,MAAM;EACN,cAAc;EACd,OAAO,SAAS,KAAK,iBAAiB;CACvC,CACD;CACA,SAAS,EAAE,YAAa,SAAS,OAAO,KAAK,OAAO,KAAK;AAC1D,CAAC"}
1
+ {"version":3,"file":"calculation.js","names":[],"sources":["../../../src/fields/builtin/calculation.ts"],"sourcesContent":["import type { JSONField, PayloadRequest } from 'payload'\nimport { formatCalcValue } from '../../calc/formatCalcValue'\nimport { type CalcAllowed, normalizeCalc } from '../../calc/normalizeCalc'\nimport { CALC_FNS, CALC_OPS } from '../../calc/types'\nimport { keys } from '../../translations/keys'\nimport { asTranslate, labelFor } from '../../translations/server'\nimport { defineFormField } from '../defineFormField'\nimport { localizedIf } from '../localizedIf'\n\n/**\n * Serializable calc-source metadata for the admin builder (threaded as clientProps): key, display\n * label, and which modes the source implements (`scalar` = has `resolve`, `weights` = has\n * `resolveWeights`). Derived where the registry is resolved; resolver functions never serialize.\n */\nexport type CalcSourceClientMeta = {\n\tkey: string\n\tlabel: string | Record<string, string>\n\tscalar: boolean\n\tweights: boolean\n}\n\ntype BuildCalculationFieldExtras = {\n\t/** Whether content-bearing display config (`prefix`, `suffix`) is localized. */\n\tlocalize?: boolean\n\t/** Registered calc-source metadata for the builder UI. */\n\tsources?: CalcSourceClientMeta[]\n}\n\n/** The calculation field's own config values (display settings; `expression` is handled separately). */\ntype CalculationConfig = {\n\tdecimals?: number\n\tprefix?: string\n\tsuffix?: string\n}\n\ntype CalcExpressionSchema = NonNullable<JSONField['jsonSchema']>\n\n/**\n * Editor-facing JSON schema for `CalcExpression` (src/calc/types.ts), used by the admin JSON\n * field's Monaco editor for autocomplete and inline diagnostics. Built per boot so registered\n * extension names (custom functions, source keys) autocomplete too. The real validity gate is\n * `buildValidateExpression`/`normalizeCalc`; this schema is a convenience, not the source of truth.\n */\nconst buildCalcExpressionSchema = (allowed?: CalcAllowed): CalcExpressionSchema => {\n\tconst sourceKeys = [...(allowed?.sources ?? [])]\n\tconst fnNames = [...CALC_FNS, ...(allowed?.functions ?? [])]\n\treturn {\n\t\turi: 'https://10xmedia.de/schemas/form-builder/calc-expression.json',\n\t\tfileMatch: ['https://10xmedia.de/schemas/form-builder/calc-expression.json'],\n\t\tschema: {\n\t\t\t$ref: '#/definitions/expr',\n\t\t\tdefinitions: {\n\t\t\t\texpr: {\n\t\t\t\t\toneOf: [\n\t\t\t\t\t\t{ $ref: '#/definitions/lit' },\n\t\t\t\t\t\t{ $ref: '#/definitions/ref' },\n\t\t\t\t\t\t{ $ref: '#/definitions/op' },\n\t\t\t\t\t\t{ $ref: '#/definitions/neg' },\n\t\t\t\t\t\t{ $ref: '#/definitions/fn' },\n\t\t\t\t\t\t{ $ref: '#/definitions/weight' },\n\t\t\t\t\t\t...(sourceKeys.length > 0 ? [{ $ref: '#/definitions/source' }] : []),\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tlit: {\n\t\t\t\t\ttype: 'object',\n\t\t\t\t\tproperties: { type: { enum: ['lit'] }, value: { type: 'number' } },\n\t\t\t\t\trequired: ['type', 'value'],\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t},\n\t\t\t\tref: {\n\t\t\t\t\ttype: 'object',\n\t\t\t\t\tproperties: { type: { enum: ['ref'] }, field: { type: 'string' } },\n\t\t\t\t\trequired: ['type', 'field'],\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t},\n\t\t\t\top: {\n\t\t\t\t\ttype: 'object',\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\ttype: { enum: ['op'] },\n\t\t\t\t\t\top: { enum: [...CALC_OPS] },\n\t\t\t\t\t\tleft: { $ref: '#/definitions/expr' },\n\t\t\t\t\t\tright: { $ref: '#/definitions/expr' },\n\t\t\t\t\t},\n\t\t\t\t\trequired: ['type', 'op', 'left', 'right'],\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t},\n\t\t\t\tneg: {\n\t\t\t\t\ttype: 'object',\n\t\t\t\t\tproperties: { type: { enum: ['neg'] }, operand: { $ref: '#/definitions/expr' } },\n\t\t\t\t\trequired: ['type', 'operand'],\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t},\n\t\t\t\tfn: {\n\t\t\t\t\ttype: 'object',\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\ttype: { enum: ['fn'] },\n\t\t\t\t\t\tfn: { enum: fnNames },\n\t\t\t\t\t\targs: { type: 'array', items: { $ref: '#/definitions/expr' } },\n\t\t\t\t\t},\n\t\t\t\t\trequired: ['type', 'fn', 'args'],\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t},\n\t\t\t\t...(sourceKeys.length > 0\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tsource: {\n\t\t\t\t\t\t\t\ttype: 'object',\n\t\t\t\t\t\t\t\tproperties: { type: { enum: ['source'] }, source: { enum: sourceKeys } },\n\t\t\t\t\t\t\t\trequired: ['type', 'source'],\n\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t\tweight: {\n\t\t\t\t\ttype: 'object',\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\ttype: { enum: ['weight'] },\n\t\t\t\t\t\tfield: { type: 'string' },\n\t\t\t\t\t\tweights: { type: 'object', additionalProperties: { type: 'number' } },\n\t\t\t\t\t\t...(sourceKeys.length > 0 ? { source: { enum: sourceKeys } } : {}),\n\t\t\t\t\t},\n\t\t\t\t\t// With sources registered a weight may carry `source` instead of inline weights; the\n\t\t\t\t\t// either-or itself is enforced by normalizeCalc, not this convenience schema.\n\t\t\t\t\trequired: sourceKeys.length > 0 ? ['type', 'field'] : ['type', 'field', 'weights'],\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n}\n\n/**\n * Validates the calculation field's `expression` against the registered extension names: unset/empty\n * is fine, otherwise it must normalize into a well-formed `CalcExpression` tree.\n */\nexport const buildValidateExpression =\n\t(allowed?: CalcAllowed) =>\n\t(value: unknown, { req }: { data?: unknown; req: PayloadRequest }): string | true => {\n\t\tif (value == null || value === '') {\n\t\t\treturn true\n\t\t}\n\t\treturn normalizeCalc(value, allowed) !== undefined\n\t\t\t? true\n\t\t\t: asTranslate(req.t)(keys.validationCalcExpressionInvalid)\n\t}\n\n/** The no-extensions validate (built-in grammar only). Exported for unit testing and spread consumers. */\nexport const validateExpression = buildValidateExpression()\n\n/**\n * A calculation is derived and read-only: it renders disabled with no text affordance (no\n * `placeholder`), and it is never validated (`runSubmission` stores its computed value and skips\n * `runValidation` entirely), so an authored `required` could never fire. Both are omitted rather\n * than offered as settings that do nothing. `allowed` threads the registered calc extension names\n * (plugin option `calc`) into the expression validate and the editor schema; `extras.sources`\n * carries serializable source metadata to the builder component as clientProps (custom function\n * names travel too, so the client-side normalize accepts every expression the server accepts).\n */\nexport const buildCalculationField = (\n\tallowed?: CalcAllowed,\n\textras?: BuildCalculationFieldExtras\n) => {\n\tconst sources = extras?.sources ?? []\n\tconst functions = [...(allowed?.functions ?? [])]\n\tconst builderRef = '@10x-media/form-builder/client#CalcExpressionBuilder'\n\treturn defineFormField<'number', CalculationConfig>({\n\t\ttype: 'calculation',\n\t\tlabel: keys.fieldTypeCalculation,\n\t\tvalue: 'number',\n\t\tomitShared: ['placeholder', 'required'],\n\t\tconfig: [\n\t\t\t{\n\t\t\t\tname: 'expression',\n\t\t\t\ttype: 'json',\n\t\t\t\tlabel: labelFor(keys.configExpression),\n\t\t\t\tadmin: {\n\t\t\t\t\tcomponents: {\n\t\t\t\t\t\tField:\n\t\t\t\t\t\t\tsources.length > 0 || functions.length > 0\n\t\t\t\t\t\t\t\t? { path: builderRef, clientProps: { sources, functions } }\n\t\t\t\t\t\t\t\t: builderRef,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tjsonSchema: buildCalcExpressionSchema(allowed),\n\t\t\t\t// generate:types embeds jsonSchema.schema into the config-level schema, where its\n\t\t\t\t// document-root-relative $refs cannot resolve; hand type generation a flat shape instead.\n\t\t\t\ttypescriptSchema: [() => ({ type: 'object' })],\n\t\t\t\tvalidate: buildValidateExpression(allowed),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'calcDisplay',\n\t\t\t\ttype: 'checkbox',\n\t\t\t\tdefaultValue: true,\n\t\t\t\tlabel: labelFor(keys.configCalcDisplay),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'decimals',\n\t\t\t\ttype: 'number',\n\t\t\t\tmin: 0,\n\t\t\t\tmax: 6,\n\t\t\t\tlabel: labelFor(keys.calcConfigDecimals),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'prefix',\n\t\t\t\ttype: 'text',\n\t\t\t\tlabel: labelFor(keys.calcConfigPrefix),\n\t\t\t\t...localizedIf(extras?.localize === true),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'suffix',\n\t\t\t\ttype: 'text',\n\t\t\t\tlabel: labelFor(keys.calcConfigSuffix),\n\t\t\t\t...localizedIf(extras?.localize === true),\n\t\t\t},\n\t\t],\n\t\tformat: ({ value, config }) => (value == null ? '' : formatCalcValue(value, config)),\n\t})\n}\n\n/** The no-extensions definition, for definition-spread consumers (the `buildSelectField`/`selectField` precedent). */\nexport const calculationField = buildCalculationField()\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { keys } from "../../translations/keys.js";
2
+ import { labelFor } from "../../translations/server.js";
2
3
  import { defineFormField } from "../defineFormField.js";
3
4
  //#region src/fields/builtin/checkbox.ts
4
5
  /**
@@ -12,6 +13,20 @@ const checkboxField = defineFormField({
12
13
  label: keys.fieldTypeCheckbox,
13
14
  value: "boolean",
14
15
  omitShared: ["placeholder"],
16
+ config: [{
17
+ name: "display",
18
+ type: "select",
19
+ defaultValue: "checkbox",
20
+ label: labelFor(keys.configCheckboxDisplay),
21
+ admin: { isClearable: false },
22
+ options: [{
23
+ label: labelFor(keys.checkboxDisplayCheckbox),
24
+ value: "checkbox"
25
+ }, {
26
+ label: labelFor(keys.checkboxDisplaySwitch),
27
+ value: "switch"
28
+ }]
29
+ }],
15
30
  validate: ({ value, config, t }) => config.required === true && value !== true ? t(keys.validationRequired) : true,
16
31
  format: ({ value, t }) => value ? t(keys.formatYes) : t(keys.formatNo)
17
32
  });
@@ -1 +1 @@
1
- {"version":3,"file":"checkbox.js","names":[],"sources":["../../../src/fields/builtin/checkbox.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { defineFormField } from '../defineFormField'\n\ntype CheckboxConfig = { required?: boolean }\n\n/**\n * A checkbox renders as a lone box with no free-text affordance, so `placeholder` is never read.\n * The intrinsic validator owns `required`: the engine's shared required guard only fires on an\n * empty value, and `false` (checked, then unchecked) is a present one, so without this a required\n * checkbox would accept an explicit refusal.\n */\nexport const checkboxField = defineFormField<'boolean', CheckboxConfig>({\n\ttype: 'checkbox',\n\tlabel: keys.fieldTypeCheckbox,\n\tvalue: 'boolean',\n\tomitShared: ['placeholder'],\n\tvalidate: ({ value, config, t }) =>\n\t\tconfig.required === true && value !== true ? t(keys.validationRequired) : true,\n\tformat: ({ value, t }) => (value ? t(keys.formatYes) : t(keys.formatNo)),\n})\n"],"mappings":";;;;;;;;;AAWA,MAAa,gBAAgB,gBAA2C;CACvE,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,YAAY,CAAC,aAAa;CAC1B,WAAW,EAAE,OAAO,QAAQ,QAC3B,OAAO,aAAa,QAAQ,UAAU,OAAO,EAAE,KAAK,kBAAkB,IAAI;CAC3E,SAAS,EAAE,OAAO,QAAS,QAAQ,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,QAAQ;AACvE,CAAC"}
1
+ {"version":3,"file":"checkbox.js","names":[],"sources":["../../../src/fields/builtin/checkbox.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport { defineFormField } from '../defineFormField'\n\ntype CheckboxConfig = { required?: boolean; display?: 'checkbox' | 'switch' }\n\n/**\n * A checkbox renders as a lone box with no free-text affordance, so `placeholder` is never read.\n * The intrinsic validator owns `required`: the engine's shared required guard only fires on an\n * empty value, and `false` (checked, then unchecked) is a present one, so without this a required\n * checkbox would accept an explicit refusal.\n */\nexport const checkboxField = defineFormField<'boolean', CheckboxConfig>({\n\ttype: 'checkbox',\n\tlabel: keys.fieldTypeCheckbox,\n\tvalue: 'boolean',\n\tomitShared: ['placeholder'],\n\tconfig: [\n\t\t{\n\t\t\tname: 'display',\n\t\t\ttype: 'select',\n\t\t\tdefaultValue: 'checkbox',\n\t\t\tlabel: labelFor(keys.configCheckboxDisplay),\n\t\t\tadmin: { isClearable: false },\n\t\t\toptions: [\n\t\t\t\t{ label: labelFor(keys.checkboxDisplayCheckbox), value: 'checkbox' },\n\t\t\t\t{ label: labelFor(keys.checkboxDisplaySwitch), value: 'switch' },\n\t\t\t],\n\t\t},\n\t],\n\tvalidate: ({ value, config, t }) =>\n\t\tconfig.required === true && value !== true ? t(keys.validationRequired) : true,\n\tformat: ({ value, t }) => (value ? t(keys.formatYes) : t(keys.formatNo)),\n})\n"],"mappings":";;;;;;;;;;AAYA,MAAa,gBAAgB,gBAA2C;CACvE,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,YAAY,CAAC,aAAa;CAC1B,QAAQ,CACP;EACC,MAAM;EACN,MAAM;EACN,cAAc;EACd,OAAO,SAAS,KAAK,qBAAqB;EAC1C,OAAO,EAAE,aAAa,MAAM;EAC5B,SAAS,CACR;GAAE,OAAO,SAAS,KAAK,uBAAuB;GAAG,OAAO;EAAW,GACnE;GAAE,OAAO,SAAS,KAAK,qBAAqB;GAAG,OAAO;EAAS,CAChE;CACD,CACD;CACA,WAAW,EAAE,OAAO,QAAQ,QAC3B,OAAO,aAAa,QAAQ,UAAU,OAAO,EAAE,KAAK,kBAAkB,IAAI;CAC3E,SAAS,EAAE,OAAO,QAAS,QAAQ,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,QAAQ;AACvE,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { keys } from "../../translations/keys.js";
2
+ import { labelFor } from "../../translations/server.js";
2
3
  import { defineFormField } from "../defineFormField.js";
3
4
  import { EMAIL_PATTERN } from "../../validation/emailPattern.js";
4
5
  //#region src/fields/builtin/email.ts
@@ -6,6 +7,12 @@ const emailField = defineFormField({
6
7
  type: "email",
7
8
  label: keys.fieldTypeEmail,
8
9
  value: "text",
10
+ advancedConfig: [{
11
+ name: "autocomplete",
12
+ type: "text",
13
+ label: labelFor(keys.configAutocomplete),
14
+ admin: { description: labelFor(keys.configAutocompleteDescription) }
15
+ }],
9
16
  validate: ({ value, t }) => {
10
17
  if (value == null || value === "") return true;
11
18
  return EMAIL_PATTERN.test(value) ? true : t(keys.validationEmail);
@@ -1 +1 @@
1
- {"version":3,"file":"email.js","names":[],"sources":["../../../src/fields/builtin/email.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { EMAIL_PATTERN } from '../../validation/emailPattern'\nimport { defineFormField } from '../defineFormField'\n\nexport const emailField = defineFormField<'text'>({\n\ttype: 'email',\n\tlabel: keys.fieldTypeEmail,\n\tvalue: 'text',\n\tvalidate: ({ value, t }) => {\n\t\tif (value == null || value === '') {\n\t\t\treturn true\n\t\t}\n\t\treturn EMAIL_PATTERN.test(value) ? true : t(keys.validationEmail)\n\t},\n\tformat: ({ value }) => (value == null ? '' : String(value)),\n})\n"],"mappings":";;;;AAIA,MAAa,aAAa,gBAAwB;CACjD,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,WAAW,EAAE,OAAO,QAAQ;EAC3B,IAAI,SAAS,QAAQ,UAAU,IAC9B,OAAO;EAER,OAAO,cAAc,KAAK,KAAK,IAAI,OAAO,EAAE,KAAK,eAAe;CACjE;CACA,SAAS,EAAE,YAAa,SAAS,OAAO,KAAK,OAAO,KAAK;AAC1D,CAAC"}
1
+ {"version":3,"file":"email.js","names":[],"sources":["../../../src/fields/builtin/email.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport { EMAIL_PATTERN } from '../../validation/emailPattern'\nimport { defineFormField } from '../defineFormField'\n\nexport const emailField = defineFormField<'text'>({\n\ttype: 'email',\n\tlabel: keys.fieldTypeEmail,\n\tvalue: 'text',\n\tadvancedConfig: [\n\t\t{\n\t\t\tname: 'autocomplete',\n\t\t\ttype: 'text',\n\t\t\tlabel: labelFor(keys.configAutocomplete),\n\t\t\tadmin: { description: labelFor(keys.configAutocompleteDescription) },\n\t\t},\n\t],\n\tvalidate: ({ value, t }) => {\n\t\tif (value == null || value === '') {\n\t\t\treturn true\n\t\t}\n\t\treturn EMAIL_PATTERN.test(value) ? true : t(keys.validationEmail)\n\t},\n\tformat: ({ value }) => (value == null ? '' : String(value)),\n})\n"],"mappings":";;;;;AAKA,MAAa,aAAa,gBAAwB;CACjD,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,gBAAgB,CACf;EACC,MAAM;EACN,MAAM;EACN,OAAO,SAAS,KAAK,kBAAkB;EACvC,OAAO,EAAE,aAAa,SAAS,KAAK,6BAA6B,EAAE;CACpE,CACD;CACA,WAAW,EAAE,OAAO,QAAQ;EAC3B,IAAI,SAAS,QAAQ,UAAU,IAC9B,OAAO;EAER,OAAO,cAAc,KAAK,KAAK,IAAI,OAAO,EAAE,KAAK,eAAe;CACjE;CACA,SAAS,EAAE,YAAa,SAAS,OAAO,KAAK,OAAO,KAAK;AAC1D,CAAC"}
@@ -1,4 +1,6 @@
1
1
  import { AnyFormFieldDefinition } from "../types.js";
2
+ import { CalcAllowed } from "../../calc/normalizeCalc.js";
3
+ import { CalcSourceClientMeta } from "./calculation.js";
2
4
  import { RichTextField } from "payload";
3
5
 
4
6
  //#region src/fields/builtin/index.d.ts
@@ -8,9 +10,11 @@ import { RichTextField } from "payload";
8
10
  * richText editor on the message content field (from the plugin's `richText.editor` option). Field
9
11
  * types without content config are shared static definitions. Definitions are authored with precise
10
12
  * value/config generics; the registry stores them erased (config is re-narrowed per matched type at
11
- * execution), hence one cast per built-in, no `any`.
13
+ * execution), hence one cast per built-in, no `any`. `calcAllowed` threads the registered calc
14
+ * extension names into the calculation field's expression validate and editor schema;
15
+ * `calcSources` carries the serializable source metadata the builder UI renders from.
12
16
  */
13
- declare const buildDefaultFieldDefinitions: (localize: boolean, editor?: RichTextField["editor"], uploadMimeTypes?: string[]) => AnyFormFieldDefinition[];
17
+ declare const buildDefaultFieldDefinitions: (localize: boolean, editor?: RichTextField["editor"], uploadMimeTypes?: string[], calcAllowed?: CalcAllowed, calcSources?: CalcSourceClientMeta[]) => AnyFormFieldDefinition[];
14
18
  declare const defaultFieldDefinitions: AnyFormFieldDefinition[];
15
19
  /**
16
20
  * `defaultFieldDefinitions` indexed by `type`, so a single built-in can be spread and tweaked
@@ -1,4 +1,4 @@
1
- import { calculationField } from "./calculation.js";
1
+ import { buildCalculationField } from "./calculation.js";
2
2
  import { checkboxField } from "./checkbox.js";
3
3
  import { consentField } from "./consent.js";
4
4
  import { dateField } from "./date.js";
@@ -17,16 +17,21 @@ import { textareaField } from "./textarea.js";
17
17
  * richText editor on the message content field (from the plugin's `richText.editor` option). Field
18
18
  * types without content config are shared static definitions. Definitions are authored with precise
19
19
  * value/config generics; the registry stores them erased (config is re-narrowed per matched type at
20
- * execution), hence one cast per built-in, no `any`.
20
+ * execution), hence one cast per built-in, no `any`. `calcAllowed` threads the registered calc
21
+ * extension names into the calculation field's expression validate and editor schema;
22
+ * `calcSources` carries the serializable source metadata the builder UI renders from.
21
23
  */
22
- const buildDefaultFieldDefinitions = (localize, editor, uploadMimeTypes) => [
24
+ const buildDefaultFieldDefinitions = (localize, editor, uploadMimeTypes, calcAllowed, calcSources) => [
23
25
  textField,
24
26
  textareaField,
25
27
  emailField,
26
28
  numberField,
27
29
  buildSelectField(localize),
28
30
  checkboxField,
29
- calculationField,
31
+ buildCalculationField(calcAllowed, {
32
+ localize,
33
+ sources: calcSources
34
+ }),
30
35
  consentField,
31
36
  buildFileField(uploadMimeTypes),
32
37
  buildRepeaterField(localize),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/fields/builtin/index.ts"],"sourcesContent":["import type { RichTextField } from 'payload'\nimport type { AnyFormFieldDefinition } from '../types'\nimport { calculationField } from './calculation'\nimport { checkboxField } from './checkbox'\nimport { consentField } from './consent'\nimport { dateField } from './date'\nimport { emailField } from './email'\nimport { buildFileField } 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, repeater add\n * label) carrying `localized: true` when `localize` is true. `editor`, when given, overrides the\n * richText editor on the message content field (from the plugin's `richText.editor` option). Field\n * types 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 at\n * execution), hence one cast per built-in, no `any`.\n */\nexport const buildDefaultFieldDefinitions = (\n\tlocalize: boolean,\n\teditor?: RichTextField['editor'],\n\tuploadMimeTypes?: string[]\n): 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\tconsentField as AnyFormFieldDefinition,\n\tbuildFileField(uploadMimeTypes) as AnyFormFieldDefinition,\n\tbuildRepeaterField(localize) as AnyFormFieldDefinition,\n\tdateField as AnyFormFieldDefinition,\n\tbuildMessageField(localize, editor) 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":";;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,gCACZ,UACA,QACA,oBAC8B;CAC9B;CACA;CACA;CACA;CACA,iBAAiB,QAAQ;CACzB;CACA;CACA;CACA,eAAe,eAAe;CAC9B,mBAAmB,QAAQ;CAC3B;CACA,kBAAkB,UAAU,MAAM;AACnC;AAEA,MAAa,0BAAoD,6BAA6B,IAAI;;;;;;;;;AAUlG,MAAa,gCACZ,OAAO,YAAY,wBAAwB,KAAK,eAAe,CAAC,WAAW,MAAM,UAAU,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/fields/builtin/index.ts"],"sourcesContent":["import type { RichTextField } from 'payload'\nimport type { CalcAllowed } from '../../calc/normalizeCalc'\nimport type { AnyFormFieldDefinition } from '../types'\nimport { buildCalculationField, type CalcSourceClientMeta } from './calculation'\nimport { checkboxField } from './checkbox'\nimport { consentField } from './consent'\nimport { dateField } from './date'\nimport { emailField } from './email'\nimport { buildFileField } 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, repeater add\n * label) carrying `localized: true` when `localize` is true. `editor`, when given, overrides the\n * richText editor on the message content field (from the plugin's `richText.editor` option). Field\n * types 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 at\n * execution), hence one cast per built-in, no `any`. `calcAllowed` threads the registered calc\n * extension names into the calculation field's expression validate and editor schema;\n * `calcSources` carries the serializable source metadata the builder UI renders from.\n */\n// biome-ignore lint/complexity/useMaxParams: the documented positional surface (localize, editor, uploadMimeTypes) plus the calc allowed sets and source metadata; an options object would break existing callers\nexport const buildDefaultFieldDefinitions = (\n\tlocalize: boolean,\n\teditor?: RichTextField['editor'],\n\tuploadMimeTypes?: string[],\n\tcalcAllowed?: CalcAllowed,\n\tcalcSources?: CalcSourceClientMeta[]\n): 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\tbuildCalculationField(calcAllowed, { localize, sources: calcSources }) as AnyFormFieldDefinition,\n\tconsentField as AnyFormFieldDefinition,\n\tbuildFileField(uploadMimeTypes) as AnyFormFieldDefinition,\n\tbuildRepeaterField(localize) as AnyFormFieldDefinition,\n\tdateField as AnyFormFieldDefinition,\n\tbuildMessageField(localize, editor) 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":";;;;;;;;;;;;;;;;;;;;;;;AA2BA,MAAa,gCACZ,UACA,QACA,iBACA,aACA,gBAC8B;CAC9B;CACA;CACA;CACA;CACA,iBAAiB,QAAQ;CACzB;CACA,sBAAsB,aAAa;EAAE;EAAU,SAAS;CAAY,CAAC;CACrE;CACA,eAAe,eAAe;CAC9B,mBAAmB,QAAQ;CAC3B;CACA,kBAAkB,UAAU,MAAM;AACnC;AAEA,MAAa,0BAAoD,6BAA6B,IAAI;;;;;;;;;AAUlG,MAAa,gCACZ,OAAO,YAAY,wBAAwB,KAAK,eAAe,CAAC,WAAW,MAAM,UAAU,CAAC,CAAC"}
@@ -21,7 +21,6 @@ const buildSelectField = (localize) => defineFormField({
21
21
  fields: [{
22
22
  name: "label",
23
23
  type: "text",
24
- required: true,
25
24
  label: labelFor(keys.configOptionLabel),
26
25
  ...localizedIf(localize)
27
26
  }, {
@@ -30,6 +29,26 @@ const buildSelectField = (localize) => defineFormField({
30
29
  required: true,
31
30
  label: labelFor(keys.configOptionValue)
32
31
  }]
32
+ }, {
33
+ name: "display",
34
+ type: "select",
35
+ defaultValue: "dropdown",
36
+ label: labelFor(keys.configSelectDisplay),
37
+ admin: { isClearable: false },
38
+ options: [
39
+ {
40
+ label: labelFor(keys.selectDisplayDropdown),
41
+ value: "dropdown"
42
+ },
43
+ {
44
+ label: labelFor(keys.selectDisplayRadio),
45
+ value: "radio"
46
+ },
47
+ {
48
+ label: labelFor(keys.selectDisplayButtons),
49
+ value: "buttons"
50
+ }
51
+ ]
33
52
  }],
34
53
  validate: ({ value, config, t }) => {
35
54
  if (value == null || value === "") return true;
@@ -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'\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\tpollEligible: true,\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,cAAc;CACd,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"}
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[]; display?: 'dropdown' | 'radio' | 'buttons' }\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\tpollEligible: true,\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\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\t{\n\t\t\t\tname: 'display',\n\t\t\t\ttype: 'select',\n\t\t\t\tdefaultValue: 'dropdown',\n\t\t\t\tlabel: labelFor(keys.configSelectDisplay),\n\t\t\t\tadmin: { isClearable: false },\n\t\t\t\toptions: [\n\t\t\t\t\t{ label: labelFor(keys.selectDisplayDropdown), value: 'dropdown' },\n\t\t\t\t\t{ label: labelFor(keys.selectDisplayRadio), value: 'radio' },\n\t\t\t\t\t{ label: labelFor(keys.selectDisplayButtons), value: 'buttons' },\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,cAAc;CACd,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,OAAO,SAAS,KAAK,iBAAiB;GACtC,GAAG,YAAY,QAAQ;EACxB,GACA;GAAE,MAAM;GAAS,MAAM;GAAQ,UAAU;GAAM,OAAO,SAAS,KAAK,iBAAiB;EAAE,CACxF;CACD,GACA;EACC,MAAM;EACN,MAAM;EACN,cAAc;EACd,OAAO,SAAS,KAAK,mBAAmB;EACxC,OAAO,EAAE,aAAa,MAAM;EAC5B,SAAS;GACR;IAAE,OAAO,SAAS,KAAK,qBAAqB;IAAG,OAAO;GAAW;GACjE;IAAE,OAAO,SAAS,KAAK,kBAAkB;IAAG,OAAO;GAAQ;GAC3D;IAAE,OAAO,SAAS,KAAK,oBAAoB;IAAG,OAAO;GAAU;EAChE;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"}
@@ -1,10 +1,17 @@
1
1
  import { keys } from "../../translations/keys.js";
2
+ import { labelFor } from "../../translations/server.js";
2
3
  import { defineFormField } from "../defineFormField.js";
3
4
  //#region src/fields/builtin/text.ts
4
5
  const textField = defineFormField({
5
6
  type: "text",
6
7
  label: keys.fieldTypeText,
7
8
  value: "text",
9
+ advancedConfig: [{
10
+ name: "autocomplete",
11
+ type: "text",
12
+ label: labelFor(keys.configAutocomplete),
13
+ admin: { description: labelFor(keys.configAutocompleteDescription) }
14
+ }],
8
15
  format: ({ value }) => value == null ? "" : String(value)
9
16
  });
10
17
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":[],"sources":["../../../src/fields/builtin/text.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { defineFormField } from '../defineFormField'\n\nexport const textField = defineFormField<'text'>({\n\ttype: 'text',\n\tlabel: keys.fieldTypeText,\n\tvalue: 'text',\n\tformat: ({ value }) => (value == null ? '' : String(value)),\n})\n"],"mappings":";;;AAGA,MAAa,YAAY,gBAAwB;CAChD,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,SAAS,EAAE,YAAa,SAAS,OAAO,KAAK,OAAO,KAAK;AAC1D,CAAC"}
1
+ {"version":3,"file":"text.js","names":[],"sources":["../../../src/fields/builtin/text.ts"],"sourcesContent":["import { keys } from '../../translations/keys'\nimport { labelFor } from '../../translations/server'\nimport { defineFormField } from '../defineFormField'\n\nexport const textField = defineFormField<'text'>({\n\ttype: 'text',\n\tlabel: keys.fieldTypeText,\n\tvalue: 'text',\n\tadvancedConfig: [\n\t\t{\n\t\t\tname: 'autocomplete',\n\t\t\ttype: 'text',\n\t\t\tlabel: labelFor(keys.configAutocomplete),\n\t\t\tadmin: { description: labelFor(keys.configAutocompleteDescription) },\n\t\t},\n\t],\n\tformat: ({ value }) => (value == null ? '' : String(value)),\n})\n"],"mappings":";;;;AAIA,MAAa,YAAY,gBAAwB;CAChD,MAAM;CACN,OAAO,KAAK;CACZ,OAAO;CACP,gBAAgB,CACf;EACC,MAAM;EACN,MAAM;EACN,OAAO,SAAS,KAAK,kBAAkB;EACvC,OAAO,EAAE,aAAa,SAAS,KAAK,6BAA6B,EAAE;CACpE,CACD;CACA,SAAS,EAAE,YAAa,SAAS,OAAO,KAAK,OAAO,KAAK;AAC1D,CAAC"}
@@ -10,7 +10,7 @@ const instanceOptionsOf = (field) => {
10
10
  if (!Array.isArray(field.options)) return;
11
11
  const options = field.options.filter((option) => typeof option?.value === "string").map((option) => ({
12
12
  value: String(option.value),
13
- label: option.label ?? String(option.value)
13
+ label: option.label?.trim() ? option.label : String(option.value)
14
14
  }));
15
15
  return options.length > 0 ? options : void 0;
16
16
  };
@@ -1 +1 @@
1
- {"version":3,"file":"instanceOptions.js","names":[],"sources":["../../src/fields/instanceOptions.ts"],"sourcesContent":["import type { FormFieldInstance } from '../submissions/types'\n\n/**\n * Normalize a field instance's authored options to `{ value, label }` pairs, dropping entries\n * without a string value and falling back to the value as label. Returns `undefined` when the\n * instance declares no usable options, so callers can distinguish \"not a choice field\" from an\n * empty choice set. Shared by response aggregation and poll outcome resolution so both read the\n * same choice set from a form document.\n */\nexport const instanceOptionsOf = (\n\tfield: FormFieldInstance\n): { value: string; label: string }[] | undefined => {\n\tif (!Array.isArray(field.options)) {\n\t\treturn undefined\n\t}\n\tconst options = (field.options as Array<{ label?: string; value?: string }>)\n\t\t.filter((option) => typeof option?.value === 'string')\n\t\t.map((option) => ({ value: String(option.value), label: option.label ?? String(option.value) }))\n\treturn options.length > 0 ? options : undefined\n}\n"],"mappings":";;;;;;;;AASA,MAAa,qBACZ,UACoD;CACpD,IAAI,CAAC,MAAM,QAAQ,MAAM,OAAO,GAC/B;CAED,MAAM,UAAW,MAAM,QACrB,QAAQ,WAAW,OAAO,QAAQ,UAAU,QAAQ,EACpD,KAAK,YAAY;EAAE,OAAO,OAAO,OAAO,KAAK;EAAG,OAAO,OAAO,SAAS,OAAO,OAAO,KAAK;CAAE,EAAE;CAChG,OAAO,QAAQ,SAAS,IAAI,UAAU,KAAA;AACvC"}
1
+ {"version":3,"file":"instanceOptions.js","names":[],"sources":["../../src/fields/instanceOptions.ts"],"sourcesContent":["import type { FormFieldInstance } from '../submissions/types'\n\n/**\n * Normalize a field instance's authored options to `{ value, label }` pairs, dropping entries\n * without a string value and falling back to the value as label. Returns `undefined` when the\n * instance declares no usable options, so callers can distinguish \"not a choice field\" from an\n * empty choice set. Shared by response aggregation and poll outcome resolution so both read the\n * same choice set from a form document.\n */\nexport const instanceOptionsOf = (\n\tfield: FormFieldInstance\n): { value: string; label: string }[] | undefined => {\n\tif (!Array.isArray(field.options)) {\n\t\treturn undefined\n\t}\n\tconst options = (field.options as Array<{ label?: string; value?: string }>)\n\t\t.filter((option) => typeof option?.value === 'string')\n\t\t.map((option) => ({\n\t\t\tvalue: String(option.value),\n\t\t\tlabel: option.label?.trim() ? option.label : String(option.value),\n\t\t}))\n\treturn options.length > 0 ? options : undefined\n}\n"],"mappings":";;;;;;;;AASA,MAAa,qBACZ,UACoD;CACpD,IAAI,CAAC,MAAM,QAAQ,MAAM,OAAO,GAC/B;CAED,MAAM,UAAW,MAAM,QACrB,QAAQ,WAAW,OAAO,QAAQ,UAAU,QAAQ,EACpD,KAAK,YAAY;EACjB,OAAO,OAAO,OAAO,KAAK;EAC1B,OAAO,OAAO,OAAO,KAAK,IAAI,OAAO,QAAQ,OAAO,OAAO,KAAK;CACjE,EAAE;CACH,OAAO,QAAQ,SAAS,IAAI,UAAU,KAAA;AACvC"}