diff --git a/app/app.css b/app/app.css
index a9160ad..35827c2 100644
--- a/app/app.css
+++ b/app/app.css
@@ -96,31 +96,59 @@ select {
font-weight: normal;
}
x-range {
+ --thumb-size: 8px;
+ --thumb-color: #fff;
+ --thumb-shadow: #000;
+ --track-size: 4px;
+ --track-color: gray;
+ --elapsed-color: lightgray;
+ --remaining-color: transparent;
+ --radius: calc(var(--track-size)/2);
width: 144px;
height: 16px;
position: relative;
- --thumb: 6px;
- --track: 4px;
- padding: 0 var(--thumb);
}
x-range .-track,
-x-range .-elapsed {
+x-range .-elapsed,
+x-range .-remaining {
position: absolute;
- top: calc(50% - var(--track)/2);
- height: var(--track);
- left: 0;
+ top: calc(50% - var(--track-size)/2);
+ height: var(--track-size);
+ border-radius: var(--radius);
}
x-range .-track {
width: 100%;
+ left: 0;
+ background-color: gray;
+}
+x-range .-elapsed {
+ left: 0;
+ background-color: var(--elapsed-color);
+}
+x-range .-remaining {
+ right: 0;
+ background-color: var(--remaining-color);
+}
+x-range .-inner {
+ position: absolute;
+ left: var(--thumb-size);
+ right: var(--thumb-size);
+ top: 0;
+ bottom: 0;
}
x-range .-thumb {
+ all: unset;
position: absolute;
top: 50%;
- border-radius: 50%;
- width: calc(2*var(--thumb));
- height: calc(2*var(--thumb));
- background-color: #fff;
transform: translate(-50%, -50%);
+ border-radius: 50%;
+ width: calc(2*var(--thumb-size));
+ height: calc(2*var(--thumb-size));
+ background-color: var(--thumb-color);
+ box-shadow: 0 0 2px var(--thumb-shadow);
+}
+x-range[disabled] {
+ opacity: 0.5;
}
main {
flex-grow: 1;
@@ -179,10 +207,9 @@ nav ul li.active {
#player:not([data-flags~=repeat]) .repeat {
opacity: 0.5;
}
-#player [type=range] {
- margin: 0;
- width: 0;
+#player x-range {
flex-grow: 1;
+ --elapsed-color: var(--primary);
}
#player .art {
margin-right: 0;
diff --git a/app/css/player.less b/app/css/player.less
index b2dd044..edab6ac 100644
--- a/app/css/player.less
+++ b/app/css/player.less
@@ -7,10 +7,9 @@
&[data-state=play] .play { display: none; }
&:not([data-flags~=random]) .random, &:not([data-flags~=repeat]) .repeat { opacity: 0.5; }
- [type=range] {
- margin: 0;
- width: 0; // fails to shrink otherwise
+ x-range {
flex-grow: 1;
+ --elapsed-color: var(--primary);
}
.art {
diff --git a/app/css/range.less b/app/css/range.less
index a6d6e01..e20475a 100644
--- a/app/css/range.less
+++ b/app/css/range.less
@@ -1,36 +1,63 @@
x-range {
+ --thumb-size: 8px;
+ --thumb-color: #fff;
+ --thumb-shadow: #000;
+ --track-size: 4px;
+ --track-color: gray;
+ --elapsed-color: lightgray;
+ --remaining-color: transparent;
+
+ --radius: calc(var(--track-size)/2);
+
width: 144px;
height: 16px;
position: relative;
- --thumb: 6px;
- --track: 4px;
- padding: 0 var(--thumb);
-
- .-track, .-elapsed {
+ .-track, .-elapsed, .-remaining {
position: absolute;
- top: calc(50% - var(--track)/2);
- height: var(--track);
- left: 0;
+ top: calc(50% - var(--track-size)/2);
+ height: var(--track-size);
+ border-radius: var(--radius);
}
.-track {
width: 100%;
+ left: 0;
+ background-color: gray;
}
.-elapsed {
-
+ left: 0;
+ background-color: var(--elapsed-color);
+ }
+
+ .-remaining {
+ right: 0;
+ background-color: var(--remaining-color);
+ }
+
+ .-inner {
+ position: absolute;
+ left: var(--thumb-size);
+ right: var(--thumb-size);
+ top: 0;
+ bottom: 0;
}
.-thumb {
+ all: unset;
position: absolute;
top: 50%;
+ transform: translate(-50%, -50%);
border-radius: 50%;
- width: calc(2*var(--thumb));
- height: calc(2*var(--thumb));
- background-color: #fff;
+ width: calc(2*var(--thumb-size));
+ height: calc(2*var(--thumb-size));
+ background-color: var(--thumb-color);
+ box-shadow: 0 0 2px var(--thumb-shadow);
+ }
- transform: translate(-50%, -50%);
+ &[disabled] {
+ opacity: 0.5;
}
}
diff --git a/app/index.html b/app/index.html
index a28bfb7..242a05e 100644
--- a/app/index.html
+++ b/app/index.html
@@ -15,7 +15,7 @@
-
+
@@ -28,8 +28,7 @@
-
-
+
diff --git a/app/js/lib/range.js b/app/js/lib/range.js
index 291dd82..86d77df 100644
--- a/app/js/lib/range.js
+++ b/app/js/lib/range.js
@@ -1,58 +1,101 @@
import * as html from "./html.js";
class Range extends HTMLElement {
- static get observedAttributes() { return ["min", "max", "value"]; }
+ static get observedAttributes() { return ["min", "max", "value", "disabled"]; }
constructor() {
super();
- this._data = {
- min: 0,
- max: 100,
- value: 50
- };
+ this._dom = {};
- this.track = html.node("span", {className:"-track"});
- this.elapsed = html.node("span", {className:"-elapsed"});
- this.thumb = html.node("span", {className:"-thumb"});
+ this.innerHTML = `
+
+
+
+
+
+
+ `;
- this.appendChild(this.track);
- this.appendChild(this.elapsed);
- this.appendChild(this.thumb);
+ Array.from(this.querySelectorAll("[class^='-']")).forEach(node => {
+ let name = node.className.substring(1);
+ this._dom[name] = node;
+ });
this._update();
+
+ this.addEventListener("mousedown", this);
}
- set value(value) {
- value = Math.max(value, this._data.min);
- value = Math.min(value, this._data.max);
- this._data.value = value;
- this._update();
+ set min(min) { this.setAttribute("min", min); }
+ set max(max) { this.setAttribute("max", max); }
+ set value(value) { this.setAttribute("value", value); }
+ set disabled(disabled) {
+ disabled ? this.setAttribute("disabled", "") : this.removeAttribute("disabled");
}
- set min(min) {
- this._data.min = Math.min(min, this._data.max);
- this._update();
- }
-
- set max(max) {
- this._data.max = Math.max(max, this._data.max);
- this._update();
- }
+ 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) {
- this[name] = newValue;
+ switch (name) {
+ case "min":
+ case "max":
+ case "value":
+ this._update();
+ break;
+ }
}
- get value() { return this._data.value; }
- get valueAsNumber() { return Number(this._data.value); }
- get min() { return this._data.min; }
- get max() { return this._data.max; }
+ handleEvent(e) {
+ switch (e.type) {
+ case "mousedown":
+ if (this.disabled) { return; }
+ document.addEventListener("mousemove", this);
+ document.addEventListener("mouseup", this);
+ this._setToMouse(e);
+ break;
+
+ case "mousemove":
+ this._setToMouse(e);
+ break;
+
+ case "mouseup":
+ document.removeEventListener("mousemove", this);
+ document.removeEventListener("mouseup", this);
+ this.dispatchEvent(new CustomEvent("change"));
+ break;
+ }
+ }
_update() {
- let frac = (this._data.value - this._data.min) / (this._data.max - this._data.min);
- this.thumb.style.left = `${frac * 100}%`;
- this.elapsed.style.width = `${frac * 100}%`;
+ let min = Number(this.min || 0);
+ let max = Number(this.max || 100);
+ 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}%`;
+ }
+
+ _setToMouse(e) {
+ let rect = this._dom.inner.getBoundingClientRect();
+ let x = e.clientX;
+ 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 frac = (x-rect.left)/(rect.right-rect.left);
+ let value = min + frac * (max-min);
+ value = Math.round(value); // fixme
+ if (value == this.valueAsNumber) { return; }
+
+ this.value = value.toString();
+ this.dispatchEvent(new CustomEvent("input"));
}
}
diff --git a/app/js/player.js b/app/js/player.js
index 22243e0..77df796 100644
--- a/app/js/player.js
+++ b/app/js/player.js
@@ -112,8 +112,8 @@ export function init(n) {
let all = node.querySelectorAll("[class]");
Array.from(all).forEach(node => DOM[node.className] = node);
- DOM.progress = DOM.timeline.querySelector("[type=range]");
- DOM.volume = DOM.volume.querySelector("[type=range]");
+ DOM.progress = DOM.timeline.querySelector("x-range");
+ DOM.volume = DOM.volume.querySelector("x-range");
DOM.play.addEventListener("click", e => command("play"));
DOM.pause.addEventListener("click", e => command("pause 1"));