swarm-ui: the refresh picker of a polling card sits in the card's header row
On the jobs, builds and hives cards the picker was alone on the first line of the body. DisclosureAction puts a body's control in the header of the disclosure it is in, beside the chevron and outside the toggle, so it costs no row and pressing it does not fold the card.
This commit is contained in:
parent
262505e16a
commit
bf7345405f
5 changed files with 43 additions and 9 deletions
|
|
@ -22,9 +22,9 @@ import type { ComponentChildren } from "preact";
|
|||
import { useState } from "preact/hooks";
|
||||
import { JobqGraph } from "@hive/shared/jobq-graph.js";
|
||||
import { JobqRollup } from "@hive/shared/jobq-rollup.js";
|
||||
import { DisclosureAction } from "../ui/disclosure/DisclosureAction.js";
|
||||
import { LazyDisclosure } from "../ui/disclosure/LazyDisclosure.js";
|
||||
import { Skeleton, SkeletonRegion } from "../ui/skeleton/Skeleton.js";
|
||||
import { Toolbar } from "../ui/toolbar/Toolbar.js";
|
||||
import { BuildsCard } from "./builds/BuildsPage.js";
|
||||
import { ReviewPage } from "./review/ReviewPage.js";
|
||||
import {
|
||||
|
|
@ -89,13 +89,13 @@ function JobsBody() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Toolbar>
|
||||
<DisclosureAction>
|
||||
<RefreshIntervalPicker
|
||||
id="jobs-refresh"
|
||||
value={intervalMs}
|
||||
onChange={setIntervalMs}
|
||||
/>
|
||||
</Toolbar>
|
||||
</DisclosureAction>
|
||||
<JobqRollup endpoint="/api/jobq/rollup" refreshToken={refreshToken} />
|
||||
<JobqGraph
|
||||
endpoint="/api/jobq/graph"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
} from "../../ui/expandable-card/ExpandableCard.js";
|
||||
import { Hint } from "../../ui/hint/Hint.js";
|
||||
import { Preformatted } from "../../ui/preformatted/Preformatted.js";
|
||||
import { Toolbar } from "../../ui/toolbar/Toolbar.js";
|
||||
import { DisclosureAction } from "../../ui/disclosure/DisclosureAction.js";
|
||||
import { LazyDisclosure } from "../../ui/disclosure/LazyDisclosure.js";
|
||||
import {
|
||||
RefreshIntervalPicker,
|
||||
|
|
@ -134,14 +134,14 @@ function BuildsBody() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Toolbar>
|
||||
<DisclosureAction>
|
||||
<RefreshIntervalPicker
|
||||
id="builds-refresh"
|
||||
value={intervalMs}
|
||||
onChange={setIntervalMs}
|
||||
refreshing={refreshing}
|
||||
/>
|
||||
</Toolbar>
|
||||
</DisclosureAction>
|
||||
<p>
|
||||
What the swarm builder built, newest first: agent config PR heads, and
|
||||
the agents it builds from internal/meta before a deploy. How each ended,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { useState } from "preact/hooks";
|
|||
import { ApiErrorPanel } from "@hive/shared/api-error-panel.js";
|
||||
import { Badge, type BadgeTone } from "@hive/shared/badge.js";
|
||||
import { ExternalLink } from "../../ui/external-link/ExternalLink.js";
|
||||
import { DisclosureAction } from "../../ui/disclosure/DisclosureAction.js";
|
||||
import { LazyDisclosure } from "../../ui/disclosure/LazyDisclosure.js";
|
||||
import { RelativeTime } from "../../ui/relative-time/RelativeTime.js";
|
||||
import {
|
||||
|
|
@ -26,7 +27,6 @@ import {
|
|||
import { fetchJson, usePolled } from "../../ui/polled/usePolled.js";
|
||||
import { Table, type TableColumn } from "../../ui/table/Table.js";
|
||||
import { TableSkeleton } from "../../ui/table/TableSkeleton.js";
|
||||
import { Toolbar } from "../../ui/toolbar/Toolbar.js";
|
||||
import { domainCell } from "../hive-domain.js";
|
||||
|
||||
type Freshness = "fresh" | "stale" | "never_reported" | "unknown";
|
||||
|
|
@ -125,14 +125,14 @@ function HiveRoster() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Toolbar>
|
||||
<DisclosureAction>
|
||||
<RefreshIntervalPicker
|
||||
id="hives-refresh"
|
||||
value={intervalMs}
|
||||
onChange={setIntervalMs}
|
||||
refreshing={refreshing}
|
||||
/>
|
||||
</Toolbar>
|
||||
</DisclosureAction>
|
||||
{error ? (
|
||||
<ApiErrorPanel
|
||||
context="failed to load the hive roster"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
/* The disclosure is the positioning box; the action sits on the title line of
|
||||
its header (the header's top padding, one line of title) and to the left of
|
||||
the chevron. The hint stops short of it, so the two do not overlap. */
|
||||
.ui-disclosure {
|
||||
position: relative;
|
||||
}
|
||||
.ui-disclosure-action {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0.75em;
|
||||
right: 2.6em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 1.6em;
|
||||
}
|
||||
.ui-disclosure:has(.ui-disclosure-action) .ui-disclosure-hint {
|
||||
margin-right: 7em;
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// <DisclosureAction> — a control for the header row of the disclosure it is in:
|
||||
// the refresh picker of a card that polls. The body owns the control's state, so
|
||||
// a body renders it where it needs the state, and it shows in the header, beside
|
||||
// the chevron, instead of taking a row of the body. It is a sibling of the
|
||||
// header's toggle, not inside it, so pressing it does not fold the card. A
|
||||
// disclosure that is shut has no body, so the control is there only while open.
|
||||
import type { ComponentChildren } from "preact";
|
||||
import "./DisclosureAction.css";
|
||||
|
||||
export function DisclosureAction({
|
||||
children,
|
||||
}: {
|
||||
children: ComponentChildren;
|
||||
}) {
|
||||
return <div class="ui-disclosure-action">{children}</div>;
|
||||
}
|
||||
Loading…
Reference in a new issue