Skip to content

Commit 4a48e91

Browse files
Add stacked button variant
1 parent 66526a8 commit 4a48e91

File tree

2 files changed

+86
-11
lines changed

2 files changed

+86
-11
lines changed

src/components/Button.tsx

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export type ButtonColor =
4040
| 'primary_subtle'
4141
| 'negative_subtle'
4242
export type ButtonSize = 'tiny' | 'small' | 'large'
43-
export type ButtonShape = 'round' | 'square' | 'default'
43+
export type ButtonShape = 'round' | 'square' | 'stacked' | 'default'
4444
export type VariantProps = {
4545
/**
4646
* The style variation of the button
@@ -509,6 +509,25 @@ export const Button = React.forwardRef<View, ButtonProps>(
509509
baseStyles.push(a.rounded_sm)
510510
}
511511
}
512+
} else if (shape === 'stacked') {
513+
if (size === 'large') {
514+
// not implemented
515+
baseStyles.push({})
516+
} else if (size === 'small') {
517+
baseStyles.push({
518+
height: 80,
519+
paddingHorizontal: 22,
520+
borderRadius: 24,
521+
gap: 4,
522+
})
523+
} else if (size === 'tiny') {
524+
baseStyles.push({
525+
paddingVertical: 10,
526+
paddingHorizontal: 16,
527+
borderRadius: 16,
528+
gap: 2,
529+
})
530+
}
512531
}
513532

514533
return {
@@ -523,9 +542,10 @@ export const Button = React.forwardRef<View, ButtonProps>(
523542
variant,
524543
color,
525544
size,
545+
shape,
526546
disabled: disabled || false,
527547
}),
528-
[state, variant, color, size, disabled],
548+
[state, variant, color, size, shape, disabled],
529549
)
530550

531551
const flattenedBaseStyles = flatten([baseStyles, style])
@@ -545,7 +565,7 @@ export const Button = React.forwardRef<View, ButtonProps>(
545565
disabled: disabled || false,
546566
}}
547567
style={[
548-
a.flex_row,
568+
shape !== 'stacked' ? a.flex_row : undefined,
549569
a.align_center,
550570
a.justify_center,
551571
a.curve_continuous,
@@ -571,7 +591,7 @@ Button.displayName = 'Button'
571591

572592
export function useSharedButtonTextStyles() {
573593
const t = useTheme()
574-
const {color, variant, disabled, size} = useButtonContext()
594+
const {color, variant, disabled, size, shape} = useButtonContext()
575595
return React.useMemo(() => {
576596
const baseStyles: TextStyle[] = []
577597

@@ -751,22 +771,29 @@ export function useSharedButtonTextStyles() {
751771
}
752772

753773
if (size === 'large') {
754-
baseStyles.push(a.text_md, a.leading_snug, a.font_medium)
774+
baseStyles.push(a.text_md, a.leading_snug, a.font_semi_bold)
755775
} else if (size === 'small') {
756-
baseStyles.push(a.text_sm, a.leading_snug, a.font_medium)
776+
baseStyles.push(a.text_sm, a.leading_snug, a.font_semi_bold)
757777
} else if (size === 'tiny') {
758-
baseStyles.push(a.text_xs, a.leading_snug, a.font_medium)
778+
baseStyles.push(a.text_xs, a.leading_snug, a.font_semi_bold)
779+
}
780+
781+
if (shape === 'stacked') {
782+
baseStyles.push(a.leading_tight)
759783
}
760784

761785
return StyleSheet.flatten(baseStyles)
762-
}, [t, variant, color, size, disabled])
786+
}, [t, variant, color, size, shape, disabled])
763787
}
764788

765789
export function ButtonText({children, style, ...rest}: ButtonTextProps) {
766790
const textStyles = useSharedButtonTextStyles()
767791

768792
return (
769-
<Text {...rest} style={[a.text_center, textStyles, style]}>
793+
<Text
794+
{...rest}
795+
style={[a.text_center, textStyles, style]}
796+
numberOfLines={2}>
770797
{children}
771798
</Text>
772799
)
@@ -783,7 +810,7 @@ export function ButtonIcon({
783810
position?: 'left' | 'right'
784811
size?: SVGIconProps['size']
785812
}) {
786-
const {size: buttonSize} = useButtonContext()
813+
const {size: buttonSize, shape} = useButtonContext()
787814
const textStyles = useSharedButtonTextStyles()
788815
const {iconSize, iconContainerSize} = React.useMemo(() => {
789816
/**
@@ -813,6 +840,20 @@ export function ButtonIcon({
813840
'2xl': 32,
814841
}[iconSizeShorthand]
815842

843+
if (shape === 'stacked') {
844+
const stackedIconSize = size
845+
? iconSize
846+
: {
847+
tiny: 16,
848+
small: 24,
849+
large: 24,
850+
}[buttonSize || 'small']
851+
return {
852+
iconSize: stackedIconSize,
853+
iconContainerSize: stackedIconSize,
854+
}
855+
}
856+
816857
/*
817858
* Goal here is to match rendered text size so that different size icons
818859
* don't increase button size
@@ -827,7 +868,7 @@ export function ButtonIcon({
827868
iconSize,
828869
iconContainerSize,
829870
}
830-
}, [buttonSize, size])
871+
}, [buttonSize, size, shape])
831872

832873
return (
833874
<View

src/view/screens/Storybook/Buttons.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,40 @@ export function Buttons() {
8282
))}
8383
</Fragment>
8484
))}
85+
86+
{['tiny', 'small'].map(size => (
87+
<View
88+
key={size}
89+
style={[a.flex_row, a.gap_md, a.align_start, {maxWidth: 350}]}>
90+
<Button
91+
label="stacked"
92+
color="secondary"
93+
shape="stacked"
94+
size={size as ButtonSize}
95+
style={[a.flex_1]}>
96+
<ButtonIcon icon={Globe} />
97+
<ButtonText>Post reply</ButtonText>
98+
</Button>
99+
<Button
100+
label="stacked"
101+
color="negative_subtle"
102+
shape="stacked"
103+
size={size as ButtonSize}
104+
style={[a.flex_1]}>
105+
<ButtonIcon icon={Globe} />
106+
<ButtonText>Delete</ButtonText>
107+
</Button>
108+
<Button
109+
label="stacked"
110+
color="primary"
111+
shape="stacked"
112+
size={size as ButtonSize}
113+
style={[a.flex_1]}>
114+
<ButtonIcon icon={Globe} />
115+
<ButtonText>Edit</ButtonText>
116+
</Button>
117+
</View>
118+
))}
85119
</View>
86120
)
87121
}

0 commit comments

Comments
 (0)