@verbb/formie-browser 1.0.13 → 1.0.15

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 (389) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/dist/chunks/{address-country-Rx6GQTND.js → address-country-BcEQKw0T.js} +11 -5
  3. package/dist/chunks/{address-finder-k7_BXaRe.js → address-finder-BWGINCSA.js} +3 -3
  4. package/dist/chunks/address-state-DI3ndhMU.js +365 -0
  5. package/dist/chunks/{api-BUb6P-xu.js → api-BK9b340y.js} +93 -63
  6. package/dist/chunks/{api-DqpfkZWL.js → api-Dxl_CKw-.js} +202 -193
  7. package/dist/chunks/{api-sPqGbOww.js → api-ghNr2uxX.js} +15 -9
  8. package/dist/chunks/{async-nPFRNQ06.js → async-2GdXNFCE.js} +18 -1
  9. package/dist/chunks/{bpoint-Dlq34XH1.js → bpoint-BwtuyN_O.js} +3 -3
  10. package/dist/chunks/calculations-YEoVCG2q.js +310 -0
  11. package/dist/chunks/{captcha-eu-CGm8Lbrj.js → captcha-eu-DWLoQFKB.js} +6 -5
  12. package/dist/chunks/{checkbox-radio-BY2aTCOG.js → checkbox-radio-gsm8QPJg.js} +74 -75
  13. package/dist/chunks/{combobox-C0EuExSI.js → combobox-Ba08_cHL.js} +340 -328
  14. package/dist/chunks/conditions-Ceeg6ReZ.js +467 -0
  15. package/dist/chunks/country-from-ip-DDj_mFki.js +29 -0
  16. package/dist/chunks/csrf-DxHg_ZYt.js +61 -0
  17. package/dist/chunks/{custom-google-maps-BsaSirEZ.js → custom-google-maps-C4l2S3GO.js} +9 -3
  18. package/dist/chunks/{custom-link-CJ1-FjEM.js → custom-link-CLazgKYH.js} +8 -2
  19. package/dist/chunks/{custom-maps-DYW_pBid.js → custom-maps-DqcAlD0v.js} +10 -4
  20. package/dist/chunks/{date-picker-F9BUBDSW.js → date-picker-DaPvMrop.js} +467 -443
  21. package/dist/chunks/{dist-D6i-VNgM.js → dist-ZLeW0zZ3.js} +1281 -453
  22. package/dist/chunks/{eway-D9_m8ooj.js → eway-uw0DzU-L.js} +5 -5
  23. package/dist/chunks/field-references.resolver-Ca8MguFv.js +104 -0
  24. package/dist/chunks/{file-upload-rqfZNqlH.js → file-upload-B8_mH012.js} +119 -99
  25. package/dist/chunks/friendly-captcha-v1-CVXaXgDO.js +34 -0
  26. package/dist/chunks/{friendly-captcha-v2-CLSAbkqx.js → friendly-captcha-v2-DkeIVTKI.js} +6 -5
  27. package/dist/chunks/{friendly-challenge-8uPatWqk.js → friendly-challenge-DPG9MJb1.js} +111 -102
  28. package/dist/chunks/{go-cardless-DAT3ZaQf.js → go-cardless-c4wLJdOt.js} +2 -2
  29. package/dist/chunks/google-address-JHYvjwgN.js +168 -0
  30. package/dist/chunks/{hcaptcha-CGLAmgjO.js → hcaptcha-Dt3ELLlP.js} +4 -4
  31. package/dist/chunks/{hidden-D7_Ch-QN.js → hidden-BZhXnNPe.js} +8 -2
  32. package/dist/chunks/http-C2QdHuWO.js +27 -0
  33. package/dist/chunks/{leaflet-src-j-lHNmhR.js → leaflet-src-DE6rv6M-.js} +42 -46
  34. package/dist/chunks/{loqate-CJPzBmSm.js → loqate-m51iPyO1.js} +3 -3
  35. package/dist/chunks/{mollie-Bibhl67p.js → mollie-Bn0PX173.js} +2 -2
  36. package/dist/chunks/{moneris-H2wSqk9h.js → moneris-BRbUvI4t.js} +3 -3
  37. package/dist/chunks/opayo-BwWdInEP.js +188 -0
  38. package/dist/chunks/{paddle-DkvIxQdG.js → paddle-CympHVvd.js} +4 -4
  39. package/dist/chunks/{password-validation-D06uH9va.js → password-validation-C8_uO_c_.js} +18 -12
  40. package/dist/chunks/{paypal-CQpAF7Ua.js → paypal-h75h6wae.js} +8 -6
  41. package/dist/chunks/{payway-CwDkC7YZ.js → payway-C9rHTCC_.js} +4 -4
  42. package/dist/chunks/{phone-country-D3ayr60L.js → phone-country-B8fv-L5_.js} +80 -63
  43. package/dist/chunks/{place-kit-BGHklIZ-.js → place-kit-BnZGQ-L1.js} +11 -11
  44. package/dist/chunks/{placekit-autocomplete.esm-DWVabQlq.js → placekit-autocomplete.esm-CZZERYur.js} +11 -15
  45. package/dist/chunks/{recaptcha-enterprise-XPId5-bS.js → recaptcha-enterprise-CUs8BO6V.js} +4 -4
  46. package/dist/chunks/{recaptcha-shared-CLYJvojk.js → recaptcha-shared-DTbTw-MF.js} +2 -2
  47. package/dist/chunks/{recaptcha-v2-checkbox-Cy-0PEPc.js → recaptcha-v2-checkbox-P1McW4Pv.js} +4 -4
  48. package/dist/chunks/{recaptcha-v2-invisible-BwRH540O.js → recaptcha-v2-invisible-FGDqozSP.js} +4 -4
  49. package/dist/chunks/{recaptcha-v3-No4Ykrhk.js → recaptcha-v3-CflUHeZD.js} +4 -4
  50. package/dist/chunks/{repeater-BVnPUqTj.js → repeater-CyhDbozO.js} +60 -40
  51. package/dist/chunks/request-profile-DhwkeCpS.js +10 -0
  52. package/dist/chunks/rich-text-uV4o_pGk.js +361 -0
  53. package/dist/chunks/{chunk-3b4jIN3o.js → rolldown-runtime-DtPi1Y-2.js} +2 -2
  54. package/dist/chunks/{scripts-CbQ7agX3.js → scripts-DrccjrFv.js} +1 -1
  55. package/dist/chunks/{sdk-BhHAYgga.js → sdk-lkwSvlpM.js} +282 -234
  56. package/dist/chunks/{shared-Bx9s0i0P.js → shared-OKhSay59.js} +27 -21
  57. package/dist/chunks/{signature-IOhuicje.js → signature-7cldFbqj.js} +138 -112
  58. package/dist/chunks/skip-CEW89ExY.js +7 -0
  59. package/dist/chunks/{snaptcha-DKx98oon.js → snaptcha-C3QLYxFT.js} +2 -2
  60. package/dist/chunks/{square-DwdcOI1G.js → square-C1z5DPVw.js} +9 -7
  61. package/dist/chunks/{stripe-DaoG2FT1.js → stripe-Dmkdkq1y.js} +11 -12
  62. package/dist/chunks/{summary-Dks0gXS3.js → summary-Bn-g5SDx.js} +48 -46
  63. package/dist/chunks/{survey-likert-DkE6bR7B.js → survey-likert-fk1hUPff.js} +7 -1
  64. package/dist/chunks/{survey-rank-DN6x4IZM.js → survey-rank-BsLGl2LX.js} +37 -25
  65. package/dist/chunks/{survey-rating-BnsNL9U5.js → survey-rating-DVS6e6fM.js} +17 -5
  66. package/dist/chunks/{table-CAjCJHrL.js → table-j7MlLLCc.js} +22 -16
  67. package/dist/chunks/{text-limit-BGaY0jhI.js → text-limit-LGMPhFuC.js} +40 -34
  68. package/dist/chunks/{theme-classes-Tv7q7ToE.js → theme-classes-DAQuEqdP.js} +1 -1
  69. package/dist/chunks/{turnstile-DJakPzQD.js → turnstile-ptihQfLl.js} +4 -4
  70. package/dist/chunks/{upload-manager-W-QDgxKD.js → upload-manager-BW7l-BhF.js} +459 -387
  71. package/dist/chunks/{utils-Vqp8i_RM.js → utils-DpUBPadZ.js} +20 -14
  72. package/dist/css/formie-base.css +104 -1
  73. package/dist/css/theme/_messages.css +2 -0
  74. package/dist/css/theme/_tokens.css +31 -25
  75. package/dist/css/theme/forms/_form.css +5 -1
  76. package/dist/index.d.ts +5 -4
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +1779 -1415
  79. package/dist/js/contracts/client.d.ts +5 -3
  80. package/dist/js/contracts/client.d.ts.map +1 -1
  81. package/dist/js/contracts/common.d.ts +2 -2
  82. package/dist/js/contracts/common.d.ts.map +1 -1
  83. package/dist/js/contracts/modules.d.ts +37 -12
  84. package/dist/js/contracts/modules.d.ts.map +1 -1
  85. package/dist/js/contracts/schema.d.ts +20 -38
  86. package/dist/js/contracts/schema.d.ts.map +1 -1
  87. package/dist/js/contracts/theme.d.ts +4 -1
  88. package/dist/js/contracts/theme.d.ts.map +1 -1
  89. package/dist/js/core/client-rendered-modules.d.ts +17 -0
  90. package/dist/js/core/client-rendered-modules.d.ts.map +1 -0
  91. package/dist/js/core/create-formie-client.d.ts.map +1 -1
  92. package/dist/js/core/field-error-aria.d.ts +23 -0
  93. package/dist/js/core/field-error-aria.d.ts.map +1 -0
  94. package/dist/js/core/hydrate-modules.d.ts +10 -8
  95. package/dist/js/core/hydrate-modules.d.ts.map +1 -1
  96. package/dist/js/core/submit-result-state.d.ts.map +1 -1
  97. package/dist/js/core/submit-result-ui.d.ts +3 -1
  98. package/dist/js/core/submit-result-ui.d.ts.map +1 -1
  99. package/dist/js/core/validation-focus.d.ts.map +1 -1
  100. package/dist/js/modules/address/address-finder.d.ts +1 -1
  101. package/dist/js/modules/address/address-finder.d.ts.map +1 -1
  102. package/dist/js/modules/address/factories.d.ts +3 -3
  103. package/dist/js/modules/address/factories.d.ts.map +1 -1
  104. package/dist/js/modules/address/google-address.d.ts +12 -1
  105. package/dist/js/modules/address/google-address.d.ts.map +1 -1
  106. package/dist/js/modules/address/index.d.ts +2 -2
  107. package/dist/js/modules/address/index.d.ts.map +1 -1
  108. package/dist/js/modules/address/loqate.d.ts +1 -1
  109. package/dist/js/modules/address/loqate.d.ts.map +1 -1
  110. package/dist/js/modules/address/place-kit.d.ts +1 -1
  111. package/dist/js/modules/address/place-kit.d.ts.map +1 -1
  112. package/dist/js/modules/captchas/captcha-eu.d.ts +1 -1
  113. package/dist/js/modules/captchas/captcha-eu.d.ts.map +1 -1
  114. package/dist/js/modules/captchas/factories.d.ts +11 -11
  115. package/dist/js/modules/captchas/factories.d.ts.map +1 -1
  116. package/dist/js/modules/captchas/friendly-captcha-v1.d.ts +1 -1
  117. package/dist/js/modules/captchas/friendly-captcha-v1.d.ts.map +1 -1
  118. package/dist/js/modules/captchas/friendly-captcha-v2.d.ts +1 -1
  119. package/dist/js/modules/captchas/friendly-captcha-v2.d.ts.map +1 -1
  120. package/dist/js/modules/captchas/hcaptcha.d.ts +1 -1
  121. package/dist/js/modules/captchas/hcaptcha.d.ts.map +1 -1
  122. package/dist/js/modules/captchas/index.d.ts +2 -2
  123. package/dist/js/modules/captchas/index.d.ts.map +1 -1
  124. package/dist/js/modules/captchas/recaptcha-enterprise.d.ts +1 -1
  125. package/dist/js/modules/captchas/recaptcha-enterprise.d.ts.map +1 -1
  126. package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts +1 -1
  127. package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts.map +1 -1
  128. package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts +1 -1
  129. package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts.map +1 -1
  130. package/dist/js/modules/captchas/recaptcha-v3.d.ts +1 -1
  131. package/dist/js/modules/captchas/recaptcha-v3.d.ts.map +1 -1
  132. package/dist/js/modules/captchas/snaptcha.d.ts +1 -1
  133. package/dist/js/modules/captchas/snaptcha.d.ts.map +1 -1
  134. package/dist/js/modules/captchas/turnstile.d.ts +1 -1
  135. package/dist/js/modules/captchas/turnstile.d.ts.map +1 -1
  136. package/dist/js/modules/fields/address-country.d.ts +2 -2
  137. package/dist/js/modules/fields/address-country.d.ts.map +1 -1
  138. package/dist/js/modules/fields/address-state.d.ts +2 -2
  139. package/dist/js/modules/fields/address-state.d.ts.map +1 -1
  140. package/dist/js/modules/fields/calculations.d.ts +2 -2
  141. package/dist/js/modules/fields/calculations.d.ts.map +1 -1
  142. package/dist/js/modules/fields/checkbox-radio.d.ts +2 -2
  143. package/dist/js/modules/fields/checkbox-radio.d.ts.map +1 -1
  144. package/dist/js/modules/fields/combobox.d.ts +2 -2
  145. package/dist/js/modules/fields/combobox.d.ts.map +1 -1
  146. package/dist/js/modules/fields/conditions/config.d.ts.map +1 -1
  147. package/dist/js/modules/fields/conditions/effects.d.ts +1 -0
  148. package/dist/js/modules/fields/conditions/effects.d.ts.map +1 -1
  149. package/dist/js/modules/fields/conditions/evaluator.d.ts +4 -1
  150. package/dist/js/modules/fields/conditions/evaluator.d.ts.map +1 -1
  151. package/dist/js/modules/fields/conditions/references.d.ts.map +1 -1
  152. package/dist/js/modules/fields/conditions/submission-context.d.ts +15 -0
  153. package/dist/js/modules/fields/conditions/submission-context.d.ts.map +1 -0
  154. package/dist/js/modules/fields/conditions/types.d.ts +3 -1
  155. package/dist/js/modules/fields/conditions/types.d.ts.map +1 -1
  156. package/dist/js/modules/fields/conditions/values.d.ts +1 -1
  157. package/dist/js/modules/fields/conditions/values.d.ts.map +1 -1
  158. package/dist/js/modules/fields/conditions.d.ts +2 -2
  159. package/dist/js/modules/fields/conditions.d.ts.map +1 -1
  160. package/dist/js/modules/fields/custom-google-maps.d.ts +2 -2
  161. package/dist/js/modules/fields/custom-google-maps.d.ts.map +1 -1
  162. package/dist/js/modules/fields/custom-link.d.ts +2 -2
  163. package/dist/js/modules/fields/custom-link.d.ts.map +1 -1
  164. package/dist/js/modules/fields/custom-maps.d.ts +2 -2
  165. package/dist/js/modules/fields/custom-maps.d.ts.map +1 -1
  166. package/dist/js/modules/fields/date-picker.d.ts +2 -2
  167. package/dist/js/modules/fields/date-picker.d.ts.map +1 -1
  168. package/dist/js/modules/fields/file-upload.d.ts +2 -2
  169. package/dist/js/modules/fields/file-upload.d.ts.map +1 -1
  170. package/dist/js/modules/fields/hidden.d.ts +2 -2
  171. package/dist/js/modules/fields/hidden.d.ts.map +1 -1
  172. package/dist/js/modules/fields/index.d.ts +2 -2
  173. package/dist/js/modules/fields/index.d.ts.map +1 -1
  174. package/dist/js/modules/fields/password-validation.d.ts +2 -2
  175. package/dist/js/modules/fields/password-validation.d.ts.map +1 -1
  176. package/dist/js/modules/fields/phone-country.d.ts +2 -2
  177. package/dist/js/modules/fields/phone-country.d.ts.map +1 -1
  178. package/dist/js/modules/fields/repeater.d.ts +2 -2
  179. package/dist/js/modules/fields/repeater.d.ts.map +1 -1
  180. package/dist/js/modules/fields/rich-text.d.ts +2 -2
  181. package/dist/js/modules/fields/rich-text.d.ts.map +1 -1
  182. package/dist/js/modules/fields/shared.d.ts +1 -0
  183. package/dist/js/modules/fields/shared.d.ts.map +1 -1
  184. package/dist/js/modules/fields/signature.d.ts +2 -2
  185. package/dist/js/modules/fields/signature.d.ts.map +1 -1
  186. package/dist/js/modules/fields/summary.d.ts +2 -2
  187. package/dist/js/modules/fields/summary.d.ts.map +1 -1
  188. package/dist/js/modules/fields/survey-likert.d.ts +2 -2
  189. package/dist/js/modules/fields/survey-likert.d.ts.map +1 -1
  190. package/dist/js/modules/fields/survey-rank.d.ts +2 -2
  191. package/dist/js/modules/fields/survey-rank.d.ts.map +1 -1
  192. package/dist/js/modules/fields/survey-rating.d.ts +2 -2
  193. package/dist/js/modules/fields/survey-rating.d.ts.map +1 -1
  194. package/dist/js/modules/fields/table.d.ts +2 -2
  195. package/dist/js/modules/fields/table.d.ts.map +1 -1
  196. package/dist/js/modules/fields/text-limit.d.ts +2 -2
  197. package/dist/js/modules/fields/text-limit.d.ts.map +1 -1
  198. package/dist/js/modules/fields/upload-manager.d.ts +2 -2
  199. package/dist/js/modules/fields/upload-manager.d.ts.map +1 -1
  200. package/dist/js/modules/loader.d.ts +10 -4
  201. package/dist/js/modules/loader.d.ts.map +1 -1
  202. package/dist/js/modules/payments/bpoint.d.ts +1 -1
  203. package/dist/js/modules/payments/bpoint.d.ts.map +1 -1
  204. package/dist/js/modules/payments/eway.d.ts +1 -1
  205. package/dist/js/modules/payments/eway.d.ts.map +1 -1
  206. package/dist/js/modules/payments/factories.d.ts +7 -7
  207. package/dist/js/modules/payments/factories.d.ts.map +1 -1
  208. package/dist/js/modules/payments/go-cardless.d.ts +1 -1
  209. package/dist/js/modules/payments/go-cardless.d.ts.map +1 -1
  210. package/dist/js/modules/payments/index.d.ts +2 -2
  211. package/dist/js/modules/payments/index.d.ts.map +1 -1
  212. package/dist/js/modules/payments/mollie.d.ts +1 -1
  213. package/dist/js/modules/payments/mollie.d.ts.map +1 -1
  214. package/dist/js/modules/payments/moneris.d.ts +1 -1
  215. package/dist/js/modules/payments/moneris.d.ts.map +1 -1
  216. package/dist/js/modules/payments/opayo.d.ts +1 -1
  217. package/dist/js/modules/payments/opayo.d.ts.map +1 -1
  218. package/dist/js/modules/payments/paddle.d.ts +1 -1
  219. package/dist/js/modules/payments/paddle.d.ts.map +1 -1
  220. package/dist/js/modules/payments/paypal.d.ts +1 -1
  221. package/dist/js/modules/payments/paypal.d.ts.map +1 -1
  222. package/dist/js/modules/payments/payway.d.ts +1 -1
  223. package/dist/js/modules/payments/payway.d.ts.map +1 -1
  224. package/dist/js/modules/payments/square.d.ts +1 -1
  225. package/dist/js/modules/payments/square.d.ts.map +1 -1
  226. package/dist/js/modules/payments/stripe.d.ts +1 -1
  227. package/dist/js/modules/payments/stripe.d.ts.map +1 -1
  228. package/dist/js/modules/payments/utils.d.ts +6 -0
  229. package/dist/js/modules/payments/utils.d.ts.map +1 -1
  230. package/dist/js/modules/registry.d.ts +6 -4
  231. package/dist/js/modules/registry.d.ts.map +1 -1
  232. package/dist/js/submit/pipeline.d.ts.map +1 -1
  233. package/dist/js/theme/browser-theme-state.generated.d.ts +24 -0
  234. package/dist/js/theme/browser-theme-state.generated.d.ts.map +1 -0
  235. package/dist/js/theme/theme-classes.d.ts +3 -3
  236. package/dist/js/theme/theme-classes.d.ts.map +1 -1
  237. package/dist/js/transport/forms-api.d.ts +5 -3
  238. package/dist/js/transport/forms-api.d.ts.map +1 -1
  239. package/dist/js/utils/async.d.ts +1 -0
  240. package/dist/js/utils/async.d.ts.map +1 -1
  241. package/dist/js/utils/country-from-ip.d.ts +2 -2
  242. package/dist/js/utils/country-from-ip.d.ts.map +1 -1
  243. package/dist/js/utils/csrf.d.ts +17 -0
  244. package/dist/js/utils/csrf.d.ts.map +1 -0
  245. package/dist/js/utils/field-references.parser.d.ts +1 -0
  246. package/dist/js/utils/field-references.parser.d.ts.map +1 -1
  247. package/dist/js/utils/field-references.resolver.d.ts.map +1 -1
  248. package/dist/js/utils/field-references.types.d.ts +1 -0
  249. package/dist/js/utils/field-references.types.d.ts.map +1 -1
  250. package/dist/js/utils/http.d.ts +3 -1
  251. package/dist/js/utils/http.d.ts.map +1 -1
  252. package/dist/js/utils/request-profile.d.ts +4 -0
  253. package/dist/js/utils/request-profile.d.ts.map +1 -0
  254. package/dist/js/utils/unload-warning.d.ts.map +1 -1
  255. package/dist/js/validation/rules/email.d.ts.map +1 -1
  256. package/dist/js/validation/rules/match.d.ts.map +1 -1
  257. package/dist/js/validation/rules/number.d.ts.map +1 -1
  258. package/dist/js/validation/rules/required.d.ts.map +1 -1
  259. package/dist/js/validation/rules/shared.d.ts.map +1 -1
  260. package/dist/js/validation/rules/url.d.ts.map +1 -1
  261. package/dist/js/validation/skip.d.ts +7 -0
  262. package/dist/js/validation/skip.d.ts.map +1 -0
  263. package/dist/js/validation/submit-readiness.d.ts.map +1 -1
  264. package/dist/js/validation/validator.d.ts.map +1 -1
  265. package/package.json +43 -12
  266. package/src/css/formie-base.css +104 -1
  267. package/src/css/theme/_messages.css +2 -0
  268. package/src/css/theme/_tokens.css +31 -25
  269. package/src/css/theme/forms/_form.css +5 -1
  270. package/src/index.ts +13 -8
  271. package/src/js/contracts/client.ts +5 -3
  272. package/src/js/contracts/common.ts +5 -6
  273. package/src/js/contracts/modules.ts +41 -12
  274. package/src/js/contracts/schema.ts +21 -43
  275. package/src/js/contracts/theme.ts +6 -1
  276. package/src/js/core/client-rendered-modules.ts +69 -0
  277. package/src/js/core/create-formie-client.ts +132 -86
  278. package/src/js/core/field-error-aria.ts +93 -0
  279. package/src/js/core/hydrate-modules.ts +54 -29
  280. package/src/js/core/submit-result-state.ts +51 -45
  281. package/src/js/core/submit-result-ui.ts +25 -68
  282. package/src/js/core/validation-focus.ts +20 -20
  283. package/src/js/modules/address/address-finder.ts +1 -1
  284. package/src/js/modules/address/factories.ts +15 -13
  285. package/src/js/modules/address/google-address.ts +102 -40
  286. package/src/js/modules/address/index.ts +2 -2
  287. package/src/js/modules/address/loqate.ts +1 -1
  288. package/src/js/modules/address/place-kit.ts +14 -8
  289. package/src/js/modules/captchas/captcha-eu.ts +2 -2
  290. package/src/js/modules/captchas/factories.ts +66 -42
  291. package/src/js/modules/captchas/friendly-captcha-v1.ts +12 -2
  292. package/src/js/modules/captchas/friendly-captcha-v2.ts +4 -2
  293. package/src/js/modules/captchas/hcaptcha.ts +5 -5
  294. package/src/js/modules/captchas/index.ts +2 -2
  295. package/src/js/modules/captchas/recaptcha-enterprise.ts +2 -2
  296. package/src/js/modules/captchas/recaptcha-v2-checkbox.ts +3 -3
  297. package/src/js/modules/captchas/recaptcha-v2-invisible.ts +2 -2
  298. package/src/js/modules/captchas/recaptcha-v3.ts +2 -2
  299. package/src/js/modules/captchas/snaptcha.ts +1 -1
  300. package/src/js/modules/captchas/turnstile.ts +3 -3
  301. package/src/js/modules/fields/address-country.ts +11 -9
  302. package/src/js/modules/fields/address-state.ts +94 -35
  303. package/src/js/modules/fields/calculations.ts +5 -3
  304. package/src/js/modules/fields/checkbox-radio.ts +24 -42
  305. package/src/js/modules/fields/combobox.ts +28 -9
  306. package/src/js/modules/fields/conditions/config.ts +17 -20
  307. package/src/js/modules/fields/conditions/effects.ts +25 -29
  308. package/src/js/modules/fields/conditions/evaluator.ts +21 -29
  309. package/src/js/modules/fields/conditions/references.ts +16 -5
  310. package/src/js/modules/fields/conditions/submission-context.ts +80 -0
  311. package/src/js/modules/fields/conditions/types.ts +3 -1
  312. package/src/js/modules/fields/conditions/values.ts +31 -6
  313. package/src/js/modules/fields/conditions.ts +65 -18
  314. package/src/js/modules/fields/custom-google-maps.ts +5 -3
  315. package/src/js/modules/fields/custom-link.ts +5 -3
  316. package/src/js/modules/fields/custom-maps.ts +5 -3
  317. package/src/js/modules/fields/date-picker.ts +40 -7
  318. package/src/js/modules/fields/file-upload.ts +62 -23
  319. package/src/js/modules/fields/hidden.ts +5 -3
  320. package/src/js/modules/fields/index.ts +2 -2
  321. package/src/js/modules/fields/password-validation.ts +5 -3
  322. package/src/js/modules/fields/phone-country.ts +35 -8
  323. package/src/js/modules/fields/repeater.ts +60 -7
  324. package/src/js/modules/fields/rich-text.ts +20 -4
  325. package/src/js/modules/fields/shared.ts +13 -0
  326. package/src/js/modules/fields/signature.ts +24 -4
  327. package/src/js/modules/fields/summary.ts +8 -20
  328. package/src/js/modules/fields/survey-likert.ts +5 -3
  329. package/src/js/modules/fields/survey-rank.ts +21 -3
  330. package/src/js/modules/fields/survey-rating.ts +17 -3
  331. package/src/js/modules/fields/table.ts +5 -3
  332. package/src/js/modules/fields/text-limit.ts +5 -3
  333. package/src/js/modules/fields/upload-manager.ts +244 -67
  334. package/src/js/modules/loader.ts +345 -281
  335. package/src/js/modules/payments/bpoint.ts +2 -2
  336. package/src/js/modules/payments/eway.ts +2 -2
  337. package/src/js/modules/payments/factories.ts +53 -52
  338. package/src/js/modules/payments/go-cardless.ts +1 -1
  339. package/src/js/modules/payments/index.ts +2 -2
  340. package/src/js/modules/payments/mollie.ts +1 -1
  341. package/src/js/modules/payments/moneris.ts +2 -2
  342. package/src/js/modules/payments/opayo.ts +21 -8
  343. package/src/js/modules/payments/paddle.ts +10 -6
  344. package/src/js/modules/payments/payment.ts +22 -18
  345. package/src/js/modules/payments/paypal.ts +1 -1
  346. package/src/js/modules/payments/payway.ts +2 -2
  347. package/src/js/modules/payments/square.ts +2 -2
  348. package/src/js/modules/payments/stripe.ts +5 -5
  349. package/src/js/modules/payments/utils.ts +14 -0
  350. package/src/js/modules/registry.ts +19 -11
  351. package/src/js/submit/pipeline.ts +10 -11
  352. package/src/js/theme/browser-theme-state.generated.ts +25 -0
  353. package/src/js/theme/theme-classes.ts +3 -3
  354. package/src/js/transport/forms-api.ts +66 -67
  355. package/src/js/utils/async.ts +34 -0
  356. package/src/js/utils/country-from-ip.ts +14 -9
  357. package/src/js/utils/csrf.ts +136 -0
  358. package/src/js/utils/field-references.parser.ts +13 -113
  359. package/src/js/utils/field-references.resolver.ts +27 -64
  360. package/src/js/utils/field-references.row-scope.ts +1 -1
  361. package/src/js/utils/field-references.types.ts +1 -0
  362. package/src/js/utils/http.ts +5 -5
  363. package/src/js/utils/request-profile.ts +10 -0
  364. package/src/js/utils/unload-warning.ts +12 -4
  365. package/src/js/validation/rules/email.ts +3 -5
  366. package/src/js/validation/rules/match.ts +2 -1
  367. package/src/js/validation/rules/minmax.ts +4 -43
  368. package/src/js/validation/rules/number.ts +3 -47
  369. package/src/js/validation/rules/pattern.ts +3 -8
  370. package/src/js/validation/rules/required.ts +14 -1
  371. package/src/js/validation/rules/shared.ts +5 -1
  372. package/src/js/validation/rules/url.ts +3 -10
  373. package/src/js/validation/skip.ts +9 -0
  374. package/src/js/validation/submit-readiness.ts +9 -2
  375. package/src/js/validation/validator.ts +49 -61
  376. package/dist/chunks/address-state-BwE01m2_.js +0 -344
  377. package/dist/chunks/calculations-Dv3hYb8_.js +0 -299
  378. package/dist/chunks/conditions-CPH1snCx.js +0 -424
  379. package/dist/chunks/country-from-ip-BwxKaBuJ.js +0 -26
  380. package/dist/chunks/field-references.resolver-CHwn0G0L.js +0 -145
  381. package/dist/chunks/friendly-captcha-v1-C_2reOXr.js +0 -31
  382. package/dist/chunks/google-address-BH3Zhi06.js +0 -161
  383. package/dist/chunks/http-D-JExro7.js +0 -27
  384. package/dist/chunks/opayo-CR0DtsLa.js +0 -179
  385. package/dist/chunks/rich-text-Bg_--4TG.js +0 -349
  386. package/dist/js/modules/fields/conditions/transforms.d.ts +0 -3
  387. package/dist/js/modules/fields/conditions/transforms.d.ts.map +0 -1
  388. package/src/js/modules/fields/conditions/transforms.ts +0 -206
  389. package/src/js/modules/payments/stub-payment-module.ts +0 -87
@@ -1,7 +1,10 @@
1
- import type { FormieModuleDefinition } from '#contracts/modules';
1
+ import { browserRequest, type BrowserRequestOptions } from '@verbb/formie-core';
2
+ import { getFormBrowserRequestOptions } from '#utils/request-profile';
3
+ import type { BrowserModuleDefinition } from '#contracts/modules';
2
4
  import { ADDRESS_SELECTORS } from '#modules/address/constants';
3
5
  import { initFormieCombobox } from '#modules/fields/combobox';
4
6
  import { dispatchFieldEvent, getModuleFieldContainers } from '#modules/fields/shared';
7
+ import { getThemeClasses, toggleThemeClasses } from '#theme/theme-classes';
5
8
  import { createDebug } from '#utils/debug';
6
9
 
7
10
  const STATE_DYNAMIC_SELECTOR = '[data-formie-address-state-dynamic]';
@@ -55,6 +58,9 @@ type ComboboxSelect = HTMLSelectElement & {
55
58
 
56
59
  type FieldState = {
57
60
  addressRoot: HTMLElement;
61
+ isCpEdit: boolean;
62
+ initialCountry: string;
63
+ initialValue: string;
58
64
  stateField: HTMLElement;
59
65
  stateControl: AddressStateControl;
60
66
  countryControl: HTMLInputElement | HTMLSelectElement | null;
@@ -102,7 +108,8 @@ function getStateControl(field: HTMLElement): AddressStateControl | null {
102
108
  }
103
109
 
104
110
  function getFieldLabel(field: HTMLElement): HTMLElement | null {
105
- return field.querySelector('[data-formie-field-label]');
111
+ return field.querySelector('[data-formie-field-label]')
112
+ || (field.querySelector('[data-formie-address-state-cp]') ? field.querySelector(':scope > .heading > label') : null);
106
113
  }
107
114
 
108
115
  function dispatchControlEvents(control: AddressStateControl): void {
@@ -127,13 +134,15 @@ function syncComboboxValue(control: AddressStateControl, value: string): void {
127
134
  }
128
135
 
129
136
  function setControlValue(control: AddressStateControl, value: string): void {
130
- if (control.value !== value) {
131
- control.value = value;
137
+ const previousValue = control.value;
138
+ control.value = value;
139
+ syncComboboxValue(control, control.value);
140
+
141
+ // A select rejects values outside its options. Dispatch only actual changes
142
+ // so an unmatched autofill value cannot recursively trigger reconciliation.
143
+ if (control.value !== previousValue) {
132
144
  dispatchControlEvents(control);
133
- return;
134
145
  }
135
-
136
- syncComboboxValue(control, value);
137
146
  }
138
147
 
139
148
  function syncAutofillAnchor(fieldState: FieldState, value: string): void {
@@ -178,6 +187,7 @@ function ensureAutofillAnchor(fieldState: FieldState): HTMLInputElement {
178
187
  anchor.setAttribute('tabindex', '-1');
179
188
  anchor.setAttribute('aria-hidden', 'true');
180
189
  anchor.className = 'formie-sr-only';
190
+ anchor.hidden = fieldState.isCpEdit;
181
191
  fieldState.addressRoot.appendChild(anchor);
182
192
  fieldState.autofillAnchor = anchor;
183
193
  }
@@ -201,18 +211,17 @@ function reconcileAutofillValue(fieldState: FieldState): void {
201
211
  }
202
212
 
203
213
  const subdivisions = fieldState.lastSubdivisions;
204
- const resolved = subdivisions.length > 0
205
- ? (resolveOptionMatch(pending, subdivisions) || pending)
214
+ const resolved = fieldState.stateControl instanceof HTMLSelectElement
215
+ ? (resolveOptionMatch(pending, subdivisions) || '')
206
216
  : pending;
207
217
 
208
- setControlValue(fieldState.stateControl, resolved);
209
- syncComboboxValue(fieldState.stateControl, resolved);
210
218
  syncAutofillAnchor(fieldState, resolved);
211
219
  fieldState.pendingStateValue = resolved;
220
+ setControlValue(fieldState.stateControl, resolved);
212
221
  }
213
222
 
214
223
  function buildSubdivisionsUrl(action: string, country: string, optionLabel: string, optionValue: string): string {
215
- const url = new URL(action.startsWith('/') ? action : `/actions/${action}`, window.location.origin);
224
+ const url = new URL(/^https?:\/\//.test(action) || action.startsWith('/') ? action : `/actions/${action}`, window.location.origin);
216
225
  url.searchParams.set('country', country);
217
226
  url.searchParams.set('optionLabel', optionLabel);
218
227
  url.searchParams.set('optionValue', optionValue);
@@ -238,6 +247,7 @@ async function fetchSubdivisions(
238
247
  optionLabel: string,
239
248
  optionValue: string,
240
249
  action: string,
250
+ requestOptions: BrowserRequestOptions,
241
251
  ): Promise<SubdivisionsResponse | null> {
242
252
  const cacheKey = getSubdivisionsCacheKey(country, optionLabel, optionValue, action);
243
253
 
@@ -248,11 +258,11 @@ async function fetchSubdivisions(
248
258
  if (!subdivisionInflight.has(cacheKey)) {
249
259
  subdivisionInflight.set(cacheKey, (async() => {
250
260
  try {
251
- const response = await fetch(buildSubdivisionsUrl(action, country, optionLabel, optionValue), {
261
+ const response = await browserRequest(buildSubdivisionsUrl(action, country, optionLabel, optionValue), {
252
262
  headers: {
253
263
  Accept: 'application/json',
254
264
  },
255
- });
265
+ }, requestOptions);
256
266
 
257
267
  if (!response.ok) {
258
268
  subdivisionDataCache.set(cacheKey, null);
@@ -316,6 +326,7 @@ function copyControlAttributes(source: AddressStateControl, target: AddressState
316
326
  'data-formie-input-type',
317
327
  'data-formie-input-error-state',
318
328
  'data-formie-address-state-dynamic',
329
+ 'data-formie-address-state-cp',
319
330
  'data-formie-address-state-hide-when-unused',
320
331
  'data-formie-address-state-use-searchable',
321
332
  'data-formie-address-state-use-datalist',
@@ -336,15 +347,17 @@ function copyControlAttributes(source: AddressStateControl, target: AddressState
336
347
  }
337
348
 
338
349
  function applyTextThemeClasses(template: AddressStateControl, input: HTMLInputElement): void {
339
- input.className = template.className;
350
+ input.className = template.hasAttribute('data-formie-address-state-cp') ? 'text fullwidth' : template.className;
340
351
  }
341
352
 
342
353
  function applySelectThemeClasses(template: AddressStateControl, select: HTMLSelectElement): void {
343
354
  const classes: string[] = [...SELECT_THEME_CLASSES];
344
355
 
345
- if (template.classList.contains('formie-input-error')) {
346
- classes.push('formie-input-error');
347
- }
356
+ getThemeClasses(template, 'fieldControlError').forEach((className) => {
357
+ if (template.classList.contains(className)) {
358
+ classes.push(className);
359
+ }
360
+ });
348
361
 
349
362
  select.className = classes.join(' ');
350
363
  }
@@ -454,12 +467,17 @@ function syncDatalist(
454
467
  function setFieldVisibility(fieldState: FieldState, visible: boolean): void {
455
468
  const { stateField, stateControl, required } = fieldState;
456
469
 
457
- stateField.classList.toggle('formie-conditionally-hidden', !visible);
458
- stateField.toggleAttribute('data-formie-conditionally-hidden', !visible);
470
+ toggleThemeClasses(stateField, stateField, 'conditionalHidden', !visible);
471
+ // Use module-owned visibility: the generic hidden attribute unmounts modules,
472
+ // which would remove the country listener needed to show this field again.
473
+ stateField.toggleAttribute('data-formie-address-state-hidden', !visible);
474
+
459
475
 
460
476
  if (!visible) {
461
477
  stateControl.required = false;
462
- stateControl.disabled = true;
478
+ // Keep the empty value in Craft's serialized form data so hiding the
479
+ // field during initialization does not count as an unsaved edit.
480
+ stateControl.disabled = !fieldState.isCpEdit;
463
481
  return;
464
482
  }
465
483
 
@@ -473,7 +491,7 @@ function ensureFetchingAnnouncement(fieldState: FieldState): HTMLElement {
473
491
  }
474
492
 
475
493
  const announcement = document.createElement('div');
476
- announcement.className = 'formie-sr-only';
494
+ announcement.className = fieldState.isCpEdit ? 'visually-hidden' : 'formie-sr-only';
477
495
  announcement.setAttribute('data-formie-address-state-fetching-announce', 'true');
478
496
  announcement.setAttribute('aria-live', 'polite');
479
497
  announcement.setAttribute('aria-atomic', 'true');
@@ -502,8 +520,8 @@ function showFetchingUI(fieldState: FieldState): void {
502
520
  fieldState.stateControl.setAttribute('aria-hidden', 'true');
503
521
  fieldState.stateControl.setAttribute('tabindex', '-1');
504
522
 
505
- fieldState.stateField.classList.remove('formie-conditionally-hidden');
506
- fieldState.stateField.removeAttribute('data-formie-conditionally-hidden');
523
+ toggleThemeClasses(fieldState.stateField, fieldState.stateField, 'conditionalHidden', false);
524
+ fieldState.stateField.removeAttribute('data-formie-address-state-hidden');
507
525
  fieldState.stateField.setAttribute('data-formie-address-state-skeleton-active', 'true');
508
526
 
509
527
  const controlWrapper = fieldState.stateField.querySelector('[data-formie-field-control]');
@@ -540,7 +558,7 @@ function updateFieldLabel(stateField: HTMLElement, label: string): void {
540
558
  return;
541
559
  }
542
560
 
543
- const required = labelEl.querySelector('[data-formie-field-required]');
561
+ const required = labelEl.querySelector('[data-formie-field-required], .required');
544
562
  labelEl.textContent = label;
545
563
 
546
564
  if (required) {
@@ -589,6 +607,10 @@ async function applyCountryState(
589
607
  const country = fieldState.countryControl?.value?.trim() || '';
590
608
  stashPendingStateValue(fieldState);
591
609
  const currentValue = getEffectiveStateValue(fieldState);
610
+ // Opening an existing submission must not discard a historical state value
611
+ // merely because current country metadata no longer includes it.
612
+ const preserveStoredValue = fieldState.isCpEdit && !!currentValue
613
+ && country === fieldState.initialCountry && currentValue === fieldState.initialValue;
592
614
  const fetchGeneration = ++fieldState.fetchGeneration;
593
615
 
594
616
  fieldState.comboboxCleanup?.();
@@ -602,7 +624,7 @@ async function applyCountryState(
602
624
 
603
625
  hideFetchingUI(fieldState);
604
626
 
605
- if (hideWhenUnused) {
627
+ if (hideWhenUnused && !preserveStoredValue) {
606
628
  setFieldVisibility(fieldState, false);
607
629
  setControlValue(fieldState.stateControl, '');
608
630
  syncAutofillAnchor(fieldState, '');
@@ -617,7 +639,7 @@ async function applyCountryState(
617
639
  const textControl = createTextControl(fieldState.stateControl, currentValue);
618
640
  fieldState.stateControl = replaceControl(fieldState.stateControl, textControl);
619
641
  } else {
620
- fieldState.stateControl.disabled = true;
642
+ fieldState.stateControl.disabled = !fieldState.isCpEdit && !preserveStoredValue;
621
643
  fieldState.stateControl.placeholder = placeholder || fieldState.stateControl.placeholder;
622
644
  }
623
645
 
@@ -632,20 +654,23 @@ async function applyCountryState(
632
654
  showFetchingUI(fieldState);
633
655
  }
634
656
 
635
- const response = await fetchSubdivisions(country, optionLabel, optionValue, subdivisionsAction);
657
+ const response = await fetchSubdivisions(country, optionLabel, optionValue, subdivisionsAction, getFormBrowserRequestOptions(fieldState.addressRoot.closest('form')));
636
658
 
637
659
  if (fetchGeneration !== fieldState.fetchGeneration) {
638
660
  return;
639
661
  }
640
662
 
641
- const subdivisions = response?.subdivisions || [];
663
+ const subdivisions = [...(response?.subdivisions || [])];
664
+ if (preserveStoredValue && subdivisions.length && !resolveOptionMatch(currentValue, subdivisions)) {
665
+ subdivisions.push({ label: currentValue, value: currentValue });
666
+ }
642
667
  const administrativeAreaUsed = response?.administrativeAreaUsed ?? true;
643
668
  const administrativeAreaLabel = response?.administrativeAreaLabel || 'State / Province';
644
669
  fieldState.lastSubdivisions = subdivisions;
645
670
 
646
671
  hideFetchingUI(fieldState);
647
672
 
648
- if (hideWhenUnused && !administrativeAreaUsed) {
673
+ if (hideWhenUnused && !administrativeAreaUsed && !preserveStoredValue) {
649
674
  setFieldVisibility(fieldState, false);
650
675
  setControlValue(fieldState.stateControl, '');
651
676
  syncAutofillAnchor(fieldState, '');
@@ -749,6 +774,9 @@ function initAddressStateField(field: HTMLElement, options: AddressStateOptions)
749
774
 
750
775
  const fieldState: FieldState = {
751
776
  addressRoot,
777
+ isCpEdit: stateControl.hasAttribute('data-formie-address-state-cp'),
778
+ initialCountry: getCountryControl(addressRoot)?.value?.trim() || '',
779
+ initialValue: stateControl.value?.trim() || '',
752
780
  stateField: field,
753
781
  stateControl,
754
782
  countryControl: getCountryControl(addressRoot),
@@ -766,6 +794,10 @@ function initAddressStateField(field: HTMLElement, options: AddressStateOptions)
766
794
  lastSubdivisions: [],
767
795
  };
768
796
 
797
+ const initialStateValue = stateControl.value;
798
+ const form = field.closest('form');
799
+ let disposed = false;
800
+ let resetTimer: number | null = null;
769
801
  ensureAutofillAnchor(fieldState);
770
802
 
771
803
  const refresh = (force = false) => {
@@ -824,6 +856,7 @@ function initAddressStateField(field: HTMLElement, options: AddressStateOptions)
824
856
 
825
857
  if (isStateControl && !isAnchor) {
826
858
  syncAutofillAnchor(fieldState, target.value);
859
+ fieldState.pendingStateValue = target.value;
827
860
  }
828
861
 
829
862
  stashPendingStateValue(fieldState);
@@ -885,12 +918,31 @@ function initAddressStateField(field: HTMLElement, options: AddressStateOptions)
885
918
  syncAutofillAnchor(fieldState, stateValue);
886
919
 
887
920
  void applyCountryState(fieldState, options).then(() => {
888
- setControlValue(fieldState.stateControl, stateValue);
889
- syncComboboxValue(fieldState.stateControl, stateValue);
921
+ // Keep the normalized option value, which may differ from a provider's
922
+ // abbreviation (for example "CA" when options store "California").
923
+ reconcileAutofillValue(fieldState);
890
924
  fieldState.lastCountry = fieldState.countryControl?.value?.trim() || '';
891
925
  });
892
926
  };
893
927
 
928
+ const onReset = (event: Event) => {
929
+ if (resetTimer !== null) window.clearTimeout(resetTimer);
930
+ // Enhanced selects finish their own reset in microtasks. Rebuild the
931
+ // dependent options after those controls have restored the country.
932
+ resetTimer = window.setTimeout(() => {
933
+ resetTimer = null;
934
+ if (event.defaultPrevented || disposed) return;
935
+ clearScheduledAutofillTimers(fieldState);
936
+ // The current select may contain another country's options. Keep the
937
+ // original value pending until the reset country's options are ready.
938
+ fieldState.stateControl.value = '';
939
+ fieldState.pendingStateValue = initialStateValue;
940
+ syncAutofillAnchor(fieldState, initialStateValue);
941
+ refresh(true);
942
+ }, 0);
943
+ };
944
+ form?.addEventListener('reset', onReset);
945
+
894
946
  fieldState.countryControl?.addEventListener('change', onCountryChange);
895
947
  fieldState.countryControl?.addEventListener('input', onCountryChange);
896
948
  addressRoot.addEventListener('input', onAddressInput);
@@ -901,10 +953,15 @@ function initAddressStateField(field: HTMLElement, options: AddressStateOptions)
901
953
  addressRoot.addEventListener('formie:address:loqate:populate', onProviderPopulate);
902
954
  addressRoot.addEventListener('formie:address:place-kit:populate', onProviderPopulate);
903
955
 
904
- refresh();
956
+ // Apply the empty-country visibility state on first mount as well.
957
+ refresh(true);
905
958
  scheduleAutofillSweep(fieldState, options);
906
959
 
907
960
  return () => {
961
+ disposed = true;
962
+ if (resetTimer !== null) window.clearTimeout(resetTimer);
963
+ fieldState.fetchGeneration++;
964
+ form?.removeEventListener('reset', onReset);
908
965
  clearScheduledAutofillTimers(fieldState);
909
966
  hideFetchingUI(fieldState);
910
967
  fieldState.skeletonEl?.remove();
@@ -923,8 +980,10 @@ function initAddressStateField(field: HTMLElement, options: AddressStateOptions)
923
980
  };
924
981
  }
925
982
 
926
- export const addressStateModule: FormieModuleDefinition = {
927
- id: MODULE_ID,
983
+ export const addressStateModule: BrowserModuleDefinition = {
984
+ moduleId: `formie:${MODULE_ID}`,
985
+ version: 2,
986
+ surfaces: ['server-rendered', 'client-rendered', 'cp-edit'],
928
987
  kind: 'field',
929
988
  match: (ctx) => {
930
989
  return !!ctx.target.querySelector(STATE_DYNAMIC_SELECTOR);
@@ -9,7 +9,7 @@ import {
9
9
  type CalculationVariableEntry,
10
10
  } from '@verbb/formie-core';
11
11
 
12
- import type { FormieModuleDefinition } from '#contracts/modules';
12
+ import type { BrowserModuleDefinition } from '#contracts/modules';
13
13
  import { dispatchFieldEvent, getModuleFieldContainers } from '#modules/fields/shared';
14
14
  import { buildFieldValueRegistry, fieldKeyToInputName, normalizeFieldKey, resolveFieldReferenceLive } from '#utils/field-references';
15
15
  import { getRowScopedWatchNames, resolveRowScopedFieldReference, type RowScopeParams } from '#utils/field-references.row-scope';
@@ -263,8 +263,10 @@ function bindCalculationsField(root: Element, field: HTMLElement, input: HTMLInp
263
263
  };
264
264
  }
265
265
 
266
- export const calculationsModule: FormieModuleDefinition = {
267
- id: MODULE_ID,
266
+ export const calculationsModule: BrowserModuleDefinition = {
267
+ moduleId: `formie:${MODULE_ID}`,
268
+ version: 2,
269
+ surfaces: ['server-rendered', 'client-rendered', 'cp-edit'],
268
270
  kind: 'field',
269
271
  match: (ctx) => {
270
272
  return !!ctx.target.querySelector(INPUT_SELECTOR);
@@ -1,5 +1,5 @@
1
- import type { FormieModuleDefinition } from '#contracts/modules';
2
- import { dispatchFieldEvent, escapeSelectorValue, releaseFormValidators, retainFormValidators } from '#modules/fields/shared';
1
+ import type { BrowserModuleDefinition } from '#contracts/modules';
2
+ import { dispatchFieldEvent, releaseFormValidators, retainFormValidators } from '#modules/fields/shared';
3
3
  import { createDebug } from '#utils/debug';
4
4
 
5
5
  const FIELD_SELECTOR = '[data-formie-checkboxes-field-layout], [data-formie-radio-field-layout]';
@@ -213,19 +213,7 @@ function unregisterValidators(form: HTMLFormElement | null): void {
213
213
  releaseFormValidators(form, VALIDATOR_SCOPE, [CHECKBOX_MINMAX_VALIDATOR, OTHER_OPTION_TEXT_VALIDATOR]);
214
214
  }
215
215
 
216
- function syncCheckedAttribute(input: HTMLInputElement): void {
217
- if (input.checked) {
218
- input.setAttribute('checked', '');
219
- } else {
220
- input.removeAttribute('checked');
221
- }
222
- }
223
-
224
- function syncRequiredCheckboxes(field: HTMLElement): void {
225
- const requiredCheckboxes = Array.from(field.querySelectorAll('input[type="checkbox"][required][data-formie-checkbox-input]')).filter((input): input is HTMLInputElement => {
226
- return input instanceof HTMLInputElement;
227
- });
228
-
216
+ function syncRequiredCheckboxes(requiredCheckboxes: HTMLInputElement[]): void {
229
217
  if (!requiredCheckboxes.length) {
230
218
  return;
231
219
  }
@@ -299,27 +287,11 @@ function toggleCheckboxGroup(field: HTMLElement, toggle: HTMLInputElement): void
299
287
  // Re-emit change/input so downstream modules (conditions, calculations,
300
288
  // validation) react exactly as if the user toggled each box manually.
301
289
  input.checked = toggle.checked;
302
- syncCheckedAttribute(input);
303
290
  input.dispatchEvent(new Event('change', { bubbles: true }));
304
291
  input.dispatchEvent(new Event('input', { bubbles: true }));
305
292
  });
306
293
  }
307
294
 
308
- function syncRadioGroup(input: HTMLInputElement, field: HTMLElement): void {
309
- if (!input.checked || !input.name) {
310
- syncCheckedAttribute(input);
311
- return;
312
- }
313
-
314
- const group = Array.from(field.querySelectorAll(`input[type="radio"][name="${escapeSelectorValue(input.name)}"]`)).filter((radio): radio is HTMLInputElement => {
315
- return radio instanceof HTMLInputElement;
316
- });
317
-
318
- group.forEach((radio) => {
319
- syncCheckedAttribute(radio);
320
- });
321
- }
322
-
323
295
  function bindField(field: HTMLElement): () => void {
324
296
  const inputs = Array.from(field.querySelectorAll('input[type="checkbox"], input[type="radio"]')).filter((input): input is HTMLInputElement => {
325
297
  return input instanceof HTMLInputElement;
@@ -330,20 +302,28 @@ function bindField(field: HTMLElement): () => void {
330
302
  return () => {};
331
303
  }
332
304
 
305
+ const requiredCheckboxes = inputs.filter((input) => input.type === 'checkbox' && input.required);
306
+ const form = field.closest('form');
307
+ const onReset = (event: Event) => {
308
+ // Native reset restores checked properties after the reset event returns.
309
+ queueMicrotask(() => {
310
+ if (!event.defaultPrevented) {
311
+ syncRequiredCheckboxes(requiredCheckboxes);
312
+ enforceMaxOptions(field);
313
+ syncOtherOptionField(field);
314
+ }
315
+ });
316
+ };
317
+ form?.addEventListener('reset', onReset);
318
+
333
319
  const listeners = inputs.map((input) => {
334
320
  const eventName = input.type === 'radio' ? 'change' : 'click';
335
321
  const handler = () => {
336
- syncCheckedAttribute(input);
337
-
338
322
  if (input.type === 'checkbox' && isToggleCheckbox(input)) {
339
323
  toggleCheckboxGroup(field, input);
340
324
  }
341
325
 
342
- if (input.type === 'radio') {
343
- syncRadioGroup(input, field);
344
- }
345
-
346
- syncRequiredCheckboxes(field);
326
+ syncRequiredCheckboxes(requiredCheckboxes);
347
327
  enforceMaxOptions(field);
348
328
  queueMicrotask(() => {
349
329
  syncOtherOptionField(field);
@@ -356,7 +336,6 @@ function bindField(field: HTMLElement): () => void {
356
336
  };
357
337
 
358
338
  input.addEventListener(eventName, handler);
359
- syncCheckedAttribute(input);
360
339
 
361
340
  return () => {
362
341
  input.removeEventListener(eventName, handler);
@@ -365,7 +344,7 @@ function bindField(field: HTMLElement): () => void {
365
344
 
366
345
  const destroyOtherOptionBinding = bindOtherOptionField(field);
367
346
 
368
- syncRequiredCheckboxes(field);
347
+ syncRequiredCheckboxes(requiredCheckboxes);
369
348
  enforceMaxOptions(field);
370
349
  syncOtherOptionField(field);
371
350
  dispatchFieldEvent(field, MODULE_ID, 'init', {
@@ -376,12 +355,15 @@ function bindField(field: HTMLElement): () => void {
376
355
  listeners.forEach((unbind) => {
377
356
  unbind();
378
357
  });
358
+ form?.removeEventListener('reset', onReset);
379
359
  destroyOtherOptionBinding();
380
360
  };
381
361
  }
382
362
 
383
- export const checkboxRadioModule: FormieModuleDefinition = {
384
- id: MODULE_ID,
363
+ export const checkboxRadioModule: BrowserModuleDefinition = {
364
+ moduleId: `formie:${MODULE_ID}`,
365
+ version: 2,
366
+ surfaces: ['server-rendered', 'client-rendered', 'cp-edit'],
385
367
  kind: 'field',
386
368
  match: (ctx) => {
387
369
  return ctx.target instanceof HTMLElement && (
@@ -1,10 +1,12 @@
1
1
  import TomSelect from 'tom-select/dist/esm/tom-select.complete.js';
2
2
 
3
- import type { FormieModuleDefinition } from '#contracts/modules';
3
+ import type { BrowserModuleDefinition } from '#contracts/modules';
4
4
  import comboboxThemeCss from '#theme-css/fields/_combobox.css?inline';
5
5
  import { dispatchFieldEvent, getModuleFieldContainers } from '#modules/fields/shared';
6
6
  import { ensureModuleStyles } from '#modules/styles';
7
+ import { getThemeClasses } from '#theme/theme-classes';
7
8
  import { createDebug } from '#utils/debug';
9
+ import { VALIDATION_SKIP_ATTR } from '#validation/skip';
8
10
 
9
11
  const SELECT_SELECTOR = 'select[data-formie-combobox-input]';
10
12
  const MODULE_ID = 'combobox';
@@ -15,7 +17,6 @@ ensureModuleStyles(MODULE_ID, [comboboxThemeCss]);
15
17
  const NATIVE_THEME_CLASSES = [
16
18
  'formie-select',
17
19
  'formie-dropdown-input',
18
- 'formie-input-error',
19
20
  ] as const;
20
21
 
21
22
  export type FormieComboboxOptions = {
@@ -35,7 +36,7 @@ type SelectElement = HTMLSelectElement & {
35
36
  function stripNativeThemeClasses(select: HTMLSelectElement): string[] {
36
37
  const removed: string[] = [];
37
38
 
38
- NATIVE_THEME_CLASSES.forEach((className) => {
39
+ [...NATIVE_THEME_CLASSES, ...getThemeClasses(select, 'fieldControlError')].forEach((className) => {
39
40
  if (select.classList.contains(className)) {
40
41
  select.classList.remove(className);
41
42
  removed.push(className);
@@ -51,8 +52,8 @@ function restoreNativeThemeClasses(select: HTMLSelectElement, classNames: string
51
52
  });
52
53
  }
53
54
 
54
- function cleanLeakedThemeClasses(element: HTMLElement): void {
55
- NATIVE_THEME_CLASSES.forEach((className) => {
55
+ function cleanLeakedThemeClasses(element: HTMLElement, source: HTMLSelectElement): void {
56
+ [...NATIVE_THEME_CLASSES, ...getThemeClasses(source, 'fieldControlError')].forEach((className) => {
56
57
  element.classList.remove(className);
57
58
  });
58
59
  }
@@ -78,6 +79,8 @@ function removeEmptyOptionFromCombobox(instance: TomSelectInstance): void {
78
79
  export function initFormieCombobox(select: SelectElement, options: FormieComboboxOptions = {}): () => void {
79
80
  select._formieTomSelect?.destroy();
80
81
 
82
+ const initialValues = Array.from(select.selectedOptions, (option) => option.value).filter(Boolean);
83
+ const form = select.form;
81
84
  const multiple = options.multiple === true;
82
85
  const restoredClasses = stripNativeThemeClasses(select);
83
86
  const placeholder = resolvePlaceholder(select, options.placeholder);
@@ -114,11 +117,13 @@ export function initFormieCombobox(select: SelectElement, options: FormieCombobo
114
117
  });
115
118
 
116
119
  const instance = new TomSelect(select, mergedOptions) as TomSelectInstance;
120
+ // The search query is temporary; the native select carries the field value.
121
+ instance.control_input.setAttribute(VALIDATION_SKIP_ATTR, '');
117
122
  removeEmptyOptionFromCombobox(instance);
118
- cleanLeakedThemeClasses(instance.wrapper);
123
+ cleanLeakedThemeClasses(instance.wrapper, select);
119
124
 
120
125
  if (instance.dropdown) {
121
- cleanLeakedThemeClasses(instance.dropdown);
126
+ cleanLeakedThemeClasses(instance.dropdown, select);
122
127
  }
123
128
 
124
129
  // Tom Select keeps the native select as a preceding sibling; ensure it never
@@ -127,6 +132,17 @@ export function initFormieCombobox(select: SelectElement, options: FormieCombobo
127
132
 
128
133
  select._formieTomSelect = instance;
129
134
 
135
+ const onReset = (event: Event) => {
136
+ // Tom Select replaces native options, so retain the original selection
137
+ // and restore its display after the browser resets the backing select.
138
+ queueMicrotask(() => {
139
+ if (!event.defaultPrevented) {
140
+ instance.setValue(initialValues);
141
+ }
142
+ });
143
+ };
144
+ form?.addEventListener('reset', onReset);
145
+
130
146
  debug.log('Initialized.', {
131
147
  inputName: select.name,
132
148
  multiple,
@@ -138,6 +154,7 @@ export function initFormieCombobox(select: SelectElement, options: FormieCombobo
138
154
  });
139
155
 
140
156
  return () => {
157
+ form?.removeEventListener('reset', onReset);
141
158
  instance.destroy();
142
159
  select.style.removeProperty('display');
143
160
  restoreNativeThemeClasses(select, restoredClasses);
@@ -148,8 +165,10 @@ export function initFormieCombobox(select: SelectElement, options: FormieCombobo
148
165
  };
149
166
  }
150
167
 
151
- export const comboboxModule: FormieModuleDefinition = {
152
- id: MODULE_ID,
168
+ export const comboboxModule: BrowserModuleDefinition = {
169
+ moduleId: `formie:${MODULE_ID}`,
170
+ version: 2,
171
+ surfaces: ['server-rendered', 'client-rendered', 'cp-edit'],
153
172
  kind: 'field',
154
173
  match: (ctx) => {
155
174
  return !!ctx.target.querySelector(SELECT_SELECTOR);
@@ -13,7 +13,7 @@ function parseConditionSource(value: unknown): ConditionSource | null {
13
13
  return {
14
14
  raw: typeof candidate.raw === 'string' ? candidate.raw : '',
15
15
  target: typeof candidate.target === 'string' ? candidate.target : '',
16
- handle: typeof candidate.handle === 'string' ? candidate.handle : '',
16
+ handle: typeof candidate.domHandle === 'string' ? candidate.domHandle : typeof candidate.handle === 'string' ? candidate.handle : '',
17
17
  selector: typeof candidate.selector === 'string' ? candidate.selector : '',
18
18
  defaultValue: typeof candidate.defaultValue === 'string' ? candidate.defaultValue : '',
19
19
  transformerId: typeof candidate.transformerId === 'string' ? candidate.transformerId : '',
@@ -45,35 +45,32 @@ export function parseConditionSettings(node: Element): ParsedConditionSettings |
45
45
 
46
46
  try {
47
47
  const parsed = JSON.parse(raw) as Record<string, unknown>;
48
- const conditions = Array.isArray(parsed.conditions)
49
- ? parsed.conditions.filter((condition): condition is ConditionDefinition => {
50
- if (!condition || typeof condition !== 'object') {
51
- return false;
52
- }
53
-
54
- const candidate = condition as Record<string, unknown>;
55
- return typeof candidate.field === 'string' && typeof candidate.condition === 'string';
56
- }).map((condition) => {
57
- const candidate = condition as Record<string, unknown>;
58
-
48
+ const effect = String(parsed.effect ?? parsed.showRule ?? 'show');
49
+ const valid = (parsed.version == null || parsed.version === 1) && ['all', 'any'].includes(String(parsed.mode ?? parsed.conditionRule ?? 'all')) && ['show', 'hide', 'enable', 'disable'].includes(effect);
50
+ const rawRules = valid ? parsed.rules ?? parsed.conditions : [null];
51
+ const conditions = Array.isArray(rawRules)
52
+ ? rawRules.map((row) => {
53
+ const candidate = row && typeof row === 'object' ? row as Record<string, unknown> : {};
59
54
  return {
60
- field: condition.field,
55
+ field: typeof candidate.field === 'string' ? candidate.field : '',
61
56
  source: parseConditionSource(candidate.source),
62
- condition: condition.condition,
63
- value: condition.value,
57
+ condition: String(candidate.operator ?? candidate.condition ?? ''),
58
+ valueType: candidate.valueType as ConditionDefinition['valueType'],
59
+ browserSafe: candidate.browserSafe !== false,
60
+ value: candidate.value,
64
61
  };
65
62
  })
66
- : [];
63
+ : [{ field: '', condition: '', value: null, browserSafe: false }];
67
64
 
68
65
  return {
69
- showRule: parsed.showRule === 'hide' ? 'hide' : 'show',
70
- conditionRule: parsed.conditionRule === 'any' ? 'any' : 'all',
66
+ showRule: ['hide', 'enable', 'disable'].includes(effect) ? effect as ParsedConditionSettings['showRule'] : 'show',
67
+ conditionRule: (parsed.mode ?? parsed.conditionRule) === 'any' ? 'any' : 'all',
71
68
  clearOnHide: parsed.clearOnHide !== false,
72
69
  isNested: Boolean(parsed.isNested),
73
70
  conditions,
74
71
  };
75
72
  } catch (error) {
76
- console.error('[formie] Invalid condition JSON.', error);
77
- return null;
73
+ console.error('[formie] Invalid condition JSON.');
74
+ return { showRule: 'show', conditionRule: 'all', clearOnHide: true, isNested: false, conditions: [{ field: '', condition: '', browserSafe: false }] };
78
75
  }
79
76
  }