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.
Files changed (27) hide show
  1. package/dist/sbwb-ds.js +4256 -4247
  2. package/dist/sbwb-ds.umd.cjs +152 -155
  3. package/dist/src/presentation/components/atoms/FloatingButton/Components/FloatingMenu/index.d.ts +8 -0
  4. package/dist/src/presentation/components/atoms/FloatingButton/Components/FloatingMenu/styles.d.ts +9 -0
  5. package/dist/src/presentation/components/atoms/FloatingButton/index.d.ts +8 -0
  6. package/dist/src/presentation/components/atoms/FloatingButton/styles.d.ts +5 -0
  7. package/dist/src/presentation/components/atoms/Tooltip/index.d.ts +3 -1
  8. package/dist/src/presentation/components/molecules/PopConfirm/index.d.ts +18 -0
  9. package/dist/src/presentation/components/molecules/PopConfirm/styles.d.ts +22 -0
  10. package/dist/src/presentation/components/molecules/SegmentedButton/index.d.ts +11 -0
  11. package/dist/src/presentation/components/molecules/SegmentedButton/styles.d.ts +13 -0
  12. package/package.json +2 -1
  13. package/src/presentation/components/atoms/ActionButton/styles.ts +1 -1
  14. package/src/presentation/components/atoms/Button/Button.stories.tsx +0 -1
  15. package/src/presentation/components/atoms/Button/styles.ts +1 -4
  16. package/src/presentation/components/atoms/FloatingButton/Components/FloatingMenu/index.tsx +29 -0
  17. package/src/presentation/components/atoms/FloatingButton/Components/FloatingMenu/styles.ts +59 -0
  18. package/src/presentation/components/atoms/FloatingButton/FloatingButton.stories.tsx +64 -0
  19. package/src/presentation/components/atoms/FloatingButton/index.tsx +78 -0
  20. package/src/presentation/components/atoms/FloatingButton/styles.ts +22 -0
  21. package/src/presentation/components/atoms/Tooltip/index.tsx +57 -50
  22. package/src/presentation/components/molecules/PopConfirm/PopConfirm.stories.tsx +72 -0
  23. package/src/presentation/components/molecules/PopConfirm/index.tsx +164 -0
  24. package/src/presentation/components/molecules/PopConfirm/styles.ts +126 -0
  25. package/src/presentation/components/molecules/SegmentedButton/SegmentedButton.stories.tsx +78 -0
  26. package/src/presentation/components/molecules/SegmentedButton/index.tsx +47 -0
  27. 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
+ `;