Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(radio): 重构radio dom接口,补充对应文档和适配样式 #468

Merged
merged 7 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion site/mobile/mobile.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default {
{
title: 'Radio 单选框',
name: 'radio',
component: () => import('tdesign-mobile-react/radio/_example/index.jsx'),
component: () => import('tdesign-mobile-react/radio/_example/index.tsx'),
},
{
title: 'Rate 评分',
Expand Down
2 changes: 1 addition & 1 deletion src/_common
142 changes: 72 additions & 70 deletions src/radio/Radio.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React, { createContext, CSSProperties, forwardRef, Ref, useContext, useRef } from 'react';
import React, { createContext, forwardRef, useContext, useRef } from 'react';
import type { CSSProperties, Ref } from 'react';
import classNames from 'classnames';
import { CheckIcon } from 'tdesign-icons-react';
import { CheckIcon, CheckCircleFilledIcon } from 'tdesign-icons-react';
import { usePrefixClass } from '../hooks/useClass';
import forwardRefWithStatics from '../_util/forwardRefWithStatics';
import useConfig from '../_util/useConfig';
import useDefault from '../_util/useDefault';
import { TdRadioProps } from './type';
import type { TdRadioProps } from './type';
import RadioGroup from './RadioGroup';
import useDefaultProps from '../hooks/useDefaultProps';
import { radioDefaultProps } from './defaultProps';

export interface RadioProps extends TdRadioProps {
ref?: Ref<HTMLDivElement>;
Expand All @@ -24,35 +27,36 @@ 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;

const {
align = 'left',
allowUncheck = false,
checked = false,
defaultChecked = false,
children,
allowUncheck,
byq1213 marked this conversation as resolved.
Show resolved Hide resolved
block,
checked,
content,
defaultChecked,
contentDisabled,
placement,
disabled,
icon,
label,
maxContentRow = 5,
maxLabelRow = 3,
maxContentRow,
maxLabelRow,
name,
value,
// borderless = false,
borderless,
onChange,
} = props;
readonly,
children,
} = 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 @@ -66,51 +70,48 @@ const Radio = forwardRef((_props: RadioProps, ref: Ref<HTMLDivElement>) => {

const renderIcon = () => {
if (Array.isArray(icon)) {
return radioChecked ? icon[0] : icon[1] ?? null;
return radioChecked ? icon[0] : (icon[1] ?? null);
}
if (radioChecked) {
if (icon === 'circle') {
return <CheckCircleFilledIcon className={`${radioClass}__icon-wrap`} />;
}
if (icon === 'line') {
return <CheckIcon className={`${radioClass}__icon-wrap`} />;
}
if (icon === 'dot') {
let dotIconClassName = `${radioClass}__icon-${icon}`;
disabled && (dotIconClassName += ` ${radioClass}__icon-${icon}--disabled`);
return <div className={dotIconClassName} />;
}
} else {
if (icon === 'circle' || icon === 'dot') {
let circleIconClassName = `${radioClass}__icon-circle`;
disabled && (circleIconClassName += ` ${radioClass}__icon-circle--disabled`);
return <div className={circleIconClassName} />;
}
if (icon === 'line') {
return <div className="placeholder" />;
}
}
return (
<div
className={classNames(
`${classPrefix}__icon`, {
[`${classPrefix}__icon--checked`]: radioChecked,
[`${classPrefix}__icon--disabled`]: disabled,
// [`${classPrefix}__icon--strock`]: icon === 'stroke-line',
[`${classPrefix}__icon--custom`]: Array.isArray(icon),
})}
>
<CheckIcon size={16} />
</div>
);
};

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

const radioClassName = classNames(
`${classPrefix}`,
{ [`${prefix}-is-checked`]: radioChecked },
{ [`${prefix}-is-disabled`]: disabled },

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

const titleClassName = classNames(
`${prefix}__content-title`,
{ [`${prefix}-is-disabled`]: disabled },
{ [`${prefix}__content-right-title`]: align === 'right' },
)
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-${align}`)}
className={classNames(`${radioClass}__original`)}
checked={radioChecked}
onClick={(e) => {
e.stopPropagation();
Expand All @@ -122,30 +123,31 @@ const Radio = forwardRef((_props: RadioProps, ref: Ref<HTMLDivElement>) => {
/>
);

const iconClass = classNames(`${radioClass}__icon`, `${radioClass}__icon--${placement}`, {
[`${radioClass}__icon--checked`]: radioChecked,
[`${radioClass}__icon--disabled`]: disabled,
});
return (
<div className={radioClassName} ref={ref} onClick={() => switchRadioChecked()}>
<span className={`${classPrefix}__content-wrap`}>
{align === 'left' &&<span className={ `${classPrefix}__icon-wrap ${classPrefix}__icon-left-wrap`}>
{input}
{renderIcon()}
</span>}
<span className={`${classPrefix}__label-wrap`}>
{label && <span className={titleClassName} style={labelStyle}>{label}</span>}
{(children || content) && (
<div className={`${classPrefix}__content-inner`} style={getLimitRow(maxContentRow)}>
{children || content}
</div>
)}
</span>
{align === 'right' &&<span className={ `${classPrefix}__icon-wrap ${classPrefix}__icon-right-wrap`}>
{input}
{renderIcon()}
</span>}
</span>
{/* 下边框 */}
{ <div className={`${classPrefix}__border ${classPrefix}__border--${align}`}></div>}
{/* { !borderless && <div className={`${classPrefix}__border ${classPrefix}__border--${align}`}></div>} */}
<div ></div>
{input}
<div className={iconClass}>{renderIcon()}</div>
<div className={`${radioClass}__content`}>
{(label || children) && (
<span className={titleClassName} style={{ WebkitLineClamp: maxLabelRow }}>
{label || children}
</span>
)}
{content && (
<div
className={`${radioClass}__description ${disabled && `${radioClass}__description--disabled`}`}
style={getLimitRow(maxContentRow)}
>
byq1213 marked this conversation as resolved.
Show resolved Hide resolved
{content}
</div>
)}
</div>

{!borderless && block && <div className={`${radioClass}__border ${radioClass}__border--${placement}`}></div>}
</div>
);
});
Expand Down
22 changes: 11 additions & 11 deletions src/radio/RadioGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import React, { FC, ReactNode, useRef } from 'react';
import React, { useRef } from 'react';
import type { FC, ReactNode } from 'react';
import useConfig from '../_util/useConfig';
import Radio, { RadioContext, RadioContextValue, RadioProps } from './Radio';
import { TdRadioGroupProps } from './type';
import useDefault from '../_util/useDefault';
import type { TdRadioGroupProps } from './type';

export interface RadioGroupProps extends TdRadioGroupProps {
children?: ReactNode;
className: string;
}

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, className } = props;
const groupRef = useRef(null);
const [internalValue, setInternalValue] = useDefault(value, defaultValue, onChange);

Expand All @@ -26,6 +28,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 All @@ -38,7 +42,7 @@ const RadioGroup: FC<RadioGroupProps> = (props) => {
};

const renderOptions = () =>
options.map((option) => {
options.map((option, index) => {
if (typeof option === 'number' || typeof option === 'string') {
return (
<Radio value={option} key={option} label={option}>
Expand All @@ -47,18 +51,14 @@ const RadioGroup: FC<RadioGroupProps> = (props) => {
);
}
return (
<Radio value={option.value} key={option.value} disabled={option.disabled}>
<Radio value={option.value} key={index} disabled={option.disabled}>
{option.label}
</Radio>
);
});
return (
<div ref={groupRef} className={`${classPrefix}-radio-group`}>
<div className={`${classPrefix}-cell-group`}>
<div className={`${classPrefix}cell-group__container`}>
<RadioContext.Provider value={context}>{options?.length ? renderOptions() : children}</RadioContext.Provider>
</div>
</div>
<div ref={groupRef} className={`${classPrefix}-radio-group ${className || ''}`}>
<RadioContext.Provider value={context}>{options?.length ? renderOptions() : children}</RadioContext.Provider>
</div>
);
};
Expand Down
8 changes: 5 additions & 3 deletions src/radio/_example/base.jsx → src/radio/_example/base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ export default function Base() {
<Radio label="单选" value="idx0"></Radio>
<Radio label="单选" value="idx1"></Radio>
<Radio label="单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选" value="idx2"></Radio>
<Radio label="单选" value="idx3">
单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选
</Radio>
<Radio
label="单选"
value="idx3"
content="描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息"
></Radio>
</RadioGroup>
);
}
25 changes: 25 additions & 0 deletions src/radio/_example/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useState } from 'react';
import { Radio, RadioGroup } from 'tdesign-mobile-react';

export default function () {
const [defaultValue, setDefaultValue] = useState('1');
const options = [
{
value: '1',
label: '单选',
},
{
value: '2',
label: '单选',
},
{
value: '3',
label: '单选标题多行单选标题多行单选标题多行单选标题多行单选标题多行',
},
];
return (
<RadioGroup className="theme-card" value={defaultValue} onChange={setDefaultValue}>
{options.map((opt) => <Radio value={opt.value} label={opt.label} key={opt.value}></Radio>)}
</RadioGroup>
);
}
39 changes: 39 additions & 0 deletions src/radio/_example/custom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useState } from 'react';
import { Radio, RadioGroup } from 'tdesign-mobile-react';
import { Icon } from 'tdesign-icons-react';
import TDemoBlock from '../../../site/mobile/components/DemoBlock';

export default function () {
const [defaultValue, setDefaultValue] = useState(0);
const [defaultValueH, setDefaultValueH] = useState(0);
return (
<>
<TDemoBlock summary="单选框尺寸规格">
<RadioGroup value={defaultValue} onChange={(value) => setDefaultValue(value)}>
{Array.from(Array(3), (_, key) => (
<div className={`card ${defaultValue === key ? 'card--active' : ''}`} key={key}>
{defaultValue === key && <Icon name="check" color="#fff" className="card__icon" />}
<Radio
value={key}
borderless
label="单选"
content="描述信息描述信息描述信息描述信息描述信息"
icon="none"
></Radio>
</div>
))}
</RadioGroup>
</TDemoBlock>
<TDemoBlock summary="横向卡片单选框">
<RadioGroup className="horizontal-box" value={defaultValueH} onChange={(value) => setDefaultValueH(value)}>
{Array.from(Array(3), (_, key) => (
<div className={`card ${defaultValueH === key ? 'card--active' : ''}`} key={key}>
{defaultValueH === key && <Icon name="check" color="#fff" className="card__icon" />}
<Radio borderless value={key} label="单选" icon="none"></Radio>
</div>
))}
</RadioGroup>
</TDemoBlock>
</>
);
}
40 changes: 0 additions & 40 deletions src/radio/_example/group.jsx

This file was deleted.

Loading
Loading