Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ class App extends Component {
</div>

<h3 className="title">Christmas Eve is coming soon (Midnight of 23rd to 24th Dec, UTC time):</h3>
<Countdown date={`${year}-12-24T00:00:00`} />
<Countdown date={new Date(`${year}-12-24T00:00:00`)} />

<h3 className="title">Countdown for 2 hours and 13 seconds, but showing just minutes and seconds:</h3>
<Countdown hours={2} seconds={13} timeComponentsToShow={['minutes', 'seconds']} />
</div>
);
}
Expand Down
330 changes: 209 additions & 121 deletions src/Countdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,128 +2,216 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'

/**
* Note :
* Note :
* If you're using react v 15.4 or less
* You can directly import PropTypes from react instead.
* You can directly import PropTypes from react instead.
* Refer to this : https://reactjs.org/warnings/dont-call-proptypes.html
*/

class Countdown extends Component {
constructor(props) {
super(props);

this.state = {
days: 0,
hours: 0,
min: 0,
sec: 0,
}
}

componentDidMount() {
// update every second
this.interval = setInterval(() => {
const date = this.calculateCountdown(this.props.date);
date ? this.setState(date) : this.stop();
}, 1000);
}

componentWillUnmount() {
this.stop();
}

calculateCountdown(endDate) {
let diff = (Date.parse(new Date(endDate)) - Date.parse(new Date())) / 1000;

// clear countdown when date is reached
if (diff <= 0) return false;

const timeLeft = {
years: 0,
days: 0,
hours: 0,
min: 0,
sec: 0
};

// calculate time difference between now and expected date
if (diff >= (365.25 * 86400)) { // 365.25 * 24 * 60 * 60
timeLeft.years = Math.floor(diff / (365.25 * 86400));
diff -= timeLeft.years * 365.25 * 86400;
}
if (diff >= 86400) { // 24 * 60 * 60
timeLeft.days = Math.floor(diff / 86400);
diff -= timeLeft.days * 86400;
}
if (diff >= 3600) { // 60 * 60
timeLeft.hours = Math.floor(diff / 3600);
diff -= timeLeft.hours * 3600;
}
if (diff >= 60) {
timeLeft.min = Math.floor(diff / 60);
diff -= timeLeft.min * 60;
}
timeLeft.sec = diff;

return timeLeft;
}

stop() {
clearInterval(this.interval);
}

addLeadingZeros(value) {
value = String(value);
while (value.length < 2) {
value = '0' + value;
}
return value;
}

render() {
const countDown = this.state;

return (
<div className="Countdown">
<span className="Countdown-col">
<span className="Countdown-col-element">
<strong>{this.addLeadingZeros(countDown.days)}</strong>
<span>{countDown.days === 1 ? 'Day' : 'Days'}</span>
</span>
</span>

<span className="Countdown-col">
<span className="Countdown-col-element">
<strong>{this.addLeadingZeros(countDown.hours)}</strong>
<span>Hours</span>
</span>
</span>


<span className="Countdown-col">
<span className="Countdown-col-element">
<strong>{this.addLeadingZeros(countDown.min)}</strong>
<span>Min</span>
</span>
</span>

<span className="Countdown-col">
<span className="Countdown-col-element">
<strong>{this.addLeadingZeros(countDown.sec)}</strong>
<span>Sec</span>
</span>
</span>
</div>
);
}
}

Countdown.propTypes = {
date: PropTypes.string.isRequired
};

Countdown.defaultProps = {
date: new Date()
};

export default Countdown;
class Countdown extends Component {
constructor(props) {
super(props);

this.state = {
days: 0,
hours: 0,
min: 0,
sec: 0,
followDate: true,
timeToShow: {
days: 0,
hours: 0,
min: 0,
sec: 0,
},
}
}

componentDidMount() {
this.setState({ days: this.props.days,
hours: this.props.hours,
min: this.props.minutes,
sec: this.props.seconds });
if (this.props.days > 0
|| this.props.hours > 0
|| this.props.minutes > 0
|| this.props.seconds > 0) {
this.setState({ followDate: false });
}

// update every second
this.interval = setInterval(() => {
const timeLeft = this.state.followDate ? this.calculateDateCountdown(this.props.date) : this.diminishSecondToCountdown(this.state);
if (timeLeft) {
const visualTimeLeft = this.adaptTimeToChosenComponents(timeLeft);
this.setState({ ...timeLeft, timeToShow: visualTimeLeft });
} else {
this.stop();
this.props.finishCallback();
}
}, 1000);
}

componentWillUnmount() {
this.stop();
}

adaptTimeToChosenComponents(time) {
let adaptedTime = { ...time }
if (!this.props.timeComponentsToShow.includes('days')) {
adaptedTime.hours += adaptedTime.days * 24;
adaptedTime.days = 0;
}
if (!this.props.timeComponentsToShow.includes('hours')) {
adaptedTime.min += adaptedTime.hours * 60;
adaptedTime.hours = 0;
}
if (!this.props.timeComponentsToShow.includes('minutes')) {
adaptedTime.sec += adaptedTime.min * 60;
adaptedTime.min = 0;
}
return adaptedTime
}

diminishSecondToCountdown(state) {
if (state.days === 0 && state.hours === 0 && state.min === 0 && state.sec === 0) {
return null;
}

const timeLeft = {
days: state.days,
hours: state.hours,
min: state.min,
sec: state.sec,
};
timeLeft.sec = timeLeft.sec - 1;
if (timeLeft.sec === -1) {
timeLeft.sec = 59;
timeLeft.min = timeLeft.min - 1;
}
if (timeLeft.min === -1) {
timeLeft.min = 59;
timeLeft.hours = timeLeft.hours - 1;
}
if (timeLeft.hours === -1) {
timeLeft.hours = 23;
timeLeft.days = timeLeft.days - 1;
}
return timeLeft;
}

calculateDateCountdown(endDate) {
let diff = (Date.parse(endDate) - Date.parse(new Date())) / 1000;

// clear countdown when date is reached
if (diff < 0) return null;

const timeLeft = {
years: 0,
days: 0,
hours: 0,
min: 0,
sec: 0,
millisec: 0,
};

// calculate time difference between now and expected date
if (diff >= (365.25 * 86400)) { // 365.25 * 24 * 60 * 60
timeLeft.years = Math.floor(diff / (365.25 * 86400));
diff -= timeLeft.years * 365.25 * 86400;
}
if (diff >= 86400) { // 24 * 60 * 60
timeLeft.days = Math.floor(diff / 86400);
diff -= timeLeft.days * 86400;
}
if (diff >= 3600) { // 60 * 60
timeLeft.hours = Math.floor(diff / 3600);
diff -= timeLeft.hours * 3600;
}
if (diff >= 60) {
timeLeft.min = Math.floor(diff / 60);
diff -= timeLeft.min * 60;
}
timeLeft.sec = diff;

return timeLeft;
}

stop() {
clearInterval(this.interval);
}

addLeadingZeros(value) {
value = String(value);
while (value.length < 2) {
value = '0' + value;
}
return value;
}

render() {
const countDown = this.state.timeToShow;
const timeComponentsToShow = this.props.timeComponentsToShow;
return (
<div className="Countdown">
{ timeComponentsToShow.includes('days') && (
<span className="Countdown-col">
<span className="Countdown-col-element">
<strong>{this.addLeadingZeros(countDown.days)}</strong>
<span>{countDown.days === 1 ? 'Day' : 'Days'}</span>
</span>
</span>
)}

{ timeComponentsToShow.includes('hours') && (
<span className="Countdown-col">
<span className="Countdown-col-element">
<strong>{this.addLeadingZeros(countDown.hours)}</strong>
<span>Hours</span>
</span>
</span>
)}

{ timeComponentsToShow.includes('minutes') && (
<span className="Countdown-col">
<span className="Countdown-col-element">
<strong>{this.addLeadingZeros(countDown.min)}</strong>
<span>Min</span>
</span>
</span>
)}

{ timeComponentsToShow.includes('seconds') && (
<span className="Countdown-col">
<span className="Countdown-col-element">
<strong>{this.addLeadingZeros(countDown.sec)}</strong>
<span>Sec</span>
</span>
</span>
)}
</div>
);
}
}

Countdown.propTypes = {
date: PropTypes.instanceOf(Date),
days: PropTypes.number,
hours: PropTypes.number,
minutes: PropTypes.number,
seconds: PropTypes.number,
timeComponentsToShow: PropTypes.arrayOf(PropTypes.oneOf(['days', 'hours', 'minutes', 'seconds'])),
finishCallback: PropTypes.func,
};

Countdown.defaultProps = {
date: new Date(),
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
timeComponentsToShow: ['days', 'hours', 'minutes', 'seconds'],
finishCallback: () => void(0),
};

export default Countdown;