@arturton/react-form-constructor 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -15,10 +15,319 @@
15
15
  ## Установка
16
16
 
17
17
  ```bash
18
- npm i react-form-constructor
18
+ npm install @arturton/react-form-constructor
19
19
  ```
20
20
 
21
- ## Быстрый старт
21
+ ## Метод 1: Provider (рекомендуется)
22
+
23
+ Используйте `FormProvider` и набор компонентов для построения формы через `children`.
24
+
25
+ ### Пример
26
+
27
+ ```tsx
28
+ import {
29
+ FormProvider,
30
+ FormInputLayout,
31
+ FormLabel,
32
+ FormInput,
33
+ FormPasswordInput,
34
+ FormTextarea,
35
+ FormMaskedInput,
36
+ FormSelect,
37
+ FormNumber,
38
+ FormDate,
39
+ FormRange,
40
+ FormFileInput,
41
+ FormCheckbox,
42
+ FormRadio,
43
+ FormError,
44
+ FormButton,
45
+ } from "react-form-constructor";
46
+
47
+ type ProfileForm = {
48
+ name: string;
49
+ password: string;
50
+ description?: string;
51
+ phone?: string;
52
+ country?: string;
53
+ age?: number;
54
+ birthDate?: string;
55
+ rating?: number;
56
+ avatar?: FileList;
57
+ terms?: boolean;
58
+ gender?: string;
59
+ };
60
+
61
+ export function Profile() {
62
+ return (
63
+ <FormProvider<ProfileForm>
64
+ funSubmit={(data) => console.log(data)}
65
+ className="flex flex-col gap-4"
66
+ >
67
+ <FormInputLayout name="name" required="Введите имя">
68
+ <FormLabel>Имя</FormLabel>
69
+ <FormInput placeholder="Введите имя" className="input" />
70
+ <FormError className="error" />
71
+ </FormInputLayout>
72
+
73
+ <FormInputLayout name="password" required="Введите пароль">
74
+ <FormLabel>Пароль</FormLabel>
75
+ <FormPasswordInput
76
+ placeholder="Введите пароль"
77
+ className="input-wrap"
78
+ inputClassName="input"
79
+ />
80
+ <FormError className="error" />
81
+ </FormInputLayout>
82
+
83
+ <FormInputLayout name="description">
84
+ <FormLabel>Описание</FormLabel>
85
+ <FormTextarea rows={4} className="textarea" />
86
+ <FormError className="error" />
87
+ </FormInputLayout>
88
+
89
+ <FormInputLayout
90
+ name="phone"
91
+ maska={{
92
+ required: "Телефон обязателен",
93
+ format: "+7 (###) ###-##-##",
94
+ mask: "_",
95
+ }}
96
+ >
97
+ <FormLabel>Телефон</FormLabel>
98
+ <FormMaskedInput className="input" />
99
+ <FormError className="error" />
100
+ </FormInputLayout>
101
+
102
+ <FormInputLayout name="country" required="Выберите страну">
103
+ <FormLabel>Страна</FormLabel>
104
+ <FormSelect
105
+ placeholder="Выберите"
106
+ options={[
107
+ { value: "ru", label: "Россия" },
108
+ { value: "kz", label: "Казахстан" },
109
+ ]}
110
+ className="select"
111
+ />
112
+ <FormError className="error" />
113
+ </FormInputLayout>
114
+
115
+ <FormInputLayout name="age">
116
+ <FormLabel>Возраст</FormLabel>
117
+ <FormNumber min={1} max={120} step={1} className="input" />
118
+ <FormError className="error" />
119
+ </FormInputLayout>
120
+
121
+ <FormInputLayout name="birthDate">
122
+ <FormLabel>Дата рождения</FormLabel>
123
+ <FormDate type="date" className="input" />
124
+ <FormError className="error" />
125
+ </FormInputLayout>
126
+
127
+ <FormInputLayout name="rating">
128
+ <FormLabel>Рейтинг</FormLabel>
129
+ <FormRange range="single" min={0} max={10} showValue className="w-60" />
130
+ <FormError className="error" />
131
+ </FormInputLayout>
132
+
133
+ <FormInputLayout name="avatar">
134
+ <FormLabel>Аватар</FormLabel>
135
+ <FormFileInput accept="image/*" className="input" />
136
+ <FormError className="error" />
137
+ </FormInputLayout>
138
+
139
+ <FormInputLayout name="gender" required="Выберите пол">
140
+ <FormLabel>Пол</FormLabel>
141
+ <div className="flex gap-4">
142
+ <label className="flex items-center gap-2">
143
+ <FormRadio value="male" /> Мужской
144
+ </label>
145
+ <label className="flex items-center gap-2">
146
+ <FormRadio value="female" /> Женский
147
+ </label>
148
+ </div>
149
+ <FormError className="error" />
150
+ </FormInputLayout>
151
+
152
+ <FormInputLayout name="terms">
153
+ <label className="flex items-center gap-2">
154
+ <FormCheckbox value={true} /> Я согласен с условиями
155
+ </label>
156
+ <FormError className="error" />
157
+ </FormInputLayout>
158
+
159
+ <FormButton className="btn" disabledError>
160
+ Отправить
161
+ </FormButton>
162
+ </FormProvider>
163
+ );
164
+ }
165
+ ```
166
+
167
+ ### Компоненты (Provider)
168
+
169
+ #### FormProvider
170
+
171
+ **Props**
172
+
173
+ - `funSubmit: (data) => void` — обработчик отправки формы.
174
+ - `children: React.ReactNode` — поля формы.
175
+ - `className?: string` — класс формы.
176
+ - `setFormApi?: (formMethods) => void` — доступ к `register`, `errors`, `control`, `values`.
177
+
178
+ #### FormInputLayout
179
+
180
+ **Props**
181
+
182
+ - `name: string` — имя поля (ключ в данных).
183
+ - `required?: string` — сообщение об обязательности.
184
+ - `minLength?: { value: number; message: string }` — минимальная длина.
185
+ - `maxLength?: { value: number; message: string }` — максимальная длина.
186
+ - `pattern?: { value: RegExp; message: string }` — регулярное выражение.
187
+ - `validate?: (value) => boolean | string` — кастомная проверка.
188
+ - `maska?: { required: string; format: string; mask: string }` — маска ввода (для `FormMaskedInput`).
189
+ - `className?: string` — класс контейнера.
190
+
191
+ #### FormInput
192
+
193
+ **Props**
194
+
195
+ - `placeholder?: string` — плейсхолдер.
196
+ - `className?: string` — класс инпута.
197
+ - `classNameError?: string` — класс ошибки.
198
+ - `type?: string` — тип (`text`, `email`, `password`, …).
199
+
200
+ #### FormPasswordInput
201
+
202
+ **Props**
203
+
204
+ - `placeholder?: string`
205
+ - `className?: string` — класс контейнера.
206
+ - `inputClassName?: string` — класс инпута.
207
+ - `classNameError?: string` — класс ошибки.
208
+ - `visibleIcon?: ReactNode` — иконка видимого пароля.
209
+ - `hiddenIcon?: ReactNode` — иконка скрытого пароля.
210
+ - `iconClassName?: string` — класс иконки.
211
+ - `iconWrapperClassName?: string` — класс контейнера иконки.
212
+
213
+ #### FormTextarea
214
+
215
+ **Props**
216
+
217
+ - `placeholder?: string`
218
+ - `className?: string`
219
+ - `classNameError?: string`
220
+ - `rows?: number`
221
+ - `cols?: number`
222
+
223
+ #### FormMaskedInput
224
+
225
+ **Props**
226
+
227
+ - `placeholder?: string`
228
+ - `className?: string`
229
+ - `classNameError?: string`
230
+
231
+ > Использует `maska` из `FormInputLayout`.
232
+
233
+ #### FormSelect
234
+
235
+ **Props**
236
+
237
+ - `options: { value: string | number; label: string }[]` — список опций.
238
+ - `multiple?: boolean` — множественный выбор.
239
+ - `placeholder?: string` — пустой вариант.
240
+ - `className?: string`
241
+ - `classNameError?: string`
242
+
243
+ #### FormNumber
244
+
245
+ **Props**
246
+
247
+ - `placeholder?: string`
248
+ - `className?: string`
249
+ - `classNameError?: string`
250
+ - `min?: number`
251
+ - `max?: number`
252
+ - `step?: number`
253
+
254
+ #### FormDate
255
+
256
+ **Props**
257
+
258
+ - `type?: "date" | "datetime-local" | "time" | "month" | "week"`
259
+ - `min?: string`
260
+ - `max?: string`
261
+ - `className?: string`
262
+ - `classNameError?: string`
263
+
264
+ #### FormRange
265
+
266
+ **Props**
267
+
268
+ - `range?: "single" | "double"` — один ползунок или два.
269
+ - `min?: number`
270
+ - `max?: number`
271
+ - `step?: number`
272
+ - `showValue?: boolean` — показать значения.
273
+ - `className?: string` — класс слайдера.
274
+ - `containerClassName?: string` — класс контейнера.
275
+ - `classNameError?: string`
276
+
277
+ #### FormFileInput
278
+
279
+ **Props**
280
+
281
+ - `accept?: string` — MIME типы/расширения.
282
+ - `multiple?: boolean`
283
+ - `className?: string`
284
+ - `classNameError?: string`
285
+
286
+ #### FormCheckbox
287
+
288
+ **Props**
289
+
290
+ - `value?: string | number | boolean`
291
+ - `defaultChecked?: boolean`
292
+ - `disabled?: boolean`
293
+ - `className?: string`
294
+ - `classNameError?: string`
295
+
296
+ #### FormRadio
297
+
298
+ **Props**
299
+
300
+ - `value: string | number`
301
+ - `defaultChecked?: boolean`
302
+ - `disabled?: boolean`
303
+ - `className?: string`
304
+ - `classNameError?: string`
305
+
306
+ #### FormButton
307
+
308
+ **Props**
309
+
310
+ - `className?: string`
311
+ - `disabledError?: boolean` — отключить кнопку, если есть ошибки.
312
+
313
+ #### FormError
314
+
315
+ **Props**
316
+
317
+ - `className?: string`
318
+
319
+ #### FormLabel
320
+
321
+ **Props**
322
+
323
+ - `className?: string`
324
+ - `classNameError?: string`
325
+
326
+ ## Метод 2: JSON (FormLayout)
327
+
328
+ `FormLayout` рендерит форму по массиву `formData` и сам регистрирует поля.
329
+
330
+ ### Пример
22
331
 
23
332
  ```tsx
24
333
  import { FormLayout, type FormField } from "react-form-constructor";
@@ -62,92 +371,33 @@ export function Login() {
62
371
  }
63
372
  ```
64
373
 
65
- ## Как это работает
66
-
67
- - **`FormLayout`** создаёт форму и сам регистрирует поля через `react-hook-form`.
68
- - Каждое поле описывается типом `FormField<T>` и привязывается к ключу `keyof T`.
69
- - Если передан `formData`, библиотека сама отрисует поля и кнопку отправки.
70
- - Если `formData` не передан можно рендерить **свои поля** через `children` и использовать `useFormContext()`.
71
-
72
- ## Пример с кастомной разметкой (children)
73
-
74
- ```tsx
75
- import { FormLayout, useFormContext } from "react-form-constructor";
76
-
77
- function CustomField() {
78
- const { register, errors } = useFormContext<{ email: string }>();
79
-
80
- return (
81
- <div>
82
- <input {...register("email", { required: "Введите email" })} />
83
- {errors.email && <span>{errors.email.message}</span>}
84
- </div>
85
- );
86
- }
87
-
88
- export function CustomForm() {
89
- return (
90
- <FormLayout funSubmit={(data) => console.log(data)}>
91
- <CustomField />
92
- </FormLayout>
93
- );
94
- }
95
- ```
96
-
97
- ## Валидация
98
-
99
- Поддерживаются стандартные правила `react-hook-form`:
100
-
101
- - `required`
102
- - `minLength`
103
- - `maxLength`
104
- - `pattern`
105
- - `validate`
106
-
107
- Пример:
108
-
109
- ```ts
110
- {
111
- key: "username",
112
- label: "Имя",
113
- required: "Обязательное поле",
114
- minLength: { value: 3, message: "Минимум 3 символа" },
115
- pattern: { value: /^[a-z]+$/i, message: "Только буквы" },
116
- }
117
- ```
118
-
119
- ## Маски ввода
120
-
121
- Поле с маской задаётся через `maska`:
122
-
123
- ```ts
124
- {
125
- key: "phone",
126
- label: "Телефон",
127
- maska: {
128
- required: "Введите телефон",
129
- format: "+7 (###) ###-##-##",
130
- mask: "_",
131
- },
132
- }
133
- ```
134
-
135
- ## Стилизация
136
-
137
- Доступны классы для быстрого оформления:
138
-
139
- - `formClass` — класс формы
140
- - `buttonClass` — класс кнопки
141
- - `inputClass` — класс поля
142
- - `labelClass` — класс лейбла
143
- - `errorClass` — класс текста ошибки
144
-
145
- ## Публичный API
146
-
147
- - `FormLayout` — рендерит форму по массиву `formData` или принимает `children`.
148
- - `InputForm` — базовый элемент поля (экспортируется по умолчанию).
149
- - `useFormContext` — доступ к `react-hook-form` контексту внутри ваших компонентов.
150
- - Типы: `FormField`, `FormLayoutProps`, `FormContextValue`.
374
+ ### FormField
375
+
376
+ **Поля**
377
+
378
+ - `label: string` текст лейбла.
379
+ - `placeholder: string` — плейсхолдер.
380
+ - `key: keyof T` — имя поля.
381
+ - `required?: string` сообщение об обязательности.
382
+ - `minLength?: { value: number; message: string }`
383
+ - `maxLength?: { value: number; message: string }`
384
+ - `pattern?: { value: RegExp; message: string }`
385
+ - `validate?: any` — кастомная проверка.
386
+ - `type?: string` — тип инпута.
387
+ - `maska?: { required: string; format: string; mask: string }` — маска.
388
+ - `textarea?: boolean` — textarea вместо input.
389
+ - `register?: object` — доп. настройки `react-hook-form`.
390
+ - `inputClass?: any` — класс инпута.
391
+ - `labelClass?: any` класс лейбла.
392
+ - `errorClass?: any` — класс ошибки.
393
+
394
+ ### FormLayoutProps
395
+
396
+ - `funSubmit: (data: T) => void` — submit.
397
+ - `formClass?: string` — класс формы.
398
+ - `buttonClass?: string` — класс кнопки.
399
+ - `buttonName?: string` — текст кнопки.
400
+ - `formData: FormField<T>[]` — массив полей.
151
401
 
152
402
  ## Требования
153
403
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { Control, UseFormRegister, FieldErrors } from 'react-hook-form';
2
+ import { FieldValues, UseFormRegister, FieldErrors, Control } from 'react-hook-form';
3
+ import React$1, { ReactNode } from 'react';
3
4
 
4
5
  type FormField<T extends object = any> = {
5
6
  label: string;
@@ -37,22 +38,24 @@ type FormLayoutProps<T extends object = any> = {
37
38
  funSubmit: (data: T) => void;
38
39
  formClass?: string;
39
40
  buttonClass?: string;
40
- } & ({
41
+ buttonName?: string;
41
42
  formData: FormField<T>[];
42
- children?: never;
43
- } | {
44
- formData?: never;
43
+ };
44
+ type FormProviderProps<T extends object = any> = {
45
+ setFormApi?: (formMethods: any) => void;
45
46
  children: React.ReactNode;
46
- });
47
-
48
- declare function FormLayout<T extends object = any>({ formData, funSubmit, formClass, buttonClass, children, }: FormLayoutProps<T>): react_jsx_runtime.JSX.Element;
49
-
50
- interface FormContextValue<T extends object = any> {
51
- control: Control<T>;
47
+ funSubmit: (data: T) => void;
48
+ className?: string;
49
+ };
50
+ type FormContextType<T extends FieldValues = any> = {
52
51
  register: UseFormRegister<T>;
53
52
  errors: FieldErrors<T>;
54
- }
55
- declare function useFormContext<T extends object = any>(): FormContextValue<T>;
53
+ control: Control<T>;
54
+ defaultValues?: T;
55
+ values?: T;
56
+ };
57
+
58
+ declare function FormLayout<T extends object = any>({ formData, funSubmit, formClass, buttonClass, buttonName, }: FormLayoutProps<T>): react_jsx_runtime.JSX.Element;
56
59
 
57
60
  declare function InputForm({ register, type, placeholder, error, name, label, required, control, maska, inputClass, labelClass, errorClass, }: {
58
61
  register: any;
@@ -73,4 +76,150 @@ declare function InputForm({ register, type, placeholder, error, name, label, re
73
76
  errorClass?: any;
74
77
  }): react_jsx_runtime.JSX.Element;
75
78
 
76
- export { type FormContextValue, type FormField, FormLayout, type FormLayoutProps, InputForm, useFormContext };
79
+ declare function FormProvider<T extends object = any>({ setFormApi, children, funSubmit, className, }: FormProviderProps<T>): react_jsx_runtime.JSX.Element;
80
+
81
+ declare function useFormContext<T extends Record<string, any> = any>(): FormContextType<T>;
82
+
83
+ interface FormInputProps {
84
+ name: string;
85
+ minLength?: {
86
+ value: number;
87
+ message: string;
88
+ };
89
+ maxLength?: {
90
+ value: number;
91
+ message: string;
92
+ };
93
+ pattern?: {
94
+ value: RegExp;
95
+ message: string;
96
+ };
97
+ required?: string;
98
+ validate?: (value: any) => boolean | string;
99
+ children: React$1.ReactNode;
100
+ labelClass?: string;
101
+ errorClass?: string;
102
+ className?: string;
103
+ maska?: {
104
+ required: string;
105
+ format: string;
106
+ mask: string;
107
+ };
108
+ }
109
+ declare function FormInputLayout({ name, minLength, maxLength, pattern, required, validate, children, className, maska, }: FormInputProps): react_jsx_runtime.JSX.Element;
110
+
111
+ declare function FormInput({ placeholder, className, classNameError, type, }: {
112
+ placeholder?: string;
113
+ className?: string;
114
+ classNameError?: string;
115
+ type?: string;
116
+ }): react_jsx_runtime.JSX.Element;
117
+
118
+ interface FormPasswordInputProps {
119
+ placeholder?: string;
120
+ className?: string;
121
+ inputClassName?: string;
122
+ classNameError?: string;
123
+ visibleIcon?: ReactNode;
124
+ hiddenIcon?: ReactNode;
125
+ iconClassName?: string;
126
+ iconWrapperClassName?: string;
127
+ }
128
+ declare function FormPasswordInput({ placeholder, className, inputClassName, classNameError, visibleIcon, hiddenIcon, iconClassName, iconWrapperClassName, }: FormPasswordInputProps): react_jsx_runtime.JSX.Element;
129
+
130
+ declare function FormTextarea({ placeholder, className, classNameError, rows, cols, }: {
131
+ placeholder?: string;
132
+ className?: string;
133
+ classNameError?: string;
134
+ rows?: number;
135
+ cols?: number;
136
+ }): react_jsx_runtime.JSX.Element;
137
+
138
+ declare function FormMaskedInput({ placeholder, className, classNameError, }: {
139
+ placeholder?: string;
140
+ className?: string;
141
+ classNameError?: string;
142
+ }): react_jsx_runtime.JSX.Element | null;
143
+
144
+ interface SelectOption {
145
+ value: string | number;
146
+ label: string;
147
+ }
148
+ declare function FormSelect({ options, className, classNameError, multiple, placeholder, }: {
149
+ options: SelectOption[];
150
+ className?: string;
151
+ classNameError?: string;
152
+ multiple?: boolean;
153
+ placeholder?: string;
154
+ }): react_jsx_runtime.JSX.Element;
155
+
156
+ declare function FormNumber({ placeholder, className, classNameError, min, max, step, }: {
157
+ placeholder?: string;
158
+ className?: string;
159
+ classNameError?: string;
160
+ min?: number;
161
+ max?: number;
162
+ step?: number;
163
+ }): react_jsx_runtime.JSX.Element;
164
+
165
+ declare function FormDate({ placeholder, className, classNameError, min, max, type, }: {
166
+ placeholder?: string;
167
+ className?: string;
168
+ classNameError?: string;
169
+ min?: string;
170
+ max?: string;
171
+ type?: "date" | "datetime-local" | "time" | "month" | "week";
172
+ }): react_jsx_runtime.JSX.Element;
173
+
174
+ declare function FormRange({ className, classNameError, min, max, step, range, showValue, containerClassName, }: {
175
+ className?: string;
176
+ classNameError?: string;
177
+ min?: number;
178
+ max?: number;
179
+ step?: number;
180
+ range?: "single" | "double";
181
+ showValue?: boolean;
182
+ containerClassName?: string;
183
+ }): react_jsx_runtime.JSX.Element;
184
+
185
+ declare function FormFileInput({ className, classNameError, accept, multiple, }: {
186
+ className?: string;
187
+ classNameError?: string;
188
+ accept?: string;
189
+ multiple?: boolean;
190
+ }): react_jsx_runtime.JSX.Element;
191
+
192
+ declare function FormCheckbox({ className, classNameError, value, defaultChecked, disabled, }: {
193
+ className?: string;
194
+ classNameError?: string;
195
+ value?: string | number | boolean;
196
+ defaultChecked?: boolean;
197
+ disabled?: boolean;
198
+ }): react_jsx_runtime.JSX.Element;
199
+
200
+ declare function FormRadio({ className, classNameError, value, defaultChecked, disabled, }: {
201
+ className?: string;
202
+ classNameError?: string;
203
+ value: string | number;
204
+ defaultChecked?: boolean;
205
+ disabled?: boolean;
206
+ }): react_jsx_runtime.JSX.Element;
207
+
208
+ declare function FormButton({ children, className, disabledError, ...props }: {
209
+ children?: React.ReactNode;
210
+ className?: string;
211
+ disabledError?: boolean;
212
+ [key: string]: any;
213
+ }): react_jsx_runtime.JSX.Element;
214
+
215
+ declare function FormError({ ...props }: {
216
+ [x: string]: any;
217
+ }): react_jsx_runtime.JSX.Element | null;
218
+
219
+ declare function FormLabel({ children, className, classNameError, }: {
220
+ children: React.ReactNode;
221
+ className?: string;
222
+ classNameError?: string;
223
+ }): react_jsx_runtime.JSX.Element;
224
+
225
+ export { FormButton, FormCheckbox, type FormContextType, FormDate, FormError, type FormField, FormFileInput, FormInput, FormInputLayout, FormLabel, FormLayout, type FormLayoutProps, FormMaskedInput, FormNumber, FormPasswordInput, FormProvider, FormRadio, FormRange, FormSelect, FormTextarea, InputForm, useFormContext };