import { forwardRef, type MouseEvent } from 'react' import styled from 'styled-components' import PrimitiveButton, { type ButtonProps as PrimitiveButtonProps, } from '../Primitives/PrimitiveButton/PrimitiveButton' import Block from '../Primitives/Block/Block' import Text from '../Text/Text' import type { ButtonProps } from './types' import { ButtonSize, ButtonState, ButtonSubType, ButtonType } from './types' import type { ButtonTokensType } from './button.tokens' import { LoaderCircle } from 'lucide-react' import { useResponsiveTokens } from '../../hooks/useResponsiveTokens' import { FOUNDATION_THEME } from '../../tokens' const StyledButtonText = styled.span` display: flex; align-items: center; justify-content: center; gap: ${FOUNDATION_THEME.unit[6]}; button:active & { transform: translateY(${FOUNDATION_THEME.unit[1]}); } ` const VisuallyHidden = styled.span` position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0; ` export type ButtonBaseProps = Omit< ButtonProps, 'showSkeleton' | 'skeletonVariant' > & { isSkeleton?: boolean tokens?: ButtonTokensType } const formatLineHeight = ( value?: number | string | null ): string | undefined => { if (value === undefined || value === null) return undefined return typeof value === 'number' ? `${value}px` : value } const ButtonBase = forwardRef( (props, ref) => { const { buttonType = ButtonType.PRIMARY, size = ButtonSize.SMALL, subType = ButtonSubType.DEFAULT, text, leadingIcon, trailingIcon, disabled, onClick, loading, buttonGroupPosition, fullWidth, width, justifyContent = 'center', state = ButtonState.DEFAULT, isSkeleton = false, tokens, ...htmlProps } = props const defaultButtonTokens = useResponsiveTokens('BUTTON') const buttonTokens = tokens ?? defaultButtonTokens const getBorderRadius = () => { const variantBorderRadius = buttonTokens.borderRadius[size][buttonType][subType].default if (buttonGroupPosition === undefined) return variantBorderRadius if (buttonGroupPosition === 'left') { return `${variantBorderRadius} 0 0 ${variantBorderRadius}` } else if (buttonGroupPosition === 'right') { return `0 ${variantBorderRadius} ${variantBorderRadius} 0` } return `0px 0px 0px 0px` } const isLoading = loading && !isSkeleton const isDisabled = isSkeleton ? true : disabled const paddingTokens = buttonTokens.padding[size][buttonType][subType] const lineHeight = formatLineHeight( FOUNDATION_THEME.font.size.body.md.lineHeight ) const buttonStatus = isLoading ? 'loading' : isDisabled ? 'disabled' : 'enabled' const handleClick = (event: MouseEvent) => { if (isSkeleton || isDisabled || isLoading) return onClick?.(event) } const isInButtonGroup = buttonGroupPosition !== undefined const buttonHeight = subType === ButtonSubType.INLINE ? 'fit-content' : isInButtonGroup ? undefined : 'fit-content' const iconMaxHeight = subType === ButtonSubType.INLINE ? '100%' : buttonTokens.slotMaxHeight[size] const buttonProps: Partial< Pick< PrimitiveButtonProps, | 'display' | 'alignItems' | 'justifyContent' | 'width' | 'height' | 'gap' > > & { ref?: React.Ref onClick?: (event: MouseEvent) => void } = { ref, onClick: handleClick, display: 'flex', alignItems: 'center', justifyContent, width: fullWidth ? '100%' : (width ?? 'fit-content'), gap: buttonTokens.gap, } if (buttonHeight !== undefined) { buttonProps.height = buttonHeight } return ( {isLoading ? ( <> ) } ) ButtonBase.displayName = 'ButtonBase' export default ButtonBase