@tangle-network/charts 0.4.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -24
- package/dist/events-Cw6abvgR.js +491 -0
- package/dist/figure.css +45 -9
- package/dist/index.d.ts +104 -135
- package/dist/index.js +735 -371
- package/dist/react/index.d.ts +56 -15
- package/dist/react/index.js +525 -154
- package/dist/react/live.css +29 -0
- package/dist/timeline-BAjzF-wL.d.ts +183 -0
- package/package.json +25 -13
- package/LICENSE +0 -21
package/dist/react/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { d as hatchPath, i as eventsGap, m as markerPath, o as isRefusal } from "../events-Cw6abvgR.js";
|
|
1
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { Fragment as Fragment$1, useEffect, useId, useRef, useState } from "react";
|
|
3
4
|
//#region src/react/sparkline.tsx
|
|
@@ -246,7 +247,7 @@ function StackedBarChart(props) {
|
|
|
246
247
|
validate(props);
|
|
247
248
|
const bySeries = new Map(series.map((entry) => [entry.id, entry]));
|
|
248
249
|
const formatted = (value) => isValue(value) ? formatValue(value) : unavailableLabel;
|
|
249
|
-
const summary = (bucket) => `${bucket.label}: total ${formatted(bucket.total)}` + (hasGap(bucket) ? ` — ${unavailableLabel}` : "") + (bucket.segments.length ? ` — ${bucket.segments.map((segment) => `${bySeries.get(segment.seriesId).label} ${formatted(segment.value)}`).join(", ")}` : "");
|
|
250
|
+
const summary = (bucket) => `${bucket.label}: total ${formatted(bucket.total)}` + (hasGap(bucket) ? ` — ${unavailableLabel}` : "") + (bucket.segments.length ? ` — ${bucket.segments.map((segment) => `${bySeries.get(segment.seriesId).label} ${formatted(segment.value)}`).join(", ")}` : "") + (bucket.description ? ` — ${bucket.description}` : "");
|
|
250
251
|
const activeIndex = selectedBucketId === null ? -1 : buckets.findIndex(({ id }) => id === selectedBucketId);
|
|
251
252
|
const activeBucket = activeIndex >= 0 ? buckets[activeIndex] : null, marginTop = 12, marginRight = 16, marginLeft = 56;
|
|
252
253
|
const chartHeight = 220;
|
|
@@ -489,7 +490,8 @@ function StackedBarChart(props) {
|
|
|
489
490
|
bySeries.get(segment.seriesId).label,
|
|
490
491
|
": ",
|
|
491
492
|
formatted(segment.value)
|
|
492
|
-
] }, segment.seriesId))
|
|
493
|
+
] }, segment.seriesId)),
|
|
494
|
+
activeBucket.description && /* @__PURE__ */ jsx("div", { children: activeBucket.description })
|
|
493
495
|
]
|
|
494
496
|
}),
|
|
495
497
|
/* @__PURE__ */ jsx("div", {
|
|
@@ -1069,17 +1071,24 @@ function Histogram({ bins, format = formatCompact, color = LIVE_SERIES[0], unit,
|
|
|
1069
1071
|
}
|
|
1070
1072
|
/** Lines over time on one axis (at most four series); the crosshair readout lists each series at the nearest time. A
|
|
1071
1073
|
* `dots` series draws its points as marks (checkpoints) instead of a line; `axis` labels the time axis (default MM-DD). */
|
|
1072
|
-
function Lines({ series, format = formatCompact, rows = 13, axis }) {
|
|
1074
|
+
function Lines({ series, format = formatCompact, rows = 13, axis, marks = [], bands = [] }) {
|
|
1073
1075
|
const [ref, width, em] = useWidth();
|
|
1074
1076
|
const height = Math.round(rows * em);
|
|
1075
1077
|
const [hover, setHover] = useState(null);
|
|
1078
|
+
const gap = eventsGap(marks, bands);
|
|
1079
|
+
if (gap) throw new RangeError(gap);
|
|
1076
1080
|
const all = series.flatMap((s) => s.points);
|
|
1077
1081
|
if (!all.length) return /* @__PURE__ */ jsx("p", {
|
|
1078
1082
|
className: "ov-empty",
|
|
1079
1083
|
children: "No history yet."
|
|
1080
1084
|
});
|
|
1081
|
-
const
|
|
1082
|
-
|
|
1085
|
+
const times = [
|
|
1086
|
+
...all.map((p) => p[0]),
|
|
1087
|
+
...marks.map((m) => m.at),
|
|
1088
|
+
...bands.flatMap((b) => [b.from, b.to])
|
|
1089
|
+
];
|
|
1090
|
+
const t0 = Math.min(...times);
|
|
1091
|
+
const t1 = Math.max(...times);
|
|
1083
1092
|
const { top, ticks } = ticksFor(Math.max(...all.map((p) => p[1])));
|
|
1084
1093
|
const left = Math.round(3.4 * em);
|
|
1085
1094
|
const bottom = Math.round(1.8 * em);
|
|
@@ -1098,87 +1107,234 @@ function Lines({ series, format = formatCompact, rows = 13, axis }) {
|
|
|
1098
1107
|
setHover(px >= left ? t0 + (px - left) / Math.max(1, plotW) * (t1 - t0) : null);
|
|
1099
1108
|
},
|
|
1100
1109
|
onMouseLeave: () => setHover(null),
|
|
1101
|
-
children: [
|
|
1102
|
-
width,
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
stroke: s.color,
|
|
1142
|
-
strokeWidth: Math.max(2, em / 8),
|
|
1143
|
-
strokeLinejoin: "round",
|
|
1144
|
-
points: s.points.map((p) => `${x(p[0])},${y(p[1])}`).join(" ")
|
|
1145
|
-
}, s.name)),
|
|
1146
|
-
hover !== null && /* @__PURE__ */ jsx("line", {
|
|
1147
|
-
x1: x(hover),
|
|
1148
|
-
x2: x(hover),
|
|
1149
|
-
y1: 8,
|
|
1150
|
-
y2: 8 + plotH,
|
|
1151
|
-
className: "ov-crosshair"
|
|
1152
|
-
}),
|
|
1153
|
-
hover !== null && series.map((s) => {
|
|
1154
|
-
const p = s.points.length ? nearest(s.points, hover) : null;
|
|
1155
|
-
return p ? /* @__PURE__ */ jsx("circle", {
|
|
1110
|
+
children: [
|
|
1111
|
+
width > 0 && /* @__PURE__ */ jsxs("svg", {
|
|
1112
|
+
width,
|
|
1113
|
+
height,
|
|
1114
|
+
role: "img",
|
|
1115
|
+
"aria-label": series.map((s) => s.name).join(", "),
|
|
1116
|
+
children: [
|
|
1117
|
+
ticks.map((t) => /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("line", {
|
|
1118
|
+
x1: left,
|
|
1119
|
+
x2: width - 10,
|
|
1120
|
+
y1: y(t),
|
|
1121
|
+
y2: y(t),
|
|
1122
|
+
className: "ov-gridline"
|
|
1123
|
+
}), /* @__PURE__ */ jsx("text", {
|
|
1124
|
+
x: left - em * .4,
|
|
1125
|
+
y: y(t),
|
|
1126
|
+
dominantBaseline: "middle",
|
|
1127
|
+
className: "ov-axis",
|
|
1128
|
+
textAnchor: "end",
|
|
1129
|
+
children: format(t)
|
|
1130
|
+
})] }, t)),
|
|
1131
|
+
[
|
|
1132
|
+
t0,
|
|
1133
|
+
(t0 + t1) / 2,
|
|
1134
|
+
t1
|
|
1135
|
+
].map((t, i) => /* @__PURE__ */ jsx("text", {
|
|
1136
|
+
x: x(t),
|
|
1137
|
+
y: height - em * .35,
|
|
1138
|
+
className: "ov-axis",
|
|
1139
|
+
textAnchor: i === 0 ? "start" : i === 2 ? "end" : "middle",
|
|
1140
|
+
children: axis ? axis(t) : day(t).slice(0, 5)
|
|
1141
|
+
}, i)),
|
|
1142
|
+
bands.map((band, i) => /* @__PURE__ */ jsx(Band, {
|
|
1143
|
+
band,
|
|
1144
|
+
x: x(band.from),
|
|
1145
|
+
y: 8,
|
|
1146
|
+
width: x(band.to) - x(band.from),
|
|
1147
|
+
height: plotH
|
|
1148
|
+
}, `band-${i}`)),
|
|
1149
|
+
series.map((s) => s.dots ? /* @__PURE__ */ jsx("g", { children: s.points.map((p) => /* @__PURE__ */ jsx("circle", {
|
|
1156
1150
|
cx: x(p[0]),
|
|
1157
1151
|
cy: y(p[1]),
|
|
1158
|
-
r: Math.max(
|
|
1152
|
+
r: Math.max(3, em / 5),
|
|
1159
1153
|
fill: s.color,
|
|
1160
1154
|
stroke: "var(--chart-live-surface, #fff)",
|
|
1161
|
-
strokeWidth:
|
|
1162
|
-
}, s.name) :
|
|
1155
|
+
strokeWidth: 1.5
|
|
1156
|
+
}, p[0])) }, s.name) : /* @__PURE__ */ jsxs("g", {
|
|
1157
|
+
"data-series": s.name,
|
|
1158
|
+
"data-dash": s.dash ?? "solid",
|
|
1159
|
+
"data-marker": s.marker,
|
|
1160
|
+
children: [/* @__PURE__ */ jsx("polyline", {
|
|
1161
|
+
fill: "none",
|
|
1162
|
+
stroke: s.color,
|
|
1163
|
+
strokeWidth: Math.max(2, em / 8),
|
|
1164
|
+
strokeLinejoin: "round",
|
|
1165
|
+
strokeLinecap: s.dash === "dot" ? "round" : void 0,
|
|
1166
|
+
strokeDasharray: dashArray(s.dash, em),
|
|
1167
|
+
points: s.points.map((p) => `${x(p[0])},${y(p[1])}`).join(" ")
|
|
1168
|
+
}), s.marker && (s.points.length <= 30 ? s.points : s.points.slice(-1)).map((p) => /* @__PURE__ */ jsx("path", {
|
|
1169
|
+
d: markerPath(s.marker, x(p[0]), y(p[1]), Math.max(3, em / 5)),
|
|
1170
|
+
fill: s.color,
|
|
1171
|
+
stroke: "var(--chart-live-surface, var(--chart-canvas))",
|
|
1172
|
+
strokeWidth: 1.5
|
|
1173
|
+
}, p[0]))]
|
|
1174
|
+
}, s.name)),
|
|
1175
|
+
[...marks].sort((a, b) => a.at - b.at).map((mark, i) => /* @__PURE__ */ jsx(Mark, {
|
|
1176
|
+
mark,
|
|
1177
|
+
x: x(mark.at),
|
|
1178
|
+
top: 8,
|
|
1179
|
+
bottom: 8 + plotH,
|
|
1180
|
+
row: i % 3,
|
|
1181
|
+
em,
|
|
1182
|
+
flip: x(mark.at) > left + plotW * .62
|
|
1183
|
+
}, `mark-${i}`)),
|
|
1184
|
+
hover !== null && /* @__PURE__ */ jsx("line", {
|
|
1185
|
+
x1: x(hover),
|
|
1186
|
+
x2: x(hover),
|
|
1187
|
+
y1: 8,
|
|
1188
|
+
y2: 8 + plotH,
|
|
1189
|
+
className: "ov-crosshair"
|
|
1190
|
+
}),
|
|
1191
|
+
hover !== null && series.map((s) => {
|
|
1192
|
+
const p = s.points.length ? nearest(s.points, hover) : null;
|
|
1193
|
+
return p ? /* @__PURE__ */ jsx("circle", {
|
|
1194
|
+
cx: x(p[0]),
|
|
1195
|
+
cy: y(p[1]),
|
|
1196
|
+
r: Math.max(4, em / 4),
|
|
1197
|
+
fill: s.color,
|
|
1198
|
+
stroke: "var(--chart-live-surface, #fff)",
|
|
1199
|
+
strokeWidth: 2
|
|
1200
|
+
}, s.name) : null;
|
|
1201
|
+
})
|
|
1202
|
+
]
|
|
1203
|
+
}),
|
|
1204
|
+
hover !== null && /* @__PURE__ */ jsx(Tooltip, { tip: {
|
|
1205
|
+
x: Math.min(x(hover) + 10, width - em * 14),
|
|
1206
|
+
y: 8,
|
|
1207
|
+
title: day(hover),
|
|
1208
|
+
lines: series.filter((s) => s.points.length).map((s) => {
|
|
1209
|
+
const p = nearest(s.points, hover);
|
|
1210
|
+
return {
|
|
1211
|
+
label: s.label ? s.label(p[0]) : s.name,
|
|
1212
|
+
value: format(p[1]),
|
|
1213
|
+
color: s.color
|
|
1214
|
+
};
|
|
1163
1215
|
})
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1216
|
+
} }),
|
|
1217
|
+
series.length > 1 && series.some((s) => s.dash || s.marker) && /* @__PURE__ */ jsx("ul", {
|
|
1218
|
+
className: "ov-key",
|
|
1219
|
+
"aria-label": "Series",
|
|
1220
|
+
children: series.map((s) => /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsxs("svg", {
|
|
1221
|
+
width: em * 2,
|
|
1222
|
+
height: em,
|
|
1223
|
+
"aria-hidden": "true",
|
|
1224
|
+
children: [/* @__PURE__ */ jsx("line", {
|
|
1225
|
+
x1: 0,
|
|
1226
|
+
x2: em * 2,
|
|
1227
|
+
y1: em / 2,
|
|
1228
|
+
y2: em / 2,
|
|
1229
|
+
stroke: s.color,
|
|
1230
|
+
strokeWidth: Math.max(2, em / 8),
|
|
1231
|
+
strokeDasharray: dashArray(s.dash, em),
|
|
1232
|
+
strokeLinecap: s.dash === "dot" ? "round" : void 0
|
|
1233
|
+
}), s.marker && /* @__PURE__ */ jsx("path", {
|
|
1234
|
+
d: markerPath(s.marker, em, em / 2, Math.max(3, em / 5)),
|
|
1235
|
+
fill: s.color
|
|
1236
|
+
})]
|
|
1237
|
+
}), s.name] }, s.name))
|
|
1238
|
+
}),
|
|
1239
|
+
/* @__PURE__ */ jsx(EventList, {
|
|
1240
|
+
marks,
|
|
1241
|
+
bands,
|
|
1242
|
+
format: axis ?? ((t) => day(t))
|
|
1176
1243
|
})
|
|
1177
|
-
|
|
1244
|
+
]
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
function dashArray(dash, em) {
|
|
1248
|
+
return dash === "dash" ? `${Math.round(em * .55)} ${Math.round(em * .3)}` : dash === "dot" ? `0.1 ${Math.round(em * .35)}` : void 0;
|
|
1249
|
+
}
|
|
1250
|
+
/** A shaded span under the data; a `missing` span is hatched because it has no data. */
|
|
1251
|
+
function Band({ band, x, y, width, height }) {
|
|
1252
|
+
return /* @__PURE__ */ jsxs("g", {
|
|
1253
|
+
"data-band": band.kind,
|
|
1254
|
+
"data-hatched": band.kind === "missing" ? "true" : void 0,
|
|
1255
|
+
children: [
|
|
1256
|
+
/* @__PURE__ */ jsx("title", { children: band.label }),
|
|
1257
|
+
/* @__PURE__ */ jsx("rect", {
|
|
1258
|
+
x,
|
|
1259
|
+
y,
|
|
1260
|
+
width: Math.max(0, width),
|
|
1261
|
+
height,
|
|
1262
|
+
className: band.kind === "missing" ? "ov-hatch-bg" : `ov-band ov-band-${band.kind}`
|
|
1263
|
+
}),
|
|
1264
|
+
band.kind === "missing" && /* @__PURE__ */ jsx("path", {
|
|
1265
|
+
d: hatchPath(x, y, width, height),
|
|
1266
|
+
className: "ov-hatch"
|
|
1267
|
+
})
|
|
1268
|
+
]
|
|
1178
1269
|
});
|
|
1179
1270
|
}
|
|
1180
|
-
/** A
|
|
1181
|
-
function
|
|
1271
|
+
/** A vertical event rule with its label; kinds differ by stroke pattern and the died cross as well as colour. */
|
|
1272
|
+
function Mark({ mark, x, top, bottom, row, em, flip }) {
|
|
1273
|
+
const s = Math.max(3, em / 4);
|
|
1274
|
+
return /* @__PURE__ */ jsxs("g", {
|
|
1275
|
+
"data-mark": mark.kind,
|
|
1276
|
+
children: [
|
|
1277
|
+
/* @__PURE__ */ jsx("title", { children: mark.label }),
|
|
1278
|
+
/* @__PURE__ */ jsx("line", {
|
|
1279
|
+
x1: x,
|
|
1280
|
+
x2: x,
|
|
1281
|
+
y1: top,
|
|
1282
|
+
y2: bottom,
|
|
1283
|
+
className: `ov-mark ov-mark-${mark.kind}`
|
|
1284
|
+
}),
|
|
1285
|
+
mark.kind === "died" && /* @__PURE__ */ jsx("path", {
|
|
1286
|
+
d: `M${x - s} ${bottom - 2 * s}L${x + s} ${bottom}M${x - s} ${bottom}L${x + s} ${bottom - 2 * s}`,
|
|
1287
|
+
className: "ov-mark ov-mark-died"
|
|
1288
|
+
}),
|
|
1289
|
+
/* @__PURE__ */ jsx("text", {
|
|
1290
|
+
x: flip ? x - em * .3 : x + em * .3,
|
|
1291
|
+
y: top + em * (.9 + row),
|
|
1292
|
+
textAnchor: flip ? "end" : "start",
|
|
1293
|
+
className: "ov-mark-label",
|
|
1294
|
+
children: mark.label
|
|
1295
|
+
})
|
|
1296
|
+
]
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
/** Every mark and band as text, in time order: the table form of the events drawn on the plot. */
|
|
1300
|
+
function EventList({ marks, bands, format }) {
|
|
1301
|
+
if (!marks.length && !bands.length) return null;
|
|
1302
|
+
const items = [...marks.map((m) => ({
|
|
1303
|
+
at: m.at,
|
|
1304
|
+
kind: m.kind,
|
|
1305
|
+
text: `${m.label} · ${format(m.at)}`
|
|
1306
|
+
})), ...bands.map((b) => ({
|
|
1307
|
+
at: b.from,
|
|
1308
|
+
kind: b.kind,
|
|
1309
|
+
text: `${b.label} · ${format(b.from)} to ${format(b.to)}${b.kind === "missing" ? " · no data" : ""}`
|
|
1310
|
+
}))].sort((a, b) => a.at - b.at);
|
|
1311
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
1312
|
+
className: "ov-events",
|
|
1313
|
+
"aria-label": "Recorded events",
|
|
1314
|
+
children: items.map((item, i) => /* @__PURE__ */ jsxs("li", {
|
|
1315
|
+
"data-kind": item.kind,
|
|
1316
|
+
children: [/* @__PURE__ */ jsx("i", {
|
|
1317
|
+
className: `ov-event-swatch ov-event-${item.kind}`,
|
|
1318
|
+
"aria-hidden": "true"
|
|
1319
|
+
}), item.text]
|
|
1320
|
+
}, i))
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
const SEGMENT_TONES = [
|
|
1324
|
+
"var(--chart-accent)",
|
|
1325
|
+
"var(--chart-tier-c)",
|
|
1326
|
+
"var(--chart-muted)"
|
|
1327
|
+
];
|
|
1328
|
+
/** A used-of-total meter with its numbers. With `segments` it is a part-of-whole bar: the remainder of `total` that no
|
|
1329
|
+
* segment accounts for is drawn hatched and labelled `untraced`, and a missing total hatches the whole track. */
|
|
1330
|
+
function Meter({ used, total, format = formatCompact, tone, text, segments, untraced = "untraced" }) {
|
|
1331
|
+
if (segments) return /* @__PURE__ */ jsx(SegmentMeter, {
|
|
1332
|
+
segments,
|
|
1333
|
+
total,
|
|
1334
|
+
format,
|
|
1335
|
+
text,
|
|
1336
|
+
untraced
|
|
1337
|
+
});
|
|
1182
1338
|
const share = used !== null && used !== void 0 && total ? Math.min(1, used / total) : null;
|
|
1183
1339
|
return /* @__PURE__ */ jsxs("div", {
|
|
1184
1340
|
className: "ov-meter",
|
|
@@ -1194,6 +1350,64 @@ function Meter({ used, total, format = formatCompact, tone, text }) {
|
|
|
1194
1350
|
})]
|
|
1195
1351
|
});
|
|
1196
1352
|
}
|
|
1353
|
+
function SegmentMeter({ segments, total, format, text, untraced }) {
|
|
1354
|
+
for (const segment of segments) if (segment.value !== null && (!Number.isFinite(segment.value) || segment.value < 0)) throw new RangeError(`${segment.label}: a segment value must be a nonnegative finite number or null.`);
|
|
1355
|
+
const known = segments.reduce((sum, segment) => sum + (segment.value ?? 0), 0);
|
|
1356
|
+
const whole = total !== null && total !== void 0 && Number.isFinite(total) && total > 0 ? total : null;
|
|
1357
|
+
if (whole !== null && known > whole * (1 + 1e-9)) throw new RangeError(`Segments sum to ${known}, more than the total ${whole}.`);
|
|
1358
|
+
const remainder = whole === null ? 0 : whole - known;
|
|
1359
|
+
const parts = [...segments.map((segment, i) => ({
|
|
1360
|
+
...segment,
|
|
1361
|
+
color: segment.color ?? SEGMENT_TONES[i % SEGMENT_TONES.length],
|
|
1362
|
+
remainder: false
|
|
1363
|
+
})), ...remainder > whole * 1e-9 ? [{
|
|
1364
|
+
label: untraced,
|
|
1365
|
+
value: remainder,
|
|
1366
|
+
kind: "untraced",
|
|
1367
|
+
color: void 0,
|
|
1368
|
+
remainder: true
|
|
1369
|
+
}] : []];
|
|
1370
|
+
const share = (value) => whole === null ? null : value / whole;
|
|
1371
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1372
|
+
className: "ov-meter",
|
|
1373
|
+
"data-segmented": "true",
|
|
1374
|
+
children: [
|
|
1375
|
+
/* @__PURE__ */ jsx("span", {
|
|
1376
|
+
className: `ov-meter-track${whole === null ? " ov-hatched" : ""}`,
|
|
1377
|
+
"data-hatched": whole === null ? "true" : void 0,
|
|
1378
|
+
"aria-hidden": "true",
|
|
1379
|
+
children: whole !== null && parts.filter((part) => part.value !== null && part.value > 0).map((part) => /* @__PURE__ */ jsx("span", {
|
|
1380
|
+
"data-segment": part.label,
|
|
1381
|
+
"data-hatched": part.kind === "untraced" ? "true" : void 0,
|
|
1382
|
+
className: part.kind === "untraced" ? "ov-hatched" : void 0,
|
|
1383
|
+
style: {
|
|
1384
|
+
width: `${part.value / whole * 100}%`,
|
|
1385
|
+
background: part.kind === "untraced" ? void 0 : part.color
|
|
1386
|
+
}
|
|
1387
|
+
}, part.label))
|
|
1388
|
+
}),
|
|
1389
|
+
/* @__PURE__ */ jsx("ul", {
|
|
1390
|
+
className: "ov-meter-key",
|
|
1391
|
+
children: parts.map((part) => /* @__PURE__ */ jsxs("li", {
|
|
1392
|
+
"data-segment": part.label,
|
|
1393
|
+
children: [
|
|
1394
|
+
/* @__PURE__ */ jsx("i", {
|
|
1395
|
+
className: part.kind === "untraced" || part.value === null ? "ov-hatched" : void 0,
|
|
1396
|
+
style: part.kind === "untraced" || part.value === null ? void 0 : { background: part.color },
|
|
1397
|
+
"aria-hidden": "true"
|
|
1398
|
+
}),
|
|
1399
|
+
/* @__PURE__ */ jsx("span", { children: part.label }),
|
|
1400
|
+
/* @__PURE__ */ jsx("b", { children: part.value === null ? "not recorded" : `${format(part.value)}${whole === null ? "" : ` · ${formatShare(share(part.value))}`}` })
|
|
1401
|
+
]
|
|
1402
|
+
}, part.label))
|
|
1403
|
+
}),
|
|
1404
|
+
/* @__PURE__ */ jsx("span", {
|
|
1405
|
+
className: "ov-meter-text",
|
|
1406
|
+
children: text ?? (whole === null ? "total not recorded" : `${format(known)} of ${format(whole)} accounted · ${formatShare(known / whole)}`)
|
|
1407
|
+
})
|
|
1408
|
+
]
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1197
1411
|
/** A small line of one series for a row in a list (one hue; the row's label carries identity). */
|
|
1198
1412
|
function Spark({ points, max = 1, color = LIVE_SERIES[0] }) {
|
|
1199
1413
|
if (points.length < 2) return /* @__PURE__ */ jsx("span", {
|
|
@@ -1219,97 +1433,254 @@ function Spark({ points, max = 1, color = LIVE_SERIES[0] }) {
|
|
|
1219
1433
|
});
|
|
1220
1434
|
}
|
|
1221
1435
|
/** One bar per row on a shared time axis (who worked when): each row's bar spans its start to its end, coloured by its
|
|
1222
|
-
* state; hovering a
|
|
1223
|
-
|
|
1436
|
+
* state; hovering a span names it, its state and its span. Times are epoch ms; `axis` labels the time axis.
|
|
1437
|
+
* `lanes` give a row several spans (the same shape as the static `timeline`); a span with `end: null` is open and runs
|
|
1438
|
+
* to `now`. `marks` draw recorded events across every lane, and states in `hatch` are drawn hatched (no data). */
|
|
1439
|
+
function Gantt({ rows, lanes: laneInput, colors, axis, span, marks = [], now, hatch = ["missing", "untraced"] }) {
|
|
1224
1440
|
const [ref, width, em] = useWidth();
|
|
1225
1441
|
const [tip, setTip] = useState(null);
|
|
1226
|
-
|
|
1442
|
+
const lanes = laneInput ?? (rows ?? []).map((row, i) => ({
|
|
1443
|
+
id: `${row.label}-${i}`,
|
|
1444
|
+
label: row.label,
|
|
1445
|
+
spans: [{
|
|
1446
|
+
start: row.start,
|
|
1447
|
+
end: row.end,
|
|
1448
|
+
state: row.state,
|
|
1449
|
+
note: row.note
|
|
1450
|
+
}]
|
|
1451
|
+
}));
|
|
1452
|
+
const gap = eventsGap(marks);
|
|
1453
|
+
if (gap) throw new RangeError(gap);
|
|
1454
|
+
if (now !== void 0 && !Number.isFinite(now)) throw new RangeError("now must be a finite time.");
|
|
1455
|
+
if (laneInput) for (const lane of lanes) for (const s of lane.spans) {
|
|
1456
|
+
if (!Number.isFinite(s.start) || s.end !== null && (!Number.isFinite(s.end) || s.end < s.start)) throw new RangeError(`${lane.label}: span times must be finite and end at or after their start.`);
|
|
1457
|
+
if (s.end === null && (now === void 0 || now < s.start)) throw new RangeError(`${lane.label}: an open span needs a now at or after its start.`);
|
|
1458
|
+
}
|
|
1459
|
+
const spans = lanes.flatMap((lane) => lane.spans);
|
|
1460
|
+
if (!spans.length) return /* @__PURE__ */ jsx("p", {
|
|
1227
1461
|
className: "ov-empty",
|
|
1228
1462
|
children: "No agent has started."
|
|
1229
1463
|
});
|
|
1464
|
+
const ends = (s) => s.end ?? now;
|
|
1230
1465
|
const rowH = Math.round(1.25 * em);
|
|
1231
1466
|
const labelW = Math.min(Math.round(13 * em), Math.round(width * .32));
|
|
1232
|
-
const top = 4;
|
|
1467
|
+
const top = marks.length ? 4 + Math.round(Math.min(3, marks.length) * em + .3 * em) : 4;
|
|
1233
1468
|
const bottom = Math.round(1.8 * em);
|
|
1234
|
-
const height = top +
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1469
|
+
const height = top + lanes.length * rowH + bottom;
|
|
1470
|
+
const times = [
|
|
1471
|
+
...spans.flatMap((s) => [s.start, ends(s)]),
|
|
1472
|
+
...marks.map((m) => m.at),
|
|
1473
|
+
...now === void 0 ? [] : [now]
|
|
1474
|
+
];
|
|
1475
|
+
const t0 = Math.min(...times);
|
|
1476
|
+
const t1 = Math.max(...times, t0 + 6e4);
|
|
1237
1477
|
const plotW = Math.max(0, width - labelW - 10);
|
|
1238
1478
|
const x = (t) => labelW + (t - t0) / (t1 - t0) * plotW;
|
|
1239
|
-
const fallback = colors.unknown ?? "
|
|
1479
|
+
const fallback = colors.unknown ?? "var(--chart-live-faint, var(--chart-muted))";
|
|
1480
|
+
const hatched = new Set(hatch);
|
|
1481
|
+
const plotBottom = top + lanes.length * rowH;
|
|
1240
1482
|
return /* @__PURE__ */ jsxs("div", {
|
|
1241
1483
|
ref,
|
|
1242
1484
|
className: "ov-plot",
|
|
1243
1485
|
onMouseLeave: () => setTip(null),
|
|
1244
|
-
children: [
|
|
1245
|
-
width,
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1486
|
+
children: [
|
|
1487
|
+
width > 0 && /* @__PURE__ */ jsxs("svg", {
|
|
1488
|
+
width,
|
|
1489
|
+
height,
|
|
1490
|
+
role: "img",
|
|
1491
|
+
"aria-label": `${lanes.length} ${laneInput ? "lanes" : "agents"} over time`,
|
|
1492
|
+
children: [
|
|
1493
|
+
[
|
|
1494
|
+
0,
|
|
1495
|
+
.25,
|
|
1496
|
+
.5,
|
|
1497
|
+
.75,
|
|
1498
|
+
1
|
|
1499
|
+
].map((f) => /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("line", {
|
|
1500
|
+
x1: labelW + f * plotW,
|
|
1501
|
+
x2: labelW + f * plotW,
|
|
1502
|
+
y1: top,
|
|
1503
|
+
y2: plotBottom,
|
|
1504
|
+
className: "ov-gridline"
|
|
1505
|
+
}), /* @__PURE__ */ jsx("text", {
|
|
1506
|
+
x: labelW + f * plotW,
|
|
1507
|
+
y: height - em * .35,
|
|
1508
|
+
className: "ov-axis",
|
|
1509
|
+
textAnchor: f === 0 ? "start" : f === 1 ? "end" : "middle",
|
|
1510
|
+
children: axis(t0 + f * (t1 - t0))
|
|
1511
|
+
})] }, f)),
|
|
1512
|
+
lanes.map((lane, i) => {
|
|
1513
|
+
const y = top + i * rowH;
|
|
1514
|
+
return /* @__PURE__ */ jsxs("g", {
|
|
1515
|
+
"data-lane": lane.id,
|
|
1516
|
+
children: [
|
|
1517
|
+
/* @__PURE__ */ jsx("rect", {
|
|
1518
|
+
x: 0,
|
|
1519
|
+
y,
|
|
1520
|
+
width,
|
|
1521
|
+
height: rowH,
|
|
1522
|
+
fill: "transparent",
|
|
1523
|
+
onMouseEnter: () => setTip({
|
|
1524
|
+
x: Math.min(x(ends(lane.spans.at(-1) ?? { end: t1 })) + 8, width - em * 16),
|
|
1525
|
+
y: Math.max(0, y - em),
|
|
1526
|
+
title: lane.label,
|
|
1527
|
+
lines: lane.spans.flatMap((s) => [{
|
|
1528
|
+
label: s.end === null ? `${s.state} (open)` : s.state,
|
|
1529
|
+
value: span(s.start, ends(s)),
|
|
1530
|
+
color: hatched.has(s.state) ? void 0 : colors[s.state] ?? fallback
|
|
1531
|
+
}, ...s.note ? [{
|
|
1532
|
+
label: s.note,
|
|
1533
|
+
value: ""
|
|
1534
|
+
}] : []])
|
|
1535
|
+
})
|
|
1536
|
+
}),
|
|
1537
|
+
/* @__PURE__ */ jsx("text", {
|
|
1538
|
+
x: labelW - em * .5,
|
|
1539
|
+
y: y + rowH / 2,
|
|
1540
|
+
dominantBaseline: "middle",
|
|
1541
|
+
textAnchor: "end",
|
|
1542
|
+
className: "ov-axis ov-gantt-label",
|
|
1543
|
+
children: lane.label.length > 26 ? `${lane.label.slice(0, 25)}…` : lane.label
|
|
1544
|
+
}),
|
|
1545
|
+
lane.spans.map((s, j) => {
|
|
1546
|
+
const color = colors[s.state] ?? fallback;
|
|
1547
|
+
const bx = x(s.start), bw = Math.max(2, x(ends(s)) - bx), by = y + rowH * .18, bh = rowH * .64;
|
|
1548
|
+
const open = s.end === null;
|
|
1549
|
+
return /* @__PURE__ */ jsx("g", {
|
|
1550
|
+
"data-state": s.state,
|
|
1551
|
+
"data-open": open ? "true" : void 0,
|
|
1552
|
+
"data-hatched": hatched.has(s.state) ? "true" : void 0,
|
|
1553
|
+
onMouseEnter: () => setTip({
|
|
1554
|
+
x: Math.min(bx + bw + 8, width - em * 16),
|
|
1555
|
+
y: Math.max(0, y - em),
|
|
1556
|
+
title: lane.label,
|
|
1557
|
+
lines: [{
|
|
1558
|
+
label: open ? `${s.state} (open)` : s.state,
|
|
1559
|
+
value: span(s.start, ends(s)),
|
|
1560
|
+
color: hatched.has(s.state) ? void 0 : color
|
|
1561
|
+
}, ...s.note ? [{
|
|
1562
|
+
label: s.note,
|
|
1563
|
+
value: ""
|
|
1564
|
+
}] : []]
|
|
1565
|
+
}),
|
|
1566
|
+
children: hatched.has(s.state) ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("rect", {
|
|
1567
|
+
x: bx,
|
|
1568
|
+
y: by,
|
|
1569
|
+
width: bw,
|
|
1570
|
+
height: bh,
|
|
1571
|
+
className: "ov-hatch-bg"
|
|
1572
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
1573
|
+
d: hatchPath(bx, by, bw, bh, 4),
|
|
1574
|
+
className: "ov-hatch"
|
|
1575
|
+
})] }) : /* @__PURE__ */ jsx("rect", {
|
|
1576
|
+
x: bx,
|
|
1577
|
+
y: by,
|
|
1578
|
+
width: bw,
|
|
1579
|
+
height: bh,
|
|
1580
|
+
rx: 2,
|
|
1581
|
+
fill: color,
|
|
1582
|
+
className: open ? "ov-gantt-open" : void 0
|
|
1583
|
+
})
|
|
1584
|
+
}, j);
|
|
1585
|
+
})
|
|
1586
|
+
]
|
|
1587
|
+
}, lane.id);
|
|
1283
1588
|
}),
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1589
|
+
now !== void 0 && /* @__PURE__ */ jsx("line", {
|
|
1590
|
+
x1: x(now),
|
|
1591
|
+
x2: x(now),
|
|
1592
|
+
y1: top,
|
|
1593
|
+
y2: plotBottom,
|
|
1594
|
+
className: "ov-now",
|
|
1595
|
+
"data-now": "true"
|
|
1596
|
+
}),
|
|
1597
|
+
[...marks].sort((a, b) => a.at - b.at).map((mark, i) => /* @__PURE__ */ jsx(Mark, {
|
|
1598
|
+
mark,
|
|
1599
|
+
x: x(mark.at),
|
|
1600
|
+
top: 4,
|
|
1601
|
+
bottom: plotBottom,
|
|
1602
|
+
row: i % 3,
|
|
1603
|
+
em,
|
|
1604
|
+
flip: x(mark.at) > labelW + plotW * .62
|
|
1605
|
+
}, `mark-${i}`))
|
|
1606
|
+
]
|
|
1607
|
+
}),
|
|
1608
|
+
/* @__PURE__ */ jsx(Tooltip, { tip }),
|
|
1609
|
+
/* @__PURE__ */ jsx(EventList, {
|
|
1610
|
+
marks,
|
|
1611
|
+
bands: [],
|
|
1612
|
+
format: axis
|
|
1613
|
+
})
|
|
1614
|
+
]
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
//#endregion
|
|
1618
|
+
//#region src/react/figure-view.tsx
|
|
1619
|
+
/** Embeds a static Figure or Refusal in React: the wide or phone render chosen by the container's width, the data
|
|
1620
|
+
* table in a disclosure. The SVG and table strings come from this library's figure functions, which escape input. */
|
|
1621
|
+
function FigureView({ figure, presentation = "chart" }) {
|
|
1622
|
+
const [ref, width] = useWidth();
|
|
1623
|
+
if (isRefusal(figure)) return /* @__PURE__ */ jsx("p", {
|
|
1624
|
+
className: "ov-refusal",
|
|
1625
|
+
role: "note",
|
|
1626
|
+
"data-refused": figure.id,
|
|
1627
|
+
children: figure.refused
|
|
1628
|
+
});
|
|
1629
|
+
const layout = width > 0 && width < 720 ? figure.svg.narrow ? "narrow" : "table" : "wide";
|
|
1630
|
+
const plot = layout === "wide" ? figure.svg.wide : layout === "narrow" ? figure.svg.narrow : figure.table;
|
|
1631
|
+
const method = [
|
|
1632
|
+
figure.note.method,
|
|
1633
|
+
figure.note.n,
|
|
1634
|
+
...figure.note.exclusions
|
|
1635
|
+
].join(" ");
|
|
1636
|
+
return /* @__PURE__ */ jsxs("figure", {
|
|
1637
|
+
ref,
|
|
1638
|
+
className: "tgc-figure tgc-figure-chart tgc-figure-view",
|
|
1639
|
+
"data-figure": figure.id,
|
|
1640
|
+
"data-layout": layout,
|
|
1641
|
+
"aria-label": figure.finding,
|
|
1642
|
+
children: [
|
|
1643
|
+
presentation === "report" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("h3", {
|
|
1644
|
+
className: "tgc-finding",
|
|
1645
|
+
children: figure.finding
|
|
1646
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
1647
|
+
className: "tgc-lede",
|
|
1648
|
+
children: figure.lede
|
|
1649
|
+
})] }),
|
|
1650
|
+
/* @__PURE__ */ jsx("div", {
|
|
1651
|
+
className: `tgc-view-plot tgc-view-${layout}`,
|
|
1652
|
+
role: figure.svg.interactive ? "group" : "img",
|
|
1653
|
+
"aria-label": `${figure.finding} ${figure.lede}`,
|
|
1654
|
+
style: layout === "narrow" ? { maxWidth: 358 } : void 0,
|
|
1655
|
+
dangerouslySetInnerHTML: { __html: plot }
|
|
1656
|
+
}),
|
|
1657
|
+
/* @__PURE__ */ jsxs("details", {
|
|
1658
|
+
className: "tgc-data",
|
|
1659
|
+
children: [
|
|
1660
|
+
/* @__PURE__ */ jsx("summary", { children: "Data and method" }),
|
|
1661
|
+
/* @__PURE__ */ jsx("p", {
|
|
1662
|
+
className: "tgc-lede",
|
|
1663
|
+
children: figure.lede
|
|
1664
|
+
}),
|
|
1665
|
+
/* @__PURE__ */ jsx("p", {
|
|
1666
|
+
className: "tgc-read",
|
|
1667
|
+
children: figure.read
|
|
1668
|
+
}),
|
|
1669
|
+
/* @__PURE__ */ jsx("p", {
|
|
1670
|
+
className: "tgc-read",
|
|
1671
|
+
children: method
|
|
1672
|
+
}),
|
|
1673
|
+
/* @__PURE__ */ jsx("div", {
|
|
1674
|
+
className: "tgc-scroll",
|
|
1675
|
+
role: "region",
|
|
1676
|
+
"aria-label": `${figure.finding} data`,
|
|
1677
|
+
tabIndex: 0,
|
|
1678
|
+
dangerouslySetInnerHTML: { __html: figure.table }
|
|
1679
|
+
})
|
|
1680
|
+
]
|
|
1681
|
+
})
|
|
1682
|
+
]
|
|
1312
1683
|
});
|
|
1313
1684
|
}
|
|
1314
1685
|
//#endregion
|
|
1315
|
-
export { DEFAULT_SPARKLINE_EMPTY_LABEL, DEFAULT_SPARKLINE_HEIGHT, DEFAULT_SPARKLINE_LABEL, DEFAULT_SPARKLINE_UNAVAILABLE_LABEL, DEFAULT_SPARKLINE_WIDTH, DayBars, Gantt, HBars, Histogram, LIVE_SERIES, Lines, Meter, Spark, Sparkline, StackedBarChart, WaterfallBar, WaterfallHeader, WaterfallRow, WaterfallTimeAxis, formatCompact, formatSparklineValue, formatWaterfallDuration, sparklineGeometry, sparklineLabel, sparklinePointsAttribute, sparklineReadings, waterfallSpanGeometry, waterfallTicks };
|
|
1686
|
+
export { DEFAULT_SPARKLINE_EMPTY_LABEL, DEFAULT_SPARKLINE_HEIGHT, DEFAULT_SPARKLINE_LABEL, DEFAULT_SPARKLINE_UNAVAILABLE_LABEL, DEFAULT_SPARKLINE_WIDTH, DayBars, FigureView, Gantt, HBars, Histogram, LIVE_SERIES, Lines, Meter, Spark, Sparkline, StackedBarChart, WaterfallBar, WaterfallHeader, WaterfallRow, WaterfallTimeAxis, formatCompact, formatSparklineValue, formatWaterfallDuration, sparklineGeometry, sparklineLabel, sparklinePointsAttribute, sparklineReadings, waterfallSpanGeometry, waterfallTicks };
|