-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathCountdown.tsx
More file actions
105 lines (87 loc) · 2.3 KB
/
Countdown.tsx
File metadata and controls
105 lines (87 loc) · 2.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { ObservedComponent, reaction } from 'mobx-react-helper';
import { HTMLAttributes } from 'react';
import { Second, TimeData } from 'web-utility';
export interface TimeUnit {
scale: number;
label: string;
}
interface TimeSection {
value: number;
label: string;
}
export interface CountdownProps extends HTMLAttributes<HTMLOListElement> {
units: TimeUnit[];
endTime: TimeData;
onEnd?: (endTime: TimeData) => any;
}
@observer
export class Countdown extends ObservedComponent<CountdownProps> {
@observable
accessor rest = 0;
private timer = 0;
get endTimestamp() {
return +new Date(this.props.endTime || Date.now());
}
@computed
get timeSections(): TimeSection[] {
const { units } = this.observedProps;
let { rest } = this;
return units.reduce((list, { label }, index) => {
const scale = units
.slice(index)
.map(({ scale }) => scale)
.reduce((sum, scale) => sum * scale, 1);
const value = ~~(rest / scale);
rest -= value * scale;
list.push({ value, label });
return list;
}, [] as TimeSection[]);
}
tick = () => {
const { onEnd, endTime } = this.props,
rest = this.endTimestamp - Date.now();
if (rest > 0) {
this.rest = rest;
} else {
this.rest = 0;
this.stop();
onEnd?.(endTime);
}
};
stop() {
if (this.timer) {
window.clearInterval(this.timer);
this.timer = 0;
}
}
componentDidMount() {
super.componentDidMount();
this.initTimer();
}
@reaction(_this => _this.observedProps.endTime)
initTimer() {
this.stop();
this.tick();
this.timer = window.setInterval(this.tick, Second);
}
componentWillUnmount() {
super.componentWillUnmount();
this.stop();
}
render() {
const { className = '', ...props } = this.props;
const { timeSections } = this;
return (
<ol className={`list-unstyled m-0 ${className}`} {...props}>
{timeSections.map(({ value, label }) => (
<li key={label} className="d-flex flex-column justify-content-center align-items-center">
<strong>{(value + '').padStart(2, '0')}</strong>
<span>{label}</span>
</li>
))}
</ol>
);
}
}