# Filmstrip

The filmstrip is an optional add-on from `@ogdakke/fluidbox/filmstrip`. The default strip draws the items' thumbnails, or you draw each thumbnail yourself.

#### React

`filmstrip.tsx`

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

function Thumbnails() {
  return (
    <div className="pattern-grid">
      {photos.map((photo) => (
        <Fluidbox.Trigger
          key={photo.src}
          item={photo}
          className="pattern-thumb"
        >
          <img src={photo.src} alt={itemLabel(photo)} />
        </Fluidbox.Trigger>
      ))}
    </div>
  );
}

export function Filmstrip() {
  return (
    <>
      <Fluidbox.Root items={photos}>
        <Thumbnails />
        {/* Being there turns the filmstrip on. */}
        <Fluidbox.Filmstrip />
      </Fluidbox.Root>
      <Fluidbox.Root items={photos}>
        <Thumbnails />
        {/* Your own thumbnails. The selected button has data-fluidbox-selected. */}
        <Fluidbox.Filmstrip>
          {({ index }) => (
            <span className="pattern-filmstrip-thumb">
              {index + 1}
            </span>
          )}
        </Fluidbox.Filmstrip>
      </Fluidbox.Root>
    </>
  );
}
```

#### Vue

`Filmstrip.vue`

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

<template>
  <Fluidbox.Root :items="photos">
    <div class="pattern-grid">
      <Fluidbox.Trigger
        v-for="photo in photos"
        :key="photo.id"
        :item="photo"
        class="pattern-thumb"
      >
        <img :src="photo.src" :alt="itemLabel(photo)" />
      </Fluidbox.Trigger>
    </div>
    <Fluidbox.Filmstrip />
  </Fluidbox.Root>

  <Fluidbox.Root :items="photos">
    <div class="pattern-grid">
      <Fluidbox.Trigger
        v-for="photo in photos"
        :key="photo.id"
        :item="photo"
        class="pattern-thumb"
      >
        <img :src="photo.src" :alt="itemLabel(photo)" />
      </Fluidbox.Trigger>
    </div>
    <!-- Your own thumbnails. The selected button has data-fluidbox-selected. -->
    <Fluidbox.Filmstrip v-slot="{ index }">
      <span class="pattern-filmstrip-thumb">{{ index + 1 }}</span>
    </Fluidbox.Filmstrip>
  </Fluidbox.Root>
</template>
```

#### Solid

`filmstrip.tsx`

```tsx
import { For } from "solid-js";
import { itemLabel } from "@ogdakke/fluidbox";
import { Fluidbox } from "@ogdakke/fluidbox/solid";
import "@ogdakke/fluidbox/filmstrip.css";
import { photos } from "./data";

function Thumbnails() {
  return (
    <div class="pattern-grid">
      <For each={photos}>
        {(photo) => (
          <Fluidbox.Trigger item={photo} class="pattern-thumb">
            <img src={photo.src} alt={itemLabel(photo)} />
          </Fluidbox.Trigger>
        )}
      </For>
    </div>
  );
}

export function Filmstrip() {
  return (
    <>
      <Fluidbox.Root items={photos}>
        <Thumbnails />
        <Fluidbox.Filmstrip />
      </Fluidbox.Root>

      <Fluidbox.Root items={photos}>
        <Thumbnails />
        {/* Your own thumbnails. The selected button has data-fluidbox-selected. */}
        <Fluidbox.Filmstrip>
          {(thumb) => (
            <span class="pattern-filmstrip-thumb">
              {thumb.index + 1}
            </span>
          )}
        </Fluidbox.Filmstrip>
      </Fluidbox.Root>
    </>
  );
}
```

#### Angular

`filmstrip.ts`

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

@Component({
  selector: "pattern-filmstrip",
  standalone: true,
  imports: [FluidboxModule],
  template: `
    <div fluidboxRoot [items]="photos" class="pattern-grid">
      @for (photo of photos; track photo.id) {
        <button [fluidboxTrigger]="photo" class="pattern-thumb">
          <img [src]="photo.src" [alt]="label(photo)" />
        </button>
      }
      <ng-container fluidboxFilmstrip />
    </div>
    <div fluidboxRoot [items]="photos" class="pattern-grid">
      @for (photo of photos; track photo.id) {
        <button [fluidboxTrigger]="photo" class="pattern-thumb">
          <img [src]="photo.src" [alt]="label(photo)" />
        </button>
      }
      <!-- Your own thumbnails. The selected button has data-fluidbox-selected. -->
      <ng-template fluidboxFilmstrip let-index="index">
        <span class="pattern-filmstrip-thumb">{{ index + 1 }}</span>
      </ng-template>
    </div>
  `,
})
export class Filmstrip {
  readonly photos = photos;
  readonly label = itemLabel;
}
```

#### Elements

`filmstrip.html`

```html
<fluidbox-gallery class="pattern-grid">
  <fluidbox-item item-id="photo-aurora">
    <button slot="trigger" type="button" class="pattern-thumb">
      <img src="/art/aurora.svg" alt="Aurora over the tundra" />
    </button>
    <template slot="content">
      <img src="/art/aurora.svg" alt="Aurora over the tundra" width="1100" height="1650" />
    </template>
  </fluidbox-item>
  <fluidbox-item item-id="photo-ridge">
    <button slot="trigger" type="button" class="pattern-thumb">
      <img src="/art/ridge.svg" alt="Night ridge" />
    </button>
    <template slot="content">
      <img src="/art/ridge.svg" alt="Night ridge" width="1200" height="1500" />
    </template>
  </fluidbox-item>
  <fluidbox-item item-id="photo-fjord">
    <button slot="trigger" type="button" class="pattern-thumb">
      <img src="/art/fjord.svg" alt="Fjord in the blue hour" />
    </button>
    <template slot="content">
      <img src="/art/fjord.svg" alt="Fjord in the blue hour" width="1200" height="1600" />
    </template>
  </fluidbox-item>
  <fluidbox-item item-id="photo-lagoon">
    <button slot="trigger" type="button" class="pattern-thumb">
      <img src="/art/lagoon.svg" alt="Lagoon after sunset" />
    </button>
    <template slot="content">
      <img src="/art/lagoon.svg" alt="Lagoon after sunset" width="1800" height="1200" />
    </template>
  </fluidbox-item>
  <fluidbox-filmstrip></fluidbox-filmstrip>
</fluidbox-gallery>
<fluidbox-gallery class="pattern-grid numbered-strip">
  <fluidbox-item item-id="photo-aurora">
    <button slot="trigger" type="button" class="pattern-thumb">
      <img src="/art/aurora.svg" alt="Aurora over the tundra" />
    </button>
    <template slot="content">
      <img src="/art/aurora.svg" alt="Aurora over the tundra" width="1100" height="1650" />
    </template>
  </fluidbox-item>
  <fluidbox-item item-id="photo-ridge">
    <button slot="trigger" type="button" class="pattern-thumb">
      <img src="/art/ridge.svg" alt="Night ridge" />
    </button>
    <template slot="content">
      <img src="/art/ridge.svg" alt="Night ridge" width="1200" height="1500" />
    </template>
  </fluidbox-item>
  <fluidbox-item item-id="photo-fjord">
    <button slot="trigger" type="button" class="pattern-thumb">
      <img src="/art/fjord.svg" alt="Fjord in the blue hour" />
    </button>
    <template slot="content">
      <img src="/art/fjord.svg" alt="Fjord in the blue hour" width="1200" height="1600" />
    </template>
  </fluidbox-item>
  <fluidbox-item item-id="photo-lagoon">
    <button slot="trigger" type="button" class="pattern-thumb">
      <img src="/art/lagoon.svg" alt="Lagoon after sunset" />
    </button>
    <template slot="content">
      <img src="/art/lagoon.svg" alt="Lagoon after sunset" width="1800" height="1200" />
    </template>
  </fluidbox-item>
  <fluidbox-filmstrip></fluidbox-filmstrip>
</fluidbox-gallery>
```

`filmstrip.ts`

```ts
import "@ogdakke/fluidbox/filmstrip.css";

export function filmstrip(root: HTMLElement) {
  // Your own thumbnails. The selected button has data-fluidbox-selected.
  const strip = root.querySelector(".numbered-strip")!.querySelector("fluidbox-filmstrip")!;
  strip.render = ({ index, target }) => {
    const thumb = document.createElement("span");
    thumb.className = "pattern-filmstrip-thumb";
    thumb.textContent = String(index + 1);
    target.append(thumb);
  };
}
```

#### JavaScript

`filmstrip.ts`

```ts
import { createFluidbox, itemLabel, type FluidboxOptions } from "@ogdakke/fluidbox";
import "@ogdakke/fluidbox/filmstrip";
import "@ogdakke/fluidbox/filmstrip.css";
import { photos } from "./data";

function gallery(root: HTMLElement, filmstrip: FluidboxOptions["filmstrip"]) {
  const fluidbox = createFluidbox({ items: photos, filmstrip });
  const grid = document.createElement("div");
  grid.className = "pattern-grid";
  photos.forEach((photo, index) => {
    const button = document.createElement("button");
    button.type = "button";
    button.className = "pattern-thumb";
    button.append(Object.assign(new Image(), { src: photo.src, alt: itemLabel(photo) }));
    grid.append(button);
    fluidbox.bind(button, index);
  });
  root.append(grid);
  return () => {
    fluidbox.destroy();
    grid.remove();
  };
}

export function mountFilmstrip(root: HTMLElement) {
  const standard = gallery(root, true);
  // Your own thumbnails. The selected button has data-fluidbox-selected.
  const custom = gallery(root, {
    render({ index, target }) {
      const thumb = document.createElement("span");
      thumb.className = "pattern-filmstrip-thumb";
      thumb.textContent = String(index + 1);
      target.append(thumb);
      return () => thumb.remove();
    },
  });

  return () => {
    standard();
    custom();
  };
}
```

- `import "@ogdakke/fluidbox/filmstrip"` once, then `filmstrip: true` turns it on. In the frameworks, rendering the `Filmstrip` part turns it on. On custom elements, a `<fluidbox-filmstrip>` child does, and it loads the add-on itself. It shows only when there is more than one item.
- For your own thumbnails, use `filmstrip: { render }` in plain JavaScript, the `render` property of `<fluidbox-filmstrip>`, or a child of the framework `Filmstrip` part.
- The selected thumbnail's button has `data-fluidbox-selected`, so CSS can mark it without your thumbnails tracking anything.
- The add-on is its own import, so it costs nothing until you use it.
