swarm-ui/agents: split the page, extract FilterableView per mara's follow-up
Three more asks from the same review thread: - "agentspage is now giant and deserves a split" - AgentsPage.tsx was 1047 lines. Split into AgentTypes.ts (AgentRow and friends), WantedMenu.tsx, AgentCard.tsx (+ its own CSS), leaving AgentsPage.tsx as state/actions/columns/the render tree - 649 lines, and every piece it composes is now independently readable. - "what about the component that represents filtered data ... that the card view and table can both use?" - extracted FilterableView (ui/filterable-view/): takes columns/rows/rowKey/storageKey/view/ renderCard, builds its filter bar from *every* filterable column (not a hand-picked subset - the old AgentFilterBar only showed 4 of the agent columns' 6 filterable fields, an accidental gap the table's own popovers didn't have), and renders either the card list or Table. AgentsPage now just tells it which view to show; the view toggle itself stays page-side since it's Panel-header chrome, not filtering. Disclosed side effect: card view's filter bar now also covers message/config-PR (text filters), matching table view exactly instead of a narrower subset. - CSS audit: AgentsPage.css now holds only what's genuinely page-specific (the view toggle, the detail-panel field grid) - everything else moved to its owning component's own colocated CSS. FilterableView gets a /components demo (view toggle + filter bar + both render modes, same day per the design guide). Verified: AgentsPage still renders the same (real screenshot), and the demo's own table toggle produces a real Table with the same rows.
This commit is contained in:
parent
3b66f0c8d6
commit
601068f1eb
9 changed files with 591 additions and 491 deletions
|
|
@ -8,6 +8,7 @@
|
|||
import { useRef, useState } from "preact/hooks";
|
||||
import type { ComponentChildren } from "preact";
|
||||
import { Card } from "../ui/card/Card.js";
|
||||
import { FilterableView } from "../ui/filterable-view/FilterableView.js";
|
||||
import {
|
||||
MultiselectFilter,
|
||||
type MultiselectFilterState,
|
||||
|
|
@ -243,6 +244,46 @@ function MultiselectFilterSample() {
|
|||
);
|
||||
}
|
||||
|
||||
// A `view` toggle here is caller-owned (same contract `AgentsPage` uses)
|
||||
// — `FilterableView` itself only renders whichever one this sample
|
||||
// says to. Reuses `TABLE_COLUMNS`/`TABLE_ROWS` from the `Table` section
|
||||
// above so the filter behavior is directly comparable between the two.
|
||||
function FilterableViewSample() {
|
||||
const [view, setView] = useState<"cards" | "table">("cards");
|
||||
return (
|
||||
<div>
|
||||
<div class="components-chip-row" style={{ marginBottom: "0.5em" }}>
|
||||
<Button
|
||||
variant={view === "cards" ? "primary" : "default"}
|
||||
onClick={() => setView("cards")}
|
||||
>
|
||||
cards
|
||||
</Button>
|
||||
<Button
|
||||
variant={view === "table" ? "primary" : "default"}
|
||||
onClick={() => setView("table")}
|
||||
>
|
||||
table
|
||||
</Button>
|
||||
</div>
|
||||
<FilterableView
|
||||
columns={TABLE_COLUMNS}
|
||||
rows={TABLE_ROWS}
|
||||
rowKey={(r) => r.name}
|
||||
storageKey="swarm-ui:components-demo:filterable-view"
|
||||
view={view}
|
||||
emptyMessage="no rows"
|
||||
renderCard={(r) => (
|
||||
<Card onClick={() => {}}>
|
||||
<strong>{r.name}</strong>
|
||||
<div>{r.detail}</div>
|
||||
</Card>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ConfirmDialogSample() {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
|
|
@ -365,6 +406,12 @@ export function ComponentsPage() {
|
|||
</Sample>
|
||||
</Section>
|
||||
|
||||
<Section title="FilterableView">
|
||||
<Sample label="filter bar (built from every filterable column) + cards/table, same filter state either way">
|
||||
<FilterableViewSample />
|
||||
</Sample>
|
||||
</Section>
|
||||
|
||||
<Section title="RelativeTime">
|
||||
<div class="components-chip-row">
|
||||
<Sample label="5s ago, ticking">
|
||||
|
|
|
|||
Loading…
Reference in a new issue