@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
|
@@ -2,8 +2,8 @@ import flatpickr from 'flatpickr';
|
|
|
2
2
|
import * as flatpickrLocales from 'flatpickr/dist/l10n/index.js';
|
|
3
3
|
import flatpickrCss from 'flatpickr/dist/flatpickr.css?inline';
|
|
4
4
|
|
|
5
|
-
import type {
|
|
6
|
-
import { dispatchFieldEvent, getModuleFieldContainers } from '#modules/fields/shared';
|
|
5
|
+
import type { BrowserModuleDefinition } from '#contracts/modules';
|
|
6
|
+
import { bindConditionalClear, dispatchFieldEvent, getModuleFieldContainers } from '#modules/fields/shared';
|
|
7
7
|
import { ensureModuleStyles } from '#modules/styles';
|
|
8
8
|
import { createDebug } from '#utils/debug';
|
|
9
9
|
|
|
@@ -30,6 +30,8 @@ type DatePickerOptions = {
|
|
|
30
30
|
|
|
31
31
|
type FlatpickrInstanceLike = {
|
|
32
32
|
destroy: () => void;
|
|
33
|
+
selectedDates: Date[];
|
|
34
|
+
setDate: (dates: Date[], triggerChange?: boolean) => void;
|
|
33
35
|
};
|
|
34
36
|
|
|
35
37
|
type FlatpickrInput = HTMLInputElement & {
|
|
@@ -43,6 +45,7 @@ type FlatpickrChangeInstance = {
|
|
|
43
45
|
|
|
44
46
|
function attributesPlugin() {
|
|
45
47
|
return (instance: { input: HTMLInputElement; altInput?: HTMLInputElement | undefined; loadedPlugins: string[] }) => {
|
|
48
|
+
const originalAttributes = new Map<string, string>();
|
|
46
49
|
return {
|
|
47
50
|
onReady: () => {
|
|
48
51
|
if (!instance.altInput) {
|
|
@@ -60,6 +63,7 @@ function attributesPlugin() {
|
|
|
60
63
|
|
|
61
64
|
const value = instance.input.getAttribute(attribute);
|
|
62
65
|
if (value !== null) {
|
|
66
|
+
originalAttributes.set(attribute, value);
|
|
63
67
|
instance.altInput?.setAttribute(attribute, value);
|
|
64
68
|
}
|
|
65
69
|
|
|
@@ -68,6 +72,15 @@ function attributesPlugin() {
|
|
|
68
72
|
|
|
69
73
|
instance.loadedPlugins.push('formie-attributes');
|
|
70
74
|
},
|
|
75
|
+
onDestroy: () => {
|
|
76
|
+
// Conditional fields and page navigation can mount this input again.
|
|
77
|
+
// Restore the markers and labels moved onto the temporary alt input.
|
|
78
|
+
originalAttributes.forEach((value, attribute) => {
|
|
79
|
+
if (!instance.input.hasAttribute(attribute)) {
|
|
80
|
+
instance.input.setAttribute(attribute, value);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
},
|
|
71
84
|
};
|
|
72
85
|
};
|
|
73
86
|
}
|
|
@@ -77,8 +90,9 @@ function normalizeOffsetDate(input: string | null | undefined, type: 'min' | 'ma
|
|
|
77
90
|
return null;
|
|
78
91
|
}
|
|
79
92
|
|
|
80
|
-
|
|
81
|
-
|
|
93
|
+
const absoluteDate = parseSubmissionDate(input);
|
|
94
|
+
if (absoluteDate) {
|
|
95
|
+
return absoluteDate;
|
|
82
96
|
}
|
|
83
97
|
|
|
84
98
|
// Form config may express relative min/max bounds like "+2 weeks", which are
|
|
@@ -196,7 +210,9 @@ function formatSubmissionDate(date: Date, options: DatePickerOptions): string {
|
|
|
196
210
|
}
|
|
197
211
|
|
|
198
212
|
function parseSubmissionDate(value: string): Date | null {
|
|
199
|
-
|
|
213
|
+
// Date-only field values represent a local calendar day. JavaScript otherwise
|
|
214
|
+
// parses YYYY-MM-DD as UTC and shifts the picker to yesterday west of UTC.
|
|
215
|
+
const parsed = new Date(/^\d{4}-\d{2}-\d{2}$/.test(value) ? `${value}T00:00:00` : value);
|
|
200
216
|
|
|
201
217
|
if (Number.isNaN(parsed.getTime())) {
|
|
202
218
|
return null;
|
|
@@ -309,6 +325,19 @@ function initDatePicker(input: FlatpickrInput, options: DatePickerOptions): () =
|
|
|
309
325
|
|
|
310
326
|
const instance = flatpickr(input, mergedOptions) as FlatpickrInstanceLike;
|
|
311
327
|
input._formieFlatpickr = instance;
|
|
328
|
+
const initialDates = instance.selectedDates.map((date) => new Date(date.getTime()));
|
|
329
|
+
const form = input.form;
|
|
330
|
+
const onReset = (event: Event) => {
|
|
331
|
+
// Flatpickr uses hidden transport inputs, whose values survive native reset.
|
|
332
|
+
// Restore the picker and its range inputs together after the native controls.
|
|
333
|
+
queueMicrotask(() => {
|
|
334
|
+
if (!event.defaultPrevented) {
|
|
335
|
+
instance.setDate(initialDates, true);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
};
|
|
339
|
+
form?.addEventListener('reset', onReset);
|
|
340
|
+
const unbindConditionalClear = bindConditionalClear(input, () => instance.setDate([], true));
|
|
312
341
|
debug.log('Initialized.', {
|
|
313
342
|
inputName: input.name,
|
|
314
343
|
isRange,
|
|
@@ -320,6 +349,8 @@ function initDatePicker(input: FlatpickrInput, options: DatePickerOptions): () =
|
|
|
320
349
|
});
|
|
321
350
|
|
|
322
351
|
return () => {
|
|
352
|
+
unbindConditionalClear();
|
|
353
|
+
form?.removeEventListener('reset', onReset);
|
|
323
354
|
instance.destroy();
|
|
324
355
|
delete input._formieFlatpickr;
|
|
325
356
|
debug.log('Destroyed.', {
|
|
@@ -328,8 +359,10 @@ function initDatePicker(input: FlatpickrInput, options: DatePickerOptions): () =
|
|
|
328
359
|
};
|
|
329
360
|
}
|
|
330
361
|
|
|
331
|
-
export const datePickerModule:
|
|
332
|
-
|
|
362
|
+
export const datePickerModule: BrowserModuleDefinition = {
|
|
363
|
+
moduleId: `formie:${MODULE_ID}`,
|
|
364
|
+
version: 2,
|
|
365
|
+
surfaces: ['server-rendered', 'client-rendered', 'cp-edit'],
|
|
333
366
|
kind: 'field',
|
|
334
367
|
match: (ctx) => {
|
|
335
368
|
return !!ctx.target.querySelector(INPUT_SELECTOR);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fileUploadCss from '#theme-css/fields/_file.css?inline';
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { BrowserModuleDefinition } from '#contracts/modules';
|
|
4
4
|
import { dispatchFieldEvent, releaseFormValidators, retainFormValidators } from '#modules/fields/shared';
|
|
5
5
|
import { ensureModuleStyles } from '#modules/styles';
|
|
6
6
|
import { getFieldModuleEventName, getFileUploadEventName, getFormStateEventName } from '#utils/event-names';
|
|
@@ -13,6 +13,7 @@ const FILE_UPLOAD_KEY_ATTR = 'data-formie-file-upload-key';
|
|
|
13
13
|
const HYDRATE_ENDPOINT_ATTR = 'data-formie-file-upload-hydrate-endpoint';
|
|
14
14
|
const HIDDEN_INPUT_ANCHOR_ATTR = 'data-formie-file-upload-anchor';
|
|
15
15
|
const HIDDEN_INPUT_VALUE_ATTR = 'data-formie-file-upload-asset-id';
|
|
16
|
+
const HIDDEN_INPUT_ENTRY_ATTR = 'data-formie-file-upload-entry';
|
|
16
17
|
const FILE_VALIDATORS = [
|
|
17
18
|
'fileLimit',
|
|
18
19
|
'fileSizeMinLimit',
|
|
@@ -29,6 +30,9 @@ type UploadedAsset = {
|
|
|
29
30
|
assetId: number | null;
|
|
30
31
|
filename: string;
|
|
31
32
|
url?: string | null;
|
|
33
|
+
uploadUid?: string | null;
|
|
34
|
+
attachToken?: string | null;
|
|
35
|
+
viewToken?: string | null;
|
|
32
36
|
};
|
|
33
37
|
|
|
34
38
|
type FileUploadHydrateResponse = {
|
|
@@ -93,7 +97,8 @@ function getUploadedAssetInputs(field: HTMLElement, input: HTMLInputElement): HT
|
|
|
93
97
|
const assetInputName = getUploadedAssetInputName(input);
|
|
94
98
|
|
|
95
99
|
return getHiddenInputs(field).filter((hiddenInput) => {
|
|
96
|
-
return hiddenInput.
|
|
100
|
+
return hiddenInput.hasAttribute(HIDDEN_INPUT_VALUE_ATTR)
|
|
101
|
+
|| (hiddenInput.name === assetInputName && hiddenInput.value.trim() !== '');
|
|
97
102
|
});
|
|
98
103
|
}
|
|
99
104
|
|
|
@@ -149,6 +154,9 @@ function normalizeUploadedAsset(candidate: unknown): UploadedAsset | null {
|
|
|
149
154
|
assetId,
|
|
150
155
|
filename,
|
|
151
156
|
url,
|
|
157
|
+
uploadUid: toTrimmedString(candidate.uploadUid) || null,
|
|
158
|
+
attachToken: toTrimmedString(candidate.attachToken) || null,
|
|
159
|
+
viewToken: toTrimmedString(candidate.viewToken ?? candidate.uploadToken) || null,
|
|
152
160
|
};
|
|
153
161
|
}
|
|
154
162
|
|
|
@@ -186,8 +194,11 @@ function readSummaryItems(summaryRoot: HTMLElement | null): UploadedAsset[] {
|
|
|
186
194
|
function readUploadedAssetsFromHiddenInputs(field: HTMLElement, input: HTMLInputElement): UploadedAsset[] {
|
|
187
195
|
return getUploadedAssetInputs(field, input).map((hiddenInput) => {
|
|
188
196
|
return {
|
|
189
|
-
assetId: toPositiveInt(hiddenInput.value),
|
|
197
|
+
assetId: toPositiveInt(hiddenInput.getAttribute(HIDDEN_INPUT_VALUE_ATTR)) ?? toPositiveInt(hiddenInput.value),
|
|
190
198
|
filename: '',
|
|
199
|
+
viewToken: hiddenInput.getAttribute('data-formie-file-upload-token'),
|
|
200
|
+
uploadUid: hiddenInput.name.endsWith('[uploadUid]') ? hiddenInput.value : null,
|
|
201
|
+
attachToken: getHiddenInputs(field).find((entry) => entry.name === hiddenInput.name.replace(/\[uploadUid\]$/, '[attachToken]'))?.value,
|
|
191
202
|
};
|
|
192
203
|
}).filter((asset) => {
|
|
193
204
|
return asset.assetId !== null;
|
|
@@ -201,6 +212,7 @@ function mergeUploadedAssets(assetIds: UploadedAsset[], summaryItems: UploadedAs
|
|
|
201
212
|
|
|
202
213
|
return assetIds.map((asset, index) => {
|
|
203
214
|
return {
|
|
215
|
+
...asset,
|
|
204
216
|
assetId: asset.assetId,
|
|
205
217
|
filename: summaryItems[index]?.filename || '',
|
|
206
218
|
};
|
|
@@ -246,8 +258,8 @@ function registerValidators(form: HTMLFormElement | null): void {
|
|
|
246
258
|
});
|
|
247
259
|
|
|
248
260
|
validator.addValidator('fileSizeMinLimit', ({ input }) => {
|
|
249
|
-
const
|
|
250
|
-
const sizeBytes =
|
|
261
|
+
const maxFileSize = parseFloat(input.getAttribute('data-formie-size-min-limit') || '');
|
|
262
|
+
const sizeBytes = maxFileSize * 1000 * 1000;
|
|
251
263
|
|
|
252
264
|
if (input.type !== 'file' || !sizeBytes || !('files' in input) || !input.files?.length) {
|
|
253
265
|
return true;
|
|
@@ -264,8 +276,8 @@ function registerValidators(form: HTMLFormElement | null): void {
|
|
|
264
276
|
});
|
|
265
277
|
|
|
266
278
|
validator.addValidator('fileSizeMaxLimit', ({ input }) => {
|
|
267
|
-
const
|
|
268
|
-
const sizeBytes =
|
|
279
|
+
const maxFileSize = parseFloat(input.getAttribute('data-formie-size-max-limit') || '');
|
|
280
|
+
const sizeBytes = maxFileSize * 1000 * 1000;
|
|
269
281
|
|
|
270
282
|
if (input.type !== 'file' || !sizeBytes || !('files' in input) || !input.files?.length) {
|
|
271
283
|
return true;
|
|
@@ -349,23 +361,41 @@ function updateUploadedAssetInputs(field: HTMLElement, input: HTMLInputElement,
|
|
|
349
361
|
let insertionPoint: HTMLInputElement = anchor;
|
|
350
362
|
// The upload event returns persisted asset ids; convert those back into the
|
|
351
363
|
// hidden-input shape the backend expects on subsequent submit/save actions.
|
|
352
|
-
const
|
|
353
|
-
|
|
364
|
+
const baseName = getFileValueName(input);
|
|
365
|
+
getHiddenInputs(field).filter((hiddenInput) => {
|
|
366
|
+
return hiddenInput.hasAttribute(HIDDEN_INPUT_ENTRY_ATTR)
|
|
367
|
+
|| (hiddenInput.name === `${baseName}[]` && hiddenInput.value.trim() !== '');
|
|
368
|
+
}).forEach((hiddenInput) => {
|
|
354
369
|
hiddenInput.remove();
|
|
355
370
|
});
|
|
356
371
|
|
|
357
|
-
uploadedAssets.forEach((asset) => {
|
|
372
|
+
uploadedAssets.forEach((asset, index) => {
|
|
358
373
|
if (!asset.assetId) {
|
|
359
374
|
return;
|
|
360
375
|
}
|
|
361
376
|
|
|
362
377
|
const hiddenInput = document.createElement('input');
|
|
363
378
|
hiddenInput.type = 'hidden';
|
|
364
|
-
|
|
365
|
-
hiddenInput.
|
|
366
|
-
hiddenInput.
|
|
379
|
+
const hasCapability = asset.uploadUid && asset.attachToken;
|
|
380
|
+
hiddenInput.name = `${baseName}[${index}][${hasCapability ? 'uploadUid' : 'assetId'}]`;
|
|
381
|
+
hiddenInput.value = hasCapability ? asset.uploadUid! : String(asset.assetId);
|
|
382
|
+
hiddenInput.setAttribute(HIDDEN_INPUT_ENTRY_ATTR, 'true');
|
|
383
|
+
hiddenInput.setAttribute(HIDDEN_INPUT_VALUE_ATTR, String(asset.assetId));
|
|
384
|
+
if (asset.viewToken) {
|
|
385
|
+
hiddenInput.setAttribute('data-formie-file-upload-token', asset.viewToken);
|
|
386
|
+
}
|
|
367
387
|
insertionPoint.insertAdjacentElement('afterend', hiddenInput);
|
|
368
388
|
insertionPoint = hiddenInput;
|
|
389
|
+
|
|
390
|
+
if (hasCapability) {
|
|
391
|
+
const tokenInput = document.createElement('input');
|
|
392
|
+
tokenInput.type = 'hidden';
|
|
393
|
+
tokenInput.name = `${baseName}[${index}][attachToken]`;
|
|
394
|
+
tokenInput.value = asset.attachToken!;
|
|
395
|
+
tokenInput.setAttribute(HIDDEN_INPUT_ENTRY_ATTR, 'true');
|
|
396
|
+
insertionPoint.insertAdjacentElement('afterend', tokenInput);
|
|
397
|
+
insertionPoint = tokenInput;
|
|
398
|
+
}
|
|
369
399
|
});
|
|
370
400
|
|
|
371
401
|
input.value = '';
|
|
@@ -385,7 +415,7 @@ function getFormHandle(form: HTMLFormElement | null): string {
|
|
|
385
415
|
return form.getAttribute('data-formie-handle')?.trim() || '';
|
|
386
416
|
}
|
|
387
417
|
|
|
388
|
-
function buildHydrateFormData(field: HTMLElement, input: HTMLInputElement,
|
|
418
|
+
function buildHydrateFormData(field: HTMLElement, input: HTMLInputElement, assets: UploadedAsset[]): FormData {
|
|
389
419
|
const form = input.form;
|
|
390
420
|
const body = new FormData();
|
|
391
421
|
const handle = getFormHandle(form);
|
|
@@ -405,8 +435,13 @@ function buildHydrateFormData(field: HTMLElement, input: HTMLInputElement, asset
|
|
|
405
435
|
body.append('submissionUid', submissionUidInput.value.trim());
|
|
406
436
|
}
|
|
407
437
|
|
|
408
|
-
|
|
409
|
-
|
|
438
|
+
assets.forEach((asset) => {
|
|
439
|
+
if (asset.assetId) {
|
|
440
|
+
body.append('assetIds[]', String(asset.assetId));
|
|
441
|
+
if (asset.viewToken) {
|
|
442
|
+
body.append(`uploadTokens[${asset.assetId}]`, asset.viewToken);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
410
445
|
});
|
|
411
446
|
|
|
412
447
|
return body;
|
|
@@ -425,8 +460,9 @@ async function hydrateUploadedAssets(field: HTMLElement, input: HTMLInputElement
|
|
|
425
460
|
|
|
426
461
|
const hydrateEndpoint = input.getAttribute(HYDRATE_ENDPOINT_ATTR)?.trim() || '/actions/formie/file-upload/hydrate';
|
|
427
462
|
const response = await requestJson<FileUploadHydrateResponse>(hydrateEndpoint, {
|
|
463
|
+
profile: field.closest('form')?.dataset.formieRequestProfile as import('@verbb/formie-core').BrowserRequestOptions['profile'],
|
|
428
464
|
method: 'POST',
|
|
429
|
-
body: buildHydrateFormData(field, input,
|
|
465
|
+
body: buildHydrateFormData(field, input, uploadedAssets),
|
|
430
466
|
});
|
|
431
467
|
const hydratedAssets = normalizeUploadedAssets(response.assets);
|
|
432
468
|
|
|
@@ -445,6 +481,7 @@ async function hydrateUploadedAssets(field: HTMLElement, input: HTMLInputElement
|
|
|
445
481
|
|
|
446
482
|
const hydratedAsset = hydratedMap.get(asset.assetId)!;
|
|
447
483
|
return {
|
|
484
|
+
...asset,
|
|
448
485
|
assetId: asset.assetId,
|
|
449
486
|
filename: hydratedAsset.filename || asset.filename,
|
|
450
487
|
url: hydratedAsset.url ?? asset.url,
|
|
@@ -473,24 +510,24 @@ function getUploadedAssetsFromEvent(event: Event, matcher: FileUploadEventMatche
|
|
|
473
510
|
|
|
474
511
|
if (detailInputKey !== '' && detailInputKey === matcher.inputKey) {
|
|
475
512
|
matchedByInputKey = true;
|
|
476
|
-
candidates.push(detail.assets, detail.assetIds, detail.data);
|
|
513
|
+
candidates.push(detail.uploadedAssets, detail.assets, detail.assetIds, detail.data);
|
|
477
514
|
}
|
|
478
515
|
|
|
479
516
|
if (detailInputName !== '' && detailInputName === matcher.inputName) {
|
|
480
517
|
matchedByInputName = true;
|
|
481
|
-
candidates.push(detail.assets, detail.assetIds, detail.data);
|
|
518
|
+
candidates.push(detail.uploadedAssets, detail.assets, detail.assetIds, detail.data);
|
|
482
519
|
}
|
|
483
520
|
|
|
484
521
|
if (detailFieldHandle === matcher.fieldHandle) {
|
|
485
522
|
matchedByFieldHandle = true;
|
|
486
|
-
candidates.push(detail.assets, detail.assetIds, detail.data);
|
|
523
|
+
candidates.push(detail.uploadedAssets, detail.assets, detail.assetIds, detail.data);
|
|
487
524
|
}
|
|
488
525
|
|
|
489
526
|
if (nestedData) {
|
|
490
527
|
const nestedAssetCandidates = [
|
|
528
|
+
nestedData.uploadedAssets,
|
|
491
529
|
nestedData.assets,
|
|
492
530
|
nestedData.assetIds,
|
|
493
|
-
nestedData.uploadedAssets,
|
|
494
531
|
nestedData.data,
|
|
495
532
|
];
|
|
496
533
|
|
|
@@ -657,8 +694,10 @@ function bindFileUploadInput(field: HTMLElement, input: HTMLInputElement, form:
|
|
|
657
694
|
};
|
|
658
695
|
}
|
|
659
696
|
|
|
660
|
-
export const fileUploadModule:
|
|
661
|
-
|
|
697
|
+
export const fileUploadModule: BrowserModuleDefinition = {
|
|
698
|
+
moduleId: `formie:${MODULE_ID}`,
|
|
699
|
+
version: 2,
|
|
700
|
+
surfaces: ['server-rendered', 'client-rendered', 'cp-edit'],
|
|
662
701
|
kind: 'field',
|
|
663
702
|
match: (ctx) => {
|
|
664
703
|
return !!ctx.target.querySelector(INPUT_SELECTOR);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BrowserModuleDefinition } from '#contracts/modules';
|
|
2
2
|
import { getModuleFieldContainers } from '#modules/fields/shared';
|
|
3
3
|
|
|
4
4
|
const INPUT_SELECTOR = 'input[data-formie-hidden-input]';
|
|
@@ -22,8 +22,10 @@ function getCookieValue(name: string): string | null {
|
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export const hiddenModule:
|
|
26
|
-
|
|
25
|
+
export const hiddenModule: BrowserModuleDefinition = {
|
|
26
|
+
moduleId: 'formie:hidden',
|
|
27
|
+
version: 2,
|
|
28
|
+
surfaces: ['server-rendered', 'client-rendered', 'cp-edit'],
|
|
27
29
|
kind: 'field',
|
|
28
30
|
match: (ctx) => {
|
|
29
31
|
return !!ctx.target.querySelector(INPUT_SELECTOR);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BrowserModuleDefinition } from '#contracts/modules';
|
|
2
2
|
|
|
3
|
-
export const builtinFieldModuleLoaders: Record<string, () => Promise<
|
|
3
|
+
export const builtinFieldModuleLoaders: Record<string, () => Promise<BrowserModuleDefinition>> = {
|
|
4
4
|
// Keep the builtin map flat and explicit so manifest ids remain the source of
|
|
5
5
|
// truth for lazy-loading first-party field enhancements.
|
|
6
6
|
'calculations': () => import('#modules/fields/calculations').then((module) => module.calculationsModule),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getTextLimitMetrics } from '@verbb/formie-core';
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { BrowserModuleDefinition } from '#contracts/modules';
|
|
4
4
|
import { getModuleFieldTarget, releaseFormValidators, retainFormValidators } from '#modules/fields/shared';
|
|
5
5
|
|
|
6
6
|
const INPUT_SELECTOR = 'input[data-formie-password-input]';
|
|
@@ -104,8 +104,10 @@ function unregisterValidators(form: HTMLFormElement | null): void {
|
|
|
104
104
|
releaseFormValidators(form, VALIDATOR_SCOPE, PASSWORD_VALIDATORS);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
export const passwordValidationModule:
|
|
108
|
-
|
|
107
|
+
export const passwordValidationModule: BrowserModuleDefinition = {
|
|
108
|
+
moduleId: 'formie:password-validation',
|
|
109
|
+
version: 2,
|
|
110
|
+
surfaces: ['server-rendered', 'client-rendered', 'cp-edit'],
|
|
109
111
|
kind: 'field',
|
|
110
112
|
match: (ctx) => {
|
|
111
113
|
return !!ctx.target.querySelector(`${INPUT_SELECTOR}[data-formie-password-min-length], ${INPUT_SELECTOR}[data-formie-password-require-uppercase], ${INPUT_SELECTOR}[data-formie-password-require-lowercase], ${INPUT_SELECTOR}[data-formie-password-require-special-character]`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import intlTelInput from 'intl-tel-input';
|
|
2
2
|
import intlTelInputCss from 'intl-tel-input/styles?inline';
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type { BrowserModuleDefinition } from '#contracts/modules';
|
|
5
5
|
import { dispatchFieldEvent, getModuleFieldContainers, releaseFormValidators, retainFormValidators } from '#modules/fields/shared';
|
|
6
6
|
import { ensureModuleStyles } from '#modules/styles';
|
|
7
7
|
import { createGeoIpLookup } from '#utils/country-from-ip';
|
|
@@ -18,7 +18,7 @@ ensureModuleStyles(MODULE_ID, [intlTelInputCss]);
|
|
|
18
18
|
|
|
19
19
|
type PhoneCountryOptions = {
|
|
20
20
|
countryDefaultValue?: string;
|
|
21
|
-
|
|
21
|
+
allowedCountryCodes?: string[];
|
|
22
22
|
countryPreselectFromIp?: boolean;
|
|
23
23
|
countryFromIpAction?: string;
|
|
24
24
|
language?: string;
|
|
@@ -81,7 +81,7 @@ function unregisterValidators(form: HTMLFormElement | null): void {
|
|
|
81
81
|
releaseFormValidators(form, VALIDATOR_SCOPE, [PHONE_COUNTRY_VALIDATOR]);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function buildOptions(options: PhoneCountryOptions): Record<string, unknown> {
|
|
84
|
+
function buildOptions(options: PhoneCountryOptions, form: HTMLFormElement | null): Record<string, unknown> {
|
|
85
85
|
const intlOptions: Record<string, unknown> = {
|
|
86
86
|
allowDropdown: true,
|
|
87
87
|
nationalMode: false,
|
|
@@ -93,7 +93,7 @@ function buildOptions(options: PhoneCountryOptions): Record<string, unknown> {
|
|
|
93
93
|
loadUtils: () => import('intl-tel-input/utils'),
|
|
94
94
|
};
|
|
95
95
|
|
|
96
|
-
const allowedCountries = (options.
|
|
96
|
+
const allowedCountries = (options.allowedCountryCodes || [])
|
|
97
97
|
.map((item) => {
|
|
98
98
|
return item.toLowerCase();
|
|
99
99
|
})
|
|
@@ -116,7 +116,7 @@ function buildOptions(options: PhoneCountryOptions): Record<string, unknown> {
|
|
|
116
116
|
intlOptions.initialCountry = options.countryDefaultValue.toLowerCase();
|
|
117
117
|
} else if (options.countryPreselectFromIp) {
|
|
118
118
|
intlOptions.initialCountry = 'auto';
|
|
119
|
-
intlOptions.geoIpLookup = createGeoIpLookup(options.countryFromIpAction);
|
|
119
|
+
intlOptions.geoIpLookup = createGeoIpLookup(options.countryFromIpAction, form);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
if (Array.isArray(intlOptions.onlyCountries) && typeof intlOptions.initialCountry === 'string') {
|
|
@@ -130,7 +130,7 @@ function buildOptions(options: PhoneCountryOptions): Record<string, unknown> {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
function initPhoneField(phoneInput: PhoneInput, countryInput: HTMLInputElement, options: PhoneCountryOptions): () => void {
|
|
133
|
-
const intlOptions = buildOptions(options);
|
|
133
|
+
const intlOptions = buildOptions(options, phoneInput.closest('form'));
|
|
134
134
|
|
|
135
135
|
dispatchFieldEvent(phoneInput, MODULE_ID, 'before-init', {
|
|
136
136
|
phoneCountry: phoneInput,
|
|
@@ -163,6 +163,30 @@ function initPhoneField(phoneInput: PhoneInput, countryInput: HTMLInputElement,
|
|
|
163
163
|
phoneInput.addEventListener('countrychange', syncCountry);
|
|
164
164
|
phoneInput.addEventListener('blur', syncCountry);
|
|
165
165
|
syncCountry();
|
|
166
|
+
|
|
167
|
+
// Keep the country dropdown button in sync when the tel input is enabled/disabled after
|
|
168
|
+
// init (multi-page page-hidden, or conditions). intl-tel-input disables the button to match
|
|
169
|
+
// a disabled input at init, but won't re-enable it when `disabled` is later removed.
|
|
170
|
+
const syncCountryButton = () => {
|
|
171
|
+
const button = phoneInput.closest('.iti')?.querySelector('.iti__selected-country');
|
|
172
|
+
|
|
173
|
+
if (!(button instanceof HTMLElement)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (phoneInput.disabled) {
|
|
178
|
+
button.setAttribute('disabled', 'true');
|
|
179
|
+
} else {
|
|
180
|
+
button.removeAttribute('disabled');
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
syncCountryButton();
|
|
185
|
+
const countryButtonObserver = new MutationObserver(() => {
|
|
186
|
+
syncCountryButton();
|
|
187
|
+
});
|
|
188
|
+
countryButtonObserver.observe(phoneInput, { attributes: true, attributeFilter: ['disabled'] });
|
|
189
|
+
|
|
166
190
|
debug.log('Initialized.', {
|
|
167
191
|
inputName: phoneInput.name,
|
|
168
192
|
restrictedCountries: phoneInput.restrictedCountries,
|
|
@@ -177,6 +201,7 @@ function initPhoneField(phoneInput: PhoneInput, countryInput: HTMLInputElement,
|
|
|
177
201
|
return () => {
|
|
178
202
|
phoneInput.removeEventListener('countrychange', syncCountry);
|
|
179
203
|
phoneInput.removeEventListener('blur', syncCountry);
|
|
204
|
+
countryButtonObserver.disconnect();
|
|
180
205
|
validator.destroy();
|
|
181
206
|
delete phoneInput.allowedCountries;
|
|
182
207
|
delete phoneInput.validator;
|
|
@@ -188,8 +213,10 @@ function initPhoneField(phoneInput: PhoneInput, countryInput: HTMLInputElement,
|
|
|
188
213
|
};
|
|
189
214
|
}
|
|
190
215
|
|
|
191
|
-
export const phoneCountryModule:
|
|
192
|
-
|
|
216
|
+
export const phoneCountryModule: BrowserModuleDefinition = {
|
|
217
|
+
moduleId: `formie:${MODULE_ID}`,
|
|
218
|
+
version: 2,
|
|
219
|
+
surfaces: ['server-rendered', 'client-rendered', 'cp-edit'],
|
|
193
220
|
kind: 'field',
|
|
194
221
|
match: (ctx) => {
|
|
195
222
|
return !!ctx.target.querySelector(PHONE_SELECTOR);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import repeaterCss from '#theme-css/fields/_repeater.css?inline';
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { BrowserModuleDefinition } from '#contracts/modules';
|
|
4
4
|
import { dispatchFieldEvent, getTemplateSource, getTemplateSourceHtml } from '#modules/fields/shared';
|
|
5
5
|
import { ensureModuleStyles } from '#modules/styles';
|
|
6
6
|
import { sleep } from '#utils/async';
|
|
@@ -32,6 +32,18 @@ function getRowCount(field: HTMLElement): number {
|
|
|
32
32
|
return field.querySelectorAll(ROW_SELECTOR).length;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
function focusRow(row: Element | null): boolean {
|
|
36
|
+
const control = Array.from(row?.querySelectorAll<HTMLElement>(
|
|
37
|
+
'input:not([type="hidden"]), select, textarea, button, a[href], [role="button"], [role="combobox"], [role="textbox"], [contenteditable="true"]',
|
|
38
|
+
) || []).find((element) => {
|
|
39
|
+
return element.tabIndex >= 0 && !element.matches(':disabled')
|
|
40
|
+
&& !element.closest('[hidden], [inert]') && element.getClientRects().length > 0;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
control?.focus();
|
|
44
|
+
return !!control;
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
function syncAddButton(addButton: HTMLButtonElement | null, rowCount: number): void {
|
|
36
48
|
if (!addButton) {
|
|
37
49
|
return;
|
|
@@ -46,7 +58,7 @@ function syncAddButton(addButton: HTMLButtonElement | null, rowCount: number): v
|
|
|
46
58
|
addButton.disabled = false;
|
|
47
59
|
}
|
|
48
60
|
|
|
49
|
-
function bindRepeaterField(field: HTMLElement): () => void {
|
|
61
|
+
function bindRepeaterField(field: HTMLElement, isCpEdit: boolean): () => void {
|
|
50
62
|
const container = field.matches(CONTAINER_SELECTOR)
|
|
51
63
|
? field
|
|
52
64
|
: field.querySelector(CONTAINER_SELECTOR);
|
|
@@ -57,6 +69,9 @@ function bindRepeaterField(field: HTMLElement): () => void {
|
|
|
57
69
|
return () => {};
|
|
58
70
|
}
|
|
59
71
|
|
|
72
|
+
const form = field.closest('form');
|
|
73
|
+
let disposed = false;
|
|
74
|
+
let generation = 0;
|
|
60
75
|
const removeHandlers = new Map<HTMLElement, EventListener>();
|
|
61
76
|
// Start after the highest existing row id so SSR rows and client-added rows
|
|
62
77
|
// keep producing unique `__ROW__` replacements across one repeater instance.
|
|
@@ -84,8 +99,17 @@ function bindRepeaterField(field: HTMLElement): () => void {
|
|
|
84
99
|
return;
|
|
85
100
|
}
|
|
86
101
|
|
|
102
|
+
const restoreFocus = row.contains(document.activeElement)
|
|
103
|
+
&& (!isCpEdit || (event instanceof MouseEvent && event.detail === 0));
|
|
104
|
+
const nextRow = row.nextElementSibling;
|
|
105
|
+
const previousRow = row.previousElementSibling;
|
|
87
106
|
row.remove();
|
|
88
107
|
syncAddButton(addButton instanceof HTMLButtonElement ? addButton : null, getRowCount(field));
|
|
108
|
+
// Removing the focused control otherwise sends keyboard users back
|
|
109
|
+
// to the document body. Keep them in the nearest surviving row.
|
|
110
|
+
if (restoreFocus && !focusRow(nextRow) && !focusRow(previousRow)) {
|
|
111
|
+
if (addButton instanceof HTMLButtonElement) addButton.focus();
|
|
112
|
+
}
|
|
89
113
|
debug.log('Row removed.', {
|
|
90
114
|
rowCount: getRowCount(field),
|
|
91
115
|
});
|
|
@@ -100,7 +124,7 @@ function bindRepeaterField(field: HTMLElement): () => void {
|
|
|
100
124
|
});
|
|
101
125
|
};
|
|
102
126
|
|
|
103
|
-
const addRow = async() => {
|
|
127
|
+
const addRow = async(focusNewRow = false) => {
|
|
104
128
|
if (!(addButton instanceof HTMLButtonElement)) {
|
|
105
129
|
return;
|
|
106
130
|
}
|
|
@@ -130,11 +154,16 @@ function bindRepeaterField(field: HTMLElement): () => void {
|
|
|
130
154
|
return;
|
|
131
155
|
}
|
|
132
156
|
|
|
157
|
+
const addedGeneration = generation;
|
|
133
158
|
container.appendChild(row);
|
|
134
159
|
// Give downstream field modules a tick to see the new row before we emit
|
|
135
160
|
// the public append/init-row lifecycle events used for nested enhancements.
|
|
136
161
|
await sleep(50);
|
|
162
|
+
if (disposed || addedGeneration !== generation || !row.isConnected) return;
|
|
137
163
|
bindRemoveButtons();
|
|
164
|
+
// Rows are inserted before Add, so leaving focus there skips the new
|
|
165
|
+
// inputs on Tab. Do not steal focus if the user moved on while mounting.
|
|
166
|
+
if (focusNewRow && document.activeElement === addButton) focusRow(row);
|
|
138
167
|
syncAddButton(addButton, getRowCount(field));
|
|
139
168
|
debug.log('Row appended.', {
|
|
140
169
|
rowCount: getRowCount(field),
|
|
@@ -151,7 +180,10 @@ function bindRepeaterField(field: HTMLElement): () => void {
|
|
|
151
180
|
|
|
152
181
|
const addHandler = (event: Event) => {
|
|
153
182
|
event.preventDefault();
|
|
154
|
-
|
|
183
|
+
// Pointer additions in the CP should not focus Craft's field wrappers
|
|
184
|
+
// and show a focus ring around the newly inserted field.
|
|
185
|
+
const focusNewRow = !isCpEdit || (event instanceof MouseEvent && event.detail === 0);
|
|
186
|
+
void addRow(focusNewRow);
|
|
155
187
|
};
|
|
156
188
|
|
|
157
189
|
if (addButton instanceof HTMLButtonElement) {
|
|
@@ -170,6 +202,23 @@ function bindRepeaterField(field: HTMLElement): () => void {
|
|
|
170
202
|
}
|
|
171
203
|
}
|
|
172
204
|
|
|
205
|
+
const initialRows = Array.from(container.children).map((row) => row.cloneNode(true));
|
|
206
|
+
|
|
207
|
+
const onReset = (event: Event) => {
|
|
208
|
+
queueMicrotask(() => {
|
|
209
|
+
if (event.defaultPrevented || disposed) return;
|
|
210
|
+
generation++;
|
|
211
|
+
removeHandlers.forEach((handler, button) => button.removeEventListener('click', handler));
|
|
212
|
+
removeHandlers.clear();
|
|
213
|
+
// Replace rows so removed initial rows return and added rows disappear.
|
|
214
|
+
// The module loader disposes old controls and hydrates the new rows.
|
|
215
|
+
container.replaceChildren(...initialRows.map((row) => row.cloneNode(true)));
|
|
216
|
+
bindRemoveButtons();
|
|
217
|
+
syncAddButton(addButton instanceof HTMLButtonElement ? addButton : null, getRowCount(field));
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
form?.addEventListener('reset', onReset);
|
|
221
|
+
|
|
173
222
|
dispatchFieldEvent(field, MODULE_ID, 'init', {
|
|
174
223
|
repeater: field,
|
|
175
224
|
});
|
|
@@ -178,6 +227,8 @@ function bindRepeaterField(field: HTMLElement): () => void {
|
|
|
178
227
|
});
|
|
179
228
|
|
|
180
229
|
return () => {
|
|
230
|
+
disposed = true;
|
|
231
|
+
form?.removeEventListener('reset', onReset);
|
|
181
232
|
if (addButton instanceof HTMLButtonElement) {
|
|
182
233
|
addButton.removeEventListener('click', addHandler);
|
|
183
234
|
}
|
|
@@ -188,8 +239,10 @@ function bindRepeaterField(field: HTMLElement): () => void {
|
|
|
188
239
|
};
|
|
189
240
|
}
|
|
190
241
|
|
|
191
|
-
export const repeaterModule:
|
|
192
|
-
|
|
242
|
+
export const repeaterModule: BrowserModuleDefinition = {
|
|
243
|
+
moduleId: `formie:${MODULE_ID}`,
|
|
244
|
+
version: 2,
|
|
245
|
+
surfaces: ['server-rendered', 'client-rendered', 'cp-edit'],
|
|
193
246
|
kind: 'field',
|
|
194
247
|
match: (ctx) => {
|
|
195
248
|
return ctx.target instanceof HTMLElement && (
|
|
@@ -209,7 +262,7 @@ export const repeaterModule: FormieModuleDefinition = {
|
|
|
209
262
|
});
|
|
210
263
|
|
|
211
264
|
const destroyBindings = fields.map((field) => {
|
|
212
|
-
return bindRepeaterField(field);
|
|
265
|
+
return bindRepeaterField(field, ctx.surface === 'cp-edit');
|
|
213
266
|
});
|
|
214
267
|
debug.log('Module setup.', { fieldCount: fields.length });
|
|
215
268
|
|