UNPKG

@spaced-out/ui-design-system

Version:
135 lines (121 loc) 3.33 kB
// @flow strict import * as React from 'react'; import type {GroupAlign} from '../../types/common'; import classify from '../../utils/classify'; import css from './RadioButton.module.css'; /** * Note: * Do not wrap Radio in a label. For simplicity's sake, Radio uses an * internal label tag to handle click delegation to the hidden input, and * nesting labels is a bad idea. */ type ClassNames = $ReadOnly<{wrapper?: string, label?: string, radio?: string}>; export type RadioButtonProps = { children?: React.Node, value?: string, disabled?: boolean, focused?: boolean, name?: string, selectedValue?: string, align?: GroupAlign, classNames?: ClassNames, error?: boolean, onChange?: (newValue: string) => mixed, tabIndex?: number, ariaLabel?: string, }; export const RadioButton: React$AbstractComponent< RadioButtonProps, HTMLInputElement, > = React.forwardRef<RadioButtonProps, HTMLInputElement>( ( { children, disabled = false, value = '', focused = false, name = 'radio', selectedValue, onChange, align = 'vertical', classNames, error = false, tabIndex = 0, ariaLabel = '', ...props }: RadioButtonProps, forwardRef, ): React.Node => { const radioInput = React.createRef<HTMLInputElement>(); React.useImperativeHandle(forwardRef, () => radioInput.current); const checked = selectedValue === value; React.useEffect(() => { if (radioInput.current && focused) { radioInput.current.focus(); } }, [focused]); const onWrapClickHandler = () => { radioInput.current?.click(); }; const onChangeHandler = (e) => { if (!disabled) { onChange && onChange(e?.target?.value); } }; const onKeyDownHandler = (e) => { if (e.key === 'Enter') { onChangeHandler(e); } }; return ( <div className={classify( css.container, { [css.horizontalRadioButton]: align === 'horizontal-fixed', [css.fluidRadioButton]: align === 'horizontal-fluid', [css.containerDisabled]: disabled, }, classNames?.wrapper, )} onClick={onWrapClickHandler} > <input type="radio" checked={checked} disabled={disabled} className={classify( css.radio, { [css.unselected]: !checked && !disabled && !error, [css.selected]: checked && !disabled && !error, [css.disabled]: disabled, [css.error]: error && !disabled, }, classNames?.radio, )} ref={radioInput} onChange={onChangeHandler} onKeyDown={onKeyDownHandler} tabIndex={disabled ? -1 : tabIndex} name={name} value={value || ''} aria-label={ariaLabel || children} {...props} /> {React.Children.count(children) > 0 && ( <div color="secondary" className={classify( css.radioLabel, {[css.labelDisabled]: disabled}, classNames?.label, )} > {children} </div> )} </div> ); }, );