nexus-shared 3.2.0 → 4.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/CHANGELOG.md +1564 -0
- package/README.md +1705 -1123
- package/dist/Client.Index.d.ts +14 -0
- package/dist/Client.Index.js +14 -0
- package/dist/Components/Documents/DocumentFrame.d.ts +50 -0
- package/dist/Components/Documents/DocumentFrame.js +83 -0
- package/dist/Components/Documents/Menu.d.ts +4 -0
- package/dist/Components/Documents/Menu.js +40 -24
- package/dist/Components/Documents/PrintDocument.d.ts +95 -0
- package/dist/Components/Documents/PrintDocument.js +62 -0
- package/dist/Components/Documents/Tabs.d.ts +59 -0
- package/dist/Components/Documents/Tabs.js +70 -0
- package/dist/Components/Forms/ApiForm.js +19 -2
- package/dist/Components/Forms/Crud.d.ts +2 -2
- package/dist/Components/Forms/Crud.js +576 -243
- package/dist/Components/Forms/CrudActions.d.ts +136 -0
- package/dist/Components/Forms/CrudActions.js +184 -0
- package/dist/Components/Forms/CrudNarrowPicker.d.ts +31 -0
- package/dist/Components/Forms/CrudNarrowPicker.js +180 -0
- package/dist/Components/Forms/CrudParts.d.ts +30 -2
- package/dist/Components/Forms/CrudParts.js +55 -5
- package/dist/Components/Forms/CrudRowForms.d.ts +26 -0
- package/dist/Components/Forms/CrudRowForms.js +114 -0
- package/dist/Components/Forms/CrudScopes.d.ts +28 -0
- package/dist/Components/Forms/CrudScopes.js +76 -0
- package/dist/Components/Forms/CrudTree.d.ts +90 -0
- package/dist/Components/Forms/CrudTree.js +363 -0
- package/dist/Components/Forms/CrudWorkspace.d.ts +63 -0
- package/dist/Components/Forms/CrudWorkspace.js +169 -0
- package/dist/Components/Forms/ExcelImport.js +3 -3
- package/dist/Components/Forms/Form.d.ts +4 -4
- package/dist/Components/Forms/Form.js +123 -17
- package/dist/Components/Forms/FormLeaveGuard.d.ts +44 -0
- package/dist/Components/Forms/FormLeaveGuard.js +65 -0
- package/dist/Components/Forms/RecordFlows.d.ts +50 -0
- package/dist/Components/Forms/RecordFlows.js +282 -0
- package/dist/Components/Forms/SubmitForm.d.ts +37 -2
- package/dist/Components/Forms/SubmitForm.js +195 -10
- package/dist/Components/Inputs/CheckBox.js +1 -1
- package/dist/Components/Inputs/ChoiceField.d.ts +21 -1
- package/dist/Components/Inputs/ChoiceField.js +28 -2
- package/dist/Components/Inputs/Dropdown.d.ts +13 -0
- package/dist/Components/Inputs/Dropdown.js +5 -5
- package/dist/Components/Inputs/DropdownParts.d.ts +3 -1
- package/dist/Components/Inputs/DropdownParts.js +16 -4
- package/dist/Components/Inputs/Field.d.ts +7 -1
- package/dist/Components/Inputs/Field.js +2 -2
- package/dist/Components/Inputs/GroupForm.d.ts +6 -6
- package/dist/Components/Inputs/GroupForm.js +10 -11
- package/dist/Components/Inputs/InputRenderer.d.ts +9 -6
- package/dist/Components/Inputs/InputRenderer.js +37 -32
- package/dist/Components/Inputs/PictureInput.d.ts +41 -0
- package/dist/Components/Inputs/PictureInput.js +70 -0
- package/dist/Components/Inputs/RadioGroup.d.ts +16 -3
- package/dist/Components/Inputs/RadioGroup.js +8 -3
- package/dist/Components/Inputs/RowsInput.d.ts +1 -1
- package/dist/Components/Inputs/RowsInput.js +18 -3
- package/dist/Components/Inputs/TabularForm.d.ts +5 -5
- package/dist/Components/Inputs/TabularForm.js +29 -29
- package/dist/Components/Inputs/TextArea.d.ts +6 -0
- package/dist/Components/Inputs/TextArea.js +11 -2
- package/dist/Components/Inputs/TextBoxTypes.d.ts +6 -0
- package/dist/Components/Inputs/TextBoxTypes.js +17 -0
- package/dist/Components/Inputs/TreeView.d.ts +53 -2
- package/dist/Components/Inputs/TreeView.js +147 -13
- package/dist/Components/Layouts/MessageBox.d.ts +15 -2
- package/dist/Components/Layouts/MessageBox.js +56 -7
- package/dist/Components/Layouts/Popup.d.ts +13 -6
- package/dist/Components/Layouts/Popup.js +37 -9
- package/dist/Components/Layouts/PopupDock.d.ts +3 -1
- package/dist/Components/Layouts/PopupDock.js +2 -1
- package/dist/Components/Layouts/PopupHost.js +5 -4
- package/dist/Components/Viewers/DataTable.js +150 -48
- package/dist/Components/Viewers/DataTableFilters.d.ts +1 -1
- package/dist/Components/Viewers/DataTableFilters.js +17 -2
- package/dist/Components/Viewers/DataTableParts.d.ts +21 -5
- package/dist/Components/Viewers/DataTableParts.js +26 -14
- package/dist/Components/Viewers/OwnerFiles.d.ts +175 -0
- package/dist/Components/Viewers/OwnerFiles.js +589 -0
- package/dist/Components/Viewers/QrCodeImage.d.ts +22 -0
- package/dist/Components/Viewers/QrCodeImage.js +15 -0
- package/dist/Components/Viewers/RecordCards.d.ts +16 -0
- package/dist/Components/Viewers/RecordCards.js +509 -0
- package/dist/Helpers/BusyHooks.d.ts +6 -0
- package/dist/Helpers/BusyHooks.js +39 -0
- package/dist/Helpers/CrudBackend.d.ts +20 -2
- package/dist/Helpers/CrudBackend.js +46 -2
- package/dist/Helpers/CrudColumns.d.ts +53 -6
- package/dist/Helpers/CrudColumns.js +98 -7
- package/dist/Helpers/CrudDetailsTabs.d.ts +58 -0
- package/dist/Helpers/CrudDetailsTabs.js +126 -0
- package/dist/Helpers/CrudHelpers.d.ts +81 -2
- package/dist/Helpers/CrudHelpers.js +195 -0
- package/dist/Helpers/CrudNarrowPick.d.ts +83 -0
- package/dist/Helpers/CrudNarrowPick.js +157 -0
- package/dist/Helpers/CrudRowForms.d.ts +15 -0
- package/dist/Helpers/CrudRowForms.js +43 -0
- package/dist/Helpers/CrudScopes.d.ts +32 -0
- package/dist/Helpers/CrudScopes.js +79 -0
- package/dist/Helpers/DropdownHelpers.d.ts +3 -1
- package/dist/Helpers/DropdownHelpers.js +6 -2
- package/dist/Helpers/ExcelHelpers.d.ts +1 -1
- package/dist/Helpers/ExcelHelpers.js +12 -11
- package/dist/Helpers/FileAdapter.d.ts +78 -0
- package/dist/Helpers/FileAdapter.js +102 -0
- package/dist/Helpers/FilePrimaries.d.ts +48 -0
- package/dist/Helpers/FilePrimaries.js +138 -0
- package/dist/Helpers/FormConfirm.d.ts +32 -0
- package/dist/Helpers/FormConfirm.js +40 -0
- package/dist/Helpers/FormStore.d.ts +90 -5
- package/dist/Helpers/FormStore.js +318 -22
- package/dist/Helpers/InputConditions.d.ts +50 -0
- package/dist/Helpers/InputConditions.js +168 -0
- package/dist/Helpers/InputMemory.d.ts +106 -0
- package/dist/Helpers/InputMemory.js +385 -0
- package/dist/Helpers/InputNarrowing.d.ts +18 -0
- package/dist/Helpers/InputNarrowing.js +67 -0
- package/dist/Helpers/InputParamsHelpers.d.ts +44 -1
- package/dist/Helpers/InputParamsHelpers.js +157 -74
- package/dist/Helpers/NexusDriveAdapter.d.ts +55 -0
- package/dist/Helpers/NexusDriveAdapter.js +207 -0
- package/dist/Helpers/NumberHelpers.d.ts +95 -0
- package/dist/Helpers/NumberHelpers.js +126 -0
- package/dist/Helpers/Permissions.js +2 -1
- package/dist/Helpers/QrCode.d.ts +26 -0
- package/dist/Helpers/QrCode.js +463 -0
- package/dist/Helpers/RecordCards.d.ts +205 -0
- package/dist/Helpers/RecordCards.js +378 -0
- package/dist/Helpers/RequestCache.d.ts +14 -2
- package/dist/Helpers/RequestCache.js +22 -3
- package/dist/Helpers/RowsStore.d.ts +37 -2
- package/dist/Helpers/RowsStore.js +128 -10
- package/dist/Helpers/ServerPreview.d.ts +69 -0
- package/dist/Helpers/ServerPreview.js +99 -0
- package/dist/Helpers/TableColumns.js +4 -3
- package/dist/Helpers/TableExport.d.ts +7 -1
- package/dist/Helpers/TableExport.js +9 -3
- package/dist/Helpers/TableFilters.d.ts +1 -1
- package/dist/Helpers/TableFilters.js +24 -23
- package/dist/Helpers/TableHelpers.d.ts +23 -2
- package/dist/Helpers/TableHelpers.js +51 -10
- package/dist/Helpers/TableJoins.d.ts +13 -0
- package/dist/Helpers/TableJoins.js +34 -0
- package/dist/Helpers/TableStore.d.ts +21 -1
- package/dist/Helpers/TableStore.js +64 -7
- package/dist/Helpers/TreeStore.d.ts +81 -0
- package/dist/Helpers/TreeStore.js +365 -0
- package/dist/Interfaces/CrudInterfaces.d.ts +465 -5
- package/dist/Interfaces/FileInterfaces.d.ts +162 -0
- package/dist/Interfaces/FileInterfaces.js +4 -0
- package/dist/Interfaces/FormInterfaces.d.ts +391 -30
- package/dist/Interfaces/FormInterfaces.js +79 -1
- package/dist/Interfaces/InputInterfaces.d.ts +15 -3
- package/dist/Interfaces/RecordInterfaces.d.ts +367 -0
- package/dist/Interfaces/RecordInterfaces.js +1 -0
- package/dist/Interfaces/TableInterfaces.d.ts +74 -1
- package/dist/Services/ExcelApi.js +5 -0
- package/dist/Services/FileApi.d.ts +13 -0
- package/dist/Services/FileApi.js +29 -0
- package/dist/Services/ImageEditorLoader.d.ts +14 -0
- package/dist/Services/ImageEditorLoader.js +51 -0
- package/dist/Services/JoinSources.d.ts +37 -5
- package/dist/Services/JoinSources.js +90 -14
- package/dist/Services/ModuleImport.d.ts +7 -0
- package/dist/Services/ModuleImport.js +22 -0
- package/dist/Shared.Index.d.ts +16 -0
- package/dist/Shared.Index.js +16 -0
- package/package.json +7 -2
- package/src/Styles/Nexus.Avatar.css +6 -5
- package/src/Styles/Nexus.Button.css +10 -4
- package/src/Styles/Nexus.Choice.css +16 -0
- package/src/Styles/Nexus.Crud.css +383 -9
- package/src/Styles/Nexus.Dropdown.css +8 -0
- package/src/Styles/Nexus.Files.css +410 -0
- package/src/Styles/Nexus.Form.css +5 -2
- package/src/Styles/Nexus.Index.css +4 -0
- package/src/Styles/Nexus.Input.css +42 -0
- package/src/Styles/Nexus.Menu.css +10 -1
- package/src/Styles/Nexus.Popup.css +77 -1
- package/src/Styles/Nexus.Print.css +288 -0
- package/src/Styles/Nexus.QrCode.css +12 -0
- package/src/Styles/Nexus.Record.css +430 -0
- package/src/Styles/Nexus.Tab.Buttons.css +87 -22
- package/src/Styles/Nexus.Table.css +67 -5
- package/src/Styles/Nexus.Themes.css +12 -6
- package/src/Styles/Nexus.Tokens.css +8 -3
- package/src/Styles/Nexus.Tree.css +31 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { deleteFormDraft, readFormDraft, writeFormDraft } from "./FormDrafts.js";
|
|
2
|
-
import {
|
|
2
|
+
import { isComputedInput } from "./InputConditions.js";
|
|
3
|
+
import { findRememberedOpening, forgetInputMemory, isEmptyInputValue, isPickerInput, readInputMemory, rememberDependencies, rememberedOpeningNow, writeInputMemory } from "./InputMemory.js";
|
|
4
|
+
import { holdsValue, initialInputValue, inputValuesEqual, isRowsInput, keepsTyping, normalizeInputValue, validateInput } from "./InputParamsHelpers.js";
|
|
3
5
|
const INITIAL_STATUS = {
|
|
4
6
|
changed: false,
|
|
5
7
|
submitting: false,
|
|
@@ -40,6 +42,21 @@ export class FormStore {
|
|
|
40
42
|
inputByName = new Map();
|
|
41
43
|
values = {};
|
|
42
44
|
initial = {};
|
|
45
|
+
/** The values the last load was given, as given: an input they name with a value is the form's own, never remembered. */
|
|
46
|
+
given = {};
|
|
47
|
+
/** Where remembered inputs (`remember`) are read and kept; set by the form that draws this store, in the browser. */
|
|
48
|
+
memory = null;
|
|
49
|
+
/** Pickers asked whether they still offer a remembered value; the next opening (a load, a reset) or a submit stops it. */
|
|
50
|
+
memoryRound = null;
|
|
51
|
+
/** The inputs whose picker is being asked: drawn loading, and a change to one (the person's pick, code) ends its ask. */
|
|
52
|
+
asking = new Set();
|
|
53
|
+
/** The values memory opened inputs on: an opening that finds nothing (a reset after the choice went) takes its own back. */
|
|
54
|
+
fromMemory = new Map();
|
|
55
|
+
/**
|
|
56
|
+
* The values a load (or a reset, a draft) is working out, while it works them out: what `get` answers then, so an
|
|
57
|
+
* input's live `hidden` (`showWhen`) is judged on the values the form opens with, never on the ones it had before.
|
|
58
|
+
*/
|
|
59
|
+
reading = null;
|
|
43
60
|
isStarted = false;
|
|
44
61
|
// Messages: an input's own rules, the form's rule, a server's, and rule messages the form shows for its inputs.
|
|
45
62
|
ruleErrors = new Map();
|
|
@@ -72,6 +89,8 @@ export class FormStore {
|
|
|
72
89
|
draftDropped = false;
|
|
73
90
|
/** An input reported a value while the values loaded and was refused: it is drawn again from the store when loading ends. */
|
|
74
91
|
refusedWhileLoading = false;
|
|
92
|
+
/** What a choice input reported beside the value the user picked last (`pickedItem`): its item, kept with that value. */
|
|
93
|
+
picked = new Map();
|
|
75
94
|
/** A load during a render tells nobody; `announce` does, once the form has mounted. */
|
|
76
95
|
quiet = false;
|
|
77
96
|
unannounced = false;
|
|
@@ -92,7 +111,13 @@ export class FormStore {
|
|
|
92
111
|
return this.values;
|
|
93
112
|
}
|
|
94
113
|
get(name) {
|
|
95
|
-
return this.values[name];
|
|
114
|
+
return this.reading ? this.reading[name] : this.values[name];
|
|
115
|
+
}
|
|
116
|
+
/** The params of the input of this name, as the form was given them; undefined for a name no input has. */
|
|
117
|
+
inputParams(name) {
|
|
118
|
+
if (!this.inputByName.size && this.options.inputs.length)
|
|
119
|
+
this.indexInputs();
|
|
120
|
+
return this.inputByName.get(name);
|
|
96
121
|
}
|
|
97
122
|
/** The values the form opened with, or saved last. */
|
|
98
123
|
getInitial() {
|
|
@@ -112,6 +137,19 @@ export class FormStore {
|
|
|
112
137
|
isChanged() {
|
|
113
138
|
return this.changedNames.size > 0;
|
|
114
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Whether the changes are worth asking about before they are dropped (Cancel, closing a popup, leaving the page): a
|
|
142
|
+
* changed value of any input but a password or a one-time code (`keepsTyping`). A form where only those were typed is
|
|
143
|
+
* changed, and Save and its line say so, but nothing asks before it goes.
|
|
144
|
+
*/
|
|
145
|
+
isWorthKeeping() {
|
|
146
|
+
for (const name of this.changedNames) {
|
|
147
|
+
const params = this.inputParams(name);
|
|
148
|
+
if (!params || keepsTyping(params))
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
115
153
|
getStatus = () => this.status;
|
|
116
154
|
/** The message an input shows for the form: a server's, the form's rule once the input was left or a submit ran, or its own rule after a submit its input missed. */
|
|
117
155
|
shownError(name) {
|
|
@@ -129,12 +167,21 @@ export class FormStore {
|
|
|
129
167
|
return this.ruleError(name) ?? undefined;
|
|
130
168
|
return undefined;
|
|
131
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* The item a choice input reported with the value it holds now, when the user picked that value: a dropdown's option
|
|
172
|
+
* (its options, with `multiple`). `undefined` for a value loaded, set from code, or changed since - so what it answers
|
|
173
|
+
* always goes with the value. A CRUD names what a save joined from it (`crudJoinedNames`).
|
|
174
|
+
*/
|
|
175
|
+
pickedItem(name) {
|
|
176
|
+
const picked = this.picked.get(name);
|
|
177
|
+
return picked && inputValuesEqual(picked.value, this.values[name]) ? picked.item : undefined;
|
|
178
|
+
}
|
|
132
179
|
/** The input's own rule message for its value, whether it shows or not. */
|
|
133
180
|
ruleError(name) {
|
|
134
181
|
if (this.staleRules.has(name)) {
|
|
135
182
|
this.staleRules.delete(name);
|
|
136
183
|
const params = this.inputByName.get(name);
|
|
137
|
-
const message = params && !params.hidden && !this.options.locked ? validateInput(params, this.values[name], this.options.context) : null;
|
|
184
|
+
const message = params && !params.hidden && !isComputedInput(params) && !this.options.locked ? validateInput(params, this.values[name], this.options.context) : null;
|
|
138
185
|
if (message)
|
|
139
186
|
this.ruleErrors.set(name, message);
|
|
140
187
|
else
|
|
@@ -152,7 +199,7 @@ export class FormStore {
|
|
|
152
199
|
}
|
|
153
200
|
/** Inputs whose values break a rule, their own or the form's, in form order. Inputs on the page may find more, such as text that is not a date yet. */
|
|
154
201
|
problems() {
|
|
155
|
-
return this.options.inputs.filter(params => !params.hidden && (this.ruleError(params.name) || this.crossErrors.has(params.name))).map(params => params.name);
|
|
202
|
+
return this.options.inputs.filter(params => !params.hidden && !isComputedInput(params) && (this.ruleError(params.name) || this.crossErrors.has(params.name))).map(params => params.name);
|
|
156
203
|
}
|
|
157
204
|
/** Whether every value passes, as far as the values tell. `validate()` also asks the inputs on the page. */
|
|
158
205
|
isValid() {
|
|
@@ -247,7 +294,10 @@ export class FormStore {
|
|
|
247
294
|
loadValues(values, keepChanged, draft) {
|
|
248
295
|
if (!this.inputByName.size && this.options.inputs.length)
|
|
249
296
|
this.indexInputs();
|
|
250
|
-
|
|
297
|
+
this.given = values;
|
|
298
|
+
// A load opens afresh: what memory opened before belongs to the values it replaces.
|
|
299
|
+
this.fromMemory.clear();
|
|
300
|
+
const next = this.openRemembered(this.normalize(values), values);
|
|
251
301
|
const merge = keepChanged && this.isStarted;
|
|
252
302
|
const conflicts = [];
|
|
253
303
|
let current = next;
|
|
@@ -301,8 +351,12 @@ export class FormStore {
|
|
|
301
351
|
this.scheduleDraft();
|
|
302
352
|
return conflicts;
|
|
303
353
|
}
|
|
304
|
-
/**
|
|
354
|
+
/**
|
|
355
|
+
* Brings back the values the form opened with or saved last, and clears every message. The draft goes too. A
|
|
356
|
+
* remembered input opens on the choice kept now, so a form reset after a save starts on what was just saved.
|
|
357
|
+
*/
|
|
305
358
|
reset() {
|
|
359
|
+
this.initial = this.openRemembered({ ...this.initial }, this.given);
|
|
306
360
|
this.values = { ...this.initial };
|
|
307
361
|
this.changedNames.clear();
|
|
308
362
|
this.generation++;
|
|
@@ -323,10 +377,11 @@ export class FormStore {
|
|
|
323
377
|
this.change(Object.entries(values), "code");
|
|
324
378
|
}
|
|
325
379
|
/**
|
|
326
|
-
* A value the user typed or picked, from the input rendered with `stamp
|
|
327
|
-
*
|
|
380
|
+
* A value the user typed or picked, from the input rendered with `stamp`, with what a choice input reported beside it
|
|
381
|
+
* (`item`: a dropdown's option, kept for `pickedItem`). An input that was replaced since (by a value from code, a load,
|
|
382
|
+
* or a reset) can still report its last value as it leaves; that value is ignored.
|
|
328
383
|
*/
|
|
329
|
-
input(name, value, stamp) {
|
|
384
|
+
input(name, value, stamp, item) {
|
|
330
385
|
if (stamp !== this.stamp(name))
|
|
331
386
|
return;
|
|
332
387
|
// Nothing is taken while the values load, not even from an input whose `disabled` was taken off by hand: the
|
|
@@ -335,6 +390,10 @@ export class FormStore {
|
|
|
335
390
|
this.refusedWhileLoading = true;
|
|
336
391
|
return;
|
|
337
392
|
}
|
|
393
|
+
if (item === undefined)
|
|
394
|
+
this.picked.delete(name);
|
|
395
|
+
else
|
|
396
|
+
this.picked.set(name, { value, item });
|
|
338
397
|
this.change([[name, value]], "input");
|
|
339
398
|
}
|
|
340
399
|
/** Marks an input as left by the user: the form's rule messages on it show from now on. */
|
|
@@ -358,7 +417,8 @@ export class FormStore {
|
|
|
358
417
|
if (!text)
|
|
359
418
|
continue;
|
|
360
419
|
const params = this.inputByName.get(name);
|
|
361
|
-
|
|
420
|
+
// A worked-out input is never typed in, so a message naming it is the form's.
|
|
421
|
+
if (params && !params.hidden && !isComputedInput(params))
|
|
362
422
|
this.serverErrors.set(name, text);
|
|
363
423
|
else
|
|
364
424
|
others.push(text);
|
|
@@ -454,6 +514,8 @@ export class FormStore {
|
|
|
454
514
|
}
|
|
455
515
|
/** @internal A submit starts: messages from the last answer go. */
|
|
456
516
|
beginSubmit() {
|
|
517
|
+
// What is in the form is what is saved: a remembered value a picker answers for now would come in after it.
|
|
518
|
+
this.stopAskingPickers();
|
|
457
519
|
const shown = this.shownSnapshot();
|
|
458
520
|
this.serverErrors.clear();
|
|
459
521
|
this.serverMessage = null;
|
|
@@ -474,6 +536,8 @@ export class FormStore {
|
|
|
474
536
|
if (extra) {
|
|
475
537
|
for (const [name, value] of Object.entries(extra)) {
|
|
476
538
|
const params = this.inputByName.get(name);
|
|
539
|
+
if (isComputedInput(params))
|
|
540
|
+
continue;
|
|
477
541
|
const next = params && !params.hidden ? normalizeInputValue(params, value) : value;
|
|
478
542
|
if (inputValuesEqual(this.values[name], next))
|
|
479
543
|
continue;
|
|
@@ -491,6 +555,67 @@ export class FormStore {
|
|
|
491
555
|
this.emit(null);
|
|
492
556
|
this.flushDraft();
|
|
493
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* @internal Whether the input's picker is being asked whether it still offers a remembered value: the input draws
|
|
560
|
+
* loading until it answers, the person picks, or the form opens again.
|
|
561
|
+
*/
|
|
562
|
+
isRemembering(name) {
|
|
563
|
+
return this.asking.has(name);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* @internal The browser storage remembered inputs read and keep (`InputLayoutParams.remember`), set by the form that
|
|
567
|
+
* draws this store once it is in the browser. Inputs the values left without one open on it at once (as the values
|
|
568
|
+
* the form opened with), a picker once it answers that it still offers the value; a form drawn on a server opened
|
|
569
|
+
* without it, so nothing differs while it hydrates.
|
|
570
|
+
*/
|
|
571
|
+
attachMemory(storage) {
|
|
572
|
+
if (this.memory === storage)
|
|
573
|
+
return;
|
|
574
|
+
this.memory = storage;
|
|
575
|
+
if (!storage || !this.isStarted)
|
|
576
|
+
return;
|
|
577
|
+
const opened = this.openRemembered({ ...this.initial }, this.given);
|
|
578
|
+
const changed = [];
|
|
579
|
+
for (const params of this.options.inputs) {
|
|
580
|
+
const { name } = params;
|
|
581
|
+
// Only an input still as the form opened it: one typed in (or restored from a draft) keeps what it has.
|
|
582
|
+
if (!params.remember || isComputedInput(params) || this.changedNames.has(name) || inputValuesEqual(opened[name], this.initial[name]))
|
|
583
|
+
continue;
|
|
584
|
+
this.initial = { ...this.initial, [name]: opened[name] };
|
|
585
|
+
this.values = { ...this.values, [name]: opened[name] };
|
|
586
|
+
this.nextRevision(name);
|
|
587
|
+
this.bump(name);
|
|
588
|
+
changed.push(name);
|
|
589
|
+
}
|
|
590
|
+
if (!changed.length)
|
|
591
|
+
return;
|
|
592
|
+
this.checkAllRules();
|
|
593
|
+
this.checkCross();
|
|
594
|
+
this.bumpFollowers(changed);
|
|
595
|
+
this.emit(null);
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Inputs that follow ones memory opened (`dependsOn`: a `compute.from`, `showWhen`, `spanWhen`, `labelWhen`, or a
|
|
599
|
+
* `narrowedBy`) draw again with their params read afresh, as after a change: a cell redraws only on its own bump.
|
|
600
|
+
*/
|
|
601
|
+
bumpFollowers(names) {
|
|
602
|
+
for (const name of this.followersOf(names))
|
|
603
|
+
if (!names.includes(name))
|
|
604
|
+
this.bump(name);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* @internal A save: each remembered input keeps the value saved (an empty one forgets), for the next form that asks -
|
|
608
|
+
* under the saved values' `per` value, and only where its `when` holds on them.
|
|
609
|
+
*/
|
|
610
|
+
rememberSaved(values) {
|
|
611
|
+
if (!this.memory)
|
|
612
|
+
return;
|
|
613
|
+
const read = (name) => values[name];
|
|
614
|
+
for (const params of this.options.inputs) {
|
|
615
|
+
if (params.remember && !params.hidden && !isComputedInput(params))
|
|
616
|
+
writeInputMemory(this.memory, params, read(params.name), read);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
494
619
|
/** @internal A save after which the form went back to its opening values (`resetOnSuccess`). */
|
|
495
620
|
markSaved() {
|
|
496
621
|
this.patchStatus({ submitting: false, savedAt: Date.now() });
|
|
@@ -521,20 +646,160 @@ export class FormStore {
|
|
|
521
646
|
/** Each input's value in the type the input reports; a missing one is the input's default. Other fields stay as they came. */
|
|
522
647
|
normalize(values) {
|
|
523
648
|
const result = { ...values };
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if (params
|
|
527
|
-
|
|
649
|
+
// A worked-out input (`compute`) holds no value: a field the data has under its name is not the form's to send.
|
|
650
|
+
for (const params of this.options.inputs)
|
|
651
|
+
if (isComputedInput(params))
|
|
652
|
+
delete result[params.name];
|
|
653
|
+
const inputs = this.options.inputs.filter(params => holdsValue(params) && !isComputedInput(params));
|
|
654
|
+
// Which inputs are hidden is judged on these values: an input shown by another's value (`showWhen`) whose other input
|
|
655
|
+
// gets its value in this very load (its default, the record's). So every input is worked out as shown first...
|
|
656
|
+
for (const params of inputs)
|
|
528
657
|
result[params.name] = params.name in values ? normalizeInputValue(params, values[params.name]) : initialInputValue(params);
|
|
658
|
+
// ...then a hidden input's value goes back to what the data held (a picture has none: the record keeps what it came with).
|
|
659
|
+
const hidden = this.readingFrom(result, () => inputs.filter(params => params.hidden));
|
|
660
|
+
for (const params of hidden) {
|
|
661
|
+
if (params.name in values)
|
|
662
|
+
result[params.name] = values[params.name];
|
|
663
|
+
else
|
|
664
|
+
delete result[params.name];
|
|
665
|
+
}
|
|
666
|
+
return result;
|
|
667
|
+
}
|
|
668
|
+
/** Runs `work` with `get` answering from these values (a load's, a reset's, a draft's), and gives back its answer. */
|
|
669
|
+
readingFrom(values, work) {
|
|
670
|
+
const before = this.reading;
|
|
671
|
+
this.reading = values;
|
|
672
|
+
try {
|
|
673
|
+
return work();
|
|
674
|
+
}
|
|
675
|
+
finally {
|
|
676
|
+
this.reading = before;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* The values with each remembered input the given values leave without one opened on the company's preferred value
|
|
681
|
+
* or the person's last choice (`rememberedOpeningNow`), its `when` and `per` read among these values; without storage
|
|
682
|
+
* (on a server, before the form is drawn) only a preferred value applies. A kept choice no longer offered is forgotten.
|
|
683
|
+
* A picker that must be asked first opens nothing now: it is asked, and its answer opens the input later
|
|
684
|
+
* (`askPickers`) - the round any earlier opening started stops here. An input memory opened before (a reset after a
|
|
685
|
+
* save, the storage arriving) that now opens on nothing takes the value the given values give it back.
|
|
686
|
+
*/
|
|
687
|
+
openRemembered(values, given) {
|
|
688
|
+
let result = values;
|
|
689
|
+
// The values as they open, in form order: a group opened on its kept value names the package kept for it.
|
|
690
|
+
const read = (name) => result[name];
|
|
691
|
+
const asks = [];
|
|
692
|
+
const set = (name, value) => {
|
|
693
|
+
if (result === values)
|
|
694
|
+
result = { ...values };
|
|
695
|
+
result[name] = value;
|
|
696
|
+
};
|
|
697
|
+
// Shown or hidden as the values being opened say, not as the store's previous ones do.
|
|
698
|
+
const shown = this.readingFrom(values, () => this.options.inputs.filter(params => params.remember && !params.hidden && holdsValue(params) && !isComputedInput(params)));
|
|
699
|
+
for (const params of shown) {
|
|
700
|
+
const { name } = params;
|
|
701
|
+
if (name in given && !isEmptyInputValue(params, given[name]))
|
|
702
|
+
continue;
|
|
703
|
+
// A picker is asked in the browser only, once the storage came: a server's draw never calls it or reads its cache.
|
|
704
|
+
if (!this.memory && isPickerInput(params))
|
|
705
|
+
continue;
|
|
706
|
+
const stored = this.memory ? readInputMemory(this.memory, params.remember, read) : undefined;
|
|
707
|
+
const opening = rememberedOpeningNow(params, stored, read);
|
|
708
|
+
if (opening.forget)
|
|
709
|
+
forgetInputMemory(this.memory, params.remember, read);
|
|
710
|
+
if (opening.ask)
|
|
711
|
+
asks.push({ params, stored });
|
|
712
|
+
if (opening.value !== undefined) {
|
|
713
|
+
const value = normalizeInputValue(params, opening.value);
|
|
714
|
+
set(name, value);
|
|
715
|
+
this.fromMemory.set(name, value);
|
|
716
|
+
}
|
|
717
|
+
else if (this.fromMemory.has(name)) {
|
|
718
|
+
// Still the value memory opened it on: the input's own comes back, so a choice no longer offered is not left in.
|
|
719
|
+
if (inputValuesEqual(result[name], this.fromMemory.get(name))) {
|
|
720
|
+
set(name, name in given ? normalizeInputValue(params, given[name]) : initialInputValue(params));
|
|
721
|
+
}
|
|
722
|
+
this.fromMemory.delete(name);
|
|
723
|
+
}
|
|
529
724
|
}
|
|
725
|
+
this.askPickers(asks, read);
|
|
530
726
|
return result;
|
|
531
727
|
}
|
|
728
|
+
/** Stops asking pickers about remembered values: their answers no longer open anything, and their inputs stop loading. */
|
|
729
|
+
stopAskingPickers() {
|
|
730
|
+
this.memoryRound?.abort();
|
|
731
|
+
this.memoryRound = null;
|
|
732
|
+
const asked = [...this.asking];
|
|
733
|
+
this.asking.clear();
|
|
734
|
+
for (const name of asked)
|
|
735
|
+
this.bump(name);
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Asks each picker whether it still offers the remembered value (`findRememberedOpening`), the input drawn loading
|
|
739
|
+
* meanwhile. An answer forgets a choice no longer offered, and opens an offered one (`openAsked`) only while the input
|
|
740
|
+
* is still asked about - no pick, no value from code since - and the values it was judged on (`rememberDependencies`:
|
|
741
|
+
* the group that narrows it, the one it is kept per, its `when`) are still the form's: a group changed meanwhile opens
|
|
742
|
+
* nothing.
|
|
743
|
+
*/
|
|
744
|
+
askPickers(asks, read) {
|
|
745
|
+
this.stopAskingPickers();
|
|
746
|
+
if (!asks.length)
|
|
747
|
+
return;
|
|
748
|
+
const round = new AbortController();
|
|
749
|
+
this.memoryRound = round;
|
|
750
|
+
const memory = this.memory;
|
|
751
|
+
for (const { params, stored } of asks) {
|
|
752
|
+
const { name } = params;
|
|
753
|
+
const judgedOn = rememberDependencies(params).map(other => [other, read(other)]);
|
|
754
|
+
this.asking.add(name);
|
|
755
|
+
this.bump(name);
|
|
756
|
+
void findRememberedOpening(params, stored, read, round.signal).then(opening => {
|
|
757
|
+
if (round.signal.aborted)
|
|
758
|
+
return;
|
|
759
|
+
if (opening.forget)
|
|
760
|
+
forgetInputMemory(memory, params.remember, read);
|
|
761
|
+
if (!this.asking.delete(name))
|
|
762
|
+
return;
|
|
763
|
+
const still = judgedOn.every(([other, value]) => inputValuesEqual(this.values[other], value));
|
|
764
|
+
if (opening.value !== undefined && still)
|
|
765
|
+
this.openAsked(name, opening.value);
|
|
766
|
+
else
|
|
767
|
+
this.bump(name);
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* A picker answered for a remembered value: the input opens on it, as the value the form opened with (not a change) -
|
|
773
|
+
* only while it is as the form opened it and still on the form. One the person typed in, or a draft gave back, keeps
|
|
774
|
+
* what it has.
|
|
775
|
+
*/
|
|
776
|
+
openAsked(name, value) {
|
|
777
|
+
const params = this.inputByName.get(name);
|
|
778
|
+
if (!params || params.hidden || !holdsValue(params) || isComputedInput(params) || this.changedNames.has(name)) {
|
|
779
|
+
this.bump(name);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
const next = normalizeInputValue(params, value);
|
|
783
|
+
this.fromMemory.set(name, next);
|
|
784
|
+
if (inputValuesEqual(this.values[name], next)) {
|
|
785
|
+
this.bump(name);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
this.initial = { ...this.initial, [name]: next };
|
|
789
|
+
this.values = { ...this.values, [name]: next };
|
|
790
|
+
this.nextRevision(name);
|
|
791
|
+
this.bump(name);
|
|
792
|
+
this.checkAllRules();
|
|
793
|
+
this.checkCross();
|
|
794
|
+
this.bumpFollowers([name]);
|
|
795
|
+
this.emit(null);
|
|
796
|
+
}
|
|
532
797
|
/** Inputs that were not there, or were hidden, get their value in the input's type, as a load gives it. */
|
|
533
798
|
adoptNewInputs(previous) {
|
|
534
799
|
const before = new Map(previous.map(params => [params.name, params]));
|
|
535
800
|
for (const params of this.options.inputs) {
|
|
536
801
|
const old = before.get(params.name);
|
|
537
|
-
if (params.hidden || (old && !old.hidden))
|
|
802
|
+
if (params.hidden || !holdsValue(params) || isComputedInput(params) || (old && !old.hidden))
|
|
538
803
|
continue;
|
|
539
804
|
const { name } = params;
|
|
540
805
|
const value = name in this.values ? normalizeInputValue(params, this.values[name]) : initialInputValue(params);
|
|
@@ -552,15 +817,22 @@ export class FormStore {
|
|
|
552
817
|
const shown = new Map();
|
|
553
818
|
const remounted = new Set();
|
|
554
819
|
const written = [];
|
|
820
|
+
/** Inputs a picker was asked about for a remembered value: this change is theirs now, and they stop loading. */
|
|
821
|
+
const unasked = [];
|
|
555
822
|
const remember = (name) => {
|
|
556
823
|
if (!shown.has(name))
|
|
557
824
|
shown.set(name, this.shownError(name));
|
|
558
825
|
};
|
|
559
826
|
const write = (name, value, from) => {
|
|
560
827
|
const params = this.inputByName.get(name);
|
|
828
|
+
// A worked-out input holds no value: one set from code (or a draft, an `onFieldChange` patch) is not taken.
|
|
829
|
+
if (isComputedInput(params))
|
|
830
|
+
return;
|
|
561
831
|
const next = params && !params.hidden && from === "code" ? normalizeInputValue(params, value) : value;
|
|
562
832
|
if (inputValuesEqual(this.values[name], next))
|
|
563
833
|
return;
|
|
834
|
+
if (this.asking.delete(name))
|
|
835
|
+
unasked.push(name);
|
|
564
836
|
remember(name);
|
|
565
837
|
this.values = { ...this.values, [name]: next };
|
|
566
838
|
if (from === "code" && params) {
|
|
@@ -592,6 +864,27 @@ export class FormStore {
|
|
|
592
864
|
write(field, value, "code");
|
|
593
865
|
}
|
|
594
866
|
}
|
|
867
|
+
// An input whose options another narrows (`narrowedBy`) loses the value it picked among the other's old options when
|
|
868
|
+
// a person changes the other one; a value set from code (a load, a patch) is the code's to keep consistent. Down a
|
|
869
|
+
// chain too: a Country change clears the Region, and with it the District picked among that Region's (each input
|
|
870
|
+
// once, whatever order the form lists them in; one the person set in this same change keeps its value).
|
|
871
|
+
if (source === "input") {
|
|
872
|
+
const changed = new Set(written);
|
|
873
|
+
let cleared = true;
|
|
874
|
+
while (cleared) {
|
|
875
|
+
cleared = false;
|
|
876
|
+
for (const params of this.options.inputs) {
|
|
877
|
+
const other = params.narrowedBy?.name;
|
|
878
|
+
if (!other || !changed.has(other) || changed.has(params.name))
|
|
879
|
+
continue;
|
|
880
|
+
changed.add(params.name);
|
|
881
|
+
cleared = true;
|
|
882
|
+
const kept = this.values[params.name];
|
|
883
|
+
if (kept !== undefined && kept !== null && kept !== "" && !(Array.isArray(kept) && kept.length === 0))
|
|
884
|
+
write(params.name, null, "code");
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
595
888
|
// Inputs that follow a changed one (`dependsOn`) draw again with their params read afresh, and their rule is asked
|
|
596
889
|
// again: a getter may have just made one required, read-only or hidden.
|
|
597
890
|
const followers = this.followersOf(written);
|
|
@@ -604,7 +897,7 @@ export class FormStore {
|
|
|
604
897
|
if (!shown.has(name))
|
|
605
898
|
shown.set(name, this.shownWith(name, crossBefore));
|
|
606
899
|
for (const [name, before] of shown)
|
|
607
|
-
if (remounted.has(name) || followers.has(name) || this.shownError(name) !== before)
|
|
900
|
+
if (remounted.has(name) || followers.has(name) || unasked.includes(name) || this.shownError(name) !== before)
|
|
608
901
|
this.bump(name);
|
|
609
902
|
this.patchStatus({ changed: this.changedNames.size > 0, message: this.formMessage(), problems: this.problemList() });
|
|
610
903
|
this.emit(source === "input" ? written[0] : written.length === 1 ? written[0] : null);
|
|
@@ -613,15 +906,16 @@ export class FormStore {
|
|
|
613
906
|
this.draftDropped = false;
|
|
614
907
|
this.scheduleDraft();
|
|
615
908
|
}
|
|
616
|
-
/** The inputs whose `dependsOn` names any of `names`. */
|
|
909
|
+
/** The inputs whose `dependsOn` (or `narrowedBy`) names any of `names`. */
|
|
617
910
|
followersOf(names) {
|
|
618
911
|
const found = new Set();
|
|
619
912
|
if (!names.length)
|
|
620
913
|
return found;
|
|
621
914
|
const changed = new Set(names);
|
|
622
|
-
for (const params of this.options.inputs)
|
|
623
|
-
if (params.dependsOn?.some(name => changed.has(name)))
|
|
915
|
+
for (const params of this.options.inputs) {
|
|
916
|
+
if (params.dependsOn?.some(name => changed.has(name)) || (params.narrowedBy?.name !== undefined && changed.has(params.narrowedBy.name)))
|
|
624
917
|
found.add(params.name);
|
|
918
|
+
}
|
|
625
919
|
return found;
|
|
626
920
|
}
|
|
627
921
|
checkRule(name) {
|
|
@@ -631,7 +925,7 @@ export class FormStore {
|
|
|
631
925
|
return;
|
|
632
926
|
}
|
|
633
927
|
this.staleRules.delete(name);
|
|
634
|
-
const message = params && !params.hidden && !this.options.locked ? validateInput(params, this.values[name], this.options.context) : null;
|
|
928
|
+
const message = params && !params.hidden && !isComputedInput(params) && !this.options.locked ? validateInput(params, this.values[name], this.options.context) : null;
|
|
635
929
|
if (message)
|
|
636
930
|
this.ruleErrors.set(name, message);
|
|
637
931
|
else
|
|
@@ -657,7 +951,7 @@ export class FormStore {
|
|
|
657
951
|
if (!message)
|
|
658
952
|
continue;
|
|
659
953
|
const params = this.inputByName.get(name);
|
|
660
|
-
if (params && !params.hidden)
|
|
954
|
+
if (params && !params.hidden && !isComputedInput(params))
|
|
661
955
|
next.set(name, message);
|
|
662
956
|
else
|
|
663
957
|
others.push(message);
|
|
@@ -751,9 +1045,11 @@ export class FormStore {
|
|
|
751
1045
|
}
|
|
752
1046
|
const entries = [];
|
|
753
1047
|
const conflicts = [];
|
|
1048
|
+
// An input the draft's own values show (its other input among them) is restored too.
|
|
1049
|
+
const hidden = this.readingFrom({ ...this.values, ...draft.values }, () => new Set(this.options.inputs.filter(params => params.hidden).map(params => params.name)));
|
|
754
1050
|
for (const params of this.options.inputs) {
|
|
755
1051
|
const { name } = params;
|
|
756
|
-
if (
|
|
1052
|
+
if (hidden.has(name) || isComputedInput(params) || !(name in draft.values) || this.changedNames.has(name))
|
|
757
1053
|
continue;
|
|
758
1054
|
const value = draft.values[name];
|
|
759
1055
|
if (sameData(value, this.values[name]))
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type FormValues, type InputCondition, type InputLabelWhen, type InputParams, type InputSpanWhen } from "../Interfaces/FormInterfaces.ts";
|
|
2
|
+
/** No value: null, undefined, an empty or blank text, an empty list. `false` and `0` are values. */
|
|
3
|
+
export declare function isEmptyValue(value: unknown): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Whether one condition holds for the other input's value. A list value (a multiple choice) holds `in` when any of its
|
|
6
|
+
* items is listed, and `notIn` when none is. Every key the condition has must hold; one with none holds always.
|
|
7
|
+
*/
|
|
8
|
+
export declare function conditionHolds(condition: InputCondition, value: unknown): boolean;
|
|
9
|
+
/** The conditions of a `showWhen` or a `readOnlyWhen`, as a list. */
|
|
10
|
+
export declare function conditionsOf(conditions: InputParams["showWhen"]): readonly InputCondition[];
|
|
11
|
+
/** Whether every condition holds, reading the other inputs' values with `read`. None holds always. */
|
|
12
|
+
export declare function conditionsHold(conditions: InputParams["showWhen"], read: (name: string) => unknown): boolean;
|
|
13
|
+
/** Whether an input with this `showWhen` shows, reading the other inputs' values with `read`: every condition must hold. */
|
|
14
|
+
export declare function inputShown(showWhen: InputParams["showWhen"], read: (name: string) => unknown): boolean;
|
|
15
|
+
/** Whether an input with this `readOnlyWhen` is read-only, reading the other inputs' values with `read`: every condition must hold. */
|
|
16
|
+
export declare function inputReadOnly(readOnlyWhen: InputParams["readOnlyWhen"], read: (name: string) => unknown): boolean;
|
|
17
|
+
/** The text of `texts` for the other input's value (compared as text), or undefined when none is listed for it. */
|
|
18
|
+
export declare function textFor(texts: Readonly<Record<string, string>> | undefined, value: unknown): string | undefined;
|
|
19
|
+
/** The label an input with this `labelWhen` shows for the other input's value: listed, else `otherwise`, else its own. */
|
|
20
|
+
export declare function labelFor(labelWhen: InputLabelWhen, value: unknown, own: InputParams["label"]): InputParams["label"];
|
|
21
|
+
/**
|
|
22
|
+
* The columns an input with this `spanWhen` spans, reading the other inputs' values with `read`: `spanWhen.span` while
|
|
23
|
+
* every condition of `when` holds, else its own `span` (none: undefined, the whole row).
|
|
24
|
+
*/
|
|
25
|
+
export declare function spanFor(spanWhen: InputSpanWhen | undefined, read: (name: string) => unknown, own: InputParams["span"]): InputParams["span"];
|
|
26
|
+
/**
|
|
27
|
+
* The inputs with a live `hidden` for each one that has `showWhen`, a live `readOnly` for each one that has
|
|
28
|
+
* `readOnlyWhen` - its own `hidden` / `readOnly` (a value or a getter), or else the conditions, read through `read` as
|
|
29
|
+
* the values are now - a live `label` (and `placeholder`) for each one that has `labelWhen`, and a live `span` for each
|
|
30
|
+
* one that has `spanWhen` (its own `span`, a value or a getter, while the conditions do not hold); and its `dependsOn`
|
|
31
|
+
* joined by the names they read (and by the inputs a `compute` is worked out from), so the form draws it again (and asks
|
|
32
|
+
* its rule again) when they change. The other params are copied with their getters as they are. The same array when no
|
|
33
|
+
* input has any.
|
|
34
|
+
*
|
|
35
|
+
* @example withInputConditions(inputs, name => store.get(name))
|
|
36
|
+
*/
|
|
37
|
+
export declare function withInputConditions(inputs: readonly InputParams[], read: (name: string) => unknown): readonly InputParams[];
|
|
38
|
+
/** Whether the input is worked out from others (`compute`): it holds no value of the form's and is never typed in. */
|
|
39
|
+
export declare function isComputedInput(params: InputParams | undefined): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* What a worked-out input (`compute`) shows for these values and the form's `computeWith`. A page's rule that throws
|
|
42
|
+
* shows nothing, and says so in the console, so one bad figure never takes the form down with it.
|
|
43
|
+
*/
|
|
44
|
+
export declare function computedValue(params: InputParams, values: FormValues, withValue: unknown): unknown;
|
|
45
|
+
/**
|
|
46
|
+
* A worked-out value as its box writes it: a Number input's figure with its `decimals` always written (1,073.50), grouped,
|
|
47
|
+
* in `locale` (the person's, unless the params name their own); any other input's value as text. Null, undefined, "",
|
|
48
|
+
* NaN, or a figure that is not finite, write nothing, so the box is blank rather than "NaN" or "0.00".
|
|
49
|
+
*/
|
|
50
|
+
export declare function computedText(params: InputParams, value: unknown, locale?: string): string;
|