@juantroconisf/lib 11.2.0 → 11.4.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 +18 -8
- package/dist/index.d.mts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +53 -0
- package/dist/index.mjs +53 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -131,6 +131,16 @@ Arrays of objects are tracked by item `id` (configurable via `arrayIdentifiers`)
|
|
|
131
131
|
</Button>;
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
### Deeply Nested Arrays (N-Level Depth)
|
|
135
|
+
|
|
136
|
+
If your schema contains arrays inside objects inside arrays (infinite depth), you can pass a variadic sequence of arguments alternating between structural paths and array IDs (or indexes).
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
// Schema: items[].form_response.input_values[].value
|
|
140
|
+
<Input {...on.input("items", itemId, "form_response.input_values", inputId, "value")} label="Deep Value" />
|
|
141
|
+
```
|
|
142
|
+
The library dynamically traverses your state, mapping IDs securely to their array indices natively, regardless of depth.
|
|
143
|
+
|
|
134
144
|
---
|
|
135
145
|
|
|
136
146
|
## The `on` API — Component Bindings
|
|
@@ -142,17 +152,17 @@ Each `on.*` method returns a set of props that you spread directly onto a HeroUI
|
|
|
142
152
|
- **Error display** — `isInvalid` and `errorMessage` from the schema
|
|
143
153
|
- **Required indicator** — `isRequired` derived from `.required()` in your schema
|
|
144
154
|
|
|
145
|
-
All methods support
|
|
155
|
+
All methods support **scalar/nested** paths, standard **array item** paths (composite `"array.field"` + `itemId`), and **variadic sequences** for N-level deep structures.
|
|
146
156
|
|
|
147
157
|
| Method | HeroUI Component | Key props returned |
|
|
148
158
|
| --------------------------------- | ------------------- | ----------------------------------------------- |
|
|
149
|
-
| `on.input(
|
|
150
|
-
| `on.numberInput(
|
|
151
|
-
| `on.select(
|
|
152
|
-
| `on.autocomplete(
|
|
153
|
-
| `on.checkbox(
|
|
154
|
-
| `on.switch(
|
|
155
|
-
| `on.radio(
|
|
159
|
+
| `on.input(...args)` | `Input`, `Textarea` | `value: string`, `onValueChange(string)` |
|
|
160
|
+
| `on.numberInput(...args)` | `NumberInput` | `value: number`, `onValueChange(number)` |
|
|
161
|
+
| `on.select(...args)` | `Select` | `selectedKeys`, `onSelectionChange` |
|
|
162
|
+
| `on.autocomplete(...args)` | `Autocomplete` | `selectedKey`, `onSelectionChange` |
|
|
163
|
+
| `on.checkbox(...args)` | `Checkbox` | `isSelected: boolean`, `onValueChange(boolean)` |
|
|
164
|
+
| `on.switch(...args)` | `Switch` | `isSelected: boolean`, `onValueChange(boolean)` |
|
|
165
|
+
| `on.radio(...args)` | `RadioGroup` | `value: string`, `onValueChange(string)` |
|
|
156
166
|
|
|
157
167
|
> **Why separate methods?** Each HeroUI component has a different prop contract (e.g. `isSelected` vs `value`, `onSelectionChange` vs `onValueChange`). Separate methods give accurate intellisense for each component.
|
|
158
168
|
|
package/dist/index.d.mts
CHANGED
|
@@ -86,6 +86,7 @@ type ValueChangeFunc<O extends StateType, K extends keyof O> = (id: K, value: O[
|
|
|
86
86
|
type BlurFunc<O extends StateType> = (id: keyof O) => void;
|
|
87
87
|
interface ComponentInputProps {
|
|
88
88
|
id: string;
|
|
89
|
+
name: string;
|
|
89
90
|
onBlur: () => void;
|
|
90
91
|
isInvalid: boolean;
|
|
91
92
|
errorMessage: string;
|
|
@@ -132,10 +133,14 @@ interface OnMethods<O extends StateType> {
|
|
|
132
133
|
input<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemInputProps<any>;
|
|
133
134
|
/** Registers a primitive array element by index. */
|
|
134
135
|
input<K extends ArrayPaths<O>>(arrayKey: K, index: number): ItemInputProps<any>;
|
|
136
|
+
/** Registers a deeply nested field using variadic path segments. */
|
|
137
|
+
input(...args: any[]): ItemInputProps<any>;
|
|
135
138
|
/** Registers a numeric field for NumberInput. */
|
|
136
139
|
numberInput<P extends AllPaths<O>>(id: P): ItemNumberInputProps;
|
|
137
140
|
/** Registers a numeric field within an object array element. */
|
|
138
141
|
numberInput<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemNumberInputProps;
|
|
142
|
+
/** Registers a deeply nested numeric field using variadic path segments. */
|
|
143
|
+
numberInput(...args: any[]): ItemNumberInputProps;
|
|
139
144
|
/** Registers a scalar or nested object field. */
|
|
140
145
|
select<P extends AllPaths<O>>(id: P): ItemSelectProps;
|
|
141
146
|
/** Registers a complete array field for multi-selection. */
|
|
@@ -144,24 +149,34 @@ interface OnMethods<O extends StateType> {
|
|
|
144
149
|
select<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemSelectProps;
|
|
145
150
|
/** Registers a primitive array element by index. */
|
|
146
151
|
select<K extends ArrayPaths<O>>(arrayKey: K, index: number): ItemSelectProps;
|
|
152
|
+
/** Registers a deeply nested field using variadic path segments. */
|
|
153
|
+
select(...args: any[]): ItemSelectProps;
|
|
147
154
|
/** Registers a scalar or nested object field. */
|
|
148
155
|
autocomplete<P extends AllPaths<O>>(id: P): ItemAutocompleteProps;
|
|
149
156
|
/** Registers an object array element's field using composite syntax "array.field". */
|
|
150
157
|
autocomplete<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemAutocompleteProps;
|
|
151
158
|
/** Registers a primitive array element by index. */
|
|
152
159
|
autocomplete<K extends ArrayPaths<O>>(arrayKey: K, index: number): ItemAutocompleteProps;
|
|
160
|
+
/** Registers a deeply nested field using variadic path segments. */
|
|
161
|
+
autocomplete(...args: any[]): ItemAutocompleteProps;
|
|
153
162
|
/** Registers a boolean field for Checkbox. */
|
|
154
163
|
checkbox<P extends AllPaths<O>>(id: P): ItemToggleProps;
|
|
155
164
|
/** Registers a boolean field within an object array element for Checkbox. */
|
|
156
165
|
checkbox<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemToggleProps;
|
|
166
|
+
/** Registers a deeply nested boolean field using variadic path segments. */
|
|
167
|
+
checkbox(...args: any[]): ItemToggleProps;
|
|
157
168
|
/** Registers a boolean field for Switch. */
|
|
158
169
|
switch<P extends AllPaths<O>>(id: P): ItemToggleProps;
|
|
159
170
|
/** Registers a boolean field within an object array element for Switch. */
|
|
160
171
|
switch<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemToggleProps;
|
|
172
|
+
/** Registers a deeply nested boolean field using variadic path segments. */
|
|
173
|
+
switch(...args: any[]): ItemToggleProps;
|
|
161
174
|
/** Registers a string field for RadioGroup. */
|
|
162
175
|
radio<P extends AllPaths<O>>(id: P): ItemRadioGroupProps;
|
|
163
176
|
/** Registers a string field within an object array element for RadioGroup. */
|
|
164
177
|
radio<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemRadioGroupProps;
|
|
178
|
+
/** Registers a deeply nested string field using variadic path segments. */
|
|
179
|
+
radio(...args: any[]): ItemRadioGroupProps;
|
|
165
180
|
}
|
|
166
181
|
/**
|
|
167
182
|
* Recursive type to find all paths to arrays in the state.
|
package/dist/index.d.ts
CHANGED
|
@@ -86,6 +86,7 @@ type ValueChangeFunc<O extends StateType, K extends keyof O> = (id: K, value: O[
|
|
|
86
86
|
type BlurFunc<O extends StateType> = (id: keyof O) => void;
|
|
87
87
|
interface ComponentInputProps {
|
|
88
88
|
id: string;
|
|
89
|
+
name: string;
|
|
89
90
|
onBlur: () => void;
|
|
90
91
|
isInvalid: boolean;
|
|
91
92
|
errorMessage: string;
|
|
@@ -132,10 +133,14 @@ interface OnMethods<O extends StateType> {
|
|
|
132
133
|
input<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemInputProps<any>;
|
|
133
134
|
/** Registers a primitive array element by index. */
|
|
134
135
|
input<K extends ArrayPaths<O>>(arrayKey: K, index: number): ItemInputProps<any>;
|
|
136
|
+
/** Registers a deeply nested field using variadic path segments. */
|
|
137
|
+
input(...args: any[]): ItemInputProps<any>;
|
|
135
138
|
/** Registers a numeric field for NumberInput. */
|
|
136
139
|
numberInput<P extends AllPaths<O>>(id: P): ItemNumberInputProps;
|
|
137
140
|
/** Registers a numeric field within an object array element. */
|
|
138
141
|
numberInput<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemNumberInputProps;
|
|
142
|
+
/** Registers a deeply nested numeric field using variadic path segments. */
|
|
143
|
+
numberInput(...args: any[]): ItemNumberInputProps;
|
|
139
144
|
/** Registers a scalar or nested object field. */
|
|
140
145
|
select<P extends AllPaths<O>>(id: P): ItemSelectProps;
|
|
141
146
|
/** Registers a complete array field for multi-selection. */
|
|
@@ -144,24 +149,34 @@ interface OnMethods<O extends StateType> {
|
|
|
144
149
|
select<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemSelectProps;
|
|
145
150
|
/** Registers a primitive array element by index. */
|
|
146
151
|
select<K extends ArrayPaths<O>>(arrayKey: K, index: number): ItemSelectProps;
|
|
152
|
+
/** Registers a deeply nested field using variadic path segments. */
|
|
153
|
+
select(...args: any[]): ItemSelectProps;
|
|
147
154
|
/** Registers a scalar or nested object field. */
|
|
148
155
|
autocomplete<P extends AllPaths<O>>(id: P): ItemAutocompleteProps;
|
|
149
156
|
/** Registers an object array element's field using composite syntax "array.field". */
|
|
150
157
|
autocomplete<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemAutocompleteProps;
|
|
151
158
|
/** Registers a primitive array element by index. */
|
|
152
159
|
autocomplete<K extends ArrayPaths<O>>(arrayKey: K, index: number): ItemAutocompleteProps;
|
|
160
|
+
/** Registers a deeply nested field using variadic path segments. */
|
|
161
|
+
autocomplete(...args: any[]): ItemAutocompleteProps;
|
|
153
162
|
/** Registers a boolean field for Checkbox. */
|
|
154
163
|
checkbox<P extends AllPaths<O>>(id: P): ItemToggleProps;
|
|
155
164
|
/** Registers a boolean field within an object array element for Checkbox. */
|
|
156
165
|
checkbox<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemToggleProps;
|
|
166
|
+
/** Registers a deeply nested boolean field using variadic path segments. */
|
|
167
|
+
checkbox(...args: any[]): ItemToggleProps;
|
|
157
168
|
/** Registers a boolean field for Switch. */
|
|
158
169
|
switch<P extends AllPaths<O>>(id: P): ItemToggleProps;
|
|
159
170
|
/** Registers a boolean field within an object array element for Switch. */
|
|
160
171
|
switch<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemToggleProps;
|
|
172
|
+
/** Registers a deeply nested boolean field using variadic path segments. */
|
|
173
|
+
switch(...args: any[]): ItemToggleProps;
|
|
161
174
|
/** Registers a string field for RadioGroup. */
|
|
162
175
|
radio<P extends AllPaths<O>>(id: P): ItemRadioGroupProps;
|
|
163
176
|
/** Registers a string field within an object array element for RadioGroup. */
|
|
164
177
|
radio<P extends ObjectArrayFieldPaths<O>>(compositePath: P, itemId: string | number): ItemRadioGroupProps;
|
|
178
|
+
/** Registers a deeply nested string field using variadic path segments. */
|
|
179
|
+
radio(...args: any[]): ItemRadioGroupProps;
|
|
165
180
|
}
|
|
166
181
|
/**
|
|
167
182
|
* Recursive type to find all paths to arrays in the state.
|
package/dist/index.js
CHANGED
|
@@ -98,6 +98,21 @@ function removeCompositeKeysByPrefix(map, prefix) {
|
|
|
98
98
|
}
|
|
99
99
|
return result;
|
|
100
100
|
}
|
|
101
|
+
function setNestedValue(state, dotPath, value) {
|
|
102
|
+
const keys = dotPath.split(".");
|
|
103
|
+
const copy = { ...state };
|
|
104
|
+
let current = copy;
|
|
105
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
106
|
+
if (Array.isArray(current[keys[i]])) {
|
|
107
|
+
current[keys[i]] = [...current[keys[i]]];
|
|
108
|
+
} else {
|
|
109
|
+
current[keys[i]] = { ...current[keys[i]] };
|
|
110
|
+
}
|
|
111
|
+
current = current[keys[i]];
|
|
112
|
+
}
|
|
113
|
+
current[keys[keys.length - 1]] = value;
|
|
114
|
+
return copy;
|
|
115
|
+
}
|
|
101
116
|
|
|
102
117
|
// src/hooks/useForm.tsx
|
|
103
118
|
var import_react3 = require("@heroui/react");
|
|
@@ -314,6 +329,41 @@ function resolveFieldData(args, state, getIndex, getNestedValue2) {
|
|
|
314
329
|
nestedField: field
|
|
315
330
|
};
|
|
316
331
|
}
|
|
332
|
+
if (argCount >= 5) {
|
|
333
|
+
let current = state;
|
|
334
|
+
const compositeKeyParts = [];
|
|
335
|
+
const fieldPathParts = [];
|
|
336
|
+
const realPathParts = [];
|
|
337
|
+
let currentPath = "";
|
|
338
|
+
const parts = args.flatMap(
|
|
339
|
+
(arg) => typeof arg === "string" ? arg.split(".") : [arg]
|
|
340
|
+
);
|
|
341
|
+
for (const part of parts) {
|
|
342
|
+
if (Array.isArray(current)) {
|
|
343
|
+
let indexNum = getIndex(currentPath, part);
|
|
344
|
+
if (indexNum === void 0 && typeof part === "number") {
|
|
345
|
+
indexNum = part;
|
|
346
|
+
}
|
|
347
|
+
if (indexNum === void 0) return null;
|
|
348
|
+
compositeKeyParts.push(String(part));
|
|
349
|
+
realPathParts.push(String(indexNum));
|
|
350
|
+
current = current[indexNum];
|
|
351
|
+
} else {
|
|
352
|
+
compositeKeyParts.push(String(part));
|
|
353
|
+
fieldPathParts.push(String(part));
|
|
354
|
+
realPathParts.push(String(part));
|
|
355
|
+
currentPath = realPathParts.join(".");
|
|
356
|
+
current = current?.[part];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
type: "deep" /* Deep */,
|
|
361
|
+
compositeKey: compositeKeyParts.join(".").replace(/\.\./g, "."),
|
|
362
|
+
fieldPath: fieldPathParts.filter(Boolean).join("."),
|
|
363
|
+
realPath: realPathParts.join("."),
|
|
364
|
+
value: current
|
|
365
|
+
};
|
|
366
|
+
}
|
|
317
367
|
return null;
|
|
318
368
|
}
|
|
319
369
|
|
|
@@ -598,6 +648,8 @@ function useForm(schema, {
|
|
|
598
648
|
parentArr[pIndex] = pItem;
|
|
599
649
|
nextState = { ...nextState, [parentKey]: parentArr };
|
|
600
650
|
}
|
|
651
|
+
} else if (type === "deep" /* Deep */) {
|
|
652
|
+
nextState = setNestedValue(nextState, resolution.realPath, finalValue);
|
|
601
653
|
}
|
|
602
654
|
setState(nextState);
|
|
603
655
|
validateField(
|
|
@@ -624,6 +676,7 @@ function useForm(schema, {
|
|
|
624
676
|
}
|
|
625
677
|
return {
|
|
626
678
|
id: compositeKey,
|
|
679
|
+
name: compositeKey,
|
|
627
680
|
isInvalid: Boolean(isTouched && meta?.isInvalid),
|
|
628
681
|
errorMessage: isTouched ? meta?.errorMessage || "" : "",
|
|
629
682
|
isRequired,
|
package/dist/index.mjs
CHANGED
|
@@ -72,6 +72,21 @@ function removeCompositeKeysByPrefix(map, prefix) {
|
|
|
72
72
|
}
|
|
73
73
|
return result;
|
|
74
74
|
}
|
|
75
|
+
function setNestedValue(state, dotPath, value) {
|
|
76
|
+
const keys = dotPath.split(".");
|
|
77
|
+
const copy = { ...state };
|
|
78
|
+
let current = copy;
|
|
79
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
80
|
+
if (Array.isArray(current[keys[i]])) {
|
|
81
|
+
current[keys[i]] = [...current[keys[i]]];
|
|
82
|
+
} else {
|
|
83
|
+
current[keys[i]] = { ...current[keys[i]] };
|
|
84
|
+
}
|
|
85
|
+
current = current[keys[i]];
|
|
86
|
+
}
|
|
87
|
+
current[keys[keys.length - 1]] = value;
|
|
88
|
+
return copy;
|
|
89
|
+
}
|
|
75
90
|
|
|
76
91
|
// src/hooks/useForm.tsx
|
|
77
92
|
import { Form } from "@heroui/react";
|
|
@@ -288,6 +303,41 @@ function resolveFieldData(args, state, getIndex, getNestedValue2) {
|
|
|
288
303
|
nestedField: field
|
|
289
304
|
};
|
|
290
305
|
}
|
|
306
|
+
if (argCount >= 5) {
|
|
307
|
+
let current = state;
|
|
308
|
+
const compositeKeyParts = [];
|
|
309
|
+
const fieldPathParts = [];
|
|
310
|
+
const realPathParts = [];
|
|
311
|
+
let currentPath = "";
|
|
312
|
+
const parts = args.flatMap(
|
|
313
|
+
(arg) => typeof arg === "string" ? arg.split(".") : [arg]
|
|
314
|
+
);
|
|
315
|
+
for (const part of parts) {
|
|
316
|
+
if (Array.isArray(current)) {
|
|
317
|
+
let indexNum = getIndex(currentPath, part);
|
|
318
|
+
if (indexNum === void 0 && typeof part === "number") {
|
|
319
|
+
indexNum = part;
|
|
320
|
+
}
|
|
321
|
+
if (indexNum === void 0) return null;
|
|
322
|
+
compositeKeyParts.push(String(part));
|
|
323
|
+
realPathParts.push(String(indexNum));
|
|
324
|
+
current = current[indexNum];
|
|
325
|
+
} else {
|
|
326
|
+
compositeKeyParts.push(String(part));
|
|
327
|
+
fieldPathParts.push(String(part));
|
|
328
|
+
realPathParts.push(String(part));
|
|
329
|
+
currentPath = realPathParts.join(".");
|
|
330
|
+
current = current?.[part];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
type: "deep" /* Deep */,
|
|
335
|
+
compositeKey: compositeKeyParts.join(".").replace(/\.\./g, "."),
|
|
336
|
+
fieldPath: fieldPathParts.filter(Boolean).join("."),
|
|
337
|
+
realPath: realPathParts.join("."),
|
|
338
|
+
value: current
|
|
339
|
+
};
|
|
340
|
+
}
|
|
291
341
|
return null;
|
|
292
342
|
}
|
|
293
343
|
|
|
@@ -572,6 +622,8 @@ function useForm(schema, {
|
|
|
572
622
|
parentArr[pIndex] = pItem;
|
|
573
623
|
nextState = { ...nextState, [parentKey]: parentArr };
|
|
574
624
|
}
|
|
625
|
+
} else if (type === "deep" /* Deep */) {
|
|
626
|
+
nextState = setNestedValue(nextState, resolution.realPath, finalValue);
|
|
575
627
|
}
|
|
576
628
|
setState(nextState);
|
|
577
629
|
validateField(
|
|
@@ -598,6 +650,7 @@ function useForm(schema, {
|
|
|
598
650
|
}
|
|
599
651
|
return {
|
|
600
652
|
id: compositeKey,
|
|
653
|
+
name: compositeKey,
|
|
601
654
|
isInvalid: Boolean(isTouched && meta?.isInvalid),
|
|
602
655
|
errorMessage: isTouched ? meta?.errorMessage || "" : "",
|
|
603
656
|
isRequired,
|