flex, search
This commit is contained in:
parent
7a5dfc9b16
commit
127a450791
19 changed files with 348 additions and 139 deletions
242
app/app.css
242
app/app.css
|
@ -9,7 +9,7 @@ html {
|
|||
body {
|
||||
box-sizing: border-box;
|
||||
font-family: lato, sans-serif;
|
||||
line-height: 1;
|
||||
line-height: 1.25;
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8);
|
||||
|
@ -28,6 +28,7 @@ input,
|
|||
select,
|
||||
button {
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
}
|
||||
button {
|
||||
-webkit-appearance: none;
|
||||
|
@ -43,6 +44,19 @@ button {
|
|||
.art img {
|
||||
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-family: 'Lato';
|
||||
src: url('font/LatoLatin-Regular.woff2') format('woff2');
|
||||
|
@ -104,8 +118,19 @@ nav ul li.active {
|
|||
#player:not([data-flags~=repeat]) .repeat {
|
||||
opacity: 0.5;
|
||||
}
|
||||
#player .art {
|
||||
margin-right: var(--icon-spacing);
|
||||
}
|
||||
#player h2 {
|
||||
font-size: 125%;
|
||||
margin-top: 0;
|
||||
}
|
||||
#player .info {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
#player .title,
|
||||
#player .subtitle {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
@ -115,15 +140,17 @@ nav ul li.active {
|
|||
text-align: center;
|
||||
}
|
||||
#player .controls .icon {
|
||||
width: 64px;
|
||||
width: 32px;
|
||||
margin: 8px;
|
||||
}
|
||||
#player .misc {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 48px;
|
||||
align-self: stretch;
|
||||
justify-content: space-around;
|
||||
}
|
||||
#player .misc .icon {
|
||||
width: 48px;
|
||||
width: 32px;
|
||||
}
|
||||
.component {
|
||||
height: 100%;
|
||||
|
@ -141,29 +168,34 @@ nav ul li.active {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.component h2 {
|
||||
.component li .info {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
}
|
||||
.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-left: 4px;
|
||||
}
|
||||
.component li .info h2,
|
||||
.component li .info div {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.component h2 .icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
.component li:not(.has-art) {
|
||||
padding: 8px;
|
||||
}
|
||||
.component li:nth-child(odd) {
|
||||
background-color: #555;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
.component .icon {
|
||||
.component button .icon {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
#queue {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
@ -180,31 +212,36 @@ nav ul li.active {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#queue h2 {
|
||||
#queue li .info {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
}
|
||||
#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-left: 4px;
|
||||
}
|
||||
#queue li .info h2,
|
||||
#queue li .info div {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#queue h2 .icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
#queue li:not(.has-art) {
|
||||
padding: 8px;
|
||||
}
|
||||
#queue li:nth-child(odd) {
|
||||
background-color: #555;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
#queue .icon {
|
||||
#queue button .icon {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
#queue .current * {
|
||||
font-weight: bold !important;
|
||||
#queue .current {
|
||||
color: var(--primary);
|
||||
}
|
||||
#library {
|
||||
height: 100%;
|
||||
|
@ -222,31 +259,44 @@ nav ul li.active {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#library h2 {
|
||||
#library li .info {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
}
|
||||
#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-left: 4px;
|
||||
}
|
||||
#library li .info h2,
|
||||
#library li .info div {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#library h2 .icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
#library li:not(.has-art) {
|
||||
padding: 8px;
|
||||
}
|
||||
#library li:nth-child(odd) {
|
||||
background-color: #555;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
#library .icon {
|
||||
#library button .icon {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
#library .art img {
|
||||
#library .art img,
|
||||
#library .art .icon {
|
||||
width: 64px;
|
||||
margin-right: var(--icon-spacing);
|
||||
}
|
||||
#library .group {
|
||||
cursor: pointer;
|
||||
}
|
||||
#library .group h2 {
|
||||
font-weight: normal;
|
||||
}
|
||||
#library .tiles {
|
||||
display: grid;
|
||||
|
@ -279,32 +329,45 @@ nav ul li.active {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#fs h2 {
|
||||
#fs li .info {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
}
|
||||
#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-left: 4px;
|
||||
}
|
||||
#fs li .info h2,
|
||||
#fs li .info div {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#fs h2 .icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
#fs li:not(.has-art) {
|
||||
padding: 8px;
|
||||
}
|
||||
#fs li:nth-child(odd) {
|
||||
background-color: #555;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
#fs .icon {
|
||||
#fs button .icon {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
#fs .group {
|
||||
cursor: pointer;
|
||||
}
|
||||
#fs .info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
#fs .info h2 {
|
||||
font-weight: normal;
|
||||
}
|
||||
#playlists {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
@ -321,28 +384,41 @@ nav ul li.active {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#playlists h2 {
|
||||
#playlists li .info {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
}
|
||||
#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-left: 4px;
|
||||
}
|
||||
#playlists li .info h2,
|
||||
#playlists li .info div {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#playlists h2 .icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
#playlists li:not(.has-art) {
|
||||
padding: 8px;
|
||||
}
|
||||
#playlists li:nth-child(odd) {
|
||||
background-color: #555;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
#playlists .icon {
|
||||
#playlists button .icon {
|
||||
width: 32px;
|
||||
}
|
||||
#playlists .info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
#playlists .info h2 {
|
||||
font-weight: normal;
|
||||
}
|
||||
#yt {
|
||||
height: 100%;
|
||||
|
@ -362,29 +438,34 @@ nav ul li.active {
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#yt h2 {
|
||||
#yt li .info {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
}
|
||||
#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-left: 4px;
|
||||
}
|
||||
#yt li .info h2,
|
||||
#yt li .info div {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#yt h2 .icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
#yt li:not(.has-art) {
|
||||
padding: 8px;
|
||||
}
|
||||
#yt li:nth-child(odd) {
|
||||
background-color: #555;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
#yt .icon {
|
||||
#yt button .icon {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
#yt .go {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
|
@ -416,8 +497,25 @@ nav ul li.active {
|
|||
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 {
|
||||
--primary: dodgerblue;
|
||||
--fg: #fff;
|
||||
--bg: #333;
|
||||
--font-size-large: 112.5%;
|
||||
--icon-spacing: 4px;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ html {
|
|||
body {
|
||||
box-sizing: border-box;
|
||||
font-family: lato, sans-serif;
|
||||
line-height: 1;
|
||||
line-height: 1.25;
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8);
|
||||
|
@ -25,6 +25,7 @@ body {
|
|||
|
||||
input, select, button {
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
button {
|
||||
|
@ -44,6 +45,20 @@ button {
|
|||
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 "icons.less";
|
||||
@import "main.less";
|
||||
|
@ -55,4 +70,5 @@ button {
|
|||
@import "fs.less";
|
||||
@import "playlists.less";
|
||||
@import "yt.less";
|
||||
@import "search.less";
|
||||
@import "variables.less";
|
||||
|
|
|
@ -15,21 +15,30 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
|
||||
.info {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.icon {
|
||||
color: var(--primary);
|
||||
margin-right: var(--icon-spacing);
|
||||
}
|
||||
|
||||
h2 {
|
||||
flex-grow: 1;
|
||||
font-size: 100%;
|
||||
font-weight: normal;
|
||||
font-size: var(--font-size-large);
|
||||
margin: 0;
|
||||
margin-left: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 4px;
|
||||
color: var(--primary);
|
||||
h2, div { .long-line; }
|
||||
}
|
||||
|
||||
&.has-art {
|
||||
|
||||
}
|
||||
|
||||
&:not(.has-art) {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +46,6 @@
|
|||
background-color: #555;
|
||||
}
|
||||
|
||||
@media (pointer: coarse) {
|
||||
.icon { width: 32px; }
|
||||
}
|
||||
button .icon { width: 32px; }
|
||||
|
||||
}
|
||||
|
|
|
@ -4,4 +4,8 @@
|
|||
.group {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.info {
|
||||
.multiline;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
#library {
|
||||
.component;
|
||||
|
||||
.art img {
|
||||
.art img, .art .icon {
|
||||
width: 64px;
|
||||
margin-right: var(--icon-spacing);
|
||||
}
|
||||
|
||||
.group {
|
||||
cursor: pointer;
|
||||
h2 { font-weight: normal; }
|
||||
}
|
||||
|
||||
.tiles {
|
||||
|
|
|
@ -7,25 +7,37 @@
|
|||
&[data-state=play] .play { display: none; }
|
||||
&: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 {
|
||||
flex-grow: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.title, .subtitle { .long-line; }
|
||||
|
||||
.controls {
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
.icon { width: 64px; }
|
||||
.icon {
|
||||
width: 32px;
|
||||
margin: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.misc {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@size: 96px / 2;
|
||||
width: @size;
|
||||
.icon { width: @size; }
|
||||
align-self: stretch;
|
||||
justify-content: space-around;
|
||||
.icon { width: 32px; }
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
#playlists {
|
||||
.component;
|
||||
|
||||
.info {
|
||||
.multiline;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#queue {
|
||||
.component;
|
||||
.current * { font-weight: bold !important; }
|
||||
|
||||
.current { color: var(--primary); }
|
||||
}
|
||||
|
|
21
app/css/search.less
Normal file
21
app/css/search.less
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,4 +2,7 @@
|
|||
--primary: dodgerblue;
|
||||
--fg: #fff;
|
||||
--bg: #333;
|
||||
|
||||
--font-size-large: 112.5%;
|
||||
--icon-spacing: 4px;
|
||||
}
|
||||
|
|
1
app/icons/account-multiple.svg
Normal file
1
app/icons/account-multiple.svg
Normal 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
1
app/icons/album.svg
Normal 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
1
app/icons/artist.svg
Normal 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 |
|
@ -12,7 +12,7 @@
|
|||
<span class="art"></span>
|
||||
<div class="info">
|
||||
<h2 class="title"></h2>
|
||||
<span class="artist-album"></span>
|
||||
<div class="subtitle"></div>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<button class="prev" data-icon="rewind"></button>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
const SEPARATOR = " · ";
|
||||
|
||||
export function time(sec) {
|
||||
sec = Math.round(sec);
|
||||
let m = Math.floor(sec / 60);
|
||||
|
@ -5,9 +7,10 @@ export function time(sec) {
|
|||
return `${m}:${s.toString().padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
export function artistAlbum(artist, album) {
|
||||
export function subtitle(data) {
|
||||
let tokens = [];
|
||||
artist && tokens.push(artist);
|
||||
album && tokens.push(album);
|
||||
return tokens.join(" – ");
|
||||
data["Artist"] && tokens.push(data["Artist"]);
|
||||
data["Album"] && tokens.push(data["Album"]);
|
||||
data["duration"] && tokens.push(time(Number(data["duration"])));
|
||||
return tokens.join(SEPARATOR);
|
||||
}
|
|
@ -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">
|
||||
<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>`;
|
||||
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">
|
||||
<path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"/>
|
||||
</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">
|
||||
<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>`;
|
||||
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">
|
||||
<path d="M13,6V18L21.5,12M4,18L12.5,12L4,6V18Z"/>
|
||||
</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">
|
||||
<path d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z"/>
|
||||
</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">
|
||||
<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>`;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import * as html from "./html.js";
|
||||
|
||||
const OPEN = "open";
|
||||
|
||||
export function normalize(str) {
|
||||
// FIXME diac/translit
|
||||
return str.toLowerCase();
|
||||
|
@ -8,13 +10,27 @@ export function normalize(str) {
|
|||
export default class Search extends EventTarget {
|
||||
constructor(parent) {
|
||||
super();
|
||||
this._node = html.node("div", {className:"search"});
|
||||
let icon = html.icon("magnify", this._node);
|
||||
this._node = html.node("label", {className:"search"});
|
||||
|
||||
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; }
|
||||
|
||||
getValue() { return this._input.value; }
|
||||
|
||||
reset() { this._input.value = ""; }
|
||||
reset() {
|
||||
this._input.value = "";
|
||||
this._node.classList.remove(OPEN);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ async function fillArt(parent, filter) {
|
|||
if (src) {
|
||||
html.node("img", {src}, "", parent);
|
||||
} else {
|
||||
html.icon("music", parent);
|
||||
html.icon(album ? "album" : "artist", parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,25 +50,29 @@ function fileName(data) {
|
|||
return data["file"].split("/").pop();
|
||||
}
|
||||
|
||||
function formatTitle(ctx, data) {
|
||||
function formatSongInfo(ctx, data) {
|
||||
let lines = [];
|
||||
let tokens = [];
|
||||
switch (ctx) {
|
||||
case CTX_FS: return fileName(data); break;
|
||||
case CTX_FS: lines.push(fileName(data)); break;
|
||||
|
||||
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:
|
||||
data["Artist"] && tokens.push(data["Artist"]);
|
||||
data["Title"] && tokens.push(data["Title"]);
|
||||
if (!tokens.length) { tokens.push(fileName(data)); }
|
||||
return tokens.join(" - ");
|
||||
if (data["Title"]) {
|
||||
if (ctx == CTX_LIBRARY && data["Track"]) {
|
||||
tokens.push(data["Track"].padStart(2, "0"));
|
||||
}
|
||||
tokens.push(data["Title"]);
|
||||
lines.push(tokens.join(" "));
|
||||
lines.push(format.subtitle(data));
|
||||
} else {
|
||||
lines.push(fileName(data));
|
||||
lines.push("\u00A0");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
function playButton(type, what, parent) {
|
||||
|
@ -128,13 +132,14 @@ function addButton(type, what, parent) {
|
|||
|
||||
export function song(ctx, data, parent) {
|
||||
let node = html.node("li", {className:"song"}, "", parent);
|
||||
let info = html.node("div", {className:"info"}, "", node);
|
||||
|
||||
let title = formatTitle(ctx, data);
|
||||
let h2 = html.node("h2", {}, "", node);
|
||||
if (ctx == CTX_FS || ctx == CTX_LIBRARY) { html.icon("music", h2); }
|
||||
html.text(title, h2);
|
||||
if (ctx == CTX_FS) { html.icon("music", info); }
|
||||
|
||||
let lines = formatSongInfo(ctx, data);
|
||||
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) {
|
||||
case CTX_QUEUE:
|
||||
|
@ -159,14 +164,14 @@ export function group(ctx, label, urlOrFilter, parent) {
|
|||
let node = html.node("li", {className:"group"}, "", parent);
|
||||
|
||||
if (ctx == CTX_LIBRARY) {
|
||||
node.classList.add("has-art");
|
||||
let art = html.node("span", {className:"art"}, "", node);
|
||||
fillArt(art, urlOrFilter);
|
||||
}
|
||||
|
||||
let h2 = html.node("h2", {}, "", node);
|
||||
if (ctx == CTX_FS) { html.icon("folder", h2); }
|
||||
html.text(label, h2);
|
||||
|
||||
let info = html.node("span", {className:"info"}, "", node);
|
||||
if (ctx == CTX_FS) { html.icon("folder", info); }
|
||||
html.node("h2", {}, label, info);
|
||||
|
||||
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) {
|
||||
let node = html.node("li", {}, "", parent);
|
||||
|
||||
let h2 = html.node("h2", {}, "", node);
|
||||
html.icon("playlist-music", h2)
|
||||
html.text(name, h2);
|
||||
let info = html.node("span", {className:"info"}, "", node);
|
||||
html.icon("playlist-music", info)
|
||||
html.node("h2", {}, name, info);
|
||||
|
||||
playButton(TYPE_PLAYLIST, name, node);
|
||||
addButton(TYPE_PLAYLIST, name, node);
|
||||
|
|
|
@ -18,11 +18,11 @@ function sync(data) {
|
|||
if (data["file"]) { // playing at all?
|
||||
DOM.duration.textContent = format.time(Number(data["duration"] || 0));
|
||||
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 {
|
||||
DOM.duration.textContent = "";
|
||||
DOM.title.textContent = "";
|
||||
DOM["artist-album"].textContent = "";
|
||||
DOM.subtitle.textContent = "";
|
||||
}
|
||||
|
||||
pubsub.publish("song-change", null, data);
|
||||
|
|
Loading…
Reference in a new issue