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
@@ -52,7 +52,7 @@ function TableCellContent({ column, row, options }) {
52
52
  // A moment needs the browser's zone, unknown while the page hydrates: nothing shows until then, not another zone's time.
53
53
  if (column.format === "date-time" && !options.timeZone && value)
54
54
  return _jsx("span", { className: "nx-table__pending" });
55
- const text = formatCellText(column, value, options);
55
+ const text = formatCellText(column, value, options, row);
56
56
  if (!text)
57
57
  return _jsx("span", { className: "nx-table__dash", children: "\u2014" });
58
58
  return _jsx(_Fragment, { children: text });
@@ -96,8 +96,11 @@ export function TableColumnGroup({ columns, widths, frame, colRefs, }) {
96
96
  const own = (key, on) => on && _jsx("col", { ref: hold(key), style: { width: widths[key] } }, key);
97
97
  return (_jsxs("colgroup", { children: [own(TABLE_INDEX_COLUMN, Boolean(frame.index)), columns.map(view => (_jsx("col", { ref: hold(view.column.key), style: { width: widths[view.column.key] ?? cssLength(view.column.width) ?? FALLBACK_COLUMN_WIDTH } }, view.column.key))), _jsx("col", { className: "nx-table__col-filler" }), own(TABLE_ACTIONS_COLUMN, Boolean(frame.actions))] }));
98
98
  }
99
- /** The number of a row, its checkbox, or both: the number until the pointer is over the row or something is selected. */
100
- export function TableIndexCell({ heading, rowSpan, own, number, numbered, selectable, checkbox, }) {
99
+ /**
100
+ * The number of a row, its checkbox, or both: the number until the pointer is over the row or something is selected. A
101
+ * row of a table whose rows are picked (`onPick`) also holds its radio button here, out of sight.
102
+ */
103
+ export function TableIndexCell({ heading, rowSpan, own, number, numbered, selectable, checkbox, pick, }) {
101
104
  const shared = {
102
105
  className: cx(heading ? "nx-table__heading" : "nx-table__cell", "nx-table__cell--index"),
103
106
  "data-col": TABLE_INDEX_COLUMN,
@@ -107,7 +110,7 @@ export function TableIndexCell({ heading, rowSpan, own, number, numbered, select
107
110
  "data-swap": (numbered && selectable) || undefined,
108
111
  style: ownStyle(own, "left"),
109
112
  };
110
- const content = (_jsxs(_Fragment, { children: [numbered && (_jsx("span", { className: "nx-table__number", "aria-hidden": selectable || undefined, children: number })), checkbox] }));
113
+ const content = (_jsxs(_Fragment, { children: [numbered && (_jsx("span", { className: "nx-table__number", "aria-hidden": selectable || undefined, children: number })), checkbox, pick] }));
111
114
  return heading ? (_jsx("th", { scope: "col", rowSpan: rowSpan, ...shared, children: content })) : (_jsx("td", { ...shared, children: content }));
112
115
  }
113
116
  /** What in a row is a control of its own: a click there does what the control does, and never opens the row. */
@@ -126,13 +129,16 @@ function opensRow(event, single) {
126
129
  return !(selection && !selection.isCollapsed && event.currentTarget.contains(selection.anchorNode));
127
130
  }
128
131
  /** One row. It renders again only when its row, its state, or the columns change. */
129
- function TableRowViewInner({ row, rowKey, number, label, columns, cellOptions, selectable, selected, selectionFull, busy, flashed, numbered, marks, frame, sized, actions, onToggle, onAction, onOpen, openOnClick, }) {
132
+ function TableRowViewInner({ row, rowKey, number, label, columns, cellOptions, selectable, selected, selectionFull, busy, flashed, numbered, marks, strong = false, frame, sized, actions, onToggle, onAction, onOpen, openOnClick, onPick, picked = false, pickName, }) {
130
133
  const inline = actions?.filter(action => action.inline && action.icon) ?? [];
131
134
  const inMenu = actions?.filter(action => !(action.inline && action.icon)) ?? [];
132
135
  const items = inMenu.map(action => ({ value: action.id, label: action.label, icon: action.icon, description: action.description, tone: action.tone, disabled: action.disabled }));
133
- return (_jsxs("tr", { className: "nx-table__row", "data-key": rowKey, "data-selected": selected || undefined, "data-busy": busy || undefined, "data-flash": flashed || undefined, "aria-busy": busy || undefined, "data-open": (onOpen && openOnClick) || undefined,
136
+ // A click picks a row where rows are picked; opening it is then the double-click's.
137
+ const clickOpens = Boolean(onOpen && openOnClick && !onPick);
138
+ return (_jsxs("tr", { className: "nx-table__row", "data-key": rowKey, "data-selected": selected || undefined, "data-picked": picked || undefined, "data-strong": strong || undefined, "data-busy": busy || undefined, "data-flash": flashed || undefined, "aria-busy": busy || undefined, "data-open": clickOpens || undefined, "data-pick": onPick ? "" : undefined,
134
139
  // One way in: with a single click the row is already open by the time a double-click would arrive.
135
- onClick: onOpen && openOnClick ? event => opensRow(event, true) && onOpen(row) : undefined, onDoubleClick: onOpen && !openOnClick ? event => opensRow(event, false) && onOpen(row) : undefined, children: [frame.index && (_jsx(TableIndexCell, { own: frame.index, number: marks?.length ? (_jsx("span", { className: "nx-table__marks", children: marks.map(mark => (_jsx(InlineIcon, { icon: mark.icon, className: "nx-table__mark", label: mark.label }, mark.label))) })) : (formatCount(number)), numbered: numbered || Boolean(marks?.length), selectable: selectable, checkbox: selectable && (_jsx("label", { className: "nx-choice nx-choice--sm nx-table__check", title: selectionFull ? "No more rows can be selected." : undefined, children: _jsx("input", { type: "checkbox", className: "nx-choice__control nx-choice__control--checkbox", checked: selected, disabled: selectionFull && !selected, "aria-label": `Select ${label}`, onClick: event => onToggle(row, event.currentTarget.checked, event), onChange: () => undefined }) })) })), columns.map(view => (_jsx("td", { className: cx("nx-table__cell", `nx-table__cell--${columnAlign(view.column)}`, view.column.wrap && "nx-table__cell--wrap"), "data-col": view.column.key, "data-pin": view.pin ?? undefined, "data-pin-edge": (view.pin && view.edge) || undefined, style: columnStyle(view, sized), children: _jsx("span", { className: "nx-table__value", children: _jsx(TableCellValue, { column: view.column, row: row, options: cellOptions }) }) }, view.column.key))), sized && _jsx("td", { className: "nx-table__cell nx-table__cell--filler" }), frame.actions && (_jsx("td", { className: "nx-table__cell nx-table__cell--actions", "data-col": TABLE_ACTIONS_COLUMN, "data-pin": "right", "data-pin-edge": frame.actions.edge || undefined, style: ownStyle(frame.actions, "right"), children: _jsxs("div", { className: "nx-table__actions", children: [inline.map(action => (_jsx(Button, { variant: "ghost", size: "sm", icon: action.icon, hoverTone: action.tone === "danger" ? "danger" : undefined, "aria-label": `${action.label}: ${label}`, title: action.label, "data-action": action.id, disabled: busy || action.disabled, onClick: () => onAction(row, action) }, action.id))), items.length > 0 && (_jsx(Menu, { items: items, align: "end", "aria-label": `Actions: ${label}`, onSelect: value => onAction(row, inMenu.find(action => action.id === value)), children: _jsx(Button, { variant: "ghost", size: "sm", icon: _jsx(InlineIcon, { icon: iconDotsVertical }), loading: busy, "aria-label": `More actions: ${label}`, title: "More actions" }) })), items.length === 0 && busy && _jsx(Button, { variant: "ghost", size: "sm", loading: true, "aria-label": `Working: ${label}` })] }) }))] }));
140
+ onClick: onPick ? event => opensRow(event, true) && onPick(row) : clickOpens ? event => opensRow(event, true) && onOpen?.(row) : undefined, onDoubleClick: onOpen && !clickOpens ? event => opensRow(event, false) && onOpen(row) : undefined, children: [frame.index && (_jsx(TableIndexCell, { own: frame.index, number: marks?.length ? (_jsx("span", { className: "nx-table__marks", children: marks.map(mark => (_jsx(InlineIcon, { icon: mark.icon, className: "nx-table__mark", label: mark.label }, mark.label))) })) : (formatCount(number)), numbered: numbered || Boolean(marks?.length), selectable: selectable, checkbox: selectable && (_jsx("label", { className: "nx-choice nx-choice--sm nx-table__check", title: selectionFull ? "No more rows can be selected." : undefined, children: _jsx("input", { type: "checkbox", className: "nx-choice__control nx-choice__control--checkbox", checked: selected, disabled: selectionFull && !selected, "aria-label": `Select ${label}`, onClick: event => onToggle(row, event.currentTarget.checked, event), onChange: () => undefined }) })), pick: onPick &&
141
+ pickName && (_jsx("input", { type: "radio", className: "nx-visually-hidden nx-table__pick", name: pickName, checked: picked, "aria-label": label, onChange: () => onPick(row) })) })), columns.map(view => (_jsx("td", { className: cx("nx-table__cell", `nx-table__cell--${columnAlign(view.column)}`, view.column.wrap && "nx-table__cell--wrap"), "data-col": view.column.key, "data-pin": view.pin ?? undefined, "data-pin-edge": (view.pin && view.edge) || undefined, style: columnStyle(view, sized), children: _jsx("span", { className: "nx-table__value", children: _jsx(TableCellValue, { column: view.column, row: row, options: cellOptions }) }) }, view.column.key))), sized && _jsx("td", { className: "nx-table__cell nx-table__cell--filler" }), frame.actions && (_jsx("td", { className: "nx-table__cell nx-table__cell--actions", "data-col": TABLE_ACTIONS_COLUMN, "data-pin": "right", "data-pin-edge": frame.actions.edge || undefined, style: ownStyle(frame.actions, "right"), children: _jsxs("div", { className: "nx-table__actions", children: [inline.map(action => (_jsx(Button, { variant: "ghost", size: "sm", icon: action.icon, hoverTone: action.tone === "danger" ? "danger" : undefined, "aria-label": `${action.label}: ${label}`, title: action.label, "data-action": action.id, disabled: busy || action.disabled, onClick: () => onAction(row, action) }, action.id))), items.length > 0 && (_jsx(Menu, { items: items, align: "end", "aria-label": `Actions: ${label}`, onSelect: value => onAction(row, inMenu.find(action => action.id === value)), children: _jsx(Button, { variant: "ghost", size: "sm", icon: _jsx(InlineIcon, { icon: iconDotsVertical }), loading: busy, "aria-label": `More actions: ${label}`, title: "More actions" }) })), items.length === 0 && busy && _jsx(Button, { variant: "ghost", size: "sm", loading: true, "aria-label": `Working: ${label}` })] }) }))] }));
136
142
  }
137
143
  export const TableRowView = memo(TableRowViewInner);
138
144
  /**
@@ -173,14 +179,20 @@ export function TablePageSize({ pageSize, pageSizes, compact = false, onChange }
173
179
  }
174
180
  /**
175
181
  * Under the table: which rows show and how many the search and the filters left ("Showing 41–50 of 234 · filtered
176
- * from 1,000"), the page of how many, and the pager.
182
+ * from 1,000"), the page of how many, the rows per page when the table keeps them here (`pageSize`), and the pager.
183
+ *
184
+ * A table read whole (`whole`, every row on one page) has nothing to page: its footer shows only while a search or the
185
+ * filters leave rows out, and is otherwise kept for screen readers alone, so the count is still heard as it changes.
177
186
  */
178
- export function TableFooter({ state, disabled, onPage }) {
187
+ export function TableFooter({ state, disabled, whole = false, pageSize, onPage }) {
179
188
  const { status, rows, offset, matches, total, query } = state;
180
- if (status !== "ready" || total === 0)
189
+ const ready = status === "ready" && total > 0;
190
+ const narrowed = Boolean(query.search || query.filters);
191
+ const range = ready && (_jsxs(_Fragment, { children: [rows.length ? (_jsxs(_Fragment, { children: ["Showing ", _jsx("b", { children: `${formatCount(rows.length ? offset + 1 : 0)}–${formatCount(offset + rows.length)}` }), " of ", _jsx("b", { children: formatCount(matches) })] })) : (_jsx(_Fragment, { children: "No rows on this page" })), narrowed && matches !== total && _jsxs("span", { className: "nx-table__range-all", children: [" \u00B7 filtered from ", formatCount(total)] })] }));
192
+ if (whole) {
193
+ return (_jsx("div", { className: ready && narrowed ? "nx-table__footer" : "nx-visually-hidden", children: _jsx("p", { className: "nx-table__range", "aria-live": "polite", children: range }) }));
194
+ }
195
+ if (!ready)
181
196
  return _jsx("div", { className: "nx-table__footer" });
182
- const first = rows.length ? offset + 1 : 0;
183
- const last = offset + rows.length;
184
- const pages = pageCount(matches, query.pageSize);
185
- return (_jsxs("div", { className: "nx-table__footer", children: [_jsxs("p", { className: "nx-table__range", "aria-live": "polite", children: [rows.length ? (_jsxs(_Fragment, { children: ["Showing ", _jsx("b", { children: `${formatCount(first)}–${formatCount(last)}` }), " of ", _jsx("b", { children: formatCount(matches) })] })) : (_jsx(_Fragment, { children: "No rows on this page" })), (query.search || query.filters) && matches !== total && _jsxs("span", { className: "nx-table__range-all", children: [" \u00B7 filtered from ", formatCount(total)] })] }), _jsxs("div", { className: "nx-table__paging", children: [_jsxs("span", { className: "nx-table__page-info", children: ["Page ", formatCount(query.page), " of ", formatCount(pages)] }), _jsx(TablePager, { page: query.page, matches: matches, pageSize: query.pageSize, disabled: disabled, onPage: onPage })] })] }));
197
+ return (_jsxs("div", { className: "nx-table__footer", children: [_jsx("p", { className: "nx-table__range", "aria-live": "polite", children: range }), _jsxs("div", { className: "nx-table__paging", children: [pageSize, _jsxs("span", { className: "nx-table__page-info", children: ["Page ", formatCount(query.page), " of ", formatCount(pageCount(matches, query.pageSize))] }), _jsx(TablePager, { page: query.page, matches: matches, pageSize: query.pageSize, disabled: disabled, onPage: onPage })] })] }));
186
198
  }
@@ -0,0 +1,227 @@
1
+ import { type ReactNode, type RefObject } from "react";
2
+ import { type OwnerFileActions } from "../../Helpers/FileAdapter.ts";
3
+ import type { ApiModuleValue, ApiResult, AppModule } from "../../Interfaces/ApiInterfaces.ts";
4
+ import { type FileChange, type FileOwner, type OwnerFiles, type PictureType, type StoredFile } from "../../Interfaces/FileInterfaces.ts";
5
+ import type { TableColumn, TableRow } from "../../Interfaces/TableInterfaces.ts";
6
+ import { type PopupHandle } from "../Layouts/PopupHost.tsx";
7
+ /** What a page declares for an owner's pictures: whose they are, and how a picture is made. */
8
+ export interface OwnerFilesParams<TModule extends ApiModuleValue = AppModule> extends FileOwner<TModule> {
9
+ /** The shape every picture is cropped to: `1` for a logo or a person, `4 / 3`, or `free` (default). */
10
+ aspect?: number | "free";
11
+ /** `circle` shows a person's picture round, and the editor's guide with it. Default `rect`. */
12
+ shape?: "rect" | "circle";
13
+ /** The largest picture kept, in pixels. Default the category's `pictureMaxSide` (1920), which it never passes. */
14
+ maxWidth?: number;
15
+ maxHeight?: number;
16
+ /**
17
+ * The kind a picture is saved as, fixed. Default: the kind it came in, and a PNG over the size limit becomes a JPEG
18
+ * (nothing see-through) or a WebP (keeping its see-through parts where the browser writes WebP).
19
+ */
20
+ type?: PictureType;
21
+ /** 0-1, for JPEG and WebP, where the editor starts. Never above the category's `pictureQuality`. */
22
+ quality?: number;
23
+ /** Whose they are, in the window's title and the pictures' names: "Kathmandu branch". */
24
+ title?: string;
25
+ /** Shows the files with no way to change them, whatever the backend allows. */
26
+ readOnly?: boolean;
27
+ /**
28
+ * Why the files may not be changed here, said under them: a record that is closed, a year that is locked. Set, the
29
+ * files are read only, as with `readOnly`. Without it, a person the backend lets only look is told the backend's own
30
+ * reason (`OwnerFiles.readOnlyReason`), or that they may look but not change.
31
+ *
32
+ * @example lockReason: row?.isClosed ? "The branch is closed: open it again to change its picture." : null
33
+ */
34
+ lockReason?: string | null;
35
+ /**
36
+ * The changes a person may make here, from the record's state, within what the backend allows: `["add"]` for a record
37
+ * whose files may only be added to - a posted goods receipt's documents - keeps Upload and offers no Rename, Delete,
38
+ * Make main or Remove (nor Replace or an Edit, which delete or mark). Default every change. `readOnly` and `lockReason`
39
+ * still show the files with none. A change the backend refuses as "only added to now" narrows them the same way for
40
+ * the rest of the page's life, under the backend's words, so a page read before the record changed stops offering it.
41
+ *
42
+ * @example changes: receipt.documentState === POSTED ? ["add"] : undefined
43
+ */
44
+ changes?: readonly FileChange[] | null;
45
+ /**
46
+ * Why only those changes, said under the files while `changes` leaves some out: "Posted: a document can be added, as a
47
+ * bill scanned later; none is removed."
48
+ */
49
+ changesReason?: string | null;
50
+ /**
51
+ * The category's Delete may wait for a review (a company's public gallery), told before the files are read - the
52
+ * backend says so too (`FileCategoryInfo.holdsRemovals`), and either is enough. The Delete then asks "If the public
53
+ * sees this picture, it stays until Nexus support approves its removal.", and the answer decides: a file answered
54
+ * `pendingRemoval` stays, shown "Removal waiting for review" with Keep in place of Delete; any other is gone at once.
55
+ */
56
+ holdsRemovals?: boolean;
57
+ /** Who reviews a held removal, in those sentences. Default `REMOVAL_REVIEWER` ("Nexus support"). */
58
+ removalReviewer?: string;
59
+ /** After a change on this page: the owner's picture now (null for none). */
60
+ onChange?: (primary: StoredFile | null) => void;
61
+ /**
62
+ * After every change made here that the backend took - an upload, an edit saved, a main picture chosen (or none), a
63
+ * rename, a delete, a delete held for a review, a Keep - with the owner's files as read again after it (as they were,
64
+ * when that read failed) and what changed. A page whose own record follows its files (a company's profile request,
65
+ * which a held Delete or a Keep changes on the server) reads it again here.
66
+ *
67
+ * @example onFilesChange: (files, change) => { if (change.kind === "hold" || change.kind === "keep") void reloadRequest(); }
68
+ */
69
+ onFilesChange?: (files: StoredFile[], change: OwnerFilesChange) => void;
70
+ }
71
+ /** What changed an owner's files, for `onFilesChange`: a delete held for a review is `hold`, its Keep `keep`. */
72
+ export type OwnerFilesChangeKind = "upload" | "edit" | "primary" | "rename" | "delete" | "hold" | "keep";
73
+ /**
74
+ * One change to an owner's files: what it was, and the file - the stored one (an upload's, an edit's last saved), the one
75
+ * made main (null for none), renamed, deleted, held or kept.
76
+ */
77
+ export interface OwnerFilesChange {
78
+ kind: OwnerFilesChangeKind;
79
+ file: StoredFile | null;
80
+ }
81
+ /** What a person the backend lets only look at an owner's files is told, when it gave no words of its own. */
82
+ export declare const READ_ONLY_REASON = "You can see these files, but you may not change them.";
83
+ /** The longest side of a stored picture when neither the params nor the backend say: Drive's default (`PICTURE_LIMITS`). */
84
+ export declare const MAX_PICTURE_SIDE: 1920;
85
+ type Status = {
86
+ kind: "idle";
87
+ } | {
88
+ kind: "loading";
89
+ } | {
90
+ kind: "ready";
91
+ data: OwnerFiles;
92
+ } | {
93
+ kind: "failed";
94
+ message: string;
95
+ };
96
+ /** Tells what a refused or failed call said, once, in the server's own words. */
97
+ export declare function toastFileFailure(title: string, answer: Pick<ApiResult, "message" | "aborted">): void;
98
+ /** One owner's files, and every change a person can make to them. `ownerId` null: nothing is read (a record not saved yet). */
99
+ export declare function useOwnerFiles(params: Omit<OwnerFilesParams<ApiModuleValue>, "ownerId"> & {
100
+ ownerId?: string | null;
101
+ }): {
102
+ status: Status;
103
+ data: OwnerFiles | null;
104
+ busy: string | null;
105
+ canWrite: boolean;
106
+ readOnlyReason: string | null;
107
+ changes: readonly FileChange[] | null;
108
+ changesNote: string | null;
109
+ offer: (file?: StoredFile | null) => OwnerFileActions;
110
+ pictures: boolean;
111
+ holdsRemovals: boolean;
112
+ editorProblem: string | null;
113
+ actions: {
114
+ reload: () => Promise<void | OwnerFiles>;
115
+ /**
116
+ * Chooses a picture, edits it, and makes it the owner's picture (the picture input's Upload). In a category that
117
+ * keeps one file, the new picture takes the place of the one kept (Replace).
118
+ */
119
+ replace: () => Promise<void>;
120
+ /** Chooses several files and stores them: each picture through the editor, one at a time (the gallery's Upload). */
121
+ upload: () => Promise<void>;
122
+ /**
123
+ * Edits a stored picture; what is saved is a new picture, the owner's picture when the edited one was. In a category
124
+ * that keeps one file, the edited picture takes its place.
125
+ */
126
+ edit: (file: StoredFile) => Promise<void>;
127
+ makePrimary: (file: StoredFile | null) => false | Promise<void> | null;
128
+ rename: (file: StoredFile) => Promise<void>;
129
+ remove: (file: StoredFile) => Promise<void>;
130
+ /** Keeps a file whose removal waits for a review: the removal is cancelled. */
131
+ keep: (file: StoredFile) => Promise<void>;
132
+ /** Shows the pictures in the editor module's viewer, from `file`. */
133
+ view: (file: StoredFile) => Promise<void>;
134
+ };
135
+ };
136
+ /**
137
+ * Keeps the keyboard focus in an owner's files through a change (`busy`). Its buttons stay focusable while it runs
138
+ * (`aria-disabled`, never `disabled`: a focused control switched off sends the focus to the page in every browser), and
139
+ * when it ends with the focused button gone - a deleted file's, Edit after Remove, a star once its picture is the main
140
+ * one - the focus goes to the first control left in the same tile, row or `role="group"` (the picture input's buttons),
141
+ * else the first in `root`. Only when the focus
142
+ * was in `root` as the change began (or on the page, after a confirmation, while `root` had it last), and only when it
143
+ * has dropped to the page since: a person who moved on keeps their place.
144
+ */
145
+ export declare function useFocusThroughChange(root: RefObject<HTMLElement | null>, busy: string | null): void;
146
+ /** A control's props while a change runs: still focusable and announced as unavailable, its press ignored. */
147
+ export declare function busyProps(busy: string | null, run: () => void): {
148
+ readonly "aria-disabled": true | undefined;
149
+ readonly onClick: () => void;
150
+ };
151
+ /** The owner's picture as the page keeps it, read with the other rows' in one call. */
152
+ export declare function usePrimaryPicture(owner: FileOwner<ApiModuleValue> | null): StoredFile | null | undefined;
153
+ export interface OwnerPictureProps {
154
+ owner: FileOwner<ApiModuleValue> | null;
155
+ /** The picture's height: pixels, or any CSS length (`var(--a-size)`). Default 28, a table row's. */
156
+ size?: number | string;
157
+ aspect?: number | "free";
158
+ shape?: "rect" | "circle";
159
+ /** What it shows, for a screen reader and the tooltip: "Kathmandu branch's picture". */
160
+ label?: string;
161
+ /**
162
+ * Whose picture it is, by name: without a picture, their initials stand in, on the tone the shared `Avatar` gives that
163
+ * name ("Ram Thapa" -> "RT"), rather than the picture icon. A person's or a company's row passes it.
164
+ */
165
+ name?: string | null;
166
+ /** Makes it a button: a table cell opens the gallery with it. */
167
+ onClick?: () => void;
168
+ className?: string;
169
+ }
170
+ /** An owner's picture, small: its thumbnail, or, when it has none, the initials of its `name` or a picture icon. */
171
+ export declare function OwnerPicture({ owner, size, aspect, shape, label, name, onClick, className }: OwnerPictureProps): import("react").JSX.Element;
172
+ export interface PictureGalleryProps extends OwnerFilesParams<ApiModuleValue> {
173
+ /** `pictures` (two panes) or `documents` (a list). Default: documents when the category takes anything but pictures. */
174
+ view?: "pictures" | "documents";
175
+ /** Once the files are read: the view shown, `documents` or `pictures` (a window's title follows it). */
176
+ onViewKnown?: (view: "pictures" | "documents") => void;
177
+ }
178
+ /**
179
+ * An owner's pictures, or documents for a category that takes PDF: upload several up to the category's limit, make
180
+ * one the main picture, view them in the editor module's viewer, edit one (saved as a new picture), delete.
181
+ *
182
+ * Pictures come in two panes, as the 2026-06-20 gallery did: the main picture large, with Upload and Remove (no main
183
+ * picture; the pictures stay) under it and the count and kinds; beside it the pictures as a grid, each with its own
184
+ * buttons inside it - View, Make main, Edit, Delete (Make main and Delete not on the main one) - shown on hover, on a
185
+ * keyboard's focus in the picture, and always where nothing can hover (a touch screen). Under 560px of its own width
186
+ * the main picture sits small beside its buttons, over the grid.
187
+ */
188
+ export declare function PictureGallery(props: PictureGalleryProps): import("react").JSX.Element;
189
+ /** An owner's documents as a list: `PictureGallery` with `view="documents"`. */
190
+ export declare function DocumentList(props: OwnerFilesParams<ApiModuleValue>): import("react").JSX.Element;
191
+ export interface OpenPictureGalleryOptions extends PictureGalleryProps {
192
+ /** The window's title. Default: "Pictures" or "Documents", and whose. */
193
+ popupTitle?: ReactNode;
194
+ }
195
+ /**
196
+ * Opens an owner's pictures (or documents) in a window; one window per owner and category. Pictures fill a wide
197
+ * window edge to edge in their two panes, the grid scrolling; documents are a list in a narrower one. Without `view`,
198
+ * the title, icon and size follow what the category takes once its files are read: a category that takes PDF says
199
+ * "Documents".
200
+ */
201
+ export declare function openPictureGallery({ popupTitle, ...props }: OpenPictureGalleryOptions): PopupHandle;
202
+ export interface PictureColumnParams<TRow extends TableRow> extends Omit<OwnerFilesParams<ApiModuleValue>, "ownerId" | "title" | "onChange" | "onFilesChange" | "lockReason">, Partial<Omit<TableColumn<TRow>, "render" | "value">> {
203
+ /** The row's field that holds the owner's id. Default `id`. */
204
+ ownerKey?: string;
205
+ /** Whose the row's pictures are, for the window's title: `row => row.branchName`. */
206
+ rowTitle?: (row: TRow) => string;
207
+ /**
208
+ * The row's field with a name whose initials stand in for a row with no picture (`"fullname"`), as the shared `Avatar`
209
+ * draws a person. Without it, such a row shows the picture icon.
210
+ */
211
+ nameKey?: string;
212
+ /** Why a row's pictures may not be changed (its window shows them read only, with this sentence), or nothing. */
213
+ lockReason?: string | null | ((row: TRow) => string | null | undefined);
214
+ /** After a change in the window a row opened. */
215
+ onChange?: (row: TRow, primary: StoredFile | null) => void;
216
+ }
217
+ /**
218
+ * A column with each row's picture: every visible row's read in one call per page, and a click opens the row's
219
+ * gallery. A change there redraws the row's picture without a reload.
220
+ * It is a `lead` column: every table draws it first of the page's columns (after the row number), pinned on the left
221
+ * when the page pins others there, wherever it stands in `columns`, until the user moves, unpins or hides it.
222
+ *
223
+ * @example
224
+ * columns: [pictureColumn({ module: NexusModule.Enterprise, category: "branch-picture", rowTitle: row => row.branchName }), ...]
225
+ */
226
+ export declare function pictureColumn<TRow extends TableRow>({ module, category, ownerKey, rowTitle, nameKey, lockReason, onChange, aspect, shape, maxWidth, maxHeight, type, quality, readOnly, changes, changesReason, holdsRemovals, removalReviewer, view, ...column }: PictureColumnParams<TRow> & Pick<PictureGalleryProps, "view">): TableColumn<TRow>;
227
+ export {};