swarm-ui: the schedules, notification flows and flow editor show placeholders while they load
The schedules card, the notification router card (flows and recent deliveries) and the flow editor showed a bare 'loading…' line. They now show grey placeholders in the layout of what is coming: schedule cards with the real card's row heights, flow cards with a block for the graph, the deliveries table's header over blank rows, and lines for the editor.
This commit is contained in:
parent
0bc0b86d1d
commit
5eef1f4a3d
3 changed files with 111 additions and 7 deletions
|
|
@ -31,3 +31,19 @@
|
|||
.ui-schedule-card-actions {
|
||||
gap: 0.5em;
|
||||
}
|
||||
/* The loading twin's list: the gap between cards in the real list. */
|
||||
.ui-schedule-card-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
}
|
||||
/* The loading twin's rows take the height of a line of text, and its head the
|
||||
height of the title, so the card is the size of a real one. */
|
||||
.ui-schedule-card-head-skeleton {
|
||||
min-height: 1.76em;
|
||||
}
|
||||
.ui-schedule-card-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 1.6em;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { ActionRow } from "../../ui/action-row/ActionRow.js";
|
|||
import { Button } from "../../ui/button/Button.js";
|
||||
import { Card } from "../../ui/card/Card.js";
|
||||
import { RelativeTime } from "../../ui/relative-time/RelativeTime.js";
|
||||
import { Skeleton, SkeletonRegion } from "../../ui/skeleton/Skeleton.js";
|
||||
import {
|
||||
fmtInterval,
|
||||
fmtLocal,
|
||||
|
|
@ -87,3 +88,49 @@ export function ScheduleCard({
|
|||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// The twin of `ScheduleCard` for a list that is loading: the same card and
|
||||
// layout classes, with a placeholder in each slot. The cards sit in the
|
||||
// container the real list uses, so the gap between them is the same.
|
||||
function ScheduleCardSkeleton() {
|
||||
return (
|
||||
<div>
|
||||
<Card class="ui-schedule-card">
|
||||
<div class="ui-schedule-card-head ui-schedule-card-head-skeleton">
|
||||
<Skeleton width="10em" height="1.2em" class="ui-schedule-card-name" />
|
||||
<Skeleton width="4em" height="1.6em" />
|
||||
<Skeleton width="6em" height="1.6em" />
|
||||
</div>
|
||||
<div class="ui-schedule-card-line">
|
||||
<Skeleton width="14em" />
|
||||
</div>
|
||||
<div class="ui-schedule-card-line">
|
||||
<Skeleton width="90%" />
|
||||
</div>
|
||||
<div class="ui-schedule-card-line">
|
||||
<Skeleton width="60%" />
|
||||
</div>
|
||||
<div class="ui-schedule-card-line">
|
||||
<Skeleton width="12em" />
|
||||
</div>
|
||||
<ActionRow align="end" class="ui-schedule-card-actions">
|
||||
<Skeleton width="4em" height="2.75em" />
|
||||
<Skeleton width="4.5em" height="2.75em" />
|
||||
<Skeleton width="5em" height="2.75em" />
|
||||
</ActionRow>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ScheduleListSkeleton({ count = 3 }: { count?: number }) {
|
||||
return (
|
||||
<SkeletonRegion label="the schedules">
|
||||
<div class="ui-schedule-card-list">
|
||||
{Array.from({ length: count }, (_, i) => (
|
||||
<ScheduleCardSkeleton key={i} />
|
||||
))}
|
||||
</div>
|
||||
</SkeletonRegion>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import {
|
|||
import { fetchJson, usePolled } from "../../ui/polled/usePolled.js";
|
||||
import type { TableColumn } from "../../ui/table/Table.js";
|
||||
import type { AgentRow } from "../../agent/AgentTypes.js";
|
||||
import { ScheduleCard } from "./ScheduleCard.js";
|
||||
import { ScheduleCard, ScheduleListSkeleton } from "./ScheduleCard.js";
|
||||
import { agentNamesFrom, agentOptionsFor } from "./agent-options.js";
|
||||
import { ScheduleForm } from "./ScheduleForm.js";
|
||||
import { scheduleState, type Schedule } from "./ScheduleTypes.js";
|
||||
|
|
@ -184,7 +184,7 @@ function SchedulesBody() {
|
|||
onCancel={() => setCreating(false)}
|
||||
/>
|
||||
) : null}
|
||||
{!error && schedules === null ? <p>loading…</p> : null}
|
||||
{!error && schedules === null ? <ScheduleListSkeleton /> : null}
|
||||
{schedules ? (
|
||||
<FilterableView
|
||||
columns={COLUMNS}
|
||||
|
|
|
|||
Loading…
Reference in a new issue