import * as React from 'react'; import classNames from 'classnames'; import { Button as CoreButton, type ButtonProps as CoreButtonProps } from '@shoptet/ui-core-web'; import type { Exact, SafeOmit, SafePick } from '@shoptet/utils'; import type { OmitPrivate, SafeExtract, Size, Variant } from '../../types'; import { Icon } from '../Icon/Icon'; import type { IconName } from '../Icon/IconName'; import { Loader } from '../Loader/Loader'; const DEFAULT_SIZE = 'sm'; const DEFAULT_VARIANT = 'primary'; const mapButtonSizeToIconSize = { sm: 'sm', md: 'md', lg: 'md' } as const; const mapButtonSizeToLoaderSize = { sm: 'sm', md: 'md', lg: 'md' } as const; export interface ButtonInheritedCoreButtonProps extends OmitPrivate> {} export interface ButtonInheritedButtonProps extends OmitPrivate> {} export interface ButtonOwnProps { /** * Variant of the button. * @default 'primary' */ variant?: SafeExtract; /** * Size of the button. * @default 'sm' */ size?: SafeExtract; /** * Displays an icon before the label. * @default undefined */ iconLeft?: IconName; /** * Displays an icon after the label. * @default undefined */ iconRight?: IconName; children?: React.ReactNode; /** * @deprecated - Use iconLeft or iconRight instead */ icon?: IconName; /** * Displays a loader while hiding its contents and disables all interactions. * @default false */ isLoading?: boolean; } export interface ButtonProps extends ButtonOwnProps, ButtonInheritedCoreButtonProps, ButtonInheritedButtonProps { form?: ButtonInheritedButtonProps['form']; id?: ButtonInheritedButtonProps['id']; onBlur?: ButtonInheritedButtonProps['onBlur']; onClick?: ButtonInheritedButtonProps['onClick']; onFocus?: ButtonInheritedButtonProps['onFocus']; /** * @default 'button' */ type?: ButtonInheritedButtonProps['type']; value?: ButtonInheritedButtonProps['value']; } export const getButtonClassName = < T extends ButtonOwnProps & ButtonInheritedCoreButtonProps & SafePick, >({ variant = DEFAULT_VARIANT, size = DEFAULT_SIZE, icon, iconLeft, iconRight, isLoading, className, }: T) => { return classNames( 'btn', `btn-${variant}`, `btn-${size}`, { [`btn--iconLeft`]: iconLeft, [`btn--iconRight`]: iconRight, [icon ?? '']: icon, [`btn--isLoading`]: isLoading, }, className ); }; export const getButtonProps = ({ variant = DEFAULT_VARIANT, size = DEFAULT_SIZE, icon, iconLeft, iconRight, children, isLoading, ...props }: T) => { return { ...props, className: getButtonClassName({ className: 'className' in props && typeof props.className === 'string' ? props.className : undefined, icon, iconLeft, iconRight, isLoading, size, variant, }), children: ( <> {iconLeft && } {children} {iconRight && } ), }; }; export const Button = React.forwardRef(function Button(props: ButtonProps, ref) { const buttonProps = getButtonProps(props); buttonProps satisfies Exact< SafeOmit & SafePick & ButtonInheritedButtonProps, typeof buttonProps >; return ( {props.isLoading && } {buttonProps.children} ); });