@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.
- package/CHANGELOG.md +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +490 -421
- package/dist/js/core/craft-forms.d.ts +12 -0
- package/dist/js/core/craft-forms.d.ts.map +1 -0
- package/package.json +6 -4
- package/src/index.ts +2 -0
- package/src/js/core/craft-forms.ts +139 -0
|
@@ -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.
|
|
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.
|
|
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
|
+
}
|