| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- import React from 'react';
- import PropTypes from 'prop-types';
- import classnames from 'classnames';
- import { noop } from 'lodash';
- import { radioGroupClasses as css, strings } from '@douyinfe/semi-foundation/radio/constants';
- import RadioGroupFoundation, { RadioGroupAdapter } from '@douyinfe/semi-foundation/radio/radioGroupFoundation';
- import { RadioChangeEvent } from '@douyinfe/semi-foundation/radio/radioInnerFoundation';
- import BaseComponent from '../_base/baseComponent';
- import { ArrayElement } from '../_base/base';
- import Radio, { RadioType } from './radio';
- import Context, { RadioGroupButtonSize, RadioMode } from './context';
- export interface OptionItem {
- label?: React.ReactNode;
- value?: string | number;
- disabled?: boolean;
- extra?: React.ReactNode;
- style?: React.CSSProperties;
- className?: string
- }
- export type Options = string[] | Array<OptionItem>;
- export type RadioGroupProps = {
- defaultValue?: string | number;
- disabled?: boolean;
- name?: string;
- options?: Options;
- value?: string | number;
- onChange?: (event: RadioChangeEvent) => void;
- className?: string;
- children?: React.ReactNode;
- style?: React.CSSProperties;
- direction?: ArrayElement<typeof strings.DIRECTION_SET>;
- mode?: RadioMode;
- type?: RadioType;
- buttonSize?: RadioGroupButtonSize;
- prefixCls?: string;
- 'aria-label'?: React.AriaAttributes['aria-label'];
- 'aria-describedby'?: React.AriaAttributes['aria-describedby'];
- 'aria-errormessage'?: React.AriaAttributes['aria-errormessage'];
- 'aria-invalid'?: React.AriaAttributes['aria-invalid'];
- 'aria-labelledby'?: React.AriaAttributes['aria-labelledby'];
- 'aria-required'?: React.AriaAttributes['aria-required'];
- id?: string
- };
- export interface RadioGroupState {
- value?: any
- }
- class RadioGroup extends BaseComponent<RadioGroupProps, RadioGroupState> {
- static propTypes = {
- defaultValue: PropTypes.any,
- disabled: PropTypes.bool,
- name: PropTypes.string,
- options: PropTypes.array,
- buttonSize: PropTypes.oneOf(strings.BUTTON_SIZE),
- type: PropTypes.oneOf([strings.TYPE_DEFAULT, strings.TYPE_BUTTON, strings.TYPE_CARD, strings.TYPE_PURECARD]),
- value: PropTypes.any,
- onChange: PropTypes.func,
- children: PropTypes.node,
- prefixCls: PropTypes.string,
- className: PropTypes.string,
- style: PropTypes.object,
- direction: PropTypes.oneOf(strings.DIRECTION_SET),
- mode: PropTypes.oneOf(strings.MODE),
- 'aria-label': PropTypes.string,
- 'aria-describedby': PropTypes.string,
- 'aria-errormessage': PropTypes.string,
- 'aria-invalid': PropTypes.bool,
- 'aria-labelledby': PropTypes.string,
- 'aria-required': PropTypes.bool,
- id: PropTypes.string,
- };
- static defaultProps: Partial<RadioGroupProps> = {
- disabled: false,
- onChange: noop,
- direction: strings.DEFAULT_DIRECTION,
- mode: '',
- type: strings.TYPE_DEFAULT,
- buttonSize: 'middle'
- };
- foundation: RadioGroupFoundation;
- constructor(props: RadioGroupProps) {
- super(props);
- this.state = {
- value: props.value || props.defaultValue,
- };
- this.foundation = new RadioGroupFoundation(this.adapter);
- }
- componentDidMount() {
- this.foundation.init();
- }
- componentDidUpdate(prevProps: RadioGroupProps) {
- if (typeof prevProps.value === 'number'
- && isNaN(prevProps.value)
- && typeof this.props.value === 'number'
- && isNaN(this.props.value)
- ) {
- // `NaN === NaN` returns false, and this will fail the next if check
- // therefore triggering an infinite loop
- return;
- }
- if (prevProps.value !== this.props.value) {
- this.foundation.handlePropValueChange(this.props.value);
- }
- }
- componentWillUnmount() {
- this.foundation.destroy();
- }
- get adapter(): RadioGroupAdapter {
- return {
- ...super.adapter,
- setValue: (value: any) => {
- this.setState({ value });
- },
- getProps: () => this.props,
- isInProps: (name: string) => Boolean(name in this.props),
- notifyChange: (evt: RadioChangeEvent) => {
- this.props.onChange && this.props.onChange(evt);
- },
- };
- }
- onChange = (evt: RadioChangeEvent) => {
- this.foundation.handleChange(evt);
- };
- getFormatName = () => this.props.name || 'default';
- render() {
- const {
- children,
- options,
- mode,
- prefixCls,
- className,
- style,
- direction,
- type,
- buttonSize,
- id,
- } = this.props;
- const isButtonRadio = type === strings.TYPE_BUTTON;
- const isPureCardRadio = type === strings.TYPE_PURECARD;
- const isCardRadio = type === strings.TYPE_CARD || isPureCardRadio;
- const isDefaultRadio = type === strings.TYPE_DEFAULT;
- const prefix = prefixCls || css.PREFIX;
- const prefixClsDisplay = classnames(className, {
- [prefix]: true,
- [`${prefix}-wrapper`]: true,
- [`${prefix}-${direction}`]: direction && !isButtonRadio,
- [`${prefix}-${direction}-default`]: direction && isDefaultRadio,
- [`${prefix}-${direction}-card`]: direction && isCardRadio,
- [`${prefix}-buttonRadio`]: isButtonRadio,
- });
- const realValue = this.state.value;
- let inner;
- if (options) {
- inner = (options || []).map((option, index) => {
- if (typeof option === 'string') {
- return (
- <Radio
- key={index}
- disabled={this.props.disabled}
- value={option}
- >
- {option}
- </Radio>
- );
- } else {
- return (
- <Radio
- key={index}
- disabled={option.disabled || this.props.disabled}
- value={option.value}
- extra={option.extra}
- className={option.className}
- style={option.style}
- >
- {option.label}
- </Radio>
- );
- }
- });
- } else if (children) {
- inner = React.Children.map(children, (itm, index) => (React.isValidElement(itm) ?
- React.cloneElement(itm, { key: index }) :
- null));
- }
- return (
- <div
- className={prefixClsDisplay}
- style={style}
- id={id}
- aria-label={this.props['aria-label']}
- aria-invalid={this.props['aria-invalid']}
- aria-errormessage={this.props['aria-errormessage']}
- aria-labelledby={this.props['aria-labelledby']}
- aria-describedby={this.props['aria-describedby']}
- aria-required={this.props['aria-required']}
- >
- <Context.Provider
- value={{
- radioGroup: {
- onChange: this.onChange,
- value: realValue,
- disabled: this.props.disabled,
- name: this.getFormatName(),
- isButtonRadio,
- isCardRadio,
- isPureCardRadio,
- buttonSize,
- prefixCls
- },
- mode
- }}
- >
- {inner}
- </Context.Provider>
- </div>
- );
- }
- }
- export default RadioGroup;
|