sbwb-ds 3.1.11 → 3.1.13
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/dist/sbwb-ds.js +4256 -4247
- package/dist/sbwb-ds.umd.cjs +152 -155
- package/dist/src/presentation/components/atoms/FloatingButton/Components/FloatingMenu/index.d.ts +8 -0
- package/dist/src/presentation/components/atoms/FloatingButton/Components/FloatingMenu/styles.d.ts +9 -0
- package/dist/src/presentation/components/atoms/FloatingButton/index.d.ts +8 -0
- package/dist/src/presentation/components/atoms/FloatingButton/styles.d.ts +5 -0
- package/dist/src/presentation/components/atoms/Tooltip/index.d.ts +3 -1
- package/dist/src/presentation/components/molecules/PopConfirm/index.d.ts +18 -0
- package/dist/src/presentation/components/molecules/PopConfirm/styles.d.ts +22 -0
- package/dist/src/presentation/components/molecules/SegmentedButton/index.d.ts +11 -0
- package/dist/src/presentation/components/molecules/SegmentedButton/styles.d.ts +13 -0
- package/package.json +2 -1
- package/src/presentation/components/atoms/ActionButton/styles.ts +1 -1
- package/src/presentation/components/atoms/Button/Button.stories.tsx +0 -1
- package/src/presentation/components/atoms/Button/styles.ts +1 -4
- package/src/presentation/components/atoms/FloatingButton/Components/FloatingMenu/index.tsx +29 -0
- package/src/presentation/components/atoms/FloatingButton/Components/FloatingMenu/styles.ts +59 -0
- package/src/presentation/components/atoms/FloatingButton/FloatingButton.stories.tsx +64 -0
- package/src/presentation/components/atoms/FloatingButton/index.tsx +78 -0
- package/src/presentation/components/atoms/FloatingButton/styles.ts +22 -0
- package/src/presentation/components/atoms/Tooltip/index.tsx +57 -50
- package/src/presentation/components/molecules/PopConfirm/PopConfirm.stories.tsx +72 -0
- package/src/presentation/components/molecules/PopConfirm/index.tsx +164 -0
- package/src/presentation/components/molecules/PopConfirm/styles.ts +126 -0
- package/src/presentation/components/molecules/SegmentedButton/SegmentedButton.stories.tsx +78 -0
- package/src/presentation/components/molecules/SegmentedButton/index.tsx +47 -0
- package/src/presentation/components/molecules/SegmentedButton/styles.ts +71 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PopConfirm from './';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Floating/PopConfirm',
|
|
6
|
+
component: PopConfirm,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
argTypes: {
|
|
9
|
+
position: {
|
|
10
|
+
control: 'select',
|
|
11
|
+
options: ['top', 'bottom', 'left', 'right'],
|
|
12
|
+
description: 'Position of the popconfirm relative to the target element.',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const Template = (args) => {
|
|
18
|
+
const [isVisible, setIsVisible] = React.useState(false);
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
style={{
|
|
22
|
+
width: '100%',
|
|
23
|
+
display: 'flex',
|
|
24
|
+
alignItems: 'center',
|
|
25
|
+
flexDirection: 'column',
|
|
26
|
+
marginTop: '100px',
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
<div
|
|
30
|
+
style={{
|
|
31
|
+
width: 'fit-content',
|
|
32
|
+
height: 'fit-content',
|
|
33
|
+
display: 'flex',
|
|
34
|
+
alignItems: 'center',
|
|
35
|
+
flexDirection: 'column',
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
<PopConfirm
|
|
39
|
+
position="bottom"
|
|
40
|
+
{...args}
|
|
41
|
+
isActive={isVisible}
|
|
42
|
+
onClickCancel={() => {
|
|
43
|
+
setIsVisible(false);
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
<button
|
|
47
|
+
style={{
|
|
48
|
+
padding: '10px',
|
|
49
|
+
borderRadius: '5px',
|
|
50
|
+
cursor: 'pointer',
|
|
51
|
+
}}
|
|
52
|
+
onClick={() => setIsVisible(!isVisible)}
|
|
53
|
+
>
|
|
54
|
+
Click me!
|
|
55
|
+
</button>
|
|
56
|
+
</PopConfirm>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const Default = Template.bind({});
|
|
63
|
+
Default.args = {
|
|
64
|
+
width: '240px',
|
|
65
|
+
height: 'fit-content',
|
|
66
|
+
type: 'danger',
|
|
67
|
+
title: "Don't use big titles!",
|
|
68
|
+
text: 'A test with a long description. Are you sure you want to do this?',
|
|
69
|
+
onClickConfirm: () => {
|
|
70
|
+
alert('Confirmed!');
|
|
71
|
+
},
|
|
72
|
+
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import * as C from './styles';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import Icon from '../../atoms/Icon';
|
|
4
|
+
import { sg } from '../../../styles';
|
|
5
|
+
import Button from '../../atoms/Button';
|
|
6
|
+
import { pxToRem } from '../../../../main/helpers/functions/measurementConverters';
|
|
7
|
+
|
|
8
|
+
import { useRef, useEffect } from 'react';
|
|
9
|
+
|
|
10
|
+
export interface PopConfirmProps {
|
|
11
|
+
id?: string;
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
type: 'danger' | 'success' | 'warning';
|
|
14
|
+
title: string;
|
|
15
|
+
text?: string;
|
|
16
|
+
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
17
|
+
isActive?: boolean;
|
|
18
|
+
popConfirmMargin?: string;
|
|
19
|
+
wrapperWidth?: string;
|
|
20
|
+
wrapperHeight?: string;
|
|
21
|
+
wrapperStyles?: React.CSSProperties;
|
|
22
|
+
onClickConfirm?: () => void;
|
|
23
|
+
onClickCancel?: () => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const PopConfirm = ({
|
|
27
|
+
id,
|
|
28
|
+
children,
|
|
29
|
+
type,
|
|
30
|
+
title,
|
|
31
|
+
text,
|
|
32
|
+
position,
|
|
33
|
+
isActive = true,
|
|
34
|
+
popConfirmMargin = '8px',
|
|
35
|
+
wrapperWidth,
|
|
36
|
+
wrapperHeight,
|
|
37
|
+
wrapperStyles,
|
|
38
|
+
onClickConfirm,
|
|
39
|
+
onClickCancel,
|
|
40
|
+
}: PopConfirmProps) => {
|
|
41
|
+
const targetRef = useRef<HTMLDivElement>(null);
|
|
42
|
+
const popconfirmRef = useRef<HTMLDivElement>(null);
|
|
43
|
+
const updatePopConfirmPosition = () => {
|
|
44
|
+
if (targetRef.current && popconfirmRef.current) {
|
|
45
|
+
const targetRect = targetRef.current.getBoundingClientRect();
|
|
46
|
+
const popconfirmRect = popconfirmRef.current.getBoundingClientRect();
|
|
47
|
+
const margin = parseInt(popConfirmMargin, 10);
|
|
48
|
+
let newStyle: React.CSSProperties = {};
|
|
49
|
+
|
|
50
|
+
switch (position) {
|
|
51
|
+
case 'top':
|
|
52
|
+
newStyle = {
|
|
53
|
+
top: `${targetRect.top - popconfirmRect.height - margin}px`,
|
|
54
|
+
left: `${targetRect.left + targetRect.width / 2}px`,
|
|
55
|
+
transform: 'translate(-50%, 0)',
|
|
56
|
+
};
|
|
57
|
+
break;
|
|
58
|
+
|
|
59
|
+
case 'bottom':
|
|
60
|
+
newStyle = {
|
|
61
|
+
top: `${targetRect.bottom + margin}px`,
|
|
62
|
+
left: `${targetRect.left + targetRect.width / 2}px`,
|
|
63
|
+
transform: 'translate(-50%, 0)',
|
|
64
|
+
};
|
|
65
|
+
break;
|
|
66
|
+
|
|
67
|
+
case 'left':
|
|
68
|
+
newStyle = {
|
|
69
|
+
top: `${targetRect.top + targetRect.height / 2}px`,
|
|
70
|
+
left: `${targetRect.left - popconfirmRect.width - margin}px`,
|
|
71
|
+
transform: 'translate(0, -50%)',
|
|
72
|
+
};
|
|
73
|
+
break;
|
|
74
|
+
|
|
75
|
+
case 'right':
|
|
76
|
+
newStyle = {
|
|
77
|
+
top: `${targetRect.top + targetRect.height / 2}px`,
|
|
78
|
+
left: `${targetRect.right + margin}px`,
|
|
79
|
+
transform: 'translate(0, -50%)',
|
|
80
|
+
};
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Object.assign(popconfirmRef.current.style, newStyle);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (isActive) {
|
|
89
|
+
updatePopConfirmPosition();
|
|
90
|
+
window.addEventListener('resize', updatePopConfirmPosition);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return () => {
|
|
94
|
+
window.removeEventListener('resize', updatePopConfirmPosition);
|
|
95
|
+
};
|
|
96
|
+
}, [isActive, position, popConfirmMargin]);
|
|
97
|
+
|
|
98
|
+
if (!isActive) {
|
|
99
|
+
return <>{children}</>;
|
|
100
|
+
}
|
|
101
|
+
const setIconName = () => {
|
|
102
|
+
switch (type) {
|
|
103
|
+
case 'danger':
|
|
104
|
+
return 'CancelFill0Md';
|
|
105
|
+
case 'success':
|
|
106
|
+
return 'CheckCircleMd';
|
|
107
|
+
case 'warning':
|
|
108
|
+
return 'WarningMd';
|
|
109
|
+
default:
|
|
110
|
+
return 'WarningMd';
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const setIconColor = () => {
|
|
115
|
+
switch (type) {
|
|
116
|
+
case 'danger':
|
|
117
|
+
return sg.colors.feedbackColors.colorFeedbackError;
|
|
118
|
+
case 'success':
|
|
119
|
+
return sg.colors.feedbackColors.colorFeedbackSuccess;
|
|
120
|
+
case 'warning':
|
|
121
|
+
return sg.colors.feedbackColors.colorFeedbackWarning;
|
|
122
|
+
default:
|
|
123
|
+
return sg.colors.feedbackColors.colorFeedbackWarning;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const popConfirmContent = (
|
|
127
|
+
<C.PopconfirmContainer ref={popconfirmRef} position={position}>
|
|
128
|
+
<C.Header>
|
|
129
|
+
<Icon iconName={setIconName()} color={setIconColor()} />
|
|
130
|
+
<C.Spacing width={sg.spacings.spacingStack.spacingStackXs} />
|
|
131
|
+
<C.Title>{title}</C.Title>
|
|
132
|
+
</C.Header>
|
|
133
|
+
<C.Spacing height={sg.spacings.spacingStack.spacingStackXs} />
|
|
134
|
+
<C.Content>{text}</C.Content>
|
|
135
|
+
<C.Spacing height={sg.spacings.spacingInline.spacingInlineAnt} />
|
|
136
|
+
<C.Footer>
|
|
137
|
+
<Button
|
|
138
|
+
variant="secondary"
|
|
139
|
+
size="Small"
|
|
140
|
+
width={pxToRem('102px')}
|
|
141
|
+
onClick={onClickCancel}
|
|
142
|
+
>
|
|
143
|
+
Cancel
|
|
144
|
+
</Button>
|
|
145
|
+
<Button size="Small" width={pxToRem('102px')} onClick={onClickConfirm}>
|
|
146
|
+
Confirm
|
|
147
|
+
</Button>
|
|
148
|
+
</C.Footer>
|
|
149
|
+
</C.PopconfirmContainer>
|
|
150
|
+
);
|
|
151
|
+
return (
|
|
152
|
+
<C.PopConfirmWrapper
|
|
153
|
+
ref={targetRef}
|
|
154
|
+
wrapperWidth={wrapperWidth}
|
|
155
|
+
wrapperHeight={wrapperHeight}
|
|
156
|
+
wrapperStyles={wrapperStyles}
|
|
157
|
+
>
|
|
158
|
+
{children}
|
|
159
|
+
{ReactDOM.createPortal(popConfirmContent, document.body)}
|
|
160
|
+
</C.PopConfirmWrapper>
|
|
161
|
+
);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export default PopConfirm;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
import { resetStyles, sg } from '../../../styles';
|
|
3
|
+
|
|
4
|
+
interface PopConfirmWrapperProps {
|
|
5
|
+
wrapperWidth?: string;
|
|
6
|
+
wrapperHeight?: string;
|
|
7
|
+
wrapperStyles?: any;
|
|
8
|
+
}
|
|
9
|
+
export const PopConfirmWrapper = styled.div<PopConfirmWrapperProps>`
|
|
10
|
+
${resetStyles}
|
|
11
|
+
width: ${({ wrapperWidth }) => wrapperWidth ?? 'fit-content'};
|
|
12
|
+
height: ${({ wrapperHeight }) => wrapperHeight ?? 'fit-content'};
|
|
13
|
+
${({ wrapperStyles }) => wrapperStyles && css(wrapperStyles)};
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
interface PopConfirmContainerProps {
|
|
17
|
+
height?: string;
|
|
18
|
+
width?: string;
|
|
19
|
+
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
20
|
+
}
|
|
21
|
+
export const PopconfirmContainer = styled.div<PopConfirmContainerProps>`
|
|
22
|
+
position: fixed;
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
height: ${({ height }) => height ?? 'fit-content'};
|
|
27
|
+
width: ${({ width }) => width ?? '240px'};
|
|
28
|
+
background-color: ${sg.colors.backgroundColors.colorBackgroundSnow};
|
|
29
|
+
border-radius: ${sg.borders.borderRadius.borderRadiusSm};
|
|
30
|
+
box-shadow: ${sg.shadows.shadowLevelNear};
|
|
31
|
+
padding: ${sg.spacings.spacingInset.spacingInsetXs};
|
|
32
|
+
border-width: ${sg.borders.borderWidth.borderWidthThinner};
|
|
33
|
+
border-color: ${sg.colors.neutralColors.colorNeutralClean};
|
|
34
|
+
border-style: solid;
|
|
35
|
+
min-width: 240px;
|
|
36
|
+
z-index: 1000;
|
|
37
|
+
&::before {
|
|
38
|
+
content: '';
|
|
39
|
+
position: absolute;
|
|
40
|
+
width: 12px;
|
|
41
|
+
height: 12px;
|
|
42
|
+
border-style: solid;
|
|
43
|
+
|
|
44
|
+
${({ position }) => {
|
|
45
|
+
const borderColor = sg.colors.neutralColors.colorNeutralClean;
|
|
46
|
+
return css`
|
|
47
|
+
${['top', 'topLeft', 'topRight'].includes(position) &&
|
|
48
|
+
css`
|
|
49
|
+
background-color: ${sg.colors.neutralColors.colorNeutralSnow};
|
|
50
|
+
bottom: -6px;
|
|
51
|
+
left: calc(50% - 6px);
|
|
52
|
+
border-width: 1px 1px 1px 1px;
|
|
53
|
+
border-color: transparent ${borderColor} ${borderColor} transparent;
|
|
54
|
+
transform: rotate(45deg);
|
|
55
|
+
border-radius: 2px;
|
|
56
|
+
`}
|
|
57
|
+
${['bottom', 'bottomLeft', 'bottomRight'].includes(position) &&
|
|
58
|
+
css`
|
|
59
|
+
top: -6px;
|
|
60
|
+
left: calc(50% - 6px);
|
|
61
|
+
border-width: 1px;
|
|
62
|
+
border-color: ${borderColor} transparent transparent ${borderColor};
|
|
63
|
+
background-color: ${sg.colors.neutralColors.colorNeutralSnow};
|
|
64
|
+
transform: rotate(45deg);
|
|
65
|
+
border-radius: 2px;
|
|
66
|
+
`}
|
|
67
|
+
${['left', 'leftStart', 'leftEnd'].includes(position) &&
|
|
68
|
+
css`
|
|
69
|
+
top: calc(50% - 6px);
|
|
70
|
+
right: -6px;
|
|
71
|
+
border-width: 1px 1px 1px 1px;
|
|
72
|
+
border-color: ${borderColor} ${borderColor} transparent transparent;
|
|
73
|
+
background-color: ${sg.colors.neutralColors.colorNeutralSnow};
|
|
74
|
+
transform: rotate(45deg);
|
|
75
|
+
border-radius: 2px;
|
|
76
|
+
`}
|
|
77
|
+
${['right', 'rightStart', 'rightEnd'].includes(position) &&
|
|
78
|
+
css`
|
|
79
|
+
top: calc(50% - 6px);
|
|
80
|
+
left: -6px;
|
|
81
|
+
border-width: 1px 1px 1px 1px;
|
|
82
|
+
border-color: transparent transparent ${borderColor} ${borderColor};
|
|
83
|
+
background-color: ${sg.colors.neutralColors.colorNeutralSnow};
|
|
84
|
+
transform: rotate(45deg);
|
|
85
|
+
border-radius: 2px;
|
|
86
|
+
`}
|
|
87
|
+
`;
|
|
88
|
+
}}
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
export const Header = styled.div`
|
|
93
|
+
display: flex;
|
|
94
|
+
flex-direction: row;
|
|
95
|
+
justify-content: flex-start;
|
|
96
|
+
align-items: center;
|
|
97
|
+
`;
|
|
98
|
+
export const Title = styled.div`
|
|
99
|
+
font-family: ${sg.fonts.fontFamily.fontFamilyPrimary};
|
|
100
|
+
font-size: ${sg.fonts.fontSize.fontSizeBodyLg};
|
|
101
|
+
font-weight: ${sg.fonts.fontWeight.fontWeightSemiBold};
|
|
102
|
+
color: ${sg.colors.neutralColors.colorNeutralDarkest};
|
|
103
|
+
`;
|
|
104
|
+
export const Content = styled.div`
|
|
105
|
+
font-family: ${sg.fonts.fontFamily.fontFamilyPrimary};
|
|
106
|
+
font-weight: ${sg.fonts.fontWeight.fontWeightRegular};
|
|
107
|
+
font-size: ${sg.fonts.fontSize.fontSizeBodyMd};
|
|
108
|
+
color: ${sg.colors.neutralColors.colorNeutralDark};
|
|
109
|
+
letter-spacing: 0%;
|
|
110
|
+
overflow-wrap: break-word;
|
|
111
|
+
overflow: hidden;
|
|
112
|
+
`;
|
|
113
|
+
export const Footer = styled.div`
|
|
114
|
+
display: flex;
|
|
115
|
+
flex-direction: row;
|
|
116
|
+
justify-content: space-between;
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
interface SpacingProps {
|
|
120
|
+
height?: string;
|
|
121
|
+
width?: string;
|
|
122
|
+
}
|
|
123
|
+
export const Spacing = styled.div<SpacingProps>`
|
|
124
|
+
height: ${({ height }) => height ?? '0px'};
|
|
125
|
+
width: ${({ width }) => width ?? '0px'};
|
|
126
|
+
`;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import SegmentedButton from '.';
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Components/SegmentedButton',
|
|
5
|
+
component: SegmentedButton,
|
|
6
|
+
tags: ['autodocs'],
|
|
7
|
+
argTypes: {
|
|
8
|
+
buttons: {
|
|
9
|
+
control: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
},
|
|
12
|
+
description:
|
|
13
|
+
'Array of button objects. Valid props are "label", "iconName", "onClick", and "disabled".',
|
|
14
|
+
},
|
|
15
|
+
onClick: {
|
|
16
|
+
control: {
|
|
17
|
+
type: 'function',
|
|
18
|
+
},
|
|
19
|
+
description: 'Function to be called when a button is clicked.',
|
|
20
|
+
},
|
|
21
|
+
disabled: {
|
|
22
|
+
control: {
|
|
23
|
+
type: 'boolean',
|
|
24
|
+
},
|
|
25
|
+
description: 'Disables the button if set to true.',
|
|
26
|
+
},
|
|
27
|
+
label: {
|
|
28
|
+
control: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
},
|
|
31
|
+
description: 'Label of the button. It is mandatory.',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const Template = (args) => <SegmentedButton {...args} />;
|
|
37
|
+
|
|
38
|
+
export const Default = Template.bind({});
|
|
39
|
+
Default.args = {
|
|
40
|
+
buttons: [
|
|
41
|
+
{
|
|
42
|
+
label: 'Button 1',
|
|
43
|
+
iconName: 'AccountCircleFILL1Sm',
|
|
44
|
+
onClick: () => console.log('Button 1 clicked'),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: 'Button 2',
|
|
48
|
+
iconName: 'AccountCircleFILL1Sm',
|
|
49
|
+
onClick: () => console.log('Button 2 clicked'),
|
|
50
|
+
disabled: false,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const Template3bt = (args) => <SegmentedButton {...args} />;
|
|
56
|
+
|
|
57
|
+
export const ThreeButtons = Template3bt.bind({});
|
|
58
|
+
ThreeButtons.args = {
|
|
59
|
+
buttons: [
|
|
60
|
+
{
|
|
61
|
+
label: 'Button 1',
|
|
62
|
+
iconName: 'AccountCircleFILL1Sm',
|
|
63
|
+
onClick: () => console.log('Button 1 clicked'),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
label: 'Button 2',
|
|
67
|
+
iconName: 'AccountCircleFILL1Sm',
|
|
68
|
+
onClick: () => console.log('Button 2 clicked'),
|
|
69
|
+
disabled: false,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
label: 'Button 3',
|
|
73
|
+
iconName: 'AccountCircleFILL1Sm',
|
|
74
|
+
onClick: () => console.log('Button 3 clicked'),
|
|
75
|
+
disabled: false,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as C from './styles';
|
|
2
|
+
import { sg } from '../../../styles/styleGuide';
|
|
3
|
+
import Icon, { IconProps } from '../../atoms/Icon';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
|
|
6
|
+
export interface SegmentedButtonProps {
|
|
7
|
+
buttons: {
|
|
8
|
+
label: string;
|
|
9
|
+
iconName?: IconProps['iconName'];
|
|
10
|
+
onClick?: () => void;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
}[];
|
|
13
|
+
}
|
|
14
|
+
const SegmentedButton = ({ buttons }: SegmentedButtonProps) => {
|
|
15
|
+
const [activeButton, setActiveButton] = useState(0);
|
|
16
|
+
const handleButtonClick = (index: number) => {
|
|
17
|
+
setActiveButton(index);
|
|
18
|
+
if (buttons[index].onClick && !buttons[index].disabled) {
|
|
19
|
+
buttons[index].onClick();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
return (
|
|
23
|
+
<C.Container>
|
|
24
|
+
{buttons.map((button, index) => (
|
|
25
|
+
<>
|
|
26
|
+
<C.BaseButtonContainer
|
|
27
|
+
onClick={() => handleButtonClick(index)}
|
|
28
|
+
key={button.label}
|
|
29
|
+
active={activeButton === index}
|
|
30
|
+
disabled={button.disabled}
|
|
31
|
+
>
|
|
32
|
+
<Icon iconName={button.iconName} color={'currentcolor'} />
|
|
33
|
+
<C.Space width={sg.spacings.spacingInline.spacingInlineNano} />
|
|
34
|
+
<C.BaseButton disabled={button.disabled}>
|
|
35
|
+
{button.label}
|
|
36
|
+
</C.BaseButton>
|
|
37
|
+
</C.BaseButtonContainer>
|
|
38
|
+
{index < buttons.length - 1 && (
|
|
39
|
+
<C.Space width={sg.spacings.spacingInline.spacingInlineNano} />
|
|
40
|
+
)}
|
|
41
|
+
</>
|
|
42
|
+
))}
|
|
43
|
+
</C.Container>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default SegmentedButton;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { sg } from '../../../styles/styleGuide';
|
|
2
|
+
import { pxToRem } from '../../../../main/helpers/functions/measurementConverters';
|
|
3
|
+
import styled, { css } from 'styled-components';
|
|
4
|
+
|
|
5
|
+
export const Container = styled.div`
|
|
6
|
+
display: flex;
|
|
7
|
+
background-color: ${sg.colors.backgroundColors.colorBackgroundSky};
|
|
8
|
+
width: fit-content;
|
|
9
|
+
min-width: fit-content;
|
|
10
|
+
border-radius: ${sg.borders.borderRadius.borderRadiusSm};
|
|
11
|
+
padding: ${sg.spacings.spacingStack.spacingStackNano}
|
|
12
|
+
${sg.spacings.spacingInline.spacingInlineNano};
|
|
13
|
+
min-height: ${pxToRem('32px')};
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
interface BaseButtonContainerProps {
|
|
17
|
+
active?: boolean;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export const BaseButtonContainer = styled.div`
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: center;
|
|
24
|
+
padding: ${sg.spacings.spacingStack.spacingStackNano}
|
|
25
|
+
${sg.spacings.spacingInline.spacingInlineXs};
|
|
26
|
+
min-width: fit-content;
|
|
27
|
+
min-height: ${pxToRem('24px')};
|
|
28
|
+
border-radius: ${sg.borders.borderRadius.borderRadiusXs};
|
|
29
|
+
color: ${sg.colors.neutralColors.colorNeutralDark};
|
|
30
|
+
${({ disabled, active }: BaseButtonContainerProps) => {
|
|
31
|
+
if (disabled) {
|
|
32
|
+
return css`
|
|
33
|
+
color: ${sg.colors.neutralColors.colorNeutralCleanest};
|
|
34
|
+
`;
|
|
35
|
+
}
|
|
36
|
+
if (active) {
|
|
37
|
+
return css`
|
|
38
|
+
background-color: ${sg.colors.backgroundColors.colorBackgroundSnow};
|
|
39
|
+
color: ${sg.colors.neutralColors.colorNeutralDarkest};
|
|
40
|
+
border-radius: ${sg.borders.borderRadius.borderRadiusXs};
|
|
41
|
+
box-shadow: ${sg.shadows.shadowLevelNear};
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
}}
|
|
45
|
+
|
|
46
|
+
&: hover {
|
|
47
|
+
color: ${({ disabled }) =>
|
|
48
|
+
!disabled && sg.colors.neutralColors.colorNeutralDarkest};
|
|
49
|
+
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
export const BaseButton = styled.button`
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
background-color: transparent;
|
|
56
|
+
justify-content: center;
|
|
57
|
+
color: currentcolor;
|
|
58
|
+
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
|
59
|
+
font-weight: ${sg.fonts.fontWeight.fontWeightRegular};
|
|
60
|
+
font-size: ${sg.fonts.fontSize.fontSizeBodyMd};
|
|
61
|
+
line-height: 16px;
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
interface SpaceProps {
|
|
65
|
+
width: string;
|
|
66
|
+
height?: string;
|
|
67
|
+
}
|
|
68
|
+
export const Space = styled.div`
|
|
69
|
+
height: ${(props: SpaceProps) => props.height || '0'};
|
|
70
|
+
width: ${(props: SpaceProps) => props.width || '0'};
|
|
71
|
+
`;
|