@flanksource/clicky-ui 0.2.4 → 0.2.6

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.
Files changed (189) hide show
  1. package/dist/components/Combobox.cjs +291 -0
  2. package/dist/components/Combobox.cjs.map +1 -0
  3. package/dist/components/Combobox.d.ts +59 -0
  4. package/dist/components/Combobox.d.ts.map +1 -0
  5. package/dist/components/Combobox.js +291 -0
  6. package/dist/components/Combobox.js.map +1 -0
  7. package/dist/components/FilterBar.cjs +153 -124
  8. package/dist/components/FilterBar.cjs.map +1 -1
  9. package/dist/components/FilterBar.d.ts +25 -1
  10. package/dist/components/FilterBar.d.ts.map +1 -1
  11. package/dist/components/FilterBar.js +153 -124
  12. package/dist/components/FilterBar.js.map +1 -1
  13. package/dist/components/FormatOptionsDropdown.cjs +62 -0
  14. package/dist/components/FormatOptionsDropdown.cjs.map +1 -0
  15. package/dist/components/FormatOptionsDropdown.d.ts +37 -0
  16. package/dist/components/FormatOptionsDropdown.d.ts.map +1 -0
  17. package/dist/components/FormatOptionsDropdown.js +62 -0
  18. package/dist/components/FormatOptionsDropdown.js.map +1 -0
  19. package/dist/components/SplitButton.cjs +87 -0
  20. package/dist/components/SplitButton.cjs.map +1 -0
  21. package/dist/components/SplitButton.d.ts +32 -0
  22. package/dist/components/SplitButton.d.ts.map +1 -0
  23. package/dist/components/SplitButton.js +87 -0
  24. package/dist/components/SplitButton.js.map +1 -0
  25. package/dist/components/icon-menu-picker.cjs +1 -1
  26. package/dist/components/icon-menu-picker.cjs.map +1 -1
  27. package/dist/components/icon-menu-picker.js +1 -1
  28. package/dist/components/icon-menu-picker.js.map +1 -1
  29. package/dist/components.cjs +7 -0
  30. package/dist/components.cjs.map +1 -1
  31. package/dist/components.d.ts +3 -0
  32. package/dist/components.d.ts.map +1 -1
  33. package/dist/components.js +7 -0
  34. package/dist/components.js.map +1 -1
  35. package/dist/data/Badge.d.ts +1 -1
  36. package/dist/data/ProgressBar.cjs +4 -2
  37. package/dist/data/ProgressBar.cjs.map +1 -1
  38. package/dist/data/ProgressBar.d.ts +1 -1
  39. package/dist/data/ProgressBar.d.ts.map +1 -1
  40. package/dist/data/ProgressBar.js +4 -2
  41. package/dist/data/ProgressBar.js.map +1 -1
  42. package/dist/data/StatusBreakdown.cjs +80 -0
  43. package/dist/data/StatusBreakdown.cjs.map +1 -0
  44. package/dist/data/StatusBreakdown.d.ts +36 -0
  45. package/dist/data/StatusBreakdown.d.ts.map +1 -0
  46. package/dist/data/StatusBreakdown.js +80 -0
  47. package/dist/data/StatusBreakdown.js.map +1 -0
  48. package/dist/data/TaskManager.cjs +162 -0
  49. package/dist/data/TaskManager.cjs.map +1 -0
  50. package/dist/data/TaskManager.d.ts +21 -0
  51. package/dist/data/TaskManager.d.ts.map +1 -0
  52. package/dist/data/TaskManager.js +162 -0
  53. package/dist/data/TaskManager.js.map +1 -0
  54. package/dist/data/TaskProgress.cjs +125 -0
  55. package/dist/data/TaskProgress.cjs.map +1 -0
  56. package/dist/data/TaskProgress.d.ts +20 -0
  57. package/dist/data/TaskProgress.d.ts.map +1 -0
  58. package/dist/data/TaskProgress.js +125 -0
  59. package/dist/data/TaskProgress.js.map +1 -0
  60. package/dist/data/TaskSnapshot.cjs +23 -0
  61. package/dist/data/TaskSnapshot.cjs.map +1 -0
  62. package/dist/data/TaskSnapshot.d.ts +49 -0
  63. package/dist/data/TaskSnapshot.d.ts.map +1 -0
  64. package/dist/data/TaskSnapshot.js +23 -0
  65. package/dist/data/TaskSnapshot.js.map +1 -0
  66. package/dist/data/TimeseriesGauge.cjs +144 -0
  67. package/dist/data/TimeseriesGauge.cjs.map +1 -0
  68. package/dist/data/TimeseriesGauge.d.ts +56 -0
  69. package/dist/data/TimeseriesGauge.d.ts.map +1 -0
  70. package/dist/data/TimeseriesGauge.js +144 -0
  71. package/dist/data/TimeseriesGauge.js.map +1 -0
  72. package/dist/data/TimeseriesPanel.axes.cjs +39 -0
  73. package/dist/data/TimeseriesPanel.axes.cjs.map +1 -0
  74. package/dist/data/TimeseriesPanel.axes.d.ts +28 -0
  75. package/dist/data/TimeseriesPanel.axes.d.ts.map +1 -0
  76. package/dist/data/TimeseriesPanel.axes.js +39 -0
  77. package/dist/data/TimeseriesPanel.axes.js.map +1 -0
  78. package/dist/data/TimeseriesPanel.cjs +324 -0
  79. package/dist/data/TimeseriesPanel.cjs.map +1 -0
  80. package/dist/data/TimeseriesPanel.d.ts +141 -0
  81. package/dist/data/TimeseriesPanel.d.ts.map +1 -0
  82. package/dist/data/TimeseriesPanel.js +324 -0
  83. package/dist/data/TimeseriesPanel.js.map +1 -0
  84. package/dist/data/Tree.cjs.map +1 -1
  85. package/dist/data/Tree.d.ts.map +1 -1
  86. package/dist/data/Tree.js.map +1 -1
  87. package/dist/data/TreeNode.cjs +44 -7
  88. package/dist/data/TreeNode.cjs.map +1 -1
  89. package/dist/data/TreeNode.d.ts +19 -1
  90. package/dist/data/TreeNode.d.ts.map +1 -1
  91. package/dist/data/TreeNode.js +44 -7
  92. package/dist/data/TreeNode.js.map +1 -1
  93. package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs +66 -44
  94. package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs.map +1 -1
  95. package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts +21 -1
  96. package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts.map +1 -1
  97. package/dist/data/diagnostics/DiagnosticsDetailPanel.js +65 -43
  98. package/dist/data/diagnostics/DiagnosticsDetailPanel.js.map +1 -1
  99. package/dist/data/diagnostics/DiagnosticsTree.cjs +2 -1
  100. package/dist/data/diagnostics/DiagnosticsTree.cjs.map +1 -1
  101. package/dist/data/diagnostics/DiagnosticsTree.js +2 -1
  102. package/dist/data/diagnostics/DiagnosticsTree.js.map +1 -1
  103. package/dist/data/diagnostics/FrameSourceWindow.cjs +37 -0
  104. package/dist/data/diagnostics/FrameSourceWindow.cjs.map +1 -0
  105. package/dist/data/diagnostics/FrameSourceWindow.d.ts +15 -0
  106. package/dist/data/diagnostics/FrameSourceWindow.d.ts.map +1 -0
  107. package/dist/data/diagnostics/FrameSourceWindow.js +37 -0
  108. package/dist/data/diagnostics/FrameSourceWindow.js.map +1 -0
  109. package/dist/data/diagnostics/JvmStackTrace.cjs +25 -12
  110. package/dist/data/diagnostics/JvmStackTrace.cjs.map +1 -1
  111. package/dist/data/diagnostics/JvmStackTrace.d.ts +8 -1
  112. package/dist/data/diagnostics/JvmStackTrace.d.ts.map +1 -1
  113. package/dist/data/diagnostics/JvmStackTrace.js +26 -13
  114. package/dist/data/diagnostics/JvmStackTrace.js.map +1 -1
  115. package/dist/data/diagnostics/ThreadCard.cjs +9 -2
  116. package/dist/data/diagnostics/ThreadCard.cjs.map +1 -1
  117. package/dist/data/diagnostics/ThreadCard.d.ts +3 -1
  118. package/dist/data/diagnostics/ThreadCard.d.ts.map +1 -1
  119. package/dist/data/diagnostics/ThreadCard.js +9 -2
  120. package/dist/data/diagnostics/ThreadCard.js.map +1 -1
  121. package/dist/data/diagnostics/jvm-stacktrace.cjs +10 -1
  122. package/dist/data/diagnostics/jvm-stacktrace.cjs.map +1 -1
  123. package/dist/data/diagnostics/jvm-stacktrace.d.ts.map +1 -1
  124. package/dist/data/diagnostics/jvm-stacktrace.js +10 -1
  125. package/dist/data/diagnostics/jvm-stacktrace.js.map +1 -1
  126. package/dist/data/diagnostics/utils.cjs +0 -12
  127. package/dist/data/diagnostics/utils.cjs.map +1 -1
  128. package/dist/data/diagnostics/utils.d.ts +1 -1
  129. package/dist/data/diagnostics/utils.d.ts.map +1 -1
  130. package/dist/data/diagnostics/utils.js +0 -12
  131. package/dist/data/diagnostics/utils.js.map +1 -1
  132. package/dist/data/task-status.cjs +108 -0
  133. package/dist/data/task-status.cjs.map +1 -0
  134. package/dist/data/task-status.d.ts +21 -0
  135. package/dist/data/task-status.d.ts.map +1 -0
  136. package/dist/data/task-status.js +108 -0
  137. package/dist/data/task-status.js.map +1 -0
  138. package/dist/data.cjs +27 -0
  139. package/dist/data.cjs.map +1 -1
  140. package/dist/data.d.ts +9 -1
  141. package/dist/data.d.ts.map +1 -1
  142. package/dist/data.js +27 -0
  143. package/dist/data.js.map +1 -1
  144. package/dist/hooks/use-task-run.cjs +137 -0
  145. package/dist/hooks/use-task-run.cjs.map +1 -0
  146. package/dist/hooks/use-task-run.d.ts +35 -0
  147. package/dist/hooks/use-task-run.d.ts.map +1 -0
  148. package/dist/hooks/use-task-run.js +137 -0
  149. package/dist/hooks/use-task-run.js.map +1 -0
  150. package/dist/hooks.cjs +3 -0
  151. package/dist/hooks.cjs.map +1 -1
  152. package/dist/hooks.d.ts +1 -0
  153. package/dist/hooks.d.ts.map +1 -1
  154. package/dist/hooks.js +3 -0
  155. package/dist/hooks.js.map +1 -1
  156. package/dist/index.cjs +43 -0
  157. package/dist/index.cjs.map +1 -1
  158. package/dist/index.js +43 -0
  159. package/dist/index.js.map +1 -1
  160. package/dist/lib/color.cjs +33 -0
  161. package/dist/lib/color.cjs.map +1 -0
  162. package/dist/lib/color.d.ts +10 -0
  163. package/dist/lib/color.d.ts.map +1 -0
  164. package/dist/lib/color.js +33 -0
  165. package/dist/lib/color.js.map +1 -0
  166. package/dist/lib/format.cjs +77 -0
  167. package/dist/lib/format.cjs.map +1 -0
  168. package/dist/lib/format.d.ts +30 -0
  169. package/dist/lib/format.d.ts.map +1 -0
  170. package/dist/lib/format.js +77 -0
  171. package/dist/lib/format.js.map +1 -0
  172. package/dist/overlay/DropdownMenu.cjs +18 -2
  173. package/dist/overlay/DropdownMenu.cjs.map +1 -1
  174. package/dist/overlay/DropdownMenu.d.ts +3 -1
  175. package/dist/overlay/DropdownMenu.d.ts.map +1 -1
  176. package/dist/overlay/DropdownMenu.js +18 -2
  177. package/dist/overlay/DropdownMenu.js.map +1 -1
  178. package/dist/rpc/types.cjs.map +1 -1
  179. package/dist/rpc/types.d.ts +4 -0
  180. package/dist/rpc/types.d.ts.map +1 -1
  181. package/dist/rpc/types.js.map +1 -1
  182. package/dist/styles.css +1 -1
  183. package/dist/utils.cjs +6 -0
  184. package/dist/utils.cjs.map +1 -1
  185. package/dist/utils.d.ts +1 -0
  186. package/dist/utils.d.ts.map +1 -1
  187. package/dist/utils.js +6 -0
  188. package/dist/utils.js.map +1 -1
  189. package/package.json +4 -1
@@ -0,0 +1,324 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const reactQuery = require("@tanstack/react-query");
5
+ const react = require("react");
6
+ const recharts = require("recharts");
7
+ const utils = require("../lib/utils.cjs");
8
+ const color = require("../lib/color.cjs");
9
+ const format = require("../lib/format.cjs");
10
+ const Modal = require("../overlay/Modal.cjs");
11
+ const ProgressBar = require("./ProgressBar.cjs");
12
+ const Icon = require("./Icon.cjs");
13
+ const TimeseriesPanel_axes = require("./TimeseriesPanel.axes.cjs");
14
+ const UiFullscreen = require("../icons/components/UiFullscreen.cjs");
15
+ const SERIES_PALETTE = [
16
+ "var(--chart-1, #3b82f6)",
17
+ "var(--chart-2, #ef4444)",
18
+ "var(--chart-3, #10b981)",
19
+ "var(--chart-4, #f59e0b)"
20
+ ];
21
+ function paletteColor(index) {
22
+ return SERIES_PALETTE[index % SERIES_PALETTE.length] ?? SERIES_PALETTE[0];
23
+ }
24
+ function resolveSeries(props) {
25
+ const base = props.baseUrl ?? "";
26
+ const list = props.series ?? (props.url ? [{ id: props.url }] : []);
27
+ return list.map((s, i) => ({
28
+ key: s.id,
29
+ url: base + s.id,
30
+ label: s.label ?? s.id,
31
+ color: s.color ?? paletteColor(i),
32
+ ...s.unit ?? props.unit ? { unit: s.unit ?? props.unit } : {},
33
+ ...s.transform ? { transform: s.transform } : {},
34
+ ...s.current !== void 0 ? { current: s.current } : {}
35
+ }));
36
+ }
37
+ function resolveBreakdown(series, rows, total) {
38
+ const withValue = series.map((s) => ({ s, value: s.current ?? TimeseriesPanel_axes.latestOf(rows, s.key) ?? 0 }));
39
+ const denom = total ?? withValue.reduce((acc, w) => acc + w.value, 0);
40
+ const items = withValue.map(({ s, value }) => ({
41
+ key: s.key,
42
+ label: s.label,
43
+ color: s.color,
44
+ ...s.unit ? { unit: s.unit } : {},
45
+ value,
46
+ percent: denom > 0 ? Math.min(100, Math.max(0, value / denom * 100)) : 0
47
+ }));
48
+ return { items, total: denom };
49
+ }
50
+ const defaultFetcher = async (url) => {
51
+ const res = await fetch(url);
52
+ if (!res.ok) throw new Error(`metrics request failed: ${res.status}`);
53
+ return res.json();
54
+ };
55
+ function formatClock(at) {
56
+ const date = new Date(at);
57
+ if (Number.isNaN(date.getTime())) return "";
58
+ return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
59
+ }
60
+ function mergeSeries(series, pointsBySeries) {
61
+ const rows = /* @__PURE__ */ new Map();
62
+ series.forEach((s, i) => {
63
+ for (const p of pointsBySeries[i] ?? []) {
64
+ const row = rows.get(p.at) ?? { at: p.at };
65
+ row[s.key] = s.transform ? s.transform(p.value) : p.value;
66
+ rows.set(p.at, row);
67
+ }
68
+ });
69
+ return [...rows.values()].sort((a, b) => a.at.localeCompare(b.at));
70
+ }
71
+ function TimeseriesPanel(props) {
72
+ var _a;
73
+ const {
74
+ title,
75
+ icon,
76
+ unit,
77
+ range = "1h",
78
+ refreshMs = 5e3,
79
+ height = 160,
80
+ variant = "area",
81
+ expandable = true,
82
+ total,
83
+ fetcher = defaultFetcher,
84
+ className
85
+ } = props;
86
+ const isBreakdown = variant === "breakdown";
87
+ const chartVariant = isBreakdown ? "area" : variant;
88
+ const expandVariant = props.expandVariant ?? chartVariant;
89
+ const [expanded, setExpanded] = react.useState(false);
90
+ const series = react.useMemo(() => resolveSeries(props), [props]);
91
+ const requestUrls = react.useMemo(
92
+ () => series.map((s) => {
93
+ const u = new URL(s.url, window.location.origin);
94
+ if (range) u.searchParams.set("since", range);
95
+ return u.pathname + u.search;
96
+ }),
97
+ [series, range]
98
+ );
99
+ const results = reactQuery.useQueries({
100
+ queries: requestUrls.map((requestUrl) => ({
101
+ queryKey: ["timeseries", requestUrl],
102
+ queryFn: () => fetcher(requestUrl),
103
+ refetchInterval: refreshMs > 0 ? refreshMs : false,
104
+ staleTime: 0,
105
+ retry: 0
106
+ }))
107
+ });
108
+ const isError = results.some((r) => r.isError);
109
+ const error = (_a = results.find((r) => r.isError)) == null ? void 0 : _a.error;
110
+ const isLoading = results.some((r) => r.isLoading) && results.every((r) => !r.data);
111
+ const pointsBySeries = results.map((r) => {
112
+ var _a2;
113
+ return (_a2 = r.data) == null ? void 0 : _a2.points;
114
+ });
115
+ const rows = react.useMemo(() => mergeSeries(series, pointsBySeries), [series, pointsBySeries]);
116
+ const hasMirrored = series.some((s) => s.transform);
117
+ const breakdown = react.useMemo(
118
+ () => isBreakdown ? resolveBreakdown(series, rows, total) : null,
119
+ [isBreakdown, series, rows, total]
120
+ );
121
+ const canExpand = expandable && rows.length >= 2;
122
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex flex-col rounded-lg border border-border bg-card p-3", className), children: [
123
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 text-xs text-muted-foreground", children: [
124
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex min-w-0 items-center gap-1.5", children: [
125
+ icon ? /* @__PURE__ */ jsxRuntime.jsx(PanelIcon, { icon }) : null,
126
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium text-foreground", children: title })
127
+ ] }),
128
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex shrink-0 items-center gap-1.5", children: [
129
+ breakdown ? /* @__PURE__ */ jsxRuntime.jsx(BreakdownReadout, { total: breakdown.total, unit, items: breakdown.items }) : /* @__PURE__ */ jsxRuntime.jsx(LatestReadout, { series, rows }),
130
+ canExpand ? /* @__PURE__ */ jsxRuntime.jsx(
131
+ "button",
132
+ {
133
+ type: "button",
134
+ "aria-label": "Expand chart",
135
+ onClick: () => setExpanded(true),
136
+ className: "text-muted-foreground transition-colors hover:text-foreground",
137
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiFullscreen.UiFullscreen, width: 14, height: 14 })
138
+ }
139
+ ) : null
140
+ ] })
141
+ ] }),
142
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", style: breakdown ? void 0 : { height }, children: breakdown ? /* @__PURE__ */ jsxRuntime.jsx(BreakdownView, { total: breakdown.total, unit, items: breakdown.items }) : isError ? /* @__PURE__ */ jsxRuntime.jsx(PanelMessage, { children: (error == null ? void 0 : error.message) ?? "Failed to load metric" }) : isLoading ? /* @__PURE__ */ jsxRuntime.jsx(PanelMessage, { children: "Loading…" }) : rows.length < 2 ? /* @__PURE__ */ jsxRuntime.jsx(PanelMessage, { children: "Collecting data…" }) : /* @__PURE__ */ jsxRuntime.jsx(Chart, { rows, series, variant: chartVariant, unit, showZeroLine: hasMirrored }) }),
143
+ !breakdown && !isError && !isLoading && rows.length >= 2 && series.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(ChartLegend, { series, rows }) : null,
144
+ canExpand ? /* @__PURE__ */ jsxRuntime.jsxs(Modal.Modal, { open: expanded, onClose: () => setExpanded(false), title, size: "xl", children: [
145
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: 360 }, children: /* @__PURE__ */ jsxRuntime.jsx(Chart, { rows, series, variant: expandVariant, unit, showZeroLine: hasMirrored }) }),
146
+ series.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(ChartLegend, { series, rows }) : null
147
+ ] }) : null
148
+ ] });
149
+ }
150
+ function LatestReadout({ series, rows }) {
151
+ const only = series.length === 1 ? series[0] : void 0;
152
+ if (!only) return null;
153
+ const v = TimeseriesPanel_axes.latestOf(rows, only.key);
154
+ if (v === void 0) return null;
155
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: format.formatUnit(Math.abs(v), only.unit) });
156
+ }
157
+ function ChartLegend({ series, rows }) {
158
+ return /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-1.5 flex flex-wrap gap-x-3 gap-y-1 text-xs text-muted-foreground", children: series.map((s) => {
159
+ const v = TimeseriesPanel_axes.latestOf(rows, s.key);
160
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "inline-flex min-w-0 items-center gap-1.5", children: [
161
+ /* @__PURE__ */ jsxRuntime.jsx(
162
+ "span",
163
+ {
164
+ className: "h-2 w-2 shrink-0 rounded-full",
165
+ style: { backgroundColor: color.resolveCssColor(s.color) },
166
+ "aria-hidden": true
167
+ }
168
+ ),
169
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: s.label }),
170
+ v !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: format.formatUnit(Math.abs(v), s.unit) }) : null
171
+ ] }, s.key);
172
+ }) });
173
+ }
174
+ function BreakdownReadout({
175
+ total,
176
+ unit,
177
+ items
178
+ }) {
179
+ const used = items.reduce((acc, it) => acc + it.value, 0);
180
+ const pct = total > 0 ? Math.min(100, used / total * 100) : 0;
181
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono tabular-nums", children: [
182
+ format.formatUnit(used, unit),
183
+ " / ",
184
+ format.formatUnit(total, unit),
185
+ " · ",
186
+ pct.toFixed(0),
187
+ "%"
188
+ ] });
189
+ }
190
+ function BreakdownView({
191
+ total,
192
+ unit,
193
+ items
194
+ }) {
195
+ const segments = items.map((it) => ({
196
+ count: it.value,
197
+ color: it.color,
198
+ label: it.label
199
+ }));
200
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
201
+ /* @__PURE__ */ jsxRuntime.jsx(ProgressBar.ProgressBar, { segments, total: total > 0 ? total : 1, height: "h-3" }),
202
+ items.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "grid grid-cols-1 gap-y-1 text-xs", children: items.map((it) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center gap-2", children: [
203
+ /* @__PURE__ */ jsxRuntime.jsx(
204
+ "span",
205
+ {
206
+ className: "h-2 w-2 shrink-0 rounded-full",
207
+ style: { backgroundColor: color.resolveCssColor(it.color) },
208
+ "aria-hidden": true
209
+ }
210
+ ),
211
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate font-mono text-muted-foreground", children: it.label }),
212
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono tabular-nums text-foreground", children: format.formatUnit(it.value, it.unit ?? unit) }),
213
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "w-10 text-right font-mono tabular-nums text-muted-foreground", children: [
214
+ it.percent.toFixed(0),
215
+ "%"
216
+ ] })
217
+ ] }, it.key)) }) : null
218
+ ] });
219
+ }
220
+ function Chart({
221
+ rows,
222
+ series,
223
+ variant,
224
+ unit,
225
+ showZeroLine
226
+ }) {
227
+ const stacked = variant === "stacked";
228
+ const seriesByKey = new Map(series.map((s) => [s.key, s]));
229
+ const strokeOf = (s) => color.resolveCssColor(s.color);
230
+ const { axes: yAxes, axisOfKey } = react.useMemo(() => TimeseriesPanel_axes.assignAxes(series), [series]);
231
+ const axisOf = (key) => axisOfKey.get(key) ?? "left";
232
+ const axes = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
233
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeOpacity: 0.12, vertical: false }),
234
+ /* @__PURE__ */ jsxRuntime.jsx(
235
+ recharts.XAxis,
236
+ {
237
+ dataKey: "at",
238
+ tickFormatter: formatClock,
239
+ tick: { fontSize: 10 },
240
+ stroke: "currentColor",
241
+ strokeOpacity: 0.35,
242
+ minTickGap: 32
243
+ }
244
+ ),
245
+ yAxes.map((ax) => /* @__PURE__ */ jsxRuntime.jsx(
246
+ recharts.YAxis,
247
+ {
248
+ yAxisId: ax.id,
249
+ orientation: ax.orientation,
250
+ tick: { fontSize: 10 },
251
+ stroke: "currentColor",
252
+ strokeOpacity: 0.35,
253
+ width: 40,
254
+ tickFormatter: (v) => format.formatUnit(Math.abs(v), ax.unit)
255
+ },
256
+ ax.id
257
+ )),
258
+ /* @__PURE__ */ jsxRuntime.jsx(
259
+ recharts.Tooltip,
260
+ {
261
+ labelFormatter: (label) => formatClock(String(label)),
262
+ formatter: (value, name) => {
263
+ const s = seriesByKey.get(String(name));
264
+ return [format.formatUnit(Math.abs(Number(value) || 0), (s == null ? void 0 : s.unit) ?? unit), (s == null ? void 0 : s.label) ?? String(name)];
265
+ },
266
+ contentStyle: { fontSize: 12 }
267
+ }
268
+ ),
269
+ showZeroLine ? /* @__PURE__ */ jsxRuntime.jsx(recharts.ReferenceLine, { yAxisId: "left", y: 0, stroke: "currentColor", strokeOpacity: 0.35 }) : null
270
+ ] });
271
+ return /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: variant === "line" ? /* @__PURE__ */ jsxRuntime.jsxs(recharts.LineChart, { data: rows, margin: { top: 4, right: 8, bottom: 0, left: 0 }, children: [
272
+ axes,
273
+ series.map((s) => /* @__PURE__ */ jsxRuntime.jsx(
274
+ recharts.Line,
275
+ {
276
+ yAxisId: axisOf(s.key),
277
+ type: "monotone",
278
+ dataKey: s.key,
279
+ name: s.key,
280
+ stroke: strokeOf(s),
281
+ strokeWidth: 2,
282
+ dot: false,
283
+ connectNulls: true,
284
+ isAnimationActive: false
285
+ },
286
+ s.key
287
+ ))
288
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(recharts.AreaChart, { data: rows, margin: { top: 4, right: 8, bottom: 0, left: 0 }, children: [
289
+ axes,
290
+ series.map((s) => /* @__PURE__ */ jsxRuntime.jsx(
291
+ recharts.Area,
292
+ {
293
+ yAxisId: axisOf(s.key),
294
+ type: "monotone",
295
+ dataKey: s.key,
296
+ name: s.key,
297
+ stroke: strokeOf(s),
298
+ fill: strokeOf(s),
299
+ ...stacked ? { stackId: `stack-${axisOf(s.key)}` } : {},
300
+ fillOpacity: stacked ? 0.7 : 0.12,
301
+ strokeWidth: stacked ? 0 : 2,
302
+ connectNulls: true,
303
+ isAnimationActive: false
304
+ },
305
+ s.key
306
+ ))
307
+ ] }) });
308
+ }
309
+ function PanelMessage({ children }) {
310
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center text-xs text-muted-foreground", children });
311
+ }
312
+ function PanelIcon({ icon }) {
313
+ if (typeof icon === "string") {
314
+ return /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { name: icon, width: 16, height: 16, className: "text-muted-foreground" });
315
+ }
316
+ return /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon, width: 16, height: 16, className: "text-muted-foreground" });
317
+ }
318
+ exports.assignAxes = TimeseriesPanel_axes.assignAxes;
319
+ exports.latestOf = TimeseriesPanel_axes.latestOf;
320
+ exports.TimeseriesPanel = TimeseriesPanel;
321
+ exports.mergeSeries = mergeSeries;
322
+ exports.resolveBreakdown = resolveBreakdown;
323
+ exports.resolveSeries = resolveSeries;
324
+ //# sourceMappingURL=TimeseriesPanel.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimeseriesPanel.cjs","sources":["../../src/data/TimeseriesPanel.tsx"],"sourcesContent":["import { useQueries } from \"@tanstack/react-query\";\nimport { useMemo, useState } from \"react\";\nimport {\n Area,\n AreaChart,\n CartesianGrid,\n Line,\n LineChart,\n ReferenceLine,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { cn } from \"../lib/utils\";\nimport { resolveCssColor } from \"../lib/color\";\nimport { formatUnit } from \"../lib/format\";\nimport { Modal } from \"../overlay/Modal\";\nimport { ProgressBar, type ProgressSegment } from \"./ProgressBar\";\nimport { UiFullscreen } from \"../icons\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport { assignAxes, latestOf } from \"./TimeseriesPanel.axes\";\n\nexport { assignAxes, latestOf } from \"./TimeseriesPanel.axes\";\nexport type { AxisAssignment, AxisOrientation, AxisSpec } from \"./TimeseriesPanel.axes\";\n\nexport interface TimeseriesPoint {\n at: string;\n value: number;\n}\n\nexport interface TimeseriesResponse {\n id: string;\n points: TimeseriesPoint[];\n}\n\n/** One metric plotted on a panel. Its request URL is `baseUrl + id`. */\nexport interface TimeseriesSeries {\n /** Metric id appended to the panel's baseUrl, e.g. \"sqlserver.iops.read\". */\n id: string;\n /** Tooltip/legend label; defaults to the id. */\n label?: string;\n /**\n * Maps each point's value before plotting — e.g. writes pass `(v) => -v` to\n * render below the zero axis. Identity when omitted.\n */\n transform?: (value: number) => number;\n /**\n * Stroke/fill override as a CSS color value (e.g. \"#10b981\", \"var(--chart-3)\")\n * or a Tailwind color class (e.g. \"bg-emerald-500\"); Tailwind classes are\n * resolved to a CSS value for the chart. Defaults cycle a small palette by index.\n */\n color?: string;\n /**\n * Grafana-style unit key for this series' readout/tooltip (e.g. \"percent\",\n * \"short\"). Falls back to the panel-level `unit`. Lets one panel mix units\n * (e.g. a percent series alongside a plain-count series).\n */\n unit?: string;\n /**\n * Instantaneous value for the `breakdown` variant — the size of this series'\n * bar segment and its legend readout. When omitted, the breakdown falls back\n * to the latest polled point. Ignored by the time-series variants.\n */\n current?: number;\n}\n\nexport interface TimeseriesPanelProps {\n /**\n * Single-metric endpoint returning a TimeseriesResponse,\n * e.g. \"/api/v1/metrics/sqlserver.cpu\". Mutually exclusive with baseUrl/series.\n */\n url?: string;\n /** Common prefix for a multi-series panel; each series requests `baseUrl + series.id`. */\n baseUrl?: string;\n /** Metrics to plot on one chart. Provide together with baseUrl. */\n series?: TimeseriesSeries[];\n title: string;\n /** Iconify name or static icon component shown beside the title. */\n icon?: string | StaticIconComponent;\n /**\n * Default Grafana-style unit key for the panel (e.g. \"bytes\", \"percent\",\n * \"ms\", \"short\"); each series may override it. Unknown strings are appended\n * as a raw suffix (so \"%\" still works). Used for the readout, axis, and tooltip.\n */\n unit?: string;\n /** Look-back window passed as ?since=; defaults to \"1h\". */\n range?: string;\n /** Poll interval in ms; defaults to 5000. Pass 0 to disable polling. */\n refreshMs?: number;\n /** Chart height in px; defaults to 160. */\n height?: number;\n /**\n * \"area\" (overlaid, default), \"line\", \"stacked\" (areas sum vertically), or\n * \"breakdown\" (an instantaneous stacked bar + legend; the expand modal still\n * shows a time-series graph of the polled history).\n */\n variant?: \"area\" | \"line\" | \"stacked\" | \"breakdown\";\n /**\n * Chart variant rendered in the expand modal. Defaults to `variant` for the\n * time-series variants and to \"area\" for `breakdown` (which has no inline chart).\n */\n expandVariant?: \"area\" | \"line\" | \"stacked\";\n /**\n * Denominator for the `breakdown` bar and per-segment percentages. Defaults to\n * the sum of the series' `current` values when omitted.\n */\n total?: number;\n /** Show a header button that opens the chart larger in a modal. Default true. */\n expandable?: boolean;\n /** Override the default fetch (e.g. to route through an app's API client). */\n fetcher?: (url: string) => Promise<TimeseriesResponse>;\n className?: string;\n}\n\nconst SERIES_PALETTE = [\n \"var(--chart-1, #3b82f6)\",\n \"var(--chart-2, #ef4444)\",\n \"var(--chart-3, #10b981)\",\n \"var(--chart-4, #f59e0b)\",\n] as const;\n\nfunction paletteColor(index: number): string {\n return SERIES_PALETTE[index % SERIES_PALETTE.length] ?? SERIES_PALETTE[0];\n}\n\n/** A series with its full request URL, resolved label, color, and unit. Exported for tests. */\nexport interface ResolvedSeries {\n /** Stable key used as the merged-row field and recharts dataKey. */\n key: string;\n url: string;\n label: string;\n color: string;\n /** Resolved Grafana unit key: the series' own unit, else the panel default. */\n unit?: string;\n transform?: (value: number) => number;\n /** Instantaneous value used by the `breakdown` variant. */\n current?: number;\n}\n\n/** Normalises the single-url and baseUrl+series forms to one list. Exported for tests. */\nexport function resolveSeries(props: TimeseriesPanelProps): ResolvedSeries[] {\n const base = props.baseUrl ?? \"\";\n const list = props.series ?? (props.url ? [{ id: props.url }] : []);\n return list.map((s, i) => ({\n key: s.id,\n url: base + s.id,\n label: s.label ?? s.id,\n color: s.color ?? paletteColor(i),\n ...(s.unit ?? props.unit ? { unit: s.unit ?? props.unit } : {}),\n ...(s.transform ? { transform: s.transform } : {}),\n ...(s.current !== undefined ? { current: s.current } : {}),\n }));\n}\n\n/** One legend/bar row of the breakdown variant: resolved value, color, label, unit, percentage. */\nexport interface BreakdownItem {\n key: string;\n label: string;\n color: string;\n unit?: string;\n value: number;\n /** Share of `total`, clamped to 0..100. */\n percent: number;\n}\n\n/**\n * Derives the breakdown bar segments and per-item percentages from the resolved\n * series' `current` values. `total` defaults to the sum of those values; the\n * percentage is each value's share of that denominator, clamped to 0..100.\n * Latest polled point fills in for a series with no explicit `current`. Pure —\n * exported for tests.\n */\nexport function resolveBreakdown(\n series: ResolvedSeries[],\n rows: MergedRow[],\n total?: number,\n): { items: BreakdownItem[]; total: number } {\n const withValue = series.map((s) => ({ s, value: s.current ?? latestOf(rows, s.key) ?? 0 }));\n const denom = total ?? withValue.reduce((acc, w) => acc + w.value, 0);\n const items = withValue.map(({ s, value }) => ({\n key: s.key,\n label: s.label,\n color: s.color,\n ...(s.unit ? { unit: s.unit } : {}),\n value,\n percent: denom > 0 ? Math.min(100, Math.max(0, (value / denom) * 100)) : 0,\n }));\n return { items, total: denom };\n}\n\nconst defaultFetcher = async (url: string): Promise<TimeseriesResponse> => {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`metrics request failed: ${res.status}`);\n return res.json();\n};\n\nfunction formatClock(at: string): string {\n const date = new Date(at);\n if (Number.isNaN(date.getTime())) return \"\";\n return date.toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" });\n}\n\n/**\n * One row of the merged chart data: the timestamp in `at` plus each series'\n * value under its key. Values are absent (undefined) where a series has no\n * point at that timestamp.\n */\nexport interface MergedRow {\n at: string;\n [seriesKey: string]: string | number | undefined;\n}\n\n/**\n * Joins per-series point lists into rows keyed by timestamp and applies each\n * series' transform. Timestamps present in any series produce a row; missing\n * values are left undefined so recharts gaps the line rather than drawing zero.\n */\nexport function mergeSeries(\n series: ResolvedSeries[],\n pointsBySeries: (TimeseriesPoint[] | undefined)[],\n): MergedRow[] {\n const rows = new Map<string, MergedRow>();\n series.forEach((s, i) => {\n for (const p of pointsBySeries[i] ?? []) {\n const row = rows.get(p.at) ?? { at: p.at };\n row[s.key] = s.transform ? s.transform(p.value) : p.value;\n rows.set(p.at, row);\n }\n });\n return [...rows.values()].sort((a, b) => a.at.localeCompare(b.at));\n}\n\nexport function TimeseriesPanel(props: TimeseriesPanelProps) {\n const {\n title,\n icon,\n unit,\n range = \"1h\",\n refreshMs = 5000,\n height = 160,\n variant = \"area\",\n expandable = true,\n total,\n fetcher = defaultFetcher,\n className,\n } = props;\n const isBreakdown = variant === \"breakdown\";\n // The inline chart (non-breakdown only) and the expand modal both need a\n // time-series variant; breakdown has no inline chart and expands to \"area\".\n const chartVariant: ChartVariant = isBreakdown ? \"area\" : variant;\n const expandVariant = props.expandVariant ?? chartVariant;\n\n const [expanded, setExpanded] = useState(false);\n\n const series = useMemo(() => resolveSeries(props), [props]);\n const requestUrls = useMemo(\n () =>\n series.map((s) => {\n const u = new URL(s.url, window.location.origin);\n if (range) u.searchParams.set(\"since\", range);\n return u.pathname + u.search;\n }),\n [series, range],\n );\n\n const results = useQueries({\n queries: requestUrls.map((requestUrl) => ({\n queryKey: [\"timeseries\", requestUrl],\n queryFn: () => fetcher(requestUrl),\n refetchInterval: refreshMs > 0 ? refreshMs : false,\n staleTime: 0,\n retry: 0,\n })),\n });\n\n const isError = results.some((r) => r.isError);\n const error = results.find((r) => r.isError)?.error;\n const isLoading = results.some((r) => r.isLoading) && results.every((r) => !r.data);\n const pointsBySeries = results.map((r) => r.data?.points);\n const rows = useMemo(() => mergeSeries(series, pointsBySeries), [series, pointsBySeries]);\n const hasMirrored = series.some((s) => s.transform);\n const breakdown = useMemo(\n () => (isBreakdown ? resolveBreakdown(series, rows, total) : null),\n [isBreakdown, series, rows, total],\n );\n\n // Breakdown renders instantly from `current`; the modal still needs polled\n // history. Other variants gate on having ≥2 points to draw a line.\n const canExpand = expandable && rows.length >= 2;\n\n return (\n <div className={cn(\"flex flex-col rounded-lg border border-border bg-card p-3\", className)}>\n <div className=\"flex items-center justify-between gap-2 text-xs text-muted-foreground\">\n <span className=\"inline-flex min-w-0 items-center gap-1.5\">\n {icon ? <PanelIcon icon={icon} /> : null}\n <span className=\"truncate font-medium text-foreground\">{title}</span>\n </span>\n <span className=\"inline-flex shrink-0 items-center gap-1.5\">\n {breakdown ? (\n <BreakdownReadout total={breakdown.total} unit={unit} items={breakdown.items} />\n ) : (\n <LatestReadout series={series} rows={rows} />\n )}\n {canExpand ? (\n <button\n type=\"button\"\n aria-label=\"Expand chart\"\n onClick={() => setExpanded(true)}\n className=\"text-muted-foreground transition-colors hover:text-foreground\"\n >\n <Icon icon={UiFullscreen} width={14} height={14} />\n </button>\n ) : null}\n </span>\n </div>\n <div className=\"mt-2\" style={breakdown ? undefined : { height }}>\n {breakdown ? (\n <BreakdownView total={breakdown.total} unit={unit} items={breakdown.items} />\n ) : isError ? (\n <PanelMessage>{(error as Error)?.message ?? \"Failed to load metric\"}</PanelMessage>\n ) : isLoading ? (\n <PanelMessage>Loading…</PanelMessage>\n ) : rows.length < 2 ? (\n <PanelMessage>Collecting data…</PanelMessage>\n ) : (\n <Chart rows={rows} series={series} variant={chartVariant} unit={unit} showZeroLine={hasMirrored} />\n )}\n </div>\n {!breakdown && !isError && !isLoading && rows.length >= 2 && series.length > 1 ? (\n <ChartLegend series={series} rows={rows} />\n ) : null}\n {canExpand ? (\n <Modal open={expanded} onClose={() => setExpanded(false)} title={title} size=\"xl\">\n <div style={{ height: 360 }}>\n <Chart rows={rows} series={series} variant={expandVariant} unit={unit} showZeroLine={hasMirrored} />\n </div>\n {series.length > 1 ? <ChartLegend series={series} rows={rows} /> : null}\n </Modal>\n ) : null}\n </div>\n );\n}\n\n/**\n * Header readout for a single-series panel: the latest value, formatted with the\n * series' unit. Multi-series panels render the per-series values in {@link ChartLegend}\n * below the chart instead, so this returns null for them to avoid overlapping the title.\n */\nfunction LatestReadout({ series, rows }: { series: ResolvedSeries[]; rows: MergedRow[] }) {\n const only = series.length === 1 ? series[0] : undefined;\n if (!only) return null;\n const v = latestOf(rows, only.key);\n if (v === undefined) return null;\n return <span className=\"tabular-nums\">{formatUnit(Math.abs(v), only.unit)}</span>;\n}\n\n/** Wrapping color-keyed legend for the time-series variants: dot + label + latest value. */\nfunction ChartLegend({ series, rows }: { series: ResolvedSeries[]; rows: MergedRow[] }) {\n return (\n <ul className=\"mt-1.5 flex flex-wrap gap-x-3 gap-y-1 text-xs text-muted-foreground\">\n {series.map((s) => {\n const v = latestOf(rows, s.key);\n return (\n <li key={s.key} className=\"inline-flex min-w-0 items-center gap-1.5\">\n <span\n className=\"h-2 w-2 shrink-0 rounded-full\"\n style={{ backgroundColor: resolveCssColor(s.color) }}\n aria-hidden\n />\n <span className=\"truncate text-foreground\">{s.label}</span>\n {v !== undefined ? (\n <span className=\"tabular-nums\">{formatUnit(Math.abs(v), s.unit)}</span>\n ) : null}\n </li>\n );\n })}\n </ul>\n );\n}\n\n/** Breakdown header readout: `used / total · pct%`, where used is the sum of segments. */\nfunction BreakdownReadout({\n total,\n unit,\n items,\n}: {\n total: number;\n unit: string | undefined;\n items: BreakdownItem[];\n}) {\n const used = items.reduce((acc, it) => acc + it.value, 0);\n const pct = total > 0 ? Math.min(100, (used / total) * 100) : 0;\n return (\n <span className=\"font-mono tabular-nums\">\n {formatUnit(used, unit)} / {formatUnit(total, unit)} · {pct.toFixed(0)}%\n </span>\n );\n}\n\n/** Instantaneous stacked bar + legend list. The expand button (in the panel header) opens the graph. */\nfunction BreakdownView({\n total,\n unit,\n items,\n}: {\n total: number;\n unit: string | undefined;\n items: BreakdownItem[];\n}) {\n const segments: ProgressSegment[] = items.map((it) => ({\n count: it.value,\n color: it.color,\n label: it.label,\n }));\n return (\n <div className=\"flex flex-col gap-2\">\n <ProgressBar segments={segments} total={total > 0 ? total : 1} height=\"h-3\" />\n {items.length > 0 ? (\n <ul className=\"grid grid-cols-1 gap-y-1 text-xs\">\n {items.map((it) => (\n <li key={it.key} className=\"flex items-center gap-2\">\n <span\n className=\"h-2 w-2 shrink-0 rounded-full\"\n style={{ backgroundColor: resolveCssColor(it.color) }}\n aria-hidden\n />\n <span className=\"min-w-0 flex-1 truncate font-mono text-muted-foreground\">\n {it.label}\n </span>\n <span className=\"font-mono tabular-nums text-foreground\">\n {formatUnit(it.value, it.unit ?? unit)}\n </span>\n <span className=\"w-10 text-right font-mono tabular-nums text-muted-foreground\">\n {it.percent.toFixed(0)}%\n </span>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n );\n}\n\ntype ChartVariant = \"area\" | \"line\" | \"stacked\";\n\nfunction Chart({\n rows,\n series,\n variant,\n unit,\n showZeroLine,\n}: {\n rows: MergedRow[];\n series: ResolvedSeries[];\n variant: ChartVariant;\n unit: string | undefined;\n showZeroLine: boolean;\n}) {\n const stacked = variant === \"stacked\";\n // Mirrored series carry negative values; show magnitudes on axis and tooltip.\n const seriesByKey = new Map(series.map((s) => [s.key, s]));\n // recharts needs a CSS color value; a series may carry a Tailwind class.\n const strokeOf = (s: ResolvedSeries) => resolveCssColor(s.color);\n // Distinct units map to a left and (when present) right axis; each series draws\n // against the axis for its unit so mixed-unit panels keep independent scales.\n const { axes: yAxes, axisOfKey } = useMemo(() => assignAxes(series), [series]);\n const axisOf = (key: string): \"left\" | \"right\" => axisOfKey.get(key) ?? \"left\";\n const axes = (\n <>\n <CartesianGrid strokeOpacity={0.12} vertical={false} />\n <XAxis\n dataKey=\"at\"\n tickFormatter={formatClock}\n tick={{ fontSize: 10 }}\n stroke=\"currentColor\"\n strokeOpacity={0.35}\n minTickGap={32}\n />\n {yAxes.map((ax) => (\n <YAxis\n key={ax.id}\n yAxisId={ax.id}\n orientation={ax.orientation}\n tick={{ fontSize: 10 }}\n stroke=\"currentColor\"\n strokeOpacity={0.35}\n width={40}\n tickFormatter={(v: number) => formatUnit(Math.abs(v), ax.unit)}\n />\n ))}\n <Tooltip\n labelFormatter={(label) => formatClock(String(label))}\n formatter={(value, name) => {\n const s = seriesByKey.get(String(name));\n return [formatUnit(Math.abs(Number(value) || 0), s?.unit ?? unit), s?.label ?? String(name)];\n }}\n contentStyle={{ fontSize: 12 }}\n />\n {showZeroLine ? (\n <ReferenceLine yAxisId=\"left\" y={0} stroke=\"currentColor\" strokeOpacity={0.35} />\n ) : null}\n </>\n );\n\n return (\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n {variant === \"line\" ? (\n <LineChart data={rows} margin={{ top: 4, right: 8, bottom: 0, left: 0 }}>\n {axes}\n {series.map((s) => (\n <Line\n key={s.key}\n yAxisId={axisOf(s.key)}\n type=\"monotone\"\n dataKey={s.key}\n name={s.key}\n stroke={strokeOf(s)}\n strokeWidth={2}\n dot={false}\n connectNulls\n isAnimationActive={false}\n />\n ))}\n </LineChart>\n ) : (\n <AreaChart data={rows} margin={{ top: 4, right: 8, bottom: 0, left: 0 }}>\n {axes}\n {series.map((s) => (\n <Area\n key={s.key}\n yAxisId={axisOf(s.key)}\n type=\"monotone\"\n dataKey={s.key}\n name={s.key}\n stroke={strokeOf(s)}\n fill={strokeOf(s)}\n {...(stacked ? { stackId: `stack-${axisOf(s.key)}` } : {})}\n fillOpacity={stacked ? 0.7 : 0.12}\n strokeWidth={stacked ? 0 : 2}\n connectNulls\n isAnimationActive={false}\n />\n ))}\n </AreaChart>\n )}\n </ResponsiveContainer>\n );\n}\n\nfunction PanelMessage({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"flex h-full items-center justify-center text-xs text-muted-foreground\">\n {children}\n </div>\n );\n}\n\nfunction PanelIcon({ icon }: { icon: string | StaticIconComponent }) {\n if (typeof icon === \"string\") {\n return <Icon name={icon} width={16} height={16} className=\"text-muted-foreground\" />;\n }\n return <Icon icon={icon} width={16} height={16} className=\"text-muted-foreground\" />;\n}\n"],"names":["latestOf","useState","useMemo","useQueries","_a","cn","jsxs","jsx","Icon","UiFullscreen","Modal","formatUnit","resolveCssColor","ProgressBar","assignAxes","Fragment","CartesianGrid","XAxis","YAxis","Tooltip","ReferenceLine","ResponsiveContainer","LineChart","Line","AreaChart","Area"],"mappings":";;;;;;;;;;;;;;AAmHA,MAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,aAAa,OAAuB;AAC3C,SAAO,eAAe,QAAQ,eAAe,MAAM,KAAK,eAAe,CAAC;AAC1E;AAiBO,SAAS,cAAc,OAA+C;AAC3E,QAAM,OAAO,MAAM,WAAW;AAC9B,QAAM,OAAO,MAAM,WAAW,MAAM,MAAM,CAAC,EAAE,IAAI,MAAM,IAAA,CAAK,IAAI,CAAA;AAChE,SAAO,KAAK,IAAI,CAAC,GAAG,OAAO;AAAA,IACzB,KAAK,EAAE;AAAA,IACP,KAAK,OAAO,EAAE;AAAA,IACd,OAAO,EAAE,SAAS,EAAE;AAAA,IACpB,OAAO,EAAE,SAAS,aAAa,CAAC;AAAA,IAChC,GAAI,EAAE,QAAQ,MAAM,OAAO,EAAE,MAAM,EAAE,QAAQ,MAAM,KAAA,IAAS,CAAA;AAAA,IAC5D,GAAI,EAAE,YAAY,EAAE,WAAW,EAAE,UAAA,IAAc,CAAA;AAAA,IAC/C,GAAI,EAAE,YAAY,SAAY,EAAE,SAAS,EAAE,YAAY,CAAA;AAAA,EAAC,EACxD;AACJ;AAoBO,SAAS,iBACd,QACA,MACA,OAC2C;AAC3C,QAAM,YAAY,OAAO,IAAI,CAAC,OAAO,EAAE,GAAG,OAAO,EAAE,WAAWA,qBAAAA,SAAS,MAAM,EAAE,GAAG,KAAK,IAAI;AAC3F,QAAM,QAAQ,SAAS,UAAU,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AACpE,QAAM,QAAQ,UAAU,IAAI,CAAC,EAAE,GAAG,aAAa;AAAA,IAC7C,KAAK,EAAE;AAAA,IACP,OAAO,EAAE;AAAA,IACT,OAAO,EAAE;AAAA,IACT,GAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAA,IAAS,CAAA;AAAA,IAChC;AAAA,IACA,SAAS,QAAQ,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAI,QAAQ,QAAS,GAAG,CAAC,IAAI;AAAA,EAAA,EACzE;AACF,SAAO,EAAE,OAAO,OAAO,MAAA;AACzB;AAEA,MAAM,iBAAiB,OAAO,QAA6C;AACzE,QAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,2BAA2B,IAAI,MAAM,EAAE;AACpE,SAAO,IAAI,KAAA;AACb;AAEA,SAAS,YAAY,IAAoB;AACvC,QAAM,OAAO,IAAI,KAAK,EAAE;AACxB,MAAI,OAAO,MAAM,KAAK,QAAA,CAAS,EAAG,QAAO;AACzC,SAAO,KAAK,mBAAmB,IAAI,EAAE,MAAM,WAAW,QAAQ,WAAW;AAC3E;AAiBO,SAAS,YACd,QACA,gBACa;AACb,QAAM,2BAAW,IAAA;AACjB,SAAO,QAAQ,CAAC,GAAG,MAAM;AACvB,eAAW,KAAK,eAAe,CAAC,KAAK,CAAA,GAAI;AACvC,YAAM,MAAM,KAAK,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAA;AACtC,UAAI,EAAE,GAAG,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,IAAI,EAAE;AACpD,WAAK,IAAI,EAAE,IAAI,GAAG;AAAA,IACpB;AAAA,EACF,CAAC;AACD,SAAO,CAAC,GAAG,KAAK,OAAA,CAAQ,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AACnE;AAEO,SAAS,gBAAgB,OAA6B;;AAC3D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb;AAAA,IACA,UAAU;AAAA,IACV;AAAA,EAAA,IACE;AACJ,QAAM,cAAc,YAAY;AAGhC,QAAM,eAA6B,cAAc,SAAS;AAC1D,QAAM,gBAAgB,MAAM,iBAAiB;AAE7C,QAAM,CAAC,UAAU,WAAW,IAAIC,MAAAA,SAAS,KAAK;AAE9C,QAAM,SAASC,MAAAA,QAAQ,MAAM,cAAc,KAAK,GAAG,CAAC,KAAK,CAAC;AAC1D,QAAM,cAAcA,MAAAA;AAAAA,IAClB,MACE,OAAO,IAAI,CAAC,MAAM;AAChB,YAAM,IAAI,IAAI,IAAI,EAAE,KAAK,OAAO,SAAS,MAAM;AAC/C,UAAI,MAAO,GAAE,aAAa,IAAI,SAAS,KAAK;AAC5C,aAAO,EAAE,WAAW,EAAE;AAAA,IACxB,CAAC;AAAA,IACH,CAAC,QAAQ,KAAK;AAAA,EAAA;AAGhB,QAAM,UAAUC,WAAAA,WAAW;AAAA,IACzB,SAAS,YAAY,IAAI,CAAC,gBAAgB;AAAA,MACxC,UAAU,CAAC,cAAc,UAAU;AAAA,MACnC,SAAS,MAAM,QAAQ,UAAU;AAAA,MACjC,iBAAiB,YAAY,IAAI,YAAY;AAAA,MAC7C,WAAW;AAAA,MACX,OAAO;AAAA,IAAA,EACP;AAAA,EAAA,CACH;AAED,QAAM,UAAU,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO;AAC7C,QAAM,SAAQ,aAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,MAA7B,mBAAgC;AAC9C,QAAM,YAAY,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,QAAQ,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI;AAClF,QAAM,iBAAiB,QAAQ,IAAI,CAAC;;AAAM,YAAAC,MAAA,EAAE,SAAF,gBAAAA,IAAQ;AAAA,GAAM;AACxD,QAAM,OAAOF,cAAQ,MAAM,YAAY,QAAQ,cAAc,GAAG,CAAC,QAAQ,cAAc,CAAC;AACxF,QAAM,cAAc,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS;AAClD,QAAM,YAAYA,MAAAA;AAAAA,IAChB,MAAO,cAAc,iBAAiB,QAAQ,MAAM,KAAK,IAAI;AAAA,IAC7D,CAAC,aAAa,QAAQ,MAAM,KAAK;AAAA,EAAA;AAKnC,QAAM,YAAY,cAAc,KAAK,UAAU;AAE/C,yCACG,OAAA,EAAI,WAAWG,MAAAA,GAAG,6DAA6D,SAAS,GACvF,UAAA;AAAA,IAAAC,2BAAAA,KAAC,OAAA,EAAI,WAAU,yEACb,UAAA;AAAA,MAAAA,2BAAAA,KAAC,QAAA,EAAK,WAAU,4CACb,UAAA;AAAA,QAAA,OAAOC,2BAAAA,IAAC,WAAA,EAAU,KAAA,CAAY,IAAK;AAAA,QACpCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,wCAAwC,UAAA,MAAA,CAAM;AAAA,MAAA,GAChE;AAAA,MACAD,2BAAAA,KAAC,QAAA,EAAK,WAAU,6CACb,UAAA;AAAA,QAAA,YACCC,2BAAAA,IAAC,kBAAA,EAAiB,OAAO,UAAU,OAAO,MAAY,OAAO,UAAU,MAAA,CAAO,IAE9EA,2BAAAA,IAAC,eAAA,EAAc,QAAgB,MAAY;AAAA,QAE5C,YACCA,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAW;AAAA,YACX,SAAS,MAAM,YAAY,IAAI;AAAA,YAC/B,WAAU;AAAA,YAEV,yCAACC,KAAAA,MAAA,EAAK,MAAMC,aAAAA,cAAc,OAAO,IAAI,QAAQ,GAAA,CAAI;AAAA,UAAA;AAAA,QAAA,IAEjD;AAAA,MAAA,EAAA,CACN;AAAA,IAAA,GACF;AAAA,IACAF,2BAAAA,IAAC,SAAI,WAAU,QAAO,OAAO,YAAY,SAAY,EAAE,OAAA,GACpD,UAAA,2CACE,eAAA,EAAc,OAAO,UAAU,OAAO,MAAY,OAAO,UAAU,OAAO,IACzE,UACFA,2BAAAA,IAAC,cAAA,EAAe,0CAAiB,YAAW,wBAAA,CAAwB,IAClE,YACFA,+BAAC,gBAAa,UAAA,WAAA,CAAQ,IACpB,KAAK,SAAS,IAChBA,2BAAAA,IAAC,gBAAa,UAAA,mBAAA,CAAgB,IAE9BA,2BAAAA,IAAC,OAAA,EAAM,MAAY,QAAgB,SAAS,cAAc,MAAY,cAAc,YAAA,CAAa,EAAA,CAErG;AAAA,IACC,CAAC,aAAa,CAAC,WAAW,CAAC,aAAa,KAAK,UAAU,KAAK,OAAO,SAAS,IAC3EA,2BAAAA,IAAC,aAAA,EAAY,QAAgB,MAAY,IACvC;AAAA,IACH,YACCD,2BAAAA,KAACI,MAAAA,OAAA,EAAM,MAAM,UAAU,SAAS,MAAM,YAAY,KAAK,GAAG,OAAc,MAAK,MAC3E,UAAA;AAAA,MAAAH,+BAAC,OAAA,EAAI,OAAO,EAAE,QAAQ,OACpB,UAAAA,+BAAC,OAAA,EAAM,MAAY,QAAgB,SAAS,eAAe,MAAY,cAAc,aAAa,GACpG;AAAA,MACC,OAAO,SAAS,mCAAK,aAAA,EAAY,QAAgB,MAAY,IAAK;AAAA,IAAA,EAAA,CACrE,IACE;AAAA,EAAA,GACN;AAEJ;AAOA,SAAS,cAAc,EAAE,QAAQ,QAAyD;AACxF,QAAM,OAAO,OAAO,WAAW,IAAI,OAAO,CAAC,IAAI;AAC/C,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAIP,qBAAAA,SAAS,MAAM,KAAK,GAAG;AACjC,MAAI,MAAM,OAAW,QAAO;AAC5B,SAAOO,2BAAAA,IAAC,QAAA,EAAK,WAAU,gBAAgB,UAAAI,OAAAA,WAAW,KAAK,IAAI,CAAC,GAAG,KAAK,IAAI,EAAA,CAAE;AAC5E;AAGA,SAAS,YAAY,EAAE,QAAQ,QAAyD;AACtF,wCACG,MAAA,EAAG,WAAU,uEACX,UAAA,OAAO,IAAI,CAAC,MAAM;AACjB,UAAM,IAAIX,qBAAAA,SAAS,MAAM,EAAE,GAAG;AAC9B,WACEM,2BAAAA,KAAC,MAAA,EAAe,WAAU,4CACxB,UAAA;AAAA,MAAAC,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO,EAAE,iBAAiBK,MAAAA,gBAAgB,EAAE,KAAK,EAAA;AAAA,UACjD,eAAW;AAAA,QAAA;AAAA,MAAA;AAAA,MAEbL,2BAAAA,IAAC,QAAA,EAAK,WAAU,4BAA4B,YAAE,OAAM;AAAA,MACnD,MAAM,SACLA,2BAAAA,IAAC,QAAA,EAAK,WAAU,gBAAgB,UAAAI,OAAAA,WAAW,KAAK,IAAI,CAAC,GAAG,EAAE,IAAI,GAAE,IAC9D;AAAA,IAAA,EAAA,GATG,EAAE,GAUX;AAAA,EAEJ,CAAC,EAAA,CACH;AAEJ;AAGA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,OAAO,MAAM,OAAO,CAAC,KAAK,OAAO,MAAM,GAAG,OAAO,CAAC;AACxD,QAAM,MAAM,QAAQ,IAAI,KAAK,IAAI,KAAM,OAAO,QAAS,GAAG,IAAI;AAC9D,SACEL,2BAAAA,KAAC,QAAA,EAAK,WAAU,0BACb,UAAA;AAAA,IAAAK,OAAAA,WAAW,MAAM,IAAI;AAAA,IAAE;AAAA,IAAIA,OAAAA,WAAW,OAAO,IAAI;AAAA,IAAE;AAAA,IAAI,IAAI,QAAQ,CAAC;AAAA,IAAE;AAAA,EAAA,GACzE;AAEJ;AAGA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,WAA8B,MAAM,IAAI,CAAC,QAAQ;AAAA,IACrD,OAAO,GAAG;AAAA,IACV,OAAO,GAAG;AAAA,IACV,OAAO,GAAG;AAAA,EAAA,EACV;AACF,SACEL,2BAAAA,KAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,IAAAC,2BAAAA,IAACM,YAAAA,aAAA,EAAY,UAAoB,OAAO,QAAQ,IAAI,QAAQ,GAAG,QAAO,MAAA,CAAM;AAAA,IAC3E,MAAM,SAAS,IACdN,2BAAAA,IAAC,QAAG,WAAU,oCACX,UAAA,MAAM,IAAI,CAAC,OACVD,2BAAAA,KAAC,MAAA,EAAgB,WAAU,2BACzB,UAAA;AAAA,MAAAC,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO,EAAE,iBAAiBK,MAAAA,gBAAgB,GAAG,KAAK,EAAA;AAAA,UAClD,eAAW;AAAA,QAAA;AAAA,MAAA;AAAA,MAEbL,2BAAAA,IAAC,QAAA,EAAK,WAAU,2DACb,aAAG,OACN;AAAA,MACAA,2BAAAA,IAAC,QAAA,EAAK,WAAU,0CACb,UAAAI,OAAAA,WAAW,GAAG,OAAO,GAAG,QAAQ,IAAI,EAAA,CACvC;AAAA,MACAL,2BAAAA,KAAC,QAAA,EAAK,WAAU,gEACb,UAAA;AAAA,QAAA,GAAG,QAAQ,QAAQ,CAAC;AAAA,QAAE;AAAA,MAAA,EAAA,CACzB;AAAA,IAAA,EAAA,GAdO,GAAG,GAeZ,CACD,EAAA,CACH,IACE;AAAA,EAAA,GACN;AAEJ;AAIA,SAAS,MAAM;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,QAAM,UAAU,YAAY;AAE5B,QAAM,cAAc,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;AAEzD,QAAM,WAAW,CAAC,MAAsBM,MAAAA,gBAAgB,EAAE,KAAK;AAG/D,QAAM,EAAE,MAAM,OAAO,cAAcV,MAAAA,QAAQ,MAAMY,qBAAAA,WAAW,MAAM,GAAG,CAAC,MAAM,CAAC;AAC7E,QAAM,SAAS,CAAC,QAAkC,UAAU,IAAI,GAAG,KAAK;AACxE,QAAM,OACJR,2BAAAA,KAAAS,WAAAA,UAAA,EACE,UAAA;AAAA,IAAAR,2BAAAA,IAACS,SAAAA,eAAA,EAAc,eAAe,MAAM,UAAU,OAAO;AAAA,IACrDT,2BAAAA;AAAAA,MAACU,SAAAA;AAAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,eAAe;AAAA,QACf,MAAM,EAAE,UAAU,GAAA;AAAA,QAClB,QAAO;AAAA,QACP,eAAe;AAAA,QACf,YAAY;AAAA,MAAA;AAAA,IAAA;AAAA,IAEb,MAAM,IAAI,CAAC,OACVV,2BAAAA;AAAAA,MAACW,SAAAA;AAAAA,MAAA;AAAA,QAEC,SAAS,GAAG;AAAA,QACZ,aAAa,GAAG;AAAA,QAChB,MAAM,EAAE,UAAU,GAAA;AAAA,QAClB,QAAO;AAAA,QACP,eAAe;AAAA,QACf,OAAO;AAAA,QACP,eAAe,CAAC,MAAcP,kBAAW,KAAK,IAAI,CAAC,GAAG,GAAG,IAAI;AAAA,MAAA;AAAA,MAPxD,GAAG;AAAA,IAAA,CASX;AAAA,IACDJ,2BAAAA;AAAAA,MAACY,SAAAA;AAAAA,MAAA;AAAA,QACC,gBAAgB,CAAC,UAAU,YAAY,OAAO,KAAK,CAAC;AAAA,QACpD,WAAW,CAAC,OAAO,SAAS;AAC1B,gBAAM,IAAI,YAAY,IAAI,OAAO,IAAI,CAAC;AACtC,iBAAO,CAACR,OAAAA,WAAW,KAAK,IAAI,OAAO,KAAK,KAAK,CAAC,IAAG,uBAAG,SAAQ,IAAI,IAAG,uBAAG,UAAS,OAAO,IAAI,CAAC;AAAA,QAC7F;AAAA,QACA,cAAc,EAAE,UAAU,GAAA;AAAA,MAAG;AAAA,IAAA;AAAA,IAE9B,eACCJ,2BAAAA,IAACa,SAAAA,eAAA,EAAc,SAAQ,QAAO,GAAG,GAAG,QAAO,gBAAe,eAAe,KAAA,CAAM,IAC7E;AAAA,EAAA,GACN;AAGF,SACEb,+BAACc,SAAAA,uBAAoB,OAAM,QAAO,QAAO,QACtC,UAAA,YAAY,SACXf,2BAAAA,KAACgB,SAAAA,WAAA,EAAU,MAAM,MAAM,QAAQ,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,EAAA,GACjE,UAAA;AAAA,IAAA;AAAA,IACA,OAAO,IAAI,CAAC,MACXf,2BAAAA;AAAAA,MAACgB,SAAAA;AAAAA,MAAA;AAAA,QAEC,SAAS,OAAO,EAAE,GAAG;AAAA,QACrB,MAAK;AAAA,QACL,SAAS,EAAE;AAAA,QACX,MAAM,EAAE;AAAA,QACR,QAAQ,SAAS,CAAC;AAAA,QAClB,aAAa;AAAA,QACb,KAAK;AAAA,QACL,cAAY;AAAA,QACZ,mBAAmB;AAAA,MAAA;AAAA,MATd,EAAE;AAAA,IAAA,CAWV;AAAA,EAAA,EAAA,CACH,IAEAjB,2BAAAA,KAACkB,SAAAA,WAAA,EAAU,MAAM,MAAM,QAAQ,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,KACjE,UAAA;AAAA,IAAA;AAAA,IACA,OAAO,IAAI,CAAC,MACXjB,2BAAAA;AAAAA,MAACkB,SAAAA;AAAAA,MAAA;AAAA,QAEC,SAAS,OAAO,EAAE,GAAG;AAAA,QACrB,MAAK;AAAA,QACL,SAAS,EAAE;AAAA,QACX,MAAM,EAAE;AAAA,QACR,QAAQ,SAAS,CAAC;AAAA,QAClB,MAAM,SAAS,CAAC;AAAA,QACf,GAAI,UAAU,EAAE,SAAS,SAAS,OAAO,EAAE,GAAG,CAAC,GAAA,IAAO,CAAA;AAAA,QACvD,aAAa,UAAU,MAAM;AAAA,QAC7B,aAAa,UAAU,IAAI;AAAA,QAC3B,cAAY;AAAA,QACZ,mBAAmB;AAAA,MAAA;AAAA,MAXd,EAAE;AAAA,IAAA,CAaV;AAAA,EAAA,EAAA,CACH,EAAA,CAEJ;AAEJ;AAEA,SAAS,aAAa,EAAE,YAA2C;AACjE,SACElB,2BAAAA,IAAC,OAAA,EAAI,WAAU,yEACZ,SAAA,CACH;AAEJ;AAEA,SAAS,UAAU,EAAE,QAAgD;AACnE,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAOA,+BAACC,KAAAA,QAAK,MAAM,MAAM,OAAO,IAAI,QAAQ,IAAI,WAAU,wBAAA,CAAwB;AAAA,EACpF;AACA,SAAOD,+BAACC,KAAAA,QAAK,MAAY,OAAO,IAAI,QAAQ,IAAI,WAAU,yBAAwB;AACpF;;;;;;;"}
@@ -0,0 +1,141 @@
1
+ import { StaticIconComponent } from './Icon';
2
+ export { assignAxes, latestOf } from './TimeseriesPanel.axes';
3
+ export type { AxisAssignment, AxisOrientation, AxisSpec } from './TimeseriesPanel.axes';
4
+ export interface TimeseriesPoint {
5
+ at: string;
6
+ value: number;
7
+ }
8
+ export interface TimeseriesResponse {
9
+ id: string;
10
+ points: TimeseriesPoint[];
11
+ }
12
+ /** One metric plotted on a panel. Its request URL is `baseUrl + id`. */
13
+ export interface TimeseriesSeries {
14
+ /** Metric id appended to the panel's baseUrl, e.g. "sqlserver.iops.read". */
15
+ id: string;
16
+ /** Tooltip/legend label; defaults to the id. */
17
+ label?: string;
18
+ /**
19
+ * Maps each point's value before plotting — e.g. writes pass `(v) => -v` to
20
+ * render below the zero axis. Identity when omitted.
21
+ */
22
+ transform?: (value: number) => number;
23
+ /**
24
+ * Stroke/fill override as a CSS color value (e.g. "#10b981", "var(--chart-3)")
25
+ * or a Tailwind color class (e.g. "bg-emerald-500"); Tailwind classes are
26
+ * resolved to a CSS value for the chart. Defaults cycle a small palette by index.
27
+ */
28
+ color?: string;
29
+ /**
30
+ * Grafana-style unit key for this series' readout/tooltip (e.g. "percent",
31
+ * "short"). Falls back to the panel-level `unit`. Lets one panel mix units
32
+ * (e.g. a percent series alongside a plain-count series).
33
+ */
34
+ unit?: string;
35
+ /**
36
+ * Instantaneous value for the `breakdown` variant — the size of this series'
37
+ * bar segment and its legend readout. When omitted, the breakdown falls back
38
+ * to the latest polled point. Ignored by the time-series variants.
39
+ */
40
+ current?: number;
41
+ }
42
+ export interface TimeseriesPanelProps {
43
+ /**
44
+ * Single-metric endpoint returning a TimeseriesResponse,
45
+ * e.g. "/api/v1/metrics/sqlserver.cpu". Mutually exclusive with baseUrl/series.
46
+ */
47
+ url?: string;
48
+ /** Common prefix for a multi-series panel; each series requests `baseUrl + series.id`. */
49
+ baseUrl?: string;
50
+ /** Metrics to plot on one chart. Provide together with baseUrl. */
51
+ series?: TimeseriesSeries[];
52
+ title: string;
53
+ /** Iconify name or static icon component shown beside the title. */
54
+ icon?: string | StaticIconComponent;
55
+ /**
56
+ * Default Grafana-style unit key for the panel (e.g. "bytes", "percent",
57
+ * "ms", "short"); each series may override it. Unknown strings are appended
58
+ * as a raw suffix (so "%" still works). Used for the readout, axis, and tooltip.
59
+ */
60
+ unit?: string;
61
+ /** Look-back window passed as ?since=; defaults to "1h". */
62
+ range?: string;
63
+ /** Poll interval in ms; defaults to 5000. Pass 0 to disable polling. */
64
+ refreshMs?: number;
65
+ /** Chart height in px; defaults to 160. */
66
+ height?: number;
67
+ /**
68
+ * "area" (overlaid, default), "line", "stacked" (areas sum vertically), or
69
+ * "breakdown" (an instantaneous stacked bar + legend; the expand modal still
70
+ * shows a time-series graph of the polled history).
71
+ */
72
+ variant?: "area" | "line" | "stacked" | "breakdown";
73
+ /**
74
+ * Chart variant rendered in the expand modal. Defaults to `variant` for the
75
+ * time-series variants and to "area" for `breakdown` (which has no inline chart).
76
+ */
77
+ expandVariant?: "area" | "line" | "stacked";
78
+ /**
79
+ * Denominator for the `breakdown` bar and per-segment percentages. Defaults to
80
+ * the sum of the series' `current` values when omitted.
81
+ */
82
+ total?: number;
83
+ /** Show a header button that opens the chart larger in a modal. Default true. */
84
+ expandable?: boolean;
85
+ /** Override the default fetch (e.g. to route through an app's API client). */
86
+ fetcher?: (url: string) => Promise<TimeseriesResponse>;
87
+ className?: string;
88
+ }
89
+ /** A series with its full request URL, resolved label, color, and unit. Exported for tests. */
90
+ export interface ResolvedSeries {
91
+ /** Stable key used as the merged-row field and recharts dataKey. */
92
+ key: string;
93
+ url: string;
94
+ label: string;
95
+ color: string;
96
+ /** Resolved Grafana unit key: the series' own unit, else the panel default. */
97
+ unit?: string;
98
+ transform?: (value: number) => number;
99
+ /** Instantaneous value used by the `breakdown` variant. */
100
+ current?: number;
101
+ }
102
+ /** Normalises the single-url and baseUrl+series forms to one list. Exported for tests. */
103
+ export declare function resolveSeries(props: TimeseriesPanelProps): ResolvedSeries[];
104
+ /** One legend/bar row of the breakdown variant: resolved value, color, label, unit, percentage. */
105
+ export interface BreakdownItem {
106
+ key: string;
107
+ label: string;
108
+ color: string;
109
+ unit?: string;
110
+ value: number;
111
+ /** Share of `total`, clamped to 0..100. */
112
+ percent: number;
113
+ }
114
+ /**
115
+ * Derives the breakdown bar segments and per-item percentages from the resolved
116
+ * series' `current` values. `total` defaults to the sum of those values; the
117
+ * percentage is each value's share of that denominator, clamped to 0..100.
118
+ * Latest polled point fills in for a series with no explicit `current`. Pure —
119
+ * exported for tests.
120
+ */
121
+ export declare function resolveBreakdown(series: ResolvedSeries[], rows: MergedRow[], total?: number): {
122
+ items: BreakdownItem[];
123
+ total: number;
124
+ };
125
+ /**
126
+ * One row of the merged chart data: the timestamp in `at` plus each series'
127
+ * value under its key. Values are absent (undefined) where a series has no
128
+ * point at that timestamp.
129
+ */
130
+ export interface MergedRow {
131
+ at: string;
132
+ [seriesKey: string]: string | number | undefined;
133
+ }
134
+ /**
135
+ * Joins per-series point lists into rows keyed by timestamp and applies each
136
+ * series' transform. Timestamps present in any series produce a row; missing
137
+ * values are left undefined so recharts gaps the line rather than drawing zero.
138
+ */
139
+ export declare function mergeSeries(series: ResolvedSeries[], pointsBySeries: (TimeseriesPoint[] | undefined)[]): MergedRow[];
140
+ export declare function TimeseriesPanel(props: TimeseriesPanelProps): import("react/jsx-runtime").JSX.Element;
141
+ //# sourceMappingURL=TimeseriesPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimeseriesPanel.d.ts","sourceRoot":"","sources":["../../src/data/TimeseriesPanel.tsx"],"names":[],"mappings":"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"}