@strictly/react-form 0.0.26 → 0.0.28

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) {
@@ -526,7 +530,9 @@ var FormModel = class {
526
530
  value: displayedValue,
527
531
  error,
528
532
  readonly: readonly && !this.forceMutableFields,
529
- required
533
+ required,
534
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
535
+ listIndexToKey: this.listIndicesToKeys[valuePath]
530
536
  };
531
537
  }
532
538
  getAccessorForValuePath(valuePath) {
@@ -539,7 +545,8 @@ var FormModel = class {
539
545
  this.value,
540
546
  (value) => {
541
547
  this.value = mobxCopy(this.type, value);
542
- }
548
+ },
549
+ this.listIndicesToKeys
543
550
  );
544
551
  }
545
552
  maybeGetAdapterForValuePath(valuePath) {
@@ -558,6 +565,9 @@ var FormModel = class {
558
565
  return this.isFieldDirty(valuePath);
559
566
  });
560
567
  }
568
+ get valueChanged() {
569
+ return !equals(this.type, this.value, this.originalValue);
570
+ }
561
571
  typePath(valuePath) {
562
572
  return valuePathToTypePath(this.type, valuePath, true);
563
573
  }
@@ -589,110 +599,47 @@ var FormModel = class {
589
599
  element,
590
600
  ...originalList.slice(definedIndex)
591
601
  ];
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
602
  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
603
  accessor.set(newList);
629
604
  delete this.fieldOverrides[listValuePath];
605
+ const indicesToKeys = assertExistsAndReturn(
606
+ this.listIndicesToKeys[listValuePath],
607
+ "no index to key mapping for list {}",
608
+ listValuePath
609
+ );
610
+ const nextKey = indicesToKeys[indicesToKeys.length - 1];
611
+ indicesToKeys.splice(definedIndex, 0, nextKey);
612
+ indicesToKeys[indicesToKeys.length - 1] = nextKey + 1;
630
613
  });
631
614
  }
632
615
  removeListItem(...elementValuePaths) {
633
- const orderedElementValuePaths = elementValuePaths.toSorted().reverse();
634
616
  runInAction(() => {
635
- orderedElementValuePaths.forEach((elementValuePath) => {
617
+ elementValuePaths.forEach((elementValuePath) => {
618
+ var _a;
636
619
  const [
637
620
  listValuePath,
638
- elementIndexString
621
+ elementKeyString
639
622
  ] = assertExistsAndReturn(
640
623
  jsonPathPop(elementValuePath),
641
624
  "expected a path with two or more segments {}",
642
625
  elementValuePath
643
626
  );
644
627
  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,
628
+ const elementKey = checkValidNumber(
629
+ parseInt(elementKeyString),
630
+ "unexpected id {} ({})",
631
+ elementKeyString,
656
632
  elementValuePath
657
633
  );
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
- });
694
- accessor.set(newList);
695
- delete this.fieldOverrides[listValuePath];
634
+ const indicesToKeys = this.listIndicesToKeys[listValuePath];
635
+ const elementIndex = (_a = indicesToKeys == null ? void 0 : indicesToKeys.indexOf(elementKey)) != null ? _a : -1;
636
+ if (elementIndex >= 0) {
637
+ const newList = [...accessor.value];
638
+ newList.splice(elementIndex, 1);
639
+ accessor.set(newList);
640
+ delete this.fieldOverrides[listValuePath];
641
+ indicesToKeys.splice(elementIndex, 1);
642
+ }
696
643
  });
697
644
  });
698
645
  }
@@ -775,7 +722,7 @@ var FormModel = class {
775
722
  });
776
723
  }
777
724
  isValuePathActive(valuePath) {
778
- const values = flattenValuesOfType(this.type, this.value);
725
+ const values = flattenValuesOfType(this.type, this.value, this.listIndicesToKeys);
779
726
  const keys = new Set(Object.keys(values));
780
727
  return keys.has(valuePath);
781
728
  }
@@ -855,6 +802,7 @@ __decorateElement(_init, 2, "fields", _fields_dec, FormModel);
855
802
  __decorateElement(_init, 2, "knownFields", _knownFields_dec, FormModel);
856
803
  __decorateElement(_init, 2, "accessors", _accessors_dec, FormModel);
857
804
  __decorateElement(_init, 2, "dirty", _dirty_dec, FormModel);
805
+ __decorateElement(_init, 2, "valueChanged", _valueChanged_dec, FormModel);
858
806
  __decoratorMetadata(_init, FormModel);
859
807
 
860
808
  // core/mobx/hooks.tsx
@@ -887,7 +835,7 @@ function useDefaultMobxFormHooks(model, {
887
835
  const onFieldBlur = useCallback(
888
836
  function(path) {
889
837
  setTimeout(function() {
890
- if (model.isValuePathActive(path) && model.isFieldDirty(path)) {
838
+ if (model.isValuePathActive(path) && model.isFieldDirty(path) && model.fields[path].error == null) {
891
839
  model.validateField(path, Math.max(1 /* Changed */, model.getValidation(path)));
892
840
  }
893
841
  }, 100);
@@ -1606,27 +1554,49 @@ function createForm(valuePath, Form, observableProps) {
1606
1554
  }
1607
1555
 
1608
1556
  // mantine/create_list.tsx
1557
+ import {
1558
+ assertExistsAndReturn as assertExistsAndReturn2
1559
+ } from "@strictly/base";
1609
1560
  import {
1610
1561
  Fragment
1611
1562
  } from "react";
1612
1563
  import { Fragment as Fragment2, jsx as jsx6 } from "react/jsx-runtime";
1613
1564
  function createList(valuePath, List) {
1614
1565
  const propSource = () => {
1615
- const values = [...this.fields[valuePath].value];
1566
+ const field = this.fields[valuePath];
1567
+ const values = [...field.value];
1616
1568
  return {
1617
1569
  values,
1618
- listPath: valuePath
1570
+ listPath: valuePath,
1571
+ indexKeys: assertExistsAndReturn2(field.listIndexToKey, "list index to key mapping missing in {}", valuePath)
1619
1572
  };
1620
1573
  };
1621
1574
  return createUnsafePartialObserverComponent(List, propSource);
1622
1575
  }
1623
1576
  function DefaultList({
1624
1577
  values,
1578
+ indexKeys,
1625
1579
  listPath,
1626
1580
  children
1627
1581
  }) {
1628
1582
  return /* @__PURE__ */ jsx6(Fragment2, { children: values.map(function(value, index) {
1629
- const valuePath = `${listPath}.${index}`;
1583
+ return [
1584
+ value,
1585
+ index,
1586
+ indexKeys[index]
1587
+ ];
1588
+ }).filter(function([
1589
+ _value2,
1590
+ _index,
1591
+ key
1592
+ ]) {
1593
+ return key != null;
1594
+ }).map(function([
1595
+ value,
1596
+ index,
1597
+ key
1598
+ ]) {
1599
+ const valuePath = `${listPath}.${key}`;
1630
1600
  return /* @__PURE__ */ jsx6(Fragment, { children: children(valuePath, value, index) }, valuePath);
1631
1601
  }) });
1632
1602
  }
@@ -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.26",
75
+ "version": "0.0.28",
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>>