form-father 0.2.11 → 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/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 {};
@@ -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
- * Порядок для КАЖДОГО `<input>`:
272
+ * Порядок для каждого поля:
150
273
  *
151
274
  * 1. `required` — если атрибут `required`
152
275
  * 2. правила из `validationSchema`
153
- * 3. правила из `data-custom-validate`
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
- private submit;
324
+ submit(): Promise<SubmitResult | undefined>;
201
325
  }
@@ -1,9 +1,10 @@
1
1
  export type ValidatorEffectCtx = {
2
2
  value: string;
3
- $input: HTMLInputElement | HTMLTextAreaElement;
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: HTMLInputElement | HTMLTextAreaElement, $form: HTMLElement, params?: any) => boolean | Promise<boolean> | ValidatorResult | Promise<ValidatorResult>;
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
+ [![npm version](https://img.shields.io/npm/v/form-father)](https://www.npmjs.com/package/form-father)
4
+ [![npm downloads](https://img.shields.io/npm/dm/form-father)](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.2.11",
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",