import * as React from 'react'; import * as PropTypes from 'prop-types'; import classNames from 'classnames'; import shallowEqual from 'shallowequal'; import omit from 'omit.js'; import Checkbox, { CheckboxChangeEvent } from './Checkbox'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; export type CheckboxValueType = string | number | boolean; export interface CheckboxOptionType { label: React.ReactNode; value: CheckboxValueType; disabled?: boolean; onChange?: (e: CheckboxChangeEvent) => void; } export interface AbstractCheckboxGroupProps { prefixCls?: string; className?: string; options?: Array; disabled?: boolean; style?: React.CSSProperties; } export interface CheckboxGroupProps extends AbstractCheckboxGroupProps { name?: string; defaultValue?: Array; value?: Array; onChange?: (checkedValue: Array) => void; } export interface CheckboxGroupState { value: CheckboxValueType[]; registeredValues: CheckboxValueType[]; } export interface CheckboxGroupContext { checkboxGroup: { toggleOption: (option: CheckboxOptionType) => void; value: any; disabled: boolean; }; } class CheckboxGroup extends React.Component { static defaultProps = { options: [], }; static propTypes = { defaultValue: PropTypes.array, value: PropTypes.array, options: PropTypes.array.isRequired, onChange: PropTypes.func, }; static childContextTypes = { checkboxGroup: PropTypes.any, }; static getDerivedStateFromProps(nextProps: CheckboxGroupProps) { if ('value' in nextProps) { return { value: nextProps.value || [], }; } return null; } constructor(props: CheckboxGroupProps) { super(props); this.state = { value: props.value || props.defaultValue || [], registeredValues: [], }; } getChildContext() { return { checkboxGroup: { toggleOption: this.toggleOption, value: this.state.value, disabled: this.props.disabled, name: this.props.name, // https://github.com/ant-design/ant-design/issues/16376 registerValue: this.registerValue, cancelValue: this.cancelValue, }, }; } shouldComponentUpdate(nextProps: CheckboxGroupProps, nextState: CheckboxGroupState) { return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState); } getOptions() { const { options } = this.props; // https://github.com/Microsoft/TypeScript/issues/7960 return (options as Array).map(option => { if (typeof option === 'string') { return { label: option, value: option, } as CheckboxOptionType; } return option; }); } cancelValue = (value: string) => { this.setState(({ registeredValues }) => ({ registeredValues: registeredValues.filter(val => val !== value), })); }; registerValue = (value: string) => { this.setState(({ registeredValues }) => ({ registeredValues: [...registeredValues, value], })); }; toggleOption = (option: CheckboxOptionType) => { const { registeredValues } = this.state; const optionIndex = this.state.value.indexOf(option.value); const value = [...this.state.value]; if (optionIndex === -1) { value.push(option.value); } else { value.splice(optionIndex, 1); } if (!('value' in this.props)) { this.setState({ value }); } const { onChange } = this.props; if (onChange) { const options = this.getOptions(); onChange( value .filter(val => registeredValues.indexOf(val) !== -1) .sort((a, b) => { const indexA = options.findIndex(opt => opt.value === a); const indexB = options.findIndex(opt => opt.value === b); return indexA - indexB; }), ); } }; renderGroup = ({ getPrefixCls }: ConfigConsumerProps) => { const { props, state } = this; const { prefixCls: customizePrefixCls, className, style, options, ...restProps } = props; const prefixCls = getPrefixCls('checkbox', customizePrefixCls); const groupPrefixCls = `${prefixCls}-group`; const domProps = omit(restProps, ['children', 'defaultValue', 'value', 'onChange', 'disabled']); let { children } = props; if (options && options.length > 0) { children = this.getOptions().map(option => ( {option.label} )); } const classString = classNames(groupPrefixCls, className); return (
{children}
); }; render() { return {this.renderGroup}; } } export default CheckboxGroup;