@snack-uikit/notification 0.6.18-preview-3cdd8d31.0 → 0.7.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 (46) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +29 -13
  3. package/dist/components/NotificationCard/NotificationCard.d.ts +6 -1
  4. package/dist/components/NotificationCard/NotificationCard.js +5 -3
  5. package/dist/components/NotificationCard/constants.d.ts +2 -0
  6. package/dist/components/NotificationCard/constants.js +2 -0
  7. package/dist/components/NotificationCard/styles.module.css +6 -0
  8. package/dist/components/NotificationPanel/NotificationPanel.d.ts +13 -7
  9. package/dist/components/NotificationPanel/NotificationPanel.js +7 -4
  10. package/dist/components/NotificationPanel/components/NotificationPanelBlank/NotificationPanelBlank.d.ts +3 -16
  11. package/dist/components/NotificationPanel/components/NotificationPanelBlank/NotificationPanelBlank.js +5 -7
  12. package/dist/components/NotificationPanel/components/NotificationPanelBlank/styles.module.css +1 -25
  13. package/dist/components/NotificationPanel/components/NotificationPanelDivider/NotificationPanelDivider.d.ts +8 -0
  14. package/dist/components/NotificationPanel/components/NotificationPanelDivider/NotificationPanelDivider.js +21 -0
  15. package/dist/components/NotificationPanel/components/NotificationPanelDivider/index.d.ts +1 -0
  16. package/dist/components/NotificationPanel/components/NotificationPanelDivider/index.js +1 -0
  17. package/dist/components/NotificationPanel/components/NotificationPanelDivider/styles.module.css +10 -0
  18. package/dist/components/NotificationPanel/constants.d.ts +0 -5
  19. package/dist/components/NotificationPanel/constants.js +0 -5
  20. package/dist/components/NotificationPanel/styles.module.css +14 -0
  21. package/dist/{helperComponents → components}/NotificationPanelPopover/NotificationPanelPopover.d.ts +4 -2
  22. package/dist/{helperComponents → components}/NotificationPanelPopover/NotificationPanelPopover.js +1 -0
  23. package/dist/components/index.d.ts +1 -0
  24. package/dist/components/index.js +1 -0
  25. package/package.json +9 -9
  26. package/src/components/NotificationCard/NotificationCard.tsx +35 -1
  27. package/src/components/NotificationCard/constants.ts +2 -0
  28. package/src/components/NotificationCard/styles.module.scss +6 -0
  29. package/src/components/NotificationPanel/NotificationPanel.tsx +63 -60
  30. package/src/components/NotificationPanel/components/NotificationPanelBlank/NotificationPanelBlank.tsx +12 -46
  31. package/src/components/NotificationPanel/components/NotificationPanelBlank/styles.module.scss +1 -27
  32. package/src/components/NotificationPanel/components/NotificationPanelDivider/NotificationPanelDivider.tsx +21 -0
  33. package/src/components/NotificationPanel/components/NotificationPanelDivider/index.ts +1 -0
  34. package/src/components/NotificationPanel/components/NotificationPanelDivider/styles.module.scss +14 -0
  35. package/src/components/NotificationPanel/constants.ts +0 -5
  36. package/src/components/NotificationPanel/styles.module.scss +14 -0
  37. package/src/{helperComponents → components}/NotificationPanelPopover/NotificationPanelPopover.tsx +4 -2
  38. package/src/components/index.ts +1 -0
  39. package/dist/helperComponents/index.d.ts +0 -1
  40. package/dist/helperComponents/index.js +0 -1
  41. package/src/helperComponents/index.ts +0 -1
  42. /package/dist/{helperComponents → components}/NotificationPanelPopover/index.d.ts +0 -0
  43. /package/dist/{helperComponents → components}/NotificationPanelPopover/index.js +0 -0
  44. /package/dist/{helperComponents → components}/NotificationPanelPopover/styles.module.css +0 -0
  45. /package/src/{helperComponents → components}/NotificationPanelPopover/index.ts +0 -0
  46. /package/src/{helperComponents → components}/NotificationPanelPopover/styles.module.scss +0 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 0.7.0 (2024-02-21)
7
+
8
+
9
+ ### Features
10
+
11
+ * **FF-4309:** remove popover and trigger from NotificationPanel, add buttons to NotificationCard, add Divider ([99d9114](https://github.com/cloud-ru-tech/snack-uikit/commit/99d9114943e2ef1b98bb8ff02b165b5cd4465432))
12
+
13
+
14
+
15
+
16
+
6
17
  ## 0.6.17 (2024-02-20)
7
18
 
8
19
 
package/README.md CHANGED
@@ -91,6 +91,8 @@ const cards = [
91
91
  | link | `Omit<LinkProps, "data-test-id" \| "appearance" \| "size" \| "textMode">` | - | Ссылка |
92
92
  | onClick | `MouseEventHandler<HTMLDivElement>` | - | Колбэк клика по карточке |
93
93
  | onVisible | `(cardId: string) => void` | - | Колбэк при попадании карточки в область видимости на 80% |
94
+ | primaryButton | `Omit<ButtonTonalProps, "data-test-id" \| "appearance" \| "size">` | - | Кнопка главного действия у карточки |
95
+ | secondaryButton | `Omit<ButtonSimpleProps, "data-test-id" \| "appearance" \| "size">` | - | Кнопка второстепенного действия у карточки |
94
96
  | actions | `Action[]` | - | Дополнительные действия у карточки |
95
97
  | className | `string` | - | CSS-класс |
96
98
  ## NotificationPanel
@@ -98,15 +100,39 @@ const cards = [
98
100
  ### Props
99
101
  | name | type | default value | description |
100
102
  |------|------|---------------|-------------|
101
- | triggerElement* | `ReactNode \| ChildrenFunction` | - | Элемент для открытия панели |
102
103
  | title* | `string` | - | Заголовок панели |
103
104
  | settings | `NotificationPanelSettingsProps` | - | Кнопка настроек и выпадающий список |
104
105
  | chips | `Omit<ChipToggleProps, "data-test-id" \| "size">[]` | - | Чипы для фильтрации |
105
106
  | readAllButton | `Omit<ButtonFunctionProps, "data-test-id"> & { onClick: MouseEventHandler<HTMLElement>; }` | - | Кнопка в "шапке" панели |
106
107
  | footerButton | `{ label: string; onClick: MouseEventHandler<HTMLButtonElement>; }` | - | Кнопка внизу панели |
108
+ | className | `string` | - | CSS-класс |
107
109
  | loading | `boolean` | - | Состояние загрузки |
108
110
  | content | `ReactNode` | - | Контент для отрисовки (e.g NotificationCard \| NotificationPanel.Blank) |
109
111
  | skeletonsAmount | `number` | 2 | Количество скелетонов карточек для отображения при загрузке |
112
+ | scrollEndRef | `RefObject<HTMLDivElement>` | - | Ссылка на элемент, обозначающий самый конец прокручиваемого списка |
113
+ | scrollContainerRef | `RefObject<HTMLElement>` | - | Ссылка на контейнер, который скроллится |
114
+ ## NotificationPanel.Blank
115
+ ### Props
116
+ | name | type | default value | description |
117
+ |------|------|---------------|-------------|
118
+ | title | `string` | - | Заголовок |
119
+ | className | `string` | - | CSS-класс |
120
+ | icon | `Pick<IconPredefinedProps, "appearance" \| "icon" \| "decor">` | - | Иконка |
121
+ | description | `string` | - | Подзаголовок |
122
+ ## NotificationPanel.Divider
123
+ ### Props
124
+ | name | type | default value | description |
125
+ |------|------|---------------|-------------|
126
+ | text* | `string` | - | Текст разделителя |
127
+ | className | `string` | - | CSS-класс |
128
+ ## NotificationPanelPopover
129
+ Компонент-обёртка для NotificationPanel для использования как выпадающий элемент
130
+ ### Props
131
+ | name | type | default value | description |
132
+ |------|------|---------------|-------------|
133
+ | children* | `ReactNode \| ChildrenFunction` | - | Триггер поповера (подробнее читайте ниже) |
134
+ | content* | `ReactElement<NotificationPanelProps, typeof NotificationPanel>` | - | |
135
+ | contentClassName | `string` | - | CSS-класс для элемента содержащего контент |
110
136
  | className | `string` | - | CSS-класс |
111
137
  | triggerClassName | `string` | - | CSS-класс триггера |
112
138
  | open | `boolean` | - | Управляет состоянием показан/не показан. |
@@ -117,18 +143,8 @@ const cards = [
117
143
  | closeOnEscapeKey | `boolean` | true | Закрывать ли по нажатию на кнопку `Esc` |
118
144
  | triggerClickByKeys | `boolean` | true | Вызывается ли попоповер по нажатию клавиш Enter/Space (при trigger = `click`) |
119
145
  | triggerRef | `ForwardedRef<HTMLElement \| ReferenceType>` | - | Ref ссылка на триггер |
120
- | trigger | enum Trigger: `"click"`, `"hover"`, `"focusVisible"`, `"focus"`, `"hoverAndFocusVisible"`, `"hoverAndFocus"`, `"clickAndFocusVisible"` | - | Условие отображения поповера: <br> - `click` - открывать по клику <br> - `hover` - открывать по ховеру <br> - `focusVisible` - открывать по focus-visible <br> - `focus` - открывать по фокусу <br> - `hoverAndFocusVisible` - открывать по ховеру и focus-visible <br> - `hoverAndFocus` - открывать по ховеру и фокусу <br> - `clickAndFocusVisible` - открывать по клику и focus-visible |
121
- | placement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | top | Положение поповера относительно своего триггера (children). |
122
- | contentClassName | `string` | - | CSS-класс для элемента содержащего контент |
123
- ## NotificationPanel.Blank
124
- ### Props
125
- | name | type | default value | description |
126
- |------|------|---------------|-------------|
127
- | title* | `ReactNode` | - | Заголовок |
128
- | description | `ReactNode` | - | Описание |
129
- | icon | `JSXElementConstructor<{ size?: number; className?: string; }>` | - | Иконка |
130
- | iconAppearance | enum Appearance: `"neutral"`, `"primary"`, `"red"`, `"orange"`, `"yellow"`, `"green"`, `"blue"`, `"violet"`, `"pink"` | - | Цвет обводки для иконки |
131
- | className | `string` | - | CSS-класс |
146
+ | trigger | enum Trigger: `"click"`, `"hover"`, `"focusVisible"`, `"focus"`, `"hoverAndFocusVisible"`, `"hoverAndFocus"`, `"clickAndFocusVisible"` | click | Условие отображения поповера: <br> - `click` - открывать по клику <br> - `hover` - открывать по ховеру <br> - `focusVisible` - открывать по focus-visible <br> - `focus` - открывать по фокусу <br> - `hoverAndFocusVisible` - открывать по ховеру и focus-visible <br> - `hoverAndFocus` - открывать по ховеру и фокусу <br> - `clickAndFocusVisible` - открывать по клику и focus-visible |
147
+ | placement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | bottom-end | Положение поповера относительно своего триггера (children). |
132
148
 
133
149
 
134
150
  [//]: DOCUMENTATION_SECTION_END
@@ -1,4 +1,5 @@
1
1
  import { MouseEventHandler, ReactElement, ReactNode } from 'react';
2
+ import { ButtonSimpleProps, ButtonTonalProps } from '@snack-uikit/button';
2
3
  import { LinkProps } from '@snack-uikit/link';
3
4
  import { BaseItemProps } from '@snack-uikit/list';
4
5
  import { WithSupportProps } from '@snack-uikit/utils';
@@ -28,11 +29,15 @@ export type NotificationCardProps = WithSupportProps<{
28
29
  onClick?: MouseEventHandler<HTMLDivElement>;
29
30
  /** Колбэк при попадании карточки в область видимости на 80% */
30
31
  onVisible?(cardId: string): void;
32
+ /** Кнопка главного действия у карточки */
33
+ primaryButton?: Omit<ButtonTonalProps, 'size' | 'appearance' | 'data-test-id'>;
34
+ /** Кнопка второстепенного действия у карточки */
35
+ secondaryButton?: Omit<ButtonSimpleProps, 'size' | 'appearance' | 'data-test-id'>;
31
36
  /** Дополнительные действия у карточки */
32
37
  actions?: Action[];
33
38
  /** CSS-класс */
34
39
  className?: string;
35
40
  }>;
36
41
  /** Компонент карточки уведомления */
37
- export declare function NotificationCard({ id, appearance, label, unread, title, content, link, date, onClick, actions, onVisible, className, ...rest }: NotificationCardProps): import("react/jsx-runtime").JSX.Element;
42
+ export declare function NotificationCard({ id, appearance, label, unread, title, content, link, date, onClick, primaryButton, secondaryButton, actions, onVisible, className, ...rest }: NotificationCardProps): import("react/jsx-runtime").JSX.Element;
38
43
  export {};
@@ -12,6 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import cn from 'classnames';
14
14
  import { useEffect, useMemo, useRef, useState } from 'react';
15
+ import { ButtonSimple, ButtonTonal } from '@snack-uikit/button';
15
16
  import { Link } from '@snack-uikit/link';
16
17
  import { TruncateString } from '@snack-uikit/truncate-string';
17
18
  import { Typography } from '@snack-uikit/typography';
@@ -22,10 +23,11 @@ import { getIcon } from './helpers';
22
23
  import styles from './styles.module.css';
23
24
  /** Компонент карточки уведомления */
24
25
  export function NotificationCard(_a) {
25
- var { id, appearance = APPEARANCE.Neutral, label, unread, title, content, link, date, onClick, actions, onVisible, className } = _a, rest = __rest(_a, ["id", "appearance", "label", "unread", "title", "content", "link", "date", "onClick", "actions", "onVisible", "className"]);
26
- const { icon, linkOnColor } = useMemo(() => ({
26
+ var { id, appearance = APPEARANCE.Neutral, label, unread, title, content, link, date, onClick, primaryButton, secondaryButton, actions, onVisible, className } = _a, rest = __rest(_a, ["id", "appearance", "label", "unread", "title", "content", "link", "date", "onClick", "primaryButton", "secondaryButton", "actions", "onVisible", "className"]);
27
+ const { icon, linkOnColor, buttonAppearance } = useMemo(() => ({
27
28
  icon: getIcon(appearance),
28
29
  linkOnColor: appearance === APPEARANCE.ErrorCritical ? 'red' : undefined,
30
+ buttonAppearance: appearance === APPEARANCE.ErrorCritical ? 'destructive' : 'primary',
29
31
  }), [appearance]);
30
32
  const [isDroplistOpen, setDroplistOpen] = useState(false);
31
33
  const cardRef = useRef(null);
@@ -55,5 +57,5 @@ export function NotificationCard(_a) {
55
57
  e.stopPropagation();
56
58
  (_a = link === null || link === void 0 ? void 0 : link.onClick) === null || _a === void 0 ? void 0 : _a.call(link, e);
57
59
  };
58
- return (_jsxs("div", Object.assign({ ref: cardRef, role: 'button', onClick: onClick, tabIndex: 0 }, extractSupportProps(rest), { "data-appearance": appearance, "data-unread": unread || undefined, "data-clickable": Boolean(onClick) || undefined, "data-droplist-open": isDroplistOpen || undefined, className: cn(styles.notificationCard, className), children: [(actions === null || actions === void 0 ? void 0 : actions.length) && (_jsx(NotificationCardFunction, { actions: actions, open: isDroplistOpen, setDroplistOpen: setDroplistOpen })), label && (_jsx(Typography.LightLabelS, { tag: 'div', className: styles.notificationCardLabel, children: _jsx(TruncateString, { maxLines: 1, text: label, "data-test-id": TEST_IDS.label }) })), _jsxs("div", { className: styles.notificationCardTitle, children: [_jsx("div", { className: styles.notificationCardTitleIcon, children: icon }), _jsx(Typography.SansTitleS, { tag: 'div', className: styles.notificationCardTitleText, children: _jsx(TruncateString, { maxLines: 2, text: title, "data-test-id": TEST_IDS.title }) })] }), content && (_jsx(Typography.SansBodyS, { tag: 'div', className: styles.notificationCardContent, "data-test-id": TEST_IDS.content, children: content })), showFooter && (_jsxs("div", { className: styles.notificationCardFooter, children: [link && (_jsx(Link, Object.assign({}, link, { onClick: handleLinkClick, appearance: linkOnColor, textMode: 'default', size: 's', "data-test-id": TEST_IDS.link }))), date && (_jsx(Typography.LightLabelS, { className: styles.notificationCardDate, "data-test-id": TEST_IDS.date, children: date }))] }))] })));
60
+ return (_jsxs("div", Object.assign({ ref: cardRef, role: 'button', onClick: onClick, tabIndex: 0 }, extractSupportProps(rest), { "data-appearance": appearance, "data-unread": unread || undefined, "data-clickable": Boolean(onClick) || undefined, "data-droplist-open": isDroplistOpen || undefined, className: cn(styles.notificationCard, className), children: [(actions === null || actions === void 0 ? void 0 : actions.length) && (_jsx(NotificationCardFunction, { actions: actions, open: isDroplistOpen, setDroplistOpen: setDroplistOpen })), label && (_jsx(Typography.LightLabelS, { tag: 'div', className: styles.notificationCardLabel, children: _jsx(TruncateString, { maxLines: 1, text: label, "data-test-id": TEST_IDS.label }) })), _jsxs("div", { className: styles.notificationCardTitle, children: [_jsx("div", { className: styles.notificationCardTitleIcon, children: icon }), _jsx(Typography.SansTitleS, { tag: 'div', className: styles.notificationCardTitleText, children: _jsx(TruncateString, { maxLines: 2, text: title, "data-test-id": TEST_IDS.title }) })] }), content && (_jsx(Typography.SansBodyS, { tag: 'div', className: styles.notificationCardContent, "data-test-id": TEST_IDS.content, children: content })), (primaryButton || secondaryButton) && (_jsxs("div", { className: styles.notificationCardButtons, children: [secondaryButton && (_jsx(ButtonSimple, Object.assign({}, secondaryButton, { appearance: buttonAppearance, size: 's', "data-test-id": TEST_IDS.primaryButton }))), primaryButton && (_jsx(ButtonTonal, Object.assign({}, primaryButton, { appearance: buttonAppearance, size: 's', "data-test-id": TEST_IDS.secondaryButton })))] })), showFooter && (_jsxs("div", { className: styles.notificationCardFooter, children: [link && (_jsx(Link, Object.assign({}, link, { onClick: handleLinkClick, appearance: linkOnColor, textMode: 'default', size: 's', "data-test-id": TEST_IDS.link }))), date && (_jsx(Typography.LightLabelS, { className: styles.notificationCardDate, "data-test-id": TEST_IDS.date, children: date }))] }))] })));
59
61
  }
@@ -17,4 +17,6 @@ export declare const TEST_IDS: {
17
17
  droplistTrigger: string;
18
18
  droplistAction: string;
19
19
  };
20
+ primaryButton: string;
21
+ secondaryButton: string;
20
22
  };
@@ -17,4 +17,6 @@ export const TEST_IDS = {
17
17
  droplistTrigger: 'notification-card__droplist-trigger',
18
18
  droplistAction: 'notification-card__droplist-action',
19
19
  },
20
+ primaryButton: 'notification-card__primary-button',
21
+ secondaryButton: 'notification-card__secondary-button',
20
22
  };
@@ -125,4 +125,10 @@
125
125
  box-sizing:border-box;
126
126
  color:var(--sys-neutral-text-light, #868892);
127
127
  text-align:right;
128
+ }
129
+
130
+ .notificationCardButtons{
131
+ display:flex;
132
+ align-items:center;
133
+ justify-content:flex-end;
128
134
  }
@@ -1,10 +1,11 @@
1
- import { MouseEventHandler, ReactNode } from 'react';
1
+ import { MouseEventHandler, ReactNode, RefObject } from 'react';
2
2
  import { ButtonFunctionProps } from '@snack-uikit/button';
3
3
  import { ChipToggleProps } from '@snack-uikit/chips';
4
- import { NotificationPanelPopoverProps } from '../../helperComponents';
4
+ import { WithSupportProps } from '@snack-uikit/utils';
5
5
  import { NotificationPanelBlank, NotificationPanelBlankProps, NotificationPanelSettingsProps } from './components';
6
+ import { NotificationPanelDivider, NotificationPanelDividerProps } from './components/NotificationPanelDivider';
6
7
  export type { NotificationPanelBlankProps };
7
- export type NotificationPanelProps = {
8
+ export type NotificationPanelProps = WithSupportProps<{
8
9
  /** Заголовок панели */
9
10
  title: string;
10
11
  /** Кнопка настроек и выпадающий список */
@@ -15,23 +16,28 @@ export type NotificationPanelProps = {
15
16
  readAllButton?: Omit<ButtonFunctionProps, 'data-test-id'> & {
16
17
  onClick: ButtonFunctionProps['onClick'];
17
18
  };
18
- /** Элемент для открытия панели */
19
- triggerElement: NotificationPanelPopoverProps['children'];
20
19
  /** Кнопка внизу панели */
21
20
  footerButton?: {
22
21
  label: string;
23
22
  onClick: MouseEventHandler<HTMLButtonElement>;
24
23
  };
24
+ className?: string;
25
25
  /** Состояние загрузки */
26
26
  loading?: boolean;
27
27
  /** Контент для отрисовки (e.g NotificationCard | NotificationPanel.Blank) */
28
28
  content?: ReactNode;
29
29
  /** Количество скелетонов карточек для отображения при загрузке */
30
30
  skeletonsAmount?: number;
31
- } & Omit<NotificationPanelPopoverProps, 'children' | 'content'>;
31
+ /** Ссылка на элемент, обозначающий самый конец прокручиваемого списка */
32
+ scrollEndRef?: RefObject<HTMLDivElement>;
33
+ /** Ссылка на контейнер, который скроллится */
34
+ scrollContainerRef?: RefObject<HTMLElement>;
35
+ }>;
32
36
  /** Компонент панели для уведомлений */
33
- export declare function NotificationPanel({ title, triggerElement, settings, chips, readAllButton, footerButton, content, loading, skeletonsAmount, ...rest }: NotificationPanelProps): import("react/jsx-runtime").JSX.Element;
37
+ export declare function NotificationPanel({ title, settings, chips, readAllButton, footerButton, content, loading, skeletonsAmount, scrollEndRef, scrollContainerRef, className, ...rest }: NotificationPanelProps): import("react/jsx-runtime").JSX.Element;
34
38
  export declare namespace NotificationPanel {
35
39
  const Blank: typeof NotificationPanelBlank;
36
40
  type BlankProps = NotificationPanelBlankProps;
41
+ const Divider: typeof NotificationPanelDivider;
42
+ type DividerProps = NotificationPanelDividerProps;
37
43
  }
@@ -10,7 +10,8 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { createElement as _createElement } from "react";
13
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
13
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
+ import cn from 'classnames';
14
15
  import { useMemo } from 'react';
15
16
  import { ButtonFunction } from '@snack-uikit/button';
16
17
  import { ChipToggle } from '@snack-uikit/chips';
@@ -18,17 +19,19 @@ import { Scroll } from '@snack-uikit/scroll';
18
19
  import { SkeletonContextProvider, WithSkeleton } from '@snack-uikit/skeleton';
19
20
  import { TruncateString } from '@snack-uikit/truncate-string';
20
21
  import { Typography } from '@snack-uikit/typography';
21
- import { NotificationPanelPopover } from '../../helperComponents';
22
+ import { extractSupportProps } from '@snack-uikit/utils';
22
23
  import { NotificationCardSkeleton } from '../NotificationCard/components';
23
24
  import { NotificationPanelBlank, NotificationPanelSettings, } from './components';
25
+ import { NotificationPanelDivider } from './components/NotificationPanelDivider';
24
26
  import { TEST_IDS } from './constants';
25
27
  import styles from './styles.module.css';
26
28
  /** Компонент панели для уведомлений */
27
29
  export function NotificationPanel(_a) {
28
- var { title, triggerElement, settings, chips, readAllButton, footerButton, content, loading, skeletonsAmount = 2 } = _a, rest = __rest(_a, ["title", "triggerElement", "settings", "chips", "readAllButton", "footerButton", "content", "loading", "skeletonsAmount"]);
30
+ var { title, settings, chips, readAllButton, footerButton, content, loading, skeletonsAmount = 2, scrollEndRef, scrollContainerRef, className } = _a, rest = __rest(_a, ["title", "settings", "chips", "readAllButton", "footerButton", "content", "loading", "skeletonsAmount", "scrollEndRef", "scrollContainerRef", "className"]);
29
31
  const skeletons = useMemo(() => Array.from({ length: skeletonsAmount }, (_, i) => i), [skeletonsAmount]);
30
- return (_jsx(NotificationPanelPopover, Object.assign({}, rest, { content: _jsxs(_Fragment, { children: [_jsxs("div", { className: styles.notificationPanelHeader, children: [_jsxs("div", { className: styles.notificationPanelHeadline, children: [_jsx(Typography.SansHeadlineS, { className: styles.notificationPanelTitle, children: _jsx(TruncateString, { text: title, "data-test-id": TEST_IDS.title }) }), settings && _jsx(NotificationPanelSettings, Object.assign({}, settings))] }), _jsxs("div", { className: styles.notificationPanelHeaderFunctions, children: [_jsx("div", { className: styles.notificationPanelChips, children: chips === null || chips === void 0 ? void 0 : chips.map(chip => (_createElement(ChipToggle, Object.assign({}, chip, { key: chip.label, "data-test-id": `${TEST_IDS.chip}-${chip.label}`, size: 'xs', disabled: chip.disabled || loading })))) }), readAllButton && (_jsx(ButtonFunction, Object.assign({}, readAllButton, { disabled: readAllButton.disabled || loading, "data-test-id": TEST_IDS.readAll })))] })] }), _jsx(Scroll, { size: 'm', className: styles.notificationPanelBody, children: loading ? (_jsx(SkeletonContextProvider, { loading: loading || false, children: skeletons.map(skeleton => (_jsx(WithSkeleton, { skeleton: _jsx(NotificationCardSkeleton, {}) }, skeleton))) })) : (content) }), !loading && footerButton && (_jsx("button", { className: styles.notificationPanelFooterButton, "data-test-id": TEST_IDS.footerButton, children: _jsx(Typography.SansLabelS, { children: footerButton.label }) }))] }), children: triggerElement })));
32
+ return (_jsxs("div", Object.assign({ className: cn(styles.wrapper, className) }, extractSupportProps(rest), { children: [_jsxs("div", { className: styles.notificationPanelHeader, children: [_jsxs("div", { className: styles.notificationPanelHeadline, children: [_jsx(Typography.SansHeadlineS, { className: styles.notificationPanelTitle, children: _jsx(TruncateString, { text: title, "data-test-id": TEST_IDS.title }) }), settings && _jsx(NotificationPanelSettings, Object.assign({}, settings))] }), _jsxs("div", { className: styles.notificationPanelHeaderFunctions, children: [_jsx("div", { className: styles.notificationPanelChips, children: chips === null || chips === void 0 ? void 0 : chips.map(chip => (_createElement(ChipToggle, Object.assign({}, chip, { key: chip.label, "data-test-id": `${TEST_IDS.chip}-${chip.label}`, size: 'xs', disabled: chip.disabled || loading })))) }), readAllButton && (_jsx(ButtonFunction, Object.assign({}, readAllButton, { disabled: readAllButton.disabled || loading, "data-test-id": TEST_IDS.readAll })))] })] }), _jsxs(Scroll, { size: 'm', className: styles.notificationPanelBody, ref: scrollContainerRef, children: [content, loading && (_jsx(SkeletonContextProvider, { loading: loading || false, children: skeletons.map(skeleton => (_jsx(WithSkeleton, { skeleton: _jsx(NotificationCardSkeleton, {}) }, skeleton))) })), _jsx("div", { className: styles.scrollStub, ref: scrollEndRef })] }), footerButton && (_jsx("button", { onClick: footerButton.onClick, className: styles.notificationPanelFooterButton, "data-test-id": TEST_IDS.footerButton, children: _jsx(Typography.SansLabelS, { children: footerButton.label }) }))] })));
31
33
  }
32
34
  (function (NotificationPanel) {
33
35
  NotificationPanel.Blank = NotificationPanelBlank;
36
+ NotificationPanel.Divider = NotificationPanelDivider;
34
37
  })(NotificationPanel || (NotificationPanel = {}));
@@ -1,17 +1,4 @@
1
- import { ReactNode } from 'react';
2
- import { IconPredefinedProps } from '@snack-uikit/icon-predefined';
3
- import { WithSupportProps } from '@snack-uikit/utils';
4
- export type NotificationPanelBlankProps = WithSupportProps<{
5
- /** Заголовок */
6
- title: ReactNode;
7
- /** Описание */
8
- description?: ReactNode;
9
- /** Иконка */
10
- icon?: IconPredefinedProps['icon'];
11
- /** Цвет обводки для иконки */
12
- iconAppearance?: IconPredefinedProps['appearance'];
13
- /** CSS-класс */
14
- className?: string;
15
- }>;
1
+ import { InfoBlockProps } from '@snack-uikit/info-block';
2
+ export type NotificationPanelBlankProps = Omit<InfoBlockProps, 'footer' | 'align' | 'size'>;
16
3
  /** Компонента для "заглушки" вместо карточек в панели */
17
- export declare function NotificationPanelBlank({ icon, iconAppearance, title, description, className, ...rest }: NotificationPanelBlankProps): import("react/jsx-runtime").JSX.Element;
4
+ export declare function NotificationPanelBlank({ icon, className, ...props }: NotificationPanelBlankProps): import("react/jsx-runtime").JSX.Element;
@@ -9,15 +9,13 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
13
13
  import cn from 'classnames';
14
- import { IconPredefined } from '@snack-uikit/icon-predefined';
15
- import { Typography } from '@snack-uikit/typography';
16
- import { extractSupportProps } from '@snack-uikit/utils';
17
- import { TEST_IDS } from '../../constants';
14
+ import { InfoBlock } from '@snack-uikit/info-block';
18
15
  import styles from './styles.module.css';
19
16
  /** Компонента для "заглушки" вместо карточек в панели */
20
17
  export function NotificationPanelBlank(_a) {
21
- var { icon, iconAppearance = 'neutral', title, description, className } = _a, rest = __rest(_a, ["icon", "iconAppearance", "title", "description", "className"]);
22
- return (_jsxs("div", Object.assign({ className: cn(styles.notificationPanelBlank, className) }, extractSupportProps(rest), { children: [icon && _jsx(IconPredefined, { icon: icon, appearance: iconAppearance, size: 'l', "data-test-id": TEST_IDS.blank.icon }), _jsxs("div", { className: styles.notificationPanelBlankContent, children: [_jsx(Typography.SansTitleS, { tag: 'div', className: styles.notificationPanelBlankTitle, "data-test-id": TEST_IDS.blank.title, children: title }), _jsx(Typography.SansBodyM, { tag: 'div', className: styles.notificationPanelBlankDescription, "data-test-id": TEST_IDS.blank.description, children: description })] })] })));
18
+ var _b;
19
+ var { icon, className } = _a, props = __rest(_a, ["icon", "className"]);
20
+ return (_jsx(_Fragment, { children: _jsx(InfoBlock, Object.assign({}, props, { icon: icon ? Object.assign(Object.assign({}, icon), { appearance: (_b = icon.appearance) !== null && _b !== void 0 ? _b : 'neutral' }) : undefined, size: 'l', align: 'vertical', className: cn(styles.notificationPanelBlank, className) })) }));
23
21
  }
@@ -1,27 +1,3 @@
1
1
  .notificationPanelBlank{
2
- gap:var(--space-notification-panel-blank-gap, 16px);
3
- padding-top:var(--space-notification-panel-blank-vertical-padding, 24px);
4
- padding-bottom:var(--space-notification-panel-blank-vertical-padding, 24px);
5
- display:flex;
6
- flex-direction:column;
7
- align-items:center;
8
- box-sizing:border-box;
9
- }
10
-
11
- .notificationPanelBlankContent{
12
- gap:var(--space-notification-panel-blank-text-gap, 4px);
13
- display:flex;
14
- flex-direction:column;
15
- align-items:center;
16
- box-sizing:border-box;
17
- text-align:center;
18
- }
19
-
20
- .notificationPanelBlankTitle{
21
- color:var(--sys-neutral-text-main, #33333b);
22
- }
23
-
24
- .notificationPanelBlankDescription{
25
- color:var(--sys-neutral-text-support, #656771);
26
- white-space:pre-line;
2
+ padding:var(--space-notification-panel-body-info-padding, 24px);
27
3
  }
@@ -0,0 +1,8 @@
1
+ import { WithSupportProps } from '@snack-uikit/utils';
2
+ export type NotificationPanelDividerProps = WithSupportProps<{
3
+ /** Текст разделителя */
4
+ text: string;
5
+ className?: string;
6
+ }>;
7
+ /** Разделитель для группировки или разделения карточек в списке */
8
+ export declare function NotificationPanelDivider({ text, className, ...rest }: NotificationPanelDividerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,21 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { jsx as _jsx } from "react/jsx-runtime";
13
+ import cn from 'classnames';
14
+ import { Typography } from '@snack-uikit/typography';
15
+ import { extractSupportProps } from '@snack-uikit/utils';
16
+ import styles from './styles.module.css';
17
+ /** Разделитель для группировки или разделения карточек в списке */
18
+ export function NotificationPanelDivider(_a) {
19
+ var { text, className } = _a, rest = __rest(_a, ["text", "className"]);
20
+ return (_jsx(Typography.LightLabelS, Object.assign({ className: cn(styles.notificationPanelDivider, className) }, extractSupportProps(rest), { children: text })));
21
+ }
@@ -0,0 +1 @@
1
+ export * from './NotificationPanelDivider';
@@ -0,0 +1 @@
1
+ export * from './NotificationPanelDivider';
@@ -0,0 +1,10 @@
1
+ .notificationPanelDivider{
2
+ height:var(--size-notification-panel-readed-divider, 32px);
3
+ border-bottom-width:var(--border-width-notification-panel-container, 1px);
4
+ display:flex;
5
+ align-items:center;
6
+ justify-content:center;
7
+ color:var(--sys-neutral-text-light, #868892);
8
+ border-bottom-color:var(--sys-neutral-decor-default, #dfe2ec);
9
+ border-bottom-style:solid;
10
+ }
@@ -8,10 +8,5 @@ export declare const TEST_IDS: {
8
8
  };
9
9
  readAll: string;
10
10
  footerButton: string;
11
- blank: {
12
- icon: string;
13
- title: string;
14
- description: string;
15
- };
16
11
  skeleton: string;
17
12
  };
@@ -8,10 +8,5 @@ export const TEST_IDS = {
8
8
  },
9
9
  readAll: 'notification-panel__readAll',
10
10
  footerButton: 'notification-panel__footerButton',
11
- blank: {
12
- icon: 'notification-panel__blank__icon',
13
- title: 'notification-panel__blank__title',
14
- description: 'notification-panel__blank__description',
15
- },
16
11
  skeleton: 'notification-panel__skeleton',
17
12
  };
@@ -94,4 +94,18 @@
94
94
  .notificationPanelFooterButton:active{
95
95
  color:var(--sys-neutral-text-main, #33333b);
96
96
  border-color:var(--sys-neutral-decor-default, #dfe2ec);
97
+ }
98
+
99
+ .wrapper{
100
+ box-sizing:border-box;
101
+ width:100%;
102
+ height:100%;
103
+ display:flex;
104
+ flex-direction:column;
105
+ }
106
+
107
+ .scrollStub{
108
+ height:calc(var(--dimension-025m, 2px) / 2);
109
+ margin-top:calc(var(--dimension-025m, 2px) / -2);
110
+ background:transparent;
97
111
  }
@@ -1,9 +1,11 @@
1
- import { ReactNode } from 'react';
1
+ import { ReactElement } from 'react';
2
2
  import { PopoverPrivateProps } from '@snack-uikit/popover-private';
3
3
  import { WithSupportProps } from '@snack-uikit/utils';
4
+ import { NotificationPanel, NotificationPanelProps } from '../NotificationPanel';
4
5
  export type NotificationPanelPopoverProps = WithSupportProps<{
5
- content: ReactNode;
6
+ content: ReactElement<NotificationPanelProps, typeof NotificationPanel>;
6
7
  /** CSS-класс для элемента содержащего контент */
7
8
  contentClassName?: string;
8
9
  } & Pick<PopoverPrivateProps, 'className' | 'triggerClassName' | 'open' | 'onOpenChange' | 'hoverDelayOpen' | 'hoverDelayClose' | 'offset' | 'children' | 'closeOnEscapeKey' | 'triggerClickByKeys' | 'triggerRef'> & Partial<Pick<PopoverPrivateProps, 'trigger' | 'placement'>>>;
10
+ /** Компонент-обёртка для NotificationPanel для использования как выпадающий элемент */
9
11
  export declare function NotificationPanelPopover({ content, trigger, placement, children, contentClassName, ...otherProps }: NotificationPanelPopoverProps): import("react/jsx-runtime").JSX.Element | null;
@@ -13,6 +13,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
13
13
  import cn from 'classnames';
14
14
  import { PopoverPrivate } from '@snack-uikit/popover-private';
15
15
  import styles from './styles.module.css';
16
+ /** Компонент-обёртка для NotificationPanel для использования как выпадающий элемент */
16
17
  export function NotificationPanelPopover(_a) {
17
18
  var { content, trigger = 'click', placement = 'bottom-end', children, contentClassName } = _a, otherProps = __rest(_a, ["content", "trigger", "placement", "children", "contentClassName"]);
18
19
  if (!children) {
@@ -1,2 +1,3 @@
1
1
  export * from './NotificationCard';
2
2
  export * from './NotificationPanel';
3
+ export * from './NotificationPanelPopover';
@@ -1,2 +1,3 @@
1
1
  export * from './NotificationCard';
2
2
  export * from './NotificationPanel';
3
+ export * from './NotificationPanelPopover';
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "Notification",
7
- "version": "0.6.18-preview-3cdd8d31.0",
7
+ "version": "0.7.0",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -33,19 +33,19 @@
33
33
  "scripts": {},
34
34
  "dependencies": {
35
35
  "@snack-uikit/button": "0.16.1",
36
- "@snack-uikit/chips": "0.11.8-preview-3cdd8d31.0",
37
- "@snack-uikit/icon-predefined": "0.4.2",
38
- "@snack-uikit/icons": "0.20.2-preview-3cdd8d31.0",
39
- "@snack-uikit/link": "0.11.3-preview-3cdd8d31.0",
40
- "@snack-uikit/list": "0.3.5-preview-3cdd8d31.0",
36
+ "@snack-uikit/chips": "0.11.7",
37
+ "@snack-uikit/icons": "0.20.1",
38
+ "@snack-uikit/info-block": "0.1.3",
39
+ "@snack-uikit/link": "0.11.2",
40
+ "@snack-uikit/list": "0.3.4",
41
41
  "@snack-uikit/popover-private": "0.13.0",
42
42
  "@snack-uikit/scroll": "0.5.1",
43
43
  "@snack-uikit/skeleton": "0.3.3",
44
- "@snack-uikit/tag": "0.7.5-preview-3cdd8d31.0",
45
- "@snack-uikit/truncate-string": "0.4.9-preview-3cdd8d31.0",
44
+ "@snack-uikit/tag": "0.7.4",
45
+ "@snack-uikit/truncate-string": "0.4.8",
46
46
  "@snack-uikit/typography": "0.6.1",
47
47
  "@snack-uikit/utils": "3.2.0",
48
48
  "classnames": "2.3.2"
49
49
  },
50
- "gitHead": "5c53d9cb0f3fcf9430b33814daca0755e43cc103"
50
+ "gitHead": "5489922b5df66a5cae5f4af6c050872a9a6cd23a"
51
51
  }
@@ -1,6 +1,7 @@
1
1
  import cn from 'classnames';
2
2
  import { MouseEventHandler, ReactElement, ReactNode, useEffect, useMemo, useRef, useState } from 'react';
3
3
 
4
+ import { ButtonSimple, ButtonSimpleProps, ButtonTonal, ButtonTonalProps } from '@snack-uikit/button';
4
5
  import { Link, LinkProps } from '@snack-uikit/link';
5
6
  import { BaseItemProps } from '@snack-uikit/list';
6
7
  import { TruncateString } from '@snack-uikit/truncate-string';
@@ -39,6 +40,10 @@ export type NotificationCardProps = WithSupportProps<{
39
40
  onClick?: MouseEventHandler<HTMLDivElement>;
40
41
  /** Колбэк при попадании карточки в область видимости на 80% */
41
42
  onVisible?(cardId: string): void;
43
+ /** Кнопка главного действия у карточки */
44
+ primaryButton?: Omit<ButtonTonalProps, 'size' | 'appearance' | 'data-test-id'>;
45
+ /** Кнопка второстепенного действия у карточки */
46
+ secondaryButton?: Omit<ButtonSimpleProps, 'size' | 'appearance' | 'data-test-id'>;
42
47
  /** Дополнительные действия у карточки */
43
48
  actions?: Action[];
44
49
  /** CSS-класс */
@@ -56,15 +61,22 @@ export function NotificationCard({
56
61
  link,
57
62
  date,
58
63
  onClick,
64
+ primaryButton,
65
+ secondaryButton,
59
66
  actions,
60
67
  onVisible,
61
68
  className,
62
69
  ...rest
63
70
  }: NotificationCardProps) {
64
- const { icon, linkOnColor } = useMemo<{ icon: ReturnType<typeof getIcon>; linkOnColor: LinkProps['appearance'] }>(
71
+ const { icon, linkOnColor, buttonAppearance } = useMemo<{
72
+ icon: ReturnType<typeof getIcon>;
73
+ linkOnColor: LinkProps['appearance'];
74
+ buttonAppearance: ButtonTonalProps['appearance'];
75
+ }>(
65
76
  () => ({
66
77
  icon: getIcon(appearance),
67
78
  linkOnColor: appearance === APPEARANCE.ErrorCritical ? 'red' : undefined,
79
+ buttonAppearance: appearance === APPEARANCE.ErrorCritical ? 'destructive' : 'primary',
68
80
  }),
69
81
  [appearance],
70
82
  );
@@ -144,6 +156,28 @@ export function NotificationCard({
144
156
  </Typography.SansBodyS>
145
157
  )}
146
158
 
159
+ {(primaryButton || secondaryButton) && (
160
+ <div className={styles.notificationCardButtons}>
161
+ {secondaryButton && (
162
+ <ButtonSimple
163
+ {...secondaryButton}
164
+ appearance={buttonAppearance}
165
+ size='s'
166
+ data-test-id={TEST_IDS.primaryButton}
167
+ />
168
+ )}
169
+
170
+ {primaryButton && (
171
+ <ButtonTonal
172
+ {...primaryButton}
173
+ appearance={buttonAppearance}
174
+ size='s'
175
+ data-test-id={TEST_IDS.secondaryButton}
176
+ />
177
+ )}
178
+ </div>
179
+ )}
180
+
147
181
  {showFooter && (
148
182
  <div className={styles.notificationCardFooter}>
149
183
  {link && (
@@ -18,4 +18,6 @@ export const TEST_IDS = {
18
18
  droplistTrigger: 'notification-card__droplist-trigger',
19
19
  droplistAction: 'notification-card__droplist-action',
20
20
  },
21
+ primaryButton: 'notification-card__primary-button',
22
+ secondaryButton: 'notification-card__secondary-button',
21
23
  };
@@ -170,3 +170,9 @@
170
170
  color: $sys-neutral-text-light;
171
171
  text-align: right;
172
172
  }
173
+
174
+ .notificationCardButtons {
175
+ display: flex;
176
+ align-items: center;
177
+ justify-content: flex-end;
178
+ }
@@ -1,4 +1,5 @@
1
- import { MouseEventHandler, ReactNode, useMemo } from 'react';
1
+ import cn from 'classnames';
2
+ import { MouseEventHandler, ReactNode, RefObject, useMemo } from 'react';
2
3
 
3
4
  import { ButtonFunction, ButtonFunctionProps } from '@snack-uikit/button';
4
5
  import { ChipToggle, ChipToggleProps } from '@snack-uikit/chips';
@@ -6,8 +7,8 @@ import { Scroll } from '@snack-uikit/scroll';
6
7
  import { SkeletonContextProvider, WithSkeleton } from '@snack-uikit/skeleton';
7
8
  import { TruncateString } from '@snack-uikit/truncate-string';
8
9
  import { Typography } from '@snack-uikit/typography';
10
+ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
9
11
 
10
- import { NotificationPanelPopover, NotificationPanelPopoverProps } from '../../helperComponents';
11
12
  import { NotificationCardSkeleton } from '../NotificationCard/components';
12
13
  import {
13
14
  NotificationPanelBlank,
@@ -15,12 +16,13 @@ import {
15
16
  NotificationPanelSettings,
16
17
  NotificationPanelSettingsProps,
17
18
  } from './components';
19
+ import { NotificationPanelDivider, NotificationPanelDividerProps } from './components/NotificationPanelDivider';
18
20
  import { TEST_IDS } from './constants';
19
21
  import styles from './styles.module.scss';
20
22
 
21
23
  export type { NotificationPanelBlankProps };
22
24
 
23
- export type NotificationPanelProps = {
25
+ export type NotificationPanelProps = WithSupportProps<{
24
26
  /** Заголовок панели */
25
27
  title: string;
26
28
  /** Кнопка настроек и выпадающий список */
@@ -31,25 +33,27 @@ export type NotificationPanelProps = {
31
33
  readAllButton?: Omit<ButtonFunctionProps, 'data-test-id'> & {
32
34
  onClick: ButtonFunctionProps['onClick'];
33
35
  };
34
- /** Элемент для открытия панели */
35
- triggerElement: NotificationPanelPopoverProps['children'];
36
36
  /** Кнопка внизу панели */
37
37
  footerButton?: {
38
38
  label: string;
39
39
  onClick: MouseEventHandler<HTMLButtonElement>;
40
40
  };
41
+ className?: string;
41
42
  /** Состояние загрузки */
42
43
  loading?: boolean;
43
44
  /** Контент для отрисовки (e.g NotificationCard | NotificationPanel.Blank) */
44
45
  content?: ReactNode;
45
46
  /** Количество скелетонов карточек для отображения при загрузке */
46
47
  skeletonsAmount?: number;
47
- } & Omit<NotificationPanelPopoverProps, 'children' | 'content'>;
48
+ /** Ссылка на элемент, обозначающий самый конец прокручиваемого списка */
49
+ scrollEndRef?: RefObject<HTMLDivElement>;
50
+ /** Ссылка на контейнер, который скроллится */
51
+ scrollContainerRef?: RefObject<HTMLElement>;
52
+ }>;
48
53
 
49
54
  /** Компонент панели для уведомлений */
50
55
  export function NotificationPanel({
51
56
  title,
52
- triggerElement,
53
57
  settings,
54
58
  chips,
55
59
  readAllButton,
@@ -57,73 +61,72 @@ export function NotificationPanel({
57
61
  content,
58
62
  loading,
59
63
  skeletonsAmount = 2,
64
+ scrollEndRef,
65
+ scrollContainerRef,
66
+ className,
60
67
  ...rest
61
68
  }: NotificationPanelProps) {
62
69
  const skeletons = useMemo(() => Array.from({ length: skeletonsAmount }, (_, i) => i), [skeletonsAmount]);
63
70
 
64
71
  return (
65
- <NotificationPanelPopover
66
- {...rest}
67
- content={
68
- <>
69
- <div className={styles.notificationPanelHeader}>
70
- <div className={styles.notificationPanelHeadline}>
71
- <Typography.SansHeadlineS className={styles.notificationPanelTitle}>
72
- <TruncateString text={title} data-test-id={TEST_IDS.title} />
73
- </Typography.SansHeadlineS>
72
+ <div className={cn(styles.wrapper, className)} {...extractSupportProps(rest)}>
73
+ <div className={styles.notificationPanelHeader}>
74
+ <div className={styles.notificationPanelHeadline}>
75
+ <Typography.SansHeadlineS className={styles.notificationPanelTitle}>
76
+ <TruncateString text={title} data-test-id={TEST_IDS.title} />
77
+ </Typography.SansHeadlineS>
74
78
 
75
- {settings && <NotificationPanelSettings {...settings} />}
76
- </div>
79
+ {settings && <NotificationPanelSettings {...settings} />}
80
+ </div>
77
81
 
78
- <div className={styles.notificationPanelHeaderFunctions}>
79
- <div className={styles.notificationPanelChips}>
80
- {chips?.map(chip => (
81
- <ChipToggle
82
- {...chip}
83
- key={chip.label}
84
- data-test-id={`${TEST_IDS.chip}-${chip.label}`}
85
- size='xs'
86
- disabled={chip.disabled || loading}
87
- />
88
- ))}
89
- </div>
90
-
91
- {readAllButton && (
92
- <ButtonFunction
93
- {...readAllButton}
94
- disabled={readAllButton.disabled || loading}
95
- data-test-id={TEST_IDS.readAll}
96
- />
97
- )}
98
- </div>
82
+ <div className={styles.notificationPanelHeaderFunctions}>
83
+ <div className={styles.notificationPanelChips}>
84
+ {chips?.map(chip => (
85
+ <ChipToggle
86
+ {...chip}
87
+ key={chip.label}
88
+ data-test-id={`${TEST_IDS.chip}-${chip.label}`}
89
+ size='xs'
90
+ disabled={chip.disabled || loading}
91
+ />
92
+ ))}
99
93
  </div>
100
94
 
101
- <Scroll size='m' className={styles.notificationPanelBody}>
102
- {loading ? (
103
- <SkeletonContextProvider loading={loading || false}>
104
- {skeletons.map(skeleton => (
105
- <WithSkeleton key={skeleton} skeleton={<NotificationCardSkeleton />} />
106
- ))}
107
- </SkeletonContextProvider>
108
- ) : (
109
- content
110
- )}
111
- </Scroll>
112
-
113
- {!loading && footerButton && (
114
- <button className={styles.notificationPanelFooterButton} data-test-id={TEST_IDS.footerButton}>
115
- <Typography.SansLabelS>{footerButton.label}</Typography.SansLabelS>
116
- </button>
95
+ {readAllButton && (
96
+ <ButtonFunction
97
+ {...readAllButton}
98
+ disabled={readAllButton.disabled || loading}
99
+ data-test-id={TEST_IDS.readAll}
100
+ />
117
101
  )}
118
- </>
119
- }
120
- >
121
- {triggerElement}
122
- </NotificationPanelPopover>
102
+ </div>
103
+ </div>
104
+
105
+ <Scroll size='m' className={styles.notificationPanelBody} ref={scrollContainerRef}>
106
+ {content}
107
+ {loading && (
108
+ <SkeletonContextProvider loading={loading || false}>
109
+ {skeletons.map(skeleton => (
110
+ <WithSkeleton key={skeleton} skeleton={<NotificationCardSkeleton />} />
111
+ ))}
112
+ </SkeletonContextProvider>
113
+ )}
114
+
115
+ <div className={styles.scrollStub} ref={scrollEndRef} />
116
+ </Scroll>
117
+
118
+ {footerButton && (
119
+ <button onClick={footerButton.onClick} className={styles.notificationPanelFooterButton} data-test-id={TEST_IDS.footerButton}>
120
+ <Typography.SansLabelS>{footerButton.label}</Typography.SansLabelS>
121
+ </button>
122
+ )}
123
+ </div>
123
124
  );
124
125
  }
125
126
 
126
127
  export namespace NotificationPanel {
127
128
  export const Blank: typeof NotificationPanelBlank = NotificationPanelBlank;
128
129
  export type BlankProps = NotificationPanelBlankProps;
130
+ export const Divider: typeof NotificationPanelDivider = NotificationPanelDivider;
131
+ export type DividerProps = NotificationPanelDividerProps;
129
132
  }
@@ -1,56 +1,22 @@
1
1
  import cn from 'classnames';
2
- import { ReactNode } from 'react';
3
2
 
4
- import { IconPredefined, IconPredefinedProps } from '@snack-uikit/icon-predefined';
5
- import { Typography } from '@snack-uikit/typography';
6
- import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
3
+ import { InfoBlock, InfoBlockProps } from '@snack-uikit/info-block';
7
4
 
8
- import { TEST_IDS } from '../../constants';
9
5
  import styles from './styles.module.scss';
10
6
 
11
- export type NotificationPanelBlankProps = WithSupportProps<{
12
- /** Заголовок */
13
- title: ReactNode;
14
- /** Описание */
15
- description?: ReactNode;
16
- /** Иконка */
17
- icon?: IconPredefinedProps['icon'];
18
- /** Цвет обводки для иконки */
19
- iconAppearance?: IconPredefinedProps['appearance'];
20
- /** CSS-класс */
21
- className?: string;
22
- }>;
7
+ export type NotificationPanelBlankProps = Omit<InfoBlockProps, 'footer' | 'align' | 'size'>;
23
8
 
24
9
  /** Компонента для "заглушки" вместо карточек в панели */
25
- export function NotificationPanelBlank({
26
- icon,
27
- iconAppearance = 'neutral',
28
- title,
29
- description,
30
- className,
31
- ...rest
32
- }: NotificationPanelBlankProps) {
10
+ export function NotificationPanelBlank({ icon, className, ...props }: NotificationPanelBlankProps) {
33
11
  return (
34
- <div className={cn(styles.notificationPanelBlank, className)} {...extractSupportProps(rest)}>
35
- {icon && <IconPredefined icon={icon} appearance={iconAppearance} size='l' data-test-id={TEST_IDS.blank.icon} />}
36
-
37
- <div className={styles.notificationPanelBlankContent}>
38
- <Typography.SansTitleS
39
- tag='div'
40
- className={styles.notificationPanelBlankTitle}
41
- data-test-id={TEST_IDS.blank.title}
42
- >
43
- {title}
44
- </Typography.SansTitleS>
45
-
46
- <Typography.SansBodyM
47
- tag='div'
48
- className={styles.notificationPanelBlankDescription}
49
- data-test-id={TEST_IDS.blank.description}
50
- >
51
- {description}
52
- </Typography.SansBodyM>
53
- </div>
54
- </div>
12
+ <>
13
+ <InfoBlock
14
+ {...props}
15
+ icon={icon ? { ...icon, appearance: icon.appearance ?? 'neutral' } : undefined}
16
+ size='l'
17
+ align='vertical'
18
+ className={cn(styles.notificationPanelBlank, className)}
19
+ />
20
+ </>
55
21
  );
56
22
  }
@@ -2,31 +2,5 @@
2
2
  @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
3
3
 
4
4
  .notificationPanelBlank {
5
- @include composite-var($notification-panel-blank-container);
6
-
7
- display: flex;
8
- flex-direction: column;
9
- align-items: center;
10
- box-sizing: border-box;
11
- }
12
-
13
- .notificationPanelBlankContent {
14
- @include composite-var($notification-panel-blank-text);
15
-
16
- display: flex;
17
- flex-direction: column;
18
- align-items: center;
19
-
20
- box-sizing: border-box;
21
-
22
- text-align: center;
23
- }
24
-
25
- .notificationPanelBlankTitle {
26
- color: $sys-neutral-text-main;
27
- }
28
-
29
- .notificationPanelBlankDescription {
30
- color: $sys-neutral-text-support;
31
- white-space: pre-line;
5
+ @include composite-var($notification-panel-body-info-container);
32
6
  }
@@ -0,0 +1,21 @@
1
+ import cn from 'classnames';
2
+
3
+ import { Typography } from '@snack-uikit/typography';
4
+ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
5
+
6
+ import styles from './styles.module.scss';
7
+
8
+ export type NotificationPanelDividerProps = WithSupportProps<{
9
+ /** Текст разделителя */
10
+ text: string;
11
+ className?: string;
12
+ }>;
13
+
14
+ /** Разделитель для группировки или разделения карточек в списке */
15
+ export function NotificationPanelDivider({ text, className, ...rest }: NotificationPanelDividerProps) {
16
+ return (
17
+ <Typography.LightLabelS className={cn(styles.notificationPanelDivider, className)} {...extractSupportProps(rest)}>
18
+ {text}
19
+ </Typography.LightLabelS>
20
+ );
21
+ }
@@ -0,0 +1 @@
1
+ export * from './NotificationPanelDivider';
@@ -0,0 +1,14 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-notification-notificationPanel';
2
+
3
+ .notificationPanelDivider {
4
+ @include composite-var($notification-panel-readed-divider);
5
+
6
+ display: flex;
7
+ align-items: center;
8
+ justify-content: center;
9
+
10
+ color: $sys-neutral-text-light;
11
+
12
+ border-bottom-color: $sys-neutral-decor-default;
13
+ border-bottom-style: solid;
14
+ }
@@ -8,10 +8,5 @@ export const TEST_IDS = {
8
8
  },
9
9
  readAll: 'notification-panel__readAll',
10
10
  footerButton: 'notification-panel__footerButton',
11
- blank: {
12
- icon: 'notification-panel__blank__icon',
13
- title: 'notification-panel__blank__title',
14
- description: 'notification-panel__blank__description',
15
- },
16
11
  skeleton: 'notification-panel__skeleton',
17
12
  };
@@ -107,3 +107,17 @@
107
107
  border-color: $sys-neutral-decor-default;
108
108
  }
109
109
  }
110
+
111
+ .wrapper {
112
+ box-sizing: border-box;
113
+ width: 100%;
114
+ height: 100%;
115
+ display: flex;
116
+ flex-direction: column;
117
+ }
118
+
119
+ .scrollStub {
120
+ height: calc($dimension-025m / 2);
121
+ margin-top: calc($dimension-025m / -2);
122
+ background: transparent;
123
+ }
@@ -1,14 +1,15 @@
1
1
  import cn from 'classnames';
2
- import { ReactNode } from 'react';
2
+ import { ReactElement } from 'react';
3
3
 
4
4
  import { PopoverPrivate, PopoverPrivateProps } from '@snack-uikit/popover-private';
5
5
  import { WithSupportProps } from '@snack-uikit/utils';
6
6
 
7
+ import { NotificationPanel, NotificationPanelProps } from '../NotificationPanel';
7
8
  import styles from './styles.module.scss';
8
9
 
9
10
  export type NotificationPanelPopoverProps = WithSupportProps<
10
11
  {
11
- content: ReactNode;
12
+ content: ReactElement<NotificationPanelProps, typeof NotificationPanel>;
12
13
  /** CSS-класс для элемента содержащего контент */
13
14
  contentClassName?: string;
14
15
  } & Pick<
@@ -28,6 +29,7 @@ export type NotificationPanelPopoverProps = WithSupportProps<
28
29
  Partial<Pick<PopoverPrivateProps, 'trigger' | 'placement'>>
29
30
  >;
30
31
 
32
+ /** Компонент-обёртка для NotificationPanel для использования как выпадающий элемент */
31
33
  export function NotificationPanelPopover({
32
34
  content,
33
35
  trigger = 'click',
@@ -1,2 +1,3 @@
1
1
  export * from './NotificationCard';
2
2
  export * from './NotificationPanel';
3
+ export * from './NotificationPanelPopover';
@@ -1 +0,0 @@
1
- export * from './NotificationPanelPopover';
@@ -1 +0,0 @@
1
- export * from './NotificationPanelPopover';
@@ -1 +0,0 @@
1
- export * from './NotificationPanelPopover';