Some checks failed
Deploy Application / deploy (push) Has been cancelled
191 lines
6.2 KiB
JavaScript
191 lines
6.2 KiB
JavaScript
/**
|
||
* Asset Preview Module
|
||
*
|
||
* Handles asset preview thumbnails, lightbox, and detail views
|
||
*/
|
||
|
||
export class AssetPreview {
|
||
constructor() {
|
||
this.lightbox = null;
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
this.setupThumbnailClicks();
|
||
this.setupLightbox();
|
||
this.setupCopyUrl();
|
||
this.setupVariantDeletion();
|
||
}
|
||
|
||
setupThumbnailClicks() {
|
||
document.addEventListener('click', (e) => {
|
||
const thumbnail = e.target.closest('.asset-preview-thumbnail');
|
||
if (!thumbnail) return;
|
||
|
||
const assetId = thumbnail.dataset.assetId;
|
||
if (!assetId) return;
|
||
|
||
// Navigate to detail view
|
||
window.location.href = `/admin/assets/${assetId}`;
|
||
});
|
||
}
|
||
|
||
setupLightbox() {
|
||
// Create lightbox HTML
|
||
const lightboxHTML = `
|
||
<div class="asset-lightbox" id="asset-lightbox" style="display: none;">
|
||
<div class="asset-lightbox__content">
|
||
<img class="asset-lightbox__image" src="" alt="Asset Preview" />
|
||
<button class="asset-lightbox__close" data-close-lightbox>×</button>
|
||
<div class="asset-lightbox__info">
|
||
<p class="asset-lightbox__filename"></p>
|
||
<p class="asset-lightbox__size"></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// Add lightbox to body if not exists
|
||
if (!document.getElementById('asset-lightbox')) {
|
||
document.body.insertAdjacentHTML('beforeend', lightboxHTML);
|
||
this.lightbox = document.getElementById('asset-lightbox');
|
||
} else {
|
||
this.lightbox = document.getElementById('asset-lightbox');
|
||
}
|
||
|
||
// Setup image clicks for lightbox
|
||
document.addEventListener('click', (e) => {
|
||
const img = e.target.closest('img[data-lightbox]');
|
||
if (!img) return;
|
||
|
||
e.preventDefault();
|
||
this.openLightbox(img.src, img.alt, img.dataset.filename, img.dataset.size);
|
||
});
|
||
|
||
// Close lightbox
|
||
document.addEventListener('click', (e) => {
|
||
if (e.target.closest('[data-close-lightbox]') || e.target === this.lightbox) {
|
||
this.closeLightbox();
|
||
}
|
||
});
|
||
|
||
// Close on Escape key
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Escape' && this.lightbox && this.lightbox.style.display !== 'none') {
|
||
this.closeLightbox();
|
||
}
|
||
});
|
||
}
|
||
|
||
openLightbox(imageUrl, alt, filename, size) {
|
||
if (!this.lightbox) return;
|
||
|
||
const img = this.lightbox.querySelector('.asset-lightbox__image');
|
||
const filenameEl = this.lightbox.querySelector('.asset-lightbox__filename');
|
||
const sizeEl = this.lightbox.querySelector('.asset-lightbox__size');
|
||
|
||
img.src = imageUrl;
|
||
img.alt = alt;
|
||
if (filenameEl) filenameEl.textContent = filename || alt;
|
||
if (sizeEl) sizeEl.textContent = size || '';
|
||
|
||
this.lightbox.style.display = 'flex';
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
|
||
closeLightbox() {
|
||
if (!this.lightbox) return;
|
||
|
||
this.lightbox.style.display = 'none';
|
||
document.body.style.overflow = '';
|
||
}
|
||
|
||
setupCopyUrl() {
|
||
document.addEventListener('click', async (e) => {
|
||
const copyButton = e.target.closest('[data-copy-url]');
|
||
if (!copyButton) return;
|
||
|
||
e.preventDefault();
|
||
|
||
try {
|
||
const url = copyButton.href;
|
||
const response = await fetch(url);
|
||
const data = await response.json();
|
||
const assetUrl = data.url || url;
|
||
|
||
await navigator.clipboard.writeText(assetUrl);
|
||
|
||
// Show success feedback
|
||
const originalText = copyButton.textContent;
|
||
copyButton.textContent = 'Copied!';
|
||
copyButton.classList.add('copied');
|
||
|
||
setTimeout(() => {
|
||
copyButton.textContent = originalText;
|
||
copyButton.classList.remove('copied');
|
||
}, 2000);
|
||
} catch (error) {
|
||
console.error('Failed to copy URL:', error);
|
||
alert('Failed to copy URL');
|
||
}
|
||
});
|
||
}
|
||
|
||
setupVariantDeletion() {
|
||
document.addEventListener('click', async (e) => {
|
||
const deleteButton = e.target.closest('[data-delete-variant]');
|
||
if (!deleteButton) return;
|
||
|
||
e.preventDefault();
|
||
|
||
const variant = deleteButton.dataset.variant;
|
||
const assetId = deleteButton.closest('[data-asset-id]')?.dataset.assetId ||
|
||
window.location.pathname.split('/').pop();
|
||
|
||
if (!confirm(`Are you sure you want to delete variant "${variant}"?`)) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
deleteButton.disabled = true;
|
||
deleteButton.textContent = 'Deleting...';
|
||
|
||
const response = await fetch(`/api/v1/assets/${assetId}/variants/${variant}`, {
|
||
method: 'DELETE',
|
||
headers: {
|
||
'X-Requested-With': 'XMLHttpRequest',
|
||
},
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP ${response.status}`);
|
||
}
|
||
|
||
// Remove variant item from DOM
|
||
deleteButton.closest('.variant-item')?.remove();
|
||
|
||
// Show success message
|
||
if (window.Toast) {
|
||
window.Toast.success(`Variant "${variant}" deleted successfully`);
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to delete variant:', error);
|
||
deleteButton.disabled = false;
|
||
deleteButton.textContent = 'Delete';
|
||
|
||
if (window.Toast) {
|
||
window.Toast.error(`Failed to delete variant: ${error.message}`);
|
||
} else {
|
||
alert(`Failed to delete variant: ${error.message}`);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// Auto-initialize
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
new AssetPreview();
|
||
});
|
||
|