nexus-shared 1.1.20 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +903 -0
  3. package/dist/Client.Index.d.ts +44 -0
  4. package/dist/Client.Index.js +49 -0
  5. package/dist/Components/Documents/Button.d.ts +29 -0
  6. package/dist/Components/Documents/Button.js +11 -0
  7. package/dist/Components/Documents/Menu.d.ts +102 -0
  8. package/dist/Components/Documents/Menu.js +238 -0
  9. package/dist/Components/Documents/SplitButton.d.ts +64 -0
  10. package/dist/Components/Documents/SplitButton.js +127 -0
  11. package/dist/Components/Documents/TabButtons.d.ts +62 -0
  12. package/dist/Components/Documents/TabButtons.js +19 -0
  13. package/dist/Components/Forms/ApiForm.d.ts +21 -0
  14. package/dist/Components/Forms/ApiForm.js +99 -0
  15. package/dist/Components/Forms/Crud.d.ts +17 -0
  16. package/dist/Components/Forms/Crud.js +556 -0
  17. package/dist/Components/Forms/CrudParts.d.ts +15 -0
  18. package/dist/Components/Forms/CrudParts.js +36 -0
  19. package/dist/Components/Forms/Form.d.ts +52 -0
  20. package/dist/Components/Forms/Form.js +319 -0
  21. package/dist/Components/Forms/SubmitForm.d.ts +65 -0
  22. package/dist/Components/Forms/SubmitForm.js +198 -0
  23. package/dist/Components/Inputs/Calendar.d.ts +69 -0
  24. package/dist/Components/Inputs/Calendar.js +258 -0
  25. package/dist/Components/Inputs/CheckBox.d.ts +53 -0
  26. package/dist/Components/Inputs/CheckBox.js +87 -0
  27. package/dist/Components/Inputs/CheckBoxGroup.d.ts +20 -0
  28. package/dist/Components/Inputs/CheckBoxGroup.js +80 -0
  29. package/dist/Components/Inputs/ChoiceField.d.ts +122 -0
  30. package/dist/Components/Inputs/ChoiceField.js +169 -0
  31. package/dist/Components/Inputs/DatePicker.d.ts +54 -0
  32. package/dist/Components/Inputs/DatePicker.js +246 -0
  33. package/dist/Components/Inputs/DateRangePicker.d.ts +62 -0
  34. package/dist/Components/Inputs/DateRangePicker.js +283 -0
  35. package/dist/Components/Inputs/DateTimePicker.d.ts +75 -0
  36. package/dist/Components/Inputs/DateTimePicker.js +399 -0
  37. package/dist/Components/Inputs/Dropdown.d.ts +117 -0
  38. package/dist/Components/Inputs/Dropdown.js +839 -0
  39. package/dist/Components/Inputs/DropdownParts.d.ts +84 -0
  40. package/dist/Components/Inputs/DropdownParts.js +102 -0
  41. package/dist/Components/Inputs/EditorShell.d.ts +45 -0
  42. package/dist/Components/Inputs/EditorShell.js +214 -0
  43. package/dist/Components/Inputs/Field.d.ts +53 -0
  44. package/dist/Components/Inputs/Field.js +16 -0
  45. package/dist/Components/Inputs/GroupForm.d.ts +20 -0
  46. package/dist/Components/Inputs/GroupForm.js +170 -0
  47. package/dist/Components/Inputs/InputField.d.ts +59 -0
  48. package/dist/Components/Inputs/InputField.js +169 -0
  49. package/dist/Components/Inputs/InputRenderer.d.ts +35 -0
  50. package/dist/Components/Inputs/InputRenderer.js +99 -0
  51. package/dist/Components/Inputs/MarkdownEditor.d.ts +67 -0
  52. package/dist/Components/Inputs/MarkdownEditor.js +289 -0
  53. package/dist/Components/Inputs/NumberBox.d.ts +53 -0
  54. package/dist/Components/Inputs/NumberBox.js +228 -0
  55. package/dist/Components/Inputs/RadioGroup.d.ts +20 -0
  56. package/dist/Components/Inputs/RadioGroup.js +43 -0
  57. package/dist/Components/Inputs/ReadOnlyNotice.d.ts +36 -0
  58. package/dist/Components/Inputs/ReadOnlyNotice.js +116 -0
  59. package/dist/Components/Inputs/RichTextEditor.d.ts +103 -0
  60. package/dist/Components/Inputs/RichTextEditor.js +1170 -0
  61. package/dist/Components/Inputs/RichTextParts.d.ts +63 -0
  62. package/dist/Components/Inputs/RichTextParts.js +157 -0
  63. package/dist/Components/Inputs/RowsInput.d.ts +104 -0
  64. package/dist/Components/Inputs/RowsInput.js +259 -0
  65. package/dist/Components/Inputs/Slider.d.ts +87 -0
  66. package/dist/Components/Inputs/Slider.js +216 -0
  67. package/dist/Components/Inputs/TabularForm.d.ts +23 -0
  68. package/dist/Components/Inputs/TabularForm.js +283 -0
  69. package/dist/Components/Inputs/TextArea.d.ts +43 -0
  70. package/dist/Components/Inputs/TextArea.js +31 -0
  71. package/dist/Components/Inputs/TextBox.d.ts +52 -0
  72. package/dist/Components/Inputs/TextBox.js +58 -0
  73. package/dist/Components/Inputs/TextField.d.ts +31 -0
  74. package/dist/Components/Inputs/TextField.js +35 -0
  75. package/dist/Components/Inputs/TimePanel.d.ts +34 -0
  76. package/dist/Components/Inputs/TimePanel.js +288 -0
  77. package/dist/Components/Inputs/TimePicker.d.ts +54 -0
  78. package/dist/Components/Inputs/TimePicker.js +239 -0
  79. package/dist/Components/Inputs/TreeView.d.ts +160 -0
  80. package/dist/Components/Inputs/TreeView.js +947 -0
  81. package/dist/Components/Layouts/MessageBox.d.ts +71 -0
  82. package/dist/Components/Layouts/MessageBox.js +104 -0
  83. package/dist/Components/Layouts/Popup.d.ts +92 -0
  84. package/dist/Components/Layouts/Popup.js +376 -0
  85. package/dist/Components/Layouts/PopupContext.d.ts +2 -0
  86. package/dist/Components/Layouts/PopupContext.js +4 -0
  87. package/dist/Components/Layouts/PopupDock.d.ts +22 -0
  88. package/dist/Components/Layouts/PopupDock.js +121 -0
  89. package/dist/Components/Layouts/PopupHost.d.ts +56 -0
  90. package/dist/Components/Layouts/PopupHost.js +163 -0
  91. package/dist/Components/Layouts/PopupRules.d.ts +30 -0
  92. package/dist/Components/Layouts/PopupRules.js +63 -0
  93. package/dist/Components/Layouts/ThemePicker.d.ts +13 -0
  94. package/dist/Components/Layouts/ThemePicker.js +22 -0
  95. package/dist/Components/Layouts/ThemeScript.d.ts +14 -0
  96. package/dist/Components/Layouts/ThemeScript.js +13 -0
  97. package/dist/Components/Layouts/ThemeSwitcher.d.ts +13 -0
  98. package/dist/Components/Layouts/ThemeSwitcher.js +29 -0
  99. package/dist/Components/Layouts/Toaster.d.ts +76 -0
  100. package/dist/Components/Layouts/Toaster.js +172 -0
  101. package/dist/Components/Viewers/DataTable.d.ts +25 -0
  102. package/dist/Components/Viewers/DataTable.js +542 -0
  103. package/dist/Components/Viewers/DataTableParts.d.ts +134 -0
  104. package/dist/Components/Viewers/DataTableParts.js +145 -0
  105. package/dist/Components/Viewers/MarkdownView.d.ts +26 -0
  106. package/dist/Components/Viewers/MarkdownView.js +79 -0
  107. package/dist/Components/Viewers/RichTextView.d.ts +18 -0
  108. package/dist/Components/Viewers/RichTextView.js +17 -0
  109. package/dist/Helpers/AnimationHelpers.d.ts +6 -0
  110. package/dist/Helpers/AnimationHelpers.js +35 -0
  111. package/dist/Helpers/ApiClient.d.ts +74 -0
  112. package/dist/Helpers/ApiClient.js +197 -0
  113. package/dist/Helpers/ApiFormHelpers.d.ts +26 -0
  114. package/dist/Helpers/ApiFormHelpers.js +56 -0
  115. package/dist/Helpers/ApiModules.d.ts +135 -0
  116. package/dist/Helpers/ApiModules.js +194 -0
  117. package/dist/Helpers/ApiResponses.d.ts +41 -0
  118. package/dist/Helpers/ApiResponses.js +159 -0
  119. package/dist/Helpers/ApiRoutes.d.ts +150 -0
  120. package/dist/Helpers/ApiRoutes.js +191 -0
  121. package/dist/Helpers/ApiToasts.d.ts +21 -0
  122. package/dist/Helpers/ApiToasts.js +22 -0
  123. package/dist/Helpers/ClassHelpers.d.ts +3 -0
  124. package/dist/Helpers/ClassHelpers.js +4 -0
  125. package/dist/Helpers/CrudBackend.d.ts +56 -0
  126. package/dist/Helpers/CrudBackend.js +119 -0
  127. package/dist/Helpers/CrudHelpers.d.ts +76 -0
  128. package/dist/Helpers/CrudHelpers.js +202 -0
  129. package/dist/Helpers/DateHelpers.d.ts +88 -0
  130. package/dist/Helpers/DateHelpers.js +363 -0
  131. package/dist/Helpers/DatePreferences.d.ts +14 -0
  132. package/dist/Helpers/DatePreferences.js +94 -0
  133. package/dist/Helpers/DateTimeHelpers.d.ts +87 -0
  134. package/dist/Helpers/DateTimeHelpers.js +286 -0
  135. package/dist/Helpers/DragHelpers.d.ts +129 -0
  136. package/dist/Helpers/DragHelpers.js +281 -0
  137. package/dist/Helpers/DropdownHelpers.d.ts +56 -0
  138. package/dist/Helpers/DropdownHelpers.js +180 -0
  139. package/dist/Helpers/FormDrafts.d.ts +18 -0
  140. package/dist/Helpers/FormDrafts.js +76 -0
  141. package/dist/Helpers/FormStore.d.ts +231 -0
  142. package/dist/Helpers/FormStore.js +734 -0
  143. package/dist/Helpers/InputParamsHelpers.d.ts +79 -0
  144. package/dist/Helpers/InputParamsHelpers.js +388 -0
  145. package/dist/Helpers/MarkdownEditing.d.ts +27 -0
  146. package/dist/Helpers/MarkdownEditing.js +250 -0
  147. package/dist/Helpers/MarkdownHelpers.d.ts +82 -0
  148. package/dist/Helpers/MarkdownHelpers.js +775 -0
  149. package/dist/Helpers/MessageBuilder.d.ts +74 -0
  150. package/dist/Helpers/MessageBuilder.js +254 -0
  151. package/dist/Helpers/NepaliCalendar.d.ts +25 -0
  152. package/dist/Helpers/NepaliCalendar.js +107 -0
  153. package/dist/Helpers/NumberHelpers.d.ts +71 -0
  154. package/dist/Helpers/NumberHelpers.js +202 -0
  155. package/dist/Helpers/Permissions.d.ts +62 -0
  156. package/dist/Helpers/Permissions.js +133 -0
  157. package/dist/Helpers/PopoverHelpers.d.ts +43 -0
  158. package/dist/Helpers/PopoverHelpers.js +91 -0
  159. package/dist/Helpers/RichTextHelpers.d.ts +77 -0
  160. package/dist/Helpers/RichTextHelpers.js +1072 -0
  161. package/dist/Helpers/RowsStore.d.ts +160 -0
  162. package/dist/Helpers/RowsStore.js +584 -0
  163. package/dist/Helpers/TableColumns.d.ts +40 -0
  164. package/dist/Helpers/TableColumns.js +254 -0
  165. package/dist/Helpers/TableExport.d.ts +30 -0
  166. package/dist/Helpers/TableExport.js +97 -0
  167. package/dist/Helpers/TableHelpers.d.ts +102 -0
  168. package/dist/Helpers/TableHelpers.js +281 -0
  169. package/dist/Helpers/TableStore.d.ts +142 -0
  170. package/dist/Helpers/TableStore.js +530 -0
  171. package/dist/Helpers/ThemeHelpers.d.ts +15 -0
  172. package/dist/Helpers/ThemeHelpers.js +16 -0
  173. package/dist/Helpers/TimeHelpers.d.ts +73 -0
  174. package/dist/Helpers/TimeHelpers.js +253 -0
  175. package/dist/Helpers/TreeStore.d.ts +188 -0
  176. package/dist/Helpers/TreeStore.js +577 -0
  177. package/dist/Helpers/ValidationHelpers.d.ts +124 -0
  178. package/dist/Helpers/ValidationHelpers.js +301 -0
  179. package/dist/Interfaces/ApiInterfaces.d.ts +149 -0
  180. package/dist/Interfaces/ApiInterfaces.js +24 -0
  181. package/dist/Interfaces/CrudInterfaces.d.ts +171 -0
  182. package/{src/sso-client.tsx → dist/Interfaces/CrudInterfaces.js} +1 -1
  183. package/dist/Interfaces/DateInterfaces.d.ts +154 -0
  184. package/dist/Interfaces/DateInterfaces.js +29 -0
  185. package/dist/Interfaces/FormInterfaces.d.ts +352 -0
  186. package/dist/Interfaces/FormInterfaces.js +1 -0
  187. package/dist/Interfaces/InputInterfaces.d.ts +141 -0
  188. package/dist/Interfaces/InputInterfaces.js +1 -0
  189. package/dist/Interfaces/MessageInterfaces.d.ts +111 -0
  190. package/dist/Interfaces/MessageInterfaces.js +5 -0
  191. package/dist/Interfaces/TableInterfaces.d.ts +291 -0
  192. package/dist/Interfaces/TableInterfaces.js +1 -0
  193. package/dist/Interfaces/ThemeInterfaces.d.ts +17 -0
  194. package/dist/Interfaces/ThemeInterfaces.js +17 -0
  195. package/dist/Interfaces/TypeInterfaces.d.ts +4 -0
  196. package/dist/Interfaces/TypeInterfaces.js +1 -0
  197. package/dist/Server.Index.d.ts +2 -0
  198. package/dist/Server.Index.js +5 -0
  199. package/dist/Services/ApiProxy.d.ts +32 -0
  200. package/dist/Services/ApiProxy.js +111 -0
  201. package/dist/Services/BrowserApi.d.ts +78 -0
  202. package/dist/Services/BrowserApi.js +111 -0
  203. package/dist/Services/ServerApi.d.ts +49 -0
  204. package/dist/Services/ServerApi.js +75 -0
  205. package/dist/Services/ThemeService.d.ts +17 -0
  206. package/dist/Services/ThemeService.js +83 -0
  207. package/dist/Shared.Index.d.ts +43 -0
  208. package/dist/Shared.Index.js +47 -0
  209. package/package.json +82 -33
  210. package/src/Styles/Nexus.Base.css +70 -0
  211. package/src/Styles/Nexus.Button.css +151 -0
  212. package/src/Styles/Nexus.Calendar.css +275 -0
  213. package/src/Styles/Nexus.Choice.css +345 -0
  214. package/src/Styles/Nexus.Crud.css +84 -0
  215. package/src/Styles/Nexus.DateTime.css +81 -0
  216. package/src/Styles/Nexus.Dropdown.css +774 -0
  217. package/src/Styles/Nexus.Editor.css +297 -0
  218. package/src/Styles/Nexus.Form.css +298 -0
  219. package/src/Styles/Nexus.Index.css +34 -0
  220. package/src/Styles/Nexus.Input.css +637 -0
  221. package/src/Styles/Nexus.Markdown.css +265 -0
  222. package/src/Styles/Nexus.Menu.css +249 -0
  223. package/src/Styles/Nexus.Popup.css +731 -0
  224. package/src/Styles/Nexus.RichText.css +463 -0
  225. package/src/Styles/Nexus.Rows.css +566 -0
  226. package/src/Styles/Nexus.Slider.css +278 -0
  227. package/src/Styles/Nexus.Tab.Buttons.css +364 -0
  228. package/src/Styles/Nexus.Table.css +1014 -0
  229. package/src/Styles/Nexus.Theme.Picker.css +159 -0
  230. package/src/Styles/Nexus.Themes.css +194 -0
  231. package/src/Styles/Nexus.Time.css +147 -0
  232. package/src/Styles/Nexus.Toast.css +265 -0
  233. package/src/Styles/Nexus.Tokens.css +72 -0
  234. package/src/Styles/Nexus.Tree.css +435 -0
  235. package/src/api-services/authentication-service.tsx +0 -23
  236. package/src/api-services/preference-service.tsx +0 -5
  237. package/src/api-services/system-service.tsx +0 -34
  238. package/src/client.ts +0 -31
  239. package/src/components/documents/button.tsx +0 -142
  240. package/src/components/documents/icon-box.tsx +0 -93
  241. package/src/components/documents/page-title.tsx +0 -7
  242. package/src/components/documents/tab-button.tsx +0 -172
  243. package/src/components/documents/tag.tsx +0 -30
  244. package/src/components/index.js +0 -0
  245. package/src/components/inputs/checkbox-input.tsx +0 -66
  246. package/src/components/inputs/input-box.tsx +0 -48
  247. package/src/components/inputs/input-element.tsx +0 -65
  248. package/src/components/inputs/input-form.tsx +0 -164
  249. package/src/components/inputs/input.tsx +0 -181
  250. package/src/components/inputs/number-input.tsx +0 -108
  251. package/src/components/inputs/radiobox-input.tsx +0 -53
  252. package/src/components/inputs/textarea-input.tsx +0 -47
  253. package/src/components/inputs/textbox-input.tsx +0 -45
  254. package/src/components/layouts/global-dialogbox.tsx +0 -431
  255. package/src/components/layouts/global-layout.tsx +0 -59
  256. package/src/components/layouts/layout-helpers.tsx +0 -20
  257. package/src/components/layouts/panels/user-panel.tsx +0 -112
  258. package/src/components/layouts/utility-menu.tsx +0 -49
  259. package/src/components/panels/theme-panel.tsx +0 -46
  260. package/src/helpers/bitwise-helpers.tsx +0 -11
  261. package/src/helpers/browser-helpers.tsx +0 -156
  262. package/src/helpers/datasource-helpers.tsx +0 -99
  263. package/src/helpers/element-helpers.tsx +0 -57
  264. package/src/helpers/input-helpers.tsx +0 -74
  265. package/src/helpers/string-helpers.tsx +0 -28
  266. package/src/helpers/utility-helpers.tsx +0 -45
  267. package/src/helpers/validation-helpers.tsx +0 -302
  268. package/src/index.ts +0 -26
  269. package/src/interface.ts +0 -19
  270. package/src/interfaces/auth-token-interfaces.tsx +0 -6
  271. package/src/interfaces/browser-interfaces.tsx +0 -36
  272. package/src/interfaces/button-interfaces.tsx +0 -63
  273. package/src/interfaces/datasource-interfaces.tsx +0 -22
  274. package/src/interfaces/datatable-interfaces.tsx +0 -25
  275. package/src/interfaces/dialogbox-interfaces.tsx +0 -5
  276. package/src/interfaces/exception-interfaces.tsx +0 -99
  277. package/src/interfaces/http-interfaces.tsx +0 -129
  278. package/src/interfaces/icon-interfaces.tsx +0 -129
  279. package/src/interfaces/input-interfaces.tsx +0 -410
  280. package/src/interfaces/layout-interfaces.tsx +0 -191
  281. package/src/interfaces/menu-interfaces.tsx +0 -49
  282. package/src/interfaces/message-interfaces.tsx +0 -32
  283. package/src/interfaces/permission-interfaces.tsx +0 -9
  284. package/src/interfaces/storage-interfaces.tsx +0 -5
  285. package/src/interfaces/system-interfaces.tsx +0 -22
  286. package/src/interfaces/theme-interfaces.tsx +0 -123
  287. package/src/interfaces/type-interfaces.tsx +0 -28
  288. package/src/interfaces/user-interfaces.tsx +0 -47
  289. package/src/nexus-client.tsx +0 -21
  290. package/src/nexus.environments.tsx +0 -66
  291. package/src/proxy-api/proxy-backend.tsx +0 -60
  292. package/src/proxy-api/proxy-constants.tsx +0 -28
  293. package/src/proxy-api/proxy-helpers.tsx +0 -91
  294. package/src/proxy-api-client.tsx +0 -1
  295. package/src/proxy-api-server.tsx +0 -2
  296. package/src/services/http-client-services.tsx +0 -75
  297. package/src/services/http-services.tsx +0 -158
  298. package/src/services/loader-service.tsx +0 -185
  299. package/src/services/localstorage-service.tsx +0 -119
  300. package/src/services/message-services.tsx +0 -383
  301. package/src/services/theme-service.tsx +0 -161
  302. package/src/services/user-services.tsx +0 -10
  303. package/src/sso-config/auth-token-validation.ts +0 -16
  304. package/src/sso-config/callback-route.tsx +0 -35
  305. package/src/sso-config/cookie-encryption.ts +0 -51
  306. package/src/sso-config/cookie-helpers.tsx +0 -102
  307. package/src/sso-config/forgot-password-route.tsx +0 -36
  308. package/src/sso-config/oauth-callback-state.ts +0 -61
  309. package/src/sso-config/pkce-helpers.ts +0 -9
  310. package/src/sso-config/provider-complete-route.tsx +0 -18
  311. package/src/sso-config/redirect-context-actions.tsx +0 -45
  312. package/src/sso-config/redirect-context-helpers.tsx +0 -159
  313. package/src/sso-config/redirect-context-persist.tsx +0 -18
  314. package/src/sso-config/redirect-context-route.tsx +0 -45
  315. package/src/sso-config/refresh-route.tsx +0 -23
  316. package/src/sso-config/sign-in-route.tsx +0 -74
  317. package/src/sso-config/sign-out-route.tsx +0 -57
  318. package/src/sso-config/sign-up-route.tsx +0 -57
  319. package/src/sso-config/sso-backend-flow.ts +0 -38
  320. package/src/sso-config/sso-complete-route.tsx +0 -50
  321. package/src/sso-config/sso-config.ts +0 -59
  322. package/src/sso-config/sso-interfaces.tsx +0 -99
  323. package/src/sso-config/sso-response-helpers.tsx +0 -20
  324. package/src/sso-server.tsx +0 -21
  325. package/src/styles/nexus.animation.css +0 -269
  326. package/src/styles/nexus.core.css +0 -119
  327. package/src/styles/nexus.dialog.css +0 -144
  328. package/src/styles/nexus.icon.css +0 -51
  329. package/src/styles/nexus.input.css +0 -207
  330. package/src/styles/nexus.loader.css +0 -11
  331. package/src/styles/nexus.logic.css +0 -47
  332. package/src/styles/nexus.utility.css +0 -347
@@ -0,0 +1,584 @@
1
+ import { duplicateMessageFor, initialInputValue, inputValuesEqual, normalizeInputValue, rowsItemNames, rowsMessage, uniqueKeyOf, validateInput, } from "./InputParamsHelpers.js";
2
+ export class RowsStore {
3
+ options;
4
+ inputByName = new Map();
5
+ entries = [];
6
+ positions = new Map();
7
+ initial = [];
8
+ seq = 0;
9
+ generation = 0;
10
+ // Messages: an input's own rules, and values repeated in `unique` inputs.
11
+ ruleErrors = new Map();
12
+ duplicateErrors = new Map();
13
+ invalidByRow = new Map();
14
+ invalidCells = 0;
15
+ message = null;
16
+ messageShown = false;
17
+ showAll = false;
18
+ touched = new Map();
19
+ removed = null;
20
+ // Rendering: a revision per value set from code (its input mounts again with it), a version per row.
21
+ revisions = new Map();
22
+ versions = new Map();
23
+ rowListeners = new Map();
24
+ listeners = new Set();
25
+ valueListeners = new Set();
26
+ snapshot;
27
+ valueCache = null;
28
+ keysCache = null;
29
+ dirtyUnique = new Set();
30
+ /** The array last given to value listeners. A controlled parent that passes it back changes nothing. */
31
+ lastValue = null;
32
+ constructor(options, rows = []) {
33
+ this.options = options;
34
+ this.indexInputs();
35
+ this.load(rows);
36
+ this.initial = this.getValue();
37
+ this.lastValue = this.initial;
38
+ this.snapshot = this.buildSnapshot();
39
+ }
40
+ /* Reading */
41
+ /** Every row as it is now. The same array until something changes; do not change it. */
42
+ getValue() {
43
+ this.valueCache ??= this.entries.map(entry => entry.values);
44
+ return this.valueCache;
45
+ }
46
+ getSnapshot = () => this.snapshot;
47
+ get count() {
48
+ return this.entries.length;
49
+ }
50
+ get inputs() {
51
+ return this.options.inputs;
52
+ }
53
+ /** The item's names for titles and messages. */
54
+ get names() {
55
+ return rowsItemNames(this.options, this.options.fallbackItem ?? "Row");
56
+ }
57
+ indexOf(key) {
58
+ return this.positions.get(key) ?? -1;
59
+ }
60
+ keyAt(index) {
61
+ return this.entries[index]?.key ?? null;
62
+ }
63
+ row(key) {
64
+ const index = this.positions.get(key);
65
+ return index === undefined ? undefined : this.entries[index]?.values;
66
+ }
67
+ get(key, name) {
68
+ return this.row(key)?.[name];
69
+ }
70
+ /** Grows when a value was set from code; an input renders again from the store with a new one. */
71
+ revision(key, name) {
72
+ return this.revisions.get(key)?.get(name) ?? 0;
73
+ }
74
+ /** Grows whenever what a row shows changes: a message, or a value set from code. */
75
+ version(key) {
76
+ return this.versions.get(key) ?? 0;
77
+ }
78
+ /** The message a value shows now: every message after `validateAll`; before, a repeated value once its input was left. */
79
+ shownError(key, name) {
80
+ const duplicate = this.duplicateErrors.get(key)?.get(name);
81
+ if (this.showAll)
82
+ return this.ruleErrors.get(key)?.get(name) ?? duplicate;
83
+ return duplicate && this.touched.get(key)?.has(name) ? duplicate : undefined;
84
+ }
85
+ /** How many values in a row fail. */
86
+ rowProblems(key) {
87
+ return this.invalidByRow.get(key) ?? 0;
88
+ }
89
+ /** Whether any value or the rows' own rules fail, shown or not. */
90
+ hasProblems() {
91
+ return this.invalidCells > 0 || this.message !== null;
92
+ }
93
+ /** The first problem in row order, then the rows' own message; `null` when all is valid. */
94
+ firstProblem() {
95
+ if (this.invalidCells > 0) {
96
+ for (const entry of this.entries) {
97
+ if (!this.invalidByRow.get(entry.key))
98
+ continue;
99
+ for (const params of this.options.inputs) {
100
+ if (this.ruleErrors.get(entry.key)?.has(params.name) || this.duplicateErrors.get(entry.key)?.has(params.name))
101
+ return { key: entry.key, name: params.name };
102
+ }
103
+ }
104
+ }
105
+ return this.message ? { key: null, name: null } : null;
106
+ }
107
+ /** The first problem's message, named with its row: "Address 2: City is required." */
108
+ firstMessage() {
109
+ const place = this.firstProblem();
110
+ if (!place)
111
+ return null;
112
+ if (place.key === null)
113
+ return this.message;
114
+ const text = this.ruleErrors.get(place.key)?.get(place.name) ?? this.duplicateErrors.get(place.key)?.get(place.name) ?? "";
115
+ return `${this.names.title} ${this.indexOf(place.key) + 1}: ${text}`;
116
+ }
117
+ /** Whether the rows differ from the ones loaded last (`replace`) or first. */
118
+ isChanged() {
119
+ const rows = this.getValue();
120
+ if (rows.length !== this.initial.length)
121
+ return true;
122
+ return rows.some((row, index) => {
123
+ const before = this.initial[index];
124
+ return row !== before && this.options.inputs.some(params => !inputValuesEqual(row[params.name], before?.[params.name]));
125
+ });
126
+ }
127
+ /* Subscribing */
128
+ /** Structure: the rows, their order, and the rows' own message. */
129
+ subscribe = (listener) => {
130
+ this.listeners.add(listener);
131
+ return () => this.listeners.delete(listener);
132
+ };
133
+ /** One row: its messages and the values set from code. */
134
+ subscribeRow(key, listener) {
135
+ let set = this.rowListeners.get(key);
136
+ if (!set)
137
+ this.rowListeners.set(key, (set = new Set()));
138
+ set.add(listener);
139
+ return () => {
140
+ set.delete(listener);
141
+ if (!set.size && this.rowListeners.get(key) === set)
142
+ this.rowListeners.delete(key);
143
+ };
144
+ }
145
+ /** Every row, after each change of a value or of the rows. */
146
+ subscribeValue(listener) {
147
+ this.valueListeners.add(listener);
148
+ return () => this.valueListeners.delete(listener);
149
+ }
150
+ /* Changing */
151
+ /**
152
+ * New rules or inputs. `recheck` checks every value again (after a change of inputs or rules); without it, only the
153
+ * callbacks (`validate`, `onRowChange`, `newRow`) are swapped, which a parent's render does every time.
154
+ */
155
+ configure(options, recheck = true) {
156
+ this.options = options;
157
+ if (!recheck)
158
+ return;
159
+ this.indexInputs();
160
+ this.checkEverything();
161
+ this.touchAllRows();
162
+ this.refresh();
163
+ }
164
+ /** Sets a value in a row. `input` is a change the user made in the row's input: it runs `onRowChange`. */
165
+ set(key, name, value, source = "code") {
166
+ const before = this.getValue();
167
+ const changed = new Set();
168
+ this.write(key, name, value, source, changed, 0);
169
+ // Nothing was written: the same value again.
170
+ if (this.getValue() === before)
171
+ return;
172
+ this.settle(changed);
173
+ }
174
+ /** Marks a value as left by the user: a repeated value shows its message from now on. */
175
+ touch(key, name) {
176
+ let names = this.touched.get(key);
177
+ if (names?.has(name))
178
+ return;
179
+ if (!names)
180
+ this.touched.set(key, (names = new Set()));
181
+ names.add(name);
182
+ if (!this.showAll && this.duplicateErrors.get(key)?.has(name))
183
+ this.bump(key);
184
+ }
185
+ /** Adds a row with the inputs' defaults, `newRow`, and `values`, at `index` or at the end. Returns its key. */
186
+ add(values, index = this.entries.length) {
187
+ const position = Math.max(0, Math.min(index, this.entries.length));
188
+ const wasEmpty = this.entries.length === 0;
189
+ const row = {};
190
+ for (const params of this.options.inputs)
191
+ row[params.name] = initialInputValue(params);
192
+ const { newRow } = this.options;
193
+ Object.assign(row, typeof newRow === "function" ? newRow(position) : newRow, values);
194
+ // The first row of a list has each radio on: the first address added is the primary one.
195
+ for (const params of this.options.inputs) {
196
+ if (params.type === "radio" && wasEmpty && values?.[params.name] === undefined)
197
+ row[params.name] = true;
198
+ }
199
+ const entry = { key: this.newKey(), values: this.normalizeRow(row) };
200
+ this.entries.splice(position, 0, entry);
201
+ this.afterStructure();
202
+ const changed = new Set();
203
+ for (const params of this.options.inputs) {
204
+ this.checkCell(entry, params);
205
+ // Only one row keeps a radio on: a new row that has it takes it from the others.
206
+ if (params.type === "radio" && entry.values[params.name] === true)
207
+ this.radioOn(entry, params.name, changed);
208
+ }
209
+ this.messageShown = true;
210
+ this.removed = null;
211
+ this.markUniqueDirty();
212
+ this.settle(changed);
213
+ return entry.key;
214
+ }
215
+ /** Adds a copy of a row after it, without its id and with its radios off. Returns the copy's key. */
216
+ duplicate(key) {
217
+ const index = this.positions.get(key);
218
+ const source = index === undefined ? undefined : this.entries[index];
219
+ if (!source || index === undefined)
220
+ return null;
221
+ const values = { ...source.values };
222
+ if (this.options.rowKey)
223
+ delete values[this.options.rowKey];
224
+ for (const params of this.options.inputs)
225
+ if (params.type === "radio")
226
+ values[params.name] = false;
227
+ return this.add(values, index + 1);
228
+ }
229
+ /** Removes a row. A radio it had on moves to the first row left. `undoRemove` puts it back. */
230
+ remove(key) {
231
+ const index = this.positions.get(key);
232
+ if (index === undefined)
233
+ return;
234
+ const [entry] = this.entries.splice(index, 1);
235
+ if (!entry)
236
+ return;
237
+ this.forget(key);
238
+ this.afterStructure();
239
+ const changed = new Set();
240
+ const moved = [];
241
+ const first = this.entries[0];
242
+ for (const params of this.options.inputs) {
243
+ if (params.type !== "radio" || entry.values[params.name] !== true || !first)
244
+ continue;
245
+ this.write(first.key, params.name, true, "code", changed, 1);
246
+ moved.push({ key: first.key, name: params.name });
247
+ }
248
+ this.removed = { entry, index, moved };
249
+ this.messageShown = true;
250
+ this.markUniqueDirty();
251
+ this.settle(changed);
252
+ }
253
+ /** Puts the last removed row back where it was. Returns its key, or `null` when there is nothing to undo. */
254
+ undoRemove() {
255
+ const removed = this.removed;
256
+ if (!removed)
257
+ return null;
258
+ this.removed = null;
259
+ const { entry, index, moved } = removed;
260
+ // A row with the same id may have been added since; the row comes back under a new key then.
261
+ if (this.positions.has(entry.key))
262
+ entry.key = this.newKey();
263
+ this.entries.splice(Math.min(index, this.entries.length), 0, entry);
264
+ this.afterStructure();
265
+ const changed = new Set();
266
+ for (const { key, name } of moved)
267
+ if (this.positions.has(key))
268
+ this.write(key, name, false, "code", changed, 1);
269
+ for (const params of this.options.inputs)
270
+ this.checkCell(entry, params);
271
+ this.markUniqueDirty();
272
+ this.settle(changed);
273
+ return entry.key;
274
+ }
275
+ /** Moves a row to a new position. */
276
+ move(key, toIndex) {
277
+ const index = this.positions.get(key);
278
+ const target = Math.max(0, Math.min(toIndex, this.entries.length - 1));
279
+ if (index === undefined || index === target)
280
+ return;
281
+ const [entry] = this.entries.splice(index, 1);
282
+ if (!entry)
283
+ return;
284
+ this.entries.splice(target, 0, entry);
285
+ this.afterStructure();
286
+ this.removed = null;
287
+ // "Same as in row 1" names positions, which just changed.
288
+ this.markUniqueDirty();
289
+ this.settle(new Set());
290
+ }
291
+ /**
292
+ * Loads new rows, e.g. fresh data from a server. With `asInitial` (default), they become the rows `reset` and
293
+ * `isChanged` compare with. `emit` tells value listeners, for a change the parent does not know of yet.
294
+ */
295
+ replace(rows, { asInitial = true, emit = false } = {}) {
296
+ this.load(rows);
297
+ if (asInitial)
298
+ this.initial = this.getValue();
299
+ this.generation++;
300
+ this.showAll = false;
301
+ this.messageShown = false;
302
+ this.touched.clear();
303
+ this.removed = null;
304
+ this.refresh(true);
305
+ if (emit)
306
+ this.emit();
307
+ else
308
+ this.lastValue = this.getValue();
309
+ }
310
+ /** Back to the rows loaded last, as a form reset does. */
311
+ reset() {
312
+ this.replace(this.initial, { asInitial: false, emit: true });
313
+ }
314
+ /** Shows every message, as a submit does. Returns the first problem, or `null` when all is valid. */
315
+ validateAll() {
316
+ this.showAll = true;
317
+ this.messageShown = true;
318
+ this.checkEverything();
319
+ this.touchAllRows();
320
+ this.refresh();
321
+ return this.firstProblem();
322
+ }
323
+ /* Inside */
324
+ indexInputs() {
325
+ this.inputByName = new Map(this.options.inputs.map(params => [params.name, params]));
326
+ }
327
+ newKey() {
328
+ return "new:" + ++this.seq;
329
+ }
330
+ /** Each input's value in the type the input reports, and the other fields of the row as they came. */
331
+ normalizeRow(row) {
332
+ const values = { ...row };
333
+ for (const params of this.options.inputs) {
334
+ // A hidden input's value stays in the data as it was.
335
+ if (params.hidden)
336
+ continue;
337
+ values[params.name] = normalizeInputValue(params, row[params.name]);
338
+ }
339
+ return values;
340
+ }
341
+ load(rows) {
342
+ const { rowKey } = this.options;
343
+ const used = new Set();
344
+ this.entries = rows.map(row => {
345
+ const id = rowKey ? row[rowKey] : undefined;
346
+ let key = id === undefined || id === null || id === "" ? "" : "id:" + String(id);
347
+ if (!key || used.has(key))
348
+ key = this.newKey();
349
+ used.add(key);
350
+ return { key, values: this.normalizeRow(row) };
351
+ });
352
+ // Saved data with a radio on in more than one row keeps the first.
353
+ for (const params of this.options.inputs) {
354
+ if (params.type !== "radio")
355
+ continue;
356
+ let found = false;
357
+ for (const entry of this.entries) {
358
+ if (entry.values[params.name] !== true)
359
+ continue;
360
+ if (found)
361
+ entry.values = { ...entry.values, [params.name]: false };
362
+ found = true;
363
+ }
364
+ }
365
+ this.revisions.clear();
366
+ this.afterStructure();
367
+ this.checkEverything();
368
+ }
369
+ afterStructure() {
370
+ this.positions = new Map(this.entries.map((entry, index) => [entry.key, index]));
371
+ this.valueCache = null;
372
+ this.keysCache = null;
373
+ }
374
+ /** Drops what the store knew about a removed row. */
375
+ forget(key) {
376
+ this.invalidCells -= this.invalidByRow.get(key) ?? 0;
377
+ this.invalidByRow.delete(key);
378
+ this.ruleErrors.delete(key);
379
+ this.duplicateErrors.delete(key);
380
+ this.touched.delete(key);
381
+ this.revisions.delete(key);
382
+ }
383
+ write(key, name, value, source, changed, depth) {
384
+ const index = this.positions.get(key);
385
+ const entry = index === undefined ? undefined : this.entries[index];
386
+ if (!entry || index === undefined)
387
+ return;
388
+ const params = this.inputByName.get(name);
389
+ const next = params && source === "code" ? normalizeInputValue(params, value) : value;
390
+ if (inputValuesEqual(entry.values[name], next))
391
+ return;
392
+ const shownBefore = this.shownError(key, name);
393
+ entry.values = { ...entry.values, [name]: next };
394
+ this.valueCache = null;
395
+ if (source === "code") {
396
+ // The input shows its value from when it mounted; a new revision mounts it again with this one.
397
+ let names = this.revisions.get(key);
398
+ if (!names)
399
+ this.revisions.set(key, (names = new Map()));
400
+ names.set(name, (names.get(name) ?? 0) + 1);
401
+ changed.add(key);
402
+ }
403
+ if (params) {
404
+ this.checkCell(entry, params);
405
+ if (params.type === "radio") {
406
+ // The row shows which radios it has on (a section's "Primary" tag), so it renders again, typed or not.
407
+ changed.add(key);
408
+ if (next === true)
409
+ this.radioOn(entry, name, changed);
410
+ }
411
+ }
412
+ if (this.options.unique?.includes(name))
413
+ this.dirtyUnique.add(name);
414
+ if (this.shownError(key, name) !== shownBefore)
415
+ changed.add(key);
416
+ // Values the row derives from the one the user changed, such as an amount from a quantity and a rate.
417
+ if (source === "input" && depth === 0 && this.options.onRowChange) {
418
+ const patch = this.options.onRowChange(entry.values, name, index);
419
+ if (patch) {
420
+ for (const [field, fieldValue] of Object.entries(patch)) {
421
+ if (field !== name)
422
+ this.write(key, field, fieldValue, "code", changed, depth + 1);
423
+ }
424
+ }
425
+ }
426
+ }
427
+ /** Turns a radio off in every row but this one. */
428
+ radioOn(entry, name, changed) {
429
+ for (const other of this.entries) {
430
+ if (other !== entry && other.values[name] === true)
431
+ this.write(other.key, name, false, "code", changed, 1);
432
+ }
433
+ }
434
+ /** Checks one value against its input's rules. */
435
+ checkCell(entry, params) {
436
+ const message = params.hidden ? null : validateInput(params, entry.values[params.name], this.options.context);
437
+ this.setError(this.ruleErrors, entry.key, params.name, message);
438
+ }
439
+ setError(map, key, name, message) {
440
+ const current = map.get(key);
441
+ if ((current?.get(name) ?? null) === message)
442
+ return;
443
+ const had = this.isInvalid(key, name);
444
+ if (message) {
445
+ if (current)
446
+ current.set(name, message);
447
+ else
448
+ map.set(key, new Map([[name, message]]));
449
+ }
450
+ else if (current) {
451
+ current.delete(name);
452
+ if (!current.size)
453
+ map.delete(key);
454
+ }
455
+ const has = this.isInvalid(key, name);
456
+ if (has !== had) {
457
+ const delta = has ? 1 : -1;
458
+ this.invalidCells += delta;
459
+ const count = (this.invalidByRow.get(key) ?? 0) + delta;
460
+ if (count > 0)
461
+ this.invalidByRow.set(key, count);
462
+ else
463
+ this.invalidByRow.delete(key);
464
+ }
465
+ }
466
+ isInvalid(key, name) {
467
+ return Boolean(this.ruleErrors.get(key)?.has(name) || this.duplicateErrors.get(key)?.has(name));
468
+ }
469
+ markUniqueDirty() {
470
+ for (const name of this.options.unique ?? [])
471
+ this.dirtyUnique.add(name);
472
+ }
473
+ /**
474
+ * Finds repeated values in one `unique` input. Every row with the value gets a message, so the one just edited says
475
+ * so: the first row names the second, the others name the first.
476
+ */
477
+ checkUnique(name, changed) {
478
+ const params = this.inputByName.get(name);
479
+ const found = new Map();
480
+ const groups = new Map();
481
+ this.entries.forEach((entry, index) => {
482
+ const value = uniqueKeyOf(entry.values[name]);
483
+ if (value === null)
484
+ return;
485
+ const group = groups.get(value);
486
+ if (group)
487
+ group.push(index);
488
+ else
489
+ groups.set(value, [index]);
490
+ });
491
+ for (const group of groups.values()) {
492
+ if (group.length < 2)
493
+ continue;
494
+ for (const index of group) {
495
+ const entry = this.entries[index];
496
+ // A value that breaks its own rules says so first.
497
+ if (this.ruleErrors.get(entry.key)?.has(name))
498
+ continue;
499
+ found.set(entry.key, duplicateMessageFor(params, name, index === group[0] ? group[1] : group[0], this.names.item));
500
+ }
501
+ }
502
+ const keys = new Set([...found.keys()]);
503
+ for (const [key, names] of this.duplicateErrors)
504
+ if (names.has(name))
505
+ keys.add(key);
506
+ for (const key of keys) {
507
+ const before = this.shownError(key, name);
508
+ this.setError(this.duplicateErrors, key, name, found.get(key) ?? null);
509
+ if (this.shownError(key, name) !== before)
510
+ changed.add(key);
511
+ }
512
+ }
513
+ checkEverything() {
514
+ this.ruleErrors.clear();
515
+ this.duplicateErrors.clear();
516
+ this.invalidByRow.clear();
517
+ this.invalidCells = 0;
518
+ for (const entry of this.entries)
519
+ for (const params of this.options.inputs)
520
+ this.checkCell(entry, params);
521
+ const ignored = new Set();
522
+ for (const name of this.options.unique ?? [])
523
+ this.checkUnique(name, ignored);
524
+ this.dirtyUnique.clear();
525
+ this.message = rowsMessage(this.getValue(), this.options, this.options.fallbackItem);
526
+ }
527
+ /** After a change: repeated values, the rows' own message, the rows to render again, and value listeners. */
528
+ settle(changed) {
529
+ for (const name of this.dirtyUnique)
530
+ this.checkUnique(name, changed);
531
+ this.dirtyUnique.clear();
532
+ this.message = rowsMessage(this.getValue(), this.options, this.options.fallbackItem);
533
+ for (const key of changed)
534
+ this.bump(key);
535
+ this.refresh();
536
+ this.emit();
537
+ }
538
+ bump(key) {
539
+ this.versions.set(key, (this.versions.get(key) ?? 0) + 1);
540
+ const listeners = this.rowListeners.get(key);
541
+ if (listeners)
542
+ for (const listener of [...listeners])
543
+ listener();
544
+ }
545
+ touchAllRows() {
546
+ for (const entry of this.entries)
547
+ this.bump(entry.key);
548
+ }
549
+ emit() {
550
+ const rows = this.getValue();
551
+ this.lastValue = rows;
552
+ for (const listener of [...this.valueListeners])
553
+ listener(rows);
554
+ }
555
+ buildSnapshot() {
556
+ return {
557
+ keys: (this.keysCache ??= this.entries.map(entry => entry.key)),
558
+ generation: this.generation,
559
+ message: this.messageShown || this.showAll ? this.message : null,
560
+ showAll: this.showAll,
561
+ invalidRows: this.showAll ? this.invalidByRow.size : 0,
562
+ removed: this.removed ? { index: this.removed.index } : null,
563
+ };
564
+ }
565
+ /** Replaces the snapshot when what it holds changed, and tells structure listeners. */
566
+ refresh(force = false) {
567
+ const next = this.buildSnapshot();
568
+ const current = this.snapshot;
569
+ const sameKeys = current && (current.keys === next.keys || (current.keys.length === next.keys.length && current.keys.every((key, index) => key === next.keys[index])));
570
+ if (!force &&
571
+ current &&
572
+ sameKeys &&
573
+ current.generation === next.generation &&
574
+ current.message === next.message &&
575
+ current.showAll === next.showAll &&
576
+ current.invalidRows === next.invalidRows &&
577
+ current.removed?.index === next.removed?.index) {
578
+ return;
579
+ }
580
+ this.snapshot = sameKeys && current ? { ...next, keys: current.keys } : next;
581
+ for (const listener of [...this.listeners])
582
+ listener();
583
+ }
584
+ }
@@ -0,0 +1,40 @@
1
+ export interface ColumnDragOptions {
2
+ /** The heading pressed. */
3
+ heading: HTMLElement;
4
+ /** The box the columns scroll in, scrolled while the pointer is near its edges. */
5
+ scroller?: HTMLElement | null;
6
+ /** The table, which carries the attribute that changes the cursor while a drag runs. */
7
+ root?: HTMLElement | null;
8
+ /** The move the user made: `key` goes just before `beforeKey`, or to the end when it is null. */
9
+ onDrop: (key: string, beforeKey: string | null) => void;
10
+ }
11
+ /**
12
+ * Drags a column heading to another place among the headings pinned to the same side. A line shows where it will land;
13
+ * Esc puts it back. Returns a function that ends the drag.
14
+ */
15
+ export declare function startColumnDrag(event: PointerEvent, { heading, scroller, root, onDrop }: ColumnDragOptions): () => void;
16
+ export interface ColumnResizeOptions {
17
+ /** The heading whose edge was pressed. */
18
+ heading: HTMLElement;
19
+ /** The table, which carries the attribute that changes the cursor while the drag runs. */
20
+ root?: HTMLElement | null;
21
+ /** Before the first move: the table writes the width of every column, so only this one changes. */
22
+ onStart?: () => void;
23
+ /** On every move, with the new width in pixels. The table writes it straight to the column, with no render. */
24
+ onResize: (width: number) => void;
25
+ /** The width to keep, once the drag ends. Esc sends the width it started at. */
26
+ onCommit: (width: number) => void;
27
+ /** Narrowest the column gets. Default 56. */
28
+ min?: number;
29
+ }
30
+ /** Drags the edge of a heading to change its width. Returns a function that ends the drag. */
31
+ export declare function startColumnResize(event: PointerEvent, { heading, root, onStart, onResize, onCommit, min }: ColumnResizeOptions): () => void;
32
+ /**
33
+ * The width a column needs for its widest value and its heading, at most `max`. Used by "Fit to content" in a heading's
34
+ * menu and by a double-click on its edge.
35
+ *
36
+ * A cell's value is a block that fills its column and cuts long text with an ellipsis, so its own width says nothing
37
+ * about the text inside it. Each one is measured at `max-content` instead, with the cap taken off: every cell is set,
38
+ * then every cell is read, so the browser lays the table out twice, not once per row.
39
+ */
40
+ export declare function fitColumnWidth(table: HTMLElement, key: string, max?: number): number;