A music player that connects to your cloud/distributed storage.
at v4 168 lines 5.0 kB view raw
1import * as Output from "~/common/output.js"; 2import { facetFromURI } from "~/common/facets/utils.js"; 3import { effect } from "~/common/signal.js"; 4 5import { output } from "./output.js"; 6 7//////////////////////////////////////////// 8// FILTER 9//////////////////////////////////////////// 10 11export function setupFilter() { 12 /** @type {NodeListOf<HTMLElement>} */ 13 const buttons = document.querySelectorAll(".grid-filter button"); 14 15 /** @type {NodeListOf<HTMLElement>} */ 16 const items = document.querySelectorAll(".grid-item"); 17 18 buttons.forEach((b) => { 19 b.addEventListener("click", () => { 20 buttons.forEach((b) => b.classList.add("button--transparent")); 21 b.classList.remove("button--transparent"); 22 23 const filter = b.dataset.filter; 24 25 items.forEach((item) => { 26 const kind = item.dataset.kind; 27 const show = filter === "all" || kind === filter; 28 item.hidden = !show; 29 30 /** @type {HTMLElement | null} */ 31 const kindEl = item.querySelector(".grid-item__kind"); 32 if (!kindEl) return; 33 kindEl.hidden = filter !== "all"; 34 }); 35 }); 36 }); 37} 38 39//////////////////////////////////////////// 40// OUTPUT INDICATOR 41//////////////////////////////////////////// 42 43/** @type {() => void | undefined} */ 44let stopOutputIndicator; 45 46export async function setupOutputIndicator() { 47 if (stopOutputIndicator) stopOutputIndicator(); 48 49 const filterEl = document.querySelector(".grid-filter"); 50 if (!filterEl) return; 51 52 const out = await output(); 53 54 /** @type {HTMLElement | null} */ 55 const indicator = filterEl.querySelector(".grid-filter--output"); 56 if (!indicator) return; 57 58 /** @type {HTMLElement | null} */ 59 const label = filterEl.querySelector(".grid-filter--label-output"); 60 if (!label) return; 61 62 setTimeout(() => { 63 indicator.style.opacity = "1"; 64 label.style.opacity = "0.4"; 65 }, 250); 66 67 stopOutputIndicator = effect(() => { 68 const selected = out.selected(); 69 const label = selected?.label ?? selected?.getAttribute?.("label") ?? 70 "Local storage"; 71 indicator.textContent = label; 72 }); 73} 74 75//////////////////////////////////////////// 76// TOGGLE BUTTONS 77//////////////////////////////////////////// 78 79export function insertToggleButtons() { 80 const gridItems = /** @type {NodeListOf<HTMLLIElement>} */ ( 81 document.querySelectorAll(".grid li") 82 ); 83 84 for (const li of gridItems) { 85 const container = li.querySelector(".grid-item__title"); 86 if (!container) continue; 87 88 const button = document.createElement("button"); 89 button.className = "button--transparent"; 90 button.style.cssText = 91 `color: oklch(from currentColor l c h / 0.4); font-size: var(--fs-md); opacity: 0; padding: 0;`; 92 button.innerHTML = `<i class="ph-fill ph-toggle-left"></i>`; 93 94 button.addEventListener("click", async (event) => { 95 event.preventDefault(); 96 97 const uri = li.getAttribute("data-uri"); 98 const name = li.getAttribute("data-name"); 99 const kind = li.getAttribute("data-kind") ?? undefined; 100 const description = li.getAttribute("data-description") ?? undefined; 101 102 if (!uri || !name) return; 103 104 const out = await output(); 105 const collection = await Output.data(out.facets); 106 const isActive = collection.some((f) => 107 f.uri === uri && f.html === undefined 108 ); 109 110 if (isActive) { 111 out.facets.save(collection.filter((f) => f.uri !== uri)); 112 } else { 113 const facet = await facetFromURI({ description, kind, name, uri }, { 114 fetchHTML: false, 115 }); 116 out.facets.save([...collection, facet]); 117 } 118 }); 119 120 container.appendChild(button); 121 } 122} 123 124//////////////////////////////////////////// 125// SYNC ACTIVE STATES 126//////////////////////////////////////////// 127 128/** @type {() => void | undefined} */ 129let stopMonitor; 130 131export async function monitorToggleButtonStates() { 132 if (stopMonitor) stopMonitor(); 133 const out = await output(); 134 135 stopMonitor = effect(() => { 136 const gridItems = /** @type {NodeListOf<HTMLLIElement>} */ ( 137 document.querySelectorAll(".grid li") 138 ); 139 140 const col = out.facets.collection(); 141 const collection = col.state === "loaded" ? col.data : []; 142 const colMap = new Map(collection.map((f) => [f.uri, f])); 143 144 for (const li of gridItems) { 145 const uri = li.getAttribute("data-uri"); 146 const button = 147 /** @type {HTMLElement | null} */ (li.querySelector("button")); 148 const icon = button?.querySelector("i"); 149 150 if (!button || !icon || !uri) continue; 151 152 button.style.opacity = "revert-layer"; 153 154 const item = colMap.get(uri); 155 const isActive = item && item.html === undefined; 156 157 button.title = isActive 158 ? "Remove from your collection" 159 : "Add to your collection"; 160 icon.className = isActive 161 ? "ph-fill ph-toggle-right" 162 : "ph-fill ph-toggle-left"; 163 /** @type {HTMLElement} */ (icon).style.color = isActive 164 ? li.dataset.activeColor ?? "var(--accent-twist-2)" 165 : ""; 166 } 167 }); 168}