pixelize-design-library 3.0.0 → 3.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 (176) hide show
  1. package/CHANGELOG.md +64 -1
  2. package/COMPONENT-GOTCHAS.md +37 -13
  3. package/DESIGN-GUIDE.md +4 -1
  4. package/dist/Components/NotificationBell/NotificationBellProps.d.ts +3 -3
  5. package/dist/Components/NotificationBell/NotificationItem.d.ts +1 -1
  6. package/dist/Components/NotificationBell/NotificationItem.js +1 -1
  7. package/dist/Components/NotificationBell/NotificationList.d.ts +1 -1
  8. package/dist/Components/NotificationBell/NotificationList.js +1 -1
  9. package/dist/Components/NotificationBell/NotificationPanel.d.ts +1 -1
  10. package/dist/Components/NotificationBell/NotificationPanel.js +1 -1
  11. package/dist/Components/NotificationCenter/center/CenterPanel.d.ts +15 -0
  12. package/dist/Components/NotificationCenter/center/CenterPanel.js +87 -0
  13. package/dist/Components/NotificationCenter/center/NotificationCenter.d.ts +4 -0
  14. package/dist/Components/NotificationCenter/center/NotificationCenter.js +130 -0
  15. package/dist/Components/NotificationCenter/center/NotificationCenterProps.d.ts +71 -0
  16. package/dist/Components/NotificationCenter/center/NotificationCenterProps.js +2 -0
  17. package/dist/Components/NotificationCenter/center/PanelRows.d.ts +10 -0
  18. package/dist/Components/NotificationCenter/center/PanelRows.js +35 -0
  19. package/dist/Components/NotificationCenter/center/PanelStates.d.ts +17 -0
  20. package/dist/Components/NotificationCenter/center/PanelStates.js +21 -0
  21. package/dist/Components/NotificationCenter/center/centerLabels.d.ts +4 -0
  22. package/dist/Components/NotificationCenter/center/centerLabels.js +38 -0
  23. package/dist/Components/NotificationCenter/center/useArrivals.d.ts +14 -0
  24. package/dist/Components/NotificationCenter/center/useArrivals.js +71 -0
  25. package/dist/Components/NotificationCenter/center/useIsSheet.d.ts +2 -0
  26. package/dist/Components/NotificationCenter/center/useIsSheet.js +20 -0
  27. package/dist/Components/NotificationCenter/core/NotificationCenterProvider.d.ts +17 -0
  28. package/dist/Components/NotificationCenter/core/NotificationCenterProvider.js +46 -0
  29. package/dist/Components/NotificationCenter/core/NotificationEngineProps.d.ts +182 -0
  30. package/dist/Components/NotificationCenter/core/NotificationEngineProps.js +2 -0
  31. package/dist/Components/NotificationCenter/core/NotificationSourceProps.d.ts +284 -0
  32. package/dist/Components/NotificationCenter/core/NotificationSourceProps.js +2 -0
  33. package/dist/Components/NotificationCenter/core/actionError.d.ts +4 -0
  34. package/dist/Components/NotificationCenter/core/actionError.js +45 -0
  35. package/dist/Components/NotificationCenter/core/format.d.ts +4 -0
  36. package/dist/Components/NotificationCenter/core/format.js +91 -0
  37. package/dist/Components/NotificationCenter/core/grouping.d.ts +14 -0
  38. package/dist/Components/NotificationCenter/core/grouping.js +102 -0
  39. package/dist/Components/NotificationCenter/core/labels.d.ts +6 -0
  40. package/dist/Components/NotificationCenter/core/labels.js +94 -0
  41. package/dist/Components/NotificationCenter/core/overlay.d.ts +109 -0
  42. package/dist/Components/NotificationCenter/core/overlay.js +281 -0
  43. package/dist/Components/NotificationCenter/core/useNotificationEngine.d.ts +13 -0
  44. package/dist/Components/NotificationCenter/core/useNotificationEngine.js +415 -0
  45. package/dist/Components/NotificationCenter/inbox/InboxList.d.ts +17 -0
  46. package/dist/Components/NotificationCenter/inbox/InboxList.js +25 -0
  47. package/dist/Components/NotificationCenter/inbox/InboxStates.d.ts +17 -0
  48. package/dist/Components/NotificationCenter/inbox/InboxStates.js +27 -0
  49. package/dist/Components/NotificationCenter/inbox/NotificationInbox.d.ts +4 -0
  50. package/dist/Components/NotificationCenter/inbox/NotificationInbox.js +434 -0
  51. package/dist/Components/NotificationCenter/inbox/NotificationInboxProps.d.ts +106 -0
  52. package/dist/Components/NotificationCenter/inbox/NotificationInboxProps.js +2 -0
  53. package/dist/Components/NotificationCenter/inbox/ShortcutsDialog.d.ts +8 -0
  54. package/dist/Components/NotificationCenter/inbox/ShortcutsDialog.js +63 -0
  55. package/dist/Components/NotificationCenter/inbox/inboxLabels.d.ts +4 -0
  56. package/dist/Components/NotificationCenter/inbox/inboxLabels.js +56 -0
  57. package/dist/Components/NotificationCenter/inbox/inboxModel.d.ts +17 -0
  58. package/dist/Components/NotificationCenter/inbox/inboxModel.js +47 -0
  59. package/dist/Components/NotificationCenter/inbox/useControllable.d.ts +1 -0
  60. package/dist/Components/NotificationCenter/inbox/useControllable.js +15 -0
  61. package/dist/Components/NotificationCenter/index.d.ts +8 -0
  62. package/dist/Components/NotificationCenter/index.js +26 -0
  63. package/dist/Components/NotificationCenter/preferences/NotificationPreferences.d.ts +4 -0
  64. package/dist/Components/NotificationCenter/preferences/NotificationPreferences.js +72 -0
  65. package/dist/Components/NotificationCenter/preferences/NotificationPreferencesProps.d.ts +111 -0
  66. package/dist/Components/NotificationCenter/preferences/NotificationPreferencesProps.js +2 -0
  67. package/dist/Components/NotificationCenter/preferences/PreferenceCells.d.ts +16 -0
  68. package/dist/Components/NotificationCenter/preferences/PreferenceCells.js +35 -0
  69. package/dist/Components/NotificationCenter/preferences/PreferenceGroups.d.ts +15 -0
  70. package/dist/Components/NotificationCenter/preferences/PreferenceGroups.js +26 -0
  71. package/dist/Components/NotificationCenter/preferences/preferencesLabels.d.ts +2 -0
  72. package/dist/Components/NotificationCenter/preferences/preferencesLabels.js +28 -0
  73. package/dist/Components/NotificationCenter/preferences/useAutosave.d.ts +8 -0
  74. package/dist/Components/NotificationCenter/preferences/useAutosave.js +41 -0
  75. package/dist/Components/NotificationCenter/row/NotificationDetail.d.ts +4 -0
  76. package/dist/Components/NotificationCenter/row/NotificationDetail.js +65 -0
  77. package/dist/Components/NotificationCenter/row/NotificationDetailProps.d.ts +15 -0
  78. package/dist/Components/NotificationCenter/row/NotificationDetailProps.js +2 -0
  79. package/dist/Components/NotificationCenter/row/NotificationRow.d.ts +3 -0
  80. package/dist/Components/NotificationCenter/row/NotificationRow.js +124 -0
  81. package/dist/Components/NotificationCenter/row/NotificationRowProps.d.ts +33 -0
  82. package/dist/Components/NotificationCenter/row/NotificationRowProps.js +2 -0
  83. package/dist/Components/NotificationCenter/row/ReasonForm.d.ts +14 -0
  84. package/dist/Components/NotificationCenter/row/ReasonForm.js +59 -0
  85. package/dist/Components/NotificationCenter/row/RowActions.d.ts +16 -0
  86. package/dist/Components/NotificationCenter/row/RowActions.js +36 -0
  87. package/dist/Components/NotificationCenter/row/RowQuickActions.d.ts +17 -0
  88. package/dist/Components/NotificationCenter/row/RowQuickActions.js +57 -0
  89. package/dist/Components/NotificationCenter/row/RowStatus.d.ts +10 -0
  90. package/dist/Components/NotificationCenter/row/RowStatus.js +42 -0
  91. package/dist/Components/NotificationCenter/row/category.d.ts +8 -0
  92. package/dist/Components/NotificationCenter/row/category.js +13 -0
  93. package/dist/Components/NotificationCenter/row/rowMenu.d.ts +14 -0
  94. package/dist/Components/NotificationCenter/row/rowMenu.js +37 -0
  95. package/dist/Components/NotificationCenter/row/rowParts.d.ts +40 -0
  96. package/dist/Components/NotificationCenter/row/rowParts.js +72 -0
  97. package/dist/Components/NotificationCenter/row/rowStyles.d.ts +11 -0
  98. package/dist/Components/NotificationCenter/row/rowStyles.js +25 -0
  99. package/dist/Components/SegmentedControl/segmentedInk.d.ts +4 -0
  100. package/dist/Components/SegmentedControl/segmentedInk.js +16 -2
  101. package/dist/Components/SideBar/SecondaryBar.js +2 -2
  102. package/dist/Components/SideBar/SideBar.js +18 -8
  103. package/dist/Components/SideBar/SideBarProps.d.ts +4 -2
  104. package/dist/Components/SideBar/components/MenuItemBox.d.ts +0 -2
  105. package/dist/Components/SideBar/components/MenuItemBox.js +2 -3
  106. package/dist/Components/SideBar/components/MenuItems.js +2 -2
  107. package/dist/Components/SideBar/components/PinnedSection.js +3 -4
  108. package/dist/Components/SideBar/sidebarScale.d.ts +0 -2
  109. package/dist/Components/SideBar/sidebarScale.js +0 -2
  110. package/dist/Components/Switch/Switch.d.ts +1 -1
  111. package/dist/Components/Switch/Switch.js +2 -2
  112. package/dist/Components/Switch/SwitchProps.d.ts +3 -0
  113. package/dist/Components/Toggle/TableToggle.js +4 -5
  114. package/dist/Components/Toggle/tableToggleInk.d.ts +7 -0
  115. package/dist/Components/Toggle/tableToggleInk.js +28 -0
  116. package/dist/esm/Components/NotificationBell/NotificationItem.js +1 -1
  117. package/dist/esm/Components/NotificationBell/NotificationList.js +1 -1
  118. package/dist/esm/Components/NotificationBell/NotificationPanel.js +1 -1
  119. package/dist/esm/Components/NotificationCenter/center/CenterPanel.js +81 -0
  120. package/dist/esm/Components/NotificationCenter/center/NotificationCenter.js +92 -0
  121. package/dist/esm/Components/NotificationCenter/center/NotificationCenterProps.js +1 -0
  122. package/dist/esm/Components/NotificationCenter/center/PanelRows.js +28 -0
  123. package/dist/esm/Components/NotificationCenter/center/PanelStates.js +11 -0
  124. package/dist/esm/Components/NotificationCenter/center/centerLabels.js +34 -0
  125. package/dist/esm/Components/NotificationCenter/center/useArrivals.js +67 -0
  126. package/dist/esm/Components/NotificationCenter/center/useIsSheet.js +16 -0
  127. package/dist/esm/Components/NotificationCenter/core/NotificationCenterProvider.js +35 -0
  128. package/dist/esm/Components/NotificationCenter/core/NotificationEngineProps.js +1 -0
  129. package/dist/esm/Components/NotificationCenter/core/NotificationSourceProps.js +1 -0
  130. package/dist/esm/Components/NotificationCenter/core/actionError.js +39 -0
  131. package/dist/esm/Components/NotificationCenter/core/format.js +87 -0
  132. package/dist/esm/Components/NotificationCenter/core/grouping.js +88 -0
  133. package/dist/esm/Components/NotificationCenter/core/labels.js +88 -0
  134. package/dist/esm/Components/NotificationCenter/core/overlay.js +274 -0
  135. package/dist/esm/Components/NotificationCenter/core/useNotificationEngine.js +408 -0
  136. package/dist/esm/Components/NotificationCenter/inbox/InboxList.js +18 -0
  137. package/dist/esm/Components/NotificationCenter/inbox/InboxStates.js +18 -0
  138. package/dist/esm/Components/NotificationCenter/inbox/NotificationInbox.js +396 -0
  139. package/dist/esm/Components/NotificationCenter/inbox/NotificationInboxProps.js +1 -0
  140. package/dist/esm/Components/NotificationCenter/inbox/ShortcutsDialog.js +23 -0
  141. package/dist/esm/Components/NotificationCenter/inbox/inboxLabels.js +52 -0
  142. package/dist/esm/Components/NotificationCenter/inbox/inboxModel.js +37 -0
  143. package/dist/esm/Components/NotificationCenter/inbox/useControllable.js +11 -0
  144. package/dist/esm/Components/NotificationCenter/index.js +8 -0
  145. package/dist/esm/Components/NotificationCenter/preferences/NotificationPreferences.js +67 -0
  146. package/dist/esm/Components/NotificationCenter/preferences/NotificationPreferencesProps.js +1 -0
  147. package/dist/esm/Components/NotificationCenter/preferences/PreferenceCells.js +27 -0
  148. package/dist/esm/Components/NotificationCenter/preferences/PreferenceGroups.js +21 -0
  149. package/dist/esm/Components/NotificationCenter/preferences/preferencesLabels.js +24 -0
  150. package/dist/esm/Components/NotificationCenter/preferences/useAutosave.js +37 -0
  151. package/dist/esm/Components/NotificationCenter/row/NotificationDetail.js +60 -0
  152. package/dist/esm/Components/NotificationCenter/row/NotificationDetailProps.js +1 -0
  153. package/dist/esm/Components/NotificationCenter/row/NotificationRow.js +119 -0
  154. package/dist/esm/Components/NotificationCenter/row/NotificationRowProps.js +1 -0
  155. package/dist/esm/Components/NotificationCenter/row/ReasonForm.js +52 -0
  156. package/dist/esm/Components/NotificationCenter/row/RowActions.js +28 -0
  157. package/dist/esm/Components/NotificationCenter/row/RowQuickActions.js +49 -0
  158. package/dist/esm/Components/NotificationCenter/row/RowStatus.js +35 -0
  159. package/dist/esm/Components/NotificationCenter/row/category.js +9 -0
  160. package/dist/esm/Components/NotificationCenter/row/rowMenu.js +30 -0
  161. package/dist/esm/Components/NotificationCenter/row/rowParts.js +59 -0
  162. package/dist/esm/Components/NotificationCenter/row/rowStyles.js +19 -0
  163. package/dist/esm/Components/SegmentedControl/segmentedInk.js +15 -2
  164. package/dist/esm/Components/SideBar/SecondaryBar.js +3 -3
  165. package/dist/esm/Components/SideBar/SideBar.js +18 -8
  166. package/dist/esm/Components/SideBar/components/MenuItemBox.js +2 -3
  167. package/dist/esm/Components/SideBar/components/MenuItems.js +2 -2
  168. package/dist/esm/Components/SideBar/components/PinnedSection.js +3 -4
  169. package/dist/esm/Components/SideBar/sidebarScale.js +0 -2
  170. package/dist/esm/Components/Switch/Switch.js +2 -2
  171. package/dist/esm/Components/Toggle/TableToggle.js +4 -5
  172. package/dist/esm/Components/Toggle/tableToggleInk.js +24 -0
  173. package/dist/esm/index.js +1 -0
  174. package/dist/index.d.ts +8 -0
  175. package/dist/index.js +16 -3
  176. package/package.json +1 -1
@@ -0,0 +1,415 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.useNotificationEngine = exports.CLOCK_TICK_MS = exports.ARCHIVE_FOLD_MS = exports.DECISION_FOLD_MS = exports.UNDO_WINDOW_MS = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = require("react");
9
+ const lucide_react_1 = require("lucide-react");
10
+ const Button_1 = __importDefault(require("../../Button/Button"));
11
+ const Toaster_1 = require("../../Toaster/Toaster");
12
+ const controlSizes_1 = require("../../../Theme/tokens/primitives/controlSizes");
13
+ const actionError_1 = require("./actionError");
14
+ const format_1 = require("./format");
15
+ const grouping_1 = require("./grouping");
16
+ const labels_1 = require("./labels");
17
+ const overlay_1 = require("./overlay");
18
+ exports.UNDO_WINDOW_MS = 6000;
19
+ exports.DECISION_FOLD_MS = 900;
20
+ exports.ARCHIVE_FOLD_MS = 220;
21
+ exports.CLOCK_TICK_MS = 60000;
22
+ const TOAST_POSITION = "bottom-right";
23
+ const ZERO_WIDTH_SPACE = String.fromCodePoint(0x200b);
24
+ const unique = (ids) => [...new Set(ids)];
25
+ const hasActionId = (entry, actionId) => Boolean(entry.actions && entry.actions.some((action) => action.id === actionId));
26
+ const isLive = (item) => item !== undefined;
27
+ const createEngineActions = (ctx) => {
28
+ const { latest, windows, undoOrder, mounted, batches, dispatch, announce } = ctx;
29
+ const nextBatch = () => {
30
+ batches.current += 1;
31
+ return `batch-${batches.current}`;
32
+ };
33
+ const toast = (props) => latest.current.toaster.showToast({ position: TOAST_POSITION, ...props });
34
+ const closeWindow = (win) => {
35
+ win.timers.forEach(clearTimeout);
36
+ if (win.toastId !== undefined)
37
+ latest.current.toaster.close(win.toastId);
38
+ windows.current.delete(win.batch);
39
+ undoOrder.current = undoOrder.current.filter((batch) => batch !== win.batch);
40
+ };
41
+ const sendWindow = (batch) => {
42
+ const win = windows.current.get(batch);
43
+ if (!win)
44
+ return;
45
+ closeWindow(win);
46
+ dispatch({ type: "fold", batch });
47
+ dispatch({ type: "send", batch });
48
+ win.send();
49
+ };
50
+ const undoWindow = (batch) => {
51
+ const win = windows.current.get(batch);
52
+ if (!win)
53
+ return false;
54
+ closeWindow(win);
55
+ dispatch({ type: "drop", batch });
56
+ const { labels } = latest.current;
57
+ announce(win.kind === "decision" ? labels.decisionUndone : labels.undone);
58
+ return true;
59
+ };
60
+ const openWindow = (win, foldAfter, props) => {
61
+ const timers = [];
62
+ if (foldAfter !== null)
63
+ timers.push(setTimeout(() => dispatch({ type: "fold", batch: win.batch }), foldAfter));
64
+ timers.push(setTimeout(() => sendWindow(win.batch), exports.UNDO_WINDOW_MS));
65
+ const record = { ...win, timers };
66
+ windows.current.set(win.batch, record);
67
+ undoOrder.current = [...undoOrder.current, win.batch];
68
+ const { labels } = latest.current;
69
+ record.toastId = toast({
70
+ ...props,
71
+ duration: null,
72
+ actions: () => ((0, jsx_runtime_1.jsx)(Button_1.default, { size: "xs", variant: "outline", colorScheme: "gray", label: labels.undo, onClick: () => undoWindow(win.batch) })),
73
+ });
74
+ };
75
+ const sendThen = (batch, call, failTitle) => {
76
+ Promise.resolve()
77
+ .then(call)
78
+ .then(() => {
79
+ if (mounted.current)
80
+ dispatch({ type: "settle", batch });
81
+ }, () => {
82
+ if (!mounted.current)
83
+ return;
84
+ dispatch({ type: "drop", batch });
85
+ toast({ status: "error", title: failTitle });
86
+ });
87
+ };
88
+ const retryButton = (ids) => {
89
+ const renderRetry = (onClose) => ((0, jsx_runtime_1.jsx)(Button_1.default, { size: "xs", variant: "outline", colorScheme: "gray", leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCw, { size: controlSizes_1.BUTTON_SIZES.xs.iconSize, "aria-hidden": "true" }), label: latest.current.labels.retry, onClick: () => {
90
+ onClose();
91
+ retryMany(ids);
92
+ } }));
93
+ return renderRetry;
94
+ };
95
+ const finishDecisions = (batch, decision, results) => {
96
+ var _a, _b;
97
+ if (!mounted.current)
98
+ return;
99
+ const { labels } = latest.current;
100
+ const outcomes = {};
101
+ results.forEach((result) => {
102
+ outcomes[result.id] = { batch, error: result.error };
103
+ });
104
+ dispatch({ type: "settleDecisions", outcomes });
105
+ const ofKind = (kind) => results.filter((result) => { var _a; return ((_a = result.error) === null || _a === void 0 ? void 0 : _a.kind) === kind; });
106
+ const soleMessage = (list) => { var _a; return (list.length === 1 ? (_a = list[0].error) === null || _a === void 0 ? void 0 : _a.message : undefined); };
107
+ const failed = ofKind("failed");
108
+ const conflicts = ofKind("conflict");
109
+ const forbidden = ofKind("forbidden");
110
+ const needsReason = ofKind("reasonRequired");
111
+ if (failed.length) {
112
+ toast({
113
+ status: "error",
114
+ title: labels.decisionFailed(decision.label, failed.length),
115
+ description: (_a = soleMessage(failed)) !== null && _a !== void 0 ? _a : labels.failedMessage,
116
+ actions: retryButton(failed.map((result) => result.id)),
117
+ });
118
+ }
119
+ if (conflicts.length) {
120
+ const title = labels.conflicted(conflicts.length);
121
+ toast({ status: "info", title, description: soleMessage(conflicts) });
122
+ announce(title);
123
+ }
124
+ if (forbidden.length) {
125
+ toast({ status: "error", title: labels.forbidden(forbidden.length), description: soleMessage(forbidden) });
126
+ }
127
+ if (needsReason.length) {
128
+ const title = labels.reasonNeeded(decision.label);
129
+ toast({ status: "warning", title, description: (_b = soleMessage(needsReason)) !== null && _b !== void 0 ? _b : labels.reasonRequiredMessage });
130
+ announce(title);
131
+ }
132
+ };
133
+ const sendDecisions = (batch, targets, decision) => {
134
+ const { onAction } = latest.current.source;
135
+ if (!onAction || !targets.length)
136
+ return;
137
+ const sends = targets.map((entry) => {
138
+ const action = entry.actions && entry.actions.find((candidate) => candidate.id === decision.actionId);
139
+ if (!action)
140
+ return Promise.resolve({ id: entry.id, error: { kind: "conflict" } });
141
+ return Promise.resolve()
142
+ .then(() => onAction(entry, action, decision.reason))
143
+ .then(() => ({ id: entry.id }), (error) => ({ id: entry.id, error: (0, actionError_1.toNotificationActionError)(error) }));
144
+ });
145
+ Promise.all(sends).then((results) => finishDecisions(batch, decision, results));
146
+ };
147
+ const retryMany = (ids) => {
148
+ const { source, live } = latest.current;
149
+ if (source.isOffline)
150
+ return;
151
+ const byDecision = new Map();
152
+ unique(ids)
153
+ .map((id) => live.byId.get(id))
154
+ .filter(isLive)
155
+ .forEach(({ entry, state }) => {
156
+ if (state.status !== "failed" || !state.decision)
157
+ return;
158
+ const group = byDecision.get(state.decision);
159
+ if (group)
160
+ group.push(entry);
161
+ else
162
+ byDecision.set(state.decision, [entry]);
163
+ });
164
+ byDecision.forEach((group, decision) => {
165
+ const batch = nextBatch();
166
+ dispatch({ type: "retry", batch, ids: group.map((entry) => entry.id) });
167
+ sendDecisions(batch, group, decision);
168
+ });
169
+ };
170
+ const setRead = (ids, read) => {
171
+ const { source, live, labels } = latest.current;
172
+ const { onMarkRead } = source;
173
+ if (!onMarkRead || source.isOffline)
174
+ return;
175
+ const targets = unique(ids).filter((id) => {
176
+ const item = live.byId.get(id);
177
+ return item !== undefined && item.entry.isRead !== undefined && item.entry.isRead !== read;
178
+ });
179
+ if (!targets.length)
180
+ return;
181
+ const batch = nextBatch();
182
+ dispatch({ type: "flag", key: "read", batch, ids: targets, value: read, phase: "sending" });
183
+ sendThen(batch, () => onMarkRead(targets, read), labels.readFailed(read));
184
+ };
185
+ const actions = {
186
+ open: (entry, event, action) => {
187
+ const { source, live } = latest.current;
188
+ const item = live.byId.get(entry.id);
189
+ if (item && item.entry.isRead === false)
190
+ setRead([entry.id], true);
191
+ source.onOpen(action && action.href ? { ...entry, href: action.href } : entry, event, action);
192
+ },
193
+ decide: (ids, action, reason) => {
194
+ var _a, _b;
195
+ const { source, live, labels } = latest.current;
196
+ if (!source.onAction || source.isOffline)
197
+ return;
198
+ const items = unique(ids)
199
+ .map((id) => live.byId.get(id))
200
+ .filter(isLive)
201
+ .filter((item) => item.state.status === "open" && hasActionId(item.entry, action.id));
202
+ if (!items.length)
203
+ return;
204
+ const trimmed = reason === undefined ? "" : reason.trim();
205
+ const decision = {
206
+ actionId: action.id,
207
+ label: action.label,
208
+ doneLabel: (_a = action.doneLabel) !== null && _a !== void 0 ? _a : action.label,
209
+ intent: (_b = action.intent) !== null && _b !== void 0 ? _b : "neutral",
210
+ reason: trimmed ? trimmed : undefined,
211
+ };
212
+ const targetIds = items.map((item) => item.entry.id);
213
+ const batch = nextBatch();
214
+ dispatch({ type: "decide", batch, ids: targetIds, decision });
215
+ const subject = items.length > 1 ? labels.requests(items.length) : items[0].entry.subject;
216
+ announce(labels.decidedAnnouncement(decision.doneLabel, subject));
217
+ const decided = items.map((item) => item.entry);
218
+ openWindow({ batch, kind: "decision", ids: targetIds, send: () => sendDecisions(batch, decided, decision) }, exports.DECISION_FOLD_MS, {
219
+ status: decision.intent === "negative" ? "info" : "success",
220
+ title: labels.decided(decision.doneLabel, subject),
221
+ description: decision.reason ? labels.reasonQuote(decision.reason) : undefined,
222
+ });
223
+ },
224
+ undo: (id) => {
225
+ const hit = [...windows.current.values()].find((win) => win.ids.includes(id));
226
+ if (hit)
227
+ undoWindow(hit.batch);
228
+ },
229
+ undoLast: () => {
230
+ const batch = undoOrder.current[undoOrder.current.length - 1];
231
+ return batch === undefined ? false : undoWindow(batch);
232
+ },
233
+ retry: (id) => retryMany([id]),
234
+ dismiss: (id) => dispatch({ type: "dismiss", id }),
235
+ clearReasonRequest: (id) => dispatch({ type: "clearReasonRequest", id }),
236
+ setRead,
237
+ markAllRead: (scope) => {
238
+ const { source, live, labels, counts } = latest.current;
239
+ const { onMarkAllRead } = source;
240
+ if (!onMarkAllRead || source.isOffline)
241
+ return;
242
+ const query = scope.query ? scope.query.trim() : "";
243
+ const ids = live.lists[scope.zone]
244
+ .filter((item) => item.entry.isRead === false &&
245
+ !item.state.isHidden &&
246
+ (!scope.category || item.entry.category === scope.category) &&
247
+ (!query || (0, grouping_1.matchesNotificationQuery)(item.entry, query)))
248
+ .map((item) => item.entry.id);
249
+ const wholeActivity = scope.zone === "activity" && !scope.category && !query;
250
+ const count = wholeActivity ? Math.max(ids.length, counts.unread) : ids.length;
251
+ if (!count)
252
+ return;
253
+ const batch = nextBatch();
254
+ dispatch({ type: "markAll", batch, scope, ids });
255
+ const title = labels.markedRead(count);
256
+ announce(title);
257
+ openWindow({ batch, kind: "markAll", ids, send: () => sendThen(batch, () => onMarkAllRead(scope), labels.markAllFailed) }, null, { status: "success", title });
258
+ },
259
+ archive: (ids) => {
260
+ const { source, live, labels } = latest.current;
261
+ const { onArchive } = source;
262
+ if (!onArchive || source.isOffline)
263
+ return;
264
+ const items = unique(ids)
265
+ .map((id) => live.byId.get(id))
266
+ .filter(isLive)
267
+ .filter((item) => item.state.zone === "activity" && !item.state.isHidden);
268
+ if (!items.length)
269
+ return;
270
+ const targetIds = items.map((item) => item.entry.id);
271
+ const batch = nextBatch();
272
+ dispatch({ type: "flag", key: "archived", batch, ids: targetIds, value: true, phase: "undo" });
273
+ const title = labels.archived(targetIds.length);
274
+ announce(title);
275
+ openWindow({ batch, kind: "archive", ids: targetIds, send: () => sendThen(batch, () => onArchive(targetIds), labels.archiveFailed) }, exports.ARCHIVE_FOLD_MS, { status: "success", title, description: targetIds.length === 1 ? (0, grouping_1.plainTitle)(items[0].entry.title) : undefined });
276
+ },
277
+ restore: (ids) => {
278
+ const { source, live, labels } = latest.current;
279
+ const { onRestore } = source;
280
+ if (!onRestore || source.isOffline)
281
+ return;
282
+ const targets = unique(ids).filter((id) => {
283
+ const item = live.byId.get(id);
284
+ return item !== undefined && item.state.zone === "archived";
285
+ });
286
+ if (!targets.length)
287
+ return;
288
+ const batch = nextBatch();
289
+ dispatch({ type: "flag", key: "archived", batch, ids: targets, value: false, phase: "sending" });
290
+ toast({ status: "success", title: labels.restored });
291
+ announce(labels.restored);
292
+ sendThen(batch, () => onRestore(targets), labels.restoreFailed);
293
+ },
294
+ extraActionsFor: (entry) => {
295
+ const { extraActions } = latest.current.source;
296
+ return extraActions ? extraActions(entry) : [];
297
+ },
298
+ runExtraAction: (entry, extra) => {
299
+ const { labels } = latest.current;
300
+ Promise.resolve()
301
+ .then(() => extra.onSelect(entry))
302
+ .then(() => {
303
+ if (mounted.current && extra.doneLabel)
304
+ toast({ status: "success", title: extra.doneLabel });
305
+ }, () => {
306
+ if (mounted.current)
307
+ toast({ status: "error", title: labels.extraFailed(extra.label) });
308
+ });
309
+ },
310
+ loadMore: () => {
311
+ const { onLoadMore } = latest.current.source;
312
+ if (onLoadMore)
313
+ onLoadMore();
314
+ },
315
+ retryLoad: () => {
316
+ const { onRetry } = latest.current.source;
317
+ if (onRetry)
318
+ onRetry();
319
+ },
320
+ announce,
321
+ flush: () => {
322
+ [...windows.current.keys()].forEach(sendWindow);
323
+ },
324
+ };
325
+ return actions;
326
+ };
327
+ const useNotificationEngine = (source) => {
328
+ const { entries, counts, categories, locale = labels_1.DEFAULT_NOTIFICATION_LOCALE, labels: labelOverrides, status = "ready", isOffline = false, hasMore = false, isLoadingMore = false, loadedAt, appName, } = source;
329
+ const toaster = (0, Toaster_1.useToaster)();
330
+ const [overlay, dispatch] = (0, react_1.useReducer)(overlay_1.overlayReducer, overlay_1.EMPTY_OVERLAY);
331
+ const [announcement, setAnnouncement] = (0, react_1.useState)("");
332
+ const [now, setNow] = (0, react_1.useState)(() => Date.now());
333
+ const cache = (0, react_1.useRef)(new WeakMap());
334
+ const labels = (0, react_1.useMemo)(() => (0, labels_1.resolveNotificationLabels)(locale, labelOverrides), [locale, labelOverrides]);
335
+ const format = (0, react_1.useMemo)(() => (0, format_1.createNotificationFormatter)(locale, labels), [locale, labels]);
336
+ const live = (0, react_1.useMemo)(() => (0, overlay_1.buildLiveLists)(entries, overlay, categories, cache.current), [entries, overlay, categories]);
337
+ const { needsYou: serverNeedsYou, unread: serverUnread } = counts;
338
+ const liveCounts = (0, react_1.useMemo)(() => (0, overlay_1.optimisticCounts)(live, overlay, categories, { needsYou: serverNeedsYou, unread: serverUnread }), [live, overlay, categories, serverNeedsYou, serverUnread]);
339
+ const latest = (0, react_1.useRef)({ source, labels, live, counts: liveCounts, toaster });
340
+ (0, react_1.useLayoutEffect)(() => {
341
+ latest.current = { source, labels, live, counts: liveCounts, toaster };
342
+ });
343
+ const windows = (0, react_1.useRef)(new Map());
344
+ const undoOrder = (0, react_1.useRef)([]);
345
+ const mounted = (0, react_1.useRef)(true);
346
+ const batches = (0, react_1.useRef)(0);
347
+ const flip = (0, react_1.useRef)(false);
348
+ const announce = (0, react_1.useCallback)((message) => {
349
+ flip.current = !flip.current;
350
+ setAnnouncement(flip.current ? `${message}${ZERO_WIDTH_SPACE}` : message);
351
+ }, []);
352
+ const actions = (0, react_1.useMemo)(() => createEngineActions({ latest, windows, undoOrder, mounted, batches, dispatch, announce }), [announce]);
353
+ const find = (0, react_1.useCallback)((id) => live.byId.get(id), [live]);
354
+ (0, react_1.useEffect)(() => {
355
+ mounted.current = true;
356
+ return () => {
357
+ mounted.current = false;
358
+ actions.flush();
359
+ };
360
+ }, [actions]);
361
+ (0, react_1.useEffect)(() => {
362
+ const onPageHide = () => actions.flush();
363
+ const onVisibility = () => {
364
+ if (document.visibilityState === "hidden")
365
+ actions.flush();
366
+ };
367
+ window.addEventListener("pagehide", onPageHide);
368
+ document.addEventListener("visibilitychange", onVisibility);
369
+ return () => {
370
+ window.removeEventListener("pagehide", onPageHide);
371
+ document.removeEventListener("visibilitychange", onVisibility);
372
+ };
373
+ }, [actions]);
374
+ (0, react_1.useEffect)(() => {
375
+ const id = setInterval(() => setNow(Date.now()), exports.CLOCK_TICK_MS);
376
+ return () => clearInterval(id);
377
+ }, []);
378
+ const countsKey = `${serverNeedsYou}|${serverUnread}`;
379
+ const lastCounts = (0, react_1.useRef)(countsKey);
380
+ (0, react_1.useEffect)(() => {
381
+ if (lastCounts.current === countsKey)
382
+ return;
383
+ lastCounts.current = countsKey;
384
+ dispatch({ type: "countsChanged" });
385
+ }, [countsKey]);
386
+ (0, react_1.useEffect)(() => {
387
+ dispatch({ type: "reconcile", entries });
388
+ }, [entries]);
389
+ const seen = (0, react_1.useRef)(null);
390
+ (0, react_1.useEffect)(() => {
391
+ if (status !== "ready")
392
+ return;
393
+ const newest = entries.reduce((max, entry) => Math.max(max, entry.createdAt), 0);
394
+ const known = seen.current;
395
+ seen.current = { ids: new Set(entries.map((entry) => entry.id)), newest: Math.max(newest, known ? known.newest : 0) };
396
+ if (!known)
397
+ return;
398
+ const fresh = entries.filter((entry) => !known.ids.has(entry.id) && entry.createdAt > known.newest && !entry.archived);
399
+ if (fresh.length === 1)
400
+ announce(labels.arrivedOne((0, grouping_1.plainTitle)(fresh[0].title)));
401
+ else if (fresh.length > 1)
402
+ announce(labels.arrivedMany(fresh.length));
403
+ }, [entries, status, labels, announce]);
404
+ const decide = Boolean(source.onAction);
405
+ const markRead = Boolean(source.onMarkRead);
406
+ const markAllRead = Boolean(source.onMarkAllRead);
407
+ const archive = Boolean(source.onArchive);
408
+ const restore = Boolean(source.onRestore);
409
+ const loadMore = Boolean(source.onLoadMore);
410
+ const features = (0, react_1.useMemo)(() => ({ decide, markRead, markAllRead, archive, restore, loadMore }), [decide, markRead, markAllRead, archive, restore, loadMore]);
411
+ const shared = (0, react_1.useMemo)(() => ({ labels, format, now, categories, features, isOffline, appName, actions }), [labels, format, now, categories, features, isOffline, appName, actions]);
412
+ const value = (0, react_1.useMemo)(() => ({ ...shared, lists: live.lists, counts: liveCounts, status, loadedAt, hasMore, isLoadingMore, find }), [shared, live, liveCounts, status, loadedAt, hasMore, isLoadingMore, find]);
413
+ return { value, shared, announcement };
414
+ };
415
+ exports.useNotificationEngine = useNotificationEngine;
@@ -0,0 +1,17 @@
1
+ import type { RefCallback } from "react";
2
+ import type { LiveNotificationEntry, NotificationGroup, NotificationThread } from "../core/NotificationEngineProps";
3
+ import type { NotificationRowOpenEvent } from "../row/NotificationRowProps";
4
+ interface InboxListProps {
5
+ groups: NotificationGroup[];
6
+ isPhone: boolean;
7
+ selectedId: string | null;
8
+ checked: ReadonlySet<string>;
9
+ openThreads: ReadonlySet<string>;
10
+ idPrefix: string;
11
+ onOpenRow: (item: LiveNotificationEntry, event: NotificationRowOpenEvent) => void;
12
+ onCheckedChange?: (item: LiveNotificationEntry, checked: boolean) => void;
13
+ onThreadToggle: (thread: NotificationThread) => void;
14
+ linkRefFor: (id: string) => RefCallback<HTMLElement>;
15
+ }
16
+ export declare const InboxList: ({ groups, isPhone, selectedId, checked, openThreads, idPrefix, onOpenRow, onCheckedChange, onThreadToggle, linkRefFor, }: InboxListProps) => import("react/jsx-runtime").JSX.Element;
17
+ export {};
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InboxList = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = require("@chakra-ui/react");
9
+ const Primitives_1 = require("../../Primitives/Primitives");
10
+ const useCustomTheme_1 = require("../../../Theme/useCustomTheme");
11
+ const NotificationRow_1 = __importDefault(require("../row/NotificationRow"));
12
+ const InboxList = ({ groups, isPhone, selectedId, checked, openThreads, idPrefix, onOpenRow, onCheckedChange, onThreadToggle, linkRefFor, }) => {
13
+ const { colors } = (0, useCustomTheme_1.useCustomTheme)();
14
+ const variant = isPhone ? "phone" : "inbox";
15
+ const rowFor = (item, extra) => ((0, jsx_runtime_1.jsx)(NotificationRow_1.default, { item: item, variant: variant, isSelected: !isPhone && selectedId === item.entry.id, isChecked: checked.has(item.entry.id), onCheckedChange: onCheckedChange, thread: extra.thread, isThreadOpen: extra.isOpen, onThreadToggle: extra.thread ? onThreadToggle : undefined, isThreadChild: extra.isChild, onOpen: onOpenRow, linkRef: linkRefFor(item.entry.id) }, item.entry.id));
16
+ return ((0, jsx_runtime_1.jsx)(Primitives_1.Box, { pb: 2, children: groups.map((group) => {
17
+ const headingId = `${idPrefix}-group-${group.key}`;
18
+ return ((0, jsx_runtime_1.jsxs)(react_1.chakra.section, { "aria-labelledby": group.label === null ? undefined : headingId, children: [group.label !== null && ((0, jsx_runtime_1.jsxs)(react_1.chakra.h2, { id: headingId, position: "sticky", top: 0, zIndex: "docked", display: "flex", alignItems: "center", gap: 1.5, m: 0, px: 4, pt: 3, pb: 1.5, bg: colors.surfaceRaised, color: colors.textMuted, fontSize: "xs-minus", fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase", children: [(0, jsx_runtime_1.jsx)("span", { children: group.label }), group.count > 0 && (0, jsx_runtime_1.jsx)(react_1.chakra.span, { sx: { fontVariantNumeric: "tabular-nums" }, children: group.count })] })), (0, jsx_runtime_1.jsx)(react_1.chakra.ul, { listStyleType: "none", display: "grid", gap: "0.125rem", m: 0, px: 1.5, py: 0, children: group.threads.flatMap((thread) => {
19
+ const isOpen = openThreads.has(thread.key);
20
+ const lead = rowFor(thread.lead, { thread: thread.rest.length > 0 ? thread : undefined, isOpen });
21
+ return isOpen ? [lead, ...thread.rest.map((item) => rowFor(item, { isChild: true }))] : [lead];
22
+ }) })] }, group.key));
23
+ }) }));
24
+ };
25
+ exports.InboxList = InboxList;
@@ -0,0 +1,17 @@
1
+ import type { NotificationInboxLabels, NotificationInboxTab } from "./NotificationInboxProps";
2
+ export declare const InboxSkeleton: ({ isPhone, label }: {
3
+ isPhone: boolean;
4
+ label: string;
5
+ }) => import("react/jsx-runtime").JSX.Element;
6
+ interface LoadFailureProps {
7
+ kind: "error" | "noAccess";
8
+ labels: NotificationInboxLabels;
9
+ appName?: string;
10
+ onRetry?: () => void;
11
+ }
12
+ export declare const InboxLoadFailure: ({ kind, labels, appName, onRetry }: LoadFailureProps) => import("react/jsx-runtime").JSX.Element;
13
+ export declare const InboxEmpty: ({ tab, labels }: {
14
+ tab: NotificationInboxTab;
15
+ labels: NotificationInboxLabels;
16
+ }) => import("react/jsx-runtime").JSX.Element;
17
+ export {};
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InboxEmpty = exports.InboxLoadFailure = exports.InboxSkeleton = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const lucide_react_1 = require("lucide-react");
9
+ const Primitives_1 = require("../../Primitives/Primitives");
10
+ const Button_1 = __importDefault(require("../../Button/Button"));
11
+ const EmptyState_1 = __importDefault(require("../../EmptyState/EmptyState"));
12
+ const Skeletons_1 = __importDefault(require("../../Skeletons/Skeletons"));
13
+ const SKELETON_ROWS = 6;
14
+ const InboxSkeleton = ({ isPhone, label }) => ((0, jsx_runtime_1.jsx)(Primitives_1.Flex, { direction: "column", gap: 1, px: 2, py: 2, role: "status", "aria-busy": "true", "aria-label": label, children: Array.from({ length: SKELETON_ROWS }, (_, index) => ((0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { gap: 3, px: 3, py: 2.5, "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "circle", size: isPhone ? "10" : "8" }), (0, jsx_runtime_1.jsx)(Primitives_1.Box, { flex: 1, children: (0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "text", noOfLines: index % 2 === 0 ? 3 : 2, spacing: 2, skeletonHeight: 2 }) })] }, index))) }));
15
+ exports.InboxSkeleton = InboxSkeleton;
16
+ const InboxLoadFailure = ({ kind, labels, appName, onRetry }) => kind === "error" ? ((0, jsx_runtime_1.jsx)(EmptyState_1.default, { icon: lucide_react_1.CircleAlert, colorScheme: "error", title: labels.loadErrorTitle, description: labels.loadErrorBody, action: onRetry && (0, jsx_runtime_1.jsx)(Button_1.default, { size: "sm", variant: "outline", colorScheme: "gray", label: labels.retry, onClick: onRetry }) })) : ((0, jsx_runtime_1.jsx)(EmptyState_1.default, { icon: lucide_react_1.Lock, colorScheme: "gray", title: labels.noAccessTitle, description: labels.noAccessBody(appName) }));
17
+ exports.InboxLoadFailure = InboxLoadFailure;
18
+ const InboxEmpty = ({ tab, labels }) => {
19
+ if (tab === "needsYou") {
20
+ return (0, jsx_runtime_1.jsx)(EmptyState_1.default, { icon: lucide_react_1.CircleCheck, colorScheme: "success", title: labels.caughtUpTitle, description: labels.caughtUpBody });
21
+ }
22
+ if (tab === "archived") {
23
+ return (0, jsx_runtime_1.jsx)(EmptyState_1.default, { icon: lucide_react_1.Archive, colorScheme: "gray", title: labels.archivedEmptyTitle, description: labels.archivedEmptyBody });
24
+ }
25
+ return (0, jsx_runtime_1.jsx)(EmptyState_1.default, { icon: lucide_react_1.Bell, colorScheme: "gray", title: labels.activityEmptyTitle, description: labels.activityEmptyBody });
26
+ };
27
+ exports.InboxEmpty = InboxEmpty;
@@ -0,0 +1,4 @@
1
+ import type { NotificationInboxProps } from "./NotificationInboxProps";
2
+ /** Full-page inbox: Needs you, Activity and Archived tabs, search, a type filter, bulk actions and a reading pane. */
3
+ declare const NotificationInbox: (props: NotificationInboxProps) => import("react/jsx-runtime").JSX.Element;
4
+ export default NotificationInbox;