import {useEffect, useRef} from 'react'; import {css, Theme} from '@emotion/react'; import styled, {Interpolation} from '@emotion/styled'; import InteractionStateLayer from 'sentry/components/interactionStateLayer'; import {FormSize} from 'sentry/utils/theme'; type CheckboxProps = React.InputHTMLAttributes; interface Props extends Omit { /** * Is the checkbox active? Supports 'indeterminate' */ checked?: CheckboxProps['checked'] | 'indeterminate'; /** * Styles to be applied to the hidden element. */ inputCss?: Interpolation; /** * The size of the checkbox. Defaults to 'sm'. */ size?: FormSize; } type CheckboxConfig = { borderRadius: string; box: string; icon: string; }; const checkboxSizeMap: Record = { xs: {box: '12px', borderRadius: '2px', icon: '10px'}, sm: {box: '16px', borderRadius: '4px', icon: '12px'}, md: {box: '22px', borderRadius: '6px', icon: '18px'}, }; const Checkbox = ({ className, inputCss, checked = false, size = 'sm', ...props }: Props) => { const checkboxRef = useRef(null); // Support setting the indeterminate value, which is only possible through // setting this attribute useEffect(() => { if (checkboxRef.current) { checkboxRef.current.indeterminate = checked === 'indeterminate'; } }, [checked]); return ( {checked === true && ( )} {checked === 'indeterminate' && ( )} {!props.disabled && ( )} ); }; const Wrapper = styled('div')<{checked: Props['checked']; size: FormSize}>` position: relative; cursor: pointer; display: inline-flex; justify-content: flex-start; color: ${p => (p.checked ? p.theme.white : p.theme.textColor)}; border-radius: ${p => checkboxSizeMap[p.size].borderRadius}; `; const HiddenInput = styled('input')` position: absolute; opacity: 0; top: 0; left: 0; height: 100%; width: 100%; margin: 0; padding: 0; cursor: pointer; &.focus-visible + * { ${p => p.checked ? ` box-shadow: ${p.theme.focus} 0 0 0 3px; ` : ` border-color: ${p.theme.focusBorder}; box-shadow: ${p.theme.focusBorder} 0 0 0 1px; `} } &:disabled + * { ${p => p.checked ? css` background: ${p.theme.disabled}; ` : css` background: ${p.theme.backgroundSecondary}; border-color: ${p.theme.disabledBorder}; `} } `; const StyledCheckbox = styled('div')<{ checked: Props['checked']; size: FormSize; }>` position: relative; display: flex; align-items: center; justify-content: center; color: inherit; box-shadow: ${p => p.theme.dropShadowMedium} inset; width: ${p => checkboxSizeMap[p.size].box}; height: ${p => checkboxSizeMap[p.size].box}; border-radius: ${p => checkboxSizeMap[p.size].borderRadius}; pointer-events: none; ${p => p.checked ? css` background: ${p.theme.active}; border: 0; ` : css` background: ${p.theme.background}; border: 1px solid ${p.theme.gray200}; `} `; const VariableWeightIcon = styled('svg')<{size: string}>` width: ${p => p.size}; height: ${p => p.size}; fill: none; stroke-linecap: round; stroke-linejoin: round; stroke: ${p => p.theme.white}; stroke-width: calc(1.4px + ${p => p.size} * 0.04); `; export default Checkbox;