empty-state
FC-0011empty-stateblockv1.0.0De 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.
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);
}