Instantly share code, notes, and snippets.
Created
December 20, 2022 13:42
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save izakfilmalter/2357f7d89ccdb07153b7ff90d8101888 to your computer and use it in GitHub Desktop.
Tamagui Custom Button
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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