nexus-shared 3.2.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/CHANGELOG.md +2359 -0
  2. package/README.md +1911 -1123
  3. package/dist/Client.Index.d.ts +27 -0
  4. package/dist/Client.Index.js +27 -0
  5. package/dist/Components/Chats/ChatComposer.d.ts +7 -1
  6. package/dist/Components/Chats/ChatComposer.js +9 -6
  7. package/dist/Components/Chats/ChatParts.d.ts +15 -2
  8. package/dist/Components/Chats/ChatParts.js +15 -4
  9. package/dist/Components/Chats/ChatPopup.d.ts +46 -0
  10. package/dist/Components/Chats/ChatPopup.js +34 -0
  11. package/dist/Components/Chats/ChatThread.d.ts +9 -1
  12. package/dist/Components/Chats/ChatThread.js +54 -6
  13. package/dist/Components/Dashboards/DashboardBoard.d.ts +62 -0
  14. package/dist/Components/Dashboards/DashboardBoard.js +594 -0
  15. package/dist/Components/Dashboards/DashboardWidgets.d.ts +155 -0
  16. package/dist/Components/Dashboards/DashboardWidgets.js +159 -0
  17. package/dist/Components/Dashboards/WidgetFrame.d.ts +51 -0
  18. package/dist/Components/Dashboards/WidgetFrame.js +45 -0
  19. package/dist/Components/Documents/Button.d.ts +6 -1
  20. package/dist/Components/Documents/Button.js +2 -2
  21. package/dist/Components/Documents/DocumentFrame.d.ts +50 -0
  22. package/dist/Components/Documents/DocumentFrame.js +83 -0
  23. package/dist/Components/Documents/Menu.d.ts +4 -0
  24. package/dist/Components/Documents/Menu.js +40 -24
  25. package/dist/Components/Documents/PrintDocument.d.ts +95 -0
  26. package/dist/Components/Documents/PrintDocument.js +62 -0
  27. package/dist/Components/Documents/TabButtons.d.ts +6 -0
  28. package/dist/Components/Documents/TabButtons.js +5 -4
  29. package/dist/Components/Documents/Tabs.d.ts +59 -0
  30. package/dist/Components/Documents/Tabs.js +70 -0
  31. package/dist/Components/Domains/DomainCard.d.ts +98 -0
  32. package/dist/Components/Domains/DomainCard.js +182 -0
  33. package/dist/Components/Forms/ApiForm.js +24 -3
  34. package/dist/Components/Forms/Crud.d.ts +2 -2
  35. package/dist/Components/Forms/Crud.js +628 -247
  36. package/dist/Components/Forms/CrudActions.d.ts +137 -0
  37. package/dist/Components/Forms/CrudActions.js +214 -0
  38. package/dist/Components/Forms/CrudHistory.d.ts +6 -1
  39. package/dist/Components/Forms/CrudHistory.js +14 -6
  40. package/dist/Components/Forms/CrudNarrowPicker.d.ts +31 -0
  41. package/dist/Components/Forms/CrudNarrowPicker.js +180 -0
  42. package/dist/Components/Forms/CrudParts.d.ts +37 -2
  43. package/dist/Components/Forms/CrudParts.js +123 -7
  44. package/dist/Components/Forms/CrudRowForms.d.ts +26 -0
  45. package/dist/Components/Forms/CrudRowForms.js +174 -0
  46. package/dist/Components/Forms/CrudScopes.d.ts +28 -0
  47. package/dist/Components/Forms/CrudScopes.js +76 -0
  48. package/dist/Components/Forms/CrudTree.d.ts +90 -0
  49. package/dist/Components/Forms/CrudTree.js +363 -0
  50. package/dist/Components/Forms/CrudWorkspace.d.ts +73 -0
  51. package/dist/Components/Forms/CrudWorkspace.js +177 -0
  52. package/dist/Components/Forms/ExcelImport.js +3 -3
  53. package/dist/Components/Forms/Form.d.ts +6 -5
  54. package/dist/Components/Forms/Form.js +134 -21
  55. package/dist/Components/Forms/FormLeaveGuard.d.ts +44 -0
  56. package/dist/Components/Forms/FormLeaveGuard.js +65 -0
  57. package/dist/Components/Forms/FormQuestion.d.ts +6 -0
  58. package/dist/Components/Forms/FormQuestion.js +28 -0
  59. package/dist/Components/Forms/RecordFlows.d.ts +50 -0
  60. package/dist/Components/Forms/RecordFlows.js +282 -0
  61. package/dist/Components/Forms/SubmitForm.d.ts +37 -2
  62. package/dist/Components/Forms/SubmitForm.js +182 -10
  63. package/dist/Components/Inputs/CheckBox.js +1 -1
  64. package/dist/Components/Inputs/ChoiceField.d.ts +21 -1
  65. package/dist/Components/Inputs/ChoiceField.js +28 -2
  66. package/dist/Components/Inputs/ComputedInput.d.ts +51 -0
  67. package/dist/Components/Inputs/ComputedInput.js +179 -0
  68. package/dist/Components/Inputs/Dropdown.d.ts +13 -0
  69. package/dist/Components/Inputs/Dropdown.js +8 -6
  70. package/dist/Components/Inputs/DropdownParts.d.ts +3 -1
  71. package/dist/Components/Inputs/DropdownParts.js +16 -4
  72. package/dist/Components/Inputs/Field.d.ts +7 -1
  73. package/dist/Components/Inputs/Field.js +2 -2
  74. package/dist/Components/Inputs/GroupForm.d.ts +6 -6
  75. package/dist/Components/Inputs/GroupForm.js +12 -12
  76. package/dist/Components/Inputs/InputRenderer.d.ts +13 -6
  77. package/dist/Components/Inputs/InputRenderer.js +37 -32
  78. package/dist/Components/Inputs/MarkdownEditor.d.ts +6 -0
  79. package/dist/Components/Inputs/MarkdownEditor.js +32 -6
  80. package/dist/Components/Inputs/PictureInput.d.ts +41 -0
  81. package/dist/Components/Inputs/PictureInput.js +75 -0
  82. package/dist/Components/Inputs/RadioGroup.d.ts +16 -3
  83. package/dist/Components/Inputs/RadioGroup.js +8 -3
  84. package/dist/Components/Inputs/RowsInput.d.ts +15 -2
  85. package/dist/Components/Inputs/RowsInput.js +96 -8
  86. package/dist/Components/Inputs/TabularForm.d.ts +5 -5
  87. package/dist/Components/Inputs/TabularForm.js +45 -32
  88. package/dist/Components/Inputs/TextArea.d.ts +6 -0
  89. package/dist/Components/Inputs/TextArea.js +11 -2
  90. package/dist/Components/Inputs/TextBoxTypes.d.ts +6 -0
  91. package/dist/Components/Inputs/TextBoxTypes.js +17 -0
  92. package/dist/Components/Inputs/TreeView.d.ts +76 -2
  93. package/dist/Components/Inputs/TreeView.js +227 -16
  94. package/dist/Components/Layouts/MessageBox.d.ts +15 -2
  95. package/dist/Components/Layouts/MessageBox.js +56 -7
  96. package/dist/Components/Layouts/Popup.d.ts +34 -7
  97. package/dist/Components/Layouts/Popup.js +143 -13
  98. package/dist/Components/Layouts/PopupDock.d.ts +3 -1
  99. package/dist/Components/Layouts/PopupDock.js +2 -1
  100. package/dist/Components/Layouts/PopupHost.js +5 -4
  101. package/dist/Components/Messages/MailClient.d.ts +50 -0
  102. package/dist/Components/Messages/MailClient.js +410 -0
  103. package/dist/Components/Messages/MailColumns.d.ts +21 -0
  104. package/dist/Components/Messages/MailColumns.js +92 -0
  105. package/dist/Components/Messages/MailCompose.d.ts +46 -0
  106. package/dist/Components/Messages/MailCompose.js +154 -0
  107. package/dist/Components/Messages/MailReader.d.ts +43 -0
  108. package/dist/Components/Messages/MailReader.js +207 -0
  109. package/dist/Components/Messages/MailThread.d.ts +71 -0
  110. package/dist/Components/Messages/MailThread.js +185 -0
  111. package/dist/Components/Messages/MessageBell.d.ts +41 -0
  112. package/dist/Components/Messages/MessageBell.js +76 -0
  113. package/dist/Components/Viewers/DataTable.js +153 -49
  114. package/dist/Components/Viewers/DataTableColumns.d.ts +2 -2
  115. package/dist/Components/Viewers/DataTableColumns.js +2 -1
  116. package/dist/Components/Viewers/DataTableFilters.d.ts +1 -1
  117. package/dist/Components/Viewers/DataTableFilters.js +17 -2
  118. package/dist/Components/Viewers/DataTableParts.d.ts +23 -5
  119. package/dist/Components/Viewers/DataTableParts.js +26 -14
  120. package/dist/Components/Viewers/OwnerFiles.d.ts +227 -0
  121. package/dist/Components/Viewers/OwnerFiles.js +700 -0
  122. package/dist/Components/Viewers/QrCodeImage.d.ts +22 -0
  123. package/dist/Components/Viewers/QrCodeImage.js +15 -0
  124. package/dist/Components/Viewers/RecordCards.d.ts +16 -0
  125. package/dist/Components/Viewers/RecordCards.js +509 -0
  126. package/dist/Helpers/ApiModules.js +4 -0
  127. package/dist/Helpers/AuditEntries.d.ts +50 -0
  128. package/dist/Helpers/AuditEntries.js +96 -0
  129. package/dist/Helpers/BusyHooks.d.ts +6 -0
  130. package/dist/Helpers/BusyHooks.js +39 -0
  131. package/dist/Helpers/ChatBackend.d.ts +8 -1
  132. package/dist/Helpers/ChatBackend.js +18 -0
  133. package/dist/Helpers/ChatHelpers.d.ts +2 -0
  134. package/dist/Helpers/ChatHelpers.js +4 -1
  135. package/dist/Helpers/CrudBackend.d.ts +20 -2
  136. package/dist/Helpers/CrudBackend.js +46 -2
  137. package/dist/Helpers/CrudColumns.d.ts +91 -6
  138. package/dist/Helpers/CrudColumns.js +148 -7
  139. package/dist/Helpers/CrudDetailsTabs.d.ts +58 -0
  140. package/dist/Helpers/CrudDetailsTabs.js +126 -0
  141. package/dist/Helpers/CrudHelpers.d.ts +116 -2
  142. package/dist/Helpers/CrudHelpers.js +256 -0
  143. package/dist/Helpers/CrudNarrowPick.d.ts +83 -0
  144. package/dist/Helpers/CrudNarrowPick.js +157 -0
  145. package/dist/Helpers/CrudPickerPlan.d.ts +58 -0
  146. package/dist/Helpers/CrudPickerPlan.js +46 -0
  147. package/dist/Helpers/CrudRowForms.d.ts +32 -0
  148. package/dist/Helpers/CrudRowForms.js +76 -0
  149. package/dist/Helpers/CrudScopes.d.ts +32 -0
  150. package/dist/Helpers/CrudScopes.js +79 -0
  151. package/dist/Helpers/DashboardHelpers.d.ts +115 -0
  152. package/dist/Helpers/DashboardHelpers.js +305 -0
  153. package/dist/Helpers/DomainCards.d.ts +140 -0
  154. package/dist/Helpers/DomainCards.js +370 -0
  155. package/dist/Helpers/DropdownHelpers.d.ts +3 -1
  156. package/dist/Helpers/DropdownHelpers.js +6 -2
  157. package/dist/Helpers/ExcelHelpers.d.ts +1 -1
  158. package/dist/Helpers/ExcelHelpers.js +12 -11
  159. package/dist/Helpers/FileAdapter.d.ts +158 -0
  160. package/dist/Helpers/FileAdapter.js +173 -0
  161. package/dist/Helpers/FilePrimaries.d.ts +48 -0
  162. package/dist/Helpers/FilePrimaries.js +138 -0
  163. package/dist/Helpers/FormConfirm.d.ts +32 -0
  164. package/dist/Helpers/FormConfirm.js +40 -0
  165. package/dist/Helpers/FormStore.d.ts +100 -5
  166. package/dist/Helpers/FormStore.js +368 -25
  167. package/dist/Helpers/InputConditions.d.ts +56 -0
  168. package/dist/Helpers/InputConditions.js +192 -0
  169. package/dist/Helpers/InputMemory.d.ts +106 -0
  170. package/dist/Helpers/InputMemory.js +395 -0
  171. package/dist/Helpers/InputNarrowing.d.ts +50 -0
  172. package/dist/Helpers/InputNarrowing.js +148 -0
  173. package/dist/Helpers/InputParamsHelpers.d.ts +44 -1
  174. package/dist/Helpers/InputParamsHelpers.js +157 -74
  175. package/dist/Helpers/MailClientHelpers.d.ts +96 -0
  176. package/dist/Helpers/MailClientHelpers.js +211 -0
  177. package/dist/Helpers/MessageBackend.d.ts +152 -0
  178. package/dist/Helpers/MessageBackend.js +329 -0
  179. package/dist/Helpers/MessageHelpers.d.ts +160 -0
  180. package/dist/Helpers/MessageHelpers.js +573 -0
  181. package/dist/Helpers/MessageUnread.d.ts +95 -0
  182. package/dist/Helpers/MessageUnread.js +245 -0
  183. package/dist/Helpers/NexusDriveAdapter.d.ts +71 -0
  184. package/dist/Helpers/NexusDriveAdapter.js +236 -0
  185. package/dist/Helpers/NumberHelpers.d.ts +156 -0
  186. package/dist/Helpers/NumberHelpers.js +242 -0
  187. package/dist/Helpers/PagedPicker.d.ts +42 -0
  188. package/dist/Helpers/PagedPicker.js +85 -0
  189. package/dist/Helpers/Permissions.js +2 -1
  190. package/dist/Helpers/QrCode.d.ts +26 -0
  191. package/dist/Helpers/QrCode.js +463 -0
  192. package/dist/Helpers/RecordCards.d.ts +205 -0
  193. package/dist/Helpers/RecordCards.js +378 -0
  194. package/dist/Helpers/RequestCache.d.ts +14 -2
  195. package/dist/Helpers/RequestCache.js +22 -3
  196. package/dist/Helpers/RowsStore.d.ts +61 -2
  197. package/dist/Helpers/RowsStore.js +203 -16
  198. package/dist/Helpers/ServerPreview.d.ts +69 -0
  199. package/dist/Helpers/ServerPreview.js +99 -0
  200. package/dist/Helpers/TableColumns.js +4 -3
  201. package/dist/Helpers/TableExport.d.ts +7 -1
  202. package/dist/Helpers/TableExport.js +9 -3
  203. package/dist/Helpers/TableFilters.d.ts +1 -1
  204. package/dist/Helpers/TableFilters.js +24 -23
  205. package/dist/Helpers/TableHelpers.d.ts +38 -4
  206. package/dist/Helpers/TableHelpers.js +78 -10
  207. package/dist/Helpers/TableJoins.d.ts +13 -0
  208. package/dist/Helpers/TableJoins.js +34 -0
  209. package/dist/Helpers/TableStore.d.ts +28 -4
  210. package/dist/Helpers/TableStore.js +73 -11
  211. package/dist/Helpers/ToolbarFit.d.ts +23 -0
  212. package/dist/Helpers/ToolbarFit.js +30 -0
  213. package/dist/Helpers/TreeStore.d.ts +156 -6
  214. package/dist/Helpers/TreeStore.js +547 -18
  215. package/dist/Interfaces/ApiInterfaces.d.ts +10 -1
  216. package/dist/Interfaces/ApiInterfaces.js +10 -1
  217. package/dist/Interfaces/ChatInterfaces.d.ts +22 -0
  218. package/dist/Interfaces/CrudInterfaces.d.ts +539 -6
  219. package/dist/Interfaces/DashboardInterfaces.d.ts +123 -0
  220. package/dist/Interfaces/DashboardInterfaces.js +1 -0
  221. package/dist/Interfaces/DomainInterfaces.d.ts +145 -0
  222. package/dist/Interfaces/DomainInterfaces.js +1 -0
  223. package/dist/Interfaces/FileInterfaces.d.ts +183 -0
  224. package/dist/Interfaces/FileInterfaces.js +6 -0
  225. package/dist/Interfaces/FormInterfaces.d.ts +460 -32
  226. package/dist/Interfaces/FormInterfaces.js +79 -1
  227. package/dist/Interfaces/InputInterfaces.d.ts +22 -3
  228. package/dist/Interfaces/MailInterfaces.d.ts +192 -0
  229. package/dist/Interfaces/MailInterfaces.js +1 -0
  230. package/dist/Interfaces/RecordInterfaces.d.ts +367 -0
  231. package/dist/Interfaces/RecordInterfaces.js +1 -0
  232. package/dist/Interfaces/TableInterfaces.d.ts +102 -3
  233. package/dist/Services/ExcelApi.js +5 -0
  234. package/dist/Services/FileApi.d.ts +13 -0
  235. package/dist/Services/FileApi.js +29 -0
  236. package/dist/Services/ImageEditorLoader.d.ts +14 -0
  237. package/dist/Services/ImageEditorLoader.js +51 -0
  238. package/dist/Services/JoinSources.d.ts +79 -6
  239. package/dist/Services/JoinSources.js +132 -21
  240. package/dist/Services/MessageApi.d.ts +18 -0
  241. package/dist/Services/MessageApi.js +64 -0
  242. package/dist/Services/ModuleImport.d.ts +7 -0
  243. package/dist/Services/ModuleImport.js +22 -0
  244. package/dist/Shared.Index.d.ts +28 -0
  245. package/dist/Shared.Index.js +28 -0
  246. package/package.json +13 -2
  247. package/src/Styles/Nexus.Avatar.css +6 -5
  248. package/src/Styles/Nexus.Button.css +39 -4
  249. package/src/Styles/Nexus.Chat.css +114 -22
  250. package/src/Styles/Nexus.Choice.css +16 -0
  251. package/src/Styles/Nexus.Crud.css +486 -21
  252. package/src/Styles/Nexus.Dashboard.css +763 -0
  253. package/src/Styles/Nexus.Domain.css +384 -0
  254. package/src/Styles/Nexus.Dropdown.css +8 -0
  255. package/src/Styles/Nexus.Editor.css +20 -0
  256. package/src/Styles/Nexus.Files.css +437 -0
  257. package/src/Styles/Nexus.Form.css +5 -2
  258. package/src/Styles/Nexus.Index.css +7 -0
  259. package/src/Styles/Nexus.Input.css +73 -0
  260. package/src/Styles/Nexus.Mail.css +1062 -0
  261. package/src/Styles/Nexus.Menu.css +10 -1
  262. package/src/Styles/Nexus.Popup.css +135 -1
  263. package/src/Styles/Nexus.Print.css +288 -0
  264. package/src/Styles/Nexus.QrCode.css +12 -0
  265. package/src/Styles/Nexus.Record.css +430 -0
  266. package/src/Styles/Nexus.Tab.Buttons.css +87 -22
  267. package/src/Styles/Nexus.Table.css +1458 -1387
  268. package/src/Styles/Nexus.Theme.Picker.css +1 -0
  269. package/src/Styles/Nexus.Themes.css +12 -6
  270. package/src/Styles/Nexus.Tokens.css +8 -3
  271. package/src/Styles/Nexus.Tree.css +41 -1
@@ -1,4 +1,5 @@
1
1
  import { DEFAULT_DATE_PREFERENCES } from "../Interfaces/DateInterfaces.js";
2
+ import { EInputTypes } from "../Interfaces/FormInterfaces.js";
2
3
  import { formatCode, loadCode, validateCode } from "./CodeHelpers.js";
3
4
  import { formatDate, formatDateRange, toDateRange, toIsoDate, usesDevanagari } from "./DateHelpers.js";
4
5
  import { formatDateTime, toUtcDateTime } from "./DateTimeHelpers.js";
@@ -15,9 +16,27 @@ import { chooseRowMessage, duplicateRowMessage, maxRowsMessage, minRowsMessage,
15
16
  /** Keys that place an input in a form; a renderer leaves them out of the component's props. */
16
17
  export const INPUT_LAYOUT_KEYS = ["span", "width", "grow", "hidden"];
17
18
  /** Inputs whose value is `true` or `false`. */
18
- export const isBooleanInput = (params) => params.type === "checkbox" || params.type === "switch" || params.type === "radio";
19
+ export const isBooleanInput = (params) => params.type === EInputTypes.CheckBox || params.type === EInputTypes.Switch || params.type === EInputTypes.Radio;
20
+ /** Each `EInputTypes` number with its member name. */
21
+ const INPUT_TYPE_NAMES = new Map(Object.entries(EInputTypes).map(([name, value]) => [value, name]));
22
+ /**
23
+ * The member name of an input type, for a message or a log: `inputTypeName(EInputTypes.RichText)` is "RichText". A number
24
+ * that is no member, such as one read from old data, comes back as its digits.
25
+ */
26
+ export const inputTypeName = (type) => INPUT_TYPE_NAMES.get(type) ?? String(type);
27
+ /**
28
+ * Whether an input holds a value of the form. A picture does not: it is the owner's file, changed at once through the
29
+ * file backend, so the form keeps no field for it and a save sends nothing under its name.
30
+ */
31
+ export const holdsValue = (params) => params.type !== EInputTypes.Picture;
32
+ /**
33
+ * Whether what is typed in an input is worth asking about before it is dropped: not a password or a one-time code (a
34
+ * `Code` box), which are typed again in a moment and never kept in a draft. Such a value alone makes no form ask
35
+ * "Discard your changes?" on Cancel or close, nor the page ask before it is left.
36
+ */
37
+ export const keepsTyping = (params) => params.type !== EInputTypes.Password && params.type !== EInputTypes.Code;
19
38
  /** Inputs whose value is an array of rows. */
20
- export const isRowsInput = (params) => params.type === "tabular" || params.type === "group";
39
+ export const isRowsInput = (params) => params.type === EInputTypes.Tabular || params.type === EInputTypes.Group;
21
40
  /**
22
41
  * The name an input's messages use, as the input itself picks it: `fieldName`, or the label when it is text. A checkbox's
23
42
  * label is usually a sentence, so it uses `fieldName` only.
@@ -26,35 +45,36 @@ export function inputFieldName(params) {
26
45
  const { fieldName } = params;
27
46
  if (fieldName)
28
47
  return fieldName;
29
- if (params.type === "checkbox" || params.type === "switch")
48
+ if (params.type === EInputTypes.CheckBox || params.type === EInputTypes.Switch)
30
49
  return undefined;
31
50
  return typeof params.label === "string" ? params.label : undefined;
32
51
  }
33
52
  /** The value of an input with nothing in it: "" for text, `null` for a number or a date, `false`, `[]`, and so on. */
34
53
  export function emptyInputValue(params) {
35
54
  switch (params.type) {
36
- case "number":
37
- case "date":
38
- case "time":
39
- case "date-time":
40
- case "radio-group":
55
+ case EInputTypes.Number:
56
+ case EInputTypes.Date:
57
+ case EInputTypes.Time:
58
+ case EInputTypes.DateTime:
59
+ case EInputTypes.RadioGroup:
60
+ case EInputTypes.Picture:
41
61
  return null;
42
- case "checkbox":
43
- case "switch":
44
- case "radio":
62
+ case EInputTypes.CheckBox:
63
+ case EInputTypes.Switch:
64
+ case EInputTypes.Radio:
45
65
  return false;
46
- case "checkbox-group":
47
- case "tree":
48
- case "tabular":
49
- case "group":
66
+ case EInputTypes.CheckBoxGroup:
67
+ case EInputTypes.Tree:
68
+ case EInputTypes.Tabular:
69
+ case EInputTypes.Group:
50
70
  return [];
51
- case "dropdown":
71
+ case EInputTypes.Dropdown:
52
72
  return params.multiple ? [] : null;
53
- case "date-range":
73
+ case EInputTypes.DateRange:
54
74
  return [null, null];
55
- case "slider":
75
+ case EInputTypes.Slider:
56
76
  return params.min ?? 0;
57
- case "range-slider":
77
+ case EInputTypes.RangeSlider:
58
78
  return [params.min ?? 0, params.max ?? 100];
59
79
  default:
60
80
  return "";
@@ -77,39 +97,39 @@ export function normalizeInputValue(params, value) {
77
97
  return emptyInputValue(params);
78
98
  }
79
99
  switch (params.type) {
80
- case "number": {
100
+ case EInputTypes.Number: {
81
101
  if (typeof value === "number")
82
102
  return Number.isNaN(value) ? null : value;
83
103
  return typeof value === "string" ? parseNumber(value, params.locale) : null;
84
104
  }
85
- case "checkbox":
86
- case "switch":
87
- case "radio":
105
+ case EInputTypes.CheckBox:
106
+ case EInputTypes.Switch:
107
+ case EInputTypes.Radio:
88
108
  return value === true || value === "true" || value === 1;
89
- case "date":
109
+ case EInputTypes.Date:
90
110
  return toIsoDate(value) ?? (typeof value === "string" ? value : null);
91
- case "date-range":
111
+ case EInputTypes.DateRange:
92
112
  return toDateRange(Array.isArray(value) ? value : null);
93
- case "time":
113
+ case EInputTypes.Time:
94
114
  return toIsoTime(value, normalizeTimeStep(params.step) % 60 !== 0) ?? (typeof value === "string" ? value : null);
95
- case "date-time":
115
+ case EInputTypes.DateTime:
96
116
  return toUtcDateTime(value) ?? (typeof value === "string" ? value : null);
97
- case "checkbox-group":
98
- case "tree":
99
- case "tabular":
100
- case "group":
117
+ case EInputTypes.CheckBoxGroup:
118
+ case EInputTypes.Tree:
119
+ case EInputTypes.Tabular:
120
+ case EInputTypes.Group:
101
121
  return Array.isArray(value) ? value : [];
102
- case "dropdown":
122
+ case EInputTypes.Dropdown:
103
123
  if (params.multiple)
104
124
  return Array.isArray(value) ? value : [value];
105
125
  return Array.isArray(value) ? (value[0] ?? null) : value;
106
- case "slider":
126
+ case EInputTypes.Slider:
107
127
  return typeof value === "number" ? value : (parseNumber(String(value)) ?? params.min ?? 0);
108
- case "range-slider":
128
+ case EInputTypes.RangeSlider:
109
129
  return Array.isArray(value) && value.length === 2 ? value : emptyInputValue(params);
110
- case "radio-group":
130
+ case EInputTypes.RadioGroup:
111
131
  return value;
112
- case "code":
132
+ case EInputTypes.Code:
113
133
  // Only separators and case: a saved code that does not fit stays as it is, so its check refuses the save.
114
134
  return loadCode(value, params);
115
135
  default:
@@ -134,6 +154,67 @@ export function inputValuesEqual(a, b) {
134
154
  return false;
135
155
  return a.every((item, index) => Object.is(item, b[index]));
136
156
  }
157
+ /**
158
+ * The inputs with some of them fixed for one opening of a form: each input `locked` names (input name -> why) turns
159
+ * read-only and says why on any try to change it (`readOnlyMessage`), the lock-and-note look every input has. Its value
160
+ * stays in the form and is sent as before. A required input with no value in `values` stays free: fixed empty, the form
161
+ * would never ask for it, since read-only inputs are not checked. Names a form does not have are skipped.
162
+ *
163
+ * Each fixed input is a copy made from its own property descriptors, never a spread: a spread would read a getter once
164
+ * (`get readOnly()` or `get options()` following other values through `dependsOn`) and freeze it.
165
+ *
166
+ * To show fixed inputs disabled instead (dimmed, out of the Tab order, no note), write `disabled` for `readOnly` in the
167
+ * copy below.
168
+ *
169
+ * `known` are records the caller holds for the fixed values - the row picked in another list that a new record is added
170
+ * for: a fixed dropdown whose value one of them holds (by the dropdown's `valueKey`) gets it as a selected option, so its
171
+ * box names the value at once, with no lookup.
172
+ *
173
+ * @example lockInputs(REGION_INPUTS, { countryId: "Added inside Bagmati." }, { countryId: "c7" })
174
+ */
175
+ export function lockInputs(inputs, locked, values = {}, known = []) {
176
+ return inputs.map(params => {
177
+ const reason = locked[params.name];
178
+ if (reason === undefined)
179
+ return params;
180
+ const required = params.required === true;
181
+ if (required && isEmptyInputValue(values[params.name]))
182
+ return params;
183
+ const fixed = {
184
+ ...Object.getOwnPropertyDescriptors(params),
185
+ readOnly: { value: true, enumerable: true, writable: true, configurable: true },
186
+ readOnlyMessage: { value: reason, enumerable: true, writable: true, configurable: true },
187
+ };
188
+ const value = values[params.name];
189
+ if (params.type === EInputTypes.Dropdown && !params.multiple && !isEmptyInputValue(value)) {
190
+ const valueKey = params.valueKey ?? "value";
191
+ const record = known.find(item => String(item[valueKey]) === String(value));
192
+ if (record)
193
+ fixed.selectedOptions = { value: [...(params.selectedOptions ?? []), record], enumerable: true, writable: true, configurable: true };
194
+ }
195
+ return Object.create(Object.getPrototypeOf(params), fixed);
196
+ });
197
+ }
198
+ /**
199
+ * A form's `onFieldChange` that never writes the inputs fixed for an opening (`lockInputs`): a rule such as "a province
200
+ * sits right under its country" still changes a free field, and leaves a fixed one as the form opened it. With nothing
201
+ * fixed, or no rule, the rule as it is.
202
+ *
203
+ * @example lockedFieldChange(form.onFieldChange, ["countryId", "parentId"])
204
+ */
205
+ export function lockedFieldChange(onFieldChange, fixed) {
206
+ if (!onFieldChange || fixed.length === 0)
207
+ return onFieldChange;
208
+ return (name, value, values) => {
209
+ const patch = onFieldChange(name, value, values);
210
+ if (!patch)
211
+ return patch;
212
+ const kept = { ...patch };
213
+ for (const field of fixed)
214
+ delete kept[field];
215
+ return kept;
216
+ };
217
+ }
137
218
  function dateSettings(params, preferences) {
138
219
  return {
139
220
  calendar: params.calendar ?? preferences.calendar,
@@ -164,35 +245,35 @@ export function validateInput(params, value, context = {}) {
164
245
  const name = inputFieldName(params);
165
246
  const preferences = context.preferences ?? DEFAULT_DATE_PREFERENCES;
166
247
  switch (params.type) {
167
- case "number":
248
+ case EInputTypes.Number:
168
249
  return validateNumber(value, params, name, limit => formatNumber(limit, { locale: params.locale, grouping: true }));
169
- case "checkbox":
170
- case "switch":
250
+ case EInputTypes.CheckBox:
251
+ case EInputTypes.Switch:
171
252
  return validateCheck(value === true, params, params.fieldName);
172
- case "radio":
253
+ case EInputTypes.Radio:
173
254
  // One row at a time can have it on; whether one must is the rows' rule.
174
255
  return null;
175
- case "checkbox-group":
256
+ case EInputTypes.CheckBoxGroup:
176
257
  return validateSelection(asArray(value), params, name);
177
- case "radio-group": {
258
+ case EInputTypes.RadioGroup: {
178
259
  const { validate } = params;
179
260
  return validateSelection(asArray(value), { required: params.required, validate: validate && (values => validate(values[0] ?? null)) }, name);
180
261
  }
181
- case "dropdown":
262
+ case EInputTypes.Dropdown:
182
263
  return validateSelection(asArray(value), { required: params.required, min: params.min, max: params.max, validate: params.validate }, name);
183
- case "date": {
264
+ case EInputTypes.Date: {
184
265
  const settings = dateSettings(params, preferences);
185
266
  return validateDate(value, params, name, iso => formatDate(iso, settings));
186
267
  }
187
- case "date-range": {
268
+ case EInputTypes.DateRange: {
188
269
  const settings = dateSettings(params, preferences);
189
270
  return validateDateRange(value, params, name, iso => formatDate(iso, settings));
190
271
  }
191
- case "time": {
272
+ case EInputTypes.Time: {
192
273
  const settings = timeSettings(params, preferences);
193
274
  return validateTime(value, params, name, iso => formatTime(iso, settings));
194
275
  }
195
- case "date-time": {
276
+ case EInputTypes.DateTime: {
196
277
  const seconds = normalizeTimeStep(params.step) % 60 !== 0;
197
278
  return validateDateTime(value, params, name, {
198
279
  ...dateSettings(params, preferences),
@@ -201,23 +282,24 @@ export function validateInput(params, value, context = {}) {
201
282
  seconds,
202
283
  });
203
284
  }
204
- case "code":
285
+ case EInputTypes.Code:
205
286
  return validateCode(value, params, name);
206
- case "slider":
287
+ case EInputTypes.Slider:
207
288
  return params.validate?.(value) || null;
208
- case "range-slider":
289
+ case EInputTypes.RangeSlider:
209
290
  return params.validate?.(value) || null;
210
- case "tree":
291
+ case EInputTypes.Tree:
292
+ case EInputTypes.Picture:
211
293
  return null;
212
- case "rich-text":
294
+ case EInputTypes.RichText:
213
295
  return validateRichText(value, params, name);
214
- case "tabular":
215
- case "group": {
296
+ case EInputTypes.Tabular:
297
+ case EInputTypes.Group: {
216
298
  const result = validateRows(asArray(value), params, context);
217
299
  if (result.message)
218
300
  return result.message;
219
301
  const first = result.cells[0];
220
- return first ? `${rowsItemNames(params, params.type === "tabular" ? "Row" : "Item").title} ${first.index + 1}: ${first.message}` : null;
302
+ return first ? `${rowsItemNames(params, params.type === EInputTypes.Tabular ? "Row" : "Item").title} ${first.index + 1}: ${first.message}` : null;
221
303
  }
222
304
  default:
223
305
  return validateText(value, params, name);
@@ -263,7 +345,7 @@ export function rowsMessage(rows, rules, fallback = "Row") {
263
345
  if (rules.max !== undefined && count > rules.max)
264
346
  return maxRowsMessage(rules.max, names.item, names.items);
265
347
  for (const params of rules.inputs) {
266
- if (params.type !== "radio" || !params.required || params.hidden)
348
+ if (params.type !== EInputTypes.Radio || !params.required || params.hidden)
267
349
  continue;
268
350
  if (!rows.some(row => row[params.name] === true))
269
351
  return chooseRowMessage(radioName(params), names.item);
@@ -348,47 +430,48 @@ export function formatInputValue(params, value, context = {}) {
348
430
  return "";
349
431
  const preferences = context.preferences ?? DEFAULT_DATE_PREFERENCES;
350
432
  switch (params.type) {
351
- case "number": {
433
+ case EInputTypes.Number: {
352
434
  const text = formatNumber(value, { ...params, grouping: params.grouping ?? true });
353
435
  const prefix = typeof params.prefix === "string" ? params.prefix + " " : "";
354
436
  const suffix = typeof params.suffix === "string" ? " " + params.suffix : "";
355
437
  return prefix + text + suffix;
356
438
  }
357
- case "checkbox":
358
- case "switch":
359
- case "radio":
439
+ case EInputTypes.CheckBox:
440
+ case EInputTypes.Switch:
441
+ case EInputTypes.Radio:
360
442
  return typeof params.label === "string" ? params.label : "";
361
- case "dropdown":
443
+ case EInputTypes.Dropdown:
362
444
  return asArray(value)
363
445
  .map(item => optionLabel(params.options, item, params.valueKey, params.labelKey))
364
446
  .join(", ");
365
- case "checkbox-group":
366
- case "radio-group":
447
+ case EInputTypes.CheckBoxGroup:
448
+ case EInputTypes.RadioGroup:
367
449
  return asArray(value)
368
450
  .map(item => optionLabel(params.options, item))
369
451
  .join(", ");
370
- case "date":
452
+ case EInputTypes.Date:
371
453
  return formatDate(value, dateSettings(params, preferences));
372
- case "date-range":
454
+ case EInputTypes.DateRange:
373
455
  return formatDateRange(value, dateSettings(params, preferences));
374
- case "time":
456
+ case EInputTypes.Time:
375
457
  return formatTime(value, timeSettings(params, preferences));
376
- case "date-time":
458
+ case EInputTypes.DateTime:
377
459
  return formatDateTime(value, { ...dateSettings(params, preferences), timeFormat: params.timeFormat ?? preferences.timeFormat, timeZone: params.timeZone });
378
- case "code":
460
+ case EInputTypes.Code:
379
461
  return formatCode(value, params);
380
- case "slider":
462
+ case EInputTypes.Slider:
381
463
  return formatNumber(value, params);
382
- case "range-slider":
464
+ case EInputTypes.RangeSlider:
383
465
  return asArray(value)
384
466
  .map(item => formatNumber(item, params))
385
467
  .join(" – ");
386
- case "tree":
387
- case "tabular":
388
- case "group":
468
+ case EInputTypes.Tree:
469
+ case EInputTypes.Tabular:
470
+ case EInputTypes.Group:
471
+ case EInputTypes.Picture:
389
472
  return "";
390
473
  default: {
391
- const text = params.type === "rich-text" ? richTextToPlainText(String(value)) : String(value).trim();
474
+ const text = params.type === EInputTypes.RichText ? richTextToPlainText(String(value)) : String(value).trim();
392
475
  const line = text.split("\n", 1)[0] ?? "";
393
476
  return line.length > 60 ? line.slice(0, 57) + "…" : line;
394
477
  }
@@ -0,0 +1,96 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ChatDomainValue, ChatId, ChatMessage, ChatSource } from "../Interfaces/ChatInterfaces.ts";
3
+ import type { MailFolders, MailInboxRow, MailKindCount, MailList, MailThreadKind, MessageSource } from "../Interfaces/MailInterfaces.ts";
4
+ import { type ChatTimeOptions } from "./ChatHelpers.ts";
5
+ /** Which conversations a mail client lists: one of its folders, narrowed to one type or not. */
6
+ export interface MailClientView {
7
+ list: MailList;
8
+ /** The type picked under the folders: `mail`, `chat`, or `domain:<domainType>`; `null` for every type. */
9
+ type: string | null;
10
+ }
11
+ /** One type of a mailbox as its rail lists it: Mail, Chat, then one per record kind. */
12
+ export interface MailTypeEntry {
13
+ /** `mail`, `chat`, or `domain:<domainType>`: the view's `type`. */
14
+ key: string;
15
+ threadKind: MailThreadKind;
16
+ domainType: ChatDomainValue | null;
17
+ label: string;
18
+ /** The kind's own icon, from its `defineChatDomains` row. */
19
+ icon?: ReactNode;
20
+ unread: number;
21
+ total: number;
22
+ }
23
+ /** How the list and the reading pane share a wide mail client: side by side, or one over the other. */
24
+ export type MailSplit = "columns" | "rows";
25
+ /** Which pane a mail client too narrow for two shows. */
26
+ export type MailPane = "rail" | "list" | "reader";
27
+ /** The key of a type: `mail`, `chat`, or `domain:<domainType>`. */
28
+ export declare function mailTypeKey(threadKind: MailThreadKind, domainType?: ChatDomainValue | null): string;
29
+ /** The kind and record kind a type's key names, for a list request. */
30
+ export declare function mailTypeOf(key: string | null | undefined): {
31
+ threadKind: MailThreadKind;
32
+ domainType?: string;
33
+ } | null;
34
+ /** The kind of a row: what the backend said, else mail or the record's kind. */
35
+ export declare function mailRowKind(row: Pick<MailInboxRow, "threadKind" | "domainType">): MailThreadKind;
36
+ /** The type key of a row, for its chip. */
37
+ export declare function mailRowTypeKey(row: Pick<MailInboxRow, "threadKind" | "domainType">): string;
38
+ /** A type's name: Mail, Chat, or the record kind's name as the app gave it (`defineChatDomains`). */
39
+ export declare function mailTypeLabel(threadKind: MailThreadKind, domainType?: ChatDomainValue | null): string;
40
+ /**
41
+ * The types a mailbox lists from its counts: Mail and Chat first, then each record kind in the order the app named
42
+ * them, then any it did not name. A type with nothing in it is left out; two counts of one type are added.
43
+ */
44
+ export declare function mailTypes(kinds: readonly MailKindCount[]): MailTypeEntry[];
45
+ /** A folder's unread count from the counts, 0 when unknown. */
46
+ export declare function mailFolderUnread(folders: MailFolders | null | undefined, list: MailList): number;
47
+ /** The time a row shows: today's as a time ("10:42 AM"), yesterday's as "Yesterday", older as a date. */
48
+ export declare function mailListTime(utc: string | null | undefined, options?: ChatTimeOptions, today?: string): string;
49
+ /**
50
+ * A thread's discussion as a chat of its own: its chat lines alone (the mails and notifications are read as mail), and
51
+ * whatever is written in it a chat line. The read mark is the thread's, so reading the discussion reads the thread up to
52
+ * its newest chat line.
53
+ */
54
+ export declare function mailDiscussionSource(thread: ChatSource): ChatSource;
55
+ /**
56
+ * The read marks a mail client has on their way. A conversation shows read as soon as it opens, before its read mark
57
+ * reaches the server, so counts read at that moment would still hold it unread: the client reads its counts once
58
+ * `pending` is empty, and reads them again when a mark is answered (`onSettled`).
59
+ */
60
+ export interface MailReadMarks {
61
+ /** The marks on their way, each settling (never failing) when the server answered or did not. */
62
+ readonly pending: Set<Promise<void>>;
63
+ /** After each mark, answered or failed. */
64
+ onSettled: () => void;
65
+ }
66
+ /** A conversation's lines whose read marks are followed in `marks`. A source without read marks is given back as it is. */
67
+ export declare function followMailReadMarks(lines: ChatSource, marks: MailReadMarks): ChatSource;
68
+ /** A mailbox whose conversations' read marks are followed in `marks` (`followMailReadMarks`), for a mail client's reader and chat windows. */
69
+ export declare function followMailboxReadMarks(source: MessageSource, marks: MailReadMarks): MessageSource;
70
+ /** How many of a thread's lines are its discussion: its chat lines. */
71
+ export declare function mailDiscussionCount(messages: readonly ChatMessage[]): number;
72
+ /** The split a viewer chose for a wide mail client, `columns` until they choose. */
73
+ export declare function readMailSplit(viewer: ChatId): MailSplit;
74
+ /** Keeps a viewer's split for the next visit. */
75
+ export declare function writeMailSplit(viewer: ChatId, split: MailSplit): void;
76
+ /** What a compose window holds while it is not sent: the people by id, the subject and the Markdown body. */
77
+ export interface MailComposeDraft {
78
+ to: ChatId[];
79
+ cc: ChatId[];
80
+ bcc: ChatId[];
81
+ subject: string;
82
+ body: string;
83
+ /** When it was last kept (milliseconds since 1970). */
84
+ savedAt: number;
85
+ }
86
+ /** An empty draft. */
87
+ export declare function emptyMailDraft(): MailComposeDraft;
88
+ /** Whether a draft holds anything worth asking about: words in the subject or the body. People alone are not. */
89
+ export declare function mailDraftHasText(draft: Pick<MailComposeDraft, "subject" | "body"> | null | undefined): boolean;
90
+ /**
91
+ * A viewer's unsent draft of one compose window (`new`, or `reply:<thread>`), or `null`. A draft kept by an older
92
+ * version, or broken by hand, reads as none.
93
+ */
94
+ export declare function readMailDraft(viewer: ChatId, key: string): MailComposeDraft | null;
95
+ /** Keeps a viewer's draft of one compose window, or forgets it (`null`, or a draft with nothing in it). */
96
+ export declare function writeMailDraft(viewer: ChatId, key: string, draft: MailComposeDraft | null): void;