radioGroup.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import classnames from 'classnames';
  4. import { noop } from 'lodash-es';
  5. import { radioGroupClasses as css, strings } from '@douyinfe/semi-foundation/radio/constants';
  6. import RadioGroupFoundation, { RadioGroupAdapter } from '@douyinfe/semi-foundation/radio/radioGroupFoundation';
  7. import { RadioChangeEvent } from '@douyinfe/semi-foundation/radio/radioInnerFoundation';
  8. import BaseComponent from '../_base/baseComponent';
  9. import { ArrayElement } from '../_base/base';
  10. import Radio, { RadioType } from './radio';
  11. import Context, { RadioGroupButtonSize, RadioMode } from './context';
  12. export interface OptionItem {
  13. label?: string;
  14. value?: string;
  15. disabled?: boolean;
  16. extra?: React.ReactNode;
  17. style?: React.CSSProperties;
  18. className?: string;
  19. }
  20. export type Options = string[] | Array<OptionItem>;
  21. export type RadioGroupProps = {
  22. defaultValue?: any;
  23. disabled?: boolean;
  24. name?: string;
  25. options?: Options;
  26. value?: any;
  27. onChange?: (event: RadioChangeEvent) => void;
  28. className?: string;
  29. style?: React.CSSProperties;
  30. direction?: ArrayElement<typeof strings.DIRECTION_SET>;
  31. mode?: RadioMode;
  32. type?: RadioType;
  33. buttonSize?: RadioGroupButtonSize;
  34. prefixCls?: string;
  35. };
  36. export interface RadioGroupState {
  37. value?: any;
  38. }
  39. class RadioGroup extends BaseComponent<RadioGroupProps, RadioGroupState> {
  40. static propTypes = {
  41. defaultValue: PropTypes.any,
  42. disabled: PropTypes.bool,
  43. name: PropTypes.string,
  44. options: PropTypes.array,
  45. buttonSize: PropTypes.oneOf(strings.BUTTON_SIZE),
  46. type: PropTypes.oneOf([strings.TYPE_DEFAULT, strings.TYPE_BUTTON, strings.TYPE_CARD, strings.TYPE_PURECARD]),
  47. value: PropTypes.any,
  48. onChange: PropTypes.func,
  49. children: PropTypes.node,
  50. prefixCls: PropTypes.string,
  51. className: PropTypes.string,
  52. style: PropTypes.object,
  53. direction: PropTypes.oneOf(strings.DIRECTION_SET),
  54. mode: PropTypes.oneOf(strings.MODE)
  55. };
  56. static defaultProps: Partial<RadioGroupProps> = {
  57. disabled: false,
  58. onChange: noop,
  59. direction: strings.DEFAULT_DIRECTION,
  60. mode: '',
  61. type: strings.TYPE_DEFAULT,
  62. buttonSize: 'middle'
  63. };
  64. constructor(props: RadioGroupProps) {
  65. super(props);
  66. this.state = {
  67. value: undefined,
  68. };
  69. this.foundation = new RadioGroupFoundation(this.adapter);
  70. }
  71. componentDidMount() {
  72. this.foundation.init();
  73. }
  74. componentDidUpdate(prevProps: RadioGroupProps) {
  75. if (prevProps.value !== this.props.value) {
  76. this.foundation.handlePropValueChange(this.props.value);
  77. }
  78. }
  79. componentWillUnmount() {
  80. this.foundation.destroy();
  81. }
  82. get adapter(): RadioGroupAdapter {
  83. return {
  84. ...super.adapter,
  85. setValue: (value: any) => {
  86. this.setState({ value });
  87. },
  88. getProps: () => this.props,
  89. isInProps: (name: string) => Boolean(name in this.props),
  90. notifyChange: (evt: RadioChangeEvent) => {
  91. this.props.onChange && this.props.onChange(evt);
  92. },
  93. };
  94. }
  95. onChange = (evt: RadioChangeEvent) => {
  96. this.foundation.handleChange(evt);
  97. };
  98. getFormatName = () => this.props.name || 'default';
  99. render() {
  100. const {
  101. children,
  102. options,
  103. mode,
  104. prefixCls,
  105. className,
  106. style,
  107. direction,
  108. type,
  109. buttonSize
  110. } = this.props;
  111. const isButtonRadio = type === strings.TYPE_BUTTON;
  112. const isPureCardRadio = type === strings.TYPE_PURECARD;
  113. const isCardRadio = type === strings.TYPE_CARD || isPureCardRadio;
  114. const isDefaultRadio = type === strings.TYPE_DEFAULT;
  115. const prefix = prefixCls || css.PREFIX;
  116. const prefixClsDisplay = classnames(className, {
  117. [prefix]: true,
  118. [`${prefix}-wrapper`]: true,
  119. [`${prefix}-${direction}`]: direction && !isButtonRadio,
  120. [`${prefix}-${direction}-default`]: direction && isDefaultRadio,
  121. [`${prefix}-${direction}-card`]: direction && isCardRadio,
  122. [`${prefix}-buttonRadio`]: isButtonRadio,
  123. });
  124. const realValue = this.state.value;
  125. let inner;
  126. if (options) {
  127. inner = (options || []).map((option, index) => {
  128. if (typeof option === 'string') {
  129. return (
  130. <Radio
  131. key={index}
  132. disabled={this.props.disabled}
  133. value={option}
  134. >
  135. {option}
  136. </Radio>
  137. );
  138. } else {
  139. return (
  140. <Radio
  141. key={index}
  142. disabled={option.disabled || this.props.disabled}
  143. value={option.value}
  144. extra={option.extra}
  145. className={option.className}
  146. style={option.style}
  147. >
  148. {option.label}
  149. </Radio>
  150. );
  151. }
  152. });
  153. } else if (children) {
  154. inner = React.Children.map(children, (itm, index) => (React.isValidElement(itm) ?
  155. React.cloneElement(itm, { key: index }) :
  156. null));
  157. }
  158. return (
  159. <div className={prefixClsDisplay} style={style}>
  160. <Context.Provider
  161. value={{
  162. radioGroup: {
  163. onChange: this.onChange,
  164. value: realValue,
  165. disabled: this.props.disabled,
  166. name: this.getFormatName(),
  167. isButtonRadio,
  168. isCardRadio,
  169. isPureCardRadio,
  170. buttonSize,
  171. prefixCls
  172. },
  173. mode
  174. }}
  175. >
  176. {inner}
  177. </Context.Provider>
  178. </div>
  179. );
  180. }
  181. }
  182. export default RadioGroup;