import React, { ChangeEvent, MouseEvent, useContext, useEffect, useState, useId, } from "react"; import classnames from "classnames"; import { Box } from "../Box"; import { Icon, ICON_TYPE } from "../Icon"; import { Label } from "../Label"; import { Text } from "../Text"; import { Input, InputProps } from "../Input"; import { Stack, StackProps } from "../Stack"; import { ORIENTATION } from "../../types"; import { bem } from "../../utilities/bem"; import isUndefined from "../../utilities/isUndefined"; import { FormGroupContext } from "../FormGroup/FormGroupContext"; const cn = "Checkbox"; export type CheckboxProps = Omit & { label?: string; /** * Hides required styling on Label if it is within a FormGroupContext with * `isRequired` equal to true */ hideRequiredStyles?: boolean; /** * Shows required styling on Label */ isRequired?: boolean; /** * Determine if the checkbox is checked. An indeterminate checkbox can be * displayed by passing the string "indeterminate" instead of true/false. */ checked?: boolean | "indeterminate"; /** * Determines the background color of the checkbox when checked. */ color?: "brandGreen" | "blue"; }; const CheckboxGroup = (props: StackProps) => { const { orientation = ORIENTATION.VERTICAL, ...rest } = props; return ; }; export enum CheckboxState { CHECKED = "checked", UNCHECKED = "unchecked", INDETERMINATE = "indeterminate", } export const Checkbox = ({ checked, children, className, disabled = false, id: idProp, label, onBlur, onChange = () => {}, onClick, onFocus, hideRequiredStyles = true, isRequired = undefined, tabIndex, theme: themeProp, color = "brandGreen", ...rest }: CheckboxProps) => { const { isRequired: isRequiredContext, theme: themeContext } = useContext( FormGroupContext, ); const fallbackId = `checkbox-${useId()}`; const id = isUndefined(idProp) ? fallbackId : idProp; const theme = themeProp || themeContext; const classes = classnames([bem(cn), { "is-disabled": disabled }, className]); const handleClick = (event: MouseEvent): void => { if (!disabled && onClick) { onClick(event); } }; let calculatedRequired = false; if (isRequired !== undefined) { calculatedRequired = isRequired; } else if (isRequiredContext !== undefined) { calculatedRequired = isRequiredContext; } const [checkedState, setCheckedState] = useState( CheckboxState.UNCHECKED, ); useEffect(() => { if (checked) { if (typeof checked === "string" && checked === "indeterminate") { setCheckedState(CheckboxState.INDETERMINATE); } else { setCheckedState(CheckboxState.CHECKED); } } }, [checked]); const handleChange = (event: ChangeEvent) => { let updatedChecked; if (checkedState === CheckboxState.CHECKED) { updatedChecked = CheckboxState.UNCHECKED; } else if (checkedState === CheckboxState.UNCHECKED) { updatedChecked = CheckboxState.CHECKED; } else if (checkedState === CheckboxState.INDETERMINATE) { updatedChecked = CheckboxState.INDETERMINATE; } else { updatedChecked = CheckboxState.UNCHECKED; } setCheckedState(updatedChecked as CheckboxState); if (onChange) { onChange(event); } }; const ariaChecked = () => { if (checkedState === CheckboxState.INDETERMINATE) { return "mixed"; } if (checkedState === CheckboxState.CHECKED) { return "true"; } return "false"; }; const bareCheckbox = ( ); const checkboxWithLabel = label || children ? ( ) : ( bareCheckbox ); return ( {checkboxWithLabel} ); }; Checkbox.Group = CheckboxGroup;