form-father 0.2.10 → 0.3.0
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 +79 -0
- package/README.md +77 -2
- package/demos/index.html +141 -0
- package/demos/main.js +92 -0
- package/demos/styles.css +221 -0
- package/dist/FormFather.min.js +1 -1
- package/dist/index.d.ts +158 -7
- package/dist/index.js +641 -130
- package/dist/index.js.map +1 -1
- package/dist/types/adapters.d.ts +28 -0
- package/dist/types/index.d.ts +128 -4
- package/dist/types/validators.d.ts +3 -2
- package/docs/en/README.md +192 -0
- package/docs/recipes/README.md +89 -0
- package/package.json +5 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ValidatorFieldElement, type ValidatorResult } from './validators';
|
|
2
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
3
|
+
export type SchemaParseResult = {
|
|
4
|
+
success: true;
|
|
5
|
+
data?: unknown;
|
|
6
|
+
} | {
|
|
7
|
+
success: false;
|
|
8
|
+
error?: unknown;
|
|
9
|
+
issues?: Array<{
|
|
10
|
+
message?: string;
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
13
|
+
export type SchemaLike = {
|
|
14
|
+
safeParse?: (value: unknown) => MaybePromise<SchemaParseResult>;
|
|
15
|
+
parse?: (value: unknown) => MaybePromise<unknown>;
|
|
16
|
+
} | ((value: unknown) => MaybePromise<boolean | ValidatorResult | void>);
|
|
17
|
+
export type AdapterValidator = (value: string, $input: ValidatorFieldElement, $form: HTMLElement, params?: any) => MaybePromise<boolean | ValidatorResult>;
|
|
18
|
+
export interface SchemaValidatorOptions {
|
|
19
|
+
message?: string | ((error: unknown) => string);
|
|
20
|
+
mapValue?: (value: string, $input: ValidatorFieldElement, $form: HTMLElement, params?: any) => unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface RegisterSchemaValidatorOptions extends SchemaValidatorOptions {
|
|
23
|
+
override?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function createSchemaValidator(schema: SchemaLike, options?: SchemaValidatorOptions): AdapterValidator;
|
|
26
|
+
export declare function registerSchemaValidator(name: string, schema: SchemaLike, defaultMessage: string, options?: RegisterSchemaValidatorOptions): void;
|
|
27
|
+
export declare function createFieldValidator(predicate: (value: string, $input: ValidatorFieldElement, $form: HTMLElement, params?: any) => MaybePromise<boolean>, message?: string): AdapterValidator;
|
|
28
|
+
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,14 +1,34 @@
|
|
|
1
1
|
export * from './validators';
|
|
2
|
+
export * from './adapters';
|
|
2
3
|
export { serializeToFormData, isEmailValid, isUrlValid, parseCommonResponseProperties, closest, isPhoneValid, blockScrollBody, unblockScrollBody, serializeFormToJSON, } from './helpers';
|
|
3
4
|
export type ValidationRule = string | {
|
|
4
5
|
rule: string;
|
|
5
6
|
params?: any;
|
|
6
7
|
};
|
|
8
|
+
export type ValidationTrigger = 'submit' | 'input' | 'blur' | 'change';
|
|
9
|
+
export type ValidationErrorSource = 'client' | 'server' | 'manual';
|
|
10
|
+
export type FieldReference = string | FormFieldElement;
|
|
11
|
+
export interface ValidationError {
|
|
12
|
+
field: string;
|
|
13
|
+
rule: string;
|
|
14
|
+
message: string;
|
|
15
|
+
source: ValidationErrorSource;
|
|
16
|
+
}
|
|
17
|
+
export interface SubmitResult {
|
|
18
|
+
success: boolean;
|
|
19
|
+
response?: Response;
|
|
20
|
+
responseBody?: ResponseBody;
|
|
21
|
+
error?: unknown;
|
|
22
|
+
}
|
|
23
|
+
export interface FormResetOptions {
|
|
24
|
+
clearErrors?: boolean;
|
|
25
|
+
}
|
|
7
26
|
export type ValidationSchema = Record<string, {
|
|
8
27
|
rules: ValidationRule[];
|
|
9
28
|
selector?: string;
|
|
10
29
|
messages?: Record<string, string>;
|
|
11
30
|
}>;
|
|
31
|
+
export type FormFieldElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
|
|
12
32
|
/** Параметры формы. */
|
|
13
33
|
export interface FormOptions {
|
|
14
34
|
/**
|
|
@@ -24,6 +44,20 @@ export interface FormOptions {
|
|
|
24
44
|
* @param formInstance - Инстанс формы.
|
|
25
45
|
*/
|
|
26
46
|
onAfterValidate?: (isValid: boolean, formInstance: Form) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Функция обратного вызова. Запускается после неуспешной клиентской валидации.
|
|
49
|
+
*
|
|
50
|
+
* @param errors - Список ошибок валидации.
|
|
51
|
+
* @param formInstance - Инстанс формы.
|
|
52
|
+
*/
|
|
53
|
+
onValidationError?: (errors: ValidationError[], formInstance: Form) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Функция обратного вызова. Запускается перед отправкой уже валидной формы. Если вернуть `false`, отправка будет
|
|
56
|
+
* отменена.
|
|
57
|
+
*
|
|
58
|
+
* @param formInstance - Инстанс формы.
|
|
59
|
+
*/
|
|
60
|
+
onBeforeSubmit?: (formInstance: Form) => void | boolean | Promise<void | boolean>;
|
|
27
61
|
/**
|
|
28
62
|
* Функция обратного вызова. Запускается, когда форма отправляется.
|
|
29
63
|
*
|
|
@@ -51,10 +85,19 @@ export interface FormOptions {
|
|
|
51
85
|
* @param formInstance - Инстанс формы.
|
|
52
86
|
*/
|
|
53
87
|
onResponseUnsuccess?: (responseBody: ResponseBody, formInstance: Form) => void;
|
|
88
|
+
/**
|
|
89
|
+
* Функция обратного вызова. Запускается при исключении во время отправки формы.
|
|
90
|
+
*
|
|
91
|
+
* @param error - Ошибка отправки.
|
|
92
|
+
* @param formInstance - Инстанс формы.
|
|
93
|
+
*/
|
|
94
|
+
onSubmitError?: (error: unknown, formInstance: Form) => void;
|
|
54
95
|
/** Нужно ли показывать loader в кнопке. По умолчанию `true`. */
|
|
55
96
|
showLoaderButton?: boolean;
|
|
56
97
|
/** Нужно ли проскроливать до первого по порядку элемента с ошибкой. По умолчанию `true`. */
|
|
57
98
|
scrollToFirstErroredInput?: boolean;
|
|
99
|
+
/** Нужно ли переводить фокус в первое ошибочное поле. По умолчанию `false`. */
|
|
100
|
+
focusFirstErroredInput?: boolean;
|
|
58
101
|
/** Кастомный тип ошибки. */
|
|
59
102
|
customTypeError?: any;
|
|
60
103
|
/** Цвет лоадера. */
|
|
@@ -68,6 +111,21 @@ export interface FormOptions {
|
|
|
68
111
|
* `.input-primary`.
|
|
69
112
|
*/
|
|
70
113
|
inputWrapperSelector?: string;
|
|
114
|
+
/**
|
|
115
|
+
* События, на которых нужно запускать валидацию поля. `submit` всегда обрабатывается отдельно. По умолчанию
|
|
116
|
+
* `submit`.
|
|
117
|
+
*/
|
|
118
|
+
validateOn?: ValidationTrigger | ValidationTrigger[];
|
|
119
|
+
/** События, на которых нужно повторно проверять уже ошибочные поля. По умолчанию `input` и `change`. */
|
|
120
|
+
revalidateOn?: ValidationTrigger | ValidationTrigger[];
|
|
121
|
+
/** Задержка live-валидации в миллисекундах. По умолчанию `0`. */
|
|
122
|
+
validationDebounce?: number;
|
|
123
|
+
/** Атрибут с CSS-селектором контейнера для ошибки поля. По умолчанию `data-error-container`. */
|
|
124
|
+
errorContainerAttribute?: string;
|
|
125
|
+
/** Атрибут, куда пишется состояние поля: `validating`, `valid` или `invalid`. */
|
|
126
|
+
validationStateAttribute?: string;
|
|
127
|
+
/** Следить за динамически добавленными/удалёнными полями и submit-кнопками. По умолчанию `false`. */
|
|
128
|
+
observeMutations?: boolean;
|
|
71
129
|
/** Функция для обёртки отправляемых данных. */
|
|
72
130
|
wrapData?: (data: Record<string, any>) => Record<string, any>;
|
|
73
131
|
/** Схема валидации: поле → массив правил + override-сообщения */
|
|
@@ -98,6 +156,12 @@ export default class Form {
|
|
|
98
156
|
private inputs;
|
|
99
157
|
private _onSubmitHandler?;
|
|
100
158
|
private _onSubmitClickHandler?;
|
|
159
|
+
private fieldEventHandlers;
|
|
160
|
+
private liveValidationTimers;
|
|
161
|
+
private fieldValidationTokens;
|
|
162
|
+
private fieldValidationTokenCounter;
|
|
163
|
+
private mutationObserver?;
|
|
164
|
+
private errors;
|
|
101
165
|
private static get defaultParams();
|
|
102
166
|
private static set defaultParams(value);
|
|
103
167
|
static get defaultValidationSchema(): ValidationSchema;
|
|
@@ -110,6 +174,26 @@ export default class Form {
|
|
|
110
174
|
*/
|
|
111
175
|
constructor($el: HTMLElement, options?: FormOptions);
|
|
112
176
|
getOptions(): FormOptions;
|
|
177
|
+
/**
|
|
178
|
+
* Инициализирует сразу несколько форм по CSS-селектору. Если форма уже инициализирована, её настройки обновятся.
|
|
179
|
+
*
|
|
180
|
+
* @param selector - CSS-селектор форм. По умолчанию `form[data-form-father]`.
|
|
181
|
+
* @param options - Общие параметры для найденных форм.
|
|
182
|
+
*/
|
|
183
|
+
static initAll(selector?: string, options?: FormOptions): Form[];
|
|
184
|
+
/** Обновляет настройки конкретного инстанса формы без пересоздания обработчиков вручную. */
|
|
185
|
+
updateOptions(options: Partial<FormOptions>): this;
|
|
186
|
+
/** Возвращает текущий список ошибок формы. */
|
|
187
|
+
getErrors(): ValidationError[];
|
|
188
|
+
/** Возвращает значения формы обычным объектом. */
|
|
189
|
+
getValues(): Record<string, any>;
|
|
190
|
+
private normalizeValueList;
|
|
191
|
+
/** Проставляет значения полей по `name` и уведомляет UI через `input`/`change` события. */
|
|
192
|
+
setValues(values: Record<string, any>): this;
|
|
193
|
+
/** Снимает все клиентские и серверные ошибки с формы, не меняя значения полей. */
|
|
194
|
+
clearErrors(): this;
|
|
195
|
+
/** Сбрасывает форму через native `reset()` и опционально очищает ошибки. */
|
|
196
|
+
reset(options?: FormResetOptions): this;
|
|
113
197
|
/**
|
|
114
198
|
* Обновляет параметры по умолчанию для настроек формы. Метод объединяет переданные параметры с уже существующими
|
|
115
199
|
* параметрами по умолчанию.
|
|
@@ -118,8 +202,17 @@ export default class Form {
|
|
|
118
202
|
* свойства, которые необходимо обновить; остальные сохранятся без изменений.
|
|
119
203
|
*/
|
|
120
204
|
static setDefaultParams(params: Partial<FormOptions>): void;
|
|
205
|
+
private refreshControls;
|
|
121
206
|
private findSubmitElements;
|
|
207
|
+
private bindSubmitHandlers;
|
|
122
208
|
private initialization;
|
|
209
|
+
private handleSubmitEvent;
|
|
210
|
+
private normalizeTriggers;
|
|
211
|
+
private clearFieldValidationEvents;
|
|
212
|
+
private bindFieldValidationEvents;
|
|
213
|
+
private scheduleFieldValidation;
|
|
214
|
+
private setupMutationObserver;
|
|
215
|
+
private getErrorContainer;
|
|
123
216
|
/**
|
|
124
217
|
* Показывает ошибку для поля ввода.
|
|
125
218
|
*
|
|
@@ -143,17 +236,48 @@ export default class Form {
|
|
|
143
236
|
* @param {HTMLElement[]} inputsList - Массив элементов с ошибкой.
|
|
144
237
|
*/
|
|
145
238
|
private scrollToFirstErroredInput;
|
|
239
|
+
private focusFirstErroredInput;
|
|
240
|
+
private getValidationSchema;
|
|
241
|
+
private getAllFields;
|
|
242
|
+
private getFieldKey;
|
|
243
|
+
private hasFieldError;
|
|
244
|
+
private clearFieldErrorRecord;
|
|
245
|
+
private getValidationStateAttribute;
|
|
246
|
+
private setFieldValidationState;
|
|
247
|
+
private startFieldValidation;
|
|
248
|
+
private isLatestFieldValidation;
|
|
249
|
+
private finishFieldValidation;
|
|
250
|
+
private clearFieldValidationState;
|
|
251
|
+
private setValidationError;
|
|
252
|
+
private resolveField;
|
|
253
|
+
private parseRuleString;
|
|
254
|
+
private parseValidationAttribute;
|
|
255
|
+
private getRuleName;
|
|
256
|
+
private getRuleIdentity;
|
|
257
|
+
private collectRules;
|
|
258
|
+
private getRuleMessage;
|
|
259
|
+
private fieldMatchesSchemaDefinition;
|
|
260
|
+
private getFieldValidationConfig;
|
|
261
|
+
private getValidationFields;
|
|
262
|
+
private validateInput;
|
|
263
|
+
private runFieldValidation;
|
|
264
|
+
private getRadioGroup;
|
|
265
|
+
/** Показывает ошибку конкретного поля по имени или DOM-элементу. */
|
|
266
|
+
showFieldError(field: FieldReference, message: string, source?: ValidationErrorSource): boolean;
|
|
267
|
+
/** Проверяет одно поле по имени или DOM-элементу. */
|
|
268
|
+
validateField(field: FieldReference): Promise<boolean>;
|
|
146
269
|
/**
|
|
147
270
|
* Проверяет все поля ввода.
|
|
148
271
|
*
|
|
149
|
-
* Порядок для
|
|
272
|
+
* Порядок для каждого поля:
|
|
150
273
|
*
|
|
151
274
|
* 1. `required` — если атрибут `required`
|
|
152
275
|
* 2. правила из `validationSchema`
|
|
153
|
-
* 3. правила из `data-
|
|
276
|
+
* 3. правила из `data-validate`
|
|
277
|
+
* 4. правила из `data-custom-validate`
|
|
154
278
|
*
|
|
155
279
|
* Для одного поля показывается только первая ошибка. Неизвестные правила фиксируются `console.warn` и исключаются ДО
|
|
156
|
-
*
|
|
280
|
+
* валидации.
|
|
157
281
|
*/
|
|
158
282
|
validate($block?: HTMLElement): Promise<boolean>;
|
|
159
283
|
/** Блокирует кнопку отправки данных. */
|
|
@@ -197,5 +321,5 @@ export default class Form {
|
|
|
197
321
|
private parseResponseBody;
|
|
198
322
|
private handleUnsuccessfulResponse;
|
|
199
323
|
private sendData;
|
|
200
|
-
|
|
324
|
+
submit(): Promise<SubmitResult | undefined>;
|
|
201
325
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export type ValidatorEffectCtx = {
|
|
2
2
|
value: string;
|
|
3
|
-
$input:
|
|
3
|
+
$input: ValidatorFieldElement;
|
|
4
4
|
$form: HTMLElement;
|
|
5
5
|
params?: any;
|
|
6
6
|
};
|
|
7
|
+
export type ValidatorFieldElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
|
|
7
8
|
export type ValidatorResult = {
|
|
8
9
|
valid: boolean;
|
|
9
10
|
/** Сообщение об ошибке, если нужно переопределить дефолт */
|
|
@@ -13,7 +14,7 @@ export type ValidatorResult = {
|
|
|
13
14
|
/** Остановить дальнейшие правила для поля (даже если valid=true). Полезно, если валидатор полностью «ведёт» поле. */
|
|
14
15
|
stopOthers?: boolean;
|
|
15
16
|
};
|
|
16
|
-
type ValidatorFn = (value: string, $input:
|
|
17
|
+
export type ValidatorFn = (value: string, $input: ValidatorFieldElement, $form: HTMLElement, params?: any) => boolean | ValidatorResult | Promise<boolean | ValidatorResult>;
|
|
17
18
|
interface Validator {
|
|
18
19
|
fn: ValidatorFn;
|
|
19
20
|
defaultMessage: string;
|
|
@@ -0,0 +1,192 @@
|
|
|
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
|
+
type FormOptions,
|
|
54
|
+
type ValidationSchema,
|
|
55
|
+
type ResponseBody,
|
|
56
|
+
type ValidationError,
|
|
57
|
+
type SubmitResult,
|
|
58
|
+
type FormResetOptions,
|
|
59
|
+
} from 'form-father';
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Options
|
|
63
|
+
|
|
64
|
+
- **onSubmit**: Callback function executed on form submission.
|
|
65
|
+
- **onBeforeSubmit**: Runs before sending an already valid form; returning `false` cancels the request.
|
|
66
|
+
- **onSubmitError**: Runs when form submission throws.
|
|
67
|
+
- **onResponse**: Callback function executed when a server response is received.
|
|
68
|
+
- **onResponseSuccess**: Callback function executed for a successful HTTP response with `success: true`.
|
|
69
|
+
- **onResponseUnsuccess**: Callback function executed for unsuccessful HTTP responses, `success !== true`, or invalid JSON.
|
|
70
|
+
- **onValidationError**: Runs when client-side validation fails.
|
|
71
|
+
- **showLoaderButton**: Whether to display a loader in the submit button. Defaults to `true`.
|
|
72
|
+
- **scrollToFirstErroredInput**: Whether to scroll to the first input with an error. Defaults to `true`.
|
|
73
|
+
- **focusFirstErroredInput**: Whether to focus the first invalid field. Defaults to `false`.
|
|
74
|
+
- **customTypeError**: Custom error type.
|
|
75
|
+
- **loaderColor**: Color of the loader in the submit button.
|
|
76
|
+
- **logging**: Specifies whether to log data to the console. Defaults to false.
|
|
77
|
+
- **validateOn**: Field live-validation events: `submit`, `input`, `blur`, `change`.
|
|
78
|
+
- **revalidateOn**: Events used to re-check already invalid fields. Defaults to `input` and `change`.
|
|
79
|
+
- **validationDebounce**: Live-validation delay in milliseconds.
|
|
80
|
+
- **errorContainerAttribute**: Attribute containing the CSS selector for a custom error container. Defaults to
|
|
81
|
+
`data-error-container`.
|
|
82
|
+
- **validationStateAttribute**: Field state attribute: `validating`, `valid`, or `invalid`. Defaults to
|
|
83
|
+
`data-form-father-state`.
|
|
84
|
+
- **observeMutations**: Watches dynamically added fields and submit buttons.
|
|
85
|
+
|
|
86
|
+
## Form Submission
|
|
87
|
+
|
|
88
|
+
- Submit elements are `button[type="submit"]`, `button` without `type`, `input[type="submit"]`, and
|
|
89
|
+
`input[type="image"]`.
|
|
90
|
+
- `submit()` is public and returns `Promise<SubmitResult | undefined>`.
|
|
91
|
+
- For `method="GET"` and `method="HEAD"`, data is appended to the `action` query string and no request body is sent.
|
|
92
|
+
- `wrapData` applies to every supported `enctype`: `application/x-www-form-urlencoded`, `multipart/form-data`,
|
|
93
|
+
`text/plain`, and `application/json`.
|
|
94
|
+
- `onResponseSuccess` runs only for a successful HTTP response with `success: true`.
|
|
95
|
+
- `onResponseUnsuccess` runs for unsuccessful HTTP responses, `success !== true`, or invalid JSON responses.
|
|
96
|
+
|
|
97
|
+
## Methods
|
|
98
|
+
|
|
99
|
+
- **Form.initAll(selector, options)**: Initializes all forms matching a selector and reuses existing instances.
|
|
100
|
+
- **updateOptions(options)**: Updates one form instance and rebinds live validation.
|
|
101
|
+
- **validate()**: Validates the whole form.
|
|
102
|
+
- **validateField(field)**: Validates one field by name or DOM element.
|
|
103
|
+
- **showFieldError(field, message, source)**: Shows a field error manually.
|
|
104
|
+
- **getErrors()**: Returns current errors as `{ field, rule, message, source }`.
|
|
105
|
+
- **getValues()**: Returns form values as a plain object.
|
|
106
|
+
- **setValues(values)**: Fills fields by `name` and dispatches `input`/`change`.
|
|
107
|
+
- **clearErrors()**: Clears errors without changing field values.
|
|
108
|
+
- **reset(options)**: Calls native `form.reset()` and clears errors by default.
|
|
109
|
+
- **clearInputs()**: Clears all input fields in the form.
|
|
110
|
+
- **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.
|
|
111
|
+
- **destroy()**: Removes event listeners and runtime artifacts.
|
|
112
|
+
|
|
113
|
+
## Validation
|
|
114
|
+
|
|
115
|
+
```html
|
|
116
|
+
<input
|
|
117
|
+
name="email"
|
|
118
|
+
type="email"
|
|
119
|
+
data-validate="required|email"
|
|
120
|
+
data-error-required="Email is required"
|
|
121
|
+
data-error-email="Enter a valid email"
|
|
122
|
+
data-error-container="#email-error"
|
|
123
|
+
/>
|
|
124
|
+
<small id="email-error" hidden></small>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- `data-validate` accepts rules separated by `|`, spaces, or commas.
|
|
128
|
+
- `data-error-rule-name` overrides a message for one rule.
|
|
129
|
+
- `data-error-container` points to a custom error container without requiring wrapper markup.
|
|
130
|
+
- During async validation, fields receive `aria-busy="true"` and a `validating` state attribute; stale validator
|
|
131
|
+
responses are ignored.
|
|
132
|
+
- `data-custom-validate` is still supported for backward compatibility.
|
|
133
|
+
|
|
134
|
+
Final rule order:
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
required → schema.rules → data-validate → data-custom-validate
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Schema Adapters
|
|
141
|
+
|
|
142
|
+
Form Father has no schema-library runtime dependency, but can wrap Zod/Valibot/Yup-like schemas with `safeParse()` or
|
|
143
|
+
`parse()`.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { registerSchemaValidator } from 'form-father';
|
|
147
|
+
|
|
148
|
+
registerSchemaValidator('company-email', z.string().email(), 'Use a company email');
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
For simple checks, use `createFieldValidator(predicate, message)`.
|
|
152
|
+
|
|
153
|
+
## Demos and Recipes
|
|
154
|
+
|
|
155
|
+
- `demos/index.html` is a static login/callback/search/upload demo after `npm run build`.
|
|
156
|
+
- `npm run demos` builds the package and starts the Vite demo server.
|
|
157
|
+
- `docs/recipes/README.md` contains short API, data-attribute, adapter, and server-error recipes.
|
|
158
|
+
|
|
159
|
+
## Helpers
|
|
160
|
+
|
|
161
|
+
The library provides a number of utility functions:
|
|
162
|
+
|
|
163
|
+
- **serializeToFormData($element)**: Serializes form data into a `FormData` object.
|
|
164
|
+
- **isEmailValid(value)**: Checks if a string is a valid email address.
|
|
165
|
+
- **isUrlValid(value)**: Checks an `http(s)` URL, domain, IP, or `localhost`; the scheme may be omitted.
|
|
166
|
+
- **isPhoneValid(value)**: Checks if a string is a valid phone number.
|
|
167
|
+
- **closest($el, selector)**: Finds the closest parent element matching the given selector.
|
|
168
|
+
- **blockScrollBody()**: Blocks page scrolling.
|
|
169
|
+
- **unblockScrollBody()**: Unblocks page scrolling.
|
|
170
|
+
- **parseCommonResponseProperties(responseBody)**: Processes common properties from the server response.
|
|
171
|
+
- **serializeFormToJSON(form)**: Serializes form data into a plain object.
|
|
172
|
+
|
|
173
|
+
## Build and Development
|
|
174
|
+
|
|
175
|
+
**Rollup** is used for building the project. Main commands:
|
|
176
|
+
|
|
177
|
+
- **npm run build**: Builds the project.
|
|
178
|
+
- **npm run watch**: Builds the project in watch mode.
|
|
179
|
+
- **npm run demos**: Runs a demo version using Vite.
|
|
180
|
+
|
|
181
|
+
## Contributing
|
|
182
|
+
|
|
183
|
+
We welcome your suggestions and improvements! Please create [issues](https://github.com/poliklot/form-father/issues) and
|
|
184
|
+
submit [pull requests](https://github.com/poliklot/form-father/pulls).
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
[MIT](LICENSE)
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
© 2024 Poliklot
|
|
@@ -0,0 +1,89 @@
|
|
|
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
|
+
## Async validation state
|
|
53
|
+
|
|
54
|
+
```css
|
|
55
|
+
[data-form-father-state="validating"] {
|
|
56
|
+
opacity: 0.7;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[data-form-father-state="invalid"] {
|
|
60
|
+
border-color: #b3261e;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Async validators are race-safe: if an older validation finishes after a newer one started, Form Father ignores the stale
|
|
65
|
+
result and keeps the latest field state.
|
|
66
|
+
|
|
67
|
+
## Zod-like schema adapter
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { registerSchemaValidator } from 'form-father';
|
|
71
|
+
|
|
72
|
+
registerSchemaValidator('email-domain', z.string().email().endsWith('@company.com'), 'Use a company email');
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The adapter only expects a `safeParse(value)` or `parse(value)` method, so it can be used with Zod-like libraries without
|
|
76
|
+
adding runtime dependencies to Form Father.
|
|
77
|
+
|
|
78
|
+
## Server response errors
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"success": false,
|
|
83
|
+
"error": true,
|
|
84
|
+
"error-msg": "Please check the form",
|
|
85
|
+
"errors": [{ "name": "email", "error-msg": "Email is already registered" }]
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Field errors are shown through the same rendering path as client-side validation errors.
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "form-father",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Form Father: Библиотека для обработки форм",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"files": ["dist", "README.md", "LICENSE", "RESPONSE_API.md"],
|
|
6
|
+
"files": ["dist", "README.md", "LICENSE", "RESPONSE_API.md", "CHANGELOG.md", "docs", "demos"],
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
9
|
"import": "./dist/index.js",
|
|
@@ -14,8 +14,11 @@
|
|
|
14
14
|
"build": "rollup -c",
|
|
15
15
|
"watch": "rollup -c -w",
|
|
16
16
|
"release": "node scripts/release.js",
|
|
17
|
+
"release:check": "npm test -- --runInBand --watchman=false && npm run build && npm run smoke:package && npm run pack:dry-run",
|
|
17
18
|
"test": "jest --coverage",
|
|
18
19
|
"prepublishOnly": "npm test -- --runInBand --watchman=false && npm run build",
|
|
20
|
+
"pack:dry-run": "npm_config_cache=/tmp/form-father-npm-cache npm pack --dry-run",
|
|
21
|
+
"smoke:package": "node scripts/smoke-package.mjs",
|
|
19
22
|
"versions:sync": "node scripts/sync-version.js",
|
|
20
23
|
"demos": "BUILD_TARGET=demos npm run build && concurrently --kill-others \"vite\" \"npm run watch\""
|
|
21
24
|
},
|