atagen

malice

public
Star0
Watch0
f12c7de2a3ab83d8b3f716f519bb7fcdee39fe58
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: web/src/lib/ImageLightbox.svelte

cf23dbf4e77catagen2026-10-23 06:36 UTC1
<script lang="ts">
cf23dbf4e77catagen2026-10-23 06:36 UTC2
  import { tick } from 'svelte';
cf23dbf4e77catagen2026-10-23 06:36 UTC3
  import { lightbox } from './lightbox';
cf23dbf4e77catagen2026-10-23 06:36 UTC4
cf23dbf4e77catagen2026-10-23 06:36 UTC5
  let closeBtn: HTMLButtonElement | null = null;
cf23dbf4e77catagen2026-10-23 06:36 UTC6
  let returnFocus: HTMLElement | null = null;
cf23dbf4e77catagen2026-10-23 06:36 UTC7
cf23dbf4e77catagen2026-10-23 06:36 UTC8
  $: if ($lightbox) void focusClose();
cf23dbf4e77catagen2026-10-23 06:36 UTC9
cf23dbf4e77catagen2026-10-23 06:36 UTC10
  async function focusClose(): Promise<void> {
cf23dbf4e77catagen2026-10-23 06:36 UTC11
    if (!returnFocus) returnFocus = document.activeElement as HTMLElement | null;
cf23dbf4e77catagen2026-10-23 06:36 UTC12
    await tick();
cf23dbf4e77catagen2026-10-23 06:36 UTC13
    closeBtn?.focus();
cf23dbf4e77catagen2026-10-23 06:36 UTC14
  }
cf23dbf4e77catagen2026-10-23 06:36 UTC15
cf23dbf4e77catagen2026-10-23 06:36 UTC16
  function close(): void {
cf23dbf4e77catagen2026-10-23 06:36 UTC17
    lightbox.set(null);
cf23dbf4e77catagen2026-10-23 06:36 UTC18
    returnFocus?.focus();
cf23dbf4e77catagen2026-10-23 06:36 UTC19
    returnFocus = null;
cf23dbf4e77catagen2026-10-23 06:36 UTC20
  }
cf23dbf4e77catagen2026-10-23 06:36 UTC21
cf23dbf4e77catagen2026-10-23 06:36 UTC22
  function onKey(ev: KeyboardEvent): void {
cf23dbf4e77catagen2026-10-23 06:36 UTC23
    if ($lightbox && ev.key === 'Escape') {
cf23dbf4e77catagen2026-10-23 06:36 UTC24
      ev.stopPropagation();
cf23dbf4e77catagen2026-10-23 06:36 UTC25
      close();
cf23dbf4e77catagen2026-10-23 06:36 UTC26
    }
cf23dbf4e77catagen2026-10-23 06:36 UTC27
  }
cf23dbf4e77catagen2026-10-23 06:36 UTC28
</script>
cf23dbf4e77catagen2026-10-23 06:36 UTC29
cf23dbf4e77catagen2026-10-23 06:36 UTC30
<svelte:window on:keydown|capture={onKey} />
cf23dbf4e77catagen2026-10-23 06:36 UTC31
cf23dbf4e77catagen2026-10-23 06:36 UTC32
{#if $lightbox}
cf23dbf4e77catagen2026-10-23 06:36 UTC33
  <!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
f12c7de2a3abatagen2026-10-23 08:10 UTC34
  <div class="lightbox" role="dialog" tabindex="-1" aria-modal="true" aria-label={$lightbox.filename} on:click|self={close}>
cf23dbf4e77catagen2026-10-23 06:36 UTC35
    <div class="lightbox-bar">
cf23dbf4e77catagen2026-10-23 06:36 UTC36
      <span class="lightbox-name">{$lightbox.filename}</span>
cf23dbf4e77catagen2026-10-23 06:36 UTC37
      <a class="ghost-btn" href={$lightbox.src} target="_blank" rel="noopener noreferrer">Open original</a>
cf23dbf4e77catagen2026-10-23 06:36 UTC38
      <a class="ghost-btn" href={$lightbox.src} download={$lightbox.filename}>Download</a>
cf23dbf4e77catagen2026-10-23 06:36 UTC39
      <button type="button" class="icon-btn" aria-label="Close image" title="Close (Esc)" bind:this={closeBtn} on:click={close}>
cf23dbf4e77catagen2026-10-23 06:36 UTC40
        <span aria-hidden="true">✕</span>
cf23dbf4e77catagen2026-10-23 06:36 UTC41
      </button>
cf23dbf4e77catagen2026-10-23 06:36 UTC42
    </div>
cf23dbf4e77catagen2026-10-23 06:36 UTC43
    <!-- svelte-ignore a11y_click_events_have_key_events a11y_no_noninteractive_element_interactions -->
cf23dbf4e77catagen2026-10-23 06:36 UTC44
    <img class="lightbox-image" src={$lightbox.src} alt={$lightbox.alt} on:click={close} />
cf23dbf4e77catagen2026-10-23 06:36 UTC45
  </div>
cf23dbf4e77catagen2026-10-23 06:36 UTC46
{/if}
cf23dbf4e77catagen2026-10-23 06:36 UTC47
cf23dbf4e77catagen2026-10-23 06:36 UTC48
<style>
cf23dbf4e77catagen2026-10-23 06:36 UTC49
  .lightbox {
cf23dbf4e77catagen2026-10-23 06:36 UTC50
    position: fixed;
cf23dbf4e77catagen2026-10-23 06:36 UTC51
    inset: 0;
cf23dbf4e77catagen2026-10-23 06:36 UTC52
    z-index: 100;
cf23dbf4e77catagen2026-10-23 06:36 UTC53
    display: flex;
cf23dbf4e77catagen2026-10-23 06:36 UTC54
    flex-direction: column;
cf23dbf4e77catagen2026-10-23 06:36 UTC55
    align-items: center;
cf23dbf4e77catagen2026-10-23 06:36 UTC56
    justify-content: center;
cf23dbf4e77catagen2026-10-23 06:36 UTC57
    gap: 12px;
cf23dbf4e77catagen2026-10-23 06:36 UTC58
    padding: 56px 24px 24px;
cf23dbf4e77catagen2026-10-23 06:36 UTC59
    background: rgb(0 0 0 / 0.82);
cf23dbf4e77catagen2026-10-23 06:36 UTC60
    backdrop-filter: blur(4px);
cf23dbf4e77catagen2026-10-23 06:36 UTC61
  }
cf23dbf4e77catagen2026-10-23 06:36 UTC62
  .lightbox-bar {
cf23dbf4e77catagen2026-10-23 06:36 UTC63
    position: absolute;
cf23dbf4e77catagen2026-10-23 06:36 UTC64
    top: 0;
cf23dbf4e77catagen2026-10-23 06:36 UTC65
    left: 0;
cf23dbf4e77catagen2026-10-23 06:36 UTC66
    right: 0;
cf23dbf4e77catagen2026-10-23 06:36 UTC67
    display: flex;
cf23dbf4e77catagen2026-10-23 06:36 UTC68
    align-items: center;
cf23dbf4e77catagen2026-10-23 06:36 UTC69
    gap: 8px;
cf23dbf4e77catagen2026-10-23 06:36 UTC70
    padding: 10px 16px;
cf23dbf4e77catagen2026-10-23 06:36 UTC71
    color: #fff;
cf23dbf4e77catagen2026-10-23 06:36 UTC72
  }
cf23dbf4e77catagen2026-10-23 06:36 UTC73
  .lightbox-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
cf23dbf4e77catagen2026-10-23 06:36 UTC74
  .lightbox-bar a { text-decoration: none; }
cf23dbf4e77catagen2026-10-23 06:36 UTC75
  .lightbox-image {
cf23dbf4e77catagen2026-10-23 06:36 UTC76
    max-width: 100%;
cf23dbf4e77catagen2026-10-23 06:36 UTC77
    max-height: 100%;
cf23dbf4e77catagen2026-10-23 06:36 UTC78
    object-fit: contain;
cf23dbf4e77catagen2026-10-23 06:36 UTC79
    border-radius: 6px;
cf23dbf4e77catagen2026-10-23 06:36 UTC80
    box-shadow: 0 12px 40px rgb(0 0 0 / 0.5);
cf23dbf4e77catagen2026-10-23 06:36 UTC81
    cursor: zoom-out;
cf23dbf4e77catagen2026-10-23 06:36 UTC82
  }
cf23dbf4e77catagen2026-10-23 06:36 UTC83
</style>