@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.
- package/CHANGELOG.md +80 -0
- package/dist/chunks/{address-country-Rx6GQTND.js → address-country-BcEQKw0T.js} +11 -5
- package/dist/chunks/{address-finder-k7_BXaRe.js → address-finder-BWGINCSA.js} +3 -3
- package/dist/chunks/address-state-DI3ndhMU.js +365 -0
- package/dist/chunks/{api-BUb6P-xu.js → api-BK9b340y.js} +93 -63
- package/dist/chunks/{api-DqpfkZWL.js → api-Dxl_CKw-.js} +202 -193
- package/dist/chunks/{api-sPqGbOww.js → api-ghNr2uxX.js} +15 -9
- package/dist/chunks/{async-nPFRNQ06.js → async-2GdXNFCE.js} +18 -1
- package/dist/chunks/{bpoint-Dlq34XH1.js → bpoint-BwtuyN_O.js} +3 -3
- package/dist/chunks/calculations-YEoVCG2q.js +310 -0
- package/dist/chunks/{captcha-eu-CGm8Lbrj.js → captcha-eu-DWLoQFKB.js} +6 -5
- package/dist/chunks/{checkbox-radio-BY2aTCOG.js → checkbox-radio-gsm8QPJg.js} +74 -75
- package/dist/chunks/{combobox-C0EuExSI.js → combobox-Ba08_cHL.js} +340 -328
- package/dist/chunks/conditions-Ceeg6ReZ.js +467 -0
- package/dist/chunks/country-from-ip-DDj_mFki.js +29 -0
- package/dist/chunks/csrf-DxHg_ZYt.js +61 -0
- package/dist/chunks/{custom-google-maps-BsaSirEZ.js → custom-google-maps-C4l2S3GO.js} +9 -3
- package/dist/chunks/{custom-link-CJ1-FjEM.js → custom-link-CLazgKYH.js} +8 -2
- package/dist/chunks/{custom-maps-DYW_pBid.js → custom-maps-DqcAlD0v.js} +10 -4
- package/dist/chunks/{date-picker-F9BUBDSW.js → date-picker-DaPvMrop.js} +467 -443
- package/dist/chunks/{dist-D6i-VNgM.js → dist-ZLeW0zZ3.js} +1281 -453
- package/dist/chunks/{eway-D9_m8ooj.js → eway-uw0DzU-L.js} +5 -5
- package/dist/chunks/field-references.resolver-Ca8MguFv.js +104 -0
- package/dist/chunks/{file-upload-rqfZNqlH.js → file-upload-B8_mH012.js} +119 -99
- package/dist/chunks/friendly-captcha-v1-CVXaXgDO.js +34 -0
- package/dist/chunks/{friendly-captcha-v2-CLSAbkqx.js → friendly-captcha-v2-DkeIVTKI.js} +6 -5
- package/dist/chunks/{friendly-challenge-8uPatWqk.js → friendly-challenge-DPG9MJb1.js} +111 -102
- package/dist/chunks/{go-cardless-DAT3ZaQf.js → go-cardless-c4wLJdOt.js} +2 -2
- package/dist/chunks/google-address-JHYvjwgN.js +168 -0
- package/dist/chunks/{hcaptcha-CGLAmgjO.js → hcaptcha-Dt3ELLlP.js} +4 -4
- package/dist/chunks/{hidden-D7_Ch-QN.js → hidden-BZhXnNPe.js} +8 -2
- package/dist/chunks/http-C2QdHuWO.js +27 -0
- package/dist/chunks/{leaflet-src-j-lHNmhR.js → leaflet-src-DE6rv6M-.js} +42 -46
- package/dist/chunks/{loqate-CJPzBmSm.js → loqate-m51iPyO1.js} +3 -3
- package/dist/chunks/{mollie-Bibhl67p.js → mollie-Bn0PX173.js} +2 -2
- package/dist/chunks/{moneris-H2wSqk9h.js → moneris-BRbUvI4t.js} +3 -3
- package/dist/chunks/opayo-BwWdInEP.js +188 -0
- package/dist/chunks/{paddle-DkvIxQdG.js → paddle-CympHVvd.js} +4 -4
- package/dist/chunks/{password-validation-D06uH9va.js → password-validation-C8_uO_c_.js} +18 -12
- package/dist/chunks/{paypal-CQpAF7Ua.js → paypal-h75h6wae.js} +8 -6
- package/dist/chunks/{payway-CwDkC7YZ.js → payway-C9rHTCC_.js} +4 -4
- package/dist/chunks/{phone-country-D3ayr60L.js → phone-country-B8fv-L5_.js} +80 -63
- package/dist/chunks/{place-kit-BGHklIZ-.js → place-kit-BnZGQ-L1.js} +11 -11
- package/dist/chunks/{placekit-autocomplete.esm-DWVabQlq.js → placekit-autocomplete.esm-CZZERYur.js} +11 -15
- package/dist/chunks/{recaptcha-enterprise-XPId5-bS.js → recaptcha-enterprise-CUs8BO6V.js} +4 -4
- package/dist/chunks/{recaptcha-shared-CLYJvojk.js → recaptcha-shared-DTbTw-MF.js} +2 -2
- package/dist/chunks/{recaptcha-v2-checkbox-Cy-0PEPc.js → recaptcha-v2-checkbox-P1McW4Pv.js} +4 -4
- package/dist/chunks/{recaptcha-v2-invisible-BwRH540O.js → recaptcha-v2-invisible-FGDqozSP.js} +4 -4
- package/dist/chunks/{recaptcha-v3-No4Ykrhk.js → recaptcha-v3-CflUHeZD.js} +4 -4
- package/dist/chunks/{repeater-BVnPUqTj.js → repeater-CyhDbozO.js} +60 -40
- package/dist/chunks/request-profile-DhwkeCpS.js +10 -0
- package/dist/chunks/rich-text-uV4o_pGk.js +361 -0
- package/dist/chunks/{chunk-3b4jIN3o.js → rolldown-runtime-DtPi1Y-2.js} +2 -2
- package/dist/chunks/{scripts-CbQ7agX3.js → scripts-DrccjrFv.js} +1 -1
- package/dist/chunks/{sdk-BhHAYgga.js → sdk-lkwSvlpM.js} +282 -234
- package/dist/chunks/{shared-Bx9s0i0P.js → shared-OKhSay59.js} +27 -21
- package/dist/chunks/{signature-IOhuicje.js → signature-7cldFbqj.js} +138 -112
- package/dist/chunks/skip-CEW89ExY.js +7 -0
- package/dist/chunks/{snaptcha-DKx98oon.js → snaptcha-C3QLYxFT.js} +2 -2
- package/dist/chunks/{square-DwdcOI1G.js → square-C1z5DPVw.js} +9 -7
- package/dist/chunks/{stripe-DaoG2FT1.js → stripe-Dmkdkq1y.js} +11 -12
- package/dist/chunks/{summary-Dks0gXS3.js → summary-Bn-g5SDx.js} +48 -46
- package/dist/chunks/{survey-likert-DkE6bR7B.js → survey-likert-fk1hUPff.js} +7 -1
- package/dist/chunks/{survey-rank-DN6x4IZM.js → survey-rank-BsLGl2LX.js} +37 -25
- package/dist/chunks/{survey-rating-BnsNL9U5.js → survey-rating-DVS6e6fM.js} +17 -5
- package/dist/chunks/{table-CAjCJHrL.js → table-j7MlLLCc.js} +22 -16
- package/dist/chunks/{text-limit-BGaY0jhI.js → text-limit-LGMPhFuC.js} +40 -34
- package/dist/chunks/{theme-classes-Tv7q7ToE.js → theme-classes-DAQuEqdP.js} +1 -1
- package/dist/chunks/{turnstile-DJakPzQD.js → turnstile-ptihQfLl.js} +4 -4
- package/dist/chunks/{upload-manager-W-QDgxKD.js → upload-manager-BW7l-BhF.js} +459 -387
- package/dist/chunks/{utils-Vqp8i_RM.js → utils-DpUBPadZ.js} +20 -14
- package/dist/css/formie-base.css +104 -1
- package/dist/css/theme/_messages.css +2 -0
- package/dist/css/theme/_tokens.css +31 -25
- package/dist/css/theme/forms/_form.css +5 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1779 -1415
- package/dist/js/contracts/client.d.ts +5 -3
- package/dist/js/contracts/client.d.ts.map +1 -1
- package/dist/js/contracts/common.d.ts +2 -2
- package/dist/js/contracts/common.d.ts.map +1 -1
- package/dist/js/contracts/modules.d.ts +37 -12
- package/dist/js/contracts/modules.d.ts.map +1 -1
- package/dist/js/contracts/schema.d.ts +20 -38
- package/dist/js/contracts/schema.d.ts.map +1 -1
- package/dist/js/contracts/theme.d.ts +4 -1
- package/dist/js/contracts/theme.d.ts.map +1 -1
- package/dist/js/core/client-rendered-modules.d.ts +17 -0
- package/dist/js/core/client-rendered-modules.d.ts.map +1 -0
- package/dist/js/core/create-formie-client.d.ts.map +1 -1
- package/dist/js/core/field-error-aria.d.ts +23 -0
- package/dist/js/core/field-error-aria.d.ts.map +1 -0
- package/dist/js/core/hydrate-modules.d.ts +10 -8
- package/dist/js/core/hydrate-modules.d.ts.map +1 -1
- package/dist/js/core/submit-result-state.d.ts.map +1 -1
- package/dist/js/core/submit-result-ui.d.ts +3 -1
- package/dist/js/core/submit-result-ui.d.ts.map +1 -1
- package/dist/js/core/validation-focus.d.ts.map +1 -1
- package/dist/js/modules/address/address-finder.d.ts +1 -1
- package/dist/js/modules/address/address-finder.d.ts.map +1 -1
- package/dist/js/modules/address/factories.d.ts +3 -3
- package/dist/js/modules/address/factories.d.ts.map +1 -1
- package/dist/js/modules/address/google-address.d.ts +12 -1
- package/dist/js/modules/address/google-address.d.ts.map +1 -1
- package/dist/js/modules/address/index.d.ts +2 -2
- package/dist/js/modules/address/index.d.ts.map +1 -1
- package/dist/js/modules/address/loqate.d.ts +1 -1
- package/dist/js/modules/address/loqate.d.ts.map +1 -1
- package/dist/js/modules/address/place-kit.d.ts +1 -1
- package/dist/js/modules/address/place-kit.d.ts.map +1 -1
- package/dist/js/modules/captchas/captcha-eu.d.ts +1 -1
- package/dist/js/modules/captchas/captcha-eu.d.ts.map +1 -1
- package/dist/js/modules/captchas/factories.d.ts +11 -11
- package/dist/js/modules/captchas/factories.d.ts.map +1 -1
- package/dist/js/modules/captchas/friendly-captcha-v1.d.ts +1 -1
- package/dist/js/modules/captchas/friendly-captcha-v1.d.ts.map +1 -1
- package/dist/js/modules/captchas/friendly-captcha-v2.d.ts +1 -1
- package/dist/js/modules/captchas/friendly-captcha-v2.d.ts.map +1 -1
- package/dist/js/modules/captchas/hcaptcha.d.ts +1 -1
- package/dist/js/modules/captchas/hcaptcha.d.ts.map +1 -1
- package/dist/js/modules/captchas/index.d.ts +2 -2
- package/dist/js/modules/captchas/index.d.ts.map +1 -1
- package/dist/js/modules/captchas/recaptcha-enterprise.d.ts +1 -1
- package/dist/js/modules/captchas/recaptcha-enterprise.d.ts.map +1 -1
- package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts +1 -1
- package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts.map +1 -1
- package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts +1 -1
- package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts.map +1 -1
- package/dist/js/modules/captchas/recaptcha-v3.d.ts +1 -1
- package/dist/js/modules/captchas/recaptcha-v3.d.ts.map +1 -1
- package/dist/js/modules/captchas/snaptcha.d.ts +1 -1
- package/dist/js/modules/captchas/snaptcha.d.ts.map +1 -1
- package/dist/js/modules/captchas/turnstile.d.ts +1 -1
- package/dist/js/modules/captchas/turnstile.d.ts.map +1 -1
- package/dist/js/modules/fields/address-country.d.ts +2 -2
- package/dist/js/modules/fields/address-country.d.ts.map +1 -1
- package/dist/js/modules/fields/address-state.d.ts +2 -2
- package/dist/js/modules/fields/address-state.d.ts.map +1 -1
- package/dist/js/modules/fields/calculations.d.ts +2 -2
- package/dist/js/modules/fields/calculations.d.ts.map +1 -1
- package/dist/js/modules/fields/checkbox-radio.d.ts +2 -2
- package/dist/js/modules/fields/checkbox-radio.d.ts.map +1 -1
- package/dist/js/modules/fields/combobox.d.ts +2 -2
- package/dist/js/modules/fields/combobox.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/config.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/effects.d.ts +1 -0
- package/dist/js/modules/fields/conditions/effects.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/evaluator.d.ts +4 -1
- package/dist/js/modules/fields/conditions/evaluator.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/references.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/submission-context.d.ts +15 -0
- package/dist/js/modules/fields/conditions/submission-context.d.ts.map +1 -0
- package/dist/js/modules/fields/conditions/types.d.ts +3 -1
- package/dist/js/modules/fields/conditions/types.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/values.d.ts +1 -1
- package/dist/js/modules/fields/conditions/values.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions.d.ts +2 -2
- package/dist/js/modules/fields/conditions.d.ts.map +1 -1
- package/dist/js/modules/fields/custom-google-maps.d.ts +2 -2
- package/dist/js/modules/fields/custom-google-maps.d.ts.map +1 -1
- package/dist/js/modules/fields/custom-link.d.ts +2 -2
- package/dist/js/modules/fields/custom-link.d.ts.map +1 -1
- package/dist/js/modules/fields/custom-maps.d.ts +2 -2
- package/dist/js/modules/fields/custom-maps.d.ts.map +1 -1
- package/dist/js/modules/fields/date-picker.d.ts +2 -2
- package/dist/js/modules/fields/date-picker.d.ts.map +1 -1
- package/dist/js/modules/fields/file-upload.d.ts +2 -2
- package/dist/js/modules/fields/file-upload.d.ts.map +1 -1
- package/dist/js/modules/fields/hidden.d.ts +2 -2
- package/dist/js/modules/fields/hidden.d.ts.map +1 -1
- package/dist/js/modules/fields/index.d.ts +2 -2
- package/dist/js/modules/fields/index.d.ts.map +1 -1
- package/dist/js/modules/fields/password-validation.d.ts +2 -2
- package/dist/js/modules/fields/password-validation.d.ts.map +1 -1
- package/dist/js/modules/fields/phone-country.d.ts +2 -2
- package/dist/js/modules/fields/phone-country.d.ts.map +1 -1
- package/dist/js/modules/fields/repeater.d.ts +2 -2
- package/dist/js/modules/fields/repeater.d.ts.map +1 -1
- package/dist/js/modules/fields/rich-text.d.ts +2 -2
- package/dist/js/modules/fields/rich-text.d.ts.map +1 -1
- package/dist/js/modules/fields/shared.d.ts +1 -0
- package/dist/js/modules/fields/shared.d.ts.map +1 -1
- package/dist/js/modules/fields/signature.d.ts +2 -2
- package/dist/js/modules/fields/signature.d.ts.map +1 -1
- package/dist/js/modules/fields/summary.d.ts +2 -2
- package/dist/js/modules/fields/summary.d.ts.map +1 -1
- package/dist/js/modules/fields/survey-likert.d.ts +2 -2
- package/dist/js/modules/fields/survey-likert.d.ts.map +1 -1
- package/dist/js/modules/fields/survey-rank.d.ts +2 -2
- package/dist/js/modules/fields/survey-rank.d.ts.map +1 -1
- package/dist/js/modules/fields/survey-rating.d.ts +2 -2
- package/dist/js/modules/fields/survey-rating.d.ts.map +1 -1
- package/dist/js/modules/fields/table.d.ts +2 -2
- package/dist/js/modules/fields/table.d.ts.map +1 -1
- package/dist/js/modules/fields/text-limit.d.ts +2 -2
- package/dist/js/modules/fields/text-limit.d.ts.map +1 -1
- package/dist/js/modules/fields/upload-manager.d.ts +2 -2
- package/dist/js/modules/fields/upload-manager.d.ts.map +1 -1
- package/dist/js/modules/loader.d.ts +10 -4
- package/dist/js/modules/loader.d.ts.map +1 -1
- package/dist/js/modules/payments/bpoint.d.ts +1 -1
- package/dist/js/modules/payments/bpoint.d.ts.map +1 -1
- package/dist/js/modules/payments/eway.d.ts +1 -1
- package/dist/js/modules/payments/eway.d.ts.map +1 -1
- package/dist/js/modules/payments/factories.d.ts +7 -7
- package/dist/js/modules/payments/factories.d.ts.map +1 -1
- package/dist/js/modules/payments/go-cardless.d.ts +1 -1
- package/dist/js/modules/payments/go-cardless.d.ts.map +1 -1
- package/dist/js/modules/payments/index.d.ts +2 -2
- package/dist/js/modules/payments/index.d.ts.map +1 -1
- package/dist/js/modules/payments/mollie.d.ts +1 -1
- package/dist/js/modules/payments/mollie.d.ts.map +1 -1
- package/dist/js/modules/payments/moneris.d.ts +1 -1
- package/dist/js/modules/payments/moneris.d.ts.map +1 -1
- package/dist/js/modules/payments/opayo.d.ts +1 -1
- package/dist/js/modules/payments/opayo.d.ts.map +1 -1
- package/dist/js/modules/payments/paddle.d.ts +1 -1
- package/dist/js/modules/payments/paddle.d.ts.map +1 -1
- package/dist/js/modules/payments/paypal.d.ts +1 -1
- package/dist/js/modules/payments/paypal.d.ts.map +1 -1
- package/dist/js/modules/payments/payway.d.ts +1 -1
- package/dist/js/modules/payments/payway.d.ts.map +1 -1
- package/dist/js/modules/payments/square.d.ts +1 -1
- package/dist/js/modules/payments/square.d.ts.map +1 -1
- package/dist/js/modules/payments/stripe.d.ts +1 -1
- package/dist/js/modules/payments/stripe.d.ts.map +1 -1
- package/dist/js/modules/payments/utils.d.ts +6 -0
- package/dist/js/modules/payments/utils.d.ts.map +1 -1
- package/dist/js/modules/registry.d.ts +6 -4
- package/dist/js/modules/registry.d.ts.map +1 -1
- package/dist/js/submit/pipeline.d.ts.map +1 -1
- package/dist/js/theme/browser-theme-state.generated.d.ts +24 -0
- package/dist/js/theme/browser-theme-state.generated.d.ts.map +1 -0
- package/dist/js/theme/theme-classes.d.ts +3 -3
- package/dist/js/theme/theme-classes.d.ts.map +1 -1
- package/dist/js/transport/forms-api.d.ts +5 -3
- package/dist/js/transport/forms-api.d.ts.map +1 -1
- package/dist/js/utils/async.d.ts +1 -0
- package/dist/js/utils/async.d.ts.map +1 -1
- package/dist/js/utils/country-from-ip.d.ts +2 -2
- package/dist/js/utils/country-from-ip.d.ts.map +1 -1
- package/dist/js/utils/csrf.d.ts +17 -0
- package/dist/js/utils/csrf.d.ts.map +1 -0
- package/dist/js/utils/field-references.parser.d.ts +1 -0
- package/dist/js/utils/field-references.parser.d.ts.map +1 -1
- package/dist/js/utils/field-references.resolver.d.ts.map +1 -1
- package/dist/js/utils/field-references.types.d.ts +1 -0
- package/dist/js/utils/field-references.types.d.ts.map +1 -1
- package/dist/js/utils/http.d.ts +3 -1
- package/dist/js/utils/http.d.ts.map +1 -1
- package/dist/js/utils/request-profile.d.ts +4 -0
- package/dist/js/utils/request-profile.d.ts.map +1 -0
- package/dist/js/utils/unload-warning.d.ts.map +1 -1
- package/dist/js/validation/rules/email.d.ts.map +1 -1
- package/dist/js/validation/rules/match.d.ts.map +1 -1
- package/dist/js/validation/rules/number.d.ts.map +1 -1
- package/dist/js/validation/rules/required.d.ts.map +1 -1
- package/dist/js/validation/rules/shared.d.ts.map +1 -1
- package/dist/js/validation/rules/url.d.ts.map +1 -1
- package/dist/js/validation/skip.d.ts +7 -0
- package/dist/js/validation/skip.d.ts.map +1 -0
- package/dist/js/validation/submit-readiness.d.ts.map +1 -1
- package/dist/js/validation/validator.d.ts.map +1 -1
- package/package.json +43 -12
- package/src/css/formie-base.css +104 -1
- package/src/css/theme/_messages.css +2 -0
- package/src/css/theme/_tokens.css +31 -25
- package/src/css/theme/forms/_form.css +5 -1
- package/src/index.ts +13 -8
- package/src/js/contracts/client.ts +5 -3
- package/src/js/contracts/common.ts +5 -6
- package/src/js/contracts/modules.ts +41 -12
- package/src/js/contracts/schema.ts +21 -43
- package/src/js/contracts/theme.ts +6 -1
- package/src/js/core/client-rendered-modules.ts +69 -0
- package/src/js/core/create-formie-client.ts +132 -86
- package/src/js/core/field-error-aria.ts +93 -0
- package/src/js/core/hydrate-modules.ts +54 -29
- package/src/js/core/submit-result-state.ts +51 -45
- package/src/js/core/submit-result-ui.ts +25 -68
- package/src/js/core/validation-focus.ts +20 -20
- package/src/js/modules/address/address-finder.ts +1 -1
- package/src/js/modules/address/factories.ts +15 -13
- package/src/js/modules/address/google-address.ts +102 -40
- package/src/js/modules/address/index.ts +2 -2
- package/src/js/modules/address/loqate.ts +1 -1
- package/src/js/modules/address/place-kit.ts +14 -8
- package/src/js/modules/captchas/captcha-eu.ts +2 -2
- package/src/js/modules/captchas/factories.ts +66 -42
- package/src/js/modules/captchas/friendly-captcha-v1.ts +12 -2
- package/src/js/modules/captchas/friendly-captcha-v2.ts +4 -2
- package/src/js/modules/captchas/hcaptcha.ts +5 -5
- package/src/js/modules/captchas/index.ts +2 -2
- package/src/js/modules/captchas/recaptcha-enterprise.ts +2 -2
- package/src/js/modules/captchas/recaptcha-v2-checkbox.ts +3 -3
- package/src/js/modules/captchas/recaptcha-v2-invisible.ts +2 -2
- package/src/js/modules/captchas/recaptcha-v3.ts +2 -2
- package/src/js/modules/captchas/snaptcha.ts +1 -1
- package/src/js/modules/captchas/turnstile.ts +3 -3
- package/src/js/modules/fields/address-country.ts +11 -9
- package/src/js/modules/fields/address-state.ts +94 -35
- package/src/js/modules/fields/calculations.ts +5 -3
- package/src/js/modules/fields/checkbox-radio.ts +24 -42
- package/src/js/modules/fields/combobox.ts +28 -9
- package/src/js/modules/fields/conditions/config.ts +17 -20
- package/src/js/modules/fields/conditions/effects.ts +25 -29
- package/src/js/modules/fields/conditions/evaluator.ts +21 -29
- package/src/js/modules/fields/conditions/references.ts +16 -5
- package/src/js/modules/fields/conditions/submission-context.ts +80 -0
- package/src/js/modules/fields/conditions/types.ts +3 -1
- package/src/js/modules/fields/conditions/values.ts +31 -6
- package/src/js/modules/fields/conditions.ts +65 -18
- package/src/js/modules/fields/custom-google-maps.ts +5 -3
- package/src/js/modules/fields/custom-link.ts +5 -3
- package/src/js/modules/fields/custom-maps.ts +5 -3
- package/src/js/modules/fields/date-picker.ts +40 -7
- package/src/js/modules/fields/file-upload.ts +62 -23
- package/src/js/modules/fields/hidden.ts +5 -3
- package/src/js/modules/fields/index.ts +2 -2
- package/src/js/modules/fields/password-validation.ts +5 -3
- package/src/js/modules/fields/phone-country.ts +35 -8
- package/src/js/modules/fields/repeater.ts +60 -7
- package/src/js/modules/fields/rich-text.ts +20 -4
- package/src/js/modules/fields/shared.ts +13 -0
- package/src/js/modules/fields/signature.ts +24 -4
- package/src/js/modules/fields/summary.ts +8 -20
- package/src/js/modules/fields/survey-likert.ts +5 -3
- package/src/js/modules/fields/survey-rank.ts +21 -3
- package/src/js/modules/fields/survey-rating.ts +17 -3
- package/src/js/modules/fields/table.ts +5 -3
- package/src/js/modules/fields/text-limit.ts +5 -3
- package/src/js/modules/fields/upload-manager.ts +244 -67
- package/src/js/modules/loader.ts +345 -281
- package/src/js/modules/payments/bpoint.ts +2 -2
- package/src/js/modules/payments/eway.ts +2 -2
- package/src/js/modules/payments/factories.ts +53 -52
- package/src/js/modules/payments/go-cardless.ts +1 -1
- package/src/js/modules/payments/index.ts +2 -2
- package/src/js/modules/payments/mollie.ts +1 -1
- package/src/js/modules/payments/moneris.ts +2 -2
- package/src/js/modules/payments/opayo.ts +21 -8
- package/src/js/modules/payments/paddle.ts +10 -6
- package/src/js/modules/payments/payment.ts +22 -18
- package/src/js/modules/payments/paypal.ts +1 -1
- package/src/js/modules/payments/payway.ts +2 -2
- package/src/js/modules/payments/square.ts +2 -2
- package/src/js/modules/payments/stripe.ts +5 -5
- package/src/js/modules/payments/utils.ts +14 -0
- package/src/js/modules/registry.ts +19 -11
- package/src/js/submit/pipeline.ts +10 -11
- package/src/js/theme/browser-theme-state.generated.ts +25 -0
- package/src/js/theme/theme-classes.ts +3 -3
- package/src/js/transport/forms-api.ts +66 -67
- package/src/js/utils/async.ts +34 -0
- package/src/js/utils/country-from-ip.ts +14 -9
- package/src/js/utils/csrf.ts +136 -0
- package/src/js/utils/field-references.parser.ts +13 -113
- package/src/js/utils/field-references.resolver.ts +27 -64
- package/src/js/utils/field-references.row-scope.ts +1 -1
- package/src/js/utils/field-references.types.ts +1 -0
- package/src/js/utils/http.ts +5 -5
- package/src/js/utils/request-profile.ts +10 -0
- package/src/js/utils/unload-warning.ts +12 -4
- package/src/js/validation/rules/email.ts +3 -5
- package/src/js/validation/rules/match.ts +2 -1
- package/src/js/validation/rules/minmax.ts +4 -43
- package/src/js/validation/rules/number.ts +3 -47
- package/src/js/validation/rules/pattern.ts +3 -8
- package/src/js/validation/rules/required.ts +14 -1
- package/src/js/validation/rules/shared.ts +5 -1
- package/src/js/validation/rules/url.ts +3 -10
- package/src/js/validation/skip.ts +9 -0
- package/src/js/validation/submit-readiness.ts +9 -2
- package/src/js/validation/validator.ts +49 -61
- package/dist/chunks/address-state-BwE01m2_.js +0 -344
- package/dist/chunks/calculations-Dv3hYb8_.js +0 -299
- package/dist/chunks/conditions-CPH1snCx.js +0 -424
- package/dist/chunks/country-from-ip-BwxKaBuJ.js +0 -26
- package/dist/chunks/field-references.resolver-CHwn0G0L.js +0 -145
- package/dist/chunks/friendly-captcha-v1-C_2reOXr.js +0 -31
- package/dist/chunks/google-address-BH3Zhi06.js +0 -161
- package/dist/chunks/http-D-JExro7.js +0 -27
- package/dist/chunks/opayo-CR0DtsLa.js +0 -179
- package/dist/chunks/rich-text-Bg_--4TG.js +0 -349
- package/dist/js/modules/fields/conditions/transforms.d.ts +0 -3
- package/dist/js/modules/fields/conditions/transforms.d.ts.map +0 -1
- package/src/js/modules/fields/conditions/transforms.ts +0 -206
- package/src/js/modules/payments/stub-payment-module.ts +0 -87
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field-error ARIA helpers.
|
|
3
|
+
*
|
|
4
|
+
* Pair `aria-errormessage` with `aria-describedby` pointing at the same error
|
|
5
|
+
* message id. `aria-errormessage` alone is inconsistently announced (notably
|
|
6
|
+
* iOS Safari + VoiceOver); Adrian Roselli's cross-AT testing found the pairing
|
|
7
|
+
* more reliable without clobbering instruction ids already on describedby.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export function appendDescribedBy(input: HTMLElement, describedById: string): void {
|
|
11
|
+
const current = (input.getAttribute('aria-describedby') || '').trim();
|
|
12
|
+
const items = current ? current.split(/\s+/) : [];
|
|
13
|
+
|
|
14
|
+
if (!items.includes(describedById)) {
|
|
15
|
+
items.push(describedById);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
input.setAttribute('aria-describedby', items.join(' ').trim());
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function removeDescribedBy(input: HTMLElement, describedById: string): void {
|
|
22
|
+
const current = (input.getAttribute('aria-describedby') || '').trim();
|
|
23
|
+
|
|
24
|
+
if (!current) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const filtered = current.split(/\s+/).filter((item) => {
|
|
29
|
+
return item !== describedById;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (filtered.length) {
|
|
33
|
+
input.setAttribute('aria-describedby', filtered.join(' '));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
input.removeAttribute('aria-describedby');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Drop describedby ids whose targets no longer exist (Formie removes error
|
|
42
|
+
* message nodes from the DOM when clearing). Keeps instruction/other ids.
|
|
43
|
+
*/
|
|
44
|
+
export function pruneMissingDescribedBy(input: HTMLElement, doc: Document = document): void {
|
|
45
|
+
const current = (input.getAttribute('aria-describedby') || '').trim();
|
|
46
|
+
|
|
47
|
+
if (!current) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const remaining = current.split(/\s+/).filter((id) => {
|
|
52
|
+
return !!id && !!doc.getElementById(id);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (remaining.length) {
|
|
56
|
+
input.setAttribute('aria-describedby', remaining.join(' '));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
input.removeAttribute('aria-describedby');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function setErrorMessageReference(input: HTMLElement, errorMessageId: string): void {
|
|
64
|
+
input.setAttribute('aria-errormessage', errorMessageId);
|
|
65
|
+
// Same id in describedby so AT that ignore aria-errormessage still hear it.
|
|
66
|
+
appendDescribedBy(input, errorMessageId);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function clearErrorMessageReference(input: HTMLElement, errorMessageId: string): void {
|
|
70
|
+
if (input.getAttribute('aria-errormessage') === errorMessageId) {
|
|
71
|
+
input.removeAttribute('aria-errormessage');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
removeDescribedBy(input, errorMessageId);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Clear errormessage refs and prune any describedby ids that no longer resolve
|
|
79
|
+
* (covers message nodes removed from the DOM and legacy container ids).
|
|
80
|
+
*/
|
|
81
|
+
export function clearFieldErrorAria(input: HTMLElement, errorMessageIds: string[] = []): void {
|
|
82
|
+
errorMessageIds.forEach((errorMessageId) => {
|
|
83
|
+
if (input.getAttribute('aria-errormessage') === errorMessageId) {
|
|
84
|
+
input.removeAttribute('aria-errormessage');
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (!errorMessageIds.length && input.hasAttribute('aria-errormessage')) {
|
|
89
|
+
input.removeAttribute('aria-errormessage');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
pruneMissingDescribedBy(input);
|
|
93
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { FormEventUnsubscribe } from '#contracts/client';
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type { FormModuleManifest } from '#contracts/schema';
|
|
2
|
+
import type { BrowserModuleDefinition, BrowserModuleHydrationReport, ModuleRegistrationOptions } from '#contracts/modules';
|
|
3
|
+
import type { BrowserModuleEntry } from '#contracts/schema';
|
|
5
4
|
import { EventBus } from '#events/event-bus';
|
|
6
5
|
import { loadModulesFromManifest } from '#modules/loader';
|
|
7
6
|
import { ModuleRegistry } from '#modules/registry';
|
|
@@ -10,34 +9,38 @@ import { createDebug } from '#utils/debug';
|
|
|
10
9
|
export type FormieModuleHydratorOptions = {
|
|
11
10
|
root: Element;
|
|
12
11
|
form?: HTMLFormElement | null;
|
|
13
|
-
modules?:
|
|
14
|
-
|
|
12
|
+
modules?: import('@verbb/formie-core').BrowserModuleManifest;
|
|
13
|
+
surface?: import('@verbb/formie-core').BrowserSurface;
|
|
15
14
|
registry?: ModuleRegistry;
|
|
16
15
|
};
|
|
17
16
|
|
|
18
|
-
export type FormieModuleHydrator = {
|
|
17
|
+
export type FormieModuleHydrator = BrowserModuleHydrationReport & {
|
|
18
|
+
assertReady: () => void;
|
|
19
|
+
prepare: (action: import('@verbb/formie-core').ClientSubmitAction) => Promise<Record<string, unknown>>;
|
|
20
|
+
result: (result: import('#contracts/schema').FormSubmitResult) => Promise<void>;
|
|
21
|
+
update: (manifest: import('@verbb/formie-core').BrowserModuleManifest) => Promise<void>;
|
|
19
22
|
destroy: () => Promise<void>;
|
|
20
23
|
on: (eventName: string, callback: (payload: unknown) => void | Promise<void>) => FormEventUnsubscribe;
|
|
21
24
|
emit: (eventName: string, payload?: unknown) => Promise<void>;
|
|
22
|
-
registerModule: (moduleDefinition:
|
|
25
|
+
registerModule: (moduleDefinition: BrowserModuleDefinition, options?: ModuleRegistrationOptions) => boolean;
|
|
23
26
|
unregisterModule: (moduleId: string) => void;
|
|
24
|
-
getRegisteredModules: () =>
|
|
27
|
+
getRegisteredModules: () => BrowserModuleDefinition[];
|
|
25
28
|
};
|
|
26
29
|
|
|
27
30
|
const debug = createDebug('general', 'module-hydrator');
|
|
28
31
|
|
|
29
32
|
export async function hydrateFormieModules(options: FormieModuleHydratorOptions): Promise<FormieModuleHydrator> {
|
|
30
33
|
const root = options.root;
|
|
31
|
-
const form = options.form ?? (root instanceof HTMLFormElement ? root : root.closest('form'));
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
+
const form = options.form ?? (root instanceof HTMLFormElement ? root : root.closest('form') ?? root.querySelector('form'));
|
|
35
|
+
const surface = options.surface ?? 'cp-edit';
|
|
36
|
+
const modules = options.modules ?? { contractVersion: 2, surface, entries: [] };
|
|
34
37
|
const registry = options.registry ?? new ModuleRegistry();
|
|
35
38
|
const bus = new EventBus();
|
|
36
39
|
|
|
37
40
|
// This helper reuses the canonical module manifest loader without mounting the
|
|
38
|
-
// full form client, which lets
|
|
41
|
+
// full form client, which lets CP edit hosts opt into shared
|
|
39
42
|
// field modules without inheriting submit or pagination ownership.
|
|
40
|
-
const
|
|
43
|
+
const runtime = await loadModulesFromManifest(modules, {
|
|
41
44
|
registry,
|
|
42
45
|
setupContext: {
|
|
43
46
|
formId: form?.id || (root as HTMLElement).id || 'formie-modules',
|
|
@@ -45,31 +48,64 @@ export async function hydrateFormieModules(options: FormieModuleHydratorOptions)
|
|
|
45
48
|
form,
|
|
46
49
|
target: root,
|
|
47
50
|
scope: 'form',
|
|
51
|
+
surface,
|
|
48
52
|
state: {},
|
|
49
53
|
options: {},
|
|
50
54
|
on: (eventName, callback) => {
|
|
51
55
|
return bus.on(eventName, callback);
|
|
52
56
|
},
|
|
53
57
|
emit: async(eventName, payload) => {
|
|
58
|
+
root.dispatchEvent(new CustomEvent(eventName, { detail: payload, bubbles: true }));
|
|
54
59
|
await bus.emit(eventName, payload);
|
|
55
60
|
},
|
|
56
61
|
},
|
|
57
62
|
matchContext: {
|
|
58
63
|
root,
|
|
59
64
|
form,
|
|
60
|
-
|
|
65
|
+
surface,
|
|
61
66
|
},
|
|
62
67
|
});
|
|
63
68
|
|
|
64
69
|
debug.log('Hydrated module manifest.', {
|
|
65
|
-
moduleCount: modules.length,
|
|
66
|
-
instanceCount: instances.length,
|
|
67
|
-
|
|
70
|
+
moduleCount: modules.entries.length,
|
|
71
|
+
instanceCount: runtime.instances.length,
|
|
72
|
+
surface,
|
|
68
73
|
});
|
|
69
74
|
|
|
70
75
|
return {
|
|
76
|
+
get instances() { return runtime.instances; },
|
|
77
|
+
get failures() { return runtime.failures; },
|
|
78
|
+
prepare: async(action) => {
|
|
79
|
+
if (!form) throw new Error('Browser modules require a mounted form element.');
|
|
80
|
+
runtime.assertReady();
|
|
81
|
+
runtime.instances.forEach((instance) => instance.assertReady?.());
|
|
82
|
+
let reason: string | undefined;
|
|
83
|
+
const context = {
|
|
84
|
+
form, action: action === 'back' || action === 'save' ? action : 'submit' as const,
|
|
85
|
+
formData: new FormData(form),
|
|
86
|
+
abort: (message?: string) => { reason = message || 'A form feature could not complete.'; },
|
|
87
|
+
isAborted: () => Boolean(reason), abortReason: () => reason,
|
|
88
|
+
};
|
|
89
|
+
const priority = { core: 0, field: 0, address: 0, captcha: 1, payment: 2 };
|
|
90
|
+
for (const instance of [...runtime.instances].sort((a, b) => priority[a.kind ?? 'core'] - priority[b.kind ?? 'core'])) {
|
|
91
|
+
await instance.beforeSubmit?.(context);
|
|
92
|
+
if (reason) throw new Error(reason);
|
|
93
|
+
}
|
|
94
|
+
// Only transient module inputs cross this seam. PHP reads CAPTCHA data
|
|
95
|
+
// and payment fields without allowing arbitrary request/authority overrides.
|
|
96
|
+
return Object.fromEntries(Array.from(form.querySelectorAll<HTMLInputElement>('input[type="hidden"][name]')).map((input) => [input.name, input.value]));
|
|
97
|
+
},
|
|
98
|
+
result: async(result) => {
|
|
99
|
+
if (!form) return;
|
|
100
|
+
const context = { form, action: result.action ?? 'submit' as const, formData: new FormData(form) };
|
|
101
|
+
await bus.emit('formie:submit:result', result);
|
|
102
|
+
for (const instance of runtime.instances) await instance.afterSubmit?.(context, result);
|
|
103
|
+
form.dispatchEvent(new CustomEvent('formie:submit:result', { detail: result, bubbles: true }));
|
|
104
|
+
},
|
|
105
|
+
update: (manifest) => runtime.updateManifest(manifest),
|
|
106
|
+
assertReady: () => { runtime.assertReady(); runtime.instances.forEach((instance) => instance.assertReady?.()); },
|
|
71
107
|
destroy: async() => {
|
|
72
|
-
await
|
|
108
|
+
await runtime.destroy();
|
|
73
109
|
bus.clear();
|
|
74
110
|
},
|
|
75
111
|
on: (eventName, callback) => {
|
|
@@ -89,14 +125,3 @@ export async function hydrateFormieModules(options: FormieModuleHydratorOptions)
|
|
|
89
125
|
},
|
|
90
126
|
};
|
|
91
127
|
}
|
|
92
|
-
|
|
93
|
-
async function destroyModuleInstances(instances: FormieModuleInstance[]): Promise<void> {
|
|
94
|
-
for (const instance of instances) {
|
|
95
|
-
try {
|
|
96
|
-
await instance.destroy();
|
|
97
|
-
} catch (error) {
|
|
98
|
-
console.error('[formie] Failed to destroy module instance.', error);
|
|
99
|
-
debug.warn('Failed destroying module instance.', { error });
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
@@ -99,22 +99,17 @@ function getProgressState(progress: number): 'start' | 'middle' | 'end' {
|
|
|
99
99
|
return 'middle';
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
function
|
|
103
|
-
return (form.dataset.formieSubmitAction || '').trim();
|
|
102
|
+
function getConfiguredCompletionBehavior(form: HTMLFormElement): string {
|
|
103
|
+
return (form.dataset.formieCompletionBehavior || form.dataset.formieSubmitAction || '').trim();
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
function
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (typeof fromResponse === 'string' && fromResponse.trim() !== '') {
|
|
110
|
-
return fromResponse.trim();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return getConfiguredSubmitAction(form);
|
|
106
|
+
function getResolvedCompletionBehavior(form: HTMLFormElement, result: FormSubmitResult): string {
|
|
107
|
+
if (result.completion) return String(result.completion.behavior);
|
|
108
|
+
return getConfiguredCompletionBehavior(form);
|
|
114
109
|
}
|
|
115
110
|
|
|
116
111
|
function shouldHideFormOnSuccess(form: HTMLFormElement): boolean {
|
|
117
|
-
const rawValue = form.dataset.formieSubmitActionFormHide;
|
|
112
|
+
const rawValue = form.dataset.formieHideFormAfterSubmit ?? form.dataset.formieSubmitActionFormHide;
|
|
118
113
|
|
|
119
114
|
if (rawValue === undefined) {
|
|
120
115
|
return false;
|
|
@@ -199,7 +194,7 @@ function applyCompletedProgressState(form: HTMLFormElement): void {
|
|
|
199
194
|
|
|
200
195
|
function scheduleFinalSubmitReset(form: HTMLFormElement): void {
|
|
201
196
|
clearPendingFinalSubmitReset(form);
|
|
202
|
-
const preserveHiddenState =
|
|
197
|
+
const preserveHiddenState = getConfiguredCompletionBehavior(form) === 'message' && shouldHideFormOnSuccess(form);
|
|
203
198
|
|
|
204
199
|
if (DEFAULT_FINAL_SUBMIT_RESET_DELAY_MS < 1) {
|
|
205
200
|
resetSubmissionState(form, { preserveHiddenState });
|
|
@@ -300,12 +295,10 @@ export function clearSubmitLoading(form: HTMLFormElement): void {
|
|
|
300
295
|
button.removeAttribute('data-formie-loading-text');
|
|
301
296
|
});
|
|
302
297
|
|
|
303
|
-
|
|
304
|
-
const formWithValidation = form as FormWithValidationApi;
|
|
298
|
+
const formWithValidation = form as FormWithValidationApi;
|
|
305
299
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
300
|
+
if (formWithValidation.formieValidation) {
|
|
301
|
+
syncSubmitReadiness(form, formWithValidation.formieValidation);
|
|
309
302
|
}
|
|
310
303
|
}
|
|
311
304
|
|
|
@@ -370,27 +363,41 @@ export function applyPageState(form: HTMLFormElement, nextPageId: string): void
|
|
|
370
363
|
setHiddenInputValue(form, 'pageId', nextPageId);
|
|
371
364
|
syncPageTabErrors(form);
|
|
372
365
|
|
|
373
|
-
|
|
374
|
-
const formWithValidation = form as FormWithValidationApi;
|
|
366
|
+
const formWithValidation = form as FormWithValidationApi;
|
|
375
367
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
368
|
+
if (formWithValidation.formieValidation) {
|
|
369
|
+
syncSubmitReadiness(form, formWithValidation.formieValidation);
|
|
379
370
|
}
|
|
380
371
|
}
|
|
381
372
|
|
|
382
373
|
function syncSubmissionIdentity(form: HTMLFormElement, result: FormSubmitResult): void {
|
|
374
|
+
if (result.meta?.resetSubmissionIdentity === true) {
|
|
375
|
+
// Saved spam is terminal. Preserve the entered values for correction,
|
|
376
|
+
// but do not try to continue the rejected record on the next submit.
|
|
377
|
+
removeHiddenInput(form, 'submissionId');
|
|
378
|
+
removeHiddenInput(form, 'submissionUid');
|
|
379
|
+
removeHiddenInput(form, 'progressId');
|
|
380
|
+
setHiddenInputValue(form, 'expectedVersion', '0');
|
|
381
|
+
clearResumeTokenState(form);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const session = result.meta?.session as { version?: number } | undefined;
|
|
386
|
+
const version = session?.version ?? result.meta?.version;
|
|
387
|
+
if (typeof version === 'number') {
|
|
388
|
+
setHiddenInputValue(form, 'expectedVersion', String(version));
|
|
389
|
+
}
|
|
383
390
|
const submissionUid = result.meta?.submissionUid;
|
|
384
391
|
if (typeof submissionUid === 'string' && submissionUid.trim() !== '') {
|
|
385
392
|
setHiddenInputValue(form, 'submissionUid', submissionUid);
|
|
386
393
|
}
|
|
387
394
|
|
|
388
|
-
const
|
|
389
|
-
?.continuation?.
|
|
390
|
-
if (typeof
|
|
391
|
-
setHiddenInputValue(form, '
|
|
395
|
+
const progressId = (result.meta?.session as { continuation?: { progressId?: unknown } } | undefined)
|
|
396
|
+
?.continuation?.progressId;
|
|
397
|
+
if (typeof progressId === 'string' && progressId.trim() !== '') {
|
|
398
|
+
setHiddenInputValue(form, 'progressId', progressId);
|
|
392
399
|
} else {
|
|
393
|
-
removeHiddenInput(form, '
|
|
400
|
+
removeHiddenInput(form, 'progressId');
|
|
394
401
|
}
|
|
395
402
|
}
|
|
396
403
|
|
|
@@ -457,8 +464,9 @@ function resetSubmissionState(form: HTMLFormElement, options: ResetSubmissionSta
|
|
|
457
464
|
setFormHiddenState(form, false);
|
|
458
465
|
}
|
|
459
466
|
removeHiddenInput(form, 'submissionId');
|
|
467
|
+
setHiddenInputValue(form, 'expectedVersion', '0');
|
|
460
468
|
removeHiddenInput(form, 'submissionUid');
|
|
461
|
-
removeHiddenInput(form, '
|
|
469
|
+
removeHiddenInput(form, 'progressId');
|
|
462
470
|
removeHiddenInput(form, 'pageId');
|
|
463
471
|
clearResumeTokenState(form);
|
|
464
472
|
validator?.resetLiveState();
|
|
@@ -508,15 +516,7 @@ function dispatchSubmitDataEvents(form: HTMLFormElement, result: FormSubmitResul
|
|
|
508
516
|
}
|
|
509
517
|
}
|
|
510
518
|
|
|
511
|
-
|
|
512
|
-
// as `paymentAction`/`paymentDecision` metadata without submitData entries.
|
|
513
|
-
const meta = (result.meta || {}) as Record<string, unknown>;
|
|
514
|
-
const paymentAction = (
|
|
515
|
-
(meta.paymentAction && typeof meta.paymentAction === 'object' ? meta.paymentAction : null)
|
|
516
|
-
|| (meta.paymentDecision && typeof meta.paymentDecision === 'object'
|
|
517
|
-
? ((meta.paymentDecision as Record<string, unknown>).action as Record<string, unknown> | null)
|
|
518
|
-
: null)
|
|
519
|
-
) as Record<string, unknown> | null;
|
|
519
|
+
const paymentAction = result.payment?.action;
|
|
520
520
|
|
|
521
521
|
const actionEvent = paymentAction ? String(paymentAction.event || '') : '';
|
|
522
522
|
const actionPayload = paymentAction ? paymentAction.payload : undefined;
|
|
@@ -553,6 +553,12 @@ export function applySubmitResultState(form: HTMLFormElement, result: FormSubmit
|
|
|
553
553
|
return;
|
|
554
554
|
}
|
|
555
555
|
|
|
556
|
+
// Provider handlers may navigate as soon as their action event is dispatched.
|
|
557
|
+
// Treat checkout navigation like the generic redirect before invoking them.
|
|
558
|
+
if (result.payment?.action?.type === 'redirect' && result.payment.action.url) {
|
|
559
|
+
markInternalNavigation(form, 'payment-redirect');
|
|
560
|
+
}
|
|
561
|
+
|
|
556
562
|
// Dispatch payment/redirect events from submitData (e.g. formie:payment:mollie:redirect)
|
|
557
563
|
const eventDispatchResult = dispatchSubmitDataEvents(form, result);
|
|
558
564
|
|
|
@@ -564,7 +570,7 @@ export function applySubmitResultState(form: HTMLFormElement, result: FormSubmit
|
|
|
564
570
|
});
|
|
565
571
|
clearPendingFinalSubmitReset(form);
|
|
566
572
|
if (result.redirect.target === 'new-tab') {
|
|
567
|
-
window.open(result.redirect.url, '_blank');
|
|
573
|
+
window.open(result.redirect.url, '_blank', 'noopener,noreferrer');
|
|
568
574
|
} else {
|
|
569
575
|
markInternalNavigation(form, 'redirect');
|
|
570
576
|
window.location.href = result.redirect.url;
|
|
@@ -609,18 +615,18 @@ export function applySubmitResultState(form: HTMLFormElement, result: FormSubmit
|
|
|
609
615
|
return;
|
|
610
616
|
}
|
|
611
617
|
|
|
612
|
-
if (action === 'submit' && !result.redirect?.url) {
|
|
613
|
-
const
|
|
614
|
-
const preserveHiddenState =
|
|
618
|
+
if (result.completion && action === 'submit' && !result.redirect?.url) {
|
|
619
|
+
const completionBehavior = getResolvedCompletionBehavior(form, result);
|
|
620
|
+
const preserveHiddenState = completionBehavior === 'message' && shouldHideFormOnSuccess(form);
|
|
615
621
|
|
|
616
|
-
if (
|
|
622
|
+
if (completionBehavior === 'reload') {
|
|
617
623
|
clearPendingFinalSubmitReset(form);
|
|
618
624
|
markInternalNavigation(form, 'reload');
|
|
619
625
|
window.location.reload();
|
|
620
626
|
return;
|
|
621
627
|
}
|
|
622
628
|
|
|
623
|
-
if (
|
|
629
|
+
if (completionBehavior === 'reset') {
|
|
624
630
|
resetSubmissionState(form);
|
|
625
631
|
return;
|
|
626
632
|
}
|
|
@@ -633,8 +639,8 @@ export function applySubmitResultState(form: HTMLFormElement, result: FormSubmit
|
|
|
633
639
|
}
|
|
634
640
|
|
|
635
641
|
if (action === 'submit' && result.redirect?.url && result.redirect.target === 'new-tab') {
|
|
636
|
-
const
|
|
637
|
-
const preserveHiddenState =
|
|
642
|
+
const completionBehavior = getResolvedCompletionBehavior(form, result);
|
|
643
|
+
const preserveHiddenState = completionBehavior === 'message' && shouldHideFormOnSuccess(form);
|
|
638
644
|
clearPendingFinalSubmitReset(form);
|
|
639
645
|
resetSubmissionState(form, { preserveHiddenState });
|
|
640
646
|
return;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { FormSubmitResult } from '#contracts/schema';
|
|
2
2
|
import { applyErrorAriaLive, getErrorAriaLivePreference, resolveSubmitErrorAriaLive } from '#core/error-aria-live';
|
|
3
|
+
import {
|
|
4
|
+
appendDescribedBy,
|
|
5
|
+
clearFieldErrorAria,
|
|
6
|
+
setErrorMessageReference,
|
|
7
|
+
} from '#core/field-error-aria';
|
|
3
8
|
import { ensureFieldErrorContainer as resolveFieldErrorContainer } from '#core/field-error-container';
|
|
4
9
|
import { syncPageTabErrors } from '#core/page-tab-errors';
|
|
5
10
|
import { focusFirstValidationError } from '#core/validation-focus';
|
|
@@ -8,8 +13,8 @@ import { addThemeClasses, removeThemeClasses } from '#theme/theme-classes';
|
|
|
8
13
|
|
|
9
14
|
const successHideTimers = new WeakMap<HTMLFormElement, number>();
|
|
10
15
|
|
|
11
|
-
function
|
|
12
|
-
return (form.dataset.formieSubmitAction || '').trim();
|
|
16
|
+
function getConfiguredCompletionBehavior(form: HTMLFormElement): string {
|
|
17
|
+
return (form.dataset.formieCompletionBehavior || form.dataset.formieSubmitAction || '').trim();
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
function getErrorMessagePosition(form: HTMLFormElement): string {
|
|
@@ -17,11 +22,11 @@ function getErrorMessagePosition(form: HTMLFormElement): string {
|
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
function getSuccessMessagePosition(form: HTMLFormElement): string {
|
|
20
|
-
return (form.dataset.formieSubmitActionMessagePosition || '').trim();
|
|
25
|
+
return (form.dataset.formieSuccessMessagePosition || form.dataset.formieSubmitActionMessagePosition || '').trim();
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
function getSuccessMessageTimeoutMs(form: HTMLFormElement): number | null {
|
|
24
|
-
const rawValue = (form.dataset.formieSubmitActionMessageTimeout || '').trim();
|
|
29
|
+
const rawValue = (form.dataset.formieSuccessMessageTimeout || form.dataset.formieSubmitActionMessageTimeout || '').trim();
|
|
25
30
|
|
|
26
31
|
if (!rawValue) {
|
|
27
32
|
return null;
|
|
@@ -37,7 +42,7 @@ function getSuccessMessageTimeoutMs(form: HTMLFormElement): number | null {
|
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
function shouldHideFormOnSuccess(form: HTMLFormElement): boolean {
|
|
40
|
-
const rawValue = form.dataset.formieSubmitActionFormHide;
|
|
45
|
+
const rawValue = form.dataset.formieHideFormAfterSubmit ?? form.dataset.formieSubmitActionFormHide;
|
|
41
46
|
|
|
42
47
|
if (rawValue === undefined) {
|
|
43
48
|
return false;
|
|
@@ -160,40 +165,10 @@ function ensureFieldErrorContainer(fieldNode: Element): HTMLElement {
|
|
|
160
165
|
});
|
|
161
166
|
}
|
|
162
167
|
|
|
163
|
-
function removeDescribedBy(input: HTMLElement, describedById: string): void {
|
|
164
|
-
const current = (input.getAttribute('aria-describedby') || '').trim();
|
|
165
|
-
|
|
166
|
-
if (!current) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const nextValue = current.split(/\s+/).filter((item) => {
|
|
171
|
-
return item !== describedById;
|
|
172
|
-
}).join(' ').trim();
|
|
173
|
-
|
|
174
|
-
if (nextValue) {
|
|
175
|
-
input.setAttribute('aria-describedby', nextValue);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
input.removeAttribute('aria-describedby');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function setErrorMessageReference(input: HTMLElement, errorMessageId: string): void {
|
|
183
|
-
input.setAttribute('aria-errormessage', errorMessageId);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function clearErrorMessageReference(input: HTMLElement, errorMessageId: string): void {
|
|
187
|
-
if (input.getAttribute('aria-errormessage') === errorMessageId) {
|
|
188
|
-
input.removeAttribute('aria-errormessage');
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
168
|
export function clearFieldErrors(form: HTMLFormElement): void {
|
|
193
169
|
form.querySelectorAll('[data-formie-field-handle]').forEach((fieldNode) => {
|
|
194
170
|
const fieldElement = fieldNode as HTMLElement;
|
|
195
171
|
const container = fieldElement.querySelector('[data-formie-field-errors]') as HTMLElement | null;
|
|
196
|
-
const containerId = container?.id || '';
|
|
197
172
|
const errorMessageIds = Array.from(fieldElement.querySelectorAll('[data-formie-field-error]')).map((node) => {
|
|
198
173
|
return (node as HTMLElement).id;
|
|
199
174
|
}).filter(Boolean);
|
|
@@ -214,14 +189,7 @@ export function clearFieldErrors(form: HTMLFormElement): void {
|
|
|
214
189
|
element.removeAttribute('aria-invalid');
|
|
215
190
|
removeThemeClasses(element, form, 'fieldControlError');
|
|
216
191
|
element.removeAttribute('data-formie-input-has-error');
|
|
217
|
-
|
|
218
|
-
if (containerId) {
|
|
219
|
-
removeDescribedBy(element, containerId);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
errorMessageIds.forEach((errorMessageId) => {
|
|
223
|
-
clearErrorMessageReference(element, errorMessageId);
|
|
224
|
-
});
|
|
192
|
+
clearFieldErrorAria(element, errorMessageIds);
|
|
225
193
|
});
|
|
226
194
|
});
|
|
227
195
|
|
|
@@ -275,7 +243,7 @@ export function clearFormSuccess(form: HTMLFormElement): void {
|
|
|
275
243
|
}
|
|
276
244
|
});
|
|
277
245
|
|
|
278
|
-
if (!(
|
|
246
|
+
if (!(getConfiguredCompletionBehavior(form) === 'message' && shouldHideFormOnSuccess(form))) {
|
|
279
247
|
setFormHiddenState(form, false);
|
|
280
248
|
}
|
|
281
249
|
}
|
|
@@ -286,22 +254,13 @@ export function clearAriaInvalid(form: HTMLFormElement): void {
|
|
|
286
254
|
});
|
|
287
255
|
}
|
|
288
256
|
|
|
289
|
-
function appendDescribedBy(input: HTMLElement, describedById: string): void {
|
|
290
|
-
const current = (input.getAttribute('aria-describedby') || '').trim();
|
|
291
|
-
const items = current ? current.split(/\s+/) : [];
|
|
292
|
-
|
|
293
|
-
if (!items.includes(describedById)) {
|
|
294
|
-
items.push(describedById);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
input.setAttribute('aria-describedby', items.join(' ').trim());
|
|
298
|
-
}
|
|
299
|
-
|
|
300
257
|
export function renderFieldErrors(form: HTMLFormElement, fieldErrors: Record<string, string[]>): void {
|
|
301
258
|
const errorAriaLive = resolveSubmitErrorAriaLive(getErrorAriaLivePreference(form));
|
|
302
259
|
|
|
303
260
|
Object.entries(fieldErrors).forEach(([handle, messages]) => {
|
|
304
|
-
const
|
|
261
|
+
const inputName = `fields[${handle.split('.').join('][')}]`;
|
|
262
|
+
const exactInput = form.querySelector(`[name="${CSS.escape(inputName)}"], [name="${CSS.escape(inputName + '[]')}"]`);
|
|
263
|
+
const fieldNode = exactInput?.closest('[data-formie-field-handle]') || form.querySelector(`[data-formie-field-handle="${CSS.escape(handle)}"]`);
|
|
305
264
|
|
|
306
265
|
if (!fieldNode) {
|
|
307
266
|
return;
|
|
@@ -319,7 +278,6 @@ export function renderFieldErrors(form: HTMLFormElement, fieldErrors: Record<str
|
|
|
319
278
|
messages.forEach((message, index) => {
|
|
320
279
|
const errorNode = document.createElement('div');
|
|
321
280
|
errorNode.setAttribute('data-formie-field-error', 'true');
|
|
322
|
-
errorNode.setAttribute('role', 'alert');
|
|
323
281
|
errorNode.id = `${containerId}-${index + 1}`;
|
|
324
282
|
addThemeClasses(errorNode, form, 'fieldError');
|
|
325
283
|
errorNode.textContent = message;
|
|
@@ -327,7 +285,8 @@ export function renderFieldErrors(form: HTMLFormElement, fieldErrors: Record<str
|
|
|
327
285
|
});
|
|
328
286
|
|
|
329
287
|
// Point every control in the field at the first rendered message while
|
|
330
|
-
// still rendering the full list for visual output.
|
|
288
|
+
// still rendering the full list for visual output. Pair aria-errormessage
|
|
289
|
+
// with aria-describedby on that message id (#2946).
|
|
331
290
|
const primaryErrorId = (container.querySelector('[data-formie-field-error]') as HTMLElement | null)?.id;
|
|
332
291
|
|
|
333
292
|
fieldNode.querySelectorAll('input, select, textarea').forEach((input) => {
|
|
@@ -335,7 +294,6 @@ export function renderFieldErrors(form: HTMLFormElement, fieldErrors: Record<str
|
|
|
335
294
|
element.setAttribute('aria-invalid', 'true');
|
|
336
295
|
addThemeClasses(element, form, 'fieldControlError');
|
|
337
296
|
element.setAttribute('data-formie-input-has-error', 'true');
|
|
338
|
-
appendDescribedBy(element, containerId);
|
|
339
297
|
|
|
340
298
|
if (primaryErrorId) {
|
|
341
299
|
setErrorMessageReference(element, primaryErrorId);
|
|
@@ -372,10 +330,9 @@ function isPaymentFollowUpResult(result: FormSubmitResult): boolean {
|
|
|
372
330
|
return false;
|
|
373
331
|
}
|
|
374
332
|
|
|
375
|
-
const
|
|
376
|
-
const paymentStatus = String(meta.paymentStatus || '');
|
|
333
|
+
const paymentStatus = result.payment?.status;
|
|
377
334
|
|
|
378
|
-
return paymentStatus === '
|
|
335
|
+
return paymentStatus === 'requiresAction' || paymentStatus === 'pending' || paymentStatus === 'unknown';
|
|
379
336
|
}
|
|
380
337
|
|
|
381
338
|
export function renderFormNotice(form: HTMLFormElement, message: string): void {
|
|
@@ -403,10 +360,10 @@ function shouldRenderSuccessMessage(form: HTMLFormElement, result: FormSubmitRes
|
|
|
403
360
|
return true;
|
|
404
361
|
}
|
|
405
362
|
|
|
406
|
-
return
|
|
363
|
+
return getConfiguredCompletionBehavior(form) === 'message' && getSuccessMessagePosition(form) !== '';
|
|
407
364
|
}
|
|
408
365
|
|
|
409
|
-
export function renderFormSuccess(form: HTMLFormElement, message: string): void {
|
|
366
|
+
export function renderFormSuccess(form: HTMLFormElement, message: string, options: { hideForm?: boolean } = {}): void {
|
|
410
367
|
const position = getSuccessMessagePosition(form);
|
|
411
368
|
|
|
412
369
|
if (!position) {
|
|
@@ -427,7 +384,7 @@ export function renderFormSuccess(form: HTMLFormElement, message: string): void
|
|
|
427
384
|
successNode.innerHTML = message;
|
|
428
385
|
container.appendChild(successNode);
|
|
429
386
|
|
|
430
|
-
if (shouldHideFormOnSuccess(form)) {
|
|
387
|
+
if (options.hideForm !== false && shouldHideFormOnSuccess(form)) {
|
|
431
388
|
setFormHiddenState(form, true);
|
|
432
389
|
}
|
|
433
390
|
|
|
@@ -451,7 +408,8 @@ export function applySubmitResultUi(form: HTMLFormElement, result: FormSubmitRes
|
|
|
451
408
|
|
|
452
409
|
if (result.ok) {
|
|
453
410
|
if (shouldRenderSuccessMessage(form, result)) {
|
|
454
|
-
|
|
411
|
+
// Saving progress acknowledges persistence without completing the form.
|
|
412
|
+
renderFormSuccess(form, result.message || '', { hideForm: result.action !== 'save' });
|
|
455
413
|
}
|
|
456
414
|
|
|
457
415
|
return;
|
|
@@ -459,8 +417,7 @@ export function applySubmitResultUi(form: HTMLFormElement, result: FormSubmitRes
|
|
|
459
417
|
|
|
460
418
|
if (!result.ok) {
|
|
461
419
|
if (isPaymentFollowUpResult(result)) {
|
|
462
|
-
const
|
|
463
|
-
const message = String(meta.paymentMessage || '').trim();
|
|
420
|
+
const message = String(result.payment?.message || '').trim();
|
|
464
421
|
|
|
465
422
|
if (message) {
|
|
466
423
|
renderFormNotice(form, message);
|