@epilot/spark-ui 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/dist/components/accordion.cjs +7 -7
  2. package/dist/components/accordion.cjs.map +1 -1
  3. package/dist/components/accordion.d.ts +1 -1
  4. package/dist/components/accordion.js +1 -1
  5. package/dist/components/alert-dialog.cjs +10 -10
  6. package/dist/components/alert-dialog.cjs.map +1 -1
  7. package/dist/components/alert-dialog.js +1 -1
  8. package/dist/components/attachment.cjs +10 -10
  9. package/dist/components/attachment.cjs.map +1 -1
  10. package/dist/components/attachment.d.cts +2 -2
  11. package/dist/components/attachment.d.ts +2 -2
  12. package/dist/components/autocomplete.cjs +13 -13
  13. package/dist/components/autocomplete.cjs.map +1 -1
  14. package/dist/components/autocomplete.js +1 -1
  15. package/dist/components/avatar.cjs +2 -2
  16. package/dist/components/avatar.cjs.map +1 -1
  17. package/dist/components/avatar.js +1 -1
  18. package/dist/components/badge.cjs +2 -2
  19. package/dist/components/badge.cjs.map +1 -1
  20. package/dist/components/badge.js +1 -1
  21. package/dist/components/bar-chart-with-total.d.cts +1 -1
  22. package/dist/components/bar-chart-with-total.d.ts +1 -1
  23. package/dist/components/bar-chart.cjs +4 -4
  24. package/dist/components/bar-chart.cjs.map +1 -1
  25. package/dist/components/bar-chart.js +2 -2
  26. package/dist/components/box.cjs +2 -2
  27. package/dist/components/box.cjs.map +1 -1
  28. package/dist/components/box.js +1 -1
  29. package/dist/components/breadcrumbs.cjs +7 -7
  30. package/dist/components/breadcrumbs.cjs.map +1 -1
  31. package/dist/components/bubble.cjs +5 -5
  32. package/dist/components/bubble.cjs.map +1 -1
  33. package/dist/components/bubble.d.cts +1 -1
  34. package/dist/components/bubble.d.ts +1 -1
  35. package/dist/components/button-group.cjs +5 -5
  36. package/dist/components/button-group.cjs.map +1 -1
  37. package/dist/components/button-group.js +1 -1
  38. package/dist/components/button.cjs +2 -2
  39. package/dist/components/button.cjs.map +1 -1
  40. package/dist/components/button.d.cts +2 -2
  41. package/dist/components/button.d.ts +2 -2
  42. package/dist/components/button.js +1 -1
  43. package/dist/components/calendar.cjs +9 -9
  44. package/dist/components/calendar.cjs.map +1 -1
  45. package/dist/components/calendar.js +1 -1
  46. package/dist/components/callout.cjs +8 -8
  47. package/dist/components/callout.cjs.map +1 -1
  48. package/dist/components/callout.d.cts +1 -1
  49. package/dist/components/callout.d.ts +2 -2
  50. package/dist/components/callout.js +1 -1
  51. package/dist/components/card.cjs +16 -16
  52. package/dist/components/card.cjs.map +1 -1
  53. package/dist/components/card.js +2 -2
  54. package/dist/components/card.js.map +1 -1
  55. package/dist/components/chart.cjs +2 -2
  56. package/dist/components/chart.cjs.map +1 -1
  57. package/dist/components/checkbox.cjs +2 -2
  58. package/dist/components/checkbox.cjs.map +1 -1
  59. package/dist/components/checkbox.js +1 -1
  60. package/dist/components/circular-gauge.cjs +3 -3
  61. package/dist/components/circular-gauge.cjs.map +1 -1
  62. package/dist/components/day-of-week-picker.cjs +4 -4
  63. package/dist/components/day-of-week-picker.cjs.map +1 -1
  64. package/dist/components/dialog.cjs +9 -9
  65. package/dist/components/dialog.cjs.map +1 -1
  66. package/dist/components/dialog.js +1 -1
  67. package/dist/components/drawer.cjs +8 -8
  68. package/dist/components/drawer.cjs.map +1 -1
  69. package/dist/components/dropdown-menu.cjs +11 -11
  70. package/dist/components/dropdown-menu.cjs.map +1 -1
  71. package/dist/components/dropdown-menu.js +1 -1
  72. package/dist/components/empty-state.d.ts +1 -1
  73. package/dist/components/energy-breakdown.cjs +80 -0
  74. package/dist/components/energy-breakdown.cjs.map +1 -0
  75. package/dist/components/energy-breakdown.d.cts +24 -0
  76. package/dist/components/energy-breakdown.d.ts +24 -0
  77. package/dist/components/energy-breakdown.js +79 -0
  78. package/dist/components/energy-breakdown.js.map +1 -0
  79. package/dist/components/energy-history-chart.cjs +63 -0
  80. package/dist/components/energy-history-chart.cjs.map +1 -0
  81. package/dist/components/energy-history-chart.d.cts +64 -0
  82. package/dist/components/energy-history-chart.d.ts +64 -0
  83. package/dist/components/energy-history-chart.js +62 -0
  84. package/dist/components/energy-history-chart.js.map +1 -0
  85. package/dist/components/entity-card.cjs +11 -11
  86. package/dist/components/entity-card.cjs.map +1 -1
  87. package/dist/components/entity-card.d.ts +1 -1
  88. package/dist/components/field.cjs +15 -15
  89. package/dist/components/field.cjs.map +1 -1
  90. package/dist/components/hero-metric.cjs +4 -4
  91. package/dist/components/hero-metric.cjs.map +1 -1
  92. package/dist/components/icon.cjs +2 -2
  93. package/dist/components/icon.cjs.map +1 -1
  94. package/dist/components/icon.js +1 -1
  95. package/dist/components/info-block.cjs +9 -9
  96. package/dist/components/info-block.cjs.map +1 -1
  97. package/dist/components/info-block.d.cts +1 -1
  98. package/dist/components/info-block.d.ts +1 -1
  99. package/dist/components/input-date.cjs +3 -3
  100. package/dist/components/input-date.cjs.map +1 -1
  101. package/dist/components/input-date.js +1 -1
  102. package/dist/components/input-group.cjs +5 -5
  103. package/dist/components/input-group.cjs.map +1 -1
  104. package/dist/components/input-otp.cjs +3 -3
  105. package/dist/components/input-otp.cjs.map +1 -1
  106. package/dist/components/input-otp.js +1 -1
  107. package/dist/components/input.cjs +3 -3
  108. package/dist/components/input.cjs.map +1 -1
  109. package/dist/components/input.js +1 -1
  110. package/dist/components/label.cjs +2 -2
  111. package/dist/components/label.cjs.map +1 -1
  112. package/dist/components/layout.cjs +2 -2
  113. package/dist/components/layout.cjs.map +1 -1
  114. package/dist/components/line-chart.cjs +7 -7
  115. package/dist/components/line-chart.cjs.map +1 -1
  116. package/dist/components/line-chart.js +4 -4
  117. package/dist/components/list.cjs +10 -10
  118. package/dist/components/list.cjs.map +1 -1
  119. package/dist/components/list.js +1 -1
  120. package/dist/components/list.js.map +1 -1
  121. package/dist/components/marker.cjs +4 -4
  122. package/dist/components/marker.cjs.map +1 -1
  123. package/dist/components/marker.d.cts +1 -1
  124. package/dist/components/marker.d.ts +1 -1
  125. package/dist/components/message-scroller.cjs +8 -8
  126. package/dist/components/message-scroller.cjs.map +1 -1
  127. package/dist/components/message-scroller.js +4 -4
  128. package/dist/components/message-scroller.js.map +1 -1
  129. package/dist/components/message.cjs +7 -7
  130. package/dist/components/message.cjs.map +1 -1
  131. package/dist/components/number-flow.d.cts +2 -12
  132. package/dist/components/number-flow.d.ts +2 -12
  133. package/dist/components/page-header.cjs +6 -6
  134. package/dist/components/page-header.cjs.map +1 -1
  135. package/dist/components/page-header.d.cts +1 -1
  136. package/dist/components/page-header.d.ts +1 -1
  137. package/dist/components/page-header.js +1 -1
  138. package/dist/components/period-tabs.cjs +6 -6
  139. package/dist/components/period-tabs.cjs.map +1 -1
  140. package/dist/components/pill.cjs +3 -3
  141. package/dist/components/pill.cjs.map +1 -1
  142. package/dist/components/pill.js +2 -2
  143. package/dist/components/popover.cjs +2 -2
  144. package/dist/components/popover.cjs.map +1 -1
  145. package/dist/components/power-history-chart.cjs +498 -0
  146. package/dist/components/power-history-chart.cjs.map +1 -0
  147. package/dist/components/power-history-chart.d.cts +71 -0
  148. package/dist/components/power-history-chart.d.ts +71 -0
  149. package/dist/components/power-history-chart.js +496 -0
  150. package/dist/components/power-history-chart.js.map +1 -0
  151. package/dist/components/power-network.cjs +559 -0
  152. package/dist/components/power-network.cjs.map +1 -0
  153. package/dist/components/power-network.d.cts +80 -0
  154. package/dist/components/power-network.d.ts +80 -0
  155. package/dist/components/power-network.js +553 -0
  156. package/dist/components/power-network.js.map +1 -0
  157. package/dist/components/progress.cjs +2 -2
  158. package/dist/components/progress.cjs.map +1 -1
  159. package/dist/components/radio-card-group.cjs +3 -3
  160. package/dist/components/radio-card-group.cjs.map +1 -1
  161. package/dist/components/radio.cjs +3 -3
  162. package/dist/components/radio.cjs.map +1 -1
  163. package/dist/components/radio.js +1 -1
  164. package/dist/components/sankey-chart.cjs +13 -3
  165. package/dist/components/sankey-chart.cjs.map +1 -1
  166. package/dist/components/sankey-chart.d.cts +3 -2
  167. package/dist/components/sankey-chart.d.ts +3 -2
  168. package/dist/components/sankey-chart.js +11 -2
  169. package/dist/components/sankey-chart.js.map +1 -1
  170. package/dist/components/schedule-editor.cjs +9 -9
  171. package/dist/components/schedule-editor.cjs.map +1 -1
  172. package/dist/components/schedule-list.cjs +11 -11
  173. package/dist/components/schedule-list.cjs.map +1 -1
  174. package/dist/components/schedule-list.js +1 -1
  175. package/dist/components/scroll-area.cjs +3 -3
  176. package/dist/components/scroll-area.cjs.map +1 -1
  177. package/dist/components/segmented-control.cjs +4 -4
  178. package/dist/components/segmented-control.cjs.map +1 -1
  179. package/dist/components/select.cjs +13 -13
  180. package/dist/components/select.cjs.map +1 -1
  181. package/dist/components/select.js +2 -2
  182. package/dist/components/separator.cjs +3 -3
  183. package/dist/components/separator.cjs.map +1 -1
  184. package/dist/components/series-legend.cjs +4 -4
  185. package/dist/components/series-legend.cjs.map +1 -1
  186. package/dist/components/sheet.cjs +9 -9
  187. package/dist/components/sheet.cjs.map +1 -1
  188. package/dist/components/sheet.js +1 -1
  189. package/dist/components/skeleton.cjs +3 -3
  190. package/dist/components/skeleton.cjs.map +1 -1
  191. package/dist/components/slider.cjs +11 -11
  192. package/dist/components/slider.cjs.map +1 -1
  193. package/dist/components/solar-forecast-chart.cjs +108 -0
  194. package/dist/components/solar-forecast-chart.cjs.map +1 -0
  195. package/dist/components/solar-forecast-chart.d.cts +77 -0
  196. package/dist/components/solar-forecast-chart.d.ts +77 -0
  197. package/dist/components/solar-forecast-chart.js +103 -0
  198. package/dist/components/solar-forecast-chart.js.map +1 -0
  199. package/dist/components/sortable-list.cjs +3 -3
  200. package/dist/components/sortable-list.cjs.map +1 -1
  201. package/dist/components/spinner.cjs +2 -2
  202. package/dist/components/spinner.cjs.map +1 -1
  203. package/dist/components/stat-card.cjs +8 -8
  204. package/dist/components/stat-card.cjs.map +1 -1
  205. package/dist/components/stepper.cjs +3 -3
  206. package/dist/components/stepper.cjs.map +1 -1
  207. package/dist/components/stepper.js +1 -1
  208. package/dist/components/switch.cjs +3 -3
  209. package/dist/components/switch.cjs.map +1 -1
  210. package/dist/components/switch.js +1 -1
  211. package/dist/components/table.cjs +9 -9
  212. package/dist/components/table.cjs.map +1 -1
  213. package/dist/components/table.js +1 -1
  214. package/dist/components/tabs.cjs +8 -8
  215. package/dist/components/tabs.cjs.map +1 -1
  216. package/dist/components/tabs.js +1 -1
  217. package/dist/components/textarea.cjs +3 -3
  218. package/dist/components/textarea.cjs.map +1 -1
  219. package/dist/components/textarea.js +1 -1
  220. package/dist/components/theme.cjs +2 -2
  221. package/dist/components/theme.cjs.map +1 -1
  222. package/dist/components/theme.js +1 -1
  223. package/dist/components/thumbnail.cjs +3 -3
  224. package/dist/components/thumbnail.cjs.map +1 -1
  225. package/dist/components/time-picker.cjs +3 -3
  226. package/dist/components/time-picker.cjs.map +1 -1
  227. package/dist/components/toast.cjs +8 -8
  228. package/dist/components/toast.cjs.map +1 -1
  229. package/dist/components/toast.js +1 -1
  230. package/dist/components/tooltip.cjs +4 -4
  231. package/dist/components/tooltip.cjs.map +1 -1
  232. package/dist/components/tooltip.js +1 -1
  233. package/dist/components/typography.cjs +6 -6
  234. package/dist/components/typography.cjs.map +1 -1
  235. package/dist/components/wizard-layout.cjs +8 -8
  236. package/dist/components/wizard-layout.cjs.map +1 -1
  237. package/dist/lib/input-box-variants.cjs +9 -9
  238. package/dist/lib/input-box-variants.cjs.map +1 -1
  239. package/dist/number-flow-Bch81ixZ.d.cts +14 -0
  240. package/dist/number-flow-Bch81ixZ.d.ts +14 -0
  241. package/dist/styles/spark-default.css +11 -0
  242. package/dist/utils/build-sankey-nodes.cjs +72 -0
  243. package/dist/utils/build-sankey-nodes.cjs.map +1 -0
  244. package/dist/utils/build-sankey-nodes.d.cts +27 -0
  245. package/dist/utils/build-sankey-nodes.d.ts +27 -0
  246. package/dist/utils/build-sankey-nodes.js +71 -0
  247. package/dist/utils/build-sankey-nodes.js.map +1 -0
  248. package/dist/utils/cn.cjs +2 -2
  249. package/dist/utils/cn.d.cts +1 -1
  250. package/dist/utils/cn.d.ts +1 -1
  251. package/dist/utils/cn.js +1 -1
  252. package/dist/utils/format.cjs +13 -0
  253. package/dist/utils/format.cjs.map +1 -1
  254. package/dist/utils/format.d.cts +7 -1
  255. package/dist/utils/format.d.ts +7 -1
  256. package/dist/utils/format.js +12 -1
  257. package/dist/utils/format.js.map +1 -1
  258. package/package.json +2 -2
@@ -0,0 +1,71 @@
1
+ "use client";
2
+
3
+ import { TimeDomain } from "../utils/chart-zoom.cjs";
4
+
5
+ //#region src/components/power-history-chart.d.ts
6
+ interface PowerHistoryPoint {
7
+ /** Epoch milliseconds. */
8
+ t: number;
9
+ solar: number;
10
+ /** Signed: positive = import, negative = feed-in. */
11
+ grid: number;
12
+ /** Signed: positive = discharge, negative = charge. */
13
+ battery: number;
14
+ consumption: number;
15
+ }
16
+ interface PowerHistoryChartProps {
17
+ data: PowerHistoryPoint[];
18
+ /** Axis/tooltip unit, e.g. "kW". */
19
+ unit: string;
20
+ formatX: (t: number) => string;
21
+ /** Tooltip time formatter. Defaults to `formatX`; use to keep the tooltip's
22
+ * full clock time when the axis ticks are compact (hour-only). */
23
+ formatTooltipX?: (t: number) => string;
24
+ formatValue?: (value: number) => string;
25
+ labels: {
26
+ solar: string;
27
+ grid: string;
28
+ battery: string;
29
+ consumption: string;
30
+ };
31
+ /** Series to drop entirely (no legend entry, area, or tooltip row). */
32
+ omitSeries?: readonly PowerHistorySeriesKey[];
33
+ /** Epoch ms to mark with the current-time dashed line (omit to hide). */
34
+ highlightAt?: number;
35
+ title?: string;
36
+ className?: string;
37
+ height?: number;
38
+ zoomable?: boolean;
39
+ zoomResetLabel?: string;
40
+ zoomDomain?: TimeDomain | null;
41
+ onZoomDomainChange?: (domain: TimeDomain | null) => void;
42
+ }
43
+ declare const SERIES_KEYS: readonly ["solar", "grid", "battery", "consumption"];
44
+ type PowerHistorySeriesKey = (typeof SERIES_KEYS)[number];
45
+ /**
46
+ * Diverging "power sources" area chart over a continuous time axis: solar
47
+ * (orange), grid (blue) and battery (green) areas around a zero baseline, with
48
+ * consumption overlaid as a line. Negative values (grid feed-in, battery
49
+ * charging) dip below zero. For instantaneous power (day view). A legend at the
50
+ * top toggles each series on/off; the y-axis rescales to the visible ones. visx.
51
+ */
52
+ declare function PowerHistoryChart({
53
+ data,
54
+ unit,
55
+ formatX,
56
+ formatTooltipX,
57
+ formatValue,
58
+ labels,
59
+ omitSeries,
60
+ highlightAt,
61
+ title,
62
+ className,
63
+ height,
64
+ zoomable,
65
+ zoomResetLabel,
66
+ zoomDomain,
67
+ onZoomDomainChange
68
+ }: PowerHistoryChartProps): import("react/jsx-runtime").JSX.Element;
69
+ //#endregion
70
+ export { PowerHistoryChart, PowerHistoryChartProps, PowerHistoryPoint, PowerHistorySeriesKey };
71
+ //# sourceMappingURL=power-history-chart.d.cts.map
@@ -0,0 +1,71 @@
1
+ "use client";
2
+
3
+ import { TimeDomain } from "../utils/chart-zoom.js";
4
+
5
+ //#region src/components/power-history-chart.d.ts
6
+ interface PowerHistoryPoint {
7
+ /** Epoch milliseconds. */
8
+ t: number;
9
+ solar: number;
10
+ /** Signed: positive = import, negative = feed-in. */
11
+ grid: number;
12
+ /** Signed: positive = discharge, negative = charge. */
13
+ battery: number;
14
+ consumption: number;
15
+ }
16
+ interface PowerHistoryChartProps {
17
+ data: PowerHistoryPoint[];
18
+ /** Axis/tooltip unit, e.g. "kW". */
19
+ unit: string;
20
+ formatX: (t: number) => string;
21
+ /** Tooltip time formatter. Defaults to `formatX`; use to keep the tooltip's
22
+ * full clock time when the axis ticks are compact (hour-only). */
23
+ formatTooltipX?: (t: number) => string;
24
+ formatValue?: (value: number) => string;
25
+ labels: {
26
+ solar: string;
27
+ grid: string;
28
+ battery: string;
29
+ consumption: string;
30
+ };
31
+ /** Series to drop entirely (no legend entry, area, or tooltip row). */
32
+ omitSeries?: readonly PowerHistorySeriesKey[];
33
+ /** Epoch ms to mark with the current-time dashed line (omit to hide). */
34
+ highlightAt?: number;
35
+ title?: string;
36
+ className?: string;
37
+ height?: number;
38
+ zoomable?: boolean;
39
+ zoomResetLabel?: string;
40
+ zoomDomain?: TimeDomain | null;
41
+ onZoomDomainChange?: (domain: TimeDomain | null) => void;
42
+ }
43
+ declare const SERIES_KEYS: readonly ["solar", "grid", "battery", "consumption"];
44
+ type PowerHistorySeriesKey = (typeof SERIES_KEYS)[number];
45
+ /**
46
+ * Diverging "power sources" area chart over a continuous time axis: solar
47
+ * (orange), grid (blue) and battery (green) areas around a zero baseline, with
48
+ * consumption overlaid as a line. Negative values (grid feed-in, battery
49
+ * charging) dip below zero. For instantaneous power (day view). A legend at the
50
+ * top toggles each series on/off; the y-axis rescales to the visible ones. visx.
51
+ */
52
+ declare function PowerHistoryChart({
53
+ data,
54
+ unit,
55
+ formatX,
56
+ formatTooltipX,
57
+ formatValue,
58
+ labels,
59
+ omitSeries,
60
+ highlightAt,
61
+ title,
62
+ className,
63
+ height,
64
+ zoomable,
65
+ zoomResetLabel,
66
+ zoomDomain,
67
+ onZoomDomainChange
68
+ }: PowerHistoryChartProps): import("react/jsx-runtime").JSX.Element;
69
+ //#endregion
70
+ export { PowerHistoryChart, PowerHistoryChartProps, PowerHistoryPoint, PowerHistorySeriesKey };
71
+ //# sourceMappingURL=power-history-chart.d.ts.map
@@ -0,0 +1,496 @@
1
+ "use client";
2
+ import { useAnimatedChartDomain, useChartZoom } from "../hooks/use-chart-zoom.js";
3
+ import { useReducedMotion } from "../hooks/use-reduced-motion.js";
4
+ import { cn } from "../utils/cn.js";
5
+ import "../utils/chars.js";
6
+ import { twoDecimalFormat } from "../utils/format.js";
7
+ import { buildYAxisScale, formatYAxisTick, measureYAxisGutter } from "../utils/chart-scale.js";
8
+ import { ChartTooltipCard, ChartZoomResetButton, clampTooltipPosition, useMeasuredSize } from "./chart.js";
9
+ import { SeriesLegend } from "./series-legend.js";
10
+ import { SectionTitle } from "./typography.js";
11
+ import { buildTimeTicks } from "../utils/chart-time-ticks.js";
12
+ import * as React$1 from "react";
13
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
14
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
+ import { localPoint } from "@visx/event";
16
+ import { scaleLinear } from "@visx/scale";
17
+ import { Area, LinePath } from "@visx/shape";
18
+ import { curveMonotoneX } from "@visx/curve";
19
+ import { useTooltip } from "@visx/tooltip";
20
+ import { bisector } from "@visx/vendor/d3-array";
21
+ //#region src/components/power-history-chart.tsx
22
+ const AREA_KEYS = [
23
+ "solar",
24
+ "grid",
25
+ "battery"
26
+ ];
27
+ const SERIES_KEYS = [
28
+ "solar",
29
+ "grid",
30
+ "battery",
31
+ "consumption"
32
+ ];
33
+ const SERIES_COLORS = {
34
+ solar: "var(--orange-a9)",
35
+ grid: "var(--blue-9)",
36
+ battery: "var(--green-a9)",
37
+ consumption: "var(--neutral-12)"
38
+ };
39
+ const PADDING_TOP = 20;
40
+ const PADDING_BOTTOM = 28;
41
+ const PADDING_RIGHT = 8;
42
+ const X_TICK_COUNT = 6;
43
+ const DEFAULT_HEIGHT = 260;
44
+ const DETAILED_X_AXIS_THRESHOLD = 360 * 60 * 1e3;
45
+ const tBisector = bisector((d) => d.t);
46
+ function touchDistance(a, b) {
47
+ return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY);
48
+ }
49
+ function touchMidpoint(a, b) {
50
+ return {
51
+ clientX: (a.clientX + b.clientX) / 2,
52
+ clientY: (a.clientY + b.clientY) / 2
53
+ };
54
+ }
55
+ /**
56
+ * Diverging "power sources" area chart over a continuous time axis: solar
57
+ * (orange), grid (blue) and battery (green) areas around a zero baseline, with
58
+ * consumption overlaid as a line. Negative values (grid feed-in, battery
59
+ * charging) dip below zero. For instantaneous power (day view). A legend at the
60
+ * top toggles each series on/off; the y-axis rescales to the visible ones. visx.
61
+ */
62
+ function PowerHistoryChart({ data, unit, formatX, formatTooltipX, formatValue, labels, omitSeries, highlightAt, title, className, height = DEFAULT_HEIGHT, zoomable = false, zoomResetLabel, zoomDomain, onZoomDomainChange }) {
63
+ const plotClipId = React$1.useId();
64
+ const fmtTooltipX = formatTooltipX ?? formatX;
65
+ const containerRef = useRef(null);
66
+ const reducedMotion = useReducedMotion();
67
+ const [width, setWidth] = useState(0);
68
+ const [hiddenSeries, setHiddenSeries] = useState(() => /* @__PURE__ */ new Set());
69
+ const omitted = useMemo(() => new Set(omitSeries), [omitSeries]);
70
+ const toggleSeries = useCallback((id) => {
71
+ setHiddenSeries((prev) => {
72
+ const next = new Set(prev);
73
+ if (next.has(id)) next.delete(id);
74
+ else next.add(id);
75
+ return next;
76
+ });
77
+ }, []);
78
+ const isHidden = useCallback((key) => omitted.has(key) || hiddenSeries.has(key), [hiddenSeries, omitted]);
79
+ const { showTooltip, hideTooltip, tooltipData, tooltipLeft, tooltipTop, tooltipOpen } = useTooltip();
80
+ useEffect(() => {
81
+ const element = containerRef.current;
82
+ if (!element) return;
83
+ const resizeObserver = new ResizeObserver((entries) => {
84
+ const entry = entries[0];
85
+ if (!entry) return;
86
+ setWidth(entry.contentRect.width);
87
+ });
88
+ resizeObserver.observe(element);
89
+ return () => resizeObserver.disconnect();
90
+ }, []);
91
+ const fmtValue = (value) => formatValue ? formatValue(value) : `${twoDecimalFormat.format(value)} ${unit}`;
92
+ const tMin = data.length ? data[0]?.t ?? 0 : 0;
93
+ const tMax = data.length ? data[data.length - 1]?.t ?? 0 : 0;
94
+ const zoom = useChartZoom({
95
+ enabled: zoomable,
96
+ fullDomain: useMemo(() => {
97
+ if (data.length === 0) return null;
98
+ return [tMin, tMax];
99
+ }, [
100
+ data.length,
101
+ tMin,
102
+ tMax
103
+ ]),
104
+ zoomDomain,
105
+ onZoomDomainChange
106
+ });
107
+ const viewMin = zoom.zoomWindow?.[0] ?? tMin;
108
+ const viewMax = zoom.zoomWindow?.[1] ?? tMax;
109
+ const visibleData = useMemo(() => data.filter((d) => d.t >= viewMin && d.t <= viewMax), [
110
+ data,
111
+ viewMin,
112
+ viewMax
113
+ ]);
114
+ const { yMin, yMax } = useMemo(() => {
115
+ let min = 0;
116
+ let max = 0;
117
+ for (const d of visibleData) {
118
+ if (!isHidden("solar")) max = Math.max(max, d.solar);
119
+ if (!isHidden("consumption")) max = Math.max(max, d.consumption);
120
+ if (!isHidden("grid")) {
121
+ max = Math.max(max, d.grid);
122
+ min = Math.min(min, d.grid);
123
+ }
124
+ if (!isHidden("battery")) {
125
+ max = Math.max(max, d.battery);
126
+ min = Math.min(min, d.battery);
127
+ }
128
+ }
129
+ return {
130
+ yMin: min,
131
+ yMax: max
132
+ };
133
+ }, [visibleData, isHidden]);
134
+ const { ticks: yTicks, min: lo, max: hi } = useMemo(() => buildYAxisScale(yMin, yMax), [yMin, yMax]);
135
+ const displayedYDomain = useAnimatedChartDomain([lo, hi], {
136
+ animateChanges: zoom.isPinching,
137
+ reducedMotion
138
+ });
139
+ const gutter = useMemo(() => measureYAxisGutter(yTicks, formatYAxisTick), [yTicks]);
140
+ const plotRight = Math.max(gutter, width - PADDING_RIGHT);
141
+ const xScale = useMemo(() => scaleLinear({
142
+ domain: [viewMin, viewMax === viewMin ? viewMin + 1 : viewMax],
143
+ range: [gutter, plotRight]
144
+ }), [
145
+ viewMin,
146
+ viewMax,
147
+ plotRight,
148
+ gutter
149
+ ]);
150
+ const plotHeight = Math.max(0, height - PADDING_BOTTOM - PADDING_TOP);
151
+ const plotWidth = Math.max(0, plotRight - gutter);
152
+ const yScale = useMemo(() => scaleLinear({
153
+ domain: [displayedYDomain[0], displayedYDomain[1] === displayedYDomain[0] ? displayedYDomain[0] + 1 : displayedYDomain[1]],
154
+ range: [height - PADDING_BOTTOM, PADDING_TOP]
155
+ }), [displayedYDomain, height]);
156
+ const xTicks = useMemo(() => buildTimeTicks(viewMin, viewMax, X_TICK_COUNT), [viewMin, viewMax]);
157
+ const formatAxisX = zoom.isZoomed && viewMax - viewMin <= DETAILED_X_AXIS_THRESHOLD ? fmtTooltipX : formatX;
158
+ const domainXFromEvent = useCallback((event) => {
159
+ const point = localPoint(event);
160
+ if (!point) return null;
161
+ return xScale.invert(point.x);
162
+ }, [xScale]);
163
+ const handleTooltip = useCallback((event) => {
164
+ if (zoom.isBrushing || zoom.isPanning) return;
165
+ const point = localPoint(event);
166
+ if (!point || data.length === 0) return;
167
+ const xMs = xScale.invert(point.x);
168
+ const index = tBisector.left(data, xMs, 1);
169
+ const d0 = data[index - 1];
170
+ const d1 = data[index];
171
+ if (!d0) return;
172
+ const d = d1 && xMs - d0.t > d1.t - xMs ? d1 : d0;
173
+ showTooltip({
174
+ tooltipData: d,
175
+ tooltipLeft: xScale(d.t),
176
+ tooltipTop: PADDING_TOP
177
+ });
178
+ }, [
179
+ data,
180
+ showTooltip,
181
+ xScale,
182
+ zoom.isBrushing,
183
+ zoom.isPanning
184
+ ]);
185
+ const legendItems = SERIES_KEYS.filter((key) => !omitted.has(key)).map((key) => ({
186
+ id: key,
187
+ label: labels[key],
188
+ color: SERIES_COLORS[key]
189
+ }));
190
+ const tooltipRows = tooltipData ? SERIES_KEYS.filter((key) => !isHidden(key)).map((key) => ({
191
+ key,
192
+ label: labels[key],
193
+ color: SERIES_COLORS[key],
194
+ value: fmtValue(tooltipData[key])
195
+ })) : [];
196
+ const showHighlight = highlightAt !== void 0 && highlightAt >= viewMin && highlightAt <= viewMax;
197
+ const [tooltipRef, tooltipSize] = useMeasuredSize();
198
+ const tooltipPos = clampTooltipPosition({
199
+ anchorX: tooltipLeft ?? 0,
200
+ anchorY: tooltipTop ?? PADDING_TOP,
201
+ width,
202
+ height,
203
+ tipWidth: tooltipSize.width,
204
+ tipHeight: tooltipSize.height,
205
+ placement: "beside"
206
+ });
207
+ return /* @__PURE__ */ jsxs("div", {
208
+ className: cn("flex w-full flex-col gap-2", className),
209
+ children: [
210
+ title && /* @__PURE__ */ jsx(SectionTitle, { children: title }),
211
+ /* @__PURE__ */ jsx(SeriesLegend, {
212
+ items: legendItems,
213
+ hiddenIds: hiddenSeries,
214
+ onToggle: toggleSeries
215
+ }),
216
+ /* @__PURE__ */ jsx("span", {
217
+ className: "px-1 text-xs text-gray-light",
218
+ children: unit
219
+ }),
220
+ /* @__PURE__ */ jsxs("div", {
221
+ ref: containerRef,
222
+ className: "relative w-full",
223
+ style: { height },
224
+ children: [
225
+ width > 0 && /* @__PURE__ */ jsxs("svg", {
226
+ width,
227
+ height,
228
+ children: [
229
+ /* @__PURE__ */ jsxs("defs", { children: [AREA_KEYS.map((key) => /* @__PURE__ */ jsxs("linearGradient", {
230
+ id: `power-history-fill-${key}`,
231
+ x1: "0",
232
+ y1: "0",
233
+ x2: "0",
234
+ y2: "1",
235
+ children: [/* @__PURE__ */ jsx("stop", {
236
+ offset: "0%",
237
+ stopColor: SERIES_COLORS[key],
238
+ stopOpacity: .35
239
+ }), /* @__PURE__ */ jsx("stop", {
240
+ offset: "100%",
241
+ stopColor: SERIES_COLORS[key],
242
+ stopOpacity: .04
243
+ })]
244
+ }, key)), /* @__PURE__ */ jsx("clipPath", {
245
+ id: plotClipId,
246
+ children: /* @__PURE__ */ jsx("rect", {
247
+ height: plotHeight,
248
+ width: plotWidth,
249
+ x: gutter,
250
+ y: PADDING_TOP
251
+ })
252
+ })] }),
253
+ yTicks.map((tick) => {
254
+ const y = yScale(tick);
255
+ return /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("line", {
256
+ x1: gutter,
257
+ x2: plotRight,
258
+ y1: y,
259
+ y2: y,
260
+ stroke: "var(--neutral-a4)",
261
+ strokeWidth: 1
262
+ }), /* @__PURE__ */ jsx("text", {
263
+ x: gutter - 7,
264
+ y: y + 3,
265
+ textAnchor: "end",
266
+ fill: "var(--neutral-a11)",
267
+ fontSize: 10,
268
+ children: formatYAxisTick(tick)
269
+ })] }, `y-tick-${tick}`);
270
+ }),
271
+ /* @__PURE__ */ jsxs("g", {
272
+ clipPath: `url(#${plotClipId})`,
273
+ "data-testid": "chart-plot-clip",
274
+ children: [
275
+ AREA_KEYS.filter((key) => !isHidden(key)).map((key) => /* @__PURE__ */ jsx(Area, {
276
+ data,
277
+ x: (d) => xScale(d.t),
278
+ y0: yScale(0),
279
+ y1: (d) => yScale(d[key]),
280
+ curve: curveMonotoneX,
281
+ fill: `url(#power-history-fill-${key})`,
282
+ stroke: "transparent"
283
+ }, `area-${key}`)),
284
+ AREA_KEYS.filter((key) => !isHidden(key)).map((key) => /* @__PURE__ */ jsx(LinePath, {
285
+ data,
286
+ x: (d) => xScale(d.t),
287
+ y: (d) => yScale(d[key]),
288
+ curve: curveMonotoneX,
289
+ stroke: SERIES_COLORS[key],
290
+ strokeWidth: 1.5,
291
+ fill: "none"
292
+ }, `line-${key}`)),
293
+ !isHidden("consumption") && /* @__PURE__ */ jsx(LinePath, {
294
+ data,
295
+ x: (d) => xScale(d.t),
296
+ y: (d) => yScale(d.consumption),
297
+ curve: curveMonotoneX,
298
+ stroke: SERIES_COLORS.consumption,
299
+ strokeWidth: 1.5,
300
+ fill: "none"
301
+ })
302
+ ]
303
+ }),
304
+ /* @__PURE__ */ jsx("line", {
305
+ x1: gutter,
306
+ x2: plotRight,
307
+ y1: yScale(0),
308
+ y2: yScale(0),
309
+ stroke: "var(--neutral-a6)",
310
+ strokeWidth: 1
311
+ }),
312
+ showHighlight && /* @__PURE__ */ jsx("line", {
313
+ x1: xScale(highlightAt),
314
+ x2: xScale(highlightAt),
315
+ y1: PADDING_TOP,
316
+ y2: height - PADDING_BOTTOM,
317
+ stroke: "var(--spark-a9)",
318
+ strokeWidth: 1.5,
319
+ strokeDasharray: "5 4",
320
+ pointerEvents: "none"
321
+ }),
322
+ xTicks.map((tick, index) => /* @__PURE__ */ jsx("text", {
323
+ x: xScale(tick),
324
+ y: height - PADDING_BOTTOM + 16,
325
+ textAnchor: index === 0 ? "start" : index === xTicks.length - 1 ? "end" : "middle",
326
+ fill: "var(--neutral-a11)",
327
+ fontSize: 10,
328
+ children: formatAxisX(tick)
329
+ }, `x-tick-${tick}`)),
330
+ tooltipOpen && tooltipData && !zoom.isBrushing && !zoom.isPanning && /* @__PURE__ */ jsxs(Fragment, { children: [
331
+ /* @__PURE__ */ jsx("line", {
332
+ x1: xScale(tooltipData.t),
333
+ x2: xScale(tooltipData.t),
334
+ y1: PADDING_TOP,
335
+ y2: height - PADDING_BOTTOM,
336
+ stroke: "var(--neutral-a6)",
337
+ strokeDasharray: "4 4",
338
+ pointerEvents: "none"
339
+ }),
340
+ AREA_KEYS.filter((key) => !isHidden(key)).map((key) => /* @__PURE__ */ jsx("circle", {
341
+ cx: xScale(tooltipData.t),
342
+ cy: yScale(tooltipData[key]),
343
+ r: 3.5,
344
+ fill: SERIES_COLORS[key],
345
+ stroke: "white",
346
+ strokeWidth: 1.5,
347
+ pointerEvents: "none"
348
+ }, `dot-${key}`)),
349
+ !isHidden("consumption") && /* @__PURE__ */ jsx("circle", {
350
+ cx: xScale(tooltipData.t),
351
+ cy: yScale(tooltipData.consumption),
352
+ r: 3.5,
353
+ fill: SERIES_COLORS.consumption,
354
+ stroke: "white",
355
+ strokeWidth: 1.5,
356
+ pointerEvents: "none"
357
+ })
358
+ ] }),
359
+ zoom.brushBand && /* @__PURE__ */ jsx("rect", {
360
+ "data-testid": "chart-brush-band",
361
+ x: Math.min(xScale(zoom.brushBand[0]), xScale(zoom.brushBand[1])),
362
+ y: PADDING_TOP,
363
+ width: Math.abs(xScale(zoom.brushBand[1]) - xScale(zoom.brushBand[0])),
364
+ height: plotHeight,
365
+ fill: "var(--spark-a4)",
366
+ pointerEvents: "none"
367
+ }),
368
+ /* @__PURE__ */ jsx("rect", {
369
+ x: gutter,
370
+ y: PADDING_TOP,
371
+ width: plotWidth,
372
+ height: plotHeight,
373
+ fill: "transparent",
374
+ style: zoomable ? { touchAction: "pan-y" } : void 0,
375
+ onMouseLeave: () => {
376
+ if (!zoom.isBrushing) hideTooltip();
377
+ },
378
+ onMouseMove: (event) => {
379
+ if (zoom.isBrushing) {
380
+ const domainX = domainXFromEvent(event);
381
+ if (domainX != null) zoom.onPointerMove(domainX);
382
+ return;
383
+ }
384
+ handleTooltip(event);
385
+ },
386
+ onPointerDown: (event) => {
387
+ if (!zoomable || event.pointerType === "touch") return;
388
+ if (event.button !== 0) return;
389
+ const domainX = domainXFromEvent(event);
390
+ if (domainX == null) return;
391
+ event.currentTarget.setPointerCapture?.(event.pointerId);
392
+ hideTooltip();
393
+ zoom.onPointerDown(domainX);
394
+ },
395
+ onPointerMove: (event) => {
396
+ if (!zoomable) return;
397
+ const domainX = domainXFromEvent(event);
398
+ if (domainX != null) zoom.onPointerMove(domainX);
399
+ },
400
+ onPointerUp: (event) => {
401
+ if (!zoomable) return;
402
+ const domainX = domainXFromEvent(event);
403
+ if (domainX != null) zoom.onPointerUp(domainX);
404
+ else zoom.onPointerCancel();
405
+ },
406
+ onPointerCancel: () => {
407
+ zoom.onPointerCancel();
408
+ },
409
+ onDoubleClick: () => {
410
+ if (zoomable) zoom.onDoubleClick();
411
+ },
412
+ onTouchStart: (event) => {
413
+ if (!zoomable) return;
414
+ if (event.touches.length === 2) {
415
+ const [a, b] = [event.touches[0], event.touches[1]];
416
+ if (!a || !b) return;
417
+ const point = localPoint(event.currentTarget, touchMidpoint(a, b));
418
+ if (!point) return;
419
+ const midpoint = Math.min(1, Math.max(0, (point.x - gutter) / plotWidth));
420
+ zoom.onPinchStart(touchDistance(a, b), midpoint);
421
+ hideTooltip();
422
+ return;
423
+ }
424
+ if (event.touches.length === 1 && zoom.isZoomed) {
425
+ const point = localPoint(event);
426
+ if (!point) return;
427
+ zoom.onPanStart({
428
+ x: (point.x - gutter) / plotWidth,
429
+ y: (point.y - PADDING_TOP) / plotHeight
430
+ });
431
+ hideTooltip();
432
+ }
433
+ },
434
+ onTouchMove: (event) => {
435
+ if (zoomable && event.touches.length === 2) {
436
+ const [a, b] = [event.touches[0], event.touches[1]];
437
+ if (!a || !b) return;
438
+ const point = localPoint(event.currentTarget, touchMidpoint(a, b));
439
+ if (!point) return;
440
+ const midpoint = Math.min(1, Math.max(0, (point.x - gutter) / plotWidth));
441
+ zoom.onPinchMove(touchDistance(a, b), midpoint);
442
+ return;
443
+ }
444
+ if (event.touches.length === 1 && zoom.isZoomed) {
445
+ const point = localPoint(event);
446
+ if (!point) return;
447
+ zoom.onPanMove({
448
+ x: (point.x - gutter) / plotWidth,
449
+ y: (point.y - PADDING_TOP) / plotHeight
450
+ });
451
+ return;
452
+ }
453
+ if (event.touches.length === 1) handleTooltip(event);
454
+ },
455
+ onTouchEnd: (event) => {
456
+ if (zoomable && event.touches.length < 2) zoom.onPinchEnd();
457
+ if (event.touches.length === 0) {
458
+ if (zoomable) zoom.onPanEnd();
459
+ if (zoomable) zoom.onTap();
460
+ hideTooltip();
461
+ }
462
+ },
463
+ onTouchCancel: () => {
464
+ zoom.onPinchEnd();
465
+ zoom.onPanEnd();
466
+ }
467
+ })
468
+ ]
469
+ }),
470
+ zoom.isZoomed && zoomResetLabel && /* @__PURE__ */ jsx(ChartZoomResetButton, {
471
+ label: zoomResetLabel,
472
+ onReset: zoom.resetZoom
473
+ }),
474
+ /* @__PURE__ */ jsx("div", {
475
+ ref: tooltipRef,
476
+ className: "pointer-events-none absolute z-10",
477
+ style: {
478
+ left: tooltipPos.left,
479
+ top: tooltipPos.top,
480
+ opacity: tooltipOpen && tooltipData && !zoom.isPanning && !zoom.isBrushing ? 1 : 0,
481
+ transition: reducedMotion ? "opacity 140ms ease-out" : "left 140ms ease-out, top 140ms ease-out, opacity 140ms ease-out"
482
+ },
483
+ children: tooltipData && /* @__PURE__ */ jsx(ChartTooltipCard, {
484
+ label: fmtTooltipX(tooltipData.t),
485
+ rows: tooltipRows
486
+ })
487
+ })
488
+ ]
489
+ })
490
+ ]
491
+ });
492
+ }
493
+ //#endregion
494
+ export { PowerHistoryChart };
495
+
496
+ //# sourceMappingURL=power-history-chart.js.map