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/docs/en/README.md
DELETED
|
@@ -1,270 +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/public API demo after `npm run build`.
|
|
230
|
-
- `npm run demos` builds the package and starts the Vite demo server.
|
|
231
|
-
- `docs/api/README.md` is a compact reference for public API, options, methods, and exports.
|
|
232
|
-
- `docs/demo/README.md` explains manual demo checks and demo form coverage.
|
|
233
|
-
- `docs/recipes/README.md` contains short API, data-attribute, adapter, and server-error recipes.
|
|
234
|
-
- `CHANGELOG.md` tracks releases and coverage baselines.
|
|
235
|
-
|
|
236
|
-
## Helpers
|
|
237
|
-
|
|
238
|
-
The library provides a number of utility functions:
|
|
239
|
-
|
|
240
|
-
- **serializeToFormData($element)**: Serializes form data into a `FormData` object.
|
|
241
|
-
- **isEmailValid(value)**: Checks if a string is a valid email address.
|
|
242
|
-
- **isUrlValid(value)**: Checks an `http(s)` URL, domain, IP, or `localhost`; the scheme may be omitted.
|
|
243
|
-
- **isPhoneValid(value)**: Checks if a string is a valid phone number.
|
|
244
|
-
- **closest($el, selector)**: Finds the closest parent element matching the given selector.
|
|
245
|
-
- **blockScrollBody()**: Blocks page scrolling.
|
|
246
|
-
- **unblockScrollBody()**: Unblocks page scrolling.
|
|
247
|
-
- **parseCommonResponseProperties(responseBody)**: Processes common properties from the server response.
|
|
248
|
-
- **serializeFormToJSON(form)**: Serializes form data into a plain object.
|
|
249
|
-
|
|
250
|
-
## Build and Development
|
|
251
|
-
|
|
252
|
-
**Rollup** is used for building the project. Main commands:
|
|
253
|
-
|
|
254
|
-
- **npm run build**: Builds the project.
|
|
255
|
-
- **npm run watch**: Builds the project in watch mode.
|
|
256
|
-
- **npm run docs:check**: Checks docs links, version references, demo files, and package README sync.
|
|
257
|
-
- **npm run demos**: Runs a demo version using Vite.
|
|
258
|
-
|
|
259
|
-
## Contributing
|
|
260
|
-
|
|
261
|
-
We welcome your suggestions and improvements! Please create [issues](https://github.com/poliklot/form-father/issues) and
|
|
262
|
-
submit [pull requests](https://github.com/poliklot/form-father/pulls).
|
|
263
|
-
|
|
264
|
-
## License
|
|
265
|
-
|
|
266
|
-
[MIT](LICENSE)
|
|
267
|
-
|
|
268
|
-
---
|
|
269
|
-
|
|
270
|
-
© 2024 Poliklot
|
package/docs/recipes/README.md
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
# Form Father Recipes
|
|
2
|
-
|
|
3
|
-
## Init several forms
|
|
4
|
-
|
|
5
|
-
```ts
|
|
6
|
-
import Form from 'form-father';
|
|
7
|
-
|
|
8
|
-
const forms = Form.initAll('form[data-form-father]', {
|
|
9
|
-
inputWrapperSelector: '.field',
|
|
10
|
-
validateOn: ['blur', 'change'],
|
|
11
|
-
revalidateOn: ['input', 'change'],
|
|
12
|
-
});
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## HTML-only rules
|
|
16
|
-
|
|
17
|
-
```html
|
|
18
|
-
<input
|
|
19
|
-
name="email"
|
|
20
|
-
type="email"
|
|
21
|
-
data-validate="required|email"
|
|
22
|
-
data-error-required="Email is required"
|
|
23
|
-
data-error-email="Enter a valid email"
|
|
24
|
-
data-error-container="#email-error"
|
|
25
|
-
/>
|
|
26
|
-
<small id="email-error" hidden></small>
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Programmatic field API
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
const form = new Form(document.querySelector('form')!);
|
|
33
|
-
|
|
34
|
-
await form.validateField('email');
|
|
35
|
-
form.showFieldError('email', 'This email is already used', 'server');
|
|
36
|
-
console.log(form.getErrors());
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## State helpers
|
|
40
|
-
|
|
41
|
-
```ts
|
|
42
|
-
form.setValues({
|
|
43
|
-
email: 'user@example.com',
|
|
44
|
-
interests: ['docs', 'api'],
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
console.log(form.getValues());
|
|
48
|
-
form.clearErrors();
|
|
49
|
-
form.reset();
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Cross-field validation
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
import { dateOrder, requiredIf, sameAsField } from 'form-father';
|
|
56
|
-
|
|
57
|
-
const form = new Form(document.querySelector('form')!, {
|
|
58
|
-
formValidators: [
|
|
59
|
-
sameAsField('passwordConfirm', 'password', 'Passwords do not match'),
|
|
60
|
-
requiredIf('email', ({ values }) => Boolean(values.subscribe), 'Email is required'),
|
|
61
|
-
dateOrder('start', 'end', 'End date must be after start'),
|
|
62
|
-
],
|
|
63
|
-
});
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Field rule helpers
|
|
67
|
-
|
|
68
|
-
```ts
|
|
69
|
-
import { createLengthValidator, createPatternValidator, registerFieldValidator, registerValidator } from 'form-father';
|
|
70
|
-
|
|
71
|
-
registerFieldValidator('starts-with-a', value => value.startsWith('A'), 'Must start with A');
|
|
72
|
-
registerValidator('slug', createPatternValidator(/^[a-z0-9-]+$/), 'Slug characters only');
|
|
73
|
-
registerValidator('short-name', createLengthValidator({ min: 2, max: 24 }), 'Use 2-24 characters');
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Applying backend errors
|
|
77
|
-
|
|
78
|
-
```ts
|
|
79
|
-
import { FORM_ERROR_FIELD } from 'form-father';
|
|
80
|
-
|
|
81
|
-
form.setErrors({
|
|
82
|
-
email: 'Email is already registered',
|
|
83
|
-
[FORM_ERROR_FIELD]: 'Please check the form',
|
|
84
|
-
});
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## Async validation state
|
|
88
|
-
|
|
89
|
-
```css
|
|
90
|
-
[data-form-father-state="validating"] {
|
|
91
|
-
opacity: 0.7;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
[data-form-father-state="invalid"] {
|
|
95
|
-
border-color: #b3261e;
|
|
96
|
-
}
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Async validators are race-safe: if an older validation finishes after a newer one started, Form Father ignores the stale
|
|
100
|
-
result and keeps the latest field state.
|
|
101
|
-
|
|
102
|
-
## Zod-like schema adapter
|
|
103
|
-
|
|
104
|
-
```ts
|
|
105
|
-
import { registerSchemaValidator } from 'form-father';
|
|
106
|
-
|
|
107
|
-
registerSchemaValidator('email-domain', z.string().email().endsWith('@company.com'), 'Use a company email');
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
The adapter only expects a `safeParse(value)` or `parse(value)` method, so it can be used with Zod-like libraries without
|
|
111
|
-
adding runtime dependencies to Form Father.
|
|
112
|
-
|
|
113
|
-
## Server response errors
|
|
114
|
-
|
|
115
|
-
```json
|
|
116
|
-
{
|
|
117
|
-
"success": false,
|
|
118
|
-
"error": true,
|
|
119
|
-
"error-msg": "Please check the form",
|
|
120
|
-
"errors": [{ "name": "email", "error-msg": "Email is already registered" }]
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Field errors are shown through the same rendering path as client-side validation errors.
|
|
125
|
-
|
|
126
|
-
## Accessible error summary
|
|
127
|
-
|
|
128
|
-
```html
|
|
129
|
-
<form data-form-father novalidate>
|
|
130
|
-
<div data-form-father-summary hidden></div>
|
|
131
|
-
<label>
|
|
132
|
-
Email
|
|
133
|
-
<input class="input" name="email" data-validate="required|email" />
|
|
134
|
-
</label>
|
|
135
|
-
<button type="submit">Submit</button>
|
|
136
|
-
</form>
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
```ts
|
|
140
|
-
const form = new Form(document.querySelector('form')!, {
|
|
141
|
-
inputWrapperSelector: 'label',
|
|
142
|
-
errorSummary: {
|
|
143
|
-
title: 'Please check the form',
|
|
144
|
-
focus: true,
|
|
145
|
-
},
|
|
146
|
-
});
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
Inline errors are linked to fields with `aria-describedby` by default. Use `ariaDescribeErrors: false` if your design
|
|
150
|
-
system owns that relationship itself.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|