make image preview dialog
This commit is contained in:
parent
c7ab879a19
commit
60f4855b06
File diff suppressed because one or more lines are too long
88
index.html
88
index.html
|
|
@ -229,38 +229,27 @@
|
||||||
<h2 class="text-3xl md:text-4xl lg:text-5xl font-black text-on-surface mb-8 md:mb-12 lg:mb-16 tracking-tight" data-i18n="completed_projects">Реализованные проекты</h2>
|
<h2 class="text-3xl md:text-4xl lg:text-5xl font-black text-on-surface mb-8 md:mb-12 lg:mb-16 tracking-tight" data-i18n="completed_projects">Реализованные проекты</h2>
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-px bg-outline-variant overflow-hidden">
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-px bg-outline-variant overflow-hidden">
|
||||||
<!-- Project 1: Water Treatment Plant -->
|
<!-- Project 1: Water Treatment Plant -->
|
||||||
<div class="group relative bg-background overflow-hidden aspect-video lg:aspect-square">
|
<div class="group relative bg-background overflow-hidden aspect-video lg:aspect-square cursor-pointer"
|
||||||
<div class="carousel absolute inset-0" data-autoplay="4000">
|
data-lightbox
|
||||||
<img class="carousel-slide active w-full h-full object-cover" loading="lazy" decoding="async" src="public/tejen_1.webp" alt="Tejen water treatment plant 1" />
|
data-lightbox-images='["public/tejen_1.webp","public/tejen_2.webp","public/tejen_3.webp","public/tejen_4.webp","public/tejen_5.webp","public/tejen_6.webp","public/tejen_7.webp"]'
|
||||||
<img class="carousel-slide w-full h-full object-cover" loading="lazy" decoding="async" src="public/tejen_2.webp" alt="Tejen water treatment plant 2" />
|
data-lightbox-title="project1_title"
|
||||||
<img class="carousel-slide w-full h-full object-cover" loading="lazy" decoding="async" src="public/tejen_3.webp" alt="Tejen water treatment plant 3" />
|
data-lightbox-desc="project1_desc">
|
||||||
<img class="carousel-slide w-full h-full object-cover" loading="lazy" decoding="async" src="public/tejen_4.webp" alt="Tejen water treatment plant 4" />
|
<img class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105" loading="lazy" decoding="async" src="public/tejen_1.webp" alt="Tejen water treatment plant" />
|
||||||
<img class="carousel-slide w-full h-full object-cover" loading="lazy" decoding="async" src="public/tejen_5.webp" alt="Tejen water treatment plant 5" />
|
<div class="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 via-black/30 to-transparent p-4 md:p-8 lg:p-12 flex flex-col justify-end z-10 pointer-events-none">
|
||||||
<img class="carousel-slide w-full h-full object-cover" loading="lazy" decoding="async" src="public/tejen_6.webp" alt="Tejen water treatment plant 6" />
|
<h4 class="text-xl md:text-2xl lg:text-3xl font-bold text-white mb-1" data-i18n="project1_title">Водоочистное сооружение в г. Теджен</h4>
|
||||||
<img class="carousel-slide w-full h-full object-cover" loading="lazy" decoding="async" src="public/tejen_7.webp" alt="Tejen water treatment plant 7" />
|
<p class="text-sm text-white/80" data-i18n="project1_desc">Строительство водоочистного сооружения мощностью 30 000 м³ в сутки в городе Теджен Ахалского велаята — завершён 2024 г.</p>
|
||||||
<button class="carousel-prev" aria-label="Previous">‹</button>
|
|
||||||
<button class="carousel-next" aria-label="Next">›</button>
|
|
||||||
<div class="carousel-dots"></div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="absolute inset-0 bg-primary/80 opacity-0 group-hover:opacity-100 transition-opacity duration-500 p-4 md:p-8 lg:p-12 flex flex-col justify-end text-on-primary z-20">
|
|
||||||
<h4 class="text-xl md:text-2xl lg:text-3xl font-bold mb-2" data-i18n="project1_title">Водоочистное сооружение в г. Теджен</h4>
|
|
||||||
<p class="text-sm opacity-90" data-i18n="project1_desc">Строительство водоочистного сооружения мощностью 30 000 м³ в сутки в городе Теджен Ахалского велаята — завершён 2024 г.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Project 2: Power Supply System -->
|
<!-- Project 2: Power Supply System -->
|
||||||
<div class="group relative bg-background overflow-hidden aspect-video lg:aspect-square">
|
<div class="group relative bg-background overflow-hidden aspect-video lg:aspect-square cursor-pointer"
|
||||||
<div class="carousel absolute inset-0" data-autoplay="4000">
|
data-lightbox
|
||||||
<img class="carousel-slide active w-full h-full object-cover" loading="lazy" decoding="async" src="public/electric_1.webp" alt="Power supply infrastructure 1" />
|
data-lightbox-images='["public/electric_1.webp","public/electric_2.webp"]'
|
||||||
<img class="carousel-slide w-full h-full object-cover" loading="lazy" decoding="async" src="public/electric_2.webp" alt="Power supply infrastructure 2" />
|
data-lightbox-title="project2_title"
|
||||||
<button class="carousel-prev" aria-label="Previous">‹</button>
|
data-lightbox-desc="project2_desc">
|
||||||
<button class="carousel-next" aria-label="Next">›</button>
|
<img class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105" loading="lazy" decoding="async" src="public/electric_1.webp" alt="Power supply infrastructure" />
|
||||||
<div class="carousel-dots"></div>
|
<div class="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 via-black/30 to-transparent p-4 md:p-8 lg:p-12 flex flex-col justify-end z-10 pointer-events-none">
|
||||||
</div>
|
<h4 class="text-xl md:text-2xl lg:text-3xl font-bold text-white mb-1" data-i18n="project2_title">Электроснабжение водоочистного сооружения</h4>
|
||||||
<div
|
<p class="text-sm text-white/80" data-i18n="project2_desc">Строительно-монтажные работы по внешней системе электроснабжения водоочистного сооружения мощностью 30 000 м³ в сутки в г. Теджен — завершён 2026 г.</p>
|
||||||
class="absolute inset-0 bg-primary/80 opacity-0 group-hover:opacity-100 transition-opacity duration-500 p-4 md:p-8 lg:p-12 flex flex-col justify-end text-on-primary z-20">
|
|
||||||
<h4 class="text-xl md:text-2xl lg:text-3xl font-bold mb-2" data-i18n="project2_title">Электроснабжение водоочистного сооружения</h4>
|
|
||||||
<p class="text-sm opacity-90" data-i18n="project2_desc">Строительно-монтажные работы по внешней системе электроснабжения водоочистного сооружения мощностью 30 000 м³ в сутки в г. Теджен — завершён 2026 г.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -280,14 +269,22 @@
|
||||||
<h3 class="text-xl md:text-2xl lg:text-3xl font-black mt-2 mb-4 uppercase tracking-tight" data-i18n="current1_title">Водоочистное сооружение в Бабадайханском районе</h3>
|
<h3 class="text-xl md:text-2xl lg:text-3xl font-black mt-2 mb-4 uppercase tracking-tight" data-i18n="current1_title">Водоочистное сооружение в Бабадайханском районе</h3>
|
||||||
<p class="text-primary-fixed leading-relaxed" data-i18n="current1_desc">Строительство водоочистного сооружения мощностью 20 000 м³ в сутки в Бабадайханском районе Ахалского велаята (Туркменистан).</p>
|
<p class="text-primary-fixed leading-relaxed" data-i18n="current1_desc">Строительство водоочистного сооружения мощностью 20 000 м³ в сутки в Бабадайханском районе Ахалского велаята (Туркменистан).</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative aspect-video overflow-hidden">
|
<div class="group/img relative aspect-video overflow-hidden cursor-pointer"
|
||||||
<img class="w-full h-full object-cover" loading="lazy" decoding="async" src="public/babdayhan.webp" alt="Babadayhan water treatment plant" />
|
data-lightbox
|
||||||
|
data-lightbox-images='["public/babdayhan.webp"]'
|
||||||
|
data-lightbox-title="current1_title"
|
||||||
|
data-lightbox-desc="current1_desc">
|
||||||
|
<img class="w-full h-full object-cover transition-transform duration-500 group-hover/img:scale-105" loading="lazy" decoding="async" src="public/babdayhan.webp" alt="Babadayhan water treatment plant" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Current Project 2: carousel left, text right -->
|
<!-- Current Project 2: carousel left, text right -->
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 md:gap-12 items-center">
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 md:gap-12 items-center">
|
||||||
<div class="relative aspect-video overflow-hidden order-2 lg:order-1">
|
<div class="group/img relative aspect-video overflow-hidden order-2 lg:order-1 cursor-pointer"
|
||||||
<img class="w-full h-full object-cover" loading="lazy" decoding="async" src="public/garashsyzlyk.webp" alt="Garashsyzlyk water supply" />
|
data-lightbox
|
||||||
|
data-lightbox-images='["public/garashsyzlyk.webp"]'
|
||||||
|
data-lightbox-title="current2_title"
|
||||||
|
data-lightbox-desc="current2_desc">
|
||||||
|
<img class="w-full h-full object-cover transition-transform duration-500 group-hover/img:scale-105" loading="lazy" decoding="async" src="public/garashsyzlyk.webp" alt="Garashsyzlyk water supply" />
|
||||||
</div>
|
</div>
|
||||||
<div class="order-1 lg:order-2">
|
<div class="order-1 lg:order-2">
|
||||||
<span class="text-xs uppercase font-bold tracking-widest text-primary-fixed/60" data-i18n="current2_status">Статус: В процессе</span>
|
<span class="text-xs uppercase font-bold tracking-widest text-primary-fixed/60" data-i18n="current2_status">Статус: В процессе</span>
|
||||||
|
|
@ -405,8 +402,31 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<!-- Lightbox -->
|
||||||
|
<div id="lightbox" class="lightbox fixed inset-0 z-[80] flex items-center justify-center p-4 md:p-8 bg-black/80">
|
||||||
|
<div id="lightbox-content" class="relative w-full max-w-5xl max-h-[90vh] flex flex-col">
|
||||||
|
<div id="lightbox-counter" class="absolute top-4 left-4 z-20 text-white/90 text-sm font-bold bg-black/50 backdrop-blur-sm px-3 py-1.5 rounded-sm"></div>
|
||||||
|
<button id="lightbox-close" class="absolute top-4 right-4 z-20 w-10 h-10 flex items-center justify-center text-white/90 hover:text-white bg-black/50 hover:bg-black/70 backdrop-blur-sm rounded-sm transition-colors" aria-label="Close">
|
||||||
|
<span class="material-symbols-outlined">close</span>
|
||||||
|
</button>
|
||||||
|
<div class="relative flex-1 min-h-0 flex items-center justify-center bg-black rounded-t-sm overflow-hidden">
|
||||||
|
<img id="lightbox-image" class="max-w-full max-h-[calc(90vh-5rem)] object-contain select-none" draggable="false" alt="" />
|
||||||
|
<button id="lightbox-prev" class="absolute left-3 top-1/2 -translate-y-1/2 w-11 h-11 md:w-12 md:h-12 flex items-center justify-center bg-black/40 hover:bg-black/60 text-white/90 hover:text-white backdrop-blur-sm rounded-sm transition-colors" aria-label="Previous">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
|
||||||
|
</button>
|
||||||
|
<button id="lightbox-next" class="absolute right-3 top-1/2 -translate-y-1/2 w-11 h-11 md:w-12 md:h-12 flex items-center justify-center bg-black/40 hover:bg-black/60 text-white/90 hover:text-white backdrop-blur-sm rounded-sm transition-colors" aria-label="Next">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white p-4 md:p-6 rounded-b-sm">
|
||||||
|
<h4 id="lightbox-title" class="text-base md:text-lg font-bold text-on-surface"></h4>
|
||||||
|
<p id="lightbox-desc" class="text-sm text-on-surface-variant mt-1"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="js/i18n.js"></script>
|
<script src="js/i18n.js"></script>
|
||||||
<script src="js/carousel.js"></script>
|
<script src="js/lightbox.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const drawer = document.getElementById('mobile-drawer');
|
const drawer = document.getElementById('mobile-drawer');
|
||||||
const backdrop = document.getElementById('drawer-backdrop');
|
const backdrop = document.getElementById('drawer-backdrop');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
(function () {
|
||||||
|
const lightbox = document.getElementById('lightbox');
|
||||||
|
const content = document.getElementById('lightbox-content');
|
||||||
|
const image = document.getElementById('lightbox-image');
|
||||||
|
const title = document.getElementById('lightbox-title');
|
||||||
|
const desc = document.getElementById('lightbox-desc');
|
||||||
|
const counter = document.getElementById('lightbox-counter');
|
||||||
|
const prevBtn = document.getElementById('lightbox-prev');
|
||||||
|
const nextBtn = document.getElementById('lightbox-next');
|
||||||
|
const closeBtn = document.getElementById('lightbox-close');
|
||||||
|
|
||||||
|
let images = [];
|
||||||
|
let current = 0;
|
||||||
|
let touchStartX = 0;
|
||||||
|
|
||||||
|
function open(imgArray, titleKey, descKey) {
|
||||||
|
images = imgArray;
|
||||||
|
current = 0;
|
||||||
|
|
||||||
|
const lang = localStorage.getItem('lang') || 'ru';
|
||||||
|
title.textContent = translations[lang][titleKey] || '';
|
||||||
|
title.dataset.i18n = titleKey;
|
||||||
|
desc.textContent = translations[lang][descKey] || '';
|
||||||
|
desc.dataset.i18n = descKey;
|
||||||
|
|
||||||
|
updateImage();
|
||||||
|
updateNav();
|
||||||
|
lightbox.classList.add('open');
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
lightbox.classList.remove('open');
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function goTo(index) {
|
||||||
|
current = (index + images.length) % images.length;
|
||||||
|
updateImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateImage() {
|
||||||
|
image.src = images[current];
|
||||||
|
if (images.length > 1) {
|
||||||
|
counter.textContent = (current + 1) + ' / ' + images.length;
|
||||||
|
counter.style.display = '';
|
||||||
|
} else {
|
||||||
|
counter.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateNav() {
|
||||||
|
const multi = images.length > 1;
|
||||||
|
prevBtn.style.display = multi ? '' : 'none';
|
||||||
|
nextBtn.style.display = multi ? '' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close handlers
|
||||||
|
closeBtn.addEventListener('click', close);
|
||||||
|
lightbox.addEventListener('click', function (e) {
|
||||||
|
if (e.target === lightbox) close();
|
||||||
|
});
|
||||||
|
content.addEventListener('click', function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
prevBtn.addEventListener('click', function () { goTo(current - 1); });
|
||||||
|
nextBtn.addEventListener('click', function () { goTo(current + 1); });
|
||||||
|
|
||||||
|
// Keyboard
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (!lightbox.classList.contains('open')) return;
|
||||||
|
if (e.key === 'Escape') { close(); e.preventDefault(); }
|
||||||
|
if (e.key === 'ArrowLeft') { goTo(current - 1); e.preventDefault(); }
|
||||||
|
if (e.key === 'ArrowRight') { goTo(current + 1); e.preventDefault(); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Touch swipe
|
||||||
|
lightbox.addEventListener('touchstart', function (e) {
|
||||||
|
touchStartX = e.changedTouches[0].screenX;
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
lightbox.addEventListener('touchend', function (e) {
|
||||||
|
var diff = touchStartX - e.changedTouches[0].screenX;
|
||||||
|
if (Math.abs(diff) > 50) {
|
||||||
|
goTo(diff > 0 ? current + 1 : current - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize triggers
|
||||||
|
document.querySelectorAll('[data-lightbox]').forEach(function (el) {
|
||||||
|
el.addEventListener('click', function () {
|
||||||
|
var imgs = JSON.parse(el.dataset.lightboxImages);
|
||||||
|
open(imgs, el.dataset.lightboxTitle, el.dataset.lightboxDesc);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
@ -90,28 +90,16 @@
|
||||||
.lang-btn { color: rgba(42, 52, 57, 0.4); }
|
.lang-btn { color: rgba(42, 52, 57, 0.4); }
|
||||||
.lang-btn.active { color: #3a5f94; background: rgba(58, 95, 148, 0.1); }
|
.lang-btn.active { color: #3a5f94; background: rgba(58, 95, 148, 0.1); }
|
||||||
|
|
||||||
.carousel { position: relative; }
|
/* Lightbox */
|
||||||
.carousel-slide { position: absolute; inset: 0; opacity: 0; transition: opacity 1s ease; }
|
.lightbox {
|
||||||
.carousel-slide.active { opacity: 0.9; }
|
opacity: 0;
|
||||||
.carousel-prev, .carousel-next {
|
pointer-events: none;
|
||||||
position: absolute; top: 50%; transform: translateY(-50%); z-index: 10;
|
transition: opacity 0.25s ease;
|
||||||
width: 40px; height: 40px; background: rgba(255, 255, 255, 0.8); border: none;
|
|
||||||
font-size: 24px; line-height: 1; cursor: pointer; color: #2a3439;
|
|
||||||
opacity: 0; transition: opacity 0.3s;
|
|
||||||
}
|
}
|
||||||
.carousel:hover .carousel-prev, .carousel:hover .carousel-next { opacity: 1; }
|
.lightbox.open {
|
||||||
.carousel-prev { left: 12px; }
|
opacity: 1;
|
||||||
.carousel-next { right: 12px; }
|
pointer-events: auto;
|
||||||
.carousel-prev:hover, .carousel-next:hover { background: #fff; }
|
|
||||||
.carousel-dots {
|
|
||||||
position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%);
|
|
||||||
display: flex; gap: 6px; z-index: 10;
|
|
||||||
}
|
}
|
||||||
.carousel-dot {
|
|
||||||
width: 8px; height: 8px; border-radius: 50%; border: none; cursor: pointer;
|
|
||||||
background: rgba(255, 255, 255, 0.5); transition: background 0.3s;
|
|
||||||
}
|
|
||||||
.carousel-dot.active { background: #fff; }
|
|
||||||
|
|
||||||
/* Mobile drawer */
|
/* Mobile drawer */
|
||||||
.mobile-drawer { transform: translateX(100%); transition: transform 0.3s ease; }
|
.mobile-drawer { transform: translateX(100%); transition: transform 0.3s ease; }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue