@uniform-ts/core 0.0.8 → 0.1.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/dist/index.mjs CHANGED
@@ -716,6 +716,124 @@ function getDefaultValue(field) {
716
716
  return void 0;
717
717
  }
718
718
  }
719
+
720
+ // src/utils/reindexDynamicMeta.ts
721
+ function buildKeyPattern(arrayName) {
722
+ return new RegExp(`^${escapeRegExp(arrayName)}\\.(\\d+)\\.(.+)$`);
723
+ }
724
+ function escapeRegExp(str) {
725
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
726
+ }
727
+ function reindexDynamicMeta(dynamicMeta, arrayName, mutation) {
728
+ const pattern = buildKeyPattern(arrayName);
729
+ const matched = [];
730
+ const result = {};
731
+ for (const [key, value] of Object.entries(dynamicMeta)) {
732
+ const match = pattern.exec(key);
733
+ if (match) {
734
+ matched.push({
735
+ key,
736
+ index: parseInt(match[1], 10),
737
+ childField: match[2],
738
+ value
739
+ });
740
+ } else {
741
+ result[key] = value;
742
+ }
743
+ }
744
+ if (!isValidMutation(mutation)) {
745
+ return dynamicMeta;
746
+ }
747
+ switch (mutation.type) {
748
+ case "remove":
749
+ applyRemove(matched, mutation.index, arrayName, result);
750
+ break;
751
+ case "move":
752
+ applyMove(matched, mutation.from, mutation.to, arrayName, result);
753
+ break;
754
+ case "duplicate":
755
+ applyDuplicate(matched, mutation.index, arrayName, result);
756
+ break;
757
+ case "add":
758
+ applyAdd(matched, mutation.index, arrayName, result);
759
+ break;
760
+ }
761
+ return result;
762
+ }
763
+ function isValidMutation(mutation, _entries) {
764
+ switch (mutation.type) {
765
+ case "remove":
766
+ return mutation.index >= 0;
767
+ case "move":
768
+ return mutation.from >= 0 && mutation.to >= 0;
769
+ case "duplicate":
770
+ return mutation.index >= 0;
771
+ case "add":
772
+ return mutation.index >= 0;
773
+ }
774
+ }
775
+ function buildKey(arrayName, index, childField) {
776
+ return `${arrayName}.${index}.${childField}`;
777
+ }
778
+ function applyRemove(entries, removedIndex, arrayName, result) {
779
+ for (const entry of entries) {
780
+ if (entry.index === removedIndex) {
781
+ continue;
782
+ } else if (entry.index > removedIndex) {
783
+ result[buildKey(arrayName, entry.index - 1, entry.childField)] = entry.value;
784
+ } else {
785
+ result[buildKey(arrayName, entry.index, entry.childField)] = entry.value;
786
+ }
787
+ }
788
+ }
789
+ function applyMove(entries, from, to, arrayName, result) {
790
+ if (from === to) {
791
+ for (const entry of entries) {
792
+ result[buildKey(arrayName, entry.index, entry.childField)] = entry.value;
793
+ }
794
+ return;
795
+ }
796
+ for (const entry of entries) {
797
+ let newIndex;
798
+ if (entry.index === from) {
799
+ newIndex = to;
800
+ } else if (from < to) {
801
+ if (entry.index > from && entry.index <= to) {
802
+ newIndex = entry.index - 1;
803
+ } else {
804
+ newIndex = entry.index;
805
+ }
806
+ } else {
807
+ if (entry.index >= to && entry.index < from) {
808
+ newIndex = entry.index + 1;
809
+ } else {
810
+ newIndex = entry.index;
811
+ }
812
+ }
813
+ result[buildKey(arrayName, newIndex, entry.childField)] = entry.value;
814
+ }
815
+ }
816
+ function applyDuplicate(entries, sourceIndex, arrayName, result) {
817
+ for (const entry of entries) {
818
+ if (entry.index === sourceIndex) {
819
+ result[buildKey(arrayName, entry.index, entry.childField)] = entry.value;
820
+ result[buildKey(arrayName, sourceIndex + 1, entry.childField)] = entry.value;
821
+ } else if (entry.index > sourceIndex) {
822
+ result[buildKey(arrayName, entry.index + 1, entry.childField)] = entry.value;
823
+ } else {
824
+ result[buildKey(arrayName, entry.index, entry.childField)] = entry.value;
825
+ }
826
+ }
827
+ }
828
+ function applyAdd(entries, newIndex, arrayName, result) {
829
+ for (const entry of entries) {
830
+ if (entry.index >= newIndex) {
831
+ result[buildKey(arrayName, entry.index + 1, entry.childField)] = entry.value;
832
+ } else {
833
+ result[buildKey(arrayName, entry.index, entry.childField)] = entry.value;
834
+ }
835
+ }
836
+ }
719
837
  function getRowSummary(row, itemConfig, index, itemSummary) {
720
838
  if (itemConfig.type === "object") {
721
839
  for (const child of itemConfig.children) {
@@ -728,8 +846,43 @@ function getRowSummary(row, itemConfig, index, itemSummary) {
728
846
  }
729
847
  return itemSummary?.(index) ?? `Item ${index + 1}`;
730
848
  }
849
+ function bindRowIndexToItemConfig(itemConfig, rowIndex) {
850
+ if (itemConfig.type !== "object") return itemConfig;
851
+ const children = itemConfig.children.map((child) => {
852
+ if (!child.meta.onChange) return child;
853
+ const originalOnChange = child.meta.onChange;
854
+ return {
855
+ ...child,
856
+ meta: {
857
+ ...child.meta,
858
+ onChange: (value, formMethods) => {
859
+ void originalOnChange(value, formMethods, rowIndex);
860
+ }
861
+ }
862
+ };
863
+ });
864
+ return { ...itemConfig, children };
865
+ }
866
+ function applyRowDynamicMeta(itemConfig, rowOverrides) {
867
+ if (itemConfig.type !== "object") return itemConfig;
868
+ const children = itemConfig.children.map((child) => {
869
+ const override = rowOverrides[child.name];
870
+ if (!override) return child;
871
+ const { options, label, ...metaOverrides } = override;
872
+ let updated = {
873
+ ...child,
874
+ ...label !== void 0 ? { label } : {},
875
+ meta: { ...child.meta, ...metaOverrides }
876
+ };
877
+ if (options !== void 0 && updated.type === "select") {
878
+ updated = { ...updated, options };
879
+ }
880
+ return updated;
881
+ });
882
+ return { ...itemConfig, children };
883
+ }
731
884
  function ArrayField({ field, control, effectiveName }) {
732
- const { classNames, layout, labels } = useAutoFormContext();
885
+ const { classNames, layout, labels, setDynamicMeta } = useAutoFormContext();
733
886
  const {
734
887
  fields: rows,
735
888
  append,
@@ -775,6 +928,7 @@ function ArrayField({ field, control, effectiveName }) {
775
928
  } = layout.arrayButtons;
776
929
  const ArrayFieldLayout = layout.arrayFieldLayout;
777
930
  const RowLayout = layout.arrayRowLayout;
931
+ const rowDynamicMeta = field._rowDynamicMeta;
778
932
  const renderedRows = rows.map((row, index) => {
779
933
  const isCollapsed = showCollapse && collapsed.has(index);
780
934
  const collapseButton = showCollapse && CollapseBtn ? /* @__PURE__ */ jsxs(
@@ -806,7 +960,16 @@ function ArrayField({ field, control, effectiveName }) {
806
960
  {
807
961
  type: "button",
808
962
  className: classNames.arrayMove,
809
- onClick: () => move(index, index - 1),
963
+ onClick: () => {
964
+ move(index, index - 1);
965
+ setDynamicMeta(
966
+ (prev) => reindexDynamicMeta(prev, effectiveName, {
967
+ type: "move",
968
+ from: index,
969
+ to: index - 1
970
+ })
971
+ );
972
+ },
810
973
  disabled: index === 0,
811
974
  "aria-label": labels.arrayAriaMoveUp?.(index) ?? `Move item ${index + 1} up`,
812
975
  children: labels.arrayMoveUp ?? "\u2191"
@@ -817,7 +980,16 @@ function ArrayField({ field, control, effectiveName }) {
817
980
  {
818
981
  type: "button",
819
982
  className: classNames.arrayMove,
820
- onClick: () => move(index, index + 1),
983
+ onClick: () => {
984
+ move(index, index + 1);
985
+ setDynamicMeta(
986
+ (prev) => reindexDynamicMeta(prev, effectiveName, {
987
+ type: "move",
988
+ from: index,
989
+ to: index + 1
990
+ })
991
+ );
992
+ },
821
993
  disabled: index === rows.length - 1,
822
994
  "aria-label": labels.arrayAriaMoveDown?.(index) ?? `Move item ${index + 1} down`,
823
995
  children: labels.arrayMoveDown ?? "\u2193"
@@ -833,6 +1005,12 @@ function ArrayField({ field, control, effectiveName }) {
833
1005
  Object.entries(row).filter(([k]) => k !== "id")
834
1006
  );
835
1007
  insert(index + 1, values);
1008
+ setDynamicMeta(
1009
+ (prev) => reindexDynamicMeta(prev, effectiveName, {
1010
+ type: "duplicate",
1011
+ index
1012
+ })
1013
+ );
836
1014
  },
837
1015
  "aria-label": labels.arrayAriaDuplicate?.(index) ?? `Duplicate item ${index + 1}`,
838
1016
  children: labels.arrayDuplicate ?? "Duplicate"
@@ -843,7 +1021,15 @@ function ArrayField({ field, control, effectiveName }) {
843
1021
  {
844
1022
  type: "button",
845
1023
  className: classNames.arrayRemove,
846
- onClick: () => remove(index),
1024
+ onClick: () => {
1025
+ remove(index);
1026
+ setDynamicMeta(
1027
+ (prev) => reindexDynamicMeta(prev, effectiveName, {
1028
+ type: "remove",
1029
+ index
1030
+ })
1031
+ );
1032
+ },
847
1033
  disabled: atMin,
848
1034
  "aria-label": labels.arrayAriaRemove?.(index) ?? `Remove item ${index + 1}`,
849
1035
  children: labels.arrayRemove ?? "Remove"
@@ -852,7 +1038,10 @@ function ArrayField({ field, control, effectiveName }) {
852
1038
  const fieldContent = !isCollapsed ? /* @__PURE__ */ jsx(
853
1039
  FieldRenderer,
854
1040
  {
855
- field: effectiveItemConfig,
1041
+ field: bindRowIndexToItemConfig(
1042
+ rowDynamicMeta?.[index] ? applyRowDynamicMeta(effectiveItemConfig, rowDynamicMeta[index]) : effectiveItemConfig,
1043
+ index
1044
+ ),
856
1045
  control,
857
1046
  namePrefix: `${effectiveName}.${index}`
858
1047
  }
@@ -880,7 +1069,16 @@ function ArrayField({ field, control, effectiveName }) {
880
1069
  type: "button",
881
1070
  className: classNames.arrayAdd,
882
1071
  disabled: atMax,
883
- onClick: () => append(getDefaultValue(itemConfig)),
1072
+ onClick: () => {
1073
+ const newIndex = rows.length;
1074
+ append(getDefaultValue(itemConfig));
1075
+ setDynamicMeta(
1076
+ (prev) => reindexDynamicMeta(prev, effectiveName, {
1077
+ type: "add",
1078
+ index: newIndex
1079
+ })
1080
+ );
1081
+ },
884
1082
  children: labels.arrayAdd ?? "Add"
885
1083
  }
886
1084
  ) : null;
@@ -1133,6 +1331,28 @@ function useLatestRef(value) {
1133
1331
  return ref;
1134
1332
  }
1135
1333
 
1334
+ // src/utils/createRowScopedContext.ts
1335
+ function createRowScopedContext(baseCtx, arrayName, rowIndex, itemFieldNames, getValues) {
1336
+ return {
1337
+ ...baseCtx,
1338
+ getValues,
1339
+ setFieldMeta: (field, meta) => {
1340
+ const fieldStr = field;
1341
+ const prefix = arrayName + ".";
1342
+ const childName = fieldStr.startsWith(prefix) ? fieldStr.slice(prefix.length) : fieldStr;
1343
+ if (itemFieldNames.has(childName)) {
1344
+ const qualifiedKey = `${arrayName}.${rowIndex}.${childName}`;
1345
+ baseCtx.setFieldMeta(
1346
+ qualifiedKey,
1347
+ meta
1348
+ );
1349
+ } else {
1350
+ baseCtx.setFieldMeta(field, meta);
1351
+ }
1352
+ }
1353
+ };
1354
+ }
1355
+
1136
1356
  // src/utils/fieldPipeline.ts
1137
1357
  function applyFieldOverrides(fields, overrides) {
1138
1358
  return fields.map((field) => {
@@ -1193,10 +1413,74 @@ function injectOnChangeHandlers(fields, uniForm, ctx, handlerKeys = new Set(uniF
1193
1413
  } else if (updated.type === "array") {
1194
1414
  const prefix = field.name + ".";
1195
1415
  const itemKeys = /* @__PURE__ */ new Set();
1416
+ const indexedKeys = /* @__PURE__ */ new Set();
1196
1417
  for (const key of handlerKeys) {
1197
- if (key.startsWith(prefix)) itemKeys.add(key.slice(prefix.length));
1418
+ if (key.startsWith(prefix)) {
1419
+ const remainder = key.slice(prefix.length);
1420
+ const indexMatch = remainder.match(/^(\d+)\.(.+)$/);
1421
+ if (indexMatch) {
1422
+ indexedKeys.add(remainder);
1423
+ itemKeys.add(indexMatch[2]);
1424
+ } else {
1425
+ itemKeys.add(remainder);
1426
+ }
1427
+ }
1198
1428
  }
1199
- if (itemKeys.size) {
1429
+ if (itemKeys.size && updated.itemConfig.type === "object") {
1430
+ const itemFieldNames = new Set(
1431
+ updated.itemConfig.children.map((c) => c.name)
1432
+ );
1433
+ const arrayName = field.name;
1434
+ const newChildren = updated.itemConfig.children.map((child) => {
1435
+ if (!itemKeys.has(child.name)) return child;
1436
+ const existingOnChange = child.meta.onChange;
1437
+ const rowAwareHandler = (value, formMethods, rowIndex) => {
1438
+ void existingOnChange?.(value, formMethods);
1439
+ const rowCtx = createRowScopedContext(
1440
+ ctx,
1441
+ arrayName,
1442
+ rowIndex,
1443
+ itemFieldNames,
1444
+ () => {
1445
+ const allValues = ctx.getValues();
1446
+ const arrayValues = allValues?.[arrayName];
1447
+ if (Array.isArray(arrayValues)) {
1448
+ return arrayValues[rowIndex] ?? {};
1449
+ }
1450
+ return {};
1451
+ }
1452
+ );
1453
+ void uniForm._fireHandler(
1454
+ `${arrayName}.${child.name}`,
1455
+ value,
1456
+ rowCtx
1457
+ );
1458
+ const indexedKey = `${rowIndex}.${child.name}`;
1459
+ if (indexedKeys.has(indexedKey)) {
1460
+ void uniForm._fireHandler(
1461
+ `${arrayName}.${indexedKey}`,
1462
+ value,
1463
+ rowCtx
1464
+ );
1465
+ }
1466
+ };
1467
+ return {
1468
+ ...child,
1469
+ meta: {
1470
+ ...child.meta,
1471
+ // Cast to FieldMeta onChange type — ArrayField's bindRowIndexToItemConfig
1472
+ // will call this with the rowIndex third argument at render time.
1473
+ onChange: rowAwareHandler
1474
+ }
1475
+ };
1476
+ });
1477
+ const newItemConfig = {
1478
+ ...updated.itemConfig,
1479
+ children: newChildren
1480
+ };
1481
+ if (newItemConfig !== updated.itemConfig)
1482
+ updated = { ...updated, itemConfig: newItemConfig };
1483
+ } else if (itemKeys.size) {
1200
1484
  const remappedUniForm = {
1201
1485
  _getWatchedFields: () => Array.from(itemKeys),
1202
1486
  _fireHandlers: (name, value, c) => uniForm._fireHandler(`${field.name}.${name}`, value, c)
@@ -1242,18 +1526,43 @@ function injectConditions(fields, conditions) {
1242
1526
  return updated;
1243
1527
  });
1244
1528
  }
1529
+ var ROW_KEY_PATTERN = /^(.+?)\.(\d+)\.(.+)$/;
1245
1530
  function applyDynamicMeta(fields, overrides) {
1246
1531
  if (!Object.keys(overrides).length) return fields;
1247
1532
  return fields.map((field) => {
1248
1533
  const override = overrides[field.name];
1249
- if (!override) return field;
1250
- const { options, label, ...metaOverrides } = override;
1251
- return {
1252
- ...field,
1253
- ...label !== void 0 ? { label } : {},
1254
- ...options !== void 0 ? { options } : {},
1255
- meta: { ...field.meta, ...metaOverrides }
1256
- };
1534
+ let updated = field;
1535
+ if (override) {
1536
+ const { options, label, ...metaOverrides } = override;
1537
+ updated = {
1538
+ ...field,
1539
+ ...label !== void 0 ? { label } : {},
1540
+ ...options !== void 0 ? { options } : {},
1541
+ meta: { ...field.meta, ...metaOverrides }
1542
+ };
1543
+ }
1544
+ if (updated.type === "array") {
1545
+ const prefix = `${updated.name}.`;
1546
+ let rowDynamicMeta;
1547
+ for (const [key, value] of Object.entries(overrides)) {
1548
+ if (!key.startsWith(prefix)) continue;
1549
+ const match = ROW_KEY_PATTERN.exec(key);
1550
+ if (!match) continue;
1551
+ const [, matchedArrayName, indexStr, childField] = match;
1552
+ if (matchedArrayName !== updated.name) continue;
1553
+ const rowIndex = Number(indexStr);
1554
+ if (!rowDynamicMeta) rowDynamicMeta = {};
1555
+ if (!rowDynamicMeta[rowIndex]) rowDynamicMeta[rowIndex] = {};
1556
+ rowDynamicMeta[rowIndex][childField] = value;
1557
+ }
1558
+ if (rowDynamicMeta) {
1559
+ updated = {
1560
+ ...updated,
1561
+ _rowDynamicMeta: rowDynamicMeta
1562
+ };
1563
+ }
1564
+ }
1565
+ return updated;
1257
1566
  });
1258
1567
  }
1259
1568
  function buildDefaults(fields) {
@@ -1571,7 +1880,8 @@ function AutoForm(props) {
1571
1880
  messages,
1572
1881
  labels,
1573
1882
  formMethods,
1574
- control
1883
+ control,
1884
+ setDynamicMeta
1575
1885
  }),
1576
1886
  [
1577
1887
  registry,
@@ -1585,7 +1895,8 @@ function AutoForm(props) {
1585
1895
  messages,
1586
1896
  labels,
1587
1897
  formMethods,
1588
- control
1898
+ control,
1899
+ setDynamicMeta
1589
1900
  ]
1590
1901
  );
1591
1902
  if (isLoadingDefaults) {
@@ -1702,6 +2013,10 @@ var UniForm = class {
1702
2013
  * Replaces any previously registered handler for that field — only one
1703
2014
  * handler per field is kept. This prevents accidental handler accumulation
1704
2015
  * when called inside a React render cycle.
2016
+ *
2017
+ * Supports both generic array paths (`"tasks.priority"` — fires for all rows)
2018
+ * and indexed paths (`"tasks.0.priority"` — fires only for row 0).
2019
+ *
1705
2020
  * Returns `this` for fluent chaining.
1706
2021
  */
1707
2022
  setOnChange(field, handler) {