import * as React from 'react' ; import { Platform , TouchableOpacityProps as RNTouchableOpacityProps } from 'react-native' ; import ConditionalWrap from 'conditional-wrap' ; import { createComponent , createElement , createHook } from 'bumbag/utils' ; import { ButtonType , Omit , Size , Palette } from 'bumbag/types' ; import { Box , BoxProps } from '../Box' ; import { IconProps } from '../Icon' ; import { SpinnerProps } from '../Spinner/Spinner' ; import { throttle as _throttle } from '../utils/throttle' ; import * as styles from './Button.styles' ; import { ButtonIcon } from './ButtonIcon' ; import { ButtonText } from './ButtonText' ; export type LocalButtonProps = { /** Icon that appears on the right side of the button. */ iconAfter ? : IconProps [ 'icon' ] ; iconAfterProps ? : Omit < IconProps , 'icon' > ; /** Icon that appears on the left side of the button. */ iconBefore ? : IconProps [ 'icon' ] ; iconBeforeProps ? : Omit < IconProps , 'icon' > ; /** Adds a loading indicator to the button. */ isLoading ? : boolean ; /** Makes the button not interactable. */ isStatic ? : boolean ; palette ? : Palette ; size ? : Size ; /** Custom props for the isLoading spinner. */ spinnerProps ? : SpinnerProps ; /** Sets a throttle for the press event (`onPress`) to be only invoked at most once every `x` milliseconds.  */ throttle ? : boolean | number ; type ? : ButtonType ; } ; export type ButtonProps = BoxProps & RNTouchableOpacityProps & LocalButtonProps ; export const ButtonContext = React . createContext < LocalButtonProps > ( { } ) ; const useProps = createHook < ButtonProps > ( ( props ) => { const { children , color , iconAfter , iconAfterProps , iconBefore , iconBeforeProps , isLoading , isStatic , onPress , palette , size , spinnerProps , throttle , variant , } = props ; const boxProps = Box . useProps ( props ) ; const activeOpacity = Platform . OS === 'web' || isStatic ? '1' : props . activeOpacity ; let spinnerColor = palette ; if ( props . variant === 'default' ) { spinnerColor = `${ props . palette }Inverted` ; } if ( props . palette === 'default' ) { spinnerColor = 'defaultInverted' ; } const contextValue = React . useMemo ( ( ) => ( { color , palette , size , variant , } ) , [ color , palette , size , variant ] ) ; const isString = typeof children === 'string' || ( Array . isArray ( children ) && ! children . some ?. ( ( child ) => typeof child !== 'string' ) ) ; return { ... boxProps , onPress : _throttle ( onPress , throttle ) , children : ( < ButtonContext . Provider value = { contextValue } > < React . Fragment > { props . isLoading && < styles . ButtonSpinner color = { spinnerColor } { ... spinnerProps } />} < ConditionalWrap condition = { isLoading } wrap = { ( children ) => < Box opacity = "0" > { children } < /Box>}> < React . Fragment > { iconBefore && ( < ButtonIcon isBefore icon = { iconBefore } { ... iconBeforeProps } />)} { isString ? ( < ButtonText > { children } < /ButtonText>) : (children)} { iconAfter && ( < ButtonIcon isAfter icon = { iconAfter } { ... iconAfterProps } />)} < /React.Fragment> < /ConditionalWrap> < /React.Fragment> < /ButtonContext.Provider>), activeOpacity , } ; } , { defaultProps : { _hover : true , _focus : true , _active : true , _hoveractive : true , disabled : false , iconAfter : undefined , iconBefore : undefined , isLoading : false , isStatic : false , variant : 'default' , palette : 'default' , size : 'default' , type : 'button' , } , themeKey : 'native.Button' , } ) ; export const Button = createComponent < ButtonProps > ( ( props ) => { const htmlProps = useProps ( props ) ; return createElement ( { children : props . children , component : styles . StyledButton , htmlProps , } ) ; } , { attach : { useProps , displayName : 'native.Button' , } , themeKey : 'native.Button' , } ) ;