← alle entries

empty-state

FC-0011empty-stateblockv1.0.0

De lege staat: beeld, wat er ontbreekt, wat je eraan doet. Alle tekst en de actie zijn slots; het beeld is standaard de folder-stack maar mag alles zijn.

zelfde component — andere tokens

Nog geen documenten

Zodra je iets uploadt verschijnt het hier, met de laatste versie bovenaan.

Footnote-slot: hier zet het project bijvoorbeeld de toegestane bestandstypen.

blocks/empty-state/empty-state.tsx
/**
 * EmptyState — het scherm dat je ziet als er nog niets is. Beeld, één regel
 * die zegt wat er ontbreekt, één regel die zegt wat je eraan doet, en één
 * actie. Alles behalve de vorm is een slot: de entry legt geen woorden op.
 *
 * Receptuur (V5): het beeld is standaard de `folder-stack`-primitive, maar
 * elk ander beeld mag in het `visual`-slot — een lege agenda vraagt om iets
 * anders dan een lege map.
 */

"use client";

import type * as React from "react";
import { FolderStack } from "../../primitives/ui/folder-stack";

export interface EmptyStateProps {
  /** De hoofdregel: wat is er niet. */
  title: React.ReactNode;
  /** De regel eronder: wat kun je nu doen. */
  description?: React.ReactNode;
  /** Het beeld; laat weg voor de map, geef `null` voor geen beeld. */
  visual?: React.ReactNode;
  /** De actie — meestal een Button of PressDepth uit de registry. */
  action?: React.ReactNode;
  /** Kleine regel onder de actie (voorwaarde, uitleg, link). */
  footnote?: React.ReactNode;
  /** "volledig" = ruime pagina-lege-staat, "compact" = binnen een kaart of paneel. */
  variant?: "volledig" | "compact";
  className?: string;
}

export function EmptyState({
  title,
  description,
  visual,
  action,
  footnote,
  variant = "volledig",
  className,
}: EmptyStateProps) {
  const beeld = visual === undefined ? <FolderStack /> : visual;
  return (
    <div
      className={["fp-empty", `fp-empty--${variant}`, className].filter(Boolean).join(" ")}
    >
      {beeld ? <div className="fp-empty__visual">{beeld}</div> : null}
      <div className="fp-empty__copy">
        <p className="fp-empty__title">{title}</p>
        {description ? <p className="fp-empty__description">{description}</p> : null}
      </div>
      {action ? <div className="fp-empty__action">{action}</div> : null}
      {footnote ? <p className="fp-empty__footnote">{footnote}</p> : null}
    </div>
  );
}
blocks/empty-state/empty-state.css
/**
 * EmptyState — stylesheet.
 * Uitsluitend tokens uit tokens/CONTRACT.md (contract v1).
 * Vereist primitives/ui/primitives.css + tokens/motion.css ernaast.
 */

.fp-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  width: 100%;
}

.fp-empty--volledig { gap: 2rem; padding: 4rem 1rem; }
.fp-empty--compact { gap: 1.25rem; padding: 2rem 1rem; }

/* In de compacte variant is het beeld een bijzin, geen aankondiging. */
.fp-empty--compact .fp-empty__visual {
  transform: scale(0.68);
  transform-origin: center;
  margin: -1.75rem 0;
}

.fp-empty__copy { display: flex; flex-direction: column; gap: 0.5rem; max-width: 34rem; }

.fp-empty__title {
  margin: 0;
  font: inherit;
  font-size: 1.125rem;
  font-weight: 500;
  line-height: 1.4;
  color: var(--foreground);
}
.fp-empty--volledig .fp-empty__title { font-size: 1.25rem; }

.fp-empty__description {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--muted-foreground);
}

.fp-empty__action { display: flex; flex-wrap: wrap; gap: 0.75rem; justify-content: center; }

.fp-empty__footnote {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--muted-foreground);
}