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,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withInboxLabels = void 0;
4
+ const labels_1 = require("../core/labels");
5
+ const withInboxLabels = (shared, format, overrides = {}) => (0, labels_1.mergeDefined)({
6
+ ...shared,
7
+ title: "Inbox",
8
+ subtitle: ({ needsYou, unread }) => `${format.number(needsYou)} need you · ${format.number(unread)} unread`,
9
+ listsLabel: "Inbox lists",
10
+ needsYouTab: "Needs you",
11
+ activityTab: "Activity",
12
+ archivedTab: "Archived",
13
+ search: "Search notifications",
14
+ typeFilter: "Type",
15
+ allTypes: "All types",
16
+ unreadOnly: "Unread only",
17
+ filters: "Filters",
18
+ markAllRead: "Mark all read",
19
+ settings: "Notification settings",
20
+ keyboardShortcuts: "Keyboard shortcuts",
21
+ shortcutsHint: "They work when focus is in the inbox, not while typing.",
22
+ shortcuts: {
23
+ next: "Next notification",
24
+ previous: "Previous notification",
25
+ show: "Show the details",
26
+ openInApp: "Open in the app",
27
+ toggleRead: "Mark as read or unread",
28
+ archive: "Archive",
29
+ select: "Select",
30
+ undo: "Undo the last action",
31
+ search: "Search",
32
+ clear: "Clear selection",
33
+ help: "Show these shortcuts",
34
+ },
35
+ selected: (count) => `${format.number(count)} selected`,
36
+ selectAll: "Select all",
37
+ clearSelection: "Clear selection",
38
+ bulkDecide: (actionLabel, count) => `${actionLabel} ${format.number(count)}`,
39
+ nothingSelected: "Nothing selected",
40
+ nothingSelectedHint: "Pick a notification to read it here.",
41
+ nothingToUndo: "Nothing to undo",
42
+ results: (count) => (count === 1 ? "1 result" : `${format.number(count)} results`),
43
+ caughtUpTitle: "You're all caught up",
44
+ caughtUpBody: "Nothing is waiting for your decision.",
45
+ activityEmptyTitle: "No activity yet",
46
+ activityEmptyBody: "Updates about your work and requests show up here.",
47
+ archivedEmptyTitle: "Nothing archived",
48
+ archivedEmptyBody: "Archived notifications stay here until you move them back.",
49
+ loadErrorTitle: "Couldn't load notifications",
50
+ loadErrorBody: "Check your connection, then try again. Nothing you did was lost.",
51
+ noAccessTitle: "Notifications aren't turned on for you",
52
+ noAccessBody: (appName) => `Your ${appName !== null && appName !== void 0 ? appName : "app"} admin hasn't given your role access to notifications in this workspace.`,
53
+ loading: "Loading notifications",
54
+ loadMore: "Load more",
55
+ }, overrides);
56
+ exports.withInboxLabels = withInboxLabels;
@@ -0,0 +1,17 @@
1
+ import type { LiveNotificationEntry, NotificationGroup, NotificationThread } from "../core/NotificationEngineProps";
2
+ import type { NotificationAction } from "../core/NotificationSourceProps";
3
+ import type { NotificationInboxTab } from "./NotificationInboxProps";
4
+ export declare const ALL_TYPES = "all";
5
+ export interface InboxFilters {
6
+ type: string;
7
+ unreadOnly: boolean;
8
+ query: string;
9
+ keepId: string | null;
10
+ }
11
+ export declare const filterItems: (items: LiveNotificationEntry[], filters: InboxFilters) => LiveNotificationEntry[];
12
+ export declare const isShown: (item: LiveNotificationEntry) => boolean;
13
+ export declare const visibleRowIds: (groups: NotificationGroup[], openThreads: ReadonlySet<string>) => string[];
14
+ export declare const threadsByLead: (groups: NotificationGroup[]) => Map<string, NotificationThread>;
15
+ export declare const bulkDecision: (items: LiveNotificationEntry[]) => NotificationAction | undefined;
16
+ export declare const tabZoneMatches: (tab: NotificationInboxTab, item: LiveNotificationEntry) => boolean;
17
+ export declare const isTypingTarget: (target: EventTarget | null) => boolean;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isTypingTarget = exports.tabZoneMatches = exports.bulkDecision = exports.threadsByLead = exports.visibleRowIds = exports.isShown = exports.filterItems = exports.ALL_TYPES = void 0;
4
+ const grouping_1 = require("../core/grouping");
5
+ exports.ALL_TYPES = "all";
6
+ const filterItems = (items, filters) => items.filter(({ entry }) => (filters.type === exports.ALL_TYPES || entry.category === filters.type) &&
7
+ (!filters.unreadOnly || entry.isRead === false || entry.id === filters.keepId) &&
8
+ (0, grouping_1.matchesNotificationQuery)(entry, filters.query));
9
+ exports.filterItems = filterItems;
10
+ const isShown = (item) => !item.state.isHidden;
11
+ exports.isShown = isShown;
12
+ const visibleRowIds = (groups, openThreads) => groups.flatMap((group) => group.threads.flatMap((thread) => {
13
+ const rows = openThreads.has(thread.key) ? (0, grouping_1.threadEntries)(thread) : [thread.lead];
14
+ return rows.filter(exports.isShown).map((item) => item.entry.id);
15
+ }));
16
+ exports.visibleRowIds = visibleRowIds;
17
+ const threadsByLead = (groups) => new Map(groups.flatMap((group) => group.threads.map((thread) => [thread.key, thread])));
18
+ exports.threadsByLead = threadsByLead;
19
+ const bulkDecision = (items) => {
20
+ var _a, _b;
21
+ const quick = (action) => action.href === undefined && action.reason === undefined;
22
+ const candidate = (_b = (_a = items[0]) === null || _a === void 0 ? void 0 : _a.entry.actions) === null || _b === void 0 ? void 0 : _b.find((action) => quick(action) && action.intent === "positive");
23
+ if (!candidate)
24
+ return undefined;
25
+ const everyone = items.every(({ entry, state }) => {
26
+ var _a;
27
+ return state.zone === "needsYou" &&
28
+ state.status === "open" &&
29
+ ((_a = entry.actions) === null || _a === void 0 ? void 0 : _a.some((action) => action.id === candidate.id && quick(action)));
30
+ });
31
+ return everyone ? candidate : undefined;
32
+ };
33
+ exports.bulkDecision = bulkDecision;
34
+ const tabZoneMatches = (tab, item) => (item.state.zone === "archived") === (tab === "archived");
35
+ exports.tabZoneMatches = tabZoneMatches;
36
+ const TYPING_ROLES = '[role="combobox"],[role="listbox"],[role="menu"],[role="textbox"],[role="searchbox"]';
37
+ const NON_TEXT_INPUTS = ["checkbox", "radio", "button", "submit"];
38
+ const isTypingTarget = (target) => {
39
+ if (!(target instanceof HTMLElement))
40
+ return false;
41
+ if (target.isContentEditable || target.closest(TYPING_ROLES))
42
+ return true;
43
+ if (target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement)
44
+ return true;
45
+ return target instanceof HTMLInputElement && !NON_TEXT_INPUTS.includes(target.type);
46
+ };
47
+ exports.isTypingTarget = isTypingTarget;
@@ -0,0 +1 @@
1
+ export declare const useControllable: <T>(controlled: T | undefined, initial: T, onChange?: (value: T) => void) => [value: T, set: (value: T) => void, isControlled: boolean];
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useControllable = void 0;
4
+ const react_1 = require("react");
5
+ const useControllable = (controlled, initial, onChange) => {
6
+ const [inner, setInner] = (0, react_1.useState)(initial);
7
+ const isControlled = controlled !== undefined;
8
+ const set = (0, react_1.useCallback)((next) => {
9
+ if (!isControlled)
10
+ setInner(next);
11
+ onChange === null || onChange === void 0 ? void 0 : onChange(next);
12
+ }, [isControlled, onChange]);
13
+ return [isControlled ? controlled : inner, set, isControlled];
14
+ };
15
+ exports.useControllable = useControllable;
@@ -0,0 +1,8 @@
1
+ export { NotificationCenterProvider, useNotificationCenter } from "./core/NotificationCenterProvider";
2
+ export { isNotificationActionError, notificationActionError, toNotificationActionError } from "./core/actionError";
3
+ export { DEFAULT_NOTIFICATION_LOCALE, defaultNotificationLabels } from "./core/labels";
4
+ export { default as NotificationRow } from "./row/NotificationRow";
5
+ export { default as NotificationDetail } from "./row/NotificationDetail";
6
+ export { default as NotificationCenter } from "./center/NotificationCenter";
7
+ export { default as NotificationInbox } from "./inbox/NotificationInbox";
8
+ export { default as NotificationPreferences } from "./preferences/NotificationPreferences";
@@ -0,0 +1,26 @@
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.NotificationPreferences = exports.NotificationInbox = exports.NotificationCenter = exports.NotificationDetail = exports.NotificationRow = exports.defaultNotificationLabels = exports.DEFAULT_NOTIFICATION_LOCALE = exports.toNotificationActionError = exports.notificationActionError = exports.isNotificationActionError = exports.useNotificationCenter = exports.NotificationCenterProvider = void 0;
7
+ var NotificationCenterProvider_1 = require("./core/NotificationCenterProvider");
8
+ Object.defineProperty(exports, "NotificationCenterProvider", { enumerable: true, get: function () { return NotificationCenterProvider_1.NotificationCenterProvider; } });
9
+ Object.defineProperty(exports, "useNotificationCenter", { enumerable: true, get: function () { return NotificationCenterProvider_1.useNotificationCenter; } });
10
+ var actionError_1 = require("./core/actionError");
11
+ Object.defineProperty(exports, "isNotificationActionError", { enumerable: true, get: function () { return actionError_1.isNotificationActionError; } });
12
+ Object.defineProperty(exports, "notificationActionError", { enumerable: true, get: function () { return actionError_1.notificationActionError; } });
13
+ Object.defineProperty(exports, "toNotificationActionError", { enumerable: true, get: function () { return actionError_1.toNotificationActionError; } });
14
+ var labels_1 = require("./core/labels");
15
+ Object.defineProperty(exports, "DEFAULT_NOTIFICATION_LOCALE", { enumerable: true, get: function () { return labels_1.DEFAULT_NOTIFICATION_LOCALE; } });
16
+ Object.defineProperty(exports, "defaultNotificationLabels", { enumerable: true, get: function () { return labels_1.defaultNotificationLabels; } });
17
+ var NotificationRow_1 = require("./row/NotificationRow");
18
+ Object.defineProperty(exports, "NotificationRow", { enumerable: true, get: function () { return __importDefault(NotificationRow_1).default; } });
19
+ var NotificationDetail_1 = require("./row/NotificationDetail");
20
+ Object.defineProperty(exports, "NotificationDetail", { enumerable: true, get: function () { return __importDefault(NotificationDetail_1).default; } });
21
+ var NotificationCenter_1 = require("./center/NotificationCenter");
22
+ Object.defineProperty(exports, "NotificationCenter", { enumerable: true, get: function () { return __importDefault(NotificationCenter_1).default; } });
23
+ var NotificationInbox_1 = require("./inbox/NotificationInbox");
24
+ Object.defineProperty(exports, "NotificationInbox", { enumerable: true, get: function () { return __importDefault(NotificationInbox_1).default; } });
25
+ var NotificationPreferences_1 = require("./preferences/NotificationPreferences");
26
+ Object.defineProperty(exports, "NotificationPreferences", { enumerable: true, get: function () { return __importDefault(NotificationPreferences_1).default; } });
@@ -0,0 +1,4 @@
1
+ import type { NotificationPreferencesProps } from "./NotificationPreferencesProps";
2
+ /** Notification settings: a type-by-channel grid with locked cells and an email summary choice; every change autosaves and rolls back on failure. */
3
+ declare const NotificationPreferences: ({ channels, groups, values, onChange, emailSummary, onEmailSummaryChange, status, onRetry, isOffline, note, appName, variant, labels: labelOverrides, "data-testid": testId, }: NotificationPreferencesProps) => import("react/jsx-runtime").JSX.Element;
4
+ export default NotificationPreferences;
@@ -0,0 +1,72 @@
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
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const lucide_react_1 = require("lucide-react");
9
+ const Primitives_1 = require("../../Primitives/Primitives");
10
+ const Card_1 = __importDefault(require("../../Card/Card"));
11
+ const PageHeader_1 = __importDefault(require("../../PageHeader/PageHeader"));
12
+ const SettingRow_1 = __importDefault(require("../../SettingRow/SettingRow"));
13
+ const SettingsSection_1 = __importDefault(require("../../SettingRow/SettingsSection"));
14
+ const SegmentedControl_1 = __importDefault(require("../../SegmentedControl/SegmentedControl"));
15
+ const Skeletons_1 = __importDefault(require("../../Skeletons/Skeletons"));
16
+ const Spinner_1 = __importDefault(require("../../Spinner/Spinner"));
17
+ const VisuallyHidden_1 = __importDefault(require("../../VisuallyHidden/VisuallyHidden"));
18
+ const Button_1 = __importDefault(require("../../Button/Button"));
19
+ const EmptyState_1 = __importDefault(require("../../EmptyState/EmptyState"));
20
+ const Toaster_1 = require("../../Toaster/Toaster");
21
+ const useCustomTheme_1 = require("../../../Theme/useCustomTheme");
22
+ const PreferenceGroups_1 = __importDefault(require("./PreferenceGroups"));
23
+ const preferencesLabels_1 = require("./preferencesLabels");
24
+ const useAutosave_1 = require("./useAutosave");
25
+ const EMAIL_KEY = "email-summary";
26
+ const SaveStatus = ({ isSaving, hasSaved, labels }) => {
27
+ const { colors } = (0, useCustomTheme_1.useCustomTheme)();
28
+ return ((0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { role: "status", align: "center", gap: 1.5, fontSize: "xs", color: hasSaved && !isSaving ? colors.semanticText.success : colors.textMuted, fontWeight: hasSaved && !isSaving ? 500 : 400, children: [isSaving && (0, jsx_runtime_1.jsx)(Spinner_1.default, { size: "xs", label: "" }), !isSaving && hasSaved && (0, jsx_runtime_1.jsx)(lucide_react_1.Check, { size: 14, "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("span", { children: isSaving ? labels.saving : hasSaved ? labels.saved : labels.autosave })] }));
29
+ };
30
+ const Banner = ({ icon, children }) => ((0, jsx_runtime_1.jsxs)(Primitives_1.Alert, { status: "info", borderRadius: "lg", alignItems: "flex-start", children: [(0, jsx_runtime_1.jsx)(Primitives_1.Box, { mt: 0.5, mr: 2, flexShrink: 0, children: icon }), (0, jsx_runtime_1.jsx)(Primitives_1.AlertDescription, { fontSize: "sm", children: children })] }));
31
+ const SkeletonCard = ({ rows }) => ((0, jsx_runtime_1.jsx)(Card_1.default, { header: (0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", height: "1rem", width: "8rem" }), children: (0, jsx_runtime_1.jsx)(Primitives_1.Flex, { direction: "column", gap: 5, children: Array.from({ length: rows }, (_, index) => ((0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { justify: "space-between", align: "center", gap: 4, children: [(0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { direction: "column", gap: 2, children: [(0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", height: "0.875rem", width: "9rem" }), (0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", height: "0.75rem", width: "14rem" })] }), (0, jsx_runtime_1.jsx)(Skeletons_1.default, { type: "skeleton", height: "1rem", width: "6rem" })] }, index))) }) }));
32
+ /** Notification settings: a type-by-channel grid with locked cells and an email summary choice; every change autosaves and rolls back on failure. */
33
+ const NotificationPreferences = ({ channels, groups, values, onChange, emailSummary, onEmailSummaryChange, status = "ready", onRetry, isOffline = false, note, appName, variant = "desktop", labels: labelOverrides, "data-testid": testId, }) => {
34
+ var _a;
35
+ const { colors } = (0, useCustomTheme_1.useCustomTheme)();
36
+ const { showToast } = (0, Toaster_1.useToaster)();
37
+ const labels = (0, react_1.useMemo)(() => (0, preferencesLabels_1.resolvePreferencesLabels)(labelOverrides), [labelOverrides]);
38
+ const saved = (0, react_1.useMemo)(() => ({ values, emailSummary }), [values, emailSummary]);
39
+ const notifyFailed = (0, react_1.useCallback)((what) => showToast({ status: "error", title: labels.saveFailed(what) }), [showToast, labels]);
40
+ const { draft, pendingKeys, hasSaved, commit } = (0, useAutosave_1.useAutosave)(saved, notifyFailed);
41
+ const isFrozen = status !== "ready" || isOffline;
42
+ const isReadOnly = status === "readOnly";
43
+ const pagePadding = variant === "phone" ? 4 : 6;
44
+ const cellKey = (rowId, channelId) => `cell:${rowId}:${channelId}`;
45
+ const valueOf = (rowId, channelId) => {
46
+ const pending = draft[cellKey(rowId, channelId)];
47
+ if (typeof pending === "boolean")
48
+ return pending;
49
+ const row = values[rowId];
50
+ return Boolean(row && row[channelId]);
51
+ };
52
+ const isPending = (rowId, channelId) => pendingKeys.includes(cellKey(rowId, channelId));
53
+ const toggle = (row, channel, next) => commit(cellKey(row.id, channel.id), next, () => onChange(row.id, channel.id, next), `${row.label} by ${channel.label.toLowerCase()}`);
54
+ const shownEmail = (_a = draft[EMAIL_KEY]) !== null && _a !== void 0 ? _a : emailSummary;
55
+ const changeEmail = (next) => {
56
+ if (!onEmailSummaryChange || next === shownEmail)
57
+ return;
58
+ commit(EMAIL_KEY, next, () => onEmailSummaryChange(next), labels.email);
59
+ };
60
+ const header = ((0, jsx_runtime_1.jsx)(PageHeader_1.default, { title: labels.title, subtitle: labels.subtitle(appName), actions: status === "ready" && !isOffline ? (0, jsx_runtime_1.jsx)(SaveStatus, { isSaving: pendingKeys.length > 0, hasSaved: hasSaved, labels: labels }) : undefined, actionsLayout: "inline" }));
61
+ if (status === "loading") {
62
+ return ((0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { direction: "column", gap: 5, px: pagePadding, py: 5, maxW: "57.5rem", "aria-busy": "true", "data-testid": testId, children: [header, (0, jsx_runtime_1.jsx)(VisuallyHidden_1.default, { role: "status", children: labels.loading }), (0, jsx_runtime_1.jsx)(SkeletonCard, { rows: 1 }), (0, jsx_runtime_1.jsx)(SkeletonCard, { rows: 4 })] }));
63
+ }
64
+ if (status === "error") {
65
+ return ((0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { direction: "column", gap: 5, px: pagePadding, py: 5, maxW: "57.5rem", "data-testid": testId, children: [header, (0, jsx_runtime_1.jsx)(Primitives_1.Box, { role: "alert", w: "100%", children: (0, jsx_runtime_1.jsx)(EmptyState_1.default, { colorScheme: "error", icon: lucide_react_1.AlertTriangle, titleAs: "h2", title: labels.errorTitle, description: labels.errorDescription, size: "md", action: (0, jsx_runtime_1.jsx)(Button_1.default, { size: "sm", variant: "outline", colorScheme: "gray", label: labels.retry, leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.RotateCw, { size: 16 }), onClick: onRetry }) }) })] }));
66
+ }
67
+ return ((0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { direction: "column", gap: 5, px: pagePadding, py: 5, maxW: "57.5rem", "data-testid": testId, children: [header, isReadOnly && (0, jsx_runtime_1.jsx)(Banner, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Lock, { size: 16, "aria-hidden": "true" }), children: labels.readOnlyBanner }), isOffline && (0, jsx_runtime_1.jsx)(Banner, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.WifiOff, { size: 16, "aria-hidden": "true" }), children: labels.offlineBanner }), shownEmail && ((0, jsx_runtime_1.jsx)(SettingsSection_1.default, { title: labels.email, children: (0, jsx_runtime_1.jsx)(SettingRow_1.default, { title: labels.emailTiming, description: labels.emailHint(appName), control: (0, jsx_runtime_1.jsx)(SegmentedControl_1.default, { "aria-label": labels.emailTiming, options: [
68
+ { value: "asItHappens", label: labels.asItHappens, isDisabled: isFrozen },
69
+ { value: "daily", label: labels.daily, isDisabled: isFrozen },
70
+ ], value: shownEmail, onChange: changeEmail }) }) })), (0, jsx_runtime_1.jsx)(PreferenceGroups_1.default, { channels: channels, groups: groups, labels: labels, variant: variant, isFrozen: isFrozen, valueOf: valueOf, isPending: isPending, onToggle: toggle, testId: testId }), note && ((0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { align: "flex-start", gap: 1.5, maxW: "72ch", fontSize: "xs", color: colors.textMuted, children: [(0, jsx_runtime_1.jsx)(Primitives_1.Box, { mt: 0.5, flexShrink: 0, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Lock, { size: 12, "aria-hidden": "true" }) }), (0, jsx_runtime_1.jsx)(Primitives_1.Text, { children: note })] }))] }));
71
+ };
72
+ exports.default = NotificationPreferences;
@@ -0,0 +1,111 @@
1
+ /** A delivery column, e.g. In-app, Email, Mobile push. */
2
+ export interface NotificationPreferenceChannel {
3
+ /** Key into each row's values. */
4
+ id: string;
5
+ /** Column heading and part of every cell's accessible name. */
6
+ label: string;
7
+ }
8
+ /** One notification type: a row of channel switches. */
9
+ export interface NotificationPreferenceRow {
10
+ /** Passed to `onChange`. */
11
+ id: string;
12
+ /** e.g. "Leave requests". */
13
+ label: string;
14
+ /** One muted line under the label. */
15
+ hint?: string;
16
+ /** Channel ids that are always on, shown checked, disabled and with a lock; in-app for approvals. */
17
+ locked?: string[];
18
+ }
19
+ /** A titled card of rows, e.g. "Requests you approve". */
20
+ export interface NotificationPreferenceGroup {
21
+ /** Stable key. */
22
+ id: string;
23
+ /** Card heading. */
24
+ label: string;
25
+ /** In display order. */
26
+ rows: NotificationPreferenceRow[];
27
+ }
28
+ /** Saved state as `values[rowId][channelId]`; a missing cell reads as off. */
29
+ export type NotificationPreferenceValues = {
30
+ [rowId: string]: {
31
+ [channelId: string]: boolean | undefined;
32
+ } | undefined;
33
+ };
34
+ /** How email arrives: one per notification, or one summary a day at 9:00. */
35
+ export type NotificationEmailSummary = "asItHappens" | "daily";
36
+ /** Load state; "readOnly" shows the settings an admin manages but nothing can change. */
37
+ export type NotificationPreferencesStatus = "loading" | "ready" | "error" | "readOnly";
38
+ /** Strings for the settings page; anything left out stays English. */
39
+ export interface NotificationPreferencesLabels {
40
+ /** Page heading. */
41
+ title: string;
42
+ /** Under the heading; gets the app name. */
43
+ subtitle: (appName?: string) => string;
44
+ /** Email card heading. */
45
+ email: string;
46
+ /** Title of the email timing row. */
47
+ emailTiming: string;
48
+ /** Under the email timing title; gets the app name. */
49
+ emailHint: (appName?: string) => string;
50
+ /** Screen-reader heading of the type column. */
51
+ typeColumn: string;
52
+ /** Tooltip and label explaining why a locked cell cannot be turned off. */
53
+ lockedTip: string;
54
+ /** "As it happens" option. */
55
+ asItHappens: string;
56
+ /** "Daily at 9:00 am" option. */
57
+ daily: string;
58
+ /** Accessible suffix of a locked cell. */
59
+ alwaysOn: string;
60
+ /** Status while a change is in flight. */
61
+ saving: string;
62
+ /** Status after the last change saved. */
63
+ saved: string;
64
+ /** Status before any change. */
65
+ autosave: string;
66
+ /** Toast after a failed save; gets what was put back. */
67
+ saveFailed: (what: string) => string;
68
+ /** Banner while `status` is "readOnly". */
69
+ readOnlyBanner: string;
70
+ /** Banner while offline. */
71
+ offlineBanner: string;
72
+ /** Screen-reader text while `status` is "loading". */
73
+ loading: string;
74
+ /** Error state title. */
75
+ errorTitle: string;
76
+ /** Error state description. */
77
+ errorDescription: string;
78
+ /** Error state retry button. */
79
+ retry: string;
80
+ }
81
+ /** Notification settings: a type-by-channel grid with locked cells and an email summary choice; every change autosaves and rolls back on failure. */
82
+ export interface NotificationPreferencesProps {
83
+ /** Columns, in order. */
84
+ channels: NotificationPreferenceChannel[];
85
+ /** Cards of rows, in order. */
86
+ groups: NotificationPreferenceGroup[];
87
+ /** Saved state; the page shows changes optimistically until `onChange` settles. */
88
+ values: NotificationPreferenceValues;
89
+ /** Saves one cell; reject to roll the cell back with a toast. */
90
+ onChange: (rowId: string, channelId: string, enabled: boolean) => Promise<unknown>;
91
+ /** Shows the email card. */
92
+ emailSummary?: NotificationEmailSummary;
93
+ /** Saves the email choice; reject to roll it back with a toast. */
94
+ onEmailSummaryChange?: (value: NotificationEmailSummary) => Promise<unknown>;
95
+ /** Defaults to "ready". */
96
+ status?: NotificationPreferencesStatus;
97
+ /** Retries a failed load from the error state. */
98
+ onRetry?: () => void;
99
+ /** Disables every cell and shows the offline banner. */
100
+ isOffline?: boolean;
101
+ /** Footnote under the cards, e.g. what admins lock. */
102
+ note?: string;
103
+ /** As in "Choose where hrms tells you about things". */
104
+ appName?: string;
105
+ /** "desktop" is a table per card; "phone" stacks a row's switches under its label. Defaults to "desktop". */
106
+ variant?: "desktop" | "phone";
107
+ /** Overrides for the page strings. */
108
+ labels?: Partial<NotificationPreferencesLabels>;
109
+ /** Test id root; each cell gets `-<rowId>-<channelId>`. */
110
+ "data-testid"?: string;
111
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,16 @@
1
+ import type { NotificationPreferenceChannel, NotificationPreferenceRow, NotificationPreferencesLabels } from "./NotificationPreferencesProps";
2
+ /** One type-by-channel cell: its saved state, lock and in-flight flags. */
3
+ interface PreferenceCellProps {
4
+ row: NotificationPreferenceRow;
5
+ channel: NotificationPreferenceChannel;
6
+ labels: NotificationPreferencesLabels;
7
+ isOn: boolean;
8
+ isLocked: boolean;
9
+ isFrozen: boolean;
10
+ isPending: boolean;
11
+ onToggle: (next: boolean) => void;
12
+ "data-testid"?: string;
13
+ }
14
+ export declare const GridCell: ({ row, channel, labels, isOn, isLocked, isFrozen, isPending, onToggle, "data-testid": testId }: PreferenceCellProps) => import("react/jsx-runtime").JSX.Element;
15
+ export declare const SwitchCell: ({ row, channel, labels, isOn, isLocked, isFrozen, isPending, onToggle, "data-testid": testId }: PreferenceCellProps) => import("react/jsx-runtime").JSX.Element;
16
+ export {};
@@ -0,0 +1,35 @@
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.SwitchCell = exports.GridCell = 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 Checkbox_1 = __importDefault(require("../../Checkbox/Checkbox"));
11
+ const ToggleSwitch_1 = __importDefault(require("../../ToggleSwitch/ToggleSwitch"));
12
+ const ToolTip_1 = __importDefault(require("../../ToolTip/ToolTip"));
13
+ const useCustomTheme_1 = require("../../../Theme/useCustomTheme");
14
+ const LockMark = ({ label }) => {
15
+ const { colors } = (0, useCustomTheme_1.useCustomTheme)();
16
+ return ((0, jsx_runtime_1.jsx)(Primitives_1.Box, { as: "span", role: "img", "aria-label": label, tabIndex: 0, display: "inline-flex", color: colors.textMuted, borderRadius: "sm", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Lock, { size: 12, "aria-hidden": "true" }) }));
17
+ };
18
+ const GridCell = ({ row, channel, labels, isOn, isLocked, isFrozen, isPending, onToggle, "data-testid": testId }) => {
19
+ const name = `${row.label}: ${channel.label}`;
20
+ const checkbox = ((0, jsx_runtime_1.jsx)(Checkbox_1.default, { "aria-label": isLocked ? `${name}, ${labels.alwaysOn}` : name, isChecked: isOn, isDisabled: isLocked || isFrozen, onChange: (event) => onToggle(event.target.checked) }));
21
+ if (!isLocked) {
22
+ return ((0, jsx_runtime_1.jsx)(Primitives_1.Flex, { "data-testid": testId, justify: "center", opacity: isPending ? 0.6 : 1, children: checkbox }));
23
+ }
24
+ return ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: labels.lockedTip, placement: "top", children: (0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { "data-testid": testId, justify: "center", position: "relative", w: "fit-content", mx: "auto", children: [checkbox, (0, jsx_runtime_1.jsx)(Primitives_1.Box, { position: "absolute", left: "calc(100% + 0.375rem)", top: "50%", transform: "translateY(-50%)", children: (0, jsx_runtime_1.jsx)(LockMark, { label: labels.lockedTip }) })] }) }));
25
+ };
26
+ exports.GridCell = GridCell;
27
+ const touchTarget = {
28
+ "& label": { display: "inline-flex", alignItems: "center", minHeight: "2.75rem" },
29
+ "& input[role='switch']": { top: "-0.6875rem", height: "calc(100% + 1.375rem)", left: "-0.25rem", width: "calc(100% + 0.5rem)" },
30
+ };
31
+ const SwitchCell = ({ row, channel, labels, isOn, isLocked, isFrozen, isPending, onToggle, "data-testid": testId }) => {
32
+ const name = `${row.label}: ${channel.label}`;
33
+ return ((0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { "data-testid": testId, align: "center", gap: 1.5, minH: "2.75rem", opacity: isPending ? 0.6 : 1, sx: touchTarget, children: [(0, jsx_runtime_1.jsx)(ToggleSwitch_1.default, { size: "md", label: isLocked ? `${channel.label} (${labels.alwaysOn})` : channel.label, "aria-label": isLocked ? `${name}, ${labels.alwaysOn}` : name, isChecked: isOn, isDisabled: isLocked || isFrozen, onChange: onToggle }), isLocked && (0, jsx_runtime_1.jsx)(LockMark, { label: labels.lockedTip })] }));
34
+ };
35
+ exports.SwitchCell = SwitchCell;
@@ -0,0 +1,15 @@
1
+ import type { NotificationPreferenceChannel, NotificationPreferenceGroup, NotificationPreferenceRow, NotificationPreferencesLabels } from "./NotificationPreferencesProps";
2
+ /** The grouped cards of the settings page, as tables or phone rows. */
3
+ interface PreferenceGroupsProps {
4
+ channels: NotificationPreferenceChannel[];
5
+ groups: NotificationPreferenceGroup[];
6
+ labels: NotificationPreferencesLabels;
7
+ variant: "desktop" | "phone";
8
+ isFrozen: boolean;
9
+ valueOf: (rowId: string, channelId: string) => boolean;
10
+ isPending: (rowId: string, channelId: string) => boolean;
11
+ onToggle: (row: NotificationPreferenceRow, channel: NotificationPreferenceChannel, next: boolean) => void;
12
+ testId?: string;
13
+ }
14
+ declare const PreferenceGroups: (props: PreferenceGroupsProps) => import("react/jsx-runtime").JSX.Element;
15
+ export default PreferenceGroups;
@@ -0,0 +1,26 @@
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
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const Primitives_1 = require("../../Primitives/Primitives");
8
+ const SettingRow_1 = __importDefault(require("../../SettingRow/SettingRow"));
9
+ const SettingsSection_1 = __importDefault(require("../../SettingRow/SettingsSection"));
10
+ const VisuallyHidden_1 = __importDefault(require("../../VisuallyHidden/VisuallyHidden"));
11
+ const useCustomTheme_1 = require("../../../Theme/useCustomTheme");
12
+ const planeStep_1 = require("../../../Theme/tokens/builders/planeStep");
13
+ const PreferenceCells_1 = require("./PreferenceCells");
14
+ const isLockedCell = (row, channelId) => Boolean(row.locked && row.locked.includes(channelId));
15
+ const cellId = (testId, rowId, channelId) => (testId ? `${testId}-${rowId}-${channelId}` : undefined);
16
+ const GroupTable = ({ group, channels, labels, isFrozen, valueOf, isPending, onToggle, testId }) => {
17
+ const { colors } = (0, useCustomTheme_1.useCustomTheme)();
18
+ const rule = (0, planeStep_1.cardRule)(colors);
19
+ return ((0, jsx_runtime_1.jsxs)(Primitives_1.Box, { as: "table", w: "100%", sx: { borderCollapse: "collapse" }, children: [(0, jsx_runtime_1.jsx)(Primitives_1.Box, { as: "thead", children: (0, jsx_runtime_1.jsxs)(Primitives_1.Box, { as: "tr", children: [(0, jsx_runtime_1.jsx)(Primitives_1.Box, { as: "th", scope: "col", textAlign: "left", pb: 2, children: (0, jsx_runtime_1.jsx)(VisuallyHidden_1.default, { children: labels.typeColumn }) }), channels.map((channel) => ((0, jsx_runtime_1.jsx)(Primitives_1.Box, { as: "th", scope: "col", w: "7rem", pb: 2, textAlign: "center", fontSize: "xs", fontWeight: 500, color: colors.textMuted, children: channel.label }, channel.id)))] }) }), (0, jsx_runtime_1.jsx)(Primitives_1.Box, { as: "tbody", children: group.rows.map((row) => ((0, jsx_runtime_1.jsxs)(Primitives_1.Box, { as: "tr", borderTop: "1px solid", borderColor: rule, children: [(0, jsx_runtime_1.jsxs)(Primitives_1.Box, { as: "th", scope: "row", textAlign: "left", fontWeight: 400, py: 3, pr: 4, children: [(0, jsx_runtime_1.jsx)(Primitives_1.Text, { fontSize: "sm", fontWeight: 500, color: colors.text[800], children: row.label }), row.hint && ((0, jsx_runtime_1.jsx)(Primitives_1.Text, { fontSize: "xs", color: colors.textMuted, children: row.hint }))] }), channels.map((channel) => ((0, jsx_runtime_1.jsx)(Primitives_1.Box, { as: "td", textAlign: "center", verticalAlign: "middle", py: 3, children: (0, jsx_runtime_1.jsx)(PreferenceCells_1.GridCell, { row: row, channel: channel, labels: labels, isOn: isLockedCell(row, channel.id) || valueOf(row.id, channel.id), isLocked: isLockedCell(row, channel.id), isFrozen: isFrozen, isPending: isPending(row.id, channel.id), onToggle: (next) => onToggle(row, channel, next), "data-testid": cellId(testId, row.id, channel.id) }) }, channel.id)))] }, row.id))) })] }));
20
+ };
21
+ const GroupRows = ({ group, channels, labels, isFrozen, valueOf, isPending, onToggle, testId }) => ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: group.rows.map((row) => {
22
+ var _a;
23
+ return ((0, jsx_runtime_1.jsx)(SettingRow_1.default, { title: row.label, description: (_a = row.hint) !== null && _a !== void 0 ? _a : null, control: (0, jsx_runtime_1.jsx)(Primitives_1.Flex, { role: "group", "aria-label": row.label, wrap: "wrap", columnGap: 5, rowGap: 0, children: channels.map((channel) => ((0, jsx_runtime_1.jsx)(PreferenceCells_1.SwitchCell, { row: row, channel: channel, labels: labels, isOn: isLockedCell(row, channel.id) || valueOf(row.id, channel.id), isLocked: isLockedCell(row, channel.id), isFrozen: isFrozen, isPending: isPending(row.id, channel.id), onToggle: (next) => onToggle(row, channel, next), "data-testid": cellId(testId, row.id, channel.id) }, channel.id))) }) }, row.id));
24
+ }) }));
25
+ const PreferenceGroups = (props) => ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: props.groups.map((group) => ((0, jsx_runtime_1.jsx)(SettingsSection_1.default, { title: group.label, children: props.variant === "phone" ? (0, jsx_runtime_1.jsx)(GroupRows, { ...props, group: group }) : (0, jsx_runtime_1.jsx)(GroupTable, { ...props, group: group }) }, group.id))) }));
26
+ exports.default = PreferenceGroups;
@@ -0,0 +1,2 @@
1
+ import type { NotificationPreferencesLabels } from "./NotificationPreferencesProps";
2
+ export declare const resolvePreferencesLabels: (overrides?: Partial<NotificationPreferencesLabels>) => NotificationPreferencesLabels;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolvePreferencesLabels = void 0;
4
+ const labels_1 = require("../core/labels");
5
+ const DEFAULT_PREFERENCES_LABELS = {
6
+ title: "Notification settings",
7
+ subtitle: (appName) => (appName ? `Choose where ${appName} tells you about things.` : "Choose where you hear about things."),
8
+ email: "Email",
9
+ emailTiming: "Email timing",
10
+ emailHint: (appName) => appName ? `A summary skips anything you've already read in ${appName}.` : "A summary skips anything you've already read.",
11
+ typeColumn: "Notification type",
12
+ lockedTip: "Always on for this type",
13
+ asItHappens: "As it happens",
14
+ daily: "Daily at 9:00 am",
15
+ alwaysOn: "always on",
16
+ saving: "Saving…",
17
+ saved: "Saved",
18
+ autosave: "Changes save automatically",
19
+ saveFailed: (what) => `Couldn't save that change. ${what} was put back.`,
20
+ readOnlyBanner: "Your admin manages notification settings for your role. You can see them, but not change them.",
21
+ offlineBanner: "You're offline. Settings can't be changed until you reconnect.",
22
+ loading: "Loading settings",
23
+ errorTitle: "Couldn't load your settings",
24
+ errorDescription: "Nothing was changed. Try again in a moment.",
25
+ retry: "Try again",
26
+ };
27
+ const resolvePreferencesLabels = (overrides) => (0, labels_1.mergeDefined)(DEFAULT_PREFERENCES_LABELS, overrides);
28
+ exports.resolvePreferencesLabels = resolvePreferencesLabels;
@@ -0,0 +1,8 @@
1
+ type Draft = Record<string, boolean | string>;
2
+ export declare const useAutosave: <Saved>(saved: Saved, onFailed: (what: string) => void) => {
3
+ draft: Draft;
4
+ pendingKeys: string[];
5
+ hasSaved: boolean;
6
+ commit: (key: string, next: boolean | string, save: () => Promise<unknown>, what: string) => void;
7
+ };
8
+ export {};
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useAutosave = void 0;
4
+ const react_1 = require("react");
5
+ const useAutosave = (saved, onFailed) => {
6
+ const [draft, setDraft] = (0, react_1.useState)({});
7
+ const [pendingKeys, setPendingKeys] = (0, react_1.useState)([]);
8
+ const [hasSaved, setHasSaved] = (0, react_1.useState)(false);
9
+ const inFlight = (0, react_1.useRef)(new Set());
10
+ (0, react_1.useEffect)(() => {
11
+ setDraft((current) => {
12
+ const kept = Object.keys(current).filter((key) => inFlight.current.has(key));
13
+ return kept.length === Object.keys(current).length ? current : Object.fromEntries(kept.map((key) => [key, current[key]]));
14
+ });
15
+ }, [saved]);
16
+ const commit = (0, react_1.useCallback)((key, next, save, what) => {
17
+ if (inFlight.current.has(key))
18
+ return;
19
+ inFlight.current.add(key);
20
+ setDraft((current) => ({ ...current, [key]: next }));
21
+ setPendingKeys(Array.from(inFlight.current));
22
+ const settle = () => {
23
+ inFlight.current.delete(key);
24
+ setPendingKeys(Array.from(inFlight.current));
25
+ };
26
+ save().then(() => {
27
+ setHasSaved(true);
28
+ settle();
29
+ }, () => {
30
+ setDraft((current) => {
31
+ const rest = { ...current };
32
+ delete rest[key];
33
+ return rest;
34
+ });
35
+ settle();
36
+ onFailed(what);
37
+ });
38
+ }, [onFailed]);
39
+ return { draft, pendingKeys, hasSaved, commit };
40
+ };
41
+ exports.useAutosave = useAutosave;
@@ -0,0 +1,4 @@
1
+ import type { NotificationDetailProps } from "./NotificationDetailProps";
2
+ /** One notification in full for a reading pane or a phone screen; text only, never markup. */
3
+ declare const NotificationDetail: ({ item, variant, onBack, titleRef }: NotificationDetailProps) => import("react/jsx-runtime").JSX.Element;
4
+ export default NotificationDetail;