fix: Standalone Badge motion style (#28240)

* fix: badge motion style

* chore: Clean up related code

* test: Update test case
This commit is contained in:
二货机器人 2020-12-07 21:23:42 +08:00 committed by GitHub
parent 237ab9a324
commit 600fc0a9fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 100 additions and 32 deletions

View File

@ -1,4 +1,5 @@
import * as React from 'react';
import { useState } from 'react';
import classNames from 'classnames';
import { ConfigContext } from '../config-provider';
import { cloneElement } from '../_util/reactNode';
@ -63,10 +64,10 @@ const ScrollNumber: React.FC<ScrollNumberProps> = ({
onAnimated = () => {},
...restProps
}) => {
const [animateStarted, setAnimateStarted] = React.useState(true);
const [count, setCount] = React.useState(customizeCount);
const [prevCount, setPrevCount] = React.useState(customizeCount);
const [lastCount, setLastCount] = React.useState(customizeCount);
const [animateStarted, setAnimateStarted] = useState(true);
const [count, setCount] = useState(customizeCount);
const [prevCount, setPrevCount] = useState(customizeCount);
const [lastCount, setLastCount] = useState(customizeCount);
const { getPrefixCls } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('scroll-number', customizePrefixCls);
@ -154,15 +155,12 @@ const ScrollNumber: React.FC<ScrollNumberProps> = ({
);
};
const numberNodeRef = React.useRef<React.ReactNode>(null);
if (show) {
numberNodeRef.current =
count && Number(count) % 1 === 0
? getNumberArray(count)
.map((num, i) => renderCurrentNumber(num, i))
.reverse()
: count;
}
const numberNode =
count && Number(count) % 1 === 0
? getNumberArray(count)
.map((num, i) => renderCurrentNumber(num, i))
.reverse()
: count;
// allow specify the border
// mock border-color by box-shadow for compatible with old usage:
@ -178,7 +176,7 @@ const ScrollNumber: React.FC<ScrollNumberProps> = ({
className: classNames(`${prefixCls}-custom-component`, oriProps?.className),
}));
}
return React.createElement(component as any, newProps, numberNodeRef.current);
return React.createElement(component as any, newProps, numberNode);
};
export default ScrollNumber;

View File

@ -994,6 +994,24 @@ exports[`renders ./components/badge/demo/no-wrapper.md correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
<div
class="ant-space-item"
style="margin-right:8px"
>
<button
aria-checked="true"
class="ant-switch ant-switch-checked"
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
/>
</button>
</div>
<div
class="ant-space-item"
style="margin-right:8px"

View File

@ -16,15 +16,29 @@ title:
Used in standalone when children is empty.
```jsx
import { Badge, Space } from 'antd';
import { Badge, Space, Switch } from 'antd';
const Demo = () => (
<Space>
<Badge count={25} />
<Badge count={4} className="site-badge-count-4" />
<Badge className="site-badge-count-109" count={109} style={{ backgroundColor: '#52c41a' }} />
</Space>
);
const Demo = () => {
const [show, setShow] = React.useState(true);
return (
<Space>
<Switch
checked={show}
onChange={() => {
setShow(!show);
}}
/>
<Badge count={show ? 25 : 0} />
<Badge count={show ? 4 : 0} className="site-badge-count-4" />
<Badge
className="site-badge-count-109"
count={show ? 109 : 0}
style={{ backgroundColor: '#52c41a' }}
/>
</Space>
);
};
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -1,4 +1,5 @@
import * as React from 'react';
import { useMemo, useRef } from 'react';
import CSSMotion from 'rc-motion';
import classNames from 'classnames';
import ScrollNumber from './ScrollNumber';
@ -68,21 +69,28 @@ const Badge: CompoundedComponent = ({
const showAsDot = (dot && !isZero) || hasStatus;
const displayCount = showAsDot ? '' : numberedDisplayCount;
const mergedCount = showAsDot ? '' : numberedDisplayCount;
const isHidden = React.useMemo(() => {
const isEmpty = displayCount === null || displayCount === undefined || displayCount === '';
const isHidden = useMemo(() => {
const isEmpty = mergedCount === null || mergedCount === undefined || mergedCount === '';
return (isEmpty || (isZero && !showZero)) && !showAsDot;
}, [displayCount, isZero, showZero, showAsDot]);
}, [mergedCount, isZero, showZero, showAsDot]);
// We need cache count since remove motion should not change count display
const displayCountRef = useRef(mergedCount);
if (!isHidden) {
displayCountRef.current = mergedCount;
}
const displayCount = displayCountRef.current;
// We will cache the dot status to avoid shaking on leaved motion
const isDotRef = React.useRef(showAsDot);
const isDotRef = useRef(showAsDot);
if (!isHidden) {
isDotRef.current = showAsDot;
}
// =============================== Styles ===============================
const mergedStyle = React.useMemo<React.CSSProperties>(() => {
const mergedStyle = useMemo<React.CSSProperties>(() => {
if (!offset) {
return { ...style };
}
@ -173,7 +181,7 @@ const Badge: CompoundedComponent = ({
[`${prefixCls}-count`]: !isDot,
[`${prefixCls}-count-sm`]: size === 'small',
[`${prefixCls}-multiple-words`]:
!isDot && count && count.toString && count.toString().length > 1,
!isDot && displayCount && displayCount?.toString().length > 1,
[`${prefixCls}-status-${status}`]: !!status,
[`${prefixCls}-status-${color}`]: isPresetColor(color),
});

View File

@ -124,16 +124,25 @@
&-zoom-appear,
&-zoom-enter {
animation: antZoomBadgeIn 0.3s @ease-out-back;
animation: antZoomBadgeIn @animation-duration-slow @ease-out-back;
animation-fill-mode: both;
}
&-zoom-leave {
animation: antZoomBadgeOut 0.3s @ease-in-back;
animation: antZoomBadgeOut @animation-duration-slow @ease-in-back;
animation-fill-mode: both;
}
&-not-a-wrapper {
.@{badge-prefix-cls}-zoom-appear,
.@{badge-prefix-cls}-zoom-enter {
animation: antNoWrapperZoomBadgeIn @animation-duration-slow @ease-out-back;
}
.@{badge-prefix-cls}-zoom-leave {
animation: antNoWrapperZoomBadgeOut @animation-duration-slow @ease-in-back;
}
&:not(.@{badge-prefix-cls}-status) {
vertical-align: middle;
}
@ -142,6 +151,7 @@
position: relative;
top: auto;
display: block;
transform-origin: 50% 50%;
}
.@{badge-prefix-cls}-count {
@ -166,7 +176,7 @@
&-only {
display: inline-block;
height: @badge-height;
transition: all 0.3s @ease-in-out;
transition: all @animation-duration-slow @ease-in-out;
> p.@{number-prefix-cls}-only-unit {
height: @badge-height;
margin: 0;
@ -198,5 +208,25 @@
}
}
@keyframes antNoWrapperZoomBadgeIn {
0% {
transform: scale(0);
opacity: 0;
}
100% {
transform: scale(1);
}
}
@keyframes antNoWrapperZoomBadgeOut {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
opacity: 0;
}
}
@import './ribbon';
@import './rtl';