import React from 'react' import { Pressable, Text, PressableProps, TextProps, ViewStyle, } from 'react-native' import {atoms, tokens, web, native} from '#/alf' export type ButtonType = 'primary' | 'secondary' | 'negative' export type ButtonSize = 'small' | 'large' export type VariantProps = { /** * The presentation styles of the button */ type?: ButtonType /** * The size of the button */ size?: ButtonSize } export type ButtonProps = Omit & VariantProps & { children: | ((props: { state: { pressed: boolean hovered: boolean focused: boolean } props: VariantProps & { disabled?: boolean } }) => React.ReactNode) | React.ReactNode | string } export type ButtonTextProps = TextProps & VariantProps & {disabled?: boolean} export function Button({ children, style, type, size, disabled = false, ...rest }: ButtonProps) { const [state, setState] = React.useState({ pressed: false, hovered: false, focused: false, }) const onPressIn = React.useCallback(() => { setState(s => ({ ...s, pressed: true, })) }, [setState]) const onPressOut = React.useCallback(() => { setState(s => ({ ...s, pressed: false, })) }, [setState]) const onHoverIn = React.useCallback(() => { setState(s => ({ ...s, hovered: true, })) }, [setState]) const onHoverOut = React.useCallback(() => { setState(s => ({ ...s, hovered: false, })) }, [setState]) const onFocus = React.useCallback(() => { setState(s => ({ ...s, focused: true, })) }, [setState]) const onBlur = React.useCallback(() => { setState(s => ({ ...s, focused: false, })) }, [setState]) const {baseStyles, hoverStyles} = React.useMemo(() => { const baseStyles: ViewStyle[] = [] const hoverStyles: ViewStyle[] = [] switch (type) { case 'primary': { if (disabled) { baseStyles.push({ backgroundColor: tokens.color.blue_300, }) } else { baseStyles.push({ backgroundColor: tokens.color.blue_500, }) } break } case 'secondary': { if (disabled) { baseStyles.push({ backgroundColor: tokens.color.gray_100, }) } else { baseStyles.push({ backgroundColor: tokens.color.gray_200, }) } break } case 'negative': { if (disabled) { baseStyles.push({ backgroundColor: tokens.color.red_400, }) } else { baseStyles.push({ backgroundColor: tokens.color.red_500, }) } break } default: } switch (size) { case 'large': { baseStyles.push( atoms.py_md, atoms.px_xl, atoms.rounded_md, atoms.gap_sm, ) break } case 'small': { baseStyles.push( atoms.py_sm, atoms.px_md, atoms.rounded_sm, atoms.gap_xs, ) break } default: } return { baseStyles, hoverStyles, } }, [type, size, disabled]) const childProps = React.useMemo( () => ({ state, props: { type, size, disabled: disabled || false, }, }), [state, type, size, disabled], ) return ( {typeof children === 'string' ? ( {children} ) : typeof children === 'function' ? ( children(childProps) ) : ( children )} ) } export function ButtonText({ children, style, type, size, disabled, ...rest }: ButtonTextProps) { const textStyles = React.useMemo(() => { const baseStyles = [] switch (type) { case 'primary': { baseStyles.push({color: tokens.color.white}) break } case 'secondary': { if (disabled) { baseStyles.push({ color: tokens.color.gray_500, }) } else { baseStyles.push({ color: tokens.color.gray_700, }) } break } case 'negative': { baseStyles.push({ color: tokens.color.white, }) break } default: } switch (size) { case 'small': { baseStyles.push( atoms.text_sm, web({paddingBottom: 1}), native({marginTop: 2}), ) break } case 'large': { baseStyles.push( atoms.text_md, web({paddingBottom: 1}), native({marginTop: 2}), ) break } default: } return baseStyles }, [type, size, disabled]) return ( {children} ) }