| 1 | import { createMemo, createSignal, type JSX, Show } from "solid-js"; |
| 2 | import type { Series } from "../types/model.ts"; |
| 3 | import { lastGood, Loaded } from "./Loaded.tsx"; |
| 4 | import { type MetricQuery, useMetric } from "./Metric.tsx"; |
| 5 | import { type ChartProps, TimeChart } from "./TimeChart.tsx"; |
| 6 | |
| 7 | /** A chart's current value for its corner; `tip` is the next thing worth knowing about it. */ |
| 8 | export type Reading = { value: JSX.Element; tip?: string; stale?: boolean }; |
| 9 | |
| 10 | /** A chart stacked with others on one time axis and crosshair, which only the `last` one labels. */ |
| 11 | export function Strip(props: Omit<ChartProps, "series" | "colors" | "inline" | "sync" | "timeAxis"> & MetricQuery & { |
| 12 | title: string; |
| 13 | height: number; |
| 14 | /** Multiplies every value, e.g. to turn cores into percent of the machine. */ |
| 15 | scale?: number; |
| 16 | tabs?: JSX.Element; |
| 17 | now: (latest: Series[]) => Reading | undefined; |
| 18 | last?: boolean; |
| 19 | }) { |
| 20 | const { data, refetch, shape } = useMetric(props); |
| 21 | const latest = lastGood(data); |
| 22 | const [hovered, setHovered] = createSignal(false); |
| 23 | return ( |
| 24 | <section class="strip" aria-label={props.title}> |
| 25 | <div class="strip-head"> |
| 26 | <h3>{props.title}</h3> |
| 27 | {props.tabs} |
| 28 | <span class="rule" /> |
| 29 | <Show when={props.now(latest() ?? [])}> |
| 30 | {(now) => ( |
| 31 | <span class="now" classList={{ stale: now().stale || hovered() }} |
| 32 | tabIndex={now().tip ? 0 : undefined} data-tip={now().tip}> |
| 33 | {now().value} |
| 34 | </span> |
| 35 | )} |
| 36 | </Show> |
| 37 | </div> |
| 38 | <Loaded data={data} what={`${props.title} history`} retry={refetch} |
| 39 | skeleton={<div class="skeleton" style={{ height: `${props.height}px` }} />}> |
| 40 | {(loaded) => { |
| 41 | const shaped = createMemo(() => { |
| 42 | const scale = props.scale ?? 1; |
| 43 | const shaped = shape(loaded()); |
| 44 | // A lone line goes by the chart's name, so its hovered value reads "51% cpu". |
| 45 | const series = shaped.series.map((item) => ({ |
| 46 | ...item, name: shaped.series.length === 1 ? props.title : item.name, v: item.v.map((v) => v && v * scale), |
| 47 | })); |
| 48 | return { ...shaped, series }; |
| 49 | }); |
| 50 | return ( |
| 51 | <TimeChart series={shaped().series} colors={shaped().colors} format={props.format} stacked={props.stacked} |
| 52 | max={props.max} zero={props.zero} height={props.height} inline sync="strips" timeAxis={!!props.last} |
| 53 | onHover={(index) => setHovered(index !== null)} /> |
| 54 | ); |
| 55 | }} |
| 56 | </Loaded> |
| 57 | </section> |
| 58 | ); |
| 59 | } |