import React from 'react'; import type { MouseEvent, RefObject } from 'react'; import { type IconType } from '../../icons'; import type { WithOptionalOnlyTextChild } from '../../../utils/childTypes'; type LinkRel = 'nofollow noindex' | 'nofollow' | 'noindex' | 'noopener nofollow noindex' | 'noopener nofollow' | 'noopener noindex' | 'noopener noreferrer nofollow noindex' | 'noopener noreferrer nofollow' | 'noopener noreferrer noindex' | 'noopener noreferrer' | 'noopener' | 'noreferrer nofollow noindex' | 'noreferrer nofollow' | 'noreferrer noindex' | 'noreferrer'; /** * Base properties interface for the polymorphic Button component that defines common props shared between button and link variants. * This interface serves as the foundation for type-safe polymorphic behavior, ensuring consistent API across different rendered elements. * All properties are optional except for the 'as' discriminator which determines the final component type and available props. */ export interface BaseProps { /** * Determines the HTML element type to render, either 'button' or 'link'. */ as: C; /** * Optional CSS class name to apply custom styling to the button component. */ className?: string; /** * Optional icon component to display alongside or instead of text content. */ icon?: IconType; /** * Optional class name for icon component to display alongside or instead of text content. */ iconClassName?: string; /** * Position of the icon relative to text content, defaults to 'start' if not specified. */ iconPosition?: 'end' | 'start'; /** * Whether the button should be disabled and non-interactive. */ isDisabled?: boolean; /** * Whether the button is in a loading state, typically used to show loading indicators and prevent user interaction during asynchronous operations. * When true, the button should display appropriate loading feedback to inform users that an action is being processed. * This property is commonly used with form submissions, API calls, or any other time-consuming operations to enhance user experience. * * @default false */ isLoading?: boolean; /** * Optional text to display when the button is in a loading state, providing users with contextual feedback about the ongoing operation. * This text replaces the normal button content during loading to inform users that an action is being processed. * When not provided, the button will show only the loading icon without additional text content. * * @default undefined */ loadingText?: string; /** * Size variant that controls the overall dimensions and spacing of the button component. * The 'normal' size provides standard button dimensions suitable for most use cases, while 'small' creates a more compact version for space-constrained layouts. * This property affects padding, font size, and overall component dimensions to maintain proper visual hierarchy and usability. * * @default 'normal' */ size?: 'large' | 'small'; /** * Data attribute for testing purposes, defaults to 'Button' if not provided. */ testId?: string; /** * Visual theme variant that affects color scheme, either 'light' or 'dark'. */ theme?: 'dark' | 'light'; /** * Visual style variant that determines the button's appearance and emphasis level. */ variant?: 'primary' | 'secondary'; /** * Width behavior controlling whether button takes content width or full container width. */ width?: 'auto' | 'full'; } /** * Properties interface for the Button component when rendered as a button element. * This interface defines button-specific props that are available when the 'as' prop is set to 'button'. * It includes form-related properties and prevents link-specific props through never types to ensure type safety. */ export interface ButtonProps { /** * The ID of the form element this button should be associated with for form submission. */ formId?: string; /** * Link destination URL - not available for button elements, enforced as never type. */ href?: never; /** * Click event handler function that receives the mouse event when the button is clicked. */ onClick?(e: MouseEvent): void; /** * React ref object to access the underlying button DOM element directly. */ ref?: RefObject; /** * Link relationship attributes - not available for button elements, enforced as never type. */ rel?: never; /** * Tab order index for keyboard navigation accessibility and focus management. */ tabIndex?: number; /** * Link target specification - not available for button elements, enforced as never type. */ target?: never; /** * HTML button type attribute that determines the button's behavior in forms. */ type?: 'button' | 'reset' | 'submit'; } /** * Properties interface for the Button component when rendered as a link element. * This interface defines link-specific props that are available when the 'as' prop is set to 'link'. * It includes navigation-related properties and prevents button-specific props through never types to ensure type safety. */ export interface LinkProps { /** * The ID of the form element - not available for link elements, enforced as never type. */ formId?: never; /** * The URL destination that the link should navigate to when clicked. */ href: string; /** * Click event handler function that receives the mouse event when the link is clicked. */ onClick?(e: MouseEvent): void; /** * React ref object to access the underlying anchor DOM element directly. */ ref?: RefObject; /** * Link relationship attributes that define the relationship between current document and linked resource for SEO and security purposes. */ rel?: LinkRel; /** * Tab order index for keyboard navigation accessibility and focus management. */ tabIndex?: number; /** * Specifies where to open the linked document when the link is clicked. */ target?: '_blank' | '_parent' | '_self' | '_top'; /** * HTML button type attribute - not available for link elements, enforced as never type. */ type?: never; } type ComponentProps = BaseProps & (C extends 'button' ? ButtonProps : C extends 'link' ? LinkProps : never); /** * A polymorphic Button component that can render as either a button or link element based on the `as` prop. * This component provides consistent styling and behavior across different use cases while maintaining semantic HTML. * It supports various visual variants, themes, icons, and accessibility features like proper focus management and ARIA attributes. * * @param props The component props. * @param props.as Determines whether to render as 'button' or 'link' element. * @param props.children The text content or child elements to display inside the button. * @param props.className Optional CSS class name to apply custom styling. * @param props.formId The ID of the form this button should be associated with (button only). * @param props.href The URL to navigate to when clicked (link only). * @param props.iconClassName Optional CSS class name to apply custom styling to the icon component. * @param props.icon Optional icon component to display alongside the button text. * @param props.iconPosition Position of the icon relative to text content, either 'start' or 'end'. * @param props.isDisabled Whether the button should be disabled and non-interactive. * @param props.isLoading Whether the button is in a loading state, typically used to show loading indicators and prevent user interaction during asynchronous operations. * @param props.loadingText Optional text to display when the button is in a loading state, providing users with contextual feedback about the ongoing operation. * @param props.onClick Click event handler function that receives the mouse event. * @param props.ref React ref object to access the underlying DOM element. * @param props.rel Link relationship attributes for SEO and security (link only). * @param props.size Size variant that controls the overall dimensions and spacing of the button component. * @param props.tabIndex Tab order index for keyboard navigation accessibility. * @param props.target Where to open the linked document (link only). * @param props.testId Data attribute for testing purposes, defaults to 'Button'. * @param props.theme Visual theme variant, either 'light' or 'dark'. * @param props.type Button type attribute for form submission behavior. * @param props.variant Visual style variant: 'primary', 'secondary'. * @param props.width Width behavior: 'auto' for content width or 'full' for 100% width. * @returns A styled button or link element wrapped in ButtonWrapper component. * * @example * ```tsx * // Primary button * * * // Link button with icon * * * // Icon-only button *