Skip to content

Instantly share code, notes, and snippets.

@izakfilmalter
Created December 20, 2022 13:42
Show Gist options
  • Save izakfilmalter/2357f7d89ccdb07153b7ff90d8101888 to your computer and use it in GitHub Desktop.
Save izakfilmalter/2357f7d89ccdb07153b7ff90d8101888 to your computer and use it in GitHub Desktop.
Tamagui Custom Button
import { GetProps, styled, TamaguiElement, themeable } from 'tamagui'
import {
ButtonFrame,
ButtonProps as TamaguiButtonProps,
buttonStaticConfig,
ButtonText,
useButton,
} from '@tamagui/button'
import { Variable } from '@tamagui/core'
import { B, matchOn, pipe } from '@steepleinc/fp'
import { colord, extend } from 'colord'
import mixPlugin from 'colord/plugins/mix'
import { ren } from '@steepleinc/app/helpers/size'
import { Platform } from 'react-native'
import { forwardRef, useCallback } from 'react'
extend([mixPlugin])
export const getStateBackgroundColors = (colorVar: Variable) => {
const color = colord(colorVar.val)
const pallet = pipe(
color.isLight(),
B.fold(
() => color.tints(16),
() => color.shades(16),
),
)
return {
hover: pallet[1].toHex(),
pressed: pallet[2].toHex(),
focus: pallet[6].toHex(),
}
}
export type ButtonVariants = 'contained' | 'outlined' | 'text' | 'fab'
export type ButtonSize = 'small' | 'medium' | 'large'
export type ButtonColor = 'primary' | 'secondary'
const CustomButtonFrame = styled(ButtonFrame, {
name: 'CustomButtonFrame',
variants: {
variant: {
contained: (z, y) => {
// We need the variable value here so that we can generate colors from it.
const backgroundColor = pipe(
y.props,
matchOn('color')<{ color: ButtonColor }, Variable>({
primary: (): Variable => y.theme.actionPrimaryBackground,
secondary: (): Variable => y.theme.actionSecondaryBackground,
}),
)
const { hover, pressed, focus } =
getStateBackgroundColors(backgroundColor)
return {
borderColor: backgroundColor,
borderWidth: ren(3),
backgroundColor,
hoverStyle: {
borderColor: hover,
backgroundColor: hover,
},
pressStyle: {
borderColor: pressed,
backgroundColor: pressed,
},
focusStyle: {
borderColor: focus,
backgroundColor,
},
}
},
outlined: (_z, _y) => {
return {}
},
text: (_z, _y) => {
return {}
},
fab: (_z, _y) => {
return {
circular: true,
}
},
},
btnsize: {
small: {
height: ren(40),
px: ren(12),
borderRadius: ren(6),
},
medium: {
height: ren(48),
px: ren(16),
borderRadius: ren(6),
},
large: {
height: ren(64),
px: ren(20),
borderRadius: ren(6),
},
},
color: {
primary: {},
secondary: {},
},
} as const,
defaultVariants: {
variant: 'contained',
btnsize: 'small',
color: 'primary',
},
})
const CustomButtonText = styled(ButtonText, {
name: 'CustomButtonText',
variants: {
variant: {
contained: (z, y) => {
const textColor = pipe(
y.props,
matchOn('color')<{ color: ButtonColor }, Variable>({
primary: () => y.theme.actionPrimaryText,
secondary: () => y.theme.actionSecondaryText,
}),
)
return {
color: textColor,
fontFamily: '$body',
fontSize: '$3',
lineHeight: '$3',
fontWeight: '$7',
pb: Platform.select({ web: ren(3), default: 0 }),
}
},
outlined: (_z, _y) => {
return {}
},
text: (_z, _y) => {
return {}
},
fab: (_z, _y) => {
return {
circular: true,
}
},
},
btnsize: {
small: {},
medium: {},
large: {},
},
color: {
primary: {},
secondary: {},
},
},
defaultVariants: {
variant: 'contained',
btnsize: 'small',
color: 'primary',
},
})
type CustomButtonProps = GetProps<typeof CustomButtonFrame>
type CustomButtonTextProps = GetProps<typeof CustomButtonText>
export type ButtonProps = TamaguiButtonProps &
CustomButtonProps &
CustomButtonTextProps
export const Button = themeable(
forwardRef<TamaguiElement, ButtonProps>((propsIn, ref) => {
const {
variant = 'contained',
btnsize = 'small',
color = 'primary',
} = propsIn
const LocalButtonText = useCallback(
(buttonTextProps) => (
<CustomButtonText
variant={variant}
btnsize={btnsize}
color={color}
{...buttonTextProps}
/>
),
[variant],
)
const { props } = useButton(
{ ...propsIn, scaleSpace: 0.2 },
{ Text: LocalButtonText },
)
return <CustomButtonFrame {...props} ref={ref} />
}),
)
export { ButtonFrame, ButtonText, useButton, buttonStaticConfig }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment