<script lang="ts">
  import type { Snippet } from 'svelte';

  let {
    title = '',
    titleAside = '',
    onclose,
    overlayKeyboard = false,
    bottomSheet = false,
    children,
  }: {
    title?: string;
    /** Optional secondary text pinned to the right of the title row, in a lighter (non-bold)
     *  muted style — e.g. the bag count beside the "Exchange or pass" title. */
    titleAside?: string;
    onclose?: () => void;
    overlayKeyboard?: boolean;
    bottomSheet?: boolean;
    children?: Snippet;
  } = $props();

  // Track the visual viewport so the backdrop covers only the area above an open
  // mobile keyboard: dvh alone shrinks the sheet but the fixed, layout-viewport
  // backdrop still centres it behind the keyboard. Sizing the backdrop to
  // visualViewport keeps the sheet (and the start of a chat) fully on screen.
  // overlayKeyboard opts out: the sheet is small and top-anchored, so the keyboard
  // simply overlays the empty lower area — no resize, no relayout jank (e.g. check word).
  // bottomSheet anchors a tall sheet (the chat) to the bottom and lifts it above the
  // keyboard with a transform (kb), driven by the visual viewport — a compositor-only
  // move, so neither the page behind nor the sheet relayouts as the keyboard animates
  // The backdrop is not resized in this mode (no per-event reflow).
  let vh = $state(0);
  let top = $state(0);
  let kb = $state(0);
  $effect(() => {
    const vv = typeof window !== 'undefined' ? window.visualViewport : null;
    if (!vv || overlayKeyboard) return;
    const update = () => {
      vh = vv.height;
      top = vv.offsetTop;
      // Soft-keyboard height: the layout viewport minus the visible viewport.
      kb = Math.max(0, window.innerHeight - vv.height - vv.offsetTop);
    };
    update();
    vv.addEventListener('resize', update);
    vv.addEventListener('scroll', update);
    return () => {
      vv.removeEventListener('resize', update);
      vv.removeEventListener('scroll', update);
    };
  });
</script>

<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
  class="backdrop"
  class:overlay={overlayKeyboard}
  class:bottom={bottomSheet}
  style:height={!bottomSheet && vh ? `${vh}px` : null}
  style:top={!bottomSheet && vh ? `${top}px` : null}
  onclick={() => onclose?.()}
>
  <div
    class="sheet"
    role="dialog"
    aria-modal="true"
    tabindex="-1"
    style:--kb={bottomSheet ? `${kb}px` : null}
    onclick={(e) => e.stopPropagation()}
  >
    {#if title}
      <div class="titlerow">
        <h2>{title}</h2>
        {#if titleAside}<span class="aside">{titleAside}</span>{/if}
      </div>
    {/if}
    {@render children?.()}
  </div>
</div>

<style>
  .backdrop {
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    /* Base fallback; overridden inline to the visual-viewport height/top so the
       backdrop (and the centred sheet) stay above an open mobile keyboard. */
    height: 100dvh;
    box-sizing: border-box;
    background: rgba(0, 0, 0, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    z-index: 40;
  }
  /* Overlay mode: top-anchor the (small) sheet and don't track the keyboard, so the
     soft keyboard overlays the empty lower area without resizing/relaying out. */
  .backdrop.overlay {
    align-items: flex-start;
    padding-top: 12vh;
  }
  /* Bottom-sheet mode (the chat): a wide sheet pinned to the bottom that lifts above the
     soft keyboard via a transform (--kb) — compositor-only, so the page behind and the
     sheet itself do not relayout as the keyboard animates. */
  .backdrop.bottom {
    align-items: flex-end;
    padding: 0;
  }
  .backdrop.bottom .sheet {
    width: 100%;
    max-width: 640px;
    border-radius: var(--radius) var(--radius) 0 0;
    transform: translateY(calc(-1 * var(--kb, 0px)));
    transition: transform 0.15s ease;
  }
  .sheet {
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: var(--pad);
    width: min(94vw, 420px);
    /* dvh tracks the dynamic viewport, so the sheet shrinks above an open mobile
       keyboard instead of being scrolled off the top (vh fallback first). */
    max-height: 86vh;
    max-height: 86dvh;
    overflow: auto;
  }
  .titlerow {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 8px;
  }
  h2 {
    margin: 0 0 10px;
    font-size: 1.05rem;
  }
  /* Secondary title-row text (e.g. the bag count): lighter and muted so the title still leads. */
  .aside {
    font-weight: 400;
    font-size: 0.9rem;
    color: var(--text-muted);
    white-space: nowrap;
  }
</style>
