packages/bui-core/src/Radio/Radio.tsx (106 lines of code) (raw):
import { isAlipay, useValue } from '@bifrostui/utils';
import clsx from 'clsx';
import React, { forwardRef, useContext } from 'react';
import { RadioProps } from './Radio.types';
import RadioButtonIcon from './RadioButtonIcon';
import RadioContext from './RadioContext';
import './Radio.less';
const prefixCls = 'bui-radio';
const Radio = forwardRef<HTMLDivElement, RadioProps>((props, ref) => {
const {
className,
defaultChecked,
checked,
inputProps,
inputRef,
name,
value,
disabled,
icon,
checkedIcon,
labelPlacement,
onChange,
children,
...others
} = props;
const groupContext = useContext(RadioContext);
let validDefaultChecked = defaultChecked;
let validChecked = checked;
// 使用RadioGroup
if (groupContext) {
const groupControlled = groupContext?.value !== undefined;
if (groupControlled) {
validChecked = groupContext?.value === value;
} else {
validDefaultChecked = groupContext?.defaultValue === value;
}
}
const [radioChecked, triggerChange] = useValue({
defaultValue: validDefaultChecked,
value: validChecked,
});
if (groupContext && !value) {
console.error('RadioGroup模式下Radio须传入value属性');
}
const radioCheckIcon = checkedIcon || <RadioButtonIcon checked />;
const radioUncheckIcon = icon || <RadioButtonIcon checked={false} />;
const radioDisabled =
disabled !== undefined ? disabled : groupContext?.disabled;
const changeAction = (e, isChecked: boolean) => {
// Radio
if (!groupContext) {
triggerChange(e, isChecked);
onChange?.(e, { checked: isChecked });
return;
}
// RadioGroup
if (isChecked) {
groupContext?.select?.(e, value);
}
};
/**
* 支付宝小程序label标签与radio配合使用时,无法响应onChange、onClick事件
* 支付宝小程序改为div标签,并通过div标签上的onClick事件触发onChange
* 微信小程序/H5使用input onChange
*/
const handleChange = (e) => {
if (radioChecked) return;
changeAction(e, true);
inputProps?.onChange?.(e);
};
return (
<div
ref={ref}
className={clsx(
prefixCls,
className,
`${prefixCls}-label-${labelPlacement}`,
{
[`${prefixCls}-disabled`]: radioDisabled,
},
)}
{...others}
onClick={(e) => {
// radio disabled 会触发onClick事件
others?.onClick?.(e);
if (radioDisabled) return;
if (isAlipay) {
handleChange(e);
}
}}
>
<input
ref={inputRef}
type="radio"
value={value}
name={name}
checked={radioChecked}
disabled={radioDisabled}
{...inputProps}
onChange={handleChange}
className={clsx(`${prefixCls}-input`, inputProps?.className)}
/>
<div className={`${prefixCls}-icon`}>
{radioChecked ? radioCheckIcon : radioUncheckIcon}
</div>
{children && <div className={`${prefixCls}-label`}>{children}</div>}
</div>
);
});
Radio.displayName = 'BuiRadio';
Radio.defaultProps = {
defaultChecked: false,
labelPlacement: 'right',
};
export default Radio;