Skip to content

Commit 843b074

Browse files
committed
feat: badge support slot type count
1 parent 27880b0 commit 843b074

File tree

8 files changed

+182
-73
lines changed

8 files changed

+182
-73
lines changed

components/badge/Badge.jsx

Lines changed: 132 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
import PropTypes from '../_util/vue-types'
33
import ScrollNumber from './ScrollNumber'
44
import classNames from 'classnames'
5-
import { initDefaultProps, filterEmpty } from '../_util/props-util'
5+
import { initDefaultProps, filterEmpty, getComponentFromProp } from '../_util/props-util'
6+
import { cloneElement } from '../_util/vnode'
67
import getTransitionProps from '../_util/getTransitionProps'
8+
import isNumeric from '../_util/isNumeric'
79

810
export const BadgeProps = {
911
/** Number to show in badge */
10-
count: PropTypes.oneOfType([PropTypes.number, PropTypes.string, null]),
12+
count: PropTypes.any,
1113
showZero: PropTypes.bool,
1214
/** Max count to show */
1315
overflowCount: PropTypes.number,
@@ -27,85 +29,162 @@ export default {
2729
props: initDefaultProps(BadgeProps, {
2830
prefixCls: 'ant-badge',
2931
scrollNumberPrefixCls: 'ant-scroll-number',
30-
count: null,
3132
showZero: false,
3233
dot: false,
3334
overflowCount: 99,
3435
}),
36+
methods: {
37+
getBadgeClassName () {
38+
const { prefixCls, status } = this.$props
39+
const children = filterEmpty(this.$slots.default)
40+
return classNames(prefixCls, {
41+
[`${prefixCls}-status`]: !!status,
42+
[`${prefixCls}-not-a-wrapper`]: !children.length,
43+
})
44+
},
45+
46+
isZero () {
47+
const numberedDispayCount = this.getNumberedDispayCount()
48+
return numberedDispayCount === '0' || numberedDispayCount === 0
49+
},
50+
51+
isDot () {
52+
const { dot, status } = this.$props
53+
const isZero = this.isZero()
54+
return (dot && !isZero) || status
55+
},
56+
57+
isHidden () {
58+
const { showZero } = this.$props
59+
const displayCount = this.getDispayCount()
60+
const isZero = this.isZero()
61+
const isDot = this.isDot()
62+
const isEmpty = displayCount === null || displayCount === undefined || displayCount === ''
63+
return (isEmpty || (isZero && !showZero)) && !isDot
64+
},
65+
66+
getNumberedDispayCount () {
67+
const { overflowCount } = this.$props
68+
const count = this.badgeCount
69+
const displayCount =
70+
count > overflowCount ? `${overflowCount}+` : count
71+
return displayCount
72+
},
73+
74+
getDispayCount () {
75+
const isDot = this.isDot()
76+
// dot mode don't need count
77+
if (isDot) {
78+
return ''
79+
}
80+
return this.getNumberedDispayCount()
81+
},
82+
83+
getScollNumberTitle () {
84+
const { title } = this.$props
85+
const count = this.badgeCount
86+
if (title) {
87+
return title
88+
}
89+
return typeof count === 'string' || typeof count === 'number' ? count : undefined
90+
},
91+
92+
getStyleWithOffset () {
93+
const { offset, numberStyle } = this.$props
94+
return offset
95+
? {
96+
right: `${-parseInt(offset[0], 10)}px`,
97+
marginTop: isNumeric(offset[1]) ? `${offset[1]}px` : offset[1],
98+
...numberStyle,
99+
}
100+
: numberStyle
101+
},
102+
103+
renderStatusText () {
104+
const { prefixCls, text } = this.$props
105+
const hidden = this.isHidden()
106+
return hidden || !text ? null : <span class={`${prefixCls}-status-text`}>{text}</span>
107+
},
108+
109+
renderDispayComponent () {
110+
const count = this.badgeCount
111+
const customNode = count
112+
if (!customNode || typeof customNode !== 'object') {
113+
return undefined
114+
}
115+
return cloneElement(customNode, {
116+
style: this.getStyleWithOffset(),
117+
})
118+
},
119+
120+
renderBadgeNumber () {
121+
const { prefixCls, scrollNumberPrefixCls, status } = this.$props
122+
const count = this.badgeCount
123+
const displayCount = this.getDispayCount()
124+
const isDot = this.isDot()
125+
const hidden = this.isHidden()
126+
127+
const scrollNumberCls = {
128+
[`${prefixCls}-dot`]: isDot,
129+
[`${prefixCls}-count`]: !isDot,
130+
[`${prefixCls}-multiple-words`]:
131+
!isDot && count && count.toString && count.toString().length > 1,
132+
[`${prefixCls}-status-${status}`]: !!status,
133+
}
134+
135+
return hidden ? null : (
136+
<ScrollNumber
137+
prefixCls={scrollNumberPrefixCls}
138+
data-show={!hidden}
139+
v-show={!hidden}
140+
className={scrollNumberCls}
141+
count={displayCount}
142+
displayComponent={this.renderDispayComponent()} // <Badge status="success" count={<Icon type="xxx" />}></Badge>
143+
title={this.getScollNumberTitle()}
144+
style={this.getStyleWithOffset()}
145+
key='scrollNumber'
146+
/>
147+
)
148+
},
149+
},
35150

36151
render () {
37152
const {
38-
count,
39-
showZero,
40153
prefixCls,
41-
scrollNumberPrefixCls,
42-
overflowCount,
43-
dot,
44154
status,
45155
text,
46-
offset,
47156
$slots,
48-
numberStyle,
49-
title,
50157
} = this
51-
let displayCount = count > overflowCount ? `${overflowCount}+` : count
52-
const isZero = displayCount === '0' || displayCount === 0
53-
const isDot = (dot && !isZero) || status
54-
// dot mode don't need count
55-
if (isDot) {
56-
displayCount = ''
57-
}
58158
const children = filterEmpty($slots.default)
59-
const isEmpty = displayCount === null || displayCount === undefined || displayCount === ''
60-
const hidden = (isEmpty || (isZero && !showZero)) && !isDot
159+
let count = getComponentFromProp(this, 'count')
160+
if (Array.isArray(count)) {
161+
count = count[0]
162+
}
163+
this.badgeCount = count
164+
const scrollNumber = this.renderBadgeNumber()
165+
const statusText = this.renderStatusText()
61166
const statusCls = classNames({
62167
[`${prefixCls}-status-dot`]: !!status,
63168
[`${prefixCls}-status-${status}`]: !!status,
64169
})
65-
const scrollNumberCls = classNames({
66-
[`${prefixCls}-dot`]: isDot,
67-
[`${prefixCls}-count`]: !isDot,
68-
[`${prefixCls}-multiple-words`]: !isDot && count && count.toString && count.toString().length > 1,
69-
[`${prefixCls}-status-${status}`]: !!status,
70-
})
71-
const badgeCls = classNames(prefixCls, {
72-
[`${prefixCls}-status`]: !!status,
73-
[`${prefixCls}-not-a-wrapper`]: !children.length,
74-
})
75-
const styleWithOffset = offset ? {
76-
right: -parseInt(offset[0], 10),
77-
marginTop: typeof offset[1] === 'number' ? `${offset[1]}px` : offset[1],
78-
...numberStyle,
79-
} : numberStyle
80-
// <Badge status="success" />
81170

171+
// <Badge status="success" />
82172
if (!children.length && status) {
83173
return (
84-
<span {...{ on: this.$listeners }} class={badgeCls} style={styleWithOffset}>
174+
<span
175+
{...{ on: this.$listeners }}
176+
class={this.getBadgeClassName()}
177+
style={this.getStyleWithOffset()}
178+
>
85179
<span class={statusCls} />
86180
<span class={`${prefixCls}-status-text`}>{text}</span>
87181
</span>
88182
)
89183
}
90184

91-
const scrollNumber = hidden ? null : (
92-
<ScrollNumber
93-
prefixCls={scrollNumberPrefixCls}
94-
v-show={!hidden}
95-
class={scrollNumberCls}
96-
count={displayCount}
97-
title={title || count}
98-
style={styleWithOffset}
99-
key='scrollNumber'
100-
/>
101-
)
102-
103-
const statusText = (hidden || !text) ? null : (
104-
<span class={`${prefixCls}-status-text`}>{text}</span>
105-
)
106185
const transitionProps = getTransitionProps(children.length ? `${prefixCls}-zoom` : '')
107186

108-
return (<span {...{ on: this.$listeners }} class={badgeCls}>
187+
return (<span {...{ on: this.$listeners }} class={this.getBadgeClassName()}>
109188
{children}
110189
<transition {...transitionProps}>
111190
{scrollNumber}

components/badge/ScrollNumber.jsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
1+
import classNames from 'classnames'
22
import PropTypes from '../_util/vue-types'
33
import BaseMixin from '../_util/BaseMixin'
44
import { getStyle } from '../_util/props-util'
55
import omit from 'omit.js'
6+
import { cloneElement } from '../_util/vnode'
67

78
function getNumberArray (num) {
89
return num
@@ -14,9 +15,11 @@ function getNumberArray (num) {
1415

1516
const ScrollNumberProps = {
1617
prefixCls: PropTypes.string.def('ant-scroll-number'),
17-
count: PropTypes.oneOfType([PropTypes.number, PropTypes.string, null]).def(null),
18+
count: PropTypes.any,
1819
component: PropTypes.string,
1920
title: PropTypes.oneOfType([PropTypes.number, PropTypes.string, null]),
21+
displayComponent: PropTypes.any,
22+
className: PropTypes.object,
2023
}
2124

2225
export default {
@@ -105,13 +108,19 @@ export default {
105108
},
106109

107110
render () {
108-
const { prefixCls, title, component: Tag = 'sup' } = this
111+
const { prefixCls, title, component: Tag = 'sup', displayComponent, className } = this
112+
if (displayComponent) {
113+
return cloneElement(displayComponent, {
114+
class: `${prefixCls}-custom-component`,
115+
})
116+
}
109117
const style = getStyle(this, true)
110118
// fix https://fb.me/react-unknown-prop
111119
const restProps = omit(this.$props, [
112120
'count',
113121
'component',
114122
'prefixCls',
123+
'displayComponent',
115124
])
116125
const newProps = {
117126
props: {
@@ -120,15 +129,16 @@ export default {
120129
attrs: {
121130
title,
122131
},
123-
class: prefixCls,
124132
style,
133+
class: classNames(prefixCls, className),
125134
}
126135
// allow specify the border
127136
// mock border-color by box-shadow for compatible with old usage:
128137
// <Badge count={4} style={{ backgroundColor: '#fff', color: '#999', borderColor: '#d9d9d9' }} />
129138
if (style && style.borderColor) {
130139
newProps.style.boxShadow = `0 0 0 1px ${style.borderColor} inset`
131140
}
141+
132142
return (
133143
<Tag {...newProps} >
134144
{ this.renderNumberElement()}

0 commit comments

Comments
 (0)