import * as React from 'react'; import type { ToggleButtonProps } from '../toggle-button'; import { styled } from '@mui/material/styles'; import type { Theme } from '../../@styles/theme-provider'; const ToggleButtonGroupRoot = styled('div', { name: 'ToggleButtonGroup', slot: 'Root' })(({ theme }: { theme: Theme }) => ({ display: 'inline-flex', '& > *:not(:first-of-type)': { marginLeft: theme.spacing(2) } })); /** * Determine if the toggle button value matches, or is contained in, the candidate group value. * @param value * @param candidate * @returns */ function isValueSelected( value: ToggleButtonProps['value'], candidate: ToggleButtonGroupProps['value'] ) { if (typeof candidate === 'undefined' || typeof value === 'undefined') { return false; } if (Array.isArray(candidate)) { return candidate.indexOf(value) >= 0; } return value === candidate; } export interface ToggleButtonGroupProps { /** * The content of the component. */ children: ToggleButtonProps['children']; /** * If `true`, the component is disabled. This implies that all ToggleButton children will be disabled. * @default false */ disabled?: boolean; /** * If `true`, allows multiple child ToggleButton values to be selected (checkbox behavior). * @default false */ multiple?: boolean; /** * The callback is fired when the value changes. * @param {React.MouseEvent} event The event source of the callback. * @param value Value of the selected button. */ onChange?: (event: React.MouseEvent, value: string | string[] | null) => void; /** * The currently selected value within the group or an array of selected * values when `multiple` is true (the value must have reference equality * with the option in order to be selected). */ value?: string | string[] | null; /** * Custom class name in case you need to add custom styles to the component. */ className?: string; } /** * Exclusive Selection vs. Multiple Selection * ------------------------------------------ * By default, ButtonToggleGroup acts like a radio-button group- only one * button can be selected. In this mode, the value of the ButtonToggleGroup * will reflect the value of the selected button. */ const ToggleButtonGroup = React.forwardRef(function ( props: ToggleButtonGroupProps, ref: React.Ref ) { const { children, disabled = false, multiple = false, value: _value, onChange, ...otherProps } = props; let value: ToggleButtonGroupProps['value']; if (!multiple) { value = _value || null; } else if (typeof _value === 'string') { value = [_value]; } else { value = _value || []; } const handleExclusiveChange = ( event: React.MouseEvent, buttonValue: ToggleButtonProps['value'] ) => { onChange?.(event, value === buttonValue ? null : buttonValue); }; const handleMultipleChange = ( event: React.MouseEvent, buttonValue: NonNullable ) => { if (!onChange) return; const index = value ? value.indexOf(buttonValue) : -1; let newValue: string[]; if (value && index >= 0) { newValue = (value as string[]).slice(); newValue.splice(index, 1); } else { newValue = value ? (value as string[]).concat(buttonValue) : [buttonValue]; } onChange(event, newValue); }; return ( {React.Children.map(children, child => { if (!React.isValidElement(child)) { return null; } return React.cloneElement(child, { disabled: child.props.disabled || disabled, selected: child.props.selected === undefined ? isValueSelected(child.props.value, value) : child.props.selected, onChange: multiple ? handleMultipleChange : handleExclusiveChange, ...child.props }); })} ); }); const m = React.memo(ToggleButtonGroup); export { m as ToggleButtonGroup };