Browse Source

refactor(badge): rewrite with hook (#23488)

* refactor(badge): rewrite with hook

* Update ScrollNumber.tsx
pull/23602/head
Tom Xu 5 years ago
committed by GitHub
parent
commit
8f30a14790
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 130
      components/badge/ScrollNumber.tsx
  2. 160
      components/badge/index.tsx

130
components/badge/ScrollNumber.tsx

@ -50,73 +50,50 @@ export interface ScrollNumberState {
count?: string | number | null;
}
class ScrollNumber extends React.Component<ScrollNumberProps, ScrollNumberState> {
static defaultProps = {
count: null,
onAnimated() {},
};
static getDerivedStateFromProps(nextProps: ScrollNumberProps, nextState: ScrollNumberState) {
if ('count' in nextProps) {
if (nextState.count === nextProps.count) {
return null;
}
return {
animateStarted: true,
};
}
return null;
}
lastCount?: string | number | null;
private timeout?: number;
constructor(props: ScrollNumberProps) {
super(props);
this.state = {
animateStarted: true,
count: props.count,
};
const ScrollNumber: React.FC<ScrollNumberProps> = props => {
const [animateStarted, setAnimateStarted] = React.useState(true);
const [count, setCount] = React.useState(props.count);
const [prevCount, setPrevCount] = React.useState(props.count);
const [lastCount, setLastCount] = React.useState(props.count);
if (prevCount !== props.count) {
setAnimateStarted(true);
setPrevCount(props.count);
}
componentDidUpdate(_: any, prevState: ScrollNumberState) {
this.lastCount = prevState.count;
const { animateStarted } = this.state;
React.useEffect(() => {
setLastCount(count);
let timeout: number;
if (animateStarted) {
this.clearTimeout();
// Let browser has time to reset the scroller before actually
// performing the transition.
this.timeout = setTimeout(() => {
// eslint-disable-next-line react/no-did-update-set-state
this.setState(
(__, props) => ({
animateStarted: false,
count: props.count,
}),
this.onAnimated,
);
timeout = setTimeout(() => {
setAnimateStarted(false);
setCount(props.count);
if (props.onAnimated) {
props.onAnimated();
}
});
}
}
componentWillUnmount() {
this.clearTimeout();
}
return () => {
if (timeout) {
clearTimeout(timeout);
}
};
}, [animateStarted, count, props.count, props.onAnimated]);
getPositionByNum(num: number, i: number) {
const { count } = this.state;
const getPositionByNum = (num: number, i: number) => {
const currentCount = Math.abs(Number(count));
const lastCount = Math.abs(Number(this.lastCount));
const currentDigit = Math.abs(getNumberArray(this.state.count)[i] as number);
const lastDigit = Math.abs(getNumberArray(this.lastCount)[i] as number);
const lstCount = Math.abs(Number(lastCount));
const currentDigit = Math.abs(getNumberArray(count)[i] as number);
const lastDigit = Math.abs(getNumberArray(lstCount)[i] as number);
if (this.state.animateStarted) {
if (animateStarted) {
return 10 + num;
}
// 同方向则在同一侧切换数字
if (currentCount > lastCount) {
if (currentCount > lstCount) {
if (currentDigit >= lastDigit) {
return 10 + num;
}
@ -126,20 +103,12 @@ class ScrollNumber extends React.Component<ScrollNumberProps, ScrollNumberState>
return 10 + num;
}
return num;
}
onAnimated = () => {
const { onAnimated } = this.props;
if (onAnimated) {
onAnimated();
}
};
renderCurrentNumber(prefixCls: string, num: number | string, i: number) {
const renderCurrentNumber = (prefixCls: string, num: number | string, i: number) => {
if (typeof num === 'number') {
const position = this.getPositionByNum(num, i);
const removeTransition =
this.state.animateStarted || getNumberArray(this.lastCount)[i] === undefined;
const position = getPositionByNum(num, i);
const removeTransition = animateStarted || getNumberArray(lastCount)[i] === undefined;
return React.createElement(
'span',
{
@ -161,19 +130,18 @@ class ScrollNumber extends React.Component<ScrollNumberProps, ScrollNumberState>
{num}
</span>
);
}
};
renderNumberElement(prefixCls: string) {
const { count } = this.state;
const renderNumberElement = (prefixCls: string) => {
if (count && Number(count) % 1 === 0) {
return getNumberArray(count)
.map((num, i) => this.renderCurrentNumber(prefixCls, num, i))
.map((num, i) => renderCurrentNumber(prefixCls, num, i))
.reverse();
}
return count;
}
};
renderScrollNumber = ({ getPrefixCls }: ConfigConsumerProps) => {
const renderScrollNumber = ({ getPrefixCls }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,
className,
@ -181,9 +149,9 @@ class ScrollNumber extends React.Component<ScrollNumberProps, ScrollNumberState>
title,
component = 'sup',
displayComponent,
} = this.props;
} = props;
// fix https://fb.me/react-unknown-prop
const restProps = omit(this.props, [
const restProps = omit(props, [
'count',
'onAnimated',
'component',
@ -214,19 +182,15 @@ class ScrollNumber extends React.Component<ScrollNumberProps, ScrollNumberState>
),
});
}
return React.createElement(component as any, newProps, this.renderNumberElement(prefixCls));
return React.createElement(component as any, newProps, renderNumberElement(prefixCls));
};
render() {
return <ConfigConsumer>{this.renderScrollNumber}</ConfigConsumer>;
}
return <ConfigConsumer>{renderScrollNumber}</ConfigConsumer>;
};
private clearTimeout(): void {
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = undefined;
}
}
}
ScrollNumber.defaultProps = {
count: null,
onAnimated() {},
};
export default ScrollNumber;

160
components/badge/index.tsx

@ -32,40 +32,47 @@ function isPresetColor(color?: string): boolean {
return (PresetColorTypes as any[]).indexOf(color) !== -1;
}
export default class Badge extends React.Component<BadgeProps, any> {
static defaultProps = {
count: null,
showZero: false,
dot: false,
overflowCount: 99,
};
getNumberedDisplayCount() {
const { count, overflowCount } = this.props;
const Badge: React.FC<BadgeProps> = props => {
const getNumberedDisplayCount = () => {
const { count, overflowCount } = props;
const displayCount =
(count as number) > (overflowCount as number) ? `${overflowCount}+` : count;
return displayCount as string | number | null;
}
};
getDisplayCount() {
const isDot = this.isDot();
const hasStatus = (): boolean => {
const { status, color } = props;
return !!status || !!color;
};
const isZero = () => {
const numberedDisplayCount = getNumberedDisplayCount();
return numberedDisplayCount === '0' || numberedDisplayCount === 0;
};
const isDot = () => {
const { dot } = props;
return (dot && !isZero()) || hasStatus();
};
const getDisplayCount = () => {
// dot mode don't need count
if (isDot) {
if (isDot()) {
return '';
}
return this.getNumberedDisplayCount();
}
return getNumberedDisplayCount();
};
getScrollNumberTitle() {
const { title, count } = this.props;
const getScrollNumberTitle = () => {
const { title, count } = props;
if (title) {
return title;
}
return typeof count === 'string' || typeof count === 'number' ? count : undefined;
}
};
getStyleWithOffset() {
const { offset, style } = this.props;
const getStyleWithOffset = () => {
const { offset, style } = props;
return offset
? {
right: -parseInt(offset[0] as string, 10),
@ -73,79 +80,61 @@ export default class Badge extends React.Component<BadgeProps, any> {
...style,
}
: style;
}
};
getBadgeClassName(prefixCls: string, direction: string = 'ltr') {
const { className, children } = this.props;
const getBadgeClassName = (prefixCls: string, direction: string = 'ltr') => {
const { className, children } = props;
return classNames(className, prefixCls, {
[`${prefixCls}-status`]: this.hasStatus(),
[`${prefixCls}-status`]: hasStatus(),
[`${prefixCls}-not-a-wrapper`]: !children,
[`${prefixCls}-rtl`]: direction === 'rtl',
}) as string;
}
hasStatus(): boolean {
const { status, color } = this.props;
return !!status || !!color;
}
};
isZero() {
const numberedDisplayCount = this.getNumberedDisplayCount();
return numberedDisplayCount === '0' || numberedDisplayCount === 0;
}
isDot() {
const { dot } = this.props;
const isZero = this.isZero();
return (dot && !isZero) || this.hasStatus();
}
isHidden() {
const { showZero } = this.props;
const displayCount = this.getDisplayCount();
const isZero = this.isZero();
const isDot = this.isDot();
const isHidden = () => {
const { showZero } = props;
const displayCount = getDisplayCount();
const isEmpty = displayCount === null || displayCount === undefined || displayCount === '';
return (isEmpty || (isZero && !showZero)) && !isDot;
}
return (isEmpty || (isZero() && !showZero)) && !isDot();
};
renderStatusText(prefixCls: string) {
const { text } = this.props;
const hidden = this.isHidden();
const renderStatusText = (prefixCls: string) => {
const { text } = props;
const hidden = isHidden();
return hidden || !text ? null : <span className={`${prefixCls}-status-text`}>{text}</span>;
}
};
renderDisplayComponent() {
const { count } = this.props;
const renderDisplayComponent = () => {
const { count } = props;
const customNode = count as React.ReactElement<any>;
if (!customNode || typeof customNode !== 'object') {
return undefined;
}
return React.cloneElement(customNode, {
style: {
...this.getStyleWithOffset(),
...getStyleWithOffset(),
...(customNode.props && customNode.props.style),
},
});
}
};
renderBadgeNumber(prefixCls: string, scrollNumberPrefixCls: string) {
const { status, count, color } = this.props;
const renderBadgeNumber = (prefixCls: string, scrollNumberPrefixCls: string) => {
const { status, count, color } = props;
const displayCount = this.getDisplayCount();
const isDot = this.isDot();
const hidden = this.isHidden();
const displayCount = getDisplayCount();
const dot = isDot();
const hidden = isHidden();
const scrollNumberCls = classNames({
[`${prefixCls}-dot`]: isDot,
[`${prefixCls}-count`]: !isDot,
[`${prefixCls}-dot`]: dot,
[`${prefixCls}-count`]: !dot,
[`${prefixCls}-multiple-words`]:
!isDot && count && count.toString && count.toString().length > 1,
!dot && count && count.toString && count.toString().length > 1,
[`${prefixCls}-status-${status}`]: !!status,
[`${prefixCls}-status-${color}`]: isPresetColor(color),
});
let statusStyle: React.CSSProperties | undefined = this.getStyleWithOffset();
let statusStyle: React.CSSProperties | undefined = getStyleWithOffset();
if (color && !isPresetColor(color)) {
statusStyle = statusStyle || {};
statusStyle.background = color;
@ -157,15 +146,15 @@ export default class Badge extends React.Component<BadgeProps, any> {
data-show={!hidden}
className={scrollNumberCls}
count={displayCount}
displayComponent={this.renderDisplayComponent()} // <Badge status="success" count={<Icon type="xxx" />}></Badge>
title={this.getScrollNumberTitle()}
displayComponent={renderDisplayComponent()} // <Badge status="success" count={<Icon type="xxx" />}></Badge>
title={getScrollNumberTitle()}
style={statusStyle}
key="scrollNumber"
/>
);
}
};
renderBadge = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
const renderBadge = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls,
scrollNumberPrefixCls: customizeScrollNumberPrefixCls,
@ -174,7 +163,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
text,
color,
...restProps
} = this.props;
} = props;
const omitArr = [
'count',
'showZero',
@ -189,11 +178,11 @@ export default class Badge extends React.Component<BadgeProps, any> {
const prefixCls = getPrefixCls('badge', customizePrefixCls);
const scrollNumberPrefixCls = getPrefixCls('scroll-number', customizeScrollNumberPrefixCls);
const scrollNumber = this.renderBadgeNumber(prefixCls, scrollNumberPrefixCls);
const statusText = this.renderStatusText(prefixCls);
const scrollNumber = renderBadgeNumber(prefixCls, scrollNumberPrefixCls);
const statusText = renderStatusText(prefixCls);
const statusCls = classNames({
[`${prefixCls}-status-dot`]: this.hasStatus(),
[`${prefixCls}-status-dot`]: hasStatus(),
[`${prefixCls}-status-${status}`]: !!status,
[`${prefixCls}-status-${color}`]: isPresetColor(color),
});
@ -203,13 +192,13 @@ export default class Badge extends React.Component<BadgeProps, any> {
}
// <Badge status="success" />
if (!children && this.hasStatus()) {
const styleWithOffset = this.getStyleWithOffset();
if (!children && hasStatus()) {
const styleWithOffset = getStyleWithOffset();
const statusTextColor = styleWithOffset && styleWithOffset.color;
return (
<span
{...omit(restProps, omitArr)}
className={this.getBadgeClassName(prefixCls, direction)}
className={getBadgeClassName(prefixCls, direction)}
style={styleWithOffset}
>
<span className={statusCls} style={statusStyle} />
@ -221,7 +210,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
}
return (
<span {...omit(restProps, omitArr)} className={this.getBadgeClassName(prefixCls, direction)}>
<span {...omit(restProps, omitArr)} className={getBadgeClassName(prefixCls, direction)}>
{children}
<Animate
component=""
@ -236,7 +225,14 @@ export default class Badge extends React.Component<BadgeProps, any> {
);
};
render() {
return <ConfigConsumer>{this.renderBadge}</ConfigConsumer>;
}
}
return <ConfigConsumer>{renderBadge}</ConfigConsumer>;
};
Badge.defaultProps = {
count: null,
showZero: false,
dot: false,
overflowCount: 99,
};
export default Badge;

Loading…
Cancel
Save