moving design to final files
This commit is contained in:
parent
f65918db8b
commit
3e635d9e44
8
NOTES.md
8
NOTES.md
|
@ -29,3 +29,11 @@
|
||||||
- [x] track progress (seek)
|
- [x] track progress (seek)
|
||||||
- [ ] track name
|
- [ ] track name
|
||||||
|
|
||||||
|
|
||||||
|
# foo
|
||||||
|
|
||||||
|
- client: connect websocket
|
||||||
|
- server: on_connect: send full state
|
||||||
|
- server: subscribe to changes
|
||||||
|
- server: on_change: send to client
|
||||||
|
|
||||||
|
|
10
server.go
10
server.go
|
@ -134,6 +134,10 @@ func wsServe(c echo.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
queue, err := mpdConn.PlaylistInfo(-1, -1)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
}
|
||||||
jsonStatus, err := json.Marshal(status)
|
jsonStatus, err := json.Marshal(status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
|
@ -142,7 +146,11 @@ func wsServe(c echo.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
err = websocket.Message.Send(ws, fmt.Sprintf("{\"mpd_status\":%s,\"mpd_current_song\":%s}", string(jsonStatus), string(jsonCurrentSong)))
|
jsonQueue, err := json.Marshal(queue)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
}
|
||||||
|
err = websocket.Message.Send(ws, fmt.Sprintf("{\"mpd_status\":%s,\"mpd_current_song\":%s,\"mpd_queue\":%s}", string(jsonStatus), string(jsonCurrentSong), string(jsonQueue)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
48
static/controls.js
vendored
48
static/controls.js
vendored
|
@ -190,6 +190,54 @@ socket.addEventListener("message", (e) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update queue
|
||||||
|
if ("mpd_queue" in msg && msg.mpd_queue != null) {
|
||||||
|
const tbody = document.createElement("tbody");
|
||||||
|
msg.mpd_queue.forEach(elem => {
|
||||||
|
const tr = document.createElement("tr");
|
||||||
|
const pos = document.createElement("td");
|
||||||
|
pos.innerText = elem.Pos;
|
||||||
|
const artist = document.createElement("td");
|
||||||
|
if ("Artist" in elem) {
|
||||||
|
artist.innerText = elem.Artist;
|
||||||
|
}
|
||||||
|
const track = document.createElement("td");
|
||||||
|
if ("Title" in elem) {
|
||||||
|
track.innerText = elem.Title;
|
||||||
|
} else {
|
||||||
|
track.innerText = elem.file;
|
||||||
|
}
|
||||||
|
const album = document.createElement("td");
|
||||||
|
// album.innerText = "";
|
||||||
|
const length = document.createElement("td");
|
||||||
|
const duration_hours = Math.floor(elem.duration / 3600);
|
||||||
|
const duration_minutes = Math.floor((elem.duration - duration_hours * 3600) / 60);
|
||||||
|
const duration_seconds = Math.floor(elem.duration - duration_hours * 3600 - duration_minutes * 60);
|
||||||
|
length.innerText = `${duration_hours}:${duration_minutes.toString().padStart(2, '0')}:${duration_seconds.toString().padStart(2, '0')}`;
|
||||||
|
const actions = document.createElement("td");
|
||||||
|
const del = document.createElement("button");
|
||||||
|
del.innerHTML = "🗑️";
|
||||||
|
del.addEventListener("click", e => {
|
||||||
|
fetch(`${API_URL}/queue_del/${elem.Pos}`).then(r => {
|
||||||
|
console.log(r.text());
|
||||||
|
})
|
||||||
|
});
|
||||||
|
actions.appendChild(del);
|
||||||
|
tr.appendChild(pos);
|
||||||
|
tr.appendChild(artist);
|
||||||
|
tr.appendChild(track);
|
||||||
|
tr.appendChild(album);
|
||||||
|
tr.appendChild(length);
|
||||||
|
tr.appendChild(actions);
|
||||||
|
tbody.appendChild(tr);
|
||||||
|
});
|
||||||
|
const currentQueue = document.querySelector("#queue tbody")
|
||||||
|
if (currentQueue.innerHTML !== tbody.innerHTML) {
|
||||||
|
console.log("Updating queue")
|
||||||
|
currentQueue.outerHTML = tbody.outerHTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ("mpd_error" in msg) {
|
if ("mpd_error" in msg) {
|
||||||
console.error(`MPD Error: ${msg.mpd_error}`)
|
console.error(`MPD Error: ${msg.mpd_error}`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,14 @@
|
||||||
<button>Login</button>
|
<button>Login</button>
|
||||||
<button>Config</button>
|
<button>Config</button>
|
||||||
<button id="control-update-db" disabled="disabled">Update DB</button>
|
<button id="control-update-db" disabled="disabled">Update DB</button>
|
||||||
</div>
|
</div><!--/#control-admin-->
|
||||||
<div>
|
<div>
|
||||||
<div class="spaced">
|
<div class="spaced">
|
||||||
<button id="control-previous">⏮︎</button> <!-- ⏮️ Last Track Button -->
|
<button id="control-previous">⏮︎</button> <!-- ⏮️ Last Track Button -->
|
||||||
<button id="control-stop">⏹︎</button> <!-- ⏹️ Stop Button -->
|
<button id="control-stop">⏹︎</button> <!-- ⏹️ Stop Button -->
|
||||||
<button id="control-play-pause">⏵︎</button> <!-- ▶️ Play or ⏸️ Pause Button -->
|
<button id="control-play-pause">⏵︎</button> <!-- ▶️ Play or ⏸️ Pause Button -->
|
||||||
<button id="control-next">⏭︎</button> <!-- ⏭️ Next Track Button -->
|
<button id="control-next">⏭︎</button> <!-- ⏭️ Next Track Button -->
|
||||||
</div>
|
</div><!--/.spaced-->
|
||||||
<div>
|
<div>
|
||||||
<label for="control-progress"></label>
|
<label for="control-progress"></label>
|
||||||
<input type="range" id="control-progress" name="progress" min="0" step="1" />
|
<input type="range" id="control-progress" name="progress" min="0" step="1" />
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
<div class="spaced">
|
<div class="spaced">
|
||||||
<button id="control-repeat">🔘 repeat</button>
|
<button id="control-repeat">🔘 repeat</button>
|
||||||
<button id="control-shuffle">🔘 shuffle</button>
|
<button id="control-shuffle">🔘 shuffle</button>
|
||||||
</div>
|
</div><!--/.spaced-->
|
||||||
<div class="spaced">
|
<div class="spaced">
|
||||||
<label for="control-xfade">xfade</label>
|
<label for="control-xfade">xfade</label>
|
||||||
<div>
|
<div>
|
||||||
|
@ -39,12 +39,12 @@
|
||||||
<input type="number" id="control-xfade" name="xfade" value="00" />
|
<input type="number" id="control-xfade" name="xfade" value="00" />
|
||||||
<button id="control-xfade-plus">➕</button>
|
<button id="control-xfade-plus">➕</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div><!--/.spaced-->
|
||||||
<div class="spaced">
|
<div class="spaced">
|
||||||
<button id="control-volume-down">🔉</button> <!-- 🔉 Speaker with sound wave -->
|
<button id="control-volume-down">🔉</button> <!-- 🔉 Speaker with sound wave -->
|
||||||
<input id="control-volume" name="volume" type="range" min="0" max="100" value="50" />
|
<input id="control-volume" name="volume" type="range" min="0" max="100" value="50" />
|
||||||
<button id="control-volume-up">🔊</button> <!-- 🔊 Speaker with sound waves -->
|
<button id="control-volume-up">🔊</button> <!-- 🔊 Speaker with sound waves -->
|
||||||
</div>
|
</div><!--/.spaced-->
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p>Now playing:</p>
|
<p>Now playing:</p>
|
||||||
|
@ -57,21 +57,23 @@
|
||||||
<input type="text" id="control-time" name="time" value="00:00:00/00:00:00" disabled="disabled" />
|
<input type="text" id="control-time" name="time" value="00:00:00/00:00:00" disabled="disabled" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div id="sanic-logo">
|
||||||
<img id="sanic-logo" alt="sanic logo" src="/img/sanic-logo.webp" />
|
<div>
|
||||||
<div>Sanic © 2023</div>
|
<img alt="sanic logo" src="/img/sanic-logo.webp" />
|
||||||
|
Sanic © 2023
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="queue">
|
<div id="queue">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Pos</td>
|
<th>Pos</th>
|
||||||
<td>Artists</td>
|
<th>Artists</th>
|
||||||
<td>Track</td>
|
<th>Track</th>
|
||||||
<td>Album</td>
|
<th>Album</th>
|
||||||
<td>Length</td>
|
<th>Length</th>
|
||||||
<td>Actions</td>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
|
@ -79,42 +81,62 @@
|
||||||
</div>
|
</div>
|
||||||
<div id="browser">browser</div>
|
<div id="browser">browser</div>
|
||||||
<div id="result">result</div>
|
<div id="result">result</div>
|
||||||
|
<footer>
|
||||||
|
<a href="https://git.berlin.ccc.de/cccb/sanic"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 92 92"><defs><clipPath id="a"><path d="M0 .113h91.887V92H0Zm0 0"/></clipPath></defs><g clip-path="url(#a)"><path style="stroke:none;fill-rule:nonzero;fill:#ffffff;fill-opacity:1" d="M90.156 41.965 50.036 1.848a5.913 5.913 0 0 0-8.368 0l-8.332 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.043 7.043 0 0 1 1.673 7.277l10.183 10.184a7.026 7.026 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.045 7.045 0 0 1-9.961 0 7.038 7.038 0 0 1-1.532-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.034 7.034 0 0 1 2.308-1.539V33.926a7.001 7.001 0 0 1-2.308-1.535 7.049 7.049 0 0 1-1.516-7.7L29.242 14.273 1.734 41.777a5.918 5.918 0 0 0 0 8.371L41.855 90.27a5.92 5.92 0 0 0 8.368 0l39.933-39.934a5.925 5.925 0 0 0 0-8.371"/></g></svg></a> Sanic MPD Web UI 0.1.0 - by XenGi and coon © 2023
|
||||||
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
<script src="controls.js"></script>
|
<script src="controls.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// create example elements in queue
|
const table = document.querySelector("#queue > table > tbody");
|
||||||
const queue = document.querySelector("#queue tbody");
|
for (let i = 1; i <= 100; i++) {
|
||||||
for (let i = 0; i < 100; i++) {
|
const tr = document.createElement("tr");
|
||||||
const tr = document.createElement("tr");
|
if (i === 1) {
|
||||||
|
tr.classList.add("playing");
|
||||||
const pos = document.createElement("td");
|
|
||||||
pos.appendChild(document.createTextNode(i.toString()));
|
|
||||||
tr.appendChild(pos);
|
|
||||||
|
|
||||||
const artist = document.createElement("td");
|
|
||||||
artist.appendChild(document.createTextNode(`Artist ${i.toString()}`));
|
|
||||||
tr.appendChild(artist);
|
|
||||||
|
|
||||||
const track = document.createElement("td");
|
|
||||||
track.appendChild(document.createTextNode(`Track ${i.toString()}`));
|
|
||||||
tr.appendChild(track);
|
|
||||||
|
|
||||||
const album = document.createElement("td");
|
|
||||||
album.appendChild(document.createTextNode(`Album ${i.toString()}`));
|
|
||||||
tr.appendChild(album);
|
|
||||||
|
|
||||||
const length = document.createElement("td");
|
|
||||||
length.appendChild(document.createTextNode("1:23"));
|
|
||||||
tr.appendChild(length);
|
|
||||||
|
|
||||||
const actions = document.createElement("td");
|
|
||||||
const del = document.createElement("button");
|
|
||||||
del.innerHTML = "🗑";
|
|
||||||
actions.appendChild(del);
|
|
||||||
tr.appendChild(actions);
|
|
||||||
|
|
||||||
queue.appendChild(tr);
|
|
||||||
}
|
}
|
||||||
|
const pos = document.createElement("td");
|
||||||
|
pos.innerText = i.toString();
|
||||||
|
const artist = document.createElement("td");
|
||||||
|
artist.innerText = `Artist ${i.toString()} with a super long name that should finally bring the length to a maximum`;
|
||||||
|
const track = document.createElement("td");
|
||||||
|
track.innerText = `Track ${i.toString()} with super long name that could potentially expand the table by a lot!`;
|
||||||
|
const album = document.createElement("td");
|
||||||
|
album.innerText = `Album ${i.toString()}`;
|
||||||
|
const length = document.createElement("td");
|
||||||
|
length.innerText = "01:00:00";
|
||||||
|
const actions = document.createElement("td");
|
||||||
|
actions.classList.add("actions");
|
||||||
|
// TODO: maybe use a instead of button?
|
||||||
|
const moveUp = document.createElement("button");
|
||||||
|
moveUp.classList.add("borderless");
|
||||||
|
moveUp.innerHTML = "🔺"; // 🔺 Red Triangle Pointed Down
|
||||||
|
moveUp.addEventListener("click", event => {
|
||||||
|
console.log(`DEBUG: move song ${i} up`);
|
||||||
|
});
|
||||||
|
// TODO: maybe use a instead of button?
|
||||||
|
const moveDown = document.createElement("button");
|
||||||
|
moveDown.classList.add("borderless");
|
||||||
|
moveDown.innerHTML = "🔻"; // 🔻 Red Triangle Pointed Up
|
||||||
|
moveDown.addEventListener("click", event => {
|
||||||
|
console.log(`DEBUG: move song ${i} down`);
|
||||||
|
});
|
||||||
|
// TODO: maybe use a instead of button?
|
||||||
|
const remove = document.createElement("button");
|
||||||
|
remove.classList.add("borderless");
|
||||||
|
remove.innerHTML = "❌"; // ❌ Cross mark; 🗑️ Wastebasket
|
||||||
|
remove.addEventListener("click", event => {
|
||||||
|
console.log(`DEBUG: remove song ${i} from queue`);
|
||||||
|
});
|
||||||
|
actions.appendChild(moveUp);
|
||||||
|
actions.appendChild(moveDown);
|
||||||
|
actions.appendChild(remove);
|
||||||
|
tr.appendChild(pos);
|
||||||
|
tr.appendChild(artist);
|
||||||
|
tr.appendChild(track);
|
||||||
|
tr.appendChild(album);
|
||||||
|
tr.appendChild(length);
|
||||||
|
tr.appendChild(actions);
|
||||||
|
table.appendChild(tr);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
157
static/style.css
157
static/style.css
|
@ -1,8 +1,15 @@
|
||||||
|
:root {
|
||||||
|
--ribbon-width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
/* #################### */
|
/* #################### */
|
||||||
/* #### structure ##### */
|
/* #### structure ##### */
|
||||||
/* #################### */
|
/* #################### */
|
||||||
|
|
||||||
html, body { margin: 0; height: 100%; }
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -11,7 +18,7 @@ main {
|
||||||
grid-template-columns: 1fr 2fr;
|
grid-template-columns: 1fr 2fr;
|
||||||
grid-template-rows: 150px 1fr 1fr;
|
grid-template-rows: 150px 1fr 1fr;
|
||||||
gap: 0 0;
|
gap: 0 0;
|
||||||
grid-template-areas: "nav nav" "queue queue" "browser result";
|
grid-template-areas: "nav nav" "queue queue" "browser result" "footer footer";
|
||||||
}
|
}
|
||||||
|
|
||||||
#queue {
|
#queue {
|
||||||
|
@ -33,6 +40,13 @@ main {
|
||||||
grid-area: browser;
|
grid-area: browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main footer {
|
||||||
|
grid-area: footer;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: #041936;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@ -43,8 +57,22 @@ table {
|
||||||
}
|
}
|
||||||
|
|
||||||
#sanic-logo {
|
#sanic-logo {
|
||||||
max-width: 80%;
|
display: flex;
|
||||||
max-height: 80%;
|
flex-grow: 1;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sanic-logo > div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sanic-logo img {
|
||||||
|
max-width: 75%;
|
||||||
|
max-height: 75%;
|
||||||
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spaced {
|
.spaced {
|
||||||
|
@ -52,14 +80,6 @@ table {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #################### */
|
|
||||||
/* ###### debug ####### */
|
|
||||||
/* #################### */
|
|
||||||
|
|
||||||
div {
|
|
||||||
border: 1px solid blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* #################### */
|
/* #################### */
|
||||||
/* ### pretty stuff ### */
|
/* ### pretty stuff ### */
|
||||||
/* #################### */
|
/* #################### */
|
||||||
|
@ -132,3 +152,116 @@ div {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
background-color: #09101d;
|
||||||
|
color: #bbb;
|
||||||
|
scrollbar-color: #490b00 #09101d; /* only in firefox: https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color */
|
||||||
|
font-weight: normal;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 12pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #bbb;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #28374a;
|
||||||
|
color: #bbb;
|
||||||
|
border-top-color: #545454;
|
||||||
|
border-right-color: #3a3a3a;
|
||||||
|
border-bottom-color: #3a3a3a;
|
||||||
|
border-left-color: #545454;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* borderless button used in queue */
|
||||||
|
.borderless {
|
||||||
|
border: none;
|
||||||
|
background-color: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
background-color: #28374a;
|
||||||
|
color: white;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-right-color: #545454;
|
||||||
|
border-bottom-color: #545454;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead {
|
||||||
|
background: rgb(15,29,47);
|
||||||
|
background: linear-gradient(0deg, rgba(15,29,47,1) 0%, rgba(15,29,47,1) 50%, rgba(7,14,23,1) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 2px 2px 2px 14px;
|
||||||
|
border: solid #1c2c1a;
|
||||||
|
border-width: 0 1px 0 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* show and hide action buttons on hover */
|
||||||
|
tbody tr td button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:hover td button {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fixed width for action buttons in queue so it doesn't change size when hovering */
|
||||||
|
tbody tr td:last-of-type {
|
||||||
|
min-width: 6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td.actions {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#queue {
|
||||||
|
border-bottom: 4px ridge #3a506b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make arrow for currently playing song look nice */
|
||||||
|
|
||||||
|
#queue table tr.playing td:first-of-type::before {
|
||||||
|
content: '\2BC8'; // ⯈
|
||||||
|
}
|
||||||
|
|
||||||
|
#queue table tr td:first-of-type {
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* align times */
|
||||||
|
|
||||||
|
#queue table tr td:nth-last-of-type(2) {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:nth-child(odd) td {
|
||||||
|
background: #1e1f1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:nth-child(even) td {
|
||||||
|
background: #171812;
|
||||||
|
}
|
||||||
|
|
||||||
|
#queue table tr:nth-child(odd).playing td,
|
||||||
|
#queue table tr:nth-child(even).playing td {
|
||||||
|
background-color: #490b00;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:hover td {
|
||||||
|
background-color: #354158 !important; /* TODO: remove !important */
|
||||||
|
}
|
||||||
|
|
||||||
|
footer svg {
|
||||||
|
color: white;
|
||||||
|
width: 12pt;
|
||||||
|
height: 12pt;
|
||||||
|
}
|
||||||
|
|
47
static/util.js
Normal file
47
static/util.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
function addSongToQueue(song) {
|
||||||
|
const table = document.querySelector("#queue tbody");
|
||||||
|
const tr = document.createElement("tr");
|
||||||
|
const pos = document.createElement("td");
|
||||||
|
pos.innerText = "?"; // TODO: figure out queue length +1
|
||||||
|
const artist = document.createElement("td");
|
||||||
|
artist.innerText = song.artist;
|
||||||
|
const track = document.createElement("td");
|
||||||
|
track.innerText = song.track;
|
||||||
|
const album = document.createElement("td");
|
||||||
|
album.innerText = song.album;
|
||||||
|
const length = document.createElement("td");
|
||||||
|
length.innerText = song.length;
|
||||||
|
const actions = document.createElement("td");
|
||||||
|
actions.classList.add("actions");
|
||||||
|
// TODO: maybe use a instead of button?
|
||||||
|
const moveUp = document.createElement("button");
|
||||||
|
moveUp.classList.add("borderless");
|
||||||
|
moveUp.innerHTML = "🔺"; // 🔺 Red Triangle Pointed Down
|
||||||
|
moveUp.addEventListener("click", event => {
|
||||||
|
console.log(`DEBUG: move song ${song.id} up`);
|
||||||
|
});
|
||||||
|
// TODO: maybe use a instead of button?
|
||||||
|
const moveDown = document.createElement("button");
|
||||||
|
moveDown.classList.add("borderless");
|
||||||
|
moveDown.innerHTML = "$#x1F53B;"; // 🔻 Red Triangle Pointed Up
|
||||||
|
moveDown.addEventListener("click", event => {
|
||||||
|
console.log(`DEBUG: move song ${song.id} down`);
|
||||||
|
});
|
||||||
|
// TODO: maybe use a instead of button?
|
||||||
|
const remove = document.createElement("button");
|
||||||
|
remove.classList.add("borderless");
|
||||||
|
remove.innerHTML = "$#x274C;"; // ❌ Cross mark; 🗑️ Wastebasket
|
||||||
|
remove.addEventListener("click", event => {
|
||||||
|
console.log(`DEBUG: remove song ${song.id} from queue`);
|
||||||
|
});
|
||||||
|
actions.appendChild(moveUp);
|
||||||
|
actions.appendChild(moveDown);
|
||||||
|
actions.appendChild(remove);
|
||||||
|
tr.appendChild(pos);
|
||||||
|
tr.appendChild(artist);
|
||||||
|
tr.appendChild(track);
|
||||||
|
tr.appendChild(album);
|
||||||
|
tr.appendChild(length);
|
||||||
|
tr.appendChild(actions);
|
||||||
|
table.appendChild(tr);
|
||||||
|
}
|
Loading…
Reference in a new issue