Set up some basic web components and basically format a track card (#25)
Co-authored-by: Savanni D'Gerinel <savanni@luminescent-dreams.com> Reviewed-on: savanni/tools#25
This commit is contained in:
parent
52ca039f45
commit
1016ba756c
|
@ -11,6 +11,7 @@
|
||||||
<div class="controls"><button class="play-pause">Pause</button></div>
|
<div class="controls"><button class="play-pause">Pause</button></div>
|
||||||
|
|
||||||
<div class="track-list">
|
<div class="track-list">
|
||||||
|
<!--
|
||||||
<div class="track-list__grouping">
|
<div class="track-list__grouping">
|
||||||
<ul class="bulletless-list">
|
<ul class="bulletless-list">
|
||||||
<li> By Artist </li>
|
<li> By Artist </li>
|
||||||
|
@ -19,60 +20,11 @@
|
||||||
<li> Dance Music </li>
|
<li> Dance Music </li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
<div class="track-list__tracks">
|
<div>
|
||||||
<table>
|
<ul class="track-list__tracks bulletless-list">
|
||||||
<thead>
|
</ul>
|
||||||
<tr>
|
|
||||||
<th> id </th>
|
|
||||||
<th> Track # </th>
|
|
||||||
<th> Title </th>
|
|
||||||
<th> Artist </th>
|
|
||||||
<th> Album </th>
|
|
||||||
<th> Length </th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
<!--
|
|
||||||
<tr class="track-list__track-row">
|
|
||||||
<td> 1 </td>
|
|
||||||
<td> Underground </td>
|
|
||||||
<td> Lindsey Stirling </td>
|
|
||||||
<td> Artemis </td>
|
|
||||||
<td> 4:24 </td>
|
|
||||||
</tr>
|
|
||||||
<tr class="track-list__track-row">
|
|
||||||
<td> 2 </td>
|
|
||||||
<td> Artemis </td>
|
|
||||||
<td> Lindsey Stirling </td>
|
|
||||||
<td> Artemis </td>
|
|
||||||
<td> 3:54 </td>
|
|
||||||
</tr>
|
|
||||||
<tr class="track-list__track-row">
|
|
||||||
<td> 3 </td>
|
|
||||||
<td> Til the Light Goes Out </td>
|
|
||||||
<td> Lindsey Stirling </td>
|
|
||||||
<td> Artemis </td>
|
|
||||||
<td> 4:46 </td>
|
|
||||||
</tr>
|
|
||||||
<tr class="track-list__track-row">
|
|
||||||
<td> 4 </td>
|
|
||||||
<td> Between Twilight </td>
|
|
||||||
<td> Lindsey Stirling </td>
|
|
||||||
<td> Artemis </td>
|
|
||||||
<td> 4:20 </td>
|
|
||||||
</tr>
|
|
||||||
<tr class="track-list__track-row">
|
|
||||||
<td> 5 </td>
|
|
||||||
<td> Foreverglow </td>
|
|
||||||
<td> Lindsey Stirling </td>
|
|
||||||
<td> Artemis </td>
|
|
||||||
<td> 3:58 </td>
|
|
||||||
</tr>
|
|
||||||
-->
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,9 +4,9 @@
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "browserify src/main.ts -p [ tsify ] > dist/bundle.js && cp index.html styles.css dist",
|
"build": "webpack",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"watch": "exa index.html styles.css src/* | entr -s 'npm run build'"
|
"watch": "webpack --watch"
|
||||||
},
|
},
|
||||||
"author": "Savanni D'Gerinel <savanni@luminescent-dreams.com>",
|
"author": "Savanni D'Gerinel <savanni@luminescent-dreams.com>",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
|
@ -16,10 +16,12 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/lodash": "^4.14.191",
|
"@types/lodash": "^4.14.191",
|
||||||
"babelify": "^10.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"browserify": "^17.0.0",
|
"css-loader": "^6.7.3",
|
||||||
"tsify": "^5.0.4",
|
"style-loader": "^3.3.1",
|
||||||
"typescript": "^4.9.4",
|
"ts-loader": "^9.4.2",
|
||||||
"watchify": "^4.0.0"
|
"typescript": "^4.9.5",
|
||||||
|
"webpack": "^5.75.0",
|
||||||
|
"webpack-cli": "^5.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
import { TrackInfo } from "../client";
|
||||||
|
|
||||||
|
export class TrackName extends HTMLElement {
|
||||||
|
container: HTMLElement;
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["name"];
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.container = document.createElement("div");
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string | null {
|
||||||
|
return this.getAttribute("name");
|
||||||
|
}
|
||||||
|
|
||||||
|
set name(name: string | null) {
|
||||||
|
while (this.container.lastChild) {
|
||||||
|
this.container.removeChild(this.container.lastChild);
|
||||||
|
}
|
||||||
|
if (name) {
|
||||||
|
this.setAttribute("name", name);
|
||||||
|
this.container.appendChild(document.createTextNode(name));
|
||||||
|
} else {
|
||||||
|
this.removeAttribute("name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.appendChild(this.container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TrackCard extends HTMLElement {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["id", "trackNumber", "name", "album", "artist"];
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangeCallback(
|
||||||
|
attrName: string,
|
||||||
|
oldValue: string,
|
||||||
|
newValue: string
|
||||||
|
): void {
|
||||||
|
if (newValue !== oldValue) {
|
||||||
|
this.updateContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string | null {
|
||||||
|
return this.getAttribute("name");
|
||||||
|
}
|
||||||
|
|
||||||
|
set name(name: string | null) {
|
||||||
|
if (name) {
|
||||||
|
this.setAttribute("name", name);
|
||||||
|
} else {
|
||||||
|
this.removeAttribute("open");
|
||||||
|
}
|
||||||
|
this.updateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
get artist(): string | null {
|
||||||
|
return this.getAttribute("artist");
|
||||||
|
}
|
||||||
|
|
||||||
|
set artist(artist: string | null) {
|
||||||
|
if (artist) {
|
||||||
|
this.setAttribute("artist", artist);
|
||||||
|
} else {
|
||||||
|
this.removeAttribute("open");
|
||||||
|
}
|
||||||
|
this.updateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
get album(): string | null {
|
||||||
|
return this.getAttribute("album");
|
||||||
|
}
|
||||||
|
|
||||||
|
set album(album: string | null) {
|
||||||
|
if (album) {
|
||||||
|
this.setAttribute("album", album);
|
||||||
|
} else {
|
||||||
|
this.removeAttribute("open");
|
||||||
|
}
|
||||||
|
this.updateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
get length(): string | null {
|
||||||
|
return this.getAttribute("length");
|
||||||
|
}
|
||||||
|
|
||||||
|
set length(length: string | null) {
|
||||||
|
if (length) {
|
||||||
|
this.setAttribute("length", length);
|
||||||
|
} else {
|
||||||
|
this.removeAttribute("open");
|
||||||
|
}
|
||||||
|
this.updateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.updateContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateContent() {
|
||||||
|
const container = document.createElement("div");
|
||||||
|
container.classList.add("track-card");
|
||||||
|
this.innerHTML = "";
|
||||||
|
|
||||||
|
this.appendChild(container);
|
||||||
|
|
||||||
|
while (container.lastChild) {
|
||||||
|
container.removeChild(container.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this["name"]) {
|
||||||
|
const trackName = document.createElement("track-name");
|
||||||
|
trackName.name = this["name"];
|
||||||
|
container.appendChild(trackName);
|
||||||
|
}
|
||||||
|
this["length"] && container.appendChild(document.createTextNode("1:23"));
|
||||||
|
this["album"] &&
|
||||||
|
container.appendChild(document.createTextNode("Shatter Me"));
|
||||||
|
this["artist"] &&
|
||||||
|
container.appendChild(document.createTextNode("Lindsey Stirling"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
export interface TrackInfo {
|
||||||
|
id: string;
|
||||||
|
track_number?: number;
|
||||||
|
name?: string;
|
||||||
|
album?: string;
|
||||||
|
artist?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getTracks = (): Promise<TrackInfo[]> =>
|
||||||
|
fetch("/api/v1/tracks").then((r) => r.json());
|
|
@ -1,11 +1,15 @@
|
||||||
import * as _ from "lodash";
|
import * as _ from "lodash";
|
||||||
|
import { TrackInfo, getTracks } from "./client";
|
||||||
|
import { TrackName, TrackCard } from "./blocks/track";
|
||||||
|
|
||||||
interface TrackInfo {
|
window.customElements.define("track-name", TrackName);
|
||||||
id: string;
|
window.customElements.define("track-card", TrackCard);
|
||||||
track_number?: number;
|
|
||||||
name?: string;
|
declare global {
|
||||||
album?: string;
|
interface HTMLElementTagNameMap {
|
||||||
artist?: string;
|
"track-name": TrackName;
|
||||||
|
"track-card": TrackCard;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const replaceTitle = () => {
|
const replaceTitle = () => {
|
||||||
|
@ -15,52 +19,22 @@ const replaceTitle = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTracks = () => fetch("/api/v1/tracks").then((r) => r.json());
|
|
||||||
|
|
||||||
const formatTrack = (track: TrackInfo) => {
|
|
||||||
let row = document.createElement("tr");
|
|
||||||
row.classList.add("track-list__row");
|
|
||||||
|
|
||||||
let track_id = document.createElement("td");
|
|
||||||
track_id.appendChild(document.createTextNode(track.id));
|
|
||||||
track_id.classList.add("track-list__cell");
|
|
||||||
|
|
||||||
let track_number = document.createElement("td");
|
|
||||||
track_number.appendChild(
|
|
||||||
document.createTextNode(track.track_number?.toString() || "")
|
|
||||||
);
|
|
||||||
track_number.classList.add("track-list__cell");
|
|
||||||
|
|
||||||
let name = document.createElement("td");
|
|
||||||
name.appendChild(document.createTextNode(track.name || ""));
|
|
||||||
name.classList.add("track-list__cell");
|
|
||||||
|
|
||||||
let album = document.createElement("td");
|
|
||||||
album.appendChild(document.createTextNode(track.album || ""));
|
|
||||||
album.classList.add("track-list__cell");
|
|
||||||
|
|
||||||
let artist = document.createElement("td");
|
|
||||||
artist.appendChild(document.createTextNode(track.artist || ""));
|
|
||||||
artist.classList.add("track-list__cell");
|
|
||||||
|
|
||||||
let length = document.createElement("td");
|
|
||||||
artist.appendChild(document.createTextNode(""));
|
|
||||||
length.classList.add("track-list__cell");
|
|
||||||
|
|
||||||
row.appendChild(track_id);
|
|
||||||
row.appendChild(track_number);
|
|
||||||
row.appendChild(name);
|
|
||||||
row.appendChild(artist);
|
|
||||||
row.appendChild(album);
|
|
||||||
row.appendChild(length);
|
|
||||||
return row;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateTrackList = (tracks: TrackInfo[]) => {
|
const updateTrackList = (tracks: TrackInfo[]) => {
|
||||||
const track_list = document.querySelector(".track-list__tracks tbody");
|
const track_list = document.querySelector(".track-list__tracks");
|
||||||
if (track_list) {
|
if (track_list) {
|
||||||
let track_formats = _.map(tracks, formatTrack);
|
let track_formats = _.map(tracks, (info) => {
|
||||||
_.map(track_formats, (trackinfo) => track_list.appendChild(trackinfo));
|
let card: TrackCard = document.createElement("track-card");
|
||||||
|
card.name = info.name || null;
|
||||||
|
return card;
|
||||||
|
});
|
||||||
|
_.map(track_formats, (trackCard) => {
|
||||||
|
let listItem = document.createElement("li");
|
||||||
|
listItem.classList.add("track-list__row");
|
||||||
|
listItem.appendChild(trackCard);
|
||||||
|
track_list.appendChild(listItem);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("track_list does not exist");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,10 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-list__row {
|
.track-list__row {
|
||||||
background-color: rgb(10, 10, 10);
|
margin-top: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
.track-list__row:nth-child(even) {
|
.track-list__row:nth-child(even) {
|
||||||
background-color: rgb(255, 255, 255);
|
background-color: rgb(255, 255, 255);
|
||||||
}
|
}
|
||||||
|
@ -36,7 +37,25 @@ body {
|
||||||
.track-list__row:nth-child(odd) {
|
.track-list__row:nth-child(odd) {
|
||||||
background-color: rgb(200, 200, 200);
|
background-color: rgb(200, 200, 200);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
.track-list__cell {
|
.track-card {
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 5px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
width: 300px;
|
||||||
|
height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.track-card__name {
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-card__length {
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-card__album {
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-card__artist {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"lib": ["es2016", "DOM"],
|
"lib": ["es2016", "DOM"],
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": true
|
"strict": true,
|
||||||
}
|
"noImplicitAny": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
const path = require('path');
|
||||||
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: 'development',
|
||||||
|
entry: './src/main.ts',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.html$/i,
|
||||||
|
type: 'asset/resource',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/i,
|
||||||
|
use: ['style-loader', 'css-loader'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new CopyPlugin({
|
||||||
|
patterns: [
|
||||||
|
{ from: "index.html", to: "index.html" },
|
||||||
|
{ from: "styles.css", to: "styles.css" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
filename: 'bundle.js',
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
},
|
||||||
|
};
|
|
@ -583,6 +583,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dbus",
|
"dbus",
|
||||||
"flow",
|
"flow",
|
||||||
|
"mime_guess",
|
||||||
"mpris",
|
"mpris",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -8,6 +8,7 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dbus = { version = "0.9.7" }
|
dbus = { version = "0.9.7" }
|
||||||
flow = { path = "../../flow" }
|
flow = { path = "../../flow" }
|
||||||
|
mime_guess = "2.0.4"
|
||||||
mpris = { version = "2.0" }
|
mpris = { version = "2.0" }
|
||||||
rusqlite = { version = "0.28" }
|
rusqlite = { version = "0.28" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
|
@ -21,20 +21,21 @@ fn tracks(index: &Arc<impl MusicIndex>) -> Vec<TrackInfo> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Bundle {
|
struct Static(PathBuf);
|
||||||
Index,
|
|
||||||
App,
|
|
||||||
Styles,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Bundle {
|
impl Static {
|
||||||
fn read(self, root: PathBuf) -> String {
|
fn read(self, root: PathBuf) -> String {
|
||||||
|
/*
|
||||||
let mut path = root;
|
let mut path = root;
|
||||||
match self {
|
match self {
|
||||||
Bundle::Index => path.push(PathBuf::from("index.html")),
|
Bundle::Index => path.push(PathBuf::from("index.html")),
|
||||||
Bundle::App => path.push(PathBuf::from("bundle.js")),
|
Bundle::App => path.push(PathBuf::from("bundle.js")),
|
||||||
Bundle::Styles => path.push(PathBuf::from("styles.css")),
|
Bundle::Styles => path.push(PathBuf::from("styles.css")),
|
||||||
};
|
};
|
||||||
|
std::fs::read_to_string(path).expect("to find the file")
|
||||||
|
*/
|
||||||
|
let mut path = root;
|
||||||
|
path.push(self.0);
|
||||||
println!("path: {:?}", path);
|
println!("path: {:?}", path);
|
||||||
std::fs::read_to_string(path).expect("to find the file")
|
std::fs::read_to_string(path).expect("to find the file")
|
||||||
}
|
}
|
||||||
|
@ -68,23 +69,22 @@ pub async fn main() {
|
||||||
move || {
|
move || {
|
||||||
warp::http::Response::builder()
|
warp::http::Response::builder()
|
||||||
.header("content-type", "text/html")
|
.header("content-type", "text/html")
|
||||||
.body(Bundle::Index.read(bundle_root.clone()))
|
.body(Static(PathBuf::from("index.html")).read(bundle_root.clone()))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let app = warp::path!("bundle.js").and(warp::get()).map({
|
let assets = warp::path!(String).and(warp::get()).map({
|
||||||
let bundle_root = bundle_root.clone();
|
let bundle_root = bundle_root.clone();
|
||||||
move || {
|
move |filename: String| {
|
||||||
|
let mime_type = mime_guess::from_path(filename.clone())
|
||||||
|
.first()
|
||||||
|
.map(|m| m.essence_str().to_owned())
|
||||||
|
.unwrap_or("text/plain".to_owned());
|
||||||
|
println!("mime_type: {:?}", mime_type);
|
||||||
|
// let mut path = PathBuf::from("assets");
|
||||||
|
// path.push(filename);
|
||||||
warp::http::Response::builder()
|
warp::http::Response::builder()
|
||||||
.header("content-type", "text/javascript")
|
.header("content-type", mime_type)
|
||||||
.body(Bundle::App.read(bundle_root.clone()))
|
.body(Static(PathBuf::from(filename)).read(bundle_root.clone()))
|
||||||
}
|
|
||||||
});
|
|
||||||
let styles = warp::path!("styles.css").and(warp::get()).map({
|
|
||||||
let bundle_root = bundle_root.clone();
|
|
||||||
move || {
|
|
||||||
warp::http::Response::builder()
|
|
||||||
.header("content-type", "text/css")
|
|
||||||
.body(Bundle::Styles.read(bundle_root.clone()))
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -128,12 +128,9 @@ pub async fn main() {
|
||||||
.or(queue)
|
.or(queue)
|
||||||
.or(playing_status);
|
.or(playing_status);
|
||||||
*/
|
*/
|
||||||
let routes = root.or(app).or(styles).or(track_list);
|
let routes = root.or(assets).or(track_list);
|
||||||
let server = warp::serve(routes);
|
let server = warp::serve(routes);
|
||||||
server
|
server
|
||||||
.run(SocketAddr::new(
|
.run(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8002))
|
||||||
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
|
|
||||||
8002,
|
|
||||||
))
|
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,10 @@ impl FileIterator {
|
||||||
id: TrackId::from(path.to_str().unwrap().to_owned()),
|
id: TrackId::from(path.to_str().unwrap().to_owned()),
|
||||||
album: None,
|
album: None,
|
||||||
artist: None,
|
artist: None,
|
||||||
name: None,
|
name: path
|
||||||
|
.file_stem()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.map(|s| s.to_owned()),
|
||||||
track_number: None,
|
track_number: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue