item mixin, playlists

This commit is contained in:
Ondrej Zara 2020-03-10 10:07:30 +01:00
parent eb195a171e
commit 4ab17b2c96
No known key found for this signature in database
GPG key ID: B0A5751E616840C5
22 changed files with 158 additions and 231 deletions

View file

@ -1,7 +1,7 @@
LESS := $(shell npm bin)/lessc LESS := $(shell npm bin)/lessc
APP := app APP := app
CSS := $(APP)/cyp.css CSS := $(APP)/cyp.css
ICONS := $(APP)/js/lib/icons.js ICONS := $(APP)/js/icons.js
SYSD_USER := ~/.config/systemd/user SYSD_USER := ~/.config/systemd/user
SERVICE := cyp.service SERVICE := cyp.service

View file

@ -38,7 +38,7 @@
margin: 0; margin: 0;
} }
h2, div { .long-line; } // h2, div { .long-line; }
} }
&.has-art { &.has-art {

View file

@ -22,7 +22,7 @@ header, footer {
footer { footer {
position: relative; position: relative;
height: 56px; height: 56px;
@media (max-width: 480px) { @media (max-width: @breakpoint-menu) {
height: 40px; height: 40px;
} }
} }

View file

@ -9,7 +9,7 @@ cyp-menu, cyp-commands {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@media (max-width: 480px) { @media (max-width: @breakpoint-menu) {
flex-direction: row; flex-direction: row;
span:not([id]) { display: none; } span:not([id]) { display: none; }
} }
@ -47,7 +47,7 @@ cyp-commands {
} }
button { button {
flex: 0 0 80px; flex: 0 0 @breakpoint-menu/6;
&.last { &.last {
order: 1; order: 1;
margin-left: auto; margin-left: auto;

View file

@ -34,7 +34,7 @@ cyp-player {
margin: 0; margin: 0;
} }
.title, .subtitle { .long-line; } .title, .subtitle { .no-wrap; }
} }
.timeline { .timeline {

View file

@ -1,7 +1,5 @@
cyp-playlist { cyp-playlist {
.flex-row; .item;
padding: 8px;
&:nth-child(odd) { &:nth-child(odd) {
background-color: var(--bg-alt); background-color: var(--bg-alt);

View file

@ -1,7 +1,2 @@
cyp-playlists { cyp-playlists {
.component;
.info {
.multiline;
}
} }

View file

@ -1,6 +1,5 @@
cyp-song { cyp-song {
.selectable; .item;
.flex-row;
.info { // FIXME zrevidovat .info { // FIXME zrevidovat
flex-grow: 1; flex-grow: 1;
@ -17,14 +16,6 @@ cyp-song {
margin: 0; margin: 0;
} }
h2, div { .long-line; } // h2, div { .long-line; } FIXME vyresit zalamovani/vypustku
}
&:nth-child(odd) {
background-color: var(--bg-alt);
}
&:not(.has-art) {
padding: 8px;
} }
} }

View file

@ -15,6 +15,6 @@
} }
.info { .info {
.multiline; // .multiline; FIXME
} }
} }

View file

@ -9,17 +9,19 @@
flex-direction: column; flex-direction: column;
} }
.long-line { .no-wrap {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
/*
.multiline { .multiline {
.flex-row; .flex-row;
h2 { font-weight: normal; } h2 { font-weight: normal; }
} }
*/
.selectable { .selectable {
border-left: 4px solid transparent; border-left: 4px solid transparent;
@ -28,3 +30,13 @@
border-left-color: var(--primary); border-left-color: var(--primary);
} }
} }
.item {
.flex-row;
.selectable;
padding: 8px;
&:nth-child(odd) {
background-color: var(--bg-alt);
}
}

View file

@ -1,3 +1,5 @@
@breakpoint-menu: 480px;
cyp-app { cyp-app {
--font-size-large: 112.5%; --font-size-large: 112.5%;
--icon-spacing: 4px; --icon-spacing: 4px;
@ -43,7 +45,7 @@ cyp-app[color=limegreen] {
--primary-raw: 50, 205, 50; --primary-raw: 50, 205, 50;
} }
@media (max-width: 480px) { @media (max-width: @breakpoint-menu) {
:root { :root {
--spacing: var(--icon-spacing); --spacing: var(--icon-spacing);
} }

View file

@ -91,27 +91,39 @@ select {
.flex-column:not([hidden]) { .flex-column:not([hidden]) {
display: flex; display: flex;
} }
.long-line { .no-wrap {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
/*
.multiline { .multiline {
flex-direction: row; .flex-row;
align-items: center;
} h2 { font-weight: normal; }
.multiline:not([hidden]) {
display: flex;
}
.multiline h2 {
font-weight: normal;
} }
*/
.selectable { .selectable {
border-left: 4px solid transparent; border-left: 4px solid transparent;
} }
.selectable.selected { .selectable.selected {
border-left-color: var(--primary); border-left-color: var(--primary);
} }
.item {
flex-direction: row;
align-items: center;
border-left: 4px solid transparent;
padding: 8px;
}
.item:not([hidden]) {
display: flex;
}
.item.selected {
border-left-color: var(--primary);
}
.item:nth-child(odd) {
background-color: var(--bg-alt);
}
.component header { .component header {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
@ -155,12 +167,6 @@ select {
font-size: var(--font-size-large); font-size: var(--font-size-large);
margin: 0; margin: 0;
} }
.component li .info h2,
.component li .info div {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.component li:not(.has-art) { .component li:not(.has-art) {
padding: 8px; padding: 8px;
} }
@ -213,12 +219,6 @@ select {
font-size: var(--font-size-large); font-size: var(--font-size-large);
margin: 0; margin: 0;
} }
#library li .info h2,
#library li .info div {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#library li:not(.has-art) { #library li:not(.has-art) {
padding: 8px; padding: 8px;
} }
@ -308,12 +308,6 @@ select {
font-size: var(--font-size-large); font-size: var(--font-size-large);
margin: 0; margin: 0;
} }
#fs li .info h2,
#fs li .info div {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#fs li:not(.has-art) { #fs li:not(.has-art) {
padding: 8px; padding: 8px;
} }
@ -335,16 +329,6 @@ select {
#fs .group { #fs .group {
cursor: pointer; cursor: pointer;
} }
#fs .info {
flex-direction: row;
align-items: center;
}
#fs .info:not([hidden]) {
display: flex;
}
#fs .info h2 {
font-weight: normal;
}
.search { .search {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
@ -509,15 +493,19 @@ cyp-commands button.last {
margin-left: auto; margin-left: auto;
} }
cyp-song { cyp-song {
border-left: 4px solid transparent;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
border-left: 4px solid transparent;
padding: 8px;
}
cyp-song:not([hidden]) {
display: flex;
} }
cyp-song.selected { cyp-song.selected {
border-left-color: var(--primary); border-left-color: var(--primary);
} }
cyp-song:not([hidden]) { cyp-song:nth-child(odd) {
display: flex; background-color: var(--bg-alt);
} }
cyp-song .info { cyp-song .info {
flex-grow: 1; flex-grow: 1;
@ -532,18 +520,6 @@ cyp-song .info h2 {
font-size: var(--font-size-large); font-size: var(--font-size-large);
margin: 0; margin: 0;
} }
cyp-song .info h2,
cyp-song .info div {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
cyp-song:nth-child(odd) {
background-color: var(--bg-alt);
}
cyp-song:not(.has-art) {
padding: 8px;
}
cyp-player { cyp-player {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
@ -662,74 +638,6 @@ cyp-player .misc .icon {
height: 96px; height: 96px;
} }
} }
cyp-playlists header {
flex-direction: row;
align-items: center;
padding: var(--spacing);
}
cyp-playlists header:not([hidden]) {
display: flex;
}
cyp-playlists header button {
font-size: var(--font-size-large);
font-weight: bold;
overflow: hidden;
}
cyp-playlists header button .icon {
margin-right: var(--icon-spacing);
}
cyp-playlists ul {
flex-grow: 1;
overflow: auto;
list-style: none;
margin: 0;
padding: 0;
}
cyp-playlists li {
flex-direction: row;
align-items: center;
}
cyp-playlists li:not([hidden]) {
display: flex;
}
cyp-playlists li .info {
flex-grow: 1;
overflow: hidden;
}
cyp-playlists li .info .icon {
color: var(--primary);
margin-right: var(--icon-spacing);
filter: drop-shadow(var(--text-shadow));
}
cyp-playlists li .info h2 {
font-size: var(--font-size-large);
margin: 0;
}
cyp-playlists li .info h2,
cyp-playlists li .info div {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
cyp-playlists li:not(.has-art) {
padding: 8px;
}
cyp-playlists li button .icon {
width: 32px;
}
cyp-playlists li:nth-child(odd) {
background-color: var(--bg-alt);
}
cyp-playlists .info {
flex-direction: row;
align-items: center;
}
cyp-playlists .info:not([hidden]) {
display: flex;
}
cyp-playlists .info h2 {
font-weight: normal;
}
cyp-queue .current { cyp-queue .current {
color: var(--primary); color: var(--primary);
} }
@ -808,12 +716,6 @@ cyp-yt li .info h2 {
font-size: var(--font-size-large); font-size: var(--font-size-large);
margin: 0; margin: 0;
} }
cyp-yt li .info h2,
cyp-yt li .info div {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
cyp-yt li:not(.has-art) { cyp-yt li:not(.has-art) {
padding: 8px; padding: 8px;
} }
@ -917,11 +819,18 @@ x-range:not([disabled]) .-thumb:hover {
cyp-playlist { cyp-playlist {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
border-left: 4px solid transparent;
padding: 8px; padding: 8px;
} }
cyp-playlist:not([hidden]) { cyp-playlist:not([hidden]) {
display: flex; display: flex;
} }
cyp-playlist.selected {
border-left-color: var(--primary);
}
cyp-playlist:nth-child(odd) {
background-color: var(--bg-alt);
}
cyp-playlist:nth-child(odd) { cyp-playlist:nth-child(odd) {
background-color: var(--bg-alt); background-color: var(--bg-alt);
} }

1
app/icons/cancel.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,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12C4,13.85 4.63,15.55 5.68,16.91L16.91,5.68C15.55,4.63 13.85,4 12,4M12,20A8,8 0 0,0 20,12C20,10.15 19.37,8.45 18.32,7.09L7.09,18.32C8.45,19.37 10.15,20 12,20Z" /></svg>

After

Width:  |  Height:  |  Size: 546 B

View file

@ -13,9 +13,9 @@ export class Item extends HasApp {
} }
export default class Component extends HasApp { export default class Component extends HasApp {
constructor() { constructor(options) {
super(); super();
this.selection = new Selection(this); if (options.selection) { this.selection = new Selection(this, options.selection); }
} }
connectedCallback() { connectedCallback() {

View file

@ -2,7 +2,7 @@ import Component from "../component.js";
class Menu extends Component { class Menu extends Component {
constructor() { constructor() {
super(); super({selection:null});
this._tabs = Array.from(this.querySelectorAll("[data-for]")); this._tabs = Array.from(this.querySelectorAll("[data-for]"));
this._tabs.forEach(tab => { this._tabs.forEach(tab => {
@ -10,19 +10,13 @@ class Menu extends Component {
}); });
} }
async _listen() {
const app = await this._app;
let mo = new MutationObserver(_ => this._sync())
mo.observe(app, {attributes:true});
}
async _activate(component) { async _activate(component) {
const app = await this._app; const app = await this._app;
app.setAttribute("component", component); app.setAttribute("component", component);
} }
_onComponentChange(component) { _onComponentChange(component) {
this._tabs.forEach(tab => { this._tabs.forEach(/** @param {HTMLElement} tab */ tab => {
tab.classList.toggle("active", tab.dataset.for == component); tab.classList.toggle("active", tab.dataset.for == component);
}); });
} }

View file

@ -8,7 +8,7 @@ const DELAY = 1000;
class Player extends Component { class Player extends Component {
constructor() { constructor() {
super(); super({selection:null});
this._current = {}; this._current = {};
this._toggledVolume = 0; this._toggledVolume = 0;
this._idleTimeout = null; this._idleTimeout = null;

View file

@ -10,12 +10,6 @@ export default class Playlist extends Item {
connectedCallback() { connectedCallback() {
html.icon("playlist-music", this) html.icon("playlist-music", this)
html.node("h2", {}, this.name, this); html.node("h2", {}, this.name, this);
/*
playButton(TYPE_PLAYLIST, name, node);
addButton(TYPE_PLAYLIST, name, node);
deleteButton(TYPE_PLAYLIST, name, node);
*/
} }
} }

View file

@ -1,11 +1,14 @@
import * as html from "../html.js"; import * as html from "../html.js";
import * as ui from "../ui.js";
import Component from "../component.js"; import Component from "../component.js";
import Playlist from "./playlist.js"; import Playlist from "./playlist.js";
class Playlists extends Component { class Playlists extends Component {
constructor() {
super({selection:"single"});
this._initCommands();
}
handleEvent(e) { handleEvent(e) {
switch (e.type) { switch (e.type) {
case "playlists-change": case "playlists-change":
@ -30,9 +33,25 @@ class Playlists extends Component {
_buildLists(lists) { _buildLists(lists) {
html.clear(this); html.clear(this);
this.selection.clear();
lists.forEach(name => this.appendChild(new Playlist(name))); lists.forEach(name => this.appendChild(new Playlist(name)));
} }
_initCommands() {
const sel = this.selection;
sel.addCommand(async items => {
}, {label:"Play", icon:"play"});
sel.addCommand(async items => {
}, {label:"Enqueue", icon:"plus"});
sel.addCommand(async items => {
}, {label:"Delete", icon:"delete"});
sel.addCommandCancel();
}
} }
customElements.define("cyp-playlists", Playlists); customElements.define("cyp-playlists", Playlists);

View file

@ -2,9 +2,10 @@ import * as html from "../html.js";
import Component from "../component.js"; import Component from "../component.js";
import Song from "./song.js"; import Song from "./song.js";
class Queue extends Component { class Queue extends Component {
constructor() { constructor() {
super(); super({selection:"multi"});
this._currentId = null; this._currentId = null;
this._initCommands(); this._initCommands();
} }
@ -35,7 +36,6 @@ class Queue extends Component {
} }
async _sync() { async _sync() {
this.selection.clear();
let songs = await this._mpd.listQueue(); let songs = await this._mpd.listQueue();
this._buildSongs(songs); this._buildSongs(songs);
@ -51,6 +51,7 @@ class Queue extends Component {
_buildSongs(songs) { _buildSongs(songs) {
html.clear(this); html.clear(this);
this.selection.clear();
songs.forEach(song => this.appendChild(new Song(song))); songs.forEach(song => this.appendChild(new Song(song)));
@ -84,7 +85,7 @@ class Queue extends Component {
this._sync(); this._sync();
}, {label:"Remove", icon:"delete"}); }, {label:"Remove", icon:"delete"});
sel.addCommandClear(); sel.addCommandCancel();
} }
} }

View file

@ -12,7 +12,7 @@ function saveToStorage(key, value) {
class Settings extends Component { class Settings extends Component {
constructor() { constructor() {
super(); super({selection:null});
this._inputs = { this._inputs = {
theme: this.querySelector("[name=theme]"), theme: this.querySelector("[name=theme]"),
color: Array.from(this.querySelectorAll("[name=color]")) color: Array.from(this.querySelectorAll("[name=color]"))

View file

@ -1,15 +1,33 @@
let ICONS={}; let ICONS={};
ICONS["library-music"] = `<svg viewBox="0 0 24 24"> ICONS["playlist-music"] = `<svg viewBox="0 0 24 24">
<path d="M4,6H2V20A2,2 0 0,0 4,22H18V20H4M18,7H15V12.5A2.5,2.5 0 0,1 12.5,15A2.5,2.5 0 0,1 10,12.5A2.5,2.5 0 0,1 12.5,10C13.07,10 13.58,10.19 14,10.5V5H18M20,2H8A2,2 0 0,0 6,4V16A2,2 0 0,0 8,18H20A2,2 0 0,0 22,16V4A2,2 0 0,0 20,2Z"/> <path d="M15,6H3V8H15V6M15,10H3V12H15V10M3,16H11V14H3V16M17,6V14.18C16.69,14.07 16.35,14 16,14A3,3 0 0,0 13,17A3,3 0 0,0 16,20A3,3 0 0,0 19,17V8H22V6H17Z"/>
</svg>`;
ICONS["plus"] = `<svg viewBox="0 0 24 24">
<path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"/>
</svg>`; </svg>`;
ICONS["folder"] = `<svg viewBox="0 0 24 24"> ICONS["folder"] = `<svg viewBox="0 0 24 24">
<path d="M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z"/> <path d="M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z"/>
</svg>`; </svg>`;
ICONS["playlist-music"] = `<svg viewBox="0 0 24 24"> ICONS["shuffle"] = `<svg viewBox="0 0 24 24">
<path d="M15,6H3V8H15V6M15,10H3V12H15V10M3,16H11V14H3V16M17,6V14.18C16.69,14.07 16.35,14 16,14A3,3 0 0,0 13,17A3,3 0 0,0 16,20A3,3 0 0,0 19,17V8H22V6H17Z"/> <path d="M14.83,13.41L13.42,14.82L16.55,17.95L14.5,20H20V14.5L17.96,16.54L14.83,13.41M14.5,4L16.54,6.04L4,18.59L5.41,20L17.96,7.46L20,9.5V4M10.59,9.17L5.41,4L4,5.41L9.17,10.58L10.59,9.17Z"/>
</svg>`;
ICONS["artist"] = `<svg 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["download"] = `<svg viewBox="0 0 24 24">
<path d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z"/>
</svg>`;
ICONS["magnify"] = `<svg viewBox="0 0 24 24">
<path d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z"/>
</svg>`;
ICONS["delete"] = `<svg viewBox="0 0 24 24">
<path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"/>
</svg>`;
ICONS["rewind"] = `<svg viewBox="0 0 24 24">
<path d="M11.5,12L20,18V6M11,18V6L2.5,12L11,18Z"/>
</svg>`;
ICONS["account-multiple"] = `<svg 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["cancel"] = `<svg viewBox="0 0 24 24">
<path d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12C4,13.85 4.63,15.55 5.68,16.91L16.91,5.68C15.55,4.63 13.85,4 12,4M12,20A8,8 0 0,0 20,12C20,10.15 19.37,8.45 18.32,7.09L7.09,18.32C8.45,19.37 10.15,20 12,20Z"/>
</svg>`; </svg>`;
ICONS["settings"] = `<svg viewBox="0 0 24 24"> ICONS["settings"] = `<svg viewBox="0 0 24 24">
<path d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"/> <path d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"/>
@ -17,55 +35,40 @@ ICONS["settings"] = `<svg viewBox="0 0 24 24">
ICONS["pause"] = `<svg viewBox="0 0 24 24"> ICONS["pause"] = `<svg viewBox="0 0 24 24">
<path d="M14,19H18V5H14M6,19H10V5H6V19Z"/> <path d="M14,19H18V5H14M6,19H10V5H6V19Z"/>
</svg>`; </svg>`;
ICONS["artist"] = `<svg 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["volume-off"] = `<svg viewBox="0 0 24 24"> ICONS["volume-off"] = `<svg viewBox="0 0 24 24">
<path d="M12,4L9.91,6.09L12,8.18M4.27,3L3,4.27L7.73,9H3V15H7L12,20V13.27L16.25,17.53C15.58,18.04 14.83,18.46 14,18.7V20.77C15.38,20.45 16.63,19.82 17.68,18.96L19.73,21L21,19.73L12,10.73M19,12C19,12.94 18.8,13.82 18.46,14.64L19.97,16.15C20.62,14.91 21,13.5 21,12C21,7.72 18,4.14 14,3.23V5.29C16.89,6.15 19,8.83 19,12M16.5,12C16.5,10.23 15.5,8.71 14,7.97V10.18L16.45,12.63C16.5,12.43 16.5,12.21 16.5,12Z"/> <path d="M12,4L9.91,6.09L12,8.18M4.27,3L3,4.27L7.73,9H3V15H7L12,20V13.27L16.25,17.53C15.58,18.04 14.83,18.46 14,18.7V20.77C15.38,20.45 16.63,19.82 17.68,18.96L19.73,21L21,19.73L12,10.73M19,12C19,12.94 18.8,13.82 18.46,14.64L19.97,16.15C20.62,14.91 21,13.5 21,12C21,7.72 18,4.14 14,3.23V5.29C16.89,6.15 19,8.83 19,12M16.5,12C16.5,10.23 15.5,8.71 14,7.97V10.18L16.45,12.63C16.5,12.43 16.5,12.21 16.5,12Z"/>
</svg>`; </svg>`;
ICONS["fast-forward"] = `<svg viewBox="0 0 24 24">
<path d="M13,6V18L21.5,12M4,18L12.5,12L4,6V18Z"/>
</svg>`;
ICONS["delete"] = `<svg viewBox="0 0 24 24">
<path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"/>
</svg>`;
ICONS["volume-high"] = `<svg viewBox="0 0 24 24">
<path d="M14,3.23V5.29C16.89,6.15 19,8.83 19,12C19,15.17 16.89,17.84 14,18.7V20.77C18,19.86 21,16.28 21,12C21,7.72 18,4.14 14,3.23M16.5,12C16.5,10.23 15.5,8.71 14,7.97V16C15.5,15.29 16.5,13.76 16.5,12M3,9V15H7L12,20V4L7,9H3Z"/>
</svg>`;
ICONS["minus"] = `<svg viewBox="0 0 24 24">
<path d="M19,13H5V11H19V13Z"/>
</svg>`;
ICONS["play"] = `<svg viewBox="0 0 24 24">
<path d="M8,5.14V19.14L19,12.14L8,5.14Z"/>
</svg>`;
ICONS["magnify"] = `<svg viewBox="0 0 24 24">
<path d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z"/>
</svg>`;
ICONS["music"] = `<svg viewBox="0 0 24 24">
<path d="M21,3V15.5A3.5,3.5 0 0,1 17.5,19A3.5,3.5 0 0,1 14,15.5A3.5,3.5 0 0,1 17.5,12C18.04,12 18.55,12.12 19,12.34V6.47L9,8.6V17.5A3.5,3.5 0 0,1 5.5,21A3.5,3.5 0 0,1 2,17.5A3.5,3.5 0 0,1 5.5,14C6.04,14 6.55,14.12 7,14.34V6L21,3Z"/>
</svg>`;
ICONS["rewind"] = `<svg viewBox="0 0 24 24">
<path d="M11.5,12L20,18V6M11,18V6L2.5,12L11,18Z"/>
</svg>`;
ICONS["album"] = `<svg 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["download"] = `<svg viewBox="0 0 24 24">
<path d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z"/>
</svg>`;
ICONS["account-multiple"] = `<svg 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["close"] = `<svg viewBox="0 0 24 24"> ICONS["close"] = `<svg viewBox="0 0 24 24">
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/> <path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/>
</svg>`; </svg>`;
ICONS["content-save"] = `<svg viewBox="0 0 24 24"> ICONS["music"] = `<svg 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="M21,3V15.5A3.5,3.5 0 0,1 17.5,19A3.5,3.5 0 0,1 14,15.5A3.5,3.5 0 0,1 17.5,12C18.04,12 18.55,12.12 19,12.34V6.47L9,8.6V17.5A3.5,3.5 0 0,1 5.5,21A3.5,3.5 0 0,1 2,17.5A3.5,3.5 0 0,1 5.5,14C6.04,14 6.55,14.12 7,14.34V6L21,3Z"/>
</svg>`; </svg>`;
ICONS["shuffle"] = `<svg viewBox="0 0 24 24"> ICONS["minus"] = `<svg viewBox="0 0 24 24">
<path d="M14.83,13.41L13.42,14.82L16.55,17.95L14.5,20H20V14.5L17.96,16.54L14.83,13.41M14.5,4L16.54,6.04L4,18.59L5.41,20L17.96,7.46L20,9.5V4M10.59,9.17L5.41,4L4,5.41L9.17,10.58L10.59,9.17Z"/> <path d="M19,13H5V11H19V13Z"/>
</svg>`; </svg>`;
ICONS["repeat"] = `<svg viewBox="0 0 24 24"> ICONS["repeat"] = `<svg viewBox="0 0 24 24">
<path d="M17,17H7V14L3,18L7,22V19H19V13H17M7,7H17V10L21,6L17,2V5H5V11H7V7Z"/> <path d="M17,17H7V14L3,18L7,22V19H19V13H17M7,7H17V10L21,6L17,2V5H5V11H7V7Z"/>
</svg>`; </svg>`;
ICONS["play"] = `<svg viewBox="0 0 24 24">
<path d="M8,5.14V19.14L19,12.14L8,5.14Z"/>
</svg>`;
ICONS["plus"] = `<svg viewBox="0 0 24 24">
<path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"/>
</svg>`;
ICONS["content-save"] = `<svg 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["library-music"] = `<svg viewBox="0 0 24 24">
<path d="M4,6H2V20A2,2 0 0,0 4,22H18V20H4M18,7H15V12.5A2.5,2.5 0 0,1 12.5,15A2.5,2.5 0 0,1 10,12.5A2.5,2.5 0 0,1 12.5,10C13.07,10 13.58,10.19 14,10.5V5H18M20,2H8A2,2 0 0,0 6,4V16A2,2 0 0,0 8,18H20A2,2 0 0,0 22,16V4A2,2 0 0,0 20,2Z"/>
</svg>`;
ICONS["fast-forward"] = `<svg viewBox="0 0 24 24">
<path d="M13,6V18L21.5,12M4,18L12.5,12L4,6V18Z"/>
</svg>`;
ICONS["volume-high"] = `<svg viewBox="0 0 24 24">
<path d="M14,3.23V5.29C16.89,6.15 19,8.83 19,12C19,15.17 16.89,17.84 14,18.7V20.77C18,19.86 21,16.28 21,12C21,7.72 18,4.14 14,3.23M16.5,12C16.5,10.23 15.5,8.71 14,7.97V16C15.5,15.29 16.5,13.76 16.5,12M3,9V15H7L12,20V4L7,9H3Z"/>
</svg>`;
ICONS["album"] = `<svg 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>`;
export default ICONS; export default ICONS;

View file

@ -1,8 +1,10 @@
import * as html from "./html.js"; import * as html from "./html.js";
export default class Selection { export default class Selection {
constructor(component) { constructor(component, mode) {
this._component = component; this._component = component;
/** @type {"single" | "multi"} */
this._mode = mode;
this._items = []; // FIXME ukladat skutecne HTML? co kdyz nastane refresh? this._items = []; // FIXME ukladat skutecne HTML? co kdyz nastane refresh?
this._node = html.node("cyp-commands", {hidden:true}); this._node = html.node("cyp-commands", {hidden:true});
} }
@ -14,18 +16,21 @@ export default class Selection {
addCommand(cb, options) { addCommand(cb, options) {
const button = html.button({icon:options.icon}, "", this._node); const button = html.button({icon:options.icon}, "", this._node);
html.node("span", {}, options.label, button); html.node("span", {}, options.label, button);
button.addEventListener("click", _ => cb(this._items)); button.addEventListener("click", _ => {
const arg = (this._mode == "single" ? this._items[0] : this._items);
cb(arg);
});
return button; return button;
} }
addCommandAll(items) { addCommandAll() {
this.addCommand(_ => { this.addCommand(_ => {
Array.from(this._component.children).forEach(node => this.add(node)); Array.from(this._component.children).forEach(node => this.add(node));
}, {label:"Select all", icon:"plus"}); }, {label:"Select all", icon:"plus"});
} }
addCommandClear() { addCommandCancel() {
const button = this.addCommand(_ => this.clear(), {icon:"close", label:"Clear"}); const button = this.addCommand(_ => this.clear(), {icon:"cancel", label:"Cancel"});
button.classList.add("last"); button.classList.add("last");
return button; return button;
} }
@ -43,6 +48,9 @@ export default class Selection {
const length = this._items.length; const length = this._items.length;
this._items.push(node); this._items.push(node);
node.classList.add("selected"); node.classList.add("selected");
if (this._mode == "single" && length > 0) { this.remove(this._items[0]); }
if (length == 0) { this._show(); } if (length == 0) { this._show(); }
} }
@ -54,7 +62,7 @@ export default class Selection {
} }
_show() { _show() {
const parent = this._component.closest("cyp-app").querySelector("footer"); const parent = this._component.closest("cyp-app").querySelector("footer"); // FIXME jde lepe?
parent.appendChild(this._node); parent.appendChild(this._node);
this._node.offsetWidth; // FIXME jde lepe? this._node.offsetWidth; // FIXME jde lepe?
this._node.hidden = false; this._node.hidden = false;