@homebound/form-state 2.32.0 → 3.0.0
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.d.ts +437 -9
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1109 -14
- package/dist/index.js.map +1 -0
- package/package.json +40 -43
- package/dist/FormStateApp.d.ts +0 -5
- package/dist/FormStateApp.js +0 -54
- package/dist/FormStateApp.test.d.ts +0 -1
- package/dist/FormStateApp.test.js +0 -26
- package/dist/config.d.ts +0 -78
- package/dist/config.js +0 -2
- package/dist/configBuilders.d.ts +0 -67
- package/dist/configBuilders.js +0 -115
- package/dist/configBuilders.test.d.ts +0 -1
- package/dist/configBuilders.test.js +0 -65
- package/dist/fields/deepEquals.d.ts +0 -7
- package/dist/fields/deepEquals.js +0 -53
- package/dist/fields/fragmentField.d.ts +0 -4
- package/dist/fields/fragmentField.js +0 -58
- package/dist/fields/listField.d.ts +0 -11
- package/dist/fields/listField.js +0 -355
- package/dist/fields/objectField.d.ts +0 -63
- package/dist/fields/objectField.js +0 -277
- package/dist/fields/valueField.d.ts +0 -81
- package/dist/fields/valueField.js +0 -244
- package/dist/formState.stories.d.ts +0 -4
- package/dist/formState.stories.js +0 -12
- package/dist/formState.test.d.ts +0 -11
- package/dist/formState.test.js +0 -2109
- package/dist/formStateDomain.d.ts +0 -47
- package/dist/formStateDomain.js +0 -28
- package/dist/mobx-behavior.test.d.ts +0 -1
- package/dist/mobx-behavior.test.js +0 -51
- package/dist/proxies.d.ts +0 -5
- package/dist/proxies.js +0 -20
- package/dist/rules.d.ts +0 -10
- package/dist/rules.js +0 -11
- package/dist/setupTests.d.ts +0 -1
- package/dist/setupTests.js +0 -13
- package/dist/stackoverlfow.d.ts +0 -17
- package/dist/stackoverlfow.js +0 -17
- package/dist/temporal.d.ts +0 -6
- package/dist/temporal.js +0 -34
- package/dist/temporal.test.d.ts +0 -1
- package/dist/temporal.test.js +0 -97
- package/dist/useFormState.d.ts +0 -82
- package/dist/useFormState.js +0 -158
- package/dist/useFormState.test.d.ts +0 -1
- package/dist/useFormState.test.js +0 -787
- package/dist/useFormStates.d.ts +0 -68
- package/dist/useFormStates.js +0 -101
- package/dist/useFormStates.test.d.ts +0 -1
- package/dist/useFormStates.test.js +0 -223
- package/dist/utils.d.ts +0 -52
- package/dist/utils.js +0 -198
- package/dist/utils.test.d.ts +0 -1
- package/dist/utils.test.js +0 -102
package/dist/fields/listField.js
DELETED
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.newListFieldState = newListFieldState;
|
|
7
|
-
const mobx_1 = require("mobx");
|
|
8
|
-
const objectField_1 = require("./objectField");
|
|
9
|
-
const rules_1 = require("../rules");
|
|
10
|
-
const utils_1 = require("../utils");
|
|
11
|
-
const object_hash_1 = __importDefault(require("object-hash"));
|
|
12
|
-
function newListFieldState(
|
|
13
|
-
// parentCopy is objectField's deepClone-d `originalCopy` that it/we use for `dirty` checking
|
|
14
|
-
parentCopy,
|
|
15
|
-
// parentInstance is objectField's `instance`, that we're currently mutating
|
|
16
|
-
parentInstance, parentState, key, rules, listConfig, config, strictOrder, maybeAutoSave, deepExhaustive) {
|
|
17
|
-
var _a;
|
|
18
|
-
// Keep a map of "item in the parentInstance list" -> "that item's ObjectState"
|
|
19
|
-
const rowMap = new Map();
|
|
20
|
-
const addedRows = new Set();
|
|
21
|
-
const _tick = (0, mobx_1.observable)({ value: 1 });
|
|
22
|
-
const _originalValueTick = (0, mobx_1.observable)({ value: 1 });
|
|
23
|
-
const _childTick = (0, mobx_1.observable)({ value: 1 });
|
|
24
|
-
// When child rows don't have ids (i.e. for new rows that aren't saved yet), we need an id-less
|
|
25
|
-
// "clone <-> current" map to tell if we're dirty or not, i.e. whether `parentInstance[key]` has
|
|
26
|
-
// drifted from `parentCopy[key]`
|
|
27
|
-
const copyMap = new Map();
|
|
28
|
-
((_a = parentCopy[key]) !== null && _a !== void 0 ? _a : []).forEach((copy, i) => {
|
|
29
|
-
copyMap.set(copy, parentInstance[key][i]);
|
|
30
|
-
});
|
|
31
|
-
// Given a child POJO (or a copy/clone of a child POJO), return its ObjectState wrapper.
|
|
32
|
-
function getOrCreateChildState(child, opts) {
|
|
33
|
-
var _a;
|
|
34
|
-
let childState = rowMap.get(child);
|
|
35
|
-
// If we're being reverted to our originalValue, i.e. values is actually
|
|
36
|
-
// a list of copies, use the copyMap to recover the non-copy original value
|
|
37
|
-
if (!childState && copyMap.has(child)) {
|
|
38
|
-
childState = rowMap.get(copyMap.get(child));
|
|
39
|
-
}
|
|
40
|
-
// Look for an existing child (requires having an id key configured)
|
|
41
|
-
if (!childState) {
|
|
42
|
-
for (const [, otherState] of rowMap.entries()) {
|
|
43
|
-
if (otherState.isSameEntity(child)) {
|
|
44
|
-
// If we're being called from `list.rows`, we should be careful to not trigger mutations
|
|
45
|
-
if (!(opts === null || opts === void 0 ? void 0 : opts.skipSet))
|
|
46
|
-
otherState.set(child, opts);
|
|
47
|
-
rowMap.set(child, otherState);
|
|
48
|
-
return otherState;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
// If we didn't have an existing child, just make a new object state
|
|
53
|
-
if (!childState) {
|
|
54
|
-
childState = (0, objectField_1.newObjectState)(config, parentState, undefined, list, child, undefined, maybeAutoSave, (_a = listConfig.update === "deep-exhaustive") !== null && _a !== void 0 ? _a : deepExhaustive);
|
|
55
|
-
rowMap.set(child, childState);
|
|
56
|
-
}
|
|
57
|
-
return childState;
|
|
58
|
-
}
|
|
59
|
-
const list = {
|
|
60
|
-
key: key,
|
|
61
|
-
// Our fundamental state of wrapped Us
|
|
62
|
-
get value() {
|
|
63
|
-
var _a;
|
|
64
|
-
return _tick.value > 0 && _childTick.value > 0 ? ((_a = parentInstance[key]) !== null && _a !== void 0 ? _a : []) : (0, utils_1.fail)();
|
|
65
|
-
},
|
|
66
|
-
_kind: "list",
|
|
67
|
-
_focused: false,
|
|
68
|
-
_readOnly: false,
|
|
69
|
-
_loading: false,
|
|
70
|
-
get readOnly() {
|
|
71
|
-
return this._readOnly || parentState().readOnly;
|
|
72
|
-
},
|
|
73
|
-
set readOnly(readOnly) {
|
|
74
|
-
this._readOnly = readOnly;
|
|
75
|
-
},
|
|
76
|
-
get loading() {
|
|
77
|
-
return this._loading || parentState().loading;
|
|
78
|
-
},
|
|
79
|
-
set loading(loading) {
|
|
80
|
-
this._loading = loading;
|
|
81
|
-
},
|
|
82
|
-
set value(v) {
|
|
83
|
-
this.set(v);
|
|
84
|
-
},
|
|
85
|
-
get dirty() {
|
|
86
|
-
return this.rows.some((r) => r.dirty) || this.hasChanged();
|
|
87
|
-
},
|
|
88
|
-
// Having a new entity is slightly different than being dirty, b/c merely having a placeholder-but-not-changed-yet
|
|
89
|
-
// `isNewEntity` child should not fire autosave (which would happen if we're dirty).
|
|
90
|
-
get hasNewEntity() {
|
|
91
|
-
return this.rows.some((r) => r.isNewEntity);
|
|
92
|
-
},
|
|
93
|
-
get focused() {
|
|
94
|
-
return this.rows.some((r) => r.focused);
|
|
95
|
-
},
|
|
96
|
-
get required() {
|
|
97
|
-
return this.rules.some((rule) => rule === rules_1.required);
|
|
98
|
-
},
|
|
99
|
-
get isNewEntity() {
|
|
100
|
-
return parentState().isNewEntity;
|
|
101
|
-
},
|
|
102
|
-
// private
|
|
103
|
-
/** Returns whether a row has been added/removed. */
|
|
104
|
-
hasChanged() {
|
|
105
|
-
const [current, original] = [this.value || [], this.originalValue];
|
|
106
|
-
if (current.length !== original.length)
|
|
107
|
-
return true;
|
|
108
|
-
if (strictOrder) {
|
|
109
|
-
// With strict order, every copy[i] === original[i] must be true
|
|
110
|
-
return original.some((e, idx) => current[idx] !== copyMap.get(e));
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
// With loose order, we just want every copy to still have its original somewhere
|
|
114
|
-
return !original.every((e) => current.includes(copyMap.get(e)));
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
// And we can derive each value's ObjectState wrapper as needed from the rowMap cache
|
|
118
|
-
get rows() {
|
|
119
|
-
// It's unclear why we need to access _tick.value here, b/c calling `this.value` should
|
|
120
|
-
// transitively register us as a dependency on it
|
|
121
|
-
if (_tick.value < 0)
|
|
122
|
-
(0, utils_1.fail)();
|
|
123
|
-
// Avoid using `this.value` to avoid registering `_childTick` as a dependency
|
|
124
|
-
const value = parentInstance[key];
|
|
125
|
-
return (value || []).map((child) => getOrCreateChildState(child, { skipSet: true }));
|
|
126
|
-
},
|
|
127
|
-
// TODO Should this be true when all rows are touched?
|
|
128
|
-
get touched() {
|
|
129
|
-
return this.rows.some((r) => r.touched) || this.hasChanged();
|
|
130
|
-
},
|
|
131
|
-
set touched(touched) {
|
|
132
|
-
this.rows.forEach((r) => (r.touched = touched));
|
|
133
|
-
},
|
|
134
|
-
rules,
|
|
135
|
-
get valid() {
|
|
136
|
-
const value = this.rows;
|
|
137
|
-
// TODO Passing `originalCopy || []` is probably not 100% right
|
|
138
|
-
const opts = { value, key: key, originalValue: this.originalValue, object: parentState() };
|
|
139
|
-
const collectionValid = this.rules.every((r) => r(opts) === undefined);
|
|
140
|
-
const entriesValid = this.rows.filter((r) => !r._considerDeleted()).every((r) => r.valid);
|
|
141
|
-
return collectionValid && entriesValid;
|
|
142
|
-
},
|
|
143
|
-
get errors() {
|
|
144
|
-
if (_tick.value < 0)
|
|
145
|
-
(0, utils_1.fail)();
|
|
146
|
-
const opts = { value: this.rows, key: key, originalValue: this.originalValue, object: parentState() };
|
|
147
|
-
return this.rules.map((r) => r(opts)).filter(utils_1.isNotUndefined);
|
|
148
|
-
},
|
|
149
|
-
get changedValue() {
|
|
150
|
-
const result = [];
|
|
151
|
-
const hasOpKey = Object.keys(listConfig.config).includes("op");
|
|
152
|
-
const hasLegacyOpKey = Object.keys(listConfig.config).some((key) => key === "delete" || key === "remove");
|
|
153
|
-
const incremental = listConfig.update === "incremental" ||
|
|
154
|
-
// Implicitly enable incremental mode if we see an op key
|
|
155
|
-
(listConfig.update === undefined && (hasOpKey || hasLegacyOpKey));
|
|
156
|
-
const exhaustive = !incremental;
|
|
157
|
-
this.rows.forEach((r) => {
|
|
158
|
-
var _a;
|
|
159
|
-
var _b;
|
|
160
|
-
if (exhaustive || r.dirty || r.isNewEntity) {
|
|
161
|
-
const changed = r.changedValue;
|
|
162
|
-
// Ensure we have an `op: include` key, following https://joist-orm.io/docs/features/partial-update-apis
|
|
163
|
-
if (incremental && hasOpKey) {
|
|
164
|
-
(_a = (_b = changed).op) !== null && _a !== void 0 ? _a : (_b.op = "include");
|
|
165
|
-
}
|
|
166
|
-
result.push(changed);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
return result;
|
|
170
|
-
},
|
|
171
|
-
focus() {
|
|
172
|
-
this._focused = true;
|
|
173
|
-
},
|
|
174
|
-
blur() {
|
|
175
|
-
this._focused = false;
|
|
176
|
-
this.maybeAutoSave();
|
|
177
|
-
},
|
|
178
|
-
maybeAutoSave() {
|
|
179
|
-
this.touched = true;
|
|
180
|
-
maybeAutoSave();
|
|
181
|
-
},
|
|
182
|
-
set(values, opts = {}) {
|
|
183
|
-
var _a, _b, _c, _d, _e;
|
|
184
|
-
if (this.readOnly && !opts.resetting && !opts.refreshing) {
|
|
185
|
-
throw new Error(`${String(key)} is currently readOnly`);
|
|
186
|
-
}
|
|
187
|
-
// Given the values, see if we've got existing child states, and use their
|
|
188
|
-
// value if so. I.e. this covers revertChanges doing `set(originalValue)` and
|
|
189
|
-
// passing us cloned rows from the parentCopy, but `getOrCreateChildState` will
|
|
190
|
-
// use the `copyMap` to recover the non-cloned rows, to avoid promoting the clone
|
|
191
|
-
// into a real row.
|
|
192
|
-
if ((this.dirty || this.hasNewEntity) && opts.refreshing) {
|
|
193
|
-
// When refreshing a dirty list, we need to preserve WIP values
|
|
194
|
-
// Start with current list, then merge in incoming changes
|
|
195
|
-
//
|
|
196
|
-
// These will all be POJOs, where `currentItems` existing in our `parentInstance`, and
|
|
197
|
-
// `incomingItems` could really be any of:
|
|
198
|
-
// - an instance from `parentInstance[key]` that user code is passing back in
|
|
199
|
-
// - an instance from `parentCopy[key]` that a cache refresh is passing in
|
|
200
|
-
// - a new instance, either from a cache refresh or user code, that we haven't seen yet
|
|
201
|
-
const currentItems = this.value || [];
|
|
202
|
-
const incomingItems = values || [];
|
|
203
|
-
const mergedItems = [];
|
|
204
|
-
// Index by idKey or a hash of the object, so we don't have to n^2 merging
|
|
205
|
-
const idKey = this.rows.length > 0 && this.rows[0].idKey;
|
|
206
|
-
// Look at our child config, i.e. `bookConfig` and find the non-id / non-list keys
|
|
207
|
-
const contentKeys = Object.entries(config.config)
|
|
208
|
-
.filter(([key, cfg]) => key !== idKey && cfg.type === "value")
|
|
209
|
-
.map(([key]) => key);
|
|
210
|
-
const hashByContent = (item) => hashValue((0, utils_1.normalizeHashValue)(Object.fromEntries(Object.entries(item).filter(([key]) => contentKeys.includes(key)))));
|
|
211
|
-
const hashById = (item) => {
|
|
212
|
-
const idValue = idKey ? item[idKey] : undefined;
|
|
213
|
-
return idValue === undefined ? hashByContent(item) : idValue;
|
|
214
|
-
};
|
|
215
|
-
const currentById = (0, utils_1.groupBy)(currentItems, hashById);
|
|
216
|
-
const incomingById = (0, utils_1.groupBy)(incomingItems, hashById);
|
|
217
|
-
// If our local instance doesn't assign in id, when we get results back from the server, strip
|
|
218
|
-
// their id and then see if we're the same based on data. Note that this is a hueristic, and will
|
|
219
|
-
// fail if the client-side has made additional changes to the child after submitting it to the server,
|
|
220
|
-
// or if the server acks back more/different data than the client sent.
|
|
221
|
-
//
|
|
222
|
-
// Only do this if one of our client-side objects is missing an id
|
|
223
|
-
const incomingByContent = idKey && currentItems.some((item) => !item[idKey]) && (0, utils_1.groupBy)(incomingItems, hashByContent);
|
|
224
|
-
// Watch for deletions
|
|
225
|
-
const hasOpKey = Object.keys(listConfig.config).includes("op");
|
|
226
|
-
// First, process all current items
|
|
227
|
-
for (const currentItem of currentItems) {
|
|
228
|
-
const childState = rowMap.get(currentItem); // We'll always have a child state for `currentItems`
|
|
229
|
-
// Try to find a matching item in the incoming data
|
|
230
|
-
const hash = hashById(currentItem);
|
|
231
|
-
const match = ((_b = (_a = incomingById.get(hash)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b :
|
|
232
|
-
// Only if we don't have `book[id]` set, look for a match based on our content
|
|
233
|
-
(!!idKey &&
|
|
234
|
-
!currentItem[idKey] &&
|
|
235
|
-
!!incomingByContent &&
|
|
236
|
-
(
|
|
237
|
-
// If we don't have an id, our `hash` variable will already be the `hashByContent`, so we can reuse it
|
|
238
|
-
(_c = incomingByContent === null || incomingByContent === void 0 ? void 0 : incomingByContent.get(hash)) === null || _c === void 0 ? void 0 : _c[0])));
|
|
239
|
-
if (match) {
|
|
240
|
-
// Defer to set to handle not nuking WIP changes
|
|
241
|
-
childState.set(match, opts);
|
|
242
|
-
mergedItems.push(childState.value);
|
|
243
|
-
// Once matched, we don't want this to be an addedRow anymore
|
|
244
|
-
addedRows.delete(currentItem);
|
|
245
|
-
}
|
|
246
|
-
else if (!childState.dirty && !childState.isNewEntity && !addedRows.has(currentItem)) {
|
|
247
|
-
// Local is not dirty/added, and it's not upstream, so let it get removed
|
|
248
|
-
}
|
|
249
|
-
else if (hasOpKey && currentItem.op === "delete") {
|
|
250
|
-
// We were locally marked as deleted, and not finding a match is the server acking that we're gone
|
|
251
|
-
}
|
|
252
|
-
else if (currentItems.length === incomingItems.length &&
|
|
253
|
-
!!idKey &&
|
|
254
|
-
currentItem[idKey] === undefined) {
|
|
255
|
-
// Assume our newly-assigned id is coming back
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
// If no incoming, but we're dirty (see above) and not deleted (see above), keep the WIP change
|
|
259
|
-
mergedItems.push(currentItem);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
// Then, add any incoming items that don't exist in current
|
|
263
|
-
for (const incomingItem of incomingItems) {
|
|
264
|
-
// Look for both `incoming.id` and the incoming-sans-id for new entities
|
|
265
|
-
const match = ((_d = currentById.get(hashById(incomingItem))) === null || _d === void 0 ? void 0 : _d[0]) || ((_e = currentById.get(hashByContent(incomingItem))) === null || _e === void 0 ? void 0 : _e[0]);
|
|
266
|
-
if (!match) {
|
|
267
|
-
// New item from server, add it
|
|
268
|
-
const childState = getOrCreateChildState(incomingItem, opts);
|
|
269
|
-
mergedItems.push(childState.value);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
parentInstance[key] = mergedItems;
|
|
273
|
-
_tick.value++;
|
|
274
|
-
// Set original to not merged...
|
|
275
|
-
this.setOriginalValue(incomingItems);
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
parentInstance[key] = (values !== null && values !== void 0 ? values : []).map((child) => getOrCreateChildState(child, opts).value);
|
|
279
|
-
_tick.value++;
|
|
280
|
-
// Reset originalCopy so that our dirty checks have the right # of rows.
|
|
281
|
-
if (opts.refreshing) {
|
|
282
|
-
this.setOriginalValue();
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
},
|
|
286
|
-
add(value, spliceIndex) {
|
|
287
|
-
// This is called by the user, so value should be a non-proxy value we should keep
|
|
288
|
-
const childState = getOrCreateChildState(value);
|
|
289
|
-
rowMap.set(value, childState);
|
|
290
|
-
// Let `.set` know this is a new row
|
|
291
|
-
addedRows.add(value);
|
|
292
|
-
this.ensureSet();
|
|
293
|
-
this.value.splice(typeof spliceIndex === "number" ? spliceIndex : this.value.length, 0, childState.value);
|
|
294
|
-
_tick.value++;
|
|
295
|
-
maybeAutoSave();
|
|
296
|
-
},
|
|
297
|
-
remove(indexOrValue) {
|
|
298
|
-
this.ensureSet();
|
|
299
|
-
if (typeof indexOrValue === "number") {
|
|
300
|
-
this.value.splice(indexOrValue, 1);
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
const index = this.value.findIndex((v) => v === indexOrValue);
|
|
304
|
-
if (index > -1) {
|
|
305
|
-
this.value.splice(index, 1);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
_tick.value++;
|
|
309
|
-
maybeAutoSave();
|
|
310
|
-
},
|
|
311
|
-
revertChanges() {
|
|
312
|
-
this.set(this.originalValue, { resetting: true });
|
|
313
|
-
this.rows.forEach((r) => r.revertChanges());
|
|
314
|
-
this.touched = false;
|
|
315
|
-
},
|
|
316
|
-
commitChanges() {
|
|
317
|
-
// Tell each child ObjectState to have its fields commit into its parentCopy
|
|
318
|
-
this.rows.forEach((r) => r.commitChanges());
|
|
319
|
-
this.setOriginalValue();
|
|
320
|
-
this.touched = false;
|
|
321
|
-
_tick.value++;
|
|
322
|
-
},
|
|
323
|
-
get originalValue() {
|
|
324
|
-
// A dummy check to for reactivity around our non-proxy value
|
|
325
|
-
const value = _originalValueTick.value > -1 ? parentCopy[key] : parentCopy[key];
|
|
326
|
-
return value !== null && value !== void 0 ? value : [];
|
|
327
|
-
},
|
|
328
|
-
// This should only be called when value === originalValue
|
|
329
|
-
setOriginalValue(incomingItems) {
|
|
330
|
-
// Use the rows' originalValues to update the parentCopy
|
|
331
|
-
parentCopy[key] = incomingItems !== null && incomingItems !== void 0 ? incomingItems : this.rows.map((r) => r.originalValue);
|
|
332
|
-
parentCopy[key].forEach((copy, i) => {
|
|
333
|
-
copyMap.set(copy, parentInstance[key][i]);
|
|
334
|
-
});
|
|
335
|
-
_originalValueTick.value++;
|
|
336
|
-
},
|
|
337
|
-
ensureSet() {
|
|
338
|
-
if (!parentInstance[key]) {
|
|
339
|
-
parentInstance[key] = [];
|
|
340
|
-
}
|
|
341
|
-
_tick.value++;
|
|
342
|
-
},
|
|
343
|
-
};
|
|
344
|
-
const proxy = (0, mobx_1.makeAutoObservable)(list, {
|
|
345
|
-
// See other makeAutoObservable comment
|
|
346
|
-
value: (0, mobx_1.computed)({ equals: () => false }),
|
|
347
|
-
});
|
|
348
|
-
// Any time a row's value changes, percolate that to our `.value` (so the callers to our
|
|
349
|
-
// `.value` will rerun given the value they saw has deeply changed.)
|
|
350
|
-
(0, mobx_1.reaction)(() => proxy.rows.map((r) => r.value), () => _childTick.value++);
|
|
351
|
-
return proxy;
|
|
352
|
-
}
|
|
353
|
-
function hashValue(value) {
|
|
354
|
-
return (0, object_hash_1.default)(value);
|
|
355
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { ObjectConfig, ObjectFieldConfig } from "../config";
|
|
2
|
-
import { FragmentField } from "./fragmentField";
|
|
3
|
-
import { ListFieldState } from "./listField";
|
|
4
|
-
import { FieldState, InternalSetOpts, SetOpts } from "./valueField";
|
|
5
|
-
import { Builtin } from "../utils";
|
|
6
|
-
/**
|
|
7
|
-
* Wraps a given input/on-the-wire type `T` for editing in a form.
|
|
8
|
-
*
|
|
9
|
-
* We basically mimic every field in `T` (i.e. `firstName`, `lastName`, etc.) but decorate them
|
|
10
|
-
* with form-specific state like `touched`, `dirty`, `errors`, etc.
|
|
11
|
-
*
|
|
12
|
-
* The intent is that, after ensuring all fields are `valid`/etc., callers can take the
|
|
13
|
-
* result of this `objectState.value` (or `objectState.originalValue` for the non-proxy version) and
|
|
14
|
-
* have exactly the on-the-wire type `T` that they need to submit to the backend, without doing the
|
|
15
|
-
* manual mapping of "data that was in the form controls" into "data that the backend wants".
|
|
16
|
-
*
|
|
17
|
-
* Note that this can be hierarchical by having a field of `ListFieldState` that
|
|
18
|
-
* themselves each wrap an `ObjectState`, i.e.:
|
|
19
|
-
*
|
|
20
|
-
* ```
|
|
21
|
-
* ObjectState for author
|
|
22
|
-
* - firstName: FieldState
|
|
23
|
-
* - lastName: FieldState
|
|
24
|
-
* - rows: ListFieldState
|
|
25
|
-
* - [0]: ObjectState for book 1
|
|
26
|
-
* - title: FieldState
|
|
27
|
-
* - [1]: ObjectState for book 2
|
|
28
|
-
* - title: FieldState
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export type ObjectState<T> = FieldStates<T> & FieldState<T> & {
|
|
32
|
-
/** Sets the state of fields in `state`. */
|
|
33
|
-
set(state: Partial<T>, opts?: SetOpts): void;
|
|
34
|
-
/** Returns whether the object can be saved, i.e. is valid, but also as a side-effect marks touched. */
|
|
35
|
-
canSave(): boolean;
|
|
36
|
-
};
|
|
37
|
-
export type ObjectStateInternal<T = unknown> = ObjectState<T> & {
|
|
38
|
-
set(value: T, opts?: InternalSetOpts): void;
|
|
39
|
-
isSameEntity(other: T): boolean;
|
|
40
|
-
idKey: string | undefined;
|
|
41
|
-
};
|
|
42
|
-
declare const fragmentSym: unique symbol;
|
|
43
|
-
export type Fragment<V> = V & {
|
|
44
|
-
[fragmentSym]: true;
|
|
45
|
-
};
|
|
46
|
-
export declare function fragment<V>(value: V): Fragment<V>;
|
|
47
|
-
/** For a given input type `T`, decorate each field into the "field state" type that holds our form-relevant state, i.e. valid/touched/etc. */
|
|
48
|
-
type FieldStates<T> = {
|
|
49
|
-
[K in keyof T]-?: T[K] extends Fragment<infer V> ? FragmentField<V> : T[K] extends Array<infer U> | null | undefined ? [U] extends [Builtin] ? FieldState<T[K]> : ListFieldState<U> : T[K] extends Builtin | null | undefined ? FieldState<T[K]> : ObjectState<T[K]>;
|
|
50
|
-
};
|
|
51
|
-
/**
|
|
52
|
-
* Creates a new `ObjectState` for a given form object `T` given config rules in `config`.
|
|
53
|
-
*
|
|
54
|
-
* The returned `ObjectState` can be used in a mobx `useLocalObservable` to drive an
|
|
55
|
-
* interactive form that tracks the current valid/touched/etc. state of both each
|
|
56
|
-
* individual fields as well as the top-level form/object itself.
|
|
57
|
-
*/
|
|
58
|
-
export declare function createObjectState<T>(config: ObjectConfig<T>, instance: T, opts?: {
|
|
59
|
-
maybeAutoSave?: () => void;
|
|
60
|
-
}): ObjectState<T>;
|
|
61
|
-
/** A more internal version of `createObjectState`. */
|
|
62
|
-
export declare function newObjectState<T, P = any>(config: ObjectFieldConfig<T>, parentState: (() => ObjectState<P>) | undefined, parentInstance: P | undefined, parentListState: FieldState<any> | undefined, instance: T, key: keyof T | undefined, maybeAutoSave: () => void, deepExhaustive: boolean): ObjectState<T>;
|
|
63
|
-
export {};
|