@snack-uikit/card 0.4.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 (136) hide show
  1. package/CHANGELOG.md +322 -0
  2. package/LICENSE +201 -0
  3. package/README.md +46 -0
  4. package/dist/components/Card/Card.d.ts +36 -0
  5. package/dist/components/Card/Card.js +36 -0
  6. package/dist/components/Card/constants.d.ts +5 -0
  7. package/dist/components/Card/constants.js +7 -0
  8. package/dist/components/Card/index.d.ts +1 -0
  9. package/dist/components/Card/index.js +1 -0
  10. package/dist/components/Card/styles.module.css +142 -0
  11. package/dist/components/Footer/components/Action/Action.d.ts +11 -0
  12. package/dist/components/Footer/components/Action/Action.js +22 -0
  13. package/dist/components/Footer/components/Action/index.d.ts +1 -0
  14. package/dist/components/Footer/components/Action/index.js +1 -0
  15. package/dist/components/Footer/components/Action/styles.module.css +7 -0
  16. package/dist/components/Footer/components/CallToAction/CallToAction.d.ts +11 -0
  17. package/dist/components/Footer/components/CallToAction/CallToAction.js +20 -0
  18. package/dist/components/Footer/components/CallToAction/index.d.ts +1 -0
  19. package/dist/components/Footer/components/CallToAction/index.js +1 -0
  20. package/dist/components/Footer/components/CallToAction/styles.module.css +22 -0
  21. package/dist/components/Footer/components/Dimension/Dimension.d.ts +12 -0
  22. package/dist/components/Footer/components/Dimension/Dimension.js +10 -0
  23. package/dist/components/Footer/components/Dimension/constants.d.ts +4 -0
  24. package/dist/components/Footer/components/Dimension/constants.js +6 -0
  25. package/dist/components/Footer/components/Dimension/index.d.ts +1 -0
  26. package/dist/components/Footer/components/Dimension/index.js +1 -0
  27. package/dist/components/Footer/components/Dimension/styles.module.css +27 -0
  28. package/dist/components/Footer/components/Promo/Promo.d.ts +18 -0
  29. package/dist/components/Footer/components/Promo/Promo.js +25 -0
  30. package/dist/components/Footer/components/Promo/index.d.ts +1 -0
  31. package/dist/components/Footer/components/Promo/index.js +1 -0
  32. package/dist/components/Footer/components/Promo/styles.module.css +6 -0
  33. package/dist/components/Footer/components/index.d.ts +3 -0
  34. package/dist/components/Footer/components/index.js +3 -0
  35. package/dist/components/Footer/index.d.ts +11 -0
  36. package/dist/components/Footer/index.js +6 -0
  37. package/dist/components/FunctionBadge/FunctionBadge.d.ts +9 -0
  38. package/dist/components/FunctionBadge/FunctionBadge.js +23 -0
  39. package/dist/components/FunctionBadge/constants.d.ts +2 -0
  40. package/dist/components/FunctionBadge/constants.js +5 -0
  41. package/dist/components/FunctionBadge/index.d.ts +1 -0
  42. package/dist/components/FunctionBadge/index.js +1 -0
  43. package/dist/components/FunctionBadge/styles.module.css +39 -0
  44. package/dist/components/Header/Header.d.ts +21 -0
  45. package/dist/components/Header/Header.js +28 -0
  46. package/dist/components/Header/constants.d.ts +8 -0
  47. package/dist/components/Header/constants.js +10 -0
  48. package/dist/components/Header/index.d.ts +1 -0
  49. package/dist/components/Header/index.js +1 -0
  50. package/dist/components/Header/styles.module.css +34 -0
  51. package/dist/components/Image/Image.d.ts +17 -0
  52. package/dist/components/Image/Image.js +7 -0
  53. package/dist/components/Image/constants.d.ts +5 -0
  54. package/dist/components/Image/constants.js +6 -0
  55. package/dist/components/Image/index.d.ts +1 -0
  56. package/dist/components/Image/index.js +1 -0
  57. package/dist/components/Image/styles.module.css +27 -0
  58. package/dist/components/index.d.ts +20 -0
  59. package/dist/components/index.js +10 -0
  60. package/dist/constants.d.ts +16 -0
  61. package/dist/constants.js +17 -0
  62. package/dist/context.d.ts +14 -0
  63. package/dist/context.js +13 -0
  64. package/dist/helperComponents/Check/Check.d.ts +5 -0
  65. package/dist/helperComponents/Check/Check.js +8 -0
  66. package/dist/helperComponents/Check/index.d.ts +1 -0
  67. package/dist/helperComponents/Check/index.js +1 -0
  68. package/dist/helperComponents/Check/styles.module.css +16 -0
  69. package/dist/helperComponents/Emblem/Emblem.d.ts +11 -0
  70. package/dist/helperComponents/Emblem/Emblem.js +17 -0
  71. package/dist/helperComponents/Emblem/index.d.ts +1 -0
  72. package/dist/helperComponents/Emblem/index.js +1 -0
  73. package/dist/helperComponents/Emblem/styled.module.css +21 -0
  74. package/dist/helperComponents/FunctionBadgeWrapper/FunctionBadgeWrapper.d.ts +6 -0
  75. package/dist/helperComponents/FunctionBadgeWrapper/FunctionBadgeWrapper.js +8 -0
  76. package/dist/helperComponents/FunctionBadgeWrapper/index.d.ts +1 -0
  77. package/dist/helperComponents/FunctionBadgeWrapper/index.js +1 -0
  78. package/dist/helperComponents/FunctionBadgeWrapper/styles.module.css +26 -0
  79. package/dist/helperComponents/PromoBadge/PromoBadge.d.ts +4 -0
  80. package/dist/helperComponents/PromoBadge/PromoBadge.js +7 -0
  81. package/dist/helperComponents/PromoBadge/index.d.ts +1 -0
  82. package/dist/helperComponents/PromoBadge/index.js +1 -0
  83. package/dist/helperComponents/PromoBadge/styles.module.css +8 -0
  84. package/dist/helperComponents/index.d.ts +4 -0
  85. package/dist/helperComponents/index.js +4 -0
  86. package/dist/index.d.ts +1 -0
  87. package/dist/index.js +1 -0
  88. package/package.json +46 -0
  89. package/src/components/Card/Card.tsx +121 -0
  90. package/src/components/Card/constants.ts +10 -0
  91. package/src/components/Card/index.ts +1 -0
  92. package/src/components/Card/styles.module.scss +181 -0
  93. package/src/components/Footer/components/Action/Action.tsx +39 -0
  94. package/src/components/Footer/components/Action/index.ts +1 -0
  95. package/src/components/Footer/components/Action/styles.module.scss +10 -0
  96. package/src/components/Footer/components/CallToAction/CallToAction.tsx +25 -0
  97. package/src/components/Footer/components/CallToAction/index.ts +1 -0
  98. package/src/components/Footer/components/CallToAction/styles.module.scss +27 -0
  99. package/src/components/Footer/components/Dimension/Dimension.tsx +50 -0
  100. package/src/components/Footer/components/Dimension/constants.ts +8 -0
  101. package/src/components/Footer/components/Dimension/index.ts +1 -0
  102. package/src/components/Footer/components/Dimension/styles.module.scss +31 -0
  103. package/src/components/Footer/components/Promo/Promo.tsx +40 -0
  104. package/src/components/Footer/components/Promo/index.ts +1 -0
  105. package/src/components/Footer/components/Promo/styles.module.scss +8 -0
  106. package/src/components/Footer/components/index.ts +3 -0
  107. package/src/components/Footer/index.ts +20 -0
  108. package/src/components/FunctionBadge/FunctionBadge.tsx +79 -0
  109. package/src/components/FunctionBadge/constants.ts +6 -0
  110. package/src/components/FunctionBadge/index.ts +1 -0
  111. package/src/components/FunctionBadge/styles.module.scss +48 -0
  112. package/src/components/Header/Header.tsx +79 -0
  113. package/src/components/Header/constants.ts +13 -0
  114. package/src/components/Header/index.ts +1 -0
  115. package/src/components/Header/styles.module.scss +39 -0
  116. package/src/components/Image/Image.tsx +18 -0
  117. package/src/components/Image/constants.ts +5 -0
  118. package/src/components/Image/index.ts +1 -0
  119. package/src/components/Image/styles.module.scss +38 -0
  120. package/src/components/index.ts +28 -0
  121. package/src/constants.ts +17 -0
  122. package/src/context.ts +27 -0
  123. package/src/helperComponents/Check/Check.tsx +20 -0
  124. package/src/helperComponents/Check/index.ts +1 -0
  125. package/src/helperComponents/Check/styles.module.scss +17 -0
  126. package/src/helperComponents/Emblem/Emblem.tsx +44 -0
  127. package/src/helperComponents/Emblem/index.ts +1 -0
  128. package/src/helperComponents/Emblem/styled.module.scss +20 -0
  129. package/src/helperComponents/FunctionBadgeWrapper/FunctionBadgeWrapper.tsx +23 -0
  130. package/src/helperComponents/FunctionBadgeWrapper/index.ts +1 -0
  131. package/src/helperComponents/FunctionBadgeWrapper/styles.module.scss +30 -0
  132. package/src/helperComponents/PromoBadge/PromoBadge.tsx +16 -0
  133. package/src/helperComponents/PromoBadge/index.ts +1 -0
  134. package/src/helperComponents/PromoBadge/styles.module.scss +10 -0
  135. package/src/helperComponents/index.ts +4 -0
  136. package/src/index.ts +1 -0
@@ -0,0 +1,181 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-card';
2
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
3
+
4
+ $sizes: 'm', 'l';
5
+
6
+ $snack-ui-card-background-color: var(--snack-ui-card-background-color);
7
+
8
+ .functionBadgeWrapper {
9
+ position: absolute;
10
+ top: 0;
11
+ right: 0;
12
+ display: none;
13
+
14
+ &[data-visible] {
15
+ display: block;
16
+ }
17
+ }
18
+
19
+ .composition {
20
+ @include composite-var($card-composition-container);
21
+
22
+ box-sizing: border-box;
23
+ }
24
+
25
+ .card {
26
+ --snack-ui-card-background-color: #{$sys-neutral-background1-level};
27
+
28
+ @include composite-var($card-container);
29
+
30
+ position: relative;
31
+
32
+ overflow: hidden;
33
+
34
+ box-sizing: border-box;
35
+ margin: 0;
36
+ padding: 0;
37
+
38
+ text-align: left;
39
+
40
+ background-color: $snack-ui-card-background-color;
41
+ border: 0;
42
+ outline-color: transparent;
43
+
44
+ &:hover {
45
+ .functionBadgeWrapper {
46
+ display: block;
47
+ }
48
+ }
49
+
50
+ &:hover,
51
+ &:focus-visible {
52
+ --snack-ui-card-background-color: #{$sys-neutral-background2-level};
53
+ }
54
+
55
+
56
+ &:focus-visible {
57
+ @include outline-var($container-focused-l);
58
+
59
+ outline-color: $sys-primary-accent-default;
60
+
61
+ .functionBadgeWrapper {
62
+ display: block;
63
+ }
64
+
65
+ .check {
66
+ background-color: $sys-primary-accent-default;
67
+ }
68
+ }
69
+
70
+ &:focus-within {
71
+ .functionBadgeWrapper {
72
+ display: block;
73
+ }
74
+ }
75
+
76
+ &[data-outline] {
77
+ @include outline-var($container-focused-s);
78
+
79
+ outline-color: $sys-neutral-decor-default;
80
+ outline-width: $border-width-card-container;
81
+ }
82
+
83
+ &[data-pointer] {
84
+ * {
85
+ cursor: pointer;
86
+ }
87
+
88
+ &[data-outline] {
89
+ &:hover {
90
+ outline-color: $sys-neutral-decor-hovered;
91
+ box-shadow: none;
92
+ }
93
+
94
+ &:focus-visible {
95
+ @include outline-var($container-focused-l);
96
+
97
+ outline-color: $sys-primary-accent-default;
98
+ }
99
+ }
100
+
101
+ &[data-checked] {
102
+ @include outline-var($container-focused-s);
103
+
104
+ outline-color: $sys-primary-decor-activated;
105
+ outline-width: $border-width-card-container;
106
+
107
+ &:hover {
108
+ outline-color: $sys-primary-decor-hovered;
109
+ box-shadow: none;
110
+ }
111
+
112
+ .contentWrapper {
113
+ background-color: color-on-background-with-opacity($sys-primary-accent-default, $snack-ui-card-background-color, $opacity-a008);
114
+ }
115
+
116
+ .check {
117
+ color: $sys-primary-on-accent;
118
+ background-color: $sys-primary-accent-default;
119
+ }
120
+ }
121
+
122
+ &[data-checked]:focus-visible {
123
+ @include outline-var($container-focused-l);
124
+
125
+ outline-color: $sys-primary-accent-default;
126
+
127
+ .check {
128
+ color: $sys-primary-on-accent;
129
+ background-color: $sys-primary-accent-default;
130
+ }
131
+ }
132
+
133
+ &:hover {
134
+ box-shadow: simple-var($theme-variables, 'box-shadow', 'elevation', 'level2');
135
+ }
136
+ }
137
+
138
+ &[data-disabled] {
139
+ @include outline-var($container-focused-s);
140
+
141
+ cursor: not-allowed;
142
+ background-color: $sys-neutral-background;
143
+ outline-color: $sys-neutral-decor-default;
144
+ outline-width: $border-width-card-container;
145
+
146
+ * {
147
+ cursor: not-allowed;
148
+ }
149
+ }
150
+ }
151
+
152
+
153
+ .body {
154
+ display: block;
155
+ color: $sys-neutral-text-support;
156
+ text-align: initial;
157
+ }
158
+
159
+ .content {
160
+ display: flex;
161
+ flex-direction: column;
162
+ align-items: flex-start;
163
+ justify-content: flex-start;
164
+
165
+ box-sizing: border-box;
166
+ max-width: 100%;
167
+
168
+ @each $size in $sizes {
169
+ &[data-size='#{$size}'] {
170
+ @include composite-var($card, 'content', $size, 'container');
171
+ }
172
+ }
173
+ }
174
+
175
+ .footer {
176
+ @include composite-var($card-footer);
177
+
178
+ position: relative;
179
+ box-sizing: border-box;
180
+ }
181
+
@@ -0,0 +1,39 @@
1
+ import cn from 'classnames';
2
+
3
+ import { ButtonFilled, ButtonFilledProps, ButtonTonal, ButtonTonalProps } from '@snack-uikit/button';
4
+ import { excludeSupportProps, WithSupportProps } from '@snack-uikit/utils';
5
+
6
+ import { useCardContext } from '../../../../context';
7
+ import styles from './styles.module.scss';
8
+
9
+ export type FooterActionProps = WithSupportProps<{
10
+ /** Параметры для основной кнопки */
11
+ button: Pick<ButtonFilledProps, 'label' | 'onClick' | 'loading' | 'icon'>;
12
+ /** Параметры для вторичной кнопки */
13
+ secondaryButton?: Pick<ButtonTonalProps, 'label' | 'onClick' | 'loading' | 'icon'>;
14
+ /** CSS-класс для элемента с контентом */
15
+ className?: string;
16
+ }>;
17
+
18
+ export function FooterAction({ button, secondaryButton, className, ...rest }: FooterActionProps) {
19
+ const { disabled } = useCardContext();
20
+
21
+ return (
22
+ <div className={cn(styles.action, className)} {...excludeSupportProps(rest)}>
23
+ <ButtonFilled
24
+ {...button}
25
+ appearance={ButtonFilled.appearances.Primary}
26
+ size={ButtonFilled.sizes.M}
27
+ disabled={disabled}
28
+ />
29
+ {secondaryButton && (
30
+ <ButtonTonal
31
+ {...secondaryButton}
32
+ appearance={ButtonTonal.appearances.Neutral}
33
+ size={ButtonTonal.sizes.M}
34
+ disabled={disabled}
35
+ />
36
+ )}
37
+ </div>
38
+ );
39
+ }
@@ -0,0 +1 @@
1
+ export * from './Action';
@@ -0,0 +1,10 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-card';
2
+
3
+ .action {
4
+ @include composite-var($card-action);
5
+
6
+ display: flex;
7
+ align-items: center;
8
+ justify-content: flex-start;
9
+ box-sizing: border-box;
10
+ }
@@ -0,0 +1,25 @@
1
+ import cn from 'classnames';
2
+ import { ReactNode } from 'react';
3
+
4
+ import { Typography } from '@snack-uikit/typography';
5
+ import { excludeSupportProps, WithSupportProps } from '@snack-uikit/utils';
6
+
7
+ import styles from './styles.module.scss';
8
+
9
+ export type FooterCallToActionProps = WithSupportProps<{
10
+ /** Лейбл */
11
+ label: string;
12
+ /** Иконка */
13
+ icon?: ReactNode;
14
+ /** CSS-класс для элемента с контентом */
15
+ className?: string;
16
+ }>;
17
+
18
+ export function FooterCallToAction({ label, icon, className, ...rest }: FooterCallToActionProps) {
19
+ return (
20
+ <div className={cn(styles.callToAction, className)} {...excludeSupportProps(rest)}>
21
+ <Typography.SansLabelL className={styles.label}>{label}</Typography.SansLabelL>
22
+ <span className={styles.icon}>{icon}</span>
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1 @@
1
+ export * from './CallToAction';
@@ -0,0 +1,27 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-card';
2
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
3
+
4
+ .callToAction {
5
+ @include composite-var($card-call-to-action);
6
+
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: flex-start;
10
+ box-sizing: border-box;
11
+ }
12
+
13
+ .label {
14
+ color: $sys-primary-accent-default;
15
+ }
16
+
17
+ .icon {
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ color: $sys-primary-accent-default;
22
+
23
+ svg {
24
+ width: $icon-s !important; /* stylelint-disable-line declaration-no-important */
25
+ height: $icon-s !important; /* stylelint-disable-line declaration-no-important */
26
+ }
27
+ }
@@ -0,0 +1,50 @@
1
+ import { Typography } from '@snack-uikit/typography';
2
+
3
+ import { Size } from '../../../../constants';
4
+ import { useCardContext } from '../../../../context';
5
+ import { TYPOGRAPHY_SIZE_MAP } from './constants';
6
+ import styles from './styles.module.scss';
7
+
8
+ export type DimensionProps = {
9
+ /** Единица измерения */
10
+ dimension?: string;
11
+ /** Текущее значение */
12
+ currentValue: string;
13
+ /** Старое значение */
14
+ oldValue?: string;
15
+ /** Размер */
16
+ size?: Size;
17
+ };
18
+
19
+ export function Dimension({ dimension, currentValue, oldValue, size: sizeProp }: DimensionProps) {
20
+ const { size: sizeContext } = useCardContext();
21
+
22
+ const size = TYPOGRAPHY_SIZE_MAP[sizeProp || sizeContext];
23
+
24
+ return (
25
+ <div className={styles.wrapper}>
26
+ {dimension && (
27
+ <Typography
28
+ role={Typography.roles.Title}
29
+ family={Typography.families.Sans}
30
+ size={size}
31
+ className={styles.dimension}
32
+ >
33
+ {dimension}
34
+ </Typography>
35
+ )}
36
+ <div className={styles.valueContainer}>
37
+ <Typography
38
+ role={Typography.roles.Title}
39
+ family={Typography.families.Sans}
40
+ size={size}
41
+ className={styles.currentValue}
42
+ >
43
+ {currentValue}
44
+ </Typography>
45
+
46
+ {oldValue && <Typography.CrossedOutBodyS className={styles.oldValue}>{oldValue}</Typography.CrossedOutBodyS>}
47
+ </div>
48
+ </div>
49
+ );
50
+ }
@@ -0,0 +1,8 @@
1
+ import { Typography } from '@snack-uikit/typography';
2
+
3
+ import { Size } from '../../../../constants';
4
+
5
+ export const TYPOGRAPHY_SIZE_MAP = {
6
+ [Size.M]: Typography.sizes.M,
7
+ [Size.L]: Typography.sizes.L,
8
+ };
@@ -0,0 +1 @@
1
+ export * from './Dimension';
@@ -0,0 +1,31 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-card';
2
+
3
+ .wrapper {
4
+ @include composite-var($card-dimension);
5
+
6
+ display: flex;
7
+ flex: 1 0 auto;
8
+ justify-content: flex-end;
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ .valueContainer {
13
+ display: flex;
14
+ flex-direction: column;
15
+ align-items: flex-end;
16
+ justify-content: flex-start;
17
+
18
+ box-sizing: border-box;
19
+ }
20
+
21
+ .currentValue {
22
+ color: $sys-neutral-text-main;
23
+ }
24
+
25
+ .oldValue {
26
+ color: $sys-neutral-text-light;
27
+ }
28
+
29
+ .dimension {
30
+ color: $sys-neutral-text-light;
31
+ }
@@ -0,0 +1,40 @@
1
+ import cn from 'classnames';
2
+
3
+ import { ButtonFilled, ButtonFilledProps } from '@snack-uikit/button';
4
+ import { excludeSupportProps, WithSupportProps } from '@snack-uikit/utils';
5
+
6
+ import { Size } from '../../../../constants';
7
+ import { useCardContext } from '../../../../context';
8
+ import { Dimension, DimensionProps } from '../Dimension';
9
+ import styles from './styles.module.scss';
10
+
11
+ export type FooterPromoProps = WithSupportProps<{
12
+ /** Параметры для блока значений */
13
+ volume?: DimensionProps;
14
+ /** Параметры для основной кнопки */
15
+ button?: Pick<ButtonFilledProps, 'label' | 'onClick' | 'loading' | 'icon'>;
16
+ /** CSS-класс для элемента с контентом */
17
+ className?: string;
18
+ /** Размер */
19
+ size?: Size;
20
+ }>;
21
+
22
+ export function FooterPromo({ volume, button, className, size, ...rest }: FooterPromoProps) {
23
+ const { disabled } = useCardContext();
24
+
25
+ return (
26
+ <div className={cn(styles.promo, className)} {...excludeSupportProps(rest)}>
27
+ {button && (
28
+ <ButtonFilled
29
+ {...button}
30
+ appearance={ButtonFilled.appearances.Primary}
31
+ size={ButtonFilled.sizes.M}
32
+ disabled={disabled}
33
+ />
34
+ )}
35
+ {volume && <Dimension {...volume} size={size} />}
36
+ </div>
37
+ );
38
+ }
39
+
40
+ FooterPromo.sizes = Size;
@@ -0,0 +1 @@
1
+ export * from './Promo';
@@ -0,0 +1,8 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-card';
2
+
3
+ .promo {
4
+ display: flex;
5
+ align-items: center;
6
+ justify-content: flex-end;
7
+ box-sizing: border-box;
8
+ }
@@ -0,0 +1,3 @@
1
+ export * from './Promo';
2
+ export * from './Action';
3
+ export * from './CallToAction';
@@ -0,0 +1,20 @@
1
+ import {
2
+ FooterAction,
3
+ FooterActionProps,
4
+ FooterCallToAction,
5
+ FooterCallToActionProps,
6
+ FooterPromo,
7
+ FooterPromoProps,
8
+ } from './components';
9
+
10
+ export const Footer = {
11
+ Promo: FooterPromo,
12
+ Action: FooterAction,
13
+ CallToAction: FooterCallToAction,
14
+ };
15
+
16
+ export namespace Footer {
17
+ export type PromoProps = FooterPromoProps;
18
+ export type ActionProps = FooterActionProps;
19
+ export type CallToActionProps = FooterCallToActionProps;
20
+ }
@@ -0,0 +1,79 @@
1
+ import { MouseEvent, ReactNode, useCallback, useContext, useLayoutEffect, useState } from 'react';
2
+
3
+ import { Droplist, ItemSingleProps } from '@snack-uikit/droplist';
4
+ import { KebabSVG } from '@snack-uikit/icons';
5
+
6
+ import { TEST_IDS } from '../../constants';
7
+ import { FunctionBadgeContext } from '../../context';
8
+ import styles from './styles.module.scss';
9
+
10
+ export type FunctionBadgeProps = {
11
+ /** Иконка */
12
+ icon?: ReactNode;
13
+ /** Вложенные опции */
14
+ options: Pick<ItemSingleProps, 'tagLabel' | 'onClick' | 'option' | 'icon' | 'disabled' | 'description' | 'caption'>[];
15
+ };
16
+
17
+ export function FunctionBadge({ icon, options }: FunctionBadgeProps) {
18
+ const [isOpen, setIsOpen] = useState<boolean>(false);
19
+
20
+ const { setVisible } = useContext(FunctionBadgeContext);
21
+
22
+ useLayoutEffect(() => {
23
+ setVisible && setVisible(isOpen);
24
+ }, [isOpen, setVisible]);
25
+
26
+ const {
27
+ firstElementRefCallback,
28
+ triggerElementRef,
29
+ handleDroplistFocusLeave,
30
+ handleDroplistItemClick,
31
+ handleTriggerKeyDown,
32
+ handleDroplistItemKeyDown,
33
+ } = Droplist.useKeyboardNavigation<HTMLButtonElement>({ setDroplistOpen: setIsOpen });
34
+
35
+ const onClick = useCallback((e: MouseEvent<HTMLButtonElement>) => {
36
+ e.stopPropagation();
37
+ setIsOpen(isOpen => !isOpen);
38
+ }, []);
39
+
40
+ return (
41
+ <span className={styles.wrapper}>
42
+ <Droplist
43
+ open={isOpen}
44
+ onOpenChange={setIsOpen}
45
+ firstElementRefCallback={firstElementRefCallback}
46
+ widthStrategy={Droplist.widthStrategies.Gte}
47
+ useScroll
48
+ onFocusLeave={handleDroplistFocusLeave}
49
+ data-test-id={TEST_IDS.droplist}
50
+ triggerClassName={styles.triggerClassName}
51
+ placement={Droplist.placements.BottomEnd}
52
+ triggerElement={
53
+ <button
54
+ data-test-id={TEST_IDS.functionBadge}
55
+ className={styles.button}
56
+ onKeyDown={handleTriggerKeyDown}
57
+ onClick={onClick}
58
+ ref={triggerElementRef}
59
+ >
60
+ {icon || <KebabSVG />}
61
+ </button>
62
+ }
63
+ >
64
+ {options.map(item => (
65
+ <Droplist.ItemSingle
66
+ {...item}
67
+ key={item.option}
68
+ className={styles.item}
69
+ data-test-id={TEST_IDS.option}
70
+ onClick={e => {
71
+ handleDroplistItemClick(e, item.onClick);
72
+ }}
73
+ onKeyDown={handleDroplistItemKeyDown}
74
+ />
75
+ ))}
76
+ </Droplist>
77
+ </span>
78
+ );
79
+ }
@@ -0,0 +1,6 @@
1
+ export const TRIGGER_OPEN_DROPLIST_KEY_KEYS = [
2
+ ' ', // <- Space key
3
+ 'ArrowDown',
4
+ ];
5
+
6
+ export const TRIGGER_CLOSE_DROPLIST_KEY_CODES = ['Escape', 'Tab'];
@@ -0,0 +1 @@
1
+ export * from './FunctionBadge';
@@ -0,0 +1,48 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-card';
2
+ @import "@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element";
3
+
4
+ .button {
5
+ @include composite-var($card-button);
6
+
7
+ cursor: pointer;
8
+
9
+ position: relative;
10
+
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+
15
+ color: $sys-neutral-text-main;
16
+
17
+ background-color: transparent;
18
+ border: 0;
19
+ outline-color: transparent;
20
+
21
+ &:hover {
22
+ color: $sys-neutral-text-support;
23
+ }
24
+
25
+ &:focus-visible {
26
+ @include outline-var($container-focused-s);
27
+
28
+ color: $sys-neutral-text-support;
29
+ outline-color: $sys-available-complementary;
30
+ }
31
+
32
+ &:active {
33
+ color: $sys-neutral-text-light;
34
+ }
35
+
36
+ svg {
37
+ width: $icon-s !important; /* stylelint-disable-line declaration-no-important */
38
+ height: $icon-s !important; /* stylelint-disable-line declaration-no-important */
39
+ }
40
+ }
41
+
42
+ .triggerClassName {
43
+ --offset: #{$space-drop-list-drop-offset};
44
+ }
45
+
46
+ .wrapper {
47
+ position: relative;
48
+ }
@@ -0,0 +1,79 @@
1
+ import cn from 'classnames';
2
+
3
+ import { TruncateString } from '@snack-uikit/truncate-string';
4
+ import { Typography } from '@snack-uikit/typography';
5
+ import { excludeSupportProps, WithSupportProps } from '@snack-uikit/utils';
6
+
7
+ import { Size, TEST_IDS } from '../../constants';
8
+ import { useCardContext } from '../../context';
9
+ import { Emblem, EmblemProps } from '../../helperComponents';
10
+ import { DESCRIPTION_SIZE_MAP, TITLE_SIZE_MAP } from './constants';
11
+ import styles from './styles.module.scss';
12
+
13
+ export type HeaderProps = WithSupportProps<{
14
+ /** Заголовок */
15
+ title: string;
16
+ /** Подзаголовок */
17
+ description?: string;
18
+ /** Метаинформация */
19
+ metadata?: string;
20
+ /** Эмблема иконка/картинка */
21
+ emblem?: EmblemProps;
22
+ /** CSS-класс для элемента с контентом */
23
+ className?: string;
24
+ /** Размер */
25
+ size?: Size;
26
+ }>;
27
+
28
+ export function Header({ title, description, metadata, emblem, className, size: sizeProp, ...rest }: HeaderProps) {
29
+ const { size: sizeContext } = useCardContext();
30
+
31
+ const size = sizeProp || sizeContext;
32
+
33
+ return (
34
+ <div className={cn(styles.titleLayout, className)} {...excludeSupportProps(rest)} data-size={size}>
35
+ {emblem && <Emblem {...emblem} />}
36
+
37
+ <div className={styles.contentLayout}>
38
+ <Typography
39
+ family={Typography.families.Sans}
40
+ size={TITLE_SIZE_MAP[size]}
41
+ role={Typography.roles.Title}
42
+ className={styles.title}
43
+ data-test-id={TEST_IDS.title}
44
+ >
45
+ <TruncateString variant={TruncateString.variants.End} maxLines={1} text={title} />
46
+ </Typography>
47
+
48
+ {metadata && (
49
+ <Typography.SansBodyS className={styles.metadata}>
50
+ <TruncateString
51
+ variant={TruncateString.variants.End}
52
+ maxLines={1}
53
+ text={metadata}
54
+ data-test-id={TEST_IDS.metadata}
55
+ />
56
+ </Typography.SansBodyS>
57
+ )}
58
+
59
+ {description && (
60
+ <Typography
61
+ family={Typography.families.Sans}
62
+ size={DESCRIPTION_SIZE_MAP[size]}
63
+ role={Typography.roles.Body}
64
+ className={styles.description}
65
+ >
66
+ <TruncateString
67
+ variant={TruncateString.variants.End}
68
+ maxLines={2}
69
+ text={description}
70
+ data-test-id={TEST_IDS.description}
71
+ />
72
+ </Typography>
73
+ )}
74
+ </div>
75
+ </div>
76
+ );
77
+ }
78
+
79
+ Header.emblemIconAppearances = Emblem.appearances;