adaptform 2.0.5 → 2.1.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 +103 -2
- package/CHANGELOG.ru.md +152 -0
- package/LICENSE.md +3 -1
- package/LICENSE.ru.md +25 -0
- package/README.md +271 -303
- package/README.ru.md +388 -0
- package/dist/{Form-Be3I6LX0.d.cts → Form-kPYL4f-n.d.cts} +58 -36
- package/dist/{Form-Be3I6LX0.d.ts → Form-kPYL4f-n.d.ts} +58 -36
- package/dist/index.cjs +184 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -6
- package/dist/index.d.ts +5 -6
- package/dist/index.mjs +183 -103
- package/dist/index.mjs.map +1 -1
- package/dist/luxon.d.cts +1 -1
- package/dist/luxon.d.ts +1 -1
- package/package.json +28 -9
package/README.ru.md
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
# AdaptForm
|
|
2
|
+
|
|
3
|
+
[English](README.md) · [Русский](README.ru.md)
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/adaptform)
|
|
6
|
+
[](https://www.npmjs.com/package/adaptform)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
|
|
10
|
+
**[Открыть интерактивное демо](https://arhimen.github.io/AdaptForm/?lang=ru)** · **[История изменений 2.1.0](https://github.com/ArhiMEN/AdaptForm/blob/master/CHANGELOG.ru.md)** · **[Исходный код демо](https://github.com/ArhiMEN/AdaptForm/tree/master/demo)**
|
|
11
|
+
|
|
12
|
+
Для сопровождающих: [настройка автоматической публикации в npm](docs/NPM_PUBLISH.ru.md).
|
|
13
|
+
|
|
14
|
+
AdaptForm — библиотека моделирования форм с приоритетом TypeScript, вдохновлённая Django Forms и Pydantic. Формы описываются классами с типизированными полями. Каждое поле управляет своим значением, приведением типов, валидацией, плагинами преобразования, ошибками и состоянием.
|
|
15
|
+
|
|
16
|
+
## Возможности
|
|
17
|
+
|
|
18
|
+
- Модели форм на основе классов с выводом типов значений в TypeScript.
|
|
19
|
+
- Проверки отдельных полей и зависимостей между полями.
|
|
20
|
+
- Условная обязательность полей и пользовательские синхронные валидаторы.
|
|
21
|
+
- Типы полей для строк, целых и десятичных чисел, логических значений и JavaScript Date.
|
|
22
|
+
- Динамические массивы полей и массивы вложенных форм.
|
|
23
|
+
- Плагины преобразования ввода, включая `MaskPlugin`.
|
|
24
|
+
- Сопоставление серверных ошибок через `ErrorsSchema`.
|
|
25
|
+
- Сериализация в JSON и `FormData`.
|
|
26
|
+
- Необязательная интеграция с Luxon через точку входа `adaptform/luxon`.
|
|
27
|
+
- Независимое от фреймворков ядро без обязательных зависимостей времени выполнения.
|
|
28
|
+
|
|
29
|
+
## Установка
|
|
30
|
+
|
|
31
|
+
Установите опубликованный npm-пакет:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install adaptform
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Интеграция с Luxon необязательна:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install luxon
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
[Интерактивное демо](https://arhimen.github.io/AdaptForm/?lang=ru) использует исходный код репозитория и автоматически публикуется из `v2.1.0` и `master`. В репозитории подготовлена версия `2.1.0`; npm продолжает предоставлять последнюю опубликованную версию до публикации нового выпуска сопровождающим проекта.
|
|
44
|
+
|
|
45
|
+
Демо поддерживает английский и русский языки с сохранением выбора: [English](https://arhimen.github.io/AdaptForm/?lang=en) · [Русский](https://arhimen.github.io/AdaptForm/?lang=ru). Переключение языка сохраняет данные формы и состояние валидации.
|
|
46
|
+
|
|
47
|
+
## Быстрый старт
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { BooleanType, Field, Form, StringType } from 'adaptform'
|
|
51
|
+
|
|
52
|
+
class RegistrationForm extends Form {
|
|
53
|
+
name = new Field(StringType, null, {
|
|
54
|
+
isRequired: true,
|
|
55
|
+
minLength: 2,
|
|
56
|
+
maxLength: 50,
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
email = new Field(StringType, null, {
|
|
60
|
+
isRequired: true,
|
|
61
|
+
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
acceptedTerms = new Field(BooleanType, false, {
|
|
65
|
+
mustBeTrue: true,
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const form = new RegistrationForm()
|
|
70
|
+
form.fields = {
|
|
71
|
+
name: 'Alex Morgan',
|
|
72
|
+
email: 'alex@example.com',
|
|
73
|
+
acceptedTerms: true,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (form.isValid) {
|
|
77
|
+
console.log(form.fieldsValue)
|
|
78
|
+
console.log(form.toJSON())
|
|
79
|
+
} else {
|
|
80
|
+
console.log(form.allErrors)
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`form.fields` присваивает переданные значения и проверяет полностью заполненную форму. Поля, отсутствующие в присваивании, сохраняют прежние значения.
|
|
85
|
+
|
|
86
|
+
## Типы полей и параметры
|
|
87
|
+
|
|
88
|
+
Все типы полей создаются через `new Field(TypeConstructor, defaultValue?, options?)`.
|
|
89
|
+
|
|
90
|
+
### StringType
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const username = new Field(StringType, null, {
|
|
94
|
+
isRequired: true,
|
|
95
|
+
minLength: 3,
|
|
96
|
+
maxLength: 32,
|
|
97
|
+
pattern: /^[a-z0-9_]+$/i,
|
|
98
|
+
messages: {
|
|
99
|
+
isRequired: 'Enter a username.',
|
|
100
|
+
minLength: 'Use at least 3 characters.',
|
|
101
|
+
pattern: 'Use only letters, numbers, and underscores.',
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Поддерживаемые ограничения: `isRequired`, `minLength`, `maxLength` и `pattern`.
|
|
107
|
+
|
|
108
|
+
### NumberType
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const age = new Field(NumberType, null, {
|
|
112
|
+
isRequired: true,
|
|
113
|
+
ge: 18,
|
|
114
|
+
le: 120,
|
|
115
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Поддерживаемые ограничения: `gt`, `ge`, `lt` и `le`. Значения должны быть конечными целыми числами; пустые строки и неконечные числа отклоняются.
|
|
119
|
+
|
|
120
|
+
### DecimalType
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
const price = new Field(DecimalType, null, {
|
|
124
|
+
decimalPlaces: 2,
|
|
125
|
+
maxDigits: 8,
|
|
126
|
+
ge: 0,
|
|
127
|
+
})
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Поддерживаются `gt`, `ge`, `lt`, `le`, `decimalPlaces` и `maxDigits`. Числовые значения с избыточной точностью отклоняются без скрытого округления. В строковых значениях десятичным разделителем может быть запятая или точка.
|
|
131
|
+
|
|
132
|
+
### BooleanType
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
const accepted = new Field(BooleanType, false, {
|
|
136
|
+
mustBeTrue: true,
|
|
137
|
+
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Поддерживаются `mustBeTrue` и `mustBeFalse`.
|
|
141
|
+
|
|
142
|
+
### DateType
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const birthday = new Field(DateType, null, {
|
|
146
|
+
isDatetime: false,
|
|
147
|
+
minDate: new Date('1900-01-01T00:00:00.000Z'),
|
|
148
|
+
maxDate: new Date(),
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Поддерживаются `minDate`, `maxDate` и `isDatetime`. При `isDatetime: false` сравниваются календарные даты; при `true` — временные метки. Некорректные даты отклоняются.
|
|
153
|
+
|
|
154
|
+
### Пользовательская валидация и сообщения
|
|
155
|
+
|
|
156
|
+
Параметры поля поддерживают `isRequired`, `messages` и синхронную функцию `validate(value, form)`. Пользовательский валидатор возвращает `true` при успехе, `false` для настроенного сообщения валидации или строку с конкретной ошибкой.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
class PasswordForm extends Form {
|
|
160
|
+
password = new Field(StringType, null, { minLength: 8 })
|
|
161
|
+
|
|
162
|
+
confirmation = new Field(StringType, null, {
|
|
163
|
+
isRequired: true,
|
|
164
|
+
validate: (value, form) =>
|
|
165
|
+
value === form?.password.valueClear || 'Passwords do not match.',
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Валидаторы синхронные; возврат Promise не поддерживается. Для асинхронных проверок, например уникальности на сервере, вызовите сервис отдельно и примените ответ через API ошибок формы.
|
|
171
|
+
|
|
172
|
+
## Жизненный цикл валидации и состояние
|
|
173
|
+
|
|
174
|
+
- Значения по умолчанию начинают со состояния «не изменено».
|
|
175
|
+
- Пустые строки и строки только из пробелов считаются пустыми значениями.
|
|
176
|
+
- Глобальный и липкий флаги регулярных выражений не влияют на повторные проверки строк.
|
|
177
|
+
- `form.checkValid()` проверяет каждое поле и возвращает `false`, если хотя бы одно поле некорректно или остались общие серверные ошибки.
|
|
178
|
+
- Чтение `isValid` не запускает валидаторы. Поля без ненулевого значения по умолчанию начинают с `isValid === null`; ненулевые значения конструктора проходят приведение и валидацию, но остаются неизменёнными по состоянию. После `reset()` корректность поля не определена до следующей проверки.
|
|
179
|
+
- Массовое присваивание запускает проверку после установки значений непосредственных полей, поэтому правила условной обязательности видят полное состояние формы.
|
|
180
|
+
- Если зависимые поля меняются по отдельности, после изменений вызовите `form.checkValid()` для пересчёта зависимых правил.
|
|
181
|
+
- `field.setRawValue(value, false)` и `form.setFields(data, false)` позволяют отложить валидацию. Прежде чем полагаться на корректность формы, вызовите `checkValid()`.
|
|
182
|
+
- `reset()` восстанавливает значения по умолчанию, очищает ошибки и оставляет корректность неопределённой до следующей проверки. Корректность коллекции после сброса равна false до её повторной проверки.
|
|
183
|
+
- Добавление, удаление и очистка элементов обновляют валидацию коллекции. Некорректные индексы удаления игнорируются.
|
|
184
|
+
|
|
185
|
+
## Динамические коллекции
|
|
186
|
+
|
|
187
|
+
### ArrayField
|
|
188
|
+
|
|
189
|
+
Используйте `ArrayField` для динамического списка однотипных полей.
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { ArrayField, Field, StringType } from 'adaptform'
|
|
193
|
+
|
|
194
|
+
const tags = new ArrayField(
|
|
195
|
+
(value?: string) => new Field(StringType, value, {
|
|
196
|
+
isRequired: true,
|
|
197
|
+
minLength: 2,
|
|
198
|
+
}),
|
|
199
|
+
{ minItems: 1, maxItems: 5 },
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
tags.add('typescript')
|
|
203
|
+
tags.add('forms')
|
|
204
|
+
console.log(tags.itemsValue)
|
|
205
|
+
console.log(tags.isValid)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Параметры включают `isRequired`, `minItems`, `maxItems`, `messages` и синхронную функцию `validate(values, form)`. `itemsValue` возвращает массив, элементы которого могут быть `null`.
|
|
209
|
+
|
|
210
|
+
### ArrayFormField
|
|
211
|
+
|
|
212
|
+
Используйте `ArrayFormField` для списка экземпляров вложенных форм.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { ArrayFormField, Field, Form, StringType } from 'adaptform'
|
|
216
|
+
|
|
217
|
+
class AddressForm extends Form {
|
|
218
|
+
city = new Field(StringType, null, { isRequired: true })
|
|
219
|
+
postalCode = new Field(StringType, null, { isRequired: true })
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const addresses = new ArrayFormField(AddressForm, {
|
|
223
|
+
minForms: 1,
|
|
224
|
+
maxForms: 3,
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
addresses.add({ city: 'Berlin', postalCode: '10115' })
|
|
228
|
+
console.log(addresses.formsFieldsValue)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Параметры включают `isRequired`, `minForms`, `maxForms`, `messages` и синхронную функцию `validate(forms, form)`.
|
|
232
|
+
|
|
233
|
+
## Маски ввода
|
|
234
|
+
|
|
235
|
+
Плагины преобразуют ввод до приведения типов и валидации. Встроенный `MaskPlugin` поддерживает постоянные символы маски и позиции для цифр или символов по пользовательскому шаблону.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { Field, MaskPlugin, StringType } from 'adaptform'
|
|
239
|
+
|
|
240
|
+
const phone = new Field(StringType, null, {
|
|
241
|
+
plugins: [
|
|
242
|
+
new MaskPlugin({
|
|
243
|
+
maskFormat: '+1 (___) ___-____',
|
|
244
|
+
maskPlaceholder: '_',
|
|
245
|
+
digitPattern: /\d/,
|
|
246
|
+
}),
|
|
247
|
+
],
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
phone.rawValue = '4155550134'
|
|
251
|
+
console.log(phone.rawValue)
|
|
252
|
+
console.log(phone.valueClear)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Серверные ошибки и ErrorsSchema
|
|
256
|
+
|
|
257
|
+
`ErrorsSchema` сопоставляет ключи ошибок сервера с полями формы. По умолчанию для ключей без соответствия сохраняются исходные имена.
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import { ErrorsSchema, Form, Field, StringType } from 'adaptform'
|
|
261
|
+
|
|
262
|
+
class AccountForm extends Form {
|
|
263
|
+
email = new Field(StringType)
|
|
264
|
+
username = new Field(StringType)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const form = new AccountForm()
|
|
268
|
+
form.errorsShema = new ErrorsSchema()
|
|
269
|
+
.staticKey('email_address', 'email')
|
|
270
|
+
.staticKey('user_name', 'username')
|
|
271
|
+
.fallback(key => key)
|
|
272
|
+
|
|
273
|
+
form.errors = {
|
|
274
|
+
email_address: 'This email is already registered.',
|
|
275
|
+
user_name: 'This username is unavailable.',
|
|
276
|
+
request_id: 'The request could not be completed.',
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
console.log(form.allErrors)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Доступные методы схемы:
|
|
283
|
+
|
|
284
|
+
- `staticKey(errorKey, fieldName)` сопоставляет точный ключ.
|
|
285
|
+
- `dynamicKey(pattern, mapping, options?)` сопоставляет ключи по регулярному выражению с необязательным приоритетом.
|
|
286
|
+
- `fallback(handler)` обрабатывает ключи, не подошедшие под настроенные правила.
|
|
287
|
+
- `transformValue(transformer)` нормализует значения серверных ошибок до сопоставления.
|
|
288
|
+
- `nest(prefix)` создаёт схему для вложенных путей полей.
|
|
289
|
+
- `clone()` и `merge(other)` позволяют составлять схемы.
|
|
290
|
+
- `ErrorsSchema.fromObject(mapping)` создаёт схему из таблицы соответствий ключей и полей.
|
|
291
|
+
- `ErrorsSchema.forArray(itemSchema, arrayFieldName, indexExtractor?)` сопоставляет ошибки элементов массива, включая индексы в скобках и через точку.
|
|
292
|
+
|
|
293
|
+
Присваивание нового объекта `form.errors` очищает предыдущие ошибки перед применением нового ответа. Неизвестные ключи полей становятся общими ошибками формы.
|
|
294
|
+
|
|
295
|
+
## Сериализация
|
|
296
|
+
|
|
297
|
+
### JSON
|
|
298
|
+
|
|
299
|
+
`toJSON()` возвращает JSON-совместимый объект. Экземпляры JavaScript `Date` рекурсивно преобразуются в строки ISO 8601, включая даты внутри массивов и обычных объектов.
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
const payload = form.toJSON()
|
|
303
|
+
await fetch('/api/profile', {
|
|
304
|
+
method: 'POST',
|
|
305
|
+
headers: { 'Content-Type': 'application/json' },
|
|
306
|
+
body: JSON.stringify(payload),
|
|
307
|
+
})
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### FormData
|
|
311
|
+
|
|
312
|
+
`toFormData()` пропускает `null` и `undefined`, добавляет скалярные значения как строки, сериализует даты в ISO 8601, сохраняет значения `Blob` и `File`, а массивы и вложенные объекты кодирует в JSON.
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
const body = form.toFormData()
|
|
316
|
+
await fetch('/api/profile', {
|
|
317
|
+
method: 'POST',
|
|
318
|
+
body,
|
|
319
|
+
})
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Не задавайте multipart-заголовок `Content-Type` вручную: браузер сам добавит разделитель boundary.
|
|
323
|
+
|
|
324
|
+
## Необязательная интеграция с Luxon
|
|
325
|
+
|
|
326
|
+
Установите Luxon отдельно, затем импортируйте дополнительный тип даты из выделенной точки входа:
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
npm install luxon
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { Field } from 'adaptform'
|
|
334
|
+
import { DateLuxonType } from 'adaptform/luxon'
|
|
335
|
+
import { DateTime } from 'luxon'
|
|
336
|
+
|
|
337
|
+
const date = new Field(DateLuxonType, null, {
|
|
338
|
+
isDatetime: false,
|
|
339
|
+
minDate: DateTime.fromISO('1900-01-01'),
|
|
340
|
+
maxDate: DateTime.now(),
|
|
341
|
+
inputFormat: 'dd.MM.yyyy',
|
|
342
|
+
})
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Обзор API
|
|
346
|
+
|
|
347
|
+
| Класс | Основной API |
|
|
348
|
+
| --- | --- |
|
|
349
|
+
| `Form` | `fields`, `setFields()`, `fieldsValue`, `checkValid()`, `isValid`, `allErrors`, `globalErrors`, `errors`, `setGlobalError()`, `clearGlobalErrors()`, `reset()`, `toJSON()`, `toFormData()` |
|
|
350
|
+
| `Field` | `rawValue`, `setRawValue()`, `valueClear`, `errors`, `error`, `isValid`, `isTouched`, `isEmpty`, `checkValid()`, `reset()` |
|
|
351
|
+
| `ArrayField` | `items`, `itemsValue`, `add()`, `remove()`, `clear()`, `checkValid()`, `reset()` |
|
|
352
|
+
| `ArrayFormField` | `forms`, `formsFieldsValue`, `add()`, `remove()`, `clear()`, `checkValid()`, `reset()` |
|
|
353
|
+
| `ErrorsSchema` | `staticKey()`, `dynamicKey()`, `fallback()`, `transformValue()`, `parseErrors()`, `parseErrorsDetailed()`, `nest()`, `clone()`, `merge()`, `forArray()`, `fromObject()` |
|
|
354
|
+
|
|
355
|
+
## Разработка
|
|
356
|
+
|
|
357
|
+
Требуются Node.js 22 (используется в CI) и npm.
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
npm ci
|
|
361
|
+
npm run lint
|
|
362
|
+
npm test
|
|
363
|
+
npm run build
|
|
364
|
+
npx tsc --noEmit -p tsconfig.json
|
|
365
|
+
npm run check:demo
|
|
366
|
+
npm run build:demo
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Для локального запуска интерактивного демо:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
npm run dev -- --config demo/vite.config.ts
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Демо собирается GitHub Actions из `v2.1.0` и `master`. GitHub Pages разрешает обеим веткам публиковать страницу по одному адресу; см. [Демо и GitHub Pages](https://github.com/ArhiMEN/AdaptForm/blob/master/docs/DEMO.ru.md).
|
|
376
|
+
|
|
377
|
+
## Ссылки
|
|
378
|
+
|
|
379
|
+
- **[Интерактивное демо](https://arhimen.github.io/AdaptForm/?lang=ru)**
|
|
380
|
+
- [Репозиторий GitHub](https://github.com/ArhiMEN/AdaptForm)
|
|
381
|
+
- [Пакет npm](https://www.npmjs.com/package/adaptform)
|
|
382
|
+
- [Задачи и ошибки](https://github.com/ArhiMEN/AdaptForm/issues)
|
|
383
|
+
- [История изменений](https://github.com/ArhiMEN/AdaptForm/blob/master/CHANGELOG.ru.md)
|
|
384
|
+
- [Лицензия](https://github.com/ArhiMEN/AdaptForm/blob/master/LICENSE.ru.md)
|
|
385
|
+
|
|
386
|
+
## Лицензия
|
|
387
|
+
|
|
388
|
+
MIT © Stanislav Orlov
|
|
@@ -14,10 +14,11 @@ declare class ArrayField<T = any> {
|
|
|
14
14
|
protected options: ArrayFieldOptions;
|
|
15
15
|
protected _items: Field<T>[];
|
|
16
16
|
protected _errors: string[];
|
|
17
|
+
private _hasValidationResult;
|
|
17
18
|
isTouched: boolean;
|
|
18
|
-
constructor(factory: (value?: T) => Field<T>, options?: ArrayFieldOptions, initial?: T[]);
|
|
19
|
-
set itemsValue(items: T[]);
|
|
20
|
-
get itemsValue(): T[];
|
|
19
|
+
constructor(factory: (value?: T) => Field<T>, options?: ArrayFieldOptions, initial?: (T | null)[]);
|
|
20
|
+
set itemsValue(items: (T | null)[]);
|
|
21
|
+
get itemsValue(): (T | null)[];
|
|
21
22
|
get items(): Field<T>[];
|
|
22
23
|
add(value?: T, force_check?: boolean): Field<T>;
|
|
23
24
|
remove(index: number): void;
|
|
@@ -37,12 +38,14 @@ declare class ArrayFormField<T extends Form = Form> {
|
|
|
37
38
|
private options;
|
|
38
39
|
private _forms;
|
|
39
40
|
private _errors;
|
|
41
|
+
private _hasValidationResult;
|
|
40
42
|
isTouched: boolean;
|
|
41
|
-
constructor(FormConstructor: new () => T, options?: ArrayFormFieldOptions, initial?:
|
|
42
|
-
set forms(forms:
|
|
43
|
-
|
|
44
|
-
get
|
|
45
|
-
|
|
43
|
+
constructor(FormConstructor: new () => T, options?: ArrayFormFieldOptions, initial?: unknown[]);
|
|
44
|
+
set forms(forms: unknown[]);
|
|
45
|
+
setForms(forms: unknown[], validate?: boolean): void;
|
|
46
|
+
get forms(): T[];
|
|
47
|
+
get formsFieldsValue(): FormFieldsValue<T>[];
|
|
48
|
+
add(data?: unknown, force_check?: boolean): T;
|
|
46
49
|
remove(index: number): void;
|
|
47
50
|
clear(): void;
|
|
48
51
|
reset(): void;
|
|
@@ -181,10 +184,12 @@ declare class Field<T = any, O extends FieldOptions = FieldOptions> {
|
|
|
181
184
|
set options(options: O);
|
|
182
185
|
get rawValue(): T | null;
|
|
183
186
|
set rawValue(input: any);
|
|
187
|
+
setRawValue(input: any, validate?: boolean): void;
|
|
188
|
+
private assignRawValue;
|
|
184
189
|
get errors(): string[];
|
|
185
190
|
get error(): string;
|
|
186
191
|
set error(message: string | null);
|
|
187
|
-
get valueClear():
|
|
192
|
+
get valueClear(): T | null;
|
|
188
193
|
private isRequired;
|
|
189
194
|
checkValid(form?: Form): boolean;
|
|
190
195
|
checkAndSetValid(form?: Form): boolean;
|
|
@@ -192,92 +197,108 @@ declare class Field<T = any, O extends FieldOptions = FieldOptions> {
|
|
|
192
197
|
reset(): void;
|
|
193
198
|
}
|
|
194
199
|
|
|
200
|
+
type ErrorFieldMapping = string | ((errorValue: any) => string);
|
|
195
201
|
interface ParsedError {
|
|
196
202
|
fieldName: string;
|
|
197
203
|
message: string;
|
|
198
204
|
sourceKey: string;
|
|
199
205
|
matchedBy: 'static' | 'dynamic' | 'fallback';
|
|
200
206
|
}
|
|
207
|
+
interface DynamicRule {
|
|
208
|
+
pattern: RegExp;
|
|
209
|
+
mapping: ErrorFieldMapping | ((match: RegExpMatchArray, value?: any) => string);
|
|
210
|
+
priority: number;
|
|
211
|
+
}
|
|
201
212
|
declare class ErrorsSchema {
|
|
202
213
|
private _staticRules;
|
|
203
214
|
private _dynamicRules;
|
|
204
215
|
private _fallbackHandler?;
|
|
205
216
|
private _valueTransformers;
|
|
206
217
|
/**
|
|
207
|
-
*
|
|
208
|
-
* @param errorKey -
|
|
209
|
-
* @param fieldName -
|
|
218
|
+
* Add a static rule that matches an exact error key.
|
|
219
|
+
* @param errorKey - Exact error key returned by the server.
|
|
220
|
+
* @param fieldName - Form field name or a function that resolves it dynamically.
|
|
210
221
|
*/
|
|
211
222
|
staticKey(errorKey: string, fieldName: string | ((errorValue: any) => string)): this;
|
|
212
223
|
/**
|
|
213
|
-
*
|
|
214
|
-
* @param pattern -
|
|
215
|
-
* @param mapping -
|
|
216
|
-
* @param options -
|
|
224
|
+
* Add a dynamic rule using a regular expression.
|
|
225
|
+
* @param pattern - Regular expression used to match an error key.
|
|
226
|
+
* @param mapping - Field name or function that derives it from the match.
|
|
227
|
+
* @param options - Additional options, such as rule priority.
|
|
217
228
|
*/
|
|
218
|
-
dynamicKey(pattern: RegExp, mapping: string | ((match: RegExpMatchArray) => string), options?: {
|
|
229
|
+
dynamicKey(pattern: RegExp, mapping: string | ((match: RegExpMatchArray, value?: any) => string), options?: {
|
|
219
230
|
priority?: number;
|
|
220
231
|
}): this;
|
|
221
232
|
/**
|
|
222
|
-
*
|
|
233
|
+
* Set a handler for errors that do not match any rule.
|
|
223
234
|
*/
|
|
224
235
|
fallback(handler: (key: string, value: any) => string | null): this;
|
|
225
236
|
/**
|
|
226
|
-
*
|
|
227
|
-
*
|
|
237
|
+
* Add a transformer for error values.
|
|
238
|
+
* Useful for normalizing messages, such as server responses containing arrays.
|
|
228
239
|
*/
|
|
229
240
|
transformValue(transformer: (value: any) => any): this;
|
|
230
241
|
/**
|
|
231
|
-
*
|
|
242
|
+
* Create a schema from a simple mapping object.
|
|
232
243
|
*/
|
|
233
244
|
static fromObject(mapping: Record<string, string>): ErrorsSchema;
|
|
234
245
|
/**
|
|
235
|
-
*
|
|
246
|
+
* Create a schema for an array of similarly structured forms.
|
|
236
247
|
*/
|
|
237
248
|
static forArray(itemSchema: ErrorsSchema, arrayFieldName: string, indexExtractor?: (key: string) => number): ErrorsSchema;
|
|
238
249
|
/**
|
|
239
|
-
*
|
|
240
|
-
*
|
|
250
|
+
* Parse errors while applying configured value transformers.
|
|
251
|
+
* Supports nested structures using dot notation.
|
|
241
252
|
*/
|
|
242
253
|
parseErrors(errors: Record<string, any>, prefix?: string): Record<string, string>;
|
|
243
254
|
/**
|
|
244
|
-
*
|
|
255
|
+
* Parse server errors and return detailed metadata.
|
|
245
256
|
*/
|
|
246
257
|
parseErrorsDetailed(errors: Record<string, any>, prefix?: string): Record<string, ParsedError>;
|
|
247
258
|
/**
|
|
248
|
-
*
|
|
259
|
+
* Create a schema for a nested form.
|
|
249
260
|
*/
|
|
250
261
|
nest(prefix: string): ErrorsSchema;
|
|
251
262
|
/**
|
|
252
|
-
*
|
|
263
|
+
* Clone the current schema.
|
|
253
264
|
*/
|
|
254
265
|
clone(): ErrorsSchema;
|
|
255
266
|
/**
|
|
256
|
-
*
|
|
267
|
+
* Merge the current schema with another schema.
|
|
257
268
|
*/
|
|
258
269
|
merge(other: ErrorsSchema): ErrorsSchema;
|
|
259
270
|
private isNestedObject;
|
|
260
271
|
private findMappingDetailed;
|
|
261
272
|
private findMapping;
|
|
262
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* Default base schema.
|
|
276
|
+
*/
|
|
277
|
+
declare const DefaultErrorsSchema: ErrorsSchema;
|
|
263
278
|
|
|
279
|
+
type FormFieldValue<TField> = TField extends Field<infer TValue, infer _Options> ? TValue | null : TField extends ArrayField<infer TValue> ? (TValue | null)[] : TField extends ArrayFormField<infer TForm> ? TForm extends Form ? FormFieldsValue<TForm>[] : unknown[] : never;
|
|
280
|
+
type FormFieldsValue<TForm extends Form> = {
|
|
281
|
+
[K in keyof TForm as TForm[K] extends Field<infer _Value, infer _Options> | ArrayField<infer _ArrayValue> | ArrayFormField<infer _NestedForm> ? K : never]: FormFieldValue<TForm[K]>;
|
|
282
|
+
};
|
|
264
283
|
declare abstract class Form {
|
|
265
284
|
private _globalErrors;
|
|
266
285
|
errorsShema: ErrorsSchema;
|
|
267
286
|
protected get _fields(): Record<string, Field<any> | ArrayField<any> | ArrayFormField<any>>;
|
|
268
287
|
checkValid(form?: this): boolean;
|
|
269
|
-
get fieldsValue():
|
|
270
|
-
set fields(data: Record<string,
|
|
288
|
+
get fieldsValue(): FormFieldsValue<this>;
|
|
289
|
+
set fields(data: Record<string, unknown>);
|
|
290
|
+
setFields(data: Record<string, unknown>, validate?: boolean): void;
|
|
271
291
|
get allErrors(): Record<string, string[]>;
|
|
272
292
|
/**
|
|
273
|
-
*
|
|
293
|
+
* Recursively collect all errors from an ArrayFormField.
|
|
274
294
|
*/
|
|
275
295
|
private collectArrayFormErrors;
|
|
276
296
|
get globalErrors(): Record<string, string>;
|
|
277
|
-
set errors(errors: Record<string,
|
|
297
|
+
set errors(errors: Record<string, unknown>);
|
|
298
|
+
private clearErrorsRecursively;
|
|
278
299
|
/**
|
|
279
|
-
*
|
|
280
|
-
*
|
|
300
|
+
* Apply an error to the corresponding field.
|
|
301
|
+
* Supports nested fields using dot notation.
|
|
281
302
|
*/
|
|
282
303
|
private applyErrorToField;
|
|
283
304
|
private applyNestedError;
|
|
@@ -287,7 +308,8 @@ declare abstract class Form {
|
|
|
287
308
|
get isTouched(): boolean;
|
|
288
309
|
get isValid(): boolean;
|
|
289
310
|
toFormData(): FormData;
|
|
290
|
-
toJSON(): Record<string,
|
|
311
|
+
toJSON(): Record<string, unknown>;
|
|
312
|
+
private serializeJsonValue;
|
|
291
313
|
}
|
|
292
314
|
|
|
293
|
-
export { ArrayField as A, BasePlugin as B, type DecimalOptions as D, Field as F, type MaskPluginOptions as M, type NumberOptions as N, type PluginOptions as P, type StringOptions as S, FieldType as a, type BooleanOptions as b, type DateOptions as c, type ArrayFieldOptions as d, ArrayFormField as e, type ArrayFormFieldOptions as f, type FieldOptions as
|
|
315
|
+
export { ArrayField as A, BasePlugin as B, type DecimalOptions as D, ErrorsSchema as E, Field as F, type MaskPluginOptions as M, type NumberOptions as N, type PluginOptions as P, type StringOptions as S, FieldType as a, type BooleanOptions as b, type DateOptions as c, type ArrayFieldOptions as d, ArrayFormField as e, type ArrayFormFieldOptions as f, DefaultErrorsSchema as g, type DynamicRule as h, type FieldOptions as i, type FieldPlugin as j, Form as k, type FormFieldValue as l, type FormFieldsValue as m, type ParsedError as n, type messagesArrayFieldOptions as o, type messagesArrayFormFieldOptions as p, type messagesBooleanOptions as q, type messagesDateOptions as r, type messagesDecimalOptions as s, type messagesNumberOptions as t, type messagesOptions as u, type messagesStringOptions as v };
|