flex, search

This commit is contained in:
Ondrej Zara 2019-03-31 22:02:26 +02:00
parent 7a5dfc9b16
commit 127a450791
19 changed files with 348 additions and 139 deletions

View file

@ -9,7 +9,7 @@ html {
body { body {
box-sizing: border-box; box-sizing: border-box;
font-family: lato, sans-serif; font-family: lato, sans-serif;
line-height: 1; line-height: 1.25;
background-color: var(--bg); background-color: var(--bg);
color: var(--fg); color: var(--fg);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8); text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8);
@ -28,6 +28,7 @@ input,
select, select,
button { button {
color: inherit; color: inherit;
font: inherit;
} }
button { button {
-webkit-appearance: none; -webkit-appearance: none;
@ -43,6 +44,19 @@ button {
.art img { .art img {
vertical-align: top; vertical-align: top;
} }
.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-face {
font-family: 'Lato'; font-family: 'Lato';
src: url('font/LatoLatin-Regular.woff2') format('woff2'); src: url('font/LatoLatin-Regular.woff2') format('woff2');
@ -104,8 +118,19 @@ nav ul li.active {
#player:not([data-flags~=repeat]) .repeat { #player:not([data-flags~=repeat]) .repeat {
opacity: 0.5; opacity: 0.5;
} }
#player .art {
margin-right: var(--icon-spacing);
}
#player h2 {
font-size: 125%;
margin-top: 0;
}
#player .info { #player .info {
flex-grow: 1; flex-grow: 1;
overflow: hidden;
}
#player .title,
#player .subtitle {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -115,15 +140,17 @@ nav ul li.active {
text-align: center; text-align: center;
} }
#player .controls .icon { #player .controls .icon {
width: 64px; width: 32px;
margin: 8px;
} }
#player .misc { #player .misc {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 48px; align-self: stretch;
justify-content: space-around;
} }
#player .misc .icon { #player .misc .icon {
width: 48px; width: 32px;
} }
.component { .component {
height: 100%; height: 100%;
@ -141,28 +168,33 @@ nav ul li.active {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
white-space: nowrap;
} }
.component h2 { .component li .info {
flex-grow: 1; flex-grow: 1;
font-size: 100%; overflow: hidden;
font-weight: normal; }
.component li .info .icon {
color: var(--primary);
margin-right: var(--icon-spacing);
}
.component li .info h2 {
font-size: var(--font-size-large);
margin: 0; margin: 0;
margin-left: 4px; }
.component li .info h2,
.component li .info div {
white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.component h2 .icon { .component li:not(.has-art) {
margin-right: 4px; padding: 8px;
color: var(--primary);
} }
.component li:nth-child(odd) { .component li:nth-child(odd) {
background-color: #555; background-color: #555;
} }
@media (pointer: coarse) { .component button .icon {
.component .icon { width: 32px;
width: 32px;
}
} }
#queue { #queue {
height: 100%; height: 100%;
@ -180,31 +212,36 @@ nav ul li.active {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
white-space: nowrap;
} }
#queue h2 { #queue li .info {
flex-grow: 1; flex-grow: 1;
font-size: 100%; overflow: hidden;
font-weight: normal; }
#queue li .info .icon {
color: var(--primary);
margin-right: var(--icon-spacing);
}
#queue li .info h2 {
font-size: var(--font-size-large);
margin: 0; margin: 0;
margin-left: 4px; }
#queue li .info h2,
#queue li .info div {
white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
#queue h2 .icon { #queue li:not(.has-art) {
margin-right: 4px; padding: 8px;
color: var(--primary);
} }
#queue li:nth-child(odd) { #queue li:nth-child(odd) {
background-color: #555; background-color: #555;
} }
@media (pointer: coarse) { #queue button .icon {
#queue .icon { width: 32px;
width: 32px;
}
} }
#queue .current * { #queue .current {
font-weight: bold !important; color: var(--primary);
} }
#library { #library {
height: 100%; height: 100%;
@ -222,31 +259,44 @@ nav ul li.active {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
white-space: nowrap;
} }
#library h2 { #library li .info {
flex-grow: 1; flex-grow: 1;
font-size: 100%; overflow: hidden;
font-weight: normal; }
#library li .info .icon {
color: var(--primary);
margin-right: var(--icon-spacing);
}
#library li .info h2 {
font-size: var(--font-size-large);
margin: 0; margin: 0;
margin-left: 4px; }
#library li .info h2,
#library li .info div {
white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
#library h2 .icon { #library li:not(.has-art) {
margin-right: 4px; padding: 8px;
color: var(--primary);
} }
#library li:nth-child(odd) { #library li:nth-child(odd) {
background-color: #555; background-color: #555;
} }
@media (pointer: coarse) { #library button .icon {
#library .icon { width: 32px;
width: 32px;
}
} }
#library .art img { #library .art img,
#library .art .icon {
width: 64px; width: 64px;
margin-right: var(--icon-spacing);
}
#library .group {
cursor: pointer;
}
#library .group h2 {
font-weight: normal;
} }
#library .tiles { #library .tiles {
display: grid; display: grid;
@ -279,32 +329,45 @@ nav ul li.active {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
white-space: nowrap;
} }
#fs h2 { #fs li .info {
flex-grow: 1; flex-grow: 1;
font-size: 100%; overflow: hidden;
font-weight: normal; }
#fs li .info .icon {
color: var(--primary);
margin-right: var(--icon-spacing);
}
#fs li .info h2 {
font-size: var(--font-size-large);
margin: 0; margin: 0;
margin-left: 4px; }
#fs li .info h2,
#fs li .info div {
white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
#fs h2 .icon { #fs li:not(.has-art) {
margin-right: 4px; padding: 8px;
color: var(--primary);
} }
#fs li:nth-child(odd) { #fs li:nth-child(odd) {
background-color: #555; background-color: #555;
} }
@media (pointer: coarse) { #fs button .icon {
#fs .icon { width: 32px;
width: 32px;
}
} }
#fs .group { #fs .group {
cursor: pointer; cursor: pointer;
} }
#fs .info {
display: flex;
flex-direction: row;
align-items: center;
}
#fs .info h2 {
font-weight: normal;
}
#playlists { #playlists {
height: 100%; height: 100%;
display: flex; display: flex;
@ -321,28 +384,41 @@ nav ul li.active {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
white-space: nowrap;
} }
#playlists h2 { #playlists li .info {
flex-grow: 1; flex-grow: 1;
font-size: 100%; overflow: hidden;
font-weight: normal; }
#playlists li .info .icon {
color: var(--primary);
margin-right: var(--icon-spacing);
}
#playlists li .info h2 {
font-size: var(--font-size-large);
margin: 0; margin: 0;
margin-left: 4px; }
#playlists li .info h2,
#playlists li .info div {
white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
#playlists h2 .icon { #playlists li:not(.has-art) {
margin-right: 4px; padding: 8px;
color: var(--primary);
} }
#playlists li:nth-child(odd) { #playlists li:nth-child(odd) {
background-color: #555; background-color: #555;
} }
@media (pointer: coarse) { #playlists button .icon {
#playlists .icon { width: 32px;
width: 32px; }
} #playlists .info {
display: flex;
flex-direction: row;
align-items: center;
}
#playlists .info h2 {
font-weight: normal;
} }
#yt { #yt {
height: 100%; height: 100%;
@ -362,28 +438,33 @@ nav ul li.active {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
white-space: nowrap;
} }
#yt h2 { #yt li .info {
flex-grow: 1; flex-grow: 1;
font-size: 100%; overflow: hidden;
font-weight: normal; }
#yt li .info .icon {
color: var(--primary);
margin-right: var(--icon-spacing);
}
#yt li .info h2 {
font-size: var(--font-size-large);
margin: 0; margin: 0;
margin-left: 4px; }
#yt li .info h2,
#yt li .info div {
white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
#yt h2 .icon { #yt li:not(.has-art) {
margin-right: 4px; padding: 8px;
color: var(--primary);
} }
#yt li:nth-child(odd) { #yt li:nth-child(odd) {
background-color: #555; background-color: #555;
} }
@media (pointer: coarse) { #yt button .icon {
#yt .icon { width: 32px;
width: 32px;
}
} }
#yt .go { #yt .go {
width: 96px; width: 96px;
@ -416,8 +497,25 @@ nav ul li.active {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
.search .icon {
width: 32px;
}
.search input {
border: none;
color: inherit;
background-color: inherit;
border-bottom: 1px solid var(--fg);
transition: all 300ms;
padding: 0;
width: 30%;
}
.search:not(.open) input {
width: 0;
}
:root { :root {
--primary: dodgerblue; --primary: dodgerblue;
--fg: #fff; --fg: #fff;
--bg: #333; --bg: #333;
--font-size-large: 112.5%;
--icon-spacing: 4px;
} }

View file

@ -7,7 +7,7 @@ html {
body { body {
box-sizing: border-box; box-sizing: border-box;
font-family: lato, sans-serif; font-family: lato, sans-serif;
line-height: 1; line-height: 1.25;
background-color: var(--bg); background-color: var(--bg);
color: var(--fg); color: var(--fg);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8); text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8);
@ -25,6 +25,7 @@ body {
input, select, button { input, select, button {
color: inherit; color: inherit;
font: inherit;
} }
button { button {
@ -44,6 +45,20 @@ button {
vertical-align: top; vertical-align: top;
} }
.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 "font.less";
@import "icons.less"; @import "icons.less";
@import "main.less"; @import "main.less";
@ -55,4 +70,5 @@ button {
@import "fs.less"; @import "fs.less";
@import "playlists.less"; @import "playlists.less";
@import "yt.less"; @import "yt.less";
@import "search.less";
@import "variables.less"; @import "variables.less";

View file

@ -15,21 +15,30 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
white-space: nowrap;
}
h2 { .info {
flex-grow: 1; flex-grow: 1;
font-size: 100%; overflow: hidden;
font-weight: normal;
margin: 0;
margin-left: 4px;
overflow: hidden;
text-overflow: ellipsis;
.icon { .icon {
margin-right: 4px; color: var(--primary);
color: var(--primary); margin-right: var(--icon-spacing);
}
h2 {
font-size: var(--font-size-large);
margin: 0;
}
h2, div { .long-line; }
}
&.has-art {
}
&:not(.has-art) {
padding: 8px;
} }
} }
@ -37,7 +46,6 @@
background-color: #555; background-color: #555;
} }
@media (pointer: coarse) { button .icon { width: 32px; }
.icon { width: 32px; }
}
} }

View file

@ -4,4 +4,8 @@
.group { .group {
cursor: pointer; cursor: pointer;
} }
.info {
.multiline;
}
} }

View file

@ -1,8 +1,14 @@
#library { #library {
.component; .component;
.art img { .art img, .art .icon {
width: 64px; width: 64px;
margin-right: var(--icon-spacing);
}
.group {
cursor: pointer;
h2 { font-weight: normal; }
} }
.tiles { .tiles {

View file

@ -7,25 +7,37 @@
&[data-state=play] .play { display: none; } &[data-state=play] .play { display: none; }
&:not([data-flags~=random]) .random, &:not([data-flags~=repeat]) .repeat { opacity: 0.5; } &:not([data-flags~=random]) .random, &:not([data-flags~=repeat]) .repeat { opacity: 0.5; }
.art {
margin-right: var(--icon-spacing);
}
h2 {
font-size: 125%;
margin-top: 0;
}
.info { .info {
flex-grow: 1; flex-grow: 1;
white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
} }
.title, .subtitle { .long-line; }
.controls { .controls {
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
.icon { width: 64px; } .icon {
width: 32px;
margin: 8px;
}
} }
.misc { .misc {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@size: 96px / 2; align-self: stretch;
width: @size; justify-content: space-around;
.icon { width: @size; } .icon { width: 32px; }
} }
} }

View file

@ -1,3 +1,7 @@
#playlists { #playlists {
.component; .component;
.info {
.multiline;
}
} }

View file

@ -1,4 +1,5 @@
#queue { #queue {
.component; .component;
.current * { font-weight: bold !important; }
.current { color: var(--primary); }
} }

21
app/css/search.less Normal file
View file

@ -0,0 +1,21 @@
.search {
.icon {
width: 32px;
}
input {
border: none;
color: inherit;
background-color: inherit;
border-bottom: 1px solid var(--fg);
transition: all 300ms;
padding: 0;
width: 30%;
}
&:not(.open) {
input {
width: 0;
}
}
}

View file

@ -2,4 +2,7 @@
--primary: dodgerblue; --primary: dodgerblue;
--fg: #fff; --fg: #fff;
--bg: #333; --bg: #333;
--font-size-large: 112.5%;
--icon-spacing: 4px;
} }

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16,13C15.71,13 15.38,13 15.03,13.05C16.19,13.89 17,15 17,16.5V19H23V16.5C23,14.17 18.33,13 16,13M8,13C5.67,13 1,14.17 1,16.5V19H15V16.5C15,14.17 10.33,13 8,13M8,11A3,3 0 0,0 11,8A3,3 0 0,0 8,5A3,3 0 0,0 5,8A3,3 0 0,0 8,11M16,11A3,3 0 0,0 19,8A3,3 0 0,0 16,5A3,3 0 0,0 13,8A3,3 0 0,0 16,11Z" /></svg>

After

Width:  |  Height:  |  Size: 585 B

1
app/icons/album.svg Normal file
View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,11A1,1 0 0,0 11,12A1,1 0 0,0 12,13A1,1 0 0,0 13,12A1,1 0 0,0 12,11M12,16.5C9.5,16.5 7.5,14.5 7.5,12C7.5,9.5 9.5,7.5 12,7.5C14.5,7.5 16.5,9.5 16.5,12C16.5,14.5 14.5,16.5 12,16.5M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" /></svg>

After

Width:  |  Height:  |  Size: 551 B

1
app/icons/artist.svg Normal file
View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M11,14C12,14 13.05,14.16 14.2,14.44C13.39,15.31 13,16.33 13,17.5C13,18.39 13.25,19.23 13.78,20H3V18C3,16.81 3.91,15.85 5.74,15.12C7.57,14.38 9.33,14 11,14M11,12C9.92,12 9,11.61 8.18,10.83C7.38,10.05 7,9.11 7,8C7,6.92 7.38,6 8.18,5.18C9,4.38 9.92,4 11,4C12.11,4 13.05,4.38 13.83,5.18C14.61,6 15,6.92 15,8C15,9.11 14.61,10.05 13.83,10.83C13.05,11.61 12.11,12 11,12M18.5,10H20L22,10V12H20V17.5A2.5,2.5 0 0,1 17.5,20A2.5,2.5 0 0,1 15,17.5A2.5,2.5 0 0,1 17.5,15C17.86,15 18.19,15.07 18.5,15.21V10Z" /></svg>

After

Width:  |  Height:  |  Size: 787 B

View file

@ -12,7 +12,7 @@
<span class="art"></span> <span class="art"></span>
<div class="info"> <div class="info">
<h2 class="title"></h2> <h2 class="title"></h2>
<span class="artist-album"></span> <div class="subtitle"></div>
</div> </div>
<div class="controls"> <div class="controls">
<button class="prev" data-icon="rewind"></button> <button class="prev" data-icon="rewind"></button>

View file

@ -1,3 +1,5 @@
const SEPARATOR = " · ";
export function time(sec) { export function time(sec) {
sec = Math.round(sec); sec = Math.round(sec);
let m = Math.floor(sec / 60); let m = Math.floor(sec / 60);
@ -5,9 +7,10 @@ export function time(sec) {
return `${m}:${s.toString().padStart(2, "0")}`; return `${m}:${s.toString().padStart(2, "0")}`;
} }
export function artistAlbum(artist, album) { export function subtitle(data) {
let tokens = []; let tokens = [];
artist && tokens.push(artist); data["Artist"] && tokens.push(data["Artist"]);
album && tokens.push(album); data["Album"] && tokens.push(data["Album"]);
return tokens.join(" "); data["duration"] && tokens.push(time(Number(data["duration"])));
return tokens.join(SEPARATOR);
} }

View file

@ -17,6 +17,9 @@ ICONS["play"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www
ICONS["play-circle-outline"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24"> ICONS["play-circle-outline"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24">
<path d="M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M10,16.5L16,12L10,7.5V16.5Z"/> <path d="M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M10,16.5L16,12L10,7.5V16.5Z"/>
</svg>`; </svg>`;
ICONS["album"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24">
<path d="M12,11A1,1 0 0,0 11,12A1,1 0 0,0 12,13A1,1 0 0,0 13,12A1,1 0 0,0 12,11M12,16.5C9.5,16.5 7.5,14.5 7.5,12C7.5,9.5 9.5,7.5 12,7.5C14.5,7.5 16.5,9.5 16.5,12C16.5,14.5 14.5,16.5 12,16.5M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"/>
</svg>`;
ICONS["plus"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24"> ICONS["plus"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24">
<path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"/> <path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"/>
</svg>`; </svg>`;
@ -32,6 +35,9 @@ ICONS["shuffle"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://
ICONS["content-save"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24"> ICONS["content-save"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24">
<path d="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z"/> <path d="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z"/>
</svg>`; </svg>`;
ICONS["account-multiple"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24">
<path d="M16,13C15.71,13 15.38,13 15.03,13.05C16.19,13.89 17,15 17,16.5V19H23V16.5C23,14.17 18.33,13 16,13M8,13C5.67,13 1,14.17 1,16.5V19H15V16.5C15,14.17 10.33,13 8,13M8,11A3,3 0 0,0 11,8A3,3 0 0,0 8,5A3,3 0 0,0 5,8A3,3 0 0,0 8,11M16,11A3,3 0 0,0 19,8A3,3 0 0,0 16,5A3,3 0 0,0 13,8A3,3 0 0,0 16,11Z"/>
</svg>`;
ICONS["fast-forward"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24"> ICONS["fast-forward"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24">
<path d="M13,6V18L21.5,12M4,18L12.5,12L4,6V18Z"/> <path d="M13,6V18L21.5,12M4,18L12.5,12L4,6V18Z"/>
</svg>`; </svg>`;
@ -77,6 +83,9 @@ ICONS["plus-circle-outline"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl
ICONS["download"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24"> ICONS["download"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24">
<path d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z"/> <path d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z"/>
</svg>`; </svg>`;
ICONS["artist"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24">
<path d="M11,14C12,14 13.05,14.16 14.2,14.44C13.39,15.31 13,16.33 13,17.5C13,18.39 13.25,19.23 13.78,20H3V18C3,16.81 3.91,15.85 5.74,15.12C7.57,14.38 9.33,14 11,14M11,12C9.92,12 9,11.61 8.18,10.83C7.38,10.05 7,9.11 7,8C7,6.92 7.38,6 8.18,5.18C9,4.38 9.92,4 11,4C12.11,4 13.05,4.38 13.83,5.18C14.61,6 15,6.92 15,8C15,9.11 14.61,10.05 13.83,10.83C13.05,11.61 12.11,12 11,12M18.5,10H20L22,10V12H20V17.5A2.5,2.5 0 0,1 17.5,20A2.5,2.5 0 0,1 15,17.5A2.5,2.5 0 0,1 17.5,15C17.86,15 18.19,15.07 18.5,15.21V10Z"/>
</svg>`;
ICONS["play-circle"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24"> ICONS["play-circle"] = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24">
<path d="M10,16.5V7.5L16,12M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"/> <path d="M10,16.5V7.5L16,12M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"/>
</svg>`; </svg>`;

View file

@ -1,5 +1,7 @@
import * as html from "./html.js"; import * as html from "./html.js";
const OPEN = "open";
export function normalize(str) { export function normalize(str) {
// FIXME diac/translit // FIXME diac/translit
return str.toLowerCase(); return str.toLowerCase();
@ -8,13 +10,27 @@ export function normalize(str) {
export default class Search extends EventTarget { export default class Search extends EventTarget {
constructor(parent) { constructor(parent) {
super(); super();
this._node = html.node("div", {className:"search"}); this._node = html.node("label", {className:"search"});
let icon = html.icon("magnify", this._node);
this._input = html.node("input", {type:"text"}, "", this._node);
html.icon("magnify", this._node);
this._node.addEventListener("click", e => {
if (e.target == this._input) { return; }
if (this._node.classList.contains(OPEN)) {
this.reset()
} else {
this._node.classList.add(OPEN);
}
});
} }
getNode() { return this._node; } getNode() { return this._node; }
getValue() { return this._input.value; } getValue() { return this._input.value; }
reset() { this._input.value = ""; } reset() {
this._input.value = "";
this._node.classList.remove(OPEN);
}
} }

View file

@ -42,7 +42,7 @@ async function fillArt(parent, filter) {
if (src) { if (src) {
html.node("img", {src}, "", parent); html.node("img", {src}, "", parent);
} else { } else {
html.icon("music", parent); html.icon(album ? "album" : "artist", parent);
} }
} }
@ -50,25 +50,29 @@ function fileName(data) {
return data["file"].split("/").pop(); return data["file"].split("/").pop();
} }
function formatTitle(ctx, data) { function formatSongInfo(ctx, data) {
let lines = [];
let tokens = []; let tokens = [];
switch (ctx) { switch (ctx) {
case CTX_FS: return fileName(data); break; case CTX_FS: lines.push(fileName(data)); break;
case CTX_LIBRARY: case CTX_LIBRARY:
data["Track"] && tokens.push(data["Track"].padStart(2, "0"));
data["Title"] && tokens.push(data["Title"]);
if (!tokens.length) {tokens.push(fileName(data)); }
return tokens.join(" ");
break;
case CTX_QUEUE: case CTX_QUEUE:
data["Artist"] && tokens.push(data["Artist"]); if (data["Title"]) {
data["Title"] && tokens.push(data["Title"]); if (ctx == CTX_LIBRARY && data["Track"]) {
if (!tokens.length) { tokens.push(fileName(data)); } tokens.push(data["Track"].padStart(2, "0"));
return tokens.join(" - "); }
tokens.push(data["Title"]);
lines.push(tokens.join(" "));
lines.push(format.subtitle(data));
} else {
lines.push(fileName(data));
lines.push("\u00A0");
}
break; break;
} }
return lines;
} }
function playButton(type, what, parent) { function playButton(type, what, parent) {
@ -128,13 +132,14 @@ function addButton(type, what, parent) {
export function song(ctx, data, parent) { export function song(ctx, data, parent) {
let node = html.node("li", {className:"song"}, "", parent); let node = html.node("li", {className:"song"}, "", parent);
let info = html.node("div", {className:"info"}, "", node);
let title = formatTitle(ctx, data); if (ctx == CTX_FS) { html.icon("music", info); }
let h2 = html.node("h2", {}, "", node);
if (ctx == CTX_FS || ctx == CTX_LIBRARY) { html.icon("music", h2); } let lines = formatSongInfo(ctx, data);
html.text(title, h2); html.node("h2", {}, lines.shift(), info);
lines.length && html.node("div", {}, lines.shift(), info);
html.node("span", {className:"duration"}, format.time(Number(data["duration"])), node);
switch (ctx) { switch (ctx) {
case CTX_QUEUE: case CTX_QUEUE:
@ -159,14 +164,14 @@ export function group(ctx, label, urlOrFilter, parent) {
let node = html.node("li", {className:"group"}, "", parent); let node = html.node("li", {className:"group"}, "", parent);
if (ctx == CTX_LIBRARY) { if (ctx == CTX_LIBRARY) {
node.classList.add("has-art");
let art = html.node("span", {className:"art"}, "", node); let art = html.node("span", {className:"art"}, "", node);
fillArt(art, urlOrFilter); fillArt(art, urlOrFilter);
} }
let h2 = html.node("h2", {}, "", node); let info = html.node("span", {className:"info"}, "", node);
if (ctx == CTX_FS) { html.icon("folder", h2); } if (ctx == CTX_FS) { html.icon("folder", info); }
html.text(label, h2); html.node("h2", {}, label, info);
let type = (ctx == CTX_FS ? TYPE_URL : TYPE_FILTER); let type = (ctx == CTX_FS ? TYPE_URL : TYPE_FILTER);
@ -179,9 +184,9 @@ export function group(ctx, label, urlOrFilter, parent) {
export function playlist(name, parent) { export function playlist(name, parent) {
let node = html.node("li", {}, "", parent); let node = html.node("li", {}, "", parent);
let h2 = html.node("h2", {}, "", node); let info = html.node("span", {className:"info"}, "", node);
html.icon("playlist-music", h2) html.icon("playlist-music", info)
html.text(name, h2); html.node("h2", {}, name, info);
playButton(TYPE_PLAYLIST, name, node); playButton(TYPE_PLAYLIST, name, node);
addButton(TYPE_PLAYLIST, name, node); addButton(TYPE_PLAYLIST, name, node);

View file

@ -18,11 +18,11 @@ function sync(data) {
if (data["file"]) { // playing at all? if (data["file"]) { // playing at all?
DOM.duration.textContent = format.time(Number(data["duration"] || 0)); DOM.duration.textContent = format.time(Number(data["duration"] || 0));
DOM.title.textContent = data["Title"] || data["file"].split("/").pop(); DOM.title.textContent = data["Title"] || data["file"].split("/").pop();
DOM["artist-album"].textContent = format.artistAlbum(data["Artist"], data["Album"]); DOM.subtitle.textContent = format.subtitle(data);
} else { } else {
DOM.duration.textContent = ""; DOM.duration.textContent = "";
DOM.title.textContent = ""; DOM.title.textContent = "";
DOM["artist-album"].textContent = ""; DOM.subtitle.textContent = "";
} }
pubsub.publish("song-change", null, data); pubsub.publish("song-change", null, data);