Watch
0
0
Fork
You've already forked hyperhive
0

fixup! swarm-ui: shared Hint, ActionRow, Toolbar, EmptyState, ExternalLink and ExpandableCard replace per-page copies

This commit is contained in:
iris 2026-11-06 17:59:32 +01:00 • committed by mara
commit 2b53fcd742
5 changed files with 31 additions and 18 deletions

View file

@ -38,7 +38,7 @@ import { ActionRow } from "../ui/action-row/ActionRow.js";
import { EmptyState } from "../ui/empty-state/EmptyState.js"; import { EmptyState } from "../ui/empty-state/EmptyState.js";
import { import {
ExpandableCard, ExpandableCard,
ExpandableCardSkeleton, ExpandableCardListSkeleton,
} from "../ui/expandable-card/ExpandableCard.js"; } from "../ui/expandable-card/ExpandableCard.js";
import { ExternalLink } from "../ui/external-link/ExternalLink.js"; import { ExternalLink } from "../ui/external-link/ExternalLink.js";
import { Hint, type HintTone } from "../ui/hint/Hint.js"; import { Hint, type HintTone } from "../ui/hint/Hint.js";
@ -653,7 +653,7 @@ export function ComponentsPage() {
> >
<Hint>The body appears while the card is open.</Hint> <Hint>The body appears while the card is open.</Hint>
</ExpandableCard> </ExpandableCard>
<ExpandableCardSkeleton /> <ExpandableCardListSkeleton label="the builds" count={1} />
</Sample> </Sample>
</Section> </Section>

View file

@ -12,7 +12,7 @@ import { Badge } from "@hive/shared/badge.js";
import { EmptyState } from "../../ui/empty-state/EmptyState.js"; import { EmptyState } from "../../ui/empty-state/EmptyState.js";
import { import {
ExpandableCard, ExpandableCard,
ExpandableCardSkeleton, ExpandableCardListSkeleton,
} from "../../ui/expandable-card/ExpandableCard.js"; } from "../../ui/expandable-card/ExpandableCard.js";
import { Hint } from "../../ui/hint/Hint.js"; import { Hint } from "../../ui/hint/Hint.js";
import { Toolbar } from "../../ui/toolbar/Toolbar.js"; import { Toolbar } from "../../ui/toolbar/Toolbar.js";
@ -163,11 +163,7 @@ function BuildsBody() {
) : null} ) : null}
<div class="ui-builds-list"> <div class="ui-builds-list">
{runs === null && !error ? ( {runs === null && !error ? (
<> <ExpandableCardListSkeleton label="the builds" />
<ExpandableCardSkeleton />
<ExpandableCardSkeleton />
<ExpandableCardSkeleton />
</>
) : null} ) : null}
{(runs ?? []).map((run) => ( {(runs ?? []).map((run) => (
<BuildCard <BuildCard

View file

@ -11,7 +11,7 @@ import { EmptyState } from "../../ui/empty-state/EmptyState.js";
import { ExternalLink } from "../../ui/external-link/ExternalLink.js"; import { ExternalLink } from "../../ui/external-link/ExternalLink.js";
import { import {
ExpandableCard, ExpandableCard,
ExpandableCardSkeleton, ExpandableCardListSkeleton,
} from "../../ui/expandable-card/ExpandableCard.js"; } from "../../ui/expandable-card/ExpandableCard.js";
import { Hint } from "../../ui/hint/Hint.js"; import { Hint } from "../../ui/hint/Hint.js";
import { Panel } from "../../ui/panel/Panel.js"; import { Panel } from "../../ui/panel/Panel.js";
@ -141,11 +141,7 @@ export function ReviewRuns() {
) : null} ) : null}
<div class="ui-runs-list"> <div class="ui-runs-list">
{runs === null && !error ? ( {runs === null && !error ? (
<> <ExpandableCardListSkeleton label="the runs" metaOnOwnRow />
<ExpandableCardSkeleton metaOnOwnRow />
<ExpandableCardSkeleton metaOnOwnRow />
<ExpandableCardSkeleton metaOnOwnRow />
</>
) : null} ) : null}
{(runs ?? []).map((run) => ( {(runs ?? []).map((run) => (
<RunCard <RunCard

View file

@ -10,8 +10,10 @@
// //
// `ExpandableCardSkeleton` is its twin for a list that is loading: the same // `ExpandableCardSkeleton` is its twin for a list that is loading: the same
// card and the same head layout with a placeholder in each slot. // card and the same head layout with a placeholder in each slot.
// `ExpandableCardListSkeleton` is a few of them in a `SkeletonRegion`, so
// assistive tech hears once that the list is loading.
import type { ComponentChildren } from "preact"; import type { ComponentChildren } from "preact";
import { Skeleton } from "../skeleton/Skeleton.js"; import { Skeleton, SkeletonRegion } from "../skeleton/Skeleton.js";
import { Card } from "../card/Card.js"; import { Card } from "../card/Card.js";
import "./ExpandableCard.css"; import "./ExpandableCard.css";
@ -92,6 +94,25 @@ export function ExpandableCard({
); );
} }
export function ExpandableCardListSkeleton({
label,
count = 3,
metaOnOwnRow = false,
}: {
/** What is loading, as a screen reader says it: "the builds". */
label: string;
count?: number;
metaOnOwnRow?: boolean;
}) {
return (
<SkeletonRegion label={label}>
{Array.from({ length: count }, (_, i) => (
<ExpandableCardSkeleton key={i} metaOnOwnRow={metaOnOwnRow} />
))}
</SkeletonRegion>
);
}
export function ExpandableCardSkeleton({ export function ExpandableCardSkeleton({
metaOnOwnRow = false, metaOnOwnRow = false,
}: { }: {

View file

@ -3,9 +3,9 @@
// for what each form and card used to restyle under its own class name, so // for what each form and card used to restyle under its own class name, so
// "muted", "warning", "error" and "ok" mean the same colour everywhere. // "muted", "warning", "error" and "ok" mean the same colour everywhere.
// //
// `error` and `warn` are read out when they appear (`role="alert"` for an // `error` is read out when it appears (`role="alert"`); the other tones are
// error); the others are plain text unless the caller passes a `role`, such as // plain text unless the caller passes a `role`, such as `status` for a line
// `status` for a line that changes after a save. `class` is for a page's // that changes after a save. `class` is for a page's
// layout around the hint (a margin, a width), not for its colour. // layout around the hint (a margin, a width), not for its colour.
import type { ComponentChildren } from "preact"; import type { ComponentChildren } from "preact";
import "./Hint.css"; import "./Hint.css";