@form-instant/react-input-mapping 2.0.2 → 2.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.
- package/README.md +453 -377
- package/dist/index.js +2 -2
- package/dist/index.js.map +3 -3
- package/package.json +31 -33
package/README.md
CHANGED
|
@@ -1,501 +1,577 @@
|
|
|
1
|
-
|
|
1
|
+
<!-- Documentación empaquetada para consumo por IA. Generado desde README.md + views/*.md -->
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Quick start guide
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
### 1. Installation and dependency order
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Install in this order (or all at once in a monorepo):
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
npm i @form-instant/react-input-mapping
|
|
11
|
-
```
|
|
9
|
+
1. **React** (peer)
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
```bash
|
|
12
|
+
bun add react react-dom
|
|
13
|
+
# or: npm i react react-dom
|
|
14
|
+
```
|
|
15
|
+
2. **Zod** (for defining schemas)
|
|
14
16
|
|
|
15
|
-
```
|
|
16
|
-
bun add
|
|
17
|
-
|
|
17
|
+
```bash
|
|
18
|
+
bun add zod
|
|
19
|
+
# or: npm i zod
|
|
20
|
+
```
|
|
21
|
+
3. **@form-instant/react-input-mapping** (field → component mapping)
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
```bash
|
|
24
|
+
bun add @form-instant/react-input-mapping
|
|
25
|
+
# or: npm i @form-instant/react-input-mapping
|
|
26
|
+
```
|
|
27
|
+
4. **@form-instant/react-resolver-zod** (resolves Zod schema and provides `FormInstantProvider` / `FormInstantElement`)
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
```bash
|
|
30
|
+
bun add @form-instant/react-resolver-zod
|
|
31
|
+
# or: npm i @form-instant/react-resolver-zod
|
|
32
|
+
```
|
|
22
33
|
|
|
23
|
-
|
|
34
|
+
This package has as peers: `react`, `@form-instant/react-input-mapping`, and `zod`. Without the mapping installed first, the resolver cannot resolve fields.
|
|
24
35
|
|
|
25
|
-
|
|
36
|
+
**Summary:** `react` → `zod` → `@form-instant/react-input-mapping` → `@form-instant/react-resolver-zod`.
|
|
26
37
|
|
|
27
|
-
<!-- tabs:start -->
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
`<span id="implementation">`
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
npm i @form-instant/react-resolver-zod
|
|
33
|
-
```
|
|
41
|
+
## Implementation in your project
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
```shell
|
|
38
|
-
bun add @form-instant/react-resolver-zod
|
|
39
|
-
```
|
|
43
|
+
### 2.1 Creating the Mapping
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
The **Mapping** is a dictionary that associates each `fieldType` (string) with a React component. You can use `InputMapping` or **`InputMappingStore`** (recommended: better support for granular re-renders).
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
- **Default keys** (`INPUT_COMPONENTS_KEYS`): `'checkbox' | 'date' | 'select' | 'radio' | 'switch' | 'textarea' | 'number' | 'file' | 'text' | 'fallback'`.
|
|
48
|
+
- **Extra keys:** you can extend with your own types (e.g. `email`, `password`, `customSelect`).
|
|
49
|
+
- Each component receives **ParsedField**: `name` (`current`, `history`), `fieldType`, `required`, `default`, `fieldConfig`, and for selects/enums `options` as `[value, label][]`.
|
|
44
50
|
|
|
45
|
-
|
|
51
|
+
Minimal example (in a file like `providers/input-mapping.tsx`):
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
```tsx
|
|
54
|
+
import {
|
|
55
|
+
InputMappingStore,
|
|
56
|
+
createFormInstantContainer,
|
|
57
|
+
type ParsedField,
|
|
58
|
+
type FieldConfig,
|
|
59
|
+
} from '@form-instant/react-input-mapping';
|
|
60
|
+
import type { FC } from 'react';
|
|
61
|
+
|
|
62
|
+
export type MyInputs = {
|
|
63
|
+
text: FieldConfig<{ placeholder?: string; label?: string }>;
|
|
64
|
+
number: FieldConfig<{ placeholder?: string; label?: string; min?: number; max?: number }>;
|
|
65
|
+
textarea: FieldConfig<{ placeholder?: string; label?: string }>;
|
|
66
|
+
date: FieldConfig;
|
|
67
|
+
email: FieldConfig<{ placeholder?: string; label?: string }>;
|
|
68
|
+
password: FieldConfig<{ placeholder?: string; label?: string }>;
|
|
69
|
+
checkbox: FieldConfig;
|
|
70
|
+
select: FieldConfig;
|
|
71
|
+
fallback: FieldConfig;
|
|
72
|
+
};
|
|
52
73
|
|
|
53
|
-
|
|
74
|
+
const TextInput: FC<ParsedField<MyInputs['text']>> = ({ name, fieldConfig, required, ...props }) => (
|
|
75
|
+
<div>
|
|
76
|
+
<label htmlFor={name.current}>{(fieldConfig as any)?.label ?? name.current}{required && ' *'}</label>
|
|
77
|
+
<input id={name.current} name={name.history} type="text" required={required} {...props} />
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
54
80
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
fallback: (props) => {
|
|
69
|
-
const { fieldConfig, name, ...prop } = props;
|
|
70
|
-
|
|
71
|
-
return <input {...prop} {...fieldConfig} />;
|
|
72
|
-
},
|
|
73
|
-
textarea: () => <textarea />,
|
|
74
|
-
number: (props) => {
|
|
75
|
-
const { fieldConfig, name, ...prop } = props;
|
|
76
|
-
|
|
77
|
-
return <input {...prop} {...fieldConfig} />;
|
|
78
|
-
},
|
|
79
|
-
text: (props) => {
|
|
80
|
-
const { fieldConfig, name, ...prop } = props;
|
|
81
|
-
|
|
82
|
-
return <input {...prop} {...fieldConfig} />;
|
|
83
|
-
},
|
|
84
|
-
date: () => <input type="date" />,
|
|
85
|
-
email: (props) => {
|
|
86
|
-
const { fieldConfig, name, ...prop } = props;
|
|
87
|
-
|
|
88
|
-
return <input {...prop} {...fieldConfig} />;
|
|
89
|
-
},
|
|
90
|
-
password: (props) => {
|
|
91
|
-
const { fieldConfig, name, ...prop } = props;
|
|
92
|
-
|
|
93
|
-
return <input {...prop} {...fieldConfig} />;
|
|
94
|
-
},
|
|
95
|
-
select: (props) => {
|
|
96
|
-
const { options } = props;
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
<select>
|
|
100
|
-
{options?.map(([k, v]) => (
|
|
101
|
-
<option key={k} value={k}>
|
|
102
|
-
{v}
|
|
103
|
-
</option>
|
|
104
|
-
))}
|
|
105
|
-
</select>
|
|
106
|
-
);
|
|
107
|
-
},
|
|
81
|
+
// Define the rest: NumberInput, TextareaInput, DateInput, EmailInput, PasswordInput,
|
|
82
|
+
// CheckboxInput, SelectInput (uses props.options as [value, label][]), FallbackInput...
|
|
83
|
+
|
|
84
|
+
export const inputMapping = new InputMappingStore<MyInputs>({
|
|
85
|
+
text: TextInput,
|
|
86
|
+
number: NumberInput,
|
|
87
|
+
textarea: TextareaInput,
|
|
88
|
+
date: DateInput,
|
|
89
|
+
email: EmailInput,
|
|
90
|
+
password: PasswordInput,
|
|
91
|
+
checkbox: CheckboxInput,
|
|
92
|
+
select: SelectInput,
|
|
93
|
+
fallback: FallbackInput,
|
|
108
94
|
});
|
|
109
|
-
```
|
|
110
95
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
export type INPUT_COMPONENTS_KEYS =
|
|
115
|
-
| 'checkbox'
|
|
116
|
-
| 'date'
|
|
117
|
-
| 'select'
|
|
118
|
-
| 'radio'
|
|
119
|
-
| 'switch'
|
|
120
|
-
| 'textarea'
|
|
121
|
-
| 'number'
|
|
122
|
-
| 'file'
|
|
123
|
-
| 'text'
|
|
124
|
-
| 'fallback';
|
|
96
|
+
export const { FormInstantInputsProvider, useInputMapping } =
|
|
97
|
+
createFormInstantContainer<MyInputs>(inputMapping);
|
|
125
98
|
```
|
|
126
99
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
We created a global provider to be able to access input mapping.
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
import { createFormInstantContainer } from '@form-instant/react-input-mapping';
|
|
133
|
-
import { inputMapping, P, K } from './inputMapping.tsx';
|
|
134
|
-
|
|
135
|
-
export const { FormInstantInputsProvider, useInputMapping } = createFormInstantContainer<P, K>(
|
|
136
|
-
inputMapping,
|
|
137
|
-
);
|
|
138
|
-
```
|
|
100
|
+
- **Select:** the component receives `options?: [string, string][]`; you can populate them from an API (data fetch) and keep using the same mapping.
|
|
139
101
|
|
|
140
|
-
|
|
102
|
+
### 2.2 Setting up providers
|
|
141
103
|
|
|
142
|
-
|
|
104
|
+
- `createFormInstantContainer<MyInputs>(inputMapping)` returns:
|
|
105
|
+
- **`FormInstantInputsProvider`**: provider that should wrap your app (or at least the area where forms are used).
|
|
106
|
+
- **`useInputMapping`**: hook to access the mapping (e.g. for custom components that use `ElementMapping`).
|
|
143
107
|
|
|
144
|
-
|
|
108
|
+
Place it at the root (e.g. `App.tsx` or `layout.tsx`):
|
|
145
109
|
|
|
146
|
-
```
|
|
147
|
-
import {
|
|
148
|
-
import { FormInstantInputsProvider } from "./components/providers";
|
|
110
|
+
```tsx
|
|
111
|
+
import { FormInstantInputsProvider } from './providers/input-mapping';
|
|
149
112
|
|
|
150
|
-
function
|
|
113
|
+
function App() {
|
|
151
114
|
return (
|
|
152
115
|
<FormInstantInputsProvider>
|
|
153
|
-
{
|
|
116
|
+
{/* routes, forms, etc. */}
|
|
154
117
|
</FormInstantInputsProvider>
|
|
155
118
|
);
|
|
156
119
|
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 2.3 Zod schema and `fieldConfig`
|
|
123
|
+
|
|
124
|
+
- Define the schema with **Zod**. With **@form-instant/react-resolver-zod** the package already extends Zod with `.fieldConfig()` on the types used by the parser.
|
|
125
|
+
- **`.fieldConfig(...)`** is used to specify `fieldType` and extra props (placeholder, label, min, max, etc.) that reach the component via `fieldConfig`.
|
|
157
126
|
|
|
158
|
-
|
|
127
|
+
Example:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { z } from 'zod';
|
|
131
|
+
// If you use @form-instant/react-resolver-zod, you don't need to call extendZodWithFieldConfig:
|
|
132
|
+
// the module applies the extension on import.
|
|
133
|
+
|
|
134
|
+
const formSchema = z.object({
|
|
135
|
+
name: z.string().min(2).fieldConfig({ fieldType: 'text', placeholder: 'Name', label: 'Name' }),
|
|
136
|
+
email: z.email().fieldConfig({ fieldType: 'email', placeholder: 'email@example.com' }),
|
|
137
|
+
age: z.number().min(18).max(100).fieldConfig({ fieldType: 'number', min: 18, max: 100 }),
|
|
138
|
+
bio: z.string().min(10).fieldConfig({ fieldType: 'textarea' }),
|
|
139
|
+
role: z.enum(['admin', 'user']).fieldConfig({ fieldType: 'select' }),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
export type FormSchemaType = z.infer<typeof formSchema>;
|
|
159
143
|
```
|
|
160
144
|
|
|
161
|
-
|
|
145
|
+
- If you omit `fieldConfig`, the resolver infers `fieldType` from the Zod type (string → text, number → number, etc.).
|
|
162
146
|
|
|
163
|
-
|
|
164
|
-
import "./App.css";
|
|
165
|
-
import { Router } from "./router";
|
|
166
|
-
import { FormInstantInputsProvider } from "./components/providers";
|
|
147
|
+
### 2.4 Rendering forms
|
|
167
148
|
|
|
168
|
-
|
|
149
|
+
- Wrap the form in **`FormInstantProvider`** with the schema. Pass a stable schema reference (e.g. defined outside the component or memoized with `useMemo`) so the provider does not re-parse the schema on every parent re-render.
|
|
150
|
+
- Use **`FormInstantElement<FormSchemaType> name="..."`** for each **path** in the schema you want to render:
|
|
151
|
+
- **Primitive field:** `name="name"` → a single input.
|
|
152
|
+
- **Nested object:** `name="personalData"` → all fields of `personalData` are rendered (the provider parses the schema and exposes `field.schema`; `FormInstantElement` iterates and uses `ElementMapping` for each child).
|
|
153
|
+
|
|
154
|
+
Minimal example:
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { FormInstantProvider, FormInstantElement } from '@form-instant/react-resolver-zod';
|
|
158
|
+
import { formSchema, type FormSchemaType } from './schema';
|
|
159
|
+
|
|
160
|
+
export function MyForm() {
|
|
169
161
|
return (
|
|
170
|
-
<
|
|
171
|
-
<
|
|
172
|
-
|
|
162
|
+
<form onSubmit={...}>
|
|
163
|
+
<FormInstantProvider schema={formSchema}>
|
|
164
|
+
<FormInstantElement<FormSchemaType> name="name" />
|
|
165
|
+
<FormInstantElement<FormSchemaType> name="email" />
|
|
166
|
+
<FormInstantElement<FormSchemaType> name="age" />
|
|
167
|
+
<FormInstantElement<FormSchemaType> name="bio" />
|
|
168
|
+
<FormInstantElement<FormSchemaType> name="role" />
|
|
169
|
+
</FormInstantProvider>
|
|
170
|
+
</form>
|
|
173
171
|
);
|
|
174
172
|
}
|
|
175
|
-
|
|
176
|
-
export default App;
|
|
177
173
|
```
|
|
178
174
|
|
|
179
|
-
|
|
175
|
+
For **nested objects**, one `FormInstantElement` per object:
|
|
180
176
|
|
|
181
|
-
|
|
177
|
+
```tsx
|
|
178
|
+
<FormInstantProvider schema={objectFormSchema}>
|
|
179
|
+
<FormInstantElement<ObjectFormType> name="personalData" />
|
|
180
|
+
<FormInstantElement<ObjectFormType> name="address" />
|
|
181
|
+
</FormInstantProvider>
|
|
182
|
+
```
|
|
182
183
|
|
|
183
|
-
To use our resolver we must add the function **_fieldConfig_**.
|
|
184
184
|
|
|
185
|
-
|
|
185
|
+
`<span id="form-types">`
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
## Form types
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
### 3.1 Static form
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
import { createFormInstantContainer } from '@form-instant/react-input-mapping';
|
|
193
|
-
import { inputMapping, P, K, extendProps } from './inputMapping.tsx';
|
|
191
|
+
Flat schema, fixed fields. You only need `FormInstantProvider` plus several `FormInstantElement` (one per field or per object group).
|
|
194
192
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
)
|
|
193
|
+
```tsx
|
|
194
|
+
const schema = z.object({
|
|
195
|
+
name: z.string().min(2),
|
|
196
|
+
email: z.email(),
|
|
197
|
+
age: z.number().min(18).max(100),
|
|
198
|
+
});
|
|
199
|
+
// ...
|
|
200
|
+
<FormInstantProvider schema={schema}>
|
|
201
|
+
<FormInstantElement<FormType> name="name" />
|
|
202
|
+
<FormInstantElement<FormType> name="email" />
|
|
203
|
+
<FormInstantElement<FormType> name="age" />
|
|
204
|
+
</FormInstantProvider>
|
|
198
205
|
```
|
|
199
206
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
import { z } from 'zod';
|
|
207
|
+
### 3.2 Inputs with data fetch (select / autocomplete)
|
|
204
208
|
|
|
205
|
-
|
|
209
|
+
- **Select:** in the schema use `z.enum([...])` or a type the parser turns into `options`; the mapping component receives `options` and can also receive data loaded from an API (store options in state and pass them via context or props to the component that renders the field).
|
|
210
|
+
- **Autocomplete:** you can define your own `fieldType` (e.g. `autocomplete`), map it to a component that uses `useFields` and internally fetches and shows suggestions; the final value is still bound to the same `name`.
|
|
206
211
|
|
|
207
|
-
|
|
208
|
-
```
|
|
212
|
+
The select mapping already uses `options?: [string, string][]`; populating `options` from an API is compatible with the same flow.
|
|
209
213
|
|
|
210
|
-
|
|
214
|
+
### 3.3 Dynamic form with `discriminatedUnion`
|
|
211
215
|
|
|
212
|
-
|
|
216
|
+
Schema that changes based on a discriminator (e.g. user type). Steps:
|
|
213
217
|
|
|
214
|
-
|
|
218
|
+
1. Define the schema with **`z.discriminatedUnion('status', [ z.object({ status: z.literal('ok'), ... }), z.object({ status: z.literal('not'), ... }) ])`**.
|
|
219
|
+
2. Use **`useSchema`** from `@form-instant/react-resolver-zod`: it receives a callback that returns the schema and a dependencies object; when the **reference** of the dependencies object changes, the schema and initial values are recalculated. Pass a stable dependencies object (e.g. from state or `useMemo`) to avoid unnecessary recalculations on parent re-renders.
|
|
220
|
+
3. Keep **dependencies** in sync with the current discriminator value in the form (e.g. with `form.watch('data.status')` if you use react-hook-form, or your own state).
|
|
221
|
+
4. The first field in the union is the discriminator; you can map that field to a select in your mapping (by `fieldType` or by the discriminator key) so the user can switch the variant.
|
|
215
222
|
|
|
216
|
-
|
|
217
|
-
import { z } from 'zod';
|
|
223
|
+
Structure example (submit and form provider depend on your form library):
|
|
218
224
|
|
|
225
|
+
```tsx
|
|
219
226
|
const formSchema = z.object({
|
|
220
|
-
data: z.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
})
|
|
227
|
+
data: z.discriminatedUnion('status', [
|
|
228
|
+
z.object({ status: z.literal('ok'), code: z.string() }),
|
|
229
|
+
z.object({ status: z.literal('not'), birthday: z.coerce.date() }),
|
|
230
|
+
]),
|
|
225
231
|
});
|
|
226
232
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
- component
|
|
233
|
+
const [dependencies, setDependencies] = useState({ status: 'ok' });
|
|
234
|
+
const { schema } = useSchema(() => formSchema, dependencies);
|
|
231
235
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
FormInstantProvider,
|
|
236
|
-
} from "@form-instant/react-resolver-zod";
|
|
237
|
-
import { z } from "zod";
|
|
238
|
-
import { formSchema, formSchemaType } from "./schema";
|
|
236
|
+
// In a useEffect or in the same flow as the form: when data.status changes,
|
|
237
|
+
// update setDependencies({ status: newStatus }) so useSchema returns
|
|
238
|
+
// the correct schema and FormInstantElement shows the fields for that variant.
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
<h1>your form</h1>
|
|
244
|
-
<FormInstantProvider schema={formSchema}>
|
|
245
|
-
<div>
|
|
246
|
-
<FormInstantElement<formSchemaType> name="security_data" />
|
|
247
|
-
<br />
|
|
248
|
-
<FormInstantElement<formSchemaType> name="personal_data" />
|
|
249
|
-
</div>
|
|
250
|
-
</FormInstantProvider>
|
|
251
|
-
|
|
252
|
-
<button type="submit">submit</button>
|
|
253
|
-
</form>
|
|
254
|
-
);
|
|
255
|
-
};
|
|
240
|
+
<FormInstantProvider schema={schema}>
|
|
241
|
+
<FormInstantElement<FormType> name="data" />
|
|
242
|
+
</FormInstantProvider>
|
|
256
243
|
```
|
|
257
244
|
|
|
258
|
-
|
|
245
|
+
### 3.4 Array with dynamic input creation
|
|
259
246
|
|
|
260
|
-
|
|
247
|
+
For **arrays of objects** (e.g. list of items or skills):
|
|
261
248
|
|
|
262
|
-
|
|
249
|
+
1. Schema with **`z.array(z.object({ ... }))`** (optionally `.min()`/`.max()`).
|
|
250
|
+
2. In the **array** component:
|
|
251
|
+
- **`useFields({ key: 'items' })`** (or the array field name) to get the parsed field.
|
|
252
|
+
- **`useInputArray(field)`** (from `@form-instant/react-input-mapping`): returns `inputs`, `append`, `remove`, `fieldConfig`, etc.
|
|
253
|
+
3. Render each item: for each entry in `inputs`, iterate over properties and use **`ElementMapping formProps={...}`** for each. +/- buttons that call `append()` and `remove(index)`.
|
|
263
254
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
255
|
+
Condensed example:
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
const arrayFormSchema = z.object({
|
|
259
|
+
items: z.array(z.object({
|
|
260
|
+
name: z.string().min(2),
|
|
261
|
+
quantity: z.number().min(1),
|
|
262
|
+
price: z.number().min(0),
|
|
263
|
+
})).min(1).max(10),
|
|
264
|
+
});
|
|
268
265
|
|
|
269
|
-
|
|
270
|
-
const {
|
|
266
|
+
function ArrayFieldComponent({ name }: { name: 'items' }) {
|
|
267
|
+
const field = useFields({ key: name });
|
|
268
|
+
const { inputs, append, remove, fieldConfig } = useInputArray(field);
|
|
271
269
|
const id = useId();
|
|
272
270
|
|
|
273
271
|
return (
|
|
274
|
-
<div
|
|
275
|
-
{
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
<ElementMapping formProps={
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
272
|
+
<div>
|
|
273
|
+
{inputs.map((inputFields, index) => (
|
|
274
|
+
<div key={`${id}-${index}`}>
|
|
275
|
+
{Object.entries(inputFields).map(([key, value]) => (
|
|
276
|
+
<ElementMapping key={key} formProps={value} />
|
|
277
|
+
))}
|
|
278
|
+
<button type="button" onClick={() => remove(index)}>Remove</button>
|
|
279
|
+
</div>
|
|
280
|
+
))}
|
|
281
|
+
<button type="button" onClick={append}>+ Add</button>
|
|
282
282
|
</div>
|
|
283
283
|
);
|
|
284
|
-
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
<FormInstantProvider schema={arrayFormSchema}>
|
|
287
|
+
<ArrayFieldComponent name="items" />
|
|
288
|
+
</FormInstantProvider>
|
|
285
289
|
```
|
|
286
290
|
|
|
287
|
-
|
|
291
|
+
`fieldConfig?.min` / `fieldConfig?.max` can come from the schema (e.g. via `.fieldConfig({ min: 1, max: 10 })`) to disable buttons based on limits.
|
|
288
292
|
|
|
289
|
-
|
|
290
|
-
import { Fragment, useId } from "react";
|
|
291
|
-
import { ElementMapping, ParsedField, useFormInstantField } from "@form-instant/react-input-mapping";
|
|
292
|
-
import { P } from "@/providers";
|
|
293
|
+
### 3.5 Nested object
|
|
293
294
|
|
|
295
|
+
Schema with **`z.object({ group: z.object({ a: z.string(), b: z.number() }) })`**. No custom component needed: a single **`FormInstantElement<FormType> name="group"`** makes the resolver render all fields of `group` (the parser fills `field.schema` and `FormInstantElement` walks those children with `ElementMapping`).
|
|
294
296
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
297
|
+
```tsx
|
|
298
|
+
const objectFormSchema = z.object({
|
|
299
|
+
personalData: z.object({
|
|
300
|
+
firstName: z.string().min(2),
|
|
301
|
+
lastName: z.string().min(2),
|
|
302
|
+
email: z.string().email(),
|
|
303
|
+
}),
|
|
304
|
+
address: z.object({
|
|
305
|
+
street: z.string(),
|
|
306
|
+
city: z.string(),
|
|
307
|
+
zipCode: z.string(),
|
|
308
|
+
}),
|
|
309
|
+
});
|
|
298
310
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
<Fragment key={`${id}-${prop.name.history}`}>
|
|
304
|
-
<div>
|
|
305
|
-
<ElementMapping formProps={prop} />
|
|
306
|
-
<button onClick={() => append()}>+</button>
|
|
307
|
-
<button onClick={() => remove(index)}>-</button>
|
|
308
|
-
</div>
|
|
309
|
-
<br />
|
|
310
|
-
<br />
|
|
311
|
-
</Fragment>
|
|
312
|
-
);
|
|
313
|
-
})}
|
|
314
|
-
</div>
|
|
315
|
-
);
|
|
316
|
-
};
|
|
311
|
+
<FormInstantProvider schema={objectFormSchema}>
|
|
312
|
+
<FormInstantElement<ObjectFormType> name="personalData" />
|
|
313
|
+
<FormInstantElement<ObjectFormType> name="address" />
|
|
314
|
+
</FormInstantProvider>
|
|
317
315
|
```
|
|
318
316
|
|
|
319
|
-
## **_reactive schemas_**
|
|
320
317
|
|
|
321
|
-
|
|
318
|
+
`<span id="full-example">`
|
|
322
319
|
|
|
323
|
-
|
|
324
|
-
import { Fragment, useId } from "react";
|
|
325
|
-
import { ElementMapping, useFormInstantField } from "@form-instant/react-input-mapping";
|
|
326
|
-
import { FormInstantElement, FormInstantProvider } from "@form-instant/react-resolver-zod";
|
|
327
|
-
import { P } from "@/providers";
|
|
328
|
-
import { z } from '@/zod';
|
|
320
|
+
## Full example: React Hook Form + Shadcn UI + Zod
|
|
329
321
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
322
|
+
This example wires **react-hook-form**, **@hookform/resolvers** (Zod resolver), **Zod**, and **shadcn/ui** (or any similar UI primitives) with Form Instant. Validation runs via `zodResolver`; inputs are rendered from your schema and mapping.
|
|
323
|
+
|
|
324
|
+
### Dependencies
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
bun add react-hook-form @hookform/resolvers zod
|
|
328
|
+
# or: npm i react-hook-form @hookform/resolvers zod
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Add **shadcn/ui** with your stack (e.g. `npx shadcn@latest init`) and install the components you need (e.g. `Input`, `Label`, `Button`). You can also use plain HTML elements or your own design system.
|
|
332
|
+
|
|
333
|
+
### 1. Zod schema
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
// lib/schemas/profile.ts
|
|
337
|
+
import { z } from 'zod';
|
|
338
|
+
|
|
339
|
+
export const profileSchema = z.object({
|
|
340
|
+
username: z.string().min(2, 'At least 2 characters').fieldConfig({ fieldType: 'text', placeholder: 'Username', label: 'Username' }),
|
|
341
|
+
email: z.string().email('Invalid email').fieldConfig({ fieldType: 'email', placeholder: 'you@example.com', label: 'Email' }),
|
|
342
|
+
age: z.number().min(18, 'Must be 18+').max(120).fieldConfig({ fieldType: 'number', label: 'Age', min: 18, max: 120 }),
|
|
343
|
+
bio: z.string().min(10, 'At least 10 characters').fieldConfig({ fieldType: 'textarea', label: 'Bio', placeholder: 'Tell us about yourself' }),
|
|
342
344
|
});
|
|
343
345
|
|
|
344
|
-
export
|
|
346
|
+
export type ProfileFormValues = z.infer<typeof profileSchema>;
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### 2. Input mapping (with react-hook-form registration)
|
|
350
|
+
|
|
351
|
+
For react-hook-form to validate and submit correctly, each rendered input must be **registered**. Use `useFormContext()` inside your mapping components and spread `register(name.history)` (and optionally show errors from `formState.errors`).
|
|
352
|
+
|
|
353
|
+
```tsx
|
|
354
|
+
// components/input-mapping.tsx
|
|
355
|
+
import {
|
|
356
|
+
InputMappingStore,
|
|
357
|
+
createFormInstantContainer,
|
|
358
|
+
type ParsedField,
|
|
359
|
+
type FieldConfig,
|
|
360
|
+
} from '@form-instant/react-input-mapping';
|
|
361
|
+
import { useFormContext } from 'react-hook-form';
|
|
362
|
+
import type { FC } from 'react';
|
|
363
|
+
import { Input } from '@/components/ui/input';
|
|
364
|
+
import { Label } from '@/components/ui/label';
|
|
365
|
+
import { Textarea } from '@/components/ui/textarea';
|
|
366
|
+
|
|
367
|
+
export type MyInputs = {
|
|
368
|
+
text: FieldConfig<{ placeholder?: string; label?: string }>;
|
|
369
|
+
number: FieldConfig<{ placeholder?: string; label?: string; min?: number; max?: number }>;
|
|
370
|
+
textarea: FieldConfig<{ placeholder?: string; label?: string }>;
|
|
371
|
+
email: FieldConfig<{ placeholder?: string; label?: string }>;
|
|
372
|
+
fallback: FieldConfig;
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const TextInput: FC<ParsedField<MyInputs['text']>> = ({ name, fieldConfig, required, ...props }) => {
|
|
376
|
+
const { register, formState: { errors } } = useFormContext();
|
|
377
|
+
const label = (fieldConfig as any)?.label ?? name.current;
|
|
378
|
+
const placeholder = (fieldConfig as any)?.placeholder ?? '';
|
|
345
379
|
|
|
346
380
|
return (
|
|
347
|
-
|
|
348
|
-
<
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
381
|
+
<div className="space-y-2">
|
|
382
|
+
<Label htmlFor={name.current}>{label}{required && ' *'}</Label>
|
|
383
|
+
<Input
|
|
384
|
+
id={name.current}
|
|
385
|
+
type="text"
|
|
386
|
+
placeholder={placeholder}
|
|
387
|
+
aria-invalid={!!errors[name.history]}
|
|
388
|
+
{...register(name.history)}
|
|
389
|
+
{...props}
|
|
390
|
+
/>
|
|
391
|
+
{errors[name.history]?.message && (
|
|
392
|
+
<p className="text-sm text-destructive">{String(errors[name.history]?.message)}</p>
|
|
393
|
+
)}
|
|
394
|
+
</div>
|
|
359
395
|
);
|
|
360
396
|
};
|
|
361
|
-
```
|
|
362
397
|
|
|
363
|
-
|
|
398
|
+
const NumberInput: FC<ParsedField<MyInputs['number']>> = ({ name, fieldConfig, required, ...props }) => {
|
|
399
|
+
const { register, formState: { errors } } = useFormContext();
|
|
400
|
+
const label = (fieldConfig as any)?.label ?? name.current;
|
|
364
401
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
402
|
+
return (
|
|
403
|
+
<div className="space-y-2">
|
|
404
|
+
<Label htmlFor={name.current}>{label}{required && ' *'}</Label>
|
|
405
|
+
<Input
|
|
406
|
+
id={name.current}
|
|
407
|
+
type="number"
|
|
408
|
+
min={fieldConfig?.min}
|
|
409
|
+
max={fieldConfig?.max}
|
|
410
|
+
aria-invalid={!!errors[name.history]}
|
|
411
|
+
{...register(name.history, { valueAsNumber: true })}
|
|
412
|
+
{...props}
|
|
413
|
+
/>
|
|
414
|
+
{errors[name.history]?.message && (
|
|
415
|
+
<p className="text-sm text-destructive">{String(errors[name.history]?.message)}</p>
|
|
416
|
+
)}
|
|
417
|
+
</div>
|
|
418
|
+
);
|
|
419
|
+
};
|
|
369
420
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
421
|
+
const TextareaInput: FC<ParsedField<MyInputs['textarea']>> = ({ name, fieldConfig, required, ...props }) => {
|
|
422
|
+
const { register, formState: { errors } } = useFormContext();
|
|
423
|
+
const label = (fieldConfig as any)?.label ?? name.current;
|
|
424
|
+
const placeholder = (fieldConfig as any)?.placeholder ?? '';
|
|
374
425
|
|
|
375
|
-
|
|
426
|
+
return (
|
|
427
|
+
<div className="space-y-2">
|
|
428
|
+
<Label htmlFor={name.current}>{label}{required && ' *'}</Label>
|
|
429
|
+
<Textarea
|
|
430
|
+
id={name.current}
|
|
431
|
+
placeholder={placeholder}
|
|
432
|
+
aria-invalid={!!errors[name.history]}
|
|
433
|
+
{...register(name.history)}
|
|
434
|
+
{...props}
|
|
435
|
+
/>
|
|
436
|
+
{errors[name.history]?.message && (
|
|
437
|
+
<p className="text-sm text-destructive">{String(errors[name.history]?.message)}</p>
|
|
438
|
+
)}
|
|
439
|
+
</div>
|
|
440
|
+
);
|
|
441
|
+
};
|
|
376
442
|
|
|
377
|
-
|
|
443
|
+
const EmailInput: FC<ParsedField<MyInputs['email']>> = ({ name, fieldConfig, required, ...props }) => {
|
|
444
|
+
const { register, formState: { errors } } = useFormContext();
|
|
445
|
+
const label = (fieldConfig as any)?.label ?? name.current;
|
|
446
|
+
const placeholder = (fieldConfig as any)?.placeholder ?? '';
|
|
378
447
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
448
|
+
return (
|
|
449
|
+
<div className="space-y-2">
|
|
450
|
+
<Label htmlFor={name.current}>{label}{required && ' *'}</Label>
|
|
451
|
+
<Input
|
|
452
|
+
id={name.current}
|
|
453
|
+
type="email"
|
|
454
|
+
placeholder={placeholder}
|
|
455
|
+
aria-invalid={!!errors[name.history]}
|
|
456
|
+
{...register(name.history)}
|
|
457
|
+
{...props}
|
|
458
|
+
/>
|
|
459
|
+
{errors[name.history]?.message && (
|
|
460
|
+
<p className="text-sm text-destructive">{String(errors[name.history]?.message)}</p>
|
|
461
|
+
)}
|
|
462
|
+
</div>
|
|
463
|
+
);
|
|
464
|
+
};
|
|
386
465
|
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
status: z.literal("ok"),
|
|
466
|
+
const FallbackInput: FC<ParsedField<MyInputs['fallback']>> = ({ name, fieldConfig, required, ...props }) => {
|
|
467
|
+
const { register, formState: { errors } } = useFormContext();
|
|
468
|
+
const label = (fieldConfig as any)?.label ?? name.current;
|
|
391
469
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
470
|
+
return (
|
|
471
|
+
<div className="space-y-2">
|
|
472
|
+
<Label htmlFor={name.current}>{label}{required && ' *'}</Label>
|
|
473
|
+
<Input
|
|
474
|
+
id={name.current}
|
|
475
|
+
type="text"
|
|
476
|
+
aria-invalid={!!errors[name.history]}
|
|
477
|
+
{...register(name.history)}
|
|
478
|
+
{...props}
|
|
479
|
+
/>
|
|
480
|
+
{errors[name.history]?.message && (
|
|
481
|
+
<p className="text-sm text-destructive">{String(errors[name.history]?.message)}</p>
|
|
482
|
+
)}
|
|
483
|
+
</div>
|
|
484
|
+
);
|
|
485
|
+
};
|
|
396
486
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
487
|
+
export const inputMapping = new InputMappingStore<MyInputs>({
|
|
488
|
+
text: TextInput,
|
|
489
|
+
number: NumberInput,
|
|
490
|
+
textarea: TextareaInput,
|
|
491
|
+
email: EmailInput,
|
|
492
|
+
fallback: FallbackInput,
|
|
400
493
|
});
|
|
401
494
|
|
|
402
|
-
export const
|
|
495
|
+
export const { FormInstantInputsProvider, useInputMapping } =
|
|
496
|
+
createFormInstantContainer<MyInputs>(inputMapping);
|
|
497
|
+
```
|
|
403
498
|
|
|
404
|
-
|
|
405
|
-
const [dependencies, setDependencies] = useState({ status: "" });
|
|
499
|
+
### 3. Form component
|
|
406
500
|
|
|
407
|
-
|
|
408
|
-
return formSchema;
|
|
409
|
-
}, dependencies);
|
|
501
|
+
Use **FormProvider** from react-hook-form, **zodResolver** with your schema, and **FormInstantProvider** + **FormInstantElement** so fields are rendered from the schema and your mapping.
|
|
410
502
|
|
|
411
|
-
|
|
412
|
-
|
|
503
|
+
```tsx
|
|
504
|
+
// components/profile-form.tsx
|
|
505
|
+
import { useForm, FormProvider } from 'react-hook-form';
|
|
506
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
507
|
+
import { FormInstantProvider, FormInstantElement } from '@form-instant/react-resolver-zod';
|
|
508
|
+
import { profileSchema, type ProfileFormValues } from '@/lib/schemas/profile';
|
|
509
|
+
import { Button } from '@/components/ui/button';
|
|
510
|
+
|
|
511
|
+
export function ProfileForm() {
|
|
512
|
+
const form = useForm<ProfileFormValues>({
|
|
513
|
+
resolver: zodResolver(profileSchema),
|
|
413
514
|
defaultValues: {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
515
|
+
username: '',
|
|
516
|
+
email: '',
|
|
517
|
+
age: undefined,
|
|
518
|
+
bio: '',
|
|
417
519
|
},
|
|
418
520
|
});
|
|
419
521
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
taken from the useFormValues hook
|
|
424
|
-
recommended by react-hook-form */
|
|
425
|
-
const fromValues = {
|
|
426
|
-
...form.getValues(),
|
|
427
|
-
...form.watch(),
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
if (
|
|
431
|
-
!dependencies.status ||
|
|
432
|
-
dependencies.status !== fromValues.data.status
|
|
433
|
-
) {
|
|
434
|
-
|
|
435
|
-
setDependencies((prev) => {
|
|
436
|
-
return {
|
|
437
|
-
...prev,
|
|
438
|
-
status: fromValues.data.status,
|
|
439
|
-
};
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
}, [form.watch(), dependencies]);
|
|
443
|
-
|
|
444
|
-
const onSubmit = form.handleSubmit(
|
|
445
|
-
(data) => {
|
|
446
|
-
console.log("data", data);
|
|
447
|
-
},
|
|
448
|
-
(err) => {
|
|
449
|
-
console.log("err", err);
|
|
450
|
-
}
|
|
451
|
-
);
|
|
522
|
+
const onSubmit = (data: ProfileFormValues) => {
|
|
523
|
+
console.log(data);
|
|
524
|
+
};
|
|
452
525
|
|
|
453
526
|
return (
|
|
454
|
-
<
|
|
455
|
-
<
|
|
456
|
-
<FormInstantProvider schema={
|
|
457
|
-
<
|
|
458
|
-
<
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
</div>
|
|
462
|
-
<button
|
|
463
|
-
onClick={(e) => {
|
|
464
|
-
e.preventDefault();
|
|
465
|
-
const pre = form.getValues("personal_data.status");
|
|
466
|
-
|
|
467
|
-
form.setValue(
|
|
468
|
-
"personal_data.status",
|
|
469
|
-
pre === "not" ? "ok" : "not"
|
|
470
|
-
);
|
|
471
|
-
}}
|
|
472
|
-
>
|
|
473
|
-
switch
|
|
474
|
-
</button>
|
|
527
|
+
<FormProvider {...form}>
|
|
528
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
|
529
|
+
<FormInstantProvider schema={profileSchema}>
|
|
530
|
+
<FormInstantElement<ProfileFormValues> name="username" />
|
|
531
|
+
<FormInstantElement<ProfileFormValues> name="email" />
|
|
532
|
+
<FormInstantElement<ProfileFormValues> name="age" />
|
|
533
|
+
<FormInstantElement<ProfileFormValues> name="bio" />
|
|
475
534
|
</FormInstantProvider>
|
|
476
|
-
|
|
477
|
-
|
|
535
|
+
<Button type="submit">Submit</Button>
|
|
536
|
+
</form>
|
|
537
|
+
</FormProvider>
|
|
478
538
|
);
|
|
479
|
-
}
|
|
539
|
+
}
|
|
480
540
|
```
|
|
481
541
|
|
|
482
|
-
|
|
542
|
+
### 4. App root
|
|
483
543
|
|
|
484
|
-
|
|
485
|
-
import { FC } from "react";
|
|
486
|
-
import { P } from "@/providers";
|
|
544
|
+
Ensure **FormInstantInputsProvider** wraps the tree where forms use the mapping (e.g. root layout or App).
|
|
487
545
|
|
|
488
|
-
|
|
489
|
-
|
|
546
|
+
```tsx
|
|
547
|
+
// App.tsx
|
|
548
|
+
import { FormInstantInputsProvider } from '@/components/input-mapping';
|
|
549
|
+
import { ProfileForm } from '@/components/profile-form';
|
|
490
550
|
|
|
551
|
+
export default function App() {
|
|
491
552
|
return (
|
|
492
|
-
<
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
{v}
|
|
496
|
-
</option>
|
|
497
|
-
))}
|
|
498
|
-
</select>
|
|
553
|
+
<FormInstantInputsProvider>
|
|
554
|
+
<ProfileForm />
|
|
555
|
+
</FormInstantInputsProvider>
|
|
499
556
|
);
|
|
500
557
|
}
|
|
501
558
|
```
|
|
559
|
+
|
|
560
|
+
**Summary:** Use **Zod** for the schema, **zodResolver** in `useForm`, **FormProvider** (react-hook-form) around the form, and **FormInstantProvider** + **FormInstantElement** to render fields from the schema. In your mapping components, call **useFormContext()** and **register(name.history)** (and optionally **formState.errors**) so react-hook-form controls and validates the inputs.
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
`<span id="api-reference">`
|
|
564
|
+
|
|
565
|
+
## API reference (minimal)
|
|
566
|
+
|
|
567
|
+
| Concept | Package | Description |
|
|
568
|
+
| -------------------------------------------------------------- | -------------------------------- | --------------------------------------------------------------------- |
|
|
569
|
+
| `InputMapping` / `InputMappingStore` | react-input-mapping | Maps fieldType → React component. |
|
|
570
|
+
| `createFormInstantContainer` | react-input-mapping | Creates `FormInstantInputsProvider` and `useInputMapping`. |
|
|
571
|
+
| `ParsedField`, `FieldConfig` | react-input-mapping | Props types for mapping components. |
|
|
572
|
+
| `ElementMapping` | react-input-mapping | Renders a single field from `formProps` (fieldType, name, etc.). |
|
|
573
|
+
| `useInputArray` | react-input-mapping | For arrays:`inputs`, `append`, `remove`, `fieldConfig`. |
|
|
574
|
+
| `FormInstantProvider`, `FormInstantElement`, `useFields` | react-resolver-zod | Schema provider, element per path, hook for a field. |
|
|
575
|
+
| `useSchema` | react-resolver-zod | Reactive schema (e.g. for `discriminatedUnion`) and initial values. Recalculates when the **reference** of the dependencies object changes; pass a stable object to avoid unnecessary recalculations. |
|
|
576
|
+
| `.fieldConfig(...)` | react-resolver-zod (extends Zod) | Associate `fieldType` and props with a schema field. |
|
|
577
|
+
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
class
|
|
1
|
+
class U extends Map{appendObj(R){for(let O in R)this.set(O,R[O])}constructor(R){super();if(!R)return;this.appendObj(R)}exists(R){if(!super.has(R))return"fallback";return R}get(R){return super.get(R)}set(R,O){if(!super.has(R))super.set(R,O);return this}extends(R){let O=Object.fromEntries(super.entries()),q=R(this);return new U({...O,...q})}}import{createContext as M}from"react";var G=M(null);import{createElement as S,memo as m,use as g}from"react";class E{listeners=new Map;subscribe(R,O){if(!this.listeners.has(R))this.listeners.set(R,new Set);return this.listeners.get(R).add(O),()=>this.unsubscribe(R,O)}unsubscribe(R,O){this.listeners.get(R)?.delete(O)}getListeners(R){return this.listeners.get(R)}hasListeners(R){return this.listeners.has(R)&&this.listeners.get(R).size>0}clear(){this.listeners.clear()}}class Z{static notify(R,O){let q=R.getListeners(O);if(q)q.forEach((z)=>z())}static notifyMany(R,O){O.forEach((q)=>this.notify(R,q))}}class X extends U{_subscriptionManager;get subscriptionManager(){if(!this._subscriptionManager)this._subscriptionManager=new E;return this._subscriptionManager}subscribe(R,O){return this.subscriptionManager.subscribe(R,O)}unsubscribe(R,O){this.subscriptionManager.unsubscribe(R,O)}set(R,O){let q=!super.has(R);if(super.set(R,O),q)Z.notify(this.subscriptionManager,String(R));return this}delete(R){let O=super.delete(R);if(O)Z.notify(this.subscriptionManager,String(R));return O}clear(){let R=Array.from(this.keys()).map((O)=>String(O));super.clear(),Z.notifyMany(this.subscriptionManager,R)}}import{use as b,useSyncExternalStore as h}from"react";function V(R){if(!(R instanceof X))throw Error("InputMapping must be an instance of InputMappingStore for granular rendering")}function w(R,O){let z=b(R);return V(z),h((B)=>z.subscribe(O,B),()=>z.get(O),()=>z.get(O))}import{use as f,useMemo as I}from"react";function C(R){let q=f(R);V(q);let z=q;return I(()=>({set:(B,J)=>{return z.set(B,J)},clear:()=>{z.clear()},delete:(B)=>{return z.delete(B)}}),[z])}function j(R){function O(z){return w(R,z)}function q(){return C(R)}return{useInputComponent:O,useInputMappingActions:q}}var P=j(G),VR=(R)=>{let O=R instanceof X?R:new X(Object.fromEntries(R.entries()));return{FormInstantInputsProvider:(B)=>S(G.Provider,{value:O},B.children),useInputMapping:()=>g(G)}},y=m(({formProps:R})=>{let O=P.useInputComponent(R.fieldType)||P.useInputComponent("fallback");if(!O)return null;return S(O,R)},(R,O)=>{return R.formProps.fieldType===O.formProps.fieldType&&R.formProps.name.history===O.formProps.name.history&&R.formProps.name.current===O.formProps.name.current});y.displayName="ElementMapping";import{useCallback as x,useState as u}from"react";function v(R){return R&&typeof R==="object"&&"name"in R&&"fieldType"in R}function _(R,O,q){if(!v(R)){let D={};for(let K in R){let Y=R[K];if(!Y)continue;D[K]=_(Y,O,q)}return D}let z=R.name.current,B=`${q}.${O}.${z}`,J={...R,name:{current:z,history:B}};if(R.schema&&Array.isArray(R.schema))J.schema=R.schema.map((D)=>_(D,O,q));return J}var TR=(R)=>{let{fieldConfig:O,name:q,...z}=R,B=q.history||q.current,J=z.schema.at(0),[D,K]=u([]),Y=x(()=>{let Q=O?.max??1/0;if(Number(D?.length)>=Q)return;K((H)=>{let W=H.length,T=_(J,W,B);return[...H,T]})},[D,O?.max,B,J]),L=x((Q)=>{let $=O?.min??0;if((D?.length||0)>$)K((W)=>{return W.filter((N,A)=>A!==Q).map((N,A)=>{return _(N,A,B)})})},[D,O?.min,B]);return{inputs:D,append:Y,remove:L,setInputs:K,fieldConfig:O,name:q,...z}};import{useCallback as F,useMemo as c,useReducer as k,useRef as d,use as l}from"react";function vR(R){return function(){let q=l(R);if(!q)throw Error("InputMappingContext not found");let z=d(q),[B,J]=k((L)=>L+1,0),D=F((L,Q)=>{let $=z.current.set(L,Q);return J(),$},[]),K=F(()=>{z.current.clear(),J()},[]),Y=F((L)=>{let Q=z.current.delete(L);return J(),Q},[]);return c(()=>({...z.current,set:D,clear:K,delete:Y}),[B])}}export{TR as useInputArray,vR as createInputMappingHook,j as createInputMappingGranularHook,VR as createFormInstantContainer,X as InputMappingStore,G as InputMappingContext,U as InputMapping,y as ElementMapping};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=CC948A0F1B63234064756E2164756E21
|
package/dist/index.js.map
CHANGED
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"import { useCallback, useState } from 'react';\nimport type { FieldMetadata } from '../../../InputMapping';\nimport type { UseInputArrayProps } from '../types';\nimport { cloneFieldWithIndex } from '../domain/services/cloneFieldWithIndex';\n\nexport const useInputArray = (data: FieldMetadata) => {\n const { fieldConfig, name, ...prop } = data as UseInputArrayProps;\n const arrayName = name.history || name.current;\n const template = prop.schema.at(0)!;\n\n // Ensure inputs is always an array type\n const [inputs, setInputs] = useState<Record<string, FieldMetadata>[]>([]);\n\n const append = useCallback(() => {\n const max = fieldConfig?.max ?? Infinity;\n // Inputs can be undefined if schema was undefined and initialInputs is [] casted to SchemaArray (which might be strict)\n // But initialInputs is initialized to [] used as fallback.\n const currentLength = Number(inputs?.length);\n if (currentLength >= max) return;\n\n setInputs((pre) => {\n // Clone the template and update names with the new index\n const newIndex = pre.length;\n const newItem = cloneFieldWithIndex(template, newIndex, arrayName) as Record<\n string,\n FieldMetadata\n >;\n\n return [...pre, newItem];\n });\n }, [inputs, fieldConfig?.max, arrayName, template]); // Original deps: [inputs, fieldConfig?.max, arrayName]. Template is closed over. I should probably add it or leave it. Refactoring usually implies fixing linter errors. I'll Include template if linter complains, otherwise keep original. Original deps: [inputs, fieldConfig?.max, arrayName].\n\n const remove = useCallback(\n (index: number) => {\n const min = fieldConfig?.min ?? 0;\n const currentLength = inputs?.length || 0;\n if (currentLength > min) {\n setInputs((pre) => {\n // Ensure pre is always an array\n const filtered = pre.filter((_, i) => i !== index);\n // Update indices for remaining items\n return filtered.map((item, newIndex) => {\n return cloneFieldWithIndex(item, newIndex, arrayName) as Record<\n string,\n FieldMetadata\n >;\n });\n });\n }\n },\n [inputs, fieldConfig?.min, arrayName],\n );\n\n return { inputs, append, remove, setInputs, fieldConfig, name, ...prop };\n};\n",
|
|
16
16
|
"import type { ParsedField } from '../../../../InputMapping';\n\n/**\n * Type guard to check if a value is a ParsedField\n * @param item - The item to check\n * @returns True if item is a ParsedField\n */\nexport function isParsedField(item: any): item is ParsedField<any, string> {\n return item && typeof item === 'object' && 'name' in item && 'fieldType' in item;\n}\n",
|
|
17
17
|
"import type { FieldMetadata, ParsedField } from '../../../../InputMapping';\nimport { isParsedField } from '../guards/isParsedField';\n\n/**\n * Helper function to deep clone a ParsedField and update names with new index\n * @param field - The field to clone\n * @param newIndex - The new index to apply\n * @param arrayName - The name of the array\n * @returns The cloned field or record of fields with updated index\n */\nexport function cloneFieldWithIndex(\n field:\n | ParsedField<any, string>\n | Record<string, ParsedField<any, string>>\n | FieldMetadata\n | Record<string, FieldMetadata>,\n newIndex: number,\n arrayName: string,\n): ParsedField<any, string> | Record<string, ParsedField<any, string>> {\n // Handle Record<string, ParsedField>\n if (!isParsedField(field)) {\n const newStructure: Record<string, ParsedField<any, string>> = {};\n for (const key in field) {\n const val = (field as any)[key];\n if (!val) continue;\n // Recursive call - result is ParsedField because val is ParsedField\n // (assuming structure is Record<string, ParsedField>)\n newStructure[key] = cloneFieldWithIndex(val, newIndex, arrayName) as ParsedField<\n any,\n string\n >;\n }\n return newStructure;\n }\n\n // Handle ParsedField\n // Extract the field name from the current history (e.g., \"items.0.name\" -> \"name\")\n const fieldName = field.name.current;\n const newHistory = `${arrayName}.${newIndex}.${fieldName}`;\n\n const cloned = {\n ...field,\n name: {\n current: fieldName,\n history: newHistory,\n },\n } as ParsedField<any, string>;\n\n if (field.schema && Array.isArray(field.schema)) {\n cloned.schema = field.schema.map((subField) =>\n cloneFieldWithIndex(subField as ParsedField<any, string>, newIndex, arrayName),\n );\n }\n\n return cloned;\n}\n",
|
|
18
|
-
"import { useReducer, useRef, use } from 'react';\nimport { InputMapping } from '../InputMapping/class';\n\nexport function createInputMappingHook<Ob extends Record<string, any>>(\n\tInputMappingContext: React.Context<InputMapping<Ob> | null>,\n) {\n\treturn function useInputMapping() {\n\t\tconst initialState = use(InputMappingContext);\n\t\tif (!initialState) throw new Error('InputMappingContext not found');\n\t\tconst mapRef = useRef(initialState);\n\t\tconst [, reRender] = useReducer((x) => x + 1, 0);\n\n\t\
|
|
18
|
+
"import { useCallback, useMemo, useReducer, useRef, use } from 'react';\nimport { InputMapping } from '../InputMapping/class';\n\nexport function createInputMappingHook<Ob extends Record<string, any>>(\n\tInputMappingContext: React.Context<InputMapping<Ob> | null>,\n) {\n\treturn function useInputMapping() {\n\t\tconst initialState = use(InputMappingContext);\n\t\tif (!initialState) throw new Error('InputMappingContext not found');\n\t\tconst mapRef = useRef(initialState);\n\t\tconst [version, reRender] = useReducer((x) => x + 1, 0);\n\n\t\tconst set = useCallback((key: keyof Ob | string, value: React.FC<any>) => {\n\t\t\tconst result = mapRef.current.set(key, value);\n\t\t\treRender();\n\t\t\treturn result;\n\t\t}, []);\n\n\t\tconst clear = useCallback(() => {\n\t\t\tmapRef.current.clear();\n\t\t\treRender();\n\t\t}, []);\n\n\t\tconst del = useCallback((key: keyof Ob | string) => {\n\t\t\tconst result = mapRef.current.delete(key);\n\t\t\treRender();\n\t\t\treturn result;\n\t\t}, []);\n\n\t\treturn useMemo(\n\t\t\t() => ({\n\t\t\t\t...mapRef.current,\n\t\t\t\tset,\n\t\t\t\tclear,\n\t\t\t\tdelete: del,\n\t\t\t}),\n\t\t\t[version],\n\t\t);\n\t};\n}\n"
|
|
19
19
|
],
|
|
20
|
-
"mappings": "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
|
|
21
|
-
"debugId": "
|
|
20
|
+
"mappings": "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",
|
|
21
|
+
"debugId": "CC948A0F1B63234064756E2164756E21",
|
|
22
22
|
"names": []
|
|
23
23
|
}
|
package/package.json
CHANGED
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"type": "module",
|
|
3
|
-
"private": false,
|
|
4
|
-
"license": "MIT",
|
|
5
|
-
"version": "2.0.2",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"module": "dist/index.js",
|
|
8
|
-
"types": "dist/src/index.d.ts",
|
|
9
|
-
"files": [
|
|
10
|
-
"dist"
|
|
11
|
-
],
|
|
12
|
-
"exports": {
|
|
13
|
-
".": {
|
|
14
|
-
"types": "./dist/src/index.d.ts",
|
|
15
|
-
"default": "./dist/index.js"
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
2
|
"name": "@form-instant/react-input-mapping",
|
|
3
|
+
"version": "2.2.0",
|
|
19
4
|
"author": {
|
|
20
5
|
"name": "leomerida15",
|
|
21
6
|
"email": "dimasmerida15@gmail.com",
|
|
@@ -25,36 +10,49 @@
|
|
|
25
10
|
"type": "git",
|
|
26
11
|
"url": "git+https://github.com/leomerida15/form-instant-react-mapping.git"
|
|
27
12
|
},
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"build:types": "bun x tsc --project tsconfig.build.json",
|
|
33
|
-
"lint": "bun -b eslint --config ../../eslint.config.mjs src --ext .ts,.tsx",
|
|
34
|
-
"lint:fix": "bun -b eslint --config ../../eslint.config.mjs src --ext .ts,.tsx --fix",
|
|
35
|
-
"start": "bun run bunpack.watch.ts",
|
|
36
|
-
"watch": "bun run bunpack.watch.ts --build",
|
|
37
|
-
"docs": "bun run docs-serve.ts",
|
|
38
|
-
"docs:dev": "bun run docs.watch.ts"
|
|
39
|
-
},
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"i": "^0.3.7"
|
|
13
|
+
"main": "dist/index.js",
|
|
14
|
+
"module": "dist/index.js",
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/react": "^19.2.10"
|
|
42
17
|
},
|
|
43
18
|
"peerDependencies": {
|
|
44
19
|
"react": "^19.2.4"
|
|
45
20
|
},
|
|
46
|
-
"
|
|
47
|
-
"
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/src/index.d.ts",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
}
|
|
48
26
|
},
|
|
49
27
|
"engines": {
|
|
50
28
|
"node": ">=22",
|
|
51
29
|
"bun": ">=1.3.0"
|
|
52
30
|
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
53
34
|
"homepage": "https://leomerida15.github.io/form-instant-react-mapping",
|
|
54
35
|
"keywords": [
|
|
55
36
|
"@form-instant",
|
|
56
37
|
"react",
|
|
57
38
|
"mapping",
|
|
58
39
|
"form"
|
|
59
|
-
]
|
|
40
|
+
],
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"private": false,
|
|
43
|
+
"scripts": {
|
|
44
|
+
"dev": "bun run bunpack.watch.ts",
|
|
45
|
+
"build": "bun run bunpack.build.ts",
|
|
46
|
+
"build:types": "bun x tsc --project tsconfig.build.json",
|
|
47
|
+
"type:check": "bun x tsc --noEmit",
|
|
48
|
+
"lint": "eslint src",
|
|
49
|
+
"lint:fix": "eslint src --fix",
|
|
50
|
+
"start": "bun run bunpack.watch.ts",
|
|
51
|
+
"watch": "bun run bunpack.watch.ts --build",
|
|
52
|
+
"test": "jest --passWithNoTests",
|
|
53
|
+
"docs": "bun run docs-serve.ts",
|
|
54
|
+
"docs:dev": "bun run docs.watch.ts"
|
|
55
|
+
},
|
|
56
|
+
"type": "module",
|
|
57
|
+
"types": "dist/src/index.d.ts"
|
|
60
58
|
}
|