cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 1 | <script lang="ts"> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 2 | import { tick } from 'svelte'; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 3 | import { lightbox } from './lightbox'; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 4 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 5 | let closeBtn: HTMLButtonElement | null = null; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 6 | let returnFocus: HTMLElement | null = null; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 7 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 8 | $: if ($lightbox) void focusClose(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 9 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 10 | async function focusClose(): Promise<void> { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 11 | if (!returnFocus) returnFocus = document.activeElement as HTMLElement | null; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 12 | await tick(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 13 | closeBtn?.focus(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 14 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 15 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 16 | function close(): void { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 17 | lightbox.set(null); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 18 | returnFocus?.focus(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 19 | returnFocus = null; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 20 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 21 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 22 | function onKey(ev: KeyboardEvent): void { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 23 | if ($lightbox && ev.key === 'Escape') { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 24 | ev.stopPropagation(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 25 | close(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 26 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 27 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 28 | </script> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 29 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 30 | <svelte:window on:keydown|capture={onKey} /> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 31 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 32 | {#if $lightbox} |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 33 | <!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions --> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 34 | <div class="lightbox" role="dialog" aria-modal="true" aria-label={$lightbox.filename} on:click|self={close}> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 35 | <div class="lightbox-bar"> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 36 | <span class="lightbox-name">{$lightbox.filename}</span> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 37 | <a class="ghost-btn" href={$lightbox.src} target="_blank" rel="noopener noreferrer">Open original</a> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 38 | <a class="ghost-btn" href={$lightbox.src} download={$lightbox.filename}>Download</a> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 39 | <button type="button" class="icon-btn" aria-label="Close image" title="Close (Esc)" bind:this={closeBtn} on:click={close}> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 40 | <span aria-hidden="true">✕</span> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 41 | </button> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 42 | </div> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 43 | <!-- svelte-ignore a11y_click_events_have_key_events a11y_no_noninteractive_element_interactions --> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 44 | <img class="lightbox-image" src={$lightbox.src} alt={$lightbox.alt} on:click={close} /> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 45 | </div> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 46 | {/if} |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 47 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 48 | <style> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 49 | .lightbox { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 50 | position: fixed; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 51 | inset: 0; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 52 | z-index: 100; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 53 | display: flex; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 54 | flex-direction: column; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 55 | align-items: center; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 56 | justify-content: center; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 57 | gap: 12px; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 58 | padding: 56px 24px 24px; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 59 | background: rgb(0 0 0 / 0.82); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 60 | backdrop-filter: blur(4px); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 61 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 62 | .lightbox-bar { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 63 | position: absolute; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 64 | top: 0; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 65 | left: 0; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 66 | right: 0; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 67 | display: flex; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 68 | align-items: center; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 69 | gap: 8px; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 70 | padding: 10px 16px; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 71 | color: #fff; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 72 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 73 | .lightbox-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 74 | .lightbox-bar a { text-decoration: none; } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 75 | .lightbox-image { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 76 | max-width: 100%; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 77 | max-height: 100%; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 78 | object-fit: contain; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 79 | border-radius: 6px; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 80 | box-shadow: 0 12px 40px rgb(0 0 0 / 0.5); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 81 | cursor: zoom-out; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 82 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 83 | </style> |