refactor(ui): organize component source tree
This commit is contained in:
parent
8927f2ab8a
commit
32ad2cebe4
66 changed files with 194 additions and 124 deletions
26
packages/ui/src/components/frames/CornerBracketFrame.tsx
Normal file
26
packages/ui/src/components/frames/CornerBracketFrame.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
export interface CornerBracketFrameProps {
|
||||
density?: "regular" | "tight";
|
||||
size?: "sm" | "md" | "lg";
|
||||
tone?: "neutral" | "accent" | "danger";
|
||||
}
|
||||
|
||||
export function CornerBracketFrame({
|
||||
density = "regular",
|
||||
size = "md",
|
||||
tone = "neutral",
|
||||
}: CornerBracketFrameProps) {
|
||||
return (
|
||||
<div
|
||||
className="corner-bracket-frame"
|
||||
data-density={density}
|
||||
data-size={size}
|
||||
data-tone={tone}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span data-corner="top-left" />
|
||||
<span data-corner="top-right" />
|
||||
<span data-corner="bottom-left" />
|
||||
<span data-corner="bottom-right" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
59
packages/ui/src/components/frames/DashboardFrame.tsx
Normal file
59
packages/ui/src/components/frames/DashboardFrame.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { useId } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import type { UiDashboardPreview } from "../../types";
|
||||
import { ModuleCard } from "./ModuleCard";
|
||||
import { ServicePanel } from "../operations/ServicePanel";
|
||||
import { StatusStrip } from "../operations/StatusStrip";
|
||||
import { TelemetryGrid } from "../telemetry/TelemetryGrid";
|
||||
|
||||
export interface DashboardFrameProps {
|
||||
actions?: ReactNode;
|
||||
dashboard: UiDashboardPreview;
|
||||
titleId?: string;
|
||||
}
|
||||
|
||||
export function DashboardFrame({
|
||||
actions,
|
||||
dashboard,
|
||||
titleId,
|
||||
}: DashboardFrameProps) {
|
||||
const generatedTitleId = useId();
|
||||
const resolvedTitleId = titleId || `${generatedTitleId}-title`;
|
||||
|
||||
return (
|
||||
<main className="dashboard-frame" aria-labelledby={resolvedTitleId}>
|
||||
<header className="dashboard-frame__header console-header">
|
||||
<div className="dashboard-frame__title">
|
||||
<div className="dashboard-frame__title-copy">
|
||||
{dashboard.eyebrow ? <p>{dashboard.eyebrow}</p> : null}
|
||||
<h1 id={resolvedTitleId}>{dashboard.title}</h1>
|
||||
{dashboard.subtitle ? <span>{dashboard.subtitle}</span> : null}
|
||||
</div>
|
||||
{actions ? (
|
||||
<div className="dashboard-frame__actions">{actions}</div>
|
||||
) : null}
|
||||
</div>
|
||||
{dashboard.modules.length ? (
|
||||
<div className="dashboard-frame__modules">
|
||||
{dashboard.modules.map((module) => (
|
||||
<ModuleCard key={module.id} module={module} />
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</header>
|
||||
|
||||
<TelemetryGrid cards={dashboard.telemetry} />
|
||||
|
||||
<section className="dashboard-frame__panels" aria-label="Service groups">
|
||||
{dashboard.serviceGroups.map((group) => (
|
||||
<ServicePanel key={group.id} group={group} />
|
||||
))}
|
||||
</section>
|
||||
|
||||
<StatusStrip
|
||||
id={dashboard.statusStripId}
|
||||
items={dashboard.statusItems}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
30
packages/ui/src/components/frames/DashboardHeader.tsx
Normal file
30
packages/ui/src/components/frames/DashboardHeader.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { useId } from "react";
|
||||
import type { UiModuleBlock } from "../../types";
|
||||
import { ModuleCard } from "./ModuleCard";
|
||||
|
||||
export interface DashboardHeaderProps {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
eyebrow?: string;
|
||||
module?: UiModuleBlock;
|
||||
}
|
||||
|
||||
export function DashboardHeader({
|
||||
title,
|
||||
subtitle,
|
||||
eyebrow,
|
||||
module,
|
||||
}: DashboardHeaderProps) {
|
||||
const titleId = useId();
|
||||
|
||||
return (
|
||||
<header className="dashboard-header" aria-labelledby={titleId}>
|
||||
<div>
|
||||
{eyebrow ? <p>{eyebrow}</p> : null}
|
||||
<h1 id={titleId}>{title}</h1>
|
||||
{subtitle ? <span>{subtitle}</span> : null}
|
||||
</div>
|
||||
{module ? <ModuleCard module={module} /> : null}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
24
packages/ui/src/components/frames/DiagonalStripeField.tsx
Normal file
24
packages/ui/src/components/frames/DiagonalStripeField.tsx
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
export interface DiagonalStripeFieldProps {
|
||||
density?: "open" | "regular" | "tight";
|
||||
direction?: "forward" | "backward";
|
||||
size?: "sm" | "md" | "lg";
|
||||
tone?: "neutral" | "accent" | "warning" | "danger";
|
||||
}
|
||||
|
||||
export function DiagonalStripeField({
|
||||
density = "regular",
|
||||
direction = "forward",
|
||||
size = "md",
|
||||
tone = "accent",
|
||||
}: DiagonalStripeFieldProps) {
|
||||
return (
|
||||
<div
|
||||
className="diagonal-stripe-field"
|
||||
data-density={density}
|
||||
data-direction={direction}
|
||||
data-size={size}
|
||||
data-tone={tone}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
);
|
||||
}
|
||||
42
packages/ui/src/components/frames/FooterCell.tsx
Normal file
42
packages/ui/src/components/frames/FooterCell.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import type { UiStatusItem } from "../../types";
|
||||
|
||||
export interface FooterCellProps {
|
||||
item: UiStatusItem;
|
||||
}
|
||||
|
||||
export function FooterCell({ item }: FooterCellProps) {
|
||||
const target = item.link?.external ? "_blank" : undefined;
|
||||
const rel = item.link?.external ? "noreferrer" : undefined;
|
||||
const content = (
|
||||
<>
|
||||
<span>{item.label}</span>
|
||||
<strong>{item.value}</strong>
|
||||
</>
|
||||
);
|
||||
|
||||
if (item.link) {
|
||||
return (
|
||||
<a
|
||||
className="footer-cell"
|
||||
data-severity={item.severity || "neutral"}
|
||||
data-model-id={item.id}
|
||||
href={item.link.href}
|
||||
target={target}
|
||||
rel={rel}
|
||||
aria-label={item.link.label}
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="footer-cell"
|
||||
data-severity={item.severity || "neutral"}
|
||||
data-model-id={item.id}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
6
packages/ui/src/components/frames/FooterStatusCell.tsx
Normal file
6
packages/ui/src/components/frames/FooterStatusCell.tsx
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import type { UiStatusItem } from "../../types";
|
||||
import { FooterCell } from "./FooterCell";
|
||||
|
||||
export function FooterStatusCell({ item }: { item: UiStatusItem }) {
|
||||
return <FooterCell item={item} />;
|
||||
}
|
||||
13
packages/ui/src/components/frames/GridFrame.tsx
Normal file
13
packages/ui/src/components/frames/GridFrame.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import type { PropsWithChildren } from "react";
|
||||
|
||||
export interface GridFrameProps extends PropsWithChildren {
|
||||
density?: "compact" | "dense";
|
||||
}
|
||||
|
||||
export function GridFrame({ children, density = "dense" }: GridFrameProps) {
|
||||
return (
|
||||
<section className="grid-frame" data-density={density}>
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
32
packages/ui/src/components/frames/ModuleCard.tsx
Normal file
32
packages/ui/src/components/frames/ModuleCard.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { useId } from "react";
|
||||
import type { UiModuleBlock } from "../../types";
|
||||
import { IconGlyph } from "../foundation/IconGlyph";
|
||||
|
||||
export interface ModuleCardProps {
|
||||
module: UiModuleBlock;
|
||||
}
|
||||
|
||||
export function ModuleCard({ module }: ModuleCardProps) {
|
||||
const generatedId = useId();
|
||||
const titleId = `${generatedId}-title`;
|
||||
const ariaLabel = module.title ? undefined : module.label || module.id;
|
||||
|
||||
return (
|
||||
<aside
|
||||
className="module-card"
|
||||
data-severity={module.severity || "neutral"}
|
||||
data-model-id={module.id}
|
||||
aria-labelledby={module.title ? titleId : undefined}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
<div>
|
||||
{module.title ? <h2 id={titleId}>{module.title}</h2> : null}
|
||||
{module.value ? <strong>{module.value}</strong> : null}
|
||||
{module.detail || module.label ? (
|
||||
<p>{module.detail || module.label}</p>
|
||||
) : null}
|
||||
</div>
|
||||
{module.icon ? <IconGlyph name={module.icon} size="lg" /> : null}
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
19
packages/ui/src/components/frames/Panel.tsx
Normal file
19
packages/ui/src/components/frames/Panel.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import type { PropsWithChildren } from "react";
|
||||
|
||||
export interface PanelProps extends PropsWithChildren {
|
||||
title?: string;
|
||||
density?: "compact" | "dense";
|
||||
}
|
||||
|
||||
export function Panel({ title, density = "dense", children }: PanelProps) {
|
||||
return (
|
||||
<section className="panel" data-density={density}>
|
||||
{title ? (
|
||||
<header className="panel__header">
|
||||
<h2>{title}</h2>
|
||||
</header>
|
||||
) : null}
|
||||
<div className="panel__body">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
21
packages/ui/src/components/frames/ScanlineField.tsx
Normal file
21
packages/ui/src/components/frames/ScanlineField.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
export interface ScanlineFieldProps {
|
||||
intensity?: "soft" | "medium" | "hard";
|
||||
size?: "sm" | "md" | "lg";
|
||||
tone?: "neutral" | "accent" | "warning";
|
||||
}
|
||||
|
||||
export function ScanlineField({
|
||||
intensity = "medium",
|
||||
size = "md",
|
||||
tone = "neutral",
|
||||
}: ScanlineFieldProps) {
|
||||
return (
|
||||
<div
|
||||
className="scanline-field"
|
||||
data-intensity={intensity}
|
||||
data-size={size}
|
||||
data-tone={tone}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue