@verbb/formie-browser 4.0.8 → 4.0.9-next.15.g698e7c066059

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.
@@ -0,0 +1,12 @@
1
+ import { FormieValidator } from '#validation/validator';
2
+ export type CraftFormsApp = {
3
+ get: (form: HTMLFormElement) => FormieValidator | null;
4
+ rescan: () => void;
5
+ destroy: () => void;
6
+ };
7
+ /** Enhance Craft action forms without taking ownership of their requests. */
8
+ export declare function craftForms({ root, observe }?: {
9
+ root?: ParentNode;
10
+ observe?: boolean;
11
+ }): CraftFormsApp;
12
+ //# sourceMappingURL=craft-forms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"craft-forms.d.ts","sourceRoot":"","sources":["../../../src/js/core/craft-forms.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAaxD,MAAM,MAAM,aAAa,GAAG;IACxB,GAAG,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,eAAe,GAAG,IAAI,CAAC;IACvD,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAkEF,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,EAAE,IAAe,EAAE,OAAe,EAAE,GAAE;IAC7D,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CAChB,GAAG,aAAa,CAgDrB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verbb/formie-browser",
3
- "version": "4.0.8",
3
+ "version": "4.0.9-next.15.g698e7c066059",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -84,7 +84,8 @@
84
84
  "./vite-dev": "./vite-dev.mjs"
85
85
  },
86
86
  "publishConfig": {
87
- "access": "public"
87
+ "access": "public",
88
+ "tag": "next"
88
89
  },
89
90
  "scripts": {
90
91
  "prebuild": "node ../../scripts/generate-browser-theme-state.mjs",
@@ -101,7 +102,7 @@
101
102
  "@placekit/autocomplete-js": "^2.2.1",
102
103
  "@uppy/core": "^5.2.0",
103
104
  "@uppy/xhr-upload": "^5.2.0",
104
- "@verbb/formie-core": "4.0.8",
105
+ "@verbb/formie-core": "4.0.9-next.15.g698e7c066059",
105
106
  "expression-language": "^2.5.4",
106
107
  "flatpickr": "^4.6.13",
107
108
  "friendly-challenge": "^0.9.19",
@@ -110,5 +111,6 @@
110
111
  "pell": "^1.0.6",
111
112
  "signature_pad": "^5.1.3",
112
113
  "tom-select": "^2.6.1"
113
- }
114
+ },
115
+ "gitHead": "698e7c06605950aac8c2c4f10b57666b325bb500"
114
116
  }
package/src/index.ts CHANGED
@@ -3,6 +3,8 @@ export { hydrateFormieModules } from '#core/hydrate-modules';
3
3
  export { formie } from '#core/formie';
4
4
  export { ModuleRegistry, clientRenderedModuleRegistry } from '#modules/registry';
5
5
  export { FormieValidator } from '#validation/validator';
6
+ export { craftForms } from '#core/craft-forms';
7
+ export type { CraftFormsApp } from '#core/craft-forms';
6
8
  export { bindLegacyDomEventCompatibility } from '#compatibility/dom-adapter';
7
9
  export { bindLegacyValidatorCompatibility } from '#compatibility/validator-adapter';
8
10
  export {
@@ -0,0 +1,139 @@
1
+ import { getErrorAriaLivePreference } from '#core/error-aria-live';
2
+ import { focusFirstValidationError } from '#core/validation-focus';
3
+ import { registerThemeClassMap } from '#theme/theme-classes';
4
+ import { FormieValidator } from '#validation/validator';
5
+ import type { BrowserThemeClassMap } from '#contracts/theme';
6
+
7
+ const SELECTOR = 'form[data-formie-craft-form]:not([data-formie-init="false"])';
8
+
9
+ type CraftFormBinding = {
10
+ validator: FormieValidator;
11
+ owners: Set<symbol>;
12
+ destroy: () => void;
13
+ };
14
+
15
+ const bindings = new WeakMap<HTMLFormElement, CraftFormBinding>();
16
+
17
+ export type CraftFormsApp = {
18
+ get: (form: HTMLFormElement) => FormieValidator | null;
19
+ rescan: () => void;
20
+ destroy: () => void;
21
+ };
22
+
23
+ function bindCraftForm(form: HTMLFormElement): CraftFormBinding {
24
+ const existing = bindings.get(form);
25
+ if (existing) return existing;
26
+
27
+ // Parse before changing native validation so malformed configuration keeps
28
+ // the ordinary Craft form usable. No submission modules or tokens are loaded.
29
+ const theme = JSON.parse(form.dataset.formieThemeClasses || '{}') as BrowserThemeClassMap;
30
+ const classes = registerThemeClassMap(form, theme);
31
+ const originalNoValidate = form.getAttribute('novalidate');
32
+ const validator = new FormieValidator(form, {
33
+ live: form.dataset.formieValidationOnFocus === 'true',
34
+ errorAriaLive: getErrorAriaLivePreference(form),
35
+ fieldContainerErrorClass: classes.fieldLayoutError || [],
36
+ inputErrorClass: classes.fieldControlError || [],
37
+ messagesClass: classes.fieldErrors || [],
38
+ messageClass: classes.fieldError || [],
39
+ });
40
+
41
+ const submit = (event: SubmitEvent): void => {
42
+ const submitter = event.submitter;
43
+ if (event.defaultPrevented || originalNoValidate !== null
44
+ || form.dataset.formieValidationOnSubmit === 'false'
45
+ || submitter?.hasAttribute('formnovalidate')) return;
46
+
47
+ if (validator.submit().length) {
48
+ event.preventDefault();
49
+ focusFirstValidationError(form);
50
+ }
51
+ // A valid form keeps the original submit event and submitter, including
52
+ // its name/value and overrides. Craft handles the response and redirects.
53
+ };
54
+ const reset = (event: Event): void => {
55
+ queueMicrotask(() => {
56
+ if (!event.defaultPrevented && bindings.has(form)) validator.resetLiveState();
57
+ });
58
+ };
59
+ form.addEventListener('submit', submit);
60
+ form.addEventListener('reset', reset);
61
+
62
+ const binding: CraftFormBinding = {
63
+ validator,
64
+ owners: new Set(),
65
+ destroy: () => {
66
+ form.removeEventListener('submit', submit);
67
+ form.removeEventListener('reset', reset);
68
+ validator.removeEventListeners();
69
+ validator.resetLiveState();
70
+ if (originalNoValidate === null) form.removeAttribute('novalidate');
71
+ else form.setAttribute('novalidate', originalNoValidate);
72
+ bindings.delete(form);
73
+ },
74
+ };
75
+ bindings.set(form, binding);
76
+ form.dispatchEvent(new CustomEvent('formie:validator:ready', {
77
+ bubbles: true,
78
+ detail: {
79
+ validator,
80
+ addValidator: validator.addValidator.bind(validator),
81
+ removeValidator: validator.removeValidator.bind(validator),
82
+ },
83
+ }));
84
+ return binding;
85
+ }
86
+
87
+ /** Enhance Craft action forms without taking ownership of their requests. */
88
+ export function craftForms({ root = document, observe = false }: {
89
+ root?: ParentNode;
90
+ observe?: boolean;
91
+ } = {}): CraftFormsApp {
92
+ const owner = Symbol('craftForms');
93
+ const mounted = new Map<HTMLFormElement, CraftFormBinding>();
94
+ let destroyed = false;
95
+
96
+ const release = (form: HTMLFormElement, binding: CraftFormBinding): void => {
97
+ binding.owners.delete(owner);
98
+ if (!binding.owners.size) binding.destroy();
99
+ mounted.delete(form);
100
+ };
101
+ const rescan = (): void => {
102
+ if (destroyed) return;
103
+ const forms = new Set(root.querySelectorAll<HTMLFormElement>(SELECTOR));
104
+ if (root instanceof HTMLFormElement && root.matches(SELECTOR)) forms.add(root);
105
+
106
+ for (const [form, binding] of mounted) {
107
+ if (!forms.has(form)) release(form, binding);
108
+ }
109
+ for (const form of forms) {
110
+ if (mounted.has(form)) continue;
111
+ try {
112
+ const binding = bindCraftForm(form);
113
+ binding.owners.add(owner);
114
+ mounted.set(form, binding);
115
+ } catch (error) {
116
+ // One malformed form must not prevent other forms from mounting.
117
+ console.warn('Formie could not initialize Craft form validation.', error);
118
+ }
119
+ }
120
+ };
121
+ rescan();
122
+ const observer = observe ? new MutationObserver(rescan) : null;
123
+ observer?.observe(root, {
124
+ childList: true,
125
+ subtree: true,
126
+ attributes: true,
127
+ attributeFilter: ['data-formie-craft-form', 'data-formie-init'],
128
+ });
129
+
130
+ return {
131
+ get: (form) => mounted.get(form)?.validator || null,
132
+ rescan,
133
+ destroy: () => {
134
+ destroyed = true;
135
+ observer?.disconnect();
136
+ for (const [form, binding] of mounted) release(form, binding);
137
+ },
138
+ };
139
+ }