@altruist03/ui 0.0.0-stage → 0.2.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 (205) hide show
  1. package/assets/images/noData.webp +0 -0
  2. package/dist/index.js +15318 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/noData-XPRHKMFR.webp +0 -0
  5. package/package.json +51 -4
  6. package/src/ThemeProvider/ThemeContext.tsx +75 -0
  7. package/src/ThemeProvider/index.ts +7 -0
  8. package/src/assets/images/noData.webp +0 -0
  9. package/src/assets.d.ts +10 -0
  10. package/src/components/Accordion/Accordion.stories.tsx +64 -0
  11. package/src/components/Accordion/Accordion.tsx +219 -0
  12. package/src/components/Accordion/Accordion.types.ts +16 -0
  13. package/src/components/Avatar/Avatar.stories.tsx +304 -0
  14. package/src/components/Avatar/Avatar.tsx +211 -0
  15. package/src/components/Avatar/Avatar.types.ts +33 -0
  16. package/src/components/Badge/Badge.stories.tsx +143 -0
  17. package/src/components/Badge/Badge.tsx +138 -0
  18. package/src/components/Badge/Badge.types.ts +24 -0
  19. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +114 -0
  20. package/src/components/Breadcrumb/Breadcrumb.tsx +120 -0
  21. package/src/components/Breadcrumb/Breadcrumb.types.ts +22 -0
  22. package/src/components/Button/Button.stories.tsx +457 -0
  23. package/src/components/Button/Button.tsx +202 -0
  24. package/src/components/Button/Button.types.ts +18 -0
  25. package/src/components/Checkbox/Checkbox.stories.tsx +254 -0
  26. package/src/components/Checkbox/Checkbox.tsx +352 -0
  27. package/src/components/Checkbox/Checkbox.types.ts +123 -0
  28. package/src/components/CloseButton/CloseButton.stories.tsx +100 -0
  29. package/src/components/CloseButton/CloseButton.tsx +117 -0
  30. package/src/components/CloseButton/CloseButton.types.ts +15 -0
  31. package/src/components/CollapsibleSearch/CollapsibleSearch.tsx +120 -0
  32. package/src/components/DataTable/DataTable.stories.tsx +233 -0
  33. package/src/components/DataTable/DataTable.tsx +968 -0
  34. package/src/components/DataTable/DataTable.types.ts +49 -0
  35. package/src/components/DatePicker/DatePicker.stories.tsx +341 -0
  36. package/src/components/DatePicker/DatePicker.tsx +669 -0
  37. package/src/components/DatePicker/DatePicker.types.ts +105 -0
  38. package/src/components/DatePicker/DateTimePicker.tsx +35 -0
  39. package/src/components/DateRangePicker/DateRangePicker.tsx +179 -0
  40. package/src/components/DateRangePicker/DateRangePicker.types.ts +34 -0
  41. package/src/components/Drawer/Drawer.stories.tsx +243 -0
  42. package/src/components/Drawer/Drawer.tsx +196 -0
  43. package/src/components/Drawer/Drawer.types.ts +60 -0
  44. package/src/components/FileUpload/FileUpload.stories.tsx +162 -0
  45. package/src/components/FileUpload/FileUpload.tsx +216 -0
  46. package/src/components/FileUpload/FileUpload.types.ts +80 -0
  47. package/src/components/FilterPopover/FilterPopover.stories.tsx +173 -0
  48. package/src/components/FilterPopover/FilterPopover.tsx +310 -0
  49. package/src/components/FilterPopover/FilterPopover.types.ts +26 -0
  50. package/src/components/ImageViewer/ImageViewerToolbar.jsx +81 -0
  51. package/src/components/ImageViewer/useImagePanRotate.js +102 -0
  52. package/src/components/IpWhitelistField/IpWhitelistField.stories.tsx +138 -0
  53. package/src/components/IpWhitelistField/IpWhitelistField.tsx +164 -0
  54. package/src/components/IpWhitelistField/IpWhitelistField.types.ts +42 -0
  55. package/src/components/Loader/Loader.stories.tsx +95 -0
  56. package/src/components/Loader/Loader.tsx +132 -0
  57. package/src/components/Loader/Loader.types.ts +6 -0
  58. package/src/components/Menu/Menu.tsx +143 -0
  59. package/src/components/Menu/Menu.types.ts +28 -0
  60. package/src/components/MenuButton/MenuButton.stories.tsx +140 -0
  61. package/src/components/MenuButton/MenuButton.tsx +45 -0
  62. package/src/components/MenuButton/MenuButton.types.ts +21 -0
  63. package/src/components/Modal/Modal.stories.tsx +266 -0
  64. package/src/components/Modal/Modal.tsx +399 -0
  65. package/src/components/Modal/index.ts +2 -0
  66. package/src/components/Modal/types.ts +126 -0
  67. package/src/components/Pagination/Pagination.stories.tsx +167 -0
  68. package/src/components/Pagination/Pagination.tsx +266 -0
  69. package/src/components/Pagination/Pagination.types.ts +20 -0
  70. package/src/components/PeriodSelector/PeriodSelector.stories.tsx +50 -0
  71. package/src/components/PeriodSelector/PeriodSelector.tsx +47 -0
  72. package/src/components/PeriodSelector/PeriodSelector.types.ts +10 -0
  73. package/src/components/PhoneField/PhoneField.stories.tsx +141 -0
  74. package/src/components/PhoneField/PhoneField.tsx +284 -0
  75. package/src/components/PhoneField/PhoneField.types.ts +89 -0
  76. package/src/components/PhoneField/countryCodes.ts +236 -0
  77. package/src/components/Progress/Progress.stories.tsx +83 -0
  78. package/src/components/Progress/Progress.tsx +91 -0
  79. package/src/components/Progress/Progress.types.ts +22 -0
  80. package/src/components/RadioButton/RadioButton.stories.tsx +332 -0
  81. package/src/components/RadioButton/RadioButton.tsx +427 -0
  82. package/src/components/RadioButton/RadioButton.types.ts +117 -0
  83. package/src/components/ReportCard/ReportCard.tsx +183 -0
  84. package/src/components/ReportCard/ReportCard.types.ts +17 -0
  85. package/src/components/SectionIcon/SectionIcon.tsx +87 -0
  86. package/src/components/SectionIcon/SectionIcon.types.ts +23 -0
  87. package/src/components/SegmentedInputField/SegmentedInputField.stories.tsx +165 -0
  88. package/src/components/SegmentedInputField/SegmentedInputField.tsx +174 -0
  89. package/src/components/SegmentedInputField/SegmentedInputField.types.ts +51 -0
  90. package/src/components/SelectScroll/SelectScroll.stories.tsx +336 -0
  91. package/src/components/SelectScroll/SelectScroll.tsx +1119 -0
  92. package/src/components/SelectScroll/SelectScroll.types.ts +60 -0
  93. package/src/components/Selector/Selector.stories.tsx +382 -0
  94. package/src/components/Selector/Selector.tsx +1310 -0
  95. package/src/components/Selector/Selector.types.ts +71 -0
  96. package/src/components/Skeleton/Skeleton.stories.tsx +133 -0
  97. package/src/components/Skeleton/Skeleton.tsx +20 -0
  98. package/src/components/Skeleton/Skeleton.types.ts +3 -0
  99. package/src/components/Spinner/Spinner.stories.tsx +84 -0
  100. package/src/components/Spinner/Spinner.tsx +79 -0
  101. package/src/components/Spinner/Spinner.types.ts +8 -0
  102. package/src/components/StatCard/StatCard.stories.tsx +125 -0
  103. package/src/components/StatCard/StatCard.tsx +167 -0
  104. package/src/components/StatCard/StatCard.types.ts +20 -0
  105. package/src/components/StatusBadge/StatusBadge.stories.tsx +179 -0
  106. package/src/components/StatusBadge/StatusBadge.tsx +74 -0
  107. package/src/components/StatusBadge/StatusBadge.types.ts +23 -0
  108. package/src/components/Stepper/Stepper.stories.tsx +85 -0
  109. package/src/components/Stepper/Stepper.tsx +138 -0
  110. package/src/components/Stepper/Stepper.types.ts +16 -0
  111. package/src/components/SubTabs/SubTabs.stories.tsx +128 -0
  112. package/src/components/SubTabs/SubTabs.tsx +279 -0
  113. package/src/components/SubTabs/SubTabs.types.ts +44 -0
  114. package/src/components/SummaryCard/SummaryCard.stories.tsx +100 -0
  115. package/src/components/SummaryCard/SummaryCard.tsx +141 -0
  116. package/src/components/SummaryCard/SummaryCard.types.ts +22 -0
  117. package/src/components/Switch/Switch.stories.tsx +59 -0
  118. package/src/components/Switch/Switch.tsx +217 -0
  119. package/src/components/Switch/Switch.types.ts +22 -0
  120. package/src/components/Tabs/Tabs.stories.tsx +178 -0
  121. package/src/components/Tabs/Tabs.tsx +197 -0
  122. package/src/components/Tabs/Tabs.types.ts +34 -0
  123. package/src/components/TextField/TextField.stories.tsx +249 -0
  124. package/src/components/TextField/TextField.tsx +462 -0
  125. package/src/components/TextField/TextField.types.ts +141 -0
  126. package/src/components/Toast/Toast.stories.tsx +237 -0
  127. package/src/components/Toast/Toast.tsx +246 -0
  128. package/src/components/Toast/Toast.types.ts +31 -0
  129. package/src/components/ToggleButton/ToggleButton.stories.tsx +105 -0
  130. package/src/components/ToggleButton/ToggleButton.tsx +267 -0
  131. package/src/components/ToggleButton/ToggleButton.types.ts +19 -0
  132. package/src/components/UploadDropzone/UploadDropzone.jsx +78 -0
  133. package/src/components/UploadDropzone2/UploadDropzone.stories.tsx +94 -0
  134. package/src/components/UploadDropzone2/UploadDropzone.tsx +222 -0
  135. package/src/components/UploadDropzone2/UploadDropzone.types.ts +54 -0
  136. package/src/components/ViewEditToggle/ViewEditToggle.stories.tsx +40 -0
  137. package/src/components/ViewEditToggle/ViewEditToggle.tsx +239 -0
  138. package/src/components/ViewEditToggle/ViewEditToggle.types.ts +27 -0
  139. package/src/components/index.ts +157 -0
  140. package/src/icons/ReportDollarIcon.tsx +71 -0
  141. package/src/icons/index.ts +27 -0
  142. package/src/index.ts +214 -0
  143. package/src/storybook/preview.tsx +64 -0
  144. package/src/theme/index.ts +4 -0
  145. package/src/theme/types.d.ts +1 -0
  146. package/src/themes/cpc.ts +4 -0
  147. package/src/themes/firebird.ts +4 -0
  148. package/src/tokens/brands/cpc/themes/dark.json +82 -0
  149. package/src/tokens/brands/cpc/themes/light.json +1 -0
  150. package/src/tokens/brands/default/brand.json +20 -0
  151. package/src/tokens/brands/firebird/themes/dark.json +28 -0
  152. package/src/tokens/brands/firebird/themes/light.json +20 -0
  153. package/src/tokens/button.ts +24 -0
  154. package/src/tokens/components/accordion.json +52 -0
  155. package/src/tokens/components/avatar.json +40 -0
  156. package/src/tokens/components/badge.json +41 -0
  157. package/src/tokens/components/breadcrumb.json +13 -0
  158. package/src/tokens/components/button.json +67 -0
  159. package/src/tokens/components/card.json +39 -0
  160. package/src/tokens/components/checkbox.json +64 -0
  161. package/src/tokens/components/closebutton.json +55 -0
  162. package/src/tokens/components/datatable.json +40 -0
  163. package/src/tokens/components/datepicker.json +99 -0
  164. package/src/tokens/components/drawer.json +23 -0
  165. package/src/tokens/components/field.json +29 -0
  166. package/src/tokens/components/fileupload.json +57 -0
  167. package/src/tokens/components/filterpopover.json +29 -0
  168. package/src/tokens/components/input.json +31 -0
  169. package/src/tokens/components/ipwhitelistfield.json +16 -0
  170. package/src/tokens/components/loader.json +15 -0
  171. package/src/tokens/components/menu.json +21 -0
  172. package/src/tokens/components/modal.json +25 -0
  173. package/src/tokens/components/pagination.json +45 -0
  174. package/src/tokens/components/phonefield.json +41 -0
  175. package/src/tokens/components/progress.json +12 -0
  176. package/src/tokens/components/radio.json +87 -0
  177. package/src/tokens/components/reportcard.json +17 -0
  178. package/src/tokens/components/selector.json +87 -0
  179. package/src/tokens/components/selectscroll.json +65 -0
  180. package/src/tokens/components/skeleton.json +6 -0
  181. package/src/tokens/components/stepper.json +38 -0
  182. package/src/tokens/components/subtabs.json +45 -0
  183. package/src/tokens/components/switch.json +55 -0
  184. package/src/tokens/components/tabs.json +38 -0
  185. package/src/tokens/components/textfield.json +59 -0
  186. package/src/tokens/components/togglebutton.json +48 -0
  187. package/src/tokens/components/uploaddropzone.json +61 -0
  188. package/src/tokens/drawer.ts +37 -0
  189. package/src/tokens/foundation/breakpoints.json +10 -0
  190. package/src/tokens/foundation/colors.json +122 -0
  191. package/src/tokens/foundation/opacity.json +16 -0
  192. package/src/tokens/foundation/radius.json +14 -0
  193. package/src/tokens/foundation/size.json +38 -0
  194. package/src/tokens/foundation/spacing.json +19 -0
  195. package/src/tokens/foundation/typography.json +43 -0
  196. package/src/tokens/index.ts +3665 -0
  197. package/src/tokens/modal.ts +42 -0
  198. package/src/tokens/semantic/color.json +148 -0
  199. package/src/tokens/semantic/field.json +28 -0
  200. package/src/tokens/semantic/opacity.json +5 -0
  201. package/src/tokens/semantic/size.json +722 -0
  202. package/src/tokens/semantic/spacing.json +27 -0
  203. package/src/tokens/semantic/typography.json +87 -0
  204. package/src/utils/scrollLock.ts +44 -0
  205. package/README.md +0 -3
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface StatCardProps {
4
+ /** Short label above the value (for example from an API `title` or `status` field). */
5
+ label: ReactNode;
6
+ /** Optional helper note shown beside the label, useful for a longer API subtitle. */
7
+ infoNote?: string;
8
+ /** Primary metric (number or formatted string from an API). */
9
+ value: ReactNode;
10
+ /** Accent color for the icon (any valid CSS color string from an API). */
11
+ accentColor: string;
12
+ /** Optional background behind the icon tile (for example from an API card background). */
13
+ iconBackgroundColor?: string;
14
+ /** Icon rendered inside the leading tile (SVG, image, or component using `currentColor` where possible). */
15
+ icon: ReactNode;
16
+ /** Optional accessible name; when omitted, a default is derived when `label` and `value` are strings. */
17
+ ariaLabel?: string;
18
+ className?: string;
19
+ style?: React.CSSProperties;
20
+ }
@@ -0,0 +1,179 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { StatusBadge } from './StatusBadge';
4
+ import type { StatusBadgeProps } from './StatusBadge.types';
5
+
6
+ const HEX_COLORS = [
7
+ { hex: '#9CA3AF', label: 'Grey' },
8
+ { hex: '#F87171', label: 'Red' },
9
+ { hex: '#FBBF24', label: 'Orange' },
10
+ { hex: '#34D399', label: 'Green' },
11
+ { hex: '#22D3EE', label: 'Cyan' },
12
+ { hex: '#60A5FA', label: 'Blue' },
13
+ { hex: '#A78BFA', label: 'Purple' },
14
+ { hex: '#F472B6', label: 'Pink' },
15
+ ];
16
+
17
+ const meta: Meta<typeof StatusBadge> = {
18
+ title: 'Design System/StatusBadge',
19
+ component: StatusBadge,
20
+ tags: [],
21
+ args: {
22
+ children: 'Badge',
23
+ color: '#34D399',
24
+ size: 'md',
25
+ variant: 'outline',
26
+ showDot: false,
27
+ } as StatusBadgeProps,
28
+ argTypes: {
29
+ color: {
30
+ control: { type: 'text' },
31
+ description: 'Any CSS hex color e.g. #34D399',
32
+ },
33
+ size: {
34
+ control: { type: 'radio' },
35
+ options: ['sm', 'md', 'lg'],
36
+ },
37
+ variant: {
38
+ control: { type: 'radio' },
39
+ options: ['outline', 'filled'],
40
+ description: '"outline" = transparent background, "filled" = subtle tinted background',
41
+ },
42
+ children: {
43
+ control: { type: 'text' },
44
+ },
45
+ showDot: {
46
+ control: { type: 'boolean' },
47
+ },
48
+ },
49
+ };
50
+
51
+ export default meta;
52
+
53
+ type Story = StoryObj<typeof StatusBadge>;
54
+
55
+ export const Playground: Story = {
56
+ args: {
57
+ children: 'Badge',
58
+ color: '#34D399',
59
+ size: 'md',
60
+ variant: 'outline',
61
+ showDot: false,
62
+ },
63
+ };
64
+
65
+ export const Showcase: Story = {
66
+ render: () => (
67
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '32px', padding: '24px' }}>
68
+ {/* Outline variant */}
69
+ <Section label="Outline (default)">
70
+ <Row>
71
+ {HEX_COLORS.map(({ hex, label }) => (
72
+ <StatusBadge key={hex} color={hex} variant="outline">
73
+ {label}
74
+ </StatusBadge>
75
+ ))}
76
+ </Row>
77
+ <Row>
78
+ {HEX_COLORS.map(({ hex, label }) => (
79
+ <StatusBadge key={hex} color={hex} variant="outline" showDot>
80
+ {label}
81
+ </StatusBadge>
82
+ ))}
83
+ </Row>
84
+ </Section>
85
+
86
+ {/* Filled variant */}
87
+ <Section label="Filled">
88
+ <Row>
89
+ {HEX_COLORS.map(({ hex, label }) => (
90
+ <StatusBadge key={hex} color={hex} variant="filled">
91
+ {label}
92
+ </StatusBadge>
93
+ ))}
94
+ </Row>
95
+ <Row>
96
+ {HEX_COLORS.map(({ hex, label }) => (
97
+ <StatusBadge key={hex} color={hex} variant="filled" showDot>
98
+ {label}
99
+ </StatusBadge>
100
+ ))}
101
+ </Row>
102
+ </Section>
103
+
104
+ {/* Size variants */}
105
+ <Section label="Sizes">
106
+ <Row>
107
+ <StatusBadge color="#34D399" variant="outline" size="sm">
108
+ Small
109
+ </StatusBadge>
110
+ <StatusBadge color="#34D399" variant="outline" size="md">
111
+ Medium
112
+ </StatusBadge>
113
+ <StatusBadge color="#34D399" variant="outline" size="lg">
114
+ Large
115
+ </StatusBadge>
116
+ </Row>
117
+ <Row>
118
+ <StatusBadge color="#34D399" variant="filled" size="sm">
119
+ Small
120
+ </StatusBadge>
121
+ <StatusBadge color="#34D399" variant="filled" size="md">
122
+ Medium
123
+ </StatusBadge>
124
+ <StatusBadge color="#34D399" variant="filled" size="lg">
125
+ Large
126
+ </StatusBadge>
127
+ </Row>
128
+ </Section>
129
+
130
+ {/* Semantic usage */}
131
+ <Section label="Semantic Usage">
132
+ <Row>
133
+ <StatusBadge color="#34D399" variant="filled" showDot>
134
+ Active
135
+ </StatusBadge>
136
+ <StatusBadge color="#F87171" variant="filled" showDot>
137
+ Error
138
+ </StatusBadge>
139
+ <StatusBadge color="#FBBF24" variant="filled" showDot>
140
+ Pending
141
+ </StatusBadge>
142
+ <StatusBadge color="#60A5FA" variant="filled" showDot>
143
+ In Progress
144
+ </StatusBadge>
145
+ <StatusBadge color="#9CA3AF" variant="filled" showDot>
146
+ Inactive
147
+ </StatusBadge>
148
+ </Row>
149
+ </Section>
150
+ </div>
151
+ ),
152
+ };
153
+
154
+ function Section({ label, children }: { label: string; children: React.ReactNode }) {
155
+ return (
156
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
157
+ <span
158
+ style={{
159
+ fontSize: '11px',
160
+ fontWeight: 600,
161
+ color: '#6B7280',
162
+ textTransform: 'uppercase',
163
+ letterSpacing: '0.05em',
164
+ }}
165
+ >
166
+ {label}
167
+ </span>
168
+ {children}
169
+ </div>
170
+ );
171
+ }
172
+
173
+ function Row({ children }: { children: React.ReactNode }) {
174
+ return (
175
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px', alignItems: 'center' }}>
176
+ {children}
177
+ </div>
178
+ );
179
+ }
@@ -0,0 +1,74 @@
1
+ import type { StatusBadgeProps } from './StatusBadge.types';
2
+
3
+ const SIZE_STYLES = {
4
+ sm: { padding: '2px 8px', fontSize: '11px', borderRadius: '50px', dotSize: 5 },
5
+ md: { padding: '4px 10px', fontSize: '12px', borderRadius: '50px', dotSize: 6 },
6
+ lg: { padding: '6px 14px', fontSize: '14px', borderRadius: '50px', dotSize: 7 },
7
+ };
8
+
9
+ function hexToRgba(hex: string, alpha: number): string {
10
+ const clean = hex.replace('#', '');
11
+ const full =
12
+ clean.length === 3 ?
13
+ clean
14
+ .split('')
15
+ .map((c) => c + c)
16
+ .join('')
17
+ : clean;
18
+ const r = parseInt(full.slice(0, 2), 16);
19
+ const g = parseInt(full.slice(2, 4), 16);
20
+ const b = parseInt(full.slice(4, 6), 16);
21
+ if (isNaN(r) || isNaN(g) || isNaN(b)) return `rgba(0,0,0,${alpha})`;
22
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
23
+ }
24
+
25
+ export const StatusBadge = ({
26
+ children,
27
+ color = '#9CA3AF',
28
+ size = 'md',
29
+ variant = 'outline',
30
+ showDot = false,
31
+ style,
32
+ className,
33
+ }: StatusBadgeProps) => {
34
+ const sizeStyle = SIZE_STYLES[size];
35
+ const backgroundColor =
36
+ variant === 'filled' && color.startsWith('#') ? hexToRgba(color, 0.12) : 'transparent';
37
+
38
+ return (
39
+ <span
40
+ className={className}
41
+ style={{
42
+ display: 'inline-flex',
43
+ alignItems: 'center',
44
+ gap: showDot ? 5 : 0,
45
+ boxSizing: 'border-box',
46
+ padding: sizeStyle.padding,
47
+ fontSize: sizeStyle.fontSize,
48
+ fontWeight: 500,
49
+ lineHeight: 1.5,
50
+ color,
51
+ backgroundColor,
52
+ border: `1px solid ${color}`,
53
+ borderRadius: sizeStyle.borderRadius,
54
+ whiteSpace: 'nowrap',
55
+ ...style,
56
+ }}
57
+ >
58
+ {showDot && (
59
+ <span
60
+ aria-hidden
61
+ style={{
62
+ display: 'inline-block',
63
+ width: sizeStyle.dotSize,
64
+ height: sizeStyle.dotSize,
65
+ borderRadius: '50%',
66
+ backgroundColor: color,
67
+ flexShrink: 0,
68
+ }}
69
+ />
70
+ )}
71
+ {children}
72
+ </span>
73
+ );
74
+ };
@@ -0,0 +1,23 @@
1
+ import type React from 'react';
2
+
3
+ export type StatusBadgeSize = 'sm' | 'md' | 'lg';
4
+
5
+ /** "outline" = transparent background (default). "filled" = subtle tinted background. */
6
+ export type StatusBadgeVariant = 'outline' | 'filled';
7
+
8
+ export interface StatusBadgeProps {
9
+ /** Badge label */
10
+ children: React.ReactNode;
11
+ /** Any CSS hex color e.g. "#34D399" */
12
+ color?: string;
13
+ /** Size variant */
14
+ size?: StatusBadgeSize;
15
+ /** "outline" keeps the background transparent; "filled" adds a subtle tinted background */
16
+ variant?: StatusBadgeVariant;
17
+ /** Show a small dot indicator before the label */
18
+ showDot?: boolean;
19
+ /** Additional CSS styles */
20
+ style?: React.CSSProperties;
21
+ /** Additional CSS class name */
22
+ className?: string;
23
+ }
@@ -0,0 +1,85 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { Stepper } from "./Stepper";
4
+
5
+ const meta: Meta<typeof Stepper> = {
6
+ title: "Design System/Stepper",
7
+ component: Stepper,
8
+ tags: ["autodocs"],
9
+ decorators: [
10
+ (Story) => (
11
+ <div style={{ padding: "24px", overflowX: "auto" }}>
12
+ <Story />
13
+ </div>
14
+ ),
15
+ ],
16
+ args: {
17
+ steps: [
18
+ { value: "nationality", label: "Nationality" },
19
+ { value: "profile", label: "Profile" },
20
+ { value: "role", label: "Role" },
21
+ { value: "client", label: "Client" },
22
+ { value: "visa-docs", label: "Visa & Docs" },
23
+ { value: "preview", label: "Preview" },
24
+ ],
25
+ value: "profile",
26
+ disabled: false,
27
+ },
28
+ };
29
+
30
+ export default meta;
31
+ type Story = StoryObj<typeof Stepper>;
32
+
33
+ export const Playground: Story = {};
34
+
35
+ const InteractiveComponent = () => {
36
+ const [value, setValue] = React.useState("profile");
37
+ const [valueWithDisabledStep, setValueWithDisabledStep] = React.useState("profile");
38
+
39
+ return (
40
+ <>
41
+ <Stepper
42
+ steps={[
43
+ { value: "nationality", label: "Nationality" },
44
+ { value: "profile", label: "Profile" },
45
+ { value: "role", label: "Role" },
46
+ { value: "client", label: "Client" },
47
+ { value: "visa-docs", label: "Visa & Docs" },
48
+ { value: "preview", label: "Preview" },
49
+ ]}
50
+ value={value}
51
+ onChange={setValue}
52
+ />
53
+ <div style={{ height: 16 }} />
54
+ <Stepper
55
+ steps={[
56
+ { value: "nationality", label: "Nationality" },
57
+ { value: "profile", label: "Profile" },
58
+ { value: "role", label: "Role", disabled: true },
59
+ { value: "client", label: "Client" },
60
+ { value: "visa-docs", label: "Visa & Docs" },
61
+ { value: "preview", label: "Preview" },
62
+ ]}
63
+ value={valueWithDisabledStep}
64
+ onChange={setValueWithDisabledStep}
65
+ />
66
+ <div style={{ height: 16 }} />
67
+ <Stepper
68
+ steps={[
69
+ { value: "nationality", label: "Nationality" },
70
+ { value: "profile", label: "Profile" },
71
+ { value: "role", label: "Role" },
72
+ { value: "client", label: "Client" },
73
+ { value: "visa-docs", label: "Visa & Docs" },
74
+ { value: "preview", label: "Preview" },
75
+ ]}
76
+ value="profile"
77
+ disabled
78
+ />
79
+ </>
80
+ );
81
+ };
82
+
83
+ export const Showcase: Story = {
84
+ render: () => <InteractiveComponent />,
85
+ };
@@ -0,0 +1,138 @@
1
+ import React from "react";
2
+ import { useTokens } from "../../ThemeProvider/ThemeContext";
3
+ import { getStepperTokensFromResolved } from "../../tokens";
4
+ import type { StepperProps } from "./Stepper.types";
5
+
6
+ export const Stepper = ({
7
+ steps,
8
+ value,
9
+ onChange,
10
+ disabled = false,
11
+ getStepDisabled,
12
+ style,
13
+ ...rest
14
+ }: StepperProps) => {
15
+ const resolvedTokens = useTokens();
16
+ const t = getStepperTokensFromResolved(resolvedTokens);
17
+ const activeIndex = steps.findIndex((step) => step.value === value);
18
+ const isComponentDisabled = disabled === true;
19
+
20
+ return (
21
+ <div
22
+ role="list"
23
+ aria-label="Stepper"
24
+ aria-disabled={disabled}
25
+ style={{
26
+ display: "flex",
27
+ alignItems: "center",
28
+ gap: t.gap,
29
+ opacity: isComponentDisabled ? 0.5 : 1,
30
+ ...style,
31
+ }}
32
+ {...rest}
33
+ >
34
+ {steps.map((step, index) => {
35
+ const isCompleted = activeIndex > -1 && index < activeIndex;
36
+ const isActive = activeIndex > -1 && index === activeIndex;
37
+ // const isPending = !isCompleted && !isActive;
38
+ const isStepDisabled =
39
+ disabled || step.disabled === true || getStepDisabled?.(step, index) === true;
40
+ const isClickable = !!onChange && !isStepDisabled;
41
+
42
+ const indicatorBackground = isCompleted
43
+ ? t.indicator.completedBackground
44
+ : isActive
45
+ ? t.indicator.activeBackground
46
+ : t.indicator.pendingBackground;
47
+ const indicatorBorder = isCompleted
48
+ ? t.indicator.completedBorder
49
+ : isActive
50
+ ? t.indicator.activeBorder
51
+ : t.indicator.pendingBorder;
52
+ const indicatorColor = isCompleted
53
+ ? t.indicator.completedColor
54
+ : isActive
55
+ ? t.indicator.activeColor
56
+ : t.indicator.pendingColor;
57
+ const labelColor = isCompleted
58
+ ? t.label.completedColor
59
+ : isActive
60
+ ? t.label.activeColor
61
+ : t.label.pendingColor;
62
+
63
+ return (
64
+ <React.Fragment key={step.value}>
65
+ <button
66
+ type="button"
67
+ role="listitem"
68
+ aria-current={isActive ? "step" : undefined}
69
+ aria-label={step.label}
70
+ disabled={!isClickable}
71
+ onClick={() => {
72
+ if (isClickable) onChange(step.value);
73
+ }}
74
+ style={{
75
+ display: "inline-flex",
76
+ alignItems: "center",
77
+ gap: t.stepGap,
78
+ background: "transparent",
79
+ border: "none",
80
+ padding: 0,
81
+ margin: 0,
82
+ cursor: isStepDisabled ? "not-allowed" : isClickable ? "pointer" : "default",
83
+ opacity: isStepDisabled && !isComponentDisabled ? 0.5 : 1,
84
+ }}
85
+ >
86
+ <span
87
+ aria-hidden
88
+ style={{
89
+ width: t.indicator.size,
90
+ height: t.indicator.size,
91
+ minWidth: t.indicator.size,
92
+ borderRadius: "50%",
93
+ border: `${t.indicator.borderWidth} solid ${indicatorBorder}`,
94
+ backgroundColor: indicatorBackground,
95
+ color: indicatorColor,
96
+ display: "inline-flex",
97
+ alignItems: "center",
98
+ justifyContent: "center",
99
+ fontFamily: t.indicator.fontFamily,
100
+ fontSize: t.indicator.fontSize,
101
+ fontWeight: t.indicator.fontWeight,
102
+ lineHeight: t.indicator.lineHeight,
103
+ boxSizing: "border-box",
104
+ }}
105
+ >
106
+ {isCompleted ? "\u2713" : index + 1}
107
+ </span>
108
+ <span
109
+ style={{
110
+ color: labelColor,
111
+ fontFamily: t.label.fontFamily,
112
+ fontSize: t.label.fontSize,
113
+ fontWeight: t.label.fontWeight,
114
+ lineHeight: t.label.lineHeight,
115
+ whiteSpace: "nowrap",
116
+ }}
117
+ >
118
+ {step.label}
119
+ </span>
120
+ </button>
121
+ {index < steps.length - 1 ? (
122
+ <span
123
+ aria-hidden
124
+ style={{
125
+ width: t.line.width,
126
+ height: t.line.height,
127
+ backgroundColor: isCompleted ? t.line.completedBackground : t.line.pendingBackground,
128
+ display: "inline-block",
129
+ borderRadius: t.line.height,
130
+ }}
131
+ />
132
+ ) : null}
133
+ </React.Fragment>
134
+ );
135
+ })}
136
+ </div>
137
+ );
138
+ };
@@ -0,0 +1,16 @@
1
+ import type React from "react";
2
+
3
+ export interface StepperStep {
4
+ value: string;
5
+ label: string;
6
+ disabled?: boolean;
7
+ }
8
+
9
+ export interface StepperProps
10
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange" | "children"> {
11
+ steps: StepperStep[];
12
+ value: string;
13
+ onChange?: (value: string) => void;
14
+ disabled?: boolean;
15
+ getStepDisabled?: (step: StepperStep, index: number) => boolean;
16
+ }
@@ -0,0 +1,128 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { useState } from 'react';
3
+ import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
4
+ import CreditCardIcon from '@mui/icons-material/CreditCard';
5
+ import { SubTabs } from './SubTabs';
6
+ import type { SubTabsOption, SubTabsProps } from './SubTabs.types';
7
+
8
+ const defaultOptions: SubTabsOption[] = [
9
+ {
10
+ value: 'basic-info',
11
+ label: 'Basic Info',
12
+ icon: <AccountBalanceWalletIcon sx={{ fontSize: 16 }} />,
13
+ },
14
+ {
15
+ value: 'payment-mode',
16
+ label: 'Payment Mode',
17
+ icon: <CreditCardIcon sx={{ fontSize: 16 }} />,
18
+ },
19
+ ];
20
+
21
+ const meta: Meta<typeof SubTabs> = {
22
+ title: 'Design System/SubTabs',
23
+ component: SubTabs,
24
+ decorators: [
25
+ (Story) => (
26
+ <div
27
+ style={{
28
+ padding: '24px',
29
+ maxWidth: '640px',
30
+ background: '#FFFFFF',
31
+ minHeight: '120px',
32
+ }}
33
+ >
34
+ <Story />
35
+ </div>
36
+ ),
37
+ ],
38
+ args: {
39
+ options: defaultOptions,
40
+ value: 'basic-info',
41
+ variant: 'pill',
42
+ color: 'green',
43
+ size: 'md',
44
+ disabled: false,
45
+ } as SubTabsProps,
46
+ argTypes: {
47
+ variant: {
48
+ control: { type: 'radio' },
49
+ options: ['pill', 'underline', 'outline'],
50
+ },
51
+ color: {
52
+ control: { type: 'radio' },
53
+ options: ['primary', 'green'],
54
+ },
55
+ size: {
56
+ control: { type: 'radio' },
57
+ options: ['sm', 'md'],
58
+ },
59
+ onChange: { action: 'changed' },
60
+ },
61
+ };
62
+
63
+ export default meta;
64
+
65
+ type Story = StoryObj<typeof SubTabs>;
66
+
67
+ export const Playground: Story = {
68
+ render: function PlaygroundRender(args) {
69
+ const [value, setValue] = useState(args.value);
70
+ return (
71
+ <SubTabs
72
+ {...args}
73
+ value={value}
74
+ onChange={(v) => {
75
+ setValue(v);
76
+ args.onChange?.(v);
77
+ }}
78
+ />
79
+ );
80
+ },
81
+ };
82
+
83
+ export const Showcase: Story = {
84
+ render: () => (
85
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '32px' }}>
86
+ <div>
87
+ <p style={{ margin: '0 0 12px', fontSize: '12px', color: '#6B7280' }}>Pill · Green</p>
88
+ <SubTabs
89
+ options={defaultOptions}
90
+ value="basic-info"
91
+ onChange={() => {}}
92
+ variant="pill"
93
+ color="green"
94
+ />
95
+ </div>
96
+ <div>
97
+ <p style={{ margin: '0 0 12px', fontSize: '12px', color: '#6B7280' }}>Pill · Primary</p>
98
+ <SubTabs
99
+ options={defaultOptions}
100
+ value="payment-mode"
101
+ onChange={() => {}}
102
+ variant="pill"
103
+ color="primary"
104
+ />
105
+ </div>
106
+ <div>
107
+ <p style={{ margin: '0 0 12px', fontSize: '12px', color: '#6B7280' }}>Underline · Green</p>
108
+ <SubTabs
109
+ options={defaultOptions}
110
+ value="basic-info"
111
+ onChange={() => {}}
112
+ variant="underline"
113
+ color="green"
114
+ />
115
+ </div>
116
+ <div>
117
+ <p style={{ margin: '0 0 12px', fontSize: '12px', color: '#6B7280' }}>Outline · Primary</p>
118
+ <SubTabs
119
+ options={defaultOptions}
120
+ value="payment-mode"
121
+ onChange={() => {}}
122
+ variant="outline"
123
+ color="primary"
124
+ />
125
+ </div>
126
+ </div>
127
+ ),
128
+ };