Files
michaelschiemer/resources/js/modules/admin/asset-preview.js
2025-11-24 21:28:25 +01:00

191 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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();
});