Files
ADS3-Design-System/src/components/atoms/IconButton/IconButton.tsx
Richie 722475215d Reorganise components into atoms/molecules/organisms and fix Input icon colours
Moved all 17 components from ui/ into atomic design tiers: atoms (Button,
IconButton, Input, Textarea, Select, Checkbox, Radio, Switch, Badge, Tag,
Chip, Tooltip) and molecules (Alert, Accordion, Card, Dialog, Popover).
Updated all Storybook titles and cross-component imports. Changed Input
icons to primary-dark and replaced palette token references with semantic
tokens.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-22 09:10:12 +10:00

88 lines
2.7 KiB
TypeScript

import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from 'react'
import { cn } from '@/lib/utils'
export interface IconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
variant?: 'primary' | 'secondary' | 'tertiary'
intent?: 'default' | 'danger' | 'neutral'
size?: 'default' | 'large' | 'compact' | 'small' | 'xsmall'
shape?: 'circle' | 'square'
icon: ReactNode
'aria-label': string
}
const variantIntentStyles: Record<string, Record<string, string>> = {
primary: {
default: 'bg-button-default text-white hover:bg-button-default/90 active:bg-button-default/80',
danger: 'bg-button-danger text-white hover:bg-button-danger/90 active:bg-button-danger/80',
neutral: 'bg-button-neutral text-white hover:bg-button-neutral/90 active:bg-button-neutral/80',
},
secondary: {
default:
'border-2 border-button-default text-button-default hover:bg-button-default/5 active:bg-button-default/10',
danger:
'border-2 border-button-danger text-button-danger hover:bg-button-danger/5 active:bg-button-danger/10',
neutral:
'border-2 border-button-neutral text-button-neutral hover:bg-button-neutral/5 active:bg-button-neutral/10',
},
tertiary: {
default: 'text-button-default hover:bg-button-default/5 active:bg-button-default/10',
danger: 'text-button-danger hover:bg-button-danger/5 active:bg-button-danger/10',
neutral: 'text-button-neutral hover:bg-button-neutral/5 active:bg-button-neutral/10',
},
}
const sizeStyles: Record<string, string> = {
large: 'size-14',
default: 'size-12',
compact: 'size-10',
small: 'size-8',
xsmall: 'size-6',
}
const iconSizeStyles: Record<string, string> = {
large: 'size-6',
default: 'size-6',
compact: 'size-[18px]',
small: 'size-4',
xsmall: 'size-3.5',
}
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
(
{
variant = 'primary',
intent = 'default',
size = 'default',
shape = 'circle',
icon,
disabled,
className,
...props
},
ref,
) => {
return (
<button
ref={ref}
disabled={disabled}
className={cn(
'inline-flex items-center justify-center transition-colors',
shape === 'circle' ? 'rounded-full' : 'rounded-lg',
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-button-default',
sizeStyles[size],
variantIntentStyles[variant][intent],
disabled && 'pointer-events-none opacity-50',
className,
)}
{...props}
>
<span className={cn('shrink-0 [&>svg]:size-full', iconSizeStyles[size])}>
{icon}
</span>
</button>
)
},
)
IconButton.displayName = 'IconButton'