From c2c1488d1e23b2f6f2955f9e317e6778364b3cb6 Mon Sep 17 00:00:00 2001 From: Ondrej Zara Date: Tue, 30 Apr 2019 09:42:16 +0200 Subject: [PATCH] range finalized --- app/app.css | 2 +- app/css/range.less | 2 +- app/js/lib/range.js | 100 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 85 insertions(+), 19 deletions(-) diff --git a/app/app.css b/app/app.css index 35827c2..5f3c9d5 100644 --- a/app/app.css +++ b/app/app.css @@ -104,7 +104,7 @@ x-range { --elapsed-color: lightgray; --remaining-color: transparent; --radius: calc(var(--track-size)/2); - width: 144px; + width: 192px; height: 16px; position: relative; } diff --git a/app/css/range.less b/app/css/range.less index e20475a..0e7d34b 100644 --- a/app/css/range.less +++ b/app/css/range.less @@ -9,7 +9,7 @@ x-range { --radius: calc(var(--track-size)/2); - width: 144px; + width: 192px; height: 16px; position: relative; diff --git a/app/js/lib/range.js b/app/js/lib/range.js index 86d77df..3f53099 100644 --- a/app/js/lib/range.js +++ b/app/js/lib/range.js @@ -1,7 +1,7 @@ import * as html from "./html.js"; class Range extends HTMLElement { - static get observedAttributes() { return ["min", "max", "value", "disabled"]; } + static get observedAttributes() { return ["min", "max", "value", "step", "disabled"]; } constructor() { super(); @@ -25,26 +25,45 @@ class Range extends HTMLElement { this._update(); this.addEventListener("mousedown", this); + this.addEventListener("keydown", this); } + get _valueAsNumber() { + let raw = (this.hasAttribute("value") ? Number(this.getAttribute("value")) : 50); + return this._constrain(raw); + } + get _minAsNumber() { + return (this.hasAttribute("min") ? Number(this.getAttribute("min")) : 0); + } + get _maxAsNumber() { + return (this.hasAttribute("max") ? Number(this.getAttribute("max")) : 100); + } + get _stepAsNumber() { + return (this.hasAttribute("step") ? Number(this.getAttribute("step")) : 1); + } + + get value() { return String(this._valueAsNumber); } + get valueAsNumber() { return this._valueAsNumber; } + get min() { return this.hasAttribute("min") ? this.getAttribute("min") : ""; } + get max() { return this.hasAttribute("max") ? this.getAttribute("max") : ""; } + get step() { return this.hasAttribute("step") ? this.getAttribute("step") : ""; } + get disabled() { return this.hasAttribute("disabled"); } + + set _valueAsNumber(value) { this.value = String(value); } set min(min) { this.setAttribute("min", min); } set max(max) { this.setAttribute("max", max); } set value(value) { this.setAttribute("value", value); } + set step(step) { this.setAttribute("step", step); } set disabled(disabled) { disabled ? this.setAttribute("disabled", "") : this.removeAttribute("disabled"); } - get value() { return (this.hasAttribute("value") ? this.getAttribute("value") : "50"); } - get valueAsNumber() { return Number(this.value); } - get min() { return this.getAttribute("min"); } - get max() { return this.getAttribute("max"); } - get disabled() { return this.hasAttribute("disabled"); } - attributeChangedCallback(name, oldValue, newValue) { switch (name) { case "min": case "max": case "value": + case "step": this._update(); break; } @@ -68,13 +87,61 @@ class Range extends HTMLElement { document.removeEventListener("mouseup", this); this.dispatchEvent(new CustomEvent("change")); break; + + case "keydown": + if (this.disabled) { return; } + this._handleKey(e.code); + this.dispatchEvent(new CustomEvent("input")); + this.dispatchEvent(new CustomEvent("change")); + break; } } + _handleKey(code) { + let min = this._minAsNumber; + let max = this._maxAsNumber; + let range = max - min; + let step = this._stepAsNumber; + + switch (code) { + case "ArrowLeft": + case "ArrowDown": + this._valueAsNumber = this._constrain(this._valueAsNumber - step); + break; + + case "ArrowRight": + case "ArrowUp": + this._valueAsNumber = this._constrain(this._valueAsNumber + step); + break; + + case "Home": this._valueAsNumber = this._constrain(min); break; + case "End": this._valueAsNumber = this._constrain(max); break; + + case "PageUp": this._valueAsNumber = this._constrain(this._valueAsNumber + range/10); break; + case "PageDown": this._valueAsNumber = this._constrain(this._valueAsNumber - range/10); break; + } + } + + _constrain(value) { + const min = this._minAsNumber; + const max = this._maxAsNumber; + const step = this._stepAsNumber; + + value = Math.max(value, min); + value = Math.min(value, max); + + value -= min; + value = Math.round(value / step) * step; + value += min; + if (value > max) { value -= step; } + + return value; + } + _update() { - let min = Number(this.min || 0); - let max = Number(this.max || 100); - let frac = (this.valueAsNumber - min) / (max - min); + let min = this._minAsNumber; + let max = this._maxAsNumber; + let frac = (this._valueAsNumber-min) / (max-min); this._dom.thumb.style.left = `${frac * 100}%`; this._dom.remaining.style.left = `${frac * 100}%`; this._dom.elapsed.style.width = `${frac * 100}%`; @@ -86,15 +153,14 @@ class Range extends HTMLElement { x = Math.max(x, rect.left); x = Math.min(x, rect.right); - let min = Number(this.min || 0); - let max = Number(this.max || 100); + let min = this._minAsNumber; + let max = this._maxAsNumber; - let frac = (x-rect.left)/(rect.right-rect.left); - let value = min + frac * (max-min); - value = Math.round(value); // fixme - if (value == this.valueAsNumber) { return; } + let frac = (x-rect.left) / (rect.right-rect.left); + let value = this._constrain(min + frac * (max-min)); + if (value == this._valueAsNumber) { return; } - this.value = value.toString(); + this._valueAsNumber = value; this.dispatchEvent(new CustomEvent("input")); } }