@snack-uikit/modal 0.7.9-preview-85c5f47b.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 (84) hide show
  1. package/CHANGELOG.md +398 -0
  2. package/LICENSE +201 -0
  3. package/README.md +74 -0
  4. package/dist/components/Modal/Modal.d.ts +9 -0
  5. package/dist/components/Modal/Modal.js +30 -0
  6. package/dist/components/Modal/index.d.ts +1 -0
  7. package/dist/components/Modal/index.js +1 -0
  8. package/dist/components/Modal/styles.module.css +3 -0
  9. package/dist/components/Modal/types.d.ts +50 -0
  10. package/dist/components/Modal/types.js +1 -0
  11. package/dist/components/ModalCustom/ModalCustom.d.ts +40 -0
  12. package/dist/components/ModalCustom/ModalCustom.js +37 -0
  13. package/dist/components/ModalCustom/index.d.ts +1 -0
  14. package/dist/components/ModalCustom/index.js +1 -0
  15. package/dist/components/ModalCustom/styles.module.css +40 -0
  16. package/dist/components/ModalCustom/utils.d.ts +1 -0
  17. package/dist/components/ModalCustom/utils.js +9 -0
  18. package/dist/components/index.d.ts +2 -0
  19. package/dist/components/index.js +2 -0
  20. package/dist/constants.d.ts +37 -0
  21. package/dist/constants.js +41 -0
  22. package/dist/helperComponents/Body/Body.d.ts +14 -0
  23. package/dist/helperComponents/Body/Body.js +22 -0
  24. package/dist/helperComponents/Body/index.d.ts +1 -0
  25. package/dist/helperComponents/Body/index.js +1 -0
  26. package/dist/helperComponents/Body/styles.module.css +13 -0
  27. package/dist/helperComponents/ButtonClose/ButtonClose.d.ts +5 -0
  28. package/dist/helperComponents/ButtonClose/ButtonClose.js +7 -0
  29. package/dist/helperComponents/ButtonClose/index.d.ts +1 -0
  30. package/dist/helperComponents/ButtonClose/index.js +1 -0
  31. package/dist/helperComponents/ButtonClose/styles.module.css +35 -0
  32. package/dist/helperComponents/Footer/Footer.d.ts +16 -0
  33. package/dist/helperComponents/Footer/Footer.js +21 -0
  34. package/dist/helperComponents/Footer/index.d.ts +1 -0
  35. package/dist/helperComponents/Footer/index.js +1 -0
  36. package/dist/helperComponents/Footer/styles.module.css +39 -0
  37. package/dist/helperComponents/Header/Header.d.ts +23 -0
  38. package/dist/helperComponents/Header/Header.js +26 -0
  39. package/dist/helperComponents/Header/index.d.ts +2 -0
  40. package/dist/helperComponents/Header/index.js +2 -0
  41. package/dist/helperComponents/Header/styles.module.css +52 -0
  42. package/dist/helperComponents/Header/types.d.ts +4 -0
  43. package/dist/helperComponents/Header/types.js +1 -0
  44. package/dist/helperComponents/OverlayElement/OverlayElement.d.ts +7 -0
  45. package/dist/helperComponents/OverlayElement/OverlayElement.js +10 -0
  46. package/dist/helperComponents/OverlayElement/index.d.ts +1 -0
  47. package/dist/helperComponents/OverlayElement/index.js +1 -0
  48. package/dist/helperComponents/OverlayElement/styles.module.css +13 -0
  49. package/dist/helperComponents/index.d.ts +5 -0
  50. package/dist/helperComponents/index.js +5 -0
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.js +1 -0
  53. package/dist/utils.d.ts +33 -0
  54. package/dist/utils.js +60 -0
  55. package/package.json +51 -0
  56. package/src/components/Modal/Modal.tsx +104 -0
  57. package/src/components/Modal/index.ts +1 -0
  58. package/src/components/Modal/styles.module.scss +7 -0
  59. package/src/components/Modal/types.ts +54 -0
  60. package/src/components/ModalCustom/ModalCustom.tsx +96 -0
  61. package/src/components/ModalCustom/index.ts +1 -0
  62. package/src/components/ModalCustom/styles.module.scss +38 -0
  63. package/src/components/ModalCustom/utils.ts +12 -0
  64. package/src/components/index.ts +2 -0
  65. package/src/constants.ts +41 -0
  66. package/src/helperComponents/Body/Body.tsx +32 -0
  67. package/src/helperComponents/Body/index.ts +1 -0
  68. package/src/helperComponents/Body/styles.module.scss +14 -0
  69. package/src/helperComponents/ButtonClose/ButtonClose.tsx +21 -0
  70. package/src/helperComponents/ButtonClose/index.ts +1 -0
  71. package/src/helperComponents/ButtonClose/styles.module.scss +44 -0
  72. package/src/helperComponents/Footer/Footer.tsx +38 -0
  73. package/src/helperComponents/Footer/index.ts +1 -0
  74. package/src/helperComponents/Footer/styles.module.scss +44 -0
  75. package/src/helperComponents/Header/Header.tsx +81 -0
  76. package/src/helperComponents/Header/index.ts +2 -0
  77. package/src/helperComponents/Header/styles.module.scss +52 -0
  78. package/src/helperComponents/Header/types.ts +4 -0
  79. package/src/helperComponents/OverlayElement/OverlayElement.tsx +30 -0
  80. package/src/helperComponents/OverlayElement/index.ts +1 -0
  81. package/src/helperComponents/OverlayElement/styles.module.scss +17 -0
  82. package/src/helperComponents/index.ts +5 -0
  83. package/src/index.ts +1 -0
  84. package/src/utils.ts +69 -0
@@ -0,0 +1,52 @@
1
+ .header{
2
+ }
3
+
4
+ .image{
5
+ border-top-left-radius:var(--radius-modal-modal-window, 32px);
6
+ border-top-right-radius:var(--radius-modal-modal-window, 32px);
7
+ height:var(--size-modal-image, 184px);
8
+ display:block;
9
+ width:100%;
10
+ -o-object-fit:cover;
11
+ object-fit:cover;
12
+ }
13
+
14
+ .icon{
15
+ padding-top:var(--space-modal-icon-decor, 32px);
16
+ display:flex;
17
+ justify-content:center;
18
+ }
19
+
20
+ .headlineLayout[data-align=default]{
21
+ padding-top:var(--space-modal-headline-layout-top, 32px);
22
+ padding-left:var(--space-modal-headline-layout-side-s, 32px);
23
+ padding-right:var(--space-modal-headline-layout-side-m, 64px);
24
+ padding-bottom:var(--space-modal-headline-layout-bottom, 24px);
25
+ gap:var(--space-modal-headline-layout-gap, 8px);
26
+ display:flex;
27
+ flex-direction:column;
28
+ }
29
+ .headlineLayout[data-align=center]{
30
+ padding-top:var(--space-modal-headline-layout-top, 32px);
31
+ padding-left:var(--space-modal-headline-layout-side-m, 64px);
32
+ padding-right:var(--space-modal-headline-layout-side-m, 64px);
33
+ padding-bottom:var(--space-modal-headline-layout-bottom, 24px);
34
+ gap:var(--space-modal-headline-layout-gap, 8px);
35
+ display:flex;
36
+ flex-direction:column;
37
+ align-items:center;
38
+ }
39
+
40
+ .headline{
41
+ gap:var(--space-modal-headline-layout-headline, 4px);
42
+ display:flex;
43
+ align-items:center;
44
+ }
45
+
46
+ .title{
47
+ display:grid;
48
+ }
49
+
50
+ .subtitle{
51
+ color:var(--sys-neutral-text-support, #565656);
52
+ }
@@ -0,0 +1,4 @@
1
+ export type ModalHeaderImage = {
2
+ src: string;
3
+ alt: string;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { ReactElement } from 'react';
2
+ export type OverlayElementProps = {
3
+ onClose(): void;
4
+ content: ReactElement;
5
+ blur?: boolean;
6
+ };
7
+ export declare function OverlayElement({ onClose, content, blur }: OverlayElementProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { TEST_IDS } from '../../constants';
3
+ import styles from './styles.module.css';
4
+ export function OverlayElement({ onClose, content, blur = false }) {
5
+ const handleClick = e => {
6
+ e.stopPropagation();
7
+ onClose();
8
+ };
9
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: styles.modalOverlay, "data-blur": blur || undefined, onClick: handleClick, "data-test-id": TEST_IDS.overlay }), content] }));
10
+ }
@@ -0,0 +1 @@
1
+ export * from './OverlayElement';
@@ -0,0 +1 @@
1
+ export * from './OverlayElement';
@@ -0,0 +1,13 @@
1
+ .modalOverlay{
2
+ position:fixed;
3
+ top:0;
4
+ left:0;
5
+ box-sizing:border-box;
6
+ width:100%;
7
+ height:100%;
8
+ background:var(--sys-blackout, rgba(0, 0, 0, 0.4784313725));
9
+ }
10
+ .modalOverlay[data-blur]{
11
+ -webkit-backdrop-filter:blur(var(--background-blur-background-blur, 4px));
12
+ backdrop-filter:blur(var(--background-blur-background-blur, 4px));
13
+ }
@@ -0,0 +1,5 @@
1
+ export * from './OverlayElement';
2
+ export * from './ButtonClose';
3
+ export * from './Header';
4
+ export * from './Body';
5
+ export * from './Footer';
@@ -0,0 +1,5 @@
1
+ export * from './OverlayElement';
2
+ export * from './ButtonClose';
3
+ export * from './Header';
4
+ export * from './Body';
5
+ export * from './Footer';
@@ -0,0 +1 @@
1
+ export * from './components';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './components';
@@ -0,0 +1,33 @@
1
+ /// <reference types="react" />
2
+ import { Align, ContentAlign, Size } from './constants';
3
+ import { ModalHeaderImage, ModalHeaderProps } from './helperComponents';
4
+ export declare function getAlignProps({ align, size }: {
5
+ align: Align;
6
+ size: Size;
7
+ }): {
8
+ header: ContentAlign;
9
+ body: ContentAlign;
10
+ footer: Align;
11
+ };
12
+ export declare function getButtonsSizes({ align, size }: {
13
+ align: Align;
14
+ size: Size;
15
+ }): {
16
+ filled: import("@snack-uikit/button/dist/constants").SizeSL;
17
+ outline: import("@snack-uikit/button/dist/constants").SizeSL;
18
+ simple: import("@snack-uikit/button/dist/constants").SizeSL;
19
+ light?: undefined;
20
+ } | {
21
+ filled: import("@snack-uikit/button/dist/constants").SizeSL;
22
+ outline: import("@snack-uikit/button/dist/constants").SizeSL;
23
+ light: import("@snack-uikit/button/dist/constants").SizeSL;
24
+ simple?: undefined;
25
+ };
26
+ export declare function isPictureImage(picture: ModalHeaderProps['picture']): picture is ModalHeaderImage;
27
+ export declare function getPicture({ size, picture }: {
28
+ size: Size;
29
+ picture: ModalHeaderProps['picture'];
30
+ }): import("react").JSXElementConstructor<{
31
+ size?: number | undefined;
32
+ className?: string | undefined;
33
+ }> | ModalHeaderImage | undefined;
package/dist/utils.js ADDED
@@ -0,0 +1,60 @@
1
+ import { ButtonFilled, ButtonOutline, ButtonSimple } from '@snack-uikit/button';
2
+ import { Align, ContentAlign, Size } from './constants';
3
+ const MAP_ALIGNS = {
4
+ [Align.Default]: {
5
+ header: ContentAlign.Default,
6
+ body: ContentAlign.Default,
7
+ footer: Align.Default,
8
+ },
9
+ [Align.Center]: {
10
+ header: ContentAlign.Center,
11
+ body: ContentAlign.Center,
12
+ footer: Align.Center,
13
+ },
14
+ [Align.Vertical]: {
15
+ header: ContentAlign.Center,
16
+ body: ContentAlign.Center,
17
+ footer: Align.Vertical,
18
+ },
19
+ };
20
+ export function getAlignProps({ align, size }) {
21
+ if (size === Size.L) {
22
+ return MAP_ALIGNS[Align.Default];
23
+ }
24
+ if (size === Size.M && align === Align.Vertical) {
25
+ return MAP_ALIGNS[Align.Default];
26
+ }
27
+ return MAP_ALIGNS[align];
28
+ }
29
+ export function getButtonsSizes({ align, size }) {
30
+ switch (true) {
31
+ case align === Align.Vertical && size === Size.S:
32
+ return {
33
+ filled: ButtonFilled.sizes.L,
34
+ outline: ButtonOutline.sizes.L,
35
+ simple: ButtonSimple.sizes.L,
36
+ };
37
+ case [Align.Default, Align.Center].includes(align):
38
+ default:
39
+ return {
40
+ filled: ButtonFilled.sizes.M,
41
+ outline: ButtonOutline.sizes.M,
42
+ light: ButtonSimple.sizes.M,
43
+ };
44
+ }
45
+ }
46
+ export function isPictureImage(picture) {
47
+ if (!picture)
48
+ return false;
49
+ return 'src' in picture;
50
+ }
51
+ export function getPicture({ size, picture }) {
52
+ switch (size) {
53
+ case Size.S:
54
+ return picture;
55
+ case Size.M:
56
+ case Size.L:
57
+ default:
58
+ return isPictureImage(picture) ? picture : undefined;
59
+ }
60
+ }
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@snack-uikit/modal",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "title": "Modal",
7
+ "version": "0.7.9-preview-85c5f47b.0",
8
+ "sideEffects": [
9
+ "*.css",
10
+ "*.woff",
11
+ "*.woff2"
12
+ ],
13
+ "description": "",
14
+ "main": "./dist/index.js",
15
+ "module": "./dist/index.js",
16
+ "homepage": "https://github.com/cloud-ru-tech/snack-uikit/tree/master/packages/modal",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/cloud-ru-tech/snack-uikit.git",
20
+ "directory": "packages/modal"
21
+ },
22
+ "author": "Белов Алексей <anbelov@cloud.ru>",
23
+ "contributors": [
24
+ "Белов Алексей <anbelov@cloud.ru>"
25
+ ],
26
+ "files": [
27
+ "dist",
28
+ "src",
29
+ "./CHANGELOG.md",
30
+ "./LICENSE"
31
+ ],
32
+ "license": "Apache-2.0",
33
+ "scripts": {},
34
+ "dependencies": {
35
+ "@snack-uikit/button": "0.13.7-preview-85c5f47b.0",
36
+ "@snack-uikit/icon-predefined": "0.2.6-preview-85c5f47b.0",
37
+ "@snack-uikit/icons": "0.18.2-preview-85c5f47b.0",
38
+ "@snack-uikit/link": "0.7.1-preview-85c5f47b.0",
39
+ "@snack-uikit/scroll": "0.3.5-preview-85c5f47b.0",
40
+ "@snack-uikit/tooltip": "0.8.1-preview-85c5f47b.0",
41
+ "@snack-uikit/truncate-string": "0.2.17-preview-85c5f47b.0",
42
+ "@snack-uikit/typography": "0.4.5-preview-85c5f47b.0",
43
+ "@snack-uikit/utils": "2.0.2-preview-85c5f47b.0",
44
+ "classnames": "2.3.2",
45
+ "react-modal": "3.16.1"
46
+ },
47
+ "devDependencies": {
48
+ "@types/react-modal": "3.16.0"
49
+ },
50
+ "gitHead": "6a7eef41d780d945f64f791448eaa72b9e0ae72d"
51
+ }
@@ -0,0 +1,104 @@
1
+ import { ButtonFilled, ButtonOutline, ButtonSimple } from '@snack-uikit/button';
2
+ import { Link } from '@snack-uikit/link';
3
+ import { TruncateString } from '@snack-uikit/truncate-string';
4
+ import { Typography } from '@snack-uikit/typography';
5
+
6
+ import { Align, Mode, Size, TEST_IDS } from '../../constants';
7
+ import { getAlignProps, getButtonsSizes, getPicture } from '../../utils';
8
+ import { ModalCustom } from '../ModalCustom';
9
+ import styles from './styles.module.scss';
10
+ import { ModalLProps, ModalMProps, ModalSProps } from './types';
11
+
12
+ export type ModalProps = ModalSProps | ModalMProps | ModalLProps;
13
+
14
+ export function Modal({
15
+ open,
16
+ onClose,
17
+ size = Size.S,
18
+ mode = Mode.Regular,
19
+ align = Align.Default,
20
+ title,
21
+ titleTooltip,
22
+ subtitle,
23
+ picture: pictureProp,
24
+ content,
25
+ approveButton,
26
+ cancelButton,
27
+ additionalButton,
28
+ disclaimer,
29
+ className,
30
+ ...rest
31
+ }: ModalProps) {
32
+ const aligns = getAlignProps({ align, size });
33
+ const buttonsSizes = getButtonsSizes({ align, size });
34
+ const picture = getPicture({ size, picture: pictureProp });
35
+
36
+ return (
37
+ <ModalCustom open={open} onClose={onClose} size={size} mode={mode} className={className} {...rest}>
38
+ <ModalCustom.Header
39
+ title={<TruncateString text={title} />}
40
+ titleTooltip={titleTooltip}
41
+ subtitle={subtitle}
42
+ picture={picture}
43
+ align={aligns.header}
44
+ />
45
+
46
+ {Boolean(content) && <ModalCustom.Body content={content} align={aligns.body} />}
47
+
48
+ <ModalCustom.Footer
49
+ actions={
50
+ <>
51
+ <ButtonFilled
52
+ {...approveButton}
53
+ size={buttonsSizes.filled}
54
+ className={styles.footerButton}
55
+ data-test-id={TEST_IDS.approveButton}
56
+ />
57
+
58
+ {cancelButton && (
59
+ <ButtonOutline
60
+ {...cancelButton}
61
+ size={buttonsSizes.outline}
62
+ className={styles.footerButton}
63
+ data-test-id={TEST_IDS.cancelButton}
64
+ />
65
+ )}
66
+
67
+ {additionalButton && (
68
+ <ButtonSimple
69
+ {...additionalButton}
70
+ size={buttonsSizes.light}
71
+ className={styles.footerButton}
72
+ data-test-id={TEST_IDS.additionalButton}
73
+ />
74
+ )}
75
+ </>
76
+ }
77
+ disclaimer={
78
+ disclaimer && (
79
+ <>
80
+ <Typography
81
+ family={Typography.families.Sans}
82
+ role={Typography.roles.Body}
83
+ size={Typography.sizes.S}
84
+ data-test-id={TEST_IDS.disclaimerText}
85
+ >
86
+ {disclaimer.text}
87
+ </Typography>
88
+
89
+ {disclaimer.link && (
90
+ <Link {...disclaimer.link} size={Link.sizes.S} external data-test-id={TEST_IDS.disclaimerLink} />
91
+ )}
92
+ </>
93
+ )
94
+ }
95
+ align={aligns.footer}
96
+ className={styles.modalFooter}
97
+ />
98
+ </ModalCustom>
99
+ );
100
+ }
101
+
102
+ Modal.aligns = Align;
103
+ Modal.modes = Mode;
104
+ Modal.sizes = Size;
@@ -0,0 +1 @@
1
+ export * from './Modal';
@@ -0,0 +1,7 @@
1
+ .modalFooter {
2
+ &[data-align='vertical'] {
3
+ .footerButton {
4
+ width: 100%;
5
+ }
6
+ }
7
+ }
@@ -0,0 +1,54 @@
1
+ import { ButtonFilledProps, ButtonOutlineProps, ButtonSimpleProps } from '@snack-uikit/button';
2
+ import { LinkProps } from '@snack-uikit/link';
3
+
4
+ import { Align, Size } from '../../constants';
5
+ import { ModalBodyProps, ModalHeaderImage, ModalHeaderProps } from '../../helperComponents';
6
+ import { ModalCustomProps } from '../ModalCustom';
7
+
8
+ type BaseModalProps = Omit<ModalCustomProps, 'children' | 'size'> & {
9
+ /** Заголовок модального окна */
10
+ title: string;
11
+ /** Всплывающая подсказка для заголовка */
12
+ titleTooltip?: ModalHeaderProps['titleTooltip'];
13
+ /** Подзаголовок */
14
+ subtitle?: string;
15
+ /** Содержимое модального окна */
16
+ content?: ModalBodyProps['content'];
17
+ /** Основная кнопка действия */
18
+ approveButton: Omit<ButtonFilledProps, 'size' | 'data-test-id'>;
19
+ /** Кнопка отмены */
20
+ cancelButton?: Omit<ButtonOutlineProps, 'size' | 'data-test-id'>;
21
+ /** Вторая кнопка действия */
22
+ additionalButton?: Omit<ButtonSimpleProps, 'size' | 'data-test-id'>;
23
+ /** Небольшой текст под кнопками футера с возможностью передать дополнительно ссылку */
24
+ disclaimer?: {
25
+ text: string;
26
+ link?: Pick<LinkProps, 'text' | 'href' | 'target'>;
27
+ };
28
+ };
29
+
30
+ export type ModalSProps = BaseModalProps & {
31
+ /** Размер */
32
+ size?: Size.S;
33
+ /**
34
+ * Выравнивание, для разных размеров доступны разные значения
35
+ * <br> для `Size.S` - все
36
+ */
37
+ align?: Align;
38
+ /** Можно передать иконку из пакета `@snack-uikit/icon-predefined`, или путь к картинке и атрибут `alt` */
39
+ picture?: ModalHeaderProps['picture'];
40
+ };
41
+
42
+ export type ModalMProps = BaseModalProps & {
43
+ size?: Size.M;
44
+ /** <br> для `Size.M` - `Align.Default | Align.Center` */
45
+ align?: Align.Default | Align.Center;
46
+ picture?: ModalHeaderImage;
47
+ };
48
+
49
+ export type ModalLProps = BaseModalProps & {
50
+ size?: Size.L;
51
+ /** <br> для `Size.L` - `Align.Default` */
52
+ align?: Align.Default;
53
+ picture?: ModalHeaderImage;
54
+ };
@@ -0,0 +1,96 @@
1
+ import cn from 'classnames';
2
+ import { ReactNode } from 'react';
3
+ import RCModal from 'react-modal';
4
+
5
+ import { WithSupportProps } from '@snack-uikit/utils';
6
+
7
+ import { Mode, Size } from '../../constants';
8
+ import {
9
+ ButtonClose,
10
+ ModalBody,
11
+ ModalBodyProps,
12
+ ModalFooter,
13
+ ModalFooterProps,
14
+ ModalHeader,
15
+ ModalHeaderProps,
16
+ OverlayElement,
17
+ } from '../../helperComponents';
18
+ import styles from './styles.module.scss';
19
+ import { getDataTestAttributes } from './utils';
20
+
21
+ export type ModalCustomProps = WithSupportProps<{
22
+ /** Управляет состоянием показан/не показан. */
23
+ open: boolean;
24
+ /** Колбек закрытия компонента. */
25
+ onClose(): void;
26
+ /**
27
+ * Режим отображения модального окна:
28
+ * <br> - __`Regular`__ - есть кнопка закрытия, клик на оверлей и нажатие кнопки `Esc` закрывают модалку
29
+ * <br> - __`Aggressive`__ - есть кнопка закрытия, но выключен клик на оверлей и не работает закрытие по клавише `Esc`
30
+ * <br> - __`Forced`__ - закрыть модальное окно можно только по нажатию на кнопку действия в нижней части
31
+ * @default Mode.Regular
32
+ */
33
+ mode?: Mode;
34
+ /**
35
+ * Размер модального окна
36
+ * @default Size.S
37
+ */
38
+ size?: Size;
39
+ /** CSS-класс */
40
+ className?: string;
41
+ /** Контент */
42
+ children: ReactNode;
43
+ }>;
44
+
45
+ export function ModalCustom({
46
+ open,
47
+ onClose,
48
+ size = Size.S,
49
+ mode = Mode.Regular,
50
+ children,
51
+ className,
52
+ ...rest
53
+ }: ModalCustomProps) {
54
+ const handleCloseButtonClick = () => {
55
+ onClose();
56
+ };
57
+
58
+ const handleClose = () => {
59
+ if (mode === Mode.Regular) {
60
+ onClose();
61
+ }
62
+ };
63
+
64
+ return (
65
+ <RCModal
66
+ data={{ ...getDataTestAttributes(rest), size }}
67
+ isOpen={open}
68
+ onRequestClose={handleClose}
69
+ appElement={document.body}
70
+ overlayElement={(_, content) => (
71
+ <OverlayElement blur={[Mode.Forced, Mode.Aggressive].includes(mode)} content={content} onClose={handleClose} />
72
+ )}
73
+ className={cn(styles.modal, className)}
74
+ >
75
+ {mode !== Mode.Forced && (
76
+ <div className={styles.headerElements}>
77
+ <ButtonClose onClick={handleCloseButtonClick} />
78
+ </div>
79
+ )}
80
+
81
+ {children}
82
+ </RCModal>
83
+ );
84
+ }
85
+
86
+ ModalCustom.modes = Mode;
87
+ ModalCustom.sizes = Size;
88
+
89
+ export namespace ModalCustom {
90
+ export type HeaderProps = ModalHeaderProps;
91
+ export type BodyProps = ModalBodyProps;
92
+ export type FooterProps = ModalFooterProps;
93
+ export const Header = ModalHeader;
94
+ export const Body = ModalBody;
95
+ export const Footer = ModalFooter;
96
+ }
@@ -0,0 +1 @@
1
+ export * from './ModalCustom';
@@ -0,0 +1,38 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-modal';
2
+
3
+ $sizes: 's', 'm', 'l';
4
+
5
+ .modal {
6
+ position: absolute;
7
+ top: 50%;
8
+ right: auto;
9
+ bottom: auto;
10
+ left: 50%;
11
+ transform: translate(-50%, -50%);
12
+
13
+ display: flex;
14
+ flex-direction: column;
15
+
16
+ box-sizing: border-box;
17
+ max-width: calc(100% - ($space-modal-outside-gap * 2));
18
+ max-height: calc(100vh - ($space-modal-outside-gap * 2));
19
+
20
+ background-color: simple-var($sys-neutral-background1-level);
21
+ outline: none;
22
+
23
+ @each $size in $sizes {
24
+ &[data-size="#{$size}"] {
25
+ @include composite-var($modal, 'window', $size);
26
+ }
27
+ }
28
+ }
29
+
30
+ .headerElements {
31
+ @include composite-var($modal, 'header-elements', 'container');
32
+ @include composite-var($modal, 'header-elements', 'elements-layout');
33
+
34
+ position: absolute;
35
+ top: 0;
36
+ right: 0;
37
+ display: flex;
38
+ }
@@ -0,0 +1,12 @@
1
+ import { extractDataTestProps } from '@snack-uikit/utils';
2
+
3
+ export const getDataTestAttributes = (rest: Record<string, unknown>) => {
4
+ const dataTestProps = extractDataTestProps(rest);
5
+
6
+ return Object.keys(dataTestProps).reduce<Record<string, unknown>>((acc, key) => {
7
+ const newKey = key.replace('data-', '');
8
+ acc[newKey] = dataTestProps[key];
9
+
10
+ return acc;
11
+ }, {});
12
+ };
@@ -0,0 +1,2 @@
1
+ export * from './ModalCustom';
2
+ export * from './Modal';
@@ -0,0 +1,41 @@
1
+ export enum Size {
2
+ S = 's',
3
+ M = 'm',
4
+ L = 'l',
5
+ }
6
+
7
+ export enum Mode {
8
+ Regular = 'regular',
9
+ Aggressive = 'aggressive',
10
+ Forced = 'forced',
11
+ }
12
+
13
+ export enum Align {
14
+ Default = 'default',
15
+ Center = 'center',
16
+ Vertical = 'vertical',
17
+ }
18
+
19
+ export enum ContentAlign {
20
+ Default = 'default',
21
+ Center = 'center',
22
+ }
23
+
24
+ export const TEST_IDS = {
25
+ overlay: 'modal__overlay',
26
+ closeButton: 'modal__close-button',
27
+ header: 'modal__header',
28
+ title: 'modal__title',
29
+ subtitle: 'modal__subtitle',
30
+ tooltip: 'modal__title-tooltip',
31
+ icon: 'modal__icon',
32
+ image: 'modal__image',
33
+ content: 'modal__body',
34
+ footer: 'modal__footer',
35
+ approveButton: 'modal__approve-button',
36
+ cancelButton: 'modal__cancel-button',
37
+ additionalButton: 'modal__additional-button',
38
+ disclaimer: 'modal__disclaimer',
39
+ disclaimerText: 'modal__disclaimer-text',
40
+ disclaimerLink: 'modal__disclaimer-link',
41
+ };
@@ -0,0 +1,32 @@
1
+ import cn from 'classnames';
2
+ import { ReactNode } from 'react';
3
+
4
+ import { Scroll } from '@snack-uikit/scroll';
5
+ import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
6
+
7
+ import { ContentAlign, TEST_IDS } from '../../constants';
8
+ import styles from './styles.module.scss';
9
+
10
+ export type ModalBodyProps = WithSupportProps<{
11
+ /** Содержимое модального окна */
12
+ content: ReactNode;
13
+ /** Выравнивание контента */
14
+ align?: ContentAlign;
15
+ className?: string;
16
+ }>;
17
+
18
+ export function ModalBody({ content, align = ContentAlign.Default, className, ...rest }: ModalBodyProps) {
19
+ return (
20
+ <Scroll
21
+ size={Scroll.sizes.M}
22
+ className={cn(styles.modalBody, className)}
23
+ {...extractSupportProps(rest)}
24
+ data-align={align}
25
+ data-test-id={TEST_IDS.content}
26
+ >
27
+ {content}
28
+ </Scroll>
29
+ );
30
+ }
31
+
32
+ ModalBody.aligns = ContentAlign;
@@ -0,0 +1 @@
1
+ export * from './Body';