@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.
@@ -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, SubmitButtonProps as B, ComponentRegistry as C, DeepKeys as D, 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, FieldDependencyResult as l, DeepFieldValue as m, ConditionValues as n, FormClassNames as o, CoercionMap as p, FormLabels as q, ArrayButtonSlots as r, FieldCondition as s, FieldMeta as t, FieldOverride as u, FieldType as v, FormWrapperProps as w, ResolvedArrayButtonSlots as x, SectionWrapperProps as y, SelectOption as z };
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, SubmitButtonProps as B, ComponentRegistry as C, DeepKeys as D, 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, FieldDependencyResult as l, DeepFieldValue as m, ConditionValues as n, FormClassNames as o, CoercionMap as p, FormLabels as q, ArrayButtonSlots as r, FieldCondition as s, FieldMeta as t, FieldOverride as u, FieldType as v, FormWrapperProps as w, ResolvedArrayButtonSlots as x, SectionWrapperProps as y, SelectOption as z };
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 DeepKeys, k as FormMethods, l as FieldDependencyResult, m as DeepFieldValue, n as ConditionValues, P as PersistStorage, R as ResolvedLayoutSlots, o as FormClassNames, p as CoercionMap$1, V as ValidationMessages, q as FormLabels } from './field-KKjnXn-d.mjs';
2
- export { r as ArrayButtonSlots, s as FieldCondition, t as FieldMeta, u as FieldOverride, v as FieldType, w as FormWrapperProps, L as LayoutSlots, x as ResolvedArrayButtonSlots, S as SectionConfig, y as SectionWrapperProps, z as SelectOption, B as SubmitButtonProps } from './field-KKjnXn-d.mjs';
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<DeepKeys<z.infer<TSchema>>, TRegistered>>(field: K, handler: Handler<TSchema, DeepFieldValue<z.infer<TSchema>, K>>): UniForm<TSchema, TRegistered | K>;
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 DeepKeys, k as FormMethods, l as FieldDependencyResult, m as DeepFieldValue, n as ConditionValues, P as PersistStorage, R as ResolvedLayoutSlots, o as FormClassNames, p as CoercionMap$1, V as ValidationMessages, q as FormLabels } from './field-KKjnXn-d.js';
2
- export { r as ArrayButtonSlots, s as FieldCondition, t as FieldMeta, u as FieldOverride, v as FieldType, w as FormWrapperProps, L as LayoutSlots, x as ResolvedArrayButtonSlots, S as SectionConfig, y as SectionWrapperProps, z as SelectOption, B as SubmitButtonProps } from './field-KKjnXn-d.js';
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<DeepKeys<z.infer<TSchema>>, TRegistered>>(field: K, handler: Handler<TSchema, DeepFieldValue<z.infer<TSchema>, K>>): UniForm<TSchema, TRegistered | K>;
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: () => move(index, index - 1),
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: () => move(index, index + 1),
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: () => remove(index),
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: effectiveItemConfig,
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: () => append(getDefaultValue(itemConfig)),
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)) itemKeys.add(key.slice(prefix.length));
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
- if (!override) return field;
1272
- const { options, label, ...metaOverrides } = override;
1273
- return {
1274
- ...field,
1275
- ...label !== void 0 ? { label } : {},
1276
- ...options !== void 0 ? { options } : {},
1277
- meta: { ...field.meta, ...metaOverrides }
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) {