UNPKG

@spaced-out/ui-design-system

Version:
169 lines (156 loc) 4.18 kB
// @flow strict import * as React from 'react'; import type {GroupAlign} from '../../types/common'; import classify from '../../utils/classify'; import {Icon} from '../Icon'; import css from './Checkbox.module.css'; type ClassNames = $ReadOnly<{ wrapper?: string, checkboxSquare?: string, label?: string, }>; /* Note: An indeterminate state has a higher priority. If true checkbox would be in an indeterminate state. If indeterminate is false, it will owner checked value. */ export type CheckboxProps = { name?: string, value?: string, children?: React.Node, checked?: boolean, indeterminate?: boolean, focused?: boolean, disabled?: boolean, error?: boolean, onChange?: ( {value: string, checked: boolean}, ?SyntheticEvent<HTMLElement>, ) => mixed, align?: GroupAlign, classNames?: ClassNames, tabIndex?: number, ariaLabel?: string, }; export const Checkbox: React$AbstractComponent< CheckboxProps, HTMLInputElement, > = React.forwardRef<CheckboxProps, HTMLInputElement>( ( { name = 'checkbox', value = '', children, checked = false, indeterminate = false, focused = false, disabled = false, error = false, onChange, align, classNames, tabIndex = 0, ariaLabel = '', }: CheckboxProps, forwardRef, ): React.Node => { const checkboxInput = React.createRef<HTMLInputElement>(); React.useImperativeHandle(forwardRef, () => checkboxInput.current); const handleOnClick = (event) => { if (!disabled) { onChange && onChange( { value, checked: !checked, }, event, ); } }; React.useEffect(() => { if (checkboxInput.current && focused) { checkboxInput.current.focus(); } }, [focused]); React.useEffect(() => { if (checkboxInput.current && indeterminate) { checkboxInput.current.indeterminate = true; } if (checkboxInput.current && !indeterminate) { checkboxInput.current.indeterminate = false; } }, [indeterminate]); return ( <div data-testid="checkbox" className={classify( css.checkboxContainer, { [css.disabled]: disabled, [css.horizontalCheckbox]: align === 'horizontal-fixed', [css.fluidCheckbox]: align === 'horizontal-fluid', }, classNames?.wrapper, )} onClick={handleOnClick} > <span className={classify( css.checkboxSquare, { [css.disabled]: disabled, }, classNames?.checkboxSquare, )} > <input tabIndex={disabled ? -1 : tabIndex} type="checkbox" checked={checked} readOnly ref={checkboxInput} className={classify(css.inputCheckbox)} disabled={disabled} name={name} aria-label={ariaLabel || children} /> <span className={classify(css.checkboxSquareInner, { [css.enabled]: !disabled && !error, [css.disabled]: disabled, [css.error]: error && !disabled, })} > {indeterminate && ( <Icon color={disabled ? 'disabled' : 'inversePrimary'} name="hyphen" size="small" type="regular" /> )} {checked && !indeterminate && ( <Icon color={disabled ? 'disabled' : 'inversePrimary'} name="check" size="small" type="regular" /> )} </span> </span> {React.Children.count(children) > 0 && ( <div className={classify( css.checkboxLabel, {[css.labelDisabled]: disabled}, classNames?.label, )} > {children} </div> )} </div> ); }, );