Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/components-dev/notification-center/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import { NotificationCenterExamplesModule } from '../../docs-examples/components
<notification-center-error-example />
<br />
<br />
<notification-center-infinite-scroll-example />
<br />
<br />
<notification-center-popover-example />
`,
changeDetection: ChangeDetectionStrategy.OnPush
Expand Down
3 changes: 2 additions & 1 deletion packages/components/core/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ export const enUSLocaleData = {
showPopUpNotifications: 'Show pop-up notifications',
noNotifications: 'No notifications',
failedToLoadNotifications: 'Failed to load notifications',
repeat: 'Repeat'
repeat: 'Repeat',
loadingMore: 'Loading more notifications'
}
};
3 changes: 2 additions & 1 deletion packages/components/core/locales/es-LA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ export const esLALocaleData = {
showPopUpNotifications: 'Mostrar notificaciones emergentes',
noNotifications: 'Sin notificaciones',
failedToLoadNotifications: 'Error al cargar las notificaciones',
repeat: 'Repetir'
repeat: 'Repetir',
loadingMore: 'Cargando más notificaciones'
}
};
3 changes: 2 additions & 1 deletion packages/components/core/locales/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ export const ptBRLocaleData = {
showPopUpNotifications: 'Mostrar notificações pop-up',
noNotifications: 'Sem notificações',
failedToLoadNotifications: 'Falha ao carregar notificações',
repeat: 'Repetir'
repeat: 'Repetir',
loadingMore: 'Carregando mais notificações'
}
};
3 changes: 2 additions & 1 deletion packages/components/core/locales/ru-RU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ export const ruRULocaleData = {
showPopUpNotifications: 'Показывать всплывающие уведомления',
noNotifications: 'Нет уведомлений',
failedToLoadNotifications: 'Не удалось загрузить уведомления',
repeat: 'Повторить'
repeat: 'Повторить',
loadingMore: 'Загрузка уведомлений'
}
};
3 changes: 2 additions & 1 deletion packages/components/core/locales/tk-TM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ export const tkTMLocaleData = {
showPopUpNotifications: 'Açylýan bildirişleri görkeziň',
noNotifications: 'Duýduryş ýok',
failedToLoadNotifications: 'Duýduryşlary ýükläp bilmedi',
repeat: 'Gaýtalama'
repeat: 'Gaýtalama',
loadingMore: 'Duýduryşlar ýüklenýär'
}
};
9 changes: 9 additions & 0 deletions packages/components/navbar/navbar-item.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@
min-height: var(--kbq-size-l);
}

// Filled fade-contrast only: its background is a semi-transparent tint, so the
// navbar icon shows through. Composite the tint over an opaque bg-tertiary substrate.
// The outline variant is intentionally left untouched — it stays a clean transparent outline.
& .kbq-badge-filled.kbq-badge_fade-contrast {
background:
linear-gradient(var(--kbq-background-contrast-fade), var(--kbq-background-contrast-fade)),
var(--kbq-background-bg-tertiary);
}

& .kbq-button-icon {
position: absolute;
padding-left: var(--kbq-size-xs);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
}
}

.kbq-notification-center-error-container {
.kbq-notification-center-error-container,
.kbq-notification-center-load-more-error {
color: var(--kbq-foreground-error);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,20 @@ In the empty state, a "No notifications" message is displayed and the "Delete al

### Error

The panel supports scrolling with a sticky header and lazy loading of records. In the empty state, a "No notifications" message is displayed and the "Delete all" button is hidden.
If notifications fail to load, a "Failed to load notifications" message and a "Repeat" button are shown instead of the list. Enable this state with `setErrorMode`, and subscribe to `KbqNotificationCenterService.onReload` to retry loading when the button is pressed.

<!-- example(notification-center-error) -->

### Infinite scroll

The list can load notifications page by page as the user scrolls to the bottom. Subscribe to `KbqNotificationCenterService.onNextPage` to fetch the next page, append the result to `items`, and control the flow with `setLoadingMore`, `setHasMore` and `setLoadMoreErrorMode`. The threshold at which loading starts is configured with the `scrolledToBottomOffset` input.

<!-- example(notification-center-infinite-scroll) -->

### Deletion

Notifications can be removed one by one, by date group, or all at once. Subscribe to `KbqNotificationCenterService.onDelete` to react to a removal — for example, to delete the items on the server. The event fires for all three cases and carries `type` (`'item'`, `'group'` or `'all'`) and `items` — the notifications that were removed.

### Dropdown window

The notification center can be opened in a popover. For example, when placed in a horizontal menu.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
#dropdownTrigger="kbqDropdownTrigger"
kbq-button
class="kbq-notification-center-title__button"
data-testid="kbq-notification-center-silent-mode-toggle"
[kbqDropdownTriggerFor]="notificationSwitcherDropdown"
[kbqTooltip]="service.silentMode.value ? localeData.doNotDisturb : localeData.showPopUpNotifications"
[kbqTooltipArrow]="false"
[kbqPlacement]="'right'"
[kbqTooltipOffset]="4"
[kbqTooltipDisabled]="dropdownTrigger.opened"
[kbqStyle]="'transparent'"
Expand All @@ -22,6 +24,7 @@
<button
kbq-button
class="kbq-notification-center-toolbar__button"
data-testid="kbq-notification-center-remove-all-button"
[kbqStyle]="'transparent'"
[color]="'contrast'"
(click)="service.removeAll()"
Expand All @@ -35,6 +38,7 @@
<button
kbq-button
class="kbq-notification-center-toolbar__button"
data-testid="kbq-notification-center-close-button"
[kbqStyle]="'transparent'"
[color]="'contrast'"
(click)="hide(0)"
Expand All @@ -47,24 +51,28 @@
<div
kbq-scrollbar
class="kbq-notification-center-container"
data-testid="kbq-notification-center-container"
[class.kbq-notification-center_bottom-overflow]="isBottomOverflow"
(onScroll)="checkOverflow()"
(onScroll)="onContainerScroll()"
>
@if (!service.errorMode.value) {
@if (!service.loadingMode.value) {
@for (group of service.groupedItems | async; track group) {
<div
class="kbq-notification-center-sub-header"
data-testid="kbq-notification-center-group"
[class.kbq-notification-center_top-overflow]="isTopOverflow"
>
<div class="kbq-notification-center-sub-header__date">{{ group.title }}</div>

<button
kbq-button
class="kbq-notification-center-sub-header__button"
data-testid="kbq-notification-center-remove-group-button"
[kbqStyle]="'transparent'"
[color]="'contrast'"
[kbqTooltip]="localeData.remove"
[kbqPlacement]="'right'"
[kbqTooltipArrow]="false"
(click)="service.removeGroup(group)"
>
Expand All @@ -76,31 +84,77 @@
<kbq-notification-item [data]="item" />
}
} @empty {
<div class="kbq-notification-center-empty-container">
<i kbq-icon-item="kbq-bell_16" [big]="true" [color]="'contrast-fade'"></i>
{{ localeData.noNotifications }}
@if (!service.loadingMore.value) {
<div class="kbq-notification-center-empty-container" data-testid="kbq-notification-center-empty">
<i kbq-icon-item="kbq-bell_16" [big]="true" [color]="'contrast-fade'"></i>
{{ localeData.noNotifications }}
</div>
}
}

@if (service.loadingMore.value) {
<div
class="kbq-notification-center-load-more"
data-testid="kbq-notification-center-load-more"
role="status"
[attr.aria-label]="localeData.loadingMore"
>
<kbq-progress-spinner [mode]="'indeterminate'" [size]="'compact'" />
</div>
}

@if (service.loadMoreErrorMode.value) {
<div
class="kbq-notification-center-load-more-error"
data-testid="kbq-notification-center-load-more-error"
role="alert"
>
{{ localeData.failedToLoadNotifications }}
<button
kbq-button
data-testid="kbq-notification-center-load-more-retry-button"
[kbqStyle]="'transparent'"
[color]="'theme'"
(click)="retryLoadMore()"
>
{{ localeData.repeat }}
</button>
</div>
}
} @else {
<kbq-loader-overlay [transparent]="true" />
<kbq-loader-overlay data-testid="kbq-notification-center-loader" [transparent]="true" />
}
} @else {
<div class="kbq-notification-center-error-container">
<div class="kbq-notification-center-error-container" data-testid="kbq-notification-center-error">
<i kbq-icon-item="kbq-bell_16" [big]="true" [color]="'error'" [fade]="true"></i>
{{ localeData.failedToLoadNotifications }}
<button kbq-button [kbqStyle]="'transparent'" [color]="'theme'" (click)="service.onReload.emit()">
<button
kbq-button
data-testid="kbq-notification-center-reload-button"
[kbqStyle]="'transparent'"
[color]="'theme'"
(click)="service.onReload.emit()"
>
{{ localeData.repeat }}
</button>
</div>
}
</div>

<kbq-dropdown #notificationSwitcherDropdown="kbqDropdown">
<button kbq-dropdown-item (click)="service.setSilentMode(false)">
<button
kbq-dropdown-item
data-testid="kbq-notification-center-show-notifications-button"
(click)="service.setSilentMode(false)"
>
<i kbq-icon="{{ !service.silentMode.value ? 'kbq-circle-xs_16' : '' }}" [style.min-width.px]="16"></i>
{{ localeData.showPopUpNotifications }}
</button>
<button kbq-dropdown-item (click)="service.setSilentMode(true)">
<button
kbq-dropdown-item
data-testid="kbq-notification-center-do-not-disturb-button"
(click)="service.setSilentMode(true)"
>
<i kbq-icon="{{ service.silentMode.value ? 'kbq-circle-xs_16' : '' }}" [style.min-width.px]="16"></i>
{{ localeData.doNotDisturb }}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ npm install overlayscrollbars@2.7.3

<!-- example(notification-center-error) -->

### Бесконечная прокрутка

Список может догружать уведомления постранично по мере прокрутки к концу списка. Подпишитесь на `KbqNotificationCenterService.onNextPage`, чтобы загрузить следующую страницу, добавьте результат в `items` и управляйте процессом через `setLoadingMore`, `setHasMore` и `setLoadMoreErrorMode`. Порог, при котором начинается загрузка, настраивается свойством `scrolledToBottomOffset`.

<!-- example(notification-center-infinite-scroll) -->

### Удаление

Уведомления можно удалять по одному, группой за день или все сразу. Подпишитесь на `KbqNotificationCenterService.onDelete`, чтобы отреагировать на удаление — например, удалить элементы на сервере. Событие срабатывает во всех трёх случаях и содержит `type` (`'item'`, `'group'` или `'all'`) и `items` — удалённые уведомления.

### Выпадающее окно

Центр уведомлений можно открыть в поповере. Например, при размещении в горизонтальном меню.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

justify-content: space-between;

padding: var(--kbq-size-xl) var(--kbq-size-xl) var(--kbq-size-xxs) var(--kbq-size-xxl);
padding: var(--kbq-size-m) var(--kbq-size-xl) var(--kbq-size-xxs) var(--kbq-size-xxl);
}

.kbq-notification-center-title {
Expand Down Expand Up @@ -67,7 +67,7 @@
display: flex;
flex-direction: row;

padding: var(--kbq-size-s) var(--kbq-size-xxl);
padding: var(--kbq-size-s) var(--kbq-size-xl);

& .kbq-notification-center-sub-header__button {
position: absolute;
Expand All @@ -92,7 +92,7 @@

border-radius: inherit;

padding-bottom: var(--kbq-size-m);
padding-bottom: var(--kbq-size-xl);

& .kbq-loader-overlay_parent {
border-radius: inherit;
Expand All @@ -103,6 +103,31 @@
}
}

.kbq-notification-center-load-more {
display: flex;

align-items: center;
justify-content: center;

padding: 26px 0;
}

.kbq-notification-center-load-more-error {
display: flex;
flex-direction: column;

align-items: center;
justify-content: center;

padding: var(--kbq-size-xxs) var(--kbq-size-xxl);

text-align: center;

& .kbq-button {
margin-top: var(--kbq-size-s);
}
}

.kbq-notification-center-empty-container,
.kbq-notification-center-error-container {
display: flex;
Expand Down
Loading