Skip to content

Commit 13b7019

Browse files
authored
Merge pull request #2689 from innovaccer/develop
Develop
2 parents 179ceb2 + 730f5d1 commit 13b7019

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+7464
-603
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,4 @@ docs/static/sb/
105105
docs/cypress/videos
106106
docs/cypress/screenshots
107107
.env
108+
.vscode
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
import * as React from 'react';
2+
import classNames from 'classnames';
3+
import { BaseProps, BaseHtmlProps } from '@/utils/types';
4+
import styles from '@css/components/flex.module.css';
5+
6+
export type FlexDirection = 'row' | 'column' | 'row-reverse' | 'column-reverse';
7+
export type FlexJustifyContent =
8+
| 'flex-start'
9+
| 'flex-end'
10+
| 'center'
11+
| 'space-between'
12+
| 'space-around'
13+
| 'space-evenly';
14+
export type FlexAlignItems = 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch';
15+
export type FlexWrap = 'wrap' | 'nowrap' | 'wrap-reverse';
16+
export type FlexGap =
17+
| 'spacing-10'
18+
| 'spacing-20'
19+
| 'spacing-30'
20+
| 'spacing-40'
21+
| 'spacing-60'
22+
| 'spacing-80'
23+
| 'spacing-120'
24+
| 'spacing-160';
25+
26+
export type ResponsiveValue<T> = T | { xs?: T; sm?: T; md?: T; lg?: T; xl?: T };
27+
28+
const spacingTokenMap: Record<FlexGap, string> = {
29+
'spacing-10': 'var(--spacing-10)',
30+
'spacing-20': 'var(--spacing-20)',
31+
'spacing-30': 'var(--spacing-30)',
32+
'spacing-40': 'var(--spacing-40)',
33+
'spacing-60': 'var(--spacing-60)',
34+
'spacing-80': 'var(--spacing-80)',
35+
'spacing-120': 'var(--spacing-120)',
36+
'spacing-160': 'var(--spacing-160)',
37+
};
38+
39+
const getSpacingValue = (spacing: FlexGap | undefined): string | undefined => {
40+
if (!spacing) return undefined;
41+
return spacingTokenMap[spacing];
42+
};
43+
44+
const buildResponsiveStyles = (
45+
value: ResponsiveValue<string | FlexGap | FlexDirection | FlexJustifyContent | FlexAlignItems | FlexWrap> | undefined,
46+
cssProperty: string
47+
): React.CSSProperties => {
48+
if (!value) return {};
49+
50+
if (typeof value === 'string') {
51+
if (cssProperty.includes('gap')) {
52+
return { [cssProperty]: getSpacingValue(value as FlexGap) } as React.CSSProperties;
53+
}
54+
return { [cssProperty]: value } as React.CSSProperties;
55+
}
56+
57+
const styles: React.CSSProperties = {};
58+
59+
if (value.xs) {
60+
if (cssProperty.includes('gap')) {
61+
const spacingValue = getSpacingValue(value.xs as FlexGap);
62+
if (spacingValue) {
63+
(styles as any)[cssProperty] = spacingValue;
64+
}
65+
} else {
66+
(styles as any)[cssProperty] = value.xs;
67+
}
68+
}
69+
70+
return styles;
71+
};
72+
73+
export interface FlexProps extends BaseProps, Omit<BaseHtmlProps<HTMLDivElement>, 'wrap'> {
74+
/**
75+
* Sets the direction of the flex container.
76+
* Controls whether flex items are laid out horizontally or vertically.
77+
* Supports responsive breakpoints: { xs: '...', sm: '...', md: '...', lg: '...', xl: '...' }
78+
*/
79+
direction?: ResponsiveValue<FlexDirection>;
80+
81+
/**
82+
* Sets how items are aligned along the main axis.
83+
* Controls the distribution of space between and around flex items.
84+
* Supports responsive breakpoints: { xs: '...', sm: '...', md: '...', lg: '...', xl: '...' }
85+
*/
86+
justifyContent?: ResponsiveValue<FlexJustifyContent>;
87+
88+
/**
89+
* Sets how items are aligned along the cross axis.
90+
* Controls the alignment of flex items perpendicular to the main axis.
91+
* Supports responsive breakpoints: { xs: '...', sm: '...', md: '...', lg: '...', xl: '...' }
92+
*/
93+
alignItems?: ResponsiveValue<FlexAlignItems>;
94+
95+
/**
96+
* Controls whether items should wrap to the next line.
97+
* Determines if flex items are forced onto one line or can wrap onto multiple lines.
98+
* Supports responsive breakpoints: { xs: '...', sm: '...', md: '...', lg: '...', xl: '...' }
99+
*/
100+
wrap?: ResponsiveValue<FlexWrap>;
101+
102+
/**
103+
* Sets the gap (spacing) between flex items in both directions.
104+
* Uses predefined spacing tokens for consistent layout.
105+
* Supports responsive breakpoints: { xs: '...', sm: '...', md: '...', lg: '...', xl: '...' }
106+
*
107+
* Available spacing tokens:
108+
* - "spacing-10" (4px)
109+
* - "spacing-20" (8px)
110+
* - "spacing-30" (12px)
111+
* - "spacing-40" (16px)
112+
* - "spacing-60" (24px)
113+
* - "spacing-80" (32px)
114+
* - "spacing-120" (48px)
115+
* - "spacing-160" (64px)
116+
*/
117+
gap?: ResponsiveValue<FlexGap>;
118+
119+
/**
120+
* Sets the horizontal gap (spacing) between flex items.
121+
* Overrides the gap property for horizontal spacing only.
122+
* Supports responsive breakpoints: { xs: '...', sm: '...', md: '...', lg: '...', xl: '...' }
123+
*
124+
* Available spacing tokens:
125+
* - "spacing-10" (4px)
126+
* - "spacing-20" (8px)
127+
* - "spacing-30" (12px)
128+
* - "spacing-40" (16px)
129+
* - "spacing-60" (24px)
130+
* - "spacing-80" (32px)
131+
* - "spacing-120" (48px)
132+
* - "spacing-160" (64px)
133+
*/
134+
columnGap?: ResponsiveValue<FlexGap>;
135+
136+
/**
137+
* Sets the vertical gap (spacing) between flex items.
138+
* Overrides the gap property for vertical spacing only.
139+
* Supports responsive breakpoints: { xs: '...', sm: '...', md: '...', lg: '...', xl: '...' }
140+
*
141+
* Available spacing tokens:
142+
* - "spacing-10" (4px)
143+
* - "spacing-20" (8px)
144+
* - "spacing-30" (12px)
145+
* - "spacing-40" (16px)
146+
* - "spacing-60" (24px)
147+
* - "spacing-80" (32px)
148+
* - "spacing-120" (48px)
149+
* - "spacing-160" (64px)
150+
*/
151+
rowGap?: ResponsiveValue<FlexGap>;
152+
153+
/**
154+
* Content to be rendered inside Flex.
155+
* Can be any valid React node.
156+
*/
157+
children?: React.ReactNode;
158+
}
159+
160+
const directionMap: Record<FlexDirection, string> = {
161+
row: 'row',
162+
column: 'column',
163+
'row-reverse': 'rowReverse',
164+
'column-reverse': 'columnReverse',
165+
};
166+
167+
const justifyContentMap: Record<FlexJustifyContent, string> = {
168+
'flex-start': 'justifyStart',
169+
'flex-end': 'justifyEnd',
170+
center: 'justifyCenter',
171+
'space-between': 'justifyBetween',
172+
'space-around': 'justifyAround',
173+
'space-evenly': 'justifyEvenly',
174+
};
175+
176+
const alignItemsMap: Record<FlexAlignItems, string> = {
177+
'flex-start': 'alignStart',
178+
'flex-end': 'alignEnd',
179+
center: 'alignCenter',
180+
baseline: 'alignBaseline',
181+
stretch: 'alignStretch',
182+
};
183+
184+
const wrapMap: Record<FlexWrap, string> = {
185+
wrap: 'wrap',
186+
nowrap: 'nowrap',
187+
'wrap-reverse': 'wrapReverse',
188+
};
189+
190+
export const Flex = (props: FlexProps) => {
191+
const {
192+
direction = 'row',
193+
justifyContent = 'flex-start',
194+
alignItems = 'stretch',
195+
wrap = 'nowrap',
196+
gap,
197+
columnGap,
198+
rowGap,
199+
className,
200+
style,
201+
children,
202+
...rest
203+
} = props;
204+
205+
const inlineStyles: React.CSSProperties = {
206+
...style,
207+
};
208+
209+
if (direction) {
210+
if (typeof direction === 'string') {
211+
inlineStyles.flexDirection = direction;
212+
} else if (direction.xs) {
213+
inlineStyles.flexDirection = direction.xs;
214+
}
215+
}
216+
217+
if (justifyContent) {
218+
if (typeof justifyContent === 'string') {
219+
inlineStyles.justifyContent = justifyContent;
220+
} else if (justifyContent.xs) {
221+
inlineStyles.justifyContent = justifyContent.xs;
222+
}
223+
}
224+
225+
if (alignItems) {
226+
if (typeof alignItems === 'string') {
227+
inlineStyles.alignItems = alignItems;
228+
} else if (alignItems.xs) {
229+
inlineStyles.alignItems = alignItems.xs;
230+
}
231+
}
232+
233+
if (wrap) {
234+
if (typeof wrap === 'string') {
235+
inlineStyles.flexWrap = wrap;
236+
} else if (wrap.xs) {
237+
inlineStyles.flexWrap = wrap.xs;
238+
}
239+
}
240+
241+
const gapStyles = buildResponsiveStyles(gap, 'gap');
242+
const columnGapStyles = buildResponsiveStyles(columnGap, 'columnGap');
243+
const rowGapStyles = buildResponsiveStyles(rowGap, 'rowGap');
244+
245+
const mergedStyles = {
246+
...inlineStyles,
247+
...gapStyles,
248+
...columnGapStyles,
249+
...rowGapStyles,
250+
};
251+
252+
const classes = classNames(
253+
{
254+
[styles.Flex]: true,
255+
[styles[`Flex--${directionMap[direction as FlexDirection]}`]]: typeof direction === 'string' && direction,
256+
[styles[`Flex--${justifyContentMap[justifyContent as FlexJustifyContent]}`]]:
257+
typeof justifyContent === 'string' && justifyContent,
258+
[styles[`Flex--${alignItemsMap[alignItems as FlexAlignItems]}`]]: typeof alignItems === 'string' && alignItems,
259+
[styles[`Flex--${wrapMap[wrap as FlexWrap]}`]]: typeof wrap === 'string' && wrap,
260+
[styles[`Flex--gap--${typeof gap === 'string' ? gap : undefined}`]]: typeof gap === 'string' && gap,
261+
[styles[`Flex--columnGap--${typeof columnGap === 'string' ? columnGap : undefined}`]]:
262+
typeof columnGap === 'string' && columnGap,
263+
[styles[`Flex--rowGap--${typeof rowGap === 'string' ? rowGap : undefined}`]]:
264+
typeof rowGap === 'string' && rowGap,
265+
},
266+
className
267+
);
268+
269+
return (
270+
<div data-test="DesignSystem-Flex" {...rest} className={classes} style={mergedStyles}>
271+
{children}
272+
</div>
273+
);
274+
};
275+
276+
Flex.displayName = 'Flex';
277+
278+
Flex.defaultProps = {
279+
direction: 'row',
280+
justifyContent: 'flex-start',
281+
alignItems: 'stretch',
282+
wrap: 'nowrap',
283+
};
284+
285+
export default Flex;

0 commit comments

Comments
 (0)