@verbb/formie-browser 1.0.5 → 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.
Files changed (98) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/chunks/{address-finder-yXU5yMjG.js → address-finder-k7_BXaRe.js} +1 -1
  3. package/dist/chunks/{api-DMK8NSUI.js → api-BUb6P-xu.js} +57 -51
  4. package/dist/chunks/{api-CmwLRq_n.js → api-DztsCJ4r.js} +178 -170
  5. package/dist/chunks/{bpoint-DSjqRv63.js → bpoint-2qDxlKD9.js} +1 -1
  6. package/dist/chunks/{calculations-CgVJcQ2P.js → calculations-CeDgB9jt.js} +13 -13
  7. package/dist/chunks/{captcha-eu-CWJb64_j.js → captcha-eu-CGm8Lbrj.js} +2 -2
  8. package/dist/chunks/{conditions-CZ3FU5QO.js → conditions-B6_nzpeE.js} +97 -85
  9. package/dist/chunks/dist-BQ4a9hmy.js +2659 -0
  10. package/dist/chunks/{eway-D3ctTbEk.js → eway-CImXrB4g.js} +2 -2
  11. package/dist/chunks/{friendly-captcha-v1-ByXzENYZ.js → friendly-captcha-v1-C_2reOXr.js} +1 -1
  12. package/dist/chunks/{friendly-captcha-v2-BPdlmhQu.js → friendly-captcha-v2-CLSAbkqx.js} +1 -1
  13. package/dist/chunks/{go-cardless-BwASSYSK.js → go-cardless-Bz7HHtH0.js} +1 -1
  14. package/dist/chunks/{google-address-OhKilPWE.js → google-address-BH3Zhi06.js} +1 -1
  15. package/dist/chunks/{hcaptcha-CdCd9oEi.js → hcaptcha-CGLAmgjO.js} +2 -2
  16. package/dist/chunks/{loqate-Cp2zvwpE.js → loqate-CJPzBmSm.js} +1 -1
  17. package/dist/chunks/{mollie-COG-ko2D.js → mollie-BtnB3KGh.js} +1 -1
  18. package/dist/chunks/{moneris-CMH8PlRc.js → moneris-Bd4vUk8b.js} +1 -1
  19. package/dist/chunks/{opayo-D2jsR5vh.js → opayo-B-eRIXo-.js} +4 -4
  20. package/dist/chunks/{paddle-DxF88RfC.js → paddle-BUFeEU4d.js} +2 -2
  21. package/dist/chunks/{paypal-b5pnwrA2.js → paypal-8Euw7Hrn.js} +2 -2
  22. package/dist/chunks/{payway-BJYu9DIR.js → payway-dV0SzJxy.js} +2 -2
  23. package/dist/chunks/{recaptcha-enterprise-BSwi8IVX.js → recaptcha-enterprise-XPId5-bS.js} +19 -20
  24. package/dist/chunks/{recaptcha-shared-B4zAescW.js → recaptcha-shared-CLYJvojk.js} +15 -3
  25. package/dist/chunks/{recaptcha-v2-checkbox-CnDQmsKa.js → recaptcha-v2-checkbox-Cy-0PEPc.js} +2 -2
  26. package/dist/chunks/{recaptcha-v2-invisible-Cz5uIlp-.js → recaptcha-v2-invisible-BwRH540O.js} +2 -2
  27. package/dist/chunks/recaptcha-v3-No4Ykrhk.js +28 -0
  28. package/dist/chunks/rich-text-D0sb8bmX.js +349 -0
  29. package/dist/chunks/scripts-CbQ7agX3.js +30 -0
  30. package/dist/chunks/{signature-CpgqHCEx.js → signature-xz4QO7CR.js} +107 -34
  31. package/dist/chunks/{snaptcha-vz1RnZ15.js → snaptcha-DKx98oon.js} +1 -1
  32. package/dist/chunks/{square-BP8RWAKB.js → square-reyJukb7.js} +2 -2
  33. package/dist/chunks/{stripe-MihN63uC.js → stripe-BC-2PBvY.js} +2 -2
  34. package/dist/chunks/{text-limit-B09elPDB.js → text-limit-DbgEFHLO.js} +1 -1
  35. package/dist/chunks/{turnstile-D_Pi1CKM.js → turnstile-DJakPzQD.js} +3 -3
  36. package/dist/chunks/upload-manager-BpqWSHmU.js +2558 -0
  37. package/dist/css/theme/_buttons.css +16 -0
  38. package/dist/css/theme/_tokens.css +2 -0
  39. package/dist/css/theme/fields/_signature.css +19 -0
  40. package/dist/css/theme/fields/_upload-manager.css +279 -0
  41. package/dist/index.js +629 -578
  42. package/dist/js/core/create-formie-client.d.ts.map +1 -1
  43. package/dist/js/core/error-aria-live.d.ts +6 -0
  44. package/dist/js/core/error-aria-live.d.ts.map +1 -0
  45. package/dist/js/core/submit-result-state.d.ts.map +1 -1
  46. package/dist/js/core/submit-result-ui.d.ts.map +1 -1
  47. package/dist/js/modules/captchas/factories.d.ts.map +1 -1
  48. package/dist/js/modules/captchas/host.d.ts +1 -0
  49. package/dist/js/modules/captchas/host.d.ts.map +1 -1
  50. package/dist/js/modules/captchas/recaptcha-enterprise.d.ts.map +1 -1
  51. package/dist/js/modules/captchas/recaptcha-shared.d.ts +1 -0
  52. package/dist/js/modules/captchas/recaptcha-shared.d.ts.map +1 -1
  53. package/dist/js/modules/captchas/recaptcha-v3.d.ts.map +1 -1
  54. package/dist/js/modules/captchas/turnstile.d.ts.map +1 -1
  55. package/dist/js/modules/fields/conditions/effects.d.ts.map +1 -1
  56. package/dist/js/modules/fields/index.d.ts.map +1 -1
  57. package/dist/js/modules/fields/signature.d.ts.map +1 -1
  58. package/dist/js/modules/fields/upload-manager.d.ts +3 -0
  59. package/dist/js/modules/fields/upload-manager.d.ts.map +1 -0
  60. package/dist/js/utils/form-started-at.d.ts +2 -0
  61. package/dist/js/utils/form-started-at.d.ts.map +1 -0
  62. package/dist/js/utils/scripts.d.ts.map +1 -1
  63. package/dist/js/utils/unload-warning.d.ts.map +1 -1
  64. package/dist/js/validation/enter-key-guard.d.ts +2 -0
  65. package/dist/js/validation/enter-key-guard.d.ts.map +1 -0
  66. package/dist/js/validation/types.d.ts +1 -0
  67. package/dist/js/validation/types.d.ts.map +1 -1
  68. package/dist/js/validation/validator.d.ts.map +1 -1
  69. package/package.json +4 -2
  70. package/src/css/theme/_buttons.css +16 -0
  71. package/src/css/theme/_tokens.css +2 -0
  72. package/src/css/theme/fields/_signature.css +19 -0
  73. package/src/css/theme/fields/_upload-manager.css +279 -0
  74. package/src/js/core/create-formie-client.ts +7 -0
  75. package/src/js/core/error-aria-live.ts +45 -0
  76. package/src/js/core/submit-result-state.ts +4 -0
  77. package/src/js/core/submit-result-ui.ts +8 -4
  78. package/src/js/modules/captchas/factories.ts +4 -0
  79. package/src/js/modules/captchas/host.ts +14 -2
  80. package/src/js/modules/captchas/recaptcha-enterprise.ts +14 -11
  81. package/src/js/modules/captchas/recaptcha-shared.ts +14 -0
  82. package/src/js/modules/captchas/recaptcha-v3.ts +7 -4
  83. package/src/js/modules/captchas/turnstile.ts +9 -3
  84. package/src/js/modules/fields/conditions/effects.ts +42 -0
  85. package/src/js/modules/fields/index.ts +1 -0
  86. package/src/js/modules/fields/signature.ts +199 -15
  87. package/src/js/modules/fields/upload-manager.ts +1277 -0
  88. package/src/js/utils/form-started-at.ts +25 -0
  89. package/src/js/utils/scripts.ts +16 -0
  90. package/src/js/utils/unload-warning.ts +1 -0
  91. package/src/js/validation/enter-key-guard.ts +63 -0
  92. package/src/js/validation/types.ts +1 -0
  93. package/src/js/validation/validator.ts +14 -5
  94. package/src/vendor.d.ts +1 -1
  95. package/dist/chunks/dist-BF5GU3_9.js +0 -2674
  96. package/dist/chunks/recaptcha-v3-BtbbP111.js +0 -27
  97. package/dist/chunks/rich-text-Cv8ADyk-.js +0 -349
  98. package/dist/chunks/scripts-6CYSUGKQ.js +0 -24
@@ -0,0 +1,25 @@
1
+ const FORM_STARTED_AT_STORAGE_PREFIX = 'formie:formStartedAt:';
2
+
3
+ export function ensureFormStartedAt(form: HTMLFormElement): void {
4
+ const startedAtInput = form.querySelector('input[name="formStartedAt"]') as HTMLInputElement | null;
5
+
6
+ if (!startedAtInput) {
7
+ return;
8
+ }
9
+
10
+ const renderIdInput = form.querySelector('input[name="renderId"]') as HTMLInputElement | null;
11
+ const renderId = renderIdInput?.value?.trim() ?? '';
12
+ const storageKey = renderId ? `${FORM_STARTED_AT_STORAGE_PREFIX}${renderId}` : null;
13
+
14
+ let startedAt = storageKey ? sessionStorage.getItem(storageKey) : null;
15
+
16
+ if (!startedAt) {
17
+ startedAt = String(Date.now());
18
+
19
+ if (storageKey) {
20
+ sessionStorage.setItem(storageKey, startedAt);
21
+ }
22
+ }
23
+
24
+ startedAtInput.value = startedAt;
25
+ }
@@ -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
  };
@@ -4,6 +4,7 @@ const DIRTY_TRACKING_IGNORED_FIELD_NAMES = new Set([
4
4
  'redirect',
5
5
  'requestToken',
6
6
  'renderId',
7
+ 'formStartedAt',
7
8
  'submitAction',
8
9
  'pageId',
9
10
  'draftContextToken',
@@ -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
- errorMessages.setAttribute('aria-live', 'polite');
340
- errorMessages.setAttribute('aria-atomic', 'true');
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 treated as inactive inputs, and hidden
494
- // pages are only re-included on the final page submit pass.
495
- if (element.closest('[data-formie-conditionally-hidden]')) {
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
 
package/src/vendor.d.ts CHANGED
@@ -94,7 +94,7 @@ declare module 'signature_pad' {
94
94
  on(): void;
95
95
  removeEventListener(eventName: string, listener: EventListener): void;
96
96
  toDataURL(type?: string, encoderOptions?: number): string;
97
- fromDataURL(dataUrl: string): void;
97
+ fromDataURL(dataUrl: string, options?: Record<string, unknown>): Promise<void>;
98
98
  isEmpty(): boolean;
99
99
  }
100
100
  }