diff --git a/components/statistic/Countdown.tsx b/components/statistic/Countdown.tsx index ce4be77ae..55b386d29 100644 --- a/components/statistic/Countdown.tsx +++ b/components/statistic/Countdown.tsx @@ -1,9 +1,9 @@ -import { defineComponent } from 'vue'; +import { defineComponent, onBeforeUnmount, onMounted, onUpdated, ref } from 'vue'; import moment from 'moment'; import interopDefault from '../_util/interopDefault'; import initDefaultProps from '../_util/props-util/initDefaultProps'; import Statistic, { StatisticProps } from './Statistic'; -import { formatCountdown, countdownValueType, FormatConfig } from './utils'; +import { formatCountdown as formatCD, countdownValueType, FormatConfig } from './utils'; const REFRESH_INTERVAL = 1000 / 30; @@ -16,73 +16,76 @@ export default defineComponent({ props: initDefaultProps(StatisticProps, { format: 'HH:mm:ss', }), - setup() { - return { - countdownId: undefined, - } as { countdownId: number }; - }, - mounted() { - this.syncTimer(); - }, - - updated() { - this.syncTimer(); - }, - - beforeUnmount() { - this.stopTimer(); - }, - - methods: { - syncTimer() { - const { value } = this.$props; + emits: ['finish', 'change'], + setup(props, { emit }) { + const countdownId = ref(); + const statistic = ref(); + const syncTimer = () => { + const { value } = props; const timestamp = getTime(value); if (timestamp >= Date.now()) { - this.startTimer(); + startTimer(); } else { - this.stopTimer(); + stopTimer(); } - }, + }; - startTimer() { - if (this.countdownId) return; - this.countdownId = window.setInterval(() => { - (this.$refs.statistic as any).$forceUpdate(); - this.syncTimer(); + const startTimer = () => { + if (countdownId.value) return; + const timestamp = getTime(props.value); + countdownId.value = window.setInterval(() => { + statistic.value.$forceUpdate(); + if (timestamp > Date.now()) { + emit('change', timestamp - Date.now()); + } }, REFRESH_INTERVAL); - }, + }; - stopTimer() { - const { value } = this.$props; - if (this.countdownId) { - clearInterval(this.countdownId); - this.countdownId = undefined; + const stopTimer = () => { + const { value } = props; + if (countdownId) { + clearInterval(countdownId.value); + countdownId.value = undefined; const timestamp = getTime(value); if (timestamp < Date.now()) { - this.$emit('finish'); + emit('finish'); } } - }, + }; - formatCountdown({ value, config }: { value: countdownValueType; config: FormatConfig }) { - const { format } = this.$props; - return formatCountdown(value, { ...config, format }); - }, + const formatCountdown = ({ + value, + config, + }: { + value: countdownValueType; + config: FormatConfig; + }) => { + const { format } = props; + return formatCD(value, { ...config, format }); + }; - valueRenderHtml: node => node, - }, - - render() { - return ( - - ); + const valueRenderHtml = (node: any) => node; + onMounted(() => { + syncTimer(); + }); + onUpdated(() => { + syncTimer(); + }); + onBeforeUnmount(() => { + stopTimer(); + }); + return () => { + return ( + + ); + }; }, }); diff --git a/components/statistic/Statistic.tsx b/components/statistic/Statistic.tsx index d82faba80..b323579b5 100644 --- a/components/statistic/Statistic.tsx +++ b/components/statistic/Statistic.tsx @@ -1,10 +1,10 @@ -import { defineComponent, inject, PropType } from 'vue'; +import { defineComponent, PropType } from 'vue'; import PropTypes from '../_util/vue-types'; -import { getComponent } from '../_util/props-util'; import initDefaultProps from '../_util/props-util/initDefaultProps'; -import { defaultConfigProvider } from '../config-provider'; import StatisticNumber from './Number'; import { countdownValueType } from './utils'; +import Skeleton from '../skeleton/Skeleton'; +import useConfigInject from '../_util/hooks/useConfigInject'; export const StatisticProps = { prefixCls: PropTypes.string, @@ -22,6 +22,7 @@ export const StatisticProps = { suffix: PropTypes.VNodeChild, title: PropTypes.VNodeChild, onFinish: PropTypes.func, + loading: PropTypes.looseBool, }; export default defineComponent({ @@ -29,45 +30,41 @@ export default defineComponent({ props: initDefaultProps(StatisticProps, { decimalSeparator: '.', groupSeparator: ',', + loading: false, }), - - setup() { - return { - configProvider: inject('configProvider', defaultConfigProvider), - }; - }, - - render() { - const { prefixCls: customizePrefixCls, value = 0, valueStyle, valueRender } = this.$props; - const { getPrefixCls } = this.configProvider; - const prefixCls = getPrefixCls('statistic', customizePrefixCls); - - const title = getComponent(this, 'title'); - const prefix = getComponent(this, 'prefix'); - const suffix = getComponent(this, 'suffix'); - const formatter = getComponent(this, 'formatter', {}, false); - const props = { - ...this.$props, - prefixCls, - value, - formatter, - }; - // data-for-update just for update component - // https://github.com/vueComponent/ant-design-vue/pull/3170 - let valueNode = ; - if (valueRender) { - valueNode = valueRender(valueNode); - } - - return ( -
- {title &&
{title}
} -
- {prefix && {prefix}} - {valueNode} - {suffix && {suffix}} + slots: ['title', 'prefix', 'suffix', 'formatter'], + setup(props, { slots }) { + const { prefixCls, direction } = useConfigInject('statistic', props); + return () => { + const { value = 0, valueStyle, valueRender } = props; + const pre = prefixCls.value; + const title = props.title ?? slots.title?.(); + const prefix = props.prefix ?? slots.prefix?.(); + const suffix = props.suffix ?? slots.suffix?.(); + const formatter = props.formatter ?? slots.formatter; + // data-for-update just for update component + // https://github.com/vueComponent/ant-design-vue/pull/3170 + let valueNode = ( + + ); + if (valueRender) { + valueNode = valueRender(valueNode); + } + return ( +
+ {title &&
{title}
} + +
+ {prefix && {prefix}} + {valueNode} + {suffix && {suffix}} +
+
-
- ); + ); + }; }, }); diff --git a/components/statistic/style/index.less b/components/statistic/style/index.less index f236fdb3f..17d023ffa 100644 --- a/components/statistic/style/index.less +++ b/components/statistic/style/index.less @@ -7,7 +7,7 @@ .reset-component(); &-title { - margin-bottom: 4px; + margin-bottom: @margin-xss; color: @text-color-secondary; font-size: @statistic-title-font-size; } @@ -18,9 +18,8 @@ font-family: @statistic-font-family; &-value { - &-decimal { - font-size: @statistic-unit-font-size; - } + display: inline-block; + direction: ltr; } &-prefix, @@ -34,7 +33,8 @@ &-suffix { margin-left: 4px; - font-size: @statistic-unit-font-size; } } } + +@import './rtl'; diff --git a/components/statistic/style/rtl.less b/components/statistic/style/rtl.less new file mode 100644 index 000000000..93e264a35 --- /dev/null +++ b/components/statistic/style/rtl.less @@ -0,0 +1,21 @@ +.@{statistic-prefix-cls} { + &-rtl { + direction: rtl; + } + + &-content { + &-prefix { + .@{statistic-prefix-cls}-rtl & { + margin-right: 0; + margin-left: 4px; + } + } + + &-suffix { + .@{statistic-prefix-cls}-rtl & { + margin-right: 4px; + margin-left: 0; + } + } + } +} diff --git a/components/statistic/utils.ts b/components/statistic/utils.ts index e0f69618e..f335e1f72 100644 --- a/components/statistic/utils.ts +++ b/components/statistic/utils.ts @@ -1,9 +1,6 @@ import { VNodeTypes } from 'vue'; -import moment from 'moment'; import padStart from 'lodash-es/padStart'; -import interopDefault from '../_util/interopDefault'; - export type valueType = number | string; export type countdownValueType = valueType | string; @@ -39,15 +36,15 @@ const timeUnits: [string, number][] = [ export function formatTimeStr(duration: number, format: string) { let leftDuration: number = duration; - const escapeRegex = /\[[^\]]*\]/g; - const keepList = (format.match(escapeRegex) || []).map(str => str.slice(1, -1)); + const escapeRegex = /\[[^\]]*]/g; + const keepList: string[] = (format.match(escapeRegex) || []).map(str => str.slice(1, -1)); const templateText = format.replace(escapeRegex, '[]'); const replacedText = timeUnits.reduce((current, [name, unit]) => { if (current.indexOf(name) !== -1) { const value = Math.floor(leftDuration / unit); leftDuration -= value * unit; - return current.replace(new RegExp(`${name}+`, 'g'), match => { + return current.replace(new RegExp(`${name}+`, 'g'), (match: string) => { const len = match.length; return padStart(value.toString(), len, '0'); }); @@ -65,8 +62,9 @@ export function formatTimeStr(duration: number, format: string) { export function formatCountdown(value: countdownValueType, config: CountdownFormatConfig) { const { format = '' } = config; - const target = interopDefault(moment)(value).valueOf(); - const current = interopDefault(moment)().valueOf(); + const target = new Date(value).getTime(); + const current = Date.now(); const diff = Math.max(target - current, 0); + return formatTimeStr(diff, format); }