Skip to content

Commit d060892

Browse files
Design system improvements (#5984)
1 parent 6243563 commit d060892

File tree

9 files changed

+137
-76
lines changed

9 files changed

+137
-76
lines changed

src/__swaps__/screens/Swap/components/ReviewPanel.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ export function ReviewPanel() {
471471
<Inline horizontalSpace="10px" alignVertical="center" alignHorizontal="justify">
472472
<ButtonPressAnimation onPress={openGasExplainer} scaleTo={0.925}>
473473
<Stack space="10px">
474-
<Inline alignVertical="center" horizontalSpace="6px">
474+
<Inline alignVertical="center" horizontalSpace="6px" wrap={false}>
475475
<View style={sx.chainBadgeContainer}>
476476
<AnimatedChainImage showMainnetBadge assetType="input" size={16} />
477477
</View>

src/design-system/color/AccentColorContext.tsx

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import chroma from 'chroma-js';
22
import React, { createContext, ReactNode, useContext, useMemo } from 'react';
3+
import { useBackgroundColor } from '../components/BackgroundProvider/BackgroundProvider';
34
import { BackgroundColorValue } from './palettes';
45

56
export const AccentColorContext = createContext<BackgroundColorValue | null>(null);
@@ -21,14 +22,14 @@ export function useAccentColor(): BackgroundColorValue {
2122
* @description Sets the `"accent"` color for an entire subtree of the app.
2223
*/
2324
export function AccentColorProvider({ color, children }: AccentColorProviderProps) {
24-
const contextValue = useMemo(
25-
() =>
26-
({
27-
color,
28-
mode: chroma.contrast(color, '#fff') > 2.125 ? 'darkTinted' : 'lightTinted',
29-
}) as const,
30-
[color]
31-
);
25+
const blue = useBackgroundColor('blue');
26+
const contextValue = useMemo(() => {
27+
const isValid = chroma.valid(color);
28+
return {
29+
color: isValid ? color : blue,
30+
mode: isValid && chroma.contrast(color, '#fff') > 2.125 ? 'darkTinted' : 'lightTinted',
31+
} as const;
32+
}, [blue, color]);
3233

3334
return <AccentColorContext.Provider value={contextValue}>{children}</AccentColorContext.Provider>;
3435
}
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,66 @@
1-
import React from 'react';
1+
import React, { memo } from 'react';
22
import { Cover, useColorMode, useForegroundColor } from '@/design-system';
33
import { ForegroundColor } from '@/design-system/color/palettes';
44
import { CustomColor } from '@/design-system/color/useForegroundColor';
55
import { IS_IOS } from '@/env';
66

7-
export interface BorderProps {
7+
export type BorderProps = {
8+
borderBottomLeftRadius?: number;
9+
borderBottomRightRadius?: number;
810
borderColor?: ForegroundColor | CustomColor;
9-
borderRadius: number;
11+
borderTopLeftRadius?: number;
12+
borderTopRightRadius?: number;
13+
borderRadius?: number;
1014
borderWidth?: number;
1115
enableInLightMode?: boolean;
1216
enableOnAndroid?: boolean;
13-
}
17+
} & (
18+
| {
19+
borderBottomRadius?: number;
20+
borderLeftRadius?: never;
21+
borderRightRadius?: never;
22+
borderTopRadius?: number;
23+
}
24+
| {
25+
borderBottomRadius?: never;
26+
borderLeftRadius?: number;
27+
borderRightRadius?: number;
28+
borderTopRadius?: never;
29+
}
30+
);
1431

15-
export const Border = ({
32+
export const Border = memo(function Border({
33+
borderBottomLeftRadius,
34+
borderBottomRadius,
35+
borderBottomRightRadius,
1636
borderColor = 'separatorSecondary',
37+
borderLeftRadius,
1738
borderRadius,
39+
borderRightRadius,
40+
borderTopLeftRadius,
41+
borderTopRadius,
42+
borderTopRightRadius,
1843
borderWidth = 1,
1944
enableInLightMode,
2045
enableOnAndroid = true,
21-
}: BorderProps) => {
46+
}: BorderProps) {
2247
const { isDarkMode } = useColorMode();
2348

2449
const color = useForegroundColor(borderColor);
2550

2651
return (isDarkMode || enableInLightMode) && (IS_IOS || enableOnAndroid) ? (
2752
<Cover
2853
style={{
54+
borderBottomLeftRadius: borderBottomLeftRadius ?? borderBottomRadius ?? borderLeftRadius ?? borderRadius,
55+
borderBottomRightRadius: borderBottomRightRadius ?? borderBottomRadius ?? borderRightRadius ?? borderRadius,
2956
borderColor: color,
3057
borderCurve: 'continuous',
31-
borderRadius,
58+
borderTopLeftRadius: borderTopLeftRadius ?? borderTopRadius ?? borderLeftRadius ?? borderRadius,
59+
borderTopRightRadius: borderTopRightRadius ?? borderTopRadius ?? borderRightRadius ?? borderRadius,
3260
borderWidth,
3361
overflow: 'hidden',
3462
pointerEvents: 'none',
3563
}}
3664
/>
3765
) : null;
38-
};
66+
});

src/design-system/components/Box/Box.tsx

+46-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import React, { forwardRef, ReactNode, useMemo } from 'react';
22
import { View, ViewStyle } from 'react-native';
3+
import Animated from 'react-native-reanimated';
34
import { useForegroundColor, useForegroundColors } from '../../color/useForegroundColor';
45
import { useColorMode } from '../../color/ColorMode';
56
import { Shadow, shadows } from '../../layout/shadow';
67
import { Height, heights, Width, widths } from '../../layout/size';
78
import { NegativeSpace, negativeSpace, positionSpace, PositionSpace, Space, space } from '../../layout/space';
89
import { BackgroundProvider, BackgroundProviderProps } from '../BackgroundProvider/BackgroundProvider';
10+
import { Border, BorderProps } from '../Border/Border';
911
import { ApplyShadow } from '../private/ApplyShadow/ApplyShadow';
1012
import type * as Polymorphic from './polymorphic';
1113

@@ -26,6 +28,8 @@ export type BoxProps = {
2628
borderTopRightRadius?: number;
2729
borderBottomLeftRadius?: number;
2830
borderBottomRightRadius?: number;
31+
borderColor?: BorderProps['borderColor'];
32+
borderWidth?: BorderProps['borderWidth'];
2933
bottom?: PositionSpace;
3034
children?: ReactNode;
3135
flexBasis?: 0;
@@ -45,6 +49,7 @@ export type BoxProps = {
4549
marginRight?: NegativeSpace;
4650
marginTop?: NegativeSpace;
4751
marginVertical?: NegativeSpace;
52+
overflow?: 'visible' | 'hidden' | 'scroll';
4853
padding?: Space;
4954
paddingBottom?: Space;
5055
paddingHorizontal?: Space;
@@ -61,7 +66,6 @@ export type BoxProps = {
6166
elevation?: number;
6267
shadowOpacity?: number;
6368
shadowRadius?: number;
64-
overflow?: 'visible' | 'hidden' | 'scroll' | 'auto';
6569
} & (
6670
| {
6771
borderBottomRadius?: number;
@@ -103,12 +107,14 @@ export const Box = forwardRef(function Box(
103107
borderBottomLeftRadius,
104108
borderBottomRadius,
105109
borderBottomRightRadius,
110+
borderColor,
106111
borderLeftRadius,
107112
borderRadius,
108113
borderRightRadius,
109114
borderTopLeftRadius,
110115
borderTopRadius,
111116
borderTopRightRadius,
117+
borderWidth,
112118
bottom: bottomProp,
113119
children,
114120
flexBasis,
@@ -165,14 +171,25 @@ export const Box = forwardRef(function Box(
165171
const right = resolveToken(positionSpace, rightProp);
166172
const top = resolveToken(positionSpace, topProp);
167173

168-
const width = resolveToken(widths, widthProp);
169-
const height = resolveToken(heights, heightProp);
174+
const width = typeof widthProp === 'number' ? widthProp : resolveToken(widths, widthProp);
175+
const height = typeof heightProp === 'number' ? heightProp : resolveToken(heights, heightProp);
176+
177+
const isView = Component === View || Component === Animated.View;
178+
179+
const shadowStylesExist =
180+
!!styleProp &&
181+
('shadowColor' in styleProp ||
182+
'shadowOffset' in styleProp ||
183+
'shadowOpacity' in styleProp ||
184+
'shadowRadius' in styleProp ||
185+
'elevation' in styleProp);
170186

171187
const shadows = useShadow(shadow);
172188

173189
const styles = useMemo(() => {
174190
return {
175191
alignItems,
192+
borderRadius: borderRadius, // Apply this first as certain components don't support individual corner radii
176193
borderBottomLeftRadius: borderBottomLeftRadius ?? borderBottomRadius ?? borderLeftRadius ?? borderRadius,
177194
borderBottomRightRadius: borderBottomRightRadius ?? borderBottomRadius ?? borderRightRadius ?? borderRadius,
178195
borderCurve: 'continuous' as ViewStyle['borderCurve'],
@@ -194,6 +211,7 @@ export const Box = forwardRef(function Box(
194211
marginRight,
195212
marginTop,
196213
marginVertical,
214+
...((isView || borderRadius) && !shadowStylesExist && { overflow: borderRadius ? 'hidden' : overflow }),
197215
padding,
198216
paddingBottom,
199217
paddingHorizontal,
@@ -224,6 +242,7 @@ export const Box = forwardRef(function Box(
224242
flexShrink,
225243
flexWrap,
226244
height,
245+
isView,
227246
justifyContent,
228247
left,
229248
margin,
@@ -233,6 +252,7 @@ export const Box = forwardRef(function Box(
233252
marginRight,
234253
marginTop,
235254
marginVertical,
255+
overflow,
236256
padding,
237257
paddingBottom,
238258
paddingHorizontal,
@@ -242,6 +262,7 @@ export const Box = forwardRef(function Box(
242262
paddingVertical,
243263
position,
244264
right,
265+
shadowStylesExist,
245266
top,
246267
width,
247268
]);
@@ -254,13 +275,35 @@ export const Box = forwardRef(function Box(
254275
<ApplyShadow backgroundColor={backgroundColor} shadows={shadows}>
255276
<Component style={backgroundStyle} {...restProps} ref={ref}>
256277
{children}
278+
{borderColor || borderWidth ? (
279+
<Border
280+
borderBottomLeftRadius={styles.borderBottomLeftRadius}
281+
borderBottomRightRadius={styles.borderBottomRightRadius}
282+
borderColor={borderColor}
283+
borderTopLeftRadius={styles.borderTopLeftRadius}
284+
borderTopRightRadius={styles.borderTopRightRadius}
285+
borderWidth={borderWidth}
286+
enableInLightMode
287+
/>
288+
) : null}
257289
</Component>
258290
</ApplyShadow>
259291
)}
260292
</BackgroundProvider>
261293
) : (
262294
<Component style={style} {...restProps} ref={ref}>
263295
{children}
296+
{borderColor || borderWidth ? (
297+
<Border
298+
borderBottomLeftRadius={styles.borderBottomLeftRadius}
299+
borderBottomRightRadius={styles.borderBottomRightRadius}
300+
borderColor={borderColor}
301+
borderTopLeftRadius={styles.borderTopLeftRadius}
302+
borderTopRightRadius={styles.borderTopRightRadius}
303+
borderWidth={borderWidth}
304+
enableInLightMode
305+
/>
306+
) : null}
264307
</Component>
265308
);
266309
}) as PolymorphicBox;

src/design-system/components/Inline/Inline.tsx

+18-25
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import React, { Children, ReactElement, ReactNode, useMemo } from 'react';
2-
import flattenChildren from 'react-flatten-children';
1+
import React, { Children, ReactElement, ReactNode } from 'react';
32
import { AlignHorizontal, alignHorizontalToFlexAlign, AlignVertical, alignVerticalToFlexAlign } from '../../layout/alignment';
4-
import { negateSpace, Space } from '../../layout/space';
5-
import { Box } from '../Box/Box';
3+
import { Space, space as spaceTokens } from '../../layout/space';
4+
import { Box, resolveToken } from '../Box/Box';
65

76
export type InlineProps = {
87
children: ReactNode;
@@ -39,34 +38,28 @@ export function Inline({
3938
const verticalSpace = verticalSpaceProp ?? space;
4039
const horizontalSpace = horizontalSpaceProp ?? space;
4140

42-
const flattenedChildren = useMemo(() => flattenChildren(children), [children]);
43-
4441
return (
4542
<Box
4643
alignItems={alignVertical ? alignVerticalToFlexAlign[alignVertical] : undefined}
4744
flexDirection="row"
4845
flexWrap={wrap ? 'wrap' : undefined}
4946
justifyContent={alignHorizontal ? alignHorizontalToFlexAlign[alignHorizontal] : undefined}
50-
marginRight={wrap && horizontalSpace ? negateSpace(horizontalSpace) : undefined}
51-
marginTop={wrap && verticalSpace ? negateSpace(verticalSpace) : undefined}
47+
style={{
48+
columnGap: horizontalSpace ? resolveToken(spaceTokens, horizontalSpace) : undefined,
49+
rowGap: verticalSpace ? resolveToken(spaceTokens, verticalSpace) : undefined,
50+
}}
5251
>
53-
{Children.map(flattenedChildren, (child, index) => {
54-
if (wrap) {
55-
return (
56-
<Box paddingRight={horizontalSpace} paddingTop={verticalSpace}>
57-
{child}
58-
</Box>
59-
);
60-
}
61-
62-
const isLastChild = index === flattenedChildren.length - 1;
63-
return (
64-
<>
65-
{horizontalSpace && !isLastChild ? <Box paddingRight={horizontalSpace}>{child}</Box> : child}
66-
{separator && !isLastChild ? <Box paddingRight={horizontalSpace}>{separator}</Box> : null}
67-
</>
68-
);
69-
})}
52+
{wrap || !separator
53+
? children
54+
: Children.map(children, (child, index) => {
55+
if (!child) return null;
56+
return (
57+
<>
58+
{index > 0 && separator}
59+
{child}
60+
</>
61+
);
62+
})}
7063
</Box>
7164
);
7265
}
+19-25
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React, { Children, isValidElement, ReactElement, ReactNode } from 'react';
2-
import flattenChildren from 'react-flatten-children';
3-
import { Space } from '../../layout/space';
4-
import { Box } from '../Box/Box';
2+
import { Space, space as spaceTokens } from '../../layout/space';
3+
import { Box, resolveToken } from '../Box/Box';
54
import { Width } from '@/design-system/layout/size';
65

76
const alignHorizontalToFlexAlign = {
@@ -28,33 +27,28 @@ export type StackProps = {
2827
* within each other for layouts with differing amounts of space between groups
2928
* of content.
3029
*/
31-
export function Stack({ children: childrenProp, alignHorizontal, separator, space, width }: StackProps) {
30+
export function Stack({ children, alignHorizontal, separator, space, width }: StackProps) {
3231
if (__DEV__ && separator && !isValidElement(separator)) {
3332
throw new Error(`Stack: The 'separator' prop must be a React element`);
3433
}
3534

36-
const children = flattenChildren(childrenProp).filter(child => isValidElement(child));
37-
3835
return (
39-
<Box width={width} alignItems={alignHorizontal ? alignHorizontalToFlexAlign[alignHorizontal] : undefined}>
40-
{Children.map(children, (child, index) => {
41-
const isLastChild = index === children.length - 1;
42-
43-
return (
44-
<>
45-
{space && !isLastChild ? <Box paddingBottom={space}>{child}</Box> : child}
46-
{separator && !isLastChild ? (
47-
<Box
48-
alignItems={alignHorizontal ? alignHorizontalToFlexAlign[alignHorizontal] : undefined}
49-
paddingBottom={space}
50-
width="full"
51-
>
52-
{separator}
53-
</Box>
54-
) : null}
55-
</>
56-
);
57-
})}
36+
<Box
37+
alignItems={alignHorizontal ? alignHorizontalToFlexAlign[alignHorizontal] : undefined}
38+
gap={resolveToken(spaceTokens, space)}
39+
width={width}
40+
>
41+
{!separator
42+
? children
43+
: Children.map(children, (child, index) => {
44+
if (!child) return null;
45+
return (
46+
<>
47+
{index > 0 && separator}
48+
{child}
49+
</>
50+
);
51+
})}
5852
</Box>
5953
);
6054
}

0 commit comments

Comments
 (0)