Files
web-petting/templates/partials/lightbox.html
T

165 lines
6.9 KiB
HTML
Raw Normal View History

2026-04-29 17:49:07 +03:00
<div class="lightbox-overlay" id="lightbox" onclick="closeLightbox(event)">
2026-08-09 01:05:47 +01:00
<button class="lightbox-close" type="button" onclick="closeLightbox(event)">&times;</button>
<div class="lightbox-stage" id="lightboxStage">
<span class="lightbox-loader" aria-hidden="true"></span>
<img id="lightboxImg" src="" alt="">
<video id="lightboxVideo" controls playsinline preload="auto" style="display:none;"></video>
</div>
2026-04-29 17:49:07 +03:00
</div>
<style>
2026-08-09 01:05:47 +01:00
.media-loading-frame {
position: relative; display: block; overflow: hidden; background: #eceaf5;
}
.media-loading-frame::after, .lightbox-loader {
content: ""; position: absolute; z-index: 3; top: 50%; left: 50%;
width: 22px; height: 22px; margin: -11px 0 0 -11px;
border: 3px solid rgba(124,108,255,.22); border-top-color: #7c6cff;
border-radius: 50%; animation: media-spinner .75s linear infinite;
}
.media-loading-frame:not(.is-loading)::after { display: none; }
.media-loading-frame img[data-media-load] { opacity: 0; transition: opacity .18s ease; }
.media-loading-frame.is-loaded img[data-media-load] { opacity: 1; }
.media-loading-frame.is-error::after {
display: block; content: "!"; width: 24px; height: 24px; margin: -12px 0 0 -12px;
border: 0; animation: none; color: #9b93bb; font-weight: 700; text-align: center;
}
.video-preview-sprite {
display: block !important; width: 400% !important; max-width: none !important;
height: 100% !important; object-fit: fill !important;
transform: translateX(0); transition: transform .08s linear;
}
@keyframes media-spinner { to { transform: rotate(360deg); } }
2026-04-29 17:49:07 +03:00
.lightbox-overlay {
display:none; position:fixed; inset:0; z-index:200;
2026-08-09 01:05:47 +01:00
background:rgba(9,8,18,.9); align-items:center; justify-content:center;
padding: 1rem;
2026-04-29 17:49:07 +03:00
}
.lightbox-overlay.is-open { display:flex; }
2026-08-09 01:05:47 +01:00
.lightbox-stage {
position: relative; display: flex; align-items: center; justify-content: center;
min-width: 96px; min-height: 96px; max-width: 94vw; max-height: 90vh;
2026-04-29 17:49:07 +03:00
}
2026-08-09 01:05:47 +01:00
.lightbox-stage img, .lightbox-stage video {
max-width:92vw; max-height:88vh; border-radius:10px; object-fit:contain;
background:#090909; box-shadow:0 18px 60px rgba(0,0,0,.42);
}
.lightbox-stage.is-loading img, .lightbox-stage.is-loading video { opacity: .18; }
.lightbox-stage:not(.is-loading) .lightbox-loader { display: none; }
.lightbox-stage.is-error .lightbox-loader {
display: block; animation: none; border: 0; color: white;
}
.lightbox-stage.is-error .lightbox-loader::after { content: "!"; font-size: 2rem; }
2026-04-29 17:49:07 +03:00
.lightbox-close {
2026-08-09 01:05:47 +01:00
position:absolute; top:0.75rem; right:1rem; background:rgba(0,0,0,.25); border:none;
2026-04-29 17:49:07 +03:00
color:#fff; font-size:2.2rem; cursor:pointer; line-height:1; z-index:201;
2026-08-09 01:05:47 +01:00
width:44px; height:44px; border-radius:50%;
2026-04-29 17:49:07 +03:00
}
</style>
<script>
2026-08-09 01:05:47 +01:00
(function() {
function finishThumbnail(image, loaded) {
var frame = image.closest('.media-loading-frame');
if (!frame) return;
frame.classList.remove('is-loading');
frame.classList.add(loaded ? 'is-loaded' : 'is-error');
}
document.querySelectorAll('img[data-media-load]').forEach(function(image) {
image.addEventListener('load', function() { finishThumbnail(image, true); });
image.addEventListener('error', function() { finishThumbnail(image, false); });
if (image.complete) finishThumbnail(image, image.naturalWidth > 0);
});
document.querySelectorAll('[data-video-preview]').forEach(function(preview) {
var sprite = preview.querySelector('[data-preview-frames]');
if (!sprite) return;
var timer;
var frame = 0;
var frames = Number(sprite.dataset.previewFrames) || 4;
function showFrame() {
sprite.style.transform = 'translateX(-' + (frame * 100 / frames) + '%)';
}
function start() {
if (timer || !preview.classList.contains('is-loaded')) return;
frame = 1;
showFrame();
timer = window.setInterval(function() {
frame = (frame + 1) % frames;
showFrame();
}, 650);
}
function stop() {
window.clearInterval(timer);
timer = null;
frame = 0;
showFrame();
}
preview.addEventListener('pointerenter', start);
preview.addEventListener('pointerleave', stop);
preview.closest('a').addEventListener('focus', start);
preview.closest('a').addEventListener('blur', stop);
});
})();
function setLightboxState(state) {
var stage = document.getElementById('lightboxStage');
stage.classList.remove('is-loading', 'is-error');
if (state) stage.classList.add(state);
}
2026-04-29 17:49:07 +03:00
function openLightbox(url, isVideo) {
var lb = document.getElementById('lightbox');
var img = document.getElementById('lightboxImg');
var vid = document.getElementById('lightboxVideo');
2026-08-09 01:05:47 +01:00
setLightboxState('is-loading');
lb.classList.add('is-open');
2026-04-29 17:49:07 +03:00
if (isVideo) {
img.style.display = 'none';
2026-08-09 01:05:47 +01:00
img.removeAttribute('src');
2026-04-29 17:49:07 +03:00
vid.style.display = '';
vid.src = url;
2026-08-09 01:05:47 +01:00
vid.load();
var playback = vid.play();
if (playback) playback.catch(function() {});
2026-04-29 17:49:07 +03:00
} else {
vid.style.display = 'none';
2026-08-09 01:05:47 +01:00
vid.pause();
vid.removeAttribute('src');
vid.load();
2026-04-29 17:49:07 +03:00
img.style.display = '';
img.src = url;
}
}
2026-08-09 01:05:47 +01:00
function closeLightbox(event) {
2026-04-29 17:49:07 +03:00
var lb = document.getElementById('lightbox');
2026-08-09 01:05:47 +01:00
if (event && event.target !== lb && !event.target.closest('.lightbox-close')) return;
2026-04-29 17:49:07 +03:00
lb.classList.remove('is-open');
var vid = document.getElementById('lightboxVideo');
2026-08-09 01:05:47 +01:00
vid.pause();
vid.removeAttribute('src');
vid.load();
var img = document.getElementById('lightboxImg');
img.removeAttribute('src');
setLightboxState(null);
2026-04-29 17:49:07 +03:00
}
2026-08-09 01:05:47 +01:00
document.getElementById('lightboxImg').addEventListener('load', function() { setLightboxState(null); });
document.getElementById('lightboxImg').addEventListener('error', function() { setLightboxState('is-error'); });
var lightboxVideo = document.getElementById('lightboxVideo');
['loadeddata', 'canplay', 'playing'].forEach(function(name) {
lightboxVideo.addEventListener(name, function() { setLightboxState(null); });
});
['waiting', 'stalled', 'seeking'].forEach(function(name) {
lightboxVideo.addEventListener(name, function() { setLightboxState('is-loading'); });
});
lightboxVideo.addEventListener('error', function() { setLightboxState('is-error'); });
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') closeLightbox(null);
});
document.addEventListener('click', function(event) {
var link = event.target.closest('[data-lightbox]');
if (!link) return;
event.preventDefault();
openLightbox(link.href, link.dataset.lightbox === 'video');
2026-04-29 17:49:07 +03:00
});
</script>