@verbb/formie-browser 1.0.6 → 1.0.7
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 +11 -0
- package/dist/chunks/{address-finder-yXU5yMjG.js → address-finder-k7_BXaRe.js} +1 -1
- package/dist/chunks/{api-DMK8NSUI.js → api-BUb6P-xu.js} +57 -51
- package/dist/chunks/{api-CmwLRq_n.js → api-DztsCJ4r.js} +178 -170
- package/dist/chunks/{bpoint-DSjqRv63.js → bpoint-2qDxlKD9.js} +1 -1
- package/dist/chunks/{captcha-eu-CWJb64_j.js → captcha-eu-CGm8Lbrj.js} +2 -2
- package/dist/chunks/{conditions-DjZOtHfS.js → conditions-B6_nzpeE.js} +95 -83
- package/dist/chunks/{eway-D3ctTbEk.js → eway-CImXrB4g.js} +2 -2
- package/dist/chunks/{friendly-captcha-v1-ByXzENYZ.js → friendly-captcha-v1-C_2reOXr.js} +1 -1
- package/dist/chunks/{friendly-captcha-v2-BPdlmhQu.js → friendly-captcha-v2-CLSAbkqx.js} +1 -1
- package/dist/chunks/{go-cardless-BwASSYSK.js → go-cardless-Bz7HHtH0.js} +1 -1
- package/dist/chunks/{google-address-OhKilPWE.js → google-address-BH3Zhi06.js} +1 -1
- package/dist/chunks/{hcaptcha-CdCd9oEi.js → hcaptcha-CGLAmgjO.js} +2 -2
- package/dist/chunks/{loqate-Cp2zvwpE.js → loqate-CJPzBmSm.js} +1 -1
- package/dist/chunks/{mollie-COG-ko2D.js → mollie-BtnB3KGh.js} +1 -1
- package/dist/chunks/{moneris-CMH8PlRc.js → moneris-Bd4vUk8b.js} +1 -1
- package/dist/chunks/{opayo-D2jsR5vh.js → opayo-B-eRIXo-.js} +4 -4
- package/dist/chunks/{paddle-DxF88RfC.js → paddle-BUFeEU4d.js} +2 -2
- package/dist/chunks/{paypal-b5pnwrA2.js → paypal-8Euw7Hrn.js} +2 -2
- package/dist/chunks/{payway-BJYu9DIR.js → payway-dV0SzJxy.js} +2 -2
- package/dist/chunks/{recaptcha-enterprise-BSwi8IVX.js → recaptcha-enterprise-XPId5-bS.js} +19 -20
- package/dist/chunks/{recaptcha-shared-B4zAescW.js → recaptcha-shared-CLYJvojk.js} +15 -3
- package/dist/chunks/{recaptcha-v2-checkbox-CnDQmsKa.js → recaptcha-v2-checkbox-Cy-0PEPc.js} +2 -2
- package/dist/chunks/{recaptcha-v2-invisible-Cz5uIlp-.js → recaptcha-v2-invisible-BwRH540O.js} +2 -2
- package/dist/chunks/recaptcha-v3-No4Ykrhk.js +28 -0
- package/dist/chunks/scripts-CbQ7agX3.js +30 -0
- package/dist/chunks/{snaptcha-vz1RnZ15.js → snaptcha-DKx98oon.js} +1 -1
- package/dist/chunks/{square-BP8RWAKB.js → square-reyJukb7.js} +2 -2
- package/dist/chunks/{stripe-MihN63uC.js → stripe-BC-2PBvY.js} +2 -2
- package/dist/chunks/{turnstile-D_Pi1CKM.js → turnstile-DJakPzQD.js} +3 -3
- package/dist/index.js +624 -574
- package/dist/js/core/create-formie-client.d.ts.map +1 -1
- package/dist/js/core/error-aria-live.d.ts +6 -0
- package/dist/js/core/error-aria-live.d.ts.map +1 -0
- package/dist/js/core/submit-result-state.d.ts.map +1 -1
- package/dist/js/core/submit-result-ui.d.ts.map +1 -1
- package/dist/js/modules/captchas/factories.d.ts.map +1 -1
- package/dist/js/modules/captchas/host.d.ts +1 -0
- package/dist/js/modules/captchas/host.d.ts.map +1 -1
- package/dist/js/modules/captchas/recaptcha-enterprise.d.ts.map +1 -1
- package/dist/js/modules/captchas/recaptcha-shared.d.ts +1 -0
- package/dist/js/modules/captchas/recaptcha-shared.d.ts.map +1 -1
- package/dist/js/modules/captchas/recaptcha-v3.d.ts.map +1 -1
- package/dist/js/modules/captchas/turnstile.d.ts.map +1 -1
- package/dist/js/modules/fields/conditions/effects.d.ts.map +1 -1
- package/dist/js/utils/form-started-at.d.ts +2 -0
- package/dist/js/utils/form-started-at.d.ts.map +1 -0
- package/dist/js/utils/scripts.d.ts.map +1 -1
- package/dist/js/utils/unload-warning.d.ts.map +1 -1
- package/dist/js/validation/enter-key-guard.d.ts +2 -0
- package/dist/js/validation/enter-key-guard.d.ts.map +1 -0
- package/dist/js/validation/types.d.ts +1 -0
- package/dist/js/validation/types.d.ts.map +1 -1
- package/dist/js/validation/validator.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/js/core/create-formie-client.ts +7 -0
- package/src/js/core/error-aria-live.ts +45 -0
- package/src/js/core/submit-result-state.ts +4 -0
- package/src/js/core/submit-result-ui.ts +8 -4
- package/src/js/modules/captchas/factories.ts +4 -0
- package/src/js/modules/captchas/host.ts +14 -2
- package/src/js/modules/captchas/recaptcha-enterprise.ts +14 -11
- package/src/js/modules/captchas/recaptcha-shared.ts +14 -0
- package/src/js/modules/captchas/recaptcha-v3.ts +7 -4
- package/src/js/modules/captchas/turnstile.ts +9 -3
- package/src/js/modules/fields/conditions/effects.ts +42 -0
- package/src/js/utils/form-started-at.ts +25 -0
- package/src/js/utils/scripts.ts +16 -0
- package/src/js/utils/unload-warning.ts +1 -0
- package/src/js/validation/enter-key-guard.ts +63 -0
- package/src/js/validation/types.ts +1 -0
- package/src/js/validation/validator.ts +14 -5
- package/dist/chunks/recaptcha-v3-BtbbP111.js +0 -27
- package/dist/chunks/scripts-6CYSUGKQ.js +0 -24
package/src/js/utils/scripts.ts
CHANGED
|
@@ -11,6 +11,16 @@ type LoadExternalScriptOptions = {
|
|
|
11
11
|
|
|
12
12
|
const scriptLoadCache = new Map<string, Promise<HTMLScriptElement>>();
|
|
13
13
|
|
|
14
|
+
function getDocumentCspNonce(): string | null {
|
|
15
|
+
const nonceMeta = document.querySelector('meta[property="csp-nonce"], meta[name="csp-nonce"]') as HTMLMetaElement | null;
|
|
16
|
+
|
|
17
|
+
if (!nonceMeta) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return nonceMeta.nonce || nonceMeta.getAttribute('nonce') || nonceMeta.getAttribute('content') || null;
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
export async function ensureGlobal<T = unknown>(globalName: string, timeoutMs = 5000): Promise<T> {
|
|
15
25
|
// Third-party SDKs often attach themselves to `window` after their script tag
|
|
16
26
|
// loads, so callers wait on the global rather than assuming load == ready.
|
|
@@ -49,6 +59,12 @@ export async function loadExternalScript({
|
|
|
49
59
|
script.src = src;
|
|
50
60
|
script.async = async;
|
|
51
61
|
script.defer = defer;
|
|
62
|
+
const nonce = getDocumentCspNonce();
|
|
63
|
+
|
|
64
|
+
if (nonce) {
|
|
65
|
+
script.setAttribute('nonce', nonce);
|
|
66
|
+
}
|
|
67
|
+
|
|
52
68
|
script.onload = () => {
|
|
53
69
|
resolve(script);
|
|
54
70
|
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { FormAction } from '#contracts/common';
|
|
2
|
+
import { getValidationScope } from '#validation/scope';
|
|
3
|
+
|
|
4
|
+
function isConditionallyInactive(element: Element): boolean {
|
|
5
|
+
return element.hasAttribute('data-formie-conditionally-hidden')
|
|
6
|
+
|| !!element.closest('[data-formie-conditionally-hidden]')
|
|
7
|
+
|| element.hasAttribute('data-formie-page-hidden')
|
|
8
|
+
|| !!element.closest('[data-formie-page-hidden]');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isActionControlVisible(form: HTMLFormElement, action: FormAction): boolean {
|
|
12
|
+
const controls = form.querySelectorAll(`[data-formie-action="${action}"]`);
|
|
13
|
+
|
|
14
|
+
return Array.from(controls).some((control) => {
|
|
15
|
+
return !isConditionallyInactive(control);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolveEnterKeyAction(form: HTMLFormElement): FormAction {
|
|
20
|
+
const { final } = getValidationScope(form);
|
|
21
|
+
|
|
22
|
+
return final ? 'submit' : 'submit';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function shouldBlockEnterSubmit(form: HTMLFormElement): boolean {
|
|
26
|
+
const action = resolveEnterKeyAction(form);
|
|
27
|
+
|
|
28
|
+
return !isActionControlVisible(form, action);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function bindEnterKeyGuard(form: HTMLFormElement): () => void {
|
|
32
|
+
const handler = (event: KeyboardEvent) => {
|
|
33
|
+
if (event.key !== 'Enter' || event.defaultPrevented) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const target = event.target;
|
|
38
|
+
|
|
39
|
+
if (!(target instanceof HTMLInputElement || target instanceof HTMLSelectElement)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (target instanceof HTMLInputElement) {
|
|
44
|
+
if (target.type === 'button' || target.type === 'submit' || target.type === 'reset' || target.type === 'file') {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!shouldBlockEnterSubmit(form)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Browsers can still implicit-submit through hidden/disabled submit
|
|
54
|
+
// controls; block that path when conditions hide the primary action.
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
form.addEventListener('keydown', handler, true);
|
|
59
|
+
|
|
60
|
+
return () => {
|
|
61
|
+
form.removeEventListener('keydown', handler, true);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -15,6 +15,7 @@ export type ValidationRules = Record<string, ValidationRuleValue>;
|
|
|
15
15
|
|
|
16
16
|
export type ValidationConfig = {
|
|
17
17
|
live: boolean;
|
|
18
|
+
errorAriaLive: 'polite' | 'assertive' | 'off';
|
|
18
19
|
errorMessage?: string;
|
|
19
20
|
fieldContainerErrorClass: string[];
|
|
20
21
|
inputErrorClass: string[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ensureFieldErrorContainer } from '#core/field-error-container';
|
|
2
2
|
import { syncPageTabErrors } from '#core/page-tab-errors';
|
|
3
3
|
import rules from '#validation/rules';
|
|
4
|
+
import { applyErrorAriaLive, resolveValidationErrorAriaLive } from '#core/error-aria-live';
|
|
4
5
|
import type {
|
|
5
6
|
ValidationConfig,
|
|
6
7
|
ValidationContext,
|
|
@@ -101,6 +102,7 @@ export class FormieValidator {
|
|
|
101
102
|
this.onInput = this.inputHandler.bind(this);
|
|
102
103
|
this.config = {
|
|
103
104
|
live: false,
|
|
105
|
+
errorAriaLive: 'polite',
|
|
104
106
|
errorMessage: '',
|
|
105
107
|
fieldContainerErrorClass: [],
|
|
106
108
|
inputErrorClass: [],
|
|
@@ -336,8 +338,10 @@ export class FormieValidator {
|
|
|
336
338
|
const handle = fieldContainer.getAttribute('data-formie-field-handle') || 'field';
|
|
337
339
|
const errorId = `${handle}-error`;
|
|
338
340
|
errorMessages.id = errorMessages.id || `${handle}-errors`;
|
|
339
|
-
|
|
340
|
-
|
|
341
|
+
applyErrorAriaLive(
|
|
342
|
+
errorMessages,
|
|
343
|
+
resolveValidationErrorAriaLive(this.config.errorAriaLive, this.submitted),
|
|
344
|
+
);
|
|
341
345
|
|
|
342
346
|
const errorElement = document.createElement('div');
|
|
343
347
|
errorElement.setAttribute('data-formie-field-error', 'true');
|
|
@@ -490,9 +494,14 @@ export class FormieValidator {
|
|
|
490
494
|
}
|
|
491
495
|
|
|
492
496
|
isVisible(element: ValidationInput, options: ValidateOptions = {}): boolean {
|
|
493
|
-
// Conditionally hidden fields are
|
|
494
|
-
// pages are only re-included on the final page submit pass.
|
|
495
|
-
if (
|
|
497
|
+
// Conditionally hidden/disabled fields are inactive for validation, and
|
|
498
|
+
// hidden pages are only re-included on the final page submit pass.
|
|
499
|
+
if (
|
|
500
|
+
element.disabled
|
|
501
|
+
|| element.hasAttribute('data-formie-conditions-disabled')
|
|
502
|
+
|| element.closest('[data-formie-conditions-disabled]')
|
|
503
|
+
|| element.closest('[data-formie-conditionally-hidden]')
|
|
504
|
+
) {
|
|
496
505
|
return false;
|
|
497
506
|
}
|
|
498
507
|
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { t as e } from "./api-DMK8NSUI.js";
|
|
2
|
-
import { t } from "./recaptcha-shared-B4zAescW.js";
|
|
3
|
-
//#region src/js/modules/captchas/recaptcha-v3.ts
|
|
4
|
-
var n = e({
|
|
5
|
-
id: "recaptcha-v3",
|
|
6
|
-
defaultPlaceholderSelector: "[data-recaptcha-placeholder]",
|
|
7
|
-
defaultTokenFieldNames: ["g-recaptcha-response"],
|
|
8
|
-
load: ({ options: e }) => t(e.provider, !1, e.provider.siteKey || void 0),
|
|
9
|
-
mount: ({ api: e, provider: t }) => new Promise((n) => {
|
|
10
|
-
e.ready(() => {
|
|
11
|
-
n(t.siteKey || "recaptcha-v3");
|
|
12
|
-
});
|
|
13
|
-
}),
|
|
14
|
-
screen: async ({ api: e, provider: t, placeholder: n, services: r, stageCtx: i }) => {
|
|
15
|
-
if (r.tokens.has()) return;
|
|
16
|
-
let a = await e.execute(t.siteKey || "", { action: t.action || "submit" });
|
|
17
|
-
if (typeof a == "string" && a.trim() !== "" && r.tokens.write(a.trim()), !await r.tokens.wait(12e4)) {
|
|
18
|
-
let e = r.errors.getDefaultMessage();
|
|
19
|
-
r.errors.show(e, n), i.abort(e);
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
unmount: ({ services: e }) => {
|
|
23
|
-
e.tokens.clear();
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
//#endregion
|
|
27
|
-
export { n as recaptchaV3Module };
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { r as e } from "./async-nPFRNQ06.js";
|
|
2
|
-
//#region src/js/utils/scripts.ts
|
|
3
|
-
var t = /* @__PURE__ */ new Map();
|
|
4
|
-
async function n(t, n = 5e3) {
|
|
5
|
-
return e(() => window[t] ?? null, {
|
|
6
|
-
timeoutMs: n,
|
|
7
|
-
intervalMs: 30
|
|
8
|
-
});
|
|
9
|
-
}
|
|
10
|
-
async function r({ id: e, src: n, async: r = !0, defer: i = !0 }) {
|
|
11
|
-
return document.getElementById(e) || (t.has(e) || t.set(e, new Promise((a, o) => {
|
|
12
|
-
let s = document.createElement("script");
|
|
13
|
-
s.id = e, s.src = n, s.async = r, s.defer = i, s.onload = () => {
|
|
14
|
-
a(s);
|
|
15
|
-
}, s.onerror = () => {
|
|
16
|
-
t.delete(e), o(/* @__PURE__ */ Error(`Failed to load external script: ${n}`));
|
|
17
|
-
}, document.body.appendChild(s);
|
|
18
|
-
})), t.get(e));
|
|
19
|
-
}
|
|
20
|
-
async function i(e, t) {
|
|
21
|
-
return window[e] ?? (await r(t), n(e, t.timeoutMs));
|
|
22
|
-
}
|
|
23
|
-
//#endregion
|
|
24
|
-
export { r as n, i as r, n as t };
|