@letar/forms 1.0.3 → 1.2.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +331 -0
  2. package/README.md +9 -9
  3. package/README.ru.md +389 -0
  4. package/analytics.js +3 -0
  5. package/analytics.js.map +1 -0
  6. package/chunk-2PSXYC3I.js +1782 -0
  7. package/chunk-2PSXYC3I.js.map +1 -0
  8. package/chunk-5D6S6EGF.js +206 -0
  9. package/chunk-5D6S6EGF.js.map +1 -0
  10. package/chunk-6E7VJAJT.js +78 -0
  11. package/chunk-6E7VJAJT.js.map +1 -0
  12. package/chunk-CGXKRCSM.js +117 -0
  13. package/chunk-CGXKRCSM.js.map +1 -0
  14. package/chunk-DQUVUMCX.js +982 -0
  15. package/chunk-DQUVUMCX.js.map +1 -0
  16. package/chunk-K3J4L26K.js +345 -0
  17. package/chunk-K3J4L26K.js.map +1 -0
  18. package/chunk-MAYUFA5K.js +822 -0
  19. package/chunk-MAYUFA5K.js.map +1 -0
  20. package/{chunk-4V6WBJ76.js → chunk-MVGXZNHP.js} +2 -2
  21. package/{chunk-4V6WBJ76.js.map → chunk-MVGXZNHP.js.map} +1 -1
  22. package/chunk-MZDTJSF7.js +299 -0
  23. package/chunk-MZDTJSF7.js.map +1 -0
  24. package/chunk-Q5EOF36Y.js +709 -0
  25. package/chunk-Q5EOF36Y.js.map +1 -0
  26. package/{chunk-7FEQFDJ7.js → chunk-R2RTCKXY.js} +2 -2
  27. package/{chunk-7FEQFDJ7.js.map → chunk-R2RTCKXY.js.map} +1 -1
  28. package/chunk-XFWLD5EO.js +1045 -0
  29. package/chunk-XFWLD5EO.js.map +1 -0
  30. package/fields/boolean.js +5 -0
  31. package/fields/boolean.js.map +1 -0
  32. package/fields/datetime.js +5 -0
  33. package/fields/datetime.js.map +1 -0
  34. package/fields/number.js +5 -0
  35. package/fields/number.js.map +1 -0
  36. package/fields/selection.js +5 -0
  37. package/fields/selection.js.map +1 -0
  38. package/fields/specialized.js +5 -0
  39. package/fields/specialized.js.map +1 -0
  40. package/fields/text.js +5 -0
  41. package/fields/text.js.map +1 -0
  42. package/hcaptcha-U4XIT3HS.js +64 -0
  43. package/hcaptcha-U4XIT3HS.js.map +1 -0
  44. package/i18n.js +1 -1
  45. package/index.js +3736 -4962
  46. package/index.js.map +1 -1
  47. package/offline.js +1 -1
  48. package/package.json +59 -4
  49. package/recaptcha-PKAUAY2S.js +56 -0
  50. package/recaptcha-PKAUAY2S.js.map +1 -0
  51. package/server-errors.js +3 -0
  52. package/server-errors.js.map +1 -0
  53. package/turnstile-7FXTBSLW.js +36 -0
  54. package/turnstile-7FXTBSLW.js.map +1 -0
  55. package/validators/ru.js +73 -0
  56. package/validators/ru.js.map +1 -0
package/README.ru.md ADDED
@@ -0,0 +1,389 @@
1
+ # @lena/form-components
2
+
3
+ Переиспользуемая UI-библиотека компонентов форм на базе TanStack Form для монорепозитория Lena.
4
+
5
+ [English documentation](./README.en.md)
6
+
7
+ ## Quick Start
8
+
9
+ ```tsx
10
+ import { Form } from '@lena/form-components'
11
+ import { z } from 'zod/v4'
12
+
13
+ const Schema = z.object({
14
+ title: z.string().min(2).meta({ ui: { title: 'Название', placeholder: 'Введите...' } }),
15
+ rating: z.number().min(0).max(10).meta({ ui: { title: 'Рейтинг' } }),
16
+ })
17
+
18
+ <Form schema={Schema} initialValue={{ title: '', rating: 5 }} onSubmit={save}>
19
+ <Form.Field.String name="title" />
20
+ <Form.Field.Number name="rating" />
21
+ <Form.Button.Submit>Сохранить</Form.Button.Submit>
22
+ </Form>
23
+ ```
24
+
25
+ **Или полная автогенерация:**
26
+
27
+ ```tsx
28
+ <Form.FromSchema schema={Schema} initialValue={data} onSubmit={handleSubmit} submitLabel="Создать" />
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Философия: Отделение вёрстки от логики
34
+
35
+ | Аспект | Где определяется | Как используется в JSX |
36
+ | ----------------- | -------------------------- | ------------------------------- |
37
+ | **Валидация** | Zod схема | `schema={Schema}` |
38
+ | **UI метаданные** | Zod `.meta({ ui: {...} })` | Автоматически из схемы |
39
+ | **Структура** | TypeScript типы | `initialValue={data}` |
40
+ | **Вёрстка** | JSX | `<HStack>`, `<VStack>`, `<Box>` |
41
+
42
+ **Результат:** JSX содержит только вёрстку и имена полей. Вся логика живёт в схеме.
43
+
44
+ ---
45
+
46
+ ## Документация
47
+
48
+ | Категория | Документация | Описание |
49
+ | ---------------- | -------------------------------------------------------- | --------------------------------------------- |
50
+ | Field компоненты | [docs/fields.md](./docs/fields.md) | 50+ типов полей (String, Number, Select, ...) |
51
+ | Form-level | [docs/form-level.md](./docs/form-level.md) | Steps, When, Watch, Errors, Persistence |
52
+ | Schema генерация | [docs/schema-generation.md](./docs/schema-generation.md) | FromSchema, AutoFields, Builder, Templates |
53
+ | Server Errors | [docs/server-errors.md](./docs/server-errors.md) | Маппинг Prisma/ZenStack/Zod ошибок на поля |
54
+ | Offline | [docs/offline.md](./docs/offline.md) | Оффлайн режим, очередь синхронизации |
55
+ | ZenStack | [docs/zenstack.md](./docs/zenstack.md) | Плагин, @form.\* директивы, withUIMeta |
56
+ | i18n | [docs/i18n.md](./docs/i18n.md) | Мультиязычность, перевод ошибок валидации |
57
+ | API Reference | [docs/api-reference.md](./docs/api-reference.md) | Хуки, контексты, типы |
58
+
59
+ ---
60
+
61
+ ## Основные возможности
62
+
63
+ ### 40+ Field компонентов
64
+
65
+ ```tsx
66
+ // Текстовые
67
+ <Form.Field.String name="title" />
68
+ <Form.Field.Textarea name="description" />
69
+ <Form.Field.RichText name="content" />
70
+
71
+ // Числовые
72
+ <Form.Field.Number name="price" />
73
+ <Form.Field.Slider name="rating" />
74
+ <Form.Field.Currency name="amount" />
75
+
76
+ // Выбор
77
+ <Form.Field.Select name="category" />
78
+ <Form.Field.RadioGroup name="type" />
79
+ <Form.Field.Checkbox name="agree" />
80
+
81
+ // Специальные
82
+ <Form.Field.Date name="birthday" />
83
+ <Form.Field.Phone name="phone" />
84
+ <Form.Field.FileUpload name="avatar" />
85
+ <Form.Field.Signature name="signature" />
86
+ <Form.Field.CreditCard name="card" />
87
+
88
+ // Защита
89
+ <Form.Captcha />
90
+ ```
91
+
92
+ [Полный список → docs/fields.md](./docs/fields.md)
93
+
94
+ ### Form-level компоненты
95
+
96
+ ```tsx
97
+ <Form schema={Schema} initialValue={data} onSubmit={save}>
98
+ {/* Реактивные побочные эффекты */}
99
+ <Form.Watch
100
+ field="name"
101
+ onChange={(v, { setFieldValue }) => {
102
+ setFieldValue('slug', transliterate(String(v)))
103
+ }}
104
+ />
105
+
106
+ {/* Условный рендеринг */}
107
+ <Form.When field="type" is="company">
108
+ <Form.Field.String name="companyName" />
109
+ </Form.When>
110
+
111
+ {/* Мультистеп формы */}
112
+ <Form.Steps animated validateOnNext>
113
+ <Form.Steps.Step title="Шаг 1">...</Form.Steps.Step>
114
+ <Form.Steps.Step title="Шаг 2">...</Form.Steps.Step>
115
+ <Form.Steps.Navigation />
116
+ </Form.Steps>
117
+
118
+ {/* Информационный блок */}
119
+ <Form.InfoBlock variant="info" title="Подсказка">
120
+ Заполните все поля для скидки.
121
+ </Form.InfoBlock>
122
+
123
+ {/* Разделитель секций */}
124
+ <Form.Divider label="Контакты" />
125
+
126
+ {/* Вычисляемые поля */}
127
+ <Form.Field.Calculated
128
+ name="total"
129
+ compute={(v) => v.price * v.qty}
130
+ format={(v) => `${v.toLocaleString()} ₽`}
131
+ deps={['price', 'qty']}
132
+ />
133
+
134
+ {/* Табличный редактор (массив объектов) */}
135
+ <Form.Field.TableEditor
136
+ name="items"
137
+ columns={[
138
+ { name: 'product', width: '40%' },
139
+ { name: 'qty', width: '15%', align: 'right' },
140
+ { name: 'price', width: '15%', align: 'right' },
141
+ { name: 'total', computed: (row) => row.qty * row.price, label: 'Итого' },
142
+ ]}
143
+ addLabel="Добавить товар"
144
+ footer={[{ column: 'total', aggregate: 'sum', label: 'Итого:' }]}
145
+ />
146
+
147
+ {/* Скрытые поля (UTM, referral) */}
148
+ <Form.Field.Hidden name="utm_source" value="landing" />
149
+
150
+ {/* Сводка ошибок */}
151
+ <Form.Errors title="Исправьте ошибки:" />
152
+
153
+ {/* JSON-инспектор значений (скрыт в production) */}
154
+ <Form.DebugValues />
155
+
156
+ <Form.Button.Submit />
157
+ </Form>
158
+ ```
159
+
160
+ [Подробнее → docs/form-level.md](./docs/form-level.md)
161
+
162
+ ### Группы и массивы
163
+
164
+ ```tsx
165
+ // Вложенный объект
166
+ <Form.Group name="address">
167
+ <Form.Field.String name="city" /> {/* → address.city */}
168
+ <Form.Field.String name="street" /> {/* → address.street */}
169
+ </Form.Group>
170
+
171
+ // Массив
172
+ <Form.Group.List name="phones">
173
+ <Form.Field.Phone />
174
+ <Form.Group.List.Button.Add>Добавить телефон</Form.Group.List.Button.Add>
175
+ </Form.Group.List>
176
+ ```
177
+
178
+ ### Smart Autofill
179
+
180
+ Поля автоматически получают правильные `autocomplete` атрибуты (+30% конверсии, WCAG 1.3.5):
181
+
182
+ ```tsx
183
+ <Form.Field.String name="email" /> // → autocomplete="email"
184
+ <Form.Field.String name="firstName" /> // → autocomplete="given-name"
185
+ <Form.Field.Password name="password" /> // → autocomplete="current-password"
186
+ ```
187
+
188
+ Override: `autoComplete` prop или `.meta({ ui: { autocomplete: 'off' } })`.
189
+
190
+ ### Автоматические constraints из Zod
191
+
192
+ ```tsx
193
+ const Schema = z.object({
194
+ title: z.string().min(2).max(100), // → minLength={2} maxLength={100}
195
+ email: z.string().email(), // → type="email"
196
+ rating: z.number().min(1).max(10), // → min={1} max={10}
197
+ })
198
+
199
+ // DRY: валидация и UI constraints в одном месте
200
+ <Form.Field.String name="title" /> {/* maxLength={100} из схемы */}
201
+ ```
202
+
203
+ ### ZenStack интеграция
204
+
205
+ ```zmodel
206
+ model Product {
207
+ /// @form.title("Название продукта")
208
+ /// @form.placeholder("Введите название")
209
+ title String
210
+
211
+ /// @form.title("Цена")
212
+ /// @form.fieldType("currency")
213
+ /// @form.props({ min: 0, currency: "RUB" })
214
+ price Int
215
+ }
216
+ ```
217
+
218
+ ```tsx
219
+ import { ProductCreateFormSchema } from '@/generated/form-schemas'
220
+ <Form.FromSchema schema={ProductCreateFormSchema} initialValue={data} onSubmit={save} />
221
+ ```
222
+
223
+ [Подробнее → docs/zenstack.md](./docs/zenstack.md)
224
+
225
+ ### Offline Support
226
+
227
+ ```tsx
228
+ <Form
229
+ initialValue={data}
230
+ offline={{
231
+ actionType: 'UPDATE_PROFILE',
232
+ onQueued: () => toast.info('Сохранено локально'),
233
+ onSynced: () => toast.success('Синхронизировано'),
234
+ }}
235
+ onSubmit={handleSubmit}
236
+ >
237
+ <Form.OfflineIndicator />
238
+ <Form.Field.String name="name" />
239
+ <Form.Button.Submit />
240
+ </Form>
241
+ ```
242
+
243
+ [Подробнее → docs/offline.md](./docs/offline.md)
244
+
245
+ ### Security
246
+
247
+ ```tsx
248
+ // Honeypot — ловушка для ботов
249
+ <Form honeypot={true} initialValue={data} onSubmit={handleSubmit}>
250
+ <Form.Field.String name="email" />
251
+ <Form.Button.Submit />
252
+ </Form>
253
+
254
+ // Rate Limiting — ограничение попыток submit
255
+ <Form rateLimit={{ maxSubmits: 3, windowMs: 60000 }} initialValue={data} onSubmit={handleSubmit}>
256
+ ...
257
+ </Form>
258
+
259
+ // Secure File Upload — проверка MIME, удаление EXIF, переименование
260
+ <Form.Field.FileUpload
261
+ name="document"
262
+ security={{
263
+ maxSize: '10MB',
264
+ allowedTypes: ['image/jpeg', 'image/png', 'application/pdf'],
265
+ stripMetadata: true,
266
+ renameFile: true,
267
+ }}
268
+ />
269
+ ```
270
+
271
+ ---
272
+
273
+ ## Установка
274
+
275
+ ```bash
276
+ # Уже установлен в монорепозитории
277
+ import { Form } from '@lena/form-components'
278
+ ```
279
+
280
+ ### Опциональные зависимости (npm)
281
+
282
+ | Пакет | Для чего |
283
+ | --------------------------- | --------------------------------- |
284
+ | `@dnd-kit/*` | Drag & drop сортировка в массивах |
285
+ | `use-mask-input` | Phone, MaskedInput |
286
+ | `@tiptap/*` | RichText редактор |
287
+ | `@uiw/react-json-view` | Form.DebugValues (JSON инспектор) |
288
+ | `next-intl` | i18n интеграция |
289
+ | `@marsidev/react-turnstile` | CAPTCHA (Cloudflare Turnstile) |
290
+
291
+ ---
292
+
293
+ ## Команды
294
+
295
+ ```bash
296
+ nx build @lena/form-components # Сборка
297
+ nx lint @lena/form-components # Линтинг
298
+ nx test @lena/form-components # Тесты
299
+ ```
300
+
301
+ ---
302
+
303
+ ## Провайдер адресов
304
+
305
+ Поля Address и City поддерживают подключаемые провайдеры геокодинга. DaData (Россия) встроен:
306
+
307
+ ```typescript
308
+ import { createForm, createDaDataProvider } from '@lena/form-components'
309
+
310
+ // Вариант 1: через createForm (рекомендуемый)
311
+ const AppForm = createForm({
312
+ addressProvider: createDaDataProvider({ token: process.env.DADATA_TOKEN }),
313
+ })
314
+
315
+ <AppForm.Field.Address name="address" />
316
+ <AppForm.Field.City name="city" />
317
+
318
+ // Вариант 2: на конкретном поле
319
+ <Form.Field.Address name="address" provider={myProvider} />
320
+
321
+ // Вариант 3: обратная совместимость через token
322
+ <Form.Field.Address name="address" token="dadata-token" />
323
+ ```
324
+
325
+ Для других сервисов — реализуйте интерфейс `AddressProvider`:
326
+
327
+ ```typescript
328
+ const myProvider: AddressProvider = {
329
+ async getSuggestions(query, options) {
330
+ const res = await fetch(`/api/geocode?q=${query}`)
331
+ return res.json() // [{ label, value, data }]
332
+ },
333
+ }
334
+ ```
335
+
336
+ ---
337
+
338
+ ## AI Tooling (MCP)
339
+
340
+ MCP сервер [`@letar/form-mcp`](../form-mcp/README.md) предоставляет AI-ассистентам (Claude Code, Cursor, VS Code Copilot) полный контекст о библиотеке: 40+ полей, паттерны форм, @form.\* директивы.
341
+
342
+ ```json
343
+ { "form-mcp": { "command": "npx", "args": ["-y", "@letar/form-mcp"] } }
344
+ ```
345
+
346
+ ## Bundle Size
347
+
348
+ Библиотека поставляется как ESM с external dependencies. Все тяжёлые зависимости (Chakra, React, Tiptap, dnd-kit) — external и не включаются в bundle.
349
+
350
+ | Модуль | Размер (brotli) | Размер (raw) |
351
+ | --------------------------------- | --------------- | ------------ |
352
+ | `@letar/forms` (все 40 полей) | **20 KB** | 109 KB |
353
+ | `@letar/forms/fields/text` | < 1 KB | re-export |
354
+ | `@letar/forms/fields/number` | < 1 KB | re-export |
355
+ | `@letar/forms/fields/datetime` | < 1 KB | re-export |
356
+ | `@letar/forms/fields/selection` | < 1 KB | re-export |
357
+ | `@letar/forms/fields/boolean` | < 1 KB | re-export |
358
+ | `@letar/forms/fields/specialized` | < 1 KB | re-export |
359
+ | `@letar/forms/offline` | < 1 KB | 5 KB |
360
+ | `@letar/forms/i18n` | < 1 KB | 13 KB |
361
+
362
+ Категорийные entry points (`fields/*`) позволяют импортировать только нужные поля:
363
+
364
+ ```typescript
365
+ // Полный импорт — все 40 полей
366
+ import { Form } from '@letar/forms'
367
+
368
+ // Категорийный импорт — только текстовые поля
369
+ import { FieldString, FieldTextarea } from '@letar/forms/fields/text'
370
+ ```
371
+
372
+ Метрики проверяются в CI через [size-limit](https://github.com/ai/size-limit).
373
+
374
+ ### Ре-рендеры
375
+
376
+ При вводе текста в одно поле формы из 10 полей — **остальные 9 полей НЕ ре-рендерятся** (0 лишних рендеров). TanStack Form обеспечивает field-level подписки — каждое поле изолировано.
377
+
378
+ ## Связанные документы
379
+
380
+ - [/.claude/docs/forms.md](../../.claude/docs/forms.md) — документация по формам
381
+ - [/.claude/docs/pwa-offline.md](../../.claude/docs/pwa-offline.md) — оффлайн-формы
382
+ - [/libs/form-mcp](../form-mcp/) — MCP сервер для AI-ассистентов
383
+ - [PLAN.md](./PLAN.md) — план развития библиотеки
384
+ - [TESTING_PLAN.md](./TESTING_PLAN.md) — план тестирования
385
+
386
+ ---
387
+
388
+ **Версия:** 0.63.0
389
+ **Последнее обновление:** 2026-04-03
package/analytics.js ADDED
@@ -0,0 +1,3 @@
1
+ export { AnalyticsPanel, createGtagAdapter, createPostHogAdapter, createUmamiAdapter, createYandexMetrikaAdapter, useFormAnalytics } from './chunk-K3J4L26K.js';
2
+ //# sourceMappingURL=analytics.js.map
3
+ //# sourceMappingURL=analytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"analytics.js"}