Implement custom input handling for duration entry
This commit is contained in:
parent
d7254da5a6
commit
2b33fd18f5
2 changed files with 160 additions and 114 deletions
242
src/index.html
242
src/index.html
|
@ -1,123 +1,139 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="css/styles.css">
|
||||
<script type="text/javascript" src="js/app.js"></script>
|
||||
<title>Interval Timer</title>
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon-180x180.png">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/img/apple-touch-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/img/apple-touch-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/img/apple-touch-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/img/apple-touch-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/img/apple-touch-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/img/apple-touch-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/img/apple-touch-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/img/apple-touch-icon-152x152.png">
|
||||
<link rel="icon" type="image/png" href="/img/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/img/favicon-194x194.png" sizes="194x194">
|
||||
<link rel="shortcut icon" type="image/png" href="/img/android-chrome-192x192.png" sizes="192x192">
|
||||
<link rel="icon" type="image/png" href="/img/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="/img/manifest.json">
|
||||
<link rel="mask-icon" href="/img/safari-pinned-tab.svg" color="#ffeb3b">
|
||||
<link rel="icon" href="/img/favicon.ico">
|
||||
<meta name="msapplication-TileColor" content="#ffc40d">
|
||||
<meta name="msapplication-TileImage" content="/img/mstile-144x144.png">
|
||||
<meta name="msapplication-config" content="/img/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffeb3b">
|
||||
</head>
|
||||
<body onload="loadTimers()">
|
||||
<header>
|
||||
<h1>Interval Timer</h1>
|
||||
</header>
|
||||
<div id="loader">
|
||||
<img class="loader" src="img/spinner.png" />
|
||||
<div class="loader-overlay"></div>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" href="css/styles.css">
|
||||
<script type="text/javascript" src="js/app.js"></script>
|
||||
<title>Interval Timer</title>
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon-180x180.png">
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/img/apple-touch-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/img/apple-touch-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/img/apple-touch-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/img/apple-touch-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/img/apple-touch-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/img/apple-touch-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/img/apple-touch-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/img/apple-touch-icon-152x152.png">
|
||||
<link rel="icon" type="image/png" href="/img/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/img/favicon-194x194.png" sizes="194x194">
|
||||
<link rel="shortcut icon" type="image/png" href="/img/android-chrome-192x192.png" sizes="192x192">
|
||||
<link rel="icon" type="image/png" href="/img/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="/img/manifest.json">
|
||||
<link rel="mask-icon" href="/img/safari-pinned-tab.svg" color="#ffeb3b">
|
||||
<link rel="icon" href="/img/favicon.ico">
|
||||
<meta name="msapplication-TileColor" content="#ffc40d">
|
||||
<meta name="msapplication-TileImage" content="/img/mstile-144x144.png">
|
||||
<meta name="msapplication-config" content="/img/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffeb3b">
|
||||
</head>
|
||||
|
||||
<body onload="initApp()">
|
||||
<header>
|
||||
<h1>Interval Timer</h1>
|
||||
</header>
|
||||
<div id="loader">
|
||||
<img class="loader" src="img/spinner.png" />
|
||||
<div class="loader-overlay"></div>
|
||||
</div>
|
||||
<div class="no-timers">
|
||||
<p style="max-width: 600px;">It looks like you haven't configured any timers yet. Click on the orange "+" icon
|
||||
below to get started!</p>
|
||||
</div>
|
||||
<div class="timers">
|
||||
<div class="timer animate-repeat">
|
||||
<a href="javascript:void(0)">
|
||||
<h2>Timer Name</h2>
|
||||
<p>Timer description</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="no-timers">
|
||||
<p style="max-width: 600px;">It looks like you haven't configured any timers yet. Click on the orange "+" icon below to get started!</p>
|
||||
</div>
|
||||
<div class="timers">
|
||||
<div class="timer animate-repeat">
|
||||
<a href="javascript:void(0)">
|
||||
<h2>Timer Name</h2>
|
||||
<p>Timer description</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="timer-interface">
|
||||
<div class="timer-container">
|
||||
<div class="timer-top">
|
||||
<a href="#" id="timer-menu-btn" class="menu-button">✕</a>
|
||||
<h2 id="timer-name">Timer Name</h2>
|
||||
<a id="timer-reset-btn" href="#" class="menu-button"><i class="fa fa-refresh"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timer-interface">
|
||||
<div class="timer-container">
|
||||
<div class="timer-top">
|
||||
<a href="#" id="timer-menu-btn" class="menu-button">✕</a>
|
||||
<h2 id="timer-name">Timer Name</h2>
|
||||
<a id="timer-reset-btn" href="#" class="menu-button"><i class="fa fa-refresh"></i></a>
|
||||
</div>
|
||||
<div class="timer-middle">
|
||||
<div class="timer-info">
|
||||
<p class="time-info">Round: round/timer.rounds - Cycle: cycle/timer.cycles</p>
|
||||
<p class="time">{{ time * 1000 | date : "mm:ss" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="timer-bottom">
|
||||
<a id="back" class="menu-button" href="#"><img class="timer-control play" src="./img/skip-back.svg" /></a>
|
||||
<a id="play" class="menu-button" href="#"><img class="timer-control play" src="./img/play.svg" /></a>
|
||||
<a id="pause" class="menu-button" href="#"><img class="timer-control pause" src="./img/pause.svg" /></a>
|
||||
<a id="next" class="menu-button" href="#"><img class="timer-control play" src="./img/skip-forward.svg" /></a>
|
||||
<div class="timer-middle">
|
||||
<div class="timer-info">
|
||||
<p class="time-info">Round: round/timer.rounds - Cycle: cycle/timer.cycles</p>
|
||||
<p class="time">{{ time * 1000 | date : "mm:ss" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="new-timer">
|
||||
<div class="top-menu">
|
||||
<button class="menu-button" onclick="cancelEdit()" >✕</a>
|
||||
<button class="menu-button" onclick="saveTimer()" >✓</a>
|
||||
<div class="timer-bottom">
|
||||
<a id="back" class="menu-button" href="#"><img class="timer-control play"
|
||||
src="./img/skip-back.svg" /></a>
|
||||
<a id="play" class="menu-button" href="#"><img class="timer-control play" src="./img/play.svg" /></a>
|
||||
<a id="pause" class="menu-button" href="#"><img class="timer-control pause" src="./img/pause.svg" /></a>
|
||||
<a id="next" class="menu-button" href="#"><img class="timer-control play"
|
||||
src="./img/skip-forward.svg" /></a>
|
||||
</div>
|
||||
<form id="timer-setup" name="timer-setup">
|
||||
<div class="input-wrapper">
|
||||
<label for="timerName">Name:</label>
|
||||
<input type="text" name="timerName">
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="timerDescription">Description:</label>
|
||||
<textarea name="timerDescription"></textarea>
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="warmUp">Warm-up:</label>
|
||||
<input type="number" name="warmUp" />
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="low">Low-intensity:</label>
|
||||
<input type="number" name="low" />
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="high">High-intensity:</label>
|
||||
<input type="number" name="high" />
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="rest">Rest:</label>
|
||||
<input type="number" name="rest" />
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="cool">Cooldown:</label>
|
||||
<input type="number" name="cool" />
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="sets">Sets:</label>
|
||||
<input type="number" name="sets">
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="rounds">Rounds:</label>
|
||||
<input type="number" name="rounds">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<button class="add-timer" onclick="editTimer()">+</button>
|
||||
<script async>
|
||||
if('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker
|
||||
.register('/sw.js')
|
||||
.then(function() { console.log("Service Worker Registered"); });
|
||||
</div>
|
||||
<div class="new-timer">
|
||||
<div class="top-menu">
|
||||
<button class="menu-button" onclick="cancelEdit()">✕</a>
|
||||
<button class="menu-button" onclick="saveTimer()">✓</a>
|
||||
</div>
|
||||
<form id="timer-setup" name="timer-setup">
|
||||
<div class="input-wrapper">
|
||||
<label for="timerName">Name:</label>
|
||||
<input type="text" name="timerName">
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="timerDescription">Description:</label>
|
||||
<textarea name="timerDescription"></textarea>
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="warmUp">Warm-up:</label>
|
||||
<input class="duration" type="text" name="warmUp" onmouseup="handleDurationCaret(event)"
|
||||
oninput="maskDuration(event)" onkeyup="handleDurationCaret(event)" regex="\d{2}h \d{2}m \d{2}s"
|
||||
value="00h 00m 00s" />
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="low">Low-intensity:</label>
|
||||
<input class="duration" type="text" name="low" onmouseup="handleDurationCaret(event)"
|
||||
oninput="maskDuration(event)" onkeyup="handleDurationCaret(event)" regex="\d{2}h \d{2}m \d{2}s"
|
||||
value="00h 00m 00s" />
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="high">High-intensity:</label>
|
||||
<input class="duration" type="text" name="high" onmouseup="handleDurationCaret(event)"
|
||||
oninput="maskDuration(event)" onkeyup="handleDurationCaret(event)" regex="\d{2}h \d{2}m \d{2}s"
|
||||
value="00h 00m 00s" />
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="rest">Rest:</label>
|
||||
<input class="duration" type="text" name="rest" onmouseup="handleDurationCaret(event)"
|
||||
oninput="maskDuration(event)" onkeyup="handleDurationCaret(event)" regex="\d{2}h \d{2}m \d{2}s"
|
||||
value="00h 00m 00s" />
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="cool">Cooldown:</label>
|
||||
<input class="duration" type="text" name="cool" onmouseup="handleDurationCaret(event)"
|
||||
oninput="maskDuration(event)" onkeyup="handleDurationCaret(event)" regex="\d{2}h \d{2}m \d{2}s"
|
||||
value="00h 00m 00s" />
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="sets">Sets:</label>
|
||||
<input type="number" name="sets" value="1">
|
||||
</div>
|
||||
<div class="input-wrapper">
|
||||
<label for="rounds">Rounds:</label>
|
||||
<input type="number" name="rounds" value="1">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<button class="add-timer" onclick="editTimer()">+</button>
|
||||
<script async>
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker
|
||||
.register('/sw.js')
|
||||
.then(function () { console.log("Service Worker Registered"); });
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -54,6 +54,14 @@ let _state = {
|
|||
let db = null;
|
||||
let timerStore = null;
|
||||
|
||||
function initApp() {
|
||||
Array.from(document.getElementsByClassName('duration')).forEach(input => {
|
||||
console.log(input)
|
||||
// input.addEventListener('beforeinput', maskDuration);
|
||||
});
|
||||
loadTimers();
|
||||
}
|
||||
|
||||
function loadTimers() {
|
||||
let state = copyState();
|
||||
const dbRequest = window.indexedDB.open('interval-timer', 1);
|
||||
|
@ -122,6 +130,28 @@ function cancelEdit() {
|
|||
})
|
||||
}
|
||||
|
||||
function maskDuration(event) {
|
||||
const input = event.target;
|
||||
let formatted = input.value.replace(/[^\d]/g, '');
|
||||
if (event.inputType === "deleteContentBackward") {
|
||||
formatted = formatted.slice(0, formatted.length - 1)
|
||||
formatted = ("000000" + formatted).slice(-6)
|
||||
} else if (event.inputType === 'insertText' && event.data.match(/\d/)) {
|
||||
formatted = ("000000" + formatted).slice(-6)
|
||||
}
|
||||
input.value = `${formatted.slice(0, 2)}h ${formatted.slice(2, 4)}m ${formatted.slice(4, 6)}s`;
|
||||
}
|
||||
|
||||
function isBeforeInputEventAvailable() {
|
||||
return window.InputEvent && typeof InputEvent.prototype.getTargetRanges === "function";
|
||||
}
|
||||
|
||||
function handleDurationCaret(event) {
|
||||
const input = event.target;
|
||||
const position = Math.min(11, input.value.length);
|
||||
input.setSelectionRange(position, position);
|
||||
}
|
||||
|
||||
function toggleTimer() {
|
||||
let state = copyState();
|
||||
if (state.timerJob != null) {
|
||||
|
|
Loading…
Reference in a new issue