Skip to content

Commit 15c645f

Browse files
authored
Merge pull request #802 from attacking/feat-1.4.0
Statistic Component
2 parents 3782411 + 1b16a43 commit 15c645f

24 files changed

+678
-0
lines changed

components/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ import { default as Slider } from './slider';
9595

9696
import { default as Spin } from './spin';
9797

98+
import { default as Statistic } from './statistic';
99+
98100
import { default as Steps } from './steps';
99101

100102
import { default as Switch } from './switch';
@@ -172,6 +174,7 @@ const components = [
172174
Select,
173175
Slider,
174176
Spin,
177+
Statistic,
175178
Steps,
176179
Switch,
177180
Table,
@@ -254,6 +257,7 @@ export {
254257
Select,
255258
Slider,
256259
Spin,
260+
Statistic,
257261
Steps,
258262
Switch,
259263
Table,

components/statistic/Countdown.jsx

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import * as moment from 'moment';
2+
import interopDefault from '../_util/interopDefault';
3+
import { cloneElement } from '../_util/vnode';
4+
import { initDefaultProps } from '../_util/props-util';
5+
import Statistic, { StatisticProps } from './Statistic';
6+
import { formatCountdown } from './utils';
7+
8+
const REFRESH_INTERVAL = 1000 / 30;
9+
10+
function getTime(value) {
11+
return interopDefault(moment)(value).valueOf();
12+
}
13+
14+
export default {
15+
name: 'AStatisticCountdown',
16+
props: initDefaultProps(StatisticProps, {
17+
format: 'HH:mm:ss',
18+
}),
19+
20+
data() {
21+
return {
22+
uniKey: 0,
23+
};
24+
},
25+
26+
countdownId: undefined,
27+
28+
mounted() {
29+
this.$nextTick(() => {
30+
this.syncTimer();
31+
});
32+
},
33+
34+
updated() {
35+
this.$nextTick(() => {
36+
this.syncTimer();
37+
});
38+
},
39+
40+
beforeDestroy() {
41+
this.stopTimer();
42+
},
43+
44+
methods: {
45+
syncTimer() {
46+
const { value } = this.$props;
47+
const timestamp = getTime(value);
48+
if (timestamp >= Date.now()) {
49+
this.startTimer();
50+
} else {
51+
this.stopTimer();
52+
}
53+
},
54+
55+
startTimer() {
56+
if (this.countdownId) {
57+
return;
58+
}
59+
this.countdownId = window.setInterval(() => {
60+
this.uniKey++;
61+
}, REFRESH_INTERVAL);
62+
},
63+
64+
stopTimer() {
65+
const { value } = this.$props;
66+
if (this.countdownId) {
67+
clearInterval(this.countdownId);
68+
this.countdownId = undefined;
69+
70+
const timestamp = getTime(value);
71+
if (timestamp < Date.now()) {
72+
this.$emit('finish');
73+
}
74+
}
75+
},
76+
77+
formatCountdown(value, config) {
78+
const { format } = this.$props;
79+
return formatCountdown(value, { ...config, format });
80+
},
81+
82+
// Countdown do not need display the timestamp
83+
valueRenderHtml: node =>
84+
cloneElement(node, {
85+
props: {
86+
title: undefined,
87+
},
88+
}),
89+
},
90+
91+
render() {
92+
return (
93+
<Statistic
94+
key={this.uniKey}
95+
{...{
96+
props: {
97+
...this.$props,
98+
valueRender: this.valueRenderHtml,
99+
formatter: this.formatCountdown,
100+
},
101+
}}
102+
/>
103+
);
104+
},
105+
};

components/statistic/Number.jsx

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import padEnd from 'lodash/padEnd';
2+
3+
export default {
4+
name: 'AStatisticNumber',
5+
functional: true,
6+
render(h, context) {
7+
const {
8+
value,
9+
formatter,
10+
precision,
11+
decimalSeparator,
12+
groupSeparator = '',
13+
prefixCls,
14+
} = context.props;
15+
16+
let valueNode;
17+
18+
if (typeof formatter === 'function') {
19+
// Customize formatter
20+
valueNode = formatter(value);
21+
} else {
22+
// Internal formatter
23+
const val = String(value);
24+
const cells = val.match(/^(-?)(\d*)(\.(\d+))?$/);
25+
// Process if illegal number
26+
if (!cells) {
27+
valueNode = val;
28+
} else {
29+
const negative = cells[1];
30+
let int = cells[2] || '0';
31+
let decimal = cells[4] || '';
32+
33+
int = int.replace(/\B(?=(\d{3})+(?!\d))/g, groupSeparator);
34+
if (typeof precision === 'number') {
35+
decimal = padEnd(decimal, precision, '0').slice(0, precision);
36+
}
37+
38+
if (decimal) {
39+
decimal = `${decimalSeparator}${decimal}`;
40+
}
41+
42+
valueNode = [
43+
<span key="int" class={`${prefixCls}-content-value-int`}>
44+
{negative}
45+
{int}
46+
</span>,
47+
decimal && (
48+
<span key="decimal" class={`${prefixCls}-content-value-decimal`}>
49+
{decimal}
50+
</span>
51+
),
52+
];
53+
}
54+
}
55+
56+
return <span class={`${prefixCls}-content-value`}>{valueNode}</span>;
57+
},
58+
};

components/statistic/Statistic.jsx

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import PropTypes from '../_util/vue-types';
2+
import { getComponentFromProp, initDefaultProps } from '../_util/props-util';
3+
import { ConfigConsumerProps } from '../config-provider';
4+
import StatisticNumber from './Number';
5+
6+
export const StatisticProps = {
7+
prefixCls: PropTypes.string,
8+
decimalSeparator: PropTypes.string,
9+
groupSeparator: PropTypes.string,
10+
format: PropTypes.string,
11+
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
12+
valueStyle: PropTypes.any,
13+
valueRender: PropTypes.any,
14+
formatter: PropTypes.any,
15+
precision: PropTypes.number,
16+
prefix: PropTypes.any,
17+
suffix: PropTypes.any,
18+
title: PropTypes.any,
19+
};
20+
21+
export default {
22+
name: 'AStatistic',
23+
props: initDefaultProps(StatisticProps, {
24+
prefixCls: 'ant-statistic',
25+
decimalSeparator: '.',
26+
groupSeparator: ',',
27+
}),
28+
inject: {
29+
configProvider: { default: () => ({}) },
30+
},
31+
32+
render() {
33+
const {
34+
prefixCls: customizePrefixCls,
35+
value = 0,
36+
valueStyle,
37+
valueRender,
38+
} = this.$props;
39+
const getPrefixCls = this.configProvider.getPrefixCls || ConfigConsumerProps.getPrefixCls;
40+
const prefixCls = getPrefixCls('statistic', customizePrefixCls);
41+
42+
const title = getComponentFromProp(this, 'title');
43+
let prefix = getComponentFromProp(this, 'prefix');
44+
let suffix = getComponentFromProp(this, 'suffix');
45+
const formatter = getComponentFromProp(this, 'formatter');
46+
let valueNode = (
47+
<StatisticNumber {...{ props: this.$props }} value={value} formatter={formatter} />
48+
);
49+
if (valueRender) {
50+
valueNode = valueRender(valueNode);
51+
}
52+
53+
return (
54+
<div class={prefixCls}>
55+
{title && <div class={`${prefixCls}-title`}>{title}</div>}
56+
<div style={valueStyle} class={`${prefixCls}-content`}>
57+
{prefix && <span class={`${prefixCls}-content-prefix`}>{prefix}</span>}
58+
{valueNode}
59+
{suffix && <span class={`${prefixCls}-content-suffix`}>{suffix}</span>}
60+
</div>
61+
</div>
62+
);
63+
},
64+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`renders ./components/statistic/demo/basic.md correctly 1`] = `
4+
<div>
5+
<div class="ant-statistic" style="margin-right: 50px;">
6+
<div class="ant-statistic-title">Active Users</div>
7+
<div class="ant-statistic-content"><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">112,893</span></span></div>
8+
</div>
9+
<div class="ant-statistic">
10+
<div class="ant-statistic-title">Account Balance (CNY)</div>
11+
<div class="ant-statistic-content"><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">112,893</span><span class="ant-statistic-content-value-decimal">.00</span></span></div>
12+
</div>
13+
</div>
14+
`;
15+
16+
exports[`renders ./components/statistic/demo/card.md correctly 1`] = `
17+
<div>
18+
<div class="ant-card ant-card-bordered" style="padding: 30px;">
19+
<div class="ant-card-body">
20+
<div class="ant-statistic" style="margin-right: 50px;">
21+
<div class="ant-statistic-title">Feedback</div>
22+
<div class="ant-statistic-content"><span class="ant-statistic-content-prefix"><i slot="prefix" aria-label="icon: like" class="anticon anticon-like"><svg viewBox="64 64 896 896" data-icon="like" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M885.9 533.7c16.8-22.2 26.1-49.4 26.1-77.7 0-44.9-25.1-87.4-65.5-111.1a67.67 67.67 0 0 0-34.3-9.3H572.4l6-122.9c1.4-29.7-9.1-57.9-29.5-79.4A106.62 106.62 0 0 0 471 99.9c-52 0-98 35-111.8 85.1l-85.9 311H144c-17.7 0-32 14.3-32 32v364c0 17.7 14.3 32 32 32h601.3c9.2 0 18.2-1.8 26.5-5.4 47.6-20.3 78.3-66.8 78.3-118.4 0-12.6-1.8-25-5.4-37 16.8-22.2 26.1-49.4 26.1-77.7 0-12.6-1.8-25-5.4-37 16.8-22.2 26.1-49.4 26.1-77.7-.2-12.6-2-25.1-5.6-37.1zM184 852V568h81v284h-81zm636.4-353l-21.9 19 13.9 25.4a56.2 56.2 0 0 1 6.9 27.3c0 16.5-7.2 32.2-19.6 43l-21.9 19 13.9 25.4a56.2 56.2 0 0 1 6.9 27.3c0 16.5-7.2 32.2-19.6 43l-21.9 19 13.9 25.4a56.2 56.2 0 0 1 6.9 27.3c0 22.4-13.2 42.6-33.6 51.8H329V564.8l99.5-360.5a44.1 44.1 0 0 1 42.2-32.3c7.6 0 15.1 2.2 21.1 6.7 9.9 7.4 15.2 18.6 14.6 30.5l-9.6 198.4h314.4C829 418.5 840 436.9 840 456c0 16.5-7.2 32.1-19.6 43z"></path></svg></i></span><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">1,128</span></span></div>
23+
</div>
24+
<div class="ant-statistic" valueclass="demo-class">
25+
<div class="ant-statistic-title">Unmerged</div>
26+
<div class="ant-statistic-content"><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">1,234,567,890</span></span><span class="ant-statistic-content-suffix"><span> / 100</span></span></div>
27+
</div>
28+
</div>
29+
</div>
30+
</div>
31+
`;
32+
33+
exports[`renders ./components/statistic/demo/countdown.md correctly 1`] = `
34+
<div>
35+
<div class="ant-statistic" style="margin-right: 50px;">
36+
<div class="ant-statistic-title">Countdown</div>
37+
<div class="ant-statistic-content"><span class="ant-statistic-content-value">48:00:30</span></div>
38+
</div>
39+
<div class="ant-statistic">
40+
<div class="ant-statistic-title">Million Seconds</div>
41+
<div class="ant-statistic-content"><span class="ant-statistic-content-value">48:00:30:000</span></div>
42+
</div>
43+
</div>
44+
`;
45+
46+
exports[`renders ./components/statistic/demo/unit.md correctly 1`] = `
47+
<div>
48+
<div class="ant-statistic" style="margin-right: 50px;">
49+
<div class="ant-statistic-title">Feedback</div>
50+
<div class="ant-statistic-content" style="color: rgb(63, 134, 0);"><span class="ant-statistic-content-prefix"><i slot="prefix" aria-label="icon: arrow-up" class="anticon anticon-arrow-up"><svg viewBox="64 64 896 896" data-icon="arrow-up" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M868 545.5L536.1 163a31.96 31.96 0 0 0-48.3 0L156 545.5a7.97 7.97 0 0 0 6 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z"></path></svg></i></span><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">11</span><span class="ant-statistic-content-value-decimal">.28</span></span><span class="ant-statistic-content-suffix">%</span></div>
51+
</div>
52+
<div class="ant-statistic" valueclass="demo-class">
53+
<div class="ant-statistic-title">Unmerged</div>
54+
<div class="ant-statistic-content"><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">78</span></span><span class="ant-statistic-content-suffix"><span> / 100</span></span></div>
55+
</div>
56+
</div>
57+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import demoTest from '../../../tests/shared/demoTest';
2+
3+
demoTest('statistic');

components/statistic/demo/basic.md

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<cn>
2+
#### 基本
3+
简单展示
4+
</cn>
5+
6+
<us>
7+
#### Basic
8+
Simplest Usage.
9+
</us>
10+
11+
```html
12+
<template>
13+
<div>
14+
<a-statistic title="Active Users" :value="112893" style="margin-right: 50px" />
15+
<a-statistic title="Account Balance (CNY)" :precision="2" :value="112893" />
16+
</div>
17+
</template>
18+
```

components/statistic/demo/card.md

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<cn>
2+
#### 在卡片中使用
3+
在卡片中展示统计数值。
4+
</cn>
5+
6+
<us>
7+
#### In Card
8+
Display statistic data in Card.
9+
</us>
10+
11+
```html
12+
<template>
13+
<div>
14+
<a-card style="padding: 30px">
15+
<a-statistic
16+
title="Feedback"
17+
:value="1128"
18+
style="margin-right: 50px"
19+
>
20+
<a-icon slot="prefix" type="like" />
21+
</a-statistic>
22+
<a-statistic
23+
title="Unmerged"
24+
:value="1234567890"
25+
valueClass="demo-class"
26+
>
27+
<span slot="suffix"> / 100</span>
28+
</a-statistic>
29+
</a-card>
30+
</div>
31+
</template>
32+
```

0 commit comments

Comments
 (0)