Skip to content

Commit

Permalink
fix(radio): readonly & allowUncheck 支持
Browse files Browse the repository at this point in the history
fix #461
  • Loading branch information
byq1213 committed Sep 22, 2024
1 parent 43a2689 commit 1bf5424
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 37 deletions.
49 changes: 22 additions & 27 deletions src/radio/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React, { createContext, forwardRef, useContext, useRef } from 'react';
import type { CSSProperties, Ref } from 'react';
import classNames from 'classnames';
import { CheckIcon, CheckCircleFilledIcon } from 'tdesign-icons-react';
import { usePrefixClass } from 'tdesign-mobile-react/hooks/useClass';
import forwardRefWithStatics from '../_util/forwardRefWithStatics';
import useConfig from '../_util/useConfig';
import useDefault from '../_util/useDefault';
import type { TdRadioProps } from './type';
import RadioGroup from './RadioGroup';
Expand All @@ -27,8 +27,7 @@ const getLimitRow = (row: number): CSSProperties => ({
});

const Radio = forwardRef((_props: RadioProps, ref: Ref<HTMLDivElement>) => {
const { classPrefix: prefix } = useConfig();
const classPrefix = `${prefix}-radio`;
const radioClass = usePrefixClass('radio');
const inputRef = useRef();
const context = useContext(RadioContext);
const props = context ? context.inject(_props) : _props;
Expand All @@ -50,12 +49,13 @@ const Radio = forwardRef((_props: RadioProps, ref: Ref<HTMLDivElement>) => {
value,
borderless,
onChange,
readonly,
} = useDefaultProps<TdRadioProps>(props, radioDefaultProps);

const [radioChecked, setRadioChecked] = useDefault(checked, defaultChecked, onChange);

const switchRadioChecked = (area?: string) => {
if (disabled) {
if (disabled || readonly) {
return;
}
if (area === 'content' && contentDisabled) {
Expand All @@ -73,20 +73,20 @@ const Radio = forwardRef((_props: RadioProps, ref: Ref<HTMLDivElement>) => {
}
if (radioChecked) {
if (icon === 'circle') {
return <CheckCircleFilledIcon className={`${classPrefix}__icon-wrap`} />;
return <CheckCircleFilledIcon className={`${radioClass}__icon-wrap`} />;
}
if (icon === 'line') {
return <CheckIcon className={`${classPrefix}__icon-wrap`} />;
return <CheckIcon className={`${radioClass}__icon-wrap`} />;
}
if (icon === 'dot') {
let dotIconClassName = `${classPrefix}__icon-${icon}`;
disabled && (dotIconClassName += ` ${classPrefix}__icon-${icon}--disabled`);
let dotIconClassName = `${radioClass}__icon-${icon}`;
disabled && (dotIconClassName += ` ${radioClass}__icon-${icon}--disabled`);
return <div className={dotIconClassName} />;
}
} else {
if (icon === 'circle' || icon === 'dot') {
let circleIconClassName = `${classPrefix}__icon-circle`;
disabled && (circleIconClassName += ` ${classPrefix}__icon-circle--disabled`);
let circleIconClassName = `${radioClass}__icon-circle`;
disabled && (circleIconClassName += ` ${radioClass}__icon-circle--disabled`);
return <div className={circleIconClassName} />;
}
if (icon === 'line') {
Expand All @@ -95,27 +95,22 @@ const Radio = forwardRef((_props: RadioProps, ref: Ref<HTMLDivElement>) => {
}
};

const labelStyle = {
...getLimitRow(maxLabelRow),
color: disabled ? '#dcdcdc' : 'inherit',
};

const radioClassName = classNames(`${classPrefix}`, `${classPrefix}--${placement}`, {
[`${classPrefix}--block`]: block,
const radioClassName = classNames(`${radioClass}`, `${radioClass}--${placement}`, {
[`${radioClass}--block`]: block,
});

const titleClassName = classNames(`${classPrefix}__title`, { [`${classPrefix}__title--disabled`]: disabled });
const titleClassName = classNames(`${radioClass}__title`, { [`${radioClass}__title--disabled`]: disabled });

const input = (
<input
type="radio"
readOnly
readOnly={readonly}
name={name}
ref={inputRef}
// @ts-ignore
value={value}
disabled={disabled}
className={classNames(`${classPrefix}__original`)}
className={classNames(`${radioClass}__original`)}
checked={radioChecked}
onClick={(e) => {
e.stopPropagation();
Expand All @@ -127,31 +122,31 @@ const Radio = forwardRef((_props: RadioProps, ref: Ref<HTMLDivElement>) => {
/>
);

const iconClass = classNames(`${classPrefix}__icon`, `${classPrefix}__icon--${placement}`, {
[`${classPrefix}__icon--checked`]: radioChecked,
[`${classPrefix}__icon--disabled`]: disabled,
const iconClass = classNames(`${radioClass}__icon`, `${radioClass}__icon--${placement}`, {
[`${radioClass}__icon--checked`]: radioChecked,
[`${radioClass}__icon--disabled`]: disabled,
});
return (
<div className={radioClassName} ref={ref} onClick={() => switchRadioChecked()}>
{input}
<div className={iconClass}>{renderIcon()}</div>
<div className={`${classPrefix}__content`}>
<div className={`${radioClass}__content`}>
{label && (
<span className={titleClassName} style={labelStyle}>
<span className={titleClassName} style={{ WebkitLineClamp: maxLabelRow }}>
{label}
</span>
)}
{content && (
<div
className={`${classPrefix}__description ${disabled && `${classPrefix}__description--disabled`}`}
className={`${radioClass}__description ${disabled && `${radioClass}__description--disabled`}`}
style={getLimitRow(maxContentRow)}
>
{content}
</div>
)}
</div>

{!borderless && block && <div className={`${classPrefix}__border ${classPrefix}__border--${placement}`}></div>}
{!borderless && block && <div className={`${radioClass}__border ${radioClass}__border--${placement}`}></div>}
</div>
);
});
Expand Down
4 changes: 3 additions & 1 deletion src/radio/RadioGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface RadioGroupProps extends TdRadioGroupProps {

const RadioGroup: FC<RadioGroupProps> = (props) => {
const { classPrefix } = useConfig();
const { disabled, options, value, defaultValue, children, onChange } = props;
const { disabled, options, value, defaultValue, children, onChange, allowUncheck, borderless } = props;
const groupRef = useRef(null);
const [internalValue, setInternalValue] = useDefault(value, defaultValue, onChange);

Expand All @@ -27,6 +27,8 @@ const RadioGroup: FC<RadioGroupProps> = (props) => {
typeof radioProps.value !== 'undefined' &&
internalValue === radioProps.value,
disabled: radioProps.disabled || disabled,
allowUncheck: radioProps.allowUncheck || allowUncheck,
borderless: radioProps.borderless || borderless,
onChange: (checked, { e }) => {
if (typeof radioProps.onChange === 'function') {
radioProps.onChange(checked, { e });
Expand Down
1 change: 1 addition & 0 deletions src/radio/defaultProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const radioDefaultProps: TdRadioProps = {
maxContentRow: 5,
maxLabelRow: 3,
placement: 'left',
readonly: false,
value: undefined,
};

Expand Down
5 changes: 3 additions & 2 deletions src/radio/radio.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ maxContentRow | Number | 5 | \- | N
maxLabelRow | Number | 3 | \- | N
name | String | - | \- | N
placement | String | left | options: left/right | N
value | String / Number / Boolean | undefined | Typescript:`string \| number \| boolean` | N
readonly | Boolean | false | \- | N
value | String / Number / Boolean | undefined | Typescript:`T` | N
onChange | Function | | Typescript:`(checked: boolean, context: { e: ChangeEvent }) => void`<br/> | N


Expand All @@ -44,4 +45,4 @@ options | Array | - | Typescript:`Array<RadioOption>` `type RadioOption = stri
placement | String | left | options: left/right | N
value | String / Number / Boolean | - | Typescript:`T` `type RadioValue = string \| number \| boolean`[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/radio/type.ts) | N
defaultValue | String / Number / Boolean | - | uncontrolled property。Typescript:`T` `type RadioValue = string \| number \| boolean`[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/radio/type.ts) | N
onChange | Function | | Typescript:`(value: T, context: { e: ChangeEvent }) => void`<br/> | N
onChange | Function | | Typescript:`(value: T, context: { e: ChangeEvent; name?: string }) => void`<br/> | N
5 changes: 3 additions & 2 deletions src/radio/radio.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ maxContentRow | Number | 5 | 内容最大行数限制 | N
maxLabelRow | Number | 3 | 主文案最大行数限制 | N
name | String | - | HTML 元素原生属性 | N
placement | String | left | 复选框和内容相对位置。可选项:left/right | N
value | String / Number / Boolean | undefined | 单选按钮的值。TS 类型:`string \| number \| boolean` | N
readonly | Boolean | false | 只读状态 | N
value | String / Number / Boolean | undefined | 单选按钮的值。TS 类型:`T` | N
onChange | Function | | TS 类型:`(checked: boolean, context: { e: ChangeEvent }) => void`<br/>选中状态变化时触发 | N


Expand All @@ -44,4 +45,4 @@ options | Array | - | 单选组件按钮形式。RadioOption 数据类型为 str
placement | String | left | 复选框和内容相对位置。可选项:left/right | N
value | String / Number / Boolean | - | 选中的值。TS 类型:`T` `type RadioValue = string \| number \| boolean`[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/radio/type.ts) | N
defaultValue | String / Number / Boolean | - | 选中的值。非受控属性。TS 类型:`T` `type RadioValue = string \| number \| boolean`[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/radio/type.ts) | N
onChange | Function | | TS 类型:`(value: T, context: { e: ChangeEvent }) => void`<br/>选中值发生变化时触发 | N
onChange | Function | | TS 类型:`(value: T, context: { e: ChangeEvent; name?: string }) => void`<br/>选中值发生变化时触发, `context.name` 指 RadioGroup 的 name 属性 | N
1 change: 0 additions & 1 deletion src/radio/style/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
import '../../_common/style/mobile/components/radio/v2/_index.less';
import '../../_common/style/mobile/components/radio-group/_index.less';
13 changes: 9 additions & 4 deletions src/radio/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { TNode, KeysType } from '../common';
import { ChangeEvent } from 'react';

export interface TdRadioProps {
export interface TdRadioProps<T = RadioValue> {
/**
* 已废弃。复选框和内容相对位置
* @default left
Expand Down Expand Up @@ -83,10 +83,15 @@ export interface TdRadioProps {
* @default left
*/
placement?: 'left' | 'right';
/**
* 只读状态
* @default false
*/
readonly?: boolean;
/**
* 单选按钮的值
*/
value?: string | number | boolean;
value?: T;
/**
* 选中状态变化时触发
*/
Expand Down Expand Up @@ -140,9 +145,9 @@ export interface TdRadioGroupProps<T = RadioValue> {
*/
defaultValue?: T;
/**
* 选中值发生变化时触发
* 选中值发生变化时触发, `context.name` 指 RadioGroup 的 name 属性
*/
onChange?: (value: T, context: { e: ChangeEvent<HTMLDivElement> }) => void;
onChange?: (value: T, context: { e: ChangeEvent<HTMLDivElement>; name?: string }) => void;
}

export type RadioOption = string | number | RadioOptionObj;
Expand Down

0 comments on commit 1bf5424

Please sign in to comment.