'use client' import { createButton } from '@gluestack-ui/button' import { PrimitiveIcon, UIIcon } from '@gluestack-ui/icon' import type { VariantProps } from '@gluestack-ui/nativewind-utils' import { tva } from '@gluestack-ui/nativewind-utils/tva' import { useStyleContext, withStyleContext } from '@gluestack-ui/nativewind-utils/withStyleContext' import { cssInterop } from 'nativewind' import * as PhosphorIcons from 'phosphor-react-native' import React from 'react' import { ActivityIndicator, Pressable, Text, View } from 'react-native' import { variables } from '@/figma-vars' const SCOPE = 'BUTTON' const Root = withStyleContext(Pressable, SCOPE) const UIButton = createButton({ Root: Root, Text, Group: View, Spinner: ActivityIndicator, Icon: UIIcon, }) cssInterop(PrimitiveIcon, { className: { target: 'style', nativeStyleToProp: { height: true, width: true, fill: true, color: 'classNameColor', stroke: true, }, }, }) const buttonStyle = tva({ base: 'group/button flex-row items-center justify-center', variants: { variant: { primary: 'bg-primary-background data-[active=true]:bg-primary-active data-[disabled=true]:bg-disabled', secondary: 'bg-secondary-background data-[active=true]:bg-secondary-active data-[disabled=true]:bg-disabled', subtle: 'bg-subtle-background data-[active=true]:bg-subtle-active data-[disabled=true]:bg-disabled', ghost: 'bg-ghost-background data-[active=true]:bg-ghost-active data-[disabled=true]:bg-disabled', outline: 'border-outline-border bg-outline-background data-[active=true]:bg-outline-active data-[disabled=true]:bg-disabled', destructive: 'bg-destructive-background data-[active=true]:bg-destructive-active data-[disabled=true]:bg-disabled', }, isLoading: { false: '', true: '', }, iconOnly: { false: '', true: '', }, size: { sm: 'h-[var(--button-height-sm)] gap-[var(--button-spacing-sm)] rounded-[var(--button-radius-sm)] px-[var(--button-padding-horizontal-sm)]', md: 'h-[var(--button-height-md)] gap-[var(--button-spacing-md)] rounded-[var(--button-radius-md)] px-[var(--button-padding-horizontal-md)]', lg: 'h-[var(--button-height-lg)] gap-[var(--button-spacing-lg)] rounded-[var(--button-radius-lg)] px-[var(--button-padding-horizontal-lg)]', }, }, compoundVariants: [ { variant: 'primary', isLoading: true, class: 'bg-primary-loading', }, { variant: 'secondary', isLoading: true, class: 'bg-secondary-loading', }, { variant: 'subtle', isLoading: true, class: 'bg-subtle-loading', }, { variant: 'ghost', isLoading: true, class: 'bg-ghost-loading', }, { variant: 'outline', isLoading: true, class: 'bg-outline-loading', }, { variant: 'destructive', isLoading: true, class: 'bg-destructive-loading', }, { iconOnly: true, size: 'sm', class: 'size-[var(--icon-only-button-height-sm)] gap-[var(--icon-only-button-spacing-sm)] rounded-[var(--icon-only-button-radius-sm)] px-[var(--icon-only-button-padding-horizontal-sm)]', }, { iconOnly: true, size: 'md', class: 'size-[var(--icon-only-button-height-md)] gap-[var(--icon-only-button-spacing-md)] rounded-[var(--icon-only-button-radius-md)] px-[var(--icon-only-button-padding-horizontal-md)]', }, { iconOnly: true, size: 'lg', class: 'size-[var(--icon-only-button-height-md)] gap-[var(--icon-only-button-spacing-lg)] rounded-[var(--icon-only-button-radius-lg)] px-[var(--icon-only-button-padding-horizontal-md)]', }, { variant: 'outline', size: 'sm', iconOnly: false, class: 'border-[length:var(--button-border-sm)]', }, { variant: 'outline', size: 'md', iconOnly: false, class: 'border-[length:var(--button-border-md)]', }, { variant: 'outline', size: 'lg', iconOnly: false, class: 'border-[length:var(--button-border-lg)]', }, { variant: 'outline', size: 'sm', iconOnly: true, class: 'border-[length:var(--icon-only-button-border-sm)]', }, { variant: 'outline', size: 'md', iconOnly: true, class: 'border-[length:var(--icon-only-button-border-md)]', }, { variant: 'outline', size: 'lg', iconOnly: true, class: 'border-[length:var(--icon-only-button-border-lg)]', }, ], }) const activityIndicatorStyle = tva({ base: '', parentVariants: { variant: { primary: '', secondary: '', subtle: '', ghost: '', outline: '', destructive: '', }, size: { sm: 'h-0', md: '', lg: '', }, }, }) const buttonTextStyle = tva({ base: 'text-typography-0 font-semibold', parentVariants: { variant: { primary: 'text-primary-foreground data-[disabled=true]:text-disabled-foreground', secondary: 'text-secondary-foreground data-[disabled=true]:text-disabled-foreground', subtle: 'text-subtle-foreground data-[disabled=true]:text-disabled-foreground', ghost: 'text-ghost-foreground data-[disabled=true]:text-disabled-foreground', outline: 'text-outline-foreground data-[disabled=true]:text-disabled-foreground', destructive: 'text-destructive-foreground data-[disabled=true]:text-disabled-foreground', }, size: { sm: 'typography-small', md: 'typography-medium', lg: 'typography-large', }, }, }) const buttonIconStyle = tva({ base: '', parentVariants: { variant: { primary: 'fill-primary-foreground', secondary: 'fill-secondary-foreground', subtle: 'fill-subtle-foreground', ghost: 'fill-ghost-foreground', outline: 'fill-outline-foreground', destructive: 'fill-destructive-foreground', }, size: { sm: 'size-icon-size-sm', md: 'size-icon-size-md', lg: 'size-icon-size-lg', }, }, }) type IButtonTextProps = React.ComponentPropsWithoutRef & VariantProps & { className?: string } const ButtonText = React.forwardRef, IButtonTextProps>( ({ className, variant, size, ...props }, ref) => { const { variant: parentVariant, size: parentSize } = useStyleContext(SCOPE) return ( ) }, ) const activityIndicatorColors = { primary: variables['theme-color']['primary-foreground'], secondary: variables['theme-color']['secondary-foreground'], subtle: variables['theme-color']['subtle-foreground'], ghost: variables['theme-color']['ghost-foreground'], outline: variables['theme-color']['outline-foreground'], destructive: variables['theme-color']['destructive-foreground'], } const ButtonSpinner = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & VariantProps & { className?: string } >(({ className, ...props }, ref) => { const { variant: parentVariant, size: parentSize } = useStyleContext(SCOPE) const color = activityIndicatorColors[parentVariant as keyof typeof activityIndicatorColors] return ( ) }) type IButtonIcon = React.ComponentPropsWithoutRef & VariantProps & { className?: string | undefined as?: React.ElementType height?: number width?: number weight?: PhosphorIcons.IconWeight } const ButtonIcon = React.forwardRef, IButtonIcon>( ({ className, size, ...props }, ref) => { const { variant: parentVariant, size: parentSize } = useStyleContext(SCOPE) return ( ) }, ) type PhosphorIconsList = Omit type IButtonProps = Omit, 'context'> & VariantProps & { className?: string leadingIcon?: keyof PhosphorIconsList | React.ReactElement trailingIcon?: keyof PhosphorIconsList | React.ReactElement leadingIconWeight?: PhosphorIcons.IconWeight trailingIconWeight?: PhosphorIcons.IconWeight } const Button = React.forwardRef, IButtonProps>( ( { className, variant = 'primary', size = 'md', isLoading = false, iconOnly = false, leadingIcon, trailingIcon, ...props }, ref, ) => { return ( {leadingIcon && typeof leadingIcon === 'string' ? ( ) : ( leadingIcon )} {isLoading && } {props.children && ( {props.children as React.ReactNode} )} {trailingIcon && typeof trailingIcon === 'string' ? ( ) : ( trailingIcon )} ) }, ) Button.displayName = 'Button' ButtonText.displayName = 'ButtonText' ButtonSpinner.displayName = 'ButtonSpinner' ButtonIcon.displayName = 'ButtonIcon' export { Button, ButtonText, ButtonSpinner, ButtonIcon }