JSFiddle - React, Tailwind, and code Playground

by jstoudt

HTML

<div class="container">
	<h1>Operation Progress</h1>
	<div class="progress-wrapper">
		<progress id="progress-bar" max="100" value="0"></progress>
		<span id="percentage" class="percentage"></span>
	</div>
	<button id="start-progress">Start Progress</button>
	<button id="cancel-progress" hidden>Cancel</button>
	<div id="message" class="message"></div>
</div>

CSS

html, body {
	height: 100%;
	color: #333;
	background-color: #ccc;
	font: 13px/1.25 sans-serif;
	margin: 0;
	padding: 0;
}

.container {
	position: absolute;
	top: 20%;
	left: 50%;
	width: 480px;
	height: 75px;
	background: #fff;
	margin-left: -240px;
	border: 1px solid #333;
	border-radius: 0.5em;
	box-shadow: 3px 3px 6px 2px rgba(0, 0, 0, 0.4);
	padding: 1em;
}

h1 {
	font-size: 15px;
	padding: 0.25em 0;
	margin: 0;
}

.message {
	margin-top: 0.25em;
	font-style: italic;
}

#progress-bar {
	-webkit-appearance: none;
	appearance: none;
	width: 350px;
	height: 1.5em;
	vertical-align: middle;
}

#progress-bar[value]::-webkit-progress-bar {
	position: relative;
	background-color: #eee;
	box-shadow: 0 1px 6px rgba(0, 0, 0, 0.3) inset;
	border-radius: 2px;
}

#progress-bar[value]::-webkit-progress-value {
	background: -webkit-linear-gradient(left, #090 , #1d1);
	box-shadow: 0 1px 6px rgba(0, 0, 0, 0.3) inset;
	border-radius: 2px;
}

#progress-bar:not([value])::-webkit-progress-bar {
	position: relative;
	background-color: #eee;
	box-shadow: 0 1px 6px rgba(0, 0, 0, 0.3) inset;
}

#progress-bar:not([value])::-webkit-progress-bar::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0;
	width: 10%;
	height: 95%;
	background: -webkit-radial-gradient(center center, ellipse farthest-corner, #0b0 0%, #1d1 100%);
	background: radial-gradient(center center, ellipse farthest-corner, #0b0 0%, #1d1 100%);
	border-radius: 2px;
	-webkit-animation: indeterminate 3.5s linear infinite;
	animation: indeterminate 3.5s linear infinite;
}

@-webkit-keyframes indeterminate {
	50% { -webkit-transform: translateX(900%); }
}

@keyframes indeterminate {
	50% { transform: translateX(900%); }
}

.progress-wrapper {
	display: inline-block;
	position: relative;
}

.percentage {
	display: block;
	position: absolute;
	left: 0;
	top: 0;
	width: 100%;
	height: 100%;
	text-align: center;
	font: bold 13px/1.5em sans-serif;
}

button {
	cursor: pointer;
	vertical-align: middle;
}

JavaScript

(function() {
	var progressBar = document.getElementById('progress-bar'),
		startBtn = document.getElementById('start-progress'),
		cancelBtn = document.getElementById('cancel-progress'),
		message = document.getElementById('message'),
		percentage = document.getElementById('percentage'),
		timeoutId = null;

	function doProgress() {
		if (progressBar.value >= progressBar.max) {
			cancelBtn.setAttribute('hidden', 'hidden');
			startBtn.removeAttribute('hidden');
			message.innerHTML = 'The operation has completed successfully.';
			return;
		}

		progressBar.value++;
		percentage.innerHTML = progressBar.value + '%';
		timeoutId = setTimeout(doProgress, 60);
	}

	startBtn.addEventListener('click', function() {
		this.setAttribute('hidden', 'hidden');
		cancelBtn.removeAttribute('hidden');
		progressBar.removeAttribute('max');
		progressBar.removeAttribute('value');
		percentage.innerHTML = '';
		message.innerHTML = 'Doing a task for an indeterminate amount of time...';

		timeoutId = setTimeout(function() {
			progressBar.max = 100;
			progressBar.value = 0;
			message.innerHTML = 'Doing a task for a determinate amount of time...';
			doProgress();
		}, 3000);

	}, false);

	cancelBtn.addEventListener('click', function() {
		if (timeoutId) {
			clearTimeout(timeoutId);
			message.innerHTML = 'Operation has been cancelled by the user.';
			progressBar.max = 100;
			progressBar.value = 0;
			cancelBtn.setAttribute('hidden', 'hidden');
			startBtn.removeAttribute('hidden');
		}
	}, false);

}());