form-father 0.7.1 → 0.8.2
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 +223 -0
- package/RESPONSE_API.md +233 -0
- package/demos/index.html +198 -0
- package/demos/main.js +149 -0
- package/demos/styles.css +276 -0
- package/dist/FormFather.min.js +2 -0
- package/dist/FormFather.min.js.map +1 -0
- package/{index.d.ts → dist/index.d.ts} +2 -1
- package/dist/index.js +1791 -0
- package/dist/index.js.map +1 -0
- package/docs/api/README.md +153 -0
- package/docs/demo/README.md +37 -0
- package/docs/en/README.md +270 -0
- package/docs/recipes/README.md +150 -0
- package/package.json +47 -33
- package/FormFather.min.js +0 -2
- package/FormFather.min.js.map +0 -1
- package/index.js +0 -2076
- package/index.js.map +0 -1
- /package/{types → dist/types}/adapters.d.ts +0 -0
- /package/{types → dist/types}/helpers.d.ts +0 -0
- /package/{types → dist/types}/index.d.ts +0 -0
- /package/{types → dist/types}/validators.d.ts +0 -0
package/demos/main.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const api = window.FormFather || {};
|
|
2
|
+
const Form = api.default || api.Form || api;
|
|
3
|
+
const { FORM_ERROR_FIELD, createLengthValidator, registerValidator, sameAsField } = api;
|
|
4
|
+
const output = document.querySelector('#demo-output');
|
|
5
|
+
|
|
6
|
+
function writeOutput(title, payload) {
|
|
7
|
+
output.textContent = `${title}\n\n${JSON.stringify(payload, null, 2)}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function json(body, status = 200) {
|
|
11
|
+
return new Response(JSON.stringify(body), {
|
|
12
|
+
status,
|
|
13
|
+
headers: { 'Content-Type': 'application/json' },
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
window.fetch = async (url, options = {}) => {
|
|
18
|
+
const requestUrl = String(url);
|
|
19
|
+
const method = options.method || 'GET';
|
|
20
|
+
|
|
21
|
+
if (requestUrl.startsWith('/demo/login')) {
|
|
22
|
+
return json({
|
|
23
|
+
success: true,
|
|
24
|
+
message: 'Logged in',
|
|
25
|
+
method,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (requestUrl.startsWith('/demo/callback')) {
|
|
30
|
+
return json(
|
|
31
|
+
{
|
|
32
|
+
success: false,
|
|
33
|
+
error: true,
|
|
34
|
+
'error-msg': 'Сервер вернул ошибки по полям.',
|
|
35
|
+
errors: [{ name: 'tel', 'error-msg': 'Этот номер уже есть в заявках' }],
|
|
36
|
+
},
|
|
37
|
+
422,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (requestUrl.startsWith('/demo/search')) {
|
|
42
|
+
return json({
|
|
43
|
+
success: true,
|
|
44
|
+
query: requestUrl,
|
|
45
|
+
results: ['Validation API', 'Server response format', 'Demo recipes'],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (requestUrl.startsWith('/demo/upload')) {
|
|
50
|
+
return json({
|
|
51
|
+
success: true,
|
|
52
|
+
message: 'FormData accepted',
|
|
53
|
+
contentType: options.headers?.['Content-Type'] || 'browser-managed multipart boundary',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (requestUrl.startsWith('/demo/api')) {
|
|
58
|
+
return json({
|
|
59
|
+
success: true,
|
|
60
|
+
message: 'Programmatic API form accepted',
|
|
61
|
+
method,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return json({ success: false, error: true, 'error-msg': `No demo route for ${requestUrl}` }, 404);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
registerValidator('min-length', createLengthValidator({ min: 6 }), 'Слишком короткое значение', { override: true });
|
|
69
|
+
registerValidator(
|
|
70
|
+
'available-email',
|
|
71
|
+
value =>
|
|
72
|
+
new Promise(resolve => {
|
|
73
|
+
setTimeout(() => resolve(value.toLowerCase() !== 'taken@example.com'), 350);
|
|
74
|
+
}),
|
|
75
|
+
'Email is already used',
|
|
76
|
+
{ override: true },
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const forms = Form.initAll('form[data-form-father]', {
|
|
80
|
+
inputSelector: '.input',
|
|
81
|
+
inputWrapperSelector: '.field',
|
|
82
|
+
validateOn: ['blur', 'change'],
|
|
83
|
+
revalidateOn: ['input', 'change'],
|
|
84
|
+
validationDebounce: 120,
|
|
85
|
+
focusFirstErroredInput: true,
|
|
86
|
+
loaderColor: 'currentColor',
|
|
87
|
+
errorSummary: {
|
|
88
|
+
title: 'Проверьте поля формы',
|
|
89
|
+
focus: true,
|
|
90
|
+
},
|
|
91
|
+
formValidators: sameAsField('passwordConfirm', 'password', 'Пароли не совпадают'),
|
|
92
|
+
onValidationError(errors) {
|
|
93
|
+
writeOutput('Client validation errors', errors);
|
|
94
|
+
},
|
|
95
|
+
onResponse(responseBody, form) {
|
|
96
|
+
writeOutput(`${form.$el.dataset.demo || 'form'} response`, responseBody);
|
|
97
|
+
},
|
|
98
|
+
onResponseSuccess(responseBody, form) {
|
|
99
|
+
if (form.$el.dataset.demo === 'login' || form.$el.dataset.demo === 'upload') {
|
|
100
|
+
form.clearInputs();
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const formByDemo = new Map(forms.map(form => [form.$el.dataset.demo, form]));
|
|
106
|
+
const apiForm = formByDemo.get('api');
|
|
107
|
+
|
|
108
|
+
document.querySelectorAll('[data-api-action]').forEach(button => {
|
|
109
|
+
button.addEventListener('click', async () => {
|
|
110
|
+
if (!apiForm) return;
|
|
111
|
+
|
|
112
|
+
switch (button.dataset.apiAction) {
|
|
113
|
+
case 'fill':
|
|
114
|
+
apiForm.setValues({
|
|
115
|
+
name: 'Ada Lovelace',
|
|
116
|
+
email: 'ada@example.com',
|
|
117
|
+
plan: 'team',
|
|
118
|
+
});
|
|
119
|
+
writeOutput('setValues()', apiForm.getValues());
|
|
120
|
+
break;
|
|
121
|
+
|
|
122
|
+
case 'validate-email': {
|
|
123
|
+
const valid = await apiForm.validateField('email');
|
|
124
|
+
writeOutput('validateField("email")', {
|
|
125
|
+
valid,
|
|
126
|
+
errors: apiForm.getErrors(),
|
|
127
|
+
});
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
case 'server-errors':
|
|
132
|
+
apiForm.setErrors({
|
|
133
|
+
email: 'Этот email уже зарегистрирован',
|
|
134
|
+
[FORM_ERROR_FIELD]: 'Сервер попросил проверить форму',
|
|
135
|
+
});
|
|
136
|
+
writeOutput('setErrors()', apiForm.getErrors());
|
|
137
|
+
break;
|
|
138
|
+
|
|
139
|
+
case 'values':
|
|
140
|
+
writeOutput('getValues()', apiForm.getValues());
|
|
141
|
+
break;
|
|
142
|
+
|
|
143
|
+
case 'clear':
|
|
144
|
+
apiForm.clearErrors();
|
|
145
|
+
writeOutput('clearErrors()', apiForm.getErrors());
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
package/demos/styles.css
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color-scheme: light;
|
|
3
|
+
--bg: #f5f7fb;
|
|
4
|
+
--panel: #ffffff;
|
|
5
|
+
--text: #172033;
|
|
6
|
+
--muted: #637083;
|
|
7
|
+
--line: #d9e0ea;
|
|
8
|
+
--accent: #176b87;
|
|
9
|
+
--accent-strong: #0f5268;
|
|
10
|
+
--danger: #b3261e;
|
|
11
|
+
--success: #176b3a;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
* {
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
body {
|
|
19
|
+
margin: 0;
|
|
20
|
+
background: var(--bg);
|
|
21
|
+
color: var(--text);
|
|
22
|
+
font: 16px/1.5 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.demo-header {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: flex-end;
|
|
28
|
+
justify-content: space-between;
|
|
29
|
+
gap: 24px;
|
|
30
|
+
padding: 40px min(6vw, 72px) 24px;
|
|
31
|
+
border-bottom: 1px solid var(--line);
|
|
32
|
+
background: #ffffff;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.demo-header h1,
|
|
36
|
+
.panel-title h2 {
|
|
37
|
+
margin: 0;
|
|
38
|
+
letter-spacing: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.demo-header h1 {
|
|
42
|
+
max-width: 760px;
|
|
43
|
+
font-size: clamp(32px, 4vw, 54px);
|
|
44
|
+
line-height: 1.05;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.eyebrow {
|
|
48
|
+
margin: 0 0 8px;
|
|
49
|
+
color: var(--accent);
|
|
50
|
+
font-size: 13px;
|
|
51
|
+
font-weight: 700;
|
|
52
|
+
letter-spacing: 0;
|
|
53
|
+
text-transform: uppercase;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.github-link,
|
|
57
|
+
button {
|
|
58
|
+
display: inline-flex;
|
|
59
|
+
align-items: center;
|
|
60
|
+
justify-content: center;
|
|
61
|
+
min-height: 42px;
|
|
62
|
+
padding: 0 16px;
|
|
63
|
+
border: 1px solid var(--accent);
|
|
64
|
+
border-radius: 6px;
|
|
65
|
+
background: var(--accent);
|
|
66
|
+
color: #ffffff;
|
|
67
|
+
font: inherit;
|
|
68
|
+
font-weight: 700;
|
|
69
|
+
text-decoration: none;
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.github-link {
|
|
74
|
+
background: #ffffff;
|
|
75
|
+
color: var(--accent);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
button:hover,
|
|
79
|
+
button:focus-visible {
|
|
80
|
+
background: var(--accent-strong);
|
|
81
|
+
border-color: var(--accent-strong);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.github-link:hover,
|
|
85
|
+
.github-link:focus-visible {
|
|
86
|
+
border-color: var(--accent-strong);
|
|
87
|
+
color: var(--accent-strong);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.demo-grid {
|
|
91
|
+
display: grid;
|
|
92
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
93
|
+
gap: 18px;
|
|
94
|
+
padding: 24px min(6vw, 72px);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.demo-panel,
|
|
98
|
+
.demo-output {
|
|
99
|
+
border: 1px solid var(--line);
|
|
100
|
+
border-radius: 8px;
|
|
101
|
+
background: var(--panel);
|
|
102
|
+
padding: 22px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.demo-panel--wide {
|
|
106
|
+
grid-column: 1 / -1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.panel-title {
|
|
110
|
+
margin-bottom: 18px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.panel-title h2 {
|
|
114
|
+
font-size: 22px;
|
|
115
|
+
line-height: 1.2;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
form {
|
|
119
|
+
display: grid;
|
|
120
|
+
gap: 14px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
[data-form-father-summary]:not([hidden]) {
|
|
124
|
+
display: grid;
|
|
125
|
+
gap: 8px;
|
|
126
|
+
padding: 12px;
|
|
127
|
+
border: 1px solid rgba(179, 38, 30, 0.28);
|
|
128
|
+
border-radius: 6px;
|
|
129
|
+
background: rgba(179, 38, 30, 0.06);
|
|
130
|
+
color: var(--danger);
|
|
131
|
+
font-size: 14px;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
[data-form-father-summary-title] {
|
|
135
|
+
margin: 0;
|
|
136
|
+
font-weight: 700;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
[data-form-father-summary-list] {
|
|
140
|
+
display: grid;
|
|
141
|
+
gap: 4px;
|
|
142
|
+
margin: 0;
|
|
143
|
+
padding-left: 18px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
[data-form-father-summary] button {
|
|
147
|
+
min-height: 0;
|
|
148
|
+
padding: 0;
|
|
149
|
+
border: 0;
|
|
150
|
+
background: transparent;
|
|
151
|
+
color: inherit;
|
|
152
|
+
font-weight: 700;
|
|
153
|
+
text-align: left;
|
|
154
|
+
text-decoration: underline;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
[data-form-father-summary] button:hover,
|
|
158
|
+
[data-form-father-summary] button:focus-visible {
|
|
159
|
+
background: transparent;
|
|
160
|
+
color: var(--accent-strong);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.demo-actions {
|
|
164
|
+
display: flex;
|
|
165
|
+
flex-wrap: wrap;
|
|
166
|
+
gap: 8px;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.demo-actions button {
|
|
170
|
+
min-height: 38px;
|
|
171
|
+
padding-inline: 12px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.field {
|
|
175
|
+
display: grid;
|
|
176
|
+
gap: 6px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.field span {
|
|
180
|
+
color: var(--muted);
|
|
181
|
+
font-size: 14px;
|
|
182
|
+
font-weight: 700;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.input {
|
|
186
|
+
width: 100%;
|
|
187
|
+
min-height: 42px;
|
|
188
|
+
padding: 8px 10px;
|
|
189
|
+
border: 1px solid var(--line);
|
|
190
|
+
border-radius: 6px;
|
|
191
|
+
background: #ffffff;
|
|
192
|
+
color: var(--text);
|
|
193
|
+
font: inherit;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.input:focus {
|
|
197
|
+
border-color: var(--accent);
|
|
198
|
+
outline: 3px solid rgba(23, 107, 135, 0.16);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.input[aria-invalid="true"] {
|
|
202
|
+
border-color: var(--danger);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.input[aria-busy="true"] {
|
|
206
|
+
opacity: 0.72;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.input[data-form-father-state="valid"] {
|
|
210
|
+
border-color: var(--success);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.input[data-form-father-state="invalid"] {
|
|
214
|
+
border-color: var(--danger);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
[data-form-father-error],
|
|
218
|
+
.field-error {
|
|
219
|
+
color: var(--danger);
|
|
220
|
+
font-size: 13px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.input__wrapper--error .input {
|
|
224
|
+
border-color: var(--danger);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.button--loading {
|
|
228
|
+
opacity: 0.74;
|
|
229
|
+
pointer-events: none;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.button__loader {
|
|
233
|
+
display: inline-flex;
|
|
234
|
+
width: 18px;
|
|
235
|
+
height: 18px;
|
|
236
|
+
margin-right: 8px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.button__loader svg {
|
|
240
|
+
width: 18px;
|
|
241
|
+
height: 18px;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.error-block-under-input__wrapper {
|
|
245
|
+
color: var(--danger);
|
|
246
|
+
font-size: 14px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.demo-output {
|
|
250
|
+
margin: 0 min(6vw, 72px) 40px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
pre {
|
|
254
|
+
min-height: 130px;
|
|
255
|
+
margin: 0;
|
|
256
|
+
overflow: auto;
|
|
257
|
+
padding: 16px;
|
|
258
|
+
border: 1px solid var(--line);
|
|
259
|
+
border-radius: 8px;
|
|
260
|
+
background: #101820;
|
|
261
|
+
color: #d8f3dc;
|
|
262
|
+
font-size: 13px;
|
|
263
|
+
white-space: pre-wrap;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
@media (max-width: 760px) {
|
|
267
|
+
.demo-header {
|
|
268
|
+
display: grid;
|
|
269
|
+
align-items: start;
|
|
270
|
+
padding-top: 28px;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.demo-grid {
|
|
274
|
+
grid-template-columns: 1fr;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var FormFather=function(t){"use strict";function e(t,e){if(null==t)return null;let r=t;for(;r;){if(r.matches(e))return r;r=r.parentElement}return null}const r=["http:","https:"];function i(t){return t.length>0&&t.length<=63&&/^[a-z\d-]+$/i.test(t)&&!t.startsWith("-")&&!t.endsWith("-")}function s(t){return!!t&&("localhost"===t||function(t){const e=t.split(".");return 4===e.length&&e.every(t=>{if(!/^\d{1,3}$/.test(t))return!1;const e=Number(t);return e>=0&&e<=255})}(t)||function(t){if(!t.startsWith("[")||!t.endsWith("]"))return!1;const e=t.slice(1,-1);return e.includes(":")&&/^[\da-f:.]+$/i.test(e)}(t)||function(t){const e=t.split(".");return e.length>=2&&e.every(i)}(t))}function n(t){const e=function(t){const e=t.trim();return 0===e.length?"":e.startsWith("//")?`https:${e}`:/^[a-z][a-z\d+.-]*:\/\//i.test(e)?e:`https://${e}`}(t);if(0===e.length)return!1;try{const t=new URL(e);return r.includes(t.protocol)&&s(t.hostname)}catch(t){return!1}}function o(t){const e="FORM"===t.tagName?Array.from(t.elements):Array.from(t.querySelectorAll("input, select, textarea, button")),r=new FormData;return e.forEach(t=>{if(!(t instanceof HTMLInputElement||t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement||t instanceof HTMLButtonElement))return;if(!t.name||t.disabled||t.hasAttribute("data-no-serialize"))return;if(t instanceof HTMLButtonElement)return;const{name:e,type:i}=t;if("file"===i)if(t.multiple){const i=t.files;if(i)for(let t=0;t<i.length;t++)r.append(`${e}[${t}]`,i[t])}else{const i=t.files;i&&i[0]&&r.append(e,i[0])}else{let s;if("select-one"===i||"select-multiple"===i){const n=t;if("select-multiple"===i){Array.from(n.selectedOptions).map(t=>t.value).forEach((t,i)=>{r.append(`${e}[${i}]`,t)})}else s=n.value,s&&r.append(e,s)}else if("tel"===i)s=t.value.split(/[\s()-]/).join("").replace(/^8/,"+7");else if("checkbox"===i||"radio"===i){if(!t.checked)return;s=t.value}else if("date"===i){const e=t.getAttribute("data-date-format");s=e?function(t,e){const r=new Date(t),i=t=>t.toString().padStart(2,"0"),s=i(r.getDate()),n=i(r.getMonth()+1),o=r.getFullYear().toString();return e.replace("dd",s).replace("mm",n).replace("yyyy",o)}(t.value,e):t.value}else s=t.value;s&&"select-one"!==i&&"select-multiple"!==i&&r.append(e,s)}}),r}function a(t){if(Object.prototype.hasOwnProperty.call(t,"redirect-url"))if(t["redirect-url-delay"]){const e=Number(t["redirect-url-delay"]);setTimeout(()=>{window.location.href=t["redirect-url"]},e)}else window.location.href=t["redirect-url"];else if(Object.prototype.hasOwnProperty.call(t,"reload")){if(!0===t.reload)if(t["reload-delay"]){const e=Number(t["reload-delay"]);setTimeout(()=>{window.location.reload()},e)}else window.location.reload()}else if(Object.prototype.hasOwnProperty.call(t,"toast")){const e=t.toast;(Array.isArray(e)?e:[e]).forEach(t=>{window.showToast(t)})}else Object.prototype.hasOwnProperty.call(t,"error-toast")&&window.showToast(t["error-toast"])}function l(t){const e={};return new FormData(t).forEach((t,r)=>{e.hasOwnProperty(r)?(Array.isArray(e[r])||(e[r]=[e[r]]),e[r].push(t)):e[r]=t}),e}var c;const u=Symbol.for("FormFather.validators"),d=null!=(c=globalThis[u])?c:globalThis[u]=new Map;function h(t,e,r,{override:i=!1}={}){!d.has(t)||i?d.set(t,{fn:e,defaultMessage:r}):console.warn(`[FormFather] Validator "${t}" already exists; pass { override: true } to replace`)}const m=t=>d.get(t);function f(t,e,r){d.has(t)||d.set(t,{fn:e,defaultMessage:r})}function p(t,e){return"function"==typeof e?e(t):e||function(t){var e;if(!t||"object"!=typeof t)return;const r=t.issues||t.errors;if(Array.isArray(r)){const t=null==(e=r.find(t=>"string"==typeof(null==t?void 0:t.message)))?void 0:e.message;if(t)return t}return"string"==typeof t.message?t.message:void 0}(t)}function g(t,e={}){return async(r,i,s,n)=>{const o=e.mapValue?e.mapValue(r,i,s,n):r;try{if("function"==typeof t)return a=await t(o),l=p(void 0,e.message),"object"==typeof a&&null!==a?a:void 0===a||!!a||{valid:!1,message:l};if("function"==typeof t.safeParse){const r=await t.safeParse(o);return!!r.success||{valid:!1,message:p(r.error||r,e.message)}}if("function"==typeof t.parse)return await t.parse(o),!0}catch(t){return{valid:!1,message:p(t,e.message)}}var a,l;return{valid:!1,message:p(void 0,e.message)}}}function y(t,e){return async(r,i,s,n)=>!!await t(r,i,s,n)||{valid:!1,message:e}}var b;f("required",(t,e,r)=>{if(e&&e.type){if("checkbox"===e.type)return e.checked;if("radio"===e.type){const t=r.querySelectorAll(`input[type="radio"][name="${e.name}"]`);return Array.from(t).some(t=>t.checked)}}return t.trim().length>0},"Пустое значение"),f("email",t=>/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t),"Неверный формат"),f("tel",t=>/^\+7\d{10}$/.test(t),"Неверный формат"),f("url",t=>n(t),"Неверный формат"),f("not-numbers",t=>!/[0-9]/.test(t),"Неверный формат");const v=Symbol.for("formfather.shared"),E=null!=(b=globalThis[v])?b:globalThis[v]={defaultParams:{},defaultValidationSchema:void 0},S="_form";function w(t,e){return t[e]}function A(t){return Array.isArray(t)?0===t.length:null==t||0===String(t).trim().length}function F(t,e){return async r=>!!await t(r)||async function(t,e,r,i){const s="function"==typeof e?await e(t):e;return"string"==typeof s?{field:r,rule:i,message:s}:{field:r,rule:i,...s}}(r,e)}let V=0,$=0;const T=new WeakMap,_="data-form-father-describedby-id",x="data-form-father-added-describedby-id",k="data-form-father-summary",O={tel:{selector:'input[type="tel"]',rules:["tel"]},email:{selector:'input[type="email"]',rules:["email"]},required:{selector:"[required]",rules:["required"]},url:{selector:'input[type="url"]',rules:["url"]},"not-numbers":{selector:'input[data-validate="not-numbers"]',rules:["not-numbers"]}};class L{constructor(t,e={}){this.$submits=[],this.waitResponse=!1,this.inputs=null,this.fieldEventHandlers=[],this.liveValidationTimers=new WeakMap,this.fieldValidationTokens=new WeakMap,this.fieldValidationTokenCounter=0,this.errors=[],this._anim={duration:220,easing:"ease",paddingTopOpened:16,iconDuration:700},this.$el=t,this.options=e;this.config=Object.assign({},{onSubmit:()=>{},onResponse:()=>{},onResponseSuccess:()=>{},onResponseUnsuccess:()=>{},customTypeError:null,loaderColor:"#fff",showLoaderButton:!0,scrollToFirstErroredInput:!0,focusFirstErroredInput:!1,logging:!1,inputSelector:".input",inputWrapperSelector:".input-primary",validateOn:"submit",revalidateOn:["input","change"],validationDebounce:0,errorContainerAttribute:"data-error-container",validationStateAttribute:"data-form-father-state",ariaDescribeErrors:!0,errorIdPrefix:"form-father-error",errorSummary:!1,observeMutations:!1},L.defaultParams,e),this.$el?(this.$submits=this.findSubmitElements(),this.isCorrectArguments()&&(this.bindSubmitHandlers(),this.initialization(),this.bindFieldValidationEvents(),this.setupMutationObserver(),T.set(this.$el,this))):console.warn("Empty $el")}static get defaultParams(){return E.defaultParams}static set defaultParams(t){E.defaultParams=t}static get defaultValidationSchema(){var t;return null!=(t=E.defaultValidationSchema)?t:E.defaultValidationSchema=O}static set defaultValidationSchema(t){E.defaultValidationSchema=t}getOptions(){return this.options}static initAll(t="form[data-form-father]",e={}){return"undefined"==typeof document?[]:Array.from(document.querySelectorAll(t)).filter(t=>"FORM"===t.tagName).map(t=>{const r=T.get(t);return r?(r.updateOptions(e),r):new L(t,e)})}updateOptions(t){return this.options={...this.options,...t},this.config={...this.config,...t},this.refreshControls(),this.bindSubmitHandlers(),this.bindFieldValidationEvents(),this.setupMutationObserver(),this}getErrors(){return this.errors.map(t=>({...t}))}getValues(){return l(this.$el)}normalizeValueList(t){return Array.isArray(t)?t.map(String):[String(t)]}setValues(t){return this.getAllFields().forEach(e=>{if(!e.name||!Object.prototype.hasOwnProperty.call(t,e.name))return;const r=t[e.name];if(!(e instanceof HTMLInputElement&&"file"===e.type)){if(e instanceof HTMLInputElement&&"checkbox"===e.type)e.checked="boolean"==typeof r?r:this.normalizeValueList(r).includes(e.value);else if(e instanceof HTMLInputElement&&"radio"===e.type)e.checked=String(r)===e.value;else if(e instanceof HTMLSelectElement&&e.multiple){const t=this.normalizeValueList(r);Array.from(e.options).forEach(e=>{e.selected=t.includes(e.value)})}else e.value=null==r?"":String(r);e.dispatchEvent(new Event("input",{bubbles:!0})),e.dispatchEvent(new Event("change",{bubbles:!0}))}}),this}clearErrors(){return this.errors=[],this.getAllFields().forEach(t=>{this.hideError(t),this.clearFieldValidationState(t)}),this.hideErrorForm(),this.hideErrorSummary(),this}isErrorResponse(t){return!!t&&"object"==typeof t&&"string"==typeof t.name&&"string"==typeof t["error-msg"]}normalizeValidationIssues(t,e){return null==t||!0===t?[]:!1===t?[{message:"Некорректные данные формы",source:e}]:"string"==typeof t?[{message:t,source:e}]:Array.isArray(t)?t.flatMap(t=>this.normalizeValidationIssues(t,e)):this.isErrorResponse(t)?[{field:t.name,rule:e,message:t["error-msg"],source:e}]:"object"==typeof t&&"message"in t&&"string"==typeof t.message?[{...t,source:t.source||e}]:"object"==typeof t?Object.entries(t).map(([t,r])=>({field:t,rule:e,message:r,source:e})):[]}applyValidationIssue(t,e){const r=t.source||e,i=t.rule||r,s=t.field||S;if(s===S)return this.setErrorRecord(S,i,t.message,r),this.showErrorForm(t.message),null;const n=this.findFieldByName(s);return n?(this.showFieldError(n,t.message,r,i),n):(this.setErrorRecord(s,i,t.message,r),console.warn(`Не найдено поле с именем: ${s}, для вывода ошибки: ${t.message}`),null)}setErrors(t,e="server"){return this.normalizeValidationIssues(t,e).forEach(t=>{this.applyValidationIssue(t,e)}),this.syncErrorSummary(),this}async validateFormValidators(){const t=this.config.formValidators?Array.isArray(this.config.formValidators)?this.config.formValidators:[this.config.formValidators]:[],e=[];let r=!0;for(const i of t){const t=await i({form:this,formElement:this.$el,values:this.getValues(),errors:this.getErrors()}),s=this.normalizeValidationIssues(t,"client");0!==s.length&&(r=!1,s.forEach(t=>{const r=this.applyValidationIssue(t,"client");r&&e.push(r)}))}return{ok:r,erroredInputs:e}}reset(t={}){return this.$el.reset(),this.refreshControls(),!1!==t.clearErrors&&this.clearErrors(),this}static setDefaultParams(t){this.defaultParams={...this.defaultParams,...t}}refreshControls(){this.inputs=this.$el.querySelectorAll(this.config.inputSelector)}findSubmitElements(){return Array.from(this.$el.querySelectorAll('input[type="submit"], input[type="image"], button[type="submit"], button:not([type])'))}bindSubmitHandlers(){this._onSubmitClickHandler||(this._onSubmitClickHandler=async t=>{await this.handleSubmitEvent(t)}),this.$submits.forEach(t=>{t.removeEventListener("click",this._onSubmitClickHandler)}),this.$submits=this.findSubmitElements(),this.$submits.forEach(t=>{t.addEventListener("click",this._onSubmitClickHandler)})}initialization(){this.refreshControls(),this._onSubmitHandler=async t=>{await this.handleSubmitEvent(t)},this.$el.addEventListener("submit",this._onSubmitHandler)}async handleSubmitEvent(t){var e,r,i,s,n,o;t.preventDefault(),null==(r=(e=this.config).onBeforeValidate)||r.call(e,this);const a=await this.validate();return null==(s=(i=this.config).onAfterValidate)||s.call(i,a,this),a?this.submit():(null==(o=(n=this.config).onValidationError)||o.call(n,this.getErrors(),this),{success:!1,error:this.getErrors()})}normalizeTriggers(t,e){const r=null==t?e:Array.isArray(t)?t:[t];return Array.from(new Set(r))}clearFieldValidationEvents(){this.fieldEventHandlers.forEach(({$input:t,event:e,handler:r})=>{t.removeEventListener(e,r)}),this.fieldEventHandlers=[]}bindFieldValidationEvents(){this.clearFieldValidationEvents();const t=this.normalizeTriggers(this.config.validateOn,["submit"]).filter(t=>"submit"!==t),e=this.normalizeTriggers(this.config.revalidateOn,["input","change"]).filter(t=>"submit"!==t),r=Array.from(new Set([...t,...e]));if(0===r.length)return;this.getAllFields().forEach(e=>{r.forEach(r=>{const i=()=>{(t.includes(r)||this.hasFieldError(e))&&this.scheduleFieldValidation(e)};e.addEventListener(r,i),this.fieldEventHandlers.push({$input:e,event:r,handler:i})})})}scheduleFieldValidation(t){const e=this.liveValidationTimers.get(t);e&&clearTimeout(e);const r=Math.max(0,Number(this.config.validationDebounce)||0),i=()=>{this.validateField(t)};if(0===r)return void i();const s=setTimeout(i,r);this.liveValidationTimers.set(t,s)}setupMutationObserver(){var t;null==(t=this.mutationObserver)||t.disconnect(),this.mutationObserver=void 0,this.config.observeMutations&&"undefined"!=typeof MutationObserver&&(this.mutationObserver=new MutationObserver(()=>{this.refreshControls(),this.bindSubmitHandlers(),this.bindFieldValidationEvents()}),this.mutationObserver.observe(this.$el,{childList:!0,subtree:!0}))}getErrorContainer(t){const e=this.config.errorContainerAttribute||"data-error-container",r=t.getAttribute(e);if(!r)return null;try{return document.querySelector(r)}catch(t){return console.warn(`[FormFather] Invalid error container selector "${r}"`),null}}toSafeIdPart(t){return t.trim().toLowerCase().replace(/[^a-z0-9_-]+/gi,"-").replace(/^-+|-+$/g,"")||"field"}createErrorId(t){const e=this.config.errorIdPrefix||"form-father-error";return`${this.toSafeIdPart(e)}-${this.toSafeIdPart(this.getFieldKey(t))}-${++$}`}ensureElementId(t,e){return e.id||(e.id=t.getAttribute(_)||this.createErrorId(t)),e.id}addErrorDescription(t,e){e.hasAttribute("role")||e.setAttribute("role","alert");const r=this.ensureElementId(t,e);if(t.setAttribute(_,r),!1===this.config.ariaDescribeErrors)return;const i=(t.getAttribute("aria-describedby")||"").split(/\s+/).filter(Boolean);i.includes(r)||(t.setAttribute("aria-describedby",[...i,r].join(" ")),t.setAttribute(x,r))}removeErrorDescription(t){const e=t.getAttribute(x);if(e){const r=(t.getAttribute("aria-describedby")||"").split(/\s+/).filter(t=>t&&t!==e);r.length>0?t.setAttribute("aria-describedby",r.join(" ")):t.removeAttribute("aria-describedby")}t.removeAttribute(_),t.removeAttribute(x)}showError(t,r){t.setAttribute("aria-invalid","true");const i=this.getErrorContainer(t);if(i)return i.textContent=r,this.addErrorDescription(t,i),void i.removeAttribute("hidden");const s=e(t,this.config.inputWrapperSelector);if(!s)return void console.warn(`Не найдена обёртка для поля с именем: ${t.name||t.type}`);const n=s.showError;if("function"==typeof n){n.call(s,r);const e=s.querySelector("[data-form-father-error]");return void(e&&this.addErrorDescription(t,e))}s.classList.add("input__wrapper--error");let o=s.querySelector("[data-form-father-error]");o||(o=document.createElement("div"),o.setAttribute("data-form-father-error",""),o.setAttribute("role","alert"),t.insertAdjacentElement("afterend",o)),o.textContent=r,this.addErrorDescription(t,o)}showLoader(){this.$submits.forEach(t=>{if(t.classList.add("button--loading"),t.querySelector(".button__loader"))return;const e="form-father-loader-"+V++,r=`\n\t\t\t\t<svg height="38" viewBox="0 0 38 38" width="38" xmlns="http://www.w3.org/2000/svg">\n\t\t\t\t\t<defs>\n\t\t\t\t\t<linearGradient id="${e}" x1="8.042%" x2="65.682%" y1="0%" y2="23.865%">\n\t\t\t\t\t\t<stop offset="0%" stop-color="${this.config.loaderColor}" stop-opacity="0"/>\n\t\t\t\t\t\t<stop offset="63.146%" stop-color="${this.config.loaderColor}" stop-opacity=".631"/>\n\t\t\t\t\t\t<stop offset="100%" stop-color="${this.config.loaderColor}"/>\n\t\t\t\t\t</linearGradient>\n\t\t\t\t\t</defs>\n\t\t\t\t\t<g fill-rule="evenodd" fill="none">\n\t\t\t\t\t<g transform="translate(1 1)">\n\t\t\t\t\t\t<path d="M36 18c0-9.94-8.06-18-18-18" id="Oval-2" stroke-width="5" stroke="url(#${e})"></path>\n\t\t\t\t\t\t<circle cx="36" cy="18" fill="${this.config.loaderColor}" r="1"></circle>\n\t\t\t\t\t</g>\n\t\t\t\t\t</g>\n\t\t\t\t</svg>\n\t\t\t`,i=document.createElement("span");i.innerHTML=r,i.className="button__loader",t.insertAdjacentElement("afterbegin",i),requestAnimationFrame(()=>{i.setAttribute("data-showed","")})})}hideLoader(){this.$submits.forEach(t=>{t.classList.remove("button--loading");const e=t.querySelector(".button__loader");e&&(e.classList.remove("active"),e.removeAttribute("data-showed"),setTimeout(()=>{e.remove()},250))})}hideError(t){var r;const i=e(t,this.config.inputWrapperSelector)||t.parentElement,s=this.getErrorContainer(t);t.removeAttribute("aria-invalid"),this.removeErrorDescription(t),s&&(s.textContent="",s.setAttribute("hidden","")),null==(r=null==i?void 0:i.querySelector("[data-form-father-error]"))||r.remove(),null==i||i.classList.remove("input__wrapper--error"),i&&i.classList.contains("input__wrapper")&&i.getAttribute("data-type-error")}getErrorSummaryOptions(){return this.config.errorSummary?!0===this.config.errorSummary?{}:this.config.errorSummary:null}getErrorSummaryContainer(t,e){if(t.selector)try{return this.$el.querySelector(t.selector)||document.querySelector(t.selector)}catch(e){return console.warn(`[FormFather] Invalid error summary selector "${t.selector}"`),null}const r=this.$el.querySelector(`[${k}]`);if(r||!e)return r;const i=document.createElement("div");return i.setAttribute(k,""),this.$el.insertAdjacentElement("afterbegin",i),i}prepareErrorSummaryContainer(t){t.setAttribute(k,""),t.hasAttribute("role")||t.setAttribute("role","alert"),t.hasAttribute("tabindex")||t.setAttribute("tabindex","-1"),t.removeAttribute("hidden")}focusErrorSummary(t){try{t.focus({preventScroll:!0})}catch(e){t.focus()}}focusFieldFromSummary(t){"function"==typeof t.scrollIntoView&&t.scrollIntoView({behavior:"smooth",block:"center"});try{t.focus({preventScroll:!0})}catch(e){t.focus()}}renderDefaultErrorSummary(t,e,r){t.textContent="";const i=document.createElement("p");i.setAttribute("data-form-father-summary-title",""),i.textContent=r.title||"Проверьте поля формы";const s=document.createElement("ul");s.setAttribute("data-form-father-summary-list",""),e.forEach(t=>{const e=document.createElement("li"),r=t.field===S?null:this.findFieldByName(t.field);if(r){const i=document.createElement("button");i.type="button",i.setAttribute("data-form-father-summary-field",t.field),i.textContent=t.message,i.addEventListener("click",()=>this.focusFieldFromSummary(r)),e.appendChild(i)}else{const r=document.createElement("span");r.textContent=t.message,e.appendChild(r)}s.appendChild(e)}),t.append(i,s)}renderErrorSummary(t=this.getErrors()){var e;const r=this.getErrorSummaryOptions();if(!r)return;if(0===t.length)return void this.hideErrorSummary();const i=this.getErrorSummaryContainer(r,!0);if(!i)return;this.prepareErrorSummaryContainer(i);const s=null==(e=r.render)?void 0:e.call(r,t.map(t=>({...t})),this);s instanceof HTMLElement?i.replaceChildren(s):"string"==typeof s?i.innerHTML=s:r.render||this.renderDefaultErrorSummary(i,t,r),r.focus&&this.focusErrorSummary(i)}hideErrorSummary(){const t=this.getErrorSummaryOptions()||{},e=this.getErrorSummaryContainer(t,!1);e&&(e.replaceChildren(),e.setAttribute("hidden",""))}syncErrorSummary(){0!==this.errors.length?this.renderErrorSummary():this.hideErrorSummary()}scrollToFirstErroredInput(t){if(t.length>0){const e=Array.from(this.$el.querySelectorAll("input, textarea, select"));let r=null,i=1/0;if(t.forEach(t=>{const s=e.findIndex(e=>e===t);s<i&&(i=s,r=t)}),r){const t=r.getBoundingClientRect(),e=window.pageYOffset||document.documentElement.scrollTop,i=t.top+e-window.innerHeight/2;document.documentElement.setAttribute("data-now-scrolling",""),window.scrollTo({top:i,behavior:"smooth"}),setTimeout(()=>{document.documentElement.removeAttribute("data-now-scrolling")},800)}}}focusFirstErroredInput(t){var e;if(0===t.length)return;const r=Array.from(this.$el.querySelectorAll("input, textarea, select")),i=t.slice().sort((t,e)=>r.indexOf(t)-r.indexOf(e))[0];null==(e=null==i?void 0:i.focus)||e.call(i,{preventScroll:!0})}getValidationSchema(){return{...L.defaultValidationSchema||{},...this.config.validationSchema||{}}}getAllFields(t=this.$el){return Array.from(t.querySelectorAll("input, textarea, select"))}getFieldKey(t){return t.name||t.id||t.getAttribute("data-field")||t.type||"field"}hasFieldError(t){const e=this.getFieldKey(t);return"true"===t.getAttribute("aria-invalid")||this.errors.some(t=>t.field===e)}clearFieldErrorRecord(t){const e=this.getFieldKey(t);this.errors=this.errors.filter(t=>t.field!==e)}setErrorRecord(t,e,r,i){this.errors.push({field:t,rule:e,message:r,source:i})}getValidationStateAttribute(){return this.config.validationStateAttribute||"data-form-father-state"}setFieldValidationState(t,e){const r=this.getValidationStateAttribute();e?t.setAttribute(r,e):t.removeAttribute(r)}startFieldValidation(t){const e=++this.fieldValidationTokenCounter;return this.fieldValidationTokens.set(t,e),t.setAttribute("aria-busy","true"),this.setFieldValidationState(t,"validating"),e}isLatestFieldValidation(t,e){return null==e||this.fieldValidationTokens.get(t)===e}finishFieldValidation(t,e,r){this.isLatestFieldValidation(t,e)&&(t.removeAttribute("aria-busy"),this.setFieldValidationState(t,r?"valid":"invalid"))}clearFieldValidationState(t){this.fieldValidationTokens.delete(t),t.removeAttribute("aria-busy"),this.setFieldValidationState(t,null)}setValidationError(t,e,r,i){this.clearFieldErrorRecord(t),this.setErrorRecord(this.getFieldKey(t),e,r,i)}resolveField(t){return"string"!=typeof t?t:this.findFieldByName(t)}parseRuleString(t){const[e,...r]=t.split(":").map(t=>t.trim());return r.length>0?{rule:e,params:1===r.length?r[0]:r}:e}parseValidationAttribute(t){return(null!=t?t:"").split(/[,\s|]+/).map(t=>t.trim()).filter(Boolean).map(t=>this.parseRuleString(t))}getRuleName(t){return"string"==typeof t?t:t.rule}getRuleIdentity(t){return JSON.stringify(t)}collectRules(t,e=[],r=new Set){const i=[];t.hasAttribute("required")&&i.push("required"),i.push(...e);const s=this.parseValidationAttribute(t.getAttribute("data-validate")).filter(t=>{const e=this.getRuleName(t);return m(e)||!r.has(e)});i.push(...s),i.push(...this.parseValidationAttribute(t.getAttribute("data-custom-validate")));const n=[],o=new Set;for(const t of i){const e=this.getRuleName(t);if(!m(e)){console.warn(`[FormFather] Unknown validation rule "${e}"`);continue}const r=this.getRuleIdentity(t);o.has(r)||(o.add(r),n.push(t))}return n}getRuleMessage(t,e,r,i,s){const n=t.getAttribute(`data-error-${e}`);return s||n||r[e]||i}fieldMatchesSchemaDefinition(t,e,r){if(r.selector)try{return t.matches(r.selector)}catch(t){return console.warn(`[FormFather] Invalid validation selector "${r.selector}"`),!1}return this.parseValidationAttribute(t.getAttribute("data-validate")).some(t=>this.getRuleName(t)===e)}getFieldValidationConfig(t,e=this.getValidationSchema()){const r=[],i={};for(const[s,n]of Object.entries(e))this.fieldMatchesSchemaDefinition(t,s,n)&&(r.push(...n.rules),Object.assign(i,n.messages||{}));return{rules:this.collectRules(t,r,new Set(Object.keys(e))),messages:i}}getValidationFields(t,e){const r=new Set,i=t=>{Array.from(t).forEach(t=>{(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement)&&r.add(t)})};for(const[s,n]of Object.entries(e))if(n.selector)try{i(t.querySelectorAll(n.selector))}catch(t){console.warn(`[FormFather] Invalid validation selector "${n.selector}"`)}else this.getAllFields(t).filter(t=>this.parseValidationAttribute(t.getAttribute("data-validate")).some(t=>this.getRuleName(t)===s)).forEach(t=>r.add(t));return i(t.querySelectorAll("[required], [data-validate], [data-custom-validate]")),Array.from(r)}async validateInput(t,e,r={},i=this.$el,s){if(t.hasAttribute("data-no-validate"))return!0;const n=t instanceof HTMLInputElement&&("checkbox"===t.type||"radio"===t.type)?!t.checked:0===t.value.trim().length,o=e.some(t=>"required"===this.getRuleName(t));if(n&&!o)return this.isLatestFieldValidation(t,s)?(this.clearFieldErrorRecord(t),this.hideError(t),!0):!this.hasFieldError(t);for(const n of e){const{rule:e,params:o}="string"==typeof n?{rule:n,params:void 0}:{rule:n.rule,params:n.params},a=m(e);if(!a)continue;const l=await a.fn(t.value,t,i,o),c="object"==typeof l&&null!==l,u=c?!0===l.valid:!!l;if(!this.isLatestFieldValidation(t,s))return!this.hasFieldError(t);if(c&&l.effect&&await l.effect({value:t.value,$input:t,$form:i,params:o}),!this.isLatestFieldValidation(t,s))return!this.hasFieldError(t);if(!u){const i=l,s=this.getRuleMessage(t,e,r,a.defaultMessage,c&&"string"==typeof i.message?i.message:void 0);return this.setValidationError(t,e,s,"client"),this.showError(t,s),!1}if(c&&l.stopOthers)return!0}return this.isLatestFieldValidation(t,s)?(this.clearFieldErrorRecord(t),this.hideError(t),!0):!this.hasFieldError(t)}async runFieldValidation(t,e,r={},i=this.$el){const s=this.startFieldValidation(t);try{const n=await this.validateInput(t,e,r,i,s);return this.finishFieldValidation(t,s,n),n}catch(e){throw this.isLatestFieldValidation(t,s)&&this.clearFieldValidationState(t),e}}getRadioGroup(t,e){return t instanceof HTMLInputElement&&"radio"===t.type&&t.name?Array.from(e.querySelectorAll('input[type="radio"]')).filter(e=>e.name===t.name):[]}showFieldError(t,e,r="manual",i=r){const s=this.resolveField(t);return s?(this.setValidationError(s,i,e,r),this.showError(s,e),s.removeAttribute("aria-busy"),this.setFieldValidationState(s,"invalid"),this.syncErrorSummary(),!0):(console.warn(`[FormFather] Field "${String(t)}" not found`),!1)}async validateField(t){const e=this.resolveField(t);if(!e)return console.warn(`[FormFather] Field "${String(t)}" not found`),!1;const{rules:r,messages:i}=this.getFieldValidationConfig(e),s=await this.runFieldValidation(e,r,i);return this.syncErrorSummary(),s}async validate(t=this.$el){const e=this.getValidationSchema(),r=[],i=new WeakSet;let s=!0;this.errors=[];for(const n of this.getValidationFields(t,e)){if(i.has(n))continue;const o=this.getRadioGroup(n,t);o.length>0?o.forEach(t=>i.add(t)):i.add(n);const{rules:a,messages:l}=this.getFieldValidationConfig(n,e);await this.runFieldValidation(n,a,l,t)||(s=!1,r.push(n))}const n=await this.validateFormValidators();n.ok||(s=!1,r.push(...n.erroredInputs));const o=r.filter(t=>!t.hasAttribute("data-no-error-scroll"));return!s&&this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(o),!s&&this.config.focusFirstErroredInput&&this.focusFirstErroredInput(o),s?this.hideErrorSummary():this.renderErrorSummary(),s}disableSubmit(){this.$submits.forEach(t=>{t.setAttribute("disabled","")})}enableSubmit(){this.$submits.forEach(t=>{t.removeAttribute("disabled")})}clearInputs(){var t;null==(t=this.inputs)||t.forEach(t=>{var e;const r=t;"radio"===r.type||"checkbox"===r.type?r.checked=!1:(r.value="",null==(e=r.parentElement)||e.classList.remove("filled"),r.dispatchEvent(new Event("input")))})}_prefersReducedMotion(){var t;return"undefined"!=typeof window&&(null==(t=window.matchMedia)?void 0:t.call(window,"(prefers-reduced-motion: reduce)").matches)}_canAnimate(t){return!(!t||!("animate"in t)||this._prefersReducedMotion())}_cancelAnimations(t){var e;const r=t;null==(e=null==r?void 0:r.getAnimations)||e.call(r).forEach(t=>t.cancel())}showErrorForm(t){var e;const r=this.$el.querySelectorAll(this.config.inputWrapperSelector),i=r[r.length-1];let s=this.$el.querySelector(".error-block-under-input__wrapper");!s||i&&i.nextElementSibling!==s?(s=document.createElement("div"),s.className="error-block-under-input__wrapper",s.innerHTML='\n <div class="error-block-under-input error-block-under-input--warning" style="display:grid;grid-template-columns:auto 1fr;gap:8px;align-items:start;">\n <div class="error-block-under-input__icon-wrapper" style="position:relative;width:20px;height:20px;">\n <span class="error-block-under-input__icon" style="display:block;width:20px;height:20px;"></span>\n <span class="error-block-under-input__icon error-block-under-input__icon--animated" style="display:block;position:absolute;inset:0;"></span>\n </div>\n <p class="error-block-under-input__text" style="margin:0;">\n <span class="error-block-under-input__main-text"></span>\n <span class="error-block-under-input__secondary-text"></span>\n </p>\n </div>\n ',Object.assign(s.style,{boxSizing:"border-box",overflow:"hidden",height:"0px",paddingTop:"0px",opacity:"0"}),i?i.insertAdjacentElement("afterend",s):this.$el.appendChild(s)):(s.style.boxSizing="border-box",s.style.overflow="hidden"),s.querySelector(".error-block-under-input__main-text").textContent=t;const n=s.querySelector(".error-block-under-input__secondary-text");n&&(n.textContent=""),null==(e=s.querySelector(".error-block-under-input"))||e.classList.remove("error-block-under-input--success");const o=s.style.height,a=s.style.paddingTop;s.style.height="auto",s.style.paddingTop=`${this._anim.paddingTopOpened}px`;const l=s.clientHeight;if(s.style.height=o||"0px",s.style.paddingTop=a||"0px",parseFloat(s.style.height)===l&&1===parseFloat(s.style.opacity||"1"))this._ringIcon(s);else{if(this._cancelAnimations(s),this._canAnimate(s)){s.animate([{height:`${parseFloat(s.style.height)||0}px`,paddingTop:`${parseFloat(s.style.paddingTop)||0}px`,opacity:parseFloat(s.style.opacity||"0")},{height:`${l}px`,paddingTop:`${this._anim.paddingTopOpened}px`,opacity:1}],{duration:this._anim.duration,easing:this._anim.easing,fill:"forwards"}).addEventListener("finish",()=>{s.style.height="auto",s.style.paddingTop=`${this._anim.paddingTopOpened}px`,s.style.opacity="1"})}else s.style.height="auto",s.style.paddingTop=`${this._anim.paddingTopOpened}px`,s.style.opacity="1";this._ringIcon(s)}this.$el.addEventListener("input",()=>this.hideErrorForm(),{passive:!0,once:!0})}hideErrorForm(){const t=this.$el.querySelectorAll(this.config.inputWrapperSelector),e=t[t.length-1],r=(null==e?void 0:e.nextElementSibling)||this.$el.querySelector(".error-block-under-input__wrapper");if(!r||!r.classList.contains("error-block-under-input__wrapper"))return;if((""===r.style.height||"auto"===r.style.height)&&(r.style.height=`${r.clientHeight}px`),r.style.paddingTop=r.style.paddingTop||`${this._anim.paddingTopOpened}px`,r.style.opacity=r.style.opacity||"1",this._cancelAnimations(r),this._canAnimate(r)){r.animate([{height:`${parseFloat(r.style.height)||r.clientHeight}px`,paddingTop:`${parseFloat(r.style.paddingTop)||this._anim.paddingTopOpened}px`,opacity:parseFloat(r.style.opacity||"1")},{height:"0px",paddingTop:"0px",opacity:0}],{duration:this._anim.duration,easing:this._anim.easing,fill:"forwards"}).addEventListener("finish",()=>{r.remove()})}else r.remove()}_ringIcon(t){const e=t.querySelector(".error-block-under-input__icon--animated");e&&(this._cancelAnimations(e),this._canAnimate(e)&&e.animate([{transform:"rotate(0deg) scale(1)",opacity:0},{transform:"rotate(-8deg) scale(1.05)",opacity:1,offset:.15},{transform:"rotate(8deg) scale(1.05)",opacity:1,offset:.3},{transform:"rotate(-6deg) scale(1.03)",opacity:1,offset:.45},{transform:"rotate(6deg) scale(1.03)",opacity:1,offset:.6},{transform:"rotate(0deg) scale(1)",opacity:0}],{duration:this._anim.iconDuration,easing:"ease"}))}destroy(){var t;this._onSubmitHandler&&(this.$el.removeEventListener("submit",this._onSubmitHandler),this._onSubmitHandler=void 0),this._onSubmitClickHandler&&(this.$submits.forEach(t=>{t.removeEventListener("click",this._onSubmitClickHandler)}),this._onSubmitClickHandler=void 0),this.clearFieldValidationEvents(),null==(t=this.mutationObserver)||t.disconnect(),this.mutationObserver=void 0;const e=this.$el.querySelector(".error-block-under-input__wrapper");e&&(this._cancelAnimations(e),e.remove()),this.hideErrorSummary(),this.$submits.forEach(t=>{t.classList.remove("button--loading");const e=t.querySelector(".button__loader");e&&e.remove()});this.$el.querySelectorAll("input, textarea, select").forEach(t=>{this.hideError(t),this.clearFieldValidationState(t)}),document.documentElement.removeAttribute("data-now-scrolling"),this.inputs=null,this.$submits=[],this.errors=[],this.waitResponse=!1,T.delete(this.$el)}isCorrectArguments(){return"FORM"!==this.$el.tagName?(console.warn("$el не является тегом form"),!1):0!==this.$submits.length||(console.warn("В форме нет кнопки с типом submit"),!1)}formDataToObject(t){const e={};return t.forEach((t,r)=>{Object.prototype.hasOwnProperty.call(e,r)?(Array.isArray(e[r])||(e[r]=[e[r]]),e[r].push(t)):e[r]=t}),e}appendFormDataValue(t,e,r){null!=r&&(Array.isArray(r)?r.forEach((r,i)=>{this.appendFormDataValue(t,`${e}[${i}]`,r)}):r instanceof Blob?t.append(e,r):t.append(e,"object"==typeof r?JSON.stringify(r):String(r)))}objectToFormData(t){const e=new FormData;return Object.entries(t).forEach(([t,r])=>{this.appendFormDataValue(e,t,r)}),e}stringifyFormDataValue(t){return"string"==typeof t?t:t.name}formDataToUrlSearchParams(t){const e=new URLSearchParams;return t.forEach((t,r)=>{e.append(r,this.stringifyFormDataValue(t))}),e}formDataToTextBody(t){return Array.from(t).map(([t,e])=>`${encodeURIComponent(t)}=${encodeURIComponent(this.stringifyFormDataValue(e))}`).join("\n")}isBodylessMethod(t){return"GET"===t||"HEAD"===t}appendQueryString(t,e){const r=e.toString();if(!r)return t;const i=t.includes("?")?"&":"?";return`${t}${i}${r}`}findFieldByName(t){return Array.from(this.$el.querySelectorAll("input, textarea, select")).find(e=>e.name===t)||null}async parseResponseBody(t){try{const e=await t.json();return e&&"object"==typeof e?e:{success:!1,error:!0,"error-msg":"Некорректный ответ сервера"}}catch(t){return{success:!1,error:!0,"error-msg":"Некорректный ответ сервера"}}}handleUnsuccessfulResponse(t){if(!0===t.error){if(t["error-msg"]&&(this.setErrorRecord(S,"server",t["error-msg"],"server"),this.showErrorForm(t["error-msg"])),t.errors){const e=[];t.errors.forEach(t=>{const r=this.findFieldByName(t.name);r?(e.push(r),this.setValidationError(r,"server",t["error-msg"],"server"),this.showError(r,t["error-msg"]),r.removeAttribute("aria-busy"),this.setFieldValidationState(r,"invalid")):(this.setErrorRecord(t.name,"server",t["error-msg"],"server"),console.warn(`Не найдено поле с именем: ${t.name}, для вывода ошибки: ${t["error-msg"]}`))}),!0===this.config.scrollToFirstErroredInput&&this.scrollToFirstErroredInput(e),!0===this.config.focusFirstErroredInput&&this.focusFirstErroredInput(e)}this.syncErrorSummary()}}async sendData(){var t;const e=this.$el.getAttribute("action")||"",r=(null==(t=this.$el.getAttribute("method"))?void 0:t.toUpperCase())||"POST",i=this.$el.getAttribute("enctype")||"application/x-www-form-urlencoded";let s=null,n={};const a=o(this.$el);let l=this.formDataToObject(a);if(this.config.logging)for(const t of a.keys())console.log(a.get(t));const c="function"==typeof this.config.wrapData;"function"==typeof this.config.wrapData&&(l=this.config.wrapData(l));const u=c?this.objectToFormData(l):a;if(this.isBodylessMethod(r))return fetch(this.appendQueryString(e,this.formDataToUrlSearchParams(u)),{method:r,headers:n});switch(i){case"application/x-www-form-urlencoded":s=this.formDataToUrlSearchParams(u).toString(),n["Content-Type"]="application/x-www-form-urlencoded";break;case"multipart/form-data":s=u;break;case"text/plain":s=this.formDataToTextBody(u),n["Content-Type"]="text/plain";break;case"application/json":s=JSON.stringify(l),n["Content-Type"]="application/json";break;default:console.warn(`Неизвестный enctype: ${i}. Используется application/x-www-form-urlencoded.`),s=this.formDataToUrlSearchParams(u).toString(),n["Content-Type"]="application/x-www-form-urlencoded"}return fetch(e,{method:r,body:s,headers:n})}async submit(){var t,e,r,i,s,n,o,l,c,u,d,h;if(!this.waitResponse){let m=!1;const f=()=>{m||(this.waitResponse=!1,!0===this.config.showLoaderButton&&this.hideLoader(),m=!0)};try{if(!1===await(null==(e=(t=this.config).onBeforeSubmit)?void 0:e.call(t,this)))return{success:!1};!0===this.config.showLoaderButton&&this.showLoader(),null==(i=(r=this.config).onSubmit)||i.call(r,this),this.waitResponse=!0;const d=await this.sendData(),h=await this.parseResponseBody(d);return f(),null==(n=(s=this.config).onResponse)||n.call(s,h,this),d.ok&&!0===h.success?null==(l=(o=this.config).onResponseSuccess)||l.call(o,h,this):(null==(u=(c=this.config).onResponseUnsuccess)||u.call(c,h,this),this.handleUnsuccessfulResponse(h)),a(h),{success:d.ok&&!0===h.success,response:d,responseBody:h}}catch(t){throw f(),null==(h=(d=this.config).onSubmitError)||h.call(d,t,this),t}}}}return t.FORM_ERROR_FIELD=S,t.blockScrollBody=function(){window.lastBlockScrollPosition=window.pageYOffset,document.body.style.top=-window.lastBlockScrollPosition+"px",document.body.classList.add("body--block-scroll")},t.closest=e,t.createFieldValidator=y,t.createFormValidator=F,t.createLengthValidator=function(t,e){return y(e=>{const r=!1===t.trim?e:e.trim();return 0===r.length&&!0===t.emptyIsValid||!("number"==typeof t.min&&r.length<t.min)&&!("number"==typeof t.max&&r.length>t.max)},e)},t.createPatternValidator=function(t,e){return y(e=>(t.lastIndex=0,t.test(e)),e)},t.createSchemaValidator=g,t.dateOrder=function(t,e,r="Дата окончания должна быть позже даты начала",i="date-order"){return F(({values:r})=>{const i=w(r,t),s=w(r,e);return!(!A(i)&&!A(s))||Date.parse(String(i))<=Date.parse(String(s))},{field:e,rule:i,message:r})},t.default=L,t.getAllValidators=function(){return new Map(d)},t.getValidator=m,t.isEmailValid=function(t){return/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/iu.test(t)},t.isPhoneValid=function(t){const e=[];return Array.from(t).forEach(t=>{(Number(t)||0===Number(t))&&e.push(t)}),!(e.length<11||e.length<12&&"+"===t[1])},t.isUrlValid=n,t.parseCommonResponseProperties=a,t.registerFieldValidator=function(t,e,r,i={}){h(t,y(e),r,i)},t.registerSchemaValidator=function(t,e,r,i={}){const{override:s,...n}=i;h(t,g(e,n),r,{override:s})},t.registerValidator=h,t.requiredIf=function(t,e,r="Поле обязательно",i="conditional-required"){return F(async r=>!await e(r)||!A(w(r.values,t)),{field:t,rule:i,message:r})},t.sameAsField=function(t,e,r="Значения не совпадают",i="same-as-field"){return F(({values:r})=>w(r,t)===w(r,e),{field:t,rule:i,message:r})},t.serializeFormToJSON=l,t.serializeToFormData=o,t.unblockScrollBody=function(){document.body.classList.remove("body--block-scroll"),document.body.style.top="",window.scrollTo(0,window.lastBlockScrollPosition||0)},Object.defineProperty(t,"__esModule",{value:!0}),t}({});
|
|
2
|
+
//# sourceMappingURL=FormFather.min.js.map
|