form-father 0.6.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/README.md +5 -1
- package/package.json +22 -19
- package/CHANGELOG.md +0 -135
- package/RESPONSE_API.md +0 -233
- package/demos/index.html +0 -161
- package/demos/main.js +0 -95
- package/demos/styles.css +0 -261
- package/docs/en/README.md +0 -266
- 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,95 +0,0 @@
|
|
|
1
|
-
const api = window.FormFather || {};
|
|
2
|
-
const Form = api.default || api.Form || api;
|
|
3
|
-
const { 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
|
-
return json({ success: false, error: true, 'error-msg': `No demo route for ${requestUrl}` }, 404);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
registerValidator('min-length', createLengthValidator({ min: 6 }), 'Слишком короткое значение', { override: true });
|
|
61
|
-
registerValidator(
|
|
62
|
-
'available-email',
|
|
63
|
-
value =>
|
|
64
|
-
new Promise(resolve => {
|
|
65
|
-
setTimeout(() => resolve(value.toLowerCase() !== 'taken@example.com'), 350);
|
|
66
|
-
}),
|
|
67
|
-
'Email is already used',
|
|
68
|
-
{ override: true },
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
Form.initAll('form[data-form-father]', {
|
|
72
|
-
inputSelector: '.input',
|
|
73
|
-
inputWrapperSelector: '.field',
|
|
74
|
-
validateOn: ['blur', 'change'],
|
|
75
|
-
revalidateOn: ['input', 'change'],
|
|
76
|
-
validationDebounce: 120,
|
|
77
|
-
focusFirstErroredInput: true,
|
|
78
|
-
loaderColor: 'currentColor',
|
|
79
|
-
errorSummary: {
|
|
80
|
-
title: 'Проверьте поля формы',
|
|
81
|
-
focus: true,
|
|
82
|
-
},
|
|
83
|
-
formValidators: sameAsField('passwordConfirm', 'password', 'Пароли не совпадают'),
|
|
84
|
-
onValidationError(errors) {
|
|
85
|
-
writeOutput('Client validation errors', errors);
|
|
86
|
-
},
|
|
87
|
-
onResponse(responseBody, form) {
|
|
88
|
-
writeOutput(`${form.$el.dataset.demo || 'form'} response`, responseBody);
|
|
89
|
-
},
|
|
90
|
-
onResponseSuccess(responseBody, form) {
|
|
91
|
-
if (form.$el.dataset.demo === 'login' || form.$el.dataset.demo === 'upload') {
|
|
92
|
-
form.clearInputs();
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
});
|
package/demos/styles.css
DELETED
|
@@ -1,261 +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
|
-
.panel-title {
|
|
106
|
-
margin-bottom: 18px;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.panel-title h2 {
|
|
110
|
-
font-size: 22px;
|
|
111
|
-
line-height: 1.2;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
form {
|
|
115
|
-
display: grid;
|
|
116
|
-
gap: 14px;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
[data-form-father-summary]:not([hidden]) {
|
|
120
|
-
display: grid;
|
|
121
|
-
gap: 8px;
|
|
122
|
-
padding: 12px;
|
|
123
|
-
border: 1px solid rgba(179, 38, 30, 0.28);
|
|
124
|
-
border-radius: 6px;
|
|
125
|
-
background: rgba(179, 38, 30, 0.06);
|
|
126
|
-
color: var(--danger);
|
|
127
|
-
font-size: 14px;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
[data-form-father-summary-title] {
|
|
131
|
-
margin: 0;
|
|
132
|
-
font-weight: 700;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
[data-form-father-summary-list] {
|
|
136
|
-
display: grid;
|
|
137
|
-
gap: 4px;
|
|
138
|
-
margin: 0;
|
|
139
|
-
padding-left: 18px;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
[data-form-father-summary] button {
|
|
143
|
-
min-height: 0;
|
|
144
|
-
padding: 0;
|
|
145
|
-
border: 0;
|
|
146
|
-
background: transparent;
|
|
147
|
-
color: inherit;
|
|
148
|
-
font-weight: 700;
|
|
149
|
-
text-align: left;
|
|
150
|
-
text-decoration: underline;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
[data-form-father-summary] button:hover,
|
|
154
|
-
[data-form-father-summary] button:focus-visible {
|
|
155
|
-
background: transparent;
|
|
156
|
-
color: var(--accent-strong);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.field {
|
|
160
|
-
display: grid;
|
|
161
|
-
gap: 6px;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
.field span {
|
|
165
|
-
color: var(--muted);
|
|
166
|
-
font-size: 14px;
|
|
167
|
-
font-weight: 700;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
.input {
|
|
171
|
-
width: 100%;
|
|
172
|
-
min-height: 42px;
|
|
173
|
-
padding: 8px 10px;
|
|
174
|
-
border: 1px solid var(--line);
|
|
175
|
-
border-radius: 6px;
|
|
176
|
-
background: #ffffff;
|
|
177
|
-
color: var(--text);
|
|
178
|
-
font: inherit;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
.input:focus {
|
|
182
|
-
border-color: var(--accent);
|
|
183
|
-
outline: 3px solid rgba(23, 107, 135, 0.16);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
.input[aria-invalid="true"] {
|
|
187
|
-
border-color: var(--danger);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
.input[aria-busy="true"] {
|
|
191
|
-
opacity: 0.72;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
.input[data-form-father-state="valid"] {
|
|
195
|
-
border-color: var(--success);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
.input[data-form-father-state="invalid"] {
|
|
199
|
-
border-color: var(--danger);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
[data-form-father-error],
|
|
203
|
-
.field-error {
|
|
204
|
-
color: var(--danger);
|
|
205
|
-
font-size: 13px;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.input__wrapper--error .input {
|
|
209
|
-
border-color: var(--danger);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.button--loading {
|
|
213
|
-
opacity: 0.74;
|
|
214
|
-
pointer-events: none;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.button__loader {
|
|
218
|
-
display: inline-flex;
|
|
219
|
-
width: 18px;
|
|
220
|
-
height: 18px;
|
|
221
|
-
margin-right: 8px;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
.button__loader svg {
|
|
225
|
-
width: 18px;
|
|
226
|
-
height: 18px;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.error-block-under-input__wrapper {
|
|
230
|
-
color: var(--danger);
|
|
231
|
-
font-size: 14px;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
.demo-output {
|
|
235
|
-
margin: 0 min(6vw, 72px) 40px;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
pre {
|
|
239
|
-
min-height: 130px;
|
|
240
|
-
margin: 0;
|
|
241
|
-
overflow: auto;
|
|
242
|
-
padding: 16px;
|
|
243
|
-
border: 1px solid var(--line);
|
|
244
|
-
border-radius: 8px;
|
|
245
|
-
background: #101820;
|
|
246
|
-
color: #d8f3dc;
|
|
247
|
-
font-size: 13px;
|
|
248
|
-
white-space: pre-wrap;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
@media (max-width: 760px) {
|
|
252
|
-
.demo-header {
|
|
253
|
-
display: grid;
|
|
254
|
-
align-items: start;
|
|
255
|
-
padding-top: 28px;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
.demo-grid {
|
|
259
|
-
grid-template-columns: 1fr;
|
|
260
|
-
}
|
|
261
|
-
}
|
package/docs/en/README.md
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
# Form Father
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/form-father)
|
|
4
|
-
[](https://www.npmjs.com/package/form-father)
|
|
5
|
-
|
|
6
|
-
[🇷🇺 Документация на русском](https://github.com/Poliklot/form-father/blob/master/README.md)
|
|
7
|
-
|
|
8
|
-
**Form Father** is a library for handling forms in pure JavaScript, providing convenient validation and form submission
|
|
9
|
-
with TypeScript support.
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
12
|
-
|
|
13
|
-
Install the library via npm:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install form-father
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Usage
|
|
20
|
-
|
|
21
|
-
```javascript
|
|
22
|
-
import Form, { isUrlValid, serializeToFormData } from 'form-father';
|
|
23
|
-
|
|
24
|
-
Form.setDefaultParams({
|
|
25
|
-
showLoaderButton: false,
|
|
26
|
-
scrollToFirstErroredInput: false,
|
|
27
|
-
logging: true,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const formElement = document.querySelector('#myForm');
|
|
31
|
-
const options = {
|
|
32
|
-
onSubmit: formInstance => {
|
|
33
|
-
// Actions on form submission
|
|
34
|
-
},
|
|
35
|
-
onResponse: (responseBody, formInstance) => {
|
|
36
|
-
// Actions on receiving server response
|
|
37
|
-
},
|
|
38
|
-
// Other options...
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const form = new Form(formElement, options);
|
|
42
|
-
const simpleForm = new Form(document.querySelector('#simpleForm')); // options are optional
|
|
43
|
-
|
|
44
|
-
const forms = Form.initAll('form[data-form-father]', {
|
|
45
|
-
inputWrapperSelector: '.field',
|
|
46
|
-
validateOn: ['blur', 'change'],
|
|
47
|
-
revalidateOn: ['input', 'change'],
|
|
48
|
-
});
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
```ts
|
|
52
|
-
import Form, {
|
|
53
|
-
FORM_ERROR_FIELD,
|
|
54
|
-
type FormOptions,
|
|
55
|
-
type ValidationSchema,
|
|
56
|
-
type ResponseBody,
|
|
57
|
-
type ValidationError,
|
|
58
|
-
type SubmitResult,
|
|
59
|
-
type FormResetOptions,
|
|
60
|
-
type FormValidator,
|
|
61
|
-
type ValidationIssue,
|
|
62
|
-
type FormValidatorPredicate,
|
|
63
|
-
type ErrorSummaryOptions,
|
|
64
|
-
} from 'form-father';
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Options
|
|
68
|
-
|
|
69
|
-
- **onSubmit**: Callback function executed on form submission.
|
|
70
|
-
- **onBeforeSubmit**: Runs before sending an already valid form; returning `false` cancels the request.
|
|
71
|
-
- **onSubmitError**: Runs when form submission throws.
|
|
72
|
-
- **onResponse**: Callback function executed when a server response is received.
|
|
73
|
-
- **onResponseSuccess**: Callback function executed for a successful HTTP response with `success: true`.
|
|
74
|
-
- **onResponseUnsuccess**: Callback function executed for unsuccessful HTTP responses, `success !== true`, or invalid JSON.
|
|
75
|
-
- **onValidationError**: Runs when client-side validation fails.
|
|
76
|
-
- **showLoaderButton**: Whether to display a loader in the submit button. Defaults to `true`.
|
|
77
|
-
- **scrollToFirstErroredInput**: Whether to scroll to the first input with an error. Defaults to `true`.
|
|
78
|
-
- **focusFirstErroredInput**: Whether to focus the first invalid field. Defaults to `false`.
|
|
79
|
-
- **customTypeError**: Custom error type.
|
|
80
|
-
- **loaderColor**: Color of the loader in the submit button.
|
|
81
|
-
- **logging**: Specifies whether to log data to the console. Defaults to false.
|
|
82
|
-
- **validateOn**: Field live-validation events: `submit`, `input`, `blur`, `change`.
|
|
83
|
-
- **revalidateOn**: Events used to re-check already invalid fields. Defaults to `input` and `change`.
|
|
84
|
-
- **validationDebounce**: Live-validation delay in milliseconds.
|
|
85
|
-
- **errorContainerAttribute**: Attribute containing the CSS selector for a custom error container. Defaults to
|
|
86
|
-
`data-error-container`.
|
|
87
|
-
- **validationStateAttribute**: Field state attribute: `validating`, `valid`, or `invalid`. Defaults to
|
|
88
|
-
`data-form-father-state`.
|
|
89
|
-
- **ariaDescribeErrors**: Links inline error text to the field with `aria-describedby`. Defaults to `true`.
|
|
90
|
-
- **errorIdPrefix**: Prefix for automatically generated error element ids. Defaults to `form-father-error`.
|
|
91
|
-
- **errorSummary**: Form error summary: `true` for the default renderer or `{ selector, title, focus, render }`.
|
|
92
|
-
- **observeMutations**: Watches dynamically added fields and submit buttons.
|
|
93
|
-
- **formValidators**: A form validator or array of validators for cross-field rules.
|
|
94
|
-
|
|
95
|
-
## Form Submission
|
|
96
|
-
|
|
97
|
-
- Submit elements are `button[type="submit"]`, `button` without `type`, `input[type="submit"]`, and
|
|
98
|
-
`input[type="image"]`.
|
|
99
|
-
- `submit()` is public and returns `Promise<SubmitResult | undefined>`.
|
|
100
|
-
- For `method="GET"` and `method="HEAD"`, data is appended to the `action` query string and no request body is sent.
|
|
101
|
-
- `wrapData` applies to every supported `enctype`: `application/x-www-form-urlencoded`, `multipart/form-data`,
|
|
102
|
-
`text/plain`, and `application/json`.
|
|
103
|
-
- `onResponseSuccess` runs only for a successful HTTP response with `success: true`.
|
|
104
|
-
- `onResponseUnsuccess` runs for unsuccessful HTTP responses, `success !== true`, or invalid JSON responses.
|
|
105
|
-
|
|
106
|
-
## Methods
|
|
107
|
-
|
|
108
|
-
- **Form.initAll(selector, options)**: Initializes all forms matching a selector and reuses existing instances.
|
|
109
|
-
- **updateOptions(options)**: Updates one form instance and rebinds live validation.
|
|
110
|
-
- **validate()**: Validates the whole form.
|
|
111
|
-
- **validateField(field)**: Validates one field by name or DOM element.
|
|
112
|
-
- **showFieldError(field, message, source)**: Shows a field error manually.
|
|
113
|
-
- **setErrors(errors, source)**: Applies backend/form-level errors from an object, array, or `ErrorResponse[]`.
|
|
114
|
-
- **getErrors()**: Returns current errors as `{ field, rule, message, source }`.
|
|
115
|
-
- **getValues()**: Returns form values as a plain object.
|
|
116
|
-
- **setValues(values)**: Fills fields by `name` and dispatches `input`/`change`.
|
|
117
|
-
- **clearErrors()**: Clears errors without changing field values.
|
|
118
|
-
- **reset(options)**: Calls native `form.reset()` and clears errors by default.
|
|
119
|
-
- **clearInputs()**: Clears all input fields in the form.
|
|
120
|
-
- **setDefaultParams(params):** The setDefaultParams method is used to set default values for all instances of the form. These parameters can be overridden when initializing a specific form.
|
|
121
|
-
- **destroy()**: Removes event listeners and runtime artifacts.
|
|
122
|
-
|
|
123
|
-
## Validation
|
|
124
|
-
|
|
125
|
-
```html
|
|
126
|
-
<input
|
|
127
|
-
name="email"
|
|
128
|
-
type="email"
|
|
129
|
-
data-validate="required|email"
|
|
130
|
-
data-error-required="Email is required"
|
|
131
|
-
data-error-email="Enter a valid email"
|
|
132
|
-
data-error-container="#email-error"
|
|
133
|
-
/>
|
|
134
|
-
<small id="email-error" hidden></small>
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
- `data-validate` accepts rules separated by `|`, spaces, or commas.
|
|
138
|
-
- `data-error-rule-name` overrides a message for one rule.
|
|
139
|
-
- `data-error-container` points to a custom error container without requiring wrapper markup.
|
|
140
|
-
- During async validation, fields receive `aria-busy="true"` and a `validating` state attribute; stale validator
|
|
141
|
-
responses are ignored.
|
|
142
|
-
- Inline errors receive `role="alert"` and are linked to fields with `aria-describedby`.
|
|
143
|
-
- `data-custom-validate` is still supported for backward compatibility.
|
|
144
|
-
|
|
145
|
-
### Error summary and a11y
|
|
146
|
-
|
|
147
|
-
```html
|
|
148
|
-
<form data-form-father novalidate>
|
|
149
|
-
<div data-form-father-summary hidden></div>
|
|
150
|
-
<label>
|
|
151
|
-
Email
|
|
152
|
-
<input class="input" name="email" data-validate="required|email" />
|
|
153
|
-
</label>
|
|
154
|
-
<button type="submit">Submit</button>
|
|
155
|
-
</form>
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
```ts
|
|
159
|
-
new Form($form, {
|
|
160
|
-
inputWrapperSelector: 'label',
|
|
161
|
-
errorSummary: {
|
|
162
|
-
title: 'Please check the form',
|
|
163
|
-
focus: true,
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
- If the form already contains `[data-form-father-summary]`, Form Father uses it; otherwise `errorSummary: true` creates
|
|
169
|
-
a summary at the start of the form.
|
|
170
|
-
- `selector` sends the summary into your own container.
|
|
171
|
-
- `render(errors, form)` lets you fully replace the summary markup.
|
|
172
|
-
- `ariaDescribeErrors: false` disables automatic error id insertion into `aria-describedby`.
|
|
173
|
-
|
|
174
|
-
Final rule order:
|
|
175
|
-
|
|
176
|
-
```text
|
|
177
|
-
required → schema.rules → data-validate → data-custom-validate
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### Cross-field and form-level validation
|
|
181
|
-
|
|
182
|
-
```ts
|
|
183
|
-
import { dateOrder, requiredIf, sameAsField } from 'form-father';
|
|
184
|
-
|
|
185
|
-
const form = new Form($form, {
|
|
186
|
-
formValidators: [
|
|
187
|
-
sameAsField('passwordConfirm', 'password', 'Passwords do not match'),
|
|
188
|
-
requiredIf('email', ({ values }) => Boolean(values.subscribe), 'Email is required for subscription'),
|
|
189
|
-
dateOrder('start', 'end', 'End date must be after start'),
|
|
190
|
-
],
|
|
191
|
-
});
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
Return a string or an issue without `field` to show a form-level error. In `getErrors()`, it uses
|
|
195
|
-
`FORM_ERROR_FIELD` (`"_form"`).
|
|
196
|
-
|
|
197
|
-
```ts
|
|
198
|
-
form.setErrors({
|
|
199
|
-
email: 'Email is already used',
|
|
200
|
-
[FORM_ERROR_FIELD]: 'Please check the form',
|
|
201
|
-
});
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Rule DX helpers
|
|
205
|
-
|
|
206
|
-
```ts
|
|
207
|
-
import { createLengthValidator, createPatternValidator, registerFieldValidator } from 'form-father';
|
|
208
|
-
|
|
209
|
-
registerFieldValidator('starts-with-a', value => value.startsWith('A'), 'Must start with A');
|
|
210
|
-
registerValidator('slug', createPatternValidator(/^[a-z0-9-]+$/), 'Slug characters only');
|
|
211
|
-
registerValidator('short-name', createLengthValidator({ min: 2, max: 24 }), 'Use 2-24 characters');
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
## Schema Adapters
|
|
215
|
-
|
|
216
|
-
Form Father has no schema-library runtime dependency, but can wrap Zod/Valibot/Yup-like schemas with `safeParse()` or
|
|
217
|
-
`parse()`.
|
|
218
|
-
|
|
219
|
-
```ts
|
|
220
|
-
import { registerSchemaValidator } from 'form-father';
|
|
221
|
-
|
|
222
|
-
registerSchemaValidator('company-email', z.string().email(), 'Use a company email');
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
For simple checks, use `createFieldValidator(predicate, message)`.
|
|
226
|
-
|
|
227
|
-
## Demos and Recipes
|
|
228
|
-
|
|
229
|
-
- `demos/index.html` is a static login/callback/search/upload demo after `npm run build`.
|
|
230
|
-
- `npm run demos` builds the package and starts the Vite demo server.
|
|
231
|
-
- `docs/recipes/README.md` contains short API, data-attribute, adapter, and server-error recipes.
|
|
232
|
-
|
|
233
|
-
## Helpers
|
|
234
|
-
|
|
235
|
-
The library provides a number of utility functions:
|
|
236
|
-
|
|
237
|
-
- **serializeToFormData($element)**: Serializes form data into a `FormData` object.
|
|
238
|
-
- **isEmailValid(value)**: Checks if a string is a valid email address.
|
|
239
|
-
- **isUrlValid(value)**: Checks an `http(s)` URL, domain, IP, or `localhost`; the scheme may be omitted.
|
|
240
|
-
- **isPhoneValid(value)**: Checks if a string is a valid phone number.
|
|
241
|
-
- **closest($el, selector)**: Finds the closest parent element matching the given selector.
|
|
242
|
-
- **blockScrollBody()**: Blocks page scrolling.
|
|
243
|
-
- **unblockScrollBody()**: Unblocks page scrolling.
|
|
244
|
-
- **parseCommonResponseProperties(responseBody)**: Processes common properties from the server response.
|
|
245
|
-
- **serializeFormToJSON(form)**: Serializes form data into a plain object.
|
|
246
|
-
|
|
247
|
-
## Build and Development
|
|
248
|
-
|
|
249
|
-
**Rollup** is used for building the project. Main commands:
|
|
250
|
-
|
|
251
|
-
- **npm run build**: Builds the project.
|
|
252
|
-
- **npm run watch**: Builds the project in watch mode.
|
|
253
|
-
- **npm run demos**: Runs a demo version using Vite.
|
|
254
|
-
|
|
255
|
-
## Contributing
|
|
256
|
-
|
|
257
|
-
We welcome your suggestions and improvements! Please create [issues](https://github.com/poliklot/form-father/issues) and
|
|
258
|
-
submit [pull requests](https://github.com/poliklot/form-father/pulls).
|
|
259
|
-
|
|
260
|
-
## License
|
|
261
|
-
|
|
262
|
-
[MIT](LICENSE)
|
|
263
|
-
|
|
264
|
-
---
|
|
265
|
-
|
|
266
|
-
© 2024 Poliklot
|