nexus-shared 3.2.0 → 4.1.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.
Files changed (271) hide show
  1. package/CHANGELOG.md +2359 -0
  2. package/README.md +1911 -1123
  3. package/dist/Client.Index.d.ts +27 -0
  4. package/dist/Client.Index.js +27 -0
  5. package/dist/Components/Chats/ChatComposer.d.ts +7 -1
  6. package/dist/Components/Chats/ChatComposer.js +9 -6
  7. package/dist/Components/Chats/ChatParts.d.ts +15 -2
  8. package/dist/Components/Chats/ChatParts.js +15 -4
  9. package/dist/Components/Chats/ChatPopup.d.ts +46 -0
  10. package/dist/Components/Chats/ChatPopup.js +34 -0
  11. package/dist/Components/Chats/ChatThread.d.ts +9 -1
  12. package/dist/Components/Chats/ChatThread.js +54 -6
  13. package/dist/Components/Dashboards/DashboardBoard.d.ts +62 -0
  14. package/dist/Components/Dashboards/DashboardBoard.js +594 -0
  15. package/dist/Components/Dashboards/DashboardWidgets.d.ts +155 -0
  16. package/dist/Components/Dashboards/DashboardWidgets.js +159 -0
  17. package/dist/Components/Dashboards/WidgetFrame.d.ts +51 -0
  18. package/dist/Components/Dashboards/WidgetFrame.js +45 -0
  19. package/dist/Components/Documents/Button.d.ts +6 -1
  20. package/dist/Components/Documents/Button.js +2 -2
  21. package/dist/Components/Documents/DocumentFrame.d.ts +50 -0
  22. package/dist/Components/Documents/DocumentFrame.js +83 -0
  23. package/dist/Components/Documents/Menu.d.ts +4 -0
  24. package/dist/Components/Documents/Menu.js +40 -24
  25. package/dist/Components/Documents/PrintDocument.d.ts +95 -0
  26. package/dist/Components/Documents/PrintDocument.js +62 -0
  27. package/dist/Components/Documents/TabButtons.d.ts +6 -0
  28. package/dist/Components/Documents/TabButtons.js +5 -4
  29. package/dist/Components/Documents/Tabs.d.ts +59 -0
  30. package/dist/Components/Documents/Tabs.js +70 -0
  31. package/dist/Components/Domains/DomainCard.d.ts +98 -0
  32. package/dist/Components/Domains/DomainCard.js +182 -0
  33. package/dist/Components/Forms/ApiForm.js +24 -3
  34. package/dist/Components/Forms/Crud.d.ts +2 -2
  35. package/dist/Components/Forms/Crud.js +628 -247
  36. package/dist/Components/Forms/CrudActions.d.ts +137 -0
  37. package/dist/Components/Forms/CrudActions.js +214 -0
  38. package/dist/Components/Forms/CrudHistory.d.ts +6 -1
  39. package/dist/Components/Forms/CrudHistory.js +14 -6
  40. package/dist/Components/Forms/CrudNarrowPicker.d.ts +31 -0
  41. package/dist/Components/Forms/CrudNarrowPicker.js +180 -0
  42. package/dist/Components/Forms/CrudParts.d.ts +37 -2
  43. package/dist/Components/Forms/CrudParts.js +123 -7
  44. package/dist/Components/Forms/CrudRowForms.d.ts +26 -0
  45. package/dist/Components/Forms/CrudRowForms.js +174 -0
  46. package/dist/Components/Forms/CrudScopes.d.ts +28 -0
  47. package/dist/Components/Forms/CrudScopes.js +76 -0
  48. package/dist/Components/Forms/CrudTree.d.ts +90 -0
  49. package/dist/Components/Forms/CrudTree.js +363 -0
  50. package/dist/Components/Forms/CrudWorkspace.d.ts +73 -0
  51. package/dist/Components/Forms/CrudWorkspace.js +177 -0
  52. package/dist/Components/Forms/ExcelImport.js +3 -3
  53. package/dist/Components/Forms/Form.d.ts +6 -5
  54. package/dist/Components/Forms/Form.js +134 -21
  55. package/dist/Components/Forms/FormLeaveGuard.d.ts +44 -0
  56. package/dist/Components/Forms/FormLeaveGuard.js +65 -0
  57. package/dist/Components/Forms/FormQuestion.d.ts +6 -0
  58. package/dist/Components/Forms/FormQuestion.js +28 -0
  59. package/dist/Components/Forms/RecordFlows.d.ts +50 -0
  60. package/dist/Components/Forms/RecordFlows.js +282 -0
  61. package/dist/Components/Forms/SubmitForm.d.ts +37 -2
  62. package/dist/Components/Forms/SubmitForm.js +182 -10
  63. package/dist/Components/Inputs/CheckBox.js +1 -1
  64. package/dist/Components/Inputs/ChoiceField.d.ts +21 -1
  65. package/dist/Components/Inputs/ChoiceField.js +28 -2
  66. package/dist/Components/Inputs/ComputedInput.d.ts +51 -0
  67. package/dist/Components/Inputs/ComputedInput.js +179 -0
  68. package/dist/Components/Inputs/Dropdown.d.ts +13 -0
  69. package/dist/Components/Inputs/Dropdown.js +8 -6
  70. package/dist/Components/Inputs/DropdownParts.d.ts +3 -1
  71. package/dist/Components/Inputs/DropdownParts.js +16 -4
  72. package/dist/Components/Inputs/Field.d.ts +7 -1
  73. package/dist/Components/Inputs/Field.js +2 -2
  74. package/dist/Components/Inputs/GroupForm.d.ts +6 -6
  75. package/dist/Components/Inputs/GroupForm.js +12 -12
  76. package/dist/Components/Inputs/InputRenderer.d.ts +13 -6
  77. package/dist/Components/Inputs/InputRenderer.js +37 -32
  78. package/dist/Components/Inputs/MarkdownEditor.d.ts +6 -0
  79. package/dist/Components/Inputs/MarkdownEditor.js +32 -6
  80. package/dist/Components/Inputs/PictureInput.d.ts +41 -0
  81. package/dist/Components/Inputs/PictureInput.js +75 -0
  82. package/dist/Components/Inputs/RadioGroup.d.ts +16 -3
  83. package/dist/Components/Inputs/RadioGroup.js +8 -3
  84. package/dist/Components/Inputs/RowsInput.d.ts +15 -2
  85. package/dist/Components/Inputs/RowsInput.js +96 -8
  86. package/dist/Components/Inputs/TabularForm.d.ts +5 -5
  87. package/dist/Components/Inputs/TabularForm.js +45 -32
  88. package/dist/Components/Inputs/TextArea.d.ts +6 -0
  89. package/dist/Components/Inputs/TextArea.js +11 -2
  90. package/dist/Components/Inputs/TextBoxTypes.d.ts +6 -0
  91. package/dist/Components/Inputs/TextBoxTypes.js +17 -0
  92. package/dist/Components/Inputs/TreeView.d.ts +76 -2
  93. package/dist/Components/Inputs/TreeView.js +227 -16
  94. package/dist/Components/Layouts/MessageBox.d.ts +15 -2
  95. package/dist/Components/Layouts/MessageBox.js +56 -7
  96. package/dist/Components/Layouts/Popup.d.ts +34 -7
  97. package/dist/Components/Layouts/Popup.js +143 -13
  98. package/dist/Components/Layouts/PopupDock.d.ts +3 -1
  99. package/dist/Components/Layouts/PopupDock.js +2 -1
  100. package/dist/Components/Layouts/PopupHost.js +5 -4
  101. package/dist/Components/Messages/MailClient.d.ts +50 -0
  102. package/dist/Components/Messages/MailClient.js +410 -0
  103. package/dist/Components/Messages/MailColumns.d.ts +21 -0
  104. package/dist/Components/Messages/MailColumns.js +92 -0
  105. package/dist/Components/Messages/MailCompose.d.ts +46 -0
  106. package/dist/Components/Messages/MailCompose.js +154 -0
  107. package/dist/Components/Messages/MailReader.d.ts +43 -0
  108. package/dist/Components/Messages/MailReader.js +207 -0
  109. package/dist/Components/Messages/MailThread.d.ts +71 -0
  110. package/dist/Components/Messages/MailThread.js +185 -0
  111. package/dist/Components/Messages/MessageBell.d.ts +41 -0
  112. package/dist/Components/Messages/MessageBell.js +76 -0
  113. package/dist/Components/Viewers/DataTable.js +153 -49
  114. package/dist/Components/Viewers/DataTableColumns.d.ts +2 -2
  115. package/dist/Components/Viewers/DataTableColumns.js +2 -1
  116. package/dist/Components/Viewers/DataTableFilters.d.ts +1 -1
  117. package/dist/Components/Viewers/DataTableFilters.js +17 -2
  118. package/dist/Components/Viewers/DataTableParts.d.ts +23 -5
  119. package/dist/Components/Viewers/DataTableParts.js +26 -14
  120. package/dist/Components/Viewers/OwnerFiles.d.ts +227 -0
  121. package/dist/Components/Viewers/OwnerFiles.js +700 -0
  122. package/dist/Components/Viewers/QrCodeImage.d.ts +22 -0
  123. package/dist/Components/Viewers/QrCodeImage.js +15 -0
  124. package/dist/Components/Viewers/RecordCards.d.ts +16 -0
  125. package/dist/Components/Viewers/RecordCards.js +509 -0
  126. package/dist/Helpers/ApiModules.js +4 -0
  127. package/dist/Helpers/AuditEntries.d.ts +50 -0
  128. package/dist/Helpers/AuditEntries.js +96 -0
  129. package/dist/Helpers/BusyHooks.d.ts +6 -0
  130. package/dist/Helpers/BusyHooks.js +39 -0
  131. package/dist/Helpers/ChatBackend.d.ts +8 -1
  132. package/dist/Helpers/ChatBackend.js +18 -0
  133. package/dist/Helpers/ChatHelpers.d.ts +2 -0
  134. package/dist/Helpers/ChatHelpers.js +4 -1
  135. package/dist/Helpers/CrudBackend.d.ts +20 -2
  136. package/dist/Helpers/CrudBackend.js +46 -2
  137. package/dist/Helpers/CrudColumns.d.ts +91 -6
  138. package/dist/Helpers/CrudColumns.js +148 -7
  139. package/dist/Helpers/CrudDetailsTabs.d.ts +58 -0
  140. package/dist/Helpers/CrudDetailsTabs.js +126 -0
  141. package/dist/Helpers/CrudHelpers.d.ts +116 -2
  142. package/dist/Helpers/CrudHelpers.js +256 -0
  143. package/dist/Helpers/CrudNarrowPick.d.ts +83 -0
  144. package/dist/Helpers/CrudNarrowPick.js +157 -0
  145. package/dist/Helpers/CrudPickerPlan.d.ts +58 -0
  146. package/dist/Helpers/CrudPickerPlan.js +46 -0
  147. package/dist/Helpers/CrudRowForms.d.ts +32 -0
  148. package/dist/Helpers/CrudRowForms.js +76 -0
  149. package/dist/Helpers/CrudScopes.d.ts +32 -0
  150. package/dist/Helpers/CrudScopes.js +79 -0
  151. package/dist/Helpers/DashboardHelpers.d.ts +115 -0
  152. package/dist/Helpers/DashboardHelpers.js +305 -0
  153. package/dist/Helpers/DomainCards.d.ts +140 -0
  154. package/dist/Helpers/DomainCards.js +370 -0
  155. package/dist/Helpers/DropdownHelpers.d.ts +3 -1
  156. package/dist/Helpers/DropdownHelpers.js +6 -2
  157. package/dist/Helpers/ExcelHelpers.d.ts +1 -1
  158. package/dist/Helpers/ExcelHelpers.js +12 -11
  159. package/dist/Helpers/FileAdapter.d.ts +158 -0
  160. package/dist/Helpers/FileAdapter.js +173 -0
  161. package/dist/Helpers/FilePrimaries.d.ts +48 -0
  162. package/dist/Helpers/FilePrimaries.js +138 -0
  163. package/dist/Helpers/FormConfirm.d.ts +32 -0
  164. package/dist/Helpers/FormConfirm.js +40 -0
  165. package/dist/Helpers/FormStore.d.ts +100 -5
  166. package/dist/Helpers/FormStore.js +368 -25
  167. package/dist/Helpers/InputConditions.d.ts +56 -0
  168. package/dist/Helpers/InputConditions.js +192 -0
  169. package/dist/Helpers/InputMemory.d.ts +106 -0
  170. package/dist/Helpers/InputMemory.js +395 -0
  171. package/dist/Helpers/InputNarrowing.d.ts +50 -0
  172. package/dist/Helpers/InputNarrowing.js +148 -0
  173. package/dist/Helpers/InputParamsHelpers.d.ts +44 -1
  174. package/dist/Helpers/InputParamsHelpers.js +157 -74
  175. package/dist/Helpers/MailClientHelpers.d.ts +96 -0
  176. package/dist/Helpers/MailClientHelpers.js +211 -0
  177. package/dist/Helpers/MessageBackend.d.ts +152 -0
  178. package/dist/Helpers/MessageBackend.js +329 -0
  179. package/dist/Helpers/MessageHelpers.d.ts +160 -0
  180. package/dist/Helpers/MessageHelpers.js +573 -0
  181. package/dist/Helpers/MessageUnread.d.ts +95 -0
  182. package/dist/Helpers/MessageUnread.js +245 -0
  183. package/dist/Helpers/NexusDriveAdapter.d.ts +71 -0
  184. package/dist/Helpers/NexusDriveAdapter.js +236 -0
  185. package/dist/Helpers/NumberHelpers.d.ts +156 -0
  186. package/dist/Helpers/NumberHelpers.js +242 -0
  187. package/dist/Helpers/PagedPicker.d.ts +42 -0
  188. package/dist/Helpers/PagedPicker.js +85 -0
  189. package/dist/Helpers/Permissions.js +2 -1
  190. package/dist/Helpers/QrCode.d.ts +26 -0
  191. package/dist/Helpers/QrCode.js +463 -0
  192. package/dist/Helpers/RecordCards.d.ts +205 -0
  193. package/dist/Helpers/RecordCards.js +378 -0
  194. package/dist/Helpers/RequestCache.d.ts +14 -2
  195. package/dist/Helpers/RequestCache.js +22 -3
  196. package/dist/Helpers/RowsStore.d.ts +61 -2
  197. package/dist/Helpers/RowsStore.js +203 -16
  198. package/dist/Helpers/ServerPreview.d.ts +69 -0
  199. package/dist/Helpers/ServerPreview.js +99 -0
  200. package/dist/Helpers/TableColumns.js +4 -3
  201. package/dist/Helpers/TableExport.d.ts +7 -1
  202. package/dist/Helpers/TableExport.js +9 -3
  203. package/dist/Helpers/TableFilters.d.ts +1 -1
  204. package/dist/Helpers/TableFilters.js +24 -23
  205. package/dist/Helpers/TableHelpers.d.ts +38 -4
  206. package/dist/Helpers/TableHelpers.js +78 -10
  207. package/dist/Helpers/TableJoins.d.ts +13 -0
  208. package/dist/Helpers/TableJoins.js +34 -0
  209. package/dist/Helpers/TableStore.d.ts +28 -4
  210. package/dist/Helpers/TableStore.js +73 -11
  211. package/dist/Helpers/ToolbarFit.d.ts +23 -0
  212. package/dist/Helpers/ToolbarFit.js +30 -0
  213. package/dist/Helpers/TreeStore.d.ts +156 -6
  214. package/dist/Helpers/TreeStore.js +547 -18
  215. package/dist/Interfaces/ApiInterfaces.d.ts +10 -1
  216. package/dist/Interfaces/ApiInterfaces.js +10 -1
  217. package/dist/Interfaces/ChatInterfaces.d.ts +22 -0
  218. package/dist/Interfaces/CrudInterfaces.d.ts +539 -6
  219. package/dist/Interfaces/DashboardInterfaces.d.ts +123 -0
  220. package/dist/Interfaces/DashboardInterfaces.js +1 -0
  221. package/dist/Interfaces/DomainInterfaces.d.ts +145 -0
  222. package/dist/Interfaces/DomainInterfaces.js +1 -0
  223. package/dist/Interfaces/FileInterfaces.d.ts +183 -0
  224. package/dist/Interfaces/FileInterfaces.js +6 -0
  225. package/dist/Interfaces/FormInterfaces.d.ts +460 -32
  226. package/dist/Interfaces/FormInterfaces.js +79 -1
  227. package/dist/Interfaces/InputInterfaces.d.ts +22 -3
  228. package/dist/Interfaces/MailInterfaces.d.ts +192 -0
  229. package/dist/Interfaces/MailInterfaces.js +1 -0
  230. package/dist/Interfaces/RecordInterfaces.d.ts +367 -0
  231. package/dist/Interfaces/RecordInterfaces.js +1 -0
  232. package/dist/Interfaces/TableInterfaces.d.ts +102 -3
  233. package/dist/Services/ExcelApi.js +5 -0
  234. package/dist/Services/FileApi.d.ts +13 -0
  235. package/dist/Services/FileApi.js +29 -0
  236. package/dist/Services/ImageEditorLoader.d.ts +14 -0
  237. package/dist/Services/ImageEditorLoader.js +51 -0
  238. package/dist/Services/JoinSources.d.ts +79 -6
  239. package/dist/Services/JoinSources.js +132 -21
  240. package/dist/Services/MessageApi.d.ts +18 -0
  241. package/dist/Services/MessageApi.js +64 -0
  242. package/dist/Services/ModuleImport.d.ts +7 -0
  243. package/dist/Services/ModuleImport.js +22 -0
  244. package/dist/Shared.Index.d.ts +28 -0
  245. package/dist/Shared.Index.js +28 -0
  246. package/package.json +13 -2
  247. package/src/Styles/Nexus.Avatar.css +6 -5
  248. package/src/Styles/Nexus.Button.css +39 -4
  249. package/src/Styles/Nexus.Chat.css +114 -22
  250. package/src/Styles/Nexus.Choice.css +16 -0
  251. package/src/Styles/Nexus.Crud.css +486 -21
  252. package/src/Styles/Nexus.Dashboard.css +763 -0
  253. package/src/Styles/Nexus.Domain.css +384 -0
  254. package/src/Styles/Nexus.Dropdown.css +8 -0
  255. package/src/Styles/Nexus.Editor.css +20 -0
  256. package/src/Styles/Nexus.Files.css +437 -0
  257. package/src/Styles/Nexus.Form.css +5 -2
  258. package/src/Styles/Nexus.Index.css +7 -0
  259. package/src/Styles/Nexus.Input.css +73 -0
  260. package/src/Styles/Nexus.Mail.css +1062 -0
  261. package/src/Styles/Nexus.Menu.css +10 -1
  262. package/src/Styles/Nexus.Popup.css +135 -1
  263. package/src/Styles/Nexus.Print.css +288 -0
  264. package/src/Styles/Nexus.QrCode.css +12 -0
  265. package/src/Styles/Nexus.Record.css +430 -0
  266. package/src/Styles/Nexus.Tab.Buttons.css +87 -22
  267. package/src/Styles/Nexus.Table.css +1458 -1387
  268. package/src/Styles/Nexus.Theme.Picker.css +1 -0
  269. package/src/Styles/Nexus.Themes.css +12 -6
  270. package/src/Styles/Nexus.Tokens.css +8 -3
  271. package/src/Styles/Nexus.Tree.css +41 -1
@@ -1,5 +1,8 @@
1
1
  import { deleteFormDraft, readFormDraft, writeFormDraft } from "./FormDrafts.js";
2
- import { initialInputValue, inputValuesEqual, isRowsInput, normalizeInputValue, validateInput } from "./InputParamsHelpers.js";
2
+ import { isComputedInput } from "./InputConditions.js";
3
+ import { formNarrowingNames, inputNarrowingsOf, isEmptyNarrowed, narrowedFollowers } from "./InputNarrowing.js";
4
+ import { findRememberedOpening, forgetInputMemory, isEmptyInputValue, isPickerInput, readInputMemory, rememberDependencies, rememberedOpeningNow, writeInputMemory } from "./InputMemory.js";
5
+ import { holdsValue, initialInputValue, inputValuesEqual, isRowsInput, keepsTyping, normalizeInputValue, validateInput } from "./InputParamsHelpers.js";
3
6
  const INITIAL_STATUS = {
4
7
  changed: false,
5
8
  submitting: false,
@@ -40,6 +43,21 @@ export class FormStore {
40
43
  inputByName = new Map();
41
44
  values = {};
42
45
  initial = {};
46
+ /** The values the last load was given, as given: an input they name with a value is the form's own, never remembered. */
47
+ given = {};
48
+ /** Where remembered inputs (`remember`) are read and kept; set by the form that draws this store, in the browser. */
49
+ memory = null;
50
+ /** Pickers asked whether they still offer a remembered value; the next opening (a load, a reset) or a submit stops it. */
51
+ memoryRound = null;
52
+ /** The inputs whose picker is being asked: drawn loading, and a change to one (the person's pick, code) ends its ask. */
53
+ asking = new Set();
54
+ /** The values memory opened inputs on: an opening that finds nothing (a reset after the choice went) takes its own back. */
55
+ fromMemory = new Map();
56
+ /**
57
+ * The values a load (or a reset, a draft) is working out, while it works them out: what `get` answers then, so an
58
+ * input's live `hidden` (`showWhen`) is judged on the values the form opens with, never on the ones it had before.
59
+ */
60
+ reading = null;
43
61
  isStarted = false;
44
62
  // Messages: an input's own rules, the form's rule, a server's, and rule messages the form shows for its inputs.
45
63
  ruleErrors = new Map();
@@ -72,6 +90,8 @@ export class FormStore {
72
90
  draftDropped = false;
73
91
  /** An input reported a value while the values loaded and was refused: it is drawn again from the store when loading ends. */
74
92
  refusedWhileLoading = false;
93
+ /** What a choice input reported beside the value the user picked last (`pickedItem`): its item, kept with that value. */
94
+ picked = new Map();
75
95
  /** A load during a render tells nobody; `announce` does, once the form has mounted. */
76
96
  quiet = false;
77
97
  unannounced = false;
@@ -92,7 +112,13 @@ export class FormStore {
92
112
  return this.values;
93
113
  }
94
114
  get(name) {
95
- return this.values[name];
115
+ return this.reading ? this.reading[name] : this.values[name];
116
+ }
117
+ /** The params of the input of this name, as the form was given them; undefined for a name no input has. */
118
+ inputParams(name) {
119
+ if (!this.inputByName.size && this.options.inputs.length)
120
+ this.indexInputs();
121
+ return this.inputByName.get(name);
96
122
  }
97
123
  /** The values the form opened with, or saved last. */
98
124
  getInitial() {
@@ -112,6 +138,19 @@ export class FormStore {
112
138
  isChanged() {
113
139
  return this.changedNames.size > 0;
114
140
  }
141
+ /**
142
+ * Whether the changes are worth asking about before they are dropped (Cancel, closing a popup, leaving the page): a
143
+ * changed value of any input but a password or a one-time code (`keepsTyping`). A form where only those were typed is
144
+ * changed, and Save and its line say so, but nothing asks before it goes.
145
+ */
146
+ isWorthKeeping() {
147
+ for (const name of this.changedNames) {
148
+ const params = this.inputParams(name);
149
+ if (!params || keepsTyping(params))
150
+ return true;
151
+ }
152
+ return false;
153
+ }
115
154
  getStatus = () => this.status;
116
155
  /** 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
156
  shownError(name) {
@@ -129,12 +168,21 @@ export class FormStore {
129
168
  return this.ruleError(name) ?? undefined;
130
169
  return undefined;
131
170
  }
171
+ /**
172
+ * The item a choice input reported with the value it holds now, when the user picked that value: a dropdown's option
173
+ * (its options, with `multiple`). `undefined` for a value loaded, set from code, or changed since - so what it answers
174
+ * always goes with the value. A CRUD names what a save joined from it (`crudJoinedNames`).
175
+ */
176
+ pickedItem(name) {
177
+ const picked = this.picked.get(name);
178
+ return picked && inputValuesEqual(picked.value, this.values[name]) ? picked.item : undefined;
179
+ }
132
180
  /** The input's own rule message for its value, whether it shows or not. */
133
181
  ruleError(name) {
134
182
  if (this.staleRules.has(name)) {
135
183
  this.staleRules.delete(name);
136
184
  const params = this.inputByName.get(name);
137
- const message = params && !params.hidden && !this.options.locked ? validateInput(params, this.values[name], this.options.context) : null;
185
+ const message = params && !params.hidden && !isComputedInput(params) && !this.options.locked ? validateInput(params, this.values[name], this.options.context) : null;
138
186
  if (message)
139
187
  this.ruleErrors.set(name, message);
140
188
  else
@@ -152,7 +200,7 @@ export class FormStore {
152
200
  }
153
201
  /** 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
202
  problems() {
155
- return this.options.inputs.filter(params => !params.hidden && (this.ruleError(params.name) || this.crossErrors.has(params.name))).map(params => params.name);
203
+ return this.options.inputs.filter(params => !params.hidden && !isComputedInput(params) && (this.ruleError(params.name) || this.crossErrors.has(params.name))).map(params => params.name);
156
204
  }
157
205
  /** Whether every value passes, as far as the values tell. `validate()` also asks the inputs on the page. */
158
206
  isValid() {
@@ -247,7 +295,10 @@ export class FormStore {
247
295
  loadValues(values, keepChanged, draft) {
248
296
  if (!this.inputByName.size && this.options.inputs.length)
249
297
  this.indexInputs();
250
- const next = this.normalize(values);
298
+ this.given = values;
299
+ // A load opens afresh: what memory opened before belongs to the values it replaces.
300
+ this.fromMemory.clear();
301
+ const next = this.openRemembered(this.normalize(values), values);
251
302
  const merge = keepChanged && this.isStarted;
252
303
  const conflicts = [];
253
304
  let current = next;
@@ -301,8 +352,12 @@ export class FormStore {
301
352
  this.scheduleDraft();
302
353
  return conflicts;
303
354
  }
304
- /** Brings back the values the form opened with or saved last, and clears every message. The draft goes too. */
355
+ /**
356
+ * Brings back the values the form opened with or saved last, and clears every message. The draft goes too. A
357
+ * remembered input opens on the choice kept now, so a form reset after a save starts on what was just saved.
358
+ */
305
359
  reset() {
360
+ this.initial = this.openRemembered({ ...this.initial }, this.given);
306
361
  this.values = { ...this.initial };
307
362
  this.changedNames.clear();
308
363
  this.generation++;
@@ -323,10 +378,26 @@ export class FormStore {
323
378
  this.change(Object.entries(values), "code");
324
379
  }
325
380
  /**
326
- * A value the user typed or picked, from the input rendered with `stamp`. An input that was replaced since (by a value
327
- * from code, a load, or a reset) can still report its last value as it leaves; that value is ignored.
381
+ * Values a person set through another input - one worked out from them and typed in (`compute.solve`) - as one change.
382
+ * Their inputs show them at once, as values from code do, and they are taken as the person's own: the form's
383
+ * `onFieldChange` runs for them, and inputs narrowed by them lose what they picked. Nothing is taken while the values
384
+ * load. Answers whether anything changed.
385
+ */
386
+ setFromPerson(values) {
387
+ if (this.status.loading) {
388
+ this.refusedWhileLoading = true;
389
+ return false;
390
+ }
391
+ const before = this.values;
392
+ this.change(Object.entries(values), "person");
393
+ return this.values !== before;
394
+ }
395
+ /**
396
+ * A value the user typed or picked, from the input rendered with `stamp`, with what a choice input reported beside it
397
+ * (`item`: a dropdown's option, kept for `pickedItem`). An input that was replaced since (by a value from code, a load,
398
+ * or a reset) can still report its last value as it leaves; that value is ignored.
328
399
  */
329
- input(name, value, stamp) {
400
+ input(name, value, stamp, item) {
330
401
  if (stamp !== this.stamp(name))
331
402
  return;
332
403
  // Nothing is taken while the values load, not even from an input whose `disabled` was taken off by hand: the
@@ -335,6 +406,10 @@ export class FormStore {
335
406
  this.refusedWhileLoading = true;
336
407
  return;
337
408
  }
409
+ if (item === undefined)
410
+ this.picked.delete(name);
411
+ else
412
+ this.picked.set(name, { value, item });
338
413
  this.change([[name, value]], "input");
339
414
  }
340
415
  /** Marks an input as left by the user: the form's rule messages on it show from now on. */
@@ -358,7 +433,8 @@ export class FormStore {
358
433
  if (!text)
359
434
  continue;
360
435
  const params = this.inputByName.get(name);
361
- if (params && !params.hidden)
436
+ // A worked-out input is never typed in, so a message naming it is the form's.
437
+ if (params && !params.hidden && !isComputedInput(params))
362
438
  this.serverErrors.set(name, text);
363
439
  else
364
440
  others.push(text);
@@ -454,6 +530,8 @@ export class FormStore {
454
530
  }
455
531
  /** @internal A submit starts: messages from the last answer go. */
456
532
  beginSubmit() {
533
+ // What is in the form is what is saved: a remembered value a picker answers for now would come in after it.
534
+ this.stopAskingPickers();
457
535
  const shown = this.shownSnapshot();
458
536
  this.serverErrors.clear();
459
537
  this.serverMessage = null;
@@ -474,6 +552,8 @@ export class FormStore {
474
552
  if (extra) {
475
553
  for (const [name, value] of Object.entries(extra)) {
476
554
  const params = this.inputByName.get(name);
555
+ if (isComputedInput(params))
556
+ continue;
477
557
  const next = params && !params.hidden ? normalizeInputValue(params, value) : value;
478
558
  if (inputValuesEqual(this.values[name], next))
479
559
  continue;
@@ -491,6 +571,67 @@ export class FormStore {
491
571
  this.emit(null);
492
572
  this.flushDraft();
493
573
  }
574
+ /**
575
+ * @internal Whether the input's picker is being asked whether it still offers a remembered value: the input draws
576
+ * loading until it answers, the person picks, or the form opens again.
577
+ */
578
+ isRemembering(name) {
579
+ return this.asking.has(name);
580
+ }
581
+ /**
582
+ * @internal The browser storage remembered inputs read and keep (`InputLayoutParams.remember`), set by the form that
583
+ * draws this store once it is in the browser. Inputs the values left without one open on it at once (as the values
584
+ * the form opened with), a picker once it answers that it still offers the value; a form drawn on a server opened
585
+ * without it, so nothing differs while it hydrates.
586
+ */
587
+ attachMemory(storage) {
588
+ if (this.memory === storage)
589
+ return;
590
+ this.memory = storage;
591
+ if (!storage || !this.isStarted)
592
+ return;
593
+ const opened = this.openRemembered({ ...this.initial }, this.given);
594
+ const changed = [];
595
+ for (const params of this.options.inputs) {
596
+ const { name } = params;
597
+ // Only an input still as the form opened it: one typed in (or restored from a draft) keeps what it has.
598
+ if (!params.remember || isComputedInput(params) || this.changedNames.has(name) || inputValuesEqual(opened[name], this.initial[name]))
599
+ continue;
600
+ this.initial = { ...this.initial, [name]: opened[name] };
601
+ this.values = { ...this.values, [name]: opened[name] };
602
+ this.nextRevision(name);
603
+ this.bump(name);
604
+ changed.push(name);
605
+ }
606
+ if (!changed.length)
607
+ return;
608
+ this.checkAllRules();
609
+ this.checkCross();
610
+ this.bumpFollowers(changed);
611
+ this.emit(null);
612
+ }
613
+ /**
614
+ * Inputs that follow ones memory opened (`dependsOn`: a `compute.from`, `showWhen`, `spanWhen`, `labelWhen`, or a
615
+ * `narrowedBy`) draw again with their params read afresh, as after a change: a cell redraws only on its own bump.
616
+ */
617
+ bumpFollowers(names) {
618
+ for (const name of this.followersOf(names))
619
+ if (!names.includes(name))
620
+ this.bump(name);
621
+ }
622
+ /**
623
+ * @internal A save: each remembered input keeps the value saved (an empty one forgets), for the next form that asks -
624
+ * under the saved values' `per` value, and only where its `when` holds on them.
625
+ */
626
+ rememberSaved(values) {
627
+ if (!this.memory)
628
+ return;
629
+ const read = (name) => values[name];
630
+ for (const params of this.options.inputs) {
631
+ if (params.remember && !params.hidden && !isComputedInput(params))
632
+ writeInputMemory(this.memory, params, read(params.name), read);
633
+ }
634
+ }
494
635
  /** @internal A save after which the form went back to its opening values (`resetOnSuccess`). */
495
636
  markSaved() {
496
637
  this.patchStatus({ submitting: false, savedAt: Date.now() });
@@ -521,20 +662,160 @@ export class FormStore {
521
662
  /** 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
663
  normalize(values) {
523
664
  const result = { ...values };
524
- for (const params of this.options.inputs) {
525
- // A hidden input's value stays in the data as it was.
526
- if (params.hidden)
527
- continue;
665
+ // A worked-out input (`compute`) holds no value: a field the data has under its name is not the form's to send.
666
+ for (const params of this.options.inputs)
667
+ if (isComputedInput(params))
668
+ delete result[params.name];
669
+ const inputs = this.options.inputs.filter(params => holdsValue(params) && !isComputedInput(params));
670
+ // Which inputs are hidden is judged on these values: an input shown by another's value (`showWhen`) whose other input
671
+ // gets its value in this very load (its default, the record's). So every input is worked out as shown first...
672
+ for (const params of inputs)
528
673
  result[params.name] = params.name in values ? normalizeInputValue(params, values[params.name]) : initialInputValue(params);
674
+ // ...then a hidden input's value goes back to what the data held (a picture has none: the record keeps what it came with).
675
+ const hidden = this.readingFrom(result, () => inputs.filter(params => params.hidden));
676
+ for (const params of hidden) {
677
+ if (params.name in values)
678
+ result[params.name] = values[params.name];
679
+ else
680
+ delete result[params.name];
681
+ }
682
+ return result;
683
+ }
684
+ /** Runs `work` with `get` answering from these values (a load's, a reset's, a draft's), and gives back its answer. */
685
+ readingFrom(values, work) {
686
+ const before = this.reading;
687
+ this.reading = values;
688
+ try {
689
+ return work();
690
+ }
691
+ finally {
692
+ this.reading = before;
693
+ }
694
+ }
695
+ /**
696
+ * The values with each remembered input the given values leave without one opened on the company's preferred value
697
+ * or the person's last choice (`rememberedOpeningNow`), its `when` and `per` read among these values; without storage
698
+ * (on a server, before the form is drawn) only a preferred value applies. A kept choice no longer offered is forgotten.
699
+ * A picker that must be asked first opens nothing now: it is asked, and its answer opens the input later
700
+ * (`askPickers`) - the round any earlier opening started stops here. An input memory opened before (a reset after a
701
+ * save, the storage arriving) that now opens on nothing takes the value the given values give it back.
702
+ */
703
+ openRemembered(values, given) {
704
+ let result = values;
705
+ // The values as they open, in form order: a group opened on its kept value names the package kept for it.
706
+ const read = (name) => result[name];
707
+ const asks = [];
708
+ const set = (name, value) => {
709
+ if (result === values)
710
+ result = { ...values };
711
+ result[name] = value;
712
+ };
713
+ // Shown or hidden as the values being opened say, not as the store's previous ones do.
714
+ const shown = this.readingFrom(values, () => this.options.inputs.filter(params => params.remember && !params.hidden && holdsValue(params) && !isComputedInput(params)));
715
+ for (const params of shown) {
716
+ const { name } = params;
717
+ if (name in given && !isEmptyInputValue(params, given[name]))
718
+ continue;
719
+ // A picker is asked in the browser only, once the storage came: a server's draw never calls it or reads its cache.
720
+ if (!this.memory && isPickerInput(params))
721
+ continue;
722
+ const stored = this.memory ? readInputMemory(this.memory, params.remember, read) : undefined;
723
+ const opening = rememberedOpeningNow(params, stored, read);
724
+ if (opening.forget)
725
+ forgetInputMemory(this.memory, params.remember, read);
726
+ if (opening.ask)
727
+ asks.push({ params, stored });
728
+ if (opening.value !== undefined) {
729
+ const value = normalizeInputValue(params, opening.value);
730
+ set(name, value);
731
+ this.fromMemory.set(name, value);
732
+ }
733
+ else if (this.fromMemory.has(name)) {
734
+ // Still the value memory opened it on: the input's own comes back, so a choice no longer offered is not left in.
735
+ if (inputValuesEqual(result[name], this.fromMemory.get(name))) {
736
+ set(name, name in given ? normalizeInputValue(params, given[name]) : initialInputValue(params));
737
+ }
738
+ this.fromMemory.delete(name);
739
+ }
529
740
  }
741
+ this.askPickers(asks, read);
530
742
  return result;
531
743
  }
744
+ /** Stops asking pickers about remembered values: their answers no longer open anything, and their inputs stop loading. */
745
+ stopAskingPickers() {
746
+ this.memoryRound?.abort();
747
+ this.memoryRound = null;
748
+ const asked = [...this.asking];
749
+ this.asking.clear();
750
+ for (const name of asked)
751
+ this.bump(name);
752
+ }
753
+ /**
754
+ * Asks each picker whether it still offers the remembered value (`findRememberedOpening`), the input drawn loading
755
+ * meanwhile. An answer forgets a choice no longer offered, and opens an offered one (`openAsked`) only while the input
756
+ * is still asked about - no pick, no value from code since - and the values it was judged on (`rememberDependencies`:
757
+ * the group that narrows it, the one it is kept per, its `when`) are still the form's: a group changed meanwhile opens
758
+ * nothing.
759
+ */
760
+ askPickers(asks, read) {
761
+ this.stopAskingPickers();
762
+ if (!asks.length)
763
+ return;
764
+ const round = new AbortController();
765
+ this.memoryRound = round;
766
+ const memory = this.memory;
767
+ for (const { params, stored } of asks) {
768
+ const { name } = params;
769
+ const judgedOn = rememberDependencies(params).map(other => [other, read(other)]);
770
+ this.asking.add(name);
771
+ this.bump(name);
772
+ void findRememberedOpening(params, stored, read, round.signal).then(opening => {
773
+ if (round.signal.aborted)
774
+ return;
775
+ if (opening.forget)
776
+ forgetInputMemory(memory, params.remember, read);
777
+ if (!this.asking.delete(name))
778
+ return;
779
+ const still = judgedOn.every(([other, value]) => inputValuesEqual(this.values[other], value));
780
+ if (opening.value !== undefined && still)
781
+ this.openAsked(name, opening.value);
782
+ else
783
+ this.bump(name);
784
+ });
785
+ }
786
+ }
787
+ /**
788
+ * A picker answered for a remembered value: the input opens on it, as the value the form opened with (not a change) -
789
+ * 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
790
+ * what it has.
791
+ */
792
+ openAsked(name, value) {
793
+ const params = this.inputByName.get(name);
794
+ if (!params || params.hidden || !holdsValue(params) || isComputedInput(params) || this.changedNames.has(name)) {
795
+ this.bump(name);
796
+ return;
797
+ }
798
+ const next = normalizeInputValue(params, value);
799
+ this.fromMemory.set(name, next);
800
+ if (inputValuesEqual(this.values[name], next)) {
801
+ this.bump(name);
802
+ return;
803
+ }
804
+ this.initial = { ...this.initial, [name]: next };
805
+ this.values = { ...this.values, [name]: next };
806
+ this.nextRevision(name);
807
+ this.bump(name);
808
+ this.checkAllRules();
809
+ this.checkCross();
810
+ this.bumpFollowers([name]);
811
+ this.emit(null);
812
+ }
532
813
  /** Inputs that were not there, or were hidden, get their value in the input's type, as a load gives it. */
533
814
  adoptNewInputs(previous) {
534
815
  const before = new Map(previous.map(params => [params.name, params]));
535
816
  for (const params of this.options.inputs) {
536
817
  const old = before.get(params.name);
537
- if (params.hidden || (old && !old.hidden))
818
+ if (params.hidden || !holdsValue(params) || isComputedInput(params) || (old && !old.hidden))
538
819
  continue;
539
820
  const { name } = params;
540
821
  const value = name in this.values ? normalizeInputValue(params, this.values[name]) : initialInputValue(params);
@@ -547,20 +828,31 @@ export class FormStore {
547
828
  this.changedNames.add(name);
548
829
  }
549
830
  }
550
- change(entries, source) {
831
+ change(entries, changedBy) {
832
+ // A person's change through another input (`setFromPerson`) is written as code writes (its inputs mount again with
833
+ // it) and followed as typing is.
834
+ const source = changedBy === "code" ? "code" : "input";
835
+ const writtenAs = changedBy === "input" ? "input" : "code";
551
836
  const crossBefore = this.crossErrors;
552
837
  const shown = new Map();
553
838
  const remounted = new Set();
554
839
  const written = [];
840
+ /** Inputs a picker was asked about for a remembered value: this change is theirs now, and they stop loading. */
841
+ const unasked = [];
555
842
  const remember = (name) => {
556
843
  if (!shown.has(name))
557
844
  shown.set(name, this.shownError(name));
558
845
  };
559
846
  const write = (name, value, from) => {
560
847
  const params = this.inputByName.get(name);
848
+ // A worked-out input holds no value: one set from code (or a draft, an `onFieldChange` patch) is not taken.
849
+ if (isComputedInput(params))
850
+ return;
561
851
  const next = params && !params.hidden && from === "code" ? normalizeInputValue(params, value) : value;
562
852
  if (inputValuesEqual(this.values[name], next))
563
853
  return;
854
+ if (this.asking.delete(name))
855
+ unasked.push(name);
564
856
  remember(name);
565
857
  this.values = { ...this.values, [name]: next };
566
858
  if (from === "code" && params) {
@@ -578,7 +870,7 @@ export class FormStore {
578
870
  written.push(name);
579
871
  };
580
872
  for (const [name, value] of entries)
581
- write(name, value, source);
873
+ write(name, value, writtenAs);
582
874
  if (!written.length)
583
875
  return;
584
876
  // Values derived from the ones the user changed, such as an amount from a quantity and a rate.
@@ -592,6 +884,44 @@ export class FormStore {
592
884
  write(field, value, "code");
593
885
  }
594
886
  }
887
+ // An input whose options another narrows (`narrowedBy`) loses the value it picked among the other's old options when
888
+ // a person changes the other one; a value set from code (a load, a patch) is the code's to keep consistent. Down a
889
+ // chain too: a Country change clears the Region, and with it the District picked among that Region's (each input
890
+ // once, whatever order the form lists them in; one the person set in this same change keeps its value).
891
+ if (source === "input") {
892
+ const followers = narrowedFollowers(this.options.inputs, written, "form");
893
+ for (const name of followers) {
894
+ if (!isEmptyNarrowed(this.values[name]))
895
+ write(name, null, "code");
896
+ }
897
+ // Rows whose inputs follow a form value (`from: "form"`, a line's Tags of the form's Store): every row loses what
898
+ // it picked among the old value's options, and down the row's own chain.
899
+ const changedNow = new Set([...written, ...followers]);
900
+ for (const params of this.options.inputs) {
901
+ if (!isRowsInput(params))
902
+ continue;
903
+ const rowInputs = params.inputs ?? [];
904
+ if (!formNarrowingNames(rowInputs).some(name => changedNow.has(name)))
905
+ continue;
906
+ const clear = narrowedFollowers(rowInputs, changedNow, "rows-of-form");
907
+ const rows = this.values[params.name];
908
+ if (!clear.length || !Array.isArray(rows))
909
+ continue;
910
+ let touched = false;
911
+ const next = rows.map(row => {
912
+ if (row === null || typeof row !== "object")
913
+ return row;
914
+ const filled = clear.filter(name => !isEmptyNarrowed(row[name]));
915
+ if (!filled.length)
916
+ return row;
917
+ touched = true;
918
+ // Each cleared as its input holds nothing: an empty list for a picker of several.
919
+ return { ...row, ...Object.fromEntries(filled.map(name => [name, emptyRowValue(rowInputs, name)])) };
920
+ });
921
+ if (touched)
922
+ write(params.name, next, "code");
923
+ }
924
+ }
595
925
  // Inputs that follow a changed one (`dependsOn`) draw again with their params read afresh, and their rule is asked
596
926
  // again: a getter may have just made one required, read-only or hidden.
597
927
  const followers = this.followersOf(written);
@@ -604,24 +934,30 @@ export class FormStore {
604
934
  if (!shown.has(name))
605
935
  shown.set(name, this.shownWith(name, crossBefore));
606
936
  for (const [name, before] of shown)
607
- if (remounted.has(name) || followers.has(name) || this.shownError(name) !== before)
937
+ if (remounted.has(name) || followers.has(name) || unasked.includes(name) || this.shownError(name) !== before)
608
938
  this.bump(name);
609
939
  this.patchStatus({ changed: this.changedNames.size > 0, message: this.formMessage(), problems: this.problemList() });
610
- this.emit(source === "input" ? written[0] : written.length === 1 ? written[0] : null);
940
+ this.emit(changedBy === "input" ? written[0] : written.length === 1 ? written[0] : null);
611
941
  // A change after a cancel is worth keeping again.
612
942
  if (source === "input")
613
943
  this.draftDropped = false;
614
944
  this.scheduleDraft();
615
945
  }
616
- /** The inputs whose `dependsOn` names any of `names`. */
946
+ /**
947
+ * The inputs whose `dependsOn` (or `narrowedBy`) names any of `names`, and the rows inputs whose row inputs are narrowed
948
+ * by one of them `from: "form"`: their rows draw again with the form's value as it is now.
949
+ */
617
950
  followersOf(names) {
618
951
  const found = new Set();
619
952
  if (!names.length)
620
953
  return found;
621
954
  const changed = new Set(names);
622
- for (const params of this.options.inputs)
623
- if (params.dependsOn?.some(name => changed.has(name)))
955
+ for (const params of this.options.inputs) {
956
+ if (params.dependsOn?.some(name => changed.has(name)) || inputNarrowingsOf(params).some(narrowing => changed.has(narrowing.name)))
624
957
  found.add(params.name);
958
+ else if (isRowsInput(params) && formNarrowingNames(params.inputs ?? []).some(name => changed.has(name)))
959
+ found.add(params.name);
960
+ }
625
961
  return found;
626
962
  }
627
963
  checkRule(name) {
@@ -631,7 +967,7 @@ export class FormStore {
631
967
  return;
632
968
  }
633
969
  this.staleRules.delete(name);
634
- const message = params && !params.hidden && !this.options.locked ? validateInput(params, this.values[name], this.options.context) : null;
970
+ const message = params && !params.hidden && !isComputedInput(params) && !this.options.locked ? validateInput(params, this.values[name], this.options.context) : null;
635
971
  if (message)
636
972
  this.ruleErrors.set(name, message);
637
973
  else
@@ -657,7 +993,7 @@ export class FormStore {
657
993
  if (!message)
658
994
  continue;
659
995
  const params = this.inputByName.get(name);
660
- if (params && !params.hidden)
996
+ if (params && !params.hidden && !isComputedInput(params))
661
997
  next.set(name, message);
662
998
  else
663
999
  others.push(message);
@@ -751,9 +1087,11 @@ export class FormStore {
751
1087
  }
752
1088
  const entries = [];
753
1089
  const conflicts = [];
1090
+ // An input the draft's own values show (its other input among them) is restored too.
1091
+ const hidden = this.readingFrom({ ...this.values, ...draft.values }, () => new Set(this.options.inputs.filter(params => params.hidden).map(params => params.name)));
754
1092
  for (const params of this.options.inputs) {
755
1093
  const { name } = params;
756
- if (params.hidden || !(name in draft.values) || this.changedNames.has(name))
1094
+ if (hidden.has(name) || isComputedInput(params) || !(name in draft.values) || this.changedNames.has(name))
757
1095
  continue;
758
1096
  const value = draft.values[name];
759
1097
  if (sameData(value, this.values[name]))
@@ -770,3 +1108,8 @@ export class FormStore {
770
1108
  this.patchStatus({ restored: { savedAt: draft.savedAt, names: entries.map(([name]) => name), conflicts }, draftSavedAt: draft.savedAt });
771
1109
  }
772
1110
  }
1111
+ /** A row input's value when nothing is picked: as the rows hold it (an empty list for a picker of several). */
1112
+ function emptyRowValue(rowInputs, name) {
1113
+ const params = rowInputs.find(input => input.name === name);
1114
+ return params ? normalizeInputValue(params, null) : null;
1115
+ }
@@ -0,0 +1,56 @@
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
+ /**
39
+ * The inputs with each worked-out one's `dependsOn` joined by the inputs it is worked out from (`compute.from`), so what
40
+ * follows `dependsOn` - a row's cells (`RowsStore`), as a form's do through `withInputConditions` - draws it again when
41
+ * one of them changes. The same array when none is worked out from another, and the same copy for the same array.
42
+ */
43
+ export declare function withComputeSources(inputs: readonly InputParams[]): readonly InputParams[];
44
+ /** Whether the input is worked out from others (`compute`): it holds no value of the form's and is never typed in. */
45
+ export declare function isComputedInput(params: InputParams | undefined): boolean;
46
+ /**
47
+ * What a worked-out input (`compute`) shows for these values and the form's `computeWith`. A page's rule that throws
48
+ * shows nothing, and says so in the console, so one bad figure never takes the form down with it.
49
+ */
50
+ export declare function computedValue(params: InputParams, values: FormValues, withValue: unknown): unknown;
51
+ /**
52
+ * A worked-out value as its box writes it: a Number input's figure with its `decimals` always written (1,073.50), grouped,
53
+ * in `locale` (the person's, unless the params name their own); any other input's value as text. Null, undefined, "",
54
+ * NaN, or a figure that is not finite, write nothing, so the box is blank rather than "NaN" or "0.00".
55
+ */
56
+ export declare function computedText(params: InputParams, value: unknown, locale?: string): string;