@uniform-ts/core 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{field-KKjnXn-d.d.mts → field-Cogi7eQc.d.mts} +16 -2
- package/dist/{field-KKjnXn-d.d.ts → field-Cogi7eQc.d.ts} +16 -2
- package/dist/index.d.mts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +333 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +333 -18
- package/dist/index.mjs.map +1 -1
- package/dist/locales/en.d.mts +1 -1
- package/dist/locales/en.d.ts +1 -1
- package/dist/locales/es.d.mts +1 -1
- package/dist/locales/es.d.ts +1 -1
- package/dist/locales/he.d.mts +1 -1
- package/dist/locales/he.d.ts +1 -1
- package/package.json +3 -2
|
@@ -27,14 +27,28 @@ type ArrayItem<T> = T extends (infer U)[] ? U : never;
|
|
|
27
27
|
type DeepKeys<T> = T extends object ? {
|
|
28
28
|
[K in keyof T & string]: T[K] extends unknown[] ? ArrayItem<T[K]> extends object ? K | `${K}.${DeepKeys<ArrayItem<T[K]>>}` : K : T[K] extends object ? K | `${K}.${DeepKeys<T[K]>}` : K;
|
|
29
29
|
}[keyof T & string] : never;
|
|
30
|
+
/**
|
|
31
|
+
* Extends `DeepKeys` with index-based array paths like `"items.0.name"`.
|
|
32
|
+
* Used by `setOnChange` to allow registering handlers for specific rows.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Given { tasks: { priority: string; notes: string }[] }
|
|
36
|
+
* // DeepKeysIndexed adds: "tasks.0.priority" | "tasks.0.notes" | "tasks.1.priority" | ...
|
|
37
|
+
* // (represented as template literal `tasks.${number}.priority`)
|
|
38
|
+
*/
|
|
39
|
+
type DeepKeysIndexed<T> = T extends object ? {
|
|
40
|
+
[K in keyof T & string]: T[K] extends unknown[] ? ArrayItem<T[K]> extends object ? K | `${K}.${DeepKeys<ArrayItem<T[K]>>}` | `${K}.${number}.${DeepKeys<ArrayItem<T[K]>>}` : K : T[K] extends object ? K | `${K}.${DeepKeysIndexed<T[K]>}` : K;
|
|
41
|
+
}[keyof T & string] : never;
|
|
30
42
|
/**
|
|
31
43
|
* Resolves the value type at a dot-notated path within a type `T`.
|
|
32
44
|
* Array fields use the unprefixed child path (matching `DeepKeys` convention).
|
|
45
|
+
* Also supports indexed paths like `"items.0.qty"` — the numeric segment is skipped.
|
|
33
46
|
*
|
|
34
47
|
* @example
|
|
35
48
|
* // DeepFieldValue<{ name: string; items: { qty: number }[] }, 'items.qty'> → number
|
|
49
|
+
* // DeepFieldValue<{ items: { qty: number }[] }, 'items.0.qty'> → number
|
|
36
50
|
*/
|
|
37
|
-
type DeepFieldValue<T, K extends string> = K extends keyof T ? T[K] : K extends `${infer Head}.${infer Tail}` ? Head extends keyof T ? T[Head] extends (infer Item)[] ? DeepFieldValue<NonNullable<Item>, Tail> : DeepFieldValue<NonNullable<T[Head]>, Tail> : unknown : unknown;
|
|
51
|
+
type DeepFieldValue<T, K extends string> = K extends keyof T ? T[K] : K extends `${infer Head}.${infer Tail}` ? Head extends keyof T ? T[Head] extends (infer Item)[] ? Tail extends `${number}.${infer Rest}` ? DeepFieldValue<NonNullable<Item>, Rest> : DeepFieldValue<NonNullable<Item>, Tail> : DeepFieldValue<NonNullable<T[Head]>, Tail> : unknown : unknown;
|
|
38
52
|
/**
|
|
39
53
|
* Resolves the values type that a `setCondition` predicate receives for a
|
|
40
54
|
* given field key `K` within form values type `TValues`.
|
|
@@ -745,4 +759,4 @@ type FieldOverride<TSchema extends z.$ZodObject = z.$ZodObject, TValue = unknown
|
|
|
745
759
|
[key: string]: unknown;
|
|
746
760
|
};
|
|
747
761
|
|
|
748
|
-
export type { AutoFormProps as A,
|
|
762
|
+
export type { AutoFormProps as A, SelectOption as B, ComponentRegistry as C, DeepKeysIndexed as D, SubmitButtonProps as E, FieldMetaBase as F, LayoutSlots as L, ObjectWrapperProps as O, PersistStorage as P, ResolvedLayoutSlots as R, SectionConfig as S, ValidationMessages as V, FieldConfig as a, AutoFormHandle as b, FieldProps as c, FieldWrapperProps as d, ArrayButtonProps as e, ArrayCollapseButtonProps as f, ArrayFieldLayoutProps as g, ArrayRowLayoutProps as h, ArrayWrapperProps as i, AutoFormConfig as j, FormMethods as k, DeepKeys as l, FieldDependencyResult as m, DeepFieldValue as n, ConditionValues as o, FormClassNames as p, CoercionMap as q, FormLabels as r, ArrayButtonSlots as s, FieldCondition as t, FieldMeta as u, FieldOverride as v, FieldType as w, FormWrapperProps as x, ResolvedArrayButtonSlots as y, SectionWrapperProps as z };
|
|
@@ -27,14 +27,28 @@ type ArrayItem<T> = T extends (infer U)[] ? U : never;
|
|
|
27
27
|
type DeepKeys<T> = T extends object ? {
|
|
28
28
|
[K in keyof T & string]: T[K] extends unknown[] ? ArrayItem<T[K]> extends object ? K | `${K}.${DeepKeys<ArrayItem<T[K]>>}` : K : T[K] extends object ? K | `${K}.${DeepKeys<T[K]>}` : K;
|
|
29
29
|
}[keyof T & string] : never;
|
|
30
|
+
/**
|
|
31
|
+
* Extends `DeepKeys` with index-based array paths like `"items.0.name"`.
|
|
32
|
+
* Used by `setOnChange` to allow registering handlers for specific rows.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Given { tasks: { priority: string; notes: string }[] }
|
|
36
|
+
* // DeepKeysIndexed adds: "tasks.0.priority" | "tasks.0.notes" | "tasks.1.priority" | ...
|
|
37
|
+
* // (represented as template literal `tasks.${number}.priority`)
|
|
38
|
+
*/
|
|
39
|
+
type DeepKeysIndexed<T> = T extends object ? {
|
|
40
|
+
[K in keyof T & string]: T[K] extends unknown[] ? ArrayItem<T[K]> extends object ? K | `${K}.${DeepKeys<ArrayItem<T[K]>>}` | `${K}.${number}.${DeepKeys<ArrayItem<T[K]>>}` : K : T[K] extends object ? K | `${K}.${DeepKeysIndexed<T[K]>}` : K;
|
|
41
|
+
}[keyof T & string] : never;
|
|
30
42
|
/**
|
|
31
43
|
* Resolves the value type at a dot-notated path within a type `T`.
|
|
32
44
|
* Array fields use the unprefixed child path (matching `DeepKeys` convention).
|
|
45
|
+
* Also supports indexed paths like `"items.0.qty"` — the numeric segment is skipped.
|
|
33
46
|
*
|
|
34
47
|
* @example
|
|
35
48
|
* // DeepFieldValue<{ name: string; items: { qty: number }[] }, 'items.qty'> → number
|
|
49
|
+
* // DeepFieldValue<{ items: { qty: number }[] }, 'items.0.qty'> → number
|
|
36
50
|
*/
|
|
37
|
-
type DeepFieldValue<T, K extends string> = K extends keyof T ? T[K] : K extends `${infer Head}.${infer Tail}` ? Head extends keyof T ? T[Head] extends (infer Item)[] ? DeepFieldValue<NonNullable<Item>, Tail> : DeepFieldValue<NonNullable<T[Head]>, Tail> : unknown : unknown;
|
|
51
|
+
type DeepFieldValue<T, K extends string> = K extends keyof T ? T[K] : K extends `${infer Head}.${infer Tail}` ? Head extends keyof T ? T[Head] extends (infer Item)[] ? Tail extends `${number}.${infer Rest}` ? DeepFieldValue<NonNullable<Item>, Rest> : DeepFieldValue<NonNullable<Item>, Tail> : DeepFieldValue<NonNullable<T[Head]>, Tail> : unknown : unknown;
|
|
38
52
|
/**
|
|
39
53
|
* Resolves the values type that a `setCondition` predicate receives for a
|
|
40
54
|
* given field key `K` within form values type `TValues`.
|
|
@@ -745,4 +759,4 @@ type FieldOverride<TSchema extends z.$ZodObject = z.$ZodObject, TValue = unknown
|
|
|
745
759
|
[key: string]: unknown;
|
|
746
760
|
};
|
|
747
761
|
|
|
748
|
-
export type { AutoFormProps as A,
|
|
762
|
+
export type { AutoFormProps as A, SelectOption as B, ComponentRegistry as C, DeepKeysIndexed as D, SubmitButtonProps as E, FieldMetaBase as F, LayoutSlots as L, ObjectWrapperProps as O, PersistStorage as P, ResolvedLayoutSlots as R, SectionConfig as S, ValidationMessages as V, FieldConfig as a, AutoFormHandle as b, FieldProps as c, FieldWrapperProps as d, ArrayButtonProps as e, ArrayCollapseButtonProps as f, ArrayFieldLayoutProps as g, ArrayRowLayoutProps as h, ArrayWrapperProps as i, AutoFormConfig as j, FormMethods as k, DeepKeys as l, FieldDependencyResult as m, DeepFieldValue as n, ConditionValues as o, FormClassNames as p, CoercionMap as q, FormLabels as r, ArrayButtonSlots as s, FieldCondition as t, FieldMeta as u, FieldOverride as v, FieldType as w, FormWrapperProps as x, ResolvedArrayButtonSlots as y, SectionWrapperProps as z };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FieldMetaBase, a as FieldConfig, A as AutoFormProps, b as AutoFormHandle, c as FieldProps, d as FieldWrapperProps, e as ArrayButtonProps, f as ArrayCollapseButtonProps, g as ArrayFieldLayoutProps, h as ArrayRowLayoutProps, O as ObjectWrapperProps, i as ArrayWrapperProps, C as ComponentRegistry, j as AutoFormConfig, D as
|
|
2
|
-
export {
|
|
1
|
+
import { F as FieldMetaBase, a as FieldConfig, A as AutoFormProps, b as AutoFormHandle, c as FieldProps, d as FieldWrapperProps, e as ArrayButtonProps, f as ArrayCollapseButtonProps, g as ArrayFieldLayoutProps, h as ArrayRowLayoutProps, O as ObjectWrapperProps, i as ArrayWrapperProps, C as ComponentRegistry, j as AutoFormConfig, D as DeepKeysIndexed, k as FormMethods, l as DeepKeys, m as FieldDependencyResult, n as DeepFieldValue, o as ConditionValues, P as PersistStorage, R as ResolvedLayoutSlots, p as FormClassNames, q as CoercionMap$1, V as ValidationMessages, r as FormLabels } from './field-Cogi7eQc.mjs';
|
|
2
|
+
export { s as ArrayButtonSlots, t as FieldCondition, u as FieldMeta, v as FieldOverride, w as FieldType, x as FormWrapperProps, L as LayoutSlots, y as ResolvedArrayButtonSlots, S as SectionConfig, z as SectionWrapperProps, B as SelectOption, E as SubmitButtonProps } from './field-Cogi7eQc.mjs';
|
|
3
3
|
import * as z from 'zod/v4/core';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
import * as React from 'react';
|
|
@@ -227,9 +227,13 @@ declare class UniForm<TSchema extends z.$ZodObject, TRegistered extends string =
|
|
|
227
227
|
* Replaces any previously registered handler for that field — only one
|
|
228
228
|
* handler per field is kept. This prevents accidental handler accumulation
|
|
229
229
|
* when called inside a React render cycle.
|
|
230
|
+
*
|
|
231
|
+
* Supports both generic array paths (`"tasks.priority"` — fires for all rows)
|
|
232
|
+
* and indexed paths (`"tasks.0.priority"` — fires only for row 0).
|
|
233
|
+
*
|
|
230
234
|
* Returns `this` for fluent chaining.
|
|
231
235
|
*/
|
|
232
|
-
setOnChange<K extends Exclude<
|
|
236
|
+
setOnChange<K extends Exclude<DeepKeysIndexed<z.infer<TSchema>>, TRegistered>>(field: K, handler: Handler<TSchema, DeepFieldValue<z.infer<TSchema>, K>>): UniForm<TSchema, TRegistered | K>;
|
|
233
237
|
/**
|
|
234
238
|
* Attach a typed condition for a specific field.
|
|
235
239
|
* The field is shown when the predicate returns `true`, hidden when `false`.
|
|
@@ -376,6 +380,7 @@ type AutoFormContextValue = {
|
|
|
376
380
|
labels: FormLabels;
|
|
377
381
|
formMethods: FormMethods;
|
|
378
382
|
control: Control;
|
|
383
|
+
setDynamicMeta: React.Dispatch<React.SetStateAction<Record<string, Partial<FieldDependencyResult>>>>;
|
|
379
384
|
};
|
|
380
385
|
declare function useAutoFormContext(): AutoFormContextValue;
|
|
381
386
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FieldMetaBase, a as FieldConfig, A as AutoFormProps, b as AutoFormHandle, c as FieldProps, d as FieldWrapperProps, e as ArrayButtonProps, f as ArrayCollapseButtonProps, g as ArrayFieldLayoutProps, h as ArrayRowLayoutProps, O as ObjectWrapperProps, i as ArrayWrapperProps, C as ComponentRegistry, j as AutoFormConfig, D as
|
|
2
|
-
export {
|
|
1
|
+
import { F as FieldMetaBase, a as FieldConfig, A as AutoFormProps, b as AutoFormHandle, c as FieldProps, d as FieldWrapperProps, e as ArrayButtonProps, f as ArrayCollapseButtonProps, g as ArrayFieldLayoutProps, h as ArrayRowLayoutProps, O as ObjectWrapperProps, i as ArrayWrapperProps, C as ComponentRegistry, j as AutoFormConfig, D as DeepKeysIndexed, k as FormMethods, l as DeepKeys, m as FieldDependencyResult, n as DeepFieldValue, o as ConditionValues, P as PersistStorage, R as ResolvedLayoutSlots, p as FormClassNames, q as CoercionMap$1, V as ValidationMessages, r as FormLabels } from './field-Cogi7eQc.js';
|
|
2
|
+
export { s as ArrayButtonSlots, t as FieldCondition, u as FieldMeta, v as FieldOverride, w as FieldType, x as FormWrapperProps, L as LayoutSlots, y as ResolvedArrayButtonSlots, S as SectionConfig, z as SectionWrapperProps, B as SelectOption, E as SubmitButtonProps } from './field-Cogi7eQc.js';
|
|
3
3
|
import * as z from 'zod/v4/core';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
import * as React from 'react';
|
|
@@ -227,9 +227,13 @@ declare class UniForm<TSchema extends z.$ZodObject, TRegistered extends string =
|
|
|
227
227
|
* Replaces any previously registered handler for that field — only one
|
|
228
228
|
* handler per field is kept. This prevents accidental handler accumulation
|
|
229
229
|
* when called inside a React render cycle.
|
|
230
|
+
*
|
|
231
|
+
* Supports both generic array paths (`"tasks.priority"` — fires for all rows)
|
|
232
|
+
* and indexed paths (`"tasks.0.priority"` — fires only for row 0).
|
|
233
|
+
*
|
|
230
234
|
* Returns `this` for fluent chaining.
|
|
231
235
|
*/
|
|
232
|
-
setOnChange<K extends Exclude<
|
|
236
|
+
setOnChange<K extends Exclude<DeepKeysIndexed<z.infer<TSchema>>, TRegistered>>(field: K, handler: Handler<TSchema, DeepFieldValue<z.infer<TSchema>, K>>): UniForm<TSchema, TRegistered | K>;
|
|
233
237
|
/**
|
|
234
238
|
* Attach a typed condition for a specific field.
|
|
235
239
|
* The field is shown when the predicate returns `true`, hidden when `false`.
|
|
@@ -376,6 +380,7 @@ type AutoFormContextValue = {
|
|
|
376
380
|
labels: FormLabels;
|
|
377
381
|
formMethods: FormMethods;
|
|
378
382
|
control: Control;
|
|
383
|
+
setDynamicMeta: React.Dispatch<React.SetStateAction<Record<string, Partial<FieldDependencyResult>>>>;
|
|
379
384
|
};
|
|
380
385
|
declare function useAutoFormContext(): AutoFormContextValue;
|
|
381
386
|
|
package/dist/index.js
CHANGED
|
@@ -738,6 +738,124 @@ function getDefaultValue(field) {
|
|
|
738
738
|
return void 0;
|
|
739
739
|
}
|
|
740
740
|
}
|
|
741
|
+
|
|
742
|
+
// src/utils/reindexDynamicMeta.ts
|
|
743
|
+
function buildKeyPattern(arrayName) {
|
|
744
|
+
return new RegExp(`^${escapeRegExp(arrayName)}\\.(\\d+)\\.(.+)$`);
|
|
745
|
+
}
|
|
746
|
+
function escapeRegExp(str) {
|
|
747
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
748
|
+
}
|
|
749
|
+
function reindexDynamicMeta(dynamicMeta, arrayName, mutation) {
|
|
750
|
+
const pattern = buildKeyPattern(arrayName);
|
|
751
|
+
const matched = [];
|
|
752
|
+
const result = {};
|
|
753
|
+
for (const [key, value] of Object.entries(dynamicMeta)) {
|
|
754
|
+
const match = pattern.exec(key);
|
|
755
|
+
if (match) {
|
|
756
|
+
matched.push({
|
|
757
|
+
key,
|
|
758
|
+
index: parseInt(match[1], 10),
|
|
759
|
+
childField: match[2],
|
|
760
|
+
value
|
|
761
|
+
});
|
|
762
|
+
} else {
|
|
763
|
+
result[key] = value;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (!isValidMutation(mutation)) {
|
|
767
|
+
return dynamicMeta;
|
|
768
|
+
}
|
|
769
|
+
switch (mutation.type) {
|
|
770
|
+
case "remove":
|
|
771
|
+
applyRemove(matched, mutation.index, arrayName, result);
|
|
772
|
+
break;
|
|
773
|
+
case "move":
|
|
774
|
+
applyMove(matched, mutation.from, mutation.to, arrayName, result);
|
|
775
|
+
break;
|
|
776
|
+
case "duplicate":
|
|
777
|
+
applyDuplicate(matched, mutation.index, arrayName, result);
|
|
778
|
+
break;
|
|
779
|
+
case "add":
|
|
780
|
+
applyAdd(matched, mutation.index, arrayName, result);
|
|
781
|
+
break;
|
|
782
|
+
}
|
|
783
|
+
return result;
|
|
784
|
+
}
|
|
785
|
+
function isValidMutation(mutation, _entries) {
|
|
786
|
+
switch (mutation.type) {
|
|
787
|
+
case "remove":
|
|
788
|
+
return mutation.index >= 0;
|
|
789
|
+
case "move":
|
|
790
|
+
return mutation.from >= 0 && mutation.to >= 0;
|
|
791
|
+
case "duplicate":
|
|
792
|
+
return mutation.index >= 0;
|
|
793
|
+
case "add":
|
|
794
|
+
return mutation.index >= 0;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
function buildKey(arrayName, index, childField) {
|
|
798
|
+
return `${arrayName}.${index}.${childField}`;
|
|
799
|
+
}
|
|
800
|
+
function applyRemove(entries, removedIndex, arrayName, result) {
|
|
801
|
+
for (const entry of entries) {
|
|
802
|
+
if (entry.index === removedIndex) {
|
|
803
|
+
continue;
|
|
804
|
+
} else if (entry.index > removedIndex) {
|
|
805
|
+
result[buildKey(arrayName, entry.index - 1, entry.childField)] = entry.value;
|
|
806
|
+
} else {
|
|
807
|
+
result[buildKey(arrayName, entry.index, entry.childField)] = entry.value;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
function applyMove(entries, from, to, arrayName, result) {
|
|
812
|
+
if (from === to) {
|
|
813
|
+
for (const entry of entries) {
|
|
814
|
+
result[buildKey(arrayName, entry.index, entry.childField)] = entry.value;
|
|
815
|
+
}
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
for (const entry of entries) {
|
|
819
|
+
let newIndex;
|
|
820
|
+
if (entry.index === from) {
|
|
821
|
+
newIndex = to;
|
|
822
|
+
} else if (from < to) {
|
|
823
|
+
if (entry.index > from && entry.index <= to) {
|
|
824
|
+
newIndex = entry.index - 1;
|
|
825
|
+
} else {
|
|
826
|
+
newIndex = entry.index;
|
|
827
|
+
}
|
|
828
|
+
} else {
|
|
829
|
+
if (entry.index >= to && entry.index < from) {
|
|
830
|
+
newIndex = entry.index + 1;
|
|
831
|
+
} else {
|
|
832
|
+
newIndex = entry.index;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
result[buildKey(arrayName, newIndex, entry.childField)] = entry.value;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
function applyDuplicate(entries, sourceIndex, arrayName, result) {
|
|
839
|
+
for (const entry of entries) {
|
|
840
|
+
if (entry.index === sourceIndex) {
|
|
841
|
+
result[buildKey(arrayName, entry.index, entry.childField)] = entry.value;
|
|
842
|
+
result[buildKey(arrayName, sourceIndex + 1, entry.childField)] = entry.value;
|
|
843
|
+
} else if (entry.index > sourceIndex) {
|
|
844
|
+
result[buildKey(arrayName, entry.index + 1, entry.childField)] = entry.value;
|
|
845
|
+
} else {
|
|
846
|
+
result[buildKey(arrayName, entry.index, entry.childField)] = entry.value;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
function applyAdd(entries, newIndex, arrayName, result) {
|
|
851
|
+
for (const entry of entries) {
|
|
852
|
+
if (entry.index >= newIndex) {
|
|
853
|
+
result[buildKey(arrayName, entry.index + 1, entry.childField)] = entry.value;
|
|
854
|
+
} else {
|
|
855
|
+
result[buildKey(arrayName, entry.index, entry.childField)] = entry.value;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
741
859
|
function getRowSummary(row, itemConfig, index, itemSummary) {
|
|
742
860
|
if (itemConfig.type === "object") {
|
|
743
861
|
for (const child of itemConfig.children) {
|
|
@@ -750,8 +868,43 @@ function getRowSummary(row, itemConfig, index, itemSummary) {
|
|
|
750
868
|
}
|
|
751
869
|
return itemSummary?.(index) ?? `Item ${index + 1}`;
|
|
752
870
|
}
|
|
871
|
+
function bindRowIndexToItemConfig(itemConfig, rowIndex) {
|
|
872
|
+
if (itemConfig.type !== "object") return itemConfig;
|
|
873
|
+
const children = itemConfig.children.map((child) => {
|
|
874
|
+
if (!child.meta.onChange) return child;
|
|
875
|
+
const originalOnChange = child.meta.onChange;
|
|
876
|
+
return {
|
|
877
|
+
...child,
|
|
878
|
+
meta: {
|
|
879
|
+
...child.meta,
|
|
880
|
+
onChange: (value, formMethods) => {
|
|
881
|
+
void originalOnChange(value, formMethods, rowIndex);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
});
|
|
886
|
+
return { ...itemConfig, children };
|
|
887
|
+
}
|
|
888
|
+
function applyRowDynamicMeta(itemConfig, rowOverrides) {
|
|
889
|
+
if (itemConfig.type !== "object") return itemConfig;
|
|
890
|
+
const children = itemConfig.children.map((child) => {
|
|
891
|
+
const override = rowOverrides[child.name];
|
|
892
|
+
if (!override) return child;
|
|
893
|
+
const { options, label, ...metaOverrides } = override;
|
|
894
|
+
let updated = {
|
|
895
|
+
...child,
|
|
896
|
+
...label !== void 0 ? { label } : {},
|
|
897
|
+
meta: { ...child.meta, ...metaOverrides }
|
|
898
|
+
};
|
|
899
|
+
if (options !== void 0 && updated.type === "select") {
|
|
900
|
+
updated = { ...updated, options };
|
|
901
|
+
}
|
|
902
|
+
return updated;
|
|
903
|
+
});
|
|
904
|
+
return { ...itemConfig, children };
|
|
905
|
+
}
|
|
753
906
|
function ArrayField({ field, control, effectiveName }) {
|
|
754
|
-
const { classNames, layout, labels } = useAutoFormContext();
|
|
907
|
+
const { classNames, layout, labels, setDynamicMeta } = useAutoFormContext();
|
|
755
908
|
const {
|
|
756
909
|
fields: rows,
|
|
757
910
|
append,
|
|
@@ -797,6 +950,7 @@ function ArrayField({ field, control, effectiveName }) {
|
|
|
797
950
|
} = layout.arrayButtons;
|
|
798
951
|
const ArrayFieldLayout = layout.arrayFieldLayout;
|
|
799
952
|
const RowLayout = layout.arrayRowLayout;
|
|
953
|
+
const rowDynamicMeta = field._rowDynamicMeta;
|
|
800
954
|
const renderedRows = rows.map((row, index) => {
|
|
801
955
|
const isCollapsed = showCollapse && collapsed.has(index);
|
|
802
956
|
const collapseButton = showCollapse && CollapseBtn ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -828,7 +982,16 @@ function ArrayField({ field, control, effectiveName }) {
|
|
|
828
982
|
{
|
|
829
983
|
type: "button",
|
|
830
984
|
className: classNames.arrayMove,
|
|
831
|
-
onClick: () =>
|
|
985
|
+
onClick: () => {
|
|
986
|
+
move(index, index - 1);
|
|
987
|
+
setDynamicMeta(
|
|
988
|
+
(prev) => reindexDynamicMeta(prev, effectiveName, {
|
|
989
|
+
type: "move",
|
|
990
|
+
from: index,
|
|
991
|
+
to: index - 1
|
|
992
|
+
})
|
|
993
|
+
);
|
|
994
|
+
},
|
|
832
995
|
disabled: index === 0,
|
|
833
996
|
"aria-label": labels.arrayAriaMoveUp?.(index) ?? `Move item ${index + 1} up`,
|
|
834
997
|
children: labels.arrayMoveUp ?? "\u2191"
|
|
@@ -839,7 +1002,16 @@ function ArrayField({ field, control, effectiveName }) {
|
|
|
839
1002
|
{
|
|
840
1003
|
type: "button",
|
|
841
1004
|
className: classNames.arrayMove,
|
|
842
|
-
onClick: () =>
|
|
1005
|
+
onClick: () => {
|
|
1006
|
+
move(index, index + 1);
|
|
1007
|
+
setDynamicMeta(
|
|
1008
|
+
(prev) => reindexDynamicMeta(prev, effectiveName, {
|
|
1009
|
+
type: "move",
|
|
1010
|
+
from: index,
|
|
1011
|
+
to: index + 1
|
|
1012
|
+
})
|
|
1013
|
+
);
|
|
1014
|
+
},
|
|
843
1015
|
disabled: index === rows.length - 1,
|
|
844
1016
|
"aria-label": labels.arrayAriaMoveDown?.(index) ?? `Move item ${index + 1} down`,
|
|
845
1017
|
children: labels.arrayMoveDown ?? "\u2193"
|
|
@@ -855,6 +1027,12 @@ function ArrayField({ field, control, effectiveName }) {
|
|
|
855
1027
|
Object.entries(row).filter(([k]) => k !== "id")
|
|
856
1028
|
);
|
|
857
1029
|
insert(index + 1, values);
|
|
1030
|
+
setDynamicMeta(
|
|
1031
|
+
(prev) => reindexDynamicMeta(prev, effectiveName, {
|
|
1032
|
+
type: "duplicate",
|
|
1033
|
+
index
|
|
1034
|
+
})
|
|
1035
|
+
);
|
|
858
1036
|
},
|
|
859
1037
|
"aria-label": labels.arrayAriaDuplicate?.(index) ?? `Duplicate item ${index + 1}`,
|
|
860
1038
|
children: labels.arrayDuplicate ?? "Duplicate"
|
|
@@ -865,7 +1043,15 @@ function ArrayField({ field, control, effectiveName }) {
|
|
|
865
1043
|
{
|
|
866
1044
|
type: "button",
|
|
867
1045
|
className: classNames.arrayRemove,
|
|
868
|
-
onClick: () =>
|
|
1046
|
+
onClick: () => {
|
|
1047
|
+
remove(index);
|
|
1048
|
+
setDynamicMeta(
|
|
1049
|
+
(prev) => reindexDynamicMeta(prev, effectiveName, {
|
|
1050
|
+
type: "remove",
|
|
1051
|
+
index
|
|
1052
|
+
})
|
|
1053
|
+
);
|
|
1054
|
+
},
|
|
869
1055
|
disabled: atMin,
|
|
870
1056
|
"aria-label": labels.arrayAriaRemove?.(index) ?? `Remove item ${index + 1}`,
|
|
871
1057
|
children: labels.arrayRemove ?? "Remove"
|
|
@@ -874,7 +1060,10 @@ function ArrayField({ field, control, effectiveName }) {
|
|
|
874
1060
|
const fieldContent = !isCollapsed ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
875
1061
|
FieldRenderer,
|
|
876
1062
|
{
|
|
877
|
-
field:
|
|
1063
|
+
field: bindRowIndexToItemConfig(
|
|
1064
|
+
rowDynamicMeta?.[index] ? applyRowDynamicMeta(effectiveItemConfig, rowDynamicMeta[index]) : effectiveItemConfig,
|
|
1065
|
+
index
|
|
1066
|
+
),
|
|
878
1067
|
control,
|
|
879
1068
|
namePrefix: `${effectiveName}.${index}`
|
|
880
1069
|
}
|
|
@@ -902,7 +1091,16 @@ function ArrayField({ field, control, effectiveName }) {
|
|
|
902
1091
|
type: "button",
|
|
903
1092
|
className: classNames.arrayAdd,
|
|
904
1093
|
disabled: atMax,
|
|
905
|
-
onClick: () =>
|
|
1094
|
+
onClick: () => {
|
|
1095
|
+
const newIndex = rows.length;
|
|
1096
|
+
append(getDefaultValue(itemConfig));
|
|
1097
|
+
setDynamicMeta(
|
|
1098
|
+
(prev) => reindexDynamicMeta(prev, effectiveName, {
|
|
1099
|
+
type: "add",
|
|
1100
|
+
index: newIndex
|
|
1101
|
+
})
|
|
1102
|
+
);
|
|
1103
|
+
},
|
|
906
1104
|
children: labels.arrayAdd ?? "Add"
|
|
907
1105
|
}
|
|
908
1106
|
) : null;
|
|
@@ -1155,6 +1353,28 @@ function useLatestRef(value) {
|
|
|
1155
1353
|
return ref;
|
|
1156
1354
|
}
|
|
1157
1355
|
|
|
1356
|
+
// src/utils/createRowScopedContext.ts
|
|
1357
|
+
function createRowScopedContext(baseCtx, arrayName, rowIndex, itemFieldNames, getValues) {
|
|
1358
|
+
return {
|
|
1359
|
+
...baseCtx,
|
|
1360
|
+
getValues,
|
|
1361
|
+
setFieldMeta: (field, meta) => {
|
|
1362
|
+
const fieldStr = field;
|
|
1363
|
+
const prefix = arrayName + ".";
|
|
1364
|
+
const childName = fieldStr.startsWith(prefix) ? fieldStr.slice(prefix.length) : fieldStr;
|
|
1365
|
+
if (itemFieldNames.has(childName)) {
|
|
1366
|
+
const qualifiedKey = `${arrayName}.${rowIndex}.${childName}`;
|
|
1367
|
+
baseCtx.setFieldMeta(
|
|
1368
|
+
qualifiedKey,
|
|
1369
|
+
meta
|
|
1370
|
+
);
|
|
1371
|
+
} else {
|
|
1372
|
+
baseCtx.setFieldMeta(field, meta);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1158
1378
|
// src/utils/fieldPipeline.ts
|
|
1159
1379
|
function applyFieldOverrides(fields, overrides) {
|
|
1160
1380
|
return fields.map((field) => {
|
|
@@ -1215,10 +1435,74 @@ function injectOnChangeHandlers(fields, uniForm, ctx, handlerKeys = new Set(uniF
|
|
|
1215
1435
|
} else if (updated.type === "array") {
|
|
1216
1436
|
const prefix = field.name + ".";
|
|
1217
1437
|
const itemKeys = /* @__PURE__ */ new Set();
|
|
1438
|
+
const indexedKeys = /* @__PURE__ */ new Set();
|
|
1218
1439
|
for (const key of handlerKeys) {
|
|
1219
|
-
if (key.startsWith(prefix))
|
|
1440
|
+
if (key.startsWith(prefix)) {
|
|
1441
|
+
const remainder = key.slice(prefix.length);
|
|
1442
|
+
const indexMatch = remainder.match(/^(\d+)\.(.+)$/);
|
|
1443
|
+
if (indexMatch) {
|
|
1444
|
+
indexedKeys.add(remainder);
|
|
1445
|
+
itemKeys.add(indexMatch[2]);
|
|
1446
|
+
} else {
|
|
1447
|
+
itemKeys.add(remainder);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1220
1450
|
}
|
|
1221
|
-
if (itemKeys.size) {
|
|
1451
|
+
if (itemKeys.size && updated.itemConfig.type === "object") {
|
|
1452
|
+
const itemFieldNames = new Set(
|
|
1453
|
+
updated.itemConfig.children.map((c) => c.name)
|
|
1454
|
+
);
|
|
1455
|
+
const arrayName = field.name;
|
|
1456
|
+
const newChildren = updated.itemConfig.children.map((child) => {
|
|
1457
|
+
if (!itemKeys.has(child.name)) return child;
|
|
1458
|
+
const existingOnChange = child.meta.onChange;
|
|
1459
|
+
const rowAwareHandler = (value, formMethods, rowIndex) => {
|
|
1460
|
+
void existingOnChange?.(value, formMethods);
|
|
1461
|
+
const rowCtx = createRowScopedContext(
|
|
1462
|
+
ctx,
|
|
1463
|
+
arrayName,
|
|
1464
|
+
rowIndex,
|
|
1465
|
+
itemFieldNames,
|
|
1466
|
+
() => {
|
|
1467
|
+
const allValues = ctx.getValues();
|
|
1468
|
+
const arrayValues = allValues?.[arrayName];
|
|
1469
|
+
if (Array.isArray(arrayValues)) {
|
|
1470
|
+
return arrayValues[rowIndex] ?? {};
|
|
1471
|
+
}
|
|
1472
|
+
return {};
|
|
1473
|
+
}
|
|
1474
|
+
);
|
|
1475
|
+
void uniForm._fireHandler(
|
|
1476
|
+
`${arrayName}.${child.name}`,
|
|
1477
|
+
value,
|
|
1478
|
+
rowCtx
|
|
1479
|
+
);
|
|
1480
|
+
const indexedKey = `${rowIndex}.${child.name}`;
|
|
1481
|
+
if (indexedKeys.has(indexedKey)) {
|
|
1482
|
+
void uniForm._fireHandler(
|
|
1483
|
+
`${arrayName}.${indexedKey}`,
|
|
1484
|
+
value,
|
|
1485
|
+
rowCtx
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
return {
|
|
1490
|
+
...child,
|
|
1491
|
+
meta: {
|
|
1492
|
+
...child.meta,
|
|
1493
|
+
// Cast to FieldMeta onChange type — ArrayField's bindRowIndexToItemConfig
|
|
1494
|
+
// will call this with the rowIndex third argument at render time.
|
|
1495
|
+
onChange: rowAwareHandler
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1498
|
+
});
|
|
1499
|
+
const newItemConfig = {
|
|
1500
|
+
...updated.itemConfig,
|
|
1501
|
+
children: newChildren
|
|
1502
|
+
};
|
|
1503
|
+
if (newItemConfig !== updated.itemConfig)
|
|
1504
|
+
updated = { ...updated, itemConfig: newItemConfig };
|
|
1505
|
+
} else if (itemKeys.size) {
|
|
1222
1506
|
const remappedUniForm = {
|
|
1223
1507
|
_getWatchedFields: () => Array.from(itemKeys),
|
|
1224
1508
|
_fireHandlers: (name, value, c) => uniForm._fireHandler(`${field.name}.${name}`, value, c)
|
|
@@ -1264,18 +1548,43 @@ function injectConditions(fields, conditions) {
|
|
|
1264
1548
|
return updated;
|
|
1265
1549
|
});
|
|
1266
1550
|
}
|
|
1551
|
+
var ROW_KEY_PATTERN = /^(.+?)\.(\d+)\.(.+)$/;
|
|
1267
1552
|
function applyDynamicMeta(fields, overrides) {
|
|
1268
1553
|
if (!Object.keys(overrides).length) return fields;
|
|
1269
1554
|
return fields.map((field) => {
|
|
1270
1555
|
const override = overrides[field.name];
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1556
|
+
let updated = field;
|
|
1557
|
+
if (override) {
|
|
1558
|
+
const { options, label, ...metaOverrides } = override;
|
|
1559
|
+
updated = {
|
|
1560
|
+
...field,
|
|
1561
|
+
...label !== void 0 ? { label } : {},
|
|
1562
|
+
...options !== void 0 ? { options } : {},
|
|
1563
|
+
meta: { ...field.meta, ...metaOverrides }
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
if (updated.type === "array") {
|
|
1567
|
+
const prefix = `${updated.name}.`;
|
|
1568
|
+
let rowDynamicMeta;
|
|
1569
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
1570
|
+
if (!key.startsWith(prefix)) continue;
|
|
1571
|
+
const match = ROW_KEY_PATTERN.exec(key);
|
|
1572
|
+
if (!match) continue;
|
|
1573
|
+
const [, matchedArrayName, indexStr, childField] = match;
|
|
1574
|
+
if (matchedArrayName !== updated.name) continue;
|
|
1575
|
+
const rowIndex = Number(indexStr);
|
|
1576
|
+
if (!rowDynamicMeta) rowDynamicMeta = {};
|
|
1577
|
+
if (!rowDynamicMeta[rowIndex]) rowDynamicMeta[rowIndex] = {};
|
|
1578
|
+
rowDynamicMeta[rowIndex][childField] = value;
|
|
1579
|
+
}
|
|
1580
|
+
if (rowDynamicMeta) {
|
|
1581
|
+
updated = {
|
|
1582
|
+
...updated,
|
|
1583
|
+
_rowDynamicMeta: rowDynamicMeta
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
return updated;
|
|
1279
1588
|
});
|
|
1280
1589
|
}
|
|
1281
1590
|
function buildDefaults(fields) {
|
|
@@ -1593,7 +1902,8 @@ function AutoForm(props) {
|
|
|
1593
1902
|
messages,
|
|
1594
1903
|
labels,
|
|
1595
1904
|
formMethods,
|
|
1596
|
-
control
|
|
1905
|
+
control,
|
|
1906
|
+
setDynamicMeta
|
|
1597
1907
|
}),
|
|
1598
1908
|
[
|
|
1599
1909
|
registry,
|
|
@@ -1607,7 +1917,8 @@ function AutoForm(props) {
|
|
|
1607
1917
|
messages,
|
|
1608
1918
|
labels,
|
|
1609
1919
|
formMethods,
|
|
1610
|
-
control
|
|
1920
|
+
control,
|
|
1921
|
+
setDynamicMeta
|
|
1611
1922
|
]
|
|
1612
1923
|
);
|
|
1613
1924
|
if (isLoadingDefaults) {
|
|
@@ -1724,6 +2035,10 @@ var UniForm = class {
|
|
|
1724
2035
|
* Replaces any previously registered handler for that field — only one
|
|
1725
2036
|
* handler per field is kept. This prevents accidental handler accumulation
|
|
1726
2037
|
* when called inside a React render cycle.
|
|
2038
|
+
*
|
|
2039
|
+
* Supports both generic array paths (`"tasks.priority"` — fires for all rows)
|
|
2040
|
+
* and indexed paths (`"tasks.0.priority"` — fires only for row 0).
|
|
2041
|
+
*
|
|
1727
2042
|
* Returns `this` for fluent chaining.
|
|
1728
2043
|
*/
|
|
1729
2044
|
setOnChange(field, handler) {
|