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; |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 6 | let copied = false; |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 7 | let copiedTimer: number | null = null; |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 8 | |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 9 | async function copyMxc(mxc: string): Promise<void> { |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 10 | try { |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 11 | await navigator.clipboard.writeText(mxc); |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 12 | } catch { |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 13 | return; |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 14 | } |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 15 | copied = true; |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 16 | if (copiedTimer !== null) window.clearTimeout(copiedTimer); |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 17 | copiedTimer = window.setTimeout(() => (copied = false), 1500); |
7cca1d5628d1 | atagen | 2026-10-24 07:36 UTC | 18 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 19 | let returnFocus: HTMLElement | null = null; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 20 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 21 | $: if ($lightbox) void focusClose(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 22 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 23 | async function focusClose(): Promise<void> { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 24 | if (!returnFocus) returnFocus = document.activeElement as HTMLElement | null; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 25 | await tick(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 26 | closeBtn?.focus(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 27 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 28 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 29 | function close(): void { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 30 | lightbox.set(null); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 31 | returnFocus?.focus(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 32 | returnFocus = null; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 33 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 34 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 35 | function onKey(ev: KeyboardEvent): void { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 36 | if ($lightbox && ev.key === 'Escape') { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 37 | ev.stopPropagation(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 38 | close(); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 39 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 40 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 41 | </script> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 42 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 43 | <svelte:window on:keydown|capture={onKey} /> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 44 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 45 | {#if $lightbox} |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 46 | <!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions --> |
f12c7de2a3ab | atagen | 2026-10-23 08:10 UTC | 47 | <div class="lightbox" role="dialog" tabindex="-1" aria-modal="true" aria-label={$lightbox.filename} on:click|self={close}> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 48 | <div class="lightbox-bar"> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 49 | <span class="lightbox-name">{$lightbox.filename}</span> |
01351a71d24a | atagen | 2026-10-24 12:16 UTC | 50 | <button |
01351a71d24a | atagen | 2026-10-24 12:16 UTC | 51 | type="button" |
01351a71d24a | atagen | 2026-10-24 12:16 UTC | 52 | class="ghost-btn" |
01351a71d24a | atagen | 2026-10-24 12:16 UTC | 53 | aria-label="Copy MXC URI" |
01351a71d24a | atagen | 2026-10-24 12:16 UTC | 54 | title={$lightbox.mxc ?? 'MXC URI unavailable for this attachment'} |
01351a71d24a | atagen | 2026-10-24 12:16 UTC | 55 | disabled={!$lightbox.mxc} |
01351a71d24a | atagen | 2026-10-24 12:16 UTC | 56 | on:click={() => $lightbox.mxc && void copyMxc($lightbox.mxc)} |
01351a71d24a | atagen | 2026-10-24 12:16 UTC | 57 | > |
01351a71d24a | atagen | 2026-10-24 12:16 UTC | 58 | {copied ? 'Copied' : 'Copy MXC'} |
01351a71d24a | atagen | 2026-10-24 12:16 UTC | 59 | </button> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 60 | <a class="ghost-btn" href={$lightbox.src} target="_blank" rel="noopener noreferrer">Open original</a> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 61 | <a class="ghost-btn" href={$lightbox.src} download={$lightbox.filename}>Download</a> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 62 | <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 | 63 | <span aria-hidden="true">✕</span> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 64 | </button> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 65 | </div> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 66 | <!-- svelte-ignore a11y_click_events_have_key_events a11y_no_noninteractive_element_interactions --> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 67 | <img class="lightbox-image" src={$lightbox.src} alt={$lightbox.alt} on:click={close} /> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 68 | </div> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 69 | {/if} |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 70 | |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 71 | <style> |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 72 | .lightbox { |
05a48e9e7942 | atagen | 2026-10-24 09:32 UTC | 73 | animation: fade-in var(--pop); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 74 | position: fixed; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 75 | inset: 0; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 76 | z-index: 100; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 77 | display: flex; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 78 | flex-direction: column; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 79 | align-items: center; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 80 | justify-content: center; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 81 | gap: 12px; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 82 | padding: 56px 24px 24px; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 83 | background: rgb(0 0 0 / 0.82); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 84 | backdrop-filter: blur(4px); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 85 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 86 | .lightbox-bar { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 87 | position: absolute; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 88 | top: 0; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 89 | left: 0; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 90 | right: 0; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 91 | display: flex; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 92 | align-items: center; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 93 | gap: 8px; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 94 | padding: 10px 16px; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 95 | color: #fff; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 96 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 97 | .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 | 98 | .lightbox-bar a { text-decoration: none; } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 99 | .lightbox-image { |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 100 | max-width: 100%; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 101 | max-height: 100%; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 102 | object-fit: contain; |
05a48e9e7942 | atagen | 2026-10-24 09:32 UTC | 103 | border-radius: var(--r-sm); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 104 | box-shadow: 0 12px 40px rgb(0 0 0 / 0.5); |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 105 | cursor: zoom-out; |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 106 | } |
cf23dbf4e77c | atagen | 2026-10-23 06:36 UTC | 107 | </style> |