| 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; |
| 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) { |
| |
| if (this.el.style.display === "none" || this.el.style.opacity === "0") return; |
|
|
| |
| 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; |
| } |
| }); |