@camstack/ui-library 1.2.328 → 1.2.330
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/dist/composites/charts/BarSeries.d.ts +4 -1
- package/dist/composites/charts/Sparkline.d.ts +6 -1
- package/dist/composites/charts/TimeSeriesChart.d.ts +4 -1
- package/dist/composites/charts/chart-tooltip-host.d.ts +27 -0
- package/dist/composites/charts/chart-tooltip-model.d.ts +174 -0
- package/dist/composites/charts/time-series-model.d.ts +3 -0
- package/dist/composites/charts/uplot-host.d.ts +9 -1
- package/dist/generated/system-hooks.d.ts +4 -0
- package/dist/index.cjs +817 -20
- package/dist/index.js +817 -22
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -10343,6 +10343,8 @@ var useAddonsListCapabilityProviders = trpc.addons.listCapabilityProviders.useQu
|
|
|
10343
10343
|
var useAddonsSetCapabilityProviderEnabled = trpc.addons.setCapabilityProviderEnabled.useMutation;
|
|
10344
10344
|
/** Generated alias around `trpc.addons.getVersions.useQuery`. */
|
|
10345
10345
|
var useAddonsGetVersions = trpc.addons.getVersions.useQuery;
|
|
10346
|
+
/** Generated alias around `trpc.addons.getPackageDocs.useQuery`. */
|
|
10347
|
+
var useAddonsGetPackageDocs = trpc.addons.getPackageDocs.useQuery;
|
|
10346
10348
|
/** Generated alias around `trpc.addons.restartAddon.useMutation`. */
|
|
10347
10349
|
var useAddonsRestartAddon = trpc.addons.restartAddon.useMutation;
|
|
10348
10350
|
/** Generated alias around `trpc.addons.retryLoad.useMutation`. */
|
|
@@ -11121,6 +11123,8 @@ var useFeatureProbeGetStatus = trpc.featureProbe.getStatus.useQuery;
|
|
|
11121
11123
|
var useFloodGetStatus = trpc.flood.getStatus.useQuery;
|
|
11122
11124
|
/** Generated alias around `trpc.gas.getStatus.useQuery`. */
|
|
11123
11125
|
var useGasGetStatus = trpc.gas.getStatus.useQuery;
|
|
11126
|
+
/** Generated alias around `trpc.groupCommands.getStatus.useQuery`. */
|
|
11127
|
+
var useGroupCommandsGetStatus = trpc.groupCommands.getStatus.useQuery;
|
|
11124
11128
|
/** Generated alias around `trpc.groupStatus.getStatus.useQuery`. */
|
|
11125
11129
|
var useGroupStatusGetStatus = trpc.groupStatus.getStatus.useQuery;
|
|
11126
11130
|
/** Generated alias around `trpc.heatingValve.getStatus.useQuery`. */
|
|
@@ -22514,6 +22518,641 @@ function bucketIndexAt(buckets, bucketMs, t) {
|
|
|
22514
22518
|
}
|
|
22515
22519
|
return null;
|
|
22516
22520
|
}
|
|
22521
|
+
var UNKNOWN$1 = "—";
|
|
22522
|
+
var NO_INFERENCE = new Set([
|
|
22523
|
+
"information",
|
|
22524
|
+
"duration",
|
|
22525
|
+
"duration-ms",
|
|
22526
|
+
"none",
|
|
22527
|
+
"device",
|
|
22528
|
+
"count",
|
|
22529
|
+
"index",
|
|
22530
|
+
"ratio",
|
|
22531
|
+
"humidity",
|
|
22532
|
+
"percentage"
|
|
22533
|
+
]);
|
|
22534
|
+
var HIDDEN_INTERACTION = {
|
|
22535
|
+
selection: {
|
|
22536
|
+
visible: false,
|
|
22537
|
+
dataX: null,
|
|
22538
|
+
anchorX: 0,
|
|
22539
|
+
anchorY: 0,
|
|
22540
|
+
pinned: false
|
|
22541
|
+
},
|
|
22542
|
+
gesture: null,
|
|
22543
|
+
lockHorizontalScroll: false
|
|
22544
|
+
};
|
|
22545
|
+
var TOOLTIP_SIDES = [
|
|
22546
|
+
{
|
|
22547
|
+
placed: "right",
|
|
22548
|
+
vertical: "above"
|
|
22549
|
+
},
|
|
22550
|
+
{
|
|
22551
|
+
placed: "left",
|
|
22552
|
+
vertical: "above"
|
|
22553
|
+
},
|
|
22554
|
+
{
|
|
22555
|
+
placed: "right",
|
|
22556
|
+
vertical: "below"
|
|
22557
|
+
},
|
|
22558
|
+
{
|
|
22559
|
+
placed: "left",
|
|
22560
|
+
vertical: "below"
|
|
22561
|
+
}
|
|
22562
|
+
];
|
|
22563
|
+
/** Index of the sample closest to `x`. A tie keeps the earlier sample. */
|
|
22564
|
+
function nearestPointIndex(xs, x) {
|
|
22565
|
+
if (xs.length === 0 || !Number.isFinite(x)) return null;
|
|
22566
|
+
let lo = 0;
|
|
22567
|
+
let hi = xs.length - 1;
|
|
22568
|
+
while (lo < hi) {
|
|
22569
|
+
const mid = Math.floor((lo + hi) / 2);
|
|
22570
|
+
if ((xs[mid] ?? 0) < x) lo = mid + 1;
|
|
22571
|
+
else hi = mid;
|
|
22572
|
+
}
|
|
22573
|
+
const right = lo;
|
|
22574
|
+
const left = Math.max(0, right - 1);
|
|
22575
|
+
return Math.abs((xs[left] ?? 0) - x) <= Math.abs((xs[right] ?? 0) - x) ? left : right;
|
|
22576
|
+
}
|
|
22577
|
+
/** Data x for a pointer inside the plot. `null` when it is outside and clamping is off. */
|
|
22578
|
+
function dataXFromPointer(input) {
|
|
22579
|
+
if (!(input.plotWidth > 0) || !Number.isFinite(input.localX)) return null;
|
|
22580
|
+
const rel = input.localX - input.plotLeft;
|
|
22581
|
+
if (!input.clamp && (rel < 0 || rel > input.plotWidth)) return null;
|
|
22582
|
+
const clamped = Math.min(input.plotWidth, Math.max(0, rel));
|
|
22583
|
+
return input.xMin + clamped / input.plotWidth * (input.xMax - input.xMin);
|
|
22584
|
+
}
|
|
22585
|
+
/** Bucket that contains `dataX`, or the nearest one when `dataX` falls in a gap. */
|
|
22586
|
+
function bucketSelectionIndex(buckets, bucketMs, dataX) {
|
|
22587
|
+
if (buckets.length === 0 || !Number.isFinite(dataX)) return null;
|
|
22588
|
+
let best = 0;
|
|
22589
|
+
let bestDist = Number.POSITIVE_INFINITY;
|
|
22590
|
+
for (let i = 0; i < buckets.length; i++) {
|
|
22591
|
+
const start = buckets[i]?.t;
|
|
22592
|
+
if (start === void 0) continue;
|
|
22593
|
+
const end = start + bucketMs;
|
|
22594
|
+
if (dataX >= start && dataX < end) return i;
|
|
22595
|
+
const dist = dataX < start ? start - dataX : dataX - end;
|
|
22596
|
+
if (dist < bestDist) {
|
|
22597
|
+
bestDist = dist;
|
|
22598
|
+
best = i;
|
|
22599
|
+
}
|
|
22600
|
+
}
|
|
22601
|
+
return best;
|
|
22602
|
+
}
|
|
22603
|
+
/** Next keyboard stop. A null current starts at the first or last stop. */
|
|
22604
|
+
function stepStop(stops, current, direction) {
|
|
22605
|
+
if (stops.length === 0) return null;
|
|
22606
|
+
const ordered = [...stops].sort((a, b) => a - b);
|
|
22607
|
+
if (current === null || !Number.isFinite(current)) return direction > 0 ? ordered[0] ?? null : ordered[ordered.length - 1] ?? null;
|
|
22608
|
+
let idx = 0;
|
|
22609
|
+
let best = Number.POSITIVE_INFINITY;
|
|
22610
|
+
for (let i = 0; i < ordered.length; i++) {
|
|
22611
|
+
const dist = Math.abs((ordered[i] ?? 0) - current);
|
|
22612
|
+
if (dist < best) {
|
|
22613
|
+
best = dist;
|
|
22614
|
+
idx = i;
|
|
22615
|
+
}
|
|
22616
|
+
}
|
|
22617
|
+
const next = idx + direction;
|
|
22618
|
+
if (next < 0 || next >= ordered.length) return ordered[idx] ?? null;
|
|
22619
|
+
return ordered[next] ?? null;
|
|
22620
|
+
}
|
|
22621
|
+
function shown(dataX, anchorX, anchorY, pinned) {
|
|
22622
|
+
return {
|
|
22623
|
+
visible: true,
|
|
22624
|
+
dataX,
|
|
22625
|
+
anchorX,
|
|
22626
|
+
anchorY,
|
|
22627
|
+
pinned
|
|
22628
|
+
};
|
|
22629
|
+
}
|
|
22630
|
+
function resting(selection, gesture, lock) {
|
|
22631
|
+
return {
|
|
22632
|
+
selection,
|
|
22633
|
+
gesture,
|
|
22634
|
+
lockHorizontalScroll: lock
|
|
22635
|
+
};
|
|
22636
|
+
}
|
|
22637
|
+
function lockAxis(gesture, x, y) {
|
|
22638
|
+
if (gesture.axis !== "pending") return gesture.axis;
|
|
22639
|
+
const dx = Math.abs(x - gesture.originX);
|
|
22640
|
+
const dy = Math.abs(y - gesture.originY);
|
|
22641
|
+
if (dx < 10 && dy < 10) return "pending";
|
|
22642
|
+
return dx >= dy ? "x" : "y";
|
|
22643
|
+
}
|
|
22644
|
+
function onDown(state, event) {
|
|
22645
|
+
if (event.kind === "mouse") return state;
|
|
22646
|
+
return resting(state.selection, {
|
|
22647
|
+
kind: event.kind,
|
|
22648
|
+
originX: event.x,
|
|
22649
|
+
originY: event.y,
|
|
22650
|
+
axis: "pending"
|
|
22651
|
+
}, false);
|
|
22652
|
+
}
|
|
22653
|
+
function onMove(state, event) {
|
|
22654
|
+
if (event.kind === "mouse") {
|
|
22655
|
+
if (event.dataX === null) return state.selection.pinned ? state : HIDDEN_INTERACTION;
|
|
22656
|
+
return resting(shown(event.dataX, event.x, event.y, false), null, false);
|
|
22657
|
+
}
|
|
22658
|
+
const gesture = state.gesture;
|
|
22659
|
+
if (gesture === null) return state;
|
|
22660
|
+
const axis = lockAxis(gesture, event.x, event.y);
|
|
22661
|
+
const nextGesture = {
|
|
22662
|
+
...gesture,
|
|
22663
|
+
axis
|
|
22664
|
+
};
|
|
22665
|
+
if (axis !== "x" || event.dataX === null) return resting(state.selection, nextGesture, false);
|
|
22666
|
+
return resting(shown(event.dataX, event.x, event.y, true), nextGesture, true);
|
|
22667
|
+
}
|
|
22668
|
+
function onUp(state, event) {
|
|
22669
|
+
if (event.kind === "mouse" || state.gesture === null) return state;
|
|
22670
|
+
if (state.gesture.axis === "y" || state.gesture.axis === "x") return resting(state.selection, null, false);
|
|
22671
|
+
if (state.selection.visible && state.selection.pinned) return HIDDEN_INTERACTION;
|
|
22672
|
+
if (event.dataX === null) return HIDDEN_INTERACTION;
|
|
22673
|
+
return resting(shown(event.dataX, event.x, event.y, true), null, false);
|
|
22674
|
+
}
|
|
22675
|
+
/** Pointer, keyboard and dismiss rules for one chart. */
|
|
22676
|
+
function reduceChartInteraction(state, event) {
|
|
22677
|
+
switch (event.type) {
|
|
22678
|
+
case "hide":
|
|
22679
|
+
case "outside": return HIDDEN_INTERACTION;
|
|
22680
|
+
case "leave": return state.selection.pinned ? resting(state.selection, null, false) : HIDDEN_INTERACTION;
|
|
22681
|
+
case "hover": return resting(shown(event.dataX, event.anchorX, event.anchorY, false), null, false);
|
|
22682
|
+
case "key": return resting(shown(event.dataX, event.anchorX, event.anchorY, true), null, false);
|
|
22683
|
+
case "down": return onDown(state, event);
|
|
22684
|
+
case "move": return onMove(state, event);
|
|
22685
|
+
case "up": return onUp(state, event);
|
|
22686
|
+
default: return event;
|
|
22687
|
+
}
|
|
22688
|
+
}
|
|
22689
|
+
function quantityFromUnit(unit) {
|
|
22690
|
+
if (unit === null || unit === void 0 || unit.length === 0) return void 0;
|
|
22691
|
+
let found;
|
|
22692
|
+
for (const key of Object.keys(_camstack_types.QUANTITY_BASE)) {
|
|
22693
|
+
if (!(0, _camstack_types.isReadingQuantity)(key) || NO_INFERENCE.has(key)) continue;
|
|
22694
|
+
if (_camstack_types.QUANTITY_BASE[key] !== unit) continue;
|
|
22695
|
+
if (found !== void 0) return void 0;
|
|
22696
|
+
found = key;
|
|
22697
|
+
}
|
|
22698
|
+
return found;
|
|
22699
|
+
}
|
|
22700
|
+
function withUnit(text, unit, unknown) {
|
|
22701
|
+
if (text.length === 0) return unknown;
|
|
22702
|
+
if (unit.length === 0) return text;
|
|
22703
|
+
if (unit === "%") return `${text}%`;
|
|
22704
|
+
return `${text} ${unit}`;
|
|
22705
|
+
}
|
|
22706
|
+
/** One sample, with its unit. Missing samples are an em dash, never zero. */
|
|
22707
|
+
function formatTooltipValue(input) {
|
|
22708
|
+
const unknown = input.unknownLabel ?? UNKNOWN$1;
|
|
22709
|
+
if (input.value === null || !Number.isFinite(input.value)) return unknown;
|
|
22710
|
+
const quantity = input.quantity === "device" ? void 0 : input.quantity ?? quantityFromUnit(input.unit);
|
|
22711
|
+
if (quantity === void 0) return formatChartValue(input.value, input.unit ?? null);
|
|
22712
|
+
const rendered = (0, _camstack_types.formatReading)({
|
|
22713
|
+
value: input.value,
|
|
22714
|
+
quantity,
|
|
22715
|
+
preference: input.preference,
|
|
22716
|
+
precision: input.precision
|
|
22717
|
+
});
|
|
22718
|
+
return withUnit(rendered.text, rendered.unit, unknown);
|
|
22719
|
+
}
|
|
22720
|
+
function liveText(heading, lines) {
|
|
22721
|
+
if (lines.length === 0) return heading;
|
|
22722
|
+
return `${heading}, ${lines.map((line) => `${line.label} ${line.value}`).join(", ")}`;
|
|
22723
|
+
}
|
|
22724
|
+
function asBody(heading, lines) {
|
|
22725
|
+
return {
|
|
22726
|
+
heading,
|
|
22727
|
+
lines,
|
|
22728
|
+
liveText: liveText(heading, lines)
|
|
22729
|
+
};
|
|
22730
|
+
}
|
|
22731
|
+
/** Time plus one line per visible series. */
|
|
22732
|
+
function lineTooltipBody(input) {
|
|
22733
|
+
const lines = input.series.filter((sample) => sample.visible !== false).map((sample) => ({
|
|
22734
|
+
id: sample.id,
|
|
22735
|
+
label: sample.label,
|
|
22736
|
+
value: formatTooltipValue({
|
|
22737
|
+
value: sample.value,
|
|
22738
|
+
quantity: sample.quantity,
|
|
22739
|
+
unit: sample.unit,
|
|
22740
|
+
precision: sample.precision,
|
|
22741
|
+
preference: input.preference
|
|
22742
|
+
})
|
|
22743
|
+
}));
|
|
22744
|
+
return asBody(input.timeLabel, lines);
|
|
22745
|
+
}
|
|
22746
|
+
/** The state in force at `t`, and how long that span lasts. */
|
|
22747
|
+
function stateSpanAt(segments, t) {
|
|
22748
|
+
for (const segment of segments) if (t >= segment.from && t < segment.to) return {
|
|
22749
|
+
state: segment.state,
|
|
22750
|
+
spanMs: Math.max(0, segment.to - segment.from)
|
|
22751
|
+
};
|
|
22752
|
+
return null;
|
|
22753
|
+
}
|
|
22754
|
+
function stateTooltipValue(stateLabel, spanMs) {
|
|
22755
|
+
if (stateLabel === null || stateLabel.length === 0) return UNKNOWN$1;
|
|
22756
|
+
if (spanMs === null || !(spanMs > 0)) return stateLabel;
|
|
22757
|
+
return `${stateLabel} · ${formatSpan(spanMs)}`;
|
|
22758
|
+
}
|
|
22759
|
+
/** One row per state series: the label and the span under the pointer. */
|
|
22760
|
+
function stateTooltipBody(input) {
|
|
22761
|
+
const lines = input.rows.map((row) => ({
|
|
22762
|
+
id: row.id,
|
|
22763
|
+
label: row.label,
|
|
22764
|
+
value: stateTooltipValue(row.stateLabel, row.spanMs)
|
|
22765
|
+
}));
|
|
22766
|
+
return asBody(input.timeLabel, lines);
|
|
22767
|
+
}
|
|
22768
|
+
/** `start – end` for one bar bucket, at the window's time precision. */
|
|
22769
|
+
function barRangeLabel(start, bucketMs, spanMs) {
|
|
22770
|
+
return `${formatCursorTime(start, spanMs)} – ${formatCursorTime(start + bucketMs, spanMs)}`;
|
|
22771
|
+
}
|
|
22772
|
+
/** The bucket's range, and each layer's value with its unit. */
|
|
22773
|
+
function barTooltipBody(input) {
|
|
22774
|
+
const lines = input.layers.map((layer) => ({
|
|
22775
|
+
id: layer.id,
|
|
22776
|
+
label: layer.label,
|
|
22777
|
+
value: formatTooltipValue({
|
|
22778
|
+
value: layer.value,
|
|
22779
|
+
quantity: layer.quantity,
|
|
22780
|
+
unit: layer.unit,
|
|
22781
|
+
precision: layer.precision,
|
|
22782
|
+
preference: input.preference
|
|
22783
|
+
})
|
|
22784
|
+
}));
|
|
22785
|
+
return asBody(input.rangeLabel, lines);
|
|
22786
|
+
}
|
|
22787
|
+
function sideOrigin(side, input, gap) {
|
|
22788
|
+
return {
|
|
22789
|
+
left: side.placed === "right" ? input.anchorX + gap : input.anchorX - gap - input.tooltipWidth,
|
|
22790
|
+
top: side.vertical === "above" ? input.anchorY - gap - input.tooltipHeight : input.anchorY + gap
|
|
22791
|
+
};
|
|
22792
|
+
}
|
|
22793
|
+
function insideBounds(left, top, input) {
|
|
22794
|
+
const pad = 4;
|
|
22795
|
+
return left >= pad && top >= pad && left + input.tooltipWidth <= input.boundsWidth - pad && top + input.tooltipHeight <= input.boundsHeight - pad;
|
|
22796
|
+
}
|
|
22797
|
+
function coversPoint(left, top, input) {
|
|
22798
|
+
return input.anchorX >= left && input.anchorX <= left + input.tooltipWidth && input.anchorY >= top && input.anchorY <= top + input.tooltipHeight;
|
|
22799
|
+
}
|
|
22800
|
+
function clampOrigin(left, top, input) {
|
|
22801
|
+
const pad = 4;
|
|
22802
|
+
const maxLeft = Math.max(pad, input.boundsWidth - pad - input.tooltipWidth);
|
|
22803
|
+
const maxTop = Math.max(pad, input.boundsHeight - pad - input.tooltipHeight);
|
|
22804
|
+
return {
|
|
22805
|
+
left: Math.min(Math.max(left, pad), maxLeft),
|
|
22806
|
+
top: Math.min(Math.max(top, pad), maxTop)
|
|
22807
|
+
};
|
|
22808
|
+
}
|
|
22809
|
+
/**
|
|
22810
|
+
* Place the tooltip beside the point, inside the chart. The first side that
|
|
22811
|
+
* fits and does not cover the point wins; the edges flip before the corners.
|
|
22812
|
+
*/
|
|
22813
|
+
function placeChartTooltip(input) {
|
|
22814
|
+
const gap = input.gap ?? 12;
|
|
22815
|
+
let fallback = null;
|
|
22816
|
+
for (const side of TOOLTIP_SIDES) {
|
|
22817
|
+
const origin = sideOrigin(side, input, gap);
|
|
22818
|
+
const clamped = clampOrigin(origin.left, origin.top, input);
|
|
22819
|
+
const placement = {
|
|
22820
|
+
...side,
|
|
22821
|
+
...clamped
|
|
22822
|
+
};
|
|
22823
|
+
if (fallback === null && !coversPoint(clamped.left, clamped.top, input)) fallback = placement;
|
|
22824
|
+
if (insideBounds(origin.left, origin.top, input) && !coversPoint(origin.left, origin.top, input)) return {
|
|
22825
|
+
...side,
|
|
22826
|
+
...origin
|
|
22827
|
+
};
|
|
22828
|
+
}
|
|
22829
|
+
if (fallback !== null) return fallback;
|
|
22830
|
+
const first = TOOLTIP_SIDES[0] ?? {
|
|
22831
|
+
placed: "right",
|
|
22832
|
+
vertical: "above"
|
|
22833
|
+
};
|
|
22834
|
+
const clamped = clampOrigin(sideOrigin(first, input, gap).left, sideOrigin(first, input, gap).top, input);
|
|
22835
|
+
return {
|
|
22836
|
+
...first,
|
|
22837
|
+
...clamped
|
|
22838
|
+
};
|
|
22839
|
+
}
|
|
22840
|
+
//#endregion
|
|
22841
|
+
//#region src/composites/charts/chart-tooltip-host.tsx
|
|
22842
|
+
/**
|
|
22843
|
+
* One tooltip for every chart. The plot stays in `UPlotHost`; this shell
|
|
22844
|
+
* tracks the pointer, a touch scrub and the arrow keys, and draws the
|
|
22845
|
+
* readout inside the chart.
|
|
22846
|
+
*/
|
|
22847
|
+
function pointerKind(pointerType) {
|
|
22848
|
+
return pointerType === "mouse" ? "mouse" : "touch";
|
|
22849
|
+
}
|
|
22850
|
+
function pointerSample(clientX, clientY, shell, plot, spec, clamp) {
|
|
22851
|
+
const box = shell.getBoundingClientRect();
|
|
22852
|
+
const anchorX = clientX - box.left;
|
|
22853
|
+
const anchorY = clientY - box.top;
|
|
22854
|
+
if (plot !== null) {
|
|
22855
|
+
const over = plot.over.getBoundingClientRect();
|
|
22856
|
+
const left = clientX - over.left;
|
|
22857
|
+
const top = clientY - over.top;
|
|
22858
|
+
const minX = plot.bbox.left;
|
|
22859
|
+
const maxX = plot.bbox.left + plot.bbox.width;
|
|
22860
|
+
const inside = left >= minX && left <= maxX && top >= plot.bbox.top && top <= plot.bbox.top + plot.bbox.height;
|
|
22861
|
+
if (!inside && !clamp) return {
|
|
22862
|
+
dataX: null,
|
|
22863
|
+
anchorX,
|
|
22864
|
+
anchorY,
|
|
22865
|
+
inside: false
|
|
22866
|
+
};
|
|
22867
|
+
return {
|
|
22868
|
+
dataX: plot.posToVal(Math.min(maxX, Math.max(minX, left)), "x"),
|
|
22869
|
+
anchorX,
|
|
22870
|
+
anchorY,
|
|
22871
|
+
inside
|
|
22872
|
+
};
|
|
22873
|
+
}
|
|
22874
|
+
const dataX = dataXFromPointer({
|
|
22875
|
+
localX: anchorX,
|
|
22876
|
+
plotLeft: 0,
|
|
22877
|
+
plotWidth: box.width,
|
|
22878
|
+
xMin: spec.xMin,
|
|
22879
|
+
xMax: spec.xMax,
|
|
22880
|
+
clamp
|
|
22881
|
+
});
|
|
22882
|
+
return {
|
|
22883
|
+
dataX,
|
|
22884
|
+
anchorX,
|
|
22885
|
+
anchorY,
|
|
22886
|
+
inside: dataX !== null
|
|
22887
|
+
};
|
|
22888
|
+
}
|
|
22889
|
+
function anchorForDataX(dataX, shell, plot, spec) {
|
|
22890
|
+
if (plot !== null && shell !== null) {
|
|
22891
|
+
const over = plot.over.getBoundingClientRect();
|
|
22892
|
+
const box = shell.getBoundingClientRect();
|
|
22893
|
+
return {
|
|
22894
|
+
x: over.left - box.left + plot.valToPos(dataX, "x"),
|
|
22895
|
+
y: over.top - box.top + plot.bbox.top + plot.bbox.height / 2
|
|
22896
|
+
};
|
|
22897
|
+
}
|
|
22898
|
+
const width = shell?.getBoundingClientRect().width ?? 0;
|
|
22899
|
+
const height = shell?.getBoundingClientRect().height ?? 0;
|
|
22900
|
+
const span = spec.xMax - spec.xMin;
|
|
22901
|
+
const frac = span === 0 ? 0 : (dataX - spec.xMin) / span;
|
|
22902
|
+
return {
|
|
22903
|
+
x: Math.min(1, Math.max(0, frac)) * width,
|
|
22904
|
+
y: height / 2
|
|
22905
|
+
};
|
|
22906
|
+
}
|
|
22907
|
+
function movePlotCursor(plot, dataX) {
|
|
22908
|
+
if (plot === null) return;
|
|
22909
|
+
if (dataX === null) {
|
|
22910
|
+
plot.setCursor({
|
|
22911
|
+
left: -10,
|
|
22912
|
+
top: -10
|
|
22913
|
+
});
|
|
22914
|
+
return;
|
|
22915
|
+
}
|
|
22916
|
+
plot.setCursor({
|
|
22917
|
+
left: plot.valToPos(dataX, "x"),
|
|
22918
|
+
top: plot.bbox.top + plot.bbox.height / 2
|
|
22919
|
+
});
|
|
22920
|
+
}
|
|
22921
|
+
function ChartTooltipCard({ view }) {
|
|
22922
|
+
const ref = (0, react$1.useRef)(null);
|
|
22923
|
+
(0, react$1.useLayoutEffect)(() => {
|
|
22924
|
+
const el = ref.current;
|
|
22925
|
+
if (el === null) return;
|
|
22926
|
+
const placed = placeChartTooltip({
|
|
22927
|
+
anchorX: view.anchorX,
|
|
22928
|
+
anchorY: view.anchorY,
|
|
22929
|
+
tooltipWidth: el.offsetWidth,
|
|
22930
|
+
tooltipHeight: el.offsetHeight,
|
|
22931
|
+
boundsWidth: view.boundsWidth,
|
|
22932
|
+
boundsHeight: view.boundsHeight
|
|
22933
|
+
});
|
|
22934
|
+
el.style.left = `${String(placed.left)}px`;
|
|
22935
|
+
el.style.top = `${String(placed.top)}px`;
|
|
22936
|
+
}, [view]);
|
|
22937
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
22938
|
+
ref,
|
|
22939
|
+
role: "tooltip",
|
|
22940
|
+
"data-chart-tooltip": "",
|
|
22941
|
+
className: "pointer-events-none absolute z-20 max-w-64 rounded-md border border-border bg-background-elevated px-2 py-1 text-foreground shadow-md",
|
|
22942
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
22943
|
+
className: "m-0 text-[10px] tabular-nums text-foreground-subtle",
|
|
22944
|
+
children: view.body.heading
|
|
22945
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
22946
|
+
className: "m-0 list-none p-0",
|
|
22947
|
+
children: view.body.lines.map((line) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
22948
|
+
className: "flex items-baseline justify-between gap-3 text-xs",
|
|
22949
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
22950
|
+
className: "min-w-0 truncate text-foreground-muted",
|
|
22951
|
+
children: line.label
|
|
22952
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
22953
|
+
className: "shrink-0 tabular-nums",
|
|
22954
|
+
children: line.value
|
|
22955
|
+
})]
|
|
22956
|
+
}, line.id))
|
|
22957
|
+
})]
|
|
22958
|
+
});
|
|
22959
|
+
}
|
|
22960
|
+
function ChartTooltipShell({ spec, plotRef, kind, label, inline = false, children }) {
|
|
22961
|
+
const shellRef = (0, react$1.useRef)(null);
|
|
22962
|
+
const specRef = (0, react$1.useRef)(spec);
|
|
22963
|
+
specRef.current = spec;
|
|
22964
|
+
const interactionRef = (0, react$1.useRef)(HIDDEN_INTERACTION);
|
|
22965
|
+
const dataXRef = (0, react$1.useRef)(null);
|
|
22966
|
+
const [view, setView] = (0, react$1.useState)(null);
|
|
22967
|
+
const pinned = view !== null && interactionRef.current.selection.pinned;
|
|
22968
|
+
const commit = (0, react$1.useCallback)((next) => {
|
|
22969
|
+
interactionRef.current = next;
|
|
22970
|
+
const current = specRef.current;
|
|
22971
|
+
const shell = shellRef.current;
|
|
22972
|
+
const dataX = next.selection.visible ? next.selection.dataX : null;
|
|
22973
|
+
const body = current !== null && dataX !== null ? current.resolve(dataX) : null;
|
|
22974
|
+
if (body === null || dataX === null) {
|
|
22975
|
+
setView(null);
|
|
22976
|
+
if (dataXRef.current !== null) {
|
|
22977
|
+
dataXRef.current = null;
|
|
22978
|
+
current?.onDataX?.(null);
|
|
22979
|
+
}
|
|
22980
|
+
return;
|
|
22981
|
+
}
|
|
22982
|
+
setView({
|
|
22983
|
+
body,
|
|
22984
|
+
anchorX: next.selection.anchorX,
|
|
22985
|
+
anchorY: next.selection.anchorY,
|
|
22986
|
+
boundsWidth: shell?.getBoundingClientRect().width ?? 0,
|
|
22987
|
+
boundsHeight: shell?.getBoundingClientRect().height ?? 0
|
|
22988
|
+
});
|
|
22989
|
+
if (dataXRef.current !== dataX) {
|
|
22990
|
+
dataXRef.current = dataX;
|
|
22991
|
+
current?.onDataX?.(dataX);
|
|
22992
|
+
}
|
|
22993
|
+
}, []);
|
|
22994
|
+
(0, react$1.useEffect)(() => {
|
|
22995
|
+
const shell = shellRef.current;
|
|
22996
|
+
if (shell === null) return;
|
|
22997
|
+
const sample = (event, clamp) => {
|
|
22998
|
+
const current = specRef.current;
|
|
22999
|
+
if (current === null) return null;
|
|
23000
|
+
return pointerSample(event.clientX, event.clientY, shell, plotRef.current, current, clamp);
|
|
23001
|
+
};
|
|
23002
|
+
const onDown = (event) => {
|
|
23003
|
+
if (pointerKind(event.pointerType) === "mouse") return;
|
|
23004
|
+
const at = sample(event, false);
|
|
23005
|
+
if (at === null) return;
|
|
23006
|
+
commit(reduceChartInteraction(interactionRef.current, {
|
|
23007
|
+
type: "down",
|
|
23008
|
+
kind: "touch",
|
|
23009
|
+
x: at.anchorX,
|
|
23010
|
+
y: at.anchorY,
|
|
23011
|
+
dataX: at.dataX
|
|
23012
|
+
}));
|
|
23013
|
+
};
|
|
23014
|
+
const onMove = (event) => {
|
|
23015
|
+
const kind = pointerKind(event.pointerType);
|
|
23016
|
+
const scrubbing = interactionRef.current.gesture?.axis === "x";
|
|
23017
|
+
const at = sample(event, kind !== "mouse" && scrubbing);
|
|
23018
|
+
if (at === null) return;
|
|
23019
|
+
if (kind === "mouse") {
|
|
23020
|
+
if (!at.inside || at.dataX === null) {
|
|
23021
|
+
commit(reduceChartInteraction(interactionRef.current, { type: "leave" }));
|
|
23022
|
+
if (!interactionRef.current.selection.visible) movePlotCursor(plotRef.current, null);
|
|
23023
|
+
return;
|
|
23024
|
+
}
|
|
23025
|
+
commit(reduceChartInteraction(interactionRef.current, {
|
|
23026
|
+
type: "hover",
|
|
23027
|
+
dataX: at.dataX,
|
|
23028
|
+
anchorX: at.anchorX,
|
|
23029
|
+
anchorY: at.anchorY
|
|
23030
|
+
}));
|
|
23031
|
+
movePlotCursor(plotRef.current, at.dataX);
|
|
23032
|
+
return;
|
|
23033
|
+
}
|
|
23034
|
+
const next = reduceChartInteraction(interactionRef.current, {
|
|
23035
|
+
type: "move",
|
|
23036
|
+
kind: "touch",
|
|
23037
|
+
x: at.anchorX,
|
|
23038
|
+
y: at.anchorY,
|
|
23039
|
+
dataX: at.dataX
|
|
23040
|
+
});
|
|
23041
|
+
if (next.lockHorizontalScroll) {
|
|
23042
|
+
event.preventDefault();
|
|
23043
|
+
if (event.pointerId !== 0 && !shell.hasPointerCapture(event.pointerId)) shell.setPointerCapture(event.pointerId);
|
|
23044
|
+
}
|
|
23045
|
+
commit(next);
|
|
23046
|
+
if (next.selection.dataX !== null && next.selection.visible) movePlotCursor(plotRef.current, next.selection.dataX);
|
|
23047
|
+
};
|
|
23048
|
+
const onUp = (event) => {
|
|
23049
|
+
if (pointerKind(event.pointerType) === "mouse") return;
|
|
23050
|
+
const at = sample(event, interactionRef.current.gesture?.axis === "x");
|
|
23051
|
+
if (at === null) return;
|
|
23052
|
+
const next = reduceChartInteraction(interactionRef.current, {
|
|
23053
|
+
type: "up",
|
|
23054
|
+
kind: "touch",
|
|
23055
|
+
x: at.anchorX,
|
|
23056
|
+
y: at.anchorY,
|
|
23057
|
+
dataX: at.dataX
|
|
23058
|
+
});
|
|
23059
|
+
commit(next);
|
|
23060
|
+
movePlotCursor(plotRef.current, next.selection.visible ? next.selection.dataX : null);
|
|
23061
|
+
};
|
|
23062
|
+
const onLeave = (event) => {
|
|
23063
|
+
if (pointerKind(event.pointerType) !== "mouse") return;
|
|
23064
|
+
const next = reduceChartInteraction(interactionRef.current, { type: "leave" });
|
|
23065
|
+
commit(next);
|
|
23066
|
+
if (!next.selection.visible) movePlotCursor(plotRef.current, null);
|
|
23067
|
+
};
|
|
23068
|
+
shell.addEventListener("pointerdown", onDown);
|
|
23069
|
+
shell.addEventListener("pointermove", onMove);
|
|
23070
|
+
shell.addEventListener("pointerup", onUp);
|
|
23071
|
+
shell.addEventListener("pointercancel", onUp);
|
|
23072
|
+
shell.addEventListener("pointerleave", onLeave);
|
|
23073
|
+
return () => {
|
|
23074
|
+
shell.removeEventListener("pointerdown", onDown);
|
|
23075
|
+
shell.removeEventListener("pointermove", onMove);
|
|
23076
|
+
shell.removeEventListener("pointerup", onUp);
|
|
23077
|
+
shell.removeEventListener("pointercancel", onUp);
|
|
23078
|
+
shell.removeEventListener("pointerleave", onLeave);
|
|
23079
|
+
};
|
|
23080
|
+
}, [commit, plotRef]);
|
|
23081
|
+
(0, react$1.useEffect)(() => {
|
|
23082
|
+
if (!pinned) return;
|
|
23083
|
+
const onDoc = (event) => {
|
|
23084
|
+
const shell = shellRef.current;
|
|
23085
|
+
if (shell !== null && event.target instanceof Node && shell.contains(event.target)) return;
|
|
23086
|
+
commit(reduceChartInteraction(interactionRef.current, { type: "outside" }));
|
|
23087
|
+
movePlotCursor(plotRef.current, null);
|
|
23088
|
+
};
|
|
23089
|
+
document.addEventListener("pointerdown", onDoc, true);
|
|
23090
|
+
return () => document.removeEventListener("pointerdown", onDoc, true);
|
|
23091
|
+
}, [
|
|
23092
|
+
commit,
|
|
23093
|
+
pinned,
|
|
23094
|
+
plotRef
|
|
23095
|
+
]);
|
|
23096
|
+
const onKeyDown = (event) => {
|
|
23097
|
+
const current = specRef.current;
|
|
23098
|
+
if (current === null) return;
|
|
23099
|
+
if (event.key === "Escape") {
|
|
23100
|
+
event.preventDefault();
|
|
23101
|
+
commit(reduceChartInteraction(interactionRef.current, { type: "hide" }));
|
|
23102
|
+
movePlotCursor(plotRef.current, null);
|
|
23103
|
+
return;
|
|
23104
|
+
}
|
|
23105
|
+
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
|
|
23106
|
+
event.preventDefault();
|
|
23107
|
+
const from = interactionRef.current.selection.visible ? interactionRef.current.selection.dataX : null;
|
|
23108
|
+
const dataX = stepStop(current.stops, from, event.key === "ArrowRight" ? 1 : -1);
|
|
23109
|
+
if (dataX === null) return;
|
|
23110
|
+
const anchor = anchorForDataX(dataX, shellRef.current, plotRef.current, current);
|
|
23111
|
+
commit(reduceChartInteraction(interactionRef.current, {
|
|
23112
|
+
type: "key",
|
|
23113
|
+
dataX,
|
|
23114
|
+
anchorX: anchor.x,
|
|
23115
|
+
anchorY: anchor.y
|
|
23116
|
+
}));
|
|
23117
|
+
movePlotCursor(plotRef.current, dataX);
|
|
23118
|
+
};
|
|
23119
|
+
const setShell = (el) => {
|
|
23120
|
+
shellRef.current = el;
|
|
23121
|
+
};
|
|
23122
|
+
const className = cn("relative min-w-0 touch-pan-y outline-none focus-visible:ring-2 focus-visible:ring-primary", inline ? "inline-block w-full" : "w-full");
|
|
23123
|
+
const tooltip = view !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartTooltipCard, { view }) : null;
|
|
23124
|
+
const live = view !== null && typeof document !== "undefined" ? (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
23125
|
+
className: "sr-only",
|
|
23126
|
+
"aria-live": "polite",
|
|
23127
|
+
"data-chart-tooltip-live": "",
|
|
23128
|
+
children: view.body.liveText
|
|
23129
|
+
}), document.body) : null;
|
|
23130
|
+
const shared = {
|
|
23131
|
+
className,
|
|
23132
|
+
"data-chart-shell": "",
|
|
23133
|
+
"data-chart-kind": kind,
|
|
23134
|
+
tabIndex: spec === null ? void 0 : 0,
|
|
23135
|
+
"aria-label": label,
|
|
23136
|
+
onKeyDown
|
|
23137
|
+
};
|
|
23138
|
+
return inline ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
23139
|
+
ref: setShell,
|
|
23140
|
+
...shared,
|
|
23141
|
+
children: [
|
|
23142
|
+
children,
|
|
23143
|
+
tooltip,
|
|
23144
|
+
live
|
|
23145
|
+
]
|
|
23146
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
23147
|
+
ref: setShell,
|
|
23148
|
+
...shared,
|
|
23149
|
+
children: [
|
|
23150
|
+
children,
|
|
23151
|
+
tooltip,
|
|
23152
|
+
live
|
|
23153
|
+
]
|
|
23154
|
+
});
|
|
23155
|
+
}
|
|
22517
23156
|
//#endregion
|
|
22518
23157
|
//#region src/composites/charts/uplot-host.tsx
|
|
22519
23158
|
/**
|
|
@@ -22544,7 +23183,7 @@ function loadUPlot() {
|
|
|
22544
23183
|
if (ctorPromise === null) ctorPromise = canDrawCanvas() ? Promise.resolve().then(() => require("./uPlot.esm-BbxBUnav.cjs")).then((m) => m.default).catch(() => null) : Promise.resolve(null);
|
|
22545
23184
|
return ctorPromise;
|
|
22546
23185
|
}
|
|
22547
|
-
function UPlotHost({ optionsKey, buildOptions, data, height, onInstance, className, inline = false }) {
|
|
23186
|
+
function UPlotHost({ optionsKey, buildOptions, data, height, onInstance, className, inline = false, tooltip = null, kind, label }) {
|
|
22548
23187
|
const hostRef = (0, react$1.useRef)(null);
|
|
22549
23188
|
const plotRef = (0, react$1.useRef)(null);
|
|
22550
23189
|
const [ctor, setCtor] = (0, react$1.useState)(null);
|
|
@@ -22576,6 +23215,8 @@ function UPlotHost({ optionsKey, buildOptions, data, height, onInstance, classNa
|
|
|
22576
23215
|
width,
|
|
22577
23216
|
height
|
|
22578
23217
|
}, latest.current.data, el);
|
|
23218
|
+
u.over.style.touchAction = "pan-y";
|
|
23219
|
+
u.root.style.touchAction = "pan-y";
|
|
22579
23220
|
plotRef.current = u;
|
|
22580
23221
|
latest.current.onInstance?.(u);
|
|
22581
23222
|
return () => {
|
|
@@ -22617,14 +23258,21 @@ function UPlotHost({ optionsKey, buildOptions, data, height, onInstance, classNa
|
|
|
22617
23258
|
const setHost = (el) => {
|
|
22618
23259
|
hostRef.current = el;
|
|
22619
23260
|
};
|
|
22620
|
-
return
|
|
22621
|
-
|
|
22622
|
-
|
|
22623
|
-
|
|
22624
|
-
|
|
22625
|
-
|
|
22626
|
-
|
|
22627
|
-
|
|
23261
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartTooltipShell, {
|
|
23262
|
+
spec: tooltip,
|
|
23263
|
+
plotRef,
|
|
23264
|
+
kind,
|
|
23265
|
+
label,
|
|
23266
|
+
inline,
|
|
23267
|
+
children: inline ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
23268
|
+
ref: setHost,
|
|
23269
|
+
className: className ?? "block w-full min-w-0",
|
|
23270
|
+
"data-chart-plot": ""
|
|
23271
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
23272
|
+
ref: setHost,
|
|
23273
|
+
className: className ?? "w-full min-w-0",
|
|
23274
|
+
"data-chart-plot": ""
|
|
23275
|
+
})
|
|
22628
23276
|
});
|
|
22629
23277
|
}
|
|
22630
23278
|
//#endregion
|
|
@@ -22639,7 +23287,7 @@ function UPlotHost({ optionsKey, buildOptions, data, height, onInstance, classNa
|
|
|
22639
23287
|
* lives inside another control; there a bucket can be selected (`onSelect`)
|
|
22640
23288
|
* and every bucket is also a real button for keyboard and screen readers.
|
|
22641
23289
|
*/
|
|
22642
|
-
function BarSeries({ title, layers, buckets, bucketMs, range, unit = null, formatValue, height, stacked, showTotal = false, syncKey, pending = false, emptyMessage, actions, footer, windowToggle, compact = false, minBarFraction = 0, selectedIndex = null, onSelect, bucketLabel, bucketTestId, className }) {
|
|
23290
|
+
function BarSeries({ title, layers, buckets, bucketMs, range, unit = null, formatValue, displayPreference, height, stacked, showTotal = false, syncKey, pending = false, emptyMessage, actions, footer, windowToggle, compact = false, minBarFraction = 0, selectedIndex = null, onSelect, bucketLabel, bucketTestId, className }) {
|
|
22643
23291
|
const t = useUiText();
|
|
22644
23292
|
const plotHeight = height ?? (compact ? 24 : 100);
|
|
22645
23293
|
const isStacked = stacked ?? layers.length > 1;
|
|
@@ -22651,6 +23299,37 @@ function BarSeries({ title, layers, buckets, bucketMs, range, unit = null, forma
|
|
|
22651
23299
|
]);
|
|
22652
23300
|
const totals = (0, react$1.useMemo)(() => bucketTotals(buckets, layers.length), [buckets, layers.length]);
|
|
22653
23301
|
const [cursorIdx, setCursorIdx] = (0, react$1.useState)(null);
|
|
23302
|
+
const tooltip = (0, react$1.useMemo)(() => ({
|
|
23303
|
+
xMin: range.from,
|
|
23304
|
+
xMax: range.to,
|
|
23305
|
+
stops: buckets.map((bucket) => bucket.t),
|
|
23306
|
+
resolve: (dataX) => {
|
|
23307
|
+
const index = bucketSelectionIndex(buckets, bucketMs, dataX);
|
|
23308
|
+
if (index === null) return null;
|
|
23309
|
+
const bucket = buckets[index];
|
|
23310
|
+
if (bucket === void 0) return null;
|
|
23311
|
+
return barTooltipBody({
|
|
23312
|
+
rangeLabel: barRangeLabel(bucket.t, bucketMs, range.to - range.from),
|
|
23313
|
+
preference: displayPreference,
|
|
23314
|
+
layers: layers.map((layer, li) => ({
|
|
23315
|
+
id: layer.id,
|
|
23316
|
+
label: layer.label,
|
|
23317
|
+
value: bucket.values[li] ?? null,
|
|
23318
|
+
unit
|
|
23319
|
+
}))
|
|
23320
|
+
});
|
|
23321
|
+
},
|
|
23322
|
+
onDataX: (dataX) => {
|
|
23323
|
+
setCursorIdx(dataX === null ? null : bucketSelectionIndex(buckets, bucketMs, dataX));
|
|
23324
|
+
}
|
|
23325
|
+
}), [
|
|
23326
|
+
buckets,
|
|
23327
|
+
bucketMs,
|
|
23328
|
+
range,
|
|
23329
|
+
layers,
|
|
23330
|
+
unit,
|
|
23331
|
+
displayPreference
|
|
23332
|
+
]);
|
|
22654
23333
|
const live = (0, react$1.useRef)({
|
|
22655
23334
|
buckets,
|
|
22656
23335
|
tops,
|
|
@@ -22813,7 +23492,10 @@ function BarSeries({ title, layers, buckets, bucketMs, range, unit = null, forma
|
|
|
22813
23492
|
optionsKey,
|
|
22814
23493
|
buildOptions,
|
|
22815
23494
|
data,
|
|
22816
|
-
height: plotHeight
|
|
23495
|
+
height: plotHeight,
|
|
23496
|
+
tooltip,
|
|
23497
|
+
kind: "bars",
|
|
23498
|
+
label: title
|
|
22817
23499
|
});
|
|
22818
23500
|
if (compact) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
22819
23501
|
className: cn("relative min-w-0", className),
|
|
@@ -66238,10 +66920,6 @@ function RetrainReviewBadge({ kind, approximate = false, className, ...props })
|
|
|
66238
66920
|
}
|
|
66239
66921
|
//#endregion
|
|
66240
66922
|
//#region src/composites/charts/time-series-model.ts
|
|
66241
|
-
/**
|
|
66242
|
-
* The pure half of `TimeSeriesChart`: which series go on which value axis,
|
|
66243
|
-
* the aligned data uPlot draws, and what the readout says at an index.
|
|
66244
|
-
*/
|
|
66245
66923
|
/** The unit key a series lives on (`''` for no / unknown unit). */
|
|
66246
66924
|
function unitKey(unit) {
|
|
66247
66925
|
return unit ?? "";
|
|
@@ -66309,7 +66987,39 @@ function latestValues(model) {
|
|
|
66309
66987
|
* for the cross-device History page, where the reader chose the series.
|
|
66310
66988
|
*/
|
|
66311
66989
|
var DEFAULT_HEIGHT = 120;
|
|
66312
|
-
function
|
|
66990
|
+
function seriesSpan(range, xs) {
|
|
66991
|
+
if (range !== void 0) return range.to - range.from;
|
|
66992
|
+
if (xs.length < 2) return 0;
|
|
66993
|
+
return (xs[xs.length - 1] ?? 0) - (xs[0] ?? 0);
|
|
66994
|
+
}
|
|
66995
|
+
function seriesTooltip(input) {
|
|
66996
|
+
const span = seriesSpan(input.range, input.model.xs);
|
|
66997
|
+
return {
|
|
66998
|
+
xMin: input.range?.from ?? input.model.xs[0] ?? 0,
|
|
66999
|
+
xMax: input.range?.to ?? input.model.xs.at(-1) ?? 0,
|
|
67000
|
+
stops: input.model.xs,
|
|
67001
|
+
resolve: (dataX) => {
|
|
67002
|
+
const index = nearestPointIndex(input.model.xs, dataX);
|
|
67003
|
+
if (index === null) return null;
|
|
67004
|
+
const at = input.model.xs[index];
|
|
67005
|
+
if (at === void 0) return null;
|
|
67006
|
+
const values = readoutAt(input.model, index);
|
|
67007
|
+
return lineTooltipBody({
|
|
67008
|
+
timeLabel: formatCursorTime(at, span),
|
|
67009
|
+
preference: input.preference,
|
|
67010
|
+
series: input.series.map((sample, i) => ({
|
|
67011
|
+
id: sample.id,
|
|
67012
|
+
label: sample.label,
|
|
67013
|
+
value: values[i] ?? null,
|
|
67014
|
+
quantity: sample.quantity,
|
|
67015
|
+
unit: sample.unit ?? null
|
|
67016
|
+
}))
|
|
67017
|
+
});
|
|
67018
|
+
},
|
|
67019
|
+
onDataX: input.onDataX
|
|
67020
|
+
};
|
|
67021
|
+
}
|
|
67022
|
+
function TimeSeriesChart({ title, series, range, height = DEFAULT_HEIGHT, stacked = false, area = false, syncKey, formatValue, displayPreference, axisFor, emptyMessage, pending = false, actions, footer, windowToggle, className }) {
|
|
66313
67023
|
const t = useUiText();
|
|
66314
67024
|
const model = (0, react$1.useMemo)(() => buildTimeSeriesModel(series, stacked), [series, stacked]);
|
|
66315
67025
|
const [cursorIdx, setCursorIdx] = (0, react$1.useState)(null);
|
|
@@ -66452,6 +67162,20 @@ function TimeSeriesChart({ title, series, range, height = DEFAULT_HEIGHT, stacke
|
|
|
66452
67162
|
fmt,
|
|
66453
67163
|
specFor
|
|
66454
67164
|
]);
|
|
67165
|
+
const tooltip = (0, react$1.useMemo)(() => seriesTooltip({
|
|
67166
|
+
model,
|
|
67167
|
+
series,
|
|
67168
|
+
range,
|
|
67169
|
+
preference: displayPreference,
|
|
67170
|
+
onDataX: (dataX) => {
|
|
67171
|
+
setCursorIdx(dataX === null ? null : nearestPointIndex(model.xs, dataX));
|
|
67172
|
+
}
|
|
67173
|
+
}), [
|
|
67174
|
+
model,
|
|
67175
|
+
series,
|
|
67176
|
+
range,
|
|
67177
|
+
displayPreference
|
|
67178
|
+
]);
|
|
66455
67179
|
const shown = readoutAt(model, cursorIdx);
|
|
66456
67180
|
const legend = series.map((s, i) => {
|
|
66457
67181
|
const style = chartSeriesStyle(s.colorIndex);
|
|
@@ -66513,7 +67237,10 @@ function TimeSeriesChart({ title, series, range, height = DEFAULT_HEIGHT, stacke
|
|
|
66513
67237
|
optionsKey,
|
|
66514
67238
|
buildOptions,
|
|
66515
67239
|
data,
|
|
66516
|
-
height
|
|
67240
|
+
height,
|
|
67241
|
+
tooltip,
|
|
67242
|
+
kind: "lines",
|
|
67243
|
+
label: title
|
|
66517
67244
|
})
|
|
66518
67245
|
});
|
|
66519
67246
|
}
|
|
@@ -66532,6 +67259,33 @@ function TimeSeriesChart({ title, series, range, height = DEFAULT_HEIGHT, stacke
|
|
|
66532
67259
|
/** Pixel height of one row band — matches the `h-5` label cells. */
|
|
66533
67260
|
var ROW_PX = 20;
|
|
66534
67261
|
var AXIS_PX = 22;
|
|
67262
|
+
function stateStops(rows, from, to) {
|
|
67263
|
+
const found = new Set([from]);
|
|
67264
|
+
for (const row of rows) for (const segment of row.segments) if (segment.from >= from && segment.from < to) found.add(segment.from);
|
|
67265
|
+
return [...found].sort((a, b) => a - b);
|
|
67266
|
+
}
|
|
67267
|
+
function stateTooltip(rows, range) {
|
|
67268
|
+
const span = range.to - range.from;
|
|
67269
|
+
return {
|
|
67270
|
+
xMin: range.from,
|
|
67271
|
+
xMax: range.to,
|
|
67272
|
+
stops: stateStops(rows, range.from, range.to),
|
|
67273
|
+
resolve: (dataX) => stateTooltipBody({
|
|
67274
|
+
timeLabel: formatCursorTime(dataX, span),
|
|
67275
|
+
rows: rows.map((row) => {
|
|
67276
|
+
const hit = stateSpanAt(row.segments, dataX);
|
|
67277
|
+
const state = hit?.state ?? null;
|
|
67278
|
+
const stateLabel = state === null ? null : row.stateLabel?.(state) ?? state;
|
|
67279
|
+
return {
|
|
67280
|
+
id: row.id,
|
|
67281
|
+
label: row.label,
|
|
67282
|
+
stateLabel,
|
|
67283
|
+
spanMs: state === null ? null : hit?.spanMs ?? null
|
|
67284
|
+
};
|
|
67285
|
+
})
|
|
67286
|
+
})
|
|
67287
|
+
};
|
|
67288
|
+
}
|
|
66535
67289
|
/** The colour role of a state on a row: a categorical slot, or the recessive "off". */
|
|
66536
67290
|
function stateStyle(row, state) {
|
|
66537
67291
|
const base = row.colorIndex ?? 0;
|
|
@@ -66606,6 +67360,10 @@ function StateTimeline({ title, rows, range, syncKey, pending = false, emptyMess
|
|
|
66606
67360
|
}]
|
|
66607
67361
|
}
|
|
66608
67362
|
}), [optionsKey]);
|
|
67363
|
+
const tooltip = (0, react$1.useMemo)(() => ({
|
|
67364
|
+
...stateTooltip(rows, range),
|
|
67365
|
+
onDataX: (dataX) => setCursorT(dataX)
|
|
67366
|
+
}), [rows, range]);
|
|
66609
67367
|
const durations = (0, react$1.useMemo)(() => rows.map((r) => r.totals ?? timeInState(r.segments)), [rows]);
|
|
66610
67368
|
const summary = rows.map((row, i) => {
|
|
66611
67369
|
const parts = row.states.map((s) => {
|
|
@@ -66659,7 +67417,10 @@ function StateTimeline({ title, rows, range, syncKey, pending = false, emptyMess
|
|
|
66659
67417
|
optionsKey,
|
|
66660
67418
|
buildOptions,
|
|
66661
67419
|
data,
|
|
66662
|
-
height: rows.length * ROW_PX + AXIS_PX
|
|
67420
|
+
height: rows.length * ROW_PX + AXIS_PX,
|
|
67421
|
+
tooltip,
|
|
67422
|
+
kind: "states",
|
|
67423
|
+
label: title
|
|
66663
67424
|
})
|
|
66664
67425
|
})]
|
|
66665
67426
|
}),
|
|
@@ -66704,7 +67465,38 @@ function StateTimeline({ title, rows, range, syncKey, pending = false, emptyMess
|
|
|
66704
67465
|
* row (a training job's mAP per epoch). The x is whatever the caller orders
|
|
66705
67466
|
* by (an epoch, a time); the value next to it names what the line is.
|
|
66706
67467
|
*/
|
|
66707
|
-
function Sparkline({ points, ariaLabel, token = "chart-series-a", height = 32, widthClassName = "w-40", zeroBased = true, className }) {
|
|
67468
|
+
function Sparkline({ points, ariaLabel, token = "chart-series-a", height = 32, widthClassName = "w-40", zeroBased = true, unit = null, quantity, displayPreference, className }) {
|
|
67469
|
+
const tooltip = (0, react$1.useMemo)(() => {
|
|
67470
|
+
const xs = points.map((point) => point.x);
|
|
67471
|
+
return {
|
|
67472
|
+
xMin: xs[0] ?? 0,
|
|
67473
|
+
xMax: xs.at(-1) ?? 0,
|
|
67474
|
+
stops: xs,
|
|
67475
|
+
resolve: (dataX) => {
|
|
67476
|
+
const index = nearestPointIndex(xs, dataX);
|
|
67477
|
+
if (index === null) return null;
|
|
67478
|
+
const point = points[index];
|
|
67479
|
+
if (point === void 0) return null;
|
|
67480
|
+
return lineTooltipBody({
|
|
67481
|
+
timeLabel: String(point.x),
|
|
67482
|
+
preference: displayPreference,
|
|
67483
|
+
series: [{
|
|
67484
|
+
id: "value",
|
|
67485
|
+
label: ariaLabel,
|
|
67486
|
+
value: point.v,
|
|
67487
|
+
unit,
|
|
67488
|
+
quantity
|
|
67489
|
+
}]
|
|
67490
|
+
});
|
|
67491
|
+
}
|
|
67492
|
+
};
|
|
67493
|
+
}, [
|
|
67494
|
+
points,
|
|
67495
|
+
ariaLabel,
|
|
67496
|
+
unit,
|
|
67497
|
+
quantity,
|
|
67498
|
+
displayPreference
|
|
67499
|
+
]);
|
|
66708
67500
|
const data = (0, react$1.useMemo)(() => [points.map((p) => p.x), points.map((p) => p.v)], [points]);
|
|
66709
67501
|
const buildOptions = (0, react$1.useCallback)((_w, lib) => ({
|
|
66710
67502
|
legend: { show: false },
|
|
@@ -66740,7 +67532,10 @@ function Sparkline({ points, ariaLabel, token = "chart-series-a", height = 32, w
|
|
|
66740
67532
|
buildOptions,
|
|
66741
67533
|
data,
|
|
66742
67534
|
height,
|
|
66743
|
-
inline: true
|
|
67535
|
+
inline: true,
|
|
67536
|
+
tooltip,
|
|
67537
|
+
kind: "spark",
|
|
67538
|
+
label: ariaLabel
|
|
66744
67539
|
})
|
|
66745
67540
|
});
|
|
66746
67541
|
}
|
|
@@ -69781,6 +70576,7 @@ exports.useAddonsGetAutoUpdateSettings = useAddonsGetAutoUpdateSettings;
|
|
|
69781
70576
|
exports.useAddonsGetJob = useAddonsGetJob;
|
|
69782
70577
|
exports.useAddonsGetLastRestart = useAddonsGetLastRestart;
|
|
69783
70578
|
exports.useAddonsGetLogs = useAddonsGetLogs;
|
|
70579
|
+
exports.useAddonsGetPackageDocs = useAddonsGetPackageDocs;
|
|
69784
70580
|
exports.useAddonsGetVersions = useAddonsGetVersions;
|
|
69785
70581
|
exports.useAddonsInstallFromWorkspace = useAddonsInstallFromWorkspace;
|
|
69786
70582
|
exports.useAddonsInstallPackage = useAddonsInstallPackage;
|
|
@@ -70216,6 +71012,7 @@ exports.useFanControlSetPreset = useFanControlSetPreset;
|
|
|
70216
71012
|
exports.useFeatureProbeGetStatus = useFeatureProbeGetStatus;
|
|
70217
71013
|
exports.useFloodGetStatus = useFloodGetStatus;
|
|
70218
71014
|
exports.useGasGetStatus = useGasGetStatus;
|
|
71015
|
+
exports.useGroupCommandsGetStatus = useGroupCommandsGetStatus;
|
|
70219
71016
|
exports.useGroupStatusGetStatus = useGroupStatusGetStatus;
|
|
70220
71017
|
exports.useHeatingValveGetStatus = useHeatingValveGetStatus;
|
|
70221
71018
|
exports.useHistoryPanels = useHistoryPanels;
|