checkboxGroup.tsx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /* eslint-disable max-len */
  2. import React from 'react';
  3. import PropTypes from 'prop-types';
  4. import classnames from 'classnames';
  5. import { checkboxGroupClasses as css, strings } from '@douyinfe/semi-foundation/checkbox/constants';
  6. import CheckboxGroupFoundation, { CheckboxGroupAdapter } from '@douyinfe/semi-foundation/checkbox/checkboxGroupFoundation';
  7. import BaseComponent from '../_base/baseComponent';
  8. import { Context } from './context';
  9. import { isEqual } from 'lodash';
  10. import Checkbox, { CheckboxEvent } from './checkbox';
  11. export type CheckboxDirection = 'horizontal' | 'vertical';
  12. export type CheckboxType = 'default' | 'card' | 'pureCard';
  13. export type CheckboxGroupProps = {
  14. 'aria-describedby'?: React.AriaAttributes['aria-describedby'];
  15. 'aria-errormessage'?: React.AriaAttributes['aria-errormessage'];
  16. 'aria-invalid'?: React.AriaAttributes['aria-invalid'];
  17. 'aria-labelledby'?: React.AriaAttributes['aria-labelledby'];
  18. 'aria-required'?: React.AriaAttributes['aria-required'];
  19. defaultValue?: any[];
  20. disabled?: boolean;
  21. name?: string;
  22. options?: any[];
  23. value?: any[];
  24. onChange?: (value: any[]) => void;
  25. children?: React.ReactNode | undefined;
  26. prefixCls?: string;
  27. direction?: CheckboxDirection;
  28. style?: React.CSSProperties;
  29. className?: string;
  30. type?: CheckboxType;
  31. id?: string;
  32. 'aria-label'?: React.AriaAttributes['aria-label'];
  33. };
  34. export type CheckboxGroupState = {
  35. value?: any[];
  36. };
  37. class CheckboxGroup extends BaseComponent<CheckboxGroupProps, CheckboxGroupState> {
  38. static propTypes = {
  39. 'aria-describedby': PropTypes.string,
  40. 'aria-errormessage': PropTypes.string,
  41. 'aria-invalid': PropTypes.bool,
  42. 'aria-labelledby': PropTypes.string,
  43. 'aria-required': PropTypes.bool,
  44. defaultValue: PropTypes.array,
  45. disabled: PropTypes.bool,
  46. name: PropTypes.string,
  47. options: PropTypes.array,
  48. value: PropTypes.array,
  49. onChange: PropTypes.func,
  50. children: PropTypes.node,
  51. prefixCls: PropTypes.string,
  52. direction: PropTypes.oneOf<CheckboxGroupProps['direction']>(strings.DIRECTION_SET),
  53. className: PropTypes.string,
  54. type: PropTypes.oneOf([strings.TYPE_DEFAULT, strings.TYPE_CARD, strings.TYPE_PURECARD]),
  55. style: PropTypes.object,
  56. };
  57. static defaultProps: Partial<CheckboxGroupProps> = {
  58. disabled: false,
  59. // eslint-disable-next-line @typescript-eslint/no-empty-function
  60. onChange: () => {},
  61. type: strings.TYPE_DEFAULT,
  62. defaultValue: [] as any,
  63. direction: strings.DEFAULT_DIRECTION,
  64. };
  65. get adapter(): CheckboxGroupAdapter {
  66. return {
  67. ...super.adapter,
  68. updateGroupValue: value => {
  69. this.setState({ value });
  70. },
  71. notifyChange: evt => {
  72. this.props.onChange && this.props.onChange(evt);
  73. },
  74. };
  75. }
  76. foundation: CheckboxGroupFoundation;
  77. constructor(props: CheckboxGroupProps) {
  78. super(props);
  79. this.state = {
  80. value: props.value || props.defaultValue,
  81. };
  82. this.foundation = new CheckboxGroupFoundation(this.adapter);
  83. this.onChange = this.onChange.bind(this);
  84. }
  85. componentDidMount() {
  86. this.foundation.init();
  87. }
  88. componentDidUpdate(prevProps: CheckboxGroupProps) {
  89. if (!isEqual(prevProps.value, this.props.value)) {
  90. this.foundation.handlePropValueChange(this.props.value);
  91. }
  92. }
  93. componentWillUnmount() {
  94. this.foundation.destroy();
  95. }
  96. onChange(evt: CheckboxEvent) {
  97. this.foundation.handleChange(evt);
  98. }
  99. render() {
  100. const { children, options, prefixCls, direction, className, id, style, type, disabled } = this.props;
  101. const isPureCardType = type === strings.TYPE_PURECARD;
  102. const isCardType = type === strings.TYPE_CARD || isPureCardType;
  103. const prefix = prefixCls || css.PREFIX;
  104. const prefixClsDisplay = classnames({
  105. [prefix as string]: true,
  106. [`${prefix }-wrapper`]: true,
  107. [`${prefix }-${ direction}`]: direction,
  108. [`${prefix}-${direction}-cardType`]: direction && isCardType,
  109. [`${prefix}-${direction}-pureCardType`]: direction && isPureCardType,
  110. }, className);
  111. const realValue = this.state.value.slice();
  112. let inner;
  113. if (options) {
  114. inner = (options || []).map((option, index) => {
  115. if (typeof option === 'string') {
  116. return (
  117. <Checkbox
  118. role="listitem"
  119. key={index}
  120. disabled={this.props.disabled}
  121. value={option}
  122. prefixCls={prefixCls}
  123. >
  124. {option}
  125. </Checkbox>
  126. );
  127. } else {
  128. return (
  129. <Checkbox
  130. role="listitem"
  131. key={index}
  132. disabled={option.disabled || this.props.disabled}
  133. value={option.value}
  134. prefixCls={prefixCls}
  135. extra={option.extra}
  136. className={option.className}
  137. style={option.style}
  138. onChange={option.onChange}
  139. >
  140. {option.label}
  141. </Checkbox>
  142. );
  143. }
  144. });
  145. } else if (children) {
  146. inner = (React.Children.toArray(children) as React.ReactElement[]).map((itm, index) => React.cloneElement(itm, { key: index, role: 'listitem' }));
  147. }
  148. return (
  149. <div
  150. id={id}
  151. role="list"
  152. aria-label={this.props['aria-label']}
  153. className={prefixClsDisplay}
  154. style={style}
  155. aria-labelledby={this.props['aria-labelledby']}
  156. aria-describedby={this.props['aria-describedby']}
  157. // aria-errormessage={this.props['aria-errormessage']}
  158. // aria-invalid={this.props['aria-invalid']}
  159. // aria-required={this.props['aria-required']}
  160. >
  161. <Context.Provider
  162. value={{
  163. checkboxGroup: {
  164. onChange: this.onChange,
  165. value: realValue,
  166. disabled: this.props.disabled,
  167. name: this.foundation.getFormatName(),
  168. isCardType,
  169. isPureCardType,
  170. },
  171. }}
  172. >
  173. {inner}
  174. </Context.Provider>
  175. </div>
  176. );
  177. }
  178. }
  179. export default CheckboxGroup;