refactor(tag): rewrite with hook (#23466)

* refactor(tag): rewrite with hook

* fix lint
This commit is contained in:
Tom Xu 2020-04-22 10:59:24 +08:00 committed by GitHub
parent d7e97aa996
commit cf3d611f59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 76 deletions

View File

@ -17,7 +17,7 @@ export interface PageHeaderProps {
subTitle?: React.ReactNode; subTitle?: React.ReactNode;
style?: React.CSSProperties; style?: React.CSSProperties;
breadcrumb?: BreadcrumbProps; breadcrumb?: BreadcrumbProps;
tags?: React.ReactElement<Tag> | React.ReactElement<Tag>[]; tags?: typeof Tag | typeof Tag[];
footer?: React.ReactNode; footer?: React.ReactNode;
extra?: React.ReactNode; extra?: React.ReactNode;
avatar?: AvatarProps; avatar?: AvatarProps;

View File

@ -10,16 +10,16 @@ export interface CheckableTagProps {
onChange?: (checked: boolean) => void; onChange?: (checked: boolean) => void;
} }
export default class CheckableTag extends React.Component<CheckableTagProps> { const CheckableTag: React.FC<CheckableTagProps> = props => {
handleClick = () => { const handleClick = () => {
const { checked, onChange } = this.props; const { checked, onChange } = props;
if (onChange) { if (onChange) {
onChange(!checked); onChange(!checked);
} }
}; };
renderCheckableTag = ({ getPrefixCls }: ConfigConsumerProps) => { const renderCheckableTag = ({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, className, checked, ...restProps } = this.props; const { prefixCls: customizePrefixCls, className, checked, ...restProps } = props;
const prefixCls = getPrefixCls('tag', customizePrefixCls); const prefixCls = getPrefixCls('tag', customizePrefixCls);
const cls = classNames( const cls = classNames(
prefixCls, prefixCls,
@ -31,10 +31,10 @@ export default class CheckableTag extends React.Component<CheckableTagProps> {
); );
delete (restProps as any).onChange; // TypeScript cannot check delete now. delete (restProps as any).onChange; // TypeScript cannot check delete now.
return <span {...(restProps as any)} className={cls} onClick={this.handleClick} />; return <span {...(restProps as any)} className={cls} onClick={handleClick} />;
}; };
render() { return <ConfigConsumer>{renderCheckableTag}</ConfigConsumer>;
return <ConfigConsumer>{this.renderCheckableTag}</ConfigConsumer>; };
}
} export default CheckableTag;

View File

@ -27,61 +27,57 @@ export interface TagProps extends React.HTMLAttributes<HTMLSpanElement> {
icon?: React.ReactNode; icon?: React.ReactNode;
} }
interface TagState {
visible: boolean;
}
const PresetColorRegex = new RegExp(`^(${PresetColorTypes.join('|')})(-inverse)?$`); const PresetColorRegex = new RegExp(`^(${PresetColorTypes.join('|')})(-inverse)?$`);
const PresetStatusColorRegex = new RegExp(`^(${PresetStatusColorTypes.join('|')})$`); const PresetStatusColorRegex = new RegExp(`^(${PresetStatusColorTypes.join('|')})$`);
class Tag extends React.Component<TagProps, TagState> { interface TagType extends React.FC<TagProps> {
static CheckableTag = CheckableTag; CheckableTag: typeof CheckableTag;
}
static defaultProps = { const Tag: TagType = props => {
closable: false, const [visible, setVisible] = React.useState(true);
};
static getDerivedStateFromProps(nextProps: TagProps) { React.useEffect(() => {
if ('visible' in nextProps) { if ('visible' in props) {
return { setVisible(props.visible!);
visible: nextProps.visible,
};
}
return null;
} }
}, [props.visible]);
state = { const isPresetColor = (): boolean => {
visible: true, const { color } = props;
if (!color) {
return false;
}
return PresetColorRegex.test(color) || PresetStatusColorRegex.test(color);
}; };
getTagStyle() { const getTagStyle = () => {
const { color, style } = this.props; const { color, style } = props;
const isPresetColor = this.isPresetColor();
return { return {
backgroundColor: color && !isPresetColor ? color : undefined, backgroundColor: color && !isPresetColor() ? color : undefined,
...style, ...style,
}; };
} };
getTagClassName({ getPrefixCls, direction }: ConfigConsumerProps) { const getTagClassName = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, className, color } = this.props; const { prefixCls: customizePrefixCls, className, color } = props;
const { visible } = this.state; const presetColor = isPresetColor();
const isPresetColor = this.isPresetColor();
const prefixCls = getPrefixCls('tag', customizePrefixCls); const prefixCls = getPrefixCls('tag', customizePrefixCls);
return classNames( return classNames(
prefixCls, prefixCls,
{ {
[`${prefixCls}-${color}`]: isPresetColor, [`${prefixCls}-${color}`]: presetColor,
[`${prefixCls}-has-color`]: color && !isPresetColor, [`${prefixCls}-has-color`]: color && !presetColor,
[`${prefixCls}-hidden`]: !visible, [`${prefixCls}-hidden`]: !visible,
[`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-rtl`]: direction === 'rtl',
}, },
className, className,
); );
} };
setVisible(visible: boolean, e: React.MouseEvent<HTMLElement>) { const handleIconClick = (e: React.MouseEvent<HTMLElement>) => {
const { onClose } = this.props; e.stopPropagation();
const { onClose } = props;
if (onClose) { if (onClose) {
onClose(e); onClose(e);
} }
@ -89,31 +85,18 @@ class Tag extends React.Component<TagProps, TagState> {
if (e.defaultPrevented) { if (e.defaultPrevented) {
return; return;
} }
if (!('visible' in this.props)) { if (!('visible' in props)) {
this.setState({ visible }); setVisible(false);
} }
}
handleIconClick = (e: React.MouseEvent<HTMLElement>) => {
e.stopPropagation();
this.setVisible(false, e);
}; };
isPresetColor(): boolean { const renderCloseIcon = () => {
const { color } = this.props; const { closable } = props;
if (!color) { return closable ? <CloseOutlined onClick={handleIconClick} /> : null;
return false; };
}
return PresetColorRegex.test(color) || PresetStatusColorRegex.test(color);
}
renderCloseIcon() { const renderTag = (configProps: ConfigConsumerProps) => {
const { closable } = this.props; const { children, icon, ...otherProps } = props;
return closable ? <CloseOutlined onClick={this.handleIconClick} /> : null;
}
renderTag = (configProps: ConfigConsumerProps) => {
const { children, icon, ...otherProps } = this.props;
const isNeedWave = const isNeedWave =
'onClick' in otherProps || (children && (children as React.ReactElement<any>).type === 'a'); 'onClick' in otherProps || (children && (children as React.ReactElement<any>).type === 'a');
const tagProps = omit(otherProps, ['onClose', 'color', 'visible', 'closable', 'prefixCls']); const tagProps = omit(otherProps, ['onClose', 'color', 'visible', 'closable', 'prefixCls']);
@ -129,26 +112,26 @@ class Tag extends React.Component<TagProps, TagState> {
return isNeedWave ? ( return isNeedWave ? (
<Wave> <Wave>
<span <span {...tagProps} className={getTagClassName(configProps)} style={getTagStyle()}>
{...tagProps}
className={this.getTagClassName(configProps)}
style={this.getTagStyle()}
>
{kids} {kids}
{this.renderCloseIcon()} {renderCloseIcon()}
</span> </span>
</Wave> </Wave>
) : ( ) : (
<span {...tagProps} className={this.getTagClassName(configProps)} style={this.getTagStyle()}> <span {...tagProps} className={getTagClassName(configProps)} style={getTagStyle()}>
{kids} {kids}
{this.renderCloseIcon()} {renderCloseIcon()}
</span> </span>
); );
}; };
render() { return <ConfigConsumer>{renderTag}</ConfigConsumer>;
return <ConfigConsumer>{this.renderTag}</ConfigConsumer>; };
}
} Tag.defaultProps = {
closable: false,
};
Tag.CheckableTag = CheckableTag;
export default Tag; export default Tag;