@strictly/react-form 0.0.27 → 0.0.29

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.js CHANGED
@@ -306,7 +306,6 @@ function listAdapter() {
306
306
  import {
307
307
  assertExists,
308
308
  assertExistsAndReturn,
309
- assertState,
310
309
  checkValidNumber,
311
310
  map,
312
311
  toArray,
@@ -333,11 +332,12 @@ var Validation = /* @__PURE__ */ ((Validation2) => {
333
332
  Validation2[Validation2["Always"] = 2] = "Always";
334
333
  return Validation2;
335
334
  })(Validation || {});
336
- var _dirty_dec, _accessors_dec, _knownFields_dec, _fields_dec, _validation_dec, _errorOverrides_dec, _fieldOverrides_dec, _value_dec, _init, _value, _fieldOverrides, _errorOverrides, _validation;
337
- _value_dec = [observable.ref], _fieldOverrides_dec = [observable.shallow], _errorOverrides_dec = [observable.shallow], _validation_dec = [observable.shallow], _fields_dec = [computed], _knownFields_dec = [computed], _accessors_dec = [computed], _dirty_dec = [computed];
335
+ var _valueChanged_dec, _dirty_dec, _accessors_dec, _knownFields_dec, _fields_dec, _validation_dec, _errorOverrides_dec, _fieldOverrides_dec, _value_dec, _init, _value, _fieldOverrides, _errorOverrides, _validation;
336
+ _value_dec = [observable.ref], _fieldOverrides_dec = [observable.shallow], _errorOverrides_dec = [observable.shallow], _validation_dec = [observable.shallow], _fields_dec = [computed], _knownFields_dec = [computed], _accessors_dec = [computed], _dirty_dec = [computed], _valueChanged_dec = [computed];
338
337
  var FormModel = class {
339
338
  constructor(type, originalValue, adapters, mode) {
340
339
  this.type = type;
340
+ this.originalValue = originalValue;
341
341
  this.adapters = adapters;
342
342
  this.mode = mode;
343
343
  __runInitializers(_init, 5, this);
@@ -349,7 +349,9 @@ var FormModel = class {
349
349
  // cannot be type safe
350
350
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
351
351
  __publicField(this, "originalValues");
352
- this.originalValues = flattenValuesOfType(type, originalValue);
352
+ // maintains the value paths of lists when the original order is destroyed by deletes or reordering
353
+ __publicField(this, "listIndicesToKeys", {});
354
+ this.originalValues = flattenValuesOfType(type, originalValue, this.listIndicesToKeys);
353
355
  this.value = mobxCopy(type, originalValue);
354
356
  this.flattenedTypeDefs = flattenTypesOfType(type);
355
357
  const conversions = flattenValueTo(
@@ -371,7 +373,8 @@ var FormModel = class {
371
373
  return;
372
374
  }
373
375
  return convert(fieldValue, valuePath, contextValue);
374
- }
376
+ },
377
+ this.listIndicesToKeys
375
378
  );
376
379
  this.fieldOverrides = map(conversions, function(_k, v) {
377
380
  return v && [v.value];
@@ -417,7 +420,8 @@ var FormModel = class {
417
420
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
418
421
  typePath
419
422
  );
420
- }
423
+ },
424
+ this.listIndicesToKeys
421
425
  );
422
426
  }
423
427
  maybeSynthesizeFieldByValuePath(valuePath) {
@@ -476,7 +480,7 @@ var FormModel = class {
476
480
  };
477
481
  }
478
482
  synthesizeFieldByPaths(valuePath, typePath) {
479
- var _a;
483
+ var _a, _b;
480
484
  const field = this.getField(valuePath, typePath);
481
485
  if (field == null) {
482
486
  return;
@@ -526,7 +530,10 @@ var FormModel = class {
526
530
  value: displayedValue,
527
531
  error,
528
532
  readonly: readonly && !this.forceMutableFields,
529
- required
533
+ required,
534
+ // make a copy of the index mapping and remove the final value (next id)
535
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
536
+ listIndexToKey: (_b = this.listIndicesToKeys[valuePath]) == null ? void 0 : _b.slice(0, -1)
530
537
  };
531
538
  }
532
539
  getAccessorForValuePath(valuePath) {
@@ -539,7 +546,8 @@ var FormModel = class {
539
546
  this.value,
540
547
  (value) => {
541
548
  this.value = mobxCopy(this.type, value);
542
- }
549
+ },
550
+ this.listIndicesToKeys
543
551
  );
544
552
  }
545
553
  maybeGetAdapterForValuePath(valuePath) {
@@ -558,6 +566,9 @@ var FormModel = class {
558
566
  return this.isFieldDirty(valuePath);
559
567
  });
560
568
  }
569
+ get valueChanged() {
570
+ return !equals(this.type, this.value, this.originalValue);
571
+ }
561
572
  typePath(valuePath) {
562
573
  return valuePathToTypePath(this.type, valuePath, true);
563
574
  }
@@ -589,118 +600,50 @@ var FormModel = class {
589
600
  element,
590
601
  ...originalList.slice(definedIndex)
591
602
  ];
592
- const targetPaths = Object.keys(this.fieldOverrides).filter(function(v) {
593
- return v.startsWith(`${listValuePath}.`);
594
- }).map(function(v) {
595
- const parts = v.substring(listValuePath.length + 1).split(".");
596
- const index2 = parseInt(parts[0]);
597
- return [
598
- index2,
599
- parts.slice(1)
600
- ];
601
- }).filter(function([index2]) {
602
- return index2 >= definedIndex;
603
- }).sort(function([a], [b]) {
604
- return b - a;
605
- });
606
603
  runInAction(() => {
607
- targetPaths.forEach(([
608
- index2,
609
- postfix
610
- ]) => {
611
- const fromJsonPath = [
612
- listValuePath,
613
- `${index2}`,
614
- ...postfix
615
- ].join(".");
616
- const toJsonPath = [
617
- listValuePath,
618
- `${index2 + 1}`,
619
- ...postfix
620
- ].join(".");
621
- const fieldOverride = this.fieldOverrides[fromJsonPath];
622
- delete this.fieldOverrides[fromJsonPath];
623
- this.fieldOverrides[toJsonPath] = fieldOverride;
624
- const validation = this.validation[fromJsonPath];
625
- delete this.validation[fromJsonPath];
626
- this.validation[toJsonPath] = validation;
627
- });
628
604
  accessor.set(newList);
629
605
  delete this.fieldOverrides[listValuePath];
606
+ const indicesToKeys = assertExistsAndReturn(
607
+ this.listIndicesToKeys[listValuePath],
608
+ "no index to key mapping for list {}",
609
+ listValuePath
610
+ );
611
+ const nextKey = indicesToKeys[indicesToKeys.length - 1];
612
+ indicesToKeys.splice(definedIndex, 0, nextKey);
613
+ indicesToKeys[indicesToKeys.length - 1] = nextKey + 1;
630
614
  });
631
615
  }
632
616
  removeListItem(...elementValuePaths) {
633
- const orderedElementValuePaths = elementValuePaths.toSorted().reverse();
634
617
  runInAction(() => {
635
- orderedElementValuePaths.forEach((elementValuePath) => {
618
+ elementValuePaths.forEach((elementValuePath) => {
619
+ var _a;
636
620
  const [
637
621
  listValuePath,
638
- elementIndexString
622
+ elementKeyString
639
623
  ] = assertExistsAndReturn(
640
624
  jsonPathPop(elementValuePath),
641
625
  "expected a path with two or more segments {}",
642
626
  elementValuePath
643
627
  );
644
628
  const accessor = this.accessors[listValuePath];
645
- const elementIndex = checkValidNumber(
646
- parseInt(elementIndexString),
647
- "unexpected index {} ({})",
648
- elementIndexString,
649
- elementValuePath
650
- );
651
- const newList = [...accessor.value];
652
- assertState(
653
- elementIndex >= 0 && elementIndex < newList.length,
654
- "invalid index from path {} ({})",
655
- elementIndex,
629
+ const elementKey = checkValidNumber(
630
+ parseInt(elementKeyString),
631
+ "unexpected id {} ({})",
632
+ elementKeyString,
656
633
  elementValuePath
657
634
  );
658
- newList.splice(elementIndex, 1);
659
- const targetPaths = Object.keys(this.fieldOverrides).filter(function(v) {
660
- return v.startsWith(`${listValuePath}.`);
661
- }).map(function(v) {
662
- const parts = v.substring(listValuePath.length + 1).split(".");
663
- const index = parseInt(parts[0]);
664
- return [
665
- index,
666
- parts.slice(1)
667
- ];
668
- }).filter(function([index]) {
669
- return index > elementIndex;
670
- }).sort(function([a], [b]) {
671
- return a - b;
672
- });
673
- targetPaths.forEach(([
674
- index,
675
- postfix
676
- ]) => {
677
- const fromJsonPath = [
678
- listValuePath,
679
- `${index}`,
680
- ...postfix
681
- ].join(".");
682
- const toJsonPath = [
683
- listValuePath,
684
- `${index - 1}`,
685
- ...postfix
686
- ].join(".");
687
- const fieldOverride = this.fieldOverrides[fromJsonPath];
688
- delete this.fieldOverrides[fromJsonPath];
689
- this.fieldOverrides[toJsonPath] = fieldOverride;
690
- const validation = this.validation[fromJsonPath];
691
- delete this.validation[fromJsonPath];
692
- this.validation[toJsonPath] = validation;
693
- this.moveListItem(fromJsonPath, toJsonPath);
694
- });
695
- accessor.set(newList);
696
- delete this.fieldOverrides[listValuePath];
635
+ const indicesToKeys = this.listIndicesToKeys[listValuePath];
636
+ const elementIndex = (_a = indicesToKeys == null ? void 0 : indicesToKeys.indexOf(elementKey)) != null ? _a : -1;
637
+ if (elementIndex >= 0) {
638
+ const newList = [...accessor.value];
639
+ newList.splice(elementIndex, 1);
640
+ accessor.set(newList);
641
+ delete this.fieldOverrides[listValuePath];
642
+ indicesToKeys.splice(elementIndex, 1);
643
+ }
697
644
  });
698
645
  });
699
646
  }
700
- moveListItem(fromValuePath, toValuePath) {
701
- fromValuePath;
702
- toValuePath;
703
- }
704
647
  internalSetFieldValue(valuePath, value, validation) {
705
648
  const { revert } = this.getAdapterForValuePath(valuePath);
706
649
  assertExists(revert, "setting value not supported {}", valuePath);
@@ -780,7 +723,7 @@ var FormModel = class {
780
723
  });
781
724
  }
782
725
  isValuePathActive(valuePath) {
783
- const values = flattenValuesOfType(this.type, this.value);
726
+ const values = flattenValuesOfType(this.type, this.value, this.listIndicesToKeys);
784
727
  const keys = new Set(Object.keys(values));
785
728
  return keys.has(valuePath);
786
729
  }
@@ -860,6 +803,7 @@ __decorateElement(_init, 2, "fields", _fields_dec, FormModel);
860
803
  __decorateElement(_init, 2, "knownFields", _knownFields_dec, FormModel);
861
804
  __decorateElement(_init, 2, "accessors", _accessors_dec, FormModel);
862
805
  __decorateElement(_init, 2, "dirty", _dirty_dec, FormModel);
806
+ __decorateElement(_init, 2, "valueChanged", _valueChanged_dec, FormModel);
863
807
  __decoratorMetadata(_init, FormModel);
864
808
 
865
809
  // core/mobx/hooks.tsx
@@ -1611,27 +1555,49 @@ function createForm(valuePath, Form, observableProps) {
1611
1555
  }
1612
1556
 
1613
1557
  // mantine/create_list.tsx
1558
+ import {
1559
+ assertExistsAndReturn as assertExistsAndReturn2
1560
+ } from "@strictly/base";
1614
1561
  import {
1615
1562
  Fragment
1616
1563
  } from "react";
1617
1564
  import { Fragment as Fragment2, jsx as jsx6 } from "react/jsx-runtime";
1618
1565
  function createList(valuePath, List) {
1619
1566
  const propSource = () => {
1620
- const values = [...this.fields[valuePath].value];
1567
+ const field = this.fields[valuePath];
1568
+ const values = [...field.value];
1621
1569
  return {
1622
1570
  values,
1623
- listPath: valuePath
1571
+ listPath: valuePath,
1572
+ indexKeys: assertExistsAndReturn2(field.listIndexToKey, "list index to key mapping missing in {}", valuePath)
1624
1573
  };
1625
1574
  };
1626
1575
  return createUnsafePartialObserverComponent(List, propSource);
1627
1576
  }
1628
1577
  function DefaultList({
1629
1578
  values,
1579
+ indexKeys,
1630
1580
  listPath,
1631
1581
  children
1632
1582
  }) {
1633
1583
  return /* @__PURE__ */ jsx6(Fragment2, { children: values.map(function(value, index) {
1634
- const valuePath = `${listPath}.${index}`;
1584
+ return [
1585
+ value,
1586
+ index,
1587
+ indexKeys[index]
1588
+ ];
1589
+ }).filter(function([
1590
+ _value2,
1591
+ _index,
1592
+ key
1593
+ ]) {
1594
+ return key != null;
1595
+ }).map(function([
1596
+ value,
1597
+ index,
1598
+ key
1599
+ ]) {
1600
+ const valuePath = `${listPath}.${key}`;
1635
1601
  return /* @__PURE__ */ jsx6(Fragment, { children: children(valuePath, value, index) }, valuePath);
1636
1602
  }) });
1637
1603
  }
@@ -1,4 +1,7 @@
1
- import { type ElementOfArray } from '@strictly/base'
1
+ import {
2
+ assertExistsAndReturn,
3
+ type ElementOfArray,
4
+ } from '@strictly/base'
2
5
  import {
3
6
  type ComponentType,
4
7
  Fragment,
@@ -15,6 +18,7 @@ import {
15
18
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
19
  export type SuppliedListProps<Value = any, ListPath extends string = string> = {
17
20
  values: readonly Value[],
21
+ indexKeys: number[],
18
22
  listPath: ListPath,
19
23
  }
20
24
 
@@ -34,10 +38,12 @@ export function createList<
34
38
  List: ComponentType<Props>,
35
39
  ): MantineFieldComponent<SuppliedListProps<ElementOfArray<ValueTypeOfField<F[K]>>>, Props, never> {
36
40
  const propSource = () => {
37
- const values = [...this.fields[valuePath].value]
41
+ const field = this.fields[valuePath]
42
+ const values = [...field.value]
38
43
  return {
39
44
  values,
40
45
  listPath: valuePath,
46
+ indexKeys: assertExistsAndReturn(field.listIndexToKey, 'list index to key mapping missing in {}', valuePath),
41
47
  }
42
48
  }
43
49
  return createUnsafePartialObserverComponent(List, propSource)
@@ -48,6 +54,7 @@ export function DefaultList<
48
54
  ListPath extends string,
49
55
  >({
50
56
  values,
57
+ indexKeys,
51
58
  listPath,
52
59
  children,
53
60
  }: SuppliedListProps<Value, ListPath> & {
@@ -56,7 +63,24 @@ export function DefaultList<
56
63
  return (
57
64
  <>
58
65
  {values.map(function (value, index) {
59
- const valuePath: `${ListPath}.${number}` = `${listPath}.${index}`
66
+ return [
67
+ value,
68
+ index,
69
+ indexKeys[index],
70
+ ] as const
71
+ }).filter(function ([
72
+ _value,
73
+ _index,
74
+ key,
75
+ ]) {
76
+ // omit entries without keys
77
+ return key != null
78
+ }).map(function ([
79
+ value,
80
+ index,
81
+ key,
82
+ ]) {
83
+ const valuePath: `${ListPath}.${number}` = `${listPath}.${key}`
60
84
  return (
61
85
  <Fragment key={valuePath}>
62
86
  {children(valuePath, value, index)}
@@ -40,7 +40,7 @@ exports[`mantine list hooks > renders Populated 1`] = `
40
40
  >
41
41
  <span>
42
42
  ValuePath:
43
- $.0
43
+ $.100
44
44
  </span>
45
45
  <br />
46
46
  <span>
@@ -78,7 +78,7 @@ exports[`mantine list hooks > renders Populated 1`] = `
78
78
  >
79
79
  <span>
80
80
  ValuePath:
81
- $.2
81
+ $.33
82
82
  </span>
83
83
  <br />
84
84
  <span>
@@ -97,7 +97,7 @@ exports[`mantine list hooks > renders Populated 1`] = `
97
97
  >
98
98
  <span>
99
99
  ValuePath:
100
- $.3
100
+ $.5
101
101
  </span>
102
102
  <br />
103
103
  <span>
@@ -70,6 +70,7 @@ export const Empty: Story = {
70
70
  readonly: false,
71
71
  required: false,
72
72
  value: [],
73
+ listIndexToKey: [0],
73
74
  },
74
75
  },
75
76
  },
@@ -87,6 +88,13 @@ export const Populated: Story = {
87
88
  'C',
88
89
  'D',
89
90
  ],
91
+ listIndexToKey: [
92
+ 100,
93
+ 1,
94
+ 33,
95
+ 5,
96
+ 101,
97
+ ],
90
98
  },
91
99
  },
92
100
  },
package/package.json CHANGED
@@ -72,7 +72,7 @@
72
72
  "test:watch": "vitest"
73
73
  },
74
74
  "type": "module",
75
- "version": "0.0.27",
75
+ "version": "0.0.29",
76
76
  "exports": {
77
77
  ".": {
78
78
  "import": {
package/types/field.ts CHANGED
@@ -4,6 +4,7 @@ export type Field<V = any, E = any> = {
4
4
  readonly error?: E | undefined,
5
5
  readonly readonly: boolean,
6
6
  readonly required: boolean,
7
+ readonly listIndexToKey?: number[],
7
8
  }
8
9
 
9
10
  export type Fields = Readonly<Record<string, Field>>