@axzydev/axzy_ui_system 1.2.16 → 1.3.1
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.cjs +3349 -1457
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.cts +719 -54
- package/dist/index.d.ts +719 -54
- package/dist/index.js +3283 -1387
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/App.tsx +32 -0
- package/src/components/atoms/badget/badget.tsx +2 -4
- package/src/components/atoms/chip/chip.doc.mdx +47 -0
- package/src/components/atoms/chip/chip.props.ts +32 -0
- package/src/components/atoms/chip/chip.stories.tsx +64 -0
- package/src/components/atoms/chip/chip.tsx +112 -0
- package/src/components/atoms/grid/grid.tsx +1 -2
- package/src/components/molecules/accordion/accordion.doc.mdx +47 -0
- package/src/components/molecules/accordion/accordion.props.ts +35 -0
- package/src/components/molecules/accordion/accordion.stories.tsx +68 -0
- package/src/components/molecules/accordion/accordion.tsx +123 -0
- package/src/components/molecules/calendar/calendar.doc.mdx +2 -1
- package/src/components/molecules/calendar/calendar.tsx +102 -24
- package/src/components/molecules/chip-input/chip-input.doc.mdx +44 -0
- package/src/components/molecules/chip-input/chip-input.props.ts +42 -0
- package/src/components/molecules/chip-input/chip-input.stories.tsx +52 -0
- package/src/components/molecules/chip-input/chip-input.tsx +205 -0
- package/src/components/molecules/date-picker/datePicker.tsx +45 -56
- package/src/components/molecules/dropdown-menu/dropdown-menu.doc.mdx +46 -0
- package/src/components/molecules/dropdown-menu/dropdown-menu.props.ts +41 -0
- package/src/components/molecules/dropdown-menu/dropdown-menu.stories.tsx +41 -0
- package/src/components/molecules/dropdown-menu/dropdown-menu.tsx +219 -0
- package/src/components/molecules/field/field.doc.mdx +43 -0
- package/src/components/molecules/field/field.props.ts +23 -0
- package/src/components/molecules/field/field.stories.tsx +42 -0
- package/src/components/molecules/field/field.tsx +54 -0
- package/src/components/molecules/masked-input/masked-input.doc.mdx +78 -0
- package/src/components/molecules/masked-input/masked-input.props.ts +58 -0
- package/src/components/molecules/masked-input/masked-input.stories.tsx +126 -0
- package/src/components/molecules/masked-input/masked-input.tsx +244 -0
- package/src/components/molecules/multi-select/multi-select.doc.mdx +55 -0
- package/src/components/molecules/multi-select/multi-select.props.ts +65 -0
- package/src/components/molecules/multi-select/multi-select.stories.tsx +59 -0
- package/src/components/molecules/multi-select/multi-select.tsx +386 -0
- package/src/components/molecules/search-select/search-select.doc.mdx +5 -0
- package/src/components/molecules/search-select/search-select.props.ts +10 -1
- package/src/components/molecules/search-select/search-select.stories.tsx +18 -0
- package/src/components/molecules/search-select/search-select.tsx +102 -28
- package/src/components/molecules/time-picker/timePicker.tsx +14 -26
- package/src/components/molecules/wysiwyg/wysiwyg.props.ts +51 -0
- package/src/components/molecules/wysiwyg/wysiwyg.stories.tsx +95 -0
- package/src/components/molecules/wysiwyg/wysiwyg.tsx +609 -0
- package/src/components/organisms/form-builder/fieldRenderer.tsx +144 -27
- package/src/components/organisms/form-builder/formBuilder.context.tsx +77 -15
- package/src/components/organisms/form-builder/formBuilder.doc.mdx +213 -86
- package/src/components/organisms/form-builder/formBuilder.props.ts +118 -36
- package/src/components/organisms/form-builder/formBuilder.stories.tsx +694 -96
- package/src/components/organisms/form-builder/formBuilder.tsx +152 -69
- package/src/components/organisms/form-builder/useFormBuilder.ts +59 -21
- package/src/hooks/useFloatingPanel.ts +100 -0
- package/src/index.ts +26 -4
- package/src/showcases/DataShowcases.tsx +76 -0
- package/src/showcases/FormShowcases.tsx +574 -10
- package/src/showcases/HomeShowcase.tsx +6 -6
- package/src/showcases/NavigationShowcases.tsx +72 -2
- package/src/showcases/StructureShowcases.tsx +64 -0
- package/src/types/chip.types.ts +13 -0
|
@@ -5,101 +5,126 @@ import * as StoriesData from './formBuilder.stories';
|
|
|
5
5
|
|
|
6
6
|
# ITFormBuilder
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Dynamic form generator that renders fields from a JSON-like array. Integrates with
|
|
9
|
+
Formik + Yup, supports both a backward-compatible V1 (`fields`) and the powerful
|
|
10
|
+
Enterprise V2 (`config`) API, and exposes a `useFormBuilder()` hook for building
|
|
11
|
+
custom submit buttons, progress meters, and dirty-state indicators.
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
- You want to drive a form from a declarative config (server-driven forms, CMS schemas).
|
|
16
|
+
- You need conditional fields (`renderWhen`), derived values (`onChangeAction`),
|
|
17
|
+
dynamic props (`dynamicProps`), or cascading selects.
|
|
18
|
+
- You need to render a custom React component as a field (`type: "custom"`).
|
|
9
19
|
|
|
10
20
|
## Props
|
|
11
21
|
|
|
12
|
-
| Prop | Type | Default | Description |
|
|
13
|
-
|
|
14
|
-
| `fields` | `FieldConfig[]` | — | Legacy V1 field definitions
|
|
15
|
-
| `config` | `FieldConfigV2[]` | — | V2 field configuration
|
|
16
|
-
| `columns` | `number` |
|
|
17
|
-
| `values` | `
|
|
18
|
-
| `handleChange` | `(event) => void` | — | Change handler
|
|
19
|
-
| `handleBlur` | `(event) => void` | — | Blur handler
|
|
20
|
-
| `touched` | `
|
|
21
|
-
| `errors` | `
|
|
22
|
-
| `setFieldValue` | `(field, value, shouldValidate?) => Promise
|
|
23
|
-
| `setFieldTouched` | `(field, touched?, shouldValidate?) => Promise
|
|
24
|
-
| `setFieldError` | `(field, message?) => void` |
|
|
25
|
-
| `isSubmitting` | `boolean` |
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
|
33
|
-
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
22
|
+
| Prop | Type | Default | Required | Description |
|
|
23
|
+
|------|------|---------|----------|-------------|
|
|
24
|
+
| `fields` | `FieldConfig[]` | — | No | Legacy V1 field definitions. Kept for backward compatibility. |
|
|
25
|
+
| `config` | `FieldConfigV2[]` | — | No | V2 field configuration. Recommended. Shadows `fields` when both supplied. |
|
|
26
|
+
| `columns` | `number` | `12` | No | Grid column count (1–12). |
|
|
27
|
+
| `values` | `Record<string, unknown>` | — | Yes | Current form values keyed by field `name`. |
|
|
28
|
+
| `handleChange` | `(event) => void` | — | Yes | Change handler — pass Formik's `handleChange` directly. |
|
|
29
|
+
| `handleBlur` | `(event) => void` | — | Yes | Blur handler — pass Formik's `handleBlur` directly. |
|
|
30
|
+
| `touched` | `Record<string, boolean>` | — | Yes | Touch state keyed by field `name`. |
|
|
31
|
+
| `errors` | `Record<string, string>` | — | Yes | Errors keyed by field `name`. |
|
|
32
|
+
| `setFieldValue` | `(field, value, shouldValidate?) => Promise` | no-op | No | Required for V2 `onChangeAction` and `dynamicProps` side effects. |
|
|
33
|
+
| `setFieldTouched` | `(field, touched?, shouldValidate?) => Promise` | no-op | No | Programmatic touch. |
|
|
34
|
+
| `setFieldError` | `(field, message?) => void` | no-op | No | Programmatic field-level error. |
|
|
35
|
+
| `isSubmitting` | `boolean` | `false` | No | Surface submit state to the context. |
|
|
36
|
+
|
|
37
|
+
When both `config` and `fields` are supplied, `config` wins. When neither is supplied the
|
|
38
|
+
component renders an empty grid.
|
|
39
|
+
|
|
40
|
+
## FieldConfigV2 reference
|
|
41
|
+
|
|
42
|
+
| Field | Type | Description |
|
|
43
|
+
|-------|------|-------------|
|
|
44
|
+
| `type` | `"text" \| "number" \| "password" \| "email" \| "select" \| "date" \| "time" \| "section" \| "custom" \| "array"` | Field kind. |
|
|
45
|
+
| `name` | `string` | Matches the key in `values` / `errors` / `touched`. |
|
|
46
|
+
| `label` | `string` | Text rendered above the field (or as the section heading). |
|
|
47
|
+
| `column` | `number \| { sm?, md?, lg?, xl? }` | Grid span. Responsive object or single number. |
|
|
48
|
+
| `dependsOn` | `string[]` | Sibling field names watched by `renderWhen`/`dynamicProps`. |
|
|
49
|
+
| `renderWhen` | `(values) => boolean` | Hide this field when returns `false`. |
|
|
50
|
+
| `dynamicProps` | `(values) => Partial<FieldConfigV2>` | Compute per-render overrides (e.g. toggle `disabled`). |
|
|
51
|
+
| `validation` | `Yup.AnySchema` | Per-field Yup schema. |
|
|
52
|
+
| `asyncValidation` | `(value, values) => Promise<string \| null>` | Async validator returning an error message or `null`. |
|
|
53
|
+
| `onChangeAction` | `(value, context) => void \| Promise<void>` | Side-effect fired on change. Use to compute derived fields. |
|
|
54
|
+
| `options` | `Array \| () => Promise<Array>` | Sync or async options for `select`. Async → renders `ITSearchSelect`. |
|
|
55
|
+
| `component` | `React.ComponentType` | Renderer for `type: "custom"`. |
|
|
56
|
+
| `fields` | `FieldConfigV2[]` | Nested fields for `type: "section"` (or `array` placeholder). |
|
|
57
|
+
| `placeholder` | `string` | Placeholder text. |
|
|
58
|
+
| `required` | `boolean \| (values) => boolean` | Required flag. |
|
|
59
|
+
| `disabled` | `boolean \| (values) => boolean` | Disabled flag. |
|
|
60
|
+
| `readOnly` | `boolean` | Read-only flag. |
|
|
61
|
+
| `defaultValue` | `any` | Initial value. |
|
|
62
|
+
| `valueField` / `labelField` | `string` | Keys read off each option object. |
|
|
63
|
+
| `leftIcon` / `rightIcon` | `React.ReactNode` | Icon adornments. |
|
|
64
|
+
| `className` | `string` | Extra classes for the field's container. |
|
|
65
|
+
| `maxLength` / `minLength` | `number` | Length bounds for text/textarea. |
|
|
66
|
+
| `rows` | `number` | Rows for textarea-style inputs. |
|
|
67
|
+
| `formatNumber` | `boolean` | Apply thousand-separator formatting as the user types. |
|
|
68
|
+
| `currencyFormat` | `boolean` | Render a `$`-style currency treatment on numeric input. |
|
|
69
|
+
| `showHintLength` | `boolean` | Show a live character counter. |
|
|
70
|
+
|
|
71
|
+
## FieldConfig (V1) reference (legacy)
|
|
72
|
+
|
|
73
|
+
| Field | Type | Description |
|
|
74
|
+
|-------|------|-------------|
|
|
75
|
+
| `name` | `string` | Key in `values`/`errors`/`touched`. |
|
|
76
|
+
| `label` | `string` | Field label. |
|
|
77
|
+
| `type` | `"text" \| "number" \| "password" \| "select" \| "date"` | Input kind. |
|
|
78
|
+
| `placeholder` | `string` | Placeholder text. |
|
|
79
|
+
| `required` | `boolean` | Required flag. |
|
|
80
|
+
| `disabled` | `boolean` | Disabled flag. |
|
|
81
|
+
| `column` | `number` | Grid span. |
|
|
82
|
+
| `options` | `{ value, label }[]` | Options for `select`. |
|
|
83
|
+
| `leftIcon` / `rightIcon` | `React.ReactNode` | Icon adornments. |
|
|
84
|
+
| `valueField` / `labelField` | `string` | Keys read off each option object. |
|
|
85
|
+
| `maxLength` / `minLength` | `number` | Length bounds. |
|
|
86
|
+
| `rows` | `number` | Rows for multi-line inputs. |
|
|
87
|
+
| `formatNumber` | `boolean` | Thousand-separator formatting. |
|
|
88
|
+
| `currencyFormat` | `boolean` | Currency-style numeric input. |
|
|
89
|
+
| `showHintLength` | `boolean` | Live character counter. |
|
|
90
|
+
| `validation` | `Yup.AnySchema` | Per-field Yup schema. |
|
|
91
|
+
| `onChangeAction` | `(value, setFieldValue) => void` | Side-effect fired on change. |
|
|
41
92
|
|
|
42
93
|
## Usage
|
|
43
94
|
|
|
95
|
+
### 1. Minimal Formik + Yup form (V2)
|
|
96
|
+
|
|
44
97
|
```tsx
|
|
45
|
-
import {
|
|
46
|
-
import
|
|
47
|
-
import
|
|
98
|
+
import { Formik } from "formik";
|
|
99
|
+
import * as Yup from "yup";
|
|
100
|
+
import { ITFormBuilder } from "@axzydev/axzy_ui_system";
|
|
101
|
+
import type { FieldConfigV2 } from "@axzydev/axzy_ui_system";
|
|
48
102
|
|
|
49
103
|
const schema = Yup.object({
|
|
50
|
-
email: Yup.string().email(
|
|
51
|
-
password: Yup.string().min(8).required(
|
|
104
|
+
email: Yup.string().email().required(),
|
|
105
|
+
password: Yup.string().min(8).required(),
|
|
52
106
|
});
|
|
53
107
|
|
|
54
|
-
const
|
|
55
|
-
{ name: "email", label: "Email", type: "email", required: true, column:
|
|
56
|
-
{ name: "password", label: "Password", type: "password", required: true, column:
|
|
57
|
-
{
|
|
58
|
-
name: "subtotal",
|
|
59
|
-
label: "Subtotal",
|
|
60
|
-
type: "number",
|
|
61
|
-
currencyFormat: true,
|
|
62
|
-
column: { sm: 12, md: 4 },
|
|
63
|
-
onChangeAction: (val, { setFieldValue }) => {
|
|
64
|
-
const num = parseFloat(val) || 0;
|
|
65
|
-
setFieldValue("total", (num * 1.16).toFixed(2));
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: "total",
|
|
70
|
-
label: "Total",
|
|
71
|
-
type: "number",
|
|
72
|
-
currencyFormat: true,
|
|
73
|
-
disabled: true,
|
|
74
|
-
column: { sm: 12, md: 4 },
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
name: "rfc",
|
|
78
|
-
label: "RFC",
|
|
79
|
-
type: "text",
|
|
80
|
-
dependsOn: ["country"],
|
|
81
|
-
renderWhen: (vals) => vals.country === "MX",
|
|
82
|
-
required: true,
|
|
83
|
-
column: { sm: 12, md: 6 },
|
|
84
|
-
},
|
|
108
|
+
const config: FieldConfigV2[] = [
|
|
109
|
+
{ name: "email", label: "Email", type: "email", required: true, column: 6 },
|
|
110
|
+
{ name: "password", label: "Password", type: "password", required: true, column: 6 },
|
|
85
111
|
];
|
|
86
112
|
|
|
87
113
|
<Formik
|
|
88
|
-
initialValues={{ email: "", password: ""
|
|
114
|
+
initialValues={{ email: "", password: "" }}
|
|
89
115
|
validationSchema={schema}
|
|
90
|
-
onSubmit={
|
|
116
|
+
onSubmit={(values) => console.log(values)}
|
|
91
117
|
>
|
|
92
|
-
{(
|
|
93
|
-
<form onSubmit={
|
|
118
|
+
{(p) => (
|
|
119
|
+
<form onSubmit={p.handleSubmit}>
|
|
94
120
|
<ITFormBuilder
|
|
95
|
-
config={
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
setFieldValue={formikProps.setFieldValue}
|
|
121
|
+
config={config}
|
|
122
|
+
values={p.values}
|
|
123
|
+
handleChange={p.handleChange}
|
|
124
|
+
handleBlur={p.handleBlur}
|
|
125
|
+
touched={p.touched}
|
|
126
|
+
errors={p.errors}
|
|
127
|
+
setFieldValue={p.setFieldValue}
|
|
103
128
|
/>
|
|
104
129
|
<button type="submit">Submit</button>
|
|
105
130
|
</form>
|
|
@@ -107,18 +132,120 @@ const fields = [
|
|
|
107
132
|
</Formik>
|
|
108
133
|
```
|
|
109
134
|
|
|
135
|
+
### 2. Conditional + derived fields
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
const config: FieldConfigV2[] = [
|
|
139
|
+
{
|
|
140
|
+
name: "country",
|
|
141
|
+
label: "País",
|
|
142
|
+
type: "select",
|
|
143
|
+
required: true,
|
|
144
|
+
options: [{ value: "MX", label: "México" }, { value: "US", label: "USA" }],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: "rfc",
|
|
148
|
+
label: "RFC",
|
|
149
|
+
type: "text",
|
|
150
|
+
required: true,
|
|
151
|
+
dependsOn: ["country"],
|
|
152
|
+
renderWhen: (v) => v.country === "MX",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: "subtotal",
|
|
156
|
+
label: "Subtotal",
|
|
157
|
+
type: "number",
|
|
158
|
+
currencyFormat: true,
|
|
159
|
+
onChangeAction: (val, ctx) => {
|
|
160
|
+
const n = parseFloat(String(val).replace(/,/g, "")) || 0;
|
|
161
|
+
ctx.setFieldValue("total", (n * 1.16).toFixed(2));
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{ name: "total", label: "Total", type: "number", disabled: true, currencyFormat: true },
|
|
165
|
+
];
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 3. Async / cascading options
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
{
|
|
172
|
+
name: "city",
|
|
173
|
+
label: "Ciudad",
|
|
174
|
+
type: "select",
|
|
175
|
+
options: async () => {
|
|
176
|
+
const res = await fetch(`/api/cities?country=${values.country}`);
|
|
177
|
+
return res.json();
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Async option lists use `ITSearchSelect` underneath and render a spinner fallback
|
|
183
|
+
while loading (`data-testid="field-loading-{name}"`).
|
|
184
|
+
|
|
185
|
+
### 4. Custom component
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
const ConfirmToggle = ({ value, onChange, label }) => (
|
|
189
|
+
<label>
|
|
190
|
+
<input type="checkbox" checked={!!value} onChange={(e) => onChange(e.target.checked)} />
|
|
191
|
+
{label}
|
|
192
|
+
</label>
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
{
|
|
196
|
+
name: "confirm",
|
|
197
|
+
label: "Acepto términos",
|
|
198
|
+
type: "custom",
|
|
199
|
+
component: ConfirmToggle,
|
|
200
|
+
defaultValue: false,
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 5. Section grouping
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
{
|
|
208
|
+
name: "identity",
|
|
209
|
+
label: "Personal Information",
|
|
210
|
+
type: "section",
|
|
211
|
+
fields: [
|
|
212
|
+
{ name: "firstName", label: "First Name", type: "text" },
|
|
213
|
+
{ name: "lastName", label: "Last Name", type: "text" },
|
|
214
|
+
],
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 6. Custom submit button via `useFormBuilder`
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
import { useFormBuilder } from "@axzydev/axzy_ui_system";
|
|
222
|
+
|
|
223
|
+
const SubmitButton = () => {
|
|
224
|
+
const { progress, isSubmitting } = useFormBuilder();
|
|
225
|
+
return (
|
|
226
|
+
<button type="submit" disabled={progress < 100 || isSubmitting}>
|
|
227
|
+
{isSubmitting ? "Saving…" : `Submit (${progress}%)`}
|
|
228
|
+
</button>
|
|
229
|
+
);
|
|
230
|
+
};
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`useFormBuilder` must be rendered inside the `<ITFormBuilder>` subtree (so the
|
|
234
|
+
V2 provider must be active). It throws a clear error if used outside.
|
|
235
|
+
|
|
110
236
|
## Notes
|
|
111
237
|
|
|
112
|
-
-
|
|
113
|
-
|
|
114
|
-
-
|
|
115
|
-
|
|
116
|
-
-
|
|
117
|
-
|
|
118
|
-
- `
|
|
238
|
+
- The component is fully controlled — it does not own form state. Pass values
|
|
239
|
+
and handlers from Formik (or any state holder of your choice).
|
|
240
|
+
- V1 and V2 can coexist on a single page; mixing both on the same `<ITFormBuilder>`
|
|
241
|
+
is not supported — `config` wins.
|
|
242
|
+
- The V2 registry of registered fields is exposed via `useFormBuilder` and via
|
|
243
|
+
`useITFormBuilderContext().getAllFieldConfigs()`.
|
|
244
|
+
- `validation`/`asyncValidation` are NOT applied automatically — wire them to
|
|
245
|
+
Yup at the Formik root, or run them manually inside `onChangeAction`.
|
|
119
246
|
|
|
120
247
|
## Stories
|
|
121
248
|
|
|
122
249
|
<Primary />
|
|
123
250
|
<Controls />
|
|
124
|
-
<Stories />
|
|
251
|
+
<Stories />
|
|
@@ -1,53 +1,135 @@
|
|
|
1
1
|
import { FieldConfig, FieldConfigV2 } from "@/types/field.types";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* A single field change event, mirroring the Formik public API surface so any
|
|
5
|
+
* standard Formik binding (`formik.handleChange`) can be passed through.
|
|
6
|
+
*/
|
|
7
|
+
export type ITFormBuilderChangeEvent =
|
|
8
|
+
| React.ChangeEvent<HTMLInputElement>
|
|
9
|
+
| React.FocusEvent<HTMLInputElement>
|
|
10
|
+
| React.ChangeEvent<HTMLSelectElement>
|
|
11
|
+
| { target: { name: string; value: any } };
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A single field blur event, mirroring the Formik public API surface so any
|
|
15
|
+
* standard Formik binding (`formik.handleBlur`) can be passed through.
|
|
16
|
+
*/
|
|
17
|
+
export type ITFormBuilderBlurEvent =
|
|
18
|
+
| React.FocusEvent<HTMLInputElement>
|
|
19
|
+
| React.FocusEvent<HTMLSelectElement, Element>
|
|
20
|
+
| React.FocusEvent<HTMLTextAreaElement, Element>
|
|
21
|
+
| { target: { name: string; value: any } };
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Form-level values keyed by field `name`. Kept as `Record<string, unknown>` so
|
|
25
|
+
* the builder does not impose a concrete domain shape on consumers.
|
|
26
|
+
*/
|
|
27
|
+
export type ITFormBuilderValues = Record<string, unknown>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Validation errors keyed by field `name`.
|
|
31
|
+
*/
|
|
32
|
+
export type ITFormBuilderErrors = Record<string, string | undefined>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Touch state keyed by field `name`.
|
|
36
|
+
*/
|
|
37
|
+
export type ITFormBuilderTouched = Record<string, boolean>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Props for {@link ITFormBuilder}. The component accepts either a legacy V1
|
|
41
|
+
* `fields` array (kept for backward compatibility with projects that adopted
|
|
42
|
+
* the original API) or the richer V2 `config` array. If both are supplied the
|
|
43
|
+
* V2 `config` takes precedence; if neither is supplied the component renders
|
|
44
|
+
* nothing.
|
|
45
|
+
*/
|
|
3
46
|
export interface ITFormBuilderProps {
|
|
4
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Legacy V1 field definitions.
|
|
49
|
+
*
|
|
50
|
+
* @deprecated Prefer the `config` prop (V2). V1 is preserved for backward
|
|
51
|
+
* compatibility and supports a reduced subset of features (no rules engine,
|
|
52
|
+
* no `section`, no `custom`, no async options).
|
|
53
|
+
*/
|
|
5
54
|
fields?: FieldConfig[];
|
|
6
|
-
|
|
7
|
-
/**
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* V2 field configuration array. The recommended entry point. Supports the
|
|
58
|
+
* rules engine (`renderWhen` / `dynamicProps` / `dependsOn`), nested
|
|
59
|
+
* `sections`, custom renderers via `type: "custom"`, async option loaders, and
|
|
60
|
+
* per-field `validation` / `asyncValidation`.
|
|
61
|
+
*
|
|
62
|
+
* When this prop is supplied it shadows any `fields` prop.
|
|
63
|
+
*/
|
|
8
64
|
config?: FieldConfigV2[];
|
|
9
|
-
|
|
10
|
-
/**
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Number of grid columns used by the responsive layout. Allowed range is
|
|
68
|
+
* 1..12; values outside the supported grid map fall back to a 12-column
|
|
69
|
+
* layout. @default 12
|
|
70
|
+
*/
|
|
11
71
|
columns?: number;
|
|
12
|
-
|
|
13
|
-
values
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
) => void;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
/** Touch state keyed by field name */
|
|
31
|
-
touched:
|
|
32
|
-
|
|
33
|
-
errors
|
|
34
|
-
|
|
72
|
+
|
|
73
|
+
/** Current form values keyed by field `name`. */
|
|
74
|
+
values: ITFormBuilderValues;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Change handler invoked by every rendered input/select/textarea. Designed
|
|
78
|
+
* to receive the native `event` from a controlled child, so a Formik
|
|
79
|
+
* `handleChange` can be passed directly.
|
|
80
|
+
*/
|
|
81
|
+
handleChange: (event: ITFormBuilderChangeEvent) => void;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Blur handler invoked by every rendered input/select/textarea. Designed
|
|
85
|
+
* to receive the native `event` from a controlled child, so a Formik
|
|
86
|
+
* `handleBlur` can be passed directly.
|
|
87
|
+
*/
|
|
88
|
+
handleBlur: (event: ITFormBuilderBlurEvent) => void;
|
|
89
|
+
|
|
90
|
+
/** Touch state keyed by field `name`. */
|
|
91
|
+
touched: ITFormBuilderTouched;
|
|
92
|
+
|
|
93
|
+
/** Validation errors keyed by field `name`. */
|
|
94
|
+
errors: ITFormBuilderErrors;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Programmatically sets a field value. Mirrors Formik's
|
|
98
|
+
* `setFieldValue(field, value, shouldValidate?)` so it can be passed through.
|
|
99
|
+
* Required to enable V2 features such as `onChangeAction` and
|
|
100
|
+
* `dynamicProps` side effects. Defaults to a no-op.
|
|
101
|
+
*/
|
|
35
102
|
setFieldValue?: (
|
|
36
103
|
field: string,
|
|
37
104
|
value: any,
|
|
38
105
|
shouldValidate?: boolean
|
|
39
106
|
) => Promise<void | any>;
|
|
40
|
-
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Programmatically marks a field as touched. Mirrors Formik's
|
|
110
|
+
* `setFieldTouched(field, touched?, shouldValidate?)`. Defaults to a no-op.
|
|
111
|
+
*/
|
|
41
112
|
setFieldTouched?: (
|
|
42
113
|
field: string,
|
|
43
114
|
touched?: boolean,
|
|
44
115
|
shouldValidate?: boolean
|
|
45
116
|
) => Promise<void | any>;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Programmatically sets a field-level error message (Formik parity). Defaults
|
|
120
|
+
* to a no-op.
|
|
121
|
+
*/
|
|
122
|
+
setFieldError?: (field: string, message: string | undefined) => void;
|
|
123
|
+
|
|
124
|
+
/** True while the parent form is submitting. Surfaced via the form context. */
|
|
52
125
|
isSubmitting?: boolean;
|
|
53
|
-
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Optional children rendered **inside** the V2 `ITFormBuilderProvider`,
|
|
129
|
+
* after the field grid. Useful for custom submit buttons or progress
|
|
130
|
+
* badges that read live state via {@link useFormBuilder} /
|
|
131
|
+
* `useITFormBuilderContext`. Ignored in the V1 (legacy) path because
|
|
132
|
+
* no provider is mounted there.
|
|
133
|
+
*/
|
|
134
|
+
children?: React.ReactNode;
|
|
135
|
+
}
|