Files
sshkeyvault/src/components/ui/Karte.tsx
T
florian.wildeandClaude Opus 5.5 be9837ba50 feat(zuweisungen): Zum Dry-Run springt zur Dry-Run-Karte (#032)
Der Link im Zell-Dialog der Zuweisungsmatrix fuehrte nur auf den Kopf
der Schluesselseite; die Dry-Run-Karte liegt weit unten, und wer aus der
Matrix kommt, will genau dorthin. Die Karte traegt jetzt id="dry-run"
(Karte nimmt dafuer eine optionale id an), der Link zeigt auf
/schluessel/<id>#dry-run. scroll-mt haelt die Karte unter der festen
Kopfleiste der schmalen Ansicht.

E2E: der Matrix-Test klickt den Link und prueft Adresse und Sichtbarkeit
der Karte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:29:49 +02:00

81 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { clsx } from "clsx";
import type { ReactNode } from "react";
/**
* Karte: --surface, 1 px Rahmen, 16 px Radius, 22–24 px Innenabstand (#002).
* Mit `titel` entsteht ein Kopf mit optionalen Aktionen rechts.
*/
export function Karte({
titel,
untertitel,
aktionen,
children,
className,
innen = "normal",
as: Element = "section",
id,
}: {
titel?: ReactNode;
untertitel?: ReactNode;
aktionen?: ReactNode;
children?: ReactNode;
className?: string;
/** „ohne“ für Inhalte, die bis an den Rand reichen (z. B. Tabellen mit eigenem Abstand). */
innen?: "normal" | "ohne";
as?: "section" | "div" | "article";
/** Sprungziel für Links von anderen Seiten (z. B. `#dry-run`, #032). */
id?: string;
}) {
return (
<Element
id={id}
className={clsx(
"border-border bg-surface min-w-0 rounded-karte border",
innen === "normal" && "p-5 sm:p-6",
className,
)}
>
{(titel || aktionen) && (
<KartenKopf titel={titel} untertitel={untertitel} aktionen={aktionen} rand={innen === "ohne"} />
)}
{children}
</Element>
);
}
function KartenKopf({
titel,
untertitel,
aktionen,
rand,
}: {
titel?: ReactNode;
untertitel?: ReactNode;
aktionen?: ReactNode;
rand: boolean;
}) {
return (
<header
className={clsx(
"mb-4 flex flex-wrap items-center justify-between gap-x-4 gap-y-2",
rand && "px-5 pt-5 sm:px-6 sm:pt-6",
)}
>
<div className="min-w-0">
{titel && <h2 className="text-text text-[17px] leading-snug font-semibold">{titel}</h2>}
{untertitel && <p className="text-muted mt-0.5 text-sm">{untertitel}</p>}
</div>
{aktionen && <div className="flex shrink-0 flex-wrap items-center gap-2">{aktionen}</div>}
</header>
);
}
/** Innere Box (--sidebar, 12 px Radius) – für Einträge innerhalb einer Karte. */
export function InnereBox({ children, className }: { children: ReactNode; className?: string }) {
return (
<div className={clsx("border-border bg-sidebar rounded-box min-w-0 border p-4", className)}>
{children}
</div>
);
}