mirror of
https://github.com/ant-design/ant-design.git
synced 2024-12-19 03:54:28 +08:00
Refactor Badge and support count as custom component
close #11134 close #12140
This commit is contained in:
parent
35e3e9b8bf
commit
fc6ac421c2
@ -15,6 +15,7 @@ export interface ScrollNumberProps {
|
|||||||
prefixCls?: string;
|
prefixCls?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
count?: string | number | null;
|
count?: string | number | null;
|
||||||
|
displayComponent?: React.ReactElement<any>;
|
||||||
component?: string;
|
component?: string;
|
||||||
onAnimated?: Function;
|
onAnimated?: Function;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
@ -116,22 +117,23 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderNumberElement() {
|
renderNumberElement() {
|
||||||
const state = this.state;
|
const { count } = this.state;
|
||||||
if (!state.count || isNaN(state.count as number)) {
|
if (!count || isNaN(count as number)) {
|
||||||
return state.count;
|
return count;
|
||||||
}
|
}
|
||||||
return getNumberArray(state.count)
|
return getNumberArray(count)
|
||||||
.map((num, i) => this.renderCurrentNumber(num, i)).reverse();
|
.map((num, i) => this.renderCurrentNumber(num, i)).reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { prefixCls, className, style, title, component = 'sup' } = this.props;
|
const { prefixCls, className, style, title, component = 'sup', displayComponent } = this.props;
|
||||||
// fix https://fb.me/react-unknown-prop
|
// fix https://fb.me/react-unknown-prop
|
||||||
const restProps = omit(this.props, [
|
const restProps = omit(this.props, [
|
||||||
'count',
|
'count',
|
||||||
'onAnimated',
|
'onAnimated',
|
||||||
'component',
|
'component',
|
||||||
'prefixCls',
|
'prefixCls',
|
||||||
|
'displayComponent',
|
||||||
]);
|
]);
|
||||||
const newProps = {
|
const newProps = {
|
||||||
...restProps,
|
...restProps,
|
||||||
@ -144,6 +146,11 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
|
|||||||
if (style && style.borderColor) {
|
if (style && style.borderColor) {
|
||||||
newProps.style.boxShadow = `0 0 0 1px ${style.borderColor} inset`;
|
newProps.style.boxShadow = `0 0 0 1px ${style.borderColor} inset`;
|
||||||
}
|
}
|
||||||
|
if (displayComponent) {
|
||||||
|
return React.cloneElement(displayComponent, {
|
||||||
|
className: `${prefixCls}-custom-component`,
|
||||||
|
});
|
||||||
|
}
|
||||||
return createElement(
|
return createElement(
|
||||||
component as any,
|
component as any,
|
||||||
newProps,
|
newProps,
|
||||||
|
@ -186,6 +186,35 @@ exports[`renders ./components/badge/demo/basic.md correctly 1`] = `
|
|||||||
0
|
0
|
||||||
</sup>
|
</sup>
|
||||||
</span>
|
</span>
|
||||||
|
<span
|
||||||
|
class="ant-badge"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="head-example"
|
||||||
|
href="#"
|
||||||
|
/>
|
||||||
|
<i
|
||||||
|
class="anticon anticon-clock-circle ant-scroll-number-custom-component"
|
||||||
|
style="color:#f5222d"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class=""
|
||||||
|
data-icon="clock-circle"
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="64 64 896 896"
|
||||||
|
width="1em"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</i>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ title:
|
|||||||
Simplest Usage. Badge will be hidden when `count` is `0`, but we can use `showZero` to show it.
|
Simplest Usage. Badge will be hidden when `count` is `0`, but we can use `showZero` to show it.
|
||||||
|
|
||||||
````jsx
|
````jsx
|
||||||
import { Badge } from 'antd';
|
import { Badge, Icon } from 'antd';
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<div>
|
<div>
|
||||||
@ -24,6 +24,9 @@ ReactDOM.render(
|
|||||||
<Badge count={0} showZero>
|
<Badge count={0} showZero>
|
||||||
<a href="#" className="head-example" />
|
<a href="#" className="head-example" />
|
||||||
</Badge>
|
</Badge>
|
||||||
|
<Badge count={<Icon type="clock-circle" style={{ color: '#f5222d' }} />}>
|
||||||
|
<a href="#" className="head-example" />
|
||||||
|
</Badge>
|
||||||
</div>,
|
</div>,
|
||||||
mountNode);
|
mountNode);
|
||||||
````
|
````
|
||||||
|
@ -8,7 +8,7 @@ export { ScrollNumberProps } from './ScrollNumber';
|
|||||||
|
|
||||||
export interface BadgeProps {
|
export interface BadgeProps {
|
||||||
/** Number to show in badge */
|
/** Number to show in badge */
|
||||||
count?: number | string | null;
|
count?: React.ReactNode;
|
||||||
showZero?: boolean;
|
showZero?: boolean;
|
||||||
/** Max count to show */
|
/** Max count to show */
|
||||||
overflowCount?: number;
|
overflowCount?: number;
|
||||||
@ -35,15 +35,125 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
count: PropTypes.oneOfType([
|
count: PropTypes.node,
|
||||||
PropTypes.string,
|
|
||||||
PropTypes.number,
|
|
||||||
]),
|
|
||||||
showZero: PropTypes.bool,
|
showZero: PropTypes.bool,
|
||||||
dot: PropTypes.bool,
|
dot: PropTypes.bool,
|
||||||
overflowCount: PropTypes.number,
|
overflowCount: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getBadgeClassName() {
|
||||||
|
const {
|
||||||
|
prefixCls,
|
||||||
|
className,
|
||||||
|
status,
|
||||||
|
children,
|
||||||
|
} = this.props;
|
||||||
|
return classNames(className, prefixCls, {
|
||||||
|
[`${prefixCls}-status`]: !!status,
|
||||||
|
[`${prefixCls}-not-a-wrapper`]: !children,
|
||||||
|
}) as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
isZero() {
|
||||||
|
const numberedDispayCount = this.getNumberedDispayCount();
|
||||||
|
return numberedDispayCount === '0' || numberedDispayCount === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDot() {
|
||||||
|
const { dot, status } = this.props;
|
||||||
|
const isZero = this.isZero();
|
||||||
|
return (dot && !isZero) || status;
|
||||||
|
}
|
||||||
|
|
||||||
|
isHidden() {
|
||||||
|
const { showZero } = this.props;
|
||||||
|
const displayCount = this.getDispayCount();
|
||||||
|
const isZero = this.isZero();
|
||||||
|
const isDot = this.isDot();
|
||||||
|
const isEmpty = displayCount === null || displayCount === undefined || displayCount === '';
|
||||||
|
return (isEmpty || (isZero && !showZero)) && !isDot;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNumberedDispayCount() {
|
||||||
|
const { count, overflowCount } = this.props;
|
||||||
|
const displayCount = (count as number) > (overflowCount as number) ? `${overflowCount}+` : count;
|
||||||
|
return displayCount as string | number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDispayCount() {
|
||||||
|
const isDot = this.isDot();
|
||||||
|
// dot mode don't need count
|
||||||
|
if (isDot) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return this.getNumberedDispayCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
getScollNumberTitle() {
|
||||||
|
const { title, count } = this.props;
|
||||||
|
if (title) {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
return (typeof count === 'string' || typeof count === 'number') ? count : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStyleWithOffset() {
|
||||||
|
const { offset, style } = this.props;
|
||||||
|
return offset ? {
|
||||||
|
right: -parseInt(offset[0] as string, 10),
|
||||||
|
marginTop: offset[1],
|
||||||
|
...style,
|
||||||
|
} : style;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStatusText() {
|
||||||
|
const { prefixCls, text } = this.props;
|
||||||
|
const hidden = this.isHidden();
|
||||||
|
return (hidden || !text) ? null : (
|
||||||
|
<span className={`${prefixCls}-status-text`}>{text}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDispayComponent() {
|
||||||
|
const { count } = this.props;
|
||||||
|
return (count && typeof count === 'object') ? (count as React.ReactElement<any>) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBadgeNumber() {
|
||||||
|
const {
|
||||||
|
count,
|
||||||
|
prefixCls,
|
||||||
|
scrollNumberPrefixCls,
|
||||||
|
status,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const displayCount = this.getDispayCount();
|
||||||
|
const isDot = this.isDot();
|
||||||
|
const hidden = this.isHidden();
|
||||||
|
|
||||||
|
const scrollNumberCls = classNames({
|
||||||
|
[`${prefixCls}-dot`]: isDot,
|
||||||
|
[`${prefixCls}-count`]: !isDot,
|
||||||
|
[`${prefixCls}-multiple-words`]: !isDot && count && count.toString && count.toString().length > 1,
|
||||||
|
[`${prefixCls}-status-${status}`]: !!status,
|
||||||
|
});
|
||||||
|
|
||||||
|
const styleWithOffset = this.getStyleWithOffset();
|
||||||
|
|
||||||
|
return hidden ? null : (
|
||||||
|
<ScrollNumber
|
||||||
|
prefixCls={scrollNumberPrefixCls}
|
||||||
|
data-show={!hidden}
|
||||||
|
className={scrollNumberCls}
|
||||||
|
count={displayCount}
|
||||||
|
displayComponent={this.renderDispayComponent()} // <Badge status="success" count={<Icon type="xxx" />}></Badge>
|
||||||
|
title={this.getScollNumberTitle()}
|
||||||
|
style={styleWithOffset}
|
||||||
|
key="scrollNumber"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
count,
|
count,
|
||||||
@ -61,62 +171,29 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
|||||||
title,
|
title,
|
||||||
...restProps
|
...restProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
let displayCount = (count as number) > (overflowCount as number) ? `${overflowCount}+` : count;
|
|
||||||
const isZero = displayCount === '0' || displayCount === 0;
|
const scrollNumber = this.renderBadgeNumber();
|
||||||
const isDot = (dot && !isZero) || status;
|
const statusText = this.renderStatusText();
|
||||||
// dot mode don't need count
|
|
||||||
if (isDot) {
|
|
||||||
displayCount = '';
|
|
||||||
}
|
|
||||||
const isEmpty = displayCount === null || displayCount === undefined || displayCount === '';
|
|
||||||
const hidden = (isEmpty || (isZero && !showZero)) && !isDot;
|
|
||||||
const statusCls = classNames({
|
const statusCls = classNames({
|
||||||
[`${prefixCls}-status-dot`]: !!status,
|
[`${prefixCls}-status-dot`]: !!status,
|
||||||
[`${prefixCls}-status-${status}`]: !!status,
|
[`${prefixCls}-status-${status}`]: !!status,
|
||||||
});
|
});
|
||||||
const scrollNumberCls = classNames({
|
|
||||||
[`${prefixCls}-dot`]: isDot,
|
const styleWithOffset = this.getStyleWithOffset();
|
||||||
[`${prefixCls}-count`]: !isDot,
|
|
||||||
[`${prefixCls}-multiple-words`]: !isDot && count && count.toString && count.toString().length > 1,
|
|
||||||
[`${prefixCls}-status-${status}`]: !!status,
|
|
||||||
});
|
|
||||||
const badgeCls = classNames(className, prefixCls, {
|
|
||||||
[`${prefixCls}-status`]: !!status,
|
|
||||||
[`${prefixCls}-not-a-wrapper`]: !children,
|
|
||||||
});
|
|
||||||
const styleWithOffset = offset ? {
|
|
||||||
right: -parseInt(offset[0] as string, 10),
|
|
||||||
marginTop: offset[1],
|
|
||||||
...style,
|
|
||||||
} : style;
|
|
||||||
// <Badge status="success" />
|
// <Badge status="success" />
|
||||||
if (!children && status) {
|
if (!children && status) {
|
||||||
return (
|
return (
|
||||||
<span {...restProps} className={badgeCls} style={styleWithOffset}>
|
<span {...restProps} className={this.getBadgeClassName()} style={styleWithOffset}>
|
||||||
<span className={statusCls} />
|
<span className={statusCls} />
|
||||||
<span className={`${prefixCls}-status-text`}>{text}</span>
|
<span className={`${prefixCls}-status-text`}>{text}</span>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrollNumber = hidden ? null : (
|
|
||||||
<ScrollNumber
|
|
||||||
prefixCls={scrollNumberPrefixCls}
|
|
||||||
data-show={!hidden}
|
|
||||||
className={scrollNumberCls}
|
|
||||||
count={displayCount}
|
|
||||||
title={title || count}
|
|
||||||
style={styleWithOffset}
|
|
||||||
key="scrollNumber"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const statusText = (hidden || !text) ? null : (
|
|
||||||
<span className={`${prefixCls}-status-text`}>{text}</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span {...restProps} className={badgeCls}>
|
<span {...restProps} className={this.getBadgeClassName()}>
|
||||||
{children}
|
{children}
|
||||||
<Animate
|
<Animate
|
||||||
component=""
|
component=""
|
||||||
|
@ -46,13 +46,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-count,
|
&-count,
|
||||||
&-dot {
|
&-dot,
|
||||||
|
.@{number-prefix-cls}-custom-component {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
transform: translateX(50%);
|
transform: translateX(50%);
|
||||||
transform-origin: 100%;
|
transform-origin: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.@{number-prefix-cls}-custom-component {
|
||||||
|
transform: translate(50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
&-status {
|
&-status {
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
|
Loading…
Reference in New Issue
Block a user