Patterns

Programmatic control

The fluidbox opens, moves and closes the viewer from code. Its state tells you what is open, and events tell you what changed and why.

Loading the live example…

programmatic.tsx
import { useRef, useState, type RefObject } from "react";
import { itemLabel } from "@ogdakke/fluidbox";
import { Fluidbox, useFluidbox } from "@ogdakke/fluidbox/react";
import { photos } from "./data";

function Toolbar({ lock }: { lock: RefObject<HTMLInputElement | null> }) {
  const open = useFluidbox(({ open }) => open);
  const status = useFluidbox(({ status, index }) => `${status} ${index ?? "-"}`);
  return (
    <div className="pattern-toolbar">
      <button type="button" onClick={() => open(2)}>
        Open the third photo
      </button>
      <label>
        <input ref={lock} type="checkbox" name="lock" /> Lock navigation
      </label>
      <output>{status}</output>
    </div>
  );
}

export function Programmatic() {
  const lock = useRef<HTMLInputElement>(null);
  const [log, setLog] = useState<string[]>([]);
  const record = (event: Event) => setLog((types) => [...types, event.type].slice(-20));
  return (
    <Fluidbox.Root
      items={photos}
      // beforeitemchange comes before every move between items; canceling it keeps the viewer still.
      onBeforeItemChange={(event) => {
        if (lock.current?.checked) event.preventDefault();
        record(event);
      }}
      onBeforeToggle={record}
      onToggle={record}
      onItemChange={record}
      onSettle={record}
    >
      <Toolbar lock={lock} />
      <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>
      <ol className="pattern-log">
        {log.map((type, index) => (
          <li key={index}>{type}</li>
        ))}
      </ol>
    </Fluidbox.Root>
  );
}
  • Commands. open(target), next(), previous() and close() return a promise that resolves true when the command happened and false when it didn't. A target is an index or an id: open(2), open("photo-fjord"). While open, open moves to the item.
  • State. status (closed, opening, open, closing), id, index, item, count, hasPrevious and hasNext. Read it with useFluidbox() in the frameworks, gallery.state and gallery.subscribe() on custom elements, or fluidbox.state and fluidbox.subscribe() in plain JavaScript.
  • Events. beforetoggle and toggle as the viewer opens and closes, like a popover's; beforeitemchange, itemchange and settle as it moves between items. Each has a reason: "keyboard", "pointer", "trigger", "api" and so on. The framework roots take them as event props.
  • Veto. beforetoggle and beforeitemchange fire before every command, whether it comes from a key, a gesture or your code. Call event.preventDefault() to cancel it.