@homebound/form-state 2.24.0 → 2.25.1
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/deepEquals.d.ts +7 -0
- package/dist/fields/deepEquals.js +42 -0
- package/dist/formState.stories.d.ts +1 -1
- package/dist/formState.test.js +16 -0
- package/dist/useFormState.js +6 -1
- package/dist/useFormStates.js +7 -2
- package/dist/utils.js +4 -7
- package/package.json +16 -17
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Our own `deepEquals` because either `fast-deep-equals` or `dequal` or etc actually
|
|
3
|
+
* handle cyclic data structures, despite ChatGTP's assertions/hallucinations.
|
|
4
|
+
*
|
|
5
|
+
* Ported from https://github.com/KoryNunn/cyclic-deep-equal which is ISC.
|
|
6
|
+
*/
|
|
7
|
+
export declare function deepEquals(a: any, b: any, visited?: Set<any>): boolean;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deepEquals = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Our own `deepEquals` because either `fast-deep-equals` or `dequal` or etc actually
|
|
6
|
+
* handle cyclic data structures, despite ChatGTP's assertions/hallucinations.
|
|
7
|
+
*
|
|
8
|
+
* Ported from https://github.com/KoryNunn/cyclic-deep-equal which is ISC.
|
|
9
|
+
*/
|
|
10
|
+
function deepEquals(a, b, visited = new Set()) {
|
|
11
|
+
const aType = typeof a;
|
|
12
|
+
if (aType !== typeof b)
|
|
13
|
+
return false;
|
|
14
|
+
if (a == null || b == null || !(aType === "object" || aType === "function")) {
|
|
15
|
+
if (aType === "number" && isNaN(a) && isNaN(b))
|
|
16
|
+
return true;
|
|
17
|
+
return a === b;
|
|
18
|
+
}
|
|
19
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
20
|
+
return false;
|
|
21
|
+
const aKeys = Object.keys(a), bKeys = Object.keys(b);
|
|
22
|
+
if (aKeys.length !== bKeys.length)
|
|
23
|
+
return false;
|
|
24
|
+
let equal = true;
|
|
25
|
+
for (const key of aKeys) {
|
|
26
|
+
if (!(key in b)) {
|
|
27
|
+
equal = false;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
if (a[key] && a[key] instanceof Object) {
|
|
31
|
+
if (visited.has(a[key]))
|
|
32
|
+
break;
|
|
33
|
+
visited.add(a[key]);
|
|
34
|
+
}
|
|
35
|
+
if (!deepEquals(a[key], b[key], visited)) {
|
|
36
|
+
equal = false;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return equal;
|
|
41
|
+
}
|
|
42
|
+
exports.deepEquals = deepEquals;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
declare const _default: import("@storybook/types").ComponentAnnotations<import("@storybook/react/dist/types-
|
|
1
|
+
declare const _default: import("@storybook/types").ComponentAnnotations<import("@storybook/react/dist/types-0fc72a6d").R, import("@storybook/types").Args>;
|
|
2
2
|
export default _default;
|
|
3
3
|
export declare function AppExample(): import("react/jsx-runtime").JSX.Element;
|
package/dist/formState.test.js
CHANGED
|
@@ -1468,6 +1468,22 @@ describe("formState", () => {
|
|
|
1468
1468
|
a.address.set({ street: "123", city: "nyc" });
|
|
1469
1469
|
expect(a.value).toEqual({ address: { street: "123", city: "nyc" } });
|
|
1470
1470
|
});
|
|
1471
|
+
it("can have child object states with cycles", () => {
|
|
1472
|
+
// Given an author with an address child
|
|
1473
|
+
// And two addresses that are basically identical and both have cycles
|
|
1474
|
+
const address1 = { city: "city2" };
|
|
1475
|
+
address1.someCycle = address1;
|
|
1476
|
+
const address2 = { city: "city2" };
|
|
1477
|
+
address2.someCycle = address2;
|
|
1478
|
+
const a = (0, objectField_1.createObjectState)({
|
|
1479
|
+
id: { type: "value" },
|
|
1480
|
+
address: { type: "value" },
|
|
1481
|
+
}, { address: address1 });
|
|
1482
|
+
// When we change the address
|
|
1483
|
+
a.address.set(address2);
|
|
1484
|
+
// Then it doesn't error on dirty checks
|
|
1485
|
+
expect(a.address.dirty).toBe(false);
|
|
1486
|
+
});
|
|
1471
1487
|
it("provides isNewEntity", () => {
|
|
1472
1488
|
// Given an author without an id
|
|
1473
1489
|
const formState = (0, objectField_1.createObjectState)(authorWithAddressConfig, {
|
package/dist/useFormState.js
CHANGED
|
@@ -39,7 +39,12 @@ function useFormState(opts) {
|
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
// Don't use canSave() because we don't want to set touched for all the fields
|
|
42
|
-
if (autoSaveRef.current && form.dirty &&
|
|
42
|
+
if (autoSaveRef.current && form.dirty && !isAutoSaving) {
|
|
43
|
+
// It's very frustrating to not know why the form is savings, to go ahead and log these
|
|
44
|
+
if (!form.valid) {
|
|
45
|
+
console.debug("Skipping auto-save b/c form is invalid: ", form.errors);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
43
48
|
isAutoSaving = "queued";
|
|
44
49
|
let maybeError;
|
|
45
50
|
// We use setTimeout as a cheap way to wait until the end of the current event listener
|
package/dist/useFormStates.js
CHANGED
|
@@ -35,8 +35,13 @@ function useFormStates(opts) {
|
|
|
35
35
|
const existing = objectStateCache[getId(input)];
|
|
36
36
|
let form = existing === null || existing === void 0 ? void 0 : existing[0];
|
|
37
37
|
async function maybeAutoSave(form) {
|
|
38
|
-
// Don't use canSave() because we don't want to set touched for all the fields
|
|
39
|
-
if (autoSaveRef.current && form.dirty
|
|
38
|
+
// Don't use form.canSave() because we don't want to set touched for all the fields
|
|
39
|
+
if (autoSaveRef.current && form.dirty) {
|
|
40
|
+
// It's very frustrating to not know why the form is savings, to go ahead and log these
|
|
41
|
+
if (!form.valid) {
|
|
42
|
+
console.debug("Skipping auto-save b/c form is invalid: ", form.errors);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
40
45
|
const { current: pending } = pendingAutoSaves;
|
|
41
46
|
if (isAutoSaving) {
|
|
42
47
|
pending.add(form);
|
package/dist/utils.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.deepClone = exports.hasToJSON = exports.areEqual = exports.isEmpty = exports.isNotUndefined = exports.pickFields = exports.isQuery = exports.isInput = exports.initValue = exports.assertNever = exports.fail = void 0;
|
|
7
|
-
const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
|
|
8
4
|
const is_plain_object_1 = require("is-plain-object");
|
|
9
5
|
const mobx_1 = require("mobx");
|
|
6
|
+
const deepEquals_1 = require("./fields/deepEquals");
|
|
10
7
|
function fail(message) {
|
|
11
8
|
throw new Error(message || "Failed");
|
|
12
9
|
}
|
|
@@ -108,16 +105,16 @@ exports.isEmpty = isEmpty;
|
|
|
108
105
|
*/
|
|
109
106
|
function areEqual(a, b, strictOrder) {
|
|
110
107
|
if ((0, is_plain_object_1.isPlainObject)(a)) {
|
|
111
|
-
return (0,
|
|
108
|
+
return (0, deepEquals_1.deepEquals)((0, mobx_1.toJS)(a), (0, mobx_1.toJS)(b));
|
|
112
109
|
}
|
|
113
110
|
if (hasToJSON(a) || hasToJSON(b)) {
|
|
114
111
|
const a1 = hasToJSON(a) ? a.toJSON() : a;
|
|
115
112
|
const b1 = hasToJSON(b) ? b.toJSON() : b;
|
|
116
|
-
return (0,
|
|
113
|
+
return (0, deepEquals_1.deepEquals)(a1, b1);
|
|
117
114
|
}
|
|
118
115
|
if (a && b && a instanceof Array && b instanceof Array) {
|
|
119
116
|
if (strictOrder !== false) {
|
|
120
|
-
return (0,
|
|
117
|
+
return (0, deepEquals_1.deepEquals)(a, b);
|
|
121
118
|
}
|
|
122
119
|
if (a.length !== b.length)
|
|
123
120
|
return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homebound/form-state",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.25.1",
|
|
4
4
|
"author": "Homebound",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"src/**/*.{ts,tsx,css,md}": "prettier --write"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"fast-deep-equal": "^3.1.3",
|
|
34
33
|
"is-plain-object": "^5.0.0"
|
|
35
34
|
},
|
|
36
35
|
"peerDependencies": {
|
|
@@ -42,17 +41,17 @@
|
|
|
42
41
|
"@babel/preset-env": "^7.22.15",
|
|
43
42
|
"@babel/preset-react": "^7.22.15",
|
|
44
43
|
"@babel/preset-typescript": "^7.22.15",
|
|
45
|
-
"@homebound/rtl-utils": "2.65.
|
|
44
|
+
"@homebound/rtl-utils": "2.65.2",
|
|
46
45
|
"@semantic-release/exec": "^6.0.3",
|
|
47
46
|
"@semantic-release/git": "^10.0.1",
|
|
48
|
-
"@storybook/addon-essentials": "^7.
|
|
49
|
-
"@storybook/addon-links": "^7.
|
|
50
|
-
"@storybook/addons": "^7.
|
|
51
|
-
"@storybook/react": "^7.
|
|
52
|
-
"@storybook/react-webpack5": "^7.
|
|
53
|
-
"@testing-library/dom": "^9.3.
|
|
54
|
-
"@testing-library/jest-dom": "^6.1.
|
|
55
|
-
"@testing-library/react": "^14.
|
|
47
|
+
"@storybook/addon-essentials": "^7.6.5",
|
|
48
|
+
"@storybook/addon-links": "^7.6.5",
|
|
49
|
+
"@storybook/addons": "^7.6.5",
|
|
50
|
+
"@storybook/react": "^7.6.5",
|
|
51
|
+
"@storybook/react-webpack5": "^7.6.5",
|
|
52
|
+
"@testing-library/dom": "^9.3.3",
|
|
53
|
+
"@testing-library/jest-dom": "^6.1.5",
|
|
54
|
+
"@testing-library/react": "^14.1.2",
|
|
56
55
|
"@tsconfig/recommended": "^1.0.2",
|
|
57
56
|
"@types/jest": "^29.5.4",
|
|
58
57
|
"@types/react": "^18.2.21",
|
|
@@ -71,7 +70,7 @@
|
|
|
71
70
|
"eslint-plugin-react": "^7.22.0",
|
|
72
71
|
"eslint-plugin-react-hooks": "^4.2.0",
|
|
73
72
|
"husky": "^3.1.0",
|
|
74
|
-
"jest": "^29.
|
|
73
|
+
"jest": "^29.7.0",
|
|
75
74
|
"jest-environment-jsdom": "^29.6.4",
|
|
76
75
|
"lint-staged": "^14.0.1",
|
|
77
76
|
"mobx": "^6.10.2",
|
|
@@ -80,13 +79,13 @@
|
|
|
80
79
|
"prettier-plugin-organize-imports": "^3.2.3",
|
|
81
80
|
"react": "^18.2.0",
|
|
82
81
|
"react-dom": "^18.2.0",
|
|
83
|
-
"semantic-release": "^
|
|
84
|
-
"storybook": "^7.
|
|
82
|
+
"semantic-release": "^22.0.12",
|
|
83
|
+
"storybook": "^7.6.5",
|
|
85
84
|
"ts-jest": "^29.1.1",
|
|
86
|
-
"ts-node": "^10.9.
|
|
87
|
-
"ts-patch": "^3.
|
|
85
|
+
"ts-node": "^10.9.2",
|
|
86
|
+
"ts-patch": "^3.1.1",
|
|
88
87
|
"tslib": "^2.6.2",
|
|
89
|
-
"typescript": "^5.
|
|
88
|
+
"typescript": "^5.3.3",
|
|
90
89
|
"typescript-transform-paths": "^3.4.6"
|
|
91
90
|
},
|
|
92
91
|
"packageManager": "yarn@3.6.3"
|