import React, { type ChangeEvent, Children, cloneElement, type FocusEvent, forwardRef, isValidElement, type MouseEvent, type ReactElement, type ReactNode, } from 'react'; import cn from 'classnames'; import { getDataTestId } from '@alfalab/core-components-shared'; import commonStyles from './index.module.css'; export type Direction = 'horizontal' | 'vertical'; export type CheckboxGroupType = 'checkbox' | 'tag'; export type BaseCheckboxGroupProps = { /** * Заголовок группы */ label?: ReactNode; /** * Направление */ direction?: Direction; /** * Тип компонента */ type?: CheckboxGroupType; /** * Дополнительный класс */ className?: string; /** * Отображение ошибки */ error?: ReactNode | boolean; /** * Текст подсказки снизу */ hint?: ReactNode; /** * Дочерние элементы. Ожидаются компоненты `Checkbox` или `Tag` */ children: ReactNode; /** * Обработчик изменения значения 'checked' одного из дочерних компонентов */ onChange?: ( event: ChangeEvent | MouseEvent, payload: { checked: boolean; name?: string; }, ) => void; /** * Обработчик блюра. */ onBlur?: (event: FocusEvent) => void; /** * Обработчик фокуса. */ onFocus?: (event: FocusEvent) => void; /** * Управление возможностью изменения состояния 'checked' дочерних компонентов CheckBox */ disabled?: boolean; /** * Идентификатор для систем автоматизированного тестирования */ dataTestId?: string; /** * Основные стили компонента. */ styles: { [key: string]: string }; }; export const BaseCheckboxGroup = forwardRef( ( { children, className, direction = 'vertical', label, error, hint, onChange, onBlur, onFocus, type = 'checkbox', dataTestId, disabled = false, styles, }, ref, ) => { const renderCheckbox = (child: ReactElement) => { const { name, checked, className: childClassName } = child.props; const handleChange = (event: ChangeEvent) => { if (onChange) { onChange(event, { name, checked: !checked }); } }; return cloneElement(child, { onChange: handleChange, disabled, ...child.props, className: cn(childClassName, commonStyles.checkbox), }); }; const renderTag = (child: ReactElement) => { const { name, checked } = child.props; const handleChange = (event: ChangeEvent | MouseEvent) => { if (onChange) { onChange(event, { name, checked: !checked }); } }; const clone = cloneElement(child, { onClick: handleChange, disabled, ...child.props }); return ( // eslint-disable-next-line jsx-a11y/label-has-associated-control ); }; const errorMessage = typeof error === 'boolean' ? '' : error; return (
{label ? ( {label} ) : null} {children ? (
{Children.map(children, (child) => { if (isValidElement(child)) { return type === 'checkbox' ? renderCheckbox(child) : renderTag(child); } return null; })}
) : null} {errorMessage && ( {errorMessage} )} {hint && !errorMessage && ( {hint} )}
); }, );