@juantroconisf/lib 7.0.0 → 9.0.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 +227 -46
- package/dist/index.d.mts +84 -80
- package/dist/index.d.ts +84 -80
- package/dist/index.js +455 -305
- package/dist/index.mjs +443 -300
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -1,62 +1,112 @@
|
|
|
1
1
|
# @juantroconisf/lib
|
|
2
2
|
|
|
3
|
-
A powerful, type-safe form management and validation library optimized for **HeroUI** (formerly NextUI)
|
|
3
|
+
A powerful, type-safe form management and validation library optimized for **HeroUI** (formerly NextUI) and **React**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Designed for complex applications, it provides **O(1) value updates**, stable **ID-based array management**, and deep nesting support, all while fully integrating with **Yup** for schema validation.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Features](#features)
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
- [Quick Start](#quick-start)
|
|
14
|
+
- [Usage Guide](#usage-guide)
|
|
15
|
+
- [Scalar Fields](#scalar-fields)
|
|
16
|
+
- [Nested Objects](#nested-objects)
|
|
17
|
+
- [Managing Arrays](#managing-arrays)
|
|
18
|
+
- [Validation](#validation)
|
|
19
|
+
- [Submitting Forms](#submitting-forms)
|
|
20
|
+
- [API Reference](#api-reference)
|
|
21
|
+
- [useForm](#useform)
|
|
22
|
+
- [The `on` Object](#the-on-object)
|
|
23
|
+
- [Array Helpers](#array-helpers)
|
|
24
|
+
- [Metadata & Reset](#metadata--reset)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Features
|
|
6
29
|
|
|
7
30
|
- 🎯 **Polymorphic `on` API**: Single consistent interface for `input`, `select`, and `autocomplete`.
|
|
8
31
|
- 🧩 **Deep Nesting**: Effortlessly manage complex objects with dot-notation support (e.g., `address.city`).
|
|
9
|
-
- 🔢 **ID-Based Arrays**: Stable state management for dynamic lists. Items are tracked by unique identifiers, preventing state loss during reordering or deletions.
|
|
32
|
+
- 🔢 **ID-Based Arrays**: Stable state management for dynamic lists. Items are tracked by unique identifiers (default: `id`), preventing state loss during reordering or deletions.
|
|
10
33
|
- ⚡ **O(1) Performance**: Internal mapping for array items ensures lightning-fast updates regardless of list size.
|
|
11
34
|
- 🛡️ **Total Type Safety**: Best-in-class Intellisense for paths, types, and array identifiers.
|
|
12
|
-
- 🎨 **HeroUI Optimized**: Returns props ready to be spread directly onto HeroUI components (`isInvalid`, `errorMessage`, `
|
|
35
|
+
- 🎨 **HeroUI Optimized**: Returns props ready to be spread directly onto HeroUI components (`isInvalid`, `errorMessage`, `onFieldBlur`, etc.).
|
|
36
|
+
- ✅ **Yup Integration**: Built-in support for Yup schemas for validation.
|
|
13
37
|
|
|
14
38
|
---
|
|
15
39
|
|
|
16
40
|
## Installation
|
|
17
41
|
|
|
18
42
|
```bash
|
|
19
|
-
pnpm add @juantroconisf/lib
|
|
43
|
+
pnpm add @juantroconisf/lib yup
|
|
20
44
|
```
|
|
21
45
|
|
|
22
46
|
---
|
|
23
47
|
|
|
24
48
|
## Quick Start
|
|
25
49
|
|
|
50
|
+
Here is a complete example demonstrating scalar fields, object arrays, and validation.
|
|
51
|
+
|
|
26
52
|
```tsx
|
|
27
53
|
import { useForm } from "@juantroconisf/lib";
|
|
54
|
+
import { string, number, array, object } from "yup";
|
|
55
|
+
import { Input, Button } from "@heroui/react";
|
|
28
56
|
|
|
29
57
|
const MyForm = () => {
|
|
30
|
-
const { on, state, helpers } = useForm(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
|
|
58
|
+
const { on, state, helpers, onSubmit } = useForm({
|
|
59
|
+
name: string().required("Name is required").default(""),
|
|
60
|
+
items: array()
|
|
61
|
+
.of(
|
|
62
|
+
object().shape({
|
|
63
|
+
id: number().required(),
|
|
64
|
+
label: string().required("Label is required"),
|
|
65
|
+
}),
|
|
66
|
+
)
|
|
67
|
+
.default([{ id: 1, label: "Initial Item" }]),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const handleSubmit = (data) => {
|
|
71
|
+
console.log("Submitted:", data);
|
|
72
|
+
};
|
|
42
73
|
|
|
43
74
|
return (
|
|
44
|
-
<form>
|
|
45
|
-
{/* Scalar
|
|
75
|
+
<form onSubmit={onSubmit(handleSubmit)} className='flex flex-col gap-4'>
|
|
76
|
+
{/* 1. Scalar Registration */}
|
|
46
77
|
<Input {...on.input("name")} label='Name' />
|
|
47
78
|
|
|
48
|
-
{/* Array Registration
|
|
49
|
-
{state.
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
|
|
79
|
+
{/* 2. Array Registration */}
|
|
80
|
+
{state.items.map((item) => (
|
|
81
|
+
<div key={item.id} className='flex gap-2 items-center'>
|
|
82
|
+
|
|
83
|
+
{/*
|
|
84
|
+
Bind by Path + ID
|
|
85
|
+
This ensures that even if the array order changes,
|
|
86
|
+
the input remains bound to the correct item.
|
|
87
|
+
*/}
|
|
88
|
+
<Input
|
|
89
|
+
{...on.input("items.label", item.id)}
|
|
90
|
+
label='Item Label'
|
|
91
|
+
/>
|
|
92
|
+
|
|
93
|
+
<Button
|
|
94
|
+
color="danger"
|
|
95
|
+
onClick={() => helpers.removeById("items", item.id)}
|
|
96
|
+
>
|
|
97
|
+
Remove
|
|
98
|
+
</Button>
|
|
99
|
+
</div>
|
|
55
100
|
))}
|
|
56
101
|
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
102
|
+
<div className="flex gap-2">
|
|
103
|
+
<Button
|
|
104
|
+
onClick={() => helpers.addItem("items", { id: Date.now(), label: "" })}
|
|
105
|
+
>
|
|
106
|
+
Add Item
|
|
107
|
+
</Button>
|
|
108
|
+
<Button type='submit' color="primary">Submit</Button>
|
|
109
|
+
</div>
|
|
60
110
|
</form>
|
|
61
111
|
);
|
|
62
112
|
};
|
|
@@ -64,35 +114,166 @@ const MyForm = () => {
|
|
|
64
114
|
|
|
65
115
|
---
|
|
66
116
|
|
|
117
|
+
## Usage Guide
|
|
118
|
+
|
|
119
|
+
### Scalar Fields
|
|
120
|
+
|
|
121
|
+
Scalar fields are the bread and butter of any form. Use `on.input`, `on.select`, or `on.autocomplete` to bind them.
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
<Input {...on.input("fullName")} label="Full Name" />
|
|
125
|
+
<Input {...on.input("email")} type="email" label="Email" />
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Nested Objects
|
|
129
|
+
|
|
130
|
+
No special configuration needed. Just use standard dot notation.
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
const { on } = useForm({
|
|
134
|
+
settings: object({
|
|
135
|
+
theme: object({
|
|
136
|
+
mode: string().default("dark"),
|
|
137
|
+
}),
|
|
138
|
+
}),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
<Input {...on.input("settings.theme.mode")} />
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Managing Arrays
|
|
145
|
+
|
|
146
|
+
The library shines with arrays. It tracks items by ID, so you can reorder or delete items without losing focus or state.
|
|
147
|
+
|
|
148
|
+
**1. Binding Inputs:**
|
|
149
|
+
|
|
150
|
+
Always bind using the composite syntax: `path.field` + `itemId`.
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
// ✅ Correct: Binds to the item with ID 123
|
|
154
|
+
on.input("users.name", 123)
|
|
155
|
+
|
|
156
|
+
// ❌ Avoid (unless primitive array): Binds to index 0
|
|
157
|
+
on.input("users", 0)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**2. Manipulating the Array:**
|
|
161
|
+
|
|
162
|
+
Use the `helpers` object.
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
// Add
|
|
166
|
+
helpers.addItem("users", { id: "new-id", name: "" });
|
|
167
|
+
|
|
168
|
+
// Remove by ID (Safe)
|
|
169
|
+
helpers.removeById("users", "user-id-123");
|
|
170
|
+
|
|
171
|
+
// Move (Reorder)
|
|
172
|
+
helpers.moveById("users", "from-id", "to-id");
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Validation
|
|
176
|
+
|
|
177
|
+
Validation is "Schema-First" via Yup.
|
|
178
|
+
|
|
179
|
+
1. **Define Rules**: Pass a Yup schema to `useForm`.
|
|
180
|
+
2. **Auto-Validation**:
|
|
181
|
+
- Fields validate on `blur`.
|
|
182
|
+
- If a field is `touched` and `invalid`, it re-validates on every keystroke (`onChange`).
|
|
183
|
+
3. **Submit Validation**: Use `onSubmit` wrapper to validate all fields before executing your handler.
|
|
184
|
+
|
|
185
|
+
### Submitting Forms
|
|
186
|
+
|
|
187
|
+
The `onSubmit` wrapper handles `preventDefault`, runs full validation, and only executes your callback if valid.
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
const { onSubmit } = useForm(...);
|
|
191
|
+
|
|
192
|
+
const handleSubmit = (data) => {
|
|
193
|
+
// 1. Validation has already passed!
|
|
194
|
+
// 2. Proceed with submission
|
|
195
|
+
api.post("/users", data);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
return <form onSubmit={onSubmit(handleSubmit)}>...</form>;
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
67
203
|
## API Reference
|
|
68
204
|
|
|
69
|
-
### `useForm
|
|
205
|
+
### `useForm`
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const {
|
|
209
|
+
state,
|
|
210
|
+
on,
|
|
211
|
+
helpers,
|
|
212
|
+
metadata,
|
|
213
|
+
reset,
|
|
214
|
+
onSubmit,
|
|
215
|
+
reset,
|
|
216
|
+
onSubmit,
|
|
217
|
+
isDirty,
|
|
218
|
+
onFieldChange,
|
|
219
|
+
onFieldBlur
|
|
220
|
+
} = useForm(schema, options?);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### Arguments
|
|
224
|
+
|
|
225
|
+
| Argument | Type | Description |
|
|
226
|
+
| :--- | :--- | :--- |
|
|
227
|
+
| **`schema`** | `Yup.Schema` | A Yup schema object defining structure, default values, and validation rules. |
|
|
228
|
+
| **`options`** | `FormOptions` | Configuration object. |
|
|
70
229
|
|
|
71
230
|
#### Options
|
|
72
231
|
|
|
73
|
-
- **`
|
|
74
|
-
- **`messages`**: Custom error messages map. Keys match rules.
|
|
75
|
-
- **`arrayIdentifiers`**: Mapping of array keys (supporting dot-notation for nested arrays) to their unique ID property (defaults to `"id"`).
|
|
232
|
+
- **`arrayIdentifiers`**: Mapping of array paths to their unique ID property (default: `"id"`).
|
|
76
233
|
|
|
77
234
|
### The `on` Object
|
|
78
235
|
|
|
79
|
-
|
|
236
|
+
The `on` object generates props for your components: `value`, `onValueChange`, `isInvalid`, `errorMessage`, `onBlur`.
|
|
80
237
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
238
|
+
| Method | Signature | Description |
|
|
239
|
+
| :--- | :--- | :--- |
|
|
240
|
+
| **`on.input`** | `(path, [id])` | Generic input handler. Returns standard input props. |
|
|
241
|
+
| **`on.select`** | `(path, [id])` | Returns props compatible with `Select` (handles `selectedKeys`). |
|
|
242
|
+
| **`on.autocomplete`** | `(path, [id])` | Returns props compatible with `Autocomplete` (handles `selectedKey`). |
|
|
84
243
|
|
|
85
|
-
### Helpers
|
|
244
|
+
### Array Helpers
|
|
86
245
|
|
|
87
|
-
Utilities for
|
|
246
|
+
Utilities for manipulating array state.
|
|
88
247
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
248
|
+
| Method | Description |
|
|
249
|
+
| :--- | :--- |
|
|
250
|
+
| **`addItem(key, item, index?)`** | Adds an item to the array. |
|
|
251
|
+
| **`removeItem(key, index)`** | Removes an item at a specific index. |
|
|
252
|
+
| **`removeById(key, id)`** | **Recommended**. Removes an item by its unique ID. |
|
|
253
|
+
| **`updateItem(key, index, val)`** | Replaces an item at a specific index. |
|
|
254
|
+
| **`moveItem(key, from, to)`** | Moves an item from one index to another. |
|
|
255
|
+
| **`moveById(key, fromId, toId)`** | Moves an item by ID. |
|
|
256
|
+
| **`getItem(key, id)`** | Retrieval helper. |
|
|
257
|
+
|
|
258
|
+
### Metadata & Reset
|
|
259
|
+
|
|
260
|
+
| Property | Type | Description |
|
|
261
|
+
| :--- | :--- | :--- |
|
|
262
|
+
| **`metadata`** | `Map<string, FieldMetadata>` | Contains validation state (`isInvalid`, `errorMessage`) and `isTouched`. |
|
|
263
|
+
| **`reset`** | `(options?) => void` | Resets form state and metadata to initial values. |
|
|
264
|
+
|
|
265
|
+
**Reset Options:**
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
// Reset everything
|
|
269
|
+
reset();
|
|
270
|
+
|
|
271
|
+
// Keep specific fields
|
|
272
|
+
reset({ keys: ["organizationId"] });
|
|
273
|
+
|
|
274
|
+
// Only clear touched status (keep values)
|
|
275
|
+
reset({ onlyTouched: true });
|
|
276
|
+
```
|
|
96
277
|
|
|
97
278
|
---
|
|
98
279
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { SelectProps, AutocompleteProps } from '@heroui/react';
|
|
1
|
+
import { SelectProps, AutocompleteProps, FormProps } from '@heroui/react';
|
|
2
2
|
import { SingleSelection } from '@react-types/shared';
|
|
3
|
+
import { Schema, InferType } from 'yup';
|
|
3
4
|
|
|
4
5
|
type StateType = Record<string, any>;
|
|
5
6
|
interface NestedChangeProps<O extends StateType> {
|
|
@@ -13,78 +14,77 @@ declare class NextUIError {
|
|
|
13
14
|
errorMessage: string;
|
|
14
15
|
constructor(bool?: boolean, msg?: string);
|
|
15
16
|
}
|
|
16
|
-
type ValidatorFunction<T, U> = (val: T, ...args: U[]) => boolean;
|
|
17
|
-
interface Validator<T, U = any> {
|
|
18
|
-
validate: ValidatorFunction<T, U>;
|
|
19
|
-
msg: string;
|
|
20
|
-
}
|
|
21
|
-
interface ValidatorTypes {
|
|
22
|
-
required: Validator<string | number, boolean>;
|
|
23
|
-
min: Validator<number | string, number | string>;
|
|
24
|
-
max: Validator<number | string, number | string>;
|
|
25
|
-
minLength: Validator<string, number>;
|
|
26
|
-
maxLength: Validator<string, number>;
|
|
27
|
-
pattern: Validator<string, RegExp>;
|
|
28
|
-
equal: Validator<string, string>;
|
|
29
|
-
numberIsEqual: Validator<number, number>;
|
|
30
|
-
email: Validator<string, boolean>;
|
|
31
|
-
password: Validator<string, boolean>;
|
|
32
|
-
custom: Validator<any, boolean>;
|
|
33
|
-
}
|
|
34
|
-
type ValidatorParams = {
|
|
35
|
-
[K in keyof ValidatorTypes]?: ValidatorTypes[K] extends Validator<any, infer U> ? U : never;
|
|
36
|
-
};
|
|
37
|
-
type ValidatorErrorMessage = {
|
|
38
|
-
[K in keyof ValidatorTypes]?: string;
|
|
39
|
-
};
|
|
40
17
|
|
|
41
18
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* @template K The key of the array in the state.
|
|
19
|
+
* Valid types for the initial state configuration.
|
|
20
|
+
* Can be a raw value or a Yup schema.
|
|
45
21
|
*/
|
|
46
|
-
type
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
22
|
+
type ConfigType = any | Schema<any>;
|
|
23
|
+
/**
|
|
24
|
+
* Helper to infer the state type from the configuration object.
|
|
25
|
+
* If a value is a Yup schema, infer its return type.
|
|
26
|
+
* Otherwise, use the value's type directly.
|
|
27
|
+
*/
|
|
28
|
+
type InferState<S extends Record<string, ConfigType>> = {
|
|
29
|
+
[K in keyof S]: S[K] extends Schema<any, any, any, any> ? InferType<S[K]> : S[K];
|
|
30
|
+
};
|
|
50
31
|
/**
|
|
51
32
|
* Options for the useForm hook.
|
|
52
33
|
* @template O The state type.
|
|
53
34
|
*/
|
|
54
35
|
interface FormOptions<O extends StateType> {
|
|
55
|
-
/** Validation rules for top-level fields. */
|
|
56
|
-
rules?: Partial<Record<RulePath<O>, ValidatorRule>>;
|
|
57
|
-
/** Custom error messages for top-level fields. */
|
|
58
|
-
messages?: Partial<Record<RulePath<O>, ValidatorErrorMessage>>;
|
|
59
36
|
/**
|
|
60
37
|
* Custom property names used as unique identifiers for items in specific arrays.
|
|
61
38
|
* Default is "id".
|
|
62
39
|
*/
|
|
63
|
-
arrayIdentifiers?:
|
|
40
|
+
arrayIdentifiers?: {
|
|
41
|
+
[K in ArrayPaths<O>]?: NestedFieldValue<O, K> extends (infer E)[] ? E extends object ? ScalarKeys<E> : never : never;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Optional submit handler that is called when the form is submitted and validation passes.
|
|
45
|
+
*/
|
|
46
|
+
onFormSubmit?: FormSubmitHandler<O>;
|
|
47
|
+
/**
|
|
48
|
+
* Whether to automatically reset the form after a successful submission.
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
51
|
+
resetOnSubmit?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Keys to preserve when resetting the form.
|
|
54
|
+
*/
|
|
55
|
+
keepValues?: (keyof O)[];
|
|
56
|
+
}
|
|
57
|
+
type FieldMetadata = {
|
|
58
|
+
isTouched: boolean;
|
|
59
|
+
isInvalid: boolean;
|
|
60
|
+
errorMessage: string;
|
|
61
|
+
};
|
|
62
|
+
type MetadataType = Map<string, FieldMetadata>;
|
|
63
|
+
type FormSubmitHandler<O extends StateType> = (data: O, e: React.FormEvent) => void;
|
|
64
|
+
interface FormResetOptions<O> {
|
|
65
|
+
keepValues?: (keyof O)[];
|
|
64
66
|
}
|
|
65
|
-
type TouchedType = Map<string, boolean>;
|
|
66
|
-
type ErrorsType = Map<string, NextUIError>;
|
|
67
67
|
type ValueChangeFunc<O extends StateType, K extends keyof O> = (id: K, value: O[K]) => void;
|
|
68
68
|
type BlurFunc<O extends StateType> = (id: keyof O) => void;
|
|
69
|
-
interface ComponentInputProps
|
|
69
|
+
interface ComponentInputProps {
|
|
70
70
|
id: string;
|
|
71
71
|
onBlur: () => void;
|
|
72
72
|
isInvalid: boolean;
|
|
73
73
|
errorMessage: string;
|
|
74
74
|
}
|
|
75
75
|
/** Props returned by on.input() */
|
|
76
|
-
interface ItemInputProps<V = any> extends ComponentInputProps
|
|
76
|
+
interface ItemInputProps<V = any> extends ComponentInputProps {
|
|
77
77
|
onValueChange: (value: V) => void;
|
|
78
78
|
value: V;
|
|
79
79
|
}
|
|
80
80
|
/** Props returned by on.select() */
|
|
81
|
-
interface ItemSelectProps extends ComponentInputProps
|
|
82
|
-
onSelectionChange: SelectProps["onSelectionChange"]
|
|
81
|
+
interface ItemSelectProps extends ComponentInputProps {
|
|
82
|
+
onSelectionChange: NonNullable<SelectProps["onSelectionChange"]>;
|
|
83
83
|
selectedKeys: string[] | number[] | string | null;
|
|
84
84
|
}
|
|
85
85
|
/** Props returned by on.autocomplete() */
|
|
86
|
-
interface ItemAutocompleteProps extends ComponentInputProps
|
|
87
|
-
onSelectionChange: AutocompleteProps["onSelectionChange"]
|
|
86
|
+
interface ItemAutocompleteProps extends ComponentInputProps {
|
|
87
|
+
onSelectionChange: NonNullable<AutocompleteProps["onSelectionChange"]>;
|
|
88
88
|
selectedKey: SingleSelection["selectedKey"];
|
|
89
89
|
}
|
|
90
90
|
/**
|
|
@@ -95,27 +95,25 @@ interface OnMethods<O extends StateType> {
|
|
|
95
95
|
input<P extends AllPaths<O>>(id: P): ItemInputProps<NestedFieldValue<O, P & string>>;
|
|
96
96
|
/** Registers an array element — adapts to primitive arrays (by index) or object arrays (by ID + field). */
|
|
97
97
|
/** Registers an array element — adapts to primitive arrays (by index). */
|
|
98
|
-
input<K extends
|
|
98
|
+
input<K extends ArrayPaths<O>>(arrayKey: K, index: number): ItemInputProps<any>;
|
|
99
99
|
/** Registers an object array element's field using composite syntax "array.field". */
|
|
100
|
-
input<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId:
|
|
100
|
+
input<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemInputProps<any>;
|
|
101
101
|
/** Registers a scalar or nested object field. */
|
|
102
102
|
select<P extends AllPaths<O>>(id: P): ItemSelectProps;
|
|
103
|
+
/** Registers a complete array field for multi-selection. */
|
|
104
|
+
select<K extends ArrayPaths<O>>(arrayKey: K): ItemSelectProps;
|
|
103
105
|
/** Registers an array element — adapts to primitive arrays (by index) or object arrays (by ID + field). */
|
|
104
|
-
|
|
105
|
-
select<K extends ArrayKeys<O>>(arrayKey: K, index: number): ItemSelectProps;
|
|
106
|
+
select<K extends ArrayPaths<O>>(arrayKey: K, index: number): ItemSelectProps;
|
|
106
107
|
/** Registers an object array element's field using composite syntax "array.field". */
|
|
107
|
-
select<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId:
|
|
108
|
+
select<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemSelectProps;
|
|
108
109
|
/** Registers a scalar or nested object field. */
|
|
109
110
|
autocomplete<P extends AllPaths<O>>(id: P): ItemAutocompleteProps;
|
|
110
111
|
/** Registers an array element — adapts to primitive arrays (by index) or object arrays (by ID + field). */
|
|
111
112
|
/** Registers an array element — adapts to primitive arrays (by index). */
|
|
112
|
-
autocomplete<K extends
|
|
113
|
+
autocomplete<K extends ArrayPaths<O>>(arrayKey: K, index: number): ItemAutocompleteProps;
|
|
113
114
|
/** Registers an object array element's field using composite syntax "array.field". */
|
|
114
|
-
autocomplete<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId:
|
|
115
|
+
autocomplete<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemAutocompleteProps;
|
|
115
116
|
}
|
|
116
|
-
type ArrayKeys<O extends StateType> = {
|
|
117
|
-
[K in keyof O]: O[K] extends any[] ? K : never;
|
|
118
|
-
}[keyof O];
|
|
119
117
|
/**
|
|
120
118
|
* Recursive type to find all paths to arrays in the state.
|
|
121
119
|
*/
|
|
@@ -133,14 +131,10 @@ type ObjectArrayKeys<O extends StateType> = {
|
|
|
133
131
|
type ObjectArrayFieldPaths<O extends StateType> = {
|
|
134
132
|
[K in keyof O]: O[K] extends Record<string, any>[] ? `${K & string}.${FieldPaths<ArrayElement<O[K]>>}` : never;
|
|
135
133
|
}[keyof O];
|
|
136
|
-
/**
|
|
137
|
-
* Helper to extract the array key from a path string like "arrayKey.field".
|
|
138
|
-
*/
|
|
139
|
-
type GetArrayKeyFromPath<O extends StateType, P extends string> = P extends `${infer K}.${string}` ? K extends keyof O ? O[K] extends any[] ? K : never : never : never;
|
|
140
134
|
type ArrayElement<T> = T extends (infer E)[] ? E : never;
|
|
141
135
|
/** Resolves the type of the identifier field for an array element (defaults to "id"). */
|
|
142
136
|
type ItemIdType<O extends StateType, K extends keyof O> = "id" extends keyof ArrayElement<O[K]> ? ArrayElement<O[K]>["id"] : string | number;
|
|
143
|
-
type NestedFieldValue<T, F extends string> = F extends `${infer First}.${infer Rest}` ? First extends keyof T ? NestedFieldValue<T[First]
|
|
137
|
+
type NestedFieldValue<T, F extends string> = F extends `${infer First}.${infer Rest}` ? First extends keyof T ? NestedFieldValue<NonNullable<T[First]>, Rest> : NonNullable<T> extends (infer U)[] ? NestedFieldValue<U, F> : any : F extends keyof T ? NonNullable<T[F]> : NonNullable<T> extends (infer U)[] ? F extends keyof U ? NonNullable<U[F]> : any : any;
|
|
144
138
|
type FieldPaths<T> = T extends Record<string, any> ? {
|
|
145
139
|
[K in keyof T & string]: T[K] extends any[] ? K : T[K] extends Record<string, any> ? `${K}.${FieldPaths<T[K]>}` : K;
|
|
146
140
|
}[keyof T & string] : never;
|
|
@@ -157,48 +151,58 @@ type AllPaths<O extends StateType> = ScalarKeys<O> | NestedObjectPaths<O>;
|
|
|
157
151
|
*/
|
|
158
152
|
interface HelpersFunc<O extends StateType> {
|
|
159
153
|
/** Adds a new item to an array. */
|
|
160
|
-
addItem: <K extends
|
|
154
|
+
addItem: <K extends ArrayPaths<O>>(arrayKey: K, item: NestedFieldValue<O, K> extends (infer E)[] ? E : never, index?: number) => void;
|
|
161
155
|
/** Removes an item from an array by its index. */
|
|
162
|
-
removeItem: <K extends
|
|
156
|
+
removeItem: <K extends ArrayPaths<O>>(arrayKey: K, index: number) => void;
|
|
163
157
|
/** Removes an item from an array by its unique identifier. */
|
|
164
|
-
removeById: <K extends
|
|
158
|
+
removeById: <K extends ArrayPaths<O>>(arrayKey: K, itemId: string | number) => void;
|
|
165
159
|
/** Replaces an item in an array at the given index. */
|
|
166
|
-
updateItem: <K extends
|
|
160
|
+
updateItem: <K extends ArrayPaths<O>>(arrayKey: K, index: number, value: NestedFieldValue<O, K> extends (infer E)[] ? E : never) => void;
|
|
167
161
|
/** Moves an item within an array using indices. */
|
|
168
|
-
moveItem: <K extends
|
|
162
|
+
moveItem: <K extends ArrayPaths<O>>(arrayKey: K, from: number, to: number) => void;
|
|
169
163
|
/** Moves an item within an array using unique identifiers. */
|
|
170
|
-
moveById: <K extends
|
|
164
|
+
moveById: <K extends ArrayPaths<O>>(arrayKey: K, fromId: string | number, toId: string | number) => void;
|
|
171
165
|
/** Gets an item from an array by its unique identifier (O(1) via indexMap). */
|
|
172
|
-
getItem: <K extends
|
|
166
|
+
getItem: <K extends ArrayPaths<O>>(arrayKey: K, itemId: string | number) => (NestedFieldValue<O, K> extends (infer E)[] ? E : never) | undefined;
|
|
173
167
|
}
|
|
174
168
|
/**
|
|
175
169
|
* The response object from the useForm hook.
|
|
176
170
|
* @template O The state type.
|
|
177
171
|
*/
|
|
178
172
|
interface UseFormResponse<O extends StateType> {
|
|
179
|
-
|
|
173
|
+
onFieldBlur: BlurFunc<O>;
|
|
180
174
|
/** Updates an object array element's field by ID. */
|
|
181
|
-
|
|
175
|
+
onFieldChange<K extends ObjectArrayKeys<O>, F extends keyof ArrayElement<O[K]> & string>(arrayKey: K, itemId: ItemIdType<O, K>, field: F, value: ArrayElement<O[K]>[F]): void;
|
|
182
176
|
/** Updates a scalar or nested object field. */
|
|
183
|
-
|
|
177
|
+
onFieldChange<P extends AllPaths<O>>(id: P, value: NestedFieldValue<O, P & string>): void;
|
|
184
178
|
onSelectionChange: ValueChangeFunc<O, keyof O>;
|
|
185
179
|
state: O;
|
|
186
180
|
setState: React.Dispatch<React.SetStateAction<O>>;
|
|
187
|
-
|
|
188
|
-
errors: ErrorsType;
|
|
181
|
+
metadata: MetadataType;
|
|
189
182
|
/** Main object to bind form elements to the state. */
|
|
190
183
|
on: OnMethods<O>;
|
|
191
184
|
/** Array manipulation helpers. */
|
|
192
185
|
helpers: HelpersFunc<O>;
|
|
193
186
|
isDirty: boolean;
|
|
194
|
-
/**
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Resets the form state and metadata.
|
|
189
|
+
* @param options Configuration for the reset (e.g., keys to keep).
|
|
190
|
+
*/
|
|
191
|
+
onFormReset: (options?: FormResetOptions<O>) => void;
|
|
192
|
+
/**
|
|
193
|
+
* Wraps a form submission handler to automatically validate the form.
|
|
194
|
+
* If validation fails, identifying errors and touching fields.
|
|
195
|
+
* If validation succeeds, calls the provided handler with the current state.
|
|
196
|
+
*/
|
|
197
|
+
onFormSubmit: (fn: FormSubmitHandler<O>) => (e: React.FormEvent) => void;
|
|
198
|
+
/**
|
|
199
|
+
* A controlled form component that handles submission and validation.
|
|
200
|
+
*/
|
|
201
|
+
ControlledForm: React.ComponentType<Omit<FormProps, "onSubmit"> & {
|
|
202
|
+
onSubmit?: (data: O, e: React.FormEvent<HTMLFormElement>) => void;
|
|
203
|
+
}>;
|
|
200
204
|
}
|
|
201
205
|
|
|
202
|
-
declare function useForm<
|
|
206
|
+
declare function useForm<S extends Record<string, ConfigType>>(schema: S, { arrayIdentifiers, onFormSubmit: onFormSubmitProp, resetOnSubmit, keepValues: keepValuesProp, }?: FormOptions<InferState<S>>): UseFormResponse<InferState<S>>;
|
|
203
207
|
|
|
204
|
-
export { type BlurFunc, type
|
|
208
|
+
export { type BlurFunc, type ConfigType, type FieldMetadata, type FormOptions, type FormSubmitHandler, type HelpersFunc, type InferState, type MetadataType, type NestedChangeProps, NextUIError, type OnMethods, type StateType, type UseFormResponse, type ValueChangeFunc, useForm };
|