nexus-shared 4.0.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 (163) hide show
  1. package/CHANGELOG.md +795 -0
  2. package/README.md +220 -14
  3. package/dist/Client.Index.d.ts +13 -0
  4. package/dist/Client.Index.js +13 -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/TabButtons.d.ts +6 -0
  22. package/dist/Components/Documents/TabButtons.js +5 -4
  23. package/dist/Components/Domains/DomainCard.d.ts +98 -0
  24. package/dist/Components/Domains/DomainCard.js +182 -0
  25. package/dist/Components/Forms/ApiForm.js +5 -1
  26. package/dist/Components/Forms/Crud.js +72 -24
  27. package/dist/Components/Forms/CrudActions.d.ts +5 -4
  28. package/dist/Components/Forms/CrudActions.js +42 -12
  29. package/dist/Components/Forms/CrudHistory.d.ts +6 -1
  30. package/dist/Components/Forms/CrudHistory.js +14 -6
  31. package/dist/Components/Forms/CrudParts.d.ts +11 -4
  32. package/dist/Components/Forms/CrudParts.js +76 -10
  33. package/dist/Components/Forms/CrudRowForms.js +65 -5
  34. package/dist/Components/Forms/CrudTree.js +7 -7
  35. package/dist/Components/Forms/CrudWorkspace.d.ts +15 -5
  36. package/dist/Components/Forms/CrudWorkspace.js +19 -11
  37. package/dist/Components/Forms/Form.d.ts +3 -2
  38. package/dist/Components/Forms/Form.js +33 -26
  39. package/dist/Components/Forms/FormQuestion.d.ts +6 -0
  40. package/dist/Components/Forms/FormQuestion.js +28 -0
  41. package/dist/Components/Forms/SubmitForm.js +2 -15
  42. package/dist/Components/Inputs/ComputedInput.d.ts +51 -0
  43. package/dist/Components/Inputs/ComputedInput.js +179 -0
  44. package/dist/Components/Inputs/Dropdown.js +4 -2
  45. package/dist/Components/Inputs/GroupForm.js +3 -2
  46. package/dist/Components/Inputs/InputRenderer.d.ts +5 -1
  47. package/dist/Components/Inputs/MarkdownEditor.d.ts +6 -0
  48. package/dist/Components/Inputs/MarkdownEditor.js +32 -6
  49. package/dist/Components/Inputs/PictureInput.js +8 -3
  50. package/dist/Components/Inputs/RowsInput.d.ts +14 -1
  51. package/dist/Components/Inputs/RowsInput.js +78 -5
  52. package/dist/Components/Inputs/TabularForm.js +16 -3
  53. package/dist/Components/Inputs/TreeView.d.ts +24 -1
  54. package/dist/Components/Inputs/TreeView.js +84 -7
  55. package/dist/Components/Layouts/Popup.d.ts +22 -2
  56. package/dist/Components/Layouts/Popup.js +108 -6
  57. package/dist/Components/Messages/MailClient.d.ts +50 -0
  58. package/dist/Components/Messages/MailClient.js +410 -0
  59. package/dist/Components/Messages/MailColumns.d.ts +21 -0
  60. package/dist/Components/Messages/MailColumns.js +92 -0
  61. package/dist/Components/Messages/MailCompose.d.ts +46 -0
  62. package/dist/Components/Messages/MailCompose.js +154 -0
  63. package/dist/Components/Messages/MailReader.d.ts +43 -0
  64. package/dist/Components/Messages/MailReader.js +207 -0
  65. package/dist/Components/Messages/MailThread.d.ts +71 -0
  66. package/dist/Components/Messages/MailThread.js +185 -0
  67. package/dist/Components/Messages/MessageBell.d.ts +41 -0
  68. package/dist/Components/Messages/MessageBell.js +76 -0
  69. package/dist/Components/Viewers/DataTable.js +7 -5
  70. package/dist/Components/Viewers/DataTableColumns.d.ts +2 -2
  71. package/dist/Components/Viewers/DataTableColumns.js +2 -1
  72. package/dist/Components/Viewers/DataTableParts.d.ts +3 -1
  73. package/dist/Components/Viewers/DataTableParts.js +2 -2
  74. package/dist/Components/Viewers/OwnerFiles.d.ts +57 -5
  75. package/dist/Components/Viewers/OwnerFiles.js +142 -31
  76. package/dist/Helpers/ApiModules.js +4 -0
  77. package/dist/Helpers/AuditEntries.d.ts +50 -0
  78. package/dist/Helpers/AuditEntries.js +96 -0
  79. package/dist/Helpers/ChatBackend.d.ts +8 -1
  80. package/dist/Helpers/ChatBackend.js +18 -0
  81. package/dist/Helpers/ChatHelpers.d.ts +2 -0
  82. package/dist/Helpers/ChatHelpers.js +4 -1
  83. package/dist/Helpers/CrudColumns.d.ts +44 -6
  84. package/dist/Helpers/CrudColumns.js +63 -13
  85. package/dist/Helpers/CrudHelpers.d.ts +36 -1
  86. package/dist/Helpers/CrudHelpers.js +61 -0
  87. package/dist/Helpers/CrudPickerPlan.d.ts +58 -0
  88. package/dist/Helpers/CrudPickerPlan.js +46 -0
  89. package/dist/Helpers/CrudRowForms.d.ts +18 -1
  90. package/dist/Helpers/CrudRowForms.js +35 -2
  91. package/dist/Helpers/DashboardHelpers.d.ts +115 -0
  92. package/dist/Helpers/DashboardHelpers.js +305 -0
  93. package/dist/Helpers/DomainCards.d.ts +140 -0
  94. package/dist/Helpers/DomainCards.js +370 -0
  95. package/dist/Helpers/FileAdapter.d.ts +82 -2
  96. package/dist/Helpers/FileAdapter.js +72 -1
  97. package/dist/Helpers/FormStore.d.ts +11 -1
  98. package/dist/Helpers/FormStore.js +66 -19
  99. package/dist/Helpers/InputConditions.d.ts +6 -0
  100. package/dist/Helpers/InputConditions.js +24 -0
  101. package/dist/Helpers/InputMemory.js +40 -30
  102. package/dist/Helpers/InputNarrowing.d.ts +38 -6
  103. package/dist/Helpers/InputNarrowing.js +102 -21
  104. package/dist/Helpers/MailClientHelpers.d.ts +96 -0
  105. package/dist/Helpers/MailClientHelpers.js +211 -0
  106. package/dist/Helpers/MessageBackend.d.ts +152 -0
  107. package/dist/Helpers/MessageBackend.js +329 -0
  108. package/dist/Helpers/MessageHelpers.d.ts +160 -0
  109. package/dist/Helpers/MessageHelpers.js +573 -0
  110. package/dist/Helpers/MessageUnread.d.ts +95 -0
  111. package/dist/Helpers/MessageUnread.js +245 -0
  112. package/dist/Helpers/NexusDriveAdapter.d.ts +17 -1
  113. package/dist/Helpers/NexusDriveAdapter.js +32 -3
  114. package/dist/Helpers/NumberHelpers.d.ts +61 -0
  115. package/dist/Helpers/NumberHelpers.js +124 -8
  116. package/dist/Helpers/PagedPicker.d.ts +42 -0
  117. package/dist/Helpers/PagedPicker.js +85 -0
  118. package/dist/Helpers/RowsStore.d.ts +24 -0
  119. package/dist/Helpers/RowsStore.js +79 -10
  120. package/dist/Helpers/TableHelpers.d.ts +15 -2
  121. package/dist/Helpers/TableHelpers.js +28 -1
  122. package/dist/Helpers/TableStore.d.ts +7 -3
  123. package/dist/Helpers/TableStore.js +9 -4
  124. package/dist/Helpers/ToolbarFit.d.ts +23 -0
  125. package/dist/Helpers/ToolbarFit.js +30 -0
  126. package/dist/Helpers/TreeStore.d.ts +75 -6
  127. package/dist/Helpers/TreeStore.js +182 -18
  128. package/dist/Interfaces/ApiInterfaces.d.ts +10 -1
  129. package/dist/Interfaces/ApiInterfaces.js +10 -1
  130. package/dist/Interfaces/ChatInterfaces.d.ts +22 -0
  131. package/dist/Interfaces/CrudInterfaces.d.ts +78 -5
  132. package/dist/Interfaces/DashboardInterfaces.d.ts +123 -0
  133. package/dist/Interfaces/DashboardInterfaces.js +1 -0
  134. package/dist/Interfaces/DomainInterfaces.d.ts +145 -0
  135. package/dist/Interfaces/DomainInterfaces.js +1 -0
  136. package/dist/Interfaces/FileInterfaces.d.ts +21 -0
  137. package/dist/Interfaces/FileInterfaces.js +2 -0
  138. package/dist/Interfaces/FormInterfaces.d.ts +78 -11
  139. package/dist/Interfaces/InputInterfaces.d.ts +12 -5
  140. package/dist/Interfaces/MailInterfaces.d.ts +192 -0
  141. package/dist/Interfaces/MailInterfaces.js +1 -0
  142. package/dist/Interfaces/TableInterfaces.d.ts +28 -2
  143. package/dist/Services/JoinSources.d.ts +47 -6
  144. package/dist/Services/JoinSources.js +68 -33
  145. package/dist/Services/MessageApi.d.ts +18 -0
  146. package/dist/Services/MessageApi.js +64 -0
  147. package/dist/Shared.Index.d.ts +12 -0
  148. package/dist/Shared.Index.js +12 -0
  149. package/package.json +7 -1
  150. package/src/Styles/Nexus.Button.css +29 -0
  151. package/src/Styles/Nexus.Chat.css +114 -22
  152. package/src/Styles/Nexus.Crud.css +119 -28
  153. package/src/Styles/Nexus.Dashboard.css +763 -0
  154. package/src/Styles/Nexus.Domain.css +384 -0
  155. package/src/Styles/Nexus.Editor.css +20 -0
  156. package/src/Styles/Nexus.Files.css +27 -0
  157. package/src/Styles/Nexus.Index.css +3 -0
  158. package/src/Styles/Nexus.Input.css +31 -0
  159. package/src/Styles/Nexus.Mail.css +1062 -0
  160. package/src/Styles/Nexus.Popup.css +58 -0
  161. package/src/Styles/Nexus.Table.css +1458 -1449
  162. package/src/Styles/Nexus.Theme.Picker.css +1 -0
  163. package/src/Styles/Nexus.Tree.css +10 -0
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { iconMinus, iconSquare, iconSquares, iconX, InlineIcon } from "nexus-icons";
4
4
  import { useContext, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
5
- import { flushSync } from "react-dom";
5
+ import { createPortal, flushSync } from "react-dom";
6
6
  import { afterAnimation, motionAllowed } from "../../Helpers/AnimationHelpers.js";
7
7
  import { cx } from "../../Helpers/ClassHelpers.js";
8
8
  import { cssVariablePosition, DRAG_CANCEL_SELECTOR, moveWithin, startDrag, viewportBounds } from "../../Helpers/DragHelpers.js";
@@ -12,6 +12,57 @@ const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]):not([t
12
12
  // Where a minimized popup flies to: the newest bar of the dock, at the bottom right.
13
13
  const DOCK_EDGE = 16;
14
14
  const DOCK_BAR = { width: 300, height: 44 };
15
+ // Docked windows (`docked`), in the order they opened (each next one to the left of the one before), and in the order
16
+ // they were last used (the last on top).
17
+ const DOCKED_GAP = 12;
18
+ let dockedOrder = [];
19
+ let dockedStack = [];
20
+ let dockedObserver = null;
21
+ /** Lays the open docked windows out along the bottom edge, and stacks them by use. */
22
+ function layoutDocked() {
23
+ const view = document.documentElement.clientWidth;
24
+ let right = DOCK_EDGE;
25
+ for (const dialog of dockedOrder) {
26
+ if (!dialog.open)
27
+ continue;
28
+ const width = dialog.offsetWidth;
29
+ // A window with no room left of the others stands at the right again, over them: the last used shows on top.
30
+ const fits = right + width + DOCK_EDGE <= view;
31
+ dialog.style.setProperty("--nx-docked-right", `${fits ? right : DOCK_EDGE}px`);
32
+ if (fits)
33
+ right += width + DOCKED_GAP;
34
+ }
35
+ dockedStack.forEach((dialog, index) => dialog.style.setProperty("--nx-docked-z", String(index + 1)));
36
+ }
37
+ function dockedJoin(dialog) {
38
+ if (!dockedOrder.includes(dialog))
39
+ dockedOrder = [...dockedOrder, dialog];
40
+ dockedRaise(dialog);
41
+ if (!dockedObserver) {
42
+ dockedObserver = new ResizeObserver(layoutDocked);
43
+ window.addEventListener("resize", layoutDocked);
44
+ }
45
+ dockedObserver.observe(dialog);
46
+ layoutDocked();
47
+ }
48
+ function dockedLeave(dialog) {
49
+ dockedOrder = dockedOrder.filter(one => one !== dialog);
50
+ dockedStack = dockedStack.filter(one => one !== dialog);
51
+ dockedObserver?.unobserve(dialog);
52
+ if (dockedOrder.length === 0 && dockedObserver) {
53
+ dockedObserver.disconnect();
54
+ dockedObserver = null;
55
+ window.removeEventListener("resize", layoutDocked);
56
+ }
57
+ layoutDocked();
58
+ }
59
+ /** The docked window last used comes on top of the others. */
60
+ function dockedRaise(dialog) {
61
+ if (dockedStack[dockedStack.length - 1] === dialog)
62
+ return;
63
+ dockedStack = [...dockedStack.filter(one => one !== dialog), dialog];
64
+ layoutDocked();
65
+ }
15
66
  function popupPosition(dialog) {
16
67
  return cssVariablePosition(dialog, "--nx-popup-x", "--nx-popup-y");
17
68
  }
@@ -30,7 +81,10 @@ const homes = new WeakMap();
30
81
  * @example
31
82
  * <Popup open={open} onClose={() => setOpen(false)} title="Edit booking" footer={<Button variant="primary">Save</Button>}>…</Popup>
32
83
  */
33
- export function Popup({ open, onClose, onClosed, title, description, icon, variant = "window", size = "md", footer, closable = true, closeOnEscape = true, closeOnBackdrop = true, minimizable, maximizable = true, movable = true, reopen = "replace", windowState: windowStateProp, defaultWindowState = "normal", onWindowStateChange, flush = false, initialFocus, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, className, children, }) {
84
+ export function Popup({ open, onClose, onClosed, title, description, icon, variant = "window", size = "md", height, footer, closable = true, closeOnEscape = true, closeOnBackdrop = true, minimizable, maximizable: maximizableProp = true, movable: movableProp = true, reopen = "replace", docked = false, drawer = false, windowState: windowStateProp, defaultWindowState = "normal", onWindowStateChange, flush = false, initialFocus, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, className, children, }) {
85
+ // A drawer stands at its edge: it neither moves nor maximizes.
86
+ const maximizable = maximizableProp && !drawer;
87
+ const movable = movableProp && !drawer;
34
88
  const id = useId();
35
89
  const dialogRef = useRef(null);
36
90
  // Content stays mounted through the exit animation, and while minimized.
@@ -61,6 +115,13 @@ export function Popup({ open, onClose, onClosed, title, description, icon, varia
61
115
  // The box before a maximize or a restore, for the animation between the two.
62
116
  const flipFromRef = useRef(null);
63
117
  const dockId = `nx-window${id}`;
118
+ // A docked window is drawn on the page's body, never inside what opened it: a scroller, a container or a transform
119
+ // there would carry it along or cut it off. Only once the page is in the browser, where a window can be opened.
120
+ const [portalReady, setPortalReady] = useState(false);
121
+ useEffect(() => {
122
+ if (docked)
123
+ setPortalReady(true);
124
+ }, [docked]);
64
125
  function changeWindowState(next) {
65
126
  if (next === latest.current.windowState)
66
127
  return;
@@ -120,8 +181,12 @@ export function Popup({ open, onClose, onClosed, title, description, icon, varia
120
181
  if (top)
121
182
  (homes.get(top) ?? (() => top.focus()))();
122
183
  }
184
+ // Where the focus starts: the asked-for element; else what the content marked as its start (`data-nx-focus`, such as a
185
+ // mail thread's subject, so a long read opens at its top and not on its first link or scroller); else the first control.
123
186
  function focusInside(dialog) {
124
- const target = initialFocus ? dialog.querySelector(initialFocus) : dialog.querySelector(`:is(.nx-popup__body, .nx-popup__foot) :is(${FOCUSABLE})`);
187
+ const target = initialFocus
188
+ ? dialog.querySelector(initialFocus)
189
+ : (dialog.querySelector(":is(.nx-popup__body, .nx-popup__foot) [data-nx-focus]") ?? dialog.querySelector(`:is(.nx-popup__body, .nx-popup__foot) :is(${FOCUSABLE})`));
125
190
  (target ?? dialog).focus();
126
191
  }
127
192
  useLayoutEffect(() => {
@@ -130,6 +195,12 @@ export function Popup({ open, onClose, onClosed, title, description, icon, varia
130
195
  homes.set(dialog, () => focusInside(dialog));
131
196
  });
132
197
  function showDialog(dialog) {
198
+ // A docked window leaves the page as it is: no backdrop, no lock, laid out beside the others.
199
+ if (docked) {
200
+ dialog.show();
201
+ dockedJoin(dialog);
202
+ return;
203
+ }
133
204
  // The first modal locks the page; the space of its scrollbar is kept as padding, so nothing shifts.
134
205
  if (!document.querySelector(".nx-popup:modal"))
135
206
  document.documentElement.style.setProperty("--nx-scrollbar-gap", `${window.innerWidth - document.documentElement.clientWidth}px`);
@@ -138,6 +209,8 @@ export function Popup({ open, onClose, onClosed, title, description, icon, varia
138
209
  function closeDialog(dialog) {
139
210
  selfCloseRef.current = true;
140
211
  dialog.close();
212
+ if (docked)
213
+ layoutDocked();
141
214
  returnFocus();
142
215
  }
143
216
  // After the popup has closed: the content unmounts, and the next opening starts centered, in its default state.
@@ -234,7 +307,7 @@ export function Popup({ open, onClose, onClosed, title, description, icon, varia
234
307
  focusInside(dialog);
235
308
  }
236
309
  // eslint-disable-next-line react-hooks/exhaustive-deps
237
- }, [open, minimized]);
310
+ }, [open, minimized, portalReady]);
238
311
  // Closing: the exit animation plays, then the dialog closes and the content unmounts. Without the animation
239
312
  // (reduced motion), it closes in the same frame.
240
313
  useLayoutEffect(() => {
@@ -319,6 +392,28 @@ export function Popup({ open, onClose, onClosed, title, description, icon, varia
319
392
  useEffect(() => () => undock(),
320
393
  // eslint-disable-next-line react-hooks/exhaustive-deps
321
394
  []);
395
+ // A docked window leaves the row of docked windows when it goes.
396
+ useEffect(() => {
397
+ const dialog = dialogRef.current;
398
+ if (!docked || !dialog)
399
+ return;
400
+ return () => dockedLeave(dialog);
401
+ }, [docked, portalReady]);
402
+ // Esc inside a docked window closes it: the browser asks only a modal one. A menu or a list inside that took the key
403
+ // first keeps it.
404
+ function onKeyDown(event) {
405
+ if (!docked || event.key !== "Escape" || event.defaultPrevented || closing)
406
+ return;
407
+ event.preventDefault();
408
+ if (closeOnEscape)
409
+ onClose("escape");
410
+ }
411
+ // The docked window used last comes on top.
412
+ function raise() {
413
+ const dialog = dialogRef.current;
414
+ if (docked && dialog)
415
+ dockedRaise(dialog);
416
+ }
322
417
  // Esc: the browser asks to close; the page decides through onClose.
323
418
  function onCancel(event) {
324
419
  event.preventDefault();
@@ -348,7 +443,8 @@ export function Popup({ open, onClose, onClosed, title, description, icon, varia
348
443
  function onClick(event) {
349
444
  const onBackdrop = event.target === event.currentTarget && downOnBackdropRef.current;
350
445
  downOnBackdropRef.current = false;
351
- if (onBackdrop && closeOnBackdrop && !closing)
446
+ // A docked window has no backdrop: a press on its own edge is not outside it.
447
+ if (onBackdrop && closeOnBackdrop && !closing && !docked)
352
448
  onClose("backdrop");
353
449
  }
354
450
  // A mouse click on minimize or maximize leaves focus in the field being typed in, as a desktop window's buttons do.
@@ -397,5 +493,11 @@ export function Popup({ open, onClose, onClosed, title, description, icon, varia
397
493
  const titleId = `${id}-title`;
398
494
  const descriptionId = `${id}-description`;
399
495
  const hasControls = canMinimize || maximizable || closable;
400
- return (_jsx("dialog", { ref: dialogRef, className: cx("nx-popup", `nx-popup--${variant}`, `nx-popup--${size}`, flush && "nx-popup--flush", maximized && "nx-popup--maximized", movable && "nx-popup--movable", closing && "nx-popup--closing", className), "data-window-state": windowState, "aria-labelledby": title ? titleId : undefined, "aria-label": title ? undefined : ariaLabel, "aria-describedby": cx(description ? descriptionId : undefined, ariaDescribedBy) || undefined, onCancel: onCancel, onClose: onNativeClose, onPointerDown: onPointerDown, onMouseDown: onMouseDown, onClick: onClick, children: shown && (_jsxs(HostedPopupContext, { value: false, children: [(title || icon || hasControls) && (_jsxs("header", { className: "nx-popup__head", onPointerDown: onHeadPointerDown, onDoubleClick: onHeadDoubleClick, children: [icon && _jsx("span", { className: "nx-popup__icon", children: icon }), _jsxs("div", { className: "nx-popup__heading", children: [title && (_jsx("h2", { id: titleId, className: "nx-popup__title", children: title })), description && (_jsx("p", { id: descriptionId, className: "nx-popup__description", children: description }))] }), hasControls && (_jsxs("div", { className: "nx-popup__controls", children: [canMinimize && (_jsx("button", { type: "button", className: "nx-popup__control nx-popup__minimize", "aria-label": "Minimize", title: "Minimize", onMouseDown: keepFocus, onClick: () => changeWindowState("minimized"), children: _jsx(InlineIcon, { icon: iconMinus }) })), maximizable && (_jsx("button", { type: "button", className: "nx-popup__control nx-popup__maximize", "aria-label": maximized ? "Restore down" : "Maximize", title: maximized ? "Restore down" : "Maximize", onMouseDown: keepFocus, onClick: toggleMaximize, children: _jsx(InlineIcon, { icon: maximized ? iconSquares : iconSquare }) })), closable && (_jsx("button", { type: "button", className: "nx-popup__control nx-popup__close", "aria-label": "Close", title: "Close", onClick: () => onClose("close"), children: _jsx(InlineIcon, { icon: iconX }) }))] }))] })), _jsx("div", { className: "nx-popup__body", children: children }), footer && _jsx("footer", { className: "nx-popup__foot", children: footer })] }, round)) }));
496
+ const element = (_jsx("dialog", { ref: dialogRef, className: cx("nx-popup", `nx-popup--${variant}`, `nx-popup--${size}`, docked && "nx-popup--docked", drawer && "nx-popup--drawer", flush && "nx-popup--flush", maximized && "nx-popup--maximized", movable && "nx-popup--movable", closing && "nx-popup--closing", className), "data-window-state": windowState, style: height === undefined ? undefined : { "--nx-popup-height": typeof height === "number" ? `${height}px` : height }, "aria-labelledby": title ? titleId : undefined, "aria-label": title ? undefined : ariaLabel, "aria-describedby": cx(description ? descriptionId : undefined, ariaDescribedBy) || undefined, onCancel: onCancel, onClose: onNativeClose, onPointerDown: event => {
497
+ raise();
498
+ onPointerDown(event);
499
+ }, onFocus: raise, onKeyDown: onKeyDown, onMouseDown: onMouseDown, onClick: onClick, children: shown && (_jsxs(HostedPopupContext, { value: false, children: [(title || icon || hasControls) && (_jsxs("header", { className: "nx-popup__head", onPointerDown: onHeadPointerDown, onDoubleClick: onHeadDoubleClick, children: [icon && _jsx("span", { className: "nx-popup__icon", children: icon }), _jsxs("div", { className: "nx-popup__heading", children: [title && (_jsx("h2", { id: titleId, className: "nx-popup__title", children: title })), description && (_jsx("p", { id: descriptionId, className: "nx-popup__description", children: description }))] }), hasControls && (_jsxs("div", { className: "nx-popup__controls", children: [canMinimize && (_jsx("button", { type: "button", className: "nx-popup__control nx-popup__minimize", "aria-label": "Minimize", title: "Minimize", onMouseDown: keepFocus, onClick: () => changeWindowState("minimized"), children: _jsx(InlineIcon, { icon: iconMinus }) })), maximizable && (_jsx("button", { type: "button", className: "nx-popup__control nx-popup__maximize", "aria-label": maximized ? "Restore down" : "Maximize", title: maximized ? "Restore down" : "Maximize", onMouseDown: keepFocus, onClick: toggleMaximize, children: _jsx(InlineIcon, { icon: maximized ? iconSquares : iconSquare }) })), closable && (_jsx("button", { type: "button", className: "nx-popup__control nx-popup__close", "aria-label": "Close", title: "Close", onClick: () => onClose("close"), children: _jsx(InlineIcon, { icon: iconX }) }))] }))] })), _jsx("div", { className: "nx-popup__body", children: children }), footer && _jsx("footer", { className: "nx-popup__foot", children: footer })] }, round)) }));
500
+ if (!docked)
501
+ return element;
502
+ return portalReady ? createPortal(element, document.body) : null;
401
503
  }
@@ -0,0 +1,50 @@
1
+ import { type ReactNode } from "react";
2
+ import { type MailClientView } from "../../Helpers/MailClientHelpers.ts";
3
+ import type { ChatId, ChatSource } from "../../Interfaces/ChatInterfaces.ts";
4
+ import type { MailDomain, MailInboxRow, MessageSource } from "../../Interfaces/MailInterfaces.ts";
5
+ import type { MailDomainContext } from "./MailThread.tsx";
6
+ export interface MailClientProps {
7
+ /** Where the mailbox's data comes from: one object for the page (the bell polls on it too). */
8
+ source: MessageSource;
9
+ /** Who is reading. */
10
+ userId: ChatId;
11
+ /**
12
+ * The bar's heading. Default "Message". `false` draws none, for a page whose own heading names the client (an admin
13
+ * page's title from its menu row): the search then starts the bar, and the page has one heading.
14
+ */
15
+ title?: ReactNode | false;
16
+ /** A sentence beside the title, clipped to the rail's width, the whole of it on hover: what a page's own heading would say. */
17
+ description?: ReactNode;
18
+ /** The title's heading level: 1 when the client takes the page's own title. Default 2. */
19
+ headingLevel?: 1 | 2 | 3;
20
+ /** The folder and type shown, when the page controls them (a URL); pair with `onViewChange`. */
21
+ view?: MailClientView;
22
+ /** The folder and type shown first, when the page does not control them. Default the Inbox, every type. */
23
+ defaultView?: MailClientView;
24
+ onViewChange?: (view: MailClientView) => void;
25
+ /**
26
+ * The conversation open in the pane, when the page controls it (`?thread=`); pair with `onThreadChange`. A chat named
27
+ * here (the bell's link, an e-mail copy's) opens its chat window too, as a click on its row does.
28
+ */
29
+ threadId?: ChatId | null;
30
+ onThreadChange?: (threadId: ChatId | null) => void;
31
+ /** At the end of the bar: the app's settings, a gear `Button` that links to its admin pages. */
32
+ settings?: ReactNode;
33
+ /** Conversations per page of the list. Default 50. */
34
+ pageSize?: number;
35
+ /** A chat's lines, when they do not come from the source's `threadSource`. */
36
+ chatSourceFor?: (row: MailInboxRow) => ChatSource;
37
+ /** Draws the record a mail is about. Default its `DomainCard`. */
38
+ renderDomain?: (domain: MailDomain, context: MailDomainContext) => ReactNode;
39
+ /**
40
+ * After anything that changes what is unread or where a conversation is - a read mark once the server answered it, a
41
+ * mark, an archive: a page's bell reads again (`refreshMessageUnread`). The client's own counts wait for every read
42
+ * mark on its way, so a page needs no wrapper of its own around the source for them.
43
+ */
44
+ onChanged?: () => void;
45
+ /** Milliseconds between quiet reads of the list and its counts while the page is in front. 0 turns it off. Default 60,000. */
46
+ pollInterval?: number;
47
+ className?: string;
48
+ }
49
+ /** A mailbox as a mail client: rail, list and reading pane, with chat popups and compose windows docked at the bottom right. */
50
+ export declare function MailClient({ source, userId, title, description, headingLevel, view: viewProp, defaultView, onViewChange, threadId: threadProp, onThreadChange, settings, pageSize, chatSourceFor, renderDomain, onChanged, pollInterval, className, }: MailClientProps): import("react").JSX.Element;