@epilot/spark-ui 0.20.0 → 0.20.1

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 (104) hide show
  1. package/dist/components/accordion.cjs +2 -2
  2. package/dist/components/accordion.d.ts +1 -1
  3. package/dist/components/accordion.js +1 -1
  4. package/dist/components/alert-dialog.cjs +1 -1
  5. package/dist/components/alert-dialog.js +1 -1
  6. package/dist/components/autocomplete.cjs +1 -1
  7. package/dist/components/autocomplete.js +1 -1
  8. package/dist/components/bar-chart-with-total.d.cts +1 -1
  9. package/dist/components/bar-chart-with-total.d.ts +1 -1
  10. package/dist/components/bar-chart.cjs +1 -1
  11. package/dist/components/bar-chart.js +2 -2
  12. package/dist/components/calendar.cjs +1 -1
  13. package/dist/components/calendar.js +1 -1
  14. package/dist/components/callout.cjs +1 -1
  15. package/dist/components/callout.d.cts +1 -1
  16. package/dist/components/callout.d.ts +2 -2
  17. package/dist/components/callout.js +1 -1
  18. package/dist/components/dialog.cjs +1 -1
  19. package/dist/components/dialog.js +1 -1
  20. package/dist/components/dropdown-menu.cjs +1 -1
  21. package/dist/components/dropdown-menu.js +1 -1
  22. package/dist/components/empty-state.d.ts +1 -1
  23. package/dist/components/energy-breakdown.cjs +80 -0
  24. package/dist/components/energy-breakdown.cjs.map +1 -0
  25. package/dist/components/energy-breakdown.d.cts +24 -0
  26. package/dist/components/energy-breakdown.d.ts +24 -0
  27. package/dist/components/energy-breakdown.js +79 -0
  28. package/dist/components/energy-breakdown.js.map +1 -0
  29. package/dist/components/energy-history-chart.cjs +63 -0
  30. package/dist/components/energy-history-chart.cjs.map +1 -0
  31. package/dist/components/energy-history-chart.d.cts +64 -0
  32. package/dist/components/energy-history-chart.d.ts +64 -0
  33. package/dist/components/energy-history-chart.js +62 -0
  34. package/dist/components/energy-history-chart.js.map +1 -0
  35. package/dist/components/entity-card.d.ts +1 -1
  36. package/dist/components/input-date.cjs +1 -1
  37. package/dist/components/input-date.js +1 -1
  38. package/dist/components/input-group.d.cts +1 -1
  39. package/dist/components/input-group.d.ts +1 -1
  40. package/dist/components/line-chart.cjs +3 -3
  41. package/dist/components/line-chart.js +4 -4
  42. package/dist/components/message-scroller.cjs +2 -2
  43. package/dist/components/message-scroller.js +2 -2
  44. package/dist/components/number-flow.d.cts +2 -12
  45. package/dist/components/number-flow.d.ts +2 -12
  46. package/dist/components/page-header.cjs +1 -1
  47. package/dist/components/page-header.js +1 -1
  48. package/dist/components/pill.cjs +1 -1
  49. package/dist/components/pill.js +1 -1
  50. package/dist/components/power-history-chart.cjs +498 -0
  51. package/dist/components/power-history-chart.cjs.map +1 -0
  52. package/dist/components/power-history-chart.d.cts +71 -0
  53. package/dist/components/power-history-chart.d.ts +71 -0
  54. package/dist/components/power-history-chart.js +496 -0
  55. package/dist/components/power-history-chart.js.map +1 -0
  56. package/dist/components/power-network.cjs +559 -0
  57. package/dist/components/power-network.cjs.map +1 -0
  58. package/dist/components/power-network.d.cts +80 -0
  59. package/dist/components/power-network.d.ts +80 -0
  60. package/dist/components/power-network.js +553 -0
  61. package/dist/components/power-network.js.map +1 -0
  62. package/dist/components/sankey-chart.cjs +11 -1
  63. package/dist/components/sankey-chart.cjs.map +1 -1
  64. package/dist/components/sankey-chart.d.cts +3 -2
  65. package/dist/components/sankey-chart.d.ts +3 -2
  66. package/dist/components/sankey-chart.js +11 -2
  67. package/dist/components/sankey-chart.js.map +1 -1
  68. package/dist/components/schedule-list.cjs +1 -1
  69. package/dist/components/schedule-list.js +1 -1
  70. package/dist/components/select.cjs +1 -1
  71. package/dist/components/select.js +1 -1
  72. package/dist/components/sheet.cjs +1 -1
  73. package/dist/components/sheet.js +1 -1
  74. package/dist/components/solar-forecast-chart.cjs +108 -0
  75. package/dist/components/solar-forecast-chart.cjs.map +1 -0
  76. package/dist/components/solar-forecast-chart.d.cts +77 -0
  77. package/dist/components/solar-forecast-chart.d.ts +77 -0
  78. package/dist/components/solar-forecast-chart.js +103 -0
  79. package/dist/components/solar-forecast-chart.js.map +1 -0
  80. package/dist/components/stepper.cjs +1 -1
  81. package/dist/components/stepper.js +1 -1
  82. package/dist/components/table.cjs +1 -1
  83. package/dist/components/table.js +1 -1
  84. package/dist/components/tabs.cjs +1 -1
  85. package/dist/components/tabs.js +1 -1
  86. package/dist/components/toast.cjs +1 -1
  87. package/dist/components/toast.js +1 -1
  88. package/dist/components/tooltip.cjs +1 -1
  89. package/dist/components/tooltip.js +1 -1
  90. package/dist/number-flow-Bch81ixZ.d.cts +14 -0
  91. package/dist/number-flow-Bch81ixZ.d.ts +14 -0
  92. package/dist/utils/build-sankey-nodes.cjs +72 -0
  93. package/dist/utils/build-sankey-nodes.cjs.map +1 -0
  94. package/dist/utils/build-sankey-nodes.d.cts +27 -0
  95. package/dist/utils/build-sankey-nodes.d.ts +27 -0
  96. package/dist/utils/build-sankey-nodes.js +71 -0
  97. package/dist/utils/build-sankey-nodes.js.map +1 -0
  98. package/dist/utils/format.cjs +13 -0
  99. package/dist/utils/format.cjs.map +1 -1
  100. package/dist/utils/format.d.cts +7 -1
  101. package/dist/utils/format.d.ts +7 -1
  102. package/dist/utils/format.js +12 -1
  103. package/dist/utils/format.js.map +1 -1
  104. package/package.json +1 -1
@@ -0,0 +1,103 @@
1
+ "use client";
2
+ import { cn } from "../utils/cn.js";
3
+ import "../utils/chars.js";
4
+ import { oneDecimalFormat } from "../utils/format.js";
5
+ import { LineChart } from "./line-chart.js";
6
+ import { useEffect, useRef, useState } from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ //#region src/components/solar-forecast-chart.tsx
9
+ const DEFAULT_HEIGHT = 220;
10
+ const BASE_MARGIN = {
11
+ top: 8,
12
+ right: 8,
13
+ bottom: 28,
14
+ left: 34
15
+ };
16
+ /** Extra headroom for the weather icon row pinned above the plot. */
17
+ const WEATHER_ROW_HEIGHT = 24;
18
+ function defaultFormatValue(value) {
19
+ return `${oneDecimalFormat.format(value)} kW`;
20
+ }
21
+ function defaultFormatTime(date) {
22
+ return date.getHours().toString().padStart(2, "0");
23
+ }
24
+ /**
25
+ * Responsive line plot of measured production (solid) against a forecast
26
+ * (dashed) over a day. Optionally overlays hourly weather icons and
27
+ * sunrise/sunset markers, all pinned to the same time axis. Sizes itself to its
28
+ * container; compose a title and any summary around it.
29
+ */
30
+ function SolarForecastChart({ produced, forecast, producedLabel = "Produced", forecastLabel = "Forecast", formatValue = defaultFormatValue, yAxisUnit = "kW", formatTime = defaultFormatTime, formatTooltipTime, weather, sunrise, sunset, formatMarkerTime, curve = "monotone", height = DEFAULT_HEIGHT, className, zoomable, zoomResetLabel, zoomDomain, onZoomDomainChange }) {
31
+ const containerRef = useRef(null);
32
+ const [width, setWidth] = useState(0);
33
+ useEffect(() => {
34
+ const el = containerRef.current;
35
+ if (!el) return;
36
+ const ro = new ResizeObserver((entries) => {
37
+ const entry = entries[0];
38
+ if (entry) setWidth(entry.contentRect.width);
39
+ });
40
+ ro.observe(el);
41
+ return () => ro.disconnect();
42
+ }, []);
43
+ const hasWeather = weather != null && weather.length > 0;
44
+ const margin = {
45
+ ...BASE_MARGIN,
46
+ top: hasWeather ? BASE_MARGIN.top + WEATHER_ROW_HEIGHT : BASE_MARGIN.top
47
+ };
48
+ const formatMarker = formatMarkerTime ?? formatTooltipTime ?? formatTime;
49
+ const topContent = hasWeather ? weather.map((point, i) => {
50
+ const WeatherGlyph = point.icon;
51
+ return {
52
+ key: `weather-${point.date.valueOf()}-${i}`,
53
+ date: point.date,
54
+ content: /* @__PURE__ */ jsx(WeatherGlyph, { className: "text-gray-light [--spark-icon-size:18px]" })
55
+ };
56
+ }) : void 0;
57
+ const referenceLines = [sunrise && {
58
+ key: "sunrise",
59
+ date: sunrise,
60
+ label: formatMarker(sunrise)
61
+ }, sunset && {
62
+ key: "sunset",
63
+ date: sunset,
64
+ label: formatMarker(sunset)
65
+ }].filter((x) => Boolean(x));
66
+ return /* @__PURE__ */ jsx("div", {
67
+ ref: containerRef,
68
+ className: cn("w-full", className),
69
+ children: width > 0 && /* @__PURE__ */ jsx(LineChart, {
70
+ width,
71
+ height,
72
+ margin,
73
+ curve,
74
+ series: [{
75
+ id: "produced",
76
+ data: produced,
77
+ label: producedLabel
78
+ }, {
79
+ id: "forecast",
80
+ data: forecast,
81
+ label: forecastLabel,
82
+ dashed: true
83
+ }],
84
+ showGrid: true,
85
+ xTickCount: 6,
86
+ yTickCount: 4,
87
+ yAxisUnit,
88
+ formatDate: formatTime,
89
+ formatTooltipDate: formatTooltipTime ?? formatTime,
90
+ formatValue,
91
+ referenceLines: referenceLines.length > 0 ? referenceLines : void 0,
92
+ topContent,
93
+ zoomable,
94
+ zoomResetLabel,
95
+ zoomDomain,
96
+ onZoomDomainChange
97
+ })
98
+ });
99
+ }
100
+ //#endregion
101
+ export { SolarForecastChart, SolarForecastChart as default };
102
+
103
+ //# sourceMappingURL=solar-forecast-chart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solar-forecast-chart.js","names":[],"sources":["../../src/components/solar-forecast-chart.tsx"],"sourcesContent":["import type { SparkIcon } from '@epilot/spark-icons'\nimport { type JSX, type ReactNode, useEffect, useRef, useState } from 'react'\nimport { NARROW_NBSP } from '../utils/chars'\nimport { cn } from '../utils/cn'\nimport { oneDecimalFormat } from '../utils/format'\nimport {\n LineChart,\n type LineChartCurve,\n type LineChartDatum,\n} from './line-chart'\n\nconst DEFAULT_HEIGHT = 220\n// `left` leaves room for the y-axis tick labels + unit (matches the 34px gutter\n// used by BarChart / PowerHistoryChart).\nconst BASE_MARGIN = { top: 8, right: 8, bottom: 28, left: 34 }\n/** Extra headroom for the weather icon row pinned above the plot. */\nconst WEATHER_ROW_HEIGHT = 24\n\nfunction defaultFormatValue(value: number): string {\n return `${oneDecimalFormat.format(value)}${NARROW_NBSP}kW`\n}\n\nfunction defaultFormatTime(date: Date): string {\n return date.getHours().toString().padStart(2, '0')\n}\n\nexport interface SolarForecastWeatherPoint {\n date: Date\n icon: SparkIcon\n}\n\nexport interface SolarForecastChartProps {\n /** Measured production, drawn as the solid line. */\n produced: LineChartDatum[]\n /** Predicted production, drawn as the dashed line. */\n forecast: LineChartDatum[]\n /** Tooltip label for the produced series. */\n producedLabel?: ReactNode\n /** Tooltip label for the forecast series. */\n forecastLabel?: ReactNode\n /** Tooltip value formatter. Defaults to one-decimal kW (power). */\n formatValue?: (value: number) => string\n /** Unit shown once atop the y-axis; ticks render as plain numbers. Defaults to \"kW\". */\n yAxisUnit?: string\n /** X-axis tick formatter. Defaults to the zero-padded hour. */\n formatTime?: (date: Date) => string\n /** Tooltip time formatter. Defaults to `formatTime`. */\n formatTooltipTime?: (date: Date) => string\n /**\n * Hourly weather icons drawn along the top of the plot, aligned to the time\n * axis. Their dates must fall within the plotted day.\n */\n weather?: SolarForecastWeatherPoint[]\n /** Sunrise time, drawn as a labelled vertical marker on the plot. */\n sunrise?: Date\n /** Sunset time, drawn as a labelled vertical marker on the plot. */\n sunset?: Date\n /** Label formatter for the sunrise/sunset markers. Defaults to `formatTime`. */\n formatMarkerTime?: (date: Date) => string\n curve?: LineChartCurve\n height?: number\n className?: string\n zoomable?: boolean\n zoomResetLabel?: string\n zoomDomain?: [Date, Date] | null\n onZoomDomainChange?: (domain: [Date, Date] | null) => void\n}\n\n/**\n * Responsive line plot of measured production (solid) against a forecast\n * (dashed) over a day. Optionally overlays hourly weather icons and\n * sunrise/sunset markers, all pinned to the same time axis. Sizes itself to its\n * container; compose a title and any summary around it.\n */\nexport function SolarForecastChart({\n produced,\n forecast,\n producedLabel = 'Produced',\n forecastLabel = 'Forecast',\n formatValue = defaultFormatValue,\n yAxisUnit = 'kW',\n formatTime = defaultFormatTime,\n formatTooltipTime,\n weather,\n sunrise,\n sunset,\n formatMarkerTime,\n curve = 'monotone',\n height = DEFAULT_HEIGHT,\n className,\n zoomable,\n zoomResetLabel,\n zoomDomain,\n onZoomDomainChange,\n}: SolarForecastChartProps): JSX.Element {\n const containerRef = useRef<HTMLDivElement>(null)\n const [width, setWidth] = useState(0)\n\n useEffect(() => {\n const el = containerRef.current\n if (!el) return\n const ro = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (entry) setWidth(entry.contentRect.width)\n })\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n const hasWeather = weather != null && weather.length > 0\n const margin = {\n ...BASE_MARGIN,\n top: hasWeather ? BASE_MARGIN.top + WEATHER_ROW_HEIGHT : BASE_MARGIN.top,\n }\n const formatMarker = formatMarkerTime ?? formatTooltipTime ?? formatTime\n\n const topContent = hasWeather\n ? weather.map((point, i) => {\n const WeatherGlyph = point.icon\n return {\n key: `weather-${point.date.valueOf()}-${i}`,\n date: point.date,\n content: (\n <WeatherGlyph className=\"text-gray-light [--spark-icon-size:18px]\" />\n ),\n }\n })\n : undefined\n\n const referenceLines = [\n sunrise && { key: 'sunrise', date: sunrise, label: formatMarker(sunrise) },\n sunset && { key: 'sunset', date: sunset, label: formatMarker(sunset) },\n ].filter((x): x is { key: string; date: Date; label: string } => Boolean(x))\n\n return (\n <div ref={containerRef} className={cn('w-full', className)}>\n {width > 0 && (\n <LineChart\n width={width}\n height={height}\n margin={margin}\n curve={curve}\n series={[\n { id: 'produced', data: produced, label: producedLabel },\n {\n id: 'forecast',\n data: forecast,\n label: forecastLabel,\n dashed: true,\n },\n ]}\n showGrid\n xTickCount={6}\n yTickCount={4}\n yAxisUnit={yAxisUnit}\n formatDate={formatTime}\n formatTooltipDate={formatTooltipTime ?? formatTime}\n formatValue={formatValue}\n referenceLines={\n referenceLines.length > 0 ? referenceLines : undefined\n }\n topContent={topContent}\n zoomable={zoomable}\n zoomResetLabel={zoomResetLabel}\n zoomDomain={zoomDomain}\n onZoomDomainChange={onZoomDomainChange}\n />\n )}\n </div>\n )\n}\n\nexport default SolarForecastChart\n"],"mappings":";;;;;;;;AAWA,MAAM,iBAAiB;AAGvB,MAAM,cAAc;CAAE,KAAK;CAAG,OAAO;CAAG,QAAQ;CAAI,MAAM;AAAG;;AAE7D,MAAM,qBAAqB;AAE3B,SAAS,mBAAmB,OAAuB;CACjD,OAAO,GAAG,iBAAiB,OAAO,KAAK;AACzC;AAEA,SAAS,kBAAkB,MAAoB;CAC7C,OAAO,KAAK,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG;AACnD;;;;;;;AAkDA,SAAgB,mBAAmB,EACjC,UACA,UACA,gBAAgB,YAChB,gBAAgB,YAChB,cAAc,oBACd,YAAY,MACZ,aAAa,mBACb,mBACA,SACA,SACA,QACA,kBACA,QAAQ,YACR,SAAS,gBACT,WACA,UACA,gBACA,YACA,sBACuC;CACvC,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,CAAC,OAAO,YAAY,SAAS,CAAC;CAEpC,gBAAgB;EACd,MAAM,KAAK,aAAa;EACxB,IAAI,CAAC,IAAI;EACT,MAAM,KAAK,IAAI,gBAAgB,YAAY;GACzC,MAAM,QAAQ,QAAQ;GACtB,IAAI,OAAO,SAAS,MAAM,YAAY,KAAK;EAC7C,CAAC;EACD,GAAG,QAAQ,EAAE;EACb,aAAa,GAAG,WAAW;CAC7B,GAAG,CAAC,CAAC;CAEL,MAAM,aAAa,WAAW,QAAQ,QAAQ,SAAS;CACvD,MAAM,SAAS;EACb,GAAG;EACH,KAAK,aAAa,YAAY,MAAM,qBAAqB,YAAY;CACvE;CACA,MAAM,eAAe,oBAAoB,qBAAqB;CAE9D,MAAM,aAAa,aACf,QAAQ,KAAK,OAAO,MAAM;EACxB,MAAM,eAAe,MAAM;EAC3B,OAAO;GACL,KAAK,WAAW,MAAM,KAAK,QAAQ,EAAE,GAAG;GACxC,MAAM,MAAM;GACZ,SACE,oBAAC,cAAD,EAAc,WAAU,2CAA4C,CAAA;EAExE;CACF,CAAC,IACD,KAAA;CAEJ,MAAM,iBAAiB,CACrB,WAAW;EAAE,KAAK;EAAW,MAAM;EAAS,OAAO,aAAa,OAAO;CAAE,GACzE,UAAU;EAAE,KAAK;EAAU,MAAM;EAAQ,OAAO,aAAa,MAAM;CAAE,CACvE,EAAE,QAAQ,MAAuD,QAAQ,CAAC,CAAC;CAE3E,OACE,oBAAC,OAAD;EAAK,KAAK;EAAc,WAAW,GAAG,UAAU,SAAS;YACtD,QAAQ,KACP,oBAAC,WAAD;GACS;GACC;GACA;GACD;GACP,QAAQ,CACN;IAAE,IAAI;IAAY,MAAM;IAAU,OAAO;GAAc,GACvD;IACE,IAAI;IACJ,MAAM;IACN,OAAO;IACP,QAAQ;GACV,CACF;GACA,UAAA;GACA,YAAY;GACZ,YAAY;GACD;GACX,YAAY;GACZ,mBAAmB,qBAAqB;GAC3B;GACb,gBACE,eAAe,SAAS,IAAI,iBAAiB,KAAA;GAEnC;GACF;GACM;GACJ;GACQ;EACrB,CAAA;CAEA,CAAA;AAET"}
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_chunk = require("../chunk-DY84GLCh.cjs");
4
- let _epilot_spark_icons = require("@epilot/spark-icons");
5
4
  let react = require("react");
6
5
  react = require_chunk.__toESM(react);
6
+ let _epilot_spark_icons = require("@epilot/spark-icons");
7
7
  let react_jsx_runtime = require("react/jsx-runtime");
8
8
  let cnfast = require("cnfast");
9
9
  //#region src/components/stepper.tsx
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../utils/cn.js";
3
- import { ChevronLeft, ChevronRight } from "@epilot/spark-icons";
4
3
  import * as React$1 from "react";
4
+ import { ChevronLeft, ChevronRight } from "@epilot/spark-icons";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/components/stepper.tsx
7
7
  const Stepper = React$1.forwardRef(({ value, onValueChange, onValueCommit, min = 0, max = 100, step = 1, formatValue = (v) => String(v), disabled, className, commitDelayMs = 400, "aria-label": ariaLabel, decreaseLabel, increaseLabel }, ref) => {
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_chunk = require("../chunk-DY84GLCh.cjs");
4
4
  const require_components_skeleton = require("./skeleton.cjs");
5
- let _epilot_spark_icons = require("@epilot/spark-icons");
6
5
  let react = require("react");
7
6
  react = require_chunk.__toESM(react);
7
+ let _epilot_spark_icons = require("@epilot/spark-icons");
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
9
  let cnfast = require("cnfast");
10
10
  //#region src/components/table.tsx
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cn } from "../utils/cn.js";
3
3
  import { Skeleton } from "./skeleton.js";
4
- import { ArrowDownward, ArrowUpward, SwapVert } from "@epilot/spark-icons";
5
4
  import * as React$1 from "react";
5
+ import { ArrowDownward, ArrowUpward, SwapVert } from "@epilot/spark-icons";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region src/components/table.tsx
8
8
  const TableContext = React$1.createContext({
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_chunk = require("../chunk-DY84GLCh.cjs");
4
- let _epilot_spark_icons = require("@epilot/spark-icons");
5
4
  let react = require("react");
6
5
  react = require_chunk.__toESM(react);
6
+ let _epilot_spark_icons = require("@epilot/spark-icons");
7
7
  let react_jsx_runtime = require("react/jsx-runtime");
8
8
  let _base_ui_react_tabs = require("@base-ui/react/tabs");
9
9
  let cnfast = require("cnfast");
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../utils/cn.js";
3
- import { ChevronLeft, ChevronRight } from "@epilot/spark-icons";
4
3
  import * as React$1 from "react";
4
+ import { ChevronLeft, ChevronRight } from "@epilot/spark-icons";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { Tabs as Tabs$1 } from "@base-ui/react/tabs";
7
7
  //#region src/components/tabs.tsx
@@ -4,9 +4,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  const require_chunk = require("../chunk-DY84GLCh.cjs");
5
5
  const require_components_button = require("./button.cjs");
6
6
  const require_components_spinner = require("./spinner.cjs");
7
- let _epilot_spark_icons = require("@epilot/spark-icons");
8
7
  let react = require("react");
9
8
  react = require_chunk.__toESM(react);
9
+ let _epilot_spark_icons = require("@epilot/spark-icons");
10
10
  let react_jsx_runtime = require("react/jsx-runtime");
11
11
  let _base_ui_react_toast = require("@base-ui/react/toast");
12
12
  let cnfast = require("cnfast");
@@ -3,8 +3,8 @@
3
3
  import { cn } from "../utils/cn.js";
4
4
  import { buttonVariants } from "./button.js";
5
5
  import { Spinner } from "./spinner.js";
6
- import { Cancel, CheckCircle, Close, Info, Warning } from "@epilot/spark-icons";
7
6
  import "react";
7
+ import { Cancel, CheckCircle, Close, Info, Warning } from "@epilot/spark-icons";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  import { Toast } from "@base-ui/react/toast";
10
10
  //#region src/components/toast.tsx
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_chunk = require("../chunk-DY84GLCh.cjs");
4
- let _epilot_spark_icons = require("@epilot/spark-icons");
5
4
  let react = require("react");
6
5
  react = require_chunk.__toESM(react);
6
+ let _epilot_spark_icons = require("@epilot/spark-icons");
7
7
  let react_jsx_runtime = require("react/jsx-runtime");
8
8
  let _base_ui_react_tooltip = require("@base-ui/react/tooltip");
9
9
  let cnfast = require("cnfast");
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../utils/cn.js";
3
- import { Info } from "@epilot/spark-icons";
4
3
  import "react";
4
+ import { Info } from "@epilot/spark-icons";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { Tooltip as Tooltip$1 } from "@base-ui/react/tooltip";
7
7
  //#region src/components/tooltip.tsx
@@ -0,0 +1,14 @@
1
+ "use client";
2
+
3
+ import { Format, NumberFlowProps, NumberFlowProps as NumberFlowProps$1 } from "@number-flow/react";
4
+
5
+ //#region src/components/number-flow.d.ts
6
+ declare function NumberFlow({
7
+ locales,
8
+ prefix,
9
+ suffix,
10
+ ...props
11
+ }: NumberFlowProps): import("react/jsx-runtime").JSX.Element;
12
+ //#endregion
13
+ export { NumberFlow as n, NumberFlowProps$1 as r, Format as t };
14
+ //# sourceMappingURL=number-flow-Bch81ixZ.d.cts.map
@@ -0,0 +1,14 @@
1
+ "use client";
2
+
3
+ import { Format, NumberFlowProps, NumberFlowProps as NumberFlowProps$1 } from "@number-flow/react";
4
+
5
+ //#region src/components/number-flow.d.ts
6
+ declare function NumberFlow({
7
+ locales,
8
+ prefix,
9
+ suffix,
10
+ ...props
11
+ }: NumberFlowProps): import("react/jsx-runtime").JSX.Element;
12
+ //#endregion
13
+ export { NumberFlow as n, NumberFlowProps$1 as r, Format as t };
14
+ //# sourceMappingURL=number-flow-Bch81ixZ.d.ts.map
@@ -0,0 +1,72 @@
1
+ "use client";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ //#region src/utils/build-sankey-nodes.ts
4
+ function buildSankeyNodes({ solarPower, batteryPower, gridPower, floorShares, deviceSharesByFloor }) {
5
+ const solar = Math.max(0, solarPower);
6
+ const gridImport = Math.max(0, gridPower);
7
+ const gridExport = Math.max(0, -gridPower);
8
+ const batteryDischarge = Math.max(0, batteryPower);
9
+ const batteryCharge = Math.max(0, -batteryPower);
10
+ const solarToBattery = Math.min(solar, batteryCharge);
11
+ const gridToBattery = Math.min(gridImport, batteryCharge - solarToBattery);
12
+ const solarToFeedIn = Math.min(solar - solarToBattery, gridExport);
13
+ const batteryToFeedIn = Math.min(batteryDischarge, gridExport - solarToFeedIn);
14
+ const links = [
15
+ {
16
+ source: "grid",
17
+ target: "home",
18
+ value: gridImport - gridToBattery
19
+ },
20
+ {
21
+ source: "solar",
22
+ target: "home",
23
+ value: solar - solarToBattery - solarToFeedIn
24
+ },
25
+ {
26
+ source: "battery",
27
+ target: "home",
28
+ value: batteryDischarge - batteryToFeedIn
29
+ },
30
+ {
31
+ source: "solar",
32
+ target: "battery",
33
+ value: solarToBattery
34
+ },
35
+ {
36
+ source: "grid",
37
+ target: "battery",
38
+ value: gridToBattery
39
+ },
40
+ {
41
+ source: "solar",
42
+ target: "gridFeedIn",
43
+ value: solarToFeedIn
44
+ },
45
+ {
46
+ source: "battery",
47
+ target: "gridFeedIn",
48
+ value: batteryToFeedIn
49
+ }
50
+ ];
51
+ if (!floorShares) return links;
52
+ const homeWatts = solar - solarToBattery - solarToFeedIn + (gridImport - gridToBattery) + (batteryDischarge - batteryToFeedIn);
53
+ for (const [floorId, share] of Object.entries(floorShares)) {
54
+ const floorWatts = homeWatts * share;
55
+ links.push({
56
+ source: "home",
57
+ target: floorId,
58
+ value: floorWatts
59
+ });
60
+ const deviceShares = deviceSharesByFloor?.[floorId] ?? {};
61
+ for (const [deviceId, deviceShare] of Object.entries(deviceShares)) links.push({
62
+ source: floorId,
63
+ target: deviceId,
64
+ value: floorWatts * deviceShare
65
+ });
66
+ }
67
+ return links;
68
+ }
69
+ //#endregion
70
+ exports.buildSankeyNodes = buildSankeyNodes;
71
+
72
+ //# sourceMappingURL=build-sankey-nodes.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-sankey-nodes.cjs","names":[],"sources":["../../src/utils/build-sankey-nodes.ts"],"sourcesContent":["import type { SankeyChartLink } from '../components/sankey-chart'\n\ntype SankeyNodesArgs = {\n solarPower: number\n /**\n * Signed storage power: positive = discharging into the home, negative =\n * charging from solar/grid. Charging power is routed into the `battery`\n * node so it isn't mistakenly attributed to home consumption.\n */\n batteryPower: number\n gridPower: number\n floorShares?: Record<string, number>\n deviceSharesByFloor?: Record<string, Record<string, number>>\n}\n\nfunction buildSankeyNodes({\n solarPower,\n batteryPower,\n gridPower,\n floorShares,\n deviceSharesByFloor,\n}: SankeyNodesArgs): SankeyChartLink[] {\n const solar = Math.max(0, solarPower)\n const gridImport = Math.max(0, gridPower)\n const gridExport = Math.max(0, -gridPower)\n const batteryDischarge = Math.max(0, batteryPower)\n const batteryCharge = Math.max(0, -batteryPower)\n\n // Charge the battery from solar surplus first, then from the grid. Routing\n // the charge flow into the `battery` node (a sink while charging) keeps it\n // out of the `home` node, which should reflect actual consumption only.\n const solarToBattery = Math.min(solar, batteryCharge)\n const gridToBattery = Math.min(gridImport, batteryCharge - solarToBattery)\n\n // Grid feed-in (export). Cover it from the solar surplus first, then fall\n // back to battery discharge. Like battery charging, this must not land in the\n // `home` node — it's power leaving the house, not consumption.\n const solarToFeedIn = Math.min(solar - solarToBattery, gridExport)\n const batteryToFeedIn = Math.min(batteryDischarge, gridExport - solarToFeedIn)\n\n const links: SankeyChartLink[] = [\n { source: 'grid', target: 'home', value: gridImport - gridToBattery },\n {\n source: 'solar',\n target: 'home',\n value: solar - solarToBattery - solarToFeedIn,\n },\n {\n source: 'battery',\n target: 'home',\n value: batteryDischarge - batteryToFeedIn,\n },\n { source: 'solar', target: 'battery', value: solarToBattery },\n { source: 'grid', target: 'battery', value: gridToBattery },\n { source: 'solar', target: 'gridFeedIn', value: solarToFeedIn },\n { source: 'battery', target: 'gridFeedIn', value: batteryToFeedIn },\n ]\n\n if (!floorShares) return links\n\n const homeWatts =\n solar -\n solarToBattery -\n solarToFeedIn +\n (gridImport - gridToBattery) +\n (batteryDischarge - batteryToFeedIn)\n\n for (const [floorId, share] of Object.entries(floorShares)) {\n const floorWatts = homeWatts * share\n links.push({ source: 'home', target: floorId, value: floorWatts })\n\n const deviceShares = deviceSharesByFloor?.[floorId] ?? {}\n for (const [deviceId, deviceShare] of Object.entries(deviceShares)) {\n links.push({\n source: floorId,\n target: deviceId,\n value: floorWatts * deviceShare,\n })\n }\n }\n\n return links\n}\n\nexport { buildSankeyNodes }\n"],"mappings":";;;AAeA,SAAS,iBAAiB,EACxB,YACA,cACA,WACA,aACA,uBACqC;CACrC,MAAM,QAAQ,KAAK,IAAI,GAAG,UAAU;CACpC,MAAM,aAAa,KAAK,IAAI,GAAG,SAAS;CACxC,MAAM,aAAa,KAAK,IAAI,GAAG,CAAC,SAAS;CACzC,MAAM,mBAAmB,KAAK,IAAI,GAAG,YAAY;CACjD,MAAM,gBAAgB,KAAK,IAAI,GAAG,CAAC,YAAY;CAK/C,MAAM,iBAAiB,KAAK,IAAI,OAAO,aAAa;CACpD,MAAM,gBAAgB,KAAK,IAAI,YAAY,gBAAgB,cAAc;CAKzE,MAAM,gBAAgB,KAAK,IAAI,QAAQ,gBAAgB,UAAU;CACjE,MAAM,kBAAkB,KAAK,IAAI,kBAAkB,aAAa,aAAa;CAE7E,MAAM,QAA2B;EAC/B;GAAE,QAAQ;GAAQ,QAAQ;GAAQ,OAAO,aAAa;EAAc;EACpE;GACE,QAAQ;GACR,QAAQ;GACR,OAAO,QAAQ,iBAAiB;EAClC;EACA;GACE,QAAQ;GACR,QAAQ;GACR,OAAO,mBAAmB;EAC5B;EACA;GAAE,QAAQ;GAAS,QAAQ;GAAW,OAAO;EAAe;EAC5D;GAAE,QAAQ;GAAQ,QAAQ;GAAW,OAAO;EAAc;EAC1D;GAAE,QAAQ;GAAS,QAAQ;GAAc,OAAO;EAAc;EAC9D;GAAE,QAAQ;GAAW,QAAQ;GAAc,OAAO;EAAgB;CACpE;CAEA,IAAI,CAAC,aAAa,OAAO;CAEzB,MAAM,YACJ,QACA,iBACA,iBACC,aAAa,kBACb,mBAAmB;CAEtB,KAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,WAAW,GAAG;EAC1D,MAAM,aAAa,YAAY;EAC/B,MAAM,KAAK;GAAE,QAAQ;GAAQ,QAAQ;GAAS,OAAO;EAAW,CAAC;EAEjE,MAAM,eAAe,sBAAsB,YAAY,CAAC;EACxD,KAAK,MAAM,CAAC,UAAU,gBAAgB,OAAO,QAAQ,YAAY,GAC/D,MAAM,KAAK;GACT,QAAQ;GACR,QAAQ;GACR,OAAO,aAAa;EACtB,CAAC;CAEL;CAEA,OAAO;AACT"}
@@ -0,0 +1,27 @@
1
+ "use client";
2
+
3
+ import { SankeyChartLink } from "../components/sankey-chart.cjs";
4
+
5
+ //#region src/utils/build-sankey-nodes.d.ts
6
+ type SankeyNodesArgs = {
7
+ solarPower: number;
8
+ /**
9
+ * Signed storage power: positive = discharging into the home, negative =
10
+ * charging from solar/grid. Charging power is routed into the `battery`
11
+ * node so it isn't mistakenly attributed to home consumption.
12
+ */
13
+ batteryPower: number;
14
+ gridPower: number;
15
+ floorShares?: Record<string, number>;
16
+ deviceSharesByFloor?: Record<string, Record<string, number>>;
17
+ };
18
+ declare function buildSankeyNodes({
19
+ solarPower,
20
+ batteryPower,
21
+ gridPower,
22
+ floorShares,
23
+ deviceSharesByFloor
24
+ }: SankeyNodesArgs): SankeyChartLink[];
25
+ //#endregion
26
+ export { buildSankeyNodes };
27
+ //# sourceMappingURL=build-sankey-nodes.d.cts.map
@@ -0,0 +1,27 @@
1
+ "use client";
2
+
3
+ import { SankeyChartLink } from "../components/sankey-chart.js";
4
+
5
+ //#region src/utils/build-sankey-nodes.d.ts
6
+ type SankeyNodesArgs = {
7
+ solarPower: number;
8
+ /**
9
+ * Signed storage power: positive = discharging into the home, negative =
10
+ * charging from solar/grid. Charging power is routed into the `battery`
11
+ * node so it isn't mistakenly attributed to home consumption.
12
+ */
13
+ batteryPower: number;
14
+ gridPower: number;
15
+ floorShares?: Record<string, number>;
16
+ deviceSharesByFloor?: Record<string, Record<string, number>>;
17
+ };
18
+ declare function buildSankeyNodes({
19
+ solarPower,
20
+ batteryPower,
21
+ gridPower,
22
+ floorShares,
23
+ deviceSharesByFloor
24
+ }: SankeyNodesArgs): SankeyChartLink[];
25
+ //#endregion
26
+ export { buildSankeyNodes };
27
+ //# sourceMappingURL=build-sankey-nodes.d.ts.map
@@ -0,0 +1,71 @@
1
+ "use client";
2
+ //#region src/utils/build-sankey-nodes.ts
3
+ function buildSankeyNodes({ solarPower, batteryPower, gridPower, floorShares, deviceSharesByFloor }) {
4
+ const solar = Math.max(0, solarPower);
5
+ const gridImport = Math.max(0, gridPower);
6
+ const gridExport = Math.max(0, -gridPower);
7
+ const batteryDischarge = Math.max(0, batteryPower);
8
+ const batteryCharge = Math.max(0, -batteryPower);
9
+ const solarToBattery = Math.min(solar, batteryCharge);
10
+ const gridToBattery = Math.min(gridImport, batteryCharge - solarToBattery);
11
+ const solarToFeedIn = Math.min(solar - solarToBattery, gridExport);
12
+ const batteryToFeedIn = Math.min(batteryDischarge, gridExport - solarToFeedIn);
13
+ const links = [
14
+ {
15
+ source: "grid",
16
+ target: "home",
17
+ value: gridImport - gridToBattery
18
+ },
19
+ {
20
+ source: "solar",
21
+ target: "home",
22
+ value: solar - solarToBattery - solarToFeedIn
23
+ },
24
+ {
25
+ source: "battery",
26
+ target: "home",
27
+ value: batteryDischarge - batteryToFeedIn
28
+ },
29
+ {
30
+ source: "solar",
31
+ target: "battery",
32
+ value: solarToBattery
33
+ },
34
+ {
35
+ source: "grid",
36
+ target: "battery",
37
+ value: gridToBattery
38
+ },
39
+ {
40
+ source: "solar",
41
+ target: "gridFeedIn",
42
+ value: solarToFeedIn
43
+ },
44
+ {
45
+ source: "battery",
46
+ target: "gridFeedIn",
47
+ value: batteryToFeedIn
48
+ }
49
+ ];
50
+ if (!floorShares) return links;
51
+ const homeWatts = solar - solarToBattery - solarToFeedIn + (gridImport - gridToBattery) + (batteryDischarge - batteryToFeedIn);
52
+ for (const [floorId, share] of Object.entries(floorShares)) {
53
+ const floorWatts = homeWatts * share;
54
+ links.push({
55
+ source: "home",
56
+ target: floorId,
57
+ value: floorWatts
58
+ });
59
+ const deviceShares = deviceSharesByFloor?.[floorId] ?? {};
60
+ for (const [deviceId, deviceShare] of Object.entries(deviceShares)) links.push({
61
+ source: floorId,
62
+ target: deviceId,
63
+ value: floorWatts * deviceShare
64
+ });
65
+ }
66
+ return links;
67
+ }
68
+ //#endregion
69
+ export { buildSankeyNodes };
70
+
71
+ //# sourceMappingURL=build-sankey-nodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-sankey-nodes.js","names":[],"sources":["../../src/utils/build-sankey-nodes.ts"],"sourcesContent":["import type { SankeyChartLink } from '../components/sankey-chart'\n\ntype SankeyNodesArgs = {\n solarPower: number\n /**\n * Signed storage power: positive = discharging into the home, negative =\n * charging from solar/grid. Charging power is routed into the `battery`\n * node so it isn't mistakenly attributed to home consumption.\n */\n batteryPower: number\n gridPower: number\n floorShares?: Record<string, number>\n deviceSharesByFloor?: Record<string, Record<string, number>>\n}\n\nfunction buildSankeyNodes({\n solarPower,\n batteryPower,\n gridPower,\n floorShares,\n deviceSharesByFloor,\n}: SankeyNodesArgs): SankeyChartLink[] {\n const solar = Math.max(0, solarPower)\n const gridImport = Math.max(0, gridPower)\n const gridExport = Math.max(0, -gridPower)\n const batteryDischarge = Math.max(0, batteryPower)\n const batteryCharge = Math.max(0, -batteryPower)\n\n // Charge the battery from solar surplus first, then from the grid. Routing\n // the charge flow into the `battery` node (a sink while charging) keeps it\n // out of the `home` node, which should reflect actual consumption only.\n const solarToBattery = Math.min(solar, batteryCharge)\n const gridToBattery = Math.min(gridImport, batteryCharge - solarToBattery)\n\n // Grid feed-in (export). Cover it from the solar surplus first, then fall\n // back to battery discharge. Like battery charging, this must not land in the\n // `home` node — it's power leaving the house, not consumption.\n const solarToFeedIn = Math.min(solar - solarToBattery, gridExport)\n const batteryToFeedIn = Math.min(batteryDischarge, gridExport - solarToFeedIn)\n\n const links: SankeyChartLink[] = [\n { source: 'grid', target: 'home', value: gridImport - gridToBattery },\n {\n source: 'solar',\n target: 'home',\n value: solar - solarToBattery - solarToFeedIn,\n },\n {\n source: 'battery',\n target: 'home',\n value: batteryDischarge - batteryToFeedIn,\n },\n { source: 'solar', target: 'battery', value: solarToBattery },\n { source: 'grid', target: 'battery', value: gridToBattery },\n { source: 'solar', target: 'gridFeedIn', value: solarToFeedIn },\n { source: 'battery', target: 'gridFeedIn', value: batteryToFeedIn },\n ]\n\n if (!floorShares) return links\n\n const homeWatts =\n solar -\n solarToBattery -\n solarToFeedIn +\n (gridImport - gridToBattery) +\n (batteryDischarge - batteryToFeedIn)\n\n for (const [floorId, share] of Object.entries(floorShares)) {\n const floorWatts = homeWatts * share\n links.push({ source: 'home', target: floorId, value: floorWatts })\n\n const deviceShares = deviceSharesByFloor?.[floorId] ?? {}\n for (const [deviceId, deviceShare] of Object.entries(deviceShares)) {\n links.push({\n source: floorId,\n target: deviceId,\n value: floorWatts * deviceShare,\n })\n }\n }\n\n return links\n}\n\nexport { buildSankeyNodes }\n"],"mappings":";;AAeA,SAAS,iBAAiB,EACxB,YACA,cACA,WACA,aACA,uBACqC;CACrC,MAAM,QAAQ,KAAK,IAAI,GAAG,UAAU;CACpC,MAAM,aAAa,KAAK,IAAI,GAAG,SAAS;CACxC,MAAM,aAAa,KAAK,IAAI,GAAG,CAAC,SAAS;CACzC,MAAM,mBAAmB,KAAK,IAAI,GAAG,YAAY;CACjD,MAAM,gBAAgB,KAAK,IAAI,GAAG,CAAC,YAAY;CAK/C,MAAM,iBAAiB,KAAK,IAAI,OAAO,aAAa;CACpD,MAAM,gBAAgB,KAAK,IAAI,YAAY,gBAAgB,cAAc;CAKzE,MAAM,gBAAgB,KAAK,IAAI,QAAQ,gBAAgB,UAAU;CACjE,MAAM,kBAAkB,KAAK,IAAI,kBAAkB,aAAa,aAAa;CAE7E,MAAM,QAA2B;EAC/B;GAAE,QAAQ;GAAQ,QAAQ;GAAQ,OAAO,aAAa;EAAc;EACpE;GACE,QAAQ;GACR,QAAQ;GACR,OAAO,QAAQ,iBAAiB;EAClC;EACA;GACE,QAAQ;GACR,QAAQ;GACR,OAAO,mBAAmB;EAC5B;EACA;GAAE,QAAQ;GAAS,QAAQ;GAAW,OAAO;EAAe;EAC5D;GAAE,QAAQ;GAAQ,QAAQ;GAAW,OAAO;EAAc;EAC1D;GAAE,QAAQ;GAAS,QAAQ;GAAc,OAAO;EAAc;EAC9D;GAAE,QAAQ;GAAW,QAAQ;GAAc,OAAO;EAAgB;CACpE;CAEA,IAAI,CAAC,aAAa,OAAO;CAEzB,MAAM,YACJ,QACA,iBACA,iBACC,aAAa,kBACb,mBAAmB;CAEtB,KAAK,MAAM,CAAC,SAAS,UAAU,OAAO,QAAQ,WAAW,GAAG;EAC1D,MAAM,aAAa,YAAY;EAC/B,MAAM,KAAK;GAAE,QAAQ;GAAQ,QAAQ;GAAS,OAAO;EAAW,CAAC;EAEjE,MAAM,eAAe,sBAAsB,YAAY,CAAC;EACxD,KAAK,MAAM,CAAC,UAAU,gBAAgB,OAAO,QAAQ,YAAY,GAC/D,MAAM,KAAK;GACT,QAAQ;GACR,QAAQ;GACR,OAAO,aAAa;EACtB,CAAC;CAEL;CAEA,OAAO;AACT"}
@@ -23,6 +23,17 @@ const integerFormat = decimalsFormat(0);
23
23
  const oneDecimalFormat = decimalsFormat(1);
24
24
  const twoDecimalFormat = decimalsFormat(2);
25
25
  const formatThousands = (n) => integerFormat.format(n);
26
+ const formatEnergy = (v) => {
27
+ if (v >= 1e6) return `${twoDecimalFormat.format(v / 1e3)} MWh`;
28
+ if (v >= 1e3) return `${integerFormat.format(v)} kWh`;
29
+ if (v >= 10) return `${oneDecimalFormat.format(v)} kWh`;
30
+ return `${twoDecimalFormat.format(v)} kWh`;
31
+ };
32
+ const formatPower = (value, { signed = false } = {}) => {
33
+ if (value == null) return "—";
34
+ const rounded = Math.round(value);
35
+ return `${signed && rounded > 0 ? "+" : ""}${integerFormat.format(rounded)} W`;
36
+ };
26
37
  const formatPercent = (value) => {
27
38
  if (value == null) return "—";
28
39
  return `${integerFormat.format(value)} %`;
@@ -33,7 +44,9 @@ const formatRatio = (value) => {
33
44
  };
34
45
  //#endregion
35
46
  exports.decimalsFormat = decimalsFormat;
47
+ exports.formatEnergy = formatEnergy;
36
48
  exports.formatPercent = formatPercent;
49
+ exports.formatPower = formatPower;
37
50
  exports.formatRatio = formatRatio;
38
51
  exports.formatThousands = formatThousands;
39
52
  exports.integerFormat = integerFormat;
@@ -1 +1 @@
1
- {"version":3,"file":"format.cjs","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["import { EM_DASH, NARROW_NBSP } from './chars'\n\ninterface LocaleNumberFormat {\n format: (value: number) => string\n}\n\nfunction getDocumentLang(): string | undefined {\n return typeof document === 'undefined'\n ? undefined\n : document.documentElement.lang || undefined\n}\n\nconst localeNumberFormat = (\n options?: Intl.NumberFormatOptions,\n): LocaleNumberFormat => {\n const cache = new Map<string, Intl.NumberFormat>()\n return {\n format: (value) => {\n const locale = getDocumentLang()\n const key = locale ?? ''\n let formatter = cache.get(key)\n if (!formatter) {\n formatter = new Intl.NumberFormat(locale, options)\n cache.set(key, formatter)\n }\n return formatter.format(value)\n },\n }\n}\n\nconst decimalsFormat = (decimals: number) =>\n localeNumberFormat({ maximumFractionDigits: decimals })\n\nconst integerFormat = decimalsFormat(0)\nconst oneDecimalFormat = decimalsFormat(1)\nconst twoDecimalFormat = decimalsFormat(2)\n\nconst formatThousands = (n: number) => integerFormat.format(n)\n\nconst formatPercent = (value: number | null): string => {\n if (value == null) return EM_DASH\n return `${integerFormat.format(value)}${NARROW_NBSP}%`\n}\n\nconst formatRatio = (value: number | null): string => {\n if (value == null) return EM_DASH\n return `${integerFormat.format(value * 100)}${NARROW_NBSP}%`\n}\n\nexport {\n formatThousands,\n formatPercent,\n formatRatio,\n localeNumberFormat,\n decimalsFormat,\n integerFormat,\n oneDecimalFormat,\n twoDecimalFormat,\n}\n"],"mappings":";;;;AAMA,SAAS,kBAAsC;CAC7C,OAAO,OAAO,aAAa,cACvB,KAAA,IACA,SAAS,gBAAgB,QAAQ,KAAA;AACvC;AAEA,MAAM,sBACJ,YACuB;CACvB,MAAM,wBAAQ,IAAI,IAA+B;CACjD,OAAO,EACL,SAAS,UAAU;EACjB,MAAM,SAAS,gBAAgB;EAC/B,MAAM,MAAM,UAAU;EACtB,IAAI,YAAY,MAAM,IAAI,GAAG;EAC7B,IAAI,CAAC,WAAW;GACd,YAAY,IAAI,KAAK,aAAa,QAAQ,OAAO;GACjD,MAAM,IAAI,KAAK,SAAS;EAC1B;EACA,OAAO,UAAU,OAAO,KAAK;CAC/B,EACF;AACF;AAEA,MAAM,kBAAkB,aACtB,mBAAmB,EAAE,uBAAuB,SAAS,CAAC;AAExD,MAAM,gBAAgB,eAAe,CAAC;AACtC,MAAM,mBAAmB,eAAe,CAAC;AACzC,MAAM,mBAAmB,eAAe,CAAC;AAEzC,MAAM,mBAAmB,MAAc,cAAc,OAAO,CAAC;AAE7D,MAAM,iBAAiB,UAAiC;CACtD,IAAI,SAAS,MAAM,OAAA;CACnB,OAAO,GAAG,cAAc,OAAO,KAAK;AACtC;AAEA,MAAM,eAAe,UAAiC;CACpD,IAAI,SAAS,MAAM,OAAA;CACnB,OAAO,GAAG,cAAc,OAAO,QAAQ,GAAG;AAC5C"}
1
+ {"version":3,"file":"format.cjs","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["import { EM_DASH, NARROW_NBSP } from './chars'\n\ninterface LocaleNumberFormat {\n format: (value: number) => string\n}\n\nfunction getDocumentLang(): string | undefined {\n return typeof document === 'undefined'\n ? undefined\n : document.documentElement.lang || undefined\n}\n\nconst localeNumberFormat = (\n options?: Intl.NumberFormatOptions,\n): LocaleNumberFormat => {\n const cache = new Map<string, Intl.NumberFormat>()\n return {\n format: (value) => {\n const locale = getDocumentLang()\n const key = locale ?? ''\n let formatter = cache.get(key)\n if (!formatter) {\n formatter = new Intl.NumberFormat(locale, options)\n cache.set(key, formatter)\n }\n return formatter.format(value)\n },\n }\n}\n\nconst decimalsFormat = (decimals: number) =>\n localeNumberFormat({ maximumFractionDigits: decimals })\n\nconst integerFormat = decimalsFormat(0)\nconst oneDecimalFormat = decimalsFormat(1)\nconst twoDecimalFormat = decimalsFormat(2)\n\nconst formatThousands = (n: number) => integerFormat.format(n)\n\nconst formatEnergy = (v: number) => {\n if (v >= 1_000_000) {\n return `${twoDecimalFormat.format(v / 1000)}${NARROW_NBSP}MWh`\n }\n if (v >= 1_000) return `${integerFormat.format(v)}${NARROW_NBSP}kWh`\n if (v >= 10) return `${oneDecimalFormat.format(v)}${NARROW_NBSP}kWh`\n return `${twoDecimalFormat.format(v)}${NARROW_NBSP}kWh`\n}\n\nconst formatPower = (\n value: number | null,\n { signed = false }: { signed?: boolean } = {},\n): string => {\n if (value == null) return EM_DASH\n const rounded = Math.round(value)\n const sign = signed && rounded > 0 ? '+' : ''\n return `${sign}${integerFormat.format(rounded)}${NARROW_NBSP}W`\n}\n\nconst formatPercent = (value: number | null): string => {\n if (value == null) return EM_DASH\n return `${integerFormat.format(value)}${NARROW_NBSP}%`\n}\n\nconst formatRatio = (value: number | null): string => {\n if (value == null) return EM_DASH\n return `${integerFormat.format(value * 100)}${NARROW_NBSP}%`\n}\n\nexport {\n formatThousands,\n formatEnergy,\n formatPower,\n formatPercent,\n formatRatio,\n localeNumberFormat,\n decimalsFormat,\n integerFormat,\n oneDecimalFormat,\n twoDecimalFormat,\n}\n"],"mappings":";;;;AAMA,SAAS,kBAAsC;CAC7C,OAAO,OAAO,aAAa,cACvB,KAAA,IACA,SAAS,gBAAgB,QAAQ,KAAA;AACvC;AAEA,MAAM,sBACJ,YACuB;CACvB,MAAM,wBAAQ,IAAI,IAA+B;CACjD,OAAO,EACL,SAAS,UAAU;EACjB,MAAM,SAAS,gBAAgB;EAC/B,MAAM,MAAM,UAAU;EACtB,IAAI,YAAY,MAAM,IAAI,GAAG;EAC7B,IAAI,CAAC,WAAW;GACd,YAAY,IAAI,KAAK,aAAa,QAAQ,OAAO;GACjD,MAAM,IAAI,KAAK,SAAS;EAC1B;EACA,OAAO,UAAU,OAAO,KAAK;CAC/B,EACF;AACF;AAEA,MAAM,kBAAkB,aACtB,mBAAmB,EAAE,uBAAuB,SAAS,CAAC;AAExD,MAAM,gBAAgB,eAAe,CAAC;AACtC,MAAM,mBAAmB,eAAe,CAAC;AACzC,MAAM,mBAAmB,eAAe,CAAC;AAEzC,MAAM,mBAAmB,MAAc,cAAc,OAAO,CAAC;AAE7D,MAAM,gBAAgB,MAAc;CAClC,IAAI,KAAK,KACP,OAAO,GAAG,iBAAiB,OAAO,IAAI,GAAI;CAE5C,IAAI,KAAK,KAAO,OAAO,GAAG,cAAc,OAAO,CAAC;CAChD,IAAI,KAAK,IAAI,OAAO,GAAG,iBAAiB,OAAO,CAAC;CAChD,OAAO,GAAG,iBAAiB,OAAO,CAAC;AACrC;AAEA,MAAM,eACJ,OACA,EAAE,SAAS,UAAgC,CAAC,MACjC;CACX,IAAI,SAAS,MAAM,OAAA;CACnB,MAAM,UAAU,KAAK,MAAM,KAAK;CAEhC,OAAO,GADM,UAAU,UAAU,IAAI,MAAM,KAC1B,cAAc,OAAO,OAAO;AAC/C;AAEA,MAAM,iBAAiB,UAAiC;CACtD,IAAI,SAAS,MAAM,OAAA;CACnB,OAAO,GAAG,cAAc,OAAO,KAAK;AACtC;AAEA,MAAM,eAAe,UAAiC;CACpD,IAAI,SAAS,MAAM,OAAA;CACnB,OAAO,GAAG,cAAc,OAAO,QAAQ,GAAG;AAC5C"}
@@ -10,8 +10,14 @@ declare const integerFormat: LocaleNumberFormat;
10
10
  declare const oneDecimalFormat: LocaleNumberFormat;
11
11
  declare const twoDecimalFormat: LocaleNumberFormat;
12
12
  declare const formatThousands: (n: number) => string;
13
+ declare const formatEnergy: (v: number) => string;
14
+ declare const formatPower: (value: number | null, {
15
+ signed
16
+ }?: {
17
+ signed?: boolean;
18
+ }) => string;
13
19
  declare const formatPercent: (value: number | null) => string;
14
20
  declare const formatRatio: (value: number | null) => string;
15
21
  //#endregion
16
- export { decimalsFormat, formatPercent, formatRatio, formatThousands, integerFormat, localeNumberFormat, oneDecimalFormat, twoDecimalFormat };
22
+ export { decimalsFormat, formatEnergy, formatPercent, formatPower, formatRatio, formatThousands, integerFormat, localeNumberFormat, oneDecimalFormat, twoDecimalFormat };
17
23
  //# sourceMappingURL=format.d.cts.map
@@ -10,8 +10,14 @@ declare const integerFormat: LocaleNumberFormat;
10
10
  declare const oneDecimalFormat: LocaleNumberFormat;
11
11
  declare const twoDecimalFormat: LocaleNumberFormat;
12
12
  declare const formatThousands: (n: number) => string;
13
+ declare const formatEnergy: (v: number) => string;
14
+ declare const formatPower: (value: number | null, {
15
+ signed
16
+ }?: {
17
+ signed?: boolean;
18
+ }) => string;
13
19
  declare const formatPercent: (value: number | null) => string;
14
20
  declare const formatRatio: (value: number | null) => string;
15
21
  //#endregion
16
- export { decimalsFormat, formatPercent, formatRatio, formatThousands, integerFormat, localeNumberFormat, oneDecimalFormat, twoDecimalFormat };
22
+ export { decimalsFormat, formatEnergy, formatPercent, formatPower, formatRatio, formatThousands, integerFormat, localeNumberFormat, oneDecimalFormat, twoDecimalFormat };
17
23
  //# sourceMappingURL=format.d.ts.map
@@ -22,6 +22,17 @@ const integerFormat = decimalsFormat(0);
22
22
  const oneDecimalFormat = decimalsFormat(1);
23
23
  const twoDecimalFormat = decimalsFormat(2);
24
24
  const formatThousands = (n) => integerFormat.format(n);
25
+ const formatEnergy = (v) => {
26
+ if (v >= 1e6) return `${twoDecimalFormat.format(v / 1e3)} MWh`;
27
+ if (v >= 1e3) return `${integerFormat.format(v)} kWh`;
28
+ if (v >= 10) return `${oneDecimalFormat.format(v)} kWh`;
29
+ return `${twoDecimalFormat.format(v)} kWh`;
30
+ };
31
+ const formatPower = (value, { signed = false } = {}) => {
32
+ if (value == null) return "—";
33
+ const rounded = Math.round(value);
34
+ return `${signed && rounded > 0 ? "+" : ""}${integerFormat.format(rounded)} W`;
35
+ };
25
36
  const formatPercent = (value) => {
26
37
  if (value == null) return "—";
27
38
  return `${integerFormat.format(value)} %`;
@@ -31,6 +42,6 @@ const formatRatio = (value) => {
31
42
  return `${integerFormat.format(value * 100)} %`;
32
43
  };
33
44
  //#endregion
34
- export { decimalsFormat, formatPercent, formatRatio, formatThousands, integerFormat, localeNumberFormat, oneDecimalFormat, twoDecimalFormat };
45
+ export { decimalsFormat, formatEnergy, formatPercent, formatPower, formatRatio, formatThousands, integerFormat, localeNumberFormat, oneDecimalFormat, twoDecimalFormat };
35
46
 
36
47
  //# sourceMappingURL=format.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["import { EM_DASH, NARROW_NBSP } from './chars'\n\ninterface LocaleNumberFormat {\n format: (value: number) => string\n}\n\nfunction getDocumentLang(): string | undefined {\n return typeof document === 'undefined'\n ? undefined\n : document.documentElement.lang || undefined\n}\n\nconst localeNumberFormat = (\n options?: Intl.NumberFormatOptions,\n): LocaleNumberFormat => {\n const cache = new Map<string, Intl.NumberFormat>()\n return {\n format: (value) => {\n const locale = getDocumentLang()\n const key = locale ?? ''\n let formatter = cache.get(key)\n if (!formatter) {\n formatter = new Intl.NumberFormat(locale, options)\n cache.set(key, formatter)\n }\n return formatter.format(value)\n },\n }\n}\n\nconst decimalsFormat = (decimals: number) =>\n localeNumberFormat({ maximumFractionDigits: decimals })\n\nconst integerFormat = decimalsFormat(0)\nconst oneDecimalFormat = decimalsFormat(1)\nconst twoDecimalFormat = decimalsFormat(2)\n\nconst formatThousands = (n: number) => integerFormat.format(n)\n\nconst formatPercent = (value: number | null): string => {\n if (value == null) return EM_DASH\n return `${integerFormat.format(value)}${NARROW_NBSP}%`\n}\n\nconst formatRatio = (value: number | null): string => {\n if (value == null) return EM_DASH\n return `${integerFormat.format(value * 100)}${NARROW_NBSP}%`\n}\n\nexport {\n formatThousands,\n formatPercent,\n formatRatio,\n localeNumberFormat,\n decimalsFormat,\n integerFormat,\n oneDecimalFormat,\n twoDecimalFormat,\n}\n"],"mappings":";;;AAMA,SAAS,kBAAsC;CAC7C,OAAO,OAAO,aAAa,cACvB,KAAA,IACA,SAAS,gBAAgB,QAAQ,KAAA;AACvC;AAEA,MAAM,sBACJ,YACuB;CACvB,MAAM,wBAAQ,IAAI,IAA+B;CACjD,OAAO,EACL,SAAS,UAAU;EACjB,MAAM,SAAS,gBAAgB;EAC/B,MAAM,MAAM,UAAU;EACtB,IAAI,YAAY,MAAM,IAAI,GAAG;EAC7B,IAAI,CAAC,WAAW;GACd,YAAY,IAAI,KAAK,aAAa,QAAQ,OAAO;GACjD,MAAM,IAAI,KAAK,SAAS;EAC1B;EACA,OAAO,UAAU,OAAO,KAAK;CAC/B,EACF;AACF;AAEA,MAAM,kBAAkB,aACtB,mBAAmB,EAAE,uBAAuB,SAAS,CAAC;AAExD,MAAM,gBAAgB,eAAe,CAAC;AACtC,MAAM,mBAAmB,eAAe,CAAC;AACzC,MAAM,mBAAmB,eAAe,CAAC;AAEzC,MAAM,mBAAmB,MAAc,cAAc,OAAO,CAAC;AAE7D,MAAM,iBAAiB,UAAiC;CACtD,IAAI,SAAS,MAAM,OAAA;CACnB,OAAO,GAAG,cAAc,OAAO,KAAK;AACtC;AAEA,MAAM,eAAe,UAAiC;CACpD,IAAI,SAAS,MAAM,OAAA;CACnB,OAAO,GAAG,cAAc,OAAO,QAAQ,GAAG;AAC5C"}
1
+ {"version":3,"file":"format.js","names":[],"sources":["../../src/utils/format.ts"],"sourcesContent":["import { EM_DASH, NARROW_NBSP } from './chars'\n\ninterface LocaleNumberFormat {\n format: (value: number) => string\n}\n\nfunction getDocumentLang(): string | undefined {\n return typeof document === 'undefined'\n ? undefined\n : document.documentElement.lang || undefined\n}\n\nconst localeNumberFormat = (\n options?: Intl.NumberFormatOptions,\n): LocaleNumberFormat => {\n const cache = new Map<string, Intl.NumberFormat>()\n return {\n format: (value) => {\n const locale = getDocumentLang()\n const key = locale ?? ''\n let formatter = cache.get(key)\n if (!formatter) {\n formatter = new Intl.NumberFormat(locale, options)\n cache.set(key, formatter)\n }\n return formatter.format(value)\n },\n }\n}\n\nconst decimalsFormat = (decimals: number) =>\n localeNumberFormat({ maximumFractionDigits: decimals })\n\nconst integerFormat = decimalsFormat(0)\nconst oneDecimalFormat = decimalsFormat(1)\nconst twoDecimalFormat = decimalsFormat(2)\n\nconst formatThousands = (n: number) => integerFormat.format(n)\n\nconst formatEnergy = (v: number) => {\n if (v >= 1_000_000) {\n return `${twoDecimalFormat.format(v / 1000)}${NARROW_NBSP}MWh`\n }\n if (v >= 1_000) return `${integerFormat.format(v)}${NARROW_NBSP}kWh`\n if (v >= 10) return `${oneDecimalFormat.format(v)}${NARROW_NBSP}kWh`\n return `${twoDecimalFormat.format(v)}${NARROW_NBSP}kWh`\n}\n\nconst formatPower = (\n value: number | null,\n { signed = false }: { signed?: boolean } = {},\n): string => {\n if (value == null) return EM_DASH\n const rounded = Math.round(value)\n const sign = signed && rounded > 0 ? '+' : ''\n return `${sign}${integerFormat.format(rounded)}${NARROW_NBSP}W`\n}\n\nconst formatPercent = (value: number | null): string => {\n if (value == null) return EM_DASH\n return `${integerFormat.format(value)}${NARROW_NBSP}%`\n}\n\nconst formatRatio = (value: number | null): string => {\n if (value == null) return EM_DASH\n return `${integerFormat.format(value * 100)}${NARROW_NBSP}%`\n}\n\nexport {\n formatThousands,\n formatEnergy,\n formatPower,\n formatPercent,\n formatRatio,\n localeNumberFormat,\n decimalsFormat,\n integerFormat,\n oneDecimalFormat,\n twoDecimalFormat,\n}\n"],"mappings":";;;AAMA,SAAS,kBAAsC;CAC7C,OAAO,OAAO,aAAa,cACvB,KAAA,IACA,SAAS,gBAAgB,QAAQ,KAAA;AACvC;AAEA,MAAM,sBACJ,YACuB;CACvB,MAAM,wBAAQ,IAAI,IAA+B;CACjD,OAAO,EACL,SAAS,UAAU;EACjB,MAAM,SAAS,gBAAgB;EAC/B,MAAM,MAAM,UAAU;EACtB,IAAI,YAAY,MAAM,IAAI,GAAG;EAC7B,IAAI,CAAC,WAAW;GACd,YAAY,IAAI,KAAK,aAAa,QAAQ,OAAO;GACjD,MAAM,IAAI,KAAK,SAAS;EAC1B;EACA,OAAO,UAAU,OAAO,KAAK;CAC/B,EACF;AACF;AAEA,MAAM,kBAAkB,aACtB,mBAAmB,EAAE,uBAAuB,SAAS,CAAC;AAExD,MAAM,gBAAgB,eAAe,CAAC;AACtC,MAAM,mBAAmB,eAAe,CAAC;AACzC,MAAM,mBAAmB,eAAe,CAAC;AAEzC,MAAM,mBAAmB,MAAc,cAAc,OAAO,CAAC;AAE7D,MAAM,gBAAgB,MAAc;CAClC,IAAI,KAAK,KACP,OAAO,GAAG,iBAAiB,OAAO,IAAI,GAAI;CAE5C,IAAI,KAAK,KAAO,OAAO,GAAG,cAAc,OAAO,CAAC;CAChD,IAAI,KAAK,IAAI,OAAO,GAAG,iBAAiB,OAAO,CAAC;CAChD,OAAO,GAAG,iBAAiB,OAAO,CAAC;AACrC;AAEA,MAAM,eACJ,OACA,EAAE,SAAS,UAAgC,CAAC,MACjC;CACX,IAAI,SAAS,MAAM,OAAA;CACnB,MAAM,UAAU,KAAK,MAAM,KAAK;CAEhC,OAAO,GADM,UAAU,UAAU,IAAI,MAAM,KAC1B,cAAc,OAAO,OAAO;AAC/C;AAEA,MAAM,iBAAiB,UAAiC;CACtD,IAAI,SAAS,MAAM,OAAA;CACnB,OAAO,GAAG,cAAc,OAAO,KAAK;AACtC;AAEA,MAAM,eAAe,UAAiC;CACpD,IAAI,SAAS,MAAM,OAAA;CACnB,OAAO,GAAG,cAAc,OAAO,QAAQ,GAAG;AAC5C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epilot/spark-ui",
3
- "version": "0.20.0",
3
+ "version": "0.20.1",
4
4
  "description": "React UI component library from Spark SDK",
5
5
  "homepage": "https://spark-ui.epilot.io/docs/ui",
6
6
  "license": "Apache-2.0",