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
@@ -8,18 +8,98 @@ import type { DateTimePickerProps } from "../Components/Inputs/DateTimePicker.ts
8
8
  import type { DropdownProps } from "../Components/Inputs/Dropdown.tsx";
9
9
  import type { MarkdownEditorProps } from "../Components/Inputs/MarkdownEditor.tsx";
10
10
  import type { NumberBoxProps } from "../Components/Inputs/NumberBox.tsx";
11
+ import type { PictureInputProps } from "../Components/Inputs/PictureInput.tsx";
11
12
  import type { RadioGroupProps } from "../Components/Inputs/RadioGroup.tsx";
12
13
  import type { RichTextEditorProps } from "../Components/Inputs/RichTextEditor.tsx";
13
14
  import type { RangeSliderProps, SliderProps } from "../Components/Inputs/Slider.tsx";
14
15
  import type { TextAreaProps } from "../Components/Inputs/TextArea.tsx";
15
- import type { TextBoxProps, TextBoxType } from "../Components/Inputs/TextBox.tsx";
16
+ import type { TextBoxProps } from "../Components/Inputs/TextBox.tsx";
16
17
  import type { TimePickerProps } from "../Components/Inputs/TimePicker.tsx";
17
18
  import type { TreeViewProps } from "../Components/Inputs/TreeView.tsx";
19
+ import type { PopupSize } from "../Components/Layouts/Popup.tsx";
18
20
  import type { FormStore } from "../Helpers/FormStore.ts";
19
21
  import type { ApiEndpoint, ApiResult } from "./ApiInterfaces.ts";
20
22
  import type { DatePreferences } from "./DateInterfaces.ts";
21
23
  import type { InputDesign, OptionValue } from "./InputInterfaces.ts";
24
+ import type { TableColumn } from "./TableInterfaces.ts";
22
25
  import type { Size } from "./TypeInterfaces.ts";
26
+ /**
27
+ * Which input a params object describes: `{ type: EInputTypes.Dropdown, name: "country", ... }`. A number, so a
28
+ * database, a generated page and a C# enum hold the same value.
29
+ *
30
+ * The numbers come in blocks by kind, each with room to grow. 0 is never a kind, so a missing value is never a valid
31
+ * one; a released number is never reused or given a new meaning.
32
+ *
33
+ * | Block | Kind | Members |
34
+ * |---|---|---|
35
+ * | 1–19 | Text boxes | Text 1, Email 2, Password 3, Search 4, Tel 5, Url 6, TextArea 7, Code 8 |
36
+ * | 20–39 | Choices | CheckBox 20, Switch 21, Radio 22, CheckBoxGroup 23, RadioGroup 24, Dropdown 25 |
37
+ * | 40–59 | Dates and times | Date 40, DateRange 41, Time 42, DateTime 43 |
38
+ * | 60–79 | Numbers and sliders | Number 60, Slider 61, RangeSlider 62 |
39
+ * | 80–99 | Editors | Markdown 80, RichText 81 |
40
+ * | 100–119 | Structures | Tree 100, Tabular 101, Group 102, Picture 103 |
41
+ *
42
+ * A dropdown is one member: `multiple` decides whether it picks one item or many. Every number fits a byte.
43
+ */
44
+ export declare const EInputTypes: {
45
+ /** `TextBox`, a single line. */
46
+ readonly Text: 1;
47
+ /** `TextBox` of HTML type email. */
48
+ readonly Email: 2;
49
+ /** `TextBox` of HTML type password, with a button that shows what was typed. */
50
+ readonly Password: 3;
51
+ /** `TextBox` of HTML type search. */
52
+ readonly Search: 4;
53
+ /** `TextBox` of HTML type tel: a phone number. */
54
+ readonly Tel: 5;
55
+ /** `TextBox` of HTML type url. */
56
+ readonly Url: 6;
57
+ /** `TextArea`: several lines of plain text. */
58
+ readonly TextArea: 7;
59
+ /** `CodeBox`: a box per character, such as a one-time code. */
60
+ readonly Code: 8;
61
+ /** `CheckBox`: `true` or `false`. */
62
+ readonly CheckBox: 20;
63
+ /** `Switch`: `true` or `false`, for a setting that takes effect. */
64
+ readonly Switch: 21;
65
+ /** `RadioButton`: one radio, on or off; in rows, only one row has it on. */
66
+ readonly Radio: 22;
67
+ /** `CheckBoxGroup`: any number of options. */
68
+ readonly CheckBoxGroup: 23;
69
+ /** `RadioGroup`: one option of a few, all in view. */
70
+ readonly RadioGroup: 24;
71
+ /** `Dropdown`: one item, or many with `multiple`. */
72
+ readonly Dropdown: 25;
73
+ /** `DatePicker`: an ISO date. */
74
+ readonly Date: 40;
75
+ /** `DateRangePicker`: two ISO dates. */
76
+ readonly DateRange: 41;
77
+ /** `TimePicker`: an ISO time. */
78
+ readonly Time: 42;
79
+ /** `DateTimePicker`: a UTC moment shown in the user's zone. */
80
+ readonly DateTime: 43;
81
+ /** `NumberBox`. */
82
+ readonly Number: 60;
83
+ /** `Slider`: one number on a track. */
84
+ readonly Slider: 61;
85
+ /** `RangeSlider`: two numbers on a track. */
86
+ readonly RangeSlider: 62;
87
+ /** `MarkdownEditor`: Markdown text. */
88
+ readonly Markdown: 80;
89
+ /** `RichTextEditor`: sanitized HTML. */
90
+ readonly RichText: 81;
91
+ /** `TreeView`: nodes picked in a tree. */
92
+ readonly Tree: 100;
93
+ /** `TabularForm`: rows of inputs in a table. */
94
+ readonly Tabular: 101;
95
+ /** `GroupForm`: the same rows, each as a section of inputs. */
96
+ readonly Group: 102;
97
+ /** `PictureInput`: a record's picture, kept by the file backend as one of the owner's files, not a value of the form. */
98
+ readonly Picture: 103;
99
+ };
100
+ export type EInputTypes = (typeof EInputTypes)[keyof typeof EInputTypes];
101
+ /** The text box kinds: the members a `TextBox` renders, each with its HTML type. */
102
+ export type TextInputType = typeof EInputTypes.Text | typeof EInputTypes.Email | typeof EInputTypes.Password | typeof EInputTypes.Search | typeof EInputTypes.Tel | typeof EInputTypes.Url;
23
103
  /** Where a form places an input. The input itself ignores these keys. */
24
104
  export interface InputLayoutParams {
25
105
  /** Optional heading before this field when the section changes (Form only). */
@@ -35,18 +115,252 @@ export interface InputLayoutParams {
35
115
  /**
36
116
  * Other inputs of the same form this one follows (Form only). When any of their values changes, this input draws
37
117
  * again — without mounting again, so nothing typed in it is lost — and its params are read afresh: a getter for
38
- * `hidden`, `readOnly`, `readOnlyMessage`, `label`, `hint` or `options` answers for the values as they are now. Its
39
- * own rule is checked again too, so a `required` that a getter just switched on or off counts at once. Name the
118
+ * `hidden`, `readOnly`, `readOnlyMessage`, `label`, `hint`, `options` or `span` answers for the values as they are now.
119
+ * Its own rule is checked again too, so a `required` that a getter just switched on or off counts at once. Name the
40
120
  * input itself to follow its own typing (a hint that says what the typed text will become).
41
121
  *
42
122
  * An input that names any is asked `hidden` every time it draws, so it can come and go; one that names none is
43
123
  * asked once, when the form draws.
44
124
  */
45
125
  dependsOn?: readonly string[];
126
+ /**
127
+ * Another input of the same form whose value narrows this one's options (Form, and the rows of its tabular and group
128
+ * forms): a Package list of the Group picked.
129
+ * The options are the records whose `key` field holds that value (static `options` filtered; `loadOptions` asked with
130
+ * `request.narrow`, which `crudPicker` answers from the server when its endpoint takes the parent, else from its kept list);
131
+ * the list loads afresh when the value changes, offers nothing while it is empty, and says what to pick first. A value
132
+ * picked here is cleared when a person changes the other one. It follows the other input as `dependsOn` would.
133
+ *
134
+ * Several narrow at once (a list): the options must fit every one, the box says what to pick first for the first one
135
+ * still empty, and the list loads afresh when any changes. In a row of a tabular or group form a narrowing names an
136
+ * input of the same row, or of the form around the rows with `from: "form"`: an asset line's Tags, the units of the
137
+ * line's Item in the form's Store. A value picked is cleared when a person changes one it follows (every row's, for
138
+ * the form's), never when code sets it (a draft loading).
139
+ *
140
+ * @example { type: EInputTypes.Dropdown, name: "subscriptionPackageId", label: "Package", ...crudPicker(PACKAGES, { labelKey: "packageName" }), narrowedBy: { name: "subscriptionGroupId" } }
141
+ * @example narrowedBy: [{ name: "storeId", from: "form" }, { name: "itemId" }] // a line's Tags: the form's Store, the row's Item
142
+ */
143
+ narrowedBy?: InputNarrowing | readonly InputNarrowing[];
144
+ /**
145
+ * When the input shows (Form only): while every condition holds, each on another input's value as it is now. The form
146
+ * follows the inputs named as `dependsOn` would, so the input comes and goes as they change, keeps what was typed in it
147
+ * (its value stays in the data, as a `hidden` one's does) and is not checked while it is hidden. A `hidden: true`, or a
148
+ * `hidden` getter, still hides it whatever the conditions say. A CRUD's Add or Edit saved while it is hidden leaves it
149
+ * on the row only as the server holds it: as the answer gives it, else as what was sent gives it (`crudSavedRecord`).
150
+ *
151
+ * @example showWhen: { name: "counterType", in: [0, 1] }
152
+ * @example showWhen: [{ name: "counterType", in: [2] }, { name: "subscriptionMiscellaneousFeeId", filled: false }]
153
+ */
154
+ showWhen?: InputCondition | readonly InputCondition[];
155
+ /**
156
+ * When the input is read-only (Form only): while every condition holds, each on another input's value as it is now -
157
+ * the same conditions as `showWhen`. It shows its value with the lock and says why on any try to change it (the
158
+ * input's own `readOnlyMessage`), its value stays in the data sent, and it is not checked while read-only. The form
159
+ * follows the inputs named as `dependsOn` would. A `readOnly: true`, or a `readOnly` getter, still locks it whatever
160
+ * the conditions say.
161
+ *
162
+ * A CRUD's Edit form holds the record it edits, its key (`rowKey`, default `id`) among the values, and its Add form
163
+ * does not: so an input picked on Add and fixed once saved is one line.
164
+ *
165
+ * @example readOnlyWhen: { name: "id", filled: true }, readOnlyMessage: "A saved price keeps its group."
166
+ */
167
+ readOnlyWhen?: InputCondition | readonly InputCondition[];
168
+ /**
169
+ * Opens the input on a value the form's own values leave it without (a Form's input, or an input in rows): the
170
+ * company's `preferred` value, else the person's last saved choice for `key`, kept in this browser per `viewer` when a
171
+ * form holding it saves - each only while the input still offers it, else dropped without a word and a kept choice
172
+ * forgotten. "Offers": a choice group's or a dropdown's own `options` list it (narrowed as `narrowedBy` says), neither
173
+ * disabled nor inactive; a picker (`loadOptions`, as `crudPicker` gives) is asked by the value's key first
174
+ * (`peekOption`, else `resolveOptions`, and its pages for a key that answers nothing) and must answer it, not inactive
175
+ * (`isOptionInactive`) and inside its narrowing - nothing opens until it answers, its box shows its spinner meanwhile,
176
+ * an answer for a group (`narrowedBy`, `per`, `when`) the form no longer holds opens nothing, and a picker that cannot
177
+ * be read opens nothing and forgets nothing. "Leave it without": the values the form opens with (its `defaultValue`, a
178
+ * CRUD's new values, a record) do not name the input or hold it empty; the input's own `defaultValue` gives way. The
179
+ * value counts as the one the form opened with, not as a change; a pick (or a value from code) before a picker answers
180
+ * is never replaced; a form reset after a save opens on the new choice.
181
+ *
182
+ * In rows (a tabular or group form): a new row's input opens on the kept value unless `newRow` or the values the row
183
+ * is added with name it - so give a remembered row input its first choice as its own `defaultValue`, not in `newRow`
184
+ * - and a save keeps the value of the row added last (none added: nothing kept).
185
+ *
186
+ * @example remember: { key: "counter-paid-by", viewer: `${userId}|${companyId}`, preferred: settings.defaultPaymentMode }
187
+ * @example remember: { key: "subscribers-package", viewer, per: "subscriptionGroupId", when: { name: "id", filled: false } }
188
+ */
189
+ remember?: InputRemember;
190
+ /**
191
+ * A label, and a placeholder, that follow another input's value (Form only): `labels` by that value (compared as
192
+ * text, so `1` matches `"1"`), `otherwise` for any other value or none (default: the input's own `label`). The
193
+ * input's messages name it by the label it shows ("VAT number is required"), unless it has a `fieldName`. The form
194
+ * follows the other input as `dependsOn` would: the field draws again, keeping what was typed.
195
+ *
196
+ * @example labelWhen: { name: "registrationType", labels: { 1: "PAN number", 2: "VAT number" }, otherwise: "PAN/VAT number" }
197
+ */
198
+ labelWhen?: InputLabelWhen;
199
+ /**
200
+ * Another width while conditions hold (Form only): `span` while every condition of `when` holds, each on another
201
+ * input's value as it is now (the conditions of `showWhen`; the input's own name follows its own value), else the
202
+ * input's own `span`. The form follows the inputs named as `dependsOn` would: the cell takes its new width without
203
+ * mounting again, so what is typed, the caret and the focus stay. A narrow form still gives every input the whole row.
204
+ *
205
+ * @example spanWhen: { when: { name: "billDate", filled: false }, span: 12 } // the whole row until a date is put, then its own 6
206
+ * @example spanWhen: { when: [{ name: "counterType", in: [0] }, { name: "billDate", filled: false }], span: 12 }
207
+ */
208
+ spanWhen?: InputSpanWhen;
209
+ /**
210
+ * A value worked out from other inputs, shown and never typed (unless `solve` makes it so): written when the form opens
211
+ * (new values, a loaded record) and again whenever an input in `from` changes - by a person, by code, a load, a reset -
212
+ * or the form's `computeWith` changes. It is no form value: never sent, never checked, never a change, never remembered
213
+ * or kept in a draft. Meant for a Number (its `decimals`, `prefix`, `suffix`, in the person's number format); any other
214
+ * type shows its text. Null, undefined, "" or NaN leave the box blank. The box is an `<output>` on the read-only tint,
215
+ * with no lock, no steppers and no Tab stop. In a tabular or group form's rows it works on each row alike: `from` names
216
+ * the row's inputs, `values` is the row, `withValue` the rows' `computeWith`, and the rows hold no value under its name
217
+ * (a loaded row's field of that name is dropped).
218
+ *
219
+ * @example compute: { from: ["price", "discountPercentage", "vatPercentage"], value: values => priceAmounts(values).netAmount }
220
+ * @example compute: { from: [], value: (_values, preview) => preview?.netAmount } // with computeWith={preview}
221
+ */
222
+ compute?: InputCompute;
223
+ }
224
+ /**
225
+ * How an input's value is worked out (`InputLayoutParams.compute`): from the form's values as they are now, and from what
226
+ * the page passes as the form's `computeWith` (a server's preview answer). `withValue` is whatever the page passed, so it
227
+ * is `any` here: name its type in the function (`(_values, line: CounterLine | null) => line?.netAmount`).
228
+ */
229
+ export interface InputCompute<TWith = any> {
230
+ /** The inputs it follows: it is worked out again when any of them changes. `[]` follows only `computeWith`. */
231
+ from: readonly string[];
232
+ /** The value to show. `withValue` is the form's `computeWith`, as the page passed it (`undefined` when it passed none). */
233
+ value(values: FormValues, withValue: TWith): unknown;
234
+ /**
235
+ * Works it backwards, which makes the box editable (a Number input): a person types the figure they want and leaves the
236
+ * box (Tab, a click away, or Enter), and this answers the values of the inputs it is worked out from that give it - the
237
+ * inverse of `value`. The form writes them as the person's own change (their rules, the form's `onFieldChange`, the
238
+ * unsaved-changes state, Save and the leave guard see those inputs; the worked-out input still holds no value), then
239
+ * shows the figure worked forward again, with a line under the box: what was set ("Price set to 2,458.21"), and, when
240
+ * the figure typed cannot be reached exactly, the nearest it now shows. Answer a sentence to refuse (the box goes back,
241
+ * the sentence shows under it), or `null` to change nothing. Escape puts back what the box showed; an emptied box changes
242
+ * nothing. The box is read-only, as without `solve`, while the form is read-only, disabled or loading. In a row it
243
+ * works the same on that row (the rows' `onRowChange` runs for what it sets); a table says the line under the rows,
244
+ * since its cells have none, and Enter with a new figure sets it rather than moving down the column.
245
+ *
246
+ * @example solve: (typed, values) => { const found = priceFromNet(typed, values); return found.ok ? { values: { price: found.price } } : found.message; }
247
+ */
248
+ solve?(typed: number, values: FormValues, withValue: TWith): InputSolved | string | null;
249
+ }
250
+ /** What an input worked backwards answers (`InputCompute.solve`): the values of the inputs it is worked out from. */
251
+ export interface InputSolved {
252
+ /** The inputs' new values, by name: `{ price: 2458.21 }`. */
253
+ values: FormValues;
254
+ /** The line under the box, in place of the form's own ("Price set to 2,458.21."). */
255
+ note?: string;
256
+ }
257
+ /** An input's label that follows another input's value (`InputLayoutParams.labelWhen`). */
258
+ export interface InputLabelWhen {
259
+ /** The other input's name: `"registrationType"`. */
260
+ name: string;
261
+ /** The label for each of its values: `{ 1: "PAN number", 2: "VAT number" }`. */
262
+ labels: Readonly<Record<string, string>>;
263
+ /** The label for any other value, or none. Default: the input's own `label`. */
264
+ otherwise?: string;
265
+ /** The placeholder for each of its values; one not listed keeps the input's own `placeholder`. */
266
+ placeholders?: Readonly<Record<string, string>>;
267
+ }
268
+ /** An input's width while conditions hold (`InputLayoutParams.spanWhen`). */
269
+ export interface InputSpanWhen {
270
+ /** The conditions, as `showWhen`'s: every one must hold. An empty list holds always. */
271
+ when: InputCondition | readonly InputCondition[];
272
+ /** The columns it spans while they hold, 1 to 12 (as `span`). */
273
+ span: number;
274
+ }
275
+ /**
276
+ * An input's remembered value (`InputLayoutParams.remember`), kept under `nx-input:{key}:{viewer}` in local storage.
277
+ */
278
+ export interface InputRemember {
279
+ /** What the value is, the same in every form that asks it: `"counter-paid-by"`. */
280
+ key: string;
281
+ /**
282
+ * Whose choice it is: the person, with the company they work in when one person serves several (`"8|3"`). Without a
283
+ * viewer nothing is read or kept; `preferred` still applies.
284
+ */
285
+ viewer?: string | null;
286
+ /** The company's default, from a setting the page reads: it wins over the remembered choice while it is offered. */
287
+ preferred?: unknown;
288
+ /**
289
+ * Keeps one value per value of another input: a package per group, since a package is one of its group's. Kept under
290
+ * `nx-input:{key}-{that value}:{viewer}`; while the other input holds no single value nothing is read or kept. Read
291
+ * among the values the form opens with and saves (an input in rows: its row's). A narrowed input (`narrowedBy`) is
292
+ * kept per the input that narrows it, so another group never forgets this one's.
293
+ *
294
+ * @example per: "subscriptionGroupId"
295
+ */
296
+ per?: string;
297
+ /**
298
+ * Remembers only while these hold on the values the form opens with and the values it saves (an input in rows: its
299
+ * row's): no opening value, no `preferred`, nothing kept otherwise. A CRUD's Edit form holds the record's key (`id`)
300
+ * and its Add form does not, so `{ name: "id", filled: false }` remembers on Add only: Edit opens on the record's own
301
+ * values and a save there keeps nothing.
302
+ *
303
+ * @example when: { name: "id", filled: false }
304
+ */
305
+ when?: InputCondition | readonly InputCondition[];
306
+ }
307
+ /**
308
+ * One condition of an input's `showWhen`, on another input's value. Values are compared as text, so `1` matches a
309
+ * value of `"1"`; a list value (a multiple choice) matches `in` when any of its items is listed. Every key given must hold.
310
+ */
311
+ export interface InputCondition {
312
+ /** The other input's name: `"counterType"`. */
313
+ name: string;
314
+ /** Holds while the value is one of these: `in: [0, 1]`. */
315
+ in?: readonly unknown[];
316
+ /** Holds while the value is none of these. */
317
+ notIn?: readonly unknown[];
318
+ /** `true`: holds while it has a value; `false`: while it is empty (null, a blank text, an empty list). `false` and `0` are values. */
319
+ filled?: boolean;
320
+ }
321
+ /**
322
+ * What narrows an input's options (`narrowedBy`): another input's value (`name`), or a value the page knows when the
323
+ * form opens (`value`, with the options' `key`): the Packages of the subscriber's own group.
324
+ */
325
+ export type InputNarrowing = InputNarrowingByInput | InputNarrowingByValue;
326
+ /** Options narrowed by another input of the same form: its value, as it changes. */
327
+ export interface InputNarrowingByInput {
328
+ /** The other input's name: `"subscriptionGroupId"`. */
329
+ name: string;
330
+ /** The options' field that must hold the other input's value. Default `name`. */
331
+ key?: string;
332
+ /** What the box says while the other input is empty. Default "Pick a {other input's label} first". */
333
+ placeholder?: string;
334
+ /** Also offers the options whose `key` field is empty: a fee of every group beside the group's own. */
335
+ orUnset?: boolean;
336
+ /**
337
+ * For an input in a row of a tabular or group form, where `name` is: `"row"` (default) an input of the same row - the
338
+ * line's Item - or `"form"` an input of the form around the rows - the document's Store. Outside rows, ignored.
339
+ */
340
+ from?: "row" | "form";
341
+ value?: never;
342
+ }
343
+ /**
344
+ * Options narrowed by a fixed value: the records whose `key` field holds `value`. Nothing is followed and nothing is
345
+ * cleared; a `null` value offers nothing (or, with `orUnset`, only the options whose field is empty). A getter on
346
+ * `narrowedBy` may answer for the form as it is now.
347
+ *
348
+ * @example narrowedBy: { key: "subscriptionGroupId", value: subscriber.subscriptionGroupId }
349
+ */
350
+ export interface InputNarrowingByValue {
351
+ /** The value the options' `key` field must hold. */
352
+ value: OptionValue | null;
353
+ /** The options' field that must hold it. */
354
+ key: string;
355
+ /** What the box says while `value` is null. Default: the input's own placeholder. */
356
+ placeholder?: string;
357
+ /** Also offers the options whose `key` field is empty: a fee of every group beside the group's own. */
358
+ orUnset?: boolean;
359
+ name?: never;
46
360
  }
47
361
  /** What every input's params have. */
48
- export interface InputParamsBase<TType extends string = string> extends InputLayoutParams {
49
- /** Which input: "text", "number", "dropdown", "date", "tabular", ... */
362
+ export interface InputParamsBase<TType extends EInputTypes = EInputTypes> extends InputLayoutParams {
363
+ /** Which input: `EInputTypes.Text`, `EInputTypes.Dropdown`, `EInputTypes.Tabular`, ... */
50
364
  type: TType;
51
365
  /** The key of the value in the form's data, and in each row of a tabular or group form. */
52
366
  name: string;
@@ -59,41 +373,47 @@ type FormOwnedProps = "type" | "name" | "ref" | "value" | "checked";
59
373
  /** Omit that keeps each member of a union apart, so a dropdown's `multiple: true` still decides its value's type. */
60
374
  type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
61
375
  /** An input's params: its component's props without the ones the form owns, plus the base keys. */
62
- export type InputParamsOf<TType extends string, TProps> = DistributiveOmit<TProps, FormOwnedProps> & InputParamsBase<TType>;
63
- /** `TextBox`: "text", "email", "password", "search", "tel", or "url". */
64
- export type TextInputParams = InputParamsOf<TextBoxType, TextBoxProps>;
65
- export type TextAreaParams = InputParamsOf<"textarea", TextAreaProps>;
66
- export type NumberInputParams = InputParamsOf<"number", NumberBoxProps>;
376
+ export type InputParamsOf<TType extends EInputTypes, TProps> = DistributiveOmit<TProps, FormOwnedProps> & InputParamsBase<TType>;
377
+ /** `TextBox`: `Text`, `Email`, `Password`, `Search`, `Tel` or `Url`, which also decides its HTML type. */
378
+ export type TextInputParams = InputParamsOf<TextInputType, TextBoxProps>;
379
+ export type TextAreaParams = InputParamsOf<typeof EInputTypes.TextArea, TextAreaProps>;
380
+ export type NumberInputParams = InputParamsOf<typeof EInputTypes.Number, NumberBoxProps>;
67
381
  /** `CodeBox`: a box per character. The value is the code's characters, without separators. */
68
- export type CodeInputParams = InputParamsOf<"code", CodeBoxProps>;
382
+ export type CodeInputParams = InputParamsOf<typeof EInputTypes.Code, CodeBoxProps>;
69
383
  /** A checkbox: `true` or `false`. `defaultChecked` is the value of a new row. */
70
- export type CheckBoxParams = InputParamsOf<"checkbox", CheckBoxProps & CheckBoxOnlyProps>;
71
- export type SwitchParams = InputParamsOf<"switch", CheckBoxProps>;
384
+ export type CheckBoxParams = InputParamsOf<typeof EInputTypes.CheckBox, CheckBoxProps & CheckBoxOnlyProps>;
385
+ export type SwitchParams = InputParamsOf<typeof EInputTypes.Switch, CheckBoxProps>;
72
386
  /**
73
387
  * One radio button, on or off. In a tabular or group form only one row can have it on, like a primary address: turning
74
388
  * it on in a row turns it off in the others. `required` there means one row must have it.
75
389
  */
76
- export type RadioParams = InputParamsOf<"radio", CheckBoxProps>;
77
- export type CheckBoxGroupParams = InputParamsOf<"checkbox-group", CheckBoxGroupProps<OptionValue>>;
78
- export type RadioGroupParams = InputParamsOf<"radio-group", RadioGroupProps<OptionValue>>;
390
+ export type RadioParams = InputParamsOf<typeof EInputTypes.Radio, CheckBoxProps>;
391
+ export type CheckBoxGroupParams = InputParamsOf<typeof EInputTypes.CheckBoxGroup, CheckBoxGroupProps<OptionValue>>;
392
+ export type RadioGroupParams = InputParamsOf<typeof EInputTypes.RadioGroup, RadioGroupProps<OptionValue>>;
79
393
  /** A list of params holds dropdowns of many item types (countries, staff), so items are `any` here: typed columns and callbacks fit. */
80
- export type DropdownParams = InputParamsOf<"dropdown", DropdownProps<any, OptionValue>>;
81
- export type DateParams = InputParamsOf<"date", DatePickerProps>;
82
- export type DateRangeParams = InputParamsOf<"date-range", DateRangePickerProps>;
83
- export type TimeParams = InputParamsOf<"time", TimePickerProps>;
84
- export type DateTimeParams = InputParamsOf<"date-time", DateTimePickerProps>;
85
- export type SliderParams = InputParamsOf<"slider", SliderProps>;
86
- export type RangeSliderParams = InputParamsOf<"range-slider", RangeSliderProps>;
87
- export type MarkdownParams = InputParamsOf<"markdown", MarkdownEditorProps>;
394
+ export type DropdownParams = InputParamsOf<typeof EInputTypes.Dropdown, DropdownProps<any, OptionValue>>;
395
+ export type DateParams = InputParamsOf<typeof EInputTypes.Date, DatePickerProps>;
396
+ export type DateRangeParams = InputParamsOf<typeof EInputTypes.DateRange, DateRangePickerProps>;
397
+ export type TimeParams = InputParamsOf<typeof EInputTypes.Time, TimePickerProps>;
398
+ export type DateTimeParams = InputParamsOf<typeof EInputTypes.DateTime, DateTimePickerProps>;
399
+ export type SliderParams = InputParamsOf<typeof EInputTypes.Slider, SliderProps>;
400
+ export type RangeSliderParams = InputParamsOf<typeof EInputTypes.RangeSlider, RangeSliderProps>;
401
+ export type MarkdownParams = InputParamsOf<typeof EInputTypes.Markdown, MarkdownEditorProps>;
88
402
  /** `RichTextEditor`: the value is sanitized HTML. */
89
- export type RichTextParams = InputParamsOf<"rich-text", RichTextEditorProps>;
90
- export type TreeParams = InputParamsOf<"tree", TreeViewProps<any>>;
403
+ export type RichTextParams = InputParamsOf<typeof EInputTypes.RichText, RichTextEditorProps>;
404
+ export type TreeParams = InputParamsOf<typeof EInputTypes.Tree, TreeViewProps<any>>;
91
405
  /** `TabularForm`: rows of inputs in a table. The value is an array of row objects. */
92
- export type TabularParams = InputParamsOf<"tabular", TabularFormProps>;
406
+ export type TabularParams = InputParamsOf<typeof EInputTypes.Tabular, TabularFormProps>;
93
407
  /** `GroupForm`: the same rows, each as a section of inputs. */
94
- export type GroupParams = InputParamsOf<"group", GroupFormProps>;
408
+ export type GroupParams = InputParamsOf<typeof EInputTypes.Group, GroupFormProps>;
409
+ /**
410
+ * `PictureInput`: the owner's main picture, changed at once through the file backend. It holds no value, so the form
411
+ * sends nothing for it. `ownerId` is the record's id (null for a record not saved yet).
412
+ */
413
+ export type PictureParams = InputParamsOf<typeof EInputTypes.Picture, PictureInputProps>;
95
414
  /** The params of any input: the root type every form takes as `inputs`. */
96
- export type InputParams = TextInputParams | TextAreaParams | NumberInputParams | CodeInputParams | CheckBoxParams | SwitchParams | RadioParams | CheckBoxGroupParams | RadioGroupParams | DropdownParams | DateParams | DateRangeParams | TimeParams | DateTimeParams | SliderParams | RangeSliderParams | MarkdownParams | RichTextParams | TreeParams | TabularParams | GroupParams;
415
+ export type InputParams = TextInputParams | TextAreaParams | NumberInputParams | CodeInputParams | CheckBoxParams | SwitchParams | RadioParams | CheckBoxGroupParams | RadioGroupParams | DropdownParams | DateParams | DateRangeParams | TimeParams | DateTimeParams | SliderParams | RangeSliderParams | MarkdownParams | RichTextParams | TreeParams | TabularParams | PictureParams | GroupParams;
416
+ /** The `type` of any input: a member of `EInputTypes`. */
97
417
  export type InputType = InputParams["type"];
98
418
  /** Settings a check needs to write its message the way the field shows values: the user's date and time settings. */
99
419
  export interface InputValidationContext {
@@ -103,6 +423,11 @@ export interface InputValidationContext {
103
423
  /** One row of a tabular or group form: the values by input name, plus any other fields the row came with, such as its id. */
104
424
  export type RowValue = Record<string, unknown>;
105
425
  /** Props shared by `TabularForm` and `GroupForm`. The value is an array of rows; each row holds one value per input. */
426
+ /** A value of the form around rows that a row input is narrowed by (`RowsInputProps.formValues`), and its label. */
427
+ export interface RowsFormValue {
428
+ value: unknown;
429
+ label?: ReactNode;
430
+ }
106
431
  export interface RowsInputProps<TRow extends RowValue = RowValue> {
107
432
  /** The inputs of one row, as params. Keep the array stable (a constant or `useMemo`): a new one checks every row again. */
108
433
  inputs: readonly InputParams[];
@@ -192,6 +517,18 @@ export interface RowsInputProps<TRow extends RowValue = RowValue> {
192
517
  * value is checked the same way in every row — so change what is offered here, not what is required.
193
518
  */
194
519
  cellParams?: (row: TRow, name: string, index: number) => Partial<InputParams> | null | undefined;
520
+ /**
521
+ * The values of the form around the rows that a row input's `narrowedBy` names `from: "form"` (the document's Store),
522
+ * each with its label for "Pick a store first". A `Form` sets them as they change, and the rows draw again then; a rows
523
+ * input used on its own takes them from the page.
524
+ */
525
+ formValues?: Readonly<Record<string, RowsFormValue>>;
526
+ /**
527
+ * What a row input worked out from others (`compute`) is handed as `withValue`, beside the row's values: a rate the
528
+ * whole document shares, the company's VAT. Every row's worked-out cells draw again when a new one comes (compared by
529
+ * identity: pass state or `useMemo`). A `Form` hands the rows its own `computeWith` unless their params name one.
530
+ */
531
+ computeWith?: unknown;
195
532
  /** Every input is read-only, and rows cannot be added, removed, or moved. */
196
533
  readOnly?: boolean;
197
534
  /** What the rows say when someone tries to change them. Default "Read-only: these rows cannot be changed." */
@@ -316,6 +653,14 @@ export interface FormProps<TValues extends FormValues = FormValues> {
316
653
  * nothing typed is taken (even with `disabled` taken off by hand) until it ends. A submit waits too.
317
654
  */
318
655
  loading?: boolean;
656
+ /**
657
+ * What the page knows beside the values, for the inputs worked out from it (`compute`): a server's preview answer, a
658
+ * record the form was opened for. Every computed input is worked out again when it changes, compared by identity, so
659
+ * pass state or a `useMemo` value - never an object made afresh on every draw, which would work them out on every draw.
660
+ *
661
+ * @example <ApiForm computeWith={line} inputs={[..., { type: EInputTypes.Number, name: "unitPrice", label: "Rate", decimals: 2, compute: { from: [], value: (_values, line) => line?.unitPrice } }]} />
662
+ */
663
+ computeWith?: unknown;
319
664
  id?: string;
320
665
  className?: string;
321
666
  style?: CSSProperties;
@@ -354,6 +699,60 @@ export type FormButtonsPlacement = "end" | "start" | false;
354
699
  * spinner already says it is working, and every field is in view with its own message under it.
355
700
  */
356
701
  export type FormStatusLineMode = "full" | "errors";
702
+ /**
703
+ * The fill of a form's submit button: `primary`, or `danger` for a save that removes something ("Remove", "Turn two-step
704
+ * off", "Make new codes"), filled with the danger colour and its light text as every solid danger fill is.
705
+ */
706
+ export type FormSubmitTone = "primary" | "danger";
707
+ /**
708
+ * What a form's question before saving shows in full (`FormConfirmParams.details`): a record and its fields, drawn as a
709
+ * record's details are (`CrudDetails`), in a section under a heading for each run of neighbouring columns with one
710
+ * `group`, each value in its column's format.
711
+ */
712
+ export interface FormConfirmDetails<TRow extends object = any> {
713
+ /** What will be saved, as a record: the body that will be sent, the values, or the server's preview of them. */
714
+ row: TRow;
715
+ /** Its fields, as a table's columns describe them. `group` names the section a field is in. */
716
+ columns: readonly TableColumn<TRow>[];
717
+ /** The currency of a `money` field without its own ("Rs."). */
718
+ currency?: string | null;
719
+ }
720
+ /**
721
+ * A question before a form saves: what will be saved, in full, then Confirm or Back (`SubmitFormProps.confirm`, and so
722
+ * `ApiForm`'s and a CRUD's Add and Edit forms'). It is asked after every value has passed its checks and before the save
723
+ * starts. While it is open nothing else submits the form (Save, Enter, Ctrl+S, `form.submit()`) and the form does not say
724
+ * "Saving…". Back, Esc and the close button send nothing and leave the form as it was, the focus back where it was;
725
+ * Confirm saves. A click outside the question leaves it open. A rule here that throws shows its message under the form
726
+ * and asks nothing.
727
+ *
728
+ * `body` is what will be sent: an `ApiForm`'s `transform` answer (worked out once, before the question, and sent as it
729
+ * is, so the question never shows figures of another body), or the values in a `SubmitForm`.
730
+ *
731
+ * @example
732
+ * confirm={{
733
+ * title: "Place this order?",
734
+ * message: (_values, body) => `Rs. ${(body as Order).total} for ${(body as Order).item}.`,
735
+ * details: (_values, body) => ({ row: body as Order, columns: ORDER_COLUMNS, currency: "Rs." }),
736
+ * confirmLabel: "Place order",
737
+ * size: "xl",
738
+ * }}
739
+ */
740
+ export interface FormConfirmParams<TValues extends FormValues = FormValues> {
741
+ /** Ask only when this holds, e.g. for the kinds that take money. Default: before every save. */
742
+ when?: (values: TValues) => boolean;
743
+ /** The question: "Save this payment?". Text, or from the values and the body. */
744
+ title: ReactNode | ((values: TValues, body: unknown) => ReactNode);
745
+ /** One line under it, in the box's muted text. */
746
+ message?: ReactNode | ((values: TValues, body: unknown) => ReactNode);
747
+ /** What will be saved, in full, drawn as a record's details: a section per run of columns with one `group`. `null` draws none. */
748
+ details?: (values: TValues, body: unknown) => FormConfirmDetails | null | undefined;
749
+ /** The button that saves. Default "Confirm and save". It has the focus, so Enter saves. */
750
+ confirmLabel?: ReactNode | ((values: TValues, body: unknown) => ReactNode);
751
+ /** The button that goes back to the form. Default "Back to the form". */
752
+ cancelLabel?: ReactNode;
753
+ /** The question's width. Default `lg` with details (two fields to a line), else `sm`; `xl` gives three to a line. */
754
+ size?: PopupSize;
755
+ }
357
756
  /** Props of `SubmitForm`: a `Form` in a `<form>`, checked on submit, with Save, Cancel, and Reset. */
358
757
  export interface SubmitFormProps<TValues extends FormValues = FormValues> extends FormProps<TValues> {
359
758
  /**
@@ -372,6 +771,8 @@ export interface SubmitFormProps<TValues extends FormValues = FormValues> extend
372
771
  submitLabel?: ReactNode;
373
772
  /** Default: a floppy disk. `null` shows none. */
374
773
  submitIcon?: ReactNode | null;
774
+ /** Default `primary`; `danger` for a save that removes something, as in a step that confirms it with a password. */
775
+ submitTone?: FormSubmitTone;
375
776
  /** Default "Cancel". */
376
777
  cancelLabel?: ReactNode;
377
778
  /** Shows a Reset button, which brings back the values the form opened with (or saved last). */
@@ -386,9 +787,20 @@ export interface SubmitFormProps<TValues extends FormValues = FormValues> extend
386
787
  statusLine?: FormStatusLineMode;
387
788
  /** Asks before the page closes or reloads with unsaved changes. Default: true without a `draftKey` (a draft keeps them). */
388
789
  confirmLeave?: boolean;
790
+ /**
791
+ * Asks before it saves, showing what will be saved in full, then Confirm or Back (`FormConfirmParams`): after every
792
+ * value has passed its checks, before "Saving…". Default: saves at once.
793
+ */
794
+ confirm?: FormConfirmParams<TValues>;
389
795
  /** Name for screen readers. */
390
796
  "aria-label"?: string;
391
797
  "aria-labelledby"?: string;
798
+ /**
799
+ * What the form does, for screen readers: the id of the line that says it ("Google Authenticator will stop giving
800
+ * codes"), heard after the form's name, so a step that puts the focus straight into its password box still says what
801
+ * goes.
802
+ */
803
+ "aria-describedby"?: string;
392
804
  }
393
805
  /** A request an API form sends to a URL outside the app's modules, through the app's `api`. A module's API is an endpoint. */
394
806
  export interface ApiRequest {
@@ -402,6 +814,15 @@ export interface ApiRequest {
402
814
  }
403
815
  /** Sends a form's values with the app's own client. Return what it answers: a `Response`, a parsed body, or an `ApiResult`. */
404
816
  export type ApiSend<TValues extends FormValues = FormValues> = (body: unknown, context: FormSubmitContext<TValues>) => Promise<unknown>;
817
+ /**
818
+ * What an `ApiForm`'s save sent (`onSuccess`): the form's values as they were sent - values typed while it ran are not
819
+ * among them - and the body `transform` made of them (the values themselves without one). A row built from the save
820
+ * reads the body: a field `transform` cleared is cleared there, where the values still hold what was typed.
821
+ */
822
+ export interface ApiFormSent<TValues extends FormValues = FormValues> {
823
+ values: TValues;
824
+ body: unknown;
825
+ }
405
826
  /** Props of `ApiForm`: a `SubmitForm` that sends the values to an API and shows the server's messages on their inputs. */
406
827
  export interface ApiFormProps<TValues extends FormValues = FormValues> extends Omit<SubmitFormProps<TValues>, "onSubmit" | "onSaved"> {
407
828
  /**
@@ -410,7 +831,11 @@ export interface ApiFormProps<TValues extends FormValues = FormValues> extends O
410
831
  * with a client of the app's own. Endpoints and requests go through `api`, with the app's headers and sign-in handling.
411
832
  */
412
833
  api: ApiRequest | ApiEndpoint | ApiSend<TValues>;
413
- /** Shapes what is sent, e.g. `{ items: [values] }` for a batch endpoint. May be async. Default: the values. */
834
+ /**
835
+ * Shapes what is sent, e.g. `{ items: [values] }` for a batch endpoint. May be async. Default: the values. With
836
+ * `confirm`, it runs once, before the question, which is handed what it made (`body`); that is then sent as it is. A
837
+ * throw shows its message under the form.
838
+ */
414
839
  transform?: (values: TValues, context: FormSubmitContext<TValues>) => unknown;
415
840
  /**
416
841
  * Reads the server's answer. Default: the app's reader (`configureApi({ read })`), else `readApiResponse`: Nexus API
@@ -418,8 +843,11 @@ export interface ApiFormProps<TValues extends FormValues = FormValues> extends O
418
843
  * any JSON by its HTTP status.
419
844
  */
420
845
  readResponse?: (body: unknown, status: number | undefined) => ApiResult;
421
- /** After a save, once the saved values are the form's own, with what the server sent back: close a popup, update a table row. */
422
- onSuccess?: (result: unknown, values: TValues, form: FormStore<TValues>) => void;
846
+ /**
847
+ * After a save, once the saved values are the form's own, with what the server sent back: close a popup, update a table
848
+ * row. `sent` is what that save sent: the values as they were then, and the body `transform` made of them.
849
+ */
850
+ onSuccess?: (result: unknown, values: TValues, form: FormStore<TValues>, sent: ApiFormSent<TValues>) => void;
423
851
  /** After a failed save, with the server's answer. The form already shows its messages. */
424
852
  onError?: (response: ApiResult, values: TValues) => void;
425
853
  /** The toast after a save: text, or `false` for none. Default: the server's message, else the endpoint's ("Room saved."), else "Saved." */