Files

76 lines
3.2 KiB
JavaScript

// A small cassette player on the desk: a cassette window with two reels that turn while the music plays,
// the track title on the cassette label, and five vintage keys (previous, play, pause, stop, next).
import { t } from "../i18n/i18n.js";
import { $ } from "./desk.js";
const KEYS = [
["prev", `<path d="M12 1v14L3 8zM22 1v14l-9-7z"/>`],
["play", `<path d="M7 1v14l11-7z"/>`],
["pause", `<path d="M6 1h4v14H6zM14 1h4v14h-4z"/>`],
["stop", `<path d="M6 2h12v12H6z"/>`],
["next", `<path d="M2 1v14l9-7zM12 1v14l9-7z"/>`],
];
const reel = (side) => `
<svg class="walkman__reel walkman__reel--${side}" viewBox="0 0 40 40" aria-hidden="true" focusable="false">
<circle class="walkman__tape" cx="20" cy="20" r="17"/>
<g class="walkman__spokes">${[0, 60, 120].map((a) => `<line x1="20" y1="9" x2="20" y2="31" transform="rotate(${a} 20 20)"/>`).join("")}</g>
<circle class="walkman__hub" cx="20" cy="20" r="6"/>
</svg>`;
export function createWalkman(root, { music, onPress = () => {} }) {
root.innerHTML = `
<div class="walkman__body">
<div class="walkman__badge"><b>VALDOR</b><span>${t("walkman.model")}</span><i class="walkman__led" aria-hidden="true"></i></div>
<div class="walkman__window">
${reel("left")}
<div class="walkman__label"><span class="walkman__title" role="status"></span></div>
${reel("right")}
</div>
<div class="walkman__keys" role="group" aria-label="${t("walkman.title")}">
${KEYS.map(([act, icon]) => `
<button class="walkman__key walkman__key--${act}" data-act="${act}" aria-label="${t("walkman." + act)}" title="${t("walkman." + act)}">
<svg viewBox="0 0 24 16" aria-hidden="true" focusable="false">${icon}</svg>
</button>`).join("")}
</div>
</div>`;
const title = $(root, ".walkman__title");
const label = $(root, ".walkman__label");
root.addEventListener("click", (e) => {
const b = e.target.closest("button[data-act]");
if (!b || b.disabled) return;
onPress();
music[b.dataset.act]();
});
function update() {
const m = music.info();
const playing = m.state === "playing";
root.classList.toggle("is-playing", playing);
root.classList.toggle("is-empty", !m.available);
root.title = m.available ? "" : t("walkman.hint");
let text = t("walkman.empty");
if (m.available) text = m.waitingForGesture ? t("walkman.tap") : m.state === "stopped" ? t("walkman.stopped") : m.title;
if (title.textContent !== text) {
title.textContent = text;
title.classList.remove("is-scroll");
// a long title scrolls across the label while the tape runs
requestAnimationFrame(() => title.classList.toggle("is-scroll", title.scrollWidth > label.clientWidth + 2));
}
for (const b of root.querySelectorAll("button[data-act]")) {
b.disabled = !m.available;
if (b.dataset.act === "play") b.setAttribute("aria-pressed", String(playing));
if (b.dataset.act === "pause") b.setAttribute("aria-pressed", String(m.state === "paused"));
if (b.dataset.act === "stop") b.setAttribute("aria-pressed", String(m.state === "stopped"));
}
}
music.setListener(update);
return { update };
}