Concis组件库封装——Radio 单选框

969 阅读2分钟

您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

Radio组件的封装一共封装了两个组件——RadioGroup和Radio,RadioGroup为父组件,主要用于控制单选组的状态,而Radio作为一个个选项使用。

在这里插入图片描述

在这里插入图片描述 RadioGroup.tsx:

import React, { FC, useState, useEffect, memo, useCallback } from 'react';
import Input from '@/Input';
import style from './index.module.less';

interface RadioGroupProps {
  children: Array<Object>;
  /**
   * @description 默认值
   * @default 0
   */
  value?: Number;
  canAddOption?: Boolean;
  boxStyle?: Boolean;
  onChange?: Function;
}
interface RadioProps {
  children: string;
  disabled: Boolean;
}

const RadioGroup: FC<RadioGroupProps> = (props) => {
  const { children, value, canAddOption, boxStyle, onChange } = props;

  const [selectIndex, setSelectIndex] = useState(value || 0); //选中索引
  const [renderOptions, setRenderOptions] = useState(children);
  const [addOptionVal, setAddOptionVal] = useState('');
  const [showAddOption, setShowAddOption] = useState(canAddOption && false);

  useEffect(() => {
    console.log(boxStyle);
  });
  const changeOptions = (item: RadioProps, i: number, e: any) => {
    if (item.disabled) return;
    e && e.stopPropagation();
    setSelectIndex(i);
    onChange && onChange(item, i);
    canAddOption && setShowAddOption(false);
  };
  const addOptions = () => {
    //新增options
    setSelectIndex(renderOptions.length);
    setShowAddOption(true);
  };
  const handleKeyDown = (e: any) => {
    //新增确认
    if (e.keyCode == 13 && addOptionVal) {
      setRenderOptions((old) => {
        const addOption = {
          props: {
            children: addOptionVal,
          },
        };
        return [...old, addOption];
      });
      setShowAddOption(false);
    }
  };
  const handleIptChange = (val: string) => {
    //新增输入框
    setAddOptionVal(val);
  };
  const boxStyleClassName = useCallback(
    (props: RadioProps, i: number) => {
      if (props.disabled) {
        return style.groupDisabledStyle;
      }
      if (i == selectIndex) {
        return style.groupActive;
      }
      return style.groupStyle;
    },
    [children, boxStyle, value, selectIndex],
  );

  return (
    <div className={style.radioGroup}>
      {renderOptions.map((item: any, index: number) => {
        return boxStyle ? (
          <div
            className={boxStyleClassName(item.props, index)}
            style={item.props.disabled ? { cursor: 'not-allowed' } : { cursor: 'pointer' }}
            key={index}
            onClick={(e) => changeOptions(item.props, index, e)}
          >
            {item.props.children}
          </div>
        ) : (
          <div
            className={style.radioBox}
            style={item.props.disabled ? { cursor: 'not-allowed' } : { cursor: 'pointer' }}
            key={index}
            onClick={(e) => changeOptions(item.props, index, e)}
          >
            <span className={item.props.disabled ? style.disabledLabel : style.radioLabel}>
              {item.props.children}
            </span>
            <input
              className={item.props.disabled ? style.disabledRadio : style.radio}
              readOnly
              type="radio"
              checked={selectIndex === index}
              disabled={item.props.disabled}
            ></input>
          </div>
        );
      })}
      {
        //新增Options项(优雅之王)
        canAddOption ? (
          boxStyle ? (
            <div className={style.addOption}>
              <div
                className={
                  selectIndex === renderOptions.length ? style.groupActive : style.groupStyle
                }
                onClick={addOptions}
              >
                More...
              </div>
              {showAddOption && (
                <Input handleKeyDown={handleKeyDown} handleIptChange={handleIptChange} />
              )}
            </div>
          ) : (
            <div className={style.addOption}>
              <div className={style.radioBox} onClick={addOptions}>
                <span className={style.radioLabel}>More...</span>
                <input
                  className={style.radio}
                  type="radio"
                  readOnly
                  checked={selectIndex === renderOptions.length}
                ></input>
              </div>
              {showAddOption && (
                <Input handleKeyDown={handleKeyDown} handleIptChange={handleIptChange} />
              )}
            </div>
          )
        ) : (
          <></>
        )
      }
    </div>
  );
};

export default memo(RadioGroup);

Radio.tsx:

import React, { FC, memo } from 'react';

interface RadioProps {
  children: Object;
  /**
   * @description 默认选中索引
   * @default 0
   */
  value?: Number;
  /**
   * @description 禁用
   * @default 0
   */
  disabled?: Boolean;
  /**
   * @description 支持手动扩展
   * @default false
   */
  canAddOption?: Boolean;
  /**
   * @description 方形样式
   * @default false
   */
  boxStyle?: Boolean;
  /**
   * @description 选项改变回调函数
   */
  onChange?: Function;
}
const Radio: FC<RadioProps> = (props) => {
  const { children } = props;
  return <div style={{ display: 'none' }}>{children}</div>;
};

export default memo(Radio);