@homebound/form-state 2.22.1 → 2.22.2

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.
@@ -19,6 +19,31 @@ function newListFieldState(parentCopy, parentInstance, parentState, key, rules,
19
19
  ((_a = parentCopy[key]) !== null && _a !== void 0 ? _a : []).forEach((copy, i) => {
20
20
  copyMap.set(copy, parentInstance[key][i]);
21
21
  });
22
+ // Given a child POJO (or a copy/clone of a child POJO), return its ObjectState wrapper.
23
+ function getOrCreateChildState(child, opts) {
24
+ let childState = rowMap.get(child);
25
+ // If we're being reverted to our originalValue, i.e. values is actually
26
+ // a list of copies, use the copyMap to recover the non-copy original value
27
+ if (!childState && copyMap.has(child)) {
28
+ childState = rowMap.get(copyMap.get(child));
29
+ }
30
+ // Look for an existing child (requires having an id key configured)
31
+ if (!childState) {
32
+ for (const [, otherState] of rowMap.entries()) {
33
+ if (otherState.isSameEntity(child)) {
34
+ otherState.set(child, opts);
35
+ rowMap.set(child, otherState);
36
+ return otherState;
37
+ }
38
+ }
39
+ }
40
+ // If we didn't have an existing child, just make a new object state
41
+ if (!childState) {
42
+ childState = (0, objectField_1.newObjectState)(config, parentState, list, child, undefined, maybeAutoSave);
43
+ rowMap.set(child, childState);
44
+ }
45
+ return childState;
46
+ }
22
47
  const list = {
23
48
  key: key,
24
49
  // Our fundamental state of wrapped Us
@@ -75,15 +100,7 @@ function newListFieldState(parentCopy, parentInstance, parentState, key, rules,
75
100
  (0, utils_1.fail)();
76
101
  // Avoid using `this.value` to avoid registering `_childTick` as a dependency
77
102
  const value = parentInstance[key];
78
- return (value || []).map((child) => {
79
- // Because we're reading from this.value, child will be the proxy version
80
- let childState = rowMap.get(child);
81
- if (!childState) {
82
- childState = (0, objectField_1.newObjectState)(config, parentState, list, child, undefined, maybeAutoSave);
83
- rowMap.set(child, childState);
84
- }
85
- return childState;
86
- });
103
+ return (value || []).map((child) => getOrCreateChildState(child));
87
104
  },
88
105
  // TODO Should this be true when all rows are touched?
89
106
  get touched() {
@@ -144,30 +161,12 @@ function newListFieldState(parentCopy, parentInstance, parentState, key, rules,
144
161
  if (this.readOnly && !opts.resetting && !opts.refreshing) {
145
162
  throw new Error(`${String(key)} is currently readOnly`);
146
163
  }
147
- // We should be passed values that are non-proxies.
148
- parentInstance[key] = values.map((value) => {
149
- let childState = rowMap.get(value);
150
- // If we're being reverted to our originalValue, i.e. values is actually
151
- // a list of copies, use the copyMap to recover our child states
152
- if (!childState && copyMap.has(value)) {
153
- childState = rowMap.get(copyMap.get(value));
154
- }
155
- if (!childState) {
156
- // Look for an existing child (requires having an id key configured)
157
- for (const [, otherState] of rowMap.entries()) {
158
- if (otherState.isSameEntity(value)) {
159
- otherState.set(value, opts);
160
- rowMap.set(value, otherState);
161
- return otherState.value;
162
- }
163
- }
164
- // If we didn't have an existing child, just make a new object state
165
- childState = (0, objectField_1.createObjectState)(config, value, { maybeAutoSave });
166
- rowMap.set(value, childState);
167
- }
168
- // Return the already-observable'd value so that our `parent.value[key] = values` doesn't re-proxy things
169
- return childState.value;
170
- });
164
+ // Given the values, see if we've got existing child states, and use their
165
+ // value if so. I.e. this covers revertChanges doing `set(originalValue)` and
166
+ // passing us cloned rows from the parentCopy, but `getOrCreateChildState` will
167
+ // use the `copyMap` to recover the non-cloned rows, to avoid promoting the clone
168
+ // into a real row.
169
+ parentInstance[key] = values.map((child) => getOrCreateChildState(child, opts).value);
171
170
  // Make sure to tick first so that `setOriginalValue` sees the latest `rows`
172
171
  _tick.value++;
173
172
  // Reset originalCopy so that our dirty checks have the right # of rows.
@@ -188,12 +188,12 @@ function newObjectState(config, parentState, parentListState, instance, key, may
188
188
  // An internal helper method to see if `other` is for "the same entity" as our current row
189
189
  isSameEntity(other) {
190
190
  const idField = getFields(this).find((f) => f._isIdKey);
191
- if (!idField) {
191
+ if (!idField)
192
192
  return false;
193
- }
194
- // If the otherIdValue is undefined, it's a new entity so can't be the same as us
193
+ const ourIdValue = idField.value;
195
194
  const otherIdValue = other[idField.key];
196
- return otherIdValue !== undefined && otherIdValue === this[idField.key].value;
195
+ // If the otherIdValue is undefined, it's a new entity so can't be the same as us
196
+ return otherIdValue !== undefined && otherIdValue === ourIdValue;
197
197
  },
198
198
  };
199
199
  proxy = (0, mobx_1.makeAutoObservable)(obj, {
@@ -1526,6 +1526,25 @@ describe("formState", () => {
1526
1526
  expect(a.id._isIdKey).toBe(false);
1527
1527
  expect(a.otherId._isIdKey).toBe(true);
1528
1528
  });
1529
+ it("uses id key to recognize same entities", () => {
1530
+ // With b1 having an id
1531
+ const b1 = (0, objectField_1.createObjectState)({ id: { type: "value" } }, { id: "b:1" });
1532
+ expect(b1.isSameEntity({ id: "b:1" })).toBe(true);
1533
+ expect(b1.isSameEntity({ id: "b:2" })).toBe(false);
1534
+ expect(b1.isSameEntity({ id: undefined })).toBe(false);
1535
+ expect(b1.isSameEntity({})).toBe(false);
1536
+ // With b2 having no id
1537
+ const b2 = (0, objectField_1.createObjectState)({ id: { type: "value" } }, {});
1538
+ expect(b2.isSameEntity({ id: "b:1" })).toBe(false);
1539
+ expect(b2.isSameEntity({ id: undefined })).toBe(false);
1540
+ expect(b2.isSameEntity({})).toBe(false);
1541
+ // With b3 having no id key
1542
+ const b3 = (0, objectField_1.createObjectState)({ title: { type: "value" } }, { title: "b3" });
1543
+ expect(b3.isSameEntity({ title: "b3" })).toBe(false);
1544
+ // With b4 having a different id key
1545
+ const b4 = (0, objectField_1.createObjectState)({ title: { type: "value", isIdKey: true } }, { title: "b4" });
1546
+ expect(b4.isSameEntity({ title: "b4" })).toBe(true);
1547
+ });
1529
1548
  });
1530
1549
  class ObservableObject {
1531
1550
  constructor() {
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export { ObjectConfig } from "./config";
2
2
  export { f } from "./configBuilders";
3
+ export { FragmentField } from "./fields/fragmentField";
3
4
  export { ListFieldState } from "./fields/listField";
4
- export { ObjectState, createObjectState } from "./fields/objectField";
5
+ export { Fragment, ObjectState, createObjectState, fragment } from "./fields/objectField";
5
6
  export { FieldState, ValueAdapter } from "./fields/valueField";
6
7
  export { Rule, required } from "./rules";
7
8
  export { useFormState } from "./useFormState";
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useFormStates = exports.useFormState = exports.required = exports.createObjectState = exports.f = void 0;
3
+ exports.useFormStates = exports.useFormState = exports.required = exports.fragment = exports.createObjectState = exports.f = void 0;
4
4
  var configBuilders_1 = require("./configBuilders");
5
5
  Object.defineProperty(exports, "f", { enumerable: true, get: function () { return configBuilders_1.f; } });
6
6
  var objectField_1 = require("./fields/objectField");
7
7
  Object.defineProperty(exports, "createObjectState", { enumerable: true, get: function () { return objectField_1.createObjectState; } });
8
+ Object.defineProperty(exports, "fragment", { enumerable: true, get: function () { return objectField_1.fragment; } });
8
9
  var rules_1 = require("./rules");
9
10
  Object.defineProperty(exports, "required", { enumerable: true, get: function () { return rules_1.required; } });
10
11
  var useFormState_1 = require("./useFormState");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homebound/form-state",
3
- "version": "2.22.1",
3
+ "version": "2.22.2",
4
4
  "author": "Homebound",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",