@cloud-ru/uikit-product-claudia 1.6.2
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.
- package/CHANGELOG.md +112 -0
- package/LICENSE +201 -0
- package/README.md +586 -0
- package/package.json +60 -0
- package/src/components/ButtonClaudia/ButtonClaudia.tsx +33 -0
- package/src/components/ButtonClaudia/constants.ts +29 -0
- package/src/components/ButtonClaudia/helperComponents/ButtonPrivate/ButtonPrivate.tsx +99 -0
- package/src/components/ButtonClaudia/helperComponents/ButtonPrivate/constants.ts +13 -0
- package/src/components/ButtonClaudia/helperComponents/ButtonPrivate/index.ts +1 -0
- package/src/components/ButtonClaudia/helperComponents/ButtonPrivate/styles.module.scss +46 -0
- package/src/components/ButtonClaudia/helperComponents/ButtonPrivate/utils.tsx +92 -0
- package/src/components/ButtonClaudia/helperComponents/index.ts +1 -0
- package/src/components/ButtonClaudia/index.ts +1 -0
- package/src/components/ButtonClaudia/styles.module.scss +141 -0
- package/src/components/ButtonClaudia/types.ts +63 -0
- package/src/components/ButtonClaudia/utils.ts +15 -0
- package/src/components/ButtonGiga/ButtonGigaFunction/ButtonGigaFunction.tsx +43 -0
- package/src/components/ButtonGiga/ButtonGigaFunction/index.ts +1 -0
- package/src/components/ButtonGiga/ButtonGigaFunction/styles.module.scss +179 -0
- package/src/components/ButtonGiga/ButtonGigaFunction/types.ts +43 -0
- package/src/components/ButtonGiga/ButtonGigaFunction/utils.ts +16 -0
- package/src/components/ButtonGiga/ButtonGigaMama/ButtonGigaMama.tsx +29 -0
- package/src/components/ButtonGiga/ButtonGigaMama/index.ts +1 -0
- package/src/components/ButtonGiga/ButtonGigaMama/styles.module.scss +180 -0
- package/src/components/ButtonGiga/ButtonGigaMama/utils.ts +15 -0
- package/src/components/ButtonGiga/ButtonGigaOutline/ButtonGigaOutline.tsx +43 -0
- package/src/components/ButtonGiga/ButtonGigaOutline/index.ts +1 -0
- package/src/components/ButtonGiga/ButtonGigaOutline/styles.module.scss +223 -0
- package/src/components/ButtonGiga/ButtonGigaOutline/types.ts +63 -0
- package/src/components/ButtonGiga/ButtonGigaOutline/utils.ts +16 -0
- package/src/components/ButtonGiga/constants.ts +29 -0
- package/src/components/ButtonGiga/helperComponents/ButtonPrivate/ButtonPrivate.tsx +99 -0
- package/src/components/ButtonGiga/helperComponents/ButtonPrivate/constants.ts +15 -0
- package/src/components/ButtonGiga/helperComponents/ButtonPrivate/index.ts +1 -0
- package/src/components/ButtonGiga/helperComponents/ButtonPrivate/styles.module.scss +46 -0
- package/src/components/ButtonGiga/helperComponents/ButtonPrivate/types.ts +63 -0
- package/src/components/ButtonGiga/helperComponents/ButtonPrivate/utils.tsx +92 -0
- package/src/components/ButtonGiga/helperComponents/index.ts +1 -0
- package/src/components/ButtonGiga/index.ts +3 -0
- package/src/components/ButtonGiga/types.ts +43 -0
- package/src/components/ChatStatusAnnouncement/ChatStatusAnnouncement.tsx +109 -0
- package/src/components/ChatStatusAnnouncement/constants.ts +1 -0
- package/src/components/ChatStatusAnnouncement/helperComponents/AlertButton/AlertButton.tsx +24 -0
- package/src/components/ChatStatusAnnouncement/helperComponents/AlertButton/index.ts +1 -0
- package/src/components/ChatStatusAnnouncement/helperComponents/AlertButton/styles.module.scss +27 -0
- package/src/components/ChatStatusAnnouncement/helperComponents/TextContent/TextContent.tsx +18 -0
- package/src/components/ChatStatusAnnouncement/helperComponents/TextContent/index.ts +1 -0
- package/src/components/ChatStatusAnnouncement/index.ts +1 -0
- package/src/components/ChatStatusAnnouncement/styled.module.scss +65 -0
- package/src/components/ChatStatusAnnouncement/types.ts +17 -0
- package/src/components/ChatStatusAnnouncement/utils/index.ts +52 -0
- package/src/components/IconGiga/IconGiga.tsx +64 -0
- package/src/components/IconGiga/constants.ts +23 -0
- package/src/components/IconGiga/index.ts +1 -0
- package/src/components/RecommendPannel/RecommendPanel.tsx +131 -0
- package/src/components/RecommendPannel/helperComponents/Chip/Chip.tsx +47 -0
- package/src/components/RecommendPannel/helperComponents/Chip/index.ts +1 -0
- package/src/components/RecommendPannel/helperComponents/Chip/styles.module.scss +45 -0
- package/src/components/RecommendPannel/helperComponents/ClaudiaChip/ClaudiaChip.tsx +40 -0
- package/src/components/RecommendPannel/helperComponents/ClaudiaChip/index.ts +1 -0
- package/src/components/RecommendPannel/helperComponents/CloseChip/CloseChip.tsx +106 -0
- package/src/components/RecommendPannel/helperComponents/CloseChip/index.ts +1 -0
- package/src/components/RecommendPannel/helperComponents/CloseChip/styles.module.scss +73 -0
- package/src/components/RecommendPannel/helperComponents/DropdownChip/DropdownChip.tsx +112 -0
- package/src/components/RecommendPannel/helperComponents/DropdownChip/index.ts +1 -0
- package/src/components/RecommendPannel/helperComponents/DropdownChip/styles.module.scss +56 -0
- package/src/components/RecommendPannel/hooks/index.ts +15 -0
- package/src/components/RecommendPannel/index.ts +1 -0
- package/src/components/RecommendPannel/styles.module.scss +4 -0
- package/src/components/RecommendPannel/types.ts +21 -0
- package/src/components/RecommendPannel/utils/gitVisibleChipsCount.ts +57 -0
- package/src/components/SshField/SshField.tsx +222 -0
- package/src/components/SshField/components/MobileFieldAi/MobileFieldAi.tsx +71 -0
- package/src/components/SshField/components/MobileFieldAi/index.ts +1 -0
- package/src/components/SshField/components/MobileFieldAi/styles.module.scss +80 -0
- package/src/components/SshField/components/TextArea/TextArea.tsx +113 -0
- package/src/components/SshField/components/TextArea/index.ts +1 -0
- package/src/components/SshField/components/TextArea/styles.module.scss +35 -0
- package/src/components/SshField/helperComponents/DropZoneContent/DropZoneContent.tsx +15 -0
- package/src/components/SshField/helperComponents/DropZoneContent/index.ts +1 -0
- package/src/components/SshField/helperComponents/DropZoneContent/styles.module.scss +17 -0
- package/src/components/SshField/helperComponents/FieldSubmitButton/FieldSubmitButton.tsx +45 -0
- package/src/components/SshField/helperComponents/FieldSubmitButton/index.ts +1 -0
- package/src/components/SshField/helperComponents/TextAreaActionsFooter/TextAreaActionsFooter.tsx +18 -0
- package/src/components/SshField/helperComponents/TextAreaActionsFooter/index.ts +1 -0
- package/src/components/SshField/helperComponents/TextAreaActionsFooter/styles.module.scss +23 -0
- package/src/components/SshField/index.ts +1 -0
- package/src/components/SshField/styles.module.scss +54 -0
- package/src/components/SshField/utils/handleFileError.ts +41 -0
- package/src/components/SshField/utils/isTouchDevice.ts +5 -0
- package/src/components/SshField/utils/readFileContent.ts +23 -0
- package/src/components/SshField/utils/validateSSHKey.ts +84 -0
- package/src/components/index.ts +6 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
@use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as stv;
|
|
2
|
+
|
|
3
|
+
.text {
|
|
4
|
+
color: stv.$sys-neutral-text-support;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.chip {
|
|
8
|
+
height: 32px;
|
|
9
|
+
border-radius: 4px;
|
|
10
|
+
padding: 8px;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
cursor: pointer;
|
|
13
|
+
max-width: 250px;
|
|
14
|
+
width: fit-content;
|
|
15
|
+
|
|
16
|
+
&[data-mobile] {
|
|
17
|
+
height: 40px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&:hover {
|
|
21
|
+
border: 1px solid stv.$sys-neutral-decor-hovered;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&:hover .text {
|
|
25
|
+
color: stv.$sys-neutral-text-main;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&:focus {
|
|
29
|
+
border: 1px solid stv.$sys-neutral-decor-hovered;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&:active {
|
|
33
|
+
border: 1px solid stv.$sys-neutral-decor-activated;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.chipDefault {
|
|
38
|
+
border: 1px solid stv.$sys-neutral-background;
|
|
39
|
+
background-color: stv.$sys-neutral-background;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.chipOutline {
|
|
43
|
+
border: 1px solid stv.$sys-neutral-decor-default;
|
|
44
|
+
background-color: transparent;
|
|
45
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { forwardRef, MouseEventHandler, ReactNode, RefObject } from 'react';
|
|
2
|
+
|
|
3
|
+
import { AgentClaudiaSVG } from '@cloud-ru/uikit-product-icons';
|
|
4
|
+
import { LAYOUT_TYPE, LayoutType } from '@cloud-ru/uikit-product-utils';
|
|
5
|
+
import { Tooltip } from '@snack-uikit/tooltip';
|
|
6
|
+
|
|
7
|
+
import { ButtonClaudia } from '../../../ButtonClaudia';
|
|
8
|
+
import { SIZE, Size } from '../../types';
|
|
9
|
+
|
|
10
|
+
type ClaudiaChipProps = {
|
|
11
|
+
onClick?: MouseEventHandler<HTMLElement>;
|
|
12
|
+
size: Size;
|
|
13
|
+
tooltip?: ReactNode;
|
|
14
|
+
layoutType?: LayoutType;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const ClaudiaChip = forwardRef<HTMLElement | HTMLButtonElement, ClaudiaChipProps>(
|
|
18
|
+
({ onClick, size, tooltip, layoutType }, ref) => {
|
|
19
|
+
const isMobile = layoutType === LAYOUT_TYPE.Mobile || size === SIZE.M;
|
|
20
|
+
const totalSize = isMobile ? SIZE.M : size;
|
|
21
|
+
|
|
22
|
+
if (!tooltip) {
|
|
23
|
+
return (
|
|
24
|
+
<ButtonClaudia
|
|
25
|
+
size={totalSize}
|
|
26
|
+
data-mobile={isMobile}
|
|
27
|
+
ref={ref as RefObject<HTMLButtonElement>}
|
|
28
|
+
onClick={onClick}
|
|
29
|
+
icon={<AgentClaudiaSVG size={24} />}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Tooltip triggerRef={ref as RefObject<HTMLElement>} tip={tooltip}>
|
|
36
|
+
<ButtonClaudia size={totalSize} data-mobile={isMobile} onClick={onClick} icon={<AgentClaudiaSVG size={24} />} />
|
|
37
|
+
</Tooltip>
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ClaudiaChip';
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ReactNode, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { MoreInterfaceSVG } from '@cloud-ru/uikit-product-icons';
|
|
4
|
+
import { MobileDropdown } from '@cloud-ru/uikit-product-mobile-dropdown';
|
|
5
|
+
import { LAYOUT_TYPE, LayoutType } from '@cloud-ru/uikit-product-utils';
|
|
6
|
+
import { Dropdown } from '@snack-uikit/dropdown';
|
|
7
|
+
import { Typography } from '@snack-uikit/typography';
|
|
8
|
+
|
|
9
|
+
import { useOutsideClick } from '../../hooks';
|
|
10
|
+
import { SIZE, Size } from '../../types';
|
|
11
|
+
import styles from './styles.module.scss';
|
|
12
|
+
|
|
13
|
+
type DropdownContentProps = {
|
|
14
|
+
content: ReactNode;
|
|
15
|
+
onClick: () => void;
|
|
16
|
+
closeDropdown: () => void;
|
|
17
|
+
isMobileChipSize: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function DropdownContent({ content, onClick, closeDropdown, isMobileChipSize }: DropdownContentProps) {
|
|
21
|
+
const onDropdownItemClick = () => {
|
|
22
|
+
closeDropdown();
|
|
23
|
+
onClick();
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const TypographyComponent = isMobileChipSize ? Typography.SansBodyM : Typography.SansBodyS;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className={styles.dropdown}>
|
|
30
|
+
<button className={styles.dropdownItem} data-mobile={isMobileChipSize || undefined} onClick={onDropdownItemClick}>
|
|
31
|
+
<TypographyComponent>{content}</TypographyComponent>
|
|
32
|
+
</button>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type CloseChipProps = {
|
|
38
|
+
content: ReactNode;
|
|
39
|
+
size: Size;
|
|
40
|
+
layoutType?: LayoutType;
|
|
41
|
+
onClick: () => void;
|
|
42
|
+
isVisible?: boolean;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export function CloseChip({ size, content, onClick, isVisible, layoutType }: CloseChipProps) {
|
|
46
|
+
const [isDropdownOpen, setDropdownOpen] = useState(false);
|
|
47
|
+
const ref = useRef<HTMLButtonElement>(null);
|
|
48
|
+
const isMobile = layoutType === LAYOUT_TYPE.Mobile;
|
|
49
|
+
const isMobileChipSize = isMobile || size === SIZE.M;
|
|
50
|
+
|
|
51
|
+
const openDropdown = () => setDropdownOpen(true);
|
|
52
|
+
const closeDropdown = () => setDropdownOpen(false);
|
|
53
|
+
|
|
54
|
+
useOutsideClick([ref], closeDropdown);
|
|
55
|
+
|
|
56
|
+
if (isMobile) {
|
|
57
|
+
return (
|
|
58
|
+
<MobileDropdown
|
|
59
|
+
open={isDropdownOpen}
|
|
60
|
+
content={
|
|
61
|
+
<DropdownContent
|
|
62
|
+
isMobileChipSize={isMobileChipSize}
|
|
63
|
+
closeDropdown={closeDropdown}
|
|
64
|
+
content={content}
|
|
65
|
+
onClick={onClick}
|
|
66
|
+
/>
|
|
67
|
+
}
|
|
68
|
+
>
|
|
69
|
+
<button
|
|
70
|
+
data-hidden={!isVisible || undefined}
|
|
71
|
+
data-mobile={true}
|
|
72
|
+
className={styles.closeChip}
|
|
73
|
+
ref={ref}
|
|
74
|
+
onClick={openDropdown}
|
|
75
|
+
>
|
|
76
|
+
<MoreInterfaceSVG size={24} />
|
|
77
|
+
</button>
|
|
78
|
+
</MobileDropdown>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<Dropdown
|
|
84
|
+
open={isDropdownOpen}
|
|
85
|
+
triggerRef={ref}
|
|
86
|
+
placement='bottom-end'
|
|
87
|
+
content={
|
|
88
|
+
<DropdownContent
|
|
89
|
+
isMobileChipSize={isMobileChipSize}
|
|
90
|
+
closeDropdown={closeDropdown}
|
|
91
|
+
content={content}
|
|
92
|
+
onClick={onClick}
|
|
93
|
+
/>
|
|
94
|
+
}
|
|
95
|
+
>
|
|
96
|
+
<button
|
|
97
|
+
data-hidden={!isVisible || undefined}
|
|
98
|
+
data-mobile={isMobileChipSize || undefined}
|
|
99
|
+
className={styles.closeChip}
|
|
100
|
+
onClick={openDropdown}
|
|
101
|
+
>
|
|
102
|
+
<MoreInterfaceSVG size={24} />
|
|
103
|
+
</button>
|
|
104
|
+
</Dropdown>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CloseChip';
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
@use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as stv;
|
|
2
|
+
|
|
3
|
+
.closeChip {
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
width: 32px;
|
|
8
|
+
height: 32px;
|
|
9
|
+
transform: rotate(90deg);
|
|
10
|
+
cursor: pointer;
|
|
11
|
+
border: unset;
|
|
12
|
+
background-color: unset;
|
|
13
|
+
|
|
14
|
+
&[data-hidden] {
|
|
15
|
+
pointer-events: none;
|
|
16
|
+
opacity: 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
&[data-mobile] {
|
|
20
|
+
width: 40px;
|
|
21
|
+
height: 40px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
& svg {
|
|
25
|
+
fill: stv.$sys-neutral-text-support;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&:hover svg {
|
|
29
|
+
fill: stv.$sys-neutral-text-main;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.dropdown {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
border-radius: 4px;
|
|
37
|
+
padding: 4px 0px;
|
|
38
|
+
box-sizing: border-box;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.dropdownItem {
|
|
42
|
+
width: 100%;
|
|
43
|
+
height: 32px;
|
|
44
|
+
display: flex;
|
|
45
|
+
justify-content: flex-start;
|
|
46
|
+
align-items: center;
|
|
47
|
+
cursor: pointer;
|
|
48
|
+
border: unset;
|
|
49
|
+
background-color: unset;
|
|
50
|
+
position: relative;
|
|
51
|
+
padding: 0px 8px;
|
|
52
|
+
|
|
53
|
+
&[data-mobile] {
|
|
54
|
+
padding: 0px 10px;
|
|
55
|
+
height: 40px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&:before {
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
content: '';
|
|
61
|
+
position: absolute;
|
|
62
|
+
top: 0;
|
|
63
|
+
left: 0;
|
|
64
|
+
width: 100%;
|
|
65
|
+
height: 100%;
|
|
66
|
+
background-color: transparent;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
&:hover::before {
|
|
70
|
+
background-color: stv.$sys-neutral-accent-default;
|
|
71
|
+
opacity: 0.08;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { MobileDropdown } from '@cloud-ru/uikit-product-mobile-dropdown';
|
|
4
|
+
import { LAYOUT_TYPE, LayoutType } from '@cloud-ru/uikit-product-utils';
|
|
5
|
+
import { Dropdown } from '@snack-uikit/dropdown';
|
|
6
|
+
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
7
|
+
import { Typography } from '@snack-uikit/typography';
|
|
8
|
+
|
|
9
|
+
import { useOutsideClick } from '../../hooks';
|
|
10
|
+
import { ChipProps, ChipType, SIZE, Size } from '../../types';
|
|
11
|
+
import { Chip } from '../Chip';
|
|
12
|
+
import styles from './styles.module.scss';
|
|
13
|
+
|
|
14
|
+
type DropdownContentProps = {
|
|
15
|
+
dropdownItems: ChipProps[];
|
|
16
|
+
closeDropdown: () => void;
|
|
17
|
+
isMobile: boolean;
|
|
18
|
+
size: Size;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function DropdownContent({ size, dropdownItems, closeDropdown, isMobile }: DropdownContentProps) {
|
|
22
|
+
const isMobileChipSize = isMobile || size === SIZE.M;
|
|
23
|
+
|
|
24
|
+
const TypographyComponent = isMobileChipSize ? Typography.SansBodyM : Typography.SansBodyS;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className={styles.dropdown} data-mobile={isMobile || undefined}>
|
|
28
|
+
{dropdownItems.map(item => (
|
|
29
|
+
<button
|
|
30
|
+
key={item.id}
|
|
31
|
+
data-mobile={isMobileChipSize || undefined}
|
|
32
|
+
className={styles.dropdownItem}
|
|
33
|
+
onClick={() => {
|
|
34
|
+
closeDropdown();
|
|
35
|
+
item?.onClick?.();
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
<TypographyComponent>
|
|
39
|
+
<TruncateString variant='end' placement='top' text={item.label} maxLines={1} />
|
|
40
|
+
</TypographyComponent>
|
|
41
|
+
</button>
|
|
42
|
+
))}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type DropdownChip = {
|
|
48
|
+
label: string;
|
|
49
|
+
type: ChipType;
|
|
50
|
+
size: Size;
|
|
51
|
+
layoutType?: LayoutType;
|
|
52
|
+
dropdownItems: ChipProps[];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export function DropdownChip({ size, layoutType, type, label, dropdownItems }: DropdownChip) {
|
|
56
|
+
const [isDropdownOpen, setDropdownOpen] = useState(false);
|
|
57
|
+
const ref = useRef<HTMLButtonElement>(null);
|
|
58
|
+
const isMobile = layoutType === LAYOUT_TYPE.Mobile;
|
|
59
|
+
|
|
60
|
+
const openDropdown = () => setDropdownOpen(true);
|
|
61
|
+
const closeDropdown = () => setDropdownOpen(false);
|
|
62
|
+
|
|
63
|
+
useOutsideClick([ref], closeDropdown);
|
|
64
|
+
|
|
65
|
+
if (isMobile) {
|
|
66
|
+
return (
|
|
67
|
+
<MobileDropdown
|
|
68
|
+
open={isDropdownOpen}
|
|
69
|
+
content={
|
|
70
|
+
<DropdownContent
|
|
71
|
+
size={size}
|
|
72
|
+
isMobile={isMobile}
|
|
73
|
+
closeDropdown={closeDropdown}
|
|
74
|
+
dropdownItems={dropdownItems}
|
|
75
|
+
/>
|
|
76
|
+
}
|
|
77
|
+
>
|
|
78
|
+
<Chip
|
|
79
|
+
ref={ref}
|
|
80
|
+
isVisible={true}
|
|
81
|
+
className={styles.dropdownChip}
|
|
82
|
+
type={type}
|
|
83
|
+
label={label}
|
|
84
|
+
size={size}
|
|
85
|
+
layoutType={layoutType}
|
|
86
|
+
onClick={openDropdown}
|
|
87
|
+
/>
|
|
88
|
+
</MobileDropdown>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<Dropdown
|
|
94
|
+
triggerRef={ref}
|
|
95
|
+
open={isDropdownOpen}
|
|
96
|
+
placement='bottom-end'
|
|
97
|
+
content={
|
|
98
|
+
<DropdownContent size={size} isMobile={isMobile} closeDropdown={closeDropdown} dropdownItems={dropdownItems} />
|
|
99
|
+
}
|
|
100
|
+
>
|
|
101
|
+
<Chip
|
|
102
|
+
isVisible={true}
|
|
103
|
+
className={styles.dropdownChip}
|
|
104
|
+
type={type}
|
|
105
|
+
label={label}
|
|
106
|
+
size={size}
|
|
107
|
+
layoutType={layoutType}
|
|
108
|
+
onClick={openDropdown}
|
|
109
|
+
/>
|
|
110
|
+
</Dropdown>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './DropdownChip';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
@use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as stv;
|
|
2
|
+
|
|
3
|
+
.dropdown {
|
|
4
|
+
width: 256px;
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
border-radius: 4px;
|
|
8
|
+
padding: 4px 0px;
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
|
|
11
|
+
&[data-mobile] {
|
|
12
|
+
width: 100%;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.dropdownChip {
|
|
17
|
+
min-width: 33px;
|
|
18
|
+
|
|
19
|
+
&[data-mobile] {
|
|
20
|
+
min-width: 40px;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.dropdownItem {
|
|
25
|
+
width: 100%;
|
|
26
|
+
height: 32px;
|
|
27
|
+
display: flex;
|
|
28
|
+
justify-content: flex-start;
|
|
29
|
+
align-items: center;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
border: unset;
|
|
32
|
+
background-color: unset;
|
|
33
|
+
padding: 4px 8px;
|
|
34
|
+
position: relative;
|
|
35
|
+
|
|
36
|
+
&[data-mobile] {
|
|
37
|
+
padding: 8px 10px;
|
|
38
|
+
height: 40px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&:before {
|
|
42
|
+
pointer-events: none;
|
|
43
|
+
content: '';
|
|
44
|
+
position: absolute;
|
|
45
|
+
top: 0;
|
|
46
|
+
left: 0;
|
|
47
|
+
width: 100%;
|
|
48
|
+
height: 100%;
|
|
49
|
+
background-color: transparent;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&:hover::before {
|
|
53
|
+
background-color: stv.$sys-neutral-accent-default;
|
|
54
|
+
opacity: 0.08;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RefObject, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useOutsideClick = (ref: RefObject<HTMLElement>[], handler: () => void) => {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
const handleClick = (event: MouseEvent) => {
|
|
6
|
+
ref.forEach(ref => {
|
|
7
|
+
if (ref.current && event.target instanceof Element && !ref.current.contains(event.target)) {
|
|
8
|
+
handler();
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
document.body.addEventListener('click', handleClick);
|
|
13
|
+
return () => document.body.removeEventListener('click', handleClick);
|
|
14
|
+
}, [ref, handler]);
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './RecommendPanel';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ValueOf } from '@snack-uikit/utils';
|
|
2
|
+
|
|
3
|
+
export const CHIP_TYPE = {
|
|
4
|
+
Default: 'default',
|
|
5
|
+
Outline: 'outline',
|
|
6
|
+
} as const;
|
|
7
|
+
|
|
8
|
+
export type ChipType = ValueOf<typeof CHIP_TYPE>;
|
|
9
|
+
|
|
10
|
+
export type ChipProps = {
|
|
11
|
+
id: string;
|
|
12
|
+
label: string;
|
|
13
|
+
onClick?: () => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const SIZE = {
|
|
17
|
+
S: 's',
|
|
18
|
+
M: 'm',
|
|
19
|
+
} as const;
|
|
20
|
+
|
|
21
|
+
export type Size = ValueOf<typeof SIZE>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ChipProps } from '../types';
|
|
2
|
+
|
|
3
|
+
const GAP = 8;
|
|
4
|
+
|
|
5
|
+
type GetVisibleChipsCountArgs = {
|
|
6
|
+
containerWidth: number;
|
|
7
|
+
chipWidths: number[];
|
|
8
|
+
chips: ChipProps[];
|
|
9
|
+
isSmall: boolean;
|
|
10
|
+
isCloseChipExist: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const getVisibleChipsCount = ({
|
|
14
|
+
containerWidth,
|
|
15
|
+
chipWidths,
|
|
16
|
+
chips,
|
|
17
|
+
isSmall,
|
|
18
|
+
isCloseChipExist,
|
|
19
|
+
}: GetVisibleChipsCountArgs) => {
|
|
20
|
+
if (containerWidth === 0 || chipWidths.length === 0) {
|
|
21
|
+
return chips.length;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const baseIcon = (isSmall ? 40 : 32) + GAP;
|
|
25
|
+
const closeIconWidth = isCloseChipExist ? baseIcon - GAP : 0;
|
|
26
|
+
|
|
27
|
+
let availableWidth = Math.round(containerWidth) - baseIcon - closeIconWidth;
|
|
28
|
+
let visibleCount = 0;
|
|
29
|
+
let isCounterChipExist = false;
|
|
30
|
+
let isCounterChipWidthSubstracted = false;
|
|
31
|
+
const chipsWidthWithGap = chipWidths.map(chip => Math.round(chip + GAP));
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < chipsWidthWithGap.length; i++) {
|
|
34
|
+
const chipWidth = chipsWidthWithGap[i];
|
|
35
|
+
|
|
36
|
+
if (availableWidth >= chipWidth) {
|
|
37
|
+
if (isCounterChipExist && !isCounterChipWidthSubstracted) {
|
|
38
|
+
availableWidth -= baseIcon;
|
|
39
|
+
isCounterChipWidthSubstracted = true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
availableWidth -= chipWidth;
|
|
43
|
+
visibleCount++;
|
|
44
|
+
|
|
45
|
+
if (visibleCount === chipsWidthWithGap.length) {
|
|
46
|
+
isCounterChipExist = false;
|
|
47
|
+
isCounterChipWidthSubstracted = false;
|
|
48
|
+
} else {
|
|
49
|
+
isCounterChipExist = true;
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return Math.max(1, visibleCount);
|
|
57
|
+
};
|