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.
- package/CHANGELOG.md +64 -1
- package/COMPONENT-GOTCHAS.md +37 -13
- package/DESIGN-GUIDE.md +4 -1
- package/dist/Components/NotificationBell/NotificationBellProps.d.ts +3 -3
- package/dist/Components/NotificationBell/NotificationItem.d.ts +1 -1
- package/dist/Components/NotificationBell/NotificationItem.js +1 -1
- package/dist/Components/NotificationBell/NotificationList.d.ts +1 -1
- package/dist/Components/NotificationBell/NotificationList.js +1 -1
- package/dist/Components/NotificationBell/NotificationPanel.d.ts +1 -1
- package/dist/Components/NotificationBell/NotificationPanel.js +1 -1
- package/dist/Components/NotificationCenter/center/CenterPanel.d.ts +15 -0
- package/dist/Components/NotificationCenter/center/CenterPanel.js +87 -0
- package/dist/Components/NotificationCenter/center/NotificationCenter.d.ts +4 -0
- package/dist/Components/NotificationCenter/center/NotificationCenter.js +130 -0
- package/dist/Components/NotificationCenter/center/NotificationCenterProps.d.ts +71 -0
- package/dist/Components/NotificationCenter/center/NotificationCenterProps.js +2 -0
- package/dist/Components/NotificationCenter/center/PanelRows.d.ts +10 -0
- package/dist/Components/NotificationCenter/center/PanelRows.js +35 -0
- package/dist/Components/NotificationCenter/center/PanelStates.d.ts +17 -0
- package/dist/Components/NotificationCenter/center/PanelStates.js +21 -0
- package/dist/Components/NotificationCenter/center/centerLabels.d.ts +4 -0
- package/dist/Components/NotificationCenter/center/centerLabels.js +38 -0
- package/dist/Components/NotificationCenter/center/useArrivals.d.ts +14 -0
- package/dist/Components/NotificationCenter/center/useArrivals.js +71 -0
- package/dist/Components/NotificationCenter/center/useIsSheet.d.ts +2 -0
- package/dist/Components/NotificationCenter/center/useIsSheet.js +20 -0
- package/dist/Components/NotificationCenter/core/NotificationCenterProvider.d.ts +17 -0
- package/dist/Components/NotificationCenter/core/NotificationCenterProvider.js +46 -0
- package/dist/Components/NotificationCenter/core/NotificationEngineProps.d.ts +182 -0
- package/dist/Components/NotificationCenter/core/NotificationEngineProps.js +2 -0
- package/dist/Components/NotificationCenter/core/NotificationSourceProps.d.ts +284 -0
- package/dist/Components/NotificationCenter/core/NotificationSourceProps.js +2 -0
- package/dist/Components/NotificationCenter/core/actionError.d.ts +4 -0
- package/dist/Components/NotificationCenter/core/actionError.js +45 -0
- package/dist/Components/NotificationCenter/core/format.d.ts +4 -0
- package/dist/Components/NotificationCenter/core/format.js +91 -0
- package/dist/Components/NotificationCenter/core/grouping.d.ts +14 -0
- package/dist/Components/NotificationCenter/core/grouping.js +102 -0
- package/dist/Components/NotificationCenter/core/labels.d.ts +6 -0
- package/dist/Components/NotificationCenter/core/labels.js +94 -0
- package/dist/Components/NotificationCenter/core/overlay.d.ts +109 -0
- package/dist/Components/NotificationCenter/core/overlay.js +281 -0
- package/dist/Components/NotificationCenter/core/useNotificationEngine.d.ts +13 -0
- package/dist/Components/NotificationCenter/core/useNotificationEngine.js +415 -0
- package/dist/Components/NotificationCenter/inbox/InboxList.d.ts +17 -0
- package/dist/Components/NotificationCenter/inbox/InboxList.js +25 -0
- package/dist/Components/NotificationCenter/inbox/InboxStates.d.ts +17 -0
- package/dist/Components/NotificationCenter/inbox/InboxStates.js +27 -0
- package/dist/Components/NotificationCenter/inbox/NotificationInbox.d.ts +4 -0
- package/dist/Components/NotificationCenter/inbox/NotificationInbox.js +434 -0
- package/dist/Components/NotificationCenter/inbox/NotificationInboxProps.d.ts +106 -0
- package/dist/Components/NotificationCenter/inbox/NotificationInboxProps.js +2 -0
- package/dist/Components/NotificationCenter/inbox/ShortcutsDialog.d.ts +8 -0
- package/dist/Components/NotificationCenter/inbox/ShortcutsDialog.js +63 -0
- package/dist/Components/NotificationCenter/inbox/inboxLabels.d.ts +4 -0
- package/dist/Components/NotificationCenter/inbox/inboxLabels.js +56 -0
- package/dist/Components/NotificationCenter/inbox/inboxModel.d.ts +17 -0
- package/dist/Components/NotificationCenter/inbox/inboxModel.js +47 -0
- package/dist/Components/NotificationCenter/inbox/useControllable.d.ts +1 -0
- package/dist/Components/NotificationCenter/inbox/useControllable.js +15 -0
- package/dist/Components/NotificationCenter/index.d.ts +8 -0
- package/dist/Components/NotificationCenter/index.js +26 -0
- package/dist/Components/NotificationCenter/preferences/NotificationPreferences.d.ts +4 -0
- package/dist/Components/NotificationCenter/preferences/NotificationPreferences.js +72 -0
- package/dist/Components/NotificationCenter/preferences/NotificationPreferencesProps.d.ts +111 -0
- package/dist/Components/NotificationCenter/preferences/NotificationPreferencesProps.js +2 -0
- package/dist/Components/NotificationCenter/preferences/PreferenceCells.d.ts +16 -0
- package/dist/Components/NotificationCenter/preferences/PreferenceCells.js +35 -0
- package/dist/Components/NotificationCenter/preferences/PreferenceGroups.d.ts +15 -0
- package/dist/Components/NotificationCenter/preferences/PreferenceGroups.js +26 -0
- package/dist/Components/NotificationCenter/preferences/preferencesLabels.d.ts +2 -0
- package/dist/Components/NotificationCenter/preferences/preferencesLabels.js +28 -0
- package/dist/Components/NotificationCenter/preferences/useAutosave.d.ts +8 -0
- package/dist/Components/NotificationCenter/preferences/useAutosave.js +41 -0
- package/dist/Components/NotificationCenter/row/NotificationDetail.d.ts +4 -0
- package/dist/Components/NotificationCenter/row/NotificationDetail.js +65 -0
- package/dist/Components/NotificationCenter/row/NotificationDetailProps.d.ts +15 -0
- package/dist/Components/NotificationCenter/row/NotificationDetailProps.js +2 -0
- package/dist/Components/NotificationCenter/row/NotificationRow.d.ts +3 -0
- package/dist/Components/NotificationCenter/row/NotificationRow.js +124 -0
- package/dist/Components/NotificationCenter/row/NotificationRowProps.d.ts +33 -0
- package/dist/Components/NotificationCenter/row/NotificationRowProps.js +2 -0
- package/dist/Components/NotificationCenter/row/ReasonForm.d.ts +14 -0
- package/dist/Components/NotificationCenter/row/ReasonForm.js +59 -0
- package/dist/Components/NotificationCenter/row/RowActions.d.ts +16 -0
- package/dist/Components/NotificationCenter/row/RowActions.js +36 -0
- package/dist/Components/NotificationCenter/row/RowQuickActions.d.ts +17 -0
- package/dist/Components/NotificationCenter/row/RowQuickActions.js +57 -0
- package/dist/Components/NotificationCenter/row/RowStatus.d.ts +10 -0
- package/dist/Components/NotificationCenter/row/RowStatus.js +42 -0
- package/dist/Components/NotificationCenter/row/category.d.ts +8 -0
- package/dist/Components/NotificationCenter/row/category.js +13 -0
- package/dist/Components/NotificationCenter/row/rowMenu.d.ts +14 -0
- package/dist/Components/NotificationCenter/row/rowMenu.js +37 -0
- package/dist/Components/NotificationCenter/row/rowParts.d.ts +40 -0
- package/dist/Components/NotificationCenter/row/rowParts.js +72 -0
- package/dist/Components/NotificationCenter/row/rowStyles.d.ts +11 -0
- package/dist/Components/NotificationCenter/row/rowStyles.js +25 -0
- package/dist/Components/SegmentedControl/segmentedInk.d.ts +4 -0
- package/dist/Components/SegmentedControl/segmentedInk.js +16 -2
- package/dist/Components/SideBar/SecondaryBar.js +2 -2
- package/dist/Components/SideBar/SideBar.js +18 -8
- package/dist/Components/SideBar/SideBarProps.d.ts +4 -2
- package/dist/Components/SideBar/components/MenuItemBox.d.ts +0 -2
- package/dist/Components/SideBar/components/MenuItemBox.js +2 -3
- package/dist/Components/SideBar/components/MenuItems.js +2 -2
- package/dist/Components/SideBar/components/PinnedSection.js +3 -4
- package/dist/Components/SideBar/sidebarScale.d.ts +0 -2
- package/dist/Components/SideBar/sidebarScale.js +0 -2
- package/dist/Components/Switch/Switch.d.ts +1 -1
- package/dist/Components/Switch/Switch.js +2 -2
- package/dist/Components/Switch/SwitchProps.d.ts +3 -0
- package/dist/Components/Toggle/TableToggle.js +4 -5
- package/dist/Components/Toggle/tableToggleInk.d.ts +7 -0
- package/dist/Components/Toggle/tableToggleInk.js +28 -0
- package/dist/esm/Components/NotificationBell/NotificationItem.js +1 -1
- package/dist/esm/Components/NotificationBell/NotificationList.js +1 -1
- package/dist/esm/Components/NotificationBell/NotificationPanel.js +1 -1
- package/dist/esm/Components/NotificationCenter/center/CenterPanel.js +81 -0
- package/dist/esm/Components/NotificationCenter/center/NotificationCenter.js +92 -0
- package/dist/esm/Components/NotificationCenter/center/NotificationCenterProps.js +1 -0
- package/dist/esm/Components/NotificationCenter/center/PanelRows.js +28 -0
- package/dist/esm/Components/NotificationCenter/center/PanelStates.js +11 -0
- package/dist/esm/Components/NotificationCenter/center/centerLabels.js +34 -0
- package/dist/esm/Components/NotificationCenter/center/useArrivals.js +67 -0
- package/dist/esm/Components/NotificationCenter/center/useIsSheet.js +16 -0
- package/dist/esm/Components/NotificationCenter/core/NotificationCenterProvider.js +35 -0
- package/dist/esm/Components/NotificationCenter/core/NotificationEngineProps.js +1 -0
- package/dist/esm/Components/NotificationCenter/core/NotificationSourceProps.js +1 -0
- package/dist/esm/Components/NotificationCenter/core/actionError.js +39 -0
- package/dist/esm/Components/NotificationCenter/core/format.js +87 -0
- package/dist/esm/Components/NotificationCenter/core/grouping.js +88 -0
- package/dist/esm/Components/NotificationCenter/core/labels.js +88 -0
- package/dist/esm/Components/NotificationCenter/core/overlay.js +274 -0
- package/dist/esm/Components/NotificationCenter/core/useNotificationEngine.js +408 -0
- package/dist/esm/Components/NotificationCenter/inbox/InboxList.js +18 -0
- package/dist/esm/Components/NotificationCenter/inbox/InboxStates.js +18 -0
- package/dist/esm/Components/NotificationCenter/inbox/NotificationInbox.js +396 -0
- package/dist/esm/Components/NotificationCenter/inbox/NotificationInboxProps.js +1 -0
- package/dist/esm/Components/NotificationCenter/inbox/ShortcutsDialog.js +23 -0
- package/dist/esm/Components/NotificationCenter/inbox/inboxLabels.js +52 -0
- package/dist/esm/Components/NotificationCenter/inbox/inboxModel.js +37 -0
- package/dist/esm/Components/NotificationCenter/inbox/useControllable.js +11 -0
- package/dist/esm/Components/NotificationCenter/index.js +8 -0
- package/dist/esm/Components/NotificationCenter/preferences/NotificationPreferences.js +67 -0
- package/dist/esm/Components/NotificationCenter/preferences/NotificationPreferencesProps.js +1 -0
- package/dist/esm/Components/NotificationCenter/preferences/PreferenceCells.js +27 -0
- package/dist/esm/Components/NotificationCenter/preferences/PreferenceGroups.js +21 -0
- package/dist/esm/Components/NotificationCenter/preferences/preferencesLabels.js +24 -0
- package/dist/esm/Components/NotificationCenter/preferences/useAutosave.js +37 -0
- package/dist/esm/Components/NotificationCenter/row/NotificationDetail.js +60 -0
- package/dist/esm/Components/NotificationCenter/row/NotificationDetailProps.js +1 -0
- package/dist/esm/Components/NotificationCenter/row/NotificationRow.js +119 -0
- package/dist/esm/Components/NotificationCenter/row/NotificationRowProps.js +1 -0
- package/dist/esm/Components/NotificationCenter/row/ReasonForm.js +52 -0
- package/dist/esm/Components/NotificationCenter/row/RowActions.js +28 -0
- package/dist/esm/Components/NotificationCenter/row/RowQuickActions.js +49 -0
- package/dist/esm/Components/NotificationCenter/row/RowStatus.js +35 -0
- package/dist/esm/Components/NotificationCenter/row/category.js +9 -0
- package/dist/esm/Components/NotificationCenter/row/rowMenu.js +30 -0
- package/dist/esm/Components/NotificationCenter/row/rowParts.js +59 -0
- package/dist/esm/Components/NotificationCenter/row/rowStyles.js +19 -0
- package/dist/esm/Components/SegmentedControl/segmentedInk.js +15 -2
- package/dist/esm/Components/SideBar/SecondaryBar.js +3 -3
- package/dist/esm/Components/SideBar/SideBar.js +18 -8
- package/dist/esm/Components/SideBar/components/MenuItemBox.js +2 -3
- package/dist/esm/Components/SideBar/components/MenuItems.js +2 -2
- package/dist/esm/Components/SideBar/components/PinnedSection.js +3 -4
- package/dist/esm/Components/SideBar/sidebarScale.js +0 -2
- package/dist/esm/Components/Switch/Switch.js +2 -2
- package/dist/esm/Components/Toggle/TableToggle.js +4 -5
- package/dist/esm/Components/Toggle/tableToggleInk.js +24 -0
- package/dist/esm/index.js +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +16 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,7 +11,70 @@ line below names its issue number.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## [3.1.0] — 2026-10-23
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`Switch` `size`** (`"sm"`, `"md"` or `"lg"`, default `"sm"`) passes through to `SegmentedControl`, so the pill takes the button
|
|
19
|
+
scale (32, 40 and 48px) like Button, Tabs and Dropdown. `"sm"` is the height it already rendered at, so omitted, the output is
|
|
20
|
+
exactly as before. The `/switch` demo gains a Sizes variant, a row beside a field and a button, and a Playground `size` control.
|
|
21
|
+
- **`NotificationCenter`** — the bell and its panel for any app. The panel has exactly two tabs, Needs you and Activity, with
|
|
22
|
+
inline Approve and Reject, a six-second Undo, threads, mark-all-read and loading, error, empty, offline and no-access
|
|
23
|
+
states. A popover on desktop and a full-height `variant="sheet"` on phones. The bell shows the server's Needs-you
|
|
24
|
+
count (99+), else a dot for unread, else nothing.
|
|
25
|
+
- **`NotificationInbox`** — the full-page inbox: Needs you, Activity and Archived tabs, search, a type filter, unread-only,
|
|
26
|
+
bulk approve, read and archive, a reading pane, and a phone list-to-detail flow. `tab` and `selectedId` can be
|
|
27
|
+
controlled, so a URL can deep-link to one entry.
|
|
28
|
+
- **`NotificationPreferences`** — the notification settings page: a type-by-channel grid with locked cells, an optional
|
|
29
|
+
email-summary choice, autosave per cell, and a rollback with a toast when `onChange` rejects.
|
|
30
|
+
- **`NotificationCenterProvider` / `useNotificationCenter`** — one optimistic overlay, one undo queue and one polite live
|
|
31
|
+
region shared by every notification component inside it. Pass the source props to a component directly, or to a
|
|
32
|
+
provider and none to the components, so the bell and the inbox move together.
|
|
33
|
+
- **`NotificationRow` / `NotificationDetail`** — one entry as a list row (`panel`, `inbox` and `phone` variants) and one
|
|
34
|
+
entry in full, for building your own surface on the same engine.
|
|
35
|
+
- **`notificationActionError` / `isNotificationActionError` / `toNotificationActionError`** — reject `onAction` with a kind
|
|
36
|
+
(`conflict`, `forbidden`, `reasonRequired` or `failed`) to get the matching row state, status line and toast. A plain
|
|
37
|
+
HTTP 409 or 403 maps to conflict and forbidden.
|
|
38
|
+
- **`defaultNotificationLabels(locale)` / `DEFAULT_NOTIFICATION_LOCALE`** — the English strings every notification component
|
|
39
|
+
falls back to; override any key through `labels`. Dates and plurals use `Intl` with the `locale`.
|
|
40
|
+
- Types for all of the above, including `NotificationEntry`, `NotificationAction`, `NotificationCategories` and
|
|
41
|
+
`NotificationSourceProps`.
|
|
42
|
+
|
|
43
|
+
### Deprecated
|
|
44
|
+
|
|
45
|
+
Each of these still ships its full implementation and behaves as before.
|
|
46
|
+
|
|
47
|
+
- **`NotificationPanel`** — use `NotificationCenter` for the bell panel and `NotificationInbox` for the page.
|
|
48
|
+
- **`NotificationList`** — use `NotificationInbox`, or `NotificationCenter` for the bell panel.
|
|
49
|
+
- **`NotificationItem`** — use `NotificationRow` inside a `NotificationCenterProvider`.
|
|
50
|
+
|
|
51
|
+
`NotificationBell` stays: `NotificationCenter` wraps it. `Notification` (the countdown card) is unrelated and not deprecated.
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- **`SegmentedControl`'s default track is pale on Zinc light.** Zinc's `background[600]` is a mid-grey (`#71717a`), so the
|
|
56
|
+
track rendered dark with near-white labels. A light track now stays no darker than the control outline (`boxborder[500]`),
|
|
57
|
+
so Zinc light uses its own `background[400]` (`#d4d4d8`) with dark labels. The other eight brands and every dark palette
|
|
58
|
+
are unchanged.
|
|
59
|
+
- **`TableToggle` gets the same pale track on Zinc light, with a visible hover.** It shares `SegmentedControl`'s track
|
|
60
|
+
step and keeps its hover fill and icons at the same offsets from it: Zinc light now has a `#d4d4d8` track, `#f4f4f5`
|
|
61
|
+
hover and `#71717a` icons. Zinc light's `brandSlide` and `underlineSlide` resting icons were light grey on the white
|
|
62
|
+
canvas (1.60:1) and now clear 3:1. The other eight brands and every dark palette are unchanged.
|
|
63
|
+
- **`SideBar` drawer rows use the docked row height, not 44px.** Below `lg`, and on desktop after the footer's Hide, the
|
|
64
|
+
rail rows, pinned rows, group captions, dividers, Add New and the user footer each had taller metrics of their own: rows
|
|
65
|
+
sat 46px apart in the drawer against 34px docked, so a 20-row menu's list was 1108px tall instead of 904px. The drawer now
|
|
66
|
+
renders the docked metrics (32px rows, 34px apart) and every row stays above the WCAG 2.5.8 24px target.
|
|
67
|
+
`SIDEBAR_ROW.overlayHeight` is removed (internal; 0 references in hrms, lfour, tickets, crm-frontend, account-frontend)
|
|
68
|
+
and `RenderMenuType.overlay` is `@deprecated`, now unread.
|
|
69
|
+
- **`SideBar` drawer: the `SecondaryBar` no longer overflows the panel on narrow phones.** The panel is capped at 88vw but the
|
|
70
|
+
primary column (14.7rem) plus the secondary's label floor need about 329px, so on a 320px phone the secondary ended
|
|
71
|
+
47.6px past the panel and 9px past the screen, on a 360px phone 12.4px past the panel, and a long label overflowed even
|
|
72
|
+
at 375px (33px) and 390px (20px), pushing its toggle off screen at 320px. In the drawer the secondary now shrinks
|
|
73
|
+
to its icon strip (`SecondaryBarProps.shrinkable`, set by `SideBar`), the primary column gives up the difference
|
|
74
|
+
(`max-width: calc(88vw - 1px - 3.5rem)`), and from 23.3em (about 373px) down it starts as the strip; its header toggle
|
|
75
|
+
stays visible when squeezed. Measured in Chromium emulation at 320, 360, 375, 390, 430 and 844px wide: every width
|
|
76
|
+
now ends at the panel edge in both states; at 375px and wider the default look is unchanged (at 375px the secondary is 0.17px
|
|
77
|
+
narrower, which removes its sub-pixel overlap of the panel border). Docked layout is untouched.
|
|
15
78
|
|
|
16
79
|
## [3.0.0] — 2026-10-21
|
|
17
80
|
|
package/COMPONENT-GOTCHAS.md
CHANGED
|
@@ -415,7 +415,7 @@ The wrapper is deliberately not a button; make the custom component a button, or
|
|
|
415
415
|
| `Table` | Radius `xl`; row height 32/40/52 by density; filter button is named "Filter {label}" (tests querying "Search" break); client pagination resets to page 1 when `data.length` changes | — |
|
|
416
416
|
| `KanbanBoard` | `height`/`maxH` props replace the clipping wrapper; `height="100%"` needs a parent with a definite height | — |
|
|
417
417
|
| `ProfileCard` / `SideBar` | `variant="outlein"` now draws an outline; header divider 1px `boxborder[200]` / mobile backdrop uses the shared scrim | — |
|
|
418
|
-
| `SideBar` | Scroll-container and submenu ids are generated (`useId`) — `#sidebar-scroll-container` / `#submenu-<label>` no longer exist; rail rows 32px
|
|
418
|
+
| `SideBar` | Scroll-container and submenu ids are generated (`useId`) — `#sidebar-scroll-container` / `#submenu-<label>` no longer exist; rail rows 32px, the mobile drawer's too, submenu rows 28px; count badges use soft tone. In the drawer, a menu with `secondaryItems` starts its panel as the icon strip (labels only as `aria-label`s until expanded) on screens up to ~373px, and the panel can no longer overflow the drawer | — |
|
|
419
419
|
| `ToggleSwitch` | No `aria-required` on the switch (invalid for `role=switch`); `isRequired` still shows `*` | — |
|
|
420
420
|
| `KanbanBoard` / `PdfViewer` / `UserDetails` | Cards, drag handles and toolbar links get accessible names; scroll panels are focusable regions; `UserDetails` badges 12px | — |
|
|
421
421
|
| `NumberInput` / `FileUpload` | Steppers exactly half the field height; drop-zone border 1px (was 2px) | — |
|
|
@@ -557,6 +557,13 @@ use `bandOn(fill, surface)` for a header strip, hover or selection tint, `recess
|
|
|
557
557
|
or off cell, `cardRule(colors)` for a rule inside a card and `raisedRule(colors)` for a rule inside an overlay.
|
|
558
558
|
All four return the old value unchanged in light mode.
|
|
559
559
|
|
|
560
|
+
### `background[600]` is a mid-grey on Zinc, not a pale tint
|
|
561
|
+
|
|
562
|
+
The light `background` ramps are hand-written per brand. Eight stay pale through `[600]` (1.41-1.91:1 off the canvas);
|
|
563
|
+
Zinc is a full neutral ramp whose `[600]` is `#71717a` (4.83:1), dark enough that ink lifted to AA on it turns white.
|
|
564
|
+
Pick a pale fill by contrast, never by step number: the default tracks of `SegmentedControl` and `TableToggle` keep
|
|
565
|
+
`[600]` only where it is no darker than `boxborder[500]`.
|
|
566
|
+
|
|
560
567
|
### A Chakra `ModalHeader` inside a coloured box keeps its own ink
|
|
561
568
|
|
|
562
569
|
The theme gives `ModalHeader` `color: colors.header[900]` and a bottom rule. Wrapping it in a
|
|
@@ -844,26 +851,43 @@ what `isLoaded` is set to. Neither `Skeletons` nor `ProgressBar` forwards a `ref
|
|
|
844
851
|
**Apply:** for `circle`/`text`, control visibility by conditionally rendering the `Skeletons` element
|
|
845
852
|
itself, not via `isLoaded`.
|
|
846
853
|
|
|
847
|
-
###
|
|
854
|
+
### Eight exports start with `Notification` and only the Notification Center family is a tray
|
|
848
855
|
|
|
849
|
-
Autocomplete offers `Notification`, `NotificationBell`, `NotificationPanel`, `NotificationList` and
|
|
850
|
-
`
|
|
851
|
-
different era, and picking it for an inbox row is the
|
|
856
|
+
Autocomplete offers `Notification`, `NotificationBell`, `NotificationPanel`, `NotificationList`, `NotificationItem` and the
|
|
857
|
+
newer `NotificationCenter`, `NotificationInbox`, `NotificationPreferences`, `NotificationRow`, `NotificationDetail`. Only
|
|
858
|
+
`Notification` is unrelated — a standalone countdown card from a different era, and picking it for an inbox row is the
|
|
859
|
+
common mistake.
|
|
852
860
|
|
|
853
861
|
| Export | What it actually is |
|
|
854
862
|
| --- | --- |
|
|
855
|
-
| `
|
|
856
|
-
| `
|
|
857
|
-
| `
|
|
858
|
-
| `
|
|
859
|
-
| `
|
|
863
|
+
| `NotificationCenter` | **Use this.** The bell plus a two-tab panel (Needs you, Activity) with inline approvals and undo |
|
|
864
|
+
| `NotificationInbox` | **Use this** for the full page: tabs, search, bulk actions, reading pane |
|
|
865
|
+
| `NotificationPreferences` | **Use this** for the settings page: type-by-channel switches with autosave |
|
|
866
|
+
| `NotificationRow` / `NotificationDetail` | One entry as a row / in full, for your own surface inside a `NotificationCenterProvider` |
|
|
867
|
+
| `NotificationBell` | The badge/bell button only. Requires `aria-label`; it renders no list. `NotificationCenter` wraps it |
|
|
868
|
+
| `NotificationPanel`, `NotificationList`, `NotificationItem` | **Deprecated**, behaviour unchanged. Replaced by `NotificationCenter`, `NotificationInbox` and `NotificationRow` |
|
|
869
|
+
| `Notification` | **Not deprecated.** A dated countdown card — its dot colour comes from `dueDate`/`endDate` math, not from a tone. `NotificationRow` cannot replace it: `formatRelativeTime` clamps every future date to "Just now" |
|
|
860
870
|
|
|
861
871
|
`Notification` is kept only because nothing else does that date-driven countdown; it is not a thin
|
|
862
872
|
alias, so it has not been removed.
|
|
863
873
|
|
|
864
|
-
**Apply:** for a notification tray use `
|
|
865
|
-
|
|
866
|
-
|
|
874
|
+
**Apply:** for a notification tray use `NotificationCenter`; for a page, `NotificationInbox`. Reach for `Notification` only
|
|
875
|
+
when you specifically want the deadline-countdown card, and read the gotcha below before you do.
|
|
876
|
+
|
|
877
|
+
### Notification Center: four silent traps
|
|
878
|
+
|
|
879
|
+
- **Counts must come from the server.** `counts.needsYou` and `counts.unread` are never derived from the loaded `entries`,
|
|
880
|
+
because a paged list under-counts. The library only adjusts them optimistically and never below 0.
|
|
881
|
+
- **`onAction` must reject with a kind to get the right row.** `notificationActionError("conflict" | "forbidden" |
|
|
882
|
+
"reasonRequired" | "failed", message?)` (or an HTTP 409/403) picks the status line. Any other rejection reads as
|
|
883
|
+
`failed` with Try again, so a "someone already decided this" case looks like a retryable error.
|
|
884
|
+
- **A feature exists only when its callback is passed.** No `onArchive` means no Archived tab and no archive button; no
|
|
885
|
+
`onRestore` no restore; no `onAction` no decision buttons; no `onMarkRead` no read toggle. Nothing is disabled, it is
|
|
886
|
+
absent.
|
|
887
|
+
- **One provider per page, or the components do not share state.** `NotificationCenter` and `NotificationInbox` given their
|
|
888
|
+
own source props each run a private provider with its own undo queue. To share, pass the props to one
|
|
889
|
+
`NotificationCenterProvider` and none to the components. Keep `categories` and `labels` stable references, and mount a
|
|
890
|
+
`ToasterProvider` above (`PixelizeThemeProvider` supplies one).
|
|
867
891
|
|
|
868
892
|
### `Notification`'s `onClick` only reaches the title text, and its status dot has two gates
|
|
869
893
|
|
package/DESIGN-GUIDE.md
CHANGED
|
@@ -191,7 +191,9 @@ sections are `Card` with the title in `header`.
|
|
|
191
191
|
| Per-day activity (leave, tasks, shifts) | `CalendarGrid` with `events` mapped from your data — don't hand-roll a month grid |
|
|
192
192
|
| Voice note capture | `AudioRecorder`; `MediaAudioPlayer` for playback |
|
|
193
193
|
| Previewing an attachment (image, video, audio, PDF, Office, text, archive) | `MediaViewer` (`items`, `isOpen`, `onClose`) — omit `index` and it owns navigation itself |
|
|
194
|
-
| Notification tray | `
|
|
194
|
+
| Notification tray | `NotificationCenter` — bell plus a two-tab panel (Needs you, Activity) with inline approvals and undo; `NotificationBell` alone is just the badge. `NotificationPanel`, `NotificationList` and `NotificationItem` are deprecated |
|
|
195
|
+
| Notification inbox page | `NotificationInbox` (tabs, search, type filter, bulk actions, reading pane, `variant="phone"`) — share one `NotificationCenterProvider` with the bell |
|
|
196
|
+
| Notification settings page | `NotificationPreferences` (`channels`, `groups`, `values`, `onChange`; locked cells, autosave with rollback) |
|
|
195
197
|
| Confirm that needs the name typed to arm it | `ConfirmDialog confirmationText="delete-me"` — `onConfirm` does **not** close; call `onClose` yourself |
|
|
196
198
|
| Scrim over the page or one panel while something runs | `Backdrop open loader` — `variant="dim"` by default. Add `container` to scope it to the nearest positioned ancestor, `allowBackgroundInteraction` to let clicks through, `onClick` to dismiss |
|
|
197
199
|
| A short "saved" / "copied" / "failed" notice | `useToaster().showToast({ title, status })` (`PixelizeThemeProvider` and `withTheme` mount the provider) — not a `ToolTip`, not an `AlertDialog` |
|
|
@@ -314,6 +316,7 @@ not shipped in this package). Start from the closest one:
|
|
|
314
316
|
| List of records | `records.tsx` | saved views, `FilterBar`, `Table` (`isGlobalSearch={false}`, `Tag` cells, row selection), `BulkSelectionBar` (count, facts, ranked actions, clear), `AlertDialog tone="danger"`, create `Drawer` |
|
|
315
317
|
| One record | `recordDetail.tsx` | `PageHeader` with breadcrumbs/avatar/meta, `Tabs` (declarative `items` with `count` badges), `DescriptionList` (grid + inline side panel), `Timeline` (per-event `icon`/`description`), `ErrorState` + back |
|
|
316
318
|
| Settings / long form | `settings.tsx` | `SectionRail` (rail from `lg`, `Select` below, dirty dots), `SettingsSection` + `SettingRow`, `SaveBar`, danger zone |
|
|
319
|
+
| Notification inbox and settings | `demo.notificationInbox.tsx` (`/notificationinbox`), `demo.notificationPreferences.tsx` (`/notificationpreferences`) in the **Feedback & overlays** group | `NotificationInbox` desktop, phone, shared provider with the bell and all load states; `NotificationPreferences` with locked cells, autosave rollback and read-only |
|
|
317
320
|
| Board | `pipeline.tsx` | `KanbanBoard` with custom cards and column totals (`height`/`maxH` to size it) |
|
|
318
321
|
| Inbox / master–detail | `inbox.tsx` | selectable list rows, conversation pane, composer, mobile list↔detail |
|
|
319
322
|
| App launcher (browse, pins, recents, search with actions) | `launcher.tsx` (`/examples/launcher`) | `NavLauncher` with an app-owned registry and role-based permission filter, pinned / frequent / recent ids, `linkAs`, remote results via `extraResults`, `isLoading`, Classic / Spotlight toggle, synonyms, scopes, quick actions, company facts with Copy, answers |
|
|
@@ -61,7 +61,7 @@ export interface NotificationItemModel {
|
|
|
61
61
|
}
|
|
62
62
|
/** How the unread state reads on a row. Defaults to "bar" (the 2px left border both apps use). */
|
|
63
63
|
export type NotificationUnreadIndicator = "bar" | "dot" | "tint" | "none";
|
|
64
|
-
/**
|
|
64
|
+
/** @deprecated Build rows with NotificationRow inside a NotificationCenterProvider. */
|
|
65
65
|
export interface NotificationItemProps {
|
|
66
66
|
/** The mapped row model that this chassis paints. */
|
|
67
67
|
item: NotificationItemModel;
|
|
@@ -82,7 +82,7 @@ export interface NotificationItemProps {
|
|
|
82
82
|
/** Fires when a collapsed run's "Show N updates" affordance is activated. */
|
|
83
83
|
onExpandGroup?: () => void;
|
|
84
84
|
}
|
|
85
|
-
/**
|
|
85
|
+
/** @deprecated Use NotificationCenter or NotificationInbox, which take NotificationEntry rows. */
|
|
86
86
|
export interface NotificationListProps<T> {
|
|
87
87
|
/** Rows in display order; each is mapped through `toItem` for rendering and, with `groupBy` "day", bucketed by its timestamp. */
|
|
88
88
|
items: T[];
|
|
@@ -131,7 +131,7 @@ export interface NotificationFilter {
|
|
|
131
131
|
/** Shown even when 0, unlike a zone count; capped at 99 like the badge. Omit for no suffix. */
|
|
132
132
|
count?: number;
|
|
133
133
|
}
|
|
134
|
-
/**
|
|
134
|
+
/** @deprecated Use NotificationCenter for the bell panel and NotificationInbox for the page. */
|
|
135
135
|
export interface NotificationPanelProps<T> extends NotificationListProps<T> {
|
|
136
136
|
/** Omit for a single-zone panel (crm, spare-cart). Two zones is the tickets/hrms shape. */
|
|
137
137
|
zones?: NotificationZone[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { NotificationItemProps } from "./NotificationBellProps";
|
|
2
|
-
/**
|
|
2
|
+
/** @deprecated Use NotificationRow inside a NotificationCenterProvider. */
|
|
3
3
|
declare const NotificationItem: ({ item, onClick, actions, actionsAlwaysVisible, preview, trailing, unreadIndicator, groupCount, onExpandGroup, }: NotificationItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
export default NotificationItem;
|
|
@@ -14,7 +14,7 @@ const tone_1 = require("../../Theme/tokens/builders/tone");
|
|
|
14
14
|
const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
|
|
15
15
|
const notificationHelpers_1 = require("./notificationHelpers");
|
|
16
16
|
const TILE_SIZE = "1.75rem";
|
|
17
|
-
/**
|
|
17
|
+
/** @deprecated Use NotificationRow inside a NotificationCenterProvider. */
|
|
18
18
|
const NotificationItem = ({ item, onClick, actions, actionsAlwaysVisible = false, preview, trailing, unreadIndicator = "bar", groupCount, onExpandGroup, }) => {
|
|
19
19
|
var _a, _b, _c;
|
|
20
20
|
const theme = (0, useCustomTheme_1.useCustomTheme)();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { NotificationListProps } from "./NotificationBellProps";
|
|
2
|
-
/**
|
|
2
|
+
/** @deprecated Use NotificationInbox, or NotificationCenter for the bell panel. */
|
|
3
3
|
declare function NotificationList<T>({ items, toItem, groupBy, collapseGroups, dayLabels, onItemClick, itemActions, renderItem, renderSectionHeader, unreadIndicator, actionsAlwaysVisible, "aria-label": ariaLabel, }: NotificationListProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
export default NotificationList;
|
|
@@ -10,7 +10,7 @@ const EmptyState_1 = __importDefault(require("../EmptyState/EmptyState"));
|
|
|
10
10
|
const NotificationItem_1 = __importDefault(require("./NotificationItem"));
|
|
11
11
|
const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
12
12
|
const notificationHelpers_1 = require("./notificationHelpers");
|
|
13
|
-
/**
|
|
13
|
+
/** @deprecated Use NotificationInbox, or NotificationCenter for the bell panel. */
|
|
14
14
|
function NotificationList({ items, toItem, groupBy = "day", collapseGroups = false, dayLabels = notificationHelpers_1.DEFAULT_DAY_LABELS, onItemClick, itemActions, renderItem, renderSectionHeader, unreadIndicator = "bar", actionsAlwaysVisible = false, "aria-label": ariaLabel, }) {
|
|
15
15
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
16
16
|
const [expandedGroups, setExpandedGroups] = (0, react_1.useState)(new Set());
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { NotificationPanelProps } from "./NotificationBellProps";
|
|
2
|
-
/**
|
|
2
|
+
/** @deprecated Use NotificationCenter, which wraps NotificationBell with a two-tab panel. */
|
|
3
3
|
declare function NotificationPanel<T>({ items, toItem, zones, activeZone, onZoneChange, filters, activeFilter, onFilterChange, isLoading, error, onRetry, empty, header, headerAction, footer, variant, maxH, onLoadMore, hasMore, isLoadingMore, panelRef, groupBy, collapseGroups, dayLabels, onItemClick, itemActions, renderItem, renderSectionHeader, unreadIndicator, actionsAlwaysVisible, "aria-label": ariaLabel, }: NotificationPanelProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
4
4
|
export default NotificationPanel;
|
|
@@ -15,7 +15,7 @@ const useCustomTheme_1 = require("../../Theme/useCustomTheme");
|
|
|
15
15
|
const surfaceRaised_1 = require("../../Theme/tokens/builders/surfaceRaised");
|
|
16
16
|
const notificationHelpers_1 = require("./notificationHelpers");
|
|
17
17
|
const DEFAULT_EMPTY = { title: "No notifications yet", description: "You're all caught up.", size: "sm" };
|
|
18
|
-
/**
|
|
18
|
+
/** @deprecated Use NotificationCenter, which wraps NotificationBell with a two-tab panel. */
|
|
19
19
|
function NotificationPanel({ items, toItem, zones, activeZone, onZoneChange, filters, activeFilter, onFilterChange, isLoading = false, error, onRetry, empty, header, headerAction, footer, variant = "popover", maxH, onLoadMore, hasMore = false, isLoadingMore = false, panelRef, groupBy, collapseGroups, dayLabels, onItemClick, itemActions, renderItem, renderSectionHeader, unreadIndicator, actionsAlwaysVisible, "aria-label": ariaLabel, }) {
|
|
20
20
|
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
21
21
|
const internalRef = (0, react_1.useRef)(null);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { NotificationCenterLabels, NotificationCenterTab } from "./NotificationCenterProps";
|
|
2
|
+
export declare const PANEL_ROW_LIMIT = 20;
|
|
3
|
+
interface CenterPanelProps {
|
|
4
|
+
isSheet: boolean;
|
|
5
|
+
tab: NotificationCenterTab;
|
|
6
|
+
onTabChange: (tab: NotificationCenterTab) => void;
|
|
7
|
+
labels: NotificationCenterLabels;
|
|
8
|
+
titleId: string;
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
onOpenInbox?: () => void;
|
|
11
|
+
onOpenSettings?: () => void;
|
|
12
|
+
"data-testid"?: string;
|
|
13
|
+
}
|
|
14
|
+
declare const CenterPanel: import("react").ForwardRefExoticComponent<CenterPanelProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
15
|
+
export default CenterPanel;
|
|
@@ -0,0 +1,87 @@
|
|
|
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.PANEL_ROW_LIMIT = 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 Primitives_1 = require("../../Primitives/Primitives");
|
|
11
|
+
const Button_1 = __importDefault(require("../../Button/Button"));
|
|
12
|
+
const IconButton_1 = __importDefault(require("../../IconButton/IconButton"));
|
|
13
|
+
const ToolTip_1 = __importDefault(require("../../ToolTip/ToolTip"));
|
|
14
|
+
const Tabs_1 = __importDefault(require("../../Tabs/Tabs"));
|
|
15
|
+
const TabsContext_1 = require("../../Tabs/TabsContext");
|
|
16
|
+
const VisuallyHidden_1 = __importDefault(require("../../VisuallyHidden/VisuallyHidden"));
|
|
17
|
+
const useCustomTheme_1 = require("../../../Theme/useCustomTheme");
|
|
18
|
+
const surfaceRaised_1 = require("../../../Theme/tokens/builders/surfaceRaised");
|
|
19
|
+
const scrollbar_1 = require("../../../Theme/tokens/builders/scrollbar");
|
|
20
|
+
const filledSurface_1 = require("../../../Theme/tokens/builders/filledSurface");
|
|
21
|
+
const tone_1 = require("../../../Theme/tokens/builders/tone");
|
|
22
|
+
const focusRing_styles_1 = require("../../../Theme/chakra/focusRing.styles");
|
|
23
|
+
const NotificationCenterProvider_1 = require("../core/NotificationCenterProvider");
|
|
24
|
+
const grouping_1 = require("../core/grouping");
|
|
25
|
+
const PanelStates_1 = require("./PanelStates");
|
|
26
|
+
const PanelRows_1 = require("./PanelRows");
|
|
27
|
+
const useArrivals_1 = require("./useArrivals");
|
|
28
|
+
exports.PANEL_ROW_LIMIT = 20;
|
|
29
|
+
const TAB_COUNT_CAP = 99;
|
|
30
|
+
const CenterPanel = (0, react_1.forwardRef)(function CenterPanel({ isSheet, tab, onTabChange, labels, titleId, onClose, onOpenInbox, onOpenSettings, "data-testid": testId }, ref) {
|
|
31
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
32
|
+
const center = (0, NotificationCenterProvider_1.useNotificationCenter)();
|
|
33
|
+
const { lists, counts, categories, features, format, now, status, isOffline, loadedAt, appName, actions } = center;
|
|
34
|
+
const bodyRef = (0, react_1.useRef)(null);
|
|
35
|
+
const all = tab === "needsYou" ? lists.needsYou : lists.activity;
|
|
36
|
+
const ids = (0, react_1.useMemo)(() => all.map((item) => item.entry.id), [all]);
|
|
37
|
+
const { heldIds, reveal, track } = (0, useArrivals_1.useArrivals)(ids, tab, bodyRef);
|
|
38
|
+
const visible = (0, react_1.useMemo)(() => {
|
|
39
|
+
const held = new Set(heldIds);
|
|
40
|
+
return all.slice(0, exports.PANEL_ROW_LIMIT).filter((item) => !held.has(item.entry.id));
|
|
41
|
+
}, [all, heldIds]);
|
|
42
|
+
const groups = (0, react_1.useMemo)(() => tab === "needsYou"
|
|
43
|
+
? (0, grouping_1.groupByCategory)(visible, categories, center.labels.otherCategory)
|
|
44
|
+
: (0, grouping_1.groupByDay)(visible, format, now), [tab, visible, categories, center.labels.otherCategory, format, now]);
|
|
45
|
+
const isReady = status === "ready";
|
|
46
|
+
const hasUnreadHere = all.some((item) => item.entry.isRead === false && !item.state.isHidden);
|
|
47
|
+
const canMarkAll = tab === "activity" ? counts.unread > 0 || hasUnreadHere : hasUnreadHere;
|
|
48
|
+
const cap = (count) => (count > TAB_COUNT_CAP ? labels.overflowCount(TAB_COUNT_CAP) : format.number(count));
|
|
49
|
+
const tabItems = [
|
|
50
|
+
{ id: "needsYou", label: labels.needsYouTab, count: isReady && counts.needsYou > 0 ? cap(counts.needsYou) : undefined },
|
|
51
|
+
{ id: "activity", label: labels.activityTab, count: isReady && counts.unread > 0 ? cap(counts.unread) : undefined },
|
|
52
|
+
];
|
|
53
|
+
const openItem = (0, react_1.useCallback)((item, event) => {
|
|
54
|
+
actions.open(item.entry, event);
|
|
55
|
+
onClose();
|
|
56
|
+
}, [actions, onClose]);
|
|
57
|
+
const pill = (0, filledSurface_1.resolveFilledSurface)(colors.primary, colors);
|
|
58
|
+
const banner = (0, tone_1.resolveSoftTone)(colors, (0, tone_1.toneScale)(colors, "neutral"), colors.surfaceRaised);
|
|
59
|
+
const rule = (0, surfaceRaised_1.raisedRule)(colors);
|
|
60
|
+
const toolSize = isSheet ? "md" : "sm";
|
|
61
|
+
const iconPx = isSheet ? 18 : 16;
|
|
62
|
+
const markAll = () => actions.markAllRead({ zone: tab });
|
|
63
|
+
let content;
|
|
64
|
+
if (status === "loading")
|
|
65
|
+
content = (0, jsx_runtime_1.jsx)(PanelStates_1.PanelSkeleton, { isSheet: isSheet });
|
|
66
|
+
else if (status === "error")
|
|
67
|
+
content = (0, jsx_runtime_1.jsx)(PanelStates_1.PanelError, { labels: labels, onRetry: actions.retryLoad });
|
|
68
|
+
else if (status === "noAccess")
|
|
69
|
+
content = (0, jsx_runtime_1.jsx)(PanelStates_1.PanelNoAccess, { labels: labels, appName: appName });
|
|
70
|
+
else if (groups.length === 0)
|
|
71
|
+
content = (0, jsx_runtime_1.jsx)(PanelStates_1.PanelEmpty, { labels: labels, tab: tab });
|
|
72
|
+
else
|
|
73
|
+
content = (0, jsx_runtime_1.jsx)(PanelRows_1.PanelRows, { groups: groups, variant: isSheet ? "phone" : "panel", format: format, onOpenItem: openItem });
|
|
74
|
+
return ((0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { ref: ref, direction: "column", flex: 1, minH: 0, w: "100%", bg: colors.surfaceRaised, color: colors.text[800], "data-testid": testId, children: [(0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { align: "center", justify: "space-between", gap: 2, flexShrink: 0, minH: isSheet ? "3.5rem" : "3rem", pl: 4, pr: 2, pt: 2, pb: 1, children: [(0, jsx_runtime_1.jsx)(Primitives_1.Heading, { as: "h2", id: titleId, fontSize: "1rem", fontWeight: 600, lineHeight: "1.5", color: colors.header[900], children: labels.title }), (0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { align: "center", gap: 1, children: [isReady && features.markAllRead && (isSheet ? ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: labels.markAllRead, children: (0, jsx_runtime_1.jsx)(IconButton_1.default, { size: "md", variant: "ghost", colorScheme: "gray", "aria-label": labels.markAllRead, icon: (0, jsx_runtime_1.jsx)(Primitives_1.Icon, { as: lucide_react_1.CheckCheck, boxSize: "1.125rem", "aria-hidden": "true" }), isDisabled: isOffline || !canMarkAll, onClick: markAll }) })) : ((0, jsx_runtime_1.jsx)(Button_1.default, { size: "sm", variant: "ghost", colorScheme: "gray", label: labels.markAllRead, leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.CheckCheck, { size: iconPx, "aria-hidden": "true" }), isDisabled: isOffline || !canMarkAll, onClick: markAll }))), onOpenSettings && ((0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: labels.settings, children: (0, jsx_runtime_1.jsx)(IconButton_1.default, { size: toolSize, variant: "ghost", colorScheme: "gray", "aria-label": labels.settings, icon: (0, jsx_runtime_1.jsx)(Primitives_1.Icon, { as: lucide_react_1.Settings, boxSize: `${iconPx / 16}rem`, "aria-hidden": "true" }), onClick: onOpenSettings }) })), isSheet && ((0, jsx_runtime_1.jsx)(IconButton_1.default, { size: "md", variant: "ghost", colorScheme: "gray", "aria-label": labels.close, icon: (0, jsx_runtime_1.jsx)(Primitives_1.Icon, { as: lucide_react_1.X, boxSize: "1.125rem", "aria-hidden": "true" }), onClick: onClose }))] })] }), isOffline && ((0, jsx_runtime_1.jsxs)(Primitives_1.Flex, { role: "status", align: "flex-start", gap: 2, flexShrink: 0, px: 4, py: 2, fontSize: "xs", lineHeight: "1.5", bg: banner.bg, color: banner.fg, borderBottom: `1px solid ${rule}`, children: [(0, jsx_runtime_1.jsx)(Primitives_1.Icon, { as: lucide_react_1.WifiOff, boxSize: "0.875rem", mt: "0.125rem", "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("span", { children: labels.offlineBanner(loadedAt === undefined ? undefined : format.absolute(loadedAt, now)) })] })), (0, jsx_runtime_1.jsx)(Primitives_1.Box, { px: 2, flexShrink: 0, children: (0, jsx_runtime_1.jsx)(Tabs_1.default, { id: `${titleId}-tabs`, items: tabItems, value: tab, onChange: (value) => onTabChange(value), variant: "line", size: isSheet ? "md" : "sm", isFitted: true, "aria-label": labels.tabsLabel }) }), (0, jsx_runtime_1.jsxs)(Primitives_1.Box, { ref: bodyRef, role: "tabpanel", "aria-labelledby": (0, TabsContext_1.tabId)(`${titleId}-tabs`, tab), "aria-busy": status === "loading" ? true : undefined, tabIndex: -1, flex: 1, minH: 0, overflowY: "auto", position: "relative", sx: {
|
|
75
|
+
overscrollBehavior: "contain",
|
|
76
|
+
...(0, scrollbar_1.scrollbarStyles)(colors, "hover"),
|
|
77
|
+
"&:focus-visible": { outline: "none", boxShadow: (0, focusRing_styles_1.insetFocusRingShadow)(colors) },
|
|
78
|
+
}, ...track, children: [heldIds.length > 0 && ((0, jsx_runtime_1.jsx)(Primitives_1.Box, { position: "sticky", top: 2, zIndex: 5, h: 0, display: "flex", justifyContent: "center", pointerEvents: "none", children: (0, jsx_runtime_1.jsx)(Button_1.default, { size: isSheet ? "md" : "xs", mt: 2, h: isSheet ? undefined : "1.75rem", borderRadius: "full", "aria-label": labels.newCountName(heldIds.length), leftIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronUp, { size: 14, "aria-hidden": "true" }), label: labels.newCount(heldIds.length), onClick: reveal, sx: {
|
|
79
|
+
bg: pill.fill,
|
|
80
|
+
color: pill.color,
|
|
81
|
+
boxShadow: "md",
|
|
82
|
+
pointerEvents: "auto",
|
|
83
|
+
"&:hover:not(:disabled)": { bg: pill.hoverFill, color: pill.hoverColor },
|
|
84
|
+
"&:active:not(:disabled)": { bg: pill.activeFill, color: pill.activeColor },
|
|
85
|
+
} }) })), status === "loading" && (0, jsx_runtime_1.jsx)(VisuallyHidden_1.default, { children: labels.loading }), content] }), onOpenInbox && ((0, jsx_runtime_1.jsx)(Primitives_1.Flex, { flexShrink: 0, p: 2, borderTop: `1px solid ${rule}`, children: (0, jsx_runtime_1.jsx)(Button_1.default, { size: isSheet ? "md" : "sm", variant: "ghost", colorScheme: "gray", width: "100%", label: labels.openInbox, rightIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { size: iconPx, "aria-hidden": "true" }), onClick: onOpenInbox }) }))] }));
|
|
86
|
+
});
|
|
87
|
+
exports.default = CenterPanel;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { NotificationCenterProps } from "./NotificationCenterProps";
|
|
2
|
+
/** Bell with the server's Needs-you count (99+), else a dot for unread Activity; opens a two-tab panel or, on phones, a sheet. */
|
|
3
|
+
declare const NotificationCenter: (props: NotificationCenterProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export default NotificationCenter;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
40
|
+
const react_1 = require("react");
|
|
41
|
+
const Primitives_1 = require("../../Primitives/Primitives");
|
|
42
|
+
const Drawer_1 = __importStar(require("../../Drawer/Drawer"));
|
|
43
|
+
const NotificationBell_1 = __importDefault(require("../../NotificationBell/NotificationBell"));
|
|
44
|
+
const ToolTip_1 = __importDefault(require("../../ToolTip/ToolTip"));
|
|
45
|
+
const useCustomTheme_1 = require("../../../Theme/useCustomTheme");
|
|
46
|
+
const radii_1 = require("../../../Theme/tokens/primitives/radii");
|
|
47
|
+
const NotificationCenterProvider_1 = require("../core/NotificationCenterProvider");
|
|
48
|
+
const centerLabels_1 = require("./centerLabels");
|
|
49
|
+
const useIsSheet_1 = require("./useIsSheet");
|
|
50
|
+
const CenterPanel_1 = __importDefault(require("./CenterPanel"));
|
|
51
|
+
const TOAST_SELECTOR = '[id^="chakra-toast-manager"]';
|
|
52
|
+
const TAB_SELECTOR = '[role="tab"][aria-selected="true"]';
|
|
53
|
+
const CenterBody = ({ variant, isOpen, onOpenChange, defaultTab, labels: overrides, onOpenInbox, onOpenSettings, "data-testid": testId, }) => {
|
|
54
|
+
const { colors } = (0, useCustomTheme_1.useCustomTheme)();
|
|
55
|
+
const { counts, labels, format, lists, status, actions } = (0, NotificationCenterProvider_1.useNotificationCenter)();
|
|
56
|
+
const text = (0, react_1.useMemo)(() => (0, centerLabels_1.withCenterLabels)(labels, format, overrides), [labels, format, overrides]);
|
|
57
|
+
const isSheet = (0, useIsSheet_1.useIsSheet)(variant);
|
|
58
|
+
const titleId = (0, react_1.useId)();
|
|
59
|
+
const bellRef = (0, react_1.useRef)(null);
|
|
60
|
+
const panelRef = (0, react_1.useRef)(null);
|
|
61
|
+
const [ownOpen, setOwnOpen] = (0, react_1.useState)(false);
|
|
62
|
+
const open = isOpen === undefined ? ownOpen : isOpen;
|
|
63
|
+
const setOpen = (0, react_1.useCallback)((next) => {
|
|
64
|
+
setOwnOpen(next);
|
|
65
|
+
onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(next);
|
|
66
|
+
}, [onOpenChange]);
|
|
67
|
+
const close = (0, react_1.useCallback)(() => setOpen(false), [setOpen]);
|
|
68
|
+
const startTab = () => {
|
|
69
|
+
if (defaultTab !== undefined)
|
|
70
|
+
return defaultTab;
|
|
71
|
+
const holdsAny = lists.needsYou.some((item) => !item.state.isHidden);
|
|
72
|
+
return status !== "ready" || holdsAny ? "needsYou" : "activity";
|
|
73
|
+
};
|
|
74
|
+
const [tab, setTab] = (0, react_1.useState)(startTab);
|
|
75
|
+
const [wasOpen, setWasOpen] = (0, react_1.useState)(open);
|
|
76
|
+
if (open !== wasOpen) {
|
|
77
|
+
setWasOpen(open);
|
|
78
|
+
if (open)
|
|
79
|
+
setTab(startTab());
|
|
80
|
+
}
|
|
81
|
+
(0, react_1.useEffect)(() => {
|
|
82
|
+
if (!open || isSheet)
|
|
83
|
+
return undefined;
|
|
84
|
+
const onPointerDown = (event) => {
|
|
85
|
+
var _a, _b;
|
|
86
|
+
const target = event.target;
|
|
87
|
+
const inside = ((_a = panelRef.current) === null || _a === void 0 ? void 0 : _a.contains(target)) || ((_b = bellRef.current) === null || _b === void 0 ? void 0 : _b.contains(target));
|
|
88
|
+
if (!inside && !target.closest(TOAST_SELECTOR))
|
|
89
|
+
close();
|
|
90
|
+
};
|
|
91
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
92
|
+
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
93
|
+
}, [open, isSheet, close]);
|
|
94
|
+
(0, react_1.useEffect)(() => {
|
|
95
|
+
if (!open)
|
|
96
|
+
return undefined;
|
|
97
|
+
const onKeyDown = (event) => {
|
|
98
|
+
const isUndo = (event.ctrlKey || event.metaKey) && !event.shiftKey && event.key.toLowerCase() === "z";
|
|
99
|
+
const isTyping = event.target instanceof HTMLTextAreaElement || event.target instanceof HTMLInputElement;
|
|
100
|
+
if (isUndo && !isTyping && actions.undoLast())
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
};
|
|
103
|
+
document.addEventListener("keydown", onKeyDown);
|
|
104
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
105
|
+
}, [open, actions]);
|
|
106
|
+
const focusTarget = (0, react_1.useMemo)(() => ({ get current() { var _a, _b; return (_b = (_a = panelRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(TAB_SELECTOR)) !== null && _b !== void 0 ? _b : null; } }), []);
|
|
107
|
+
const panel = ((0, jsx_runtime_1.jsx)(CenterPanel_1.default, { ref: panelRef, isSheet: isSheet, tab: tab, onTabChange: setTab, labels: text, titleId: titleId, onClose: close, onOpenInbox: onOpenInbox, onOpenSettings: onOpenSettings, "data-testid": testId ? `${testId}-panel` : undefined }));
|
|
108
|
+
const bellProps = {
|
|
109
|
+
ref: bellRef,
|
|
110
|
+
count: counts.needsYou,
|
|
111
|
+
showDot: counts.unread > 0,
|
|
112
|
+
"aria-label": text.bell(counts),
|
|
113
|
+
isOpen: open,
|
|
114
|
+
};
|
|
115
|
+
if (isSheet) {
|
|
116
|
+
return ((0, jsx_runtime_1.jsxs)(Primitives_1.Box, { display: "inline-flex", "data-testid": testId ? `${testId}-bell` : undefined, children: [(0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: text.title, isDisabled: open, children: (0, jsx_runtime_1.jsx)(Primitives_1.Box, { display: "inline-flex", children: (0, jsx_runtime_1.jsx)(NotificationBell_1.default, { ...bellProps, onClick: () => setOpen(!open) }) }) }), (0, jsx_runtime_1.jsx)(Drawer_1.default, { isOpen: open, onClose: close, placement: "bottom", size: "full", hideCloseButton: true, "aria-labelledby": titleId, initialFocusRef: focusTarget, finalFocusRef: bellRef, contentProps: {
|
|
117
|
+
bg: colors.surfaceRaised,
|
|
118
|
+
borderRadius: 0,
|
|
119
|
+
border: "none",
|
|
120
|
+
sx: { paddingTop: "env(safe-area-inset-top)", paddingBottom: "env(safe-area-inset-bottom)" },
|
|
121
|
+
}, children: (0, jsx_runtime_1.jsx)(Drawer_1.DrawerBody, { p: 0, display: "flex", flexDirection: "column", overflow: "hidden", children: panel }) })] }));
|
|
122
|
+
}
|
|
123
|
+
return ((0, jsx_runtime_1.jsxs)(Primitives_1.Popover, { isOpen: open, onOpen: () => setOpen(true), onClose: close, placement: "bottom-end", gutter: 8, isLazy: true, lazyBehavior: "keepMounted", closeOnBlur: false, initialFocusRef: focusTarget, children: [(0, jsx_runtime_1.jsx)(Primitives_1.Box, { display: "inline-flex", "data-testid": testId ? `${testId}-bell` : undefined, children: (0, jsx_runtime_1.jsx)(ToolTip_1.default, { label: text.title, isDisabled: open, children: (0, jsx_runtime_1.jsx)(Primitives_1.Box, { display: "inline-flex", children: (0, jsx_runtime_1.jsx)(Primitives_1.PopoverTrigger, { children: (0, jsx_runtime_1.jsx)(NotificationBell_1.default, { ...bellProps }) }) }) }) }), (0, jsx_runtime_1.jsx)(Primitives_1.Portal, { children: (0, jsx_runtime_1.jsx)(Primitives_1.PopoverContent, { w: "25rem", maxW: "calc(100vw - 3rem)", p: 0, display: "flex", flexDirection: "column", overflow: "hidden", borderRadius: radii_1.RADIUS_ROLE.float, bg: colors.surfaceRaised, minH: "18.75rem", maxH: "min(40rem, calc(100dvh - 15rem))", _focus: { outline: "none", boxShadow: undefined }, _focusVisible: { outline: "none" }, "aria-label": text.title, children: panel }) })] }));
|
|
124
|
+
};
|
|
125
|
+
/** Bell with the server's Needs-you count (99+), else a dot for unread Activity; opens a two-tab panel or, on phones, a sheet. */
|
|
126
|
+
const NotificationCenter = (props) => {
|
|
127
|
+
const { variant, onOpenInbox, onOpenSettings, isOpen, onOpenChange, defaultTab, labels, "data-testid": testId } = props;
|
|
128
|
+
return ((0, jsx_runtime_1.jsx)(NotificationCenterProvider_1.NotificationScope, { source: props, children: (0, jsx_runtime_1.jsx)(CenterBody, { variant: variant, onOpenInbox: onOpenInbox, onOpenSettings: onOpenSettings, isOpen: isOpen, onOpenChange: onOpenChange, defaultTab: defaultTab, labels: labels, "data-testid": testId }) }));
|
|
129
|
+
};
|
|
130
|
+
exports.default = NotificationCenter;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { NotificationCounts, NotificationLabels, NotificationSourceBinding } from "../core/NotificationSourceProps";
|
|
2
|
+
/** Strings the bell and the panel add to the shared ones; anything left out stays English. */
|
|
3
|
+
export interface NotificationCenterLabels extends NotificationLabels {
|
|
4
|
+
/** The bell's accessible name, built from the server counts. */
|
|
5
|
+
bell: (counts: NotificationCounts) => string;
|
|
6
|
+
/** Panel heading. */
|
|
7
|
+
title: string;
|
|
8
|
+
/** First tab: decisions and unread mentions. */
|
|
9
|
+
needsYouTab: string;
|
|
10
|
+
/** Second tab: everything else. */
|
|
11
|
+
activityTab: string;
|
|
12
|
+
/** Accessible name of the tab list. */
|
|
13
|
+
tabsLabel: string;
|
|
14
|
+
/** Header action for the open tab. */
|
|
15
|
+
markAllRead: string;
|
|
16
|
+
/** Footer link to the full inbox. */
|
|
17
|
+
openInbox: string;
|
|
18
|
+
/** Header gear. */
|
|
19
|
+
settings: string;
|
|
20
|
+
/** Close button of the phone sheet. */
|
|
21
|
+
close: string;
|
|
22
|
+
/** Spoken while the first fetch is running. */
|
|
23
|
+
loading: string;
|
|
24
|
+
/** Heading of the failed-load panel. */
|
|
25
|
+
loadErrorTitle: string;
|
|
26
|
+
/** Body of the failed-load panel. */
|
|
27
|
+
loadErrorDescription: string;
|
|
28
|
+
/** Heading shown when the person's role can't see notifications. */
|
|
29
|
+
noAccessTitle: string;
|
|
30
|
+
/** Body of the no-access panel; `appName` comes from the source. */
|
|
31
|
+
noAccessDescription: (appName?: string) => string;
|
|
32
|
+
/** Heading of an empty Needs you tab. */
|
|
33
|
+
emptyNeedsYouTitle: string;
|
|
34
|
+
/** Body of an empty Needs you tab. */
|
|
35
|
+
emptyNeedsYouDescription: string;
|
|
36
|
+
/** Heading of an empty Activity tab. */
|
|
37
|
+
emptyActivityTitle: string;
|
|
38
|
+
/** Body of an empty Activity tab. */
|
|
39
|
+
emptyActivityDescription: string;
|
|
40
|
+
/** Offline banner; `loadedAt` is the already formatted time of the last fetch, if known. */
|
|
41
|
+
offlineBanner: (loadedAt: string | undefined) => string;
|
|
42
|
+
/** Visible text of the "N new" pill. */
|
|
43
|
+
newCount: (count: number) => string;
|
|
44
|
+
/** Accessible name of the "N new" pill; must start with the visible text. */
|
|
45
|
+
newCountName: (count: number) => string;
|
|
46
|
+
/** A tab count above the cap, e.g. "99+". */
|
|
47
|
+
overflowCount: (cap: number) => string;
|
|
48
|
+
}
|
|
49
|
+
/** Forces one presentation; leave it out and the panel follows the viewport. */
|
|
50
|
+
export type NotificationCenterVariant = "popover" | "sheet";
|
|
51
|
+
/** The panel's two tabs; type filters live only in the inbox. */
|
|
52
|
+
export type NotificationCenterTab = "needsYou" | "activity";
|
|
53
|
+
/** Bell plus panel with exactly two tabs; pass the source props, or render it inside a `NotificationCenterProvider`. Needs a `ToasterProvider` above. */
|
|
54
|
+
export type NotificationCenterProps = NotificationSourceBinding & {
|
|
55
|
+
/** "popover" anchors the panel to the bell, "sheet" opens it full screen; defaults to a popover from 48em up and a sheet below. */
|
|
56
|
+
variant?: NotificationCenterVariant;
|
|
57
|
+
/** Shows "Open inbox" in the panel footer; the library never routes. */
|
|
58
|
+
onOpenInbox?: () => void;
|
|
59
|
+
/** Shows the settings gear in the panel header. */
|
|
60
|
+
onOpenSettings?: () => void;
|
|
61
|
+
/** Controlled open state; leave it out and the bell toggles the panel itself. */
|
|
62
|
+
isOpen?: boolean;
|
|
63
|
+
/** Called when the bell, Esc, an outside click or opening a row asks to open or close. */
|
|
64
|
+
onOpenChange?: (isOpen: boolean) => void;
|
|
65
|
+
/** Tab on open; defaults to Needs you while it holds anything, else Activity. */
|
|
66
|
+
defaultTab?: NotificationCenterTab;
|
|
67
|
+
/** Bell and panel strings; shared keys override the provider's for this component only. */
|
|
68
|
+
labels?: Partial<NotificationCenterLabels>;
|
|
69
|
+
/** Test id root: the bell gets `-bell`, the panel `-panel`. */
|
|
70
|
+
"data-testid"?: string;
|
|
71
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { NotificationRowOpenEvent } from "../row/NotificationRowProps";
|
|
2
|
+
import type { LiveNotificationEntry, NotificationFormatter, NotificationGroup } from "../core/NotificationEngineProps";
|
|
3
|
+
interface PanelRowsProps {
|
|
4
|
+
groups: NotificationGroup[];
|
|
5
|
+
variant: "panel" | "phone";
|
|
6
|
+
format: NotificationFormatter;
|
|
7
|
+
onOpenItem: (item: LiveNotificationEntry, event: NotificationRowOpenEvent) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare const PanelRows: ({ groups, variant, format, onOpenItem }: PanelRowsProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|