@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 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 both **scalar/nested** paths and **array item** paths (composite `"array.field"` + `itemId`).
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(path, [itemId])` | `Input`, `Textarea` | `value: string`, `onValueChange(string)` |
150
- | `on.numberInput(path, [itemId])` | `NumberInput` | `value: number`, `onValueChange(number)` |
151
- | `on.select(path, [itemId])` | `Select` | `selectedKeys`, `onSelectionChange` |
152
- | `on.autocomplete(path, [itemId])` | `Autocomplete` | `selectedKey`, `onSelectionChange` |
153
- | `on.checkbox(path, [itemId])` | `Checkbox` | `isSelected: boolean`, `onValueChange(boolean)` |
154
- | `on.switch(path, [itemId])` | `Switch` | `isSelected: boolean`, `onValueChange(boolean)` |
155
- | `on.radio(path, [itemId])` | `RadioGroup` | `value: string`, `onValueChange(string)` |
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juantroconisf/lib",
3
- "version": "11.2.0",
3
+ "version": "11.4.0",
4
4
  "description": "A form validation library for HeroUI.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",