feat(ui): base.html, macros (badges, lightbox), shared.js, register_shared_ui
This commit is contained in:
@@ -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;
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user