import React from 'react'; import clsx from 'clsx'; import { LinkArrow, LinkArrowVariant } from './LinkArrow'; export type LinkVariant = 'internal' | 'external' | 'inline'; export type LinkSize = 'small' | 'medium' | 'large'; export type LinkIconType = 'arrow' | 'external' | null; export interface LinkProps extends React.AnchorHTMLAttributes { /** * Link variant - internal, external, or inline * @default 'internal' */ variant?: LinkVariant; /** * Size of the link * @default 'medium' */ size?: LinkSize; /** * Icon type - arrow, external, or null * If null, icon is determined by variant (internal/external) * Arrow icons animate to chevron shape on hover * @default null */ icon?: LinkIconType; /** * Disabled state - prevents navigation and applies disabled styles * @default false */ disabled?: boolean; /** * Link URL (required) */ href: string; /** * Link text content */ children: React.ReactNode; } /** * Link Component * * A comprehensive link component supporting multiple sizes, icon types, and states. * Arrow icons animate to chevron shape on hover. * * Color states are handled automatically via CSS per theme: * * Light Mode: * - Enabled: Green 400 (#0DAA3E) * - Hover/Focus: Green 500 (#078139) + underline + arrow animates to chevron * - Active: Green 400 (#0DAA3E) + underline * - Visited: Lilac 400 (#7649E3) * - Disabled: Gray 400 (#A2A2A4) * - Focus outline: Black (#000000) * * Dark Mode: * - Enabled: Green 300 (#21E46B) * - Hover/Focus: Green 200 (#70EE97) + underline + arrow animates to chevron * - Active: Green 300 (#21E46B) + underline * - Visited: Lilac 300 (#C0A7FF) * - Disabled: Gray 500 (#838386) * - Focus outline: White (#FFFFFF) * * @see Link.md for full documentation * * @example * ```tsx * // Basic internal link (arrow animates to chevron on hover) * * View documentation * * * // External link * * External resource * * * // Disabled link * * Coming soon * * * // Inline link (no icon) * * Learn more * * ``` */ export const Link: React.FC = ({ variant = 'internal', size = 'medium', icon = null, disabled = false, href, children, className, onClick, ...rest }) => { // Determine icon type based on variant if not explicitly provided const getIconType = (): LinkArrowVariant | null => { if (icon === null) { // Auto-determine icon based on variant if (variant === 'external') { return 'external'; } if (variant === 'internal') { return 'internal'; // Default to internal arrow for internal variant } return null; // Inline links have no icon } // Map icon prop to LinkArrow variant if (icon === 'arrow') return 'internal'; if (icon === 'external') return 'external'; return null; }; const iconType = getIconType(); const shouldShowIcon = variant !== 'inline' && iconType !== null; const classes = clsx( 'bds-link', `bds-link--${variant}`, `bds-link--${size}`, { 'bds-link--disabled': disabled, }, className ); const handleClick = (e: React.MouseEvent) => { if (disabled) { e.preventDefault(); e.stopPropagation(); return; } onClick?.(e); }; return ( {children} {shouldShowIcon && ( )} ); }; Link.displayName = 'Link';