form-father 0.7.0 → 0.7.1
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/package.json +22 -20
- package/CHANGELOG.md +0 -154
- package/RESPONSE_API.md +0 -233
- package/demos/index.html +0 -198
- package/demos/main.js +0 -149
- package/demos/styles.css +0 -276
- package/docs/api/README.md +0 -153
- package/docs/demo/README.md +0 -37
- package/docs/en/README.md +0 -270
- package/docs/recipes/README.md +0 -150
- /package/{dist/FormFather.min.js → FormFather.min.js} +0 -0
- /package/{dist/FormFather.min.js.map → FormFather.min.js.map} +0 -0
- /package/{dist/index.d.ts → index.d.ts} +0 -0
- /package/{dist/index.js → index.js} +0 -0
- /package/{dist/index.js.map → index.js.map} +0 -0
- /package/{dist/types → types}/adapters.d.ts +0 -0
- /package/{dist/types → types}/helpers.d.ts +0 -0
- /package/{dist/types → types}/index.d.ts +0 -0
- /package/{dist/types → types}/validators.d.ts +0 -0
package/demos/main.js
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
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
|
-
}
|
package/docs/api/README.md
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
# Form Father API Reference
|
|
2
|
-
|
|
3
|
-
This page is a compact reference for the public API exported by `form-father`.
|
|
4
|
-
|
|
5
|
-
## Main Import
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
import Form from 'form-father';
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
`Form` is the default export and is constructed with a native `<form>` element.
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
const form = new Form(document.querySelector('form')!, {
|
|
15
|
-
inputWrapperSelector: '.field',
|
|
16
|
-
validateOn: ['blur', 'change'],
|
|
17
|
-
revalidateOn: ['input', 'change'],
|
|
18
|
-
errorSummary: true,
|
|
19
|
-
});
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Form Options
|
|
23
|
-
|
|
24
|
-
| Option | Type | Default | Notes |
|
|
25
|
-
| --- | --- | --- | --- |
|
|
26
|
-
| `inputSelector` | `string` | `.input` | Selector used by legacy input collection helpers. |
|
|
27
|
-
| `inputWrapperSelector` | `string` | `.input-primary` | Field wrapper selector used for inline error rendering. |
|
|
28
|
-
| `showLoaderButton` | `boolean` | `true` | Adds a loader to submit buttons while a request is pending. |
|
|
29
|
-
| `loaderColor` | `string` | `#fff` | Loader SVG color. |
|
|
30
|
-
| `scrollToFirstErroredInput` | `boolean` | `true` | Scrolls to the first invalid field. |
|
|
31
|
-
| `focusFirstErroredInput` | `boolean` | `false` | Focuses the first invalid field after validation. |
|
|
32
|
-
| `validateOn` | `ValidationTrigger | ValidationTrigger[]` | `submit` | Live validation triggers: `submit`, `input`, `blur`, `change`. |
|
|
33
|
-
| `revalidateOn` | `ValidationTrigger | ValidationTrigger[]` | `input`, `change` | Rechecks fields that are already invalid. |
|
|
34
|
-
| `validationDebounce` | `number` | `0` | Delay for live validation. |
|
|
35
|
-
| `validationSchema` | `ValidationSchema` | built in defaults | Field rules matched by selectors or `data-validate`. |
|
|
36
|
-
| `formValidators` | `FormValidator | FormValidator[]` | none | Cross-field and form-level validation. |
|
|
37
|
-
| `errorContainerAttribute` | `string` | `data-error-container` | Attribute containing a selector for a custom error container. |
|
|
38
|
-
| `validationStateAttribute` | `string` | `data-form-father-state` | Field state attribute: `validating`, `valid`, `invalid`. |
|
|
39
|
-
| `ariaDescribeErrors` | `boolean` | `true` | Links inline errors to fields with `aria-describedby`. |
|
|
40
|
-
| `errorIdPrefix` | `string` | `form-father-error` | Prefix for generated error element ids. |
|
|
41
|
-
| `errorSummary` | `boolean | ErrorSummaryOptions` | `false` | Enables an accessible form error summary. |
|
|
42
|
-
| `observeMutations` | `boolean` | `false` | Rebinds controls when fields/buttons are added dynamically. |
|
|
43
|
-
| `wrapData` | `(data) => data` | none | Transforms form values before request body creation. |
|
|
44
|
-
| `logging` | `boolean` | `false` | Logs submitted `FormData` values. |
|
|
45
|
-
|
|
46
|
-
## Lifecycle Callbacks
|
|
47
|
-
|
|
48
|
-
| Callback | When it runs |
|
|
49
|
-
| --- | --- |
|
|
50
|
-
| `onBeforeValidate(form)` | Before submit-time validation. |
|
|
51
|
-
| `onAfterValidate(isValid, form)` | After validation and before submit. |
|
|
52
|
-
| `onValidationError(errors, form)` | When client-side validation fails. |
|
|
53
|
-
| `onBeforeSubmit(form)` | Before a valid form is sent. Return `false` to cancel. |
|
|
54
|
-
| `onSubmit(form)` | When request sending starts. |
|
|
55
|
-
| `onResponse(body, form)` | After a response body is parsed. |
|
|
56
|
-
| `onResponseSuccess(body, form)` | For successful HTTP responses with `success: true`. |
|
|
57
|
-
| `onResponseUnsuccess(body, form)` | For non-OK responses, `success !== true`, or invalid JSON. |
|
|
58
|
-
| `onSubmitError(error, form)` | When sending or response parsing throws. |
|
|
59
|
-
|
|
60
|
-
## Instance Methods
|
|
61
|
-
|
|
62
|
-
| Method | Returns | Purpose |
|
|
63
|
-
| --- | --- | --- |
|
|
64
|
-
| `validate()` | `Promise<boolean>` | Validates the whole form. |
|
|
65
|
-
| `validateField(field)` | `Promise<boolean>` | Validates one field by `name` or element. |
|
|
66
|
-
| `submit()` | `Promise<SubmitResult | undefined>` | Sends the form programmatically. |
|
|
67
|
-
| `showFieldError(field, message, source?)` | `boolean` | Shows a field error manually. |
|
|
68
|
-
| `setErrors(errors, source?)` | `this` | Applies backend, form-level, or manual errors. |
|
|
69
|
-
| `getErrors()` | `ValidationError[]` | Returns current error records. |
|
|
70
|
-
| `getValues()` | `Record<string, any>` | Serializes the form into a plain object. |
|
|
71
|
-
| `setValues(values)` | `this` | Sets field values by `name` and dispatches `input`/`change`. |
|
|
72
|
-
| `clearErrors()` | `this` | Clears field and form errors. |
|
|
73
|
-
| `reset(options?)` | `this` | Calls native `form.reset()` and clears errors by default. |
|
|
74
|
-
| `clearInputs()` | `void` | Clears input values without resetting native defaults. |
|
|
75
|
-
| `updateOptions(options)` | `this` | Updates options and rebinds listeners. |
|
|
76
|
-
| `destroy()` | `void` | Removes listeners, loaders, error nodes, and runtime state. |
|
|
77
|
-
|
|
78
|
-
## Static Methods and Constants
|
|
79
|
-
|
|
80
|
-
```ts
|
|
81
|
-
Form.initAll('form[data-form-father]', options);
|
|
82
|
-
Form.setDefaultParams({ inputWrapperSelector: '.field' });
|
|
83
|
-
Form.defaultValidationSchema = {};
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
| Export | Purpose |
|
|
87
|
-
| --- | --- |
|
|
88
|
-
| `FORM_ERROR_FIELD` | Constant for form-level errors in `getErrors()` and `setErrors()`. |
|
|
89
|
-
| `Form.initAll(selector, options)` | Initializes matching forms and reuses existing instances. |
|
|
90
|
-
| `Form.setDefaultParams(params)` | Merges global default options for future instances. |
|
|
91
|
-
| `Form.defaultValidationSchema` | Global default validation schema. |
|
|
92
|
-
|
|
93
|
-
## Validators
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
import {
|
|
97
|
-
createLengthValidator,
|
|
98
|
-
createPatternValidator,
|
|
99
|
-
registerFieldValidator,
|
|
100
|
-
registerValidator,
|
|
101
|
-
} from 'form-father';
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
| Export | Purpose |
|
|
105
|
-
| --- | --- |
|
|
106
|
-
| `registerValidator(name, fn, message, options?)` | Registers a full validator. |
|
|
107
|
-
| `registerFieldValidator(name, predicate, message, options?)` | Registers a predicate-only field validator. |
|
|
108
|
-
| `getValidator(name)` | Reads one validator definition. |
|
|
109
|
-
| `getAllValidators()` | Reads the validator registry. |
|
|
110
|
-
| `createPatternValidator(pattern)` | Builds a regex validator. |
|
|
111
|
-
| `createLengthValidator(options)` | Builds min/max length validation. |
|
|
112
|
-
|
|
113
|
-
## Form Validators
|
|
114
|
-
|
|
115
|
-
```ts
|
|
116
|
-
import { dateOrder, requiredIf, sameAsField } from 'form-father';
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
| Export | Purpose |
|
|
120
|
-
| --- | --- |
|
|
121
|
-
| `createFormValidator(predicate, issue)` | Builds a reusable cross-field validator. |
|
|
122
|
-
| `sameAsField(field, otherField, message?, rule?)` | Checks that two values match. |
|
|
123
|
-
| `requiredIf(field, condition, message?, rule?)` | Makes a field required when a predicate is true. |
|
|
124
|
-
| `dateOrder(startField, endField, message?, rule?)` | Checks chronological field order. |
|
|
125
|
-
|
|
126
|
-
## Schema Adapters
|
|
127
|
-
|
|
128
|
-
```ts
|
|
129
|
-
import { createSchemaValidator, registerSchemaValidator } from 'form-father';
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Adapters expect a schema-like object with either `safeParse(value)` or `parse(value)`.
|
|
133
|
-
|
|
134
|
-
| Export | Purpose |
|
|
135
|
-
| --- | --- |
|
|
136
|
-
| `createSchemaValidator(schema, message?)` | Converts a schema into a field validator function. |
|
|
137
|
-
| `registerSchemaValidator(name, schema, message?, options?)` | Registers a schema-backed rule. |
|
|
138
|
-
| `createFieldValidator(predicate, message)` | Creates a simple predicate validator. |
|
|
139
|
-
|
|
140
|
-
## Helpers
|
|
141
|
-
|
|
142
|
-
| Export | Purpose |
|
|
143
|
-
| --- | --- |
|
|
144
|
-
| `serializeToFormData(element)` | Serializes a form-like element into `FormData`. |
|
|
145
|
-
| `serializeFormToJSON(form)` | Serializes a form into a plain object. |
|
|
146
|
-
| `isEmailValid(value)` | Checks email format. |
|
|
147
|
-
| `isUrlValid(value)` | Checks URLs, domains, IPs, and `localhost`. |
|
|
148
|
-
| `isPhoneValid(value)` | Checks Russian `+7XXXXXXXXXX` phone numbers. |
|
|
149
|
-
| `closest(element, selector)` | Finds a closest matching ancestor. |
|
|
150
|
-
| `parseCommonResponseProperties(body)` | Normalizes common response flags. |
|
|
151
|
-
| `blockScrollBody()` | Locks body scrolling. |
|
|
152
|
-
| `unblockScrollBody()` | Restores body scrolling. |
|
|
153
|
-
|
package/docs/demo/README.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Demo Guide
|
|
2
|
-
|
|
3
|
-
The demo is a static, dependency-light page that exercises common Form Father flows after a package build.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
npm run build
|
|
7
|
-
npm run demos
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
Open the Vite URL printed by the command. The page loads `dist/FormFather.min.js` and `demos/main.js`, so it reflects the
|
|
11
|
-
actual browser bundle.
|
|
12
|
-
|
|
13
|
-
## Demo Forms
|
|
14
|
-
|
|
15
|
-
| Form | What it demonstrates |
|
|
16
|
-
| --- | --- |
|
|
17
|
-
| Login | Live validation, async validator state, password confirmation, and custom error containers. |
|
|
18
|
-
| Callback | Server-side field errors and form-level API errors. |
|
|
19
|
-
| Search | `GET` forms and query-string body handling. |
|
|
20
|
-
| Multipart | Browser-managed `FormData` upload flow. |
|
|
21
|
-
| Public API | `setValues()`, `validateField()`, `setErrors()`, `getValues()`, and `clearErrors()`. |
|
|
22
|
-
|
|
23
|
-
## Useful Manual Checks
|
|
24
|
-
|
|
25
|
-
- Enter `taken@example.com` in the Login email field to see async validation fail.
|
|
26
|
-
- Submit Callback with a valid phone to see backend field errors applied after the mocked response.
|
|
27
|
-
- Clear the Search query and submit to inspect the accessible error summary.
|
|
28
|
-
- Use Public API buttons to inspect programmatic state changes in the Output panel.
|
|
29
|
-
|
|
30
|
-
## Files
|
|
31
|
-
|
|
32
|
-
| File | Purpose |
|
|
33
|
-
| --- | --- |
|
|
34
|
-
| `demos/index.html` | Demo markup and forms. |
|
|
35
|
-
| `demos/main.js` | Mock API, validator registration, `Form.initAll()`, and public API playground actions. |
|
|
36
|
-
| `demos/styles.css` | Minimal UI styling for fields, summaries, output, and loading states. |
|
|
37
|
-
|