import { ChangeEvent, ReactNode } from 'react'; import styled from 'styled-components'; import { spacing, Stack } from '../../spacing'; import { Text } from '../text/Text.component'; import { Tooltip } from '../tooltip/Tooltip.component'; import { FocusVisibleStyle } from '../buttonv2/Buttonv2.component'; export type RadioOption = { value: string; label: string; disabled?: boolean; disabledReason?: ReactNode; }; type RadioGroupBaseProps = { name: string; value: string; onChange: (value: string) => void; options: RadioOption[]; disabled?: boolean; direction?: 'vertical' | 'horizontal'; }; type WithLegend = RadioGroupBaseProps & { label: string; 'aria-label'?: never; 'aria-labelledby'?: never; }; type WithAriaLabel = RadioGroupBaseProps & { label?: never; 'aria-label': string; 'aria-labelledby'?: never; }; type WithAriaLabelledBy = RadioGroupBaseProps & { label?: never; 'aria-label'?: never; 'aria-labelledby': string; }; export type RadioGroupProps = WithLegend | WithAriaLabel | WithAriaLabelledBy; const Fieldset = styled.fieldset<{ $direction: 'vertical' | 'horizontal' }>` border: 0; margin: 0; padding: 0; display: flex; flex-direction: ${({ $direction }) => $direction === 'horizontal' ? 'row' : 'column'}; gap: ${({ $direction }) => $direction === 'horizontal' ? spacing.r16 : spacing.r12}; `; const Group = styled.div<{ $direction: 'vertical' | 'horizontal' }>` display: flex; flex-direction: ${({ $direction }) => $direction === 'horizontal' ? 'row' : 'column'}; gap: ${({ $direction }) => $direction === 'horizontal' ? spacing.r16 : spacing.r12}; `; const Legend = styled.legend` padding: 0; margin-bottom: ${spacing.r8}; color: ${({ theme }) => theme.textPrimary}; `; const RadioInput = styled.input` transform: scale(1.5); `; const RadioLabel = styled.label<{ $disabled?: boolean }>` ${(props) => (props.$disabled ? 'opacity: 0.5;' : '')} [type='radio'] { width: 0.75rem; height: 0.75rem; color: ${(props) => props.theme.textPrimary}; vertical-align: middle; -webkit-appearance: none; -moz-appearance: none; appearance: none; background: none; border: 0; outline: 0; flex-grow: 0; border-radius: 50%; background-color: ${(props) => props.theme.backgroundLevel1}; transition: background 300ms; cursor: pointer; } [type='radio']::before { content: ''; color: transparent; display: block; width: inherit; height: inherit; border-radius: inherit; border: 0; background-color: transparent; box-shadow: inset 0 0 0 ${spacing.r1} ${(props) => props.theme.textSecondary}; } [type='radio']:checked { background-color: ${(props) => props.theme.selectedActive}; } [type='radio']:checked::before { box-shadow: none; background-image: radial-gradient( circle, ${(props) => props.theme.textPrimary} 35%, transparent 35% ); } [type='radio']:hover { ${(props) => !props.$disabled && `background-color: ${props.theme.highlight};`} } [type='radio']:hover::before { ${(props) => !props.$disabled && `box-shadow: inset 0 0 0 ${spacing.r1} ${props.theme.selectedActive};`} } [type='radio']:focus-visible:enabled { ${FocusVisibleStyle} } [type='radio']:checked:disabled { cursor: not-allowed; background-color: ${(props) => props.theme.selectedActive}; } [type='radio']:not(:checked):disabled { cursor: not-allowed; } `; type RadioOptionItemProps = { name: string; option: RadioOption; checked: boolean; groupDisabled?: boolean; onChange: (value: string) => void; }; const RadioOptionItem = ({ name, option, checked, groupDisabled, onChange, }: RadioOptionItemProps) => { const isDisabled = groupDisabled || option.disabled; const handleChange = (e: ChangeEvent) => { if (e.target.checked) onChange(option.value); }; const input = ( {option.label} ); if (isDisabled && option.disabledReason) { return ( {input} ); } return input; }; const RadioGroup = (props: RadioGroupProps) => { const { name, value, onChange, options, disabled, direction = 'vertical', } = props; const items = options.map((option) => ( )); if ('label' in props && props.label) { return (
{props.label} {items}
); } const ariaLabel = (props as WithAriaLabel)['aria-label']; const ariaLabelledBy = (props as WithAriaLabelledBy)['aria-labelledby']; return ( {items} ); }; export { RadioGroup };