Files
@ 4bab5bbce195
Branch filter:
Location: light9/web/ascoltami/Light9SongListing.ts
4bab5bbce195
3.8 KiB
video/MP2T
show-specific changes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | import debug from "debug";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { NamedNode } from "n3";
import { getTopGraph } from "../RdfdbSyncedGraph";
import { show } from "../show_specific";
import { SyncedGraph } from "../SyncedGraph";
export { Light9TimelineAudio } from "../light9-timeline-audio";
export { Light9CursorCanvas } from "../Light9CursorCanvas";
export { RdfdbSyncedGraph } from "../RdfdbSyncedGraph";
export { ResourceDisplay } from "../ResourceDisplay";
const log = debug("songs");
async function postJson(url: string, jsBody: Object) {
return fetch(url, {
method: "POST",
headers: { "Content-Type": "applcation/json" },
body: JSON.stringify(jsBody),
});
}
class SongRow {
constructor(public uri: NamedNode, public label: string) {}
}
@customElement("light9-song-listing")
export class Light9SongListing extends LitElement {
static styles = [
css`
td {
font-size: 18pt;
white-space: nowrap;
padding: 0 10px;
background: #9b9b9b;
}
tr.current td {
background: #dbf5e4;
}
tr td:first-child {
box-shadow: initial;
}
tr.current td:first-child {
box-shadow: inset 0px 2px 10px 0px black;
}
button {
min-height: 30pt;
min-width: 65pt;
}
`,
];
graph!: SyncedGraph;
@state() playingSong: NamedNode | null = null;
@property() selectedSong: NamedNode | null = null;
@state() songs: SongRow[] = [];
@state() isPlaying = false;
constructor() {
super();
getTopGraph().then((g) => {
this.graph = g;
this.graph.runHandler(this.getSongs.bind(this), "getsongs");
this.graph.runHandler(this.playState.bind(this), "playstate");
});
}
getSongs() {
this.songs = [];
const U = this.graph.U();
try {
const playlist: NamedNode = this.graph.uriValue(show, U(":playList"));
(this.graph.items(playlist) as NamedNode[]).forEach((song: NamedNode) => {
this.songs.push(new SongRow(song, this.graph.labelOrTail(song)));
});
} catch (e) {
// likely it's startup- no graph yet
log(e);
}
}
playState() {
const U = this.graph.U();
try {
this.isPlaying = this.graph.booleanValue(U(":ascoltami"), U(":playing"));
this.playingSong = this.graph.uriValue(U(":ascoltami"), U(":song"));
} catch (e) {
log(e);
}
}
render() {
return html`
<table>
${this.songs.map((song) => {
const playing = this.playingSong && song.uri.equals(this.playingSong);
const selected = this.selectedSong && song.uri.equals(this.selectedSong);
// could use <resource-display .uri=${song} noclick></resource-display>
return html`
<tr class="song ${playing ? "current" : ""}" data-uri=${song.uri.value} @click=${this.clickSongRow}>
<td>
<a href=${song.uri.value}>
<span class="num">${song.label.slice(0, 2)}</span>
<span class="song-name">${song.label.slice(2).trim()}</span>
</a>
</td>
<td>
<button ?disabled=${this.isPlaying || (this.selectedSong != null && !selected)}>${selected ? "Deselect" : "Select"}</button>
</td>
</tr>
`;
})}
</table>
`;
}
clickSongRow(ev: MouseEvent) {
ev.preventDefault();
const row = (ev.target as HTMLElement).closest(".song") as HTMLElement;
const song = new NamedNode(row.dataset.uri!);
if (this.selectedSong && song.equals(this.selectedSong)) {
this.selectedSong = null;
} else {
this.selectedSong = song;
}
this.dispatchEvent(new CustomEvent("selectsong", { detail: { song: this.selectedSong } }));
}
}
|