feat(ui): base.html, macros (badges, lightbox), shared.js, register_shared_ui

This commit is contained in:
2026-06-27 17:11:49 +02:00
parent 4db684fbdc
commit c36d93e3c7
5 changed files with 151 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
// Generic grid + lightbox behavior shared across photoflow apps.
// Apps spread this into their own Alpine component: { ...photoGrid(), ...appLogic }
function photoGrid() {
return {
focused: null,
lightboxOpen: false,
cards() {
return [...document.querySelectorAll('.photo-card')]
.filter(c => c.style.display !== 'none');
},
select(el) {
if (this.focused) this.focused.classList.remove('ring-4', 'ring-white', 'z-10');
this.focused = el;
if (el) {
el.classList.add('ring-4', 'ring-white', 'z-10');
el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
if (this.lightboxOpen) this.updateLightbox();
}
},
selectFirst() {
const cards = this.cards();
if (cards.length) this.select(cards[0]);
},
navigate(dir) {
const cards = this.cards();
if (!cards.length) return;
const idx = this.focused ? cards.indexOf(this.focused) : -1;
const next = cards[Math.max(0, Math.min(cards.length - 1, idx + dir))];
if (next) this.select(next);
},
openLightbox(el) {
this.select(el);
this.lightboxOpen = true;
document.getElementById('lb').style.display = '';
this.updateLightbox();
},
closeLightbox() {
this.lightboxOpen = false;
const lb = document.getElementById('lb');
if (lb) lb.style.display = 'none';
},
updateLightbox() {
const el = this.focused;
if (!el) return;
const img = el.querySelector('img');
const lbImg = document.getElementById('lb-img');
if (img && lbImg) lbImg.src = img.src;
},
};
}