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

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 (406) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/README.md +4 -4
  3. package/dist/actions/body/serializeBody.d.ts +22 -4
  4. package/dist/actions/body/serializeBody.js +3 -1
  5. package/dist/actions/body/serializeBody.js.map +1 -1
  6. package/dist/actions/body/textOfBody.d.ts +13 -0
  7. package/dist/actions/body/textOfBody.js +35 -0
  8. package/dist/actions/body/textOfBody.js.map +1 -0
  9. package/dist/actions/builtin/confirmation.js +51 -12
  10. package/dist/actions/builtin/confirmation.js.map +1 -1
  11. package/dist/actions/builtin/emailTeam.js +82 -34
  12. package/dist/actions/builtin/emailTeam.js.map +1 -1
  13. package/dist/actions/builtin/index.d.ts +18 -1
  14. package/dist/actions/builtin/index.js +21 -7
  15. package/dist/actions/builtin/index.js.map +1 -1
  16. package/dist/actions/builtin/signedWebhook.js +2 -16
  17. package/dist/actions/builtin/signedWebhook.js.map +1 -1
  18. package/dist/actions/defineAction.d.ts +1 -1
  19. package/dist/actions/fromAddresses.d.ts +21 -0
  20. package/dist/actions/fromAddresses.js +74 -0
  21. package/dist/actions/fromAddresses.js.map +1 -0
  22. package/dist/actions/runActions.js +2 -0
  23. package/dist/actions/runActions.js.map +1 -1
  24. package/dist/actions/task.js +9 -8
  25. package/dist/actions/task.js.map +1 -1
  26. package/dist/aggregation/aggregateResponses.d.ts +9 -0
  27. package/dist/aggregation/aggregateResponses.js +8 -14
  28. package/dist/aggregation/aggregateResponses.js.map +1 -1
  29. package/dist/aggregation/resolveResultsRequest.d.ts +35 -6
  30. package/dist/aggregation/resolveResultsRequest.js +68 -11
  31. package/dist/aggregation/resolveResultsRequest.js.map +1 -1
  32. package/dist/calc/computeCalcFields.js +2 -1
  33. package/dist/calc/computeCalcFields.js.map +1 -1
  34. package/dist/client/ByteSizeField.d.ts +8 -0
  35. package/dist/client/ByteSizeField.js +3 -1
  36. package/dist/client/ByteSizeField.js.map +1 -1
  37. package/dist/client/ClosePollButton.d.ts +15 -0
  38. package/dist/client/ClosePollButton.js +80 -0
  39. package/dist/client/ClosePollButton.js.map +1 -0
  40. package/dist/client/CondensedArray.d.ts +15 -0
  41. package/dist/client/CondensedArray.js +97 -0
  42. package/dist/client/CondensedArray.js.map +1 -0
  43. package/dist/client/EndpointOptionsSelect.d.ts +45 -0
  44. package/dist/client/EndpointOptionsSelect.js +103 -0
  45. package/dist/client/EndpointOptionsSelect.js.map +1 -0
  46. package/dist/client/FieldBlockLabel.d.ts +18 -0
  47. package/dist/client/FieldBlockLabel.js +34 -0
  48. package/dist/client/FieldBlockLabel.js.map +1 -0
  49. package/dist/client/FieldNameSelect.d.ts +20 -4
  50. package/dist/client/FieldNameSelect.js +25 -14
  51. package/dist/client/FieldNameSelect.js.map +1 -1
  52. package/dist/client/FlowBuilder.d.ts +2 -1
  53. package/dist/client/FlowBuilder.js +134 -83
  54. package/dist/client/FlowBuilder.js.map +1 -1
  55. package/dist/client/RuleDescription.d.ts +21 -0
  56. package/dist/client/RuleDescription.js +19 -0
  57. package/dist/client/RuleDescription.js.map +1 -0
  58. package/dist/client/admin.css +83 -5
  59. package/dist/client/endpointOptions.js +29 -0
  60. package/dist/client/endpointOptions.js.map +1 -0
  61. package/dist/client/flowAuthoring.js +142 -0
  62. package/dist/client/flowAuthoring.js.map +1 -0
  63. package/dist/client/synthesizeClientField.js +21 -10
  64. package/dist/client/synthesizeClientField.js.map +1 -1
  65. package/dist/collections/buttonFields.d.ts +34 -0
  66. package/dist/collections/buttonFields.js +35 -0
  67. package/dist/collections/buttonFields.js.map +1 -0
  68. package/dist/collections/cells/FieldCountCell.d.ts +15 -0
  69. package/dist/collections/cells/FieldCountCell.js +18 -0
  70. package/dist/collections/cells/FieldCountCell.js.map +1 -0
  71. package/dist/collections/cells/FlowStepsCell.d.ts +14 -0
  72. package/dist/collections/cells/FlowStepsCell.js +17 -0
  73. package/dist/collections/cells/FlowStepsCell.js.map +1 -0
  74. package/dist/collections/formSubmissions.js +55 -12
  75. package/dist/collections/formSubmissions.js.map +1 -1
  76. package/dist/collections/forms.js +376 -57
  77. package/dist/collections/forms.js.map +1 -1
  78. package/dist/collections/redirectFields.d.ts +29 -0
  79. package/dist/conditions/conditionType.js +9 -2
  80. package/dist/conditions/conditionType.js.map +1 -1
  81. package/dist/conditions/normalizeConditions.js +2 -1
  82. package/dist/conditions/normalizeConditions.js.map +1 -1
  83. package/dist/consent/applyConsentStatements.d.ts +14 -0
  84. package/dist/consent/applyConsentStatements.js +17 -0
  85. package/dist/consent/applyConsentStatements.js.map +1 -0
  86. package/dist/consent/captureConsent.d.ts +19 -8
  87. package/dist/consent/captureConsent.js +34 -11
  88. package/dist/consent/captureConsent.js.map +1 -1
  89. package/dist/consent/consentSourcesField.d.ts +58 -0
  90. package/dist/consent/consentSourcesField.js +79 -0
  91. package/dist/consent/consentSourcesField.js.map +1 -0
  92. package/dist/consent/resolveConsentEntries.d.ts +24 -0
  93. package/dist/consent/resolveConsentEntries.js +43 -0
  94. package/dist/consent/resolveConsentEntries.js.map +1 -0
  95. package/dist/consent/resolveConsentSourcesRequest.d.ts +37 -0
  96. package/dist/consent/resolveConsentSourcesRequest.js +55 -0
  97. package/dist/consent/resolveConsentSourcesRequest.js.map +1 -0
  98. package/dist/consent/resolveConsentStatements.d.ts +45 -0
  99. package/dist/consent/resolveConsentStatements.js +44 -0
  100. package/dist/consent/resolveConsentStatements.js.map +1 -0
  101. package/dist/consent/resolvePublishedVersionRef.d.ts +17 -11
  102. package/dist/consent/resolvePublishedVersionRef.js +13 -10
  103. package/dist/consent/resolvePublishedVersionRef.js.map +1 -1
  104. package/dist/consent/types.d.ts +49 -0
  105. package/dist/email/departments.d.ts +55 -0
  106. package/dist/email/departments.js +130 -0
  107. package/dist/email/departments.js.map +1 -0
  108. package/dist/email/departmentsField.d.ts +49 -0
  109. package/dist/email/departmentsField.js +69 -0
  110. package/dist/email/departmentsField.js.map +1 -0
  111. package/dist/exports/client.d.ts +8 -1
  112. package/dist/exports/client.js +8 -1
  113. package/dist/exports/react.d.ts +15 -6
  114. package/dist/exports/react.js +11 -3
  115. package/dist/exports/rsc.d.ts +8 -1
  116. package/dist/exports/rsc.js +8 -1
  117. package/dist/exports/types.d.ts +18 -10
  118. package/dist/fields/buildFieldBlocks.js +43 -19
  119. package/dist/fields/buildFieldBlocks.js.map +1 -1
  120. package/dist/fields/builtin/calculation.js +7 -0
  121. package/dist/fields/builtin/calculation.js.map +1 -1
  122. package/dist/fields/builtin/checkbox.js +8 -0
  123. package/dist/fields/builtin/checkbox.js.map +1 -1
  124. package/dist/fields/builtin/consent.js +25 -15
  125. package/dist/fields/builtin/consent.js.map +1 -1
  126. package/dist/fields/builtin/country.d.ts +13 -0
  127. package/dist/fields/builtin/country.js +30 -0
  128. package/dist/fields/builtin/country.js.map +1 -0
  129. package/dist/fields/builtin/date.js +2 -0
  130. package/dist/fields/builtin/date.js.map +1 -1
  131. package/dist/fields/builtin/file.js +13 -8
  132. package/dist/fields/builtin/file.js.map +1 -1
  133. package/dist/fields/builtin/index.d.ts +13 -1
  134. package/dist/fields/builtin/index.js +20 -7
  135. package/dist/fields/builtin/index.js.map +1 -1
  136. package/dist/fields/builtin/message.js +30 -0
  137. package/dist/fields/builtin/message.js.map +1 -0
  138. package/dist/fields/builtin/repeater.js +8 -3
  139. package/dist/fields/builtin/repeater.js.map +1 -1
  140. package/dist/fields/builtin/select.js +8 -3
  141. package/dist/fields/builtin/select.js.map +1 -1
  142. package/dist/fields/builtin/state.d.ts +13 -0
  143. package/dist/fields/builtin/state.js +30 -0
  144. package/dist/fields/builtin/state.js.map +1 -0
  145. package/dist/fields/data/regions.d.ts +21 -0
  146. package/dist/fields/data/regions.js +1238 -0
  147. package/dist/fields/data/regions.js.map +1 -0
  148. package/dist/fields/fieldKey.d.ts +15 -0
  149. package/dist/fields/fieldKey.js +16 -0
  150. package/dist/fields/fieldKey.js.map +1 -0
  151. package/dist/fields/fieldNamesOfType.js +13 -8
  152. package/dist/fields/fieldNamesOfType.js.map +1 -1
  153. package/dist/fields/instanceOptions.js +20 -0
  154. package/dist/fields/instanceOptions.js.map +1 -0
  155. package/dist/fields/localizedIf.d.ts +13 -0
  156. package/dist/fields/localizedIf.js +12 -0
  157. package/dist/fields/localizedIf.js.map +1 -0
  158. package/dist/fields/registry.d.ts +1 -0
  159. package/dist/fields/registry.js +21 -2
  160. package/dist/fields/registry.js.map +1 -1
  161. package/dist/fields/sharedConfig.js +101 -51
  162. package/dist/fields/sharedConfig.js.map +1 -1
  163. package/dist/fields/types.d.ts +61 -5
  164. package/dist/flow/engine.d.ts +6 -4
  165. package/dist/flow/engine.js +11 -6
  166. package/dist/flow/engine.js.map +1 -1
  167. package/dist/flow/normalizeFlow.js +22 -7
  168. package/dist/flow/normalizeFlow.js.map +1 -1
  169. package/dist/flow/stepLabel.js +15 -0
  170. package/dist/flow/stepLabel.js.map +1 -0
  171. package/dist/flow/types.d.ts +12 -3
  172. package/dist/flow/types.js +11 -0
  173. package/dist/flow/types.js.map +1 -0
  174. package/dist/form/pollState.d.ts +22 -0
  175. package/dist/form/pollState.js +17 -0
  176. package/dist/form/pollState.js.map +1 -0
  177. package/dist/form/toFormDocument.d.ts +85 -0
  178. package/dist/form/toFormDocument.js +75 -0
  179. package/dist/form/toFormDocument.js.map +1 -0
  180. package/dist/form/types.d.ts +65 -0
  181. package/dist/index.d.ts +193 -27
  182. package/dist/index.js +59 -19
  183. package/dist/index.js.map +1 -1
  184. package/dist/plugin/customState.js +26 -0
  185. package/dist/plugin/customState.js.map +1 -0
  186. package/dist/plugin/registerCollections.js +33 -44
  187. package/dist/plugin/registerCollections.js.map +1 -1
  188. package/dist/plugin/uploadsCollection.d.ts +13 -0
  189. package/dist/plugin/uploadsCollection.js +44 -0
  190. package/dist/plugin/uploadsCollection.js.map +1 -0
  191. package/dist/poll/applyPollOptions.js +18 -0
  192. package/dist/poll/applyPollOptions.js.map +1 -0
  193. package/dist/poll/buildPollOptionSourceFields.js +50 -0
  194. package/dist/poll/buildPollOptionSourceFields.js.map +1 -0
  195. package/dist/poll/closeJob.d.ts +52 -0
  196. package/dist/poll/closeJob.js +119 -0
  197. package/dist/poll/closeJob.js.map +1 -0
  198. package/dist/poll/definePollOptionSource.d.ts +44 -0
  199. package/dist/poll/definePollOptionSource.js +6 -0
  200. package/dist/poll/definePollOptionSource.js.map +1 -0
  201. package/dist/poll/definePollType.d.ts +34 -0
  202. package/dist/poll/definePollType.js +6 -0
  203. package/dist/poll/definePollType.js.map +1 -0
  204. package/dist/poll/effectivePollOptions.d.ts +38 -0
  205. package/dist/poll/effectivePollOptions.js +64 -0
  206. package/dist/poll/effectivePollOptions.js.map +1 -0
  207. package/dist/poll/mostVoted.d.ts +21 -0
  208. package/dist/poll/mostVoted.js +32 -0
  209. package/dist/poll/mostVoted.js.map +1 -0
  210. package/dist/poll/outcomeBeforeChange.js +91 -0
  211. package/dist/poll/outcomeBeforeChange.js.map +1 -0
  212. package/dist/poll/outcomeFields.d.ts +38 -0
  213. package/dist/poll/outcomeFields.js +52 -0
  214. package/dist/poll/outcomeFields.js.map +1 -0
  215. package/dist/poll/pollTypeRegistry.d.ts +34 -0
  216. package/dist/poll/pollTypeRegistry.js +71 -0
  217. package/dist/poll/pollTypeRegistry.js.map +1 -0
  218. package/dist/poll/registry.d.ts +16 -0
  219. package/dist/poll/registry.js +16 -0
  220. package/dist/poll/registry.js.map +1 -0
  221. package/dist/poll/resolvePollCloseRequest.js +105 -0
  222. package/dist/poll/resolvePollCloseRequest.js.map +1 -0
  223. package/dist/poll/resolvePollOptions.d.ts +29 -0
  224. package/dist/poll/resolvePollOptions.js +52 -0
  225. package/dist/poll/resolvePollOptions.js.map +1 -0
  226. package/dist/poll/resolvePollOptionsRequest.js +51 -0
  227. package/dist/poll/resolvePollOptionsRequest.js.map +1 -0
  228. package/dist/poll/resolvePollOutcome.d.ts +34 -0
  229. package/dist/poll/resolvePollOutcome.js +76 -0
  230. package/dist/poll/resolvePollOutcome.js.map +1 -0
  231. package/dist/poll/resultsField.js +21 -0
  232. package/dist/poll/resultsField.js.map +1 -0
  233. package/dist/prefill/valuesFromSearchParams.js +3 -2
  234. package/dist/prefill/valuesFromSearchParams.js.map +1 -1
  235. package/dist/presentations/types.d.ts +3 -2
  236. package/dist/react/Form.d.ts +15 -33
  237. package/dist/react/Form.js +144 -119
  238. package/dist/react/Form.js.map +1 -1
  239. package/dist/react/FormContext.d.ts +37 -1
  240. package/dist/react/FormContext.js.map +1 -1
  241. package/dist/react/FormControls.d.ts +55 -0
  242. package/dist/react/FormControls.js +56 -0
  243. package/dist/react/FormControls.js.map +1 -0
  244. package/dist/react/FormResults.d.ts +6 -3
  245. package/dist/react/FormResults.js +37 -29
  246. package/dist/react/FormResults.js.map +1 -1
  247. package/dist/react/FormSteps.d.ts +17 -0
  248. package/dist/react/FormSteps.js +35 -0
  249. package/dist/react/FormSteps.js.map +1 -0
  250. package/dist/react/Poll.d.ts +17 -6
  251. package/dist/react/Poll.js +66 -18
  252. package/dist/react/Poll.js.map +1 -1
  253. package/dist/react/contract.d.ts +2 -3
  254. package/dist/react/contract.js.map +1 -1
  255. package/dist/react/fetchResults.d.ts +1 -1
  256. package/dist/react/fetchResults.js +1 -1
  257. package/dist/react/fetchResults.js.map +1 -1
  258. package/dist/react/primitives/Checkbox.d.ts +4 -2
  259. package/dist/react/primitives/Checkbox.js +2 -1
  260. package/dist/react/primitives/Checkbox.js.map +1 -1
  261. package/dist/react/primitives/FieldShell.d.ts +2 -4
  262. package/dist/react/primitives/FieldShell.js +12 -19
  263. package/dist/react/primitives/FieldShell.js.map +1 -1
  264. package/dist/react/renderers/calculation.js +1 -2
  265. package/dist/react/renderers/calculation.js.map +1 -1
  266. package/dist/react/renderers/checkbox.js +1 -2
  267. package/dist/react/renderers/checkbox.js.map +1 -1
  268. package/dist/react/renderers/consent.js +78 -34
  269. package/dist/react/renderers/consent.js.map +1 -1
  270. package/dist/react/renderers/date.js +1 -3
  271. package/dist/react/renderers/date.js.map +1 -1
  272. package/dist/react/renderers/email.js +1 -2
  273. package/dist/react/renderers/email.js.map +1 -1
  274. package/dist/react/renderers/file.js +6 -3
  275. package/dist/react/renderers/file.js.map +1 -1
  276. package/dist/react/renderers/index.js +5 -0
  277. package/dist/react/renderers/index.js.map +1 -1
  278. package/dist/react/renderers/message.js +35 -0
  279. package/dist/react/renderers/message.js.map +1 -0
  280. package/dist/react/renderers/number.js +1 -2
  281. package/dist/react/renderers/number.js.map +1 -1
  282. package/dist/react/renderers/region.js +45 -0
  283. package/dist/react/renderers/region.js.map +1 -0
  284. package/dist/react/renderers/repeater.js +7 -10
  285. package/dist/react/renderers/repeater.js.map +1 -1
  286. package/dist/react/renderers/select.js +1 -2
  287. package/dist/react/renderers/select.js.map +1 -1
  288. package/dist/react/renderers/text.js +1 -2
  289. package/dist/react/renderers/text.js.map +1 -1
  290. package/dist/react/renderers/textarea.js +1 -2
  291. package/dist/react/renderers/textarea.js.map +1 -1
  292. package/dist/react/state.d.ts +23 -2
  293. package/dist/react/state.js +16 -7
  294. package/dist/react/state.js.map +1 -1
  295. package/dist/react/uploadFile.d.ts +2 -2
  296. package/dist/react/uploadFile.js +1 -1
  297. package/dist/react/uploadFile.js.map +1 -1
  298. package/dist/react/useField.d.ts +0 -1
  299. package/dist/react/useField.js +0 -1
  300. package/dist/react/useField.js.map +1 -1
  301. package/dist/react/useFormState.d.ts +1 -1
  302. package/dist/react/useFormState.js +1 -1
  303. package/dist/react/useFormState.js.map +1 -1
  304. package/dist/react/validateField.js +3 -6
  305. package/dist/react/validateField.js.map +1 -1
  306. package/dist/recall/interpolate.d.ts +4 -3
  307. package/dist/recall/interpolate.js +5 -4
  308. package/dist/recall/interpolate.js.map +1 -1
  309. package/dist/recall/resolver.d.ts +1 -1
  310. package/dist/recall/resolver.js +14 -2
  311. package/dist/recall/resolver.js.map +1 -1
  312. package/dist/spam/captcha.d.ts +1 -1
  313. package/dist/spam/captcha.js +1 -1
  314. package/dist/spam/captcha.js.map +1 -1
  315. package/dist/submissions/SubmissionAnswers.js +6 -2
  316. package/dist/submissions/SubmissionAnswers.js.map +1 -1
  317. package/dist/submissions/SubmissionAnswersClient.js +91 -62
  318. package/dist/submissions/SubmissionAnswersClient.js.map +1 -1
  319. package/dist/submissions/createSubmission.d.ts +36 -0
  320. package/dist/submissions/createSubmission.js +26 -0
  321. package/dist/submissions/createSubmission.js.map +1 -0
  322. package/dist/submissions/runSubmission.d.ts +10 -0
  323. package/dist/submissions/runSubmission.js +92 -44
  324. package/dist/submissions/runSubmission.js.map +1 -1
  325. package/dist/submissions/types.d.ts +11 -4
  326. package/dist/submissions/validateSubmission.js +59 -10
  327. package/dist/submissions/validateSubmission.js.map +1 -1
  328. package/dist/submissions/votedCookie.d.ts +13 -0
  329. package/dist/submissions/votedCookie.js +27 -0
  330. package/dist/submissions/votedCookie.js.map +1 -0
  331. package/dist/translations/de.js +295 -0
  332. package/dist/translations/de.js.map +1 -0
  333. package/dist/translations/en.js +138 -23
  334. package/dist/translations/en.js.map +1 -1
  335. package/dist/translations/index.d.ts +2 -1
  336. package/dist/translations/index.js +6 -2
  337. package/dist/translations/index.js.map +1 -1
  338. package/dist/translations/keys.d.ts +134 -19
  339. package/dist/translations/keys.js +135 -20
  340. package/dist/translations/keys.js.map +1 -1
  341. package/dist/uploads/types.d.ts +1 -2
  342. package/dist/validation/buildRuleBlocks.js +18 -17
  343. package/dist/validation/buildRuleBlocks.js.map +1 -1
  344. package/dist/validation/builtin/email.js +1 -0
  345. package/dist/validation/builtin/email.js.map +1 -1
  346. package/dist/validation/builtin/matchesField.js +3 -7
  347. package/dist/validation/builtin/matchesField.js.map +1 -1
  348. package/dist/validation/builtin/max.js +1 -0
  349. package/dist/validation/builtin/max.js.map +1 -1
  350. package/dist/validation/builtin/maxDate.js +1 -0
  351. package/dist/validation/builtin/maxDate.js.map +1 -1
  352. package/dist/validation/builtin/maxLength.js +1 -0
  353. package/dist/validation/builtin/maxLength.js.map +1 -1
  354. package/dist/validation/builtin/min.js +1 -0
  355. package/dist/validation/builtin/min.js.map +1 -1
  356. package/dist/validation/builtin/minDate.js +1 -0
  357. package/dist/validation/builtin/minDate.js.map +1 -1
  358. package/dist/validation/builtin/minLength.js +1 -0
  359. package/dist/validation/builtin/minLength.js.map +1 -1
  360. package/dist/validation/builtin/notAlreadySubmitted.js +1 -0
  361. package/dist/validation/builtin/notAlreadySubmitted.js.map +1 -1
  362. package/dist/validation/builtin/oneOf.js +1 -0
  363. package/dist/validation/builtin/oneOf.js.map +1 -1
  364. package/dist/validation/builtin/pattern.js +11 -2
  365. package/dist/validation/builtin/pattern.js.map +1 -1
  366. package/dist/validation/builtin/url.js +1 -0
  367. package/dist/validation/builtin/url.js.map +1 -1
  368. package/dist/validation/builtin/validateRegexParams.js +44 -0
  369. package/dist/validation/builtin/validateRegexParams.js.map +1 -0
  370. package/dist/validation/fieldTargetParam.d.ts +22 -0
  371. package/dist/validation/fieldTargetParam.js +38 -0
  372. package/dist/validation/fieldTargetParam.js.map +1 -0
  373. package/dist/validation/runValidation.js +8 -29
  374. package/dist/validation/runValidation.js.map +1 -1
  375. package/dist/validation/types.d.ts +5 -11
  376. package/dist/validation/validateUrl.js +22 -0
  377. package/dist/validation/validateUrl.js.map +1 -0
  378. package/package.json +7 -6
  379. package/styles/form-builder.css +10 -0
  380. package/dist/collections/uploads.d.ts +0 -30
  381. package/dist/collections/uploads.js +0 -55
  382. package/dist/collections/uploads.js.map +0 -1
  383. package/dist/consent/buildConsentSourceConfig.js +0 -49
  384. package/dist/consent/buildConsentSourceConfig.js.map +0 -1
  385. package/dist/consent/builtin/index.d.ts +0 -7
  386. package/dist/consent/builtin/index.js +0 -11
  387. package/dist/consent/builtin/index.js.map +0 -1
  388. package/dist/consent/builtin/pageReference.js +0 -75
  389. package/dist/consent/builtin/pageReference.js.map +0 -1
  390. package/dist/consent/builtin/static.js +0 -44
  391. package/dist/consent/builtin/static.js.map +0 -1
  392. package/dist/consent/defineConsentSource.d.ts +0 -35
  393. package/dist/consent/defineConsentSource.js +0 -6
  394. package/dist/consent/defineConsentSource.js.map +0 -1
  395. package/dist/consent/registry.d.ts +0 -16
  396. package/dist/consent/registry.js +0 -16
  397. package/dist/consent/registry.js.map +0 -1
  398. package/dist/consent/resolveConsentLinks.d.ts +0 -19
  399. package/dist/consent/resolveConsentLinks.js +0 -26
  400. package/dist/consent/resolveConsentLinks.js.map +0 -1
  401. package/dist/presentations/registry.d.ts +0 -15
  402. package/dist/presentations/registry.js +0 -15
  403. package/dist/presentations/registry.js.map +0 -1
  404. package/dist/react/toFormDocument.d.ts +0 -24
  405. package/dist/react/toFormDocument.js +0 -22
  406. package/dist/react/toFormDocument.js.map +0 -1
@@ -1,4 +1,10 @@
1
1
  import { FormFlow } from "../flow/types.js";
2
+ import { FormDocument } from "../form/types.js";
3
+ import { RendererTranslate } from "./contract.js";
4
+ import { RendererRegistry } from "./registry.js";
5
+ import { FormAction, FormState } from "./state.js";
6
+ import { Dispatch } from "react";
7
+
2
8
  //#region src/react/FormContext.d.ts
3
9
  /** Multi-step navigation state. Defaults to a single terminal step when the form has no flow. */
4
10
  type FormStepInfo = {
@@ -11,6 +17,36 @@ type FormStepInfo = {
11
17
  goNext: () => void;
12
18
  goBack: () => void;
13
19
  };
20
+ /** Resolved chrome button labels. Precedence per label: the `<Form>` prop, then the form's `buttons` value, then the translated default. */
21
+ type FormControlLabels = {
22
+ prev: string;
23
+ next: string;
24
+ submit: string;
25
+ };
26
+ /**
27
+ * The form controller's context, read via `useFormContext` (or the focused `useField`,
28
+ * `useFormState`, and `useFormStep` hooks). Available anywhere under `<Form>`, including
29
+ * custom `children` layouts and custom field renderers.
30
+ */
31
+ type FormContextValue = {
32
+ /** The document rendered by this `<Form>`. Custom chrome reads host-added `buttons` keys from here. */form: FormDocument; /** Current form state: values, errors, touched, submitting, submitted, submitError. */
33
+ state: FormState;
34
+ /**
35
+ * Dispatch a `FormAction` (see `./state` for the action union). Custom field layouts
36
+ * typically dispatch `TOUCH` or `SET_FIELD_ISSUES`; prefer `useField` for value binding,
37
+ * which wires `SET_VALUE` and validation for you.
38
+ */
39
+ dispatch: Dispatch<FormAction>; /** Validate one field now (client mode) against the supplied value and store its issues. */
40
+ validateField: (name: string, value: unknown) => void; /** The locale passed to `<Form>` (defaults to `'en'`). */
41
+ locale: string; /** Multi-step navigation state and the `goNext`/`goBack` handlers. */
42
+ step: FormStepInfo; /** The active renderer registry, exposed so nested renderers (e.g. repeater) can look up sub-renderers. */
43
+ rendererRegistry: RendererRegistry; /** Resolved prev/next/submit labels used by `<FormControls>` and available to custom chrome. */
44
+ labels: FormControlLabels; /** The active translator: the `<Form>` `t` prop, else the bundled English fallback. */
45
+ t: RendererTranslate; /** Calc-authoritative answers: `state.values` overlaid with derived calc values. Consumers fall back to `state.values` when absent. */
46
+ effectiveValues?: Record<string, unknown>;
47
+ };
48
+ /** Read the form controller context. Throws if used outside `<Form>`. */
49
+ declare const useFormContext: () => FormContextValue;
14
50
  //#endregion
15
- export { FormStepInfo };
51
+ export { FormContextValue, FormControlLabels, FormStepInfo, useFormContext };
16
52
  //# sourceMappingURL=FormContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FormContext.js","names":[],"sources":["../../src/react/FormContext.ts"],"sourcesContent":["'use client'\n\nimport type { Dispatch } from 'react'\nimport { createContext, useContext } from 'react'\nimport type { FormFlow } from '../flow/types'\nimport type { RendererRegistry } from './registry'\nimport type { FormAction, FormState } from './state'\n\n/** Multi-step navigation state. Defaults to a single terminal step when the form has no flow. */\nexport type FormStepInfo = {\n\tflow?: FormFlow\n\tcurrentStepId?: string\n\tstepIndex: number\n\tstepCount: number\n\tisFirst: boolean\n\tisTerminal: boolean\n\tgoNext: () => void\n\tgoBack: () => void\n}\n\nexport type FormContextValue = {\n\tstate: FormState\n\tdispatch: Dispatch<FormAction>\n\t/** Validate one field now (client mode) against the supplied value and store its issues. */\n\tvalidateField: (name: string, value: unknown) => void\n\tlocale: string\n\tstep: FormStepInfo\n\t/** The active renderer registry, exposed so nested renderers (e.g. repeater) can look up sub-renderers. */\n\trendererRegistry: RendererRegistry\n}\n\nexport const FormContext = createContext<FormContextValue | null>(null)\n\n/** Read the form controller context. Throws if used outside `<Form>`. */\nexport const useFormContext = (): FormContextValue => {\n\tconst context = useContext(FormContext)\n\tif (!context) {\n\t\tthrow new Error('useFormContext must be used within a <Form>')\n\t}\n\treturn context\n}\n"],"mappings":";;;AA+BA,MAAa,cAAc,cAAuC,IAAI;;AAGtE,MAAa,uBAAyC;CACrD,MAAM,UAAU,WAAW,WAAW;CACtC,IAAI,CAAC,SACJ,MAAM,IAAI,MAAM,6CAA6C;CAE9D,OAAO;AACR"}
1
+ {"version":3,"file":"FormContext.js","names":[],"sources":["../../src/react/FormContext.ts"],"sourcesContent":["'use client'\n\nimport type { Dispatch } from 'react'\nimport { createContext, useContext } from 'react'\nimport type { FormFlow } from '../flow/types'\nimport type { FormDocument } from '../form/types'\nimport type { RendererTranslate } from './contract'\nimport type { RendererRegistry } from './registry'\nimport type { FormAction, FormState } from './state'\n\n/** Multi-step navigation state. Defaults to a single terminal step when the form has no flow. */\nexport type FormStepInfo = {\n\tflow?: FormFlow\n\tcurrentStepId?: string\n\tstepIndex: number\n\tstepCount: number\n\tisFirst: boolean\n\tisTerminal: boolean\n\tgoNext: () => void\n\tgoBack: () => void\n}\n\n/** Resolved chrome button labels. Precedence per label: the `<Form>` prop, then the form's `buttons` value, then the translated default. */\nexport type FormControlLabels = {\n\tprev: string\n\tnext: string\n\tsubmit: string\n}\n\n/**\n * The form controller's context, read via `useFormContext` (or the focused `useField`,\n * `useFormState`, and `useFormStep` hooks). Available anywhere under `<Form>`, including\n * custom `children` layouts and custom field renderers.\n */\nexport type FormContextValue = {\n\t/** The document rendered by this `<Form>`. Custom chrome reads host-added `buttons` keys from here. */\n\tform: FormDocument\n\t/** Current form state: values, errors, touched, submitting, submitted, submitError. */\n\tstate: FormState\n\t/**\n\t * Dispatch a `FormAction` (see `./state` for the action union). Custom field layouts\n\t * typically dispatch `TOUCH` or `SET_FIELD_ISSUES`; prefer `useField` for value binding,\n\t * which wires `SET_VALUE` and validation for you.\n\t */\n\tdispatch: Dispatch<FormAction>\n\t/** Validate one field now (client mode) against the supplied value and store its issues. */\n\tvalidateField: (name: string, value: unknown) => void\n\t/** The locale passed to `<Form>` (defaults to `'en'`). */\n\tlocale: string\n\t/** Multi-step navigation state and the `goNext`/`goBack` handlers. */\n\tstep: FormStepInfo\n\t/** The active renderer registry, exposed so nested renderers (e.g. repeater) can look up sub-renderers. */\n\trendererRegistry: RendererRegistry\n\t/** Resolved prev/next/submit labels used by `<FormControls>` and available to custom chrome. */\n\tlabels: FormControlLabels\n\t/** The active translator: the `<Form>` `t` prop, else the bundled English fallback. */\n\tt: RendererTranslate\n\t/** Calc-authoritative answers: `state.values` overlaid with derived calc values. Consumers fall back to `state.values` when absent. */\n\teffectiveValues?: Record<string, unknown>\n}\n\nexport const FormContext = createContext<FormContextValue | null>(null)\n\n/** Read the form controller context. Throws if used outside `<Form>`. */\nexport const useFormContext = (): FormContextValue => {\n\tconst context = useContext(FormContext)\n\tif (!context) {\n\t\tthrow new Error('useFormContext must be used within a <Form>')\n\t}\n\treturn context\n}\n"],"mappings":";;;AA6DA,MAAa,cAAc,cAAuC,IAAI;;AAGtE,MAAa,uBAAyC;CACrD,MAAM,UAAU,WAAW,WAAW;CACtC,IAAI,CAAC,SACJ,MAAM,IAAI,MAAM,6CAA6C;CAE9D,OAAO;AACR"}
@@ -0,0 +1,55 @@
1
+ import { ReactNode } from "react";
2
+
3
+ //#region src/react/FormControls.d.ts
4
+ /** Props passed to `renderSubmit`. */
5
+ type SubmitButtonRenderProps = {
6
+ label: string;
7
+ submitting: boolean;
8
+ };
9
+ /** Props passed to `renderNext`. */
10
+ type NextButtonRenderProps = {
11
+ label: string;
12
+ submitting: boolean;
13
+ onClick: () => void;
14
+ };
15
+ /** Props passed to `renderBack`. */
16
+ type BackButtonRenderProps = {
17
+ label: string;
18
+ submitting: boolean;
19
+ onClick: () => void;
20
+ };
21
+ type FormControlsProps = {
22
+ /** Override the context-resolved back label for this instance. */prevLabel?: string; /** Override the context-resolved next label for this instance. */
23
+ nextLabel?: string; /** Override the context-resolved submit label for this instance. */
24
+ submitLabel?: string; /** Additional CSS class on the wrapping `.fb-form__controls` element. */
25
+ className?: string; /** CSS class forwarded to the default "Back" `<button>`. Ignored when `renderBack` is provided. */
26
+ backButtonClassName?: string; /** CSS class forwarded to the default "Next" `<button>`. Ignored when `renderNext` is provided. */
27
+ nextButtonClassName?: string; /** CSS class forwarded to the default submit `<button>`. Ignored when `renderSubmit` is provided. */
28
+ submitButtonClassName?: string; /** Replace the default "Back" button entirely. */
29
+ renderBack?: (props: BackButtonRenderProps) => ReactNode; /** Replace the default "Next" button entirely. */
30
+ renderNext?: (props: NextButtonRenderProps) => ReactNode; /** Replace the default submit button entirely. Receives the resolved label and submitting state. */
31
+ renderSubmit?: (props: SubmitButtonRenderProps) => ReactNode;
32
+ };
33
+ /**
34
+ * The form's navigation chrome: back on non-first steps, next on non-terminal steps, submit on
35
+ * the terminal step (a single-step form is first and terminal, so it gets only submit). Driven
36
+ * entirely by the form context, so it composes into custom `children` layouts exactly like the
37
+ * default field loop's chrome. Labels resolve from context (`<Form>` props, the form's
38
+ * `buttons` labels, translated defaults) unless overridden per button here. Must render
39
+ * inside `<Form>`, within its `<form>` element so the submit button submits.
40
+ */
41
+ declare const FormControls: ({
42
+ prevLabel,
43
+ nextLabel,
44
+ submitLabel,
45
+ className,
46
+ backButtonClassName,
47
+ nextButtonClassName,
48
+ submitButtonClassName,
49
+ renderBack,
50
+ renderNext,
51
+ renderSubmit
52
+ }: FormControlsProps) => import("react/jsx-runtime").JSX.Element;
53
+ //#endregion
54
+ export { BackButtonRenderProps, FormControls, FormControlsProps, NextButtonRenderProps, SubmitButtonRenderProps };
55
+ //# sourceMappingURL=FormControls.d.ts.map
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { cn } from "./cn.js";
3
+ import { useFormContext } from "./FormContext.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/react/FormControls.tsx
6
+ /**
7
+ * The form's navigation chrome: back on non-first steps, next on non-terminal steps, submit on
8
+ * the terminal step (a single-step form is first and terminal, so it gets only submit). Driven
9
+ * entirely by the form context, so it composes into custom `children` layouts exactly like the
10
+ * default field loop's chrome. Labels resolve from context (`<Form>` props, the form's
11
+ * `buttons` labels, translated defaults) unless overridden per button here. Must render
12
+ * inside `<Form>`, within its `<form>` element so the submit button submits.
13
+ */
14
+ const FormControls = ({ prevLabel, nextLabel, submitLabel, className, backButtonClassName, nextButtonClassName, submitButtonClassName, renderBack, renderNext, renderSubmit }) => {
15
+ const { state, step, labels } = useFormContext();
16
+ const submitting = state.submitting;
17
+ const back = prevLabel ?? labels.prev;
18
+ const next = nextLabel ?? labels.next;
19
+ const submit = submitLabel ?? labels.submit;
20
+ return /* @__PURE__ */ jsxs("div", {
21
+ className: cn("fb-form__controls", className),
22
+ children: [!step.isFirst ? renderBack ? renderBack({
23
+ label: back,
24
+ submitting,
25
+ onClick: step.goBack
26
+ }) : /* @__PURE__ */ jsx("button", {
27
+ type: "button",
28
+ className: cn("fb-form__back", backButtonClassName),
29
+ onClick: step.goBack,
30
+ disabled: submitting,
31
+ children: back
32
+ }) : null, step.isTerminal ? renderSubmit ? renderSubmit({
33
+ label: submit,
34
+ submitting
35
+ }) : /* @__PURE__ */ jsx("button", {
36
+ type: "submit",
37
+ className: cn("fb-form__submit", submitButtonClassName),
38
+ disabled: submitting,
39
+ children: submit
40
+ }) : renderNext ? renderNext({
41
+ label: next,
42
+ submitting,
43
+ onClick: step.goNext
44
+ }) : /* @__PURE__ */ jsx("button", {
45
+ type: "button",
46
+ className: cn("fb-form__next", nextButtonClassName),
47
+ onClick: step.goNext,
48
+ disabled: submitting,
49
+ children: next
50
+ })]
51
+ });
52
+ };
53
+ //#endregion
54
+ export { FormControls };
55
+
56
+ //# sourceMappingURL=FormControls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormControls.js","names":[],"sources":["../../src/react/FormControls.tsx"],"sourcesContent":["'use client'\n\nimport type { ReactNode } from 'react'\nimport { cn } from './cn'\nimport { useFormContext } from './FormContext'\n\n/** Props passed to `renderSubmit`. */\nexport type SubmitButtonRenderProps = {\n\tlabel: string\n\tsubmitting: boolean\n}\n\n/** Props passed to `renderNext`. */\nexport type NextButtonRenderProps = {\n\tlabel: string\n\tsubmitting: boolean\n\tonClick: () => void\n}\n\n/** Props passed to `renderBack`. */\nexport type BackButtonRenderProps = {\n\tlabel: string\n\tsubmitting: boolean\n\tonClick: () => void\n}\n\nexport type FormControlsProps = {\n\t/** Override the context-resolved back label for this instance. */\n\tprevLabel?: string\n\t/** Override the context-resolved next label for this instance. */\n\tnextLabel?: string\n\t/** Override the context-resolved submit label for this instance. */\n\tsubmitLabel?: string\n\t/** Additional CSS class on the wrapping `.fb-form__controls` element. */\n\tclassName?: string\n\t/** CSS class forwarded to the default \"Back\" `<button>`. Ignored when `renderBack` is provided. */\n\tbackButtonClassName?: string\n\t/** CSS class forwarded to the default \"Next\" `<button>`. Ignored when `renderNext` is provided. */\n\tnextButtonClassName?: string\n\t/** CSS class forwarded to the default submit `<button>`. Ignored when `renderSubmit` is provided. */\n\tsubmitButtonClassName?: string\n\t/** Replace the default \"Back\" button entirely. */\n\trenderBack?: (props: BackButtonRenderProps) => ReactNode\n\t/** Replace the default \"Next\" button entirely. */\n\trenderNext?: (props: NextButtonRenderProps) => ReactNode\n\t/** Replace the default submit button entirely. Receives the resolved label and submitting state. */\n\trenderSubmit?: (props: SubmitButtonRenderProps) => ReactNode\n}\n\n/**\n * The form's navigation chrome: back on non-first steps, next on non-terminal steps, submit on\n * the terminal step (a single-step form is first and terminal, so it gets only submit). Driven\n * entirely by the form context, so it composes into custom `children` layouts exactly like the\n * default field loop's chrome. Labels resolve from context (`<Form>` props, the form's\n * `buttons` labels, translated defaults) unless overridden per button here. Must render\n * inside `<Form>`, within its `<form>` element so the submit button submits.\n */\nexport const FormControls = ({\n\tprevLabel,\n\tnextLabel,\n\tsubmitLabel,\n\tclassName,\n\tbackButtonClassName,\n\tnextButtonClassName,\n\tsubmitButtonClassName,\n\trenderBack,\n\trenderNext,\n\trenderSubmit,\n}: FormControlsProps) => {\n\tconst { state, step, labels } = useFormContext()\n\tconst submitting = state.submitting\n\tconst back = prevLabel ?? labels.prev\n\tconst next = nextLabel ?? labels.next\n\tconst submit = submitLabel ?? labels.submit\n\treturn (\n\t\t<div className={cn('fb-form__controls', className)}>\n\t\t\t{!step.isFirst ? (\n\t\t\t\trenderBack ? (\n\t\t\t\t\trenderBack({ label: back, submitting, onClick: step.goBack })\n\t\t\t\t) : (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName={cn('fb-form__back', backButtonClassName)}\n\t\t\t\t\t\tonClick={step.goBack}\n\t\t\t\t\t\tdisabled={submitting}\n\t\t\t\t\t>\n\t\t\t\t\t\t{back}\n\t\t\t\t\t</button>\n\t\t\t\t)\n\t\t\t) : null}\n\t\t\t{step.isTerminal ? (\n\t\t\t\trenderSubmit ? (\n\t\t\t\t\trenderSubmit({ label: submit, submitting })\n\t\t\t\t) : (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\tclassName={cn('fb-form__submit', submitButtonClassName)}\n\t\t\t\t\t\tdisabled={submitting}\n\t\t\t\t\t>\n\t\t\t\t\t\t{submit}\n\t\t\t\t\t</button>\n\t\t\t\t)\n\t\t\t) : renderNext ? (\n\t\t\t\trenderNext({ label: next, submitting, onClick: step.goNext })\n\t\t\t) : (\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName={cn('fb-form__next', nextButtonClassName)}\n\t\t\t\t\tonClick={step.goNext}\n\t\t\t\t\tdisabled={submitting}\n\t\t\t\t>\n\t\t\t\t\t{next}\n\t\t\t\t</button>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n"],"mappings":";;;;;;;;;;;;;AAyDA,MAAa,gBAAgB,EAC5B,WACA,WACA,aACA,WACA,qBACA,qBACA,uBACA,YACA,YACA,mBACwB;CACxB,MAAM,EAAE,OAAO,MAAM,WAAW,eAAe;CAC/C,MAAM,aAAa,MAAM;CACzB,MAAM,OAAO,aAAa,OAAO;CACjC,MAAM,OAAO,aAAa,OAAO;CACjC,MAAM,SAAS,eAAe,OAAO;CACrC,OACC,qBAAC,OAAD;EAAK,WAAW,GAAG,qBAAqB,SAAS;YAAjD,CACE,CAAC,KAAK,UACN,aACC,WAAW;GAAE,OAAO;GAAM;GAAY,SAAS,KAAK;EAAO,CAAC,IAE5D,oBAAC,UAAD;GACC,MAAK;GACL,WAAW,GAAG,iBAAiB,mBAAmB;GAClD,SAAS,KAAK;GACd,UAAU;aAET;EACM,CAAA,IAEN,MACH,KAAK,aACL,eACC,aAAa;GAAE,OAAO;GAAQ;EAAW,CAAC,IAE1C,oBAAC,UAAD;GACC,MAAK;GACL,WAAW,GAAG,mBAAmB,qBAAqB;GACtD,UAAU;aAET;EACM,CAAA,IAEN,aACH,WAAW;GAAE,OAAO;GAAM;GAAY,SAAS,KAAK;EAAO,CAAC,IAE5D,oBAAC,UAAD;GACC,MAAK;GACL,WAAW,GAAG,iBAAiB,mBAAmB;GAClD,SAAS,KAAK;GACd,UAAU;aAET;EACM,CAAA,CAEL;;AAEP"}
@@ -7,18 +7,21 @@ type FormResultsProps = {
7
7
  /** One field aggregation or several (a survey summary), resolved server-side and passed in. */results: FieldAggregation | FieldAggregation[]; /** Localized translator for built-in copy. Defaults to identity (returns the key). */
8
8
  t?: RendererTranslate;
9
9
  locale?: string; /** Show the raw count beside each option. Default true. */
10
- showCounts?: boolean;
10
+ showCounts?: boolean; /** The recorded outcome's stable option values; every matching bucket is highlighted as a winner (a tie highlights more than one). */
11
+ winningValues?: string[];
11
12
  };
12
13
  /**
13
14
  * Presentational results view for polls and survey summaries: per-option bars with percentages. Headless,
14
15
  * data resolved server-side via `aggregateFieldResponses`/`aggregateFormResponses` and passed in (it never
15
16
  * fetches). The option label, count, and percentage are real text (the accessible content); the bar fill
16
- * is `aria-hidden` visual sugar sized by inline width.
17
+ * is `aria-hidden` visual sugar sized by inline width. With `winningValues`, every matching bucket gets
18
+ * the winner class and a translated badge (real text, so the outcome is accessible); a tie highlights more than one.
17
19
  */
18
20
  declare const FormResults: ({
19
21
  results,
20
22
  t,
21
- showCounts
23
+ showCounts,
24
+ winningValues
22
25
  }: FormResultsProps) => ReactNode;
23
26
  //#endregion
24
27
  export { FormResults, FormResultsProps };
@@ -4,7 +4,7 @@ import { en } from "../translations/en.js";
4
4
  import { makeTranslate } from "../translations/makeTranslate.js";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/react/FormResults.tsx
7
- const OneResult = ({ result, t, showCounts }) => {
7
+ const OneResult = ({ result, t, showCounts, winningValues }) => {
8
8
  if (result.total === 0) return /* @__PURE__ */ jsxs("section", {
9
9
  className: "fb-results__field",
10
10
  children: [result.label ? /* @__PURE__ */ jsx("h3", {
@@ -24,31 +24,37 @@ const OneResult = ({ result, t, showCounts }) => {
24
24
  }) : null,
25
25
  /* @__PURE__ */ jsx("ul", {
26
26
  className: "fb-results__list",
27
- children: result.buckets.map((bucket) => /* @__PURE__ */ jsxs("li", {
28
- className: "fb-results__row",
29
- children: [
30
- /* @__PURE__ */ jsx("span", {
31
- className: "fb-results__option",
32
- children: bucket.label
33
- }),
34
- /* @__PURE__ */ jsx("span", {
35
- className: "fb-results__bar",
36
- children: /* @__PURE__ */ jsx("span", {
37
- className: "fb-results__bar-fill",
38
- style: { width: `${bucket.percentage}%` },
39
- "aria-hidden": "true"
40
- })
41
- }),
42
- /* @__PURE__ */ jsxs("span", {
43
- className: "fb-results__pct",
44
- children: [bucket.percentage, "%"]
45
- }),
46
- showCounts ? /* @__PURE__ */ jsx("span", {
47
- className: "fb-results__count",
48
- children: bucket.count
49
- }) : null
50
- ]
51
- }, bucket.value))
27
+ children: result.buckets.map((bucket) => {
28
+ const isWinner = winningValues?.includes(bucket.value) ?? false;
29
+ return /* @__PURE__ */ jsxs("li", {
30
+ className: isWinner ? "fb-results__row fb-results__bucket--winner" : "fb-results__row",
31
+ children: [
32
+ /* @__PURE__ */ jsxs("span", {
33
+ className: "fb-results__option",
34
+ children: [bucket.label, isWinner ? /* @__PURE__ */ jsx("span", {
35
+ className: "fb-results__winner-badge",
36
+ children: t(keys.resultsWinner)
37
+ }) : null]
38
+ }),
39
+ /* @__PURE__ */ jsx("span", {
40
+ className: "fb-results__bar",
41
+ children: /* @__PURE__ */ jsx("span", {
42
+ className: "fb-results__bar-fill",
43
+ style: { width: `${bucket.percentage}%` },
44
+ "aria-hidden": "true"
45
+ })
46
+ }),
47
+ /* @__PURE__ */ jsxs("span", {
48
+ className: "fb-results__pct",
49
+ children: [bucket.percentage, "%"]
50
+ }),
51
+ showCounts ? /* @__PURE__ */ jsx("span", {
52
+ className: "fb-results__count",
53
+ children: bucket.count
54
+ }) : null
55
+ ]
56
+ }, bucket.value);
57
+ })
52
58
  }),
53
59
  /* @__PURE__ */ jsxs("p", {
54
60
  className: "fb-results__total",
@@ -66,16 +72,18 @@ const OneResult = ({ result, t, showCounts }) => {
66
72
  * Presentational results view for polls and survey summaries: per-option bars with percentages. Headless,
67
73
  * data resolved server-side via `aggregateFieldResponses`/`aggregateFormResponses` and passed in (it never
68
74
  * fetches). The option label, count, and percentage are real text (the accessible content); the bar fill
69
- * is `aria-hidden` visual sugar sized by inline width.
75
+ * is `aria-hidden` visual sugar sized by inline width. With `winningValues`, every matching bucket gets
76
+ * the winner class and a translated badge (real text, so the outcome is accessible); a tie highlights more than one.
70
77
  */
71
- const FormResults = ({ results, t, showCounts = true }) => {
78
+ const FormResults = ({ results, t, showCounts = true, winningValues }) => {
72
79
  const translate = t ?? makeTranslate(en);
73
80
  return /* @__PURE__ */ jsx("div", {
74
81
  className: "fb-results",
75
82
  children: (Array.isArray(results) ? results : [results]).map((result) => /* @__PURE__ */ jsx(OneResult, {
76
83
  result,
77
84
  t: translate,
78
- showCounts
85
+ showCounts,
86
+ winningValues
79
87
  }, result.field))
80
88
  });
81
89
  };
@@ -1 +1 @@
1
- {"version":3,"file":"FormResults.js","names":[],"sources":["../../src/react/FormResults.tsx"],"sourcesContent":["'use client'\n\nimport type { ReactNode } from 'react'\nimport type { FieldAggregation } from '../aggregation/types'\nimport { en } from '../translations/en'\nimport { keys } from '../translations/keys'\nimport { makeTranslate } from '../translations/makeTranslate'\nimport type { RendererTranslate } from './contract'\n\nexport type FormResultsProps = {\n\t/** One field aggregation or several (a survey summary), resolved server-side and passed in. */\n\tresults: FieldAggregation | FieldAggregation[]\n\t/** Localized translator for built-in copy. Defaults to identity (returns the key). */\n\tt?: RendererTranslate\n\tlocale?: string\n\t/** Show the raw count beside each option. Default true. */\n\tshowCounts?: boolean\n}\n\nconst OneResult = ({\n\tresult,\n\tt,\n\tshowCounts,\n}: {\n\tresult: FieldAggregation\n\tt: RendererTranslate\n\tshowCounts: boolean\n}): ReactNode => {\n\tif (result.total === 0) {\n\t\treturn (\n\t\t\t<section className=\"fb-results__field\">\n\t\t\t\t{result.label ? <h3 className=\"fb-results__label\">{result.label}</h3> : null}\n\t\t\t\t<p className=\"fb-results__empty\">{t(keys.resultsNoResponses)}</p>\n\t\t\t</section>\n\t\t)\n\t}\n\treturn (\n\t\t<section className=\"fb-results__field\">\n\t\t\t{result.label ? <h3 className=\"fb-results__label\">{result.label}</h3> : null}\n\t\t\t<ul className=\"fb-results__list\">\n\t\t\t\t{result.buckets.map((bucket) => (\n\t\t\t\t\t<li key={bucket.value} className=\"fb-results__row\">\n\t\t\t\t\t\t<span className=\"fb-results__option\">{bucket.label}</span>\n\t\t\t\t\t\t<span className=\"fb-results__bar\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName=\"fb-results__bar-fill\"\n\t\t\t\t\t\t\t\tstyle={{ width: `${bucket.percentage}%` }}\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"fb-results__pct\">{bucket.percentage}%</span>\n\t\t\t\t\t\t{showCounts ? <span className=\"fb-results__count\">{bucket.count}</span> : null}\n\t\t\t\t\t</li>\n\t\t\t\t))}\n\t\t\t</ul>\n\t\t\t<p className=\"fb-results__total\">\n\t\t\t\t{result.total} {t(keys.resultsResponses)}\n\t\t\t\t{result.truncated ? ` (${t(keys.resultsTruncated)})` : ''}\n\t\t\t</p>\n\t\t</section>\n\t)\n}\n\n/**\n * Presentational results view for polls and survey summaries: per-option bars with percentages. Headless,\n * data resolved server-side via `aggregateFieldResponses`/`aggregateFormResponses` and passed in (it never\n * fetches). The option label, count, and percentage are real text (the accessible content); the bar fill\n * is `aria-hidden` visual sugar sized by inline width.\n */\nexport const FormResults = ({ results, t, showCounts = true }: FormResultsProps): ReactNode => {\n\tconst translate: RendererTranslate = t ?? makeTranslate(en)\n\tconst list = Array.isArray(results) ? results : [results]\n\treturn (\n\t\t<div className=\"fb-results\">\n\t\t\t{list.map((result) => (\n\t\t\t\t<OneResult key={result.field} result={result} t={translate} showCounts={showCounts} />\n\t\t\t))}\n\t\t</div>\n\t)\n}\n"],"mappings":";;;;;;AAmBA,MAAM,aAAa,EAClB,QACA,GACA,iBAKgB;CAChB,IAAI,OAAO,UAAU,GACpB,OACC,qBAAC,WAAD;EAAS,WAAU;YAAnB,CACE,OAAO,QAAQ,oBAAC,MAAD;GAAI,WAAU;aAAqB,OAAO;EAAU,CAAA,IAAI,MACxE,oBAAC,KAAD;GAAG,WAAU;aAAqB,EAAE,KAAK,kBAAkB;EAAK,CAAA,CACxD;;CAGX,OACC,qBAAC,WAAD;EAAS,WAAU;YAAnB;GACE,OAAO,QAAQ,oBAAC,MAAD;IAAI,WAAU;cAAqB,OAAO;GAAU,CAAA,IAAI;GACxE,oBAAC,MAAD;IAAI,WAAU;cACZ,OAAO,QAAQ,KAAK,WACpB,qBAAC,MAAD;KAAuB,WAAU;eAAjC;MACC,oBAAC,QAAD;OAAM,WAAU;iBAAsB,OAAO;MAAY,CAAA;MACzD,oBAAC,QAAD;OAAM,WAAU;iBACf,oBAAC,QAAD;QACC,WAAU;QACV,OAAO,EAAE,OAAO,GAAG,OAAO,WAAW,GAAG;QACxC,eAAY;OACZ,CAAA;MACI,CAAA;MACN,qBAAC,QAAD;OAAM,WAAU;iBAAhB,CAAmC,OAAO,YAAW,GAAO;;MAC3D,aAAa,oBAAC,QAAD;OAAM,WAAU;iBAAqB,OAAO;MAAY,CAAA,IAAI;KACvE;OAXK,OAAO,KAWZ,CACJ;GACE,CAAA;GACJ,qBAAC,KAAD;IAAG,WAAU;cAAb;KACE,OAAO;KAAM;KAAE,EAAE,KAAK,gBAAgB;KACtC,OAAO,YAAY,KAAK,EAAE,KAAK,gBAAgB,EAAE,KAAK;IACrD;;EACK;;AAEX;;;;;;;AAQA,MAAa,eAAe,EAAE,SAAS,GAAG,aAAa,WAAwC;CAC9F,MAAM,YAA+B,KAAK,cAAc,EAAE;CAE1D,OACC,oBAAC,OAAD;EAAK,WAAU;aAFH,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,OAAO,GAGhD,KAAK,WACV,oBAAC,WAAD;GAAsC;GAAQ,GAAG;GAAuB;EAAa,GAArE,OAAO,KAA8D,CACrF;CACG,CAAA;AAEP"}
1
+ {"version":3,"file":"FormResults.js","names":[],"sources":["../../src/react/FormResults.tsx"],"sourcesContent":["'use client'\n\nimport type { ReactNode } from 'react'\nimport type { FieldAggregation } from '../aggregation/types'\nimport { en } from '../translations/en'\nimport { keys } from '../translations/keys'\nimport { makeTranslate } from '../translations/makeTranslate'\nimport type { RendererTranslate } from './contract'\n\nexport type FormResultsProps = {\n\t/** One field aggregation or several (a survey summary), resolved server-side and passed in. */\n\tresults: FieldAggregation | FieldAggregation[]\n\t/** Localized translator for built-in copy. Defaults to identity (returns the key). */\n\tt?: RendererTranslate\n\tlocale?: string\n\t/** Show the raw count beside each option. Default true. */\n\tshowCounts?: boolean\n\t/** The recorded outcome's stable option values; every matching bucket is highlighted as a winner (a tie highlights more than one). */\n\twinningValues?: string[]\n}\n\nconst OneResult = ({\n\tresult,\n\tt,\n\tshowCounts,\n\twinningValues,\n}: {\n\tresult: FieldAggregation\n\tt: RendererTranslate\n\tshowCounts: boolean\n\twinningValues?: string[]\n}): ReactNode => {\n\tif (result.total === 0) {\n\t\treturn (\n\t\t\t<section className=\"fb-results__field\">\n\t\t\t\t{result.label ? <h3 className=\"fb-results__label\">{result.label}</h3> : null}\n\t\t\t\t<p className=\"fb-results__empty\">{t(keys.resultsNoResponses)}</p>\n\t\t\t</section>\n\t\t)\n\t}\n\treturn (\n\t\t<section className=\"fb-results__field\">\n\t\t\t{result.label ? <h3 className=\"fb-results__label\">{result.label}</h3> : null}\n\t\t\t<ul className=\"fb-results__list\">\n\t\t\t\t{result.buckets.map((bucket) => {\n\t\t\t\t\tconst isWinner = winningValues?.includes(bucket.value) ?? false\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tkey={bucket.value}\n\t\t\t\t\t\t\tclassName={\n\t\t\t\t\t\t\t\tisWinner ? 'fb-results__row fb-results__bucket--winner' : 'fb-results__row'\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"fb-results__option\">\n\t\t\t\t\t\t\t\t{bucket.label}\n\t\t\t\t\t\t\t\t{isWinner ? (\n\t\t\t\t\t\t\t\t\t<span className=\"fb-results__winner-badge\">{t(keys.resultsWinner)}</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span className=\"fb-results__bar\">\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclassName=\"fb-results__bar-fill\"\n\t\t\t\t\t\t\t\t\tstyle={{ width: `${bucket.percentage}%` }}\n\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span className=\"fb-results__pct\">{bucket.percentage}%</span>\n\t\t\t\t\t\t\t{showCounts ? <span className=\"fb-results__count\">{bucket.count}</span> : null}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t)\n\t\t\t\t})}\n\t\t\t</ul>\n\t\t\t<p className=\"fb-results__total\">\n\t\t\t\t{result.total} {t(keys.resultsResponses)}\n\t\t\t\t{result.truncated ? ` (${t(keys.resultsTruncated)})` : ''}\n\t\t\t</p>\n\t\t</section>\n\t)\n}\n\n/**\n * Presentational results view for polls and survey summaries: per-option bars with percentages. Headless,\n * data resolved server-side via `aggregateFieldResponses`/`aggregateFormResponses` and passed in (it never\n * fetches). The option label, count, and percentage are real text (the accessible content); the bar fill\n * is `aria-hidden` visual sugar sized by inline width. With `winningValues`, every matching bucket gets\n * the winner class and a translated badge (real text, so the outcome is accessible); a tie highlights more than one.\n */\nexport const FormResults = ({\n\tresults,\n\tt,\n\tshowCounts = true,\n\twinningValues,\n}: FormResultsProps): ReactNode => {\n\tconst translate: RendererTranslate = t ?? makeTranslate(en)\n\tconst list = Array.isArray(results) ? results : [results]\n\treturn (\n\t\t<div className=\"fb-results\">\n\t\t\t{list.map((result) => (\n\t\t\t\t<OneResult\n\t\t\t\t\tkey={result.field}\n\t\t\t\t\tresult={result}\n\t\t\t\t\tt={translate}\n\t\t\t\t\tshowCounts={showCounts}\n\t\t\t\t\twinningValues={winningValues}\n\t\t\t\t/>\n\t\t\t))}\n\t\t</div>\n\t)\n}\n"],"mappings":";;;;;;AAqBA,MAAM,aAAa,EAClB,QACA,GACA,YACA,oBAMgB;CAChB,IAAI,OAAO,UAAU,GACpB,OACC,qBAAC,WAAD;EAAS,WAAU;YAAnB,CACE,OAAO,QAAQ,oBAAC,MAAD;GAAI,WAAU;aAAqB,OAAO;EAAU,CAAA,IAAI,MACxE,oBAAC,KAAD;GAAG,WAAU;aAAqB,EAAE,KAAK,kBAAkB;EAAK,CAAA,CACxD;;CAGX,OACC,qBAAC,WAAD;EAAS,WAAU;YAAnB;GACE,OAAO,QAAQ,oBAAC,MAAD;IAAI,WAAU;cAAqB,OAAO;GAAU,CAAA,IAAI;GACxE,oBAAC,MAAD;IAAI,WAAU;cACZ,OAAO,QAAQ,KAAK,WAAW;KAC/B,MAAM,WAAW,eAAe,SAAS,OAAO,KAAK,KAAK;KAC1D,OACC,qBAAC,MAAD;MAEC,WACC,WAAW,+CAA+C;gBAH5D;OAMC,qBAAC,QAAD;QAAM,WAAU;kBAAhB,CACE,OAAO,OACP,WACA,oBAAC,QAAD;SAAM,WAAU;mBAA4B,EAAE,KAAK,aAAa;QAAQ,CAAA,IACrE,IACC;;OACN,oBAAC,QAAD;QAAM,WAAU;kBACf,oBAAC,QAAD;SACC,WAAU;SACV,OAAO,EAAE,OAAO,GAAG,OAAO,WAAW,GAAG;SACxC,eAAY;QACZ,CAAA;OACI,CAAA;OACN,qBAAC,QAAD;QAAM,WAAU;kBAAhB,CAAmC,OAAO,YAAW,GAAO;;OAC3D,aAAa,oBAAC,QAAD;QAAM,WAAU;kBAAqB,OAAO;OAAY,CAAA,IAAI;MACvE;QApBE,OAAO,KAoBT;IAEN,CAAC;GACE,CAAA;GACJ,qBAAC,KAAD;IAAG,WAAU;cAAb;KACE,OAAO;KAAM;KAAE,EAAE,KAAK,gBAAgB;KACtC,OAAO,YAAY,KAAK,EAAE,KAAK,gBAAgB,EAAE,KAAK;IACrD;;EACK;;AAEX;;;;;;;;AASA,MAAa,eAAe,EAC3B,SACA,GACA,aAAa,MACb,oBACkC;CAClC,MAAM,YAA+B,KAAK,cAAc,EAAE;CAE1D,OACC,oBAAC,OAAD;EAAK,WAAU;aAFH,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,OAAO,GAGhD,KAAK,WACV,oBAAC,WAAD;GAES;GACR,GAAG;GACS;GACG;EACf,GALK,OAAO,KAKZ,CACD;CACG,CAAA;AAEP"}
@@ -0,0 +1,17 @@
1
+ //#region src/react/FormSteps.d.ts
2
+ type FormStepsProps = {
3
+ /** Additional CSS class on the wrapping `.fb-form-steps` list. */className?: string;
4
+ };
5
+ /**
6
+ * Step-progress indicator for multi-step forms: an ordered list of the flow's steps with the
7
+ * current one marked via `aria-current="step"` and a `--current` class. Step labels use the
8
+ * step's title, falling back to the translated "Step {n}" (same semantics as the admin flow
9
+ * builder). Carries no styling opinions beyond `fb-form-steps` classes. Renders nothing when
10
+ * the form has fewer than two steps. Must render inside `<Form>`.
11
+ */
12
+ declare const FormSteps: ({
13
+ className
14
+ }: FormStepsProps) => import("react/jsx-runtime").JSX.Element | null;
15
+ //#endregion
16
+ export { FormSteps, FormStepsProps };
17
+ //# sourceMappingURL=FormSteps.d.ts.map
@@ -0,0 +1,35 @@
1
+ "use client";
2
+ import { keys } from "../translations/keys.js";
3
+ import { stepLabel } from "../flow/stepLabel.js";
4
+ import { cn } from "./cn.js";
5
+ import { useFormContext } from "./FormContext.js";
6
+ import { jsx } from "react/jsx-runtime";
7
+ //#region src/react/FormSteps.tsx
8
+ /**
9
+ * Step-progress indicator for multi-step forms: an ordered list of the flow's steps with the
10
+ * current one marked via `aria-current="step"` and a `--current` class. Step labels use the
11
+ * step's title, falling back to the translated "Step {n}" (same semantics as the admin flow
12
+ * builder). Carries no styling opinions beyond `fb-form-steps` classes. Renders nothing when
13
+ * the form has fewer than two steps. Must render inside `<Form>`.
14
+ */
15
+ const FormSteps = ({ className }) => {
16
+ const { step, t } = useFormContext();
17
+ const steps = step.flow?.steps;
18
+ if (!steps || steps.length < 2) return null;
19
+ const fallbackTitle = t(keys.flowStepFallbackTitle);
20
+ return /* @__PURE__ */ jsx("ol", {
21
+ className: cn("fb-form-steps", className),
22
+ children: steps.map((flowStep, index) => {
23
+ const current = index === step.stepIndex;
24
+ return /* @__PURE__ */ jsx("li", {
25
+ className: cn("fb-form-steps__step", current && "fb-form-steps__step--current"),
26
+ "aria-current": current ? "step" : void 0,
27
+ children: stepLabel(flowStep, index, fallbackTitle)
28
+ }, flowStep.id);
29
+ })
30
+ });
31
+ };
32
+ //#endregion
33
+ export { FormSteps };
34
+
35
+ //# sourceMappingURL=FormSteps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormSteps.js","names":[],"sources":["../../src/react/FormSteps.tsx"],"sourcesContent":["'use client'\n\nimport { stepLabel } from '../flow/stepLabel'\nimport { keys } from '../translations/keys'\nimport { cn } from './cn'\nimport { useFormContext } from './FormContext'\n\nexport type FormStepsProps = {\n\t/** Additional CSS class on the wrapping `.fb-form-steps` list. */\n\tclassName?: string\n}\n\n/**\n * Step-progress indicator for multi-step forms: an ordered list of the flow's steps with the\n * current one marked via `aria-current=\"step\"` and a `--current` class. Step labels use the\n * step's title, falling back to the translated \"Step {n}\" (same semantics as the admin flow\n * builder). Carries no styling opinions beyond `fb-form-steps` classes. Renders nothing when\n * the form has fewer than two steps. Must render inside `<Form>`.\n */\nexport const FormSteps = ({ className }: FormStepsProps) => {\n\tconst { step, t } = useFormContext()\n\tconst steps = step.flow?.steps\n\tif (!steps || steps.length < 2) {\n\t\treturn null\n\t}\n\tconst fallbackTitle = t(keys.flowStepFallbackTitle)\n\treturn (\n\t\t<ol className={cn('fb-form-steps', className)}>\n\t\t\t{steps.map((flowStep, index) => {\n\t\t\t\tconst current = index === step.stepIndex\n\t\t\t\treturn (\n\t\t\t\t\t<li\n\t\t\t\t\t\tkey={flowStep.id}\n\t\t\t\t\t\tclassName={cn('fb-form-steps__step', current && 'fb-form-steps__step--current')}\n\t\t\t\t\t\taria-current={current ? 'step' : undefined}\n\t\t\t\t\t>\n\t\t\t\t\t\t{stepLabel(flowStep, index, fallbackTitle)}\n\t\t\t\t\t</li>\n\t\t\t\t)\n\t\t\t})}\n\t\t</ol>\n\t)\n}\n"],"mappings":";;;;;;;;;;;;;;AAmBA,MAAa,aAAa,EAAE,gBAAgC;CAC3D,MAAM,EAAE,MAAM,MAAM,eAAe;CACnC,MAAM,QAAQ,KAAK,MAAM;CACzB,IAAI,CAAC,SAAS,MAAM,SAAS,GAC5B,OAAO;CAER,MAAM,gBAAgB,EAAE,KAAK,qBAAqB;CAClD,OACC,oBAAC,MAAD;EAAI,WAAW,GAAG,iBAAiB,SAAS;YAC1C,MAAM,KAAK,UAAU,UAAU;GAC/B,MAAM,UAAU,UAAU,KAAK;GAC/B,OACC,oBAAC,MAAD;IAEC,WAAW,GAAG,uBAAuB,WAAW,8BAA8B;IAC9E,gBAAc,UAAU,SAAS,KAAA;cAEhC,UAAU,UAAU,OAAO,aAAa;GACtC,GALE,SAAS,EAKX;EAEN,CAAC;CACE,CAAA;AAEN"}
@@ -3,8 +3,13 @@ import { fetchFormResults } from "./fetchResults.js";
3
3
 
4
4
  //#region src/react/Poll.d.ts
5
5
  type PollProps = FormProps & {
6
- /** The choice field whose results are shown after voting (should match the form's public `resultsField`). */resultsField: string; /** localStorage key for the per-browser voted guard. Default `fb-poll-{form.id}`. */
7
- storageKey?: string; /** Force the voted state (controlled). Omit to use the localStorage guard. */
6
+ /** The choice field whose results are shown after voting (should match the form's public `poll.resultsField`). */resultsField: string; /** localStorage key for the per-browser voted guard. Default `fb-poll-{form.id}`. */
7
+ storageKey?: string;
8
+ /**
9
+ * Server-known voted state, ORed with the localStorage guard: `true` marks the visitor as voted;
10
+ * `false`/omitted falls back to localStorage. SSR hosts using the plugin's `poll.votedCookie`
11
+ * option pass `hasVotedCookie(cookieHeader, form.id)` here.
12
+ */
8
13
  hasVoted?: boolean;
9
14
  /**
10
15
  * Injectable results fetch (testing); defaults to `fetchFormResults`. Pass a stable reference (module
@@ -13,10 +18,16 @@ type PollProps = FormProps & {
13
18
  fetchResultsImpl?: typeof fetchFormResults;
14
19
  };
15
20
  /**
16
- * A poll: renders `<Form>` until the visitor votes, then fetches the aggregate results and shows
17
- * `<FormResults>`. A per-browser localStorage flag (`storageKey`) skips straight to results on revisit. The
18
- * guard is UX, not integrity (bypassable): server-enforced one-per-identity dedup composes via `req.user`
19
- * (authed forms) or a `notAlreadySubmitted` rule, with cookie/IP identity deferred to the spam phase.
21
+ * A poll: renders `<Form>` while open and not yet voted, then fetches the aggregate results and shows
22
+ * `<FormResults>`. Lifecycle comes from `form.poll`: past `closesAt` the poll is closed (a translated
23
+ * notice plus results, which the endpoint serves for any visibility once closed); a voted-but-open
24
+ * `afterClose` poll shows a translated wait notice instead of fetching (the endpoint would refuse). A
25
+ * recorded `outcome.winningValues` (via `resolvePollOutcome`) supersedes everything: a translated final
26
+ * notice plus results with every winning bucket highlighted (a tie highlights more than one). A
27
+ * per-browser localStorage flag (`storageKey`) skips straight to results on revisit; `hasVoted: true`
28
+ * (e.g. from the server-set voted cookie) marks voted regardless of localStorage. The guard is UX, not
29
+ * integrity (bypassable): server-enforced
30
+ * one-per-identity dedup composes via `req.user` (authed forms) or a `notAlreadySubmitted` rule.
20
31
  */
21
32
  declare const Poll: ({
22
33
  resultsField,
@@ -1,9 +1,13 @@
1
1
  "use client";
2
+ import { keys } from "../translations/keys.js";
3
+ import { isPollClosed } from "../form/pollState.js";
4
+ import { en } from "../translations/en.js";
5
+ import { makeTranslate } from "../translations/makeTranslate.js";
2
6
  import { Form } from "./Form.js";
3
7
  import { FormResults } from "./FormResults.js";
4
8
  import { fetchFormResults } from "./fetchResults.js";
5
- import { jsx } from "react/jsx-runtime";
6
- import { useCallback, useEffect, useState } from "react";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ import { useCallback, useEffect, useMemo, useState } from "react";
7
11
  //#region src/react/Poll.tsx
8
12
  const readVoted = (key) => {
9
13
  try {
@@ -18,15 +22,27 @@ const writeVoted = (key) => {
18
22
  } catch {}
19
23
  };
20
24
  /**
21
- * A poll: renders `<Form>` until the visitor votes, then fetches the aggregate results and shows
22
- * `<FormResults>`. A per-browser localStorage flag (`storageKey`) skips straight to results on revisit. The
23
- * guard is UX, not integrity (bypassable): server-enforced one-per-identity dedup composes via `req.user`
24
- * (authed forms) or a `notAlreadySubmitted` rule, with cookie/IP identity deferred to the spam phase.
25
+ * A poll: renders `<Form>` while open and not yet voted, then fetches the aggregate results and shows
26
+ * `<FormResults>`. Lifecycle comes from `form.poll`: past `closesAt` the poll is closed (a translated
27
+ * notice plus results, which the endpoint serves for any visibility once closed); a voted-but-open
28
+ * `afterClose` poll shows a translated wait notice instead of fetching (the endpoint would refuse). A
29
+ * recorded `outcome.winningValues` (via `resolvePollOutcome`) supersedes everything: a translated final
30
+ * notice plus results with every winning bucket highlighted (a tie highlights more than one). A
31
+ * per-browser localStorage flag (`storageKey`) skips straight to results on revisit; `hasVoted: true`
32
+ * (e.g. from the server-set voted cookie) marks voted regardless of localStorage. The guard is UX, not
33
+ * integrity (bypassable): server-enforced
34
+ * one-per-identity dedup composes via `req.user` (authed forms) or a `notAlreadySubmitted` rule.
25
35
  */
26
36
  const Poll = ({ resultsField, storageKey, hasVoted, fetchResultsImpl = fetchFormResults, apiRoute, onSuccess, ...formProps }) => {
27
37
  const key = storageKey ?? `fb-poll-${formProps.form.id}`;
38
+ const poll = formProps.form.poll;
39
+ const closed = isPollClosed(poll);
40
+ const winningValues = poll?.outcome?.winningValues;
41
+ const finalized = Array.isArray(winningValues) && winningValues.length > 0;
42
+ const resultsAwaitClose = !closed && !finalized && poll?.resultsVisibility === "afterClose";
28
43
  const [voted, setVoted] = useState(false);
29
44
  const [results, setResults] = useState(null);
45
+ const translate = useMemo(() => formProps.t ?? makeTranslate(en), [formProps.t]);
30
46
  const loadResults = useCallback(async () => {
31
47
  const result = await fetchResultsImpl({
32
48
  formId: formProps.form.id,
@@ -41,30 +57,62 @@ const Poll = ({ resultsField, storageKey, hasVoted, fetchResultsImpl = fetchForm
41
57
  apiRoute
42
58
  ]);
43
59
  useEffect(() => {
44
- if (hasVoted ?? readVoted(key)) {
45
- setVoted(true);
46
- loadResults();
47
- }
60
+ const already = hasVoted === true || readVoted(key);
61
+ if (already) setVoted(true);
62
+ if (already && !resultsAwaitClose || closed || finalized) loadResults();
48
63
  }, [
49
64
  hasVoted,
50
65
  key,
51
- loadResults
66
+ loadResults,
67
+ closed,
68
+ resultsAwaitClose,
69
+ finalized
52
70
  ]);
53
71
  const handleSuccess = useCallback((submissionId) => {
54
72
  writeVoted(key);
55
73
  setVoted(true);
56
- loadResults();
74
+ if (!resultsAwaitClose) loadResults();
57
75
  onSuccess?.(submissionId);
58
76
  }, [
59
77
  key,
60
78
  loadResults,
61
- onSuccess
79
+ onSuccess,
80
+ resultsAwaitClose
62
81
  ]);
63
- if (voted) return results ? /* @__PURE__ */ jsx(FormResults, {
64
- results,
65
- t: formProps.t,
66
- locale: formProps.locale
67
- }) : null;
82
+ if (finalized) return /* @__PURE__ */ jsxs("div", {
83
+ className: "fb-poll fb-poll--final",
84
+ children: [/* @__PURE__ */ jsx("p", {
85
+ className: "fb-poll__final",
86
+ children: translate(keys.pollFinalResult)
87
+ }), results ? /* @__PURE__ */ jsx(FormResults, {
88
+ results,
89
+ winningValues,
90
+ t: formProps.t,
91
+ locale: formProps.locale
92
+ }) : null]
93
+ });
94
+ if (closed) return /* @__PURE__ */ jsxs("div", {
95
+ className: "fb-poll fb-poll--closed",
96
+ children: [/* @__PURE__ */ jsx("p", {
97
+ className: "fb-poll__closed",
98
+ children: translate(keys.pollClosed)
99
+ }), results ? /* @__PURE__ */ jsx(FormResults, {
100
+ results,
101
+ t: formProps.t,
102
+ locale: formProps.locale
103
+ }) : null]
104
+ });
105
+ if (voted) {
106
+ if (resultsAwaitClose) return /* @__PURE__ */ jsx("p", {
107
+ className: "fb-poll__await-close",
108
+ children: translate(keys.pollResultsAfterClose)
109
+ });
110
+ return results ? /* @__PURE__ */ jsx(FormResults, {
111
+ results,
112
+ t: formProps.t,
113
+ locale: formProps.locale
114
+ }) : null;
115
+ }
68
116
  return /* @__PURE__ */ jsx(Form, {
69
117
  ...formProps,
70
118
  apiRoute,
@@ -1 +1 @@
1
- {"version":3,"file":"Poll.js","names":[],"sources":["../../src/react/Poll.tsx"],"sourcesContent":["'use client'\n\nimport { useCallback, useEffect, useState } from 'react'\nimport type { FieldAggregation } from '../aggregation/types'\nimport { Form, type FormProps } from './Form'\nimport { FormResults } from './FormResults'\nimport { type FetchResultsResult, fetchFormResults } from './fetchResults'\n\nexport type PollProps = FormProps & {\n\t/** The choice field whose results are shown after voting (should match the form's public `resultsField`). */\n\tresultsField: string\n\t/** localStorage key for the per-browser voted guard. Default `fb-poll-{form.id}`. */\n\tstorageKey?: string\n\t/** Force the voted state (controlled). Omit to use the localStorage guard. */\n\thasVoted?: boolean\n\t/**\n\t * Injectable results fetch (testing); defaults to `fetchFormResults`. Pass a stable reference (module\n\t * scope, `useCallback`, or `useMemo`): an inline function re-runs the load effect and double-fetches.\n\t */\n\tfetchResultsImpl?: typeof fetchFormResults\n}\n\nconst readVoted = (key: string): boolean => {\n\ttry {\n\t\treturn window.localStorage.getItem(key) != null\n\t} catch {\n\t\treturn false\n\t}\n}\n\nconst writeVoted = (key: string): void => {\n\ttry {\n\t\twindow.localStorage.setItem(key, '1')\n\t} catch {\n\t\t// Private mode / storage disabled: the guard is best-effort UX, never integrity.\n\t}\n}\n\n/**\n * A poll: renders `<Form>` until the visitor votes, then fetches the aggregate results and shows\n * `<FormResults>`. A per-browser localStorage flag (`storageKey`) skips straight to results on revisit. The\n * guard is UX, not integrity (bypassable): server-enforced one-per-identity dedup composes via `req.user`\n * (authed forms) or a `notAlreadySubmitted` rule, with cookie/IP identity deferred to the spam phase.\n */\nexport const Poll = ({\n\tresultsField,\n\tstorageKey,\n\thasVoted,\n\tfetchResultsImpl = fetchFormResults,\n\tapiRoute,\n\tonSuccess,\n\t...formProps\n}: PollProps) => {\n\tconst key = storageKey ?? `fb-poll-${formProps.form.id}`\n\tconst [voted, setVoted] = useState(false)\n\tconst [results, setResults] = useState<FieldAggregation[] | null>(null)\n\n\tconst loadResults = useCallback(async () => {\n\t\tconst result: FetchResultsResult = await fetchResultsImpl({\n\t\t\tformId: formProps.form.id,\n\t\t\tfield: resultsField,\n\t\t\tapiRoute,\n\t\t})\n\t\tsetResults(result.ok ? result.results : [])\n\t}, [fetchResultsImpl, formProps.form.id, resultsField, apiRoute])\n\n\tuseEffect(() => {\n\t\tconst already = hasVoted ?? readVoted(key)\n\t\tif (already) {\n\t\t\tsetVoted(true)\n\t\t\tvoid loadResults()\n\t\t}\n\t}, [hasVoted, key, loadResults])\n\n\tconst handleSuccess = useCallback(\n\t\t(submissionId?: string) => {\n\t\t\twriteVoted(key)\n\t\t\tsetVoted(true)\n\t\t\tvoid loadResults()\n\t\t\tonSuccess?.(submissionId)\n\t\t},\n\t\t[key, loadResults, onSuccess]\n\t)\n\n\tif (voted) {\n\t\treturn results ? (\n\t\t\t<FormResults results={results} t={formProps.t} locale={formProps.locale} />\n\t\t) : null\n\t}\n\n\treturn <Form {...formProps} apiRoute={apiRoute} onSuccess={handleSuccess} />\n}\n"],"mappings":";;;;;;;AAsBA,MAAM,aAAa,QAAyB;CAC3C,IAAI;EACH,OAAO,OAAO,aAAa,QAAQ,GAAG,KAAK;CAC5C,QAAQ;EACP,OAAO;CACR;AACD;AAEA,MAAM,cAAc,QAAsB;CACzC,IAAI;EACH,OAAO,aAAa,QAAQ,KAAK,GAAG;CACrC,QAAQ,CAER;AACD;;;;;;;AAQA,MAAa,QAAQ,EACpB,cACA,YACA,UACA,mBAAmB,kBACnB,UACA,WACA,GAAG,gBACa;CAChB,MAAM,MAAM,cAAc,WAAW,UAAU,KAAK;CACpD,MAAM,CAAC,OAAO,YAAY,SAAS,KAAK;CACxC,MAAM,CAAC,SAAS,cAAc,SAAoC,IAAI;CAEtE,MAAM,cAAc,YAAY,YAAY;EAC3C,MAAM,SAA6B,MAAM,iBAAiB;GACzD,QAAQ,UAAU,KAAK;GACvB,OAAO;GACP;EACD,CAAC;EACD,WAAW,OAAO,KAAK,OAAO,UAAU,CAAC,CAAC;CAC3C,GAAG;EAAC;EAAkB,UAAU,KAAK;EAAI;EAAc;CAAQ,CAAC;CAEhE,gBAAgB;EAEf,IADgB,YAAY,UAAU,GAAG,GAC5B;GACZ,SAAS,IAAI;GACb,YAAiB;EAClB;CACD,GAAG;EAAC;EAAU;EAAK;CAAW,CAAC;CAE/B,MAAM,gBAAgB,aACpB,iBAA0B;EAC1B,WAAW,GAAG;EACd,SAAS,IAAI;EACb,YAAiB;EACjB,YAAY,YAAY;CACzB,GACA;EAAC;EAAK;EAAa;CAAS,CAC7B;CAEA,IAAI,OACH,OAAO,UACN,oBAAC,aAAD;EAAsB;EAAS,GAAG,UAAU;EAAG,QAAQ,UAAU;CAAS,CAAA,IACvE;CAGL,OAAO,oBAAC,MAAD;EAAM,GAAI;EAAqB;EAAU,WAAW;CAAgB,CAAA;AAC5E"}
1
+ {"version":3,"file":"Poll.js","names":[],"sources":["../../src/react/Poll.tsx"],"sourcesContent":["'use client'\n\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport type { FieldAggregation } from '../aggregation/types'\nimport { isPollClosed } from '../form/pollState'\nimport { en } from '../translations/en'\nimport { keys } from '../translations/keys'\nimport { makeTranslate } from '../translations/makeTranslate'\nimport { Form, type FormProps } from './Form'\nimport { FormResults } from './FormResults'\nimport { type FetchResultsResult, fetchFormResults } from './fetchResults'\n\nexport type PollProps = FormProps & {\n\t/** The choice field whose results are shown after voting (should match the form's public `poll.resultsField`). */\n\tresultsField: string\n\t/** localStorage key for the per-browser voted guard. Default `fb-poll-{form.id}`. */\n\tstorageKey?: string\n\t/**\n\t * Server-known voted state, ORed with the localStorage guard: `true` marks the visitor as voted;\n\t * `false`/omitted falls back to localStorage. SSR hosts using the plugin's `poll.votedCookie`\n\t * option pass `hasVotedCookie(cookieHeader, form.id)` here.\n\t */\n\thasVoted?: boolean\n\t/**\n\t * Injectable results fetch (testing); defaults to `fetchFormResults`. Pass a stable reference (module\n\t * scope, `useCallback`, or `useMemo`): an inline function re-runs the load effect and double-fetches.\n\t */\n\tfetchResultsImpl?: typeof fetchFormResults\n}\n\nconst readVoted = (key: string): boolean => {\n\ttry {\n\t\treturn window.localStorage.getItem(key) != null\n\t} catch {\n\t\treturn false\n\t}\n}\n\nconst writeVoted = (key: string): void => {\n\ttry {\n\t\twindow.localStorage.setItem(key, '1')\n\t} catch {\n\t\t// Private mode / storage disabled: the guard is best-effort UX, never integrity.\n\t}\n}\n\n/**\n * A poll: renders `<Form>` while open and not yet voted, then fetches the aggregate results and shows\n * `<FormResults>`. Lifecycle comes from `form.poll`: past `closesAt` the poll is closed (a translated\n * notice plus results, which the endpoint serves for any visibility once closed); a voted-but-open\n * `afterClose` poll shows a translated wait notice instead of fetching (the endpoint would refuse). A\n * recorded `outcome.winningValues` (via `resolvePollOutcome`) supersedes everything: a translated final\n * notice plus results with every winning bucket highlighted (a tie highlights more than one). A\n * per-browser localStorage flag (`storageKey`) skips straight to results on revisit; `hasVoted: true`\n * (e.g. from the server-set voted cookie) marks voted regardless of localStorage. The guard is UX, not\n * integrity (bypassable): server-enforced\n * one-per-identity dedup composes via `req.user` (authed forms) or a `notAlreadySubmitted` rule.\n */\nexport const Poll = ({\n\tresultsField,\n\tstorageKey,\n\thasVoted,\n\tfetchResultsImpl = fetchFormResults,\n\tapiRoute,\n\tonSuccess,\n\t...formProps\n}: PollProps) => {\n\tconst key = storageKey ?? `fb-poll-${formProps.form.id}`\n\tconst poll = formProps.form.poll\n\tconst closed = isPollClosed(poll)\n\tconst winningValues = poll?.outcome?.winningValues\n\tconst finalized = Array.isArray(winningValues) && winningValues.length > 0\n\tconst resultsAwaitClose = !closed && !finalized && poll?.resultsVisibility === 'afterClose'\n\tconst [voted, setVoted] = useState(false)\n\tconst [results, setResults] = useState<FieldAggregation[] | null>(null)\n\tconst translate = useMemo(() => formProps.t ?? makeTranslate(en), [formProps.t])\n\n\tconst loadResults = useCallback(async () => {\n\t\tconst result: FetchResultsResult = await fetchResultsImpl({\n\t\t\tformId: formProps.form.id,\n\t\t\tfield: resultsField,\n\t\t\tapiRoute,\n\t\t})\n\t\tsetResults(result.ok ? result.results : [])\n\t}, [fetchResultsImpl, formProps.form.id, resultsField, apiRoute])\n\n\tuseEffect(() => {\n\t\tconst already = hasVoted === true || readVoted(key)\n\t\tif (already) {\n\t\t\tsetVoted(true)\n\t\t}\n\t\tif ((already && !resultsAwaitClose) || closed || finalized) {\n\t\t\tvoid loadResults()\n\t\t}\n\t}, [hasVoted, key, loadResults, closed, resultsAwaitClose, finalized])\n\n\tconst handleSuccess = useCallback(\n\t\t(submissionId?: string) => {\n\t\t\twriteVoted(key)\n\t\t\tsetVoted(true)\n\t\t\tif (!resultsAwaitClose) {\n\t\t\t\tvoid loadResults()\n\t\t\t}\n\t\t\tonSuccess?.(submissionId)\n\t\t},\n\t\t[key, loadResults, onSuccess, resultsAwaitClose]\n\t)\n\n\tif (finalized) {\n\t\treturn (\n\t\t\t<div className=\"fb-poll fb-poll--final\">\n\t\t\t\t<p className=\"fb-poll__final\">{translate(keys.pollFinalResult)}</p>\n\t\t\t\t{results ? (\n\t\t\t\t\t<FormResults\n\t\t\t\t\t\tresults={results}\n\t\t\t\t\t\twinningValues={winningValues}\n\t\t\t\t\t\tt={formProps.t}\n\t\t\t\t\t\tlocale={formProps.locale}\n\t\t\t\t\t/>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (closed) {\n\t\treturn (\n\t\t\t<div className=\"fb-poll fb-poll--closed\">\n\t\t\t\t<p className=\"fb-poll__closed\">{translate(keys.pollClosed)}</p>\n\t\t\t\t{results ? (\n\t\t\t\t\t<FormResults results={results} t={formProps.t} locale={formProps.locale} />\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t)\n\t}\n\n\tif (voted) {\n\t\tif (resultsAwaitClose) {\n\t\t\treturn <p className=\"fb-poll__await-close\">{translate(keys.pollResultsAfterClose)}</p>\n\t\t}\n\t\treturn results ? (\n\t\t\t<FormResults results={results} t={formProps.t} locale={formProps.locale} />\n\t\t) : null\n\t}\n\n\treturn <Form {...formProps} apiRoute={apiRoute} onSuccess={handleSuccess} />\n}\n"],"mappings":";;;;;;;;;;;AA8BA,MAAM,aAAa,QAAyB;CAC3C,IAAI;EACH,OAAO,OAAO,aAAa,QAAQ,GAAG,KAAK;CAC5C,QAAQ;EACP,OAAO;CACR;AACD;AAEA,MAAM,cAAc,QAAsB;CACzC,IAAI;EACH,OAAO,aAAa,QAAQ,KAAK,GAAG;CACrC,QAAQ,CAER;AACD;;;;;;;;;;;;;AAcA,MAAa,QAAQ,EACpB,cACA,YACA,UACA,mBAAmB,kBACnB,UACA,WACA,GAAG,gBACa;CAChB,MAAM,MAAM,cAAc,WAAW,UAAU,KAAK;CACpD,MAAM,OAAO,UAAU,KAAK;CAC5B,MAAM,SAAS,aAAa,IAAI;CAChC,MAAM,gBAAgB,MAAM,SAAS;CACrC,MAAM,YAAY,MAAM,QAAQ,aAAa,KAAK,cAAc,SAAS;CACzE,MAAM,oBAAoB,CAAC,UAAU,CAAC,aAAa,MAAM,sBAAsB;CAC/E,MAAM,CAAC,OAAO,YAAY,SAAS,KAAK;CACxC,MAAM,CAAC,SAAS,cAAc,SAAoC,IAAI;CACtE,MAAM,YAAY,cAAc,UAAU,KAAK,cAAc,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;CAE/E,MAAM,cAAc,YAAY,YAAY;EAC3C,MAAM,SAA6B,MAAM,iBAAiB;GACzD,QAAQ,UAAU,KAAK;GACvB,OAAO;GACP;EACD,CAAC;EACD,WAAW,OAAO,KAAK,OAAO,UAAU,CAAC,CAAC;CAC3C,GAAG;EAAC;EAAkB,UAAU,KAAK;EAAI;EAAc;CAAQ,CAAC;CAEhE,gBAAgB;EACf,MAAM,UAAU,aAAa,QAAQ,UAAU,GAAG;EAClD,IAAI,SACH,SAAS,IAAI;EAEd,IAAK,WAAW,CAAC,qBAAsB,UAAU,WAChD,YAAiB;CAEnB,GAAG;EAAC;EAAU;EAAK;EAAa;EAAQ;EAAmB;CAAS,CAAC;CAErE,MAAM,gBAAgB,aACpB,iBAA0B;EAC1B,WAAW,GAAG;EACd,SAAS,IAAI;EACb,IAAI,CAAC,mBACJ,YAAiB;EAElB,YAAY,YAAY;CACzB,GACA;EAAC;EAAK;EAAa;EAAW;CAAiB,CAChD;CAEA,IAAI,WACH,OACC,qBAAC,OAAD;EAAK,WAAU;YAAf,CACC,oBAAC,KAAD;GAAG,WAAU;aAAkB,UAAU,KAAK,eAAe;EAAK,CAAA,GACjE,UACA,oBAAC,aAAD;GACU;GACM;GACf,GAAG,UAAU;GACb,QAAQ,UAAU;EAClB,CAAA,IACE,IACA;;CAIP,IAAI,QACH,OACC,qBAAC,OAAD;EAAK,WAAU;YAAf,CACC,oBAAC,KAAD;GAAG,WAAU;aAAmB,UAAU,KAAK,UAAU;EAAK,CAAA,GAC7D,UACA,oBAAC,aAAD;GAAsB;GAAS,GAAG,UAAU;GAAG,QAAQ,UAAU;EAAS,CAAA,IACvE,IACA;;CAIP,IAAI,OAAO;EACV,IAAI,mBACH,OAAO,oBAAC,KAAD;GAAG,WAAU;aAAwB,UAAU,KAAK,qBAAqB;EAAK,CAAA;EAEtF,OAAO,UACN,oBAAC,aAAD;GAAsB;GAAS,GAAG,UAAU;GAAG,QAAQ,UAAU;EAAS,CAAA,IACvE;CACL;CAEA,OAAO,oBAAC,MAAD;EAAM,GAAI;EAAqB;EAAU,WAAW;CAAgB,CAAA;AAC5E"}
@@ -15,9 +15,8 @@ type FieldRendererProps<TValue = unknown> = {
15
15
  name: string;
16
16
  value: TValue | undefined;
17
17
  onChange: (value: TValue) => void;
18
- onBlur: () => void; /** Blocking error messages for this field (already filtered to error severity). */
19
- errors: string[]; /** Advisory messages (warning severity). */
20
- warnings?: string[];
18
+ onBlur: () => void; /** Blocking error messages for this field. */
19
+ errors: string[];
21
20
  required: boolean;
22
21
  disabled?: boolean;
23
22
  locale: string;
@@ -1 +1 @@
1
- {"version":3,"file":"contract.js","names":[],"sources":["../../src/react/contract.ts"],"sourcesContent":["import type { ReactNode } from 'react'\nimport type { FormFieldInstance } from '../submissions/types'\n\n/** A localized translator the renderer uses for built-in copy. `(key) => string`; unknown keys return the key. */\nexport type RendererTranslate = (key: string) => string\n\n/**\n * The props every field renderer receives. The renderer turns one field instance plus its current value\n * into an accessible control. Supplied by `<Form>` or directly in tests. Generic over the\n * value type so custom renderers can narrow it.\n */\nexport type FieldRendererProps<TValue = unknown> = {\n\t/** The stored field instance (name, label, blockType, required, placeholder, description, options, ...). */\n\tfield: FormFieldInstance\n\t/** The control's stable id, for label/aria wiring. */\n\tid: string\n\t/** The field machine name (form value key). */\n\tname: string\n\tvalue: TValue | undefined\n\tonChange: (value: TValue) => void\n\tonBlur: () => void\n\t/** Blocking error messages for this field (already filtered to error severity). */\n\terrors: string[]\n\t/** Advisory messages (warning severity). */\n\twarnings?: string[]\n\trequired: boolean\n\tdisabled?: boolean\n\tlocale: string\n\tt: RendererTranslate\n}\n\n/** A field renderer: maps `FieldRendererProps` to React output. */\nexport type FieldRenderer<TValue = unknown> = (props: FieldRendererProps<TValue>) => ReactNode\n\n/** Identity helper that pins a renderer to the `FieldRenderer` type so authors get prop inference. */\nexport const defineFieldRenderer = <TValue = unknown>(\n\trenderer: FieldRenderer<TValue>\n): FieldRenderer<TValue> => renderer\n"],"mappings":";;AAmCA,MAAa,uBACZ,aAC2B"}
1
+ {"version":3,"file":"contract.js","names":[],"sources":["../../src/react/contract.ts"],"sourcesContent":["import type { ReactNode } from 'react'\nimport type { FormFieldInstance } from '../submissions/types'\n\n/** A localized translator the renderer uses for built-in copy. `(key) => string`; unknown keys return the key. */\nexport type RendererTranslate = (key: string) => string\n\n/**\n * The props every field renderer receives. The renderer turns one field instance plus its current value\n * into an accessible control. Supplied by `<Form>` or directly in tests. Generic over the\n * value type so custom renderers can narrow it.\n */\nexport type FieldRendererProps<TValue = unknown> = {\n\t/** The stored field instance (name, label, blockType, required, placeholder, description, options, ...). */\n\tfield: FormFieldInstance\n\t/** The control's stable id, for label/aria wiring. */\n\tid: string\n\t/** The field machine name (form value key). */\n\tname: string\n\tvalue: TValue | undefined\n\tonChange: (value: TValue) => void\n\tonBlur: () => void\n\t/** Blocking error messages for this field. */\n\terrors: string[]\n\trequired: boolean\n\tdisabled?: boolean\n\tlocale: string\n\tt: RendererTranslate\n}\n\n/** A field renderer: maps `FieldRendererProps` to React output. */\nexport type FieldRenderer<TValue = unknown> = (props: FieldRendererProps<TValue>) => ReactNode\n\n/** Identity helper that pins a renderer to the `FieldRenderer` type so authors get prop inference. */\nexport const defineFieldRenderer = <TValue = unknown>(\n\trenderer: FieldRenderer<TValue>\n): FieldRenderer<TValue> => renderer\n"],"mappings":";;AAiCA,MAAa,uBACZ,aAC2B"}