beem-component 2.1.29 → 2.1.31

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 (76) hide show
  1. package/Dockerfile +1 -1
  2. package/Jenkinsfile +20 -5
  3. package/dist/assets/voiceCallIcon.svg +5 -0
  4. package/dist/components/Alert/Alert.js +83 -0
  5. package/dist/components/Alert/Alert.stories.js +66 -0
  6. package/dist/components/BmCustomCardTitle/CustomCardTitle.js +181 -0
  7. package/dist/components/BmCustomCardTitle/CustomCardTitle.stories.js +92 -0
  8. package/dist/components/BmSelector/BmSelector.js +15 -2
  9. package/dist/components/BmSelector/BmSelector.stories.js +14 -1
  10. package/dist/components/BmTabv2/BmTabv2.js +51 -0
  11. package/dist/components/BmTabv2/BmTabv2.stories.js +73 -0
  12. package/dist/components/Card_v2/Card.js +38 -12
  13. package/dist/components/ChatComponents/ChatBody/chatBody.js +402 -104
  14. package/dist/components/DepartmentCard/DepartmentCard.js +99 -0
  15. package/dist/components/DepartmentCard/DepartmentCard.stories.js +53 -0
  16. package/dist/components/HorizontalCard/HorizontalCard.js +142 -0
  17. package/dist/components/HorizontalCard/HorizontalCard.stories.js +40 -0
  18. package/dist/components/InfoPanel/InfoPanel.js +54 -17
  19. package/dist/components/InfoPanel/InfoPanel.stories.js +56 -4
  20. package/dist/components/Modals/modal.js +26 -10
  21. package/dist/components/Modals/modals.stories.js +13 -6
  22. package/dist/components/ProfileIcon/ProfileIcon.js +5 -0
  23. package/dist/components/ResourceCard/ResourceCard.js +132 -0
  24. package/dist/components/ResourceCard/ResourceCard.stories.js +94 -0
  25. package/dist/components/globalStyles.js +1 -1
  26. package/dist/components/index.js +42 -0
  27. package/dist/components/text.js +11 -10
  28. package/dist/components/typography.js +3 -2
  29. package/package.json +2 -1
  30. package/public/index.html +1 -0
  31. package/src/App.js +804 -1412
  32. package/src/fonts/Inter-Black.woff2 +0 -0
  33. package/src/fonts/Inter-Bold.woff2 +0 -0
  34. package/src/fonts/Inter-ExtraBold.woff2 +0 -0
  35. package/src/fonts/Inter-ExtraLight.woff2 +0 -0
  36. package/src/fonts/Inter-Light.woff2 +0 -0
  37. package/src/fonts/Inter-Medium.woff2 +0 -0
  38. package/src/fonts/Inter-Regular.woff2 +0 -0
  39. package/src/fonts/Inter-SemiBold.woff2 +0 -0
  40. package/src/fonts/Inter-Thin.woff2 +0 -0
  41. package/src/fonts/Inter-VariableFont_opsz,wght.ttf +0 -0
  42. package/src/fonts/InterDisplay-Black.woff2 +0 -0
  43. package/src/fonts/InterDisplay-Bold.woff2 +0 -0
  44. package/src/fonts/InterDisplay-ExtraBold.woff2 +0 -0
  45. package/src/fonts/InterDisplay-ExtraLight.woff2 +0 -0
  46. package/src/fonts/InterDisplay-Light.woff2 +0 -0
  47. package/src/fonts/InterDisplay-Medium.woff2 +0 -0
  48. package/src/fonts/InterDisplay-SemiBold.woff2 +0 -0
  49. package/src/fonts/InterDisplay-Thin.woff2 +0 -0
  50. package/src/fonts.scss +4 -1
  51. package/src/lib/assets/voiceCallIcon.svg +5 -0
  52. package/src/lib/components/Alert/Alert.js +111 -0
  53. package/src/lib/components/Alert/Alert.stories.jsx +66 -0
  54. package/src/lib/components/BmCustomCardTitle/CustomCardTitle.js +162 -0
  55. package/src/lib/components/BmCustomCardTitle/CustomCardTitle.stories.jsx +92 -0
  56. package/src/lib/components/BmSelector/BmSelector.js +14 -1
  57. package/src/lib/components/BmSelector/BmSelector.stories.jsx +10 -0
  58. package/src/lib/components/BmTabv2/BmTabv2.js +109 -0
  59. package/src/lib/components/BmTabv2/BmTabv2.stories.jsx +51 -0
  60. package/src/lib/components/Card_v2/Card.js +46 -13
  61. package/src/lib/components/ChatComponents/ChatBody/chatBody.js +551 -57
  62. package/src/lib/components/DepartmentCard/DepartmentCard.js +130 -0
  63. package/src/lib/components/DepartmentCard/DepartmentCard.stories.jsx +38 -0
  64. package/src/lib/components/HorizontalCard/HorizontalCard.js +276 -0
  65. package/src/lib/components/HorizontalCard/HorizontalCard.stories.jsx +33 -0
  66. package/src/lib/components/InfoPanel/InfoPanel.js +35 -11
  67. package/src/lib/components/InfoPanel/InfoPanel.stories.jsx +42 -2
  68. package/src/lib/components/Modals/modal.js +17 -4
  69. package/src/lib/components/Modals/modals.stories.js +10 -6
  70. package/src/lib/components/ProfileIcon/ProfileIcon.js +4 -0
  71. package/src/lib/components/ResourceCard/ResourceCard.js +213 -0
  72. package/src/lib/components/ResourceCard/ResourceCard.stories.jsx +68 -0
  73. package/src/lib/components/globalStyles.js +2 -1
  74. package/src/lib/components/index.js +13 -0
  75. package/src/lib/components/text.js +17 -11
  76. package/src/lib/components/typography.js +1 -0
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/src/fonts.scss CHANGED
@@ -25,4 +25,7 @@
25
25
  @font-face {
26
26
  font-family: 'OpenSans';
27
27
  src: url('../src/fonts/OpenSans-Regular.ttf');
28
- }
28
+ }
29
+
30
+
31
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
@@ -0,0 +1,5 @@
1
+ <svg width="418" height="418" viewBox="0 0 418 418" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <circle cx="209" cy="209" r="209" fill="#33B1BA"/>
3
+ <path d="M128.728 105.326L100.728 122.826C92.4077 128.026 91.4251 141.826 92.2276 152.826C100.728 269.326 177.228 310.326 194.728 323.326C208.728 333.726 221.561 331.326 226.228 328.826L252.728 312.326C265.528 302.726 263.061 286.993 260.228 280.326L244.728 255.326C231.528 237.326 211.561 245.493 203.228 251.826C193.228 258.326 192.228 255.326 186.728 251.826C168.328 235.026 155.394 206.159 151.228 193.826C147.628 187.026 152.728 181.993 155.728 180.326L169.728 170.826C178.928 163.226 179.228 149.326 176.228 141.826C171.894 134.326 161.387 117.426 154.024 109.826C146.661 102.226 134.092 103.659 128.728 105.326Z" fill="white"/>
4
+ <path d="M220 156C236.5 158.5 259.5 170.5 256.5 206M225 121.5C281 132.5 295.5 180.5 290.5 211M230.5 88C300.5 97 333 164.5 324.5 216.5" stroke="white" stroke-width="16" stroke-linecap="round"/>
5
+ </svg>
@@ -0,0 +1,111 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ const hexToRgba = (hex, opacity = 0.6) => {
5
+ try {
6
+ const normalizedHex = hex?.replace('#', '');
7
+ if (
8
+ !normalizedHex ||
9
+ normalizedHex.length !== 6 ||
10
+ !/^[0-9a-fA-F]{6}$/.test(normalizedHex)
11
+ ) {
12
+ throw new Error('Invalid hex');
13
+ }
14
+
15
+ const r = parseInt(normalizedHex.slice(0, 2), 16);
16
+ const g = parseInt(normalizedHex.slice(2, 4), 16);
17
+ const b = parseInt(normalizedHex.slice(4, 6), 16);
18
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
19
+ } catch (e) {
20
+ return `rgba(0, 0, 0, ${opacity})`;
21
+ }
22
+ };
23
+
24
+ const AlertWrapper = styled.div`
25
+ border: 1px solid ${({ themeColor }) => hexToRgba(themeColor, 0.2)};
26
+ background-color: ${({ themeColor }) => hexToRgba(themeColor, 0.05)};
27
+ border-radius: 0.5rem;
28
+ padding: 0.75rem;
29
+
30
+ @media (min-width: 640px) {
31
+ padding: 1rem;
32
+ }
33
+ `;
34
+
35
+ const Content = styled.div`
36
+ display: flex;
37
+ align-items: flex-start;
38
+ gap: 0.75rem;
39
+ `;
40
+
41
+ const IconContainer = styled.div`
42
+ background-color: ${({ themeColor }) => hexToRgba(themeColor, 0.1)};
43
+ border-radius: 9999px;
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: center;
47
+ width: 2rem;
48
+ height: 2rem;
49
+ margin-top: 0.125rem;
50
+
51
+ @media (min-width: 640px) {
52
+ width: 2.1rem;
53
+ height: 2.1rem;
54
+ }
55
+
56
+ svg {
57
+ width: 1rem;
58
+ height: 1rem;
59
+ color: ${({ themeColor }) => themeColor};
60
+
61
+ @media (min-width: 640px) {
62
+ width: 1.25rem;
63
+ height: 1.25rem;
64
+ }
65
+ }
66
+ `;
67
+
68
+ const TextContent = styled.div`
69
+ flex: 1;
70
+ `;
71
+
72
+ const Title = styled.h3`
73
+ font-weight: 500;
74
+ color: ${({ themeColor }) => themeColor};
75
+ font-size: 0.875rem;
76
+ margin-bottom: 0.25rem;
77
+ `;
78
+
79
+ const Description = styled.p`
80
+ color: #6b7280;
81
+ font-size: 0.75rem;
82
+
83
+ @media (min-width: 640px) {
84
+ font-size: 0.875rem;
85
+ }
86
+ `;
87
+
88
+ const BmAlertBox = ({
89
+ icon: Icon,
90
+ themeColor = '#EF4444',
91
+ title,
92
+ description,
93
+ }) => {
94
+ return (
95
+ <AlertWrapper themeColor={themeColor}>
96
+ <Content>
97
+ {Icon && (
98
+ <IconContainer themeColor={themeColor}>
99
+ <Icon />
100
+ </IconContainer>
101
+ )}
102
+ <TextContent>
103
+ <Title themeColor={themeColor}>{title}</Title>
104
+ <Description>{description}</Description>
105
+ </TextContent>
106
+ </Content>
107
+ </AlertWrapper>
108
+ );
109
+ };
110
+
111
+ export default BmAlertBox;
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import AccessTimeIcon from '@mui/icons-material/AccessTime';
3
+ import PersonIcon from '@mui/icons-material/Person';
4
+ import CalendarTodayOutlinedIcon from '@mui/icons-material/CalendarTodayOutlined';
5
+ import BmAlertBox from './Alert';
6
+
7
+ const iconOptions = {
8
+ AccessTimeIcon,
9
+ PersonIcon,
10
+ CalendarTodayOutlinedIcon,
11
+ None: null,
12
+ };
13
+
14
+ const Template = (args) => {
15
+ return (
16
+ <div style={{ maxWidth: 500, margin: '2rem auto' }}>
17
+ <BmAlertBox {...args} />
18
+ </div>
19
+ );
20
+ };
21
+
22
+ export const Default = Template.bind({});
23
+ Default.args = {
24
+ icon: AccessTimeIcon,
25
+ themeColor: '#EF4444',
26
+ title: 'Submission Failed',
27
+ description:
28
+ 'There was an issue processing your request. Please try again later.',
29
+ };
30
+
31
+ Default.argTypes = {
32
+ icon: {
33
+ control: {
34
+ type: 'select',
35
+ labels: Object.keys(iconOptions),
36
+ },
37
+ options: Object.keys(iconOptions),
38
+ mapping: iconOptions,
39
+ },
40
+ themeColor: {
41
+ control: 'color',
42
+ },
43
+ title: {
44
+ control: 'text',
45
+ },
46
+ description: {
47
+ control: 'text',
48
+ },
49
+ };
50
+
51
+ export default {
52
+ title: 'Components/BmAlertBox',
53
+ component: BmAlertBox,
54
+ argTypes: Default.argTypes,
55
+ };
56
+
57
+ export const Example = () => {
58
+ return (
59
+ <BmAlertBox
60
+ icon={AccessTimeIcon}
61
+ themeColor="#DC2626"
62
+ title="Payment Error"
63
+ description="We couldn't process your payment. Please try again later or contact support."
64
+ />
65
+ );
66
+ };
@@ -0,0 +1,162 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ const hexToRgba = (hex, opacity = 0.6) => {
5
+ try {
6
+ const normalizedHex = hex?.replace('#', '');
7
+ if (
8
+ !normalizedHex ||
9
+ normalizedHex.length !== 6 ||
10
+ !/^[0-9a-fA-F]{6}$/.test(normalizedHex)
11
+ ) {
12
+ throw new Error('Invalid hex');
13
+ }
14
+
15
+ const r = parseInt(normalizedHex.slice(0, 2), 16);
16
+ const g = parseInt(normalizedHex.slice(2, 4), 16);
17
+ const b = parseInt(normalizedHex.slice(4, 6), 16);
18
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
19
+ } catch (e) {
20
+ return `rgba(0, 0, 0, ${opacity})`;
21
+ }
22
+ };
23
+
24
+ const BackgroundStripe = styled.div`
25
+ background-color: ${({ themeColor }) => hexToRgba(themeColor, 0.1)};
26
+ padding-top: ${({ variant }) =>
27
+ variant === 'confirmation' ? '1rem' : '1.5rem'};
28
+ padding-bottom: ${({ variant }) =>
29
+ variant === 'confirmation' ? '1rem' : '2rem'};
30
+ padding-left: 1rem;
31
+ padding-right: 1rem;
32
+
33
+ @media (min-width: 640px) {
34
+ padding-top: ${({ variant }) =>
35
+ variant === 'confirmation' ? '1.5rem' : '2rem'};
36
+ padding-bottom: ${({ variant }) =>
37
+ variant === 'confirmation' ? '1.5rem' : '2.5rem'};
38
+ padding-left: 1.5rem;
39
+ padding-right: 1.5rem;
40
+ }
41
+ `;
42
+
43
+ const CardContainer = styled.div`
44
+ background-color: ${hexToRgba('#ffffff', 0.7)};
45
+ padding: 1rem;
46
+ border-radius: 0.75rem;
47
+
48
+ @media (min-width: 640px) {
49
+ padding: 1.5rem;
50
+ }
51
+ `;
52
+
53
+ const IconWrapper = styled.div`
54
+ background-color: ${({ themeColor }) => hexToRgba(themeColor, 0.1)};
55
+ height: ${({ variant }) => (variant === 'confirmation' ? '3.5rem' : '4rem')};
56
+ width: ${({ variant }) => (variant === 'confirmation' ? '3.5rem' : '4rem')};
57
+ border-radius: 9999px;
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ margin: 0 auto
62
+ ${({ variant }) => (variant === 'confirmation' ? '0.75rem' : '1rem')};
63
+
64
+ @media (min-width: 640px) {
65
+ height: ${({ variant }) => (variant === 'confirmation' ? '4rem' : '5rem')};
66
+ width: ${({ variant }) => (variant === 'confirmation' ? '4rem' : '5rem')};
67
+ margin-bottom: ${({ variant }) =>
68
+ variant === 'confirmation' ? '1rem' : '1.25rem'};
69
+ }
70
+
71
+ svg {
72
+ height: ${({ variant }) =>
73
+ variant === 'confirmation' ? '1.75rem' : '2rem'};
74
+ width: ${({ variant }) =>
75
+ variant === 'confirmation' ? '1.75rem' : '2rem'};
76
+ color: ${({ themeColor }) => themeColor};
77
+
78
+ @media (min-width: 640px) {
79
+ height: ${({ variant }) =>
80
+ variant === 'confirmation' ? '2rem' : '2.5rem'};
81
+ width: ${({ variant }) =>
82
+ variant === 'confirmation' ? '2rem' : '2.5rem'};
83
+ }
84
+ }
85
+ `;
86
+
87
+ const Title = styled.h2`
88
+ text-align: center;
89
+ font-size: 1.125rem;
90
+ color: ${({ themeColor }) => themeColor};
91
+ margin-bottom: ${({ variant }) =>
92
+ variant === 'confirmation' ? '0.25rem' : '0.5rem'};
93
+
94
+ @media (min-width: 640px) {
95
+ font-size: 1.25rem;
96
+ margin-bottom: ${({ variant }) =>
97
+ variant === 'confirmation' ? '0.5rem' : '0.75rem'};
98
+ }
99
+ `;
100
+
101
+ const Description = styled.p`
102
+ text-align: center;
103
+ font-size: ${({ variant }) =>
104
+ variant === 'confirmation' ? '0.75rem' : '0.875rem'};
105
+ color: #6b7280;
106
+
107
+ @media (min-width: 640px) {
108
+ font-size: ${({ variant }) =>
109
+ variant === 'confirmation' ? '0.875rem' : '1rem'};
110
+ }
111
+ `;
112
+
113
+ const ImageWrapper = styled.div`
114
+ width: 100%;
115
+ aspect-ratio: 1 / 1;
116
+ overflow: hidden;
117
+ border-radius: 0.75rem;
118
+ margin-top: 1rem;
119
+
120
+ img {
121
+ width: 100%;
122
+ height: 100%;
123
+ object-fit: cover;
124
+ display: block;
125
+ }
126
+ `;
127
+ const BmCustomCardTitle = ({
128
+ icon: Icon,
129
+ themeColor = '#33B1BA',
130
+ title,
131
+ description,
132
+ variant = 'booking',
133
+ withStripe = false,
134
+ imageSrc,
135
+ }) => {
136
+ const content = (
137
+ <CardContainer variant={variant}>
138
+ <IconWrapper themeColor={themeColor} variant={variant}>
139
+ {Icon && <Icon />}
140
+ </IconWrapper>
141
+ {title && <Title variant={variant}>{title}</Title>}
142
+ {description && (
143
+ <Description variant={variant}>{description}</Description>
144
+ )}
145
+ {imageSrc && (
146
+ <ImageWrapper>
147
+ <img src={imageSrc} alt={title || 'Card image'} />
148
+ </ImageWrapper>
149
+ )}
150
+ </CardContainer>
151
+ );
152
+
153
+ return withStripe ? (
154
+ <BackgroundStripe themeColor={themeColor} variant={variant}>
155
+ {content}
156
+ </BackgroundStripe>
157
+ ) : (
158
+ content
159
+ );
160
+ };
161
+
162
+ export default BmCustomCardTitle;
@@ -0,0 +1,92 @@
1
+ import React from 'react';
2
+
3
+ // MUI icons
4
+ import CalendarTodayOutlinedIcon from '@mui/icons-material/CalendarTodayOutlined';
5
+ import CheckIcon from '@mui/icons-material/Check';
6
+ import AccessTimeIcon from '@mui/icons-material/AccessTime';
7
+ import FavoriteIcon from '@mui/icons-material/Favorite';
8
+ import BmCustomCardTitle from './CustomCardTitle';
9
+
10
+ // Icon options mapped by name
11
+ const iconOptions = {
12
+ Calendar: CalendarTodayOutlinedIcon,
13
+ Check: CheckIcon,
14
+ Clock: AccessTimeIcon,
15
+ Heart: FavoriteIcon,
16
+ };
17
+
18
+ const imgs = 'https://i.imgur.com/HiAzUHl.jpeg';
19
+ const Template = (args) => {
20
+ return (
21
+ <div style={{ maxWidth: 500, margin: '2rem auto' }}>
22
+ <BmCustomCardTitle {...args} />
23
+ </div>
24
+ );
25
+ };
26
+
27
+ export const Default = Template.bind({});
28
+ Default.args = {
29
+ icon: CalendarTodayOutlinedIcon,
30
+ themeColor: '#33B1BA',
31
+ title: 'Book Your Medical Appointment',
32
+ description:
33
+ 'Schedule a 30-minute consultation with our healthcare specialists',
34
+ variant: 'booking',
35
+ withStripe: true,
36
+ imageSrc: imgs,
37
+ };
38
+
39
+ Default.argTypes = {
40
+ icon: {
41
+ control: {
42
+ type: 'select',
43
+ labels: Object.keys(iconOptions),
44
+ },
45
+ options: Object.keys(iconOptions),
46
+ mapping: iconOptions,
47
+ },
48
+ themeColor: {
49
+ control: 'color',
50
+ },
51
+ title: {
52
+ control: 'text',
53
+ },
54
+ description: {
55
+ control: 'text',
56
+ },
57
+ variant: {
58
+ control: {
59
+ type: 'radio',
60
+ },
61
+ options: ['booking', 'confirmation'],
62
+ },
63
+ withStripe: {
64
+ control: {
65
+ type: 'boolean',
66
+ },
67
+ },
68
+ imageSrc: {
69
+ control: 'text',
70
+ description: 'URL of the image to display in the card',
71
+ },
72
+ };
73
+
74
+ export default {
75
+ title: 'Components/BmCustomCardTitle',
76
+ component: BmCustomCardTitle,
77
+ argTypes: Default.argTypes,
78
+ };
79
+
80
+ export const Example = () => {
81
+ return (
82
+ <BmCustomCardTitle
83
+ icon={CalendarTodayOutlinedIcon}
84
+ themeColor="#33B1BA"
85
+ title="Book Your Medical Appointment"
86
+ description="Schedule a 30-minute consultation with our healthcare specialists"
87
+ variant="booking"
88
+ withStripe
89
+ imageSrc={imgs}
90
+ />
91
+ );
92
+ };
@@ -120,11 +120,24 @@ const BmSelector = ({
120
120
  textColor,
121
121
  fontWeight,
122
122
  name = 'selection',
123
+ useFormik = false,
123
124
  }) => {
124
125
  return (
125
126
  <RadioGroupWrapper>
126
127
  {data.map((item) => {
127
128
  const isSelected = selectedDataId === item.id;
129
+ const handleChange = () => {
130
+ if (useFormik) {
131
+ onChange({
132
+ target: {
133
+ name,
134
+ value: item.id,
135
+ },
136
+ });
137
+ } else {
138
+ onChange(item.id);
139
+ }
140
+ };
128
141
 
129
142
  return (
130
143
  <label key={item.id} htmlFor={item.id}>
@@ -133,7 +146,7 @@ const BmSelector = ({
133
146
  name={name}
134
147
  value={item.id}
135
148
  checked={isSelected}
136
- onChange={() => onChange(item.id)}
149
+ onChange={handleChange}
137
150
  />
138
151
  <TimeSlotCard selected={isSelected} themeColor={themeColor}>
139
152
  <CustomRadio selected={isSelected} themeColor={themeColor} />
@@ -44,6 +44,10 @@ export default {
44
44
  },
45
45
  ],
46
46
  },
47
+ useFormik: {
48
+ control: { type: 'boolean' },
49
+ defaultValue: false,
50
+ },
47
51
  },
48
52
  };
49
53
 
@@ -89,6 +93,7 @@ Default.args = {
89
93
  value: { startDate: '09:00 AM', endDate: '09:30 AM' },
90
94
  },
91
95
  ],
96
+ useFormik: false,
92
97
 
93
98
  themeColor: '#33B1BA',
94
99
  textColor: '#33B1BA',
@@ -110,9 +115,13 @@ export const Example = () => {
110
115
  },
111
116
  ];
112
117
 
118
+ // When you use useFormik as true then you need to pass handleChange func and the name [name="time"].
119
+ // so that is works accordingly
120
+
113
121
  const [selectedDataId, setSelectedDataId] = useState(null);
114
122
  return (
115
123
  <BmSelector
124
+ name="time"
116
125
  data={slots}
117
126
  selectedDataId={selectedDataId}
118
127
  onChange={setSelectedDataId}
@@ -120,6 +129,7 @@ export const Example = () => {
120
129
  themeColor="#33B1BA"
121
130
  textColor="#33B1BA"
122
131
  fontWeight="600"
132
+ useFormik="false"
123
133
  />
124
134
  );
125
135
  };
@@ -0,0 +1,109 @@
1
+ import React from 'react';
2
+ import styled, { css } from 'styled-components';
3
+
4
+ const TabsContainer = styled.div`
5
+ width: 100%;
6
+ `;
7
+
8
+ const TabsHeader = styled.div`
9
+ display: flex;
10
+ justify-content: flex-start;
11
+ align-items: center;
12
+ margin-bottom: 14px;
13
+ padding: 0 14px;
14
+ border-radius: var(--radius-lg, 12px);
15
+ flex-wrap: wrap;
16
+ `;
17
+
18
+ const TabsList = styled.div`
19
+ width: 100%;
20
+ display: inline-flex;
21
+ background-color: var(--color-muted, #f2f2f2);
22
+ color: var(--color-muted-foreground, #666);
23
+ align-items: center;
24
+ justify-content: center;
25
+ border-radius: var(--radius-lg, 12px);
26
+ padding: 4px 4px;
27
+ `;
28
+
29
+ const TabsTriggerButton = styled.button`
30
+ display: inline-flex;
31
+ flex: 1;
32
+ align-items: center;
33
+ justify-content: center;
34
+ gap: 6px;
35
+ border: 1px solid transparent;
36
+ border-radius: var(--radius-lg, 12px);
37
+ padding: 6px 12px;
38
+ font-size: 14px;
39
+ font-weight: 500;
40
+ white-space: nowrap;
41
+ transition: color 0.2s, box-shadow 0.2s, background-color 0.2s;
42
+ cursor: pointer;
43
+
44
+ ${({ active }) =>
45
+ active
46
+ ? css`
47
+ background-color: var(--color-card, #fff);
48
+ color: var(--color-foreground, #000);
49
+ border-color: var(--color-border, #ddd);
50
+ `
51
+ : css`
52
+ background-color: transparent;
53
+ color: var(--color-muted-foreground, #666);
54
+ `}
55
+
56
+ &:focus-visible {
57
+ outline: none;
58
+ border-color: var(--color-ring, #2684ff);
59
+ box-shadow: 0 0 0 3px rgba(38, 132, 255, 0.3);
60
+ }
61
+
62
+ &:disabled {
63
+ pointer-events: none;
64
+ opacity: 0.5;
65
+ }
66
+
67
+ svg {
68
+ flex-shrink: 0;
69
+ pointer-events: none;
70
+ width: 16px;
71
+ height: 16px;
72
+ }
73
+ `;
74
+
75
+ const IconWrapper = styled.span`
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: center;
79
+ `;
80
+
81
+ const BmCustomTab = ({ value, onValueChange, tabs = [], className }) => {
82
+ return (
83
+ <TabsContainer className={className}>
84
+ <TabsHeader>
85
+ <TabsList>
86
+ {tabs.map((tab) => {
87
+ const Icon = tab.icon;
88
+ return (
89
+ <TabsTriggerButton
90
+ key={tab.value}
91
+ active={value === tab.value}
92
+ onClick={() => onValueChange(tab.value)}
93
+ >
94
+ {Icon && (
95
+ <IconWrapper>
96
+ <Icon fontSize="small" />
97
+ </IconWrapper>
98
+ )}
99
+ <span>{tab.label}</span>
100
+ </TabsTriggerButton>
101
+ );
102
+ })}
103
+ </TabsList>
104
+ </TabsHeader>
105
+ </TabsContainer>
106
+ );
107
+ };
108
+
109
+ export default BmCustomTab;