@spaced-out/ui-design-system
Version:
Sense UI components library
169 lines (156 loc) • 4.18 kB
Flow
// @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>
);
},
);