@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.
- package/dist/fields/listField.js +32 -33
- package/dist/fields/objectField.js +4 -4
- package/dist/formState.test.js +19 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/package.json +1 -1
package/dist/fields/listField.js
CHANGED
|
@@ -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
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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, {
|
package/dist/formState.test.js
CHANGED
|
@@ -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");
|