import { $el } from "../../../../scripts/ui.js"; import { addStylesheet, getUrl, loadImage } from "./utils.js"; import { createSpinner } from "./spinner.js"; addStylesheet(getUrl("lightbox.css", import.meta.url)); const $$el = (tag, name, ...args) => { if (name) name = "-" + name; return $el(tag + ".pysssss-lightbox" + name, ...args); }; const ani = async (a, t, b) => { a(); await new Promise((r) => setTimeout(r, t)); b(); }; export class Lightbox { constructor() { this.el = $$el("div", "", { parent: document.body, onclick: (e) => { e.stopImmediatePropagation(); this.close(); }, style: { display: "none", opacity: 0, }, }); this.closeBtn = $$el("div", "close", { parent: this.el, }); this.prev = $$el("div", "prev", { parent: this.el, onclick: (e) => { this.update(-1); e.stopImmediatePropagation(); }, }); this.main = $$el("div", "main", { parent: this.el, }); this.next = $$el("div", "next", { parent: this.el, onclick: (e) => { this.update(1); e.stopImmediatePropagation(); }, }); this.link = $$el("a", "link", { parent: this.main, target: "_blank", }); this.spinner = createSpinner(); this.link.appendChild(this.spinner); this.img = $$el("img", "img", { style: { opacity: 0, }, parent: this.link, onclick: (e) => { e.stopImmediatePropagation(); }, onwheel: (e) => { if (!(e instanceof WheelEvent) || e.ctrlKey) { return; } const direction = Math.sign(e.deltaY); this.update(direction); }, }); } close() { ani( () => (this.el.style.opacity = 0), 200, () => (this.el.style.display = "none") ); } async show(images, index) { this.images = images; this.index = index || 0; await this.update(0); } async update(shift) { if (shift < 0 && this.index <= 0) { return; } if (shift > 0 && this.index >= this.images.length - 1) { return; } this.index += shift; this.prev.style.visibility = this.index ? "unset" : "hidden"; this.next.style.visibility = this.index === this.images.length - 1 ? "hidden" : "unset"; const img = this.images[this.index]; this.el.style.display = "flex"; this.el.clientWidth; // Force a reflow this.el.style.opacity = 1; this.img.style.opacity = 0; this.spinner.style.display = "inline-block"; try { await loadImage(img); } catch (err) { console.error('failed to load image', img, err); } this.spinner.style.display = "none"; this.link.href = img; this.img.src = img; this.img.style.opacity = 1; } async updateWithNewImage(img, feedDirection) { // No-op if lightbox is not open if (this.el.style.display === "none" || this.el.style.opacity === "0") return; // Ensure currently shown image does not change const [method, shift] = feedDirection === "newest first" ? ["unshift", 1] : ["push", 0]; this.images[method](img); await this.update(shift); } } export const lightbox = new Lightbox(); addEventListener('keydown', (event) => { if (lightbox.el.style.display === 'none') { return; } const { key } = event; switch (key) { case 'ArrowLeft': case 'a': lightbox.update(-1); break; case 'ArrowRight': case 'd': lightbox.update(1); break; case 'Escape': lightbox.close(); break; } });