# One trigger for an album

One thumbnail stands for a whole album. It opens the first item, and every item in the album returns to it on close. It shows whichever item the viewer showed last, so the closing flight lands on a matching picture.

#### React

`overlay.tsx`

```tsx
import { itemLabel } from "@ogdakke/fluidbox";
import { Fluidbox } from "@ogdakke/fluidbox/react";
import { album } from "./data";

export function Overlay() {
  return (
    <Fluidbox.Root items={album}>
      {/* The first item opens; every album item closes back onto this trigger. */}
      <Fluidbox.Trigger
        item={album[0]!}
        returns={album}
        className="pattern-thumb pattern-album"
      >
        {({ item }) => (
          <>
            {/* Shows the item the viewer last showed. Keep the img mounted. */}
            <img src={item?.src} alt={item ? itemLabel(item) : ""} />
            <Fluidbox.ThumbnailOverlay travel className="pattern-overlay">
              +{album.length - 1}
            </Fluidbox.ThumbnailOverlay>
          </>
        )}
      </Fluidbox.Trigger>
    </Fluidbox.Root>
  );
}
```

#### Vue

`Overlay.vue`

```vue
<script setup lang="ts">
import { itemLabel } from "@ogdakke/fluidbox";
import { Fluidbox } from "@ogdakke/fluidbox/vue";
import { album } from "./data";
</script>

<template>
  <Fluidbox.Root :items="album">
    <!-- The cover opens the first photo; every album item closes back onto it. -->
    <Fluidbox.Trigger
      v-slot="{ item }"
      :item="album[0]"
      :returns="album"
      class="pattern-thumb pattern-album"
    >
      <!-- Shows the item the viewer last showed. -->
      <img :src="item?.src" :alt="item ? itemLabel(item) : ''" />
      <Fluidbox.ThumbnailOverlay travel class="pattern-overlay">
        +{{ album.length - 1 }}
      </Fluidbox.ThumbnailOverlay>
    </Fluidbox.Trigger>
  </Fluidbox.Root>
</template>
```

#### Solid

`overlay.tsx`

```tsx
import { itemLabel } from "@ogdakke/fluidbox";
import { Fluidbox } from "@ogdakke/fluidbox/solid";
import { album } from "./data";

export function Overlay() {
  return (
    <Fluidbox.Root items={album}>
      {/* The first item opens; every one of them closes back onto the trigger. */}
      <Fluidbox.Trigger
        item={album[0]!}
        returns={album}
        class="pattern-thumb pattern-album"
      >
        {(preview) => (
          <>
            {/* The preview follows the viewer, so the close lands on the right photo. */}
            <img
              src={preview().item?.src}
              alt={preview().item ? itemLabel(preview().item!) : ""}
            />
            <Fluidbox.ThumbnailOverlay travel class="pattern-overlay">
              +{album.length - 1}
            </Fluidbox.ThumbnailOverlay>
          </>
        )}
      </Fluidbox.Trigger>
    </Fluidbox.Root>
  );
}
```

#### Angular

`overlay.ts`

```ts
import { Component } from "@angular/core";
import { itemLabel } from "@ogdakke/fluidbox";
import { FluidboxModule } from "@ogdakke/fluidbox/angular";
import { album } from "./data";

@Component({
  selector: "pattern-overlay",
  standalone: true,
  imports: [FluidboxModule],
  template: `
    <div fluidboxRoot [items]="album">
      <!-- The cover opens the first photo; every one of them closes back onto it. -->
      <button
        [fluidboxTrigger]="album[0]"
        [fluidboxReturns]="album"
        #trigger="fluidboxTrigger"
        class="pattern-thumb pattern-album"
      >
        <img
          [src]="(trigger.preview().item ?? album[0]).src"
          [alt]="label(trigger.preview().item ?? album[0])"
        />
        <!-- The count flies with the photo, rendered live from its template. -->
        <span fluidboxThumbnailOverlay travel class="pattern-overlay">
          <ng-template>+{{ album.length - 1 }}</ng-template>
        </span>
      </button>
    </div>
  `,
})
export class Overlay {
  readonly album = album;
  readonly label = itemLabel;
}
```

#### Elements

`overlay.html`

```html
<fluidbox-gallery class="pattern-album">
  <fluidbox-item item-id="album-dune" returns="album-canyon album-coast album-meadow album-alpine">
    <button slot="trigger" type="button" class="pattern-thumb">
      <img src="/art/dune.svg" alt="Dune at golden hour" />
      <span class="pattern-overlay" data-fluidbox-thumbnail-overlay="travel"
        >+4</span
      >
    </button>
    <template slot="content">
      <img src="/art/dune.svg" alt="Dune at golden hour" width="1800" height="1200" />
    </template>
  </fluidbox-item>
  <fluidbox-item item-id="album-canyon">
    <template slot="content">
      <img src="/art/canyon.svg" alt="Canyon walls" width="1400" height="1400" />
    </template>
  </fluidbox-item>
  <fluidbox-item item-id="album-coast">
    <template slot="content">
      <img src="/art/coast.svg" alt="Coastline panorama" width="2400" height="1000" />
    </template>
  </fluidbox-item>
  <fluidbox-item item-id="album-meadow">
    <template slot="content">
      <img src="/art/meadow.svg" alt="Meadow hills" width="1600" height="1200" />
    </template>
  </fluidbox-item>
  <fluidbox-item item-id="album-alpine">
    <template slot="content">
      <img src="/art/alpine.svg" alt="Alpine ridge at dawn" width="1920" height="1080" />
    </template>
  </fluidbox-item>
</fluidbox-gallery>
```

`overlay.ts`

```ts
export function overlay(root: HTMLElement) {
  const cover = root.querySelector("fluidbox-item")!;
  const preview = cover.querySelector<HTMLImageElement>("img")!;
  // Before the close flight lands, show the item it lands on.
  cover.addEventListener("fluidbox-preview-change", ({ detail: { item } }) => {
    if (item.type !== "img") return;
    preview.src = item.src;
    preview.alt = item.alt;
  });
}
```

#### JavaScript

`overlay.ts`

```ts
import { createFluidbox, itemLabel } from "@ogdakke/fluidbox";
import { album } from "./data";

export function mountOverlay(root: HTMLElement) {
  const fluidbox = createFluidbox({ items: album });

  const button = document.createElement("button");
  button.type = "button";
  button.className = "pattern-thumb pattern-album";
  const preview = Object.assign(new Image(), { src: album[0]!.src, alt: itemLabel(album[0]!) });
  const count = document.createElement("span");
  count.className = "pattern-overlay";
  count.textContent = `+${album.length - 1}`;
  button.append(preview, count);
  root.append(button);

  // The cover opens the first photo; every one of them closes back onto it.
  fluidbox.bind(button, album[0]!, {
    returns: album,
    onPreviewChange(item) {
      preview.src = item.src;
      preview.alt = itemLabel(item);
    },
  });
  // The count flies with the photo.
  fluidbox.bindOverlay(count, album[0]!, { returns: album, travel: true });

  return () => {
    fluidbox.destroy();
    button.remove();
  };
}
```

- **Multi-item trigger.** The trigger opens its own item, and `returns` lists every item that may close back onto it. Set it as `returns` in `fluidbox.bind` and on the framework `Trigger` (`fluidboxReturns` in Angular), or as `returns="id id …"` on a `<fluidbox-item>`. It may hold the whole album, the opened item included.
- **Preview follows the selection.** The framework triggers hand their content the item they show (`{ item, index, status }`). Plain JavaScript gets `onPreviewChange(item, index)`. On custom elements, the `<fluidbox-item>` dispatches `fluidbox-preview-change` with `{ id, item }`, and you swap the trigger's image in its handler. Changes inside a trigger never make the gallery re-read its items. The preview updates before the closing flight measures it.
- **Thumbnail overlays.** For a badge or caption on the thumbnail, use `fluidbox.bindOverlay()`, the `ThumbnailOverlay` part, or `data-fluidbox-thumbnail-overlay`. With `travel` the overlay flies with the image and fades on landing; `travel` can also be a renderer that draws live content into the flying frame. The framework parts render their content live there on their own. Angular's part takes it as an `<ng-template>` child. Without `travel`, the overlay stays in the page and gets `--fluidbox-closed` to animate against. A frame layer moves into the flying frame itself and scales with it, as a scrim does. It is `FrameLayer` in the frameworks (`fluidboxFrameLayer` in Angular), `frameLayer` in plain JavaScript, and `data-fluidbox-frame-layer` in markup.
