swarm-ui: give Panel an optional header icon slot
Adds an `icon` prop to Panel (small emoji glyph left of the title, aria-hidden, chosen per panel with no default) and wires it into every current Panel caller: hives (bee), create-agent's form (robot) and info panel (identity card, moved off the info panel's body copy where it started as a one-off), jobs (puzzle piece), the components gallery itself (toolbox), and the 404 page (compass). Adds a components-page demo section and a whimsy-section pointer in the design guide. Closes: #3508
This commit is contained in:
parent
582b5cf83d
commit
b25aa157df
9 changed files with 49 additions and 15 deletions
|
|
@ -24,7 +24,13 @@ repeated here.
|
|||
home.js`, is the reference example — currently in the dashboard
|
||||
package, not swarm-ui itself, but the pattern it sets applies here
|
||||
too). Whimsy still has to clear the accessibility bar below (motion,
|
||||
in particular).
|
||||
in particular). swarm-ui's own reference example: `Panel`'s optional
|
||||
`icon` prop (`src/ui/panel/Panel.tsx`), a small emoji glyph in a
|
||||
panel's header, chosen per panel with no default — grew out of a
|
||||
one-off emoji dropped into a single page's copy, which wasn't whimsy
|
||||
in this sense (a *consistent*, reusable touch) until it became a real
|
||||
prop every panel can opt into. `aria-hidden`, since it's decorative —
|
||||
the title text is still the actual label.
|
||||
- **Efficient navigation** — minimize clicks/hops for a common task.
|
||||
- **Avoid junk drawers.** A control belongs next to the thing it
|
||||
affects, not tucked into a catch-all menu. Concrete anti-example (not
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { Panel } from './ui/panel/Panel.js';
|
|||
|
||||
function NotFound() {
|
||||
return (
|
||||
<Panel title="404">
|
||||
<Panel title="404" icon="🧭">
|
||||
<p>no route here.</p>
|
||||
</Panel>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ function RefreshIntervalPickerSample() {
|
|||
|
||||
export function ComponentsPage() {
|
||||
return (
|
||||
<Panel title="components">
|
||||
<Panel title="components" icon="🧰">
|
||||
<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.
|
||||
|
|
@ -101,6 +101,11 @@ export function ComponentsPage() {
|
|||
<Sample label="without title">
|
||||
<Panel>panel body content, no title</Panel>
|
||||
</Sample>
|
||||
<Sample label="with icon (per panel, no default — see the doc comment)">
|
||||
<Panel title="example title" icon="✨">
|
||||
panel body content
|
||||
</Panel>
|
||||
</Sample>
|
||||
<Sample label="with actions (e.g. HivesPage's refresh picker)">
|
||||
<Panel
|
||||
title="example title"
|
||||
|
|
|
|||
|
|
@ -36,10 +36,6 @@
|
|||
flex: 1 1 0;
|
||||
min-width: 16em;
|
||||
}
|
||||
.create-agent-info-glyph {
|
||||
margin: 0 0 0.5em;
|
||||
font-size: 2em;
|
||||
}
|
||||
.create-agent-intro {
|
||||
margin: 0 0 1.5em;
|
||||
color: var(--muted);
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ export function CreateAgentPage() {
|
|||
return (
|
||||
<div class="create-agent-layout">
|
||||
<div class="create-agent-form-col">
|
||||
<Panel title="create agent">
|
||||
<Panel title="create agent" icon="🤖">
|
||||
<p class="create-agent-intro">
|
||||
Create a new agent's swarm-level identity. This only queues the job — check{' '}
|
||||
<Link href="/jobs">jobs</Link> to watch it settle.
|
||||
|
|
@ -180,11 +180,11 @@ export function CreateAgentPage() {
|
|||
literal behaviour (deploying the container onto the hive isn't
|
||||
wired up server-side yet — see this file's top comment) —
|
||||
mara's explicit call on this copy: read as finished, not as a
|
||||
running commentary on partial implementation. */}
|
||||
<Panel title="what this creates">
|
||||
<p class="create-agent-info-glyph" aria-hidden="true">
|
||||
🪪
|
||||
</p>
|
||||
running commentary on partial implementation. The 🪪 glyph that
|
||||
used to sit here as body copy now lives on `Panel`'s own `icon`
|
||||
prop instead — it's what prompted that prop to exist at all
|
||||
(see Panel.tsx's doc comment) */}
|
||||
<Panel title="what this creates" icon="🪪">
|
||||
<p>
|
||||
Submitting this queues everything a new agent needs: a swarm-level identity, a config
|
||||
repo on the forge with the operator added as a collaborator, and a container running
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ export function HivesPage() {
|
|||
return (
|
||||
<Panel
|
||||
title="hives"
|
||||
icon="🐝"
|
||||
actions={<RefreshIntervalPicker id="hives-refresh" value={intervalMs} onChange={setIntervalMs} />}
|
||||
>
|
||||
{error ? <ApiErrorPanel context="failed to load the hive roster" problem={error} /> : null}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import './JobsPage.css';
|
|||
|
||||
export function JobsPage() {
|
||||
return (
|
||||
<Panel title="jobs">
|
||||
<Panel title="jobs" icon="🧩">
|
||||
<JobqRollup endpoint="/api/jobq/rollup" />
|
||||
<JobqGraph endpoint="/api/jobq/graph" />
|
||||
</Panel>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@
|
|||
font-size: 1em;
|
||||
font-weight: 600;
|
||||
}
|
||||
.ui-panel-icon {
|
||||
font-size: 1.2em;
|
||||
line-height: 1;
|
||||
}
|
||||
/* `margin-left: auto` (not `justify-content: space-between` on the
|
||||
header) so actions still land at the right edge even on the rare
|
||||
panel that has actions but no title. */
|
||||
|
|
|
|||
|
|
@ -11,22 +11,44 @@
|
|||
// dedicated row stealing vertical space from the panel's actual
|
||||
// content either). `HivesPage`'s refresh-interval picker is the
|
||||
// motivating caller.
|
||||
//
|
||||
// `icon` is a small header glyph, left of the title — the swarm-ui
|
||||
// design guide's own whimsy reference (before this, the guide only
|
||||
// pointed at the dashboard's matrix-rain background). Grew out of
|
||||
// CreateAgentPage's one-off 🪪 dropped straight into a panel's body
|
||||
// copy: mara's call on review was that a single ad-hoc emoji isn't
|
||||
// whimsy in the guide's sense (small, delightful, *consistent*), it
|
||||
// should be a real theme every panel can opt into the same way. Plain
|
||||
// `string` (an emoji literal), not an icon-library asset — same
|
||||
// lightweight-glyph precedent `RefreshIntervalPicker`'s 🕐 set before
|
||||
// this. Chosen per panel, no default: not every panel needs one, and
|
||||
// there's no single semantic mapping (e.g. "jobs" pages) worth
|
||||
// hardcoding. `aria-hidden` — decorative only, the title text still
|
||||
// carries the actual label, so this never becomes a second source of
|
||||
// truth an assistive-tech user has to parse.
|
||||
import type { ComponentChildren } from 'preact';
|
||||
import './Panel.css';
|
||||
|
||||
export function Panel({
|
||||
title,
|
||||
icon,
|
||||
actions,
|
||||
children,
|
||||
}: {
|
||||
title?: string;
|
||||
icon?: string;
|
||||
actions?: ComponentChildren;
|
||||
children: ComponentChildren;
|
||||
}) {
|
||||
return (
|
||||
<section class="ui-panel">
|
||||
{title || actions ? (
|
||||
{title || icon || actions ? (
|
||||
<div class="ui-panel-header">
|
||||
{icon ? (
|
||||
<span class="ui-panel-icon" aria-hidden="true">
|
||||
{icon}
|
||||
</span>
|
||||
) : null}
|
||||
{title ? <h2 class="ui-panel-title">{title}</h2> : null}
|
||||
{actions ? <div class="ui-panel-actions">{actions}</div> : null}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue