~james/core

core/web/src/lib/lightbox.svelte.ts -rw-r--r-- 1.9 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 16 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
export interface LightboxImage {
	src: string;
	alt: string;
	width?: number;
	height?: number;
	origin?: HTMLElement;
	class?: string;
}

class LightboxState {
	images = $state.raw<LightboxImage[]>([]);
	index = $state(0);
	open = $state(false);

	readonly current = $derived<LightboxImage | undefined>(this.images[this.index]);

	show(images: LightboxImage[], index: number): void {
		if (images.length === 0) return;
		this.images = images;
		this.index = Math.min(Math.max(index, 0), images.length - 1);
		this.open = true;
	}

	close(): void {
		this.open = false;
	}

	clear(): void {
		if (this.open) return;
		this.images = [];
		this.index = 0;
	}

	step(delta: number): void {
		const count = this.images.length;
		if (count < 2) return;
		this.index = (this.index + delta + count) % count;
	}
}

export const lightbox = new LightboxState();

const zoomable = (image: HTMLImageElement): boolean => image.closest("a") === null;

const describe = (image: HTMLImageElement): LightboxImage => ({
	src: image.currentSrc || image.src,
	alt: image.alt,
	width: image.naturalWidth || undefined,
	height: image.naturalHeight || undefined,
	origin: image
});

export const zoomMarkupImages = (): (() => void) => {
	const onClick = (event: MouseEvent): void => {
		if (event.defaultPrevented || event.button !== 0) return;
		if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;

		const image = event.target;
		if (!(image instanceof HTMLImageElement) || !zoomable(image)) return;

		const markup = image.closest(".markup");
		if (!markup) return;

		const gallery = [...markup.querySelectorAll("img")].filter(zoomable);
		const index = gallery.indexOf(image);
		if (index === -1) return;

		event.preventDefault();
		lightbox.show(gallery.map(describe), index);
	};

	document.addEventListener("click", onClick);
	return () => document.removeEventListener("click", onClick);
};