# Collection example

A collection is the most natural home for a card, so it makes sense to open cards there. Each one lies face down in its slot. Press it and it lifts out, turns over in the middle of the screen, and lands back in the same slot, face up. Press an opened one to look at it again.

> Live preview: see it play at https://bettertyped.github.io/react-cards-opening/docs/examples/collection/
>
> Four slots. Open them in any order.

## The code

`collection.tsx`

```tsx
import { useCardOpening, useOpenedCards, useOpeningPhase } from "@react-cards-opening/core";
import { Card, CardOpening, type GlyphCard } from "@react-cards-opening/ui";
import { useRef } from "react";
import "@react-cards-opening/ui/styles.css";

export function Collection({ cards }: { cards: GlyphCard[] }) {
  const { opened, open } = useOpenedCards("collection");
  const hunt = useCardOpening({ cards, opened, onOpen: open });
  const phase = useOpeningPhase(hunt.opening);
  const slots = useRef(new Map<string, HTMLElement>());
  const slotOf = (card: GlyphCard) => slots.current.get(card.id) ?? null;
  // While a card is in the air, its slot is empty.
  const away = hunt.current && phase !== "idle" && phase !== "lead" ? hunt.current.id : null;

  return (
    <>
      <ul className="shelf">
        {cards.map((card) => {
          const isOpen = opened.includes(card.id);
          return (
            <li key={card.id}>
              <button
                ref={(element) => {
                  if (element) slots.current.set(card.id, element);
                }}
                // Seen through, not hidden: a hidden button could not take the focus back after a peek.
                style={{ opacity: away === card.id ? 0 : 1 }}
                disabled={hunt.running}
                aria-label={isOpen ? `Look at ${card.name} again` : "Open this card"}
                onClick={(event) => (isOpen ? hunt.peek(card.id, event.currentTarget) : hunt.start([card.id]))}
              >
                <Card card={card} faceDown={!isOpen} width="100%" />
              </button>
            </li>
          );
        })}
      </ul>
      <CardOpening opening={hunt.opening} launch={slotOf} landing={slotOf} />
    </>
  );
}
```

`collection.css`

```css
.shelf {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 16px;
  list-style: none;
  padding: 0;
}

.shelf button {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
```

## Why it looks like one card

- The reveal starts at the slot's exact place and size, and the slot is see-through while its card is away. The eye reads it as the same card lifting out.
- A [Card](https://bettertyped.github.io/react-cards-opening/docs/components/card/) scales everything with its width, so the revealed card and the one on the shelf are the same picture at two sizes.
- `faceDown` flips to false while the slot is see-through, so the card is already face up when it lands back.

## Open them all

```tsx
<button onClick={() => hunt.start()} disabled={hunt.running || hunt.allOpened}>
  Open the rest ({hunt.pending.length})
</button>
```

`start()` with no arguments queues every unopened card. They open one after another, each from its own slot, and a press beside a card stops the run.

## Related

- [Card](https://bettertyped.github.io/react-cards-opening/docs/components/card/)
- [Origin and landing](https://bettertyped.github.io/react-cards-opening/docs/guides/origin-and-landing/)
- [CardOpening](https://bettertyped.github.io/react-cards-opening/docs/components/card-opening/)
- [Card hunt example](https://bettertyped.github.io/react-cards-opening/docs/examples/card-hunt/)

Source: https://bettertyped.github.io/react-cards-opening/docs/examples/collection/
