Initial commit
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
/**
|
||||
* Building blocks for the detail / settings rhythm system. All spacing
|
||||
* resolves to CSS tokens declared in `app/globals.css` under @theme:
|
||||
* --spacing-card → p-card (card interior padding)
|
||||
* --spacing-card-gap → gap-card-gap (gap between sibling cards)
|
||||
* --spacing-section → gap-section / pt-section (intra-card section gap)
|
||||
* --spacing-chip → gap-chip (chip clusters, pill grids, button bars)
|
||||
* --spacing-label → mb-label (label header → content)
|
||||
* --spacing-stat → mb-stat (hero-stat label → big number)
|
||||
* --spacing-stat-gap → gap-stat-gap (horizontal between hero-stat cols)
|
||||
*
|
||||
* Use these instead of raw `p-[15px]` / `gap-[9px]` so a single token
|
||||
* change ripples across every page.
|
||||
*/
|
||||
|
||||
/** Outer card frame. Glass surface, rounded corners, uniform padding. */
|
||||
export function Panel({
|
||||
children,
|
||||
className,
|
||||
as: Tag = "div",
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
/** Render as a different tag if needed (e.g. "section", "aside"). */
|
||||
as?: keyof React.JSX.IntrinsicElements;
|
||||
}) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const T = Tag as any;
|
||||
return (
|
||||
<T className={cn("glass rounded-2xl p-card", className)}>
|
||||
{children}
|
||||
</T>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertical container for sibling Panels. Replaces ad-hoc `space-y-N` on
|
||||
* an aside or column wrapper — picks up the panel-to-panel rhythm.
|
||||
*/
|
||||
export function PanelStack({
|
||||
children,
|
||||
className,
|
||||
as: Tag = "div",
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
as?: keyof React.JSX.IntrinsicElements;
|
||||
}) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const T = Tag as any;
|
||||
return (
|
||||
<T className={cn("flex flex-col gap-card-gap", className)}>
|
||||
{children}
|
||||
</T>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertical flow inside a Panel — pulls children apart by the
|
||||
* intra-card section gap. Use for stacked blocks (header → flag pills
|
||||
* → meta strip → hero stats), NOT for label→content (use PanelHeader for that).
|
||||
*/
|
||||
export function PanelSection({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-section", className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mono caps label, e.g. "ACTRESSES" / "TAGS". Renders the label and
|
||||
* applies the standard label→content margin to whatever sits beneath.
|
||||
* Pair with the chip cluster or any other content block.
|
||||
*/
|
||||
export function PanelHeader({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"text-[10px] uppercase tracking-wider font-mono text-[var(--color-fg-muted)] mb-label",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Horizontal flex-wrap chip strip used for actresses / genres / tags /
|
||||
* collections / flag pill rows. Spacing is the unified `gap-chip` token.
|
||||
*/
|
||||
export function ChipCluster({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className={cn("flex flex-wrap gap-chip", className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equal-column button row that sits OUTSIDE any Panel (Edit Metadata /
|
||||
* Import / Delete pattern). Lives in the same vertical rhythm as
|
||||
* Panels via PanelStack's gap-card-gap.
|
||||
*/
|
||||
export function ActionBar({
|
||||
children,
|
||||
cols = 3,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
cols?: 2 | 3 | 4;
|
||||
className?: string;
|
||||
}) {
|
||||
const colClass =
|
||||
cols === 2 ? "grid-cols-2" : cols === 4 ? "grid-cols-4" : "grid-cols-3";
|
||||
return (
|
||||
<div className={cn("grid gap-chip", colClass, className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user