Add image lightbox: click any image to view full-screen
Deploy to LXC / deploy (push) Successful in 20s
Deploy to LXC / deploy (push) Successful in 20s
Reusable ImageLightbox component with dark overlay, close on click/Escape, zoom-in cursor on thumbnails. Applied to both device and component detail pages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
src: string;
|
||||
alt?: string;
|
||||
onclose: () => void;
|
||||
}
|
||||
|
||||
let { src, alt = '', onclose }: Props = $props();
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape') onclose();
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={handleKeydown} />
|
||||
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/80" onclick={onclose}>
|
||||
<button onclick={onclose}
|
||||
class="absolute top-4 right-4 rounded-full bg-black/50 p-2 text-white hover:bg-black/70" aria-label="Close">
|
||||
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
||||
<img {src} {alt} class="max-h-[90vh] max-w-[90vw] rounded-lg object-contain shadow-2xl" onclick={(e) => e.stopPropagation()} />
|
||||
</div>
|
||||
@@ -4,6 +4,9 @@
|
||||
import { formatDate, timeAgo } from '$lib/utils/date.js';
|
||||
import ImageUpload from '$lib/components/ui/ImageUpload.svelte';
|
||||
import DocumentUpload from '$lib/components/ui/DocumentUpload.svelte';
|
||||
import ImageLightbox from '$lib/components/ui/ImageLightbox.svelte';
|
||||
|
||||
let lightboxSrc = $state<string | null>(null);
|
||||
|
||||
let { data, form } = $props();
|
||||
const c = $derived(data.component);
|
||||
@@ -100,7 +103,9 @@
|
||||
<div class="grid gap-2 sm:grid-cols-3">
|
||||
{#each data.images as img}
|
||||
<div class="group relative overflow-hidden rounded-md">
|
||||
<img src={img.filePath} alt={img.caption ?? c.title} class="h-32 w-full object-cover" />
|
||||
<button type="button" onclick={() => (lightboxSrc = img.filePath)} class="w-full">
|
||||
<img src={img.filePath} alt={img.caption ?? c.title} class="h-32 w-full cursor-zoom-in object-cover" />
|
||||
</button>
|
||||
<form method="POST" action="?/deleteImage" use:enhance
|
||||
class="absolute top-1 right-1 hidden group-hover:block">
|
||||
<input type="hidden" name="imageId" value={img.id} />
|
||||
@@ -348,3 +353,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if lightboxSrc}
|
||||
<ImageLightbox src={lightboxSrc} alt={c.title} onclose={() => (lightboxSrc = null)} />
|
||||
{/if}
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
import { DEVICE_CONDITIONS, DEVICE_LOG_TYPES } from '$lib/constants.js';
|
||||
import ImageUpload from '$lib/components/ui/ImageUpload.svelte';
|
||||
import DocumentUpload from '$lib/components/ui/DocumentUpload.svelte';
|
||||
import ImageLightbox from '$lib/components/ui/ImageLightbox.svelte';
|
||||
|
||||
let lightboxSrc = $state<string | null>(null);
|
||||
import { formatDate, timeAgo } from '$lib/utils/date.js';
|
||||
|
||||
let { data, form } = $props();
|
||||
@@ -160,7 +163,9 @@
|
||||
<div class="grid gap-2 sm:grid-cols-3">
|
||||
{#each data.images as img}
|
||||
<div class="group relative overflow-hidden rounded-md">
|
||||
<img src={img.filePath} alt={img.caption ?? data.device.title} class="h-32 w-full object-cover" />
|
||||
<button type="button" onclick={() => (lightboxSrc = img.filePath)} class="w-full">
|
||||
<img src={img.filePath} alt={img.caption ?? data.device.title} class="h-32 w-full cursor-zoom-in object-cover" />
|
||||
</button>
|
||||
<form method="POST" action="?/deleteImage" use:enhance
|
||||
class="absolute top-1 right-1 hidden group-hover:block">
|
||||
<input type="hidden" name="imageId" value={img.id} />
|
||||
@@ -707,3 +712,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if lightboxSrc}
|
||||
<ImageLightbox src={lightboxSrc} alt={data.device.title} onclose={() => (lightboxSrc = null)} />
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user