PageSourceSearch

https://cooksclub.worldspice.com/assets/controllers/lightbox_controller-b7680707.js

js worldspice.com collected 2026-10-03 21:50:30 UTC 2,605 bytes, 73 lines download raw bytes

1// @swarm_description: Opens any element carrying data-lightbox-src in a full-page image modal
2import { Controller } from "@hotwired/stimulus"
3
4// Anything with data-lightbox-src opens here — the <a> around a comment's photo tile, and the
5// <img> Action Text renders for a picture dropped into a Lexxy body. Delegated from document
6// so images that arrive later (a Turbo Stream appending a new comment, a video finishing
7// normalization) need no wiring of their own.
8const TRIGGER = "[data-lightbox-src]"
9
10// Clicks inside an editor belong to the editor. Lexxy renders the same attachment markup while
11// the comment is still being written, and swallowing that click would break dragging and
12// deleting an image mid-compose.
13const EDITOR = "[contenteditable='true'], lexxy-editor, trix-editor"
14
15export default class extends Controller {
16  static targets = ["dialog", "image", "spinner"]
17
18  connect() {
19    this.boundClick = this.handleClick.bind(this)
20    document.addEventListener("click", this.boundClick)
21  }
22
23  disconnect() {
24    document.removeEventListener("click", this.boundClick)
25    document.body.classList.remove("overflow-hidden")
26  }
27
28  handleClick(event) {
29    // Modified clicks still mean "open in a new tab" — the anchor's href is left intact for
30    // exactly that, and for anyone browsing without JavaScript.
31    if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) return
32
33    const trigger = event.target.closest(TRIGGER)
34    if (!trigger || trigger.closest(EDITOR)) return
35
36    const src = trigger.dataset.lightboxSrc
37    if (!src) return
38
39    event.preventDefault()
40    this.open(src, trigger.dataset.lightboxAlt || "")
41  }
42
43  open(src, alt) {
44    this.imageTarget.classList.add("opacity-0")
45    this.spinnerTarget.classList.remove("hidden")
46    this.imageTarget.alt = alt
47    this.imageTarget.src = src
48    this.dialogTarget.showModal()
49    document.body.classList.add("overflow-hidden")
50  }
51
52  imageLoaded() {
53    this.spinnerTarget.classList.add("hidden")
54    this.imageTarget.classList.remove("opacity-0")
55  }
56
57  close() {
58    this.dialogTarget.close()
59  }
60
61  // Escape closes a native <dialog> without going through #close, so the cleanup hangs off the
62  // dialog's own close event instead.
63  onClose() {
64    document.body.classList.remove("overflow-hidden")
65    this.imageTarget.classList.add("opacity-0")
66    this.imageTarget.removeAttribute("src")
67  }
68
69  // A click on the ::backdrop is reported as a click on the dialog element itself.
70  clickBackdrop(event) {
71    if (event.target === this.dialogTarget) this.close()
72  }
73}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.