From 0873dcfe113192ed2754473f178ccfbb4e6744e3 Mon Sep 17 00:00:00 2001 From: Ondrej Zara Date: Tue, 2 Apr 2019 19:19:46 +0200 Subject: [PATCH] headers --- app/app.css | 168 ++++++++++++++++++++++++++++++++--------- app/css/app.less | 24 ++---- app/css/component.less | 22 +++--- app/css/fs.less | 9 +++ app/css/library.less | 9 +++ app/css/mixins.less | 23 ++++++ app/css/nav.less | 3 +- app/css/player.less | 4 +- app/css/search.less | 13 ++-- app/js/fs.js | 9 +-- app/js/lib/format.js | 2 +- app/js/lib/mpd.js | 2 +- app/js/library.js | 12 +-- index.js | 3 +- 14 files changed, 213 insertions(+), 90 deletions(-) create mode 100644 app/css/mixins.less diff --git a/app/app.css b/app/app.css index b3442df..213c918 100644 --- a/app/app.css +++ b/app/app.css @@ -7,6 +7,8 @@ html { background-color: var(--fg); } body { + display: flex; + flex-direction: column; box-sizing: border-box; font-family: lato, sans-serif; line-height: 1.25; @@ -16,8 +18,6 @@ body { max-width: 800px; margin: 0 auto; overflow: hidden; - display: flex; - flex-direction: column; height: 100vh; } body > header, @@ -34,6 +34,10 @@ button { -webkit-appearance: none; -moz-appearance: none; appearance: none; + display: flex; + flex-direction: row; + align-items: center; + display: inline-flex; background-color: transparent; padding: 0; border: none; @@ -41,22 +45,6 @@ button { cursor: pointer; flex-shrink: 0; } -button .icon { - vertical-align: middle; -} -.long-line { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} -.multiline { - display: flex; - flex-direction: row; - align-items: center; -} -.multiline h2 { - font-weight: normal; -} @font-face { font-family: 'Lato'; src: url('font/LatoLatin-Regular.woff2') format('woff2'); @@ -77,6 +65,28 @@ button .icon { .icon circle:not([fill]) { fill: currentColor; } +.flex-row { + display: flex; + flex-direction: row; + align-items: center; +} +.flex-column { + display: flex; + flex-direction: column; +} +.long-line { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.multiline { + display: flex; + flex-direction: row; + align-items: center; +} +.multiline h2 { + font-weight: normal; +} main { flex-grow: 1; overflow: hidden; @@ -87,6 +97,7 @@ nav ul { list-style: none; display: flex; flex-direction: row; + align-items: center; } nav ul .icon { display: block; @@ -158,6 +169,16 @@ nav ul li.active { } .component header { display: flex; + flex-direction: row; + align-items: center; + padding: 8px; +} +.component header button { + font-size: var(--font-size-large); + font-weight: bold; +} +.component header button .icon { + margin-right: var(--icon-spacing); } .component ul { flex-grow: 1; @@ -193,12 +214,12 @@ nav ul li.active { .component li:not(.has-art) { padding: 8px; } +.component li button .icon { + width: 32px; +} .component li:nth-child(odd) { background-color: #555; } -.component button .icon { - width: 32px; -} #queue { height: 100%; display: flex; @@ -206,6 +227,16 @@ nav ul li.active { } #queue header { display: flex; + flex-direction: row; + align-items: center; + padding: 8px; +} +#queue header button { + font-size: var(--font-size-large); + font-weight: bold; +} +#queue header button .icon { + margin-right: var(--icon-spacing); } #queue ul { flex-grow: 1; @@ -241,12 +272,12 @@ nav ul li.active { #queue li:not(.has-art) { padding: 8px; } +#queue li button .icon { + width: 32px; +} #queue li:nth-child(odd) { background-color: #555; } -#queue button .icon { - width: 32px; -} #queue .current { color: var(--primary); } @@ -257,6 +288,16 @@ nav ul li.active { } #library header { display: flex; + flex-direction: row; + align-items: center; + padding: 8px; +} +#library header button { + font-size: var(--font-size-large); + font-weight: bold; +} +#library header button .icon { + margin-right: var(--icon-spacing); } #library ul { flex-grow: 1; @@ -292,11 +333,20 @@ nav ul li.active { #library li:not(.has-art) { padding: 8px; } +#library li button .icon { + width: 32px; +} #library li:nth-child(odd) { background-color: #555; } -#library button .icon { - width: 32px; +#library header { + white-space: pre; +} +#library .search { + order: 1; +} +#library .search.open ~ * { + display: none; } #library .art img, #library .art .icon { @@ -333,6 +383,16 @@ nav ul li.active { } #fs header { display: flex; + flex-direction: row; + align-items: center; + padding: 8px; +} +#fs header button { + font-size: var(--font-size-large); + font-weight: bold; +} +#fs header button .icon { + margin-right: var(--icon-spacing); } #fs ul { flex-grow: 1; @@ -368,11 +428,20 @@ nav ul li.active { #fs li:not(.has-art) { padding: 8px; } +#fs li button .icon { + width: 32px; +} #fs li:nth-child(odd) { background-color: #555; } -#fs button .icon { - width: 32px; +#fs header { + white-space: pre; +} +#fs .search { + order: 1; +} +#fs .search.open ~ * { + display: none; } #fs .group { cursor: pointer; @@ -392,6 +461,16 @@ nav ul li.active { } #playlists header { display: flex; + flex-direction: row; + align-items: center; + padding: 8px; +} +#playlists header button { + font-size: var(--font-size-large); + font-weight: bold; +} +#playlists header button .icon { + margin-right: var(--icon-spacing); } #playlists ul { flex-grow: 1; @@ -427,12 +506,12 @@ nav ul li.active { #playlists li:not(.has-art) { padding: 8px; } +#playlists li button .icon { + width: 32px; +} #playlists li:nth-child(odd) { background-color: #555; } -#playlists button .icon { - width: 32px; -} #playlists .info { display: flex; flex-direction: row; @@ -450,6 +529,16 @@ nav ul li.active { } #yt header { display: flex; + flex-direction: row; + align-items: center; + padding: 8px; +} +#yt header button { + font-size: var(--font-size-large); + font-weight: bold; +} +#yt header button .icon { + margin-right: var(--icon-spacing); } #yt ul { flex-grow: 1; @@ -485,12 +574,12 @@ nav ul li.active { #yt li:not(.has-art) { padding: 8px; } +#yt li button .icon { + width: 32px; +} #yt li:nth-child(odd) { background-color: #555; } -#yt button .icon { - width: 32px; -} #yt .go { width: 96px; height: 96px; @@ -524,8 +613,12 @@ nav ul li.active { } .search { display: flex; + flex-direction: row; align-items: center; margin-left: auto; + transition: all 300ms; + width: 32px; + max-width: 20ch; } .search .icon { width: 32px; @@ -533,15 +626,16 @@ nav ul li.active { } .search input { border: none; + outline: none; color: inherit; background-color: inherit; border-bottom: 1px solid var(--fg); - transition: all 300ms; width: 0; padding: 0; + flex-grow: 1; } -.search.open input { - width: 10vw; +.search.open { + flex: 1; } .art { margin-right: var(--icon-spacing); diff --git a/app/css/app.less b/app/css/app.less index 8eaa677..478cc79 100644 --- a/app/css/app.less +++ b/app/css/app.less @@ -5,6 +5,8 @@ html { } body { + .flex-column; + box-sizing: border-box; font-family: lato, sans-serif; line-height: 1.25; @@ -14,8 +16,6 @@ body { max-width: 800px; margin: 0 auto; overflow: hidden; - display: flex; - flex-direction: column; height: 100vh; > header, > footer { @@ -33,32 +33,20 @@ button { -moz-appearance: none; appearance: none; + .flex-row; + display: inline-flex; + background-color: transparent; padding: 0; border: none; line-height: 1; cursor: pointer; flex-shrink: 0; - - .icon { vertical-align: middle; } -} - -.long-line { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.multiline { - display: flex; - flex-direction: row; - align-items: center; - - h2 { font-weight: normal; } } @import "font.less"; @import "icons.less"; +@import "mixins.less"; @import "main.less"; @import "nav.less"; @import "player.less"; diff --git a/app/css/component.less b/app/css/component.less index 898fafc..6fae25d 100644 --- a/app/css/component.less +++ b/app/css/component.less @@ -1,10 +1,17 @@ .component { height: 100%; - display: flex; - flex-direction: column; + .flex-column; header { - display: flex; + .flex-row; + padding: 8px; + + button { + font-size: var(--font-size-large); + font-weight: bold; + .icon { margin-right: var(--icon-spacing); } + } + } ul { @@ -16,9 +23,7 @@ } li { - display: flex; - flex-direction: row; - align-items: center; + .flex-row; .info { flex-grow: 1; @@ -45,12 +50,11 @@ &:not(.has-art) { padding: 8px; } + + button .icon { width: 32px; } } li:nth-child(odd) { background-color: #555; } - - button .icon { width: 32px; } - } diff --git a/app/css/fs.less b/app/css/fs.less index 0f8c32d..1584d2a 100644 --- a/app/css/fs.less +++ b/app/css/fs.less @@ -1,6 +1,15 @@ #fs { .component; + header { + white-space: pre; // separator + } + + .search { + order: 1; + &.open ~ * { display: none; } + } + .group { cursor: pointer; } diff --git a/app/css/library.less b/app/css/library.less index aa9f9cf..1212899 100644 --- a/app/css/library.less +++ b/app/css/library.less @@ -1,6 +1,15 @@ #library { .component; + header { + white-space: pre; // separator + } + + .search { + order: 1; + &.open ~ * { display: none; } + } + .art img, .art .icon { width: 64px; } diff --git a/app/css/mixins.less b/app/css/mixins.less new file mode 100644 index 0000000..c58c756 --- /dev/null +++ b/app/css/mixins.less @@ -0,0 +1,23 @@ +.flex-row { + display: flex; + flex-direction: row; + align-items: center; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.long-line { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.multiline { + .flex-row; + + h2 { font-weight: normal; } +} + diff --git a/app/css/nav.less b/app/css/nav.less index b3de634..c4bde31 100644 --- a/app/css/nav.less +++ b/app/css/nav.less @@ -2,8 +2,7 @@ nav ul { margin: 0; padding: 0; list-style: none; - display: flex; - flex-direction: row; + .flex-row; .icon { display: block; diff --git a/app/css/player.less b/app/css/player.less index d1adc1b..3260e95 100644 --- a/app/css/player.less +++ b/app/css/player.less @@ -1,7 +1,5 @@ #player { - display: flex; - flex-direction: row; - align-items: center; + .flex-row; &:not([data-state=play]) .pause { display: none; } &[data-state=play] .play { display: none; } diff --git a/app/css/search.less b/app/css/search.less index b563f8e..0280401 100644 --- a/app/css/search.less +++ b/app/css/search.less @@ -1,7 +1,9 @@ .search { - display: flex; - align-items: center; + .flex-row; margin-left: auto; + transition: all 300ms; + width: 32px; + max-width: 20ch; .icon { width: 32px; @@ -10,17 +12,16 @@ input { border: none; + outline: none; color: inherit; background-color: inherit; border-bottom: 1px solid var(--fg); - transition: all 300ms; width: 0; padding: 0; + flex-grow: 1; } &.open { - input { - width: 10vw; - } + flex: 1; } } \ No newline at end of file diff --git a/app/js/fs.js b/app/js/fs.js index 7d195a1..81d0ee3 100644 --- a/app/js/fs.js +++ b/app/js/fs.js @@ -13,17 +13,16 @@ function buildHeader(path) { let header = node.querySelector("header"); html.clear(header); - let button = html.button({}, "/", header); - button.addEventListener("click", e => list("")); + search.reset(); + header.appendChild(search.getNode()); path.split("/").filter(x => x).forEach((name, index, all) => { - let button = html.button({}, name, header); + index && html.node("span", {}, " / ", header); + let button = html.button({icon:"folder"}, name, header); let path = all.slice(0, index+1).join("/"); button.addEventListener("click", e => list(path)); }); - search.reset(); - header.appendChild(search.getNode()); } function buildDirectory(data, parent) { diff --git a/app/js/lib/format.js b/app/js/lib/format.js index b9fb645..f3d3088 100644 --- a/app/js/lib/format.js +++ b/app/js/lib/format.js @@ -1,4 +1,4 @@ -const SEPARATOR = " · "; +export const SEPARATOR = " · "; export function time(sec) { sec = Math.round(sec); diff --git a/app/js/lib/mpd.js b/app/js/lib/mpd.js index cff1388..42f0961 100644 --- a/app/js/lib/mpd.js +++ b/app/js/lib/mpd.js @@ -137,7 +137,7 @@ export async function albumArt(songUrl) { export async function init() { return new Promise((resolve, reject) => { try { - ws = new WebSocket("ws://localhost:8080"); + ws = new WebSocket(`ws://${location.host}`); } catch (e) { reject(e); } current = {resolve, reject}; diff --git a/app/js/library.js b/app/js/library.js index b5c4fa8..a1d5679 100644 --- a/app/js/library.js +++ b/app/js/library.js @@ -1,6 +1,7 @@ import * as mpd from "./lib/mpd.js"; import * as html from "./lib/html.js"; import * as ui from "./lib/ui.js"; +import * as format from "./lib/format.js"; import Search from "./lib/search.js"; @@ -13,25 +14,24 @@ function buildHeader(filter) { let header = node.querySelector("header"); html.clear(header); - let button = html.button({}, "Music Library", header); - button.addEventListener("click", e => listArtists()); + search.reset(); + header.appendChild(search.getNode()); let artist = filter["Artist"]; if (artist) { let artistFilter = {"Artist":artist}; - let button = html.button({}, artist, header); + let button = html.button({icon:"artist"}, artist, header); button.addEventListener("click", e => listAlbums(artistFilter)); let album = filter["Album"]; if (album) { + html.node("span", {}, format.SEPARATOR, header); let albumFilter = Object.assign({}, artistFilter, {"Album":album}); - let button = html.button({}, album, header); + let button = html.button({icon:"album"}, album, header); button.addEventListener("click", e => listSongs(albumFilter)); } } - search.reset(); - header.appendChild(search.getNode()); } function buildAlbum(album, filter, parent) { diff --git a/index.js b/index.js index f0e6b3f..74809b0 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ const static = require("node-static"); const app = new static.Server("./app"); -const port = 8080; +const port = Number(process.argv[2]) || 8080; function downloadYoutube(url, response) { // FIXME create directory @@ -41,7 +41,6 @@ function handleYoutube(request, response) { request.on("data", chunk => str += chunk); request.on("end", () => { let url = require("querystring").parse(str)["url"]; - console.log(url); if (url) { downloadYoutube(url, response); } else {