JSFiddle - React, Tailwind, and code Playground

by Matt Ruby

HTML

<link rel="stylesheet" href="//cdn.jsdelivr.net/pure/0.6.0/pure-min.css">
<div id="mount"></div>

<script src="https://cdn.jsdelivr.net/g/[email protected](react.min.js+react-dom.min.js),[email protected]"></script>
<script src="https://unpkg.com/[email protected]/lib/mobx.umd.min.js"></script>
<script src="https://unpkg.com/[email protected]/index.js"></script>
<script src="https://unpkg.com/mobx-react-devtools"></script>

CSS

.poker-timer {
	width: 80%;
	margin: 10px auto;
}

.active-blind {
	padding: 10px 0;
}
.timer {
	display: flex;
	justify-content: center;
}

.active-blind .timer {
	display: flex;
	justify-content: center;
	padding: 0 0 10px 0;
	font-size: 120px;
}

.divider {
	padding: 0 5px;
}

.progress-bar-container {
	border: 1px solid #ccc;
}

.progress-bar {
	height: 60px;
	background-color: aquamarine;
}

.blind-counts {
	padding: 20px;
	display: flex;
	justify-content: space-around;
	font-size: 80px;
}
.blind-listing,
.blind-listing th {
	text-align: center;
}
.pure-table td {
	padding: 1em;
}
.pure-table tr.active td {
	font-weight: bold;
}
.timer-controls {
	padding: 0 0 20px 0
}

.blind-listing table {
	margin: 20px auto;
	width: 80%
}

JavaScript

mobx.useStrict(true);

var countdownTimerFactory = function (durationMilliseconds, options) {

	var intervalID;

	var timer = {
		id: _.uniqueId('countdownTimer_'),
		originalMilliseconds: durationMilliseconds
	};

	var settings = _.assign({
		interval: 10,
		runTime: 0,
		resetOnComplete: true
	}, options);

	mobx.extendObservable(timer, {
		durationAsMilliseconds: timer.originalMilliseconds,
		isTimerRunning: false,
		get isComplete () {
			return timer.durationAsMilliseconds <= 0;
		},
		get durationAsDate () {
			return new Date(timer.durationAsMilliseconds);
		},
		get millisecondsRemaining () {
			return timer.durationAsDate.getUTCMilliseconds();
		},
		get secondsRemaining () {
			return timer.durationAsDate.getUTCSeconds();
		},
		get minutesRemaining () {
			return timer.durationAsDate.getUTCMinutes();
		},
		get hoursRemaining () {
			return timer.durationAsDate.getUTCHours();
		},
		get percentageComplete () {
			return 100 - _.round((timer.durationAsMilliseconds / timer.originalMilliseconds) * 100, 2);
		},
		startTimer: mobx.action('startTimer', function () {
			timer.isTimerRunning = true;
		}),
		stopTimer: mobx.action('stopTimer', function () {
			timer.isTimerRunning = false;
		}),
		reset: mobx.action('resetTimer', function () {
			timer.stopTimer();
			timer.durationAsMilliseconds = timer.originalMilliseconds;
		})
	});

	mobx.autorun('countDownTimer', function () {
		if (timer.isTimerRunning) {
			intervalID = window.setInterval(function () {
				mobx.runInAction('timer tick', function () {
					timer.durationAsMilliseconds -= settings.interval;
					if (timer.isComplete) {
						timer.isTimerRunning = false;
					}
				});
			}, settings.interval);
		} else if (intervalID) {
			window.clearInterval(intervalID);
		}
	});

	return timer;
};

var blindFactory = function (durationInMinutes, smallBlind, bigBlind) {

	var blind = {
		id: _.uniqueId('blind_')
	};

	mobx.extendObservable(blind, {
		smallBlind: smallBlind,
		bigBlind:...