@cloud-ru/uikit-product-mobile-drawer 0.9.15

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 (32) hide show
  1. package/CHANGELOG.md +927 -0
  2. package/LICENSE +201 -0
  3. package/README.md +8 -0
  4. package/package.json +54 -0
  5. package/src/components/MobileDrawer/MobileDrawer.tsx +118 -0
  6. package/src/components/MobileDrawer/index.ts +1 -0
  7. package/src/components/MobileDrawer/styles.module.scss +5 -0
  8. package/src/components/MobileDrawerCustom/MobileDrawerCustom.tsx +186 -0
  9. package/src/components/MobileDrawerCustom/constants.ts +35 -0
  10. package/src/components/MobileDrawerCustom/hooks.ts +196 -0
  11. package/src/components/MobileDrawerCustom/index.ts +1 -0
  12. package/src/components/MobileDrawerCustom/motion.css +117 -0
  13. package/src/components/MobileDrawerCustom/styles.module.scss +219 -0
  14. package/src/components/index.ts +2 -0
  15. package/src/constants.ts +42 -0
  16. package/src/helperComponents/Body/Body.tsx +28 -0
  17. package/src/helperComponents/Body/index.ts +1 -0
  18. package/src/helperComponents/Body/styles.module.scss +11 -0
  19. package/src/helperComponents/ButtonClose/ButtonClose.tsx +22 -0
  20. package/src/helperComponents/ButtonClose/index.ts +1 -0
  21. package/src/helperComponents/ButtonClose/styles.module.scss +44 -0
  22. package/src/helperComponents/Footer/Footer.tsx +25 -0
  23. package/src/helperComponents/Footer/index.ts +1 -0
  24. package/src/helperComponents/Footer/styles.module.scss +18 -0
  25. package/src/helperComponents/Header/Header.tsx +50 -0
  26. package/src/helperComponents/Header/index.ts +1 -0
  27. package/src/helperComponents/Header/styles.module.scss +37 -0
  28. package/src/helperComponents/WithTooltip/WithTooltip.tsx +16 -0
  29. package/src/helperComponents/WithTooltip/index.ts +1 -0
  30. package/src/helperComponents/index.ts +4 -0
  31. package/src/index.ts +1 -0
  32. package/src/types.ts +11 -0
@@ -0,0 +1,196 @@
1
+ import mergeRefs from 'merge-refs';
2
+ import { CSSProperties, useRef, useState } from 'react';
3
+
4
+ import { DATA_SWIPE_DIRECTIONS_ATTRIBUTE, SwipeCallback, SwipeEventData, useSwipeable } from '@snack-uikit/utils';
5
+
6
+ import { Position } from '../../types';
7
+ import { POSITION_TO_SWIPE_DIRECTION_MAP, SWIPE_DIRECTION_TO_POSITION_MAP } from './constants';
8
+
9
+ type UseSwipePropsProps = {
10
+ onSwiped(): void;
11
+ position: Position;
12
+ enabled: boolean;
13
+ };
14
+
15
+ const TRANSFORM = 0;
16
+ const SWIPE_DURATION = 500;
17
+
18
+ export function useSwipeProps({ onSwiped, position, enabled }: UseSwipePropsProps) {
19
+ const swipeRef = useRef<HTMLDivElement>(null);
20
+ const canCloseDrawer = useRef(true);
21
+ const itemSize =
22
+ (position === 'bottom' || position === 'top' ? swipeRef.current?.offsetHeight : swipeRef.current?.offsetWidth) ?? 0;
23
+ const swipeStart = useRef(0);
24
+
25
+ const getInitialDrag = () => ({
26
+ initial: TRANSFORM,
27
+ start: 0,
28
+ isDown: false,
29
+ drag: 0,
30
+ finished: true,
31
+ pointers: true,
32
+ });
33
+
34
+ const [drag, setDrag] = useState(getInitialDrag);
35
+
36
+ const [styles, setStyles] = useState<{
37
+ drawer: CSSProperties | undefined;
38
+ mask: CSSProperties | undefined;
39
+ }>();
40
+
41
+ const getDrawerTransform = (value: number) => {
42
+ switch (position) {
43
+ case 'bottom':
44
+ case 'top':
45
+ return `translateY(${TRANSFORM - value}px)`;
46
+ case 'right':
47
+ case 'left':
48
+ default:
49
+ return `translateX(${TRANSFORM - value}px)`;
50
+ }
51
+ };
52
+
53
+ const getMaskOpacity = (value: number) => 1 + value / itemSize;
54
+
55
+ const resetStyles = () => {
56
+ setStyles(undefined);
57
+ setDrag(getInitialDrag());
58
+ };
59
+
60
+ const isContainedInArea = ({
61
+ element,
62
+ condition,
63
+ }: {
64
+ element: HTMLElement | undefined;
65
+ condition(element: HTMLElement): boolean;
66
+ }): boolean => {
67
+ if (!element) {
68
+ return false;
69
+ }
70
+
71
+ if (element === swipeRef.current) {
72
+ return false;
73
+ }
74
+
75
+ if (!element.parentElement) {
76
+ return false;
77
+ }
78
+
79
+ return condition(element) || isContainedInArea({ element: element.parentElement, condition });
80
+ };
81
+
82
+ const isSwipeEnabled = (eventData: SwipeEventData): boolean => {
83
+ const element = eventData.event.target as HTMLElement;
84
+ const equalDirectionsCondition = (el: HTMLElement) => {
85
+ const directions = el.getAttribute(DATA_SWIPE_DIRECTIONS_ATTRIBUTE)?.split(' ') ?? [];
86
+ return directions.some(direction => direction === eventData.dir);
87
+ };
88
+
89
+ if (isContainedInArea({ element, condition: equalDirectionsCondition })) {
90
+ return false;
91
+ }
92
+
93
+ return position === SWIPE_DIRECTION_TO_POSITION_MAP[eventData.dir];
94
+ };
95
+
96
+ const handleSwipeStart: SwipeCallback = () => {
97
+ swipeStart.current = Date.now();
98
+ };
99
+
100
+ const handleSwiping: SwipeCallback = eventData => {
101
+ if (!isSwipeEnabled(eventData)) {
102
+ return;
103
+ }
104
+
105
+ if (!canCloseDrawer.current) {
106
+ return;
107
+ }
108
+
109
+ let adjustedDrag = 0;
110
+ const element = eventData.event.target as HTMLElement;
111
+
112
+ switch (position) {
113
+ case 'left':
114
+ if (isContainedInArea({ element, condition: el => el.scrollWidth - el.offsetWidth > el.scrollLeft })) {
115
+ canCloseDrawer.current = false;
116
+ return;
117
+ }
118
+ adjustedDrag = Math.max(0, -eventData.deltaX);
119
+ break;
120
+ case 'right':
121
+ if (isContainedInArea({ element, condition: el => el.scrollLeft > 0 })) {
122
+ canCloseDrawer.current = false;
123
+ return;
124
+ }
125
+ adjustedDrag = Math.min(0, -eventData.deltaX);
126
+ break;
127
+ case 'top':
128
+ if (isContainedInArea({ element, condition: el => el.scrollHeight - el.offsetHeight > el.scrollTop })) {
129
+ canCloseDrawer.current = false;
130
+ return;
131
+ }
132
+ adjustedDrag = Math.max(0, -eventData.deltaY);
133
+ break;
134
+ case 'bottom':
135
+ if (isContainedInArea({ element, condition: el => el.scrollTop > 0 })) {
136
+ canCloseDrawer.current = false;
137
+ return;
138
+ }
139
+ adjustedDrag = Math.min(0, -eventData.deltaY);
140
+ break;
141
+ default:
142
+ break;
143
+ }
144
+
145
+ setDrag(prevDrag => ({
146
+ ...prevDrag,
147
+ drag: adjustedDrag,
148
+ pointers: eventData.absX < Number.MIN_SAFE_INTEGER,
149
+ }));
150
+
151
+ setStyles({
152
+ drawer: { transform: getDrawerTransform(adjustedDrag), transition: 'none' },
153
+ mask: { opacity: getMaskOpacity(adjustedDrag), transition: 'none' },
154
+ });
155
+ };
156
+
157
+ const handleSwiped: SwipeCallback = eventData => {
158
+ if (!isSwipeEnabled(eventData)) {
159
+ return resetStyles();
160
+ }
161
+
162
+ if (Date.now() - swipeStart.current > SWIPE_DURATION) {
163
+ return resetStyles();
164
+ }
165
+
166
+ if (!canCloseDrawer.current) {
167
+ canCloseDrawer.current = true;
168
+ return resetStyles();
169
+ }
170
+
171
+ onSwiped();
172
+ setStyles({ drawer: { transform: getDrawerTransform(drag.drag) }, mask: { opacity: getMaskOpacity(drag.drag) } });
173
+ setDrag(prevDrag => ({ ...prevDrag, drag: 0, isDown: false, finished: true, pointers: true }));
174
+ };
175
+
176
+ const { ref, ...swipeProps } = useSwipeable({
177
+ onSwipeStart: handleSwipeStart,
178
+ onSwiping: handleSwiping,
179
+ onSwiped: handleSwiped,
180
+ enabled,
181
+ availableDirections: [POSITION_TO_SWIPE_DIRECTION_MAP[position]],
182
+ trackMouse: true,
183
+ });
184
+
185
+ return {
186
+ swipeRef: mergeRefs(ref as (value: HTMLDivElement) => void, swipeRef),
187
+ drawerStyles: styles?.drawer,
188
+ maskStyles: styles?.mask,
189
+ showPointer: !drag.pointers,
190
+ swipeProps,
191
+ drawerMotionProps: {
192
+ onLeaveActive: () => setStyles(undefined),
193
+ onLeaveEnd: () => setStyles(undefined),
194
+ },
195
+ };
196
+ }
@@ -0,0 +1 @@
1
+ export * from './MobileDrawerCustom';
@@ -0,0 +1,117 @@
1
+ .maskMotionMobile {
2
+ opacity: unset;
3
+ }
4
+ .maskMotionMobile-enter, .maskMotionMobile-appear {
5
+ opacity: 0;
6
+ }
7
+ .maskMotionMobile-enter-active, .maskMotionMobile-appear-active {
8
+ opacity: 1;
9
+ transition: opacity 0.2s;
10
+ }
11
+ .maskMotionMobile-leave {
12
+ opacity: 1;
13
+ }
14
+ .maskMotionMobile-leave-active {
15
+ opacity: 0;
16
+ transition: opacity 0.1s;
17
+ }
18
+
19
+ .panelMotionMobile {
20
+ transform: unset;
21
+ }
22
+
23
+ /** LEFT */
24
+ .panelMotionMobile-left-enter-start, .panelMotionMobile-left-appear-start, .panelMotionMobile-left-leave-start {
25
+ transition: none !important;
26
+ }
27
+ .panelMotionMobile-left-enter-active, .panelMotionMobile-left-appear-active {
28
+ transition: transform 0.2s ease-in-out;
29
+ }
30
+ .panelMotionMobile-left-leave-active {
31
+ transition: transform 0.1s ease-out;
32
+ }
33
+ .panelMotionMobile-left-enter, .panelMotionMobile-left-appear {
34
+ transform: translateX(-100%);
35
+ }
36
+ .panelMotionMobile-left-enter-active, .panelMotionMobile-left-appear-active {
37
+ transform: translateX(0);
38
+ }
39
+ .panelMotionMobile-left-leave {
40
+ transform: translateX(0);
41
+ }
42
+ .panelMotionMobile-left-leave-active {
43
+ transform: translateX(-100%) !important;
44
+ }
45
+ /*********/
46
+
47
+ /** RIGHT */
48
+ .panelMotionMobile-right-enter-start, .panelMotionMobile-right-appear-start, .panelMotionMobile-right-leave-start {
49
+ transition: none !important;
50
+ }
51
+ .panelMotionMobile-right-enter-active, .panelMotionMobile-right-appear-active {
52
+ transition: transform 0.2s ease-in-out;
53
+ }
54
+ .panelMotionMobile-right-leave-active {
55
+ transition: transform 0.1s ease-out;
56
+ }
57
+ .panelMotionMobile-right-enter, .panelMotionMobile-right-appear {
58
+ transform: translateX(100%);
59
+ }
60
+ .panelMotionMobile-right-enter-active, .panelMotionMobile-right-appear-active {
61
+ transform: translateX(0);
62
+ }
63
+ .panelMotionMobile-right-leave {
64
+ transform: translateX(0);
65
+ }
66
+ .panelMotionMobile-right-leave-active {
67
+ transform: translateX(100%) !important;
68
+ }
69
+ /**********/
70
+
71
+ /** BOTTOM */
72
+ .panelMotionMobile-bottom-enter-start, .panelMotionMobile-bottom-appear-start, .panelMotionMobile-bottom-leave-start {
73
+ transition: none !important;
74
+ }
75
+ .panelMotionMobile-bottom-enter-active, .panelMotionMobile-bottom-appear-active {
76
+ transition: transform 0.2s ease-in-out;
77
+ }
78
+ .panelMotionMobile-bottom-leave-active {
79
+ transition: transform 0.1s ease-out;
80
+ }
81
+ .panelMotionMobile-bottom-enter, .panelMotionMobile-bottom-appear {
82
+ transform: translateY(100%);
83
+ }
84
+ .panelMotionMobile-bottom-enter-active, .panelMotionMobile-bottom-appear-active {
85
+ transform: translateY(0);
86
+ }
87
+ .panelMotionMobile-bottom-leave {
88
+ transform: translateY(0);
89
+ }
90
+ .panelMotionMobile-bottom-leave-active {
91
+ transform: translateY(100%) !important;
92
+ }
93
+ /***********/
94
+
95
+ /** TOP */
96
+ .panelMotionMobile-top-enter-start, .panelMotionMobile-top-appear-start, .panelMotionMobile-top-leave-start {
97
+ transition: none !important;
98
+ }
99
+ .panelMotionMobile-top-enter-active, .panelMotionMobile-top-appear-active {
100
+ transition: transform 0.2s ease-in-out;
101
+ }
102
+ .panelMotionMobile-top-leave-active {
103
+ transition: transform 0.1s ease-out;
104
+ }
105
+ .panelMotionMobile-top-enter, .panelMotionMobile-top-appear {
106
+ transform: translateY(-100%);
107
+ }
108
+ .panelMotionMobile-top-enter-active, .panelMotionMobile-top-appear-active {
109
+ transform: translateY(0);
110
+ }
111
+ .panelMotionMobile-top-leave {
112
+ transform: translateY(0);
113
+ }
114
+ .panelMotionMobile-top-leave-active {
115
+ transform: translateY(-100%) !important;
116
+ }
117
+ /********/
@@ -0,0 +1,219 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/components/styles-tokens-drawer';
2
+
3
+ $modes: ('regular', 'soft');
4
+ $sizes: ('s', 'm', 'l');
5
+ $drawer-background-color: (
6
+ 'regular': styles-tokens-drawer.$sys-neutral-background1-level,
7
+ 'soft': styles-tokens-drawer.$sys-neutral-background2-level,
8
+ );
9
+
10
+ .content {
11
+ flex-grow: 1;
12
+ min-width: styles-tokens-drawer.$dimension-5m;
13
+ min-height: styles-tokens-drawer.$dimension-5m;
14
+ display: flex;
15
+ flex-direction: column;
16
+
17
+ &[data-swipe] {
18
+ cursor: grab;
19
+
20
+ &[data-pointers] {
21
+ cursor: grabbing;
22
+ user-select: none;
23
+
24
+ -webkit-user-drag: none;
25
+ }
26
+ }
27
+ }
28
+
29
+ .drawerRoot {
30
+ z-index: initial !important; /* stylelint-disable-line declaration-no-important */
31
+
32
+ &,
33
+ & * {
34
+ overscroll-behavior-y: contain;
35
+ }
36
+
37
+ div[data-content-wrapper='true'] {
38
+ will-change: transform;
39
+
40
+ z-index: initial;
41
+ overflow: hidden;
42
+ touch-action: none;
43
+
44
+ &[data-position='left'],
45
+ &[data-position='right'] {
46
+ height: 100%;
47
+ max-height: 100%;
48
+ max-width: calc(100% - #{styles-tokens-drawer.$space-drawer-outside-gap});
49
+ top: 0;
50
+ bottom: 0;
51
+
52
+ .content {
53
+ max-height: 100dvh;
54
+ }
55
+
56
+ @each $size in $sizes {
57
+ &[data-size='#{$size}'] {
58
+ @include styles-tokens-drawer.composite-var(styles-tokens-drawer.$drawer, 'window', $size);
59
+ }
60
+ }
61
+ }
62
+
63
+ &[data-position='left'] {
64
+ left: 0;
65
+
66
+ &[data-border-radius] {
67
+ border-bottom-right-radius: 12px;
68
+ border-top-right-radius: 12px;
69
+ }
70
+
71
+ &[data-outline] {
72
+ border-right: 1px solid styles-tokens-drawer.$sys-neutral-decor-default;
73
+ }
74
+ }
75
+
76
+ &[data-position='right'] {
77
+ right: 0;
78
+
79
+ &[data-border-radius] {
80
+ border-bottom-left-radius: 12px;
81
+ border-top-left-radius: 12px;
82
+ }
83
+
84
+ &[data-outline] {
85
+ border-left: 1px solid styles-tokens-drawer.$sys-neutral-decor-default;
86
+ }
87
+ }
88
+
89
+ &[data-position='bottom'],
90
+ &[data-position='top'] {
91
+ width: 100%;
92
+ max-width: 100%;
93
+ max-height: calc(100% - #{styles-tokens-drawer.$space-drawer-outside-gap});
94
+ left: 0;
95
+ right: 0;
96
+
97
+ .content {
98
+ max-height: calc(100dvh - #{styles-tokens-drawer.$space-drawer-outside-gap});
99
+ }
100
+
101
+ @each $size in $sizes {
102
+ &[data-size='#{$size}'] {
103
+ height: styles-tokens-drawer.simple-var(styles-tokens-drawer.$drawer, 'window', $size, 'width');
104
+ min-height: styles-tokens-drawer.simple-var(styles-tokens-drawer.$drawer, 'window', $size, 'min-width');
105
+ }
106
+ }
107
+ }
108
+
109
+ &[data-position='bottom'] {
110
+ bottom: 0;
111
+
112
+ &[data-border-radius] {
113
+ border-top-left-radius: 12px;
114
+ border-top-right-radius: 12px;
115
+ }
116
+
117
+ &[data-outline] {
118
+ border-top: 1px solid styles-tokens-drawer.$sys-neutral-decor-default;
119
+ }
120
+ }
121
+
122
+ &[data-position='top'] {
123
+ top: 0;
124
+
125
+ &[data-border-radius] {
126
+ border-bottom-left-radius: 12px;
127
+ border-bottom-right-radius: 12px;
128
+ }
129
+
130
+ &[data-outline] {
131
+ border-bottom: 1px solid styles-tokens-drawer.$sys-neutral-decor-default;
132
+ }
133
+ }
134
+
135
+ @each $mode in $modes {
136
+ &[data-mode='#{$mode}'] {
137
+ .drawer {
138
+ background-color: styles-tokens-drawer.simple-var($drawer-background-color, $mode);
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ .drawer {
146
+ overflow: unset;
147
+ display: flex;
148
+ flex-direction: column;
149
+
150
+ box-sizing: border-box;
151
+
152
+ box-shadow: styles-tokens-drawer.simple-var(styles-tokens-drawer.$box-shadow-elevation-level5);
153
+ }
154
+
155
+ .mask {
156
+ z-index: initial !important; /* stylelint-disable-line declaration-no-important */
157
+ background-color: styles-tokens-drawer.simple-var(styles-tokens-drawer.$sys-blackout);
158
+ }
159
+
160
+ .maskBlur {
161
+ backdrop-filter: blur(styles-tokens-drawer.simple-var(styles-tokens-drawer.$background-blur-background-blur));
162
+ }
163
+
164
+ .drawerBlur {
165
+ outline: none;
166
+ }
167
+
168
+ $swiper-length: styles-tokens-drawer.$dimension-10m;
169
+
170
+ .swiper {
171
+ position: absolute;
172
+
173
+ background-color: styles-tokens-drawer.$sys-neutral-decor-default;
174
+ border-radius: styles-tokens-drawer.$dimension-050m;
175
+
176
+ &[data-position='left'],
177
+ &[data-position='right'] {
178
+ width: styles-tokens-drawer.$dimension-050m;
179
+ height: $swiper-length;
180
+ }
181
+
182
+ &[data-position='left'] {
183
+ right: styles-tokens-drawer.$dimension-050m;
184
+ top: calc(50% - $swiper-length / 2);
185
+ }
186
+
187
+ &[data-position='right'] {
188
+ left: styles-tokens-drawer.$dimension-050m;
189
+ top: calc(50% - $swiper-length / 2);
190
+ }
191
+
192
+ &[data-position='bottom'],
193
+ &[data-position='top'] {
194
+ width: $swiper-length;
195
+ height: styles-tokens-drawer.$dimension-050m;
196
+ }
197
+
198
+ &[data-position='bottom'] {
199
+ top: styles-tokens-drawer.$dimension-050m;
200
+ left: calc(50% - $swiper-length / 2);
201
+ }
202
+
203
+ &[data-position='top'] {
204
+ bottom: styles-tokens-drawer.$dimension-050m;
205
+ left: calc(50% - $swiper-length / 2);
206
+ }
207
+ }
208
+
209
+ .headerElements {
210
+ @include styles-tokens-drawer.composite-var(styles-tokens-drawer.$drawer-header-elements-container);
211
+ @include styles-tokens-drawer.composite-var(styles-tokens-drawer.$drawer-header-elements-elements-layout);
212
+
213
+ position: absolute;
214
+ top: 0;
215
+ right: 0;
216
+
217
+ display: flex;
218
+ align-items: center;
219
+ }
@@ -0,0 +1,2 @@
1
+ export * from './MobileDrawer';
2
+ export * from './MobileDrawerCustom';
@@ -0,0 +1,42 @@
1
+ export const SIZE = {
2
+ S: 's',
3
+ M: 'm',
4
+ L: 'l',
5
+ } as const;
6
+
7
+ export const SIZE_AS_VALUES: string[] = Object.values(SIZE);
8
+
9
+ export const MODE = {
10
+ Regular: 'regular',
11
+ Soft: 'soft',
12
+ } as const;
13
+
14
+ export const MODAL_MODE = {
15
+ Regular: 'regular',
16
+ Aggressive: 'aggressive',
17
+ Forced: 'forced',
18
+ } as const;
19
+
20
+ export const POSITION = {
21
+ Right: 'right',
22
+ Left: 'left',
23
+ Bottom: 'bottom',
24
+ Top: 'top',
25
+ } as const;
26
+
27
+ export const NESTED_DRAWER_PUSH_DISTANCE = 24;
28
+
29
+ export const TEST_IDS = {
30
+ closeButton: 'drawer__close-button',
31
+ header: 'drawer__header',
32
+ title: 'drawer__title',
33
+ tooltip: 'drawer__title-tooltip',
34
+ subtitle: 'drawer__subtitle',
35
+ image: 'drawer__image',
36
+ content: 'drawer__body',
37
+ footer: 'drawer__footer',
38
+ footerActions: 'drawer__footer-actions',
39
+ approveButton: 'drawer__approve-button',
40
+ cancelButton: 'drawer__cancel-button',
41
+ additionalButton: 'drawer__additional-button',
42
+ };
@@ -0,0 +1,28 @@
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 styles from './styles.module.scss';
8
+
9
+ export type DrawerBodyProps = WithSupportProps<{
10
+ /** Контент */
11
+ content: ReactNode;
12
+ /** CSS-класс */
13
+ className?: string;
14
+ }>;
15
+
16
+ /** Вспомогательный компонент для добавления "тела" в DrawerCustom */
17
+ export function DrawerBody({ content, className, ...rest }: DrawerBodyProps) {
18
+ return (
19
+ <Scroll
20
+ size='m'
21
+ barHideStrategy='never'
22
+ className={cn(styles.drawerBody, className)}
23
+ {...extractSupportProps(rest)}
24
+ >
25
+ {content}
26
+ </Scroll>
27
+ );
28
+ }
@@ -0,0 +1 @@
1
+ export * from './Body';
@@ -0,0 +1,11 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/components/styles-tokens-drawer';
2
+
3
+ .drawerBody {
4
+ @include styles-tokens-drawer.composite-var(styles-tokens-drawer.$drawer-body);
5
+ padding: 0 styles-tokens-drawer.$dimension-2m;
6
+
7
+ flex: 1 1 auto;
8
+ box-sizing: border-box;
9
+ min-height: styles-tokens-drawer.$dimension-2m;
10
+ color: styles-tokens-drawer.$sys-neutral-text-main;
11
+ }
@@ -0,0 +1,22 @@
1
+ import { CrossSVG } from '@cloud-ru/uikit-product-icons';
2
+
3
+ import { TEST_IDS } from '../../constants';
4
+ import styles from './styles.module.scss';
5
+
6
+ type ButtonCloseProps = {
7
+ onClick(): void;
8
+ };
9
+
10
+ export function ButtonClose({ onClick }: ButtonCloseProps) {
11
+ return (
12
+ <button
13
+ type='button'
14
+ className={styles.buttonClose}
15
+ onClick={onClick}
16
+ aria-label='close drawer'
17
+ data-test-id={TEST_IDS.closeButton}
18
+ >
19
+ <CrossSVG />
20
+ </button>
21
+ );
22
+ }
@@ -0,0 +1 @@
1
+ export * from './ButtonClose';
@@ -0,0 +1,44 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/components/styles-tokens-drawer';
2
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/components/styles-tokens-element';
3
+
4
+ .buttonClose {
5
+ @include styles-tokens-element.composite-var(styles-tokens-drawer.$drawer-button-close);
6
+
7
+ cursor: pointer;
8
+
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+
13
+ box-sizing: border-box;
14
+ margin: 0;
15
+ padding: 0;
16
+
17
+ color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-text-support);
18
+
19
+ background-color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-decor-default);
20
+ border: 0 solid transparent;
21
+ outline: 0;
22
+ outline-offset: styles-tokens-element.$spacing-state-focus-offset;
23
+
24
+ &:hover, &:focus-visible {
25
+ color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-text-main);
26
+ background-color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-decor-hovered);
27
+ }
28
+
29
+ &:focus-visible {
30
+ @include styles-tokens-element.outline-var(styles-tokens-element.$container-focused-s);
31
+
32
+ outline-color: styles-tokens-element.$sys-available-complementary;
33
+ }
34
+
35
+ &:active {
36
+ color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-text-main);
37
+ background-color: styles-tokens-element.simple-var(styles-tokens-element.$sys-neutral-decor-activated);
38
+ }
39
+
40
+ svg {
41
+ width: styles-tokens-element.$icon-s !important; /* stylelint-disable-line declaration-no-important */
42
+ height: styles-tokens-element.$icon-s !important; /* stylelint-disable-line declaration-no-important */
43
+ }
44
+ }