/* eslint-disable jsx-a11y/no-noninteractive-element-to-interactive-role */ import React, { forwardRef, useContext, useState } from "react" import PropTypes from "prop-types" import classNames from "classnames" import { GroupContext } from "./Context" import { ConfigContext, getComputedSize } from "../config-provider" import Group from "./Group" import Icon, { IconNames } from "../icon" import Popover, { IPopoverProps } from "../popover" import "./style" const prefix = "adui-radio" export interface IRadioProps { [key: string]: any /** * 是否选中 */ checked?: null | boolean /** * 子节点 */ children?: React.ReactNode /** * 附加类名 */ className?: string /** * 是否禁用 */ disabled?: boolean /** * 加入问号气泡提示内容 */ helper?: React.ReactNode /** * 自定义问号气泡的 Icon */ helperIcon?: IconNames /** * 问号气泡提示的 props,会将此对象都传递给 */ helperProps?: IPopoverProps /** * 选中态发生变化时的 handler */ onChange?: ((checked: boolean) => void) | null /** * 点击时的 handler */ onClick?: | (( e: | React.MouseEvent | React.KeyboardEvent ) => void) | null /** * 设置尺寸 */ size?: "mini" | "small" | "medium" | "large" /** * 值,只在使用 Radio.Group 时会读取使用 */ value?: null | React.ReactText } export interface IRadio extends React.ForwardRefExoticComponent< IRadioProps & React.RefAttributes > { Group: typeof Group } /** * 单选提供给用户在互斥的多个选项中,对选项内容进行单个选择。 */ // @ts-ignore const Radio: IRadio = forwardRef( ( { checked: checkedProp, children, className, disabled, helper, helperIcon, helperProps, onChange, onClick, size: sizeProp, value, ...otherProps }: IRadioProps, ref ) => { const [checked, setChecked] = useState(!!checkedProp) const { disabled: disabledContext, handleGroupValueChange, size: sizeContext, value: valueContext, } = useContext(GroupContext) // 相当于生命周期 getDerivedStateFromProps if (checkedProp !== null && checked !== !!checkedProp) { setChecked(!!checkedProp) } const { size: sizeConfig } = useContext(ConfigContext) const size = getComputedSize(sizeProp, sizeContext, sizeConfig) const popover = helper ? ( ) : null /** * 当有 radioGroup context 时,使用 value: * 如果存在 value prop,则直接使用 value; * 如果不存在,就 string 化 children 作为 value。 */ const classSet = classNames( className, `${prefix}-base`, `${prefix}-${size}`, { [`${prefix}-checked`]: valueContext !== null && valueContext !== undefined ? valueContext === (value === null ? children?.toString() : value) : checked, [`${prefix}-noChildren`]: !children, [`${prefix}-disabled`]: disabledContext || disabled, } ) const handleClick = ( e: | React.MouseEvent | React.KeyboardEvent ) => { if (onClick) { onClick(e) } const computedValue = value === null ? children?.toString() : value /** * 单选与多选处理点击的不同在于: * 单选选中状态是单项的,一旦选中不再触发事件。 */ if ( (valueContext !== null ? valueContext === computedValue : checked) || disabled || disabledContext ) { return } if (handleGroupValueChange) { handleGroupValueChange(computedValue === undefined ? "" : computedValue) } else { if (checkedProp === null) { setChecked(true) } if (onChange) { onChange(true) } } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.keyCode === 13) { handleClick(e) } } return ( ) } ) Radio.Group = Group Radio.displayName = "Radio" Radio.propTypes = { /** * 是否选中 */ checked: PropTypes.oneOf([null, true, false]), /** * 子节点 */ children: PropTypes.node, /** * 附加类名 */ className: PropTypes.string, /** * 是否禁用 */ disabled: PropTypes.bool, /** * 加入问号气泡提示内容 */ helper: PropTypes.node, /** * 自定义问号气泡的 Icon */ helperIcon: PropTypes.any, /** * 问号气泡提示的 props,会将此对象都传递给 */ helperProps: PropTypes.object, /** * 选中态发生变化时的 handler */ onChange: PropTypes.func, /** * 点击时的 handler */ onClick: PropTypes.func, /** * 设置尺寸 */ size: PropTypes.oneOf(["mini", "small", "medium", "large"]), /** * 值,只在使用 Radio.Group 时会读取使用 */ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), } Radio.defaultProps = { checked: null, children: "", className: undefined, disabled: false, helper: null, helperIcon: "help-circle", helperProps: {}, onChange: null, onClick: null, size: "small", value: null, } export default Radio