swarm-ui: add a components-library page (hyperhive#3288)
New /components route showing every ui primitive (Panel, StatusChip, Table) in each mode it supports: Panel with/without a title, StatusChip's four tones, Table populated and empty. Sample data only, no API calls, so it renders identically regardless of backend state. Linked from the Shell nav next to "overview". Verified with npm run build + npm run typecheck, and a headless chromium screenshot of the built dist.
This commit is contained in:
parent
1e0ec9b5af
commit
cfc8f2b22c
4 changed files with 137 additions and 1 deletions
|
|
@ -6,6 +6,7 @@
|
|||
import { useEffect, useState } from 'preact/hooks';
|
||||
import { Route, Switch } from 'wouter-preact';
|
||||
import { Shell } from './shell/Shell.js';
|
||||
import { ComponentsPage } from './pages/ComponentsPage.js';
|
||||
import { Panel } from './ui/panel/Panel.js';
|
||||
import { StatusChip } from './ui/status-chip/StatusChip.js';
|
||||
import { Table, type TableColumn } from './ui/table/Table.js';
|
||||
|
|
@ -69,6 +70,7 @@ export function App() {
|
|||
<Shell>
|
||||
<Switch>
|
||||
<Route path="/" component={Home} />
|
||||
<Route path="/components" component={ComponentsPage} />
|
||||
<Route component={NotFound} />
|
||||
</Switch>
|
||||
</Shell>
|
||||
|
|
|
|||
44
frontend/packages/swarm-ui/src/pages/ComponentsPage.css
Normal file
44
frontend/packages/swarm-ui/src/pages/ComponentsPage.css
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* <ComponentsPage> layout — a plain vertical stack of sections, each a
|
||||
labelled grid of samples. No new colour vars: reuses the same
|
||||
base16 slots (../theme.css) every other component draws from, so
|
||||
this page can't drift from the palette it's demonstrating. */
|
||||
.components-intro {
|
||||
margin: 0 0 1.5em;
|
||||
color: var(--muted);
|
||||
}
|
||||
.components-intro code {
|
||||
color: var(--fg);
|
||||
}
|
||||
.components-section {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
.components-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.components-section-title {
|
||||
margin: 0 0 0.75em;
|
||||
font-size: 0.9em;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--muted);
|
||||
}
|
||||
.components-section-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
}
|
||||
.components-chip-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1em;
|
||||
}
|
||||
.components-sample {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4em;
|
||||
}
|
||||
.components-sample-label {
|
||||
font-size: 0.8em;
|
||||
color: var(--muted);
|
||||
}
|
||||
87
frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx
Normal file
87
frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// <ComponentsPage> — living style guide for every ui primitive this
|
||||
// package ships. One section per component, each mode it supports
|
||||
// rendered side by side with a caption naming the mode — so a visual
|
||||
// regression or a missing variant is obvious at a glance, and a new
|
||||
// primitive gets a section here the same day it's added. Sample data
|
||||
// only, no network calls — this page must render the same whether
|
||||
// swarm-controller's API is up or not.
|
||||
import type { ComponentChildren } from 'preact';
|
||||
import { Panel } from '../ui/panel/Panel.js';
|
||||
import { StatusChip, type ChipTone } from '../ui/status-chip/StatusChip.js';
|
||||
import { Table, type TableColumn } from '../ui/table/Table.js';
|
||||
import './ComponentsPage.css';
|
||||
|
||||
function Section({ title, children }: { title: string; children: ComponentChildren }) {
|
||||
return (
|
||||
<section class="components-section">
|
||||
<h2 class="components-section-title">{title}</h2>
|
||||
<div class="components-section-body">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function Sample({ label, children }: { label: string; children: ComponentChildren }) {
|
||||
return (
|
||||
<div class="components-sample">
|
||||
<div class="components-sample-label">{label}</div>
|
||||
<div class="components-sample-content">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const CHIP_TONES: ChipTone[] = ['neutral', 'positive', 'warning', 'negative'];
|
||||
|
||||
interface Row {
|
||||
name: string;
|
||||
detail: string;
|
||||
}
|
||||
|
||||
const TABLE_COLUMNS: TableColumn<Row>[] = [
|
||||
{ key: 'name', header: 'name', render: (r) => r.name },
|
||||
{ key: 'detail', header: 'detail', render: (r) => r.detail },
|
||||
{ key: 'status', header: 'status', render: () => <StatusChip tone="positive" label="ok" /> },
|
||||
];
|
||||
|
||||
const TABLE_ROWS: Row[] = [
|
||||
{ name: 'alpha', detail: 'sample row one' },
|
||||
{ name: 'beta', detail: 'sample row two' },
|
||||
];
|
||||
|
||||
export function ComponentsPage() {
|
||||
return (
|
||||
<Panel title="components">
|
||||
<p class="components-intro">
|
||||
Every primitive in <code>src/ui/</code>, shown in each mode it supports. Sample data
|
||||
only — nothing here calls the API.
|
||||
</p>
|
||||
|
||||
<Section title="Panel">
|
||||
<Sample label="with title">
|
||||
<Panel title="example title">panel body content</Panel>
|
||||
</Sample>
|
||||
<Sample label="without title">
|
||||
<Panel>panel body content, no title</Panel>
|
||||
</Sample>
|
||||
</Section>
|
||||
|
||||
<Section title="StatusChip">
|
||||
<div class="components-chip-row">
|
||||
{CHIP_TONES.map((tone) => (
|
||||
<Sample key={tone} label={tone}>
|
||||
<StatusChip tone={tone} label={tone} />
|
||||
</Sample>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section title="Table">
|
||||
<Sample label="populated">
|
||||
<Table columns={TABLE_COLUMNS} rows={TABLE_ROWS} rowKey={(r) => r.name} />
|
||||
</Sample>
|
||||
<Sample label="empty">
|
||||
<Table columns={TABLE_COLUMNS} rows={[]} rowKey={(r) => r.name} />
|
||||
</Sample>
|
||||
</Section>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
|
@ -16,7 +16,10 @@ import type { ComponentChildren } from 'preact';
|
|||
import { Link, useRoute } from 'wouter-preact';
|
||||
import './Shell.css';
|
||||
|
||||
const NAV_ITEMS: { href: string; label: string }[] = [{ href: '/', label: 'overview' }];
|
||||
const NAV_ITEMS: { href: string; label: string }[] = [
|
||||
{ href: '/', label: 'overview' },
|
||||
{ href: '/components', label: 'components' },
|
||||
];
|
||||
|
||||
function NavLink({ href, label }: { href: string; label: string }) {
|
||||
const [active] = useRoute(href);
|
||||
|
|
|
|||
Loading…
Reference in a new issue