diff --git a/app/Makefile b/app/Makefile index 725e23a..819f958 100644 --- a/app/Makefile +++ b/app/Makefile @@ -1,10 +1,18 @@ LESS := $(shell npm bin)/lessc APP := app.css +ICONS := js/lib/icons.js all: $(APP) +icons: $(ICONS) + +$(ICONS): icons/* + ./svg2js.sh icons > $@ + $(APP): css/* $(LESS) css/app.less > $@ watch: all while inotifywait -e MODIFY -r css js ; do make $^ ; done + +.PHONY: all watch icons diff --git a/app/app.css b/app/app.css index b20cd3b..1424fe8 100644 --- a/app/app.css +++ b/app/app.css @@ -1,9 +1,10 @@ html { background-color: #fff; - font-family: lato, sans-serif; } body { - background-color: #888; + font-family: lato, sans-serif; + line-height: 1.3; + background-color: #333; color: #fff; text-shadow: 0 1px 1px #000; max-width: 800px; @@ -17,6 +18,29 @@ body > header, body > footer { box-shadow: 0 0 3px #000; } +input, +select, +button { + color: inherit; +} +button { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: transparent; + padding: 0; + border: none; + line-height: 1; + cursor: pointer; +} +.icon { + width: 24px; +} +.icon path:not([fill]), +.icon polygon:not([fill]), +.icon circle:not([fill]) { + fill: currentColor; +} @font-face { font-family: 'Lato'; src: url('font/LatoLatin-Regular.woff2') format('woff2'); @@ -71,6 +95,10 @@ nav ul li.active { #player:not([data-flags~=repeat]) .repeat { opacity: 0.5; } +#player button { + width: 64px; + height: 64px; +} .component { height: 100%; display: flex; @@ -86,16 +114,25 @@ nav ul li.active { .component .grid li { display: flex; flex-direction: row; + align-items: center; } .component .grid li h2 { flex-grow: 1; + font-size: 100%; + font-weight: normal; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .component .grid li:nth-child(odd) { - background-color: #666; + background-color: #555; +} +.component .grid button { + flex-shrink: 0; + width: 32px; + height: 32px; + font-size: 32px; } #queue { height: 100%; @@ -112,16 +149,25 @@ nav ul li.active { #queue .grid li { display: flex; flex-direction: row; + align-items: center; } #queue .grid li h2 { flex-grow: 1; + font-size: 100%; + font-weight: normal; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #queue .grid li:nth-child(odd) { - background-color: #666; + background-color: #555; +} +#queue .grid button { + flex-shrink: 0; + width: 32px; + height: 32px; + font-size: 32px; } #queue li { display: flex; @@ -148,16 +194,25 @@ nav ul li.active { #library .grid li { display: flex; flex-direction: row; + align-items: center; } #library .grid li h2 { flex-grow: 1; + font-size: 100%; + font-weight: normal; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #library .grid li:nth-child(odd) { - background-color: #666; + background-color: #555; +} +#library .grid button { + flex-shrink: 0; + width: 32px; + height: 32px; + font-size: 32px; } #fs { height: 100%; @@ -174,14 +229,23 @@ nav ul li.active { #fs .grid li { display: flex; flex-direction: row; + align-items: center; } #fs .grid li h2 { flex-grow: 1; + font-size: 100%; + font-weight: normal; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #fs .grid li:nth-child(odd) { - background-color: #666; + background-color: #555; +} +#fs .grid button { + flex-shrink: 0; + width: 32px; + height: 32px; + font-size: 32px; } diff --git a/app/css/app.less b/app/css/app.less index ae00589..a1ffb8d 100644 --- a/app/css/app.less +++ b/app/css/app.less @@ -1,10 +1,11 @@ html { background-color: #fff; - font-family: lato, sans-serif; } body { - background-color: #888; + font-family: lato, sans-serif; + line-height: 1.3; + background-color: #333; color: #fff; text-shadow: 0 1px 1px #000; max-width: 800px; @@ -19,6 +20,32 @@ body { } } +input, select, button { + color: inherit; +} + +button { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + + background-color: transparent; + padding: 0; + border: none; + line-height: 1; + cursor: pointer; +} + +.icon { + width: 24px; + + path, polygon, circle { + &:not([fill]) { + fill: currentColor; + } + } +} + @import "font.less"; @import "main.less"; diff --git a/app/css/component.less b/app/css/component.less index 0beea7e..dce52f9 100644 --- a/app/css/component.less +++ b/app/css/component.less @@ -15,9 +15,12 @@ li { display: flex; flex-direction: row; + align-items: center; h2 { flex-grow: 1; + font-size: 100%; + font-weight: normal; margin: 0; white-space: nowrap; overflow: hidden; @@ -26,7 +29,14 @@ } li:nth-child(odd) { - background-color: #666; + background-color: #555; + } + + button { + flex-shrink: 0; + width: 32px; + height: 32px; + font-size: 32px; } } } diff --git a/app/css/player.less b/app/css/player.less index af59629..285183d 100644 --- a/app/css/player.less +++ b/app/css/player.less @@ -8,4 +8,9 @@ &:not([data-state=play]) .pause { display: none; } &[data-state=play] .play { display: none; } &:not([data-flags~=random]) .random, &:not([data-flags~=repeat]) .repeat { opacity: 0.5; } + + button { + width: 64px; + height: 64px; + } } \ No newline at end of file diff --git a/app/icons/close-circle-outline.svg b/app/icons/close-circle-outline.svg new file mode 100644 index 0000000..dad58cf --- /dev/null +++ b/app/icons/close-circle-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/close-circle.svg b/app/icons/close-circle.svg new file mode 100644 index 0000000..5a37396 --- /dev/null +++ b/app/icons/close-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/close.svg b/app/icons/close.svg new file mode 100644 index 0000000..18691d7 --- /dev/null +++ b/app/icons/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/fast-forward.svg b/app/icons/fast-forward.svg new file mode 100644 index 0000000..bfe744f --- /dev/null +++ b/app/icons/fast-forward.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/minus-circle-outline.svg b/app/icons/minus-circle-outline.svg new file mode 100644 index 0000000..889d629 --- /dev/null +++ b/app/icons/minus-circle-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/minus-circle.svg b/app/icons/minus-circle.svg new file mode 100644 index 0000000..445910b --- /dev/null +++ b/app/icons/minus-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/minus.svg b/app/icons/minus.svg new file mode 100644 index 0000000..4cee9d4 --- /dev/null +++ b/app/icons/minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/pause-circle-outline.svg b/app/icons/pause-circle-outline.svg new file mode 100644 index 0000000..f9825b5 --- /dev/null +++ b/app/icons/pause-circle-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/pause-circle.svg b/app/icons/pause-circle.svg new file mode 100644 index 0000000..9f33cb7 --- /dev/null +++ b/app/icons/pause-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/pause.svg b/app/icons/pause.svg new file mode 100644 index 0000000..37ed32d --- /dev/null +++ b/app/icons/pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/play-circle-outline.svg b/app/icons/play-circle-outline.svg new file mode 100644 index 0000000..792051f --- /dev/null +++ b/app/icons/play-circle-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/play-circle.svg b/app/icons/play-circle.svg new file mode 100644 index 0000000..9835bb8 --- /dev/null +++ b/app/icons/play-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/play.svg b/app/icons/play.svg new file mode 100644 index 0000000..87a70f2 --- /dev/null +++ b/app/icons/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/plus-circle-outline.svg b/app/icons/plus-circle-outline.svg new file mode 100644 index 0000000..3fe6bec --- /dev/null +++ b/app/icons/plus-circle-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/plus-circle.svg b/app/icons/plus-circle.svg new file mode 100644 index 0000000..64b5a32 --- /dev/null +++ b/app/icons/plus-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/plus.svg b/app/icons/plus.svg new file mode 100644 index 0000000..2c21839 --- /dev/null +++ b/app/icons/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/repeat.svg b/app/icons/repeat.svg new file mode 100644 index 0000000..8086726 --- /dev/null +++ b/app/icons/repeat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/rewind.svg b/app/icons/rewind.svg new file mode 100644 index 0000000..ee45c02 --- /dev/null +++ b/app/icons/rewind.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/icons/shuffle.svg b/app/icons/shuffle.svg new file mode 100644 index 0000000..dcd9763 --- /dev/null +++ b/app/icons/shuffle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/index.html b/app/index.html index 2d8f4ac..a22234e 100644 --- a/app/index.html +++ b/app/index.html @@ -15,12 +15,12 @@ / - - - - - - + + + + + +
diff --git a/app/js/lib/html.js b/app/js/lib/html.js index 50bd354..ee109c7 100644 --- a/app/js/lib/html.js +++ b/app/js/lib/html.js @@ -1,3 +1,5 @@ +import icons from "./icons.js"; + export function node(name, attrs, content, parent) { let n = document.createElement(name); Object.assign(n, attrs); @@ -9,8 +11,32 @@ export function node(name, attrs, content, parent) { return n; } +export function icon(type, parent) { + let str = icons[type]; + if (!str) { + console.error("Bad icon type '%s'", type); + return node("span", {}, "‽"); + } + + let tmp = node("div"); + tmp.innerHTML = str; + let s = tmp.querySelector("svg"); + if (!s) { throw new Error(`Bad icon source for type '${type}'`); } + + s.classList.add("icon"); + s.classList.add(`icon-${type}`); + + parent && parent.appendChild(s); + return s; +} + export function button(attrs, content, parent) { - return node("button", attrs, content, parent); + let result = node("button", attrs, content, parent); + if (attrs && attrs.icon) { + let i = icon(attrs.icon); + result.insertBefore(i, result.firstChild); + } + return result; } export function clear(node) { diff --git a/app/js/lib/icons.js b/app/js/lib/icons.js new file mode 100644 index 0000000..f09a849 --- /dev/null +++ b/app/js/lib/icons.js @@ -0,0 +1,59 @@ +let ICONS={}; +ICONS["play-circle-outline"] = ` + +`; +ICONS["shuffle"] = ` + +`; +ICONS["minus-circle"] = ` + +`; +ICONS["rewind"] = ` + +`; +ICONS["pause"] = ` + +`; +ICONS["pause-circle"] = ` + +`; +ICONS["close-circle-outline"] = ` + +`; +ICONS["close"] = ` + +`; +ICONS["minus"] = ` + +`; +ICONS["close-circle"] = ` + +`; +ICONS["repeat"] = ` + +`; +ICONS["play"] = ` + +`; +ICONS["pause-circle-outline"] = ` + +`; +ICONS["plus"] = ` + +`; +ICONS["fast-forward"] = ` + +`; +ICONS["minus-circle-outline"] = ` + +`; +ICONS["plus-circle-outline"] = ` + +`; +ICONS["plus-circle"] = ` + +`; +ICONS["play-circle"] = ` + +`; +export default ICONS; diff --git a/app/js/lib/ui.js b/app/js/lib/ui.js index 3d04bec..75488b8 100644 --- a/app/js/lib/ui.js +++ b/app/js/lib/ui.js @@ -37,7 +37,7 @@ function formatTitle(type, data) { } function playButton(id, parent) { - let button = html.button({className:"play"}, "▶", parent); + let button = html.button({icon:"play", title:"Play"}, "", parent); button.addEventListener("click", async e => { await mpd.command(`playid ${id}`); player.update(); @@ -45,7 +45,7 @@ function playButton(id, parent) { } function deleteButton(id, parent) { - let button = html.button({className:"delete"}, "🗙", parent); + let button = html.button({icon:"close"}, "", parent); button.addEventListener("click", async e => { await mpd.command(`deleteid ${id}`); pubsub.publish("queue-change"); @@ -54,7 +54,7 @@ function deleteButton(id, parent) { } function addAndPlayButton(urlOrFilter, parent) { - let button = html.button({}, "▶", parent); + let button = html.button({icon:"play", title:"Play"}, "", parent); button.addEventListener("click", async e => { e.stopPropagation(); await mpd.command("clear"); @@ -67,7 +67,7 @@ function addAndPlayButton(urlOrFilter, parent) { } function addButton(urlOrFilter, parent) { - let button = html.button({}, "+", parent); + let button = html.button({icon:"plus"}, "", parent); button.addEventListener("click", async e => { e.stopPropagation(); await mpd.enqueue(urlOrFilter, SORT); diff --git a/app/js/player.js b/app/js/player.js index d5b3ef5..dfd1088 100644 --- a/app/js/player.js +++ b/app/js/player.js @@ -76,5 +76,12 @@ export function init(n) { DOM.random.addEventListener("click", e => command(`random ${current["random"] == "1" ? "0" : "1"}`)); DOM.repeat.addEventListener("click", e => command(`repeat ${current["repeat"] == "1" ? "0" : "1"}`)); + DOM.play.appendChild(html.icon("play")); + DOM.pause.appendChild(html.icon("pause")); + DOM.prev.appendChild(html.icon("rewind")); + DOM.next.appendChild(html.icon("fast-forward")); + DOM.random.appendChild(html.icon("shuffle")); + DOM.repeat.appendChild(html.icon("repeat")); + update(); } diff --git a/app/svg2js.sh b/app/svg2js.sh new file mode 100755 index 0000000..bd3d9d2 --- /dev/null +++ b/app/svg2js.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +ARGS="ed -O -N svg=http://www.w3.org/2000/svg" +DELETE="-d //comment() -d //svg:defs -d //svg:title -d //svg:desc -d //@fill -d //@stroke -d //@stroke-width -d //@fill-rule -d //@width -d //@height" + +process_svg () { + NAME=$1 + ID=$(basename $NAME | sed -e 's/.svg//') + + DATA=$(cat $NAME | xmlstarlet fo -D -N | xmlstarlet $ARGS $DELETE) + printf "ICONS[\"$ID\"] = \`$DATA\`;\n" +} + +if [ "$#" -ne 1 ]; then + echo "Usage: $0 svg_directory" + exit 1 +fi + +IMAGES=$(find "$1" -name "*.svg") + +printf "let ICONS={};\n" +for i in $IMAGES; do + process_svg $i +done +printf "export default ICONS;\n"