tangled
alpha
login
or
join now
dunkirk.sh
/
zera
5
fork
atom
the home site for me: also iteration 3 or 4 of my site
5
fork
atom
overview
issues
pulls
pipelines
feat: move lightbox over to web components
dunkirk.sh
1 month ago
8cd4e7f4
481559f0
verified
This commit was signed with the committer's
known signature
.
dunkirk.sh
SSH Key Fingerprint:
SHA256:DqcG0RXYExE26KiWo3VxJnsxswN1QNfTBvB+bdSpk80=
+132
-72
3 changed files
expand all
collapse all
unified
split
static
js
lightbox.js
templates
shortcodes
img.html
imgs.html
+130
-70
static/js/lightbox.js
···
1
-
let currentLightboxImages = [];
2
-
let currentLightboxIndex = 0;
0
0
0
0
0
0
3
4
-
function openLightbox(src) {
5
-
currentLightboxImages = [src];
6
-
currentLightboxIndex = 0;
7
-
showLightbox();
8
-
}
9
10
-
function openLightboxGroup(element) {
11
-
const group = element.closest('.img-group');
12
-
const images = Array.from(group.querySelectorAll('img')).map(img => img.src);
13
-
const clickedImg = element.querySelector('img');
14
-
15
-
currentLightboxImages = images;
16
-
currentLightboxIndex = images.indexOf(clickedImg.src);
17
-
showLightbox();
18
-
}
19
20
-
function showLightbox() {
21
-
let lightbox = document.getElementById('lightbox');
22
-
23
-
if (!lightbox) {
24
-
lightbox = document.createElement('div');
25
-
lightbox.id = 'lightbox';
26
-
lightbox.innerHTML = `
27
<div class="lightbox-content">
28
-
<button class="lightbox-close" onclick="closeLightbox()">×</button>
29
-
<img id="lightbox-img" src="" alt="">
30
<div class="lightbox-controls">
31
-
<button class="lightbox-prev" onclick="prevImage()">←</button>
32
-
<button class="lightbox-next" onclick="nextImage()">→</button>
33
</div>
34
</div>
35
`;
36
-
document.body.appendChild(lightbox);
37
-
38
-
lightbox.addEventListener('click', (e) => {
39
-
if (e.target === lightbox) closeLightbox();
0
0
0
0
0
0
0
0
0
0
40
});
41
-
42
-
document.addEventListener('keydown', handleKeyPress);
0
43
}
44
-
45
-
updateLightboxImage();
46
-
lightbox.style.display = 'flex';
47
-
document.body.style.overflow = 'hidden';
48
-
}
0
0
0
0
0
0
0
0
0
0
49
50
-
function closeLightbox() {
51
-
const lightbox = document.getElementById('lightbox');
52
-
if (lightbox) {
53
-
lightbox.style.display = 'none';
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
54
document.body.style.overflow = '';
55
}
56
-
}
57
58
-
function updateLightboxImage() {
59
-
const img = document.getElementById('lightbox-img');
60
-
const controls = document.querySelector('.lightbox-controls');
61
-
62
-
img.src = currentLightboxImages[currentLightboxIndex];
63
-
64
-
if (currentLightboxImages.length === 1) {
65
-
controls.style.display = 'none';
66
-
} else {
67
-
controls.style.display = 'flex';
0
0
0
68
}
69
-
}
70
71
-
function prevImage() {
72
-
currentLightboxIndex = (currentLightboxIndex - 1 + currentLightboxImages.length) % currentLightboxImages.length;
73
-
updateLightboxImage();
0
0
74
}
75
76
-
function nextImage() {
77
-
currentLightboxIndex = (currentLightboxIndex + 1) % currentLightboxImages.length;
78
-
updateLightboxImage();
0
0
0
0
79
}
80
81
-
function handleKeyPress(e) {
82
-
const lightbox = document.getElementById('lightbox');
83
-
if (!lightbox || lightbox.style.display !== 'flex') return;
84
-
85
-
if (e.key === 'Escape') {
86
-
closeLightbox();
87
-
} else if (e.key === 'ArrowLeft') {
88
-
prevImage();
89
-
} else if (e.key === 'ArrowRight') {
90
-
nextImage();
91
}
92
}
···
1
+
class ImageLightbox extends HTMLElement {
2
+
constructor() {
3
+
super();
4
+
this.currentImages = [];
5
+
this.currentIndex = 0;
6
+
this.handleImageClick = this.handleImageClick.bind(this);
7
+
this.handleKeyPress = this.handleKeyPress.bind(this);
8
+
}
9
10
+
connectedCallback() {
11
+
this.render();
12
+
this.setupEventListeners();
13
+
}
0
14
15
+
disconnectedCallback() {
16
+
this.cleanup();
17
+
}
0
0
0
0
0
0
18
19
+
render() {
20
+
this.id = 'lightbox';
21
+
this.style.display = 'none';
22
+
this.innerHTML = `
0
0
0
23
<div class="lightbox-content">
24
+
<button class="lightbox-close" aria-label="Close lightbox">×</button>
25
+
<img id="lightbox-img" src="" alt="" />
26
<div class="lightbox-controls">
27
+
<button class="lightbox-prev" aria-label="Previous image">←</button>
28
+
<button class="lightbox-next" aria-label="Next image">→</button>
29
</div>
30
</div>
31
`;
32
+
}
33
+
34
+
setupEventListeners() {
35
+
// Delegate clicks on images with data-lightbox attribute
36
+
document.addEventListener('click', this.handleImageClick);
37
+
38
+
// Lightbox controls
39
+
this.querySelector('.lightbox-close').addEventListener('click', () => this.close());
40
+
this.querySelector('.lightbox-prev').addEventListener('click', () => this.prev());
41
+
this.querySelector('.lightbox-next').addEventListener('click', () => this.next());
42
+
43
+
// Click backdrop to close
44
+
this.addEventListener('click', (e) => {
45
+
if (e.target === this) this.close();
46
});
47
+
48
+
// Keyboard navigation
49
+
document.addEventListener('keydown', this.handleKeyPress);
50
}
51
+
52
+
handleImageClick(e) {
53
+
// Check if clicked element or its parent has data-lightbox
54
+
const target = e.target.closest('[data-lightbox]');
55
+
if (!target) return;
56
+
57
+
e.preventDefault();
58
+
59
+
const group = target.dataset.lightboxGroup;
60
+
61
+
if (group) {
62
+
// Find all images in the same group
63
+
const groupElements = Array.from(
64
+
document.querySelectorAll(`[data-lightbox-group="${group}"]`)
65
+
);
66
67
+
// Extract image sources
68
+
this.currentImages = groupElements.map(el => {
69
+
const img = el.tagName === 'IMG' ? el : el.querySelector('img');
70
+
return img ? img.src : null;
71
+
}).filter(Boolean);
72
+
73
+
// Find the index of the clicked image
74
+
this.currentIndex = groupElements.indexOf(target);
75
+
} else {
76
+
// Single image
77
+
const img = target.tagName === 'IMG' ? target : target.querySelector('img');
78
+
this.currentImages = img ? [img.src] : [];
79
+
this.currentIndex = 0;
80
+
}
81
+
82
+
if (this.currentImages.length > 0) {
83
+
this.open();
84
+
}
85
+
}
86
+
87
+
handleKeyPress(e) {
88
+
if (this.style.display !== 'flex') return;
89
+
90
+
if (e.key === 'Escape') {
91
+
this.close();
92
+
} else if (e.key === 'ArrowLeft') {
93
+
this.prev();
94
+
} else if (e.key === 'ArrowRight') {
95
+
this.next();
96
+
}
97
+
}
98
+
99
+
open() {
100
+
this.style.display = 'flex';
101
+
const controls = this.querySelector('.lightbox-controls');
102
+
if (this.currentImages.length > 1) {
103
+
controls.style.display = 'flex';
104
+
} else {
105
+
controls.style.display = 'none';
106
+
}
107
+
document.body.style.overflow = 'hidden';
108
+
this.updateImage();
109
+
}
110
+
111
+
close() {
112
+
this.style.display = 'none';
113
document.body.style.overflow = '';
114
}
0
115
116
+
updateImage() {
117
+
const img = this.querySelector('#lightbox-img');
118
+
img.src = this.currentImages[this.currentIndex];
119
+
}
120
+
121
+
prev() {
122
+
this.currentIndex = (this.currentIndex - 1 + this.currentImages.length) % this.currentImages.length;
123
+
this.updateImage();
124
+
}
125
+
126
+
next() {
127
+
this.currentIndex = (this.currentIndex + 1) % this.currentImages.length;
128
+
this.updateImage();
129
}
0
130
131
+
cleanup() {
132
+
document.removeEventListener('click', this.handleImageClick);
133
+
document.removeEventListener('keydown', this.handleKeyPress);
134
+
document.body.style.overflow = '';
135
+
}
136
}
137
138
+
customElements.define('image-lightbox', ImageLightbox);
139
+
140
+
// Auto-initialize: add lightbox to page if not already present
141
+
if (document.readyState === 'loading') {
142
+
document.addEventListener('DOMContentLoaded', initLightbox);
143
+
} else {
144
+
initLightbox();
145
}
146
147
+
function initLightbox() {
148
+
if (!document.querySelector('image-lightbox')) {
149
+
const lightbox = document.createElement('image-lightbox');
150
+
document.body.appendChild(lightbox);
0
0
0
0
0
0
151
}
152
}
+1
-1
templates/shortcodes/img.html
···
1
<figure {% if class %}class="{{class}}" {% else %}class="center" {% endif %}>
2
-
<div class="img-container" onclick="openLightbox('{{id}}')">
3
<img src="{{id}}" {% if alt %}alt="{{alt}}" {% endif %} />
4
</div>
5
{% if caption %}
···
1
<figure {% if class %}class="{{class}}" {% else %}class="center" {% endif %}>
2
+
<div class="img-container" data-lightbox>
3
<img src="{{id}}" {% if alt %}alt="{{alt}}" {% endif %} />
4
</div>
5
{% if caption %}
+1
-1
templates/shortcodes/imgs.html
···
3
{% set images = id | split(pat=",") %}
4
{% set alts = alt | default(value="") | split(pat=",") %}
5
{% for image in images %}
6
-
<div class="img-container" onclick="openLightboxGroup(this)">
7
<img src="{{image | trim}}" {% if alts[loop.index0] %}alt="{{alts[loop.index0] | trim}}" {% endif %} />
8
</div>
9
{% endfor %}
···
3
{% set images = id | split(pat=",") %}
4
{% set alts = alt | default(value="") | split(pat=",") %}
5
{% for image in images %}
6
+
<div class="img-container" data-lightbox data-lightbox-group="group-{{ id | slugify }}">
7
<img src="{{image | trim}}" {% if alts[loop.index0] %}alt="{{alts[loop.index0] | trim}}" {% endif %} />
8
</div>
9
{% endfor %}