@spaced-out/ui-design-system
Version:
Sense UI components library
114 lines (104 loc) • 2.99 kB
Flow
// @flow strict
import * as React from 'react';
import classify from '../../utils/classify';
import css from './Toggle.module.css';
/**
* Note:
* Do not wrap Toggle in a label. For simplicity's sake, Toggle 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}>;
export type ToggleProps = {
name?: string,
classNames?: ClassNames,
children?: React.Node,
checked?: boolean,
onChange?: ({value: string, checked: boolean}) => mixed,
disabled?: boolean,
focused?: boolean,
value?: string,
ariaLabel?: string,
labelPosition?: 'left' | 'right',
};
export const Toggle: React$AbstractComponent<ToggleProps, HTMLInputElement> =
React.forwardRef<ToggleProps, HTMLInputElement>(
(
{
name = 'toggle',
value = '',
classNames,
children,
disabled,
checked,
focused,
onChange,
ariaLabel,
labelPosition = 'right',
...props
}: ToggleProps,
forwardRef,
): React.Node => {
const toggleInput = React.createRef<HTMLInputElement>();
React.useImperativeHandle(forwardRef, () => toggleInput.current);
const handleOnChange = () => {
if (!disabled) {
onChange &&
onChange({
value,
checked: !checked,
});
}
};
const onWrapClickHandler = (e) => {
e.nativeEvent.stopImmediatePropagation();
toggleInput.current?.click();
};
React.useEffect(() => {
if (toggleInput.current && focused) {
toggleInput.current.focus();
}
}, [focused]);
return (
<div
className={classify(
css.container,
{
[css.disabled]: disabled,
},
classNames?.wrapper,
)}
onClick={onWrapClickHandler}
>
{labelPosition === 'left' && React.Children.count(children) > 0 && (
<div className={classify(css.toggleLabel, classNames?.label)}>
{children}
</div>
)}
<span className={css.toggleWrap}>
<input
type="checkbox"
name={name}
value={value}
checked={checked}
onChange={handleOnChange}
ref={toggleInput}
disabled={disabled}
aria-label={ariaLabel || children}
{...props}
/>
<span
className={classify(css.toggle, {
[css.disabledButton]: disabled,
})}
/>
</span>
{labelPosition === 'right' && React.Children.count(children) > 0 && (
<div className={classify(css.toggleLabel, classNames?.label)}>
{children}
</div>
)}
</div>
);
},
);