@epilot/spark-ui 0.22.0 → 0.23.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 (128) hide show
  1. package/dist/components/attachment.d.cts +1 -1
  2. package/dist/components/attachment.d.ts +1 -1
  3. package/dist/components/badge.cjs +1 -1
  4. package/dist/components/badge.cjs.map +1 -1
  5. package/dist/components/badge.js +1 -1
  6. package/dist/components/badge.js.map +1 -1
  7. package/dist/components/bar-chart.cjs.map +1 -1
  8. package/dist/components/bar-chart.js.map +1 -1
  9. package/dist/components/bubble.d.cts +1 -1
  10. package/dist/components/bubble.d.ts +1 -1
  11. package/dist/components/button.cjs.map +1 -1
  12. package/dist/components/button.d.cts +3 -3
  13. package/dist/components/button.d.ts +3 -3
  14. package/dist/components/button.js.map +1 -1
  15. package/dist/components/calendar.cjs +4 -3
  16. package/dist/components/calendar.cjs.map +1 -1
  17. package/dist/components/calendar.js +4 -3
  18. package/dist/components/calendar.js.map +1 -1
  19. package/dist/components/checkbox.cjs.map +1 -1
  20. package/dist/components/checkbox.js.map +1 -1
  21. package/dist/components/circular-gauge.cjs +1 -1
  22. package/dist/components/circular-gauge.cjs.map +1 -1
  23. package/dist/components/circular-gauge.js +1 -1
  24. package/dist/components/circular-gauge.js.map +1 -1
  25. package/dist/components/donut-chart.cjs.map +1 -1
  26. package/dist/components/donut-chart.js.map +1 -1
  27. package/dist/components/drawer.cjs +2 -2
  28. package/dist/components/drawer.cjs.map +1 -1
  29. package/dist/components/drawer.js +2 -2
  30. package/dist/components/drawer.js.map +1 -1
  31. package/dist/components/energy-breakdown.cjs +1 -1
  32. package/dist/components/energy-breakdown.cjs.map +1 -1
  33. package/dist/components/energy-breakdown.js +1 -1
  34. package/dist/components/energy-breakdown.js.map +1 -1
  35. package/dist/components/field.cjs +1 -1
  36. package/dist/components/field.cjs.map +1 -1
  37. package/dist/components/field.js +1 -1
  38. package/dist/components/field.js.map +1 -1
  39. package/dist/components/icon.cjs +4 -4
  40. package/dist/components/icon.cjs.map +1 -1
  41. package/dist/components/icon.js +4 -4
  42. package/dist/components/icon.js.map +1 -1
  43. package/dist/components/input-date.cjs +2 -1
  44. package/dist/components/input-date.cjs.map +1 -1
  45. package/dist/components/input-date.d.cts +3 -1
  46. package/dist/components/input-date.d.ts +3 -1
  47. package/dist/components/input-date.js +2 -1
  48. package/dist/components/input-date.js.map +1 -1
  49. package/dist/components/input-group.d.cts +1 -1
  50. package/dist/components/input-group.d.ts +1 -1
  51. package/dist/components/line-chart.cjs +2 -2
  52. package/dist/components/line-chart.cjs.map +1 -1
  53. package/dist/components/line-chart.js +3 -3
  54. package/dist/components/line-chart.js.map +1 -1
  55. package/dist/components/pill.cjs +2 -2
  56. package/dist/components/pill.cjs.map +1 -1
  57. package/dist/components/pill.js +2 -2
  58. package/dist/components/pill.js.map +1 -1
  59. package/dist/components/power-history-chart.cjs +1 -1
  60. package/dist/components/power-history-chart.cjs.map +1 -1
  61. package/dist/components/power-history-chart.js +1 -1
  62. package/dist/components/power-history-chart.js.map +1 -1
  63. package/dist/components/progress.cjs +1 -1
  64. package/dist/components/progress.cjs.map +1 -1
  65. package/dist/components/progress.js +1 -1
  66. package/dist/components/progress.js.map +1 -1
  67. package/dist/components/sankey-chart.cjs +2 -2
  68. package/dist/components/sankey-chart.cjs.map +1 -1
  69. package/dist/components/sankey-chart.js +2 -2
  70. package/dist/components/sankey-chart.js.map +1 -1
  71. package/dist/components/segmented-control.cjs +4 -4
  72. package/dist/components/segmented-control.cjs.map +1 -1
  73. package/dist/components/segmented-control.d.cts +2 -16
  74. package/dist/components/segmented-control.d.ts +2 -16
  75. package/dist/components/segmented-control.js +5 -5
  76. package/dist/components/segmented-control.js.map +1 -1
  77. package/dist/components/slider.cjs.map +1 -1
  78. package/dist/components/slider.js.map +1 -1
  79. package/dist/components/switch.cjs +1 -1
  80. package/dist/components/switch.cjs.map +1 -1
  81. package/dist/components/switch.js +1 -1
  82. package/dist/components/switch.js.map +1 -1
  83. package/dist/components/tabs.cjs +2 -2
  84. package/dist/components/tabs.cjs.map +1 -1
  85. package/dist/components/tabs.d.cts +1 -8
  86. package/dist/components/tabs.d.ts +1 -8
  87. package/dist/components/tabs.js +2 -2
  88. package/dist/components/tabs.js.map +1 -1
  89. package/dist/components/toast.cjs +32 -7
  90. package/dist/components/toast.cjs.map +1 -1
  91. package/dist/components/toast.d.cts +15 -2
  92. package/dist/components/toast.d.ts +15 -2
  93. package/dist/components/toast.js +32 -7
  94. package/dist/components/toast.js.map +1 -1
  95. package/dist/components/toggle-group.cjs +40 -0
  96. package/dist/components/toggle-group.cjs.map +1 -0
  97. package/dist/components/toggle-group.d.cts +28 -0
  98. package/dist/components/toggle-group.d.ts +28 -0
  99. package/dist/components/toggle-group.js +37 -0
  100. package/dist/components/toggle-group.js.map +1 -0
  101. package/dist/components/toggle.cjs +69 -0
  102. package/dist/components/toggle.cjs.map +1 -0
  103. package/dist/components/toggle.d.cts +23 -0
  104. package/dist/components/toggle.d.ts +23 -0
  105. package/dist/components/toggle.js +66 -0
  106. package/dist/components/toggle.js.map +1 -0
  107. package/dist/components/tooltip.cjs +1 -1
  108. package/dist/components/tooltip.cjs.map +1 -1
  109. package/dist/components/tooltip.js +1 -1
  110. package/dist/components/tooltip.js.map +1 -1
  111. package/dist/lib/generate-radix-colors.cjs +1 -1
  112. package/dist/lib/generate-radix-colors.cjs.map +1 -1
  113. package/dist/lib/generate-radix-colors.js +1 -1
  114. package/dist/lib/generate-radix-colors.js.map +1 -1
  115. package/dist/lib/toggle-group-context.cjs +12 -0
  116. package/dist/lib/toggle-group-context.cjs.map +1 -0
  117. package/dist/lib/toggle-group-context.d.cts +17 -0
  118. package/dist/lib/toggle-group-context.d.ts +17 -0
  119. package/dist/lib/toggle-group-context.js +9 -0
  120. package/dist/lib/toggle-group-context.js.map +1 -0
  121. package/dist/styles/theme.css +15 -4
  122. package/dist/utils/format.cjs +1 -0
  123. package/dist/utils/format.cjs.map +1 -1
  124. package/dist/utils/format.d.cts +2 -1
  125. package/dist/utils/format.d.ts +2 -1
  126. package/dist/utils/format.js +1 -1
  127. package/dist/utils/format.js.map +1 -1
  128. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"power-history-chart.cjs","names":["React","useReducedMotion","valueFormat","useChartZoom","buildYAxisScale","useAnimatedChartDomain","measureYAxisGutter","formatYAxisTick","buildTimeTicks","useMeasuredSize","clampTooltipPosition","SectionTitle","SeriesLegend","Area","curveMonotoneX","LinePath","ChartZoomResetButton","ChartTooltipCard"],"sources":["../../src/components/power-history-chart.tsx"],"sourcesContent":["import { curveMonotoneX } from '@visx/curve'\nimport { localPoint } from '@visx/event'\nimport { scaleLinear } from '@visx/scale'\nimport { Area, LinePath } from '@visx/shape'\nimport { useTooltip } from '@visx/tooltip'\nimport { bisector } from '@visx/vendor/d3-array'\nimport * as React from 'react'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { useAnimatedChartDomain, useChartZoom } from '../hooks/use-chart-zoom'\nimport { useReducedMotion } from '../hooks/use-reduced-motion'\nimport { NARROW_NBSP } from '../utils/chars'\nimport {\n buildYAxisScale,\n formatYAxisTick,\n measureYAxisGutter,\n} from '../utils/chart-scale'\nimport { buildTimeTicks } from '../utils/chart-time-ticks'\nimport type { TimeDomain } from '../utils/chart-zoom'\nimport { cn } from '../utils/cn'\nimport { twoDecimalFormat as valueFormat } from '../utils/format'\nimport {\n ChartTooltipCard,\n type ChartTooltipRow,\n ChartZoomResetButton,\n clampTooltipPosition,\n useMeasuredSize,\n} from './chart'\nimport { SeriesLegend, type SeriesLegendItem } from './series-legend'\nimport { SectionTitle } from './typography'\n\nexport interface PowerHistoryPoint {\n /** Epoch milliseconds. */\n t: number\n solar: number\n /** Signed: positive = import, negative = feed-in. */\n grid: number\n /** Signed: positive = discharge, negative = charge. */\n battery: number\n consumption: number\n}\n\nexport interface PowerHistoryChartProps {\n data: PowerHistoryPoint[]\n /** Axis/tooltip unit, e.g. \"kW\". */\n unit: string\n formatX: (t: number) => string\n /** Tooltip time formatter. Defaults to `formatX`; use to keep the tooltip's\n * full clock time when the axis ticks are compact (hour-only). */\n formatTooltipX?: (t: number) => string\n formatValue?: (value: number) => string\n labels: {\n solar: string\n grid: string\n battery: string\n consumption: string\n }\n /** Series to drop entirely (no legend entry, area, or tooltip row). */\n omitSeries?: readonly PowerHistorySeriesKey[]\n /** Epoch ms to mark with the current-time dashed line (omit to hide). */\n highlightAt?: number\n title?: string\n className?: string\n height?: number\n zoomable?: boolean\n zoomResetLabel?: string\n zoomDomain?: TimeDomain | null\n onZoomDomainChange?: (domain: TimeDomain | null) => void\n}\n\nconst AREA_KEYS = ['solar', 'grid', 'battery'] as const\n\n// All toggleable series, in legend order. The areas (solar/grid/battery) plus\n// the consumption overlay line.\nconst SERIES_KEYS = ['solar', 'grid', 'battery', 'consumption'] as const\nexport type PowerHistorySeriesKey = (typeof SERIES_KEYS)[number]\ntype SeriesKey = PowerHistorySeriesKey\n\nconst SERIES_COLORS = {\n solar: 'var(--orange-a9)',\n grid: 'var(--blue-9)',\n battery: 'var(--green-a9)',\n consumption: 'var(--neutral-12)',\n} as const\n\nconst PADDING_TOP = 20\nconst PADDING_BOTTOM = 28\nconst PADDING_RIGHT = 8\nconst X_TICK_COUNT = 6\nconst DEFAULT_HEIGHT = 260\nconst DETAILED_X_AXIS_THRESHOLD = 6 * 60 * 60 * 1000\n\nconst tBisector = bisector<PowerHistoryPoint, number>((d) => d.t)\n\nfunction touchDistance(a: React.Touch, b: React.Touch): number {\n return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY)\n}\n\nfunction touchMidpoint(\n a: React.Touch,\n b: React.Touch,\n): {\n clientX: number\n clientY: number\n} {\n return {\n clientX: (a.clientX + b.clientX) / 2,\n clientY: (a.clientY + b.clientY) / 2,\n }\n}\n\n/**\n * Diverging \"power sources\" area chart over a continuous time axis: solar\n * (orange), grid (blue) and battery (green) areas around a zero baseline, with\n * consumption overlaid as a line. Negative values (grid feed-in, battery\n * charging) dip below zero. For instantaneous power (day view). A legend at the\n * top toggles each series on/off; the y-axis rescales to the visible ones. visx.\n */\nexport function PowerHistoryChart({\n data,\n unit,\n formatX,\n formatTooltipX,\n formatValue,\n labels,\n omitSeries,\n highlightAt,\n title,\n className,\n height = DEFAULT_HEIGHT,\n zoomable = false,\n zoomResetLabel,\n zoomDomain,\n onZoomDomainChange,\n}: PowerHistoryChartProps) {\n const plotClipId = React.useId()\n const fmtTooltipX = formatTooltipX ?? formatX\n const containerRef = useRef<HTMLDivElement>(null)\n const reducedMotion = useReducedMotion()\n const [width, setWidth] = useState(0)\n const [hiddenSeries, setHiddenSeries] = useState<ReadonlySet<string>>(\n () => new Set(),\n )\n const omitted = useMemo(() => new Set<SeriesKey>(omitSeries), [omitSeries])\n\n const toggleSeries = useCallback((id: string) => {\n setHiddenSeries((prev) => {\n const next = new Set(prev)\n if (next.has(id)) {\n next.delete(id)\n } else {\n next.add(id)\n }\n return next\n })\n }, [])\n\n const isHidden = useCallback(\n (key: SeriesKey) => omitted.has(key) || hiddenSeries.has(key),\n [hiddenSeries, omitted],\n )\n\n const {\n showTooltip,\n hideTooltip,\n tooltipData,\n tooltipLeft,\n tooltipTop,\n tooltipOpen,\n } = useTooltip<PowerHistoryPoint>()\n\n useEffect(() => {\n const element = containerRef.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n setWidth(entry.contentRect.width)\n })\n\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [])\n\n const fmtValue = (value: number) =>\n formatValue\n ? formatValue(value)\n : `${valueFormat.format(value)}${NARROW_NBSP}${unit}`\n\n const tMin = data.length ? (data[0]?.t ?? 0) : 0\n const tMax = data.length ? (data[data.length - 1]?.t ?? 0) : 0\n const fullDomain = useMemo((): TimeDomain | null => {\n if (data.length === 0) return null\n return [tMin, tMax]\n }, [data.length, tMin, tMax])\n\n const zoom = useChartZoom({\n enabled: zoomable,\n fullDomain,\n zoomDomain,\n onZoomDomainChange,\n })\n\n const viewMin = zoom.zoomWindow?.[0] ?? tMin\n const viewMax = zoom.zoomWindow?.[1] ?? tMax\n\n const visibleData = useMemo(\n () => data.filter((d) => d.t >= viewMin && d.t <= viewMax),\n [data, viewMin, viewMax],\n )\n\n const { yMin, yMax } = useMemo(() => {\n let min = 0\n let max = 0\n for (const d of visibleData) {\n if (!isHidden('solar')) max = Math.max(max, d.solar)\n if (!isHidden('consumption')) max = Math.max(max, d.consumption)\n if (!isHidden('grid')) {\n max = Math.max(max, d.grid)\n min = Math.min(min, d.grid)\n }\n if (!isHidden('battery')) {\n max = Math.max(max, d.battery)\n min = Math.min(min, d.battery)\n }\n }\n return { yMin: min, yMax: max }\n }, [visibleData, isHidden])\n\n const {\n ticks: yTicks,\n min: lo,\n max: hi,\n } = useMemo(() => buildYAxisScale(yMin, yMax), [yMin, yMax])\n const displayedYDomain = useAnimatedChartDomain([lo, hi], {\n animateChanges: zoom.isPinching,\n reducedMotion,\n })\n\n const gutter = useMemo(\n () => measureYAxisGutter(yTicks, formatYAxisTick),\n [yTicks],\n )\n\n const plotRight = Math.max(gutter, width - PADDING_RIGHT)\n\n const xScale = useMemo(\n () =>\n scaleLinear<number>({\n domain: [viewMin, viewMax === viewMin ? viewMin + 1 : viewMax],\n range: [gutter, plotRight],\n }),\n [viewMin, viewMax, plotRight, gutter],\n )\n\n const plotHeight = Math.max(0, height - PADDING_BOTTOM - PADDING_TOP)\n const plotWidth = Math.max(0, plotRight - gutter)\n\n const yScale = useMemo(\n () =>\n scaleLinear<number>({\n domain: [\n displayedYDomain[0],\n displayedYDomain[1] === displayedYDomain[0]\n ? displayedYDomain[0] + 1\n : displayedYDomain[1],\n ],\n range: [height - PADDING_BOTTOM, PADDING_TOP],\n }),\n [displayedYDomain, height],\n )\n\n const xTicks = useMemo(\n () => buildTimeTicks(viewMin, viewMax, X_TICK_COUNT),\n [viewMin, viewMax],\n )\n const formatAxisX =\n zoom.isZoomed && viewMax - viewMin <= DETAILED_X_AXIS_THRESHOLD\n ? fmtTooltipX\n : formatX\n\n const domainXFromEvent = useCallback(\n (\n event:\n | React.MouseEvent<SVGRectElement>\n | React.PointerEvent<SVGRectElement>\n | React.TouchEvent<SVGRectElement>,\n ) => {\n const point = localPoint(event)\n if (!point) return null\n return xScale.invert(point.x)\n },\n [xScale],\n )\n\n const handleTooltip = useCallback(\n (\n event:\n | React.TouchEvent<SVGRectElement>\n | React.MouseEvent<SVGRectElement>,\n ) => {\n if (zoom.isBrushing || zoom.isPanning) return\n const point = localPoint(event)\n if (!point || data.length === 0) return\n\n const xMs = xScale.invert(point.x)\n const index = tBisector.left(data, xMs, 1)\n const d0 = data[index - 1]\n const d1 = data[index]\n if (!d0) return\n\n const d = d1 && xMs - d0.t > d1.t - xMs ? d1 : d0\n showTooltip({\n tooltipData: d,\n tooltipLeft: xScale(d.t),\n tooltipTop: PADDING_TOP,\n })\n },\n [data, showTooltip, xScale, zoom.isBrushing, zoom.isPanning],\n )\n\n const legendItems: SeriesLegendItem[] = SERIES_KEYS.filter(\n (key) => !omitted.has(key),\n ).map((key) => ({\n id: key,\n label: labels[key],\n color: SERIES_COLORS[key],\n }))\n\n const tooltipRows: ChartTooltipRow[] = tooltipData\n ? SERIES_KEYS.filter((key) => !isHidden(key)).map((key) => ({\n key,\n label: labels[key],\n color: SERIES_COLORS[key],\n value: fmtValue(tooltipData[key]),\n }))\n : []\n\n const showHighlight =\n highlightAt !== undefined &&\n highlightAt >= viewMin &&\n highlightAt <= viewMax\n\n const [tooltipRef, tooltipSize] = useMeasuredSize<HTMLDivElement>()\n const tooltipPos = clampTooltipPosition({\n anchorX: tooltipLeft ?? 0,\n anchorY: tooltipTop ?? PADDING_TOP,\n width,\n height,\n tipWidth: tooltipSize.width,\n tipHeight: tooltipSize.height,\n placement: 'beside',\n })\n\n return (\n <div className={cn('flex w-full flex-col gap-2', className)}>\n {title && <SectionTitle>{title}</SectionTitle>}\n <SeriesLegend\n items={legendItems}\n hiddenIds={hiddenSeries}\n onToggle={toggleSeries}\n />\n <span className=\"px-1 text-xs text-gray-light\">{unit}</span>\n <div ref={containerRef} className=\"relative w-full\" style={{ height }}>\n {width > 0 && (\n <svg width={width} height={height}>\n <defs>\n {AREA_KEYS.map((key) => (\n <linearGradient\n key={key}\n id={`power-history-fill-${key}`}\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"1\"\n >\n <stop\n offset=\"0%\"\n stopColor={SERIES_COLORS[key]}\n stopOpacity={0.35}\n />\n <stop\n offset=\"100%\"\n stopColor={SERIES_COLORS[key]}\n stopOpacity={0.04}\n />\n </linearGradient>\n ))}\n <clipPath id={plotClipId}>\n <rect\n height={plotHeight}\n width={plotWidth}\n x={gutter}\n y={PADDING_TOP}\n />\n </clipPath>\n </defs>\n\n {yTicks.map((tick) => {\n const y = yScale(tick)\n return (\n <g key={`y-tick-${tick}`}>\n <line\n x1={gutter}\n x2={plotRight}\n y1={y}\n y2={y}\n stroke=\"var(--neutral-a4)\"\n strokeWidth={1}\n />\n <text\n x={gutter - 7}\n y={y + 3}\n textAnchor=\"end\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n >\n {formatYAxisTick(tick)}\n </text>\n </g>\n )\n })}\n\n <g clipPath={`url(#${plotClipId})`} data-testid=\"chart-plot-clip\">\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <Area<PowerHistoryPoint>\n key={`area-${key}`}\n data={data}\n x={(d) => xScale(d.t)}\n y0={yScale(0)}\n y1={(d) => yScale(d[key])}\n curve={curveMonotoneX}\n fill={`url(#power-history-fill-${key})`}\n stroke=\"transparent\"\n />\n ))}\n\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <LinePath<PowerHistoryPoint>\n key={`line-${key}`}\n data={data}\n x={(d) => xScale(d.t)}\n y={(d) => yScale(d[key])}\n curve={curveMonotoneX}\n stroke={SERIES_COLORS[key]}\n strokeWidth={1.5}\n fill=\"none\"\n />\n ))}\n\n {!isHidden('consumption') && (\n <LinePath<PowerHistoryPoint>\n data={data}\n x={(d) => xScale(d.t)}\n y={(d) => yScale(d.consumption)}\n curve={curveMonotoneX}\n stroke={SERIES_COLORS.consumption}\n strokeWidth={1.5}\n fill=\"none\"\n />\n )}\n </g>\n\n <line\n x1={gutter}\n x2={plotRight}\n y1={yScale(0)}\n y2={yScale(0)}\n stroke=\"var(--neutral-a6)\"\n strokeWidth={1}\n />\n\n {showHighlight && (\n <line\n x1={xScale(highlightAt)}\n x2={xScale(highlightAt)}\n y1={PADDING_TOP}\n y2={height - PADDING_BOTTOM}\n stroke=\"var(--spark-a9)\"\n strokeWidth={1.5}\n strokeDasharray=\"5 4\"\n pointerEvents=\"none\"\n />\n )}\n\n {xTicks.map((tick, index) => (\n <text\n key={`x-tick-${tick}`}\n x={xScale(tick)}\n y={height - PADDING_BOTTOM + 16}\n textAnchor={\n index === 0\n ? 'start'\n : index === xTicks.length - 1\n ? 'end'\n : 'middle'\n }\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n >\n {formatAxisX(tick)}\n </text>\n ))}\n\n {tooltipOpen &&\n tooltipData &&\n !zoom.isBrushing &&\n !zoom.isPanning && (\n <>\n <line\n x1={xScale(tooltipData.t)}\n x2={xScale(tooltipData.t)}\n y1={PADDING_TOP}\n y2={height - PADDING_BOTTOM}\n stroke=\"var(--neutral-a6)\"\n strokeDasharray=\"4 4\"\n pointerEvents=\"none\"\n />\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <circle\n key={`dot-${key}`}\n cx={xScale(tooltipData.t)}\n cy={yScale(tooltipData[key])}\n r={3.5}\n fill={SERIES_COLORS[key]}\n stroke=\"white\"\n strokeWidth={1.5}\n pointerEvents=\"none\"\n />\n ))}\n {!isHidden('consumption') && (\n <circle\n cx={xScale(tooltipData.t)}\n cy={yScale(tooltipData.consumption)}\n r={3.5}\n fill={SERIES_COLORS.consumption}\n stroke=\"white\"\n strokeWidth={1.5}\n pointerEvents=\"none\"\n />\n )}\n </>\n )}\n\n {zoom.brushBand && (\n <rect\n data-testid=\"chart-brush-band\"\n x={Math.min(\n xScale(zoom.brushBand[0]),\n xScale(zoom.brushBand[1]),\n )}\n y={PADDING_TOP}\n width={Math.abs(\n xScale(zoom.brushBand[1]) - xScale(zoom.brushBand[0]),\n )}\n height={plotHeight}\n fill=\"var(--spark-a4)\"\n pointerEvents=\"none\"\n />\n )}\n\n <rect\n x={gutter}\n y={PADDING_TOP}\n width={plotWidth}\n height={plotHeight}\n fill=\"transparent\"\n style={zoomable ? { touchAction: 'pan-y' } : undefined}\n onMouseLeave={() => {\n if (!zoom.isBrushing) hideTooltip()\n }}\n onMouseMove={(event) => {\n if (zoom.isBrushing) {\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n return\n }\n handleTooltip(event)\n }}\n onPointerDown={(event) => {\n if (!zoomable || event.pointerType === 'touch') return\n if (event.button !== 0) return\n const domainX = domainXFromEvent(event)\n if (domainX == null) return\n event.currentTarget.setPointerCapture?.(event.pointerId)\n hideTooltip()\n zoom.onPointerDown(domainX)\n }}\n onPointerMove={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n }}\n onPointerUp={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerUp(domainX)\n else zoom.onPointerCancel()\n }}\n onPointerCancel={() => {\n zoom.onPointerCancel()\n }}\n onDoubleClick={() => {\n if (zoomable) zoom.onDoubleClick()\n }}\n onTouchStart={(event) => {\n if (!zoomable) return\n if (event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - gutter) / plotWidth),\n )\n zoom.onPinchStart(touchDistance(a, b), midpoint)\n hideTooltip()\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanStart({\n x: (point.x - gutter) / plotWidth,\n y: (point.y - PADDING_TOP) / plotHeight,\n })\n hideTooltip()\n }\n }}\n onTouchMove={(event) => {\n if (zoomable && event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - gutter) / plotWidth),\n )\n zoom.onPinchMove(touchDistance(a, b), midpoint)\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanMove({\n x: (point.x - gutter) / plotWidth,\n y: (point.y - PADDING_TOP) / plotHeight,\n })\n return\n }\n if (event.touches.length === 1) handleTooltip(event)\n }}\n onTouchEnd={(event) => {\n if (zoomable && event.touches.length < 2) zoom.onPinchEnd()\n if (event.touches.length === 0) {\n if (zoomable) zoom.onPanEnd()\n if (zoomable) zoom.onTap()\n hideTooltip()\n }\n }}\n onTouchCancel={() => {\n zoom.onPinchEnd()\n zoom.onPanEnd()\n }}\n />\n </svg>\n )}\n\n {zoom.isZoomed && zoomResetLabel && (\n <ChartZoomResetButton\n label={zoomResetLabel}\n onReset={zoom.resetZoom}\n />\n )}\n\n <div\n ref={tooltipRef}\n className=\"pointer-events-none absolute z-10\"\n style={{\n left: tooltipPos.left,\n top: tooltipPos.top,\n opacity:\n tooltipOpen && tooltipData && !zoom.isPanning && !zoom.isBrushing\n ? 1\n : 0,\n transition: reducedMotion\n ? 'opacity 140ms ease-out'\n : 'left 140ms ease-out, top 140ms ease-out, opacity 140ms ease-out',\n }}\n >\n {tooltipData && (\n <ChartTooltipCard\n label={fmtTooltipX(tooltipData.t)}\n rows={tooltipRows}\n />\n )}\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"power-history-chart.cjs","names":["React","useReducedMotion","valueFormat","useChartZoom","buildYAxisScale","useAnimatedChartDomain","measureYAxisGutter","formatYAxisTick","buildTimeTicks","useMeasuredSize","clampTooltipPosition","SectionTitle","SeriesLegend","Area","curveMonotoneX","LinePath","ChartZoomResetButton","ChartTooltipCard"],"sources":["../../src/components/power-history-chart.tsx"],"sourcesContent":["import { curveMonotoneX } from '@visx/curve'\nimport { localPoint } from '@visx/event'\nimport { scaleLinear } from '@visx/scale'\nimport { Area, LinePath } from '@visx/shape'\nimport { useTooltip } from '@visx/tooltip'\nimport { bisector } from '@visx/vendor/d3-array'\nimport * as React from 'react'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { useAnimatedChartDomain, useChartZoom } from '../hooks/use-chart-zoom'\nimport { useReducedMotion } from '../hooks/use-reduced-motion'\nimport { NARROW_NBSP } from '../utils/chars'\nimport {\n buildYAxisScale,\n formatYAxisTick,\n measureYAxisGutter,\n} from '../utils/chart-scale'\nimport { buildTimeTicks } from '../utils/chart-time-ticks'\nimport type { TimeDomain } from '../utils/chart-zoom'\nimport { cn } from '../utils/cn'\nimport { twoDecimalFormat as valueFormat } from '../utils/format'\nimport {\n ChartTooltipCard,\n type ChartTooltipRow,\n ChartZoomResetButton,\n clampTooltipPosition,\n useMeasuredSize,\n} from './chart'\nimport { SeriesLegend, type SeriesLegendItem } from './series-legend'\nimport { SectionTitle } from './typography'\n\nexport interface PowerHistoryPoint {\n /** Epoch milliseconds. */\n t: number\n solar: number\n /** Signed: positive = import, negative = feed-in. */\n grid: number\n /** Signed: positive = discharge, negative = charge. */\n battery: number\n consumption: number\n}\n\nexport interface PowerHistoryChartProps {\n data: PowerHistoryPoint[]\n /** Axis/tooltip unit, e.g. \"kW\". */\n unit: string\n formatX: (t: number) => string\n /** Tooltip time formatter. Defaults to `formatX`; use to keep the tooltip's\n * full clock time when the axis ticks are compact (hour-only). */\n formatTooltipX?: (t: number) => string\n formatValue?: (value: number) => string\n labels: {\n solar: string\n grid: string\n battery: string\n consumption: string\n }\n /** Series to drop entirely (no legend entry, area, or tooltip row). */\n omitSeries?: readonly PowerHistorySeriesKey[]\n /** Epoch ms to mark with the current-time dashed line (omit to hide). */\n highlightAt?: number\n title?: string\n className?: string\n height?: number\n zoomable?: boolean\n zoomResetLabel?: string\n zoomDomain?: TimeDomain | null\n onZoomDomainChange?: (domain: TimeDomain | null) => void\n}\n\nconst AREA_KEYS = ['solar', 'grid', 'battery'] as const\n\n// All toggleable series, in legend order. The areas (solar/grid/battery) plus\n// the consumption overlay line.\nconst SERIES_KEYS = ['solar', 'grid', 'battery', 'consumption'] as const\nexport type PowerHistorySeriesKey = (typeof SERIES_KEYS)[number]\ntype SeriesKey = PowerHistorySeriesKey\n\nconst SERIES_COLORS = {\n solar: 'var(--orange-a9)',\n grid: 'var(--blue-9)',\n battery: 'var(--green-a9)',\n consumption: 'var(--neutral-12)',\n} as const\n\nconst PADDING_TOP = 20\nconst PADDING_BOTTOM = 28\nconst PADDING_RIGHT = 8\nconst X_TICK_COUNT = 6\nconst DEFAULT_HEIGHT = 260\nconst DETAILED_X_AXIS_THRESHOLD = 6 * 60 * 60 * 1000\n\nconst tBisector = bisector<PowerHistoryPoint, number>((d) => d.t)\n\nfunction touchDistance(a: React.Touch, b: React.Touch): number {\n return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY)\n}\n\nfunction touchMidpoint(\n a: React.Touch,\n b: React.Touch,\n): {\n clientX: number\n clientY: number\n} {\n return {\n clientX: (a.clientX + b.clientX) / 2,\n clientY: (a.clientY + b.clientY) / 2,\n }\n}\n\n/**\n * Diverging \"power sources\" area chart over a continuous time axis: solar\n * (orange), grid (blue) and battery (green) areas around a zero baseline, with\n * consumption overlaid as a line. Negative values (grid feed-in, battery\n * charging) dip below zero. For instantaneous power (day view). A legend at the\n * top toggles each series on/off; the y-axis rescales to the visible ones. visx.\n */\n// oxlint-disable-next-line eslint/complexity -- pre-existing; split it before adding branches\nexport function PowerHistoryChart({\n data,\n unit,\n formatX,\n formatTooltipX,\n formatValue,\n labels,\n omitSeries,\n highlightAt,\n title,\n className,\n height = DEFAULT_HEIGHT,\n zoomable = false,\n zoomResetLabel,\n zoomDomain,\n onZoomDomainChange,\n}: PowerHistoryChartProps) {\n const plotClipId = React.useId()\n const fmtTooltipX = formatTooltipX ?? formatX\n const containerRef = useRef<HTMLDivElement>(null)\n const reducedMotion = useReducedMotion()\n const [width, setWidth] = useState(0)\n const [hiddenSeries, setHiddenSeries] = useState<ReadonlySet<string>>(\n () => new Set(),\n )\n const omitted = useMemo(() => new Set<SeriesKey>(omitSeries), [omitSeries])\n\n const toggleSeries = useCallback((id: string) => {\n setHiddenSeries((prev) => {\n const next = new Set(prev)\n if (next.has(id)) {\n next.delete(id)\n } else {\n next.add(id)\n }\n return next\n })\n }, [])\n\n const isHidden = useCallback(\n (key: SeriesKey) => omitted.has(key) || hiddenSeries.has(key),\n [hiddenSeries, omitted],\n )\n\n const {\n showTooltip,\n hideTooltip,\n tooltipData,\n tooltipLeft,\n tooltipTop,\n tooltipOpen,\n } = useTooltip<PowerHistoryPoint>()\n\n useEffect(() => {\n const element = containerRef.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n setWidth(entry.contentRect.width)\n })\n\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [])\n\n const fmtValue = (value: number) =>\n formatValue\n ? formatValue(value)\n : `${valueFormat.format(value)}${NARROW_NBSP}${unit}`\n\n const tMin = data.length ? (data[0]?.t ?? 0) : 0\n const tMax = data.length ? (data[data.length - 1]?.t ?? 0) : 0\n const fullDomain = useMemo((): TimeDomain | null => {\n if (data.length === 0) return null\n return [tMin, tMax]\n }, [data.length, tMin, tMax])\n\n const zoom = useChartZoom({\n enabled: zoomable,\n fullDomain,\n zoomDomain,\n onZoomDomainChange,\n })\n\n const viewMin = zoom.zoomWindow?.[0] ?? tMin\n const viewMax = zoom.zoomWindow?.[1] ?? tMax\n\n const visibleData = useMemo(\n () => data.filter((d) => d.t >= viewMin && d.t <= viewMax),\n [data, viewMin, viewMax],\n )\n\n const { yMin, yMax } = useMemo(() => {\n let min = 0\n let max = 0\n for (const d of visibleData) {\n if (!isHidden('solar')) max = Math.max(max, d.solar)\n if (!isHidden('consumption')) max = Math.max(max, d.consumption)\n if (!isHidden('grid')) {\n max = Math.max(max, d.grid)\n min = Math.min(min, d.grid)\n }\n if (!isHidden('battery')) {\n max = Math.max(max, d.battery)\n min = Math.min(min, d.battery)\n }\n }\n return { yMin: min, yMax: max }\n }, [visibleData, isHidden])\n\n const {\n ticks: yTicks,\n min: lo,\n max: hi,\n } = useMemo(() => buildYAxisScale(yMin, yMax), [yMin, yMax])\n const displayedYDomain = useAnimatedChartDomain([lo, hi], {\n animateChanges: zoom.isPinching,\n reducedMotion,\n })\n\n const gutter = useMemo(\n () => measureYAxisGutter(yTicks, formatYAxisTick),\n [yTicks],\n )\n\n const plotRight = Math.max(gutter, width - PADDING_RIGHT)\n\n const xScale = useMemo(\n () =>\n scaleLinear<number>({\n domain: [viewMin, viewMax === viewMin ? viewMin + 1 : viewMax],\n range: [gutter, plotRight],\n }),\n [viewMin, viewMax, plotRight, gutter],\n )\n\n const plotHeight = Math.max(0, height - PADDING_BOTTOM - PADDING_TOP)\n const plotWidth = Math.max(0, plotRight - gutter)\n\n const yScale = useMemo(\n () =>\n scaleLinear<number>({\n domain: [\n displayedYDomain[0],\n displayedYDomain[1] === displayedYDomain[0]\n ? displayedYDomain[0] + 1\n : displayedYDomain[1],\n ],\n range: [height - PADDING_BOTTOM, PADDING_TOP],\n }),\n [displayedYDomain, height],\n )\n\n const xTicks = useMemo(\n () => buildTimeTicks(viewMin, viewMax, X_TICK_COUNT),\n [viewMin, viewMax],\n )\n const formatAxisX =\n zoom.isZoomed && viewMax - viewMin <= DETAILED_X_AXIS_THRESHOLD\n ? fmtTooltipX\n : formatX\n\n const domainXFromEvent = useCallback(\n (\n event:\n | React.MouseEvent<SVGRectElement>\n | React.PointerEvent<SVGRectElement>\n | React.TouchEvent<SVGRectElement>,\n ) => {\n const point = localPoint(event)\n if (!point) return null\n return xScale.invert(point.x)\n },\n [xScale],\n )\n\n const handleTooltip = useCallback(\n (\n event:\n | React.TouchEvent<SVGRectElement>\n | React.MouseEvent<SVGRectElement>,\n ) => {\n if (zoom.isBrushing || zoom.isPanning) return\n const point = localPoint(event)\n if (!point || data.length === 0) return\n\n const xMs = xScale.invert(point.x)\n const index = tBisector.left(data, xMs, 1)\n const d0 = data[index - 1]\n const d1 = data[index]\n if (!d0) return\n\n const d = d1 && xMs - d0.t > d1.t - xMs ? d1 : d0\n showTooltip({\n tooltipData: d,\n tooltipLeft: xScale(d.t),\n tooltipTop: PADDING_TOP,\n })\n },\n [data, showTooltip, xScale, zoom.isBrushing, zoom.isPanning],\n )\n\n const legendItems: SeriesLegendItem[] = SERIES_KEYS.filter(\n (key) => !omitted.has(key),\n ).map((key) => ({\n id: key,\n label: labels[key],\n color: SERIES_COLORS[key],\n }))\n\n const tooltipRows: ChartTooltipRow[] = tooltipData\n ? SERIES_KEYS.filter((key) => !isHidden(key)).map((key) => ({\n key,\n label: labels[key],\n color: SERIES_COLORS[key],\n value: fmtValue(tooltipData[key]),\n }))\n : []\n\n const showHighlight =\n highlightAt !== undefined &&\n highlightAt >= viewMin &&\n highlightAt <= viewMax\n\n const [tooltipRef, tooltipSize] = useMeasuredSize<HTMLDivElement>()\n const tooltipPos = clampTooltipPosition({\n anchorX: tooltipLeft ?? 0,\n anchorY: tooltipTop ?? PADDING_TOP,\n width,\n height,\n tipWidth: tooltipSize.width,\n tipHeight: tooltipSize.height,\n placement: 'beside',\n })\n\n return (\n <div className={cn('flex w-full flex-col gap-2', className)}>\n {title && <SectionTitle>{title}</SectionTitle>}\n <SeriesLegend\n items={legendItems}\n hiddenIds={hiddenSeries}\n onToggle={toggleSeries}\n />\n <span className=\"px-1 text-xs text-gray-light\">{unit}</span>\n <div ref={containerRef} className=\"relative w-full\" style={{ height }}>\n {width > 0 && (\n <svg width={width} height={height}>\n <defs>\n {AREA_KEYS.map((key) => (\n <linearGradient\n key={key}\n id={`power-history-fill-${key}`}\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"1\"\n >\n <stop\n offset=\"0%\"\n stopColor={SERIES_COLORS[key]}\n stopOpacity={0.35}\n />\n <stop\n offset=\"100%\"\n stopColor={SERIES_COLORS[key]}\n stopOpacity={0.04}\n />\n </linearGradient>\n ))}\n <clipPath id={plotClipId}>\n <rect\n height={plotHeight}\n width={plotWidth}\n x={gutter}\n y={PADDING_TOP}\n />\n </clipPath>\n </defs>\n\n {yTicks.map((tick) => {\n const y = yScale(tick)\n return (\n <g key={`y-tick-${tick}`}>\n <line\n x1={gutter}\n x2={plotRight}\n y1={y}\n y2={y}\n stroke=\"var(--neutral-a4)\"\n strokeWidth={1}\n />\n <text\n x={gutter - 7}\n y={y + 3}\n textAnchor=\"end\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n >\n {formatYAxisTick(tick)}\n </text>\n </g>\n )\n })}\n\n <g clipPath={`url(#${plotClipId})`} data-testid=\"chart-plot-clip\">\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <Area<PowerHistoryPoint>\n key={`area-${key}`}\n data={data}\n x={(d) => xScale(d.t)}\n y0={yScale(0)}\n y1={(d) => yScale(d[key])}\n curve={curveMonotoneX}\n fill={`url(#power-history-fill-${key})`}\n stroke=\"transparent\"\n />\n ))}\n\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <LinePath<PowerHistoryPoint>\n key={`line-${key}`}\n data={data}\n x={(d) => xScale(d.t)}\n y={(d) => yScale(d[key])}\n curve={curveMonotoneX}\n stroke={SERIES_COLORS[key]}\n strokeWidth={1.5}\n fill=\"none\"\n />\n ))}\n\n {!isHidden('consumption') && (\n <LinePath<PowerHistoryPoint>\n data={data}\n x={(d) => xScale(d.t)}\n y={(d) => yScale(d.consumption)}\n curve={curveMonotoneX}\n stroke={SERIES_COLORS.consumption}\n strokeWidth={1.5}\n fill=\"none\"\n />\n )}\n </g>\n\n <line\n x1={gutter}\n x2={plotRight}\n y1={yScale(0)}\n y2={yScale(0)}\n stroke=\"var(--neutral-a6)\"\n strokeWidth={1}\n />\n\n {showHighlight && (\n <line\n x1={xScale(highlightAt)}\n x2={xScale(highlightAt)}\n y1={PADDING_TOP}\n y2={height - PADDING_BOTTOM}\n stroke=\"var(--spark-a9)\"\n strokeWidth={1.5}\n strokeDasharray=\"5 4\"\n pointerEvents=\"none\"\n />\n )}\n\n {xTicks.map((tick, index) => (\n <text\n key={`x-tick-${tick}`}\n x={xScale(tick)}\n y={height - PADDING_BOTTOM + 16}\n textAnchor={\n index === 0\n ? 'start'\n : index === xTicks.length - 1\n ? 'end'\n : 'middle'\n }\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n >\n {formatAxisX(tick)}\n </text>\n ))}\n\n {tooltipOpen &&\n tooltipData &&\n !zoom.isBrushing &&\n !zoom.isPanning && (\n <>\n <line\n x1={xScale(tooltipData.t)}\n x2={xScale(tooltipData.t)}\n y1={PADDING_TOP}\n y2={height - PADDING_BOTTOM}\n stroke=\"var(--neutral-a6)\"\n strokeDasharray=\"4 4\"\n pointerEvents=\"none\"\n />\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <circle\n key={`dot-${key}`}\n cx={xScale(tooltipData.t)}\n cy={yScale(tooltipData[key])}\n r={3.5}\n fill={SERIES_COLORS[key]}\n stroke=\"white\"\n strokeWidth={1.5}\n pointerEvents=\"none\"\n />\n ))}\n {!isHidden('consumption') && (\n <circle\n cx={xScale(tooltipData.t)}\n cy={yScale(tooltipData.consumption)}\n r={3.5}\n fill={SERIES_COLORS.consumption}\n stroke=\"white\"\n strokeWidth={1.5}\n pointerEvents=\"none\"\n />\n )}\n </>\n )}\n\n {zoom.brushBand && (\n <rect\n data-testid=\"chart-brush-band\"\n x={Math.min(\n xScale(zoom.brushBand[0]),\n xScale(zoom.brushBand[1]),\n )}\n y={PADDING_TOP}\n width={Math.abs(\n xScale(zoom.brushBand[1]) - xScale(zoom.brushBand[0]),\n )}\n height={plotHeight}\n fill=\"var(--spark-a4)\"\n pointerEvents=\"none\"\n />\n )}\n\n <rect\n x={gutter}\n y={PADDING_TOP}\n width={plotWidth}\n height={plotHeight}\n fill=\"transparent\"\n style={zoomable ? { touchAction: 'pan-y' } : undefined}\n onMouseLeave={() => {\n if (!zoom.isBrushing) hideTooltip()\n }}\n onMouseMove={(event) => {\n if (zoom.isBrushing) {\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n return\n }\n handleTooltip(event)\n }}\n onPointerDown={(event) => {\n if (!zoomable || event.pointerType === 'touch') return\n if (event.button !== 0) return\n const domainX = domainXFromEvent(event)\n if (domainX == null) return\n event.currentTarget.setPointerCapture?.(event.pointerId)\n hideTooltip()\n zoom.onPointerDown(domainX)\n }}\n onPointerMove={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n }}\n onPointerUp={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerUp(domainX)\n else zoom.onPointerCancel()\n }}\n onPointerCancel={() => {\n zoom.onPointerCancel()\n }}\n onDoubleClick={() => {\n if (zoomable) zoom.onDoubleClick()\n }}\n onTouchStart={(event) => {\n if (!zoomable) return\n if (event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - gutter) / plotWidth),\n )\n zoom.onPinchStart(touchDistance(a, b), midpoint)\n hideTooltip()\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanStart({\n x: (point.x - gutter) / plotWidth,\n y: (point.y - PADDING_TOP) / plotHeight,\n })\n hideTooltip()\n }\n }}\n onTouchMove={(event) => {\n if (zoomable && event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - gutter) / plotWidth),\n )\n zoom.onPinchMove(touchDistance(a, b), midpoint)\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanMove({\n x: (point.x - gutter) / plotWidth,\n y: (point.y - PADDING_TOP) / plotHeight,\n })\n return\n }\n if (event.touches.length === 1) handleTooltip(event)\n }}\n onTouchEnd={(event) => {\n if (zoomable && event.touches.length < 2) zoom.onPinchEnd()\n if (event.touches.length === 0) {\n if (zoomable) zoom.onPanEnd()\n if (zoomable) zoom.onTap()\n hideTooltip()\n }\n }}\n onTouchCancel={() => {\n zoom.onPinchEnd()\n zoom.onPanEnd()\n }}\n />\n </svg>\n )}\n\n {zoom.isZoomed && zoomResetLabel && (\n <ChartZoomResetButton\n label={zoomResetLabel}\n onReset={zoom.resetZoom}\n />\n )}\n\n <div\n ref={tooltipRef}\n className=\"pointer-events-none absolute z-10\"\n style={{\n left: tooltipPos.left,\n top: tooltipPos.top,\n opacity:\n tooltipOpen && tooltipData && !zoom.isPanning && !zoom.isBrushing\n ? 1\n : 0,\n transition: reducedMotion\n ? 'opacity 140ms ease-out'\n : 'left 140ms ease-out, top 140ms ease-out, opacity 140ms ease-out',\n }}\n >\n {tooltipData && (\n <ChartTooltipCard\n label={fmtTooltipX(tooltipData.t)}\n rows={tooltipRows}\n />\n )}\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
@@ -5,10 +5,10 @@ import { cn } from "../utils/cn.js";
5
5
  import "../utils/chars.js";
6
6
  import { twoDecimalFormat } from "../utils/format.js";
7
7
  import { buildYAxisScale, formatYAxisTick, measureYAxisGutter } from "../utils/chart-scale.js";
8
+ import { buildTimeTicks } from "../utils/chart-time-ticks.js";
8
9
  import { ChartTooltipCard, ChartZoomResetButton, clampTooltipPosition, useMeasuredSize } from "./chart.js";
9
10
  import { SeriesLegend } from "./series-legend.js";
10
11
  import { SectionTitle } from "./typography.js";
11
- import { buildTimeTicks } from "../utils/chart-time-ticks.js";
12
12
  import * as React$1 from "react";
13
13
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
14
14
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"power-history-chart.js","names":["React","valueFormat"],"sources":["../../src/components/power-history-chart.tsx"],"sourcesContent":["import { curveMonotoneX } from '@visx/curve'\nimport { localPoint } from '@visx/event'\nimport { scaleLinear } from '@visx/scale'\nimport { Area, LinePath } from '@visx/shape'\nimport { useTooltip } from '@visx/tooltip'\nimport { bisector } from '@visx/vendor/d3-array'\nimport * as React from 'react'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { useAnimatedChartDomain, useChartZoom } from '../hooks/use-chart-zoom'\nimport { useReducedMotion } from '../hooks/use-reduced-motion'\nimport { NARROW_NBSP } from '../utils/chars'\nimport {\n buildYAxisScale,\n formatYAxisTick,\n measureYAxisGutter,\n} from '../utils/chart-scale'\nimport { buildTimeTicks } from '../utils/chart-time-ticks'\nimport type { TimeDomain } from '../utils/chart-zoom'\nimport { cn } from '../utils/cn'\nimport { twoDecimalFormat as valueFormat } from '../utils/format'\nimport {\n ChartTooltipCard,\n type ChartTooltipRow,\n ChartZoomResetButton,\n clampTooltipPosition,\n useMeasuredSize,\n} from './chart'\nimport { SeriesLegend, type SeriesLegendItem } from './series-legend'\nimport { SectionTitle } from './typography'\n\nexport interface PowerHistoryPoint {\n /** Epoch milliseconds. */\n t: number\n solar: number\n /** Signed: positive = import, negative = feed-in. */\n grid: number\n /** Signed: positive = discharge, negative = charge. */\n battery: number\n consumption: number\n}\n\nexport interface PowerHistoryChartProps {\n data: PowerHistoryPoint[]\n /** Axis/tooltip unit, e.g. \"kW\". */\n unit: string\n formatX: (t: number) => string\n /** Tooltip time formatter. Defaults to `formatX`; use to keep the tooltip's\n * full clock time when the axis ticks are compact (hour-only). */\n formatTooltipX?: (t: number) => string\n formatValue?: (value: number) => string\n labels: {\n solar: string\n grid: string\n battery: string\n consumption: string\n }\n /** Series to drop entirely (no legend entry, area, or tooltip row). */\n omitSeries?: readonly PowerHistorySeriesKey[]\n /** Epoch ms to mark with the current-time dashed line (omit to hide). */\n highlightAt?: number\n title?: string\n className?: string\n height?: number\n zoomable?: boolean\n zoomResetLabel?: string\n zoomDomain?: TimeDomain | null\n onZoomDomainChange?: (domain: TimeDomain | null) => void\n}\n\nconst AREA_KEYS = ['solar', 'grid', 'battery'] as const\n\n// All toggleable series, in legend order. The areas (solar/grid/battery) plus\n// the consumption overlay line.\nconst SERIES_KEYS = ['solar', 'grid', 'battery', 'consumption'] as const\nexport type PowerHistorySeriesKey = (typeof SERIES_KEYS)[number]\ntype SeriesKey = PowerHistorySeriesKey\n\nconst SERIES_COLORS = {\n solar: 'var(--orange-a9)',\n grid: 'var(--blue-9)',\n battery: 'var(--green-a9)',\n consumption: 'var(--neutral-12)',\n} as const\n\nconst PADDING_TOP = 20\nconst PADDING_BOTTOM = 28\nconst PADDING_RIGHT = 8\nconst X_TICK_COUNT = 6\nconst DEFAULT_HEIGHT = 260\nconst DETAILED_X_AXIS_THRESHOLD = 6 * 60 * 60 * 1000\n\nconst tBisector = bisector<PowerHistoryPoint, number>((d) => d.t)\n\nfunction touchDistance(a: React.Touch, b: React.Touch): number {\n return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY)\n}\n\nfunction touchMidpoint(\n a: React.Touch,\n b: React.Touch,\n): {\n clientX: number\n clientY: number\n} {\n return {\n clientX: (a.clientX + b.clientX) / 2,\n clientY: (a.clientY + b.clientY) / 2,\n }\n}\n\n/**\n * Diverging \"power sources\" area chart over a continuous time axis: solar\n * (orange), grid (blue) and battery (green) areas around a zero baseline, with\n * consumption overlaid as a line. Negative values (grid feed-in, battery\n * charging) dip below zero. For instantaneous power (day view). A legend at the\n * top toggles each series on/off; the y-axis rescales to the visible ones. visx.\n */\nexport function PowerHistoryChart({\n data,\n unit,\n formatX,\n formatTooltipX,\n formatValue,\n labels,\n omitSeries,\n highlightAt,\n title,\n className,\n height = DEFAULT_HEIGHT,\n zoomable = false,\n zoomResetLabel,\n zoomDomain,\n onZoomDomainChange,\n}: PowerHistoryChartProps) {\n const plotClipId = React.useId()\n const fmtTooltipX = formatTooltipX ?? formatX\n const containerRef = useRef<HTMLDivElement>(null)\n const reducedMotion = useReducedMotion()\n const [width, setWidth] = useState(0)\n const [hiddenSeries, setHiddenSeries] = useState<ReadonlySet<string>>(\n () => new Set(),\n )\n const omitted = useMemo(() => new Set<SeriesKey>(omitSeries), [omitSeries])\n\n const toggleSeries = useCallback((id: string) => {\n setHiddenSeries((prev) => {\n const next = new Set(prev)\n if (next.has(id)) {\n next.delete(id)\n } else {\n next.add(id)\n }\n return next\n })\n }, [])\n\n const isHidden = useCallback(\n (key: SeriesKey) => omitted.has(key) || hiddenSeries.has(key),\n [hiddenSeries, omitted],\n )\n\n const {\n showTooltip,\n hideTooltip,\n tooltipData,\n tooltipLeft,\n tooltipTop,\n tooltipOpen,\n } = useTooltip<PowerHistoryPoint>()\n\n useEffect(() => {\n const element = containerRef.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n setWidth(entry.contentRect.width)\n })\n\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [])\n\n const fmtValue = (value: number) =>\n formatValue\n ? formatValue(value)\n : `${valueFormat.format(value)}${NARROW_NBSP}${unit}`\n\n const tMin = data.length ? (data[0]?.t ?? 0) : 0\n const tMax = data.length ? (data[data.length - 1]?.t ?? 0) : 0\n const fullDomain = useMemo((): TimeDomain | null => {\n if (data.length === 0) return null\n return [tMin, tMax]\n }, [data.length, tMin, tMax])\n\n const zoom = useChartZoom({\n enabled: zoomable,\n fullDomain,\n zoomDomain,\n onZoomDomainChange,\n })\n\n const viewMin = zoom.zoomWindow?.[0] ?? tMin\n const viewMax = zoom.zoomWindow?.[1] ?? tMax\n\n const visibleData = useMemo(\n () => data.filter((d) => d.t >= viewMin && d.t <= viewMax),\n [data, viewMin, viewMax],\n )\n\n const { yMin, yMax } = useMemo(() => {\n let min = 0\n let max = 0\n for (const d of visibleData) {\n if (!isHidden('solar')) max = Math.max(max, d.solar)\n if (!isHidden('consumption')) max = Math.max(max, d.consumption)\n if (!isHidden('grid')) {\n max = Math.max(max, d.grid)\n min = Math.min(min, d.grid)\n }\n if (!isHidden('battery')) {\n max = Math.max(max, d.battery)\n min = Math.min(min, d.battery)\n }\n }\n return { yMin: min, yMax: max }\n }, [visibleData, isHidden])\n\n const {\n ticks: yTicks,\n min: lo,\n max: hi,\n } = useMemo(() => buildYAxisScale(yMin, yMax), [yMin, yMax])\n const displayedYDomain = useAnimatedChartDomain([lo, hi], {\n animateChanges: zoom.isPinching,\n reducedMotion,\n })\n\n const gutter = useMemo(\n () => measureYAxisGutter(yTicks, formatYAxisTick),\n [yTicks],\n )\n\n const plotRight = Math.max(gutter, width - PADDING_RIGHT)\n\n const xScale = useMemo(\n () =>\n scaleLinear<number>({\n domain: [viewMin, viewMax === viewMin ? viewMin + 1 : viewMax],\n range: [gutter, plotRight],\n }),\n [viewMin, viewMax, plotRight, gutter],\n )\n\n const plotHeight = Math.max(0, height - PADDING_BOTTOM - PADDING_TOP)\n const plotWidth = Math.max(0, plotRight - gutter)\n\n const yScale = useMemo(\n () =>\n scaleLinear<number>({\n domain: [\n displayedYDomain[0],\n displayedYDomain[1] === displayedYDomain[0]\n ? displayedYDomain[0] + 1\n : displayedYDomain[1],\n ],\n range: [height - PADDING_BOTTOM, PADDING_TOP],\n }),\n [displayedYDomain, height],\n )\n\n const xTicks = useMemo(\n () => buildTimeTicks(viewMin, viewMax, X_TICK_COUNT),\n [viewMin, viewMax],\n )\n const formatAxisX =\n zoom.isZoomed && viewMax - viewMin <= DETAILED_X_AXIS_THRESHOLD\n ? fmtTooltipX\n : formatX\n\n const domainXFromEvent = useCallback(\n (\n event:\n | React.MouseEvent<SVGRectElement>\n | React.PointerEvent<SVGRectElement>\n | React.TouchEvent<SVGRectElement>,\n ) => {\n const point = localPoint(event)\n if (!point) return null\n return xScale.invert(point.x)\n },\n [xScale],\n )\n\n const handleTooltip = useCallback(\n (\n event:\n | React.TouchEvent<SVGRectElement>\n | React.MouseEvent<SVGRectElement>,\n ) => {\n if (zoom.isBrushing || zoom.isPanning) return\n const point = localPoint(event)\n if (!point || data.length === 0) return\n\n const xMs = xScale.invert(point.x)\n const index = tBisector.left(data, xMs, 1)\n const d0 = data[index - 1]\n const d1 = data[index]\n if (!d0) return\n\n const d = d1 && xMs - d0.t > d1.t - xMs ? d1 : d0\n showTooltip({\n tooltipData: d,\n tooltipLeft: xScale(d.t),\n tooltipTop: PADDING_TOP,\n })\n },\n [data, showTooltip, xScale, zoom.isBrushing, zoom.isPanning],\n )\n\n const legendItems: SeriesLegendItem[] = SERIES_KEYS.filter(\n (key) => !omitted.has(key),\n ).map((key) => ({\n id: key,\n label: labels[key],\n color: SERIES_COLORS[key],\n }))\n\n const tooltipRows: ChartTooltipRow[] = tooltipData\n ? SERIES_KEYS.filter((key) => !isHidden(key)).map((key) => ({\n key,\n label: labels[key],\n color: SERIES_COLORS[key],\n value: fmtValue(tooltipData[key]),\n }))\n : []\n\n const showHighlight =\n highlightAt !== undefined &&\n highlightAt >= viewMin &&\n highlightAt <= viewMax\n\n const [tooltipRef, tooltipSize] = useMeasuredSize<HTMLDivElement>()\n const tooltipPos = clampTooltipPosition({\n anchorX: tooltipLeft ?? 0,\n anchorY: tooltipTop ?? PADDING_TOP,\n width,\n height,\n tipWidth: tooltipSize.width,\n tipHeight: tooltipSize.height,\n placement: 'beside',\n })\n\n return (\n <div className={cn('flex w-full flex-col gap-2', className)}>\n {title && <SectionTitle>{title}</SectionTitle>}\n <SeriesLegend\n items={legendItems}\n hiddenIds={hiddenSeries}\n onToggle={toggleSeries}\n />\n <span className=\"px-1 text-xs text-gray-light\">{unit}</span>\n <div ref={containerRef} className=\"relative w-full\" style={{ height }}>\n {width > 0 && (\n <svg width={width} height={height}>\n <defs>\n {AREA_KEYS.map((key) => (\n <linearGradient\n key={key}\n id={`power-history-fill-${key}`}\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"1\"\n >\n <stop\n offset=\"0%\"\n stopColor={SERIES_COLORS[key]}\n stopOpacity={0.35}\n />\n <stop\n offset=\"100%\"\n stopColor={SERIES_COLORS[key]}\n stopOpacity={0.04}\n />\n </linearGradient>\n ))}\n <clipPath id={plotClipId}>\n <rect\n height={plotHeight}\n width={plotWidth}\n x={gutter}\n y={PADDING_TOP}\n />\n </clipPath>\n </defs>\n\n {yTicks.map((tick) => {\n const y = yScale(tick)\n return (\n <g key={`y-tick-${tick}`}>\n <line\n x1={gutter}\n x2={plotRight}\n y1={y}\n y2={y}\n stroke=\"var(--neutral-a4)\"\n strokeWidth={1}\n />\n <text\n x={gutter - 7}\n y={y + 3}\n textAnchor=\"end\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n >\n {formatYAxisTick(tick)}\n </text>\n </g>\n )\n })}\n\n <g clipPath={`url(#${plotClipId})`} data-testid=\"chart-plot-clip\">\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <Area<PowerHistoryPoint>\n key={`area-${key}`}\n data={data}\n x={(d) => xScale(d.t)}\n y0={yScale(0)}\n y1={(d) => yScale(d[key])}\n curve={curveMonotoneX}\n fill={`url(#power-history-fill-${key})`}\n stroke=\"transparent\"\n />\n ))}\n\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <LinePath<PowerHistoryPoint>\n key={`line-${key}`}\n data={data}\n x={(d) => xScale(d.t)}\n y={(d) => yScale(d[key])}\n curve={curveMonotoneX}\n stroke={SERIES_COLORS[key]}\n strokeWidth={1.5}\n fill=\"none\"\n />\n ))}\n\n {!isHidden('consumption') && (\n <LinePath<PowerHistoryPoint>\n data={data}\n x={(d) => xScale(d.t)}\n y={(d) => yScale(d.consumption)}\n curve={curveMonotoneX}\n stroke={SERIES_COLORS.consumption}\n strokeWidth={1.5}\n fill=\"none\"\n />\n )}\n </g>\n\n <line\n x1={gutter}\n x2={plotRight}\n y1={yScale(0)}\n y2={yScale(0)}\n stroke=\"var(--neutral-a6)\"\n strokeWidth={1}\n />\n\n {showHighlight && (\n <line\n x1={xScale(highlightAt)}\n x2={xScale(highlightAt)}\n y1={PADDING_TOP}\n y2={height - PADDING_BOTTOM}\n stroke=\"var(--spark-a9)\"\n strokeWidth={1.5}\n strokeDasharray=\"5 4\"\n pointerEvents=\"none\"\n />\n )}\n\n {xTicks.map((tick, index) => (\n <text\n key={`x-tick-${tick}`}\n x={xScale(tick)}\n y={height - PADDING_BOTTOM + 16}\n textAnchor={\n index === 0\n ? 'start'\n : index === xTicks.length - 1\n ? 'end'\n : 'middle'\n }\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n >\n {formatAxisX(tick)}\n </text>\n ))}\n\n {tooltipOpen &&\n tooltipData &&\n !zoom.isBrushing &&\n !zoom.isPanning && (\n <>\n <line\n x1={xScale(tooltipData.t)}\n x2={xScale(tooltipData.t)}\n y1={PADDING_TOP}\n y2={height - PADDING_BOTTOM}\n stroke=\"var(--neutral-a6)\"\n strokeDasharray=\"4 4\"\n pointerEvents=\"none\"\n />\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <circle\n key={`dot-${key}`}\n cx={xScale(tooltipData.t)}\n cy={yScale(tooltipData[key])}\n r={3.5}\n fill={SERIES_COLORS[key]}\n stroke=\"white\"\n strokeWidth={1.5}\n pointerEvents=\"none\"\n />\n ))}\n {!isHidden('consumption') && (\n <circle\n cx={xScale(tooltipData.t)}\n cy={yScale(tooltipData.consumption)}\n r={3.5}\n fill={SERIES_COLORS.consumption}\n stroke=\"white\"\n strokeWidth={1.5}\n pointerEvents=\"none\"\n />\n )}\n </>\n )}\n\n {zoom.brushBand && (\n <rect\n data-testid=\"chart-brush-band\"\n x={Math.min(\n xScale(zoom.brushBand[0]),\n xScale(zoom.brushBand[1]),\n )}\n y={PADDING_TOP}\n width={Math.abs(\n xScale(zoom.brushBand[1]) - xScale(zoom.brushBand[0]),\n )}\n height={plotHeight}\n fill=\"var(--spark-a4)\"\n pointerEvents=\"none\"\n />\n )}\n\n <rect\n x={gutter}\n y={PADDING_TOP}\n width={plotWidth}\n height={plotHeight}\n fill=\"transparent\"\n style={zoomable ? { touchAction: 'pan-y' } : undefined}\n onMouseLeave={() => {\n if (!zoom.isBrushing) hideTooltip()\n }}\n onMouseMove={(event) => {\n if (zoom.isBrushing) {\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n return\n }\n handleTooltip(event)\n }}\n onPointerDown={(event) => {\n if (!zoomable || event.pointerType === 'touch') return\n if (event.button !== 0) return\n const domainX = domainXFromEvent(event)\n if (domainX == null) return\n event.currentTarget.setPointerCapture?.(event.pointerId)\n hideTooltip()\n zoom.onPointerDown(domainX)\n }}\n onPointerMove={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n }}\n onPointerUp={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerUp(domainX)\n else zoom.onPointerCancel()\n }}\n onPointerCancel={() => {\n zoom.onPointerCancel()\n }}\n onDoubleClick={() => {\n if (zoomable) zoom.onDoubleClick()\n }}\n onTouchStart={(event) => {\n if (!zoomable) return\n if (event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - gutter) / plotWidth),\n )\n zoom.onPinchStart(touchDistance(a, b), midpoint)\n hideTooltip()\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanStart({\n x: (point.x - gutter) / plotWidth,\n y: (point.y - PADDING_TOP) / plotHeight,\n })\n hideTooltip()\n }\n }}\n onTouchMove={(event) => {\n if (zoomable && event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - gutter) / plotWidth),\n )\n zoom.onPinchMove(touchDistance(a, b), midpoint)\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanMove({\n x: (point.x - gutter) / plotWidth,\n y: (point.y - PADDING_TOP) / plotHeight,\n })\n return\n }\n if (event.touches.length === 1) handleTooltip(event)\n }}\n onTouchEnd={(event) => {\n if (zoomable && event.touches.length < 2) zoom.onPinchEnd()\n if (event.touches.length === 0) {\n if (zoomable) zoom.onPanEnd()\n if (zoomable) zoom.onTap()\n hideTooltip()\n }\n }}\n onTouchCancel={() => {\n zoom.onPinchEnd()\n zoom.onPanEnd()\n }}\n />\n </svg>\n )}\n\n {zoom.isZoomed && zoomResetLabel && (\n <ChartZoomResetButton\n label={zoomResetLabel}\n onReset={zoom.resetZoom}\n />\n )}\n\n <div\n ref={tooltipRef}\n className=\"pointer-events-none absolute z-10\"\n style={{\n left: tooltipPos.left,\n top: tooltipPos.top,\n opacity:\n tooltipOpen && tooltipData && !zoom.isPanning && !zoom.isBrushing\n ? 1\n : 0,\n transition: reducedMotion\n ? 'opacity 140ms ease-out'\n : 'left 140ms ease-out, top 140ms ease-out, opacity 140ms ease-out',\n }}\n >\n {tooltipData && (\n <ChartTooltipCard\n label={fmtTooltipX(tooltipData.t)}\n rows={tooltipRows}\n />\n )}\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"power-history-chart.js","names":["React","valueFormat"],"sources":["../../src/components/power-history-chart.tsx"],"sourcesContent":["import { curveMonotoneX } from '@visx/curve'\nimport { localPoint } from '@visx/event'\nimport { scaleLinear } from '@visx/scale'\nimport { Area, LinePath } from '@visx/shape'\nimport { useTooltip } from '@visx/tooltip'\nimport { bisector } from '@visx/vendor/d3-array'\nimport * as React from 'react'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { useAnimatedChartDomain, useChartZoom } from '../hooks/use-chart-zoom'\nimport { useReducedMotion } from '../hooks/use-reduced-motion'\nimport { NARROW_NBSP } from '../utils/chars'\nimport {\n buildYAxisScale,\n formatYAxisTick,\n measureYAxisGutter,\n} from '../utils/chart-scale'\nimport { buildTimeTicks } from '../utils/chart-time-ticks'\nimport type { TimeDomain } from '../utils/chart-zoom'\nimport { cn } from '../utils/cn'\nimport { twoDecimalFormat as valueFormat } from '../utils/format'\nimport {\n ChartTooltipCard,\n type ChartTooltipRow,\n ChartZoomResetButton,\n clampTooltipPosition,\n useMeasuredSize,\n} from './chart'\nimport { SeriesLegend, type SeriesLegendItem } from './series-legend'\nimport { SectionTitle } from './typography'\n\nexport interface PowerHistoryPoint {\n /** Epoch milliseconds. */\n t: number\n solar: number\n /** Signed: positive = import, negative = feed-in. */\n grid: number\n /** Signed: positive = discharge, negative = charge. */\n battery: number\n consumption: number\n}\n\nexport interface PowerHistoryChartProps {\n data: PowerHistoryPoint[]\n /** Axis/tooltip unit, e.g. \"kW\". */\n unit: string\n formatX: (t: number) => string\n /** Tooltip time formatter. Defaults to `formatX`; use to keep the tooltip's\n * full clock time when the axis ticks are compact (hour-only). */\n formatTooltipX?: (t: number) => string\n formatValue?: (value: number) => string\n labels: {\n solar: string\n grid: string\n battery: string\n consumption: string\n }\n /** Series to drop entirely (no legend entry, area, or tooltip row). */\n omitSeries?: readonly PowerHistorySeriesKey[]\n /** Epoch ms to mark with the current-time dashed line (omit to hide). */\n highlightAt?: number\n title?: string\n className?: string\n height?: number\n zoomable?: boolean\n zoomResetLabel?: string\n zoomDomain?: TimeDomain | null\n onZoomDomainChange?: (domain: TimeDomain | null) => void\n}\n\nconst AREA_KEYS = ['solar', 'grid', 'battery'] as const\n\n// All toggleable series, in legend order. The areas (solar/grid/battery) plus\n// the consumption overlay line.\nconst SERIES_KEYS = ['solar', 'grid', 'battery', 'consumption'] as const\nexport type PowerHistorySeriesKey = (typeof SERIES_KEYS)[number]\ntype SeriesKey = PowerHistorySeriesKey\n\nconst SERIES_COLORS = {\n solar: 'var(--orange-a9)',\n grid: 'var(--blue-9)',\n battery: 'var(--green-a9)',\n consumption: 'var(--neutral-12)',\n} as const\n\nconst PADDING_TOP = 20\nconst PADDING_BOTTOM = 28\nconst PADDING_RIGHT = 8\nconst X_TICK_COUNT = 6\nconst DEFAULT_HEIGHT = 260\nconst DETAILED_X_AXIS_THRESHOLD = 6 * 60 * 60 * 1000\n\nconst tBisector = bisector<PowerHistoryPoint, number>((d) => d.t)\n\nfunction touchDistance(a: React.Touch, b: React.Touch): number {\n return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY)\n}\n\nfunction touchMidpoint(\n a: React.Touch,\n b: React.Touch,\n): {\n clientX: number\n clientY: number\n} {\n return {\n clientX: (a.clientX + b.clientX) / 2,\n clientY: (a.clientY + b.clientY) / 2,\n }\n}\n\n/**\n * Diverging \"power sources\" area chart over a continuous time axis: solar\n * (orange), grid (blue) and battery (green) areas around a zero baseline, with\n * consumption overlaid as a line. Negative values (grid feed-in, battery\n * charging) dip below zero. For instantaneous power (day view). A legend at the\n * top toggles each series on/off; the y-axis rescales to the visible ones. visx.\n */\n// oxlint-disable-next-line eslint/complexity -- pre-existing; split it before adding branches\nexport function PowerHistoryChart({\n data,\n unit,\n formatX,\n formatTooltipX,\n formatValue,\n labels,\n omitSeries,\n highlightAt,\n title,\n className,\n height = DEFAULT_HEIGHT,\n zoomable = false,\n zoomResetLabel,\n zoomDomain,\n onZoomDomainChange,\n}: PowerHistoryChartProps) {\n const plotClipId = React.useId()\n const fmtTooltipX = formatTooltipX ?? formatX\n const containerRef = useRef<HTMLDivElement>(null)\n const reducedMotion = useReducedMotion()\n const [width, setWidth] = useState(0)\n const [hiddenSeries, setHiddenSeries] = useState<ReadonlySet<string>>(\n () => new Set(),\n )\n const omitted = useMemo(() => new Set<SeriesKey>(omitSeries), [omitSeries])\n\n const toggleSeries = useCallback((id: string) => {\n setHiddenSeries((prev) => {\n const next = new Set(prev)\n if (next.has(id)) {\n next.delete(id)\n } else {\n next.add(id)\n }\n return next\n })\n }, [])\n\n const isHidden = useCallback(\n (key: SeriesKey) => omitted.has(key) || hiddenSeries.has(key),\n [hiddenSeries, omitted],\n )\n\n const {\n showTooltip,\n hideTooltip,\n tooltipData,\n tooltipLeft,\n tooltipTop,\n tooltipOpen,\n } = useTooltip<PowerHistoryPoint>()\n\n useEffect(() => {\n const element = containerRef.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n setWidth(entry.contentRect.width)\n })\n\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [])\n\n const fmtValue = (value: number) =>\n formatValue\n ? formatValue(value)\n : `${valueFormat.format(value)}${NARROW_NBSP}${unit}`\n\n const tMin = data.length ? (data[0]?.t ?? 0) : 0\n const tMax = data.length ? (data[data.length - 1]?.t ?? 0) : 0\n const fullDomain = useMemo((): TimeDomain | null => {\n if (data.length === 0) return null\n return [tMin, tMax]\n }, [data.length, tMin, tMax])\n\n const zoom = useChartZoom({\n enabled: zoomable,\n fullDomain,\n zoomDomain,\n onZoomDomainChange,\n })\n\n const viewMin = zoom.zoomWindow?.[0] ?? tMin\n const viewMax = zoom.zoomWindow?.[1] ?? tMax\n\n const visibleData = useMemo(\n () => data.filter((d) => d.t >= viewMin && d.t <= viewMax),\n [data, viewMin, viewMax],\n )\n\n const { yMin, yMax } = useMemo(() => {\n let min = 0\n let max = 0\n for (const d of visibleData) {\n if (!isHidden('solar')) max = Math.max(max, d.solar)\n if (!isHidden('consumption')) max = Math.max(max, d.consumption)\n if (!isHidden('grid')) {\n max = Math.max(max, d.grid)\n min = Math.min(min, d.grid)\n }\n if (!isHidden('battery')) {\n max = Math.max(max, d.battery)\n min = Math.min(min, d.battery)\n }\n }\n return { yMin: min, yMax: max }\n }, [visibleData, isHidden])\n\n const {\n ticks: yTicks,\n min: lo,\n max: hi,\n } = useMemo(() => buildYAxisScale(yMin, yMax), [yMin, yMax])\n const displayedYDomain = useAnimatedChartDomain([lo, hi], {\n animateChanges: zoom.isPinching,\n reducedMotion,\n })\n\n const gutter = useMemo(\n () => measureYAxisGutter(yTicks, formatYAxisTick),\n [yTicks],\n )\n\n const plotRight = Math.max(gutter, width - PADDING_RIGHT)\n\n const xScale = useMemo(\n () =>\n scaleLinear<number>({\n domain: [viewMin, viewMax === viewMin ? viewMin + 1 : viewMax],\n range: [gutter, plotRight],\n }),\n [viewMin, viewMax, plotRight, gutter],\n )\n\n const plotHeight = Math.max(0, height - PADDING_BOTTOM - PADDING_TOP)\n const plotWidth = Math.max(0, plotRight - gutter)\n\n const yScale = useMemo(\n () =>\n scaleLinear<number>({\n domain: [\n displayedYDomain[0],\n displayedYDomain[1] === displayedYDomain[0]\n ? displayedYDomain[0] + 1\n : displayedYDomain[1],\n ],\n range: [height - PADDING_BOTTOM, PADDING_TOP],\n }),\n [displayedYDomain, height],\n )\n\n const xTicks = useMemo(\n () => buildTimeTicks(viewMin, viewMax, X_TICK_COUNT),\n [viewMin, viewMax],\n )\n const formatAxisX =\n zoom.isZoomed && viewMax - viewMin <= DETAILED_X_AXIS_THRESHOLD\n ? fmtTooltipX\n : formatX\n\n const domainXFromEvent = useCallback(\n (\n event:\n | React.MouseEvent<SVGRectElement>\n | React.PointerEvent<SVGRectElement>\n | React.TouchEvent<SVGRectElement>,\n ) => {\n const point = localPoint(event)\n if (!point) return null\n return xScale.invert(point.x)\n },\n [xScale],\n )\n\n const handleTooltip = useCallback(\n (\n event:\n | React.TouchEvent<SVGRectElement>\n | React.MouseEvent<SVGRectElement>,\n ) => {\n if (zoom.isBrushing || zoom.isPanning) return\n const point = localPoint(event)\n if (!point || data.length === 0) return\n\n const xMs = xScale.invert(point.x)\n const index = tBisector.left(data, xMs, 1)\n const d0 = data[index - 1]\n const d1 = data[index]\n if (!d0) return\n\n const d = d1 && xMs - d0.t > d1.t - xMs ? d1 : d0\n showTooltip({\n tooltipData: d,\n tooltipLeft: xScale(d.t),\n tooltipTop: PADDING_TOP,\n })\n },\n [data, showTooltip, xScale, zoom.isBrushing, zoom.isPanning],\n )\n\n const legendItems: SeriesLegendItem[] = SERIES_KEYS.filter(\n (key) => !omitted.has(key),\n ).map((key) => ({\n id: key,\n label: labels[key],\n color: SERIES_COLORS[key],\n }))\n\n const tooltipRows: ChartTooltipRow[] = tooltipData\n ? SERIES_KEYS.filter((key) => !isHidden(key)).map((key) => ({\n key,\n label: labels[key],\n color: SERIES_COLORS[key],\n value: fmtValue(tooltipData[key]),\n }))\n : []\n\n const showHighlight =\n highlightAt !== undefined &&\n highlightAt >= viewMin &&\n highlightAt <= viewMax\n\n const [tooltipRef, tooltipSize] = useMeasuredSize<HTMLDivElement>()\n const tooltipPos = clampTooltipPosition({\n anchorX: tooltipLeft ?? 0,\n anchorY: tooltipTop ?? PADDING_TOP,\n width,\n height,\n tipWidth: tooltipSize.width,\n tipHeight: tooltipSize.height,\n placement: 'beside',\n })\n\n return (\n <div className={cn('flex w-full flex-col gap-2', className)}>\n {title && <SectionTitle>{title}</SectionTitle>}\n <SeriesLegend\n items={legendItems}\n hiddenIds={hiddenSeries}\n onToggle={toggleSeries}\n />\n <span className=\"px-1 text-xs text-gray-light\">{unit}</span>\n <div ref={containerRef} className=\"relative w-full\" style={{ height }}>\n {width > 0 && (\n <svg width={width} height={height}>\n <defs>\n {AREA_KEYS.map((key) => (\n <linearGradient\n key={key}\n id={`power-history-fill-${key}`}\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"1\"\n >\n <stop\n offset=\"0%\"\n stopColor={SERIES_COLORS[key]}\n stopOpacity={0.35}\n />\n <stop\n offset=\"100%\"\n stopColor={SERIES_COLORS[key]}\n stopOpacity={0.04}\n />\n </linearGradient>\n ))}\n <clipPath id={plotClipId}>\n <rect\n height={plotHeight}\n width={plotWidth}\n x={gutter}\n y={PADDING_TOP}\n />\n </clipPath>\n </defs>\n\n {yTicks.map((tick) => {\n const y = yScale(tick)\n return (\n <g key={`y-tick-${tick}`}>\n <line\n x1={gutter}\n x2={plotRight}\n y1={y}\n y2={y}\n stroke=\"var(--neutral-a4)\"\n strokeWidth={1}\n />\n <text\n x={gutter - 7}\n y={y + 3}\n textAnchor=\"end\"\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n >\n {formatYAxisTick(tick)}\n </text>\n </g>\n )\n })}\n\n <g clipPath={`url(#${plotClipId})`} data-testid=\"chart-plot-clip\">\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <Area<PowerHistoryPoint>\n key={`area-${key}`}\n data={data}\n x={(d) => xScale(d.t)}\n y0={yScale(0)}\n y1={(d) => yScale(d[key])}\n curve={curveMonotoneX}\n fill={`url(#power-history-fill-${key})`}\n stroke=\"transparent\"\n />\n ))}\n\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <LinePath<PowerHistoryPoint>\n key={`line-${key}`}\n data={data}\n x={(d) => xScale(d.t)}\n y={(d) => yScale(d[key])}\n curve={curveMonotoneX}\n stroke={SERIES_COLORS[key]}\n strokeWidth={1.5}\n fill=\"none\"\n />\n ))}\n\n {!isHidden('consumption') && (\n <LinePath<PowerHistoryPoint>\n data={data}\n x={(d) => xScale(d.t)}\n y={(d) => yScale(d.consumption)}\n curve={curveMonotoneX}\n stroke={SERIES_COLORS.consumption}\n strokeWidth={1.5}\n fill=\"none\"\n />\n )}\n </g>\n\n <line\n x1={gutter}\n x2={plotRight}\n y1={yScale(0)}\n y2={yScale(0)}\n stroke=\"var(--neutral-a6)\"\n strokeWidth={1}\n />\n\n {showHighlight && (\n <line\n x1={xScale(highlightAt)}\n x2={xScale(highlightAt)}\n y1={PADDING_TOP}\n y2={height - PADDING_BOTTOM}\n stroke=\"var(--spark-a9)\"\n strokeWidth={1.5}\n strokeDasharray=\"5 4\"\n pointerEvents=\"none\"\n />\n )}\n\n {xTicks.map((tick, index) => (\n <text\n key={`x-tick-${tick}`}\n x={xScale(tick)}\n y={height - PADDING_BOTTOM + 16}\n textAnchor={\n index === 0\n ? 'start'\n : index === xTicks.length - 1\n ? 'end'\n : 'middle'\n }\n fill=\"var(--neutral-a11)\"\n fontSize={10}\n >\n {formatAxisX(tick)}\n </text>\n ))}\n\n {tooltipOpen &&\n tooltipData &&\n !zoom.isBrushing &&\n !zoom.isPanning && (\n <>\n <line\n x1={xScale(tooltipData.t)}\n x2={xScale(tooltipData.t)}\n y1={PADDING_TOP}\n y2={height - PADDING_BOTTOM}\n stroke=\"var(--neutral-a6)\"\n strokeDasharray=\"4 4\"\n pointerEvents=\"none\"\n />\n {AREA_KEYS.filter((key) => !isHidden(key)).map((key) => (\n <circle\n key={`dot-${key}`}\n cx={xScale(tooltipData.t)}\n cy={yScale(tooltipData[key])}\n r={3.5}\n fill={SERIES_COLORS[key]}\n stroke=\"white\"\n strokeWidth={1.5}\n pointerEvents=\"none\"\n />\n ))}\n {!isHidden('consumption') && (\n <circle\n cx={xScale(tooltipData.t)}\n cy={yScale(tooltipData.consumption)}\n r={3.5}\n fill={SERIES_COLORS.consumption}\n stroke=\"white\"\n strokeWidth={1.5}\n pointerEvents=\"none\"\n />\n )}\n </>\n )}\n\n {zoom.brushBand && (\n <rect\n data-testid=\"chart-brush-band\"\n x={Math.min(\n xScale(zoom.brushBand[0]),\n xScale(zoom.brushBand[1]),\n )}\n y={PADDING_TOP}\n width={Math.abs(\n xScale(zoom.brushBand[1]) - xScale(zoom.brushBand[0]),\n )}\n height={plotHeight}\n fill=\"var(--spark-a4)\"\n pointerEvents=\"none\"\n />\n )}\n\n <rect\n x={gutter}\n y={PADDING_TOP}\n width={plotWidth}\n height={plotHeight}\n fill=\"transparent\"\n style={zoomable ? { touchAction: 'pan-y' } : undefined}\n onMouseLeave={() => {\n if (!zoom.isBrushing) hideTooltip()\n }}\n onMouseMove={(event) => {\n if (zoom.isBrushing) {\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n return\n }\n handleTooltip(event)\n }}\n onPointerDown={(event) => {\n if (!zoomable || event.pointerType === 'touch') return\n if (event.button !== 0) return\n const domainX = domainXFromEvent(event)\n if (domainX == null) return\n event.currentTarget.setPointerCapture?.(event.pointerId)\n hideTooltip()\n zoom.onPointerDown(domainX)\n }}\n onPointerMove={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerMove(domainX)\n }}\n onPointerUp={(event) => {\n if (!zoomable) return\n const domainX = domainXFromEvent(event)\n if (domainX != null) zoom.onPointerUp(domainX)\n else zoom.onPointerCancel()\n }}\n onPointerCancel={() => {\n zoom.onPointerCancel()\n }}\n onDoubleClick={() => {\n if (zoomable) zoom.onDoubleClick()\n }}\n onTouchStart={(event) => {\n if (!zoomable) return\n if (event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - gutter) / plotWidth),\n )\n zoom.onPinchStart(touchDistance(a, b), midpoint)\n hideTooltip()\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanStart({\n x: (point.x - gutter) / plotWidth,\n y: (point.y - PADDING_TOP) / plotHeight,\n })\n hideTooltip()\n }\n }}\n onTouchMove={(event) => {\n if (zoomable && event.touches.length === 2) {\n const [a, b] = [event.touches[0], event.touches[1]]\n if (!a || !b) return\n const point = localPoint(\n event.currentTarget,\n touchMidpoint(a, b) as MouseEvent,\n )\n if (!point) return\n const midpoint = Math.min(\n 1,\n Math.max(0, (point.x - gutter) / plotWidth),\n )\n zoom.onPinchMove(touchDistance(a, b), midpoint)\n return\n }\n if (event.touches.length === 1 && zoom.isZoomed) {\n const point = localPoint(event)\n if (!point) return\n zoom.onPanMove({\n x: (point.x - gutter) / plotWidth,\n y: (point.y - PADDING_TOP) / plotHeight,\n })\n return\n }\n if (event.touches.length === 1) handleTooltip(event)\n }}\n onTouchEnd={(event) => {\n if (zoomable && event.touches.length < 2) zoom.onPinchEnd()\n if (event.touches.length === 0) {\n if (zoomable) zoom.onPanEnd()\n if (zoomable) zoom.onTap()\n hideTooltip()\n }\n }}\n onTouchCancel={() => {\n zoom.onPinchEnd()\n zoom.onPanEnd()\n }}\n />\n </svg>\n )}\n\n {zoom.isZoomed && zoomResetLabel && (\n <ChartZoomResetButton\n label={zoomResetLabel}\n onReset={zoom.resetZoom}\n />\n )}\n\n <div\n ref={tooltipRef}\n className=\"pointer-events-none absolute z-10\"\n style={{\n left: tooltipPos.left,\n top: tooltipPos.top,\n opacity:\n tooltipOpen && tooltipData && !zoom.isPanning && !zoom.isBrushing\n ? 1\n : 0,\n transition: reducedMotion\n ? 'opacity 140ms ease-out'\n : 'left 140ms ease-out, top 140ms ease-out, opacity 140ms ease-out',\n }}\n >\n {tooltipData && (\n <ChartTooltipCard\n label={fmtTooltipX(tooltipData.t)}\n rows={tooltipRows}\n />\n )}\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
@@ -22,7 +22,7 @@ const Progress = react.forwardRef(({ className, value, max = 100, size = "defaul
22
22
  role: "progressbar",
23
23
  ...props,
24
24
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
25
- className: "h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300",
25
+ className: "h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300 motion-reduce:transition-none",
26
26
  style: { width: `${percentage}%` }
27
27
  })
28
28
  });
@@ -1 +1 @@
1
- {"version":3,"file":"progress.cjs","names":["React"],"sources":["../../src/components/progress.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\nexport interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Current value */\n value: number\n /** Maximum value. Default: 100 */\n max?: number\n /** Height variant */\n size?: 'sm' | 'default' | 'lg'\n}\n\nconst sizeClasses = {\n sm: 'h-1',\n default: 'h-2',\n lg: 'h-3',\n} as const\n\nconst Progress = React.forwardRef<HTMLDivElement, ProgressProps>(\n ({ className, value, max = 100, size = 'default', ...props }, ref) => {\n const percentage = Math.min(100, Math.max(0, (value / max) * 100))\n\n return (\n <div\n aria-valuemax={max}\n aria-valuemin={0}\n aria-valuenow={value}\n className={cn(\n 'w-full overflow-hidden rounded-full bg-gray-a4',\n sizeClasses[size],\n className,\n )}\n ref={ref}\n role=\"progressbar\"\n {...props}\n >\n <div\n className=\"h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300\"\n style={{ width: `${percentage}%` }}\n />\n </div>\n )\n },\n)\n\nProgress.displayName = 'Progress'\n\nexport { Progress }\n"],"mappings":";;;;;;;;AAYA,MAAM,cAAc;CAClB,IAAI;CACJ,SAAS;CACT,IAAI;AACN;AAEA,MAAM,WAAWA,MAAM,YACpB,EAAE,WAAW,OAAO,MAAM,KAAK,OAAO,WAAW,GAAG,SAAS,QAAQ;CACpE,MAAM,aAAa,KAAK,IAAI,KAAK,KAAK,IAAI,GAAI,QAAQ,MAAO,GAAG,CAAC;CAEjE,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,YAAA,GAAA,GAAA,IACE,kDACA,YAAY,OACZ,SACF;EACK;EACL,MAAK;EACL,GAAI;YAEJ,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,OAAO,GAAG,WAAW,GAAG;EAClC,CAAA;CACE,CAAA;AAET,CACF;AAEA,SAAS,cAAc"}
1
+ {"version":3,"file":"progress.cjs","names":["React"],"sources":["../../src/components/progress.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\nexport interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Current value */\n value: number\n /** Maximum value. Default: 100 */\n max?: number\n /** Height variant */\n size?: 'sm' | 'default' | 'lg'\n}\n\nconst sizeClasses = {\n sm: 'h-1',\n default: 'h-2',\n lg: 'h-3',\n} as const\n\nconst Progress = React.forwardRef<HTMLDivElement, ProgressProps>(\n ({ className, value, max = 100, size = 'default', ...props }, ref) => {\n const percentage = Math.min(100, Math.max(0, (value / max) * 100))\n\n return (\n <div\n aria-valuemax={max}\n aria-valuemin={0}\n aria-valuenow={value}\n className={cn(\n 'w-full overflow-hidden rounded-full bg-gray-a4',\n sizeClasses[size],\n className,\n )}\n ref={ref}\n role=\"progressbar\"\n {...props}\n >\n <div\n className=\"h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300 motion-reduce:transition-none\"\n style={{ width: `${percentage}%` }}\n />\n </div>\n )\n },\n)\n\nProgress.displayName = 'Progress'\n\nexport { Progress }\n"],"mappings":";;;;;;;;AAYA,MAAM,cAAc;CAClB,IAAI;CACJ,SAAS;CACT,IAAI;AACN;AAEA,MAAM,WAAWA,MAAM,YACpB,EAAE,WAAW,OAAO,MAAM,KAAK,OAAO,WAAW,GAAG,SAAS,QAAQ;CACpE,MAAM,aAAa,KAAK,IAAI,KAAK,KAAK,IAAI,GAAI,QAAQ,MAAO,GAAG,CAAC;CAEjE,OACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EACE,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,YAAA,GAAA,GAAA,IACE,kDACA,YAAY,OACZ,SACF;EACK;EACL,MAAK;EACL,GAAI;YAEJ,iBAAA,GAAA,kBAAA,KAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,OAAO,GAAG,WAAW,GAAG;EAClC,CAAA;CACE,CAAA;AAET,CACF;AAEA,SAAS,cAAc"}
@@ -19,7 +19,7 @@ const Progress = React$1.forwardRef(({ className, value, max = 100, size = "defa
19
19
  role: "progressbar",
20
20
  ...props,
21
21
  children: /* @__PURE__ */ jsx("div", {
22
- className: "h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300",
22
+ className: "h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300 motion-reduce:transition-none",
23
23
  style: { width: `${percentage}%` }
24
24
  })
25
25
  });
@@ -1 +1 @@
1
- {"version":3,"file":"progress.js","names":["React"],"sources":["../../src/components/progress.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\nexport interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Current value */\n value: number\n /** Maximum value. Default: 100 */\n max?: number\n /** Height variant */\n size?: 'sm' | 'default' | 'lg'\n}\n\nconst sizeClasses = {\n sm: 'h-1',\n default: 'h-2',\n lg: 'h-3',\n} as const\n\nconst Progress = React.forwardRef<HTMLDivElement, ProgressProps>(\n ({ className, value, max = 100, size = 'default', ...props }, ref) => {\n const percentage = Math.min(100, Math.max(0, (value / max) * 100))\n\n return (\n <div\n aria-valuemax={max}\n aria-valuemin={0}\n aria-valuenow={value}\n className={cn(\n 'w-full overflow-hidden rounded-full bg-gray-a4',\n sizeClasses[size],\n className,\n )}\n ref={ref}\n role=\"progressbar\"\n {...props}\n >\n <div\n className=\"h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300\"\n style={{ width: `${percentage}%` }}\n />\n </div>\n )\n },\n)\n\nProgress.displayName = 'Progress'\n\nexport { Progress }\n"],"mappings":";;;;;AAYA,MAAM,cAAc;CAClB,IAAI;CACJ,SAAS;CACT,IAAI;AACN;AAEA,MAAM,WAAWA,QAAM,YACpB,EAAE,WAAW,OAAO,MAAM,KAAK,OAAO,WAAW,GAAG,SAAS,QAAQ;CACpE,MAAM,aAAa,KAAK,IAAI,KAAK,KAAK,IAAI,GAAI,QAAQ,MAAO,GAAG,CAAC;CAEjE,OACE,oBAAC,OAAD;EACE,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,WAAW,GACT,kDACA,YAAY,OACZ,SACF;EACK;EACL,MAAK;EACL,GAAI;YAEJ,oBAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,OAAO,GAAG,WAAW,GAAG;EAClC,CAAA;CACE,CAAA;AAET,CACF;AAEA,SAAS,cAAc"}
1
+ {"version":3,"file":"progress.js","names":["React"],"sources":["../../src/components/progress.tsx"],"sourcesContent":["import * as React from 'react'\nimport { cn } from '../utils/cn'\n\nexport interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Current value */\n value: number\n /** Maximum value. Default: 100 */\n max?: number\n /** Height variant */\n size?: 'sm' | 'default' | 'lg'\n}\n\nconst sizeClasses = {\n sm: 'h-1',\n default: 'h-2',\n lg: 'h-3',\n} as const\n\nconst Progress = React.forwardRef<HTMLDivElement, ProgressProps>(\n ({ className, value, max = 100, size = 'default', ...props }, ref) => {\n const percentage = Math.min(100, Math.max(0, (value / max) * 100))\n\n return (\n <div\n aria-valuemax={max}\n aria-valuemin={0}\n aria-valuenow={value}\n className={cn(\n 'w-full overflow-hidden rounded-full bg-gray-a4',\n sizeClasses[size],\n className,\n )}\n ref={ref}\n role=\"progressbar\"\n {...props}\n >\n <div\n className=\"h-full rounded-full bg-(--spark-style-bg,var(--color-primary)) transition-all duration-300 motion-reduce:transition-none\"\n style={{ width: `${percentage}%` }}\n />\n </div>\n )\n },\n)\n\nProgress.displayName = 'Progress'\n\nexport { Progress }\n"],"mappings":";;;;;AAYA,MAAM,cAAc;CAClB,IAAI;CACJ,SAAS;CACT,IAAI;AACN;AAEA,MAAM,WAAWA,QAAM,YACpB,EAAE,WAAW,OAAO,MAAM,KAAK,OAAO,WAAW,GAAG,SAAS,QAAQ;CACpE,MAAM,aAAa,KAAK,IAAI,KAAK,KAAK,IAAI,GAAI,QAAQ,MAAO,GAAG,CAAC;CAEjE,OACE,oBAAC,OAAD;EACE,iBAAe;EACf,iBAAe;EACf,iBAAe;EACf,WAAW,GACT,kDACA,YAAY,OACZ,SACF;EACK;EACL,MAAK;EACL,GAAI;YAEJ,oBAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,OAAO,GAAG,WAAW,GAAG;EAClC,CAAA;CACE,CAAA;AAET,CACF;AAEA,SAAS,cAAc"}
@@ -119,7 +119,7 @@ function SankeyChart({ nodes, links, categoryColors = SANKEY_CATEGORY_COLORS, de
119
119
  })
120
120
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_visx_group.Group, {
121
121
  className: "visx-sankey-nodes",
122
- children: laidOut.nodes.map((node, index) => {
122
+ children: laidOut.nodes.map((node) => {
123
123
  if (node.x0 === void 0 || node.x1 === void 0 || node.y0 === void 0 || node.y1 === void 0) return null;
124
124
  const w = node.x1 - node.x0;
125
125
  const h = node.y1 - node.y0;
@@ -131,7 +131,7 @@ function SankeyChart({ nodes, links, categoryColors = SANKEY_CATEGORY_COLORS, de
131
131
  height: Math.max(1, h),
132
132
  fill,
133
133
  rx: 2
134
- }, `sankey-node-${index}`);
134
+ }, node.id);
135
135
  })
136
136
  })]
137
137
  })
@@ -1 +1 @@
1
- {"version":3,"file":"sankey-chart.cjs","names":["SectionTitle","Sankey","Group","NumberFlow"],"sources":["../../src/components/sankey-chart.tsx"],"sourcesContent":["import { Group } from '@visx/group'\nimport {\n Sankey,\n sankeyJustify,\n type SankeyLink,\n type SankeyNode,\n} from '@visx/sankey'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { cn } from '../utils/cn'\nimport NumberFlow, { type Format } from './number-flow'\nimport { SectionTitle } from './typography'\n\nconst SANKEY_CATEGORY_COLORS: Record<string, string> = {\n solar: 'var(--orange-a9)',\n battery: 'var(--green-a9)',\n grid: 'var(--blue-9)',\n gridFeedIn: 'var(--blue-7)',\n home: 'var(--spark-9)',\n floor: 'var(--neutral-a9)',\n device: 'var(--neutral-a8)',\n}\n\nexport interface SankeyChartNode {\n id: string\n label: string\n /** Optional category — used to colour the node and any outgoing links. */\n category?: string\n /** Override the colour resolved from `category`. */\n color?: string\n}\n\nexport interface SankeyChartLink {\n source: string\n target: string\n value: number\n /** Override the colour of the link. Defaults to the source node colour. */\n color?: string\n}\n\nexport interface SankeyChartProps {\n nodes: SankeyChartNode[]\n links: SankeyChartLink[]\n /** Resolves a category to a colour. */\n categoryColors?: Record<string, string>\n /** Fallback colour for nodes/links without a category. */\n defaultColor?: string\n /** Suffix appended to each animated value label (e.g. \" W\"). */\n valueSuffix?: string\n /** NumberFlow `format` for value labels. */\n valueFormat?: Format\n height?: number\n nodeWidth?: number\n nodePadding?: number\n /** Reserved horizontal space on the right for labels. */\n labelPadding?: number\n /**\n * Node ids kept at their natural (computed) column instead of being pushed to\n * the last column. By default `sankeyJustify` sends every node with no\n * outgoing link to the right edge; use this to hold a terminal node (e.g. grid\n * feed-in) in a middle column alongside its siblings.\n */\n keepAtDepthIds?: readonly string[]\n title?: string\n className?: string\n}\n\ntype SankeyDatumNode = SankeyChartNode & {\n /** Computed by d3-sankey; present after layout. */\n}\n\ntype SankeyDatumLink = SankeyChartLink\n\nconst DEFAULT_HEIGHT = 260\nconst DEFAULT_NODE_WIDTH = 12\nconst DEFAULT_NODE_PADDING = 10\nconst DEFAULT_LABEL_PADDING = 96\nconst LEFT_INSET = 4\nconst TOP_INSET = 8\nconst LABEL_GAP = 8\nconst LABEL_STACK_HEIGHT = 26\nconst DEFAULT_COLOR = 'var(--neutral-a9)'\n\nfunction resolveNodeColor(\n node: SankeyChartNode,\n categoryColors: Record<string, string> | undefined,\n defaultColor: string,\n) {\n if (node.color) return node.color\n if (node.category && categoryColors?.[node.category]) {\n return categoryColors[node.category] as string\n }\n return defaultColor\n}\n\nfunction SankeyChart({\n nodes,\n links,\n categoryColors = SANKEY_CATEGORY_COLORS,\n defaultColor = DEFAULT_COLOR,\n valueSuffix,\n valueFormat,\n height = DEFAULT_HEIGHT,\n nodeWidth = DEFAULT_NODE_WIDTH,\n nodePadding = DEFAULT_NODE_PADDING,\n labelPadding = DEFAULT_LABEL_PADDING,\n keepAtDepthIds,\n title,\n className,\n}: SankeyChartProps): React.ReactElement {\n const containerRef = useRef<HTMLDivElement>(null)\n const [width, setWidth] = useState(0)\n\n useEffect(() => {\n const element = containerRef.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n setWidth(entry.contentRect.width)\n })\n\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [])\n\n const nodeIds = useMemo(() => new Set(nodes.map((node) => node.id)), [nodes])\n\n // Drop zero-value links and any whose endpoints aren't in the current node\n // set (a stale link referencing a removed node makes d3-sankey throw\n // `missing: <id>`).\n const visibleLinks = useMemo(\n () =>\n links.filter(\n (link) =>\n link.value > 0 &&\n nodeIds.has(link.source) &&\n nodeIds.has(link.target),\n ),\n [links, nodeIds],\n )\n\n const visibleNodes = useMemo(() => {\n const referencedIds = new Set<string>()\n for (const link of visibleLinks) {\n referencedIds.add(link.source)\n referencedIds.add(link.target)\n }\n return nodes.filter((node) => referencedIds.has(node.id))\n }, [nodes, visibleLinks])\n\n const colorById = useMemo(() => {\n const map = new Map<string, string>()\n for (const node of nodes) {\n map.set(node.id, resolveNodeColor(node, categoryColors, defaultColor))\n }\n return map\n }, [categoryColors, defaultColor, nodes])\n\n const orderById = useMemo(() => {\n const map = new Map<string, number>()\n visibleNodes.forEach((node, index) => map.set(node.id, index))\n return map\n }, [visibleNodes])\n\n const nodeSort = useMemo(\n () =>\n (\n a: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n b: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n ) =>\n (orderById.get(a.id) ?? 0) - (orderById.get(b.id) ?? 0),\n [orderById],\n )\n\n const graph = useMemo(\n () => ({\n nodes: visibleNodes.map((node) => ({ ...node })),\n links: visibleLinks.map((link) => ({ ...link })),\n }),\n [visibleNodes, visibleLinks],\n )\n\n const nodeAlign = useMemo(() => {\n const keep = new Set(keepAtDepthIds)\n return (node: SankeyNode<SankeyDatumNode, SankeyDatumLink>, n: number) =>\n keep.has(node.id) ? (node.depth ?? 0) : sankeyJustify(node, n)\n }, [keepAtDepthIds])\n\n const innerWidth = Math.max(0, width - labelPadding - LEFT_INSET)\n const innerHeight = Math.max(0, height - TOP_INSET * 2)\n const halfLabelStack = LABEL_STACK_HEIGHT / 2\n const labelWidth = Math.max(40, labelPadding - LABEL_GAP - 4)\n\n return (\n <div className={cn('flex w-full flex-col gap-2 py-2', className)}>\n {title && <SectionTitle>{title}</SectionTitle>}\n\n <div ref={containerRef} className=\"relative w-full\" style={{ height }}>\n {width > 0 && innerWidth > 0 && (\n <Sankey<SankeyDatumNode, SankeyDatumLink>\n root={graph}\n size={[innerWidth, innerHeight]}\n nodeWidth={nodeWidth}\n nodePadding={nodePadding}\n nodeAlign={nodeAlign}\n nodeId={(node) => node.id}\n nodeSort={nodeSort}\n >\n {({ graph: laidOut, createPath }) => (\n <>\n <svg\n aria-hidden=\"true\"\n className=\"block\"\n height={height}\n width={width}\n >\n <Group left={LEFT_INSET} top={TOP_INSET}>\n <Group className=\"visx-sankey-links\">\n {laidOut.links.map((link, index) => {\n const sourceId =\n typeof link.source === 'object'\n ? (\n link.source as SankeyNode<\n SankeyDatumNode,\n SankeyDatumLink\n >\n ).id\n : String(link.source)\n const stroke =\n link.color ?? colorById.get(sourceId) ?? defaultColor\n\n return (\n <path\n key={`sankey-link-${index}`}\n d={createPath(link) ?? ''}\n fill=\"none\"\n stroke={stroke}\n strokeOpacity={0.32}\n strokeWidth={Math.max(1, link.width ?? 0)}\n />\n )\n })}\n </Group>\n\n <Group className=\"visx-sankey-nodes\">\n {laidOut.nodes.map((node, index) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const w = node.x1 - node.x0\n const h = node.y1 - node.y0\n const fill = colorById.get(node.id) ?? defaultColor\n\n return (\n <rect\n key={`sankey-node-${index}`}\n x={node.x0}\n y={node.y0}\n width={w}\n height={Math.max(1, h)}\n fill={fill}\n rx={2}\n />\n )\n })}\n </Group>\n </Group>\n </svg>\n\n <div className=\"pointer-events-none absolute inset-0 [-webkit-text-size-adjust:100%] [text-size-adjust:100%]\">\n {laidOut.nodes.map((node, index) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const h = node.y1 - node.y0\n const nodeCenterY = TOP_INSET + node.y0 + h / 2\n const labelCenterY = Math.min(\n Math.max(nodeCenterY, halfLabelStack),\n height - halfLabelStack,\n )\n\n return (\n <div\n key={`sankey-label-${node.id ?? index}`}\n className=\"absolute flex -translate-y-1/2 flex-col items-start gap-1\"\n style={{\n left: LEFT_INSET + node.x1 + LABEL_GAP,\n top: labelCenterY,\n width: labelWidth,\n }}\n >\n <span className=\"text-[11px] leading-none font-semibold whitespace-nowrap text-gray\">\n {node.label}\n </span>\n <NumberFlow\n value={node.value ?? 0}\n {...(valueSuffix !== undefined && {\n suffix: valueSuffix,\n })}\n {...(valueFormat !== undefined && {\n format: valueFormat,\n })}\n className=\"block min-h-[10px] text-[10px] leading-none text-gray-light tabular-nums\"\n />\n </div>\n )\n })}\n </div>\n </>\n )}\n </Sankey>\n )}\n </div>\n </div>\n )\n}\n\nexport type { SankeyLink, SankeyNode }\nexport { SankeyChart, SANKEY_CATEGORY_COLORS }\n"],"mappings":"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"}
1
+ {"version":3,"file":"sankey-chart.cjs","names":["SectionTitle","Sankey","Group","NumberFlow"],"sources":["../../src/components/sankey-chart.tsx"],"sourcesContent":["import { Group } from '@visx/group'\nimport {\n Sankey,\n sankeyJustify,\n type SankeyLink,\n type SankeyNode,\n} from '@visx/sankey'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { cn } from '../utils/cn'\nimport NumberFlow, { type Format } from './number-flow'\nimport { SectionTitle } from './typography'\n\nconst SANKEY_CATEGORY_COLORS: Record<string, string> = {\n solar: 'var(--orange-a9)',\n battery: 'var(--green-a9)',\n grid: 'var(--blue-9)',\n gridFeedIn: 'var(--blue-7)',\n home: 'var(--spark-9)',\n floor: 'var(--neutral-a9)',\n device: 'var(--neutral-a8)',\n}\n\nexport interface SankeyChartNode {\n id: string\n label: string\n /** Optional category — used to colour the node and any outgoing links. */\n category?: string\n /** Override the colour resolved from `category`. */\n color?: string\n}\n\nexport interface SankeyChartLink {\n source: string\n target: string\n value: number\n /** Override the colour of the link. Defaults to the source node colour. */\n color?: string\n}\n\nexport interface SankeyChartProps {\n nodes: SankeyChartNode[]\n links: SankeyChartLink[]\n /** Resolves a category to a colour. */\n categoryColors?: Record<string, string>\n /** Fallback colour for nodes/links without a category. */\n defaultColor?: string\n /** Suffix appended to each animated value label (e.g. \" W\"). */\n valueSuffix?: string\n /** NumberFlow `format` for value labels. */\n valueFormat?: Format\n height?: number\n nodeWidth?: number\n nodePadding?: number\n /** Reserved horizontal space on the right for labels. */\n labelPadding?: number\n /**\n * Node ids kept at their natural (computed) column instead of being pushed to\n * the last column. By default `sankeyJustify` sends every node with no\n * outgoing link to the right edge; use this to hold a terminal node (e.g. grid\n * feed-in) in a middle column alongside its siblings.\n */\n keepAtDepthIds?: readonly string[]\n title?: string\n className?: string\n}\n\ntype SankeyDatumNode = SankeyChartNode & {\n /** Computed by d3-sankey; present after layout. */\n}\n\ntype SankeyDatumLink = SankeyChartLink\n\nconst DEFAULT_HEIGHT = 260\nconst DEFAULT_NODE_WIDTH = 12\nconst DEFAULT_NODE_PADDING = 10\nconst DEFAULT_LABEL_PADDING = 96\nconst LEFT_INSET = 4\nconst TOP_INSET = 8\nconst LABEL_GAP = 8\nconst LABEL_STACK_HEIGHT = 26\nconst DEFAULT_COLOR = 'var(--neutral-a9)'\n\nfunction resolveNodeColor(\n node: SankeyChartNode,\n categoryColors: Record<string, string> | undefined,\n defaultColor: string,\n) {\n if (node.color) return node.color\n if (node.category && categoryColors?.[node.category]) {\n return categoryColors[node.category] as string\n }\n return defaultColor\n}\n\nfunction SankeyChart({\n nodes,\n links,\n categoryColors = SANKEY_CATEGORY_COLORS,\n defaultColor = DEFAULT_COLOR,\n valueSuffix,\n valueFormat,\n height = DEFAULT_HEIGHT,\n nodeWidth = DEFAULT_NODE_WIDTH,\n nodePadding = DEFAULT_NODE_PADDING,\n labelPadding = DEFAULT_LABEL_PADDING,\n keepAtDepthIds,\n title,\n className,\n}: SankeyChartProps): React.ReactElement {\n const containerRef = useRef<HTMLDivElement>(null)\n const [width, setWidth] = useState(0)\n\n useEffect(() => {\n const element = containerRef.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n setWidth(entry.contentRect.width)\n })\n\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [])\n\n const nodeIds = useMemo(() => new Set(nodes.map((node) => node.id)), [nodes])\n\n // Drop zero-value links and any whose endpoints aren't in the current node\n // set (a stale link referencing a removed node makes d3-sankey throw\n // `missing: <id>`).\n const visibleLinks = useMemo(\n () =>\n links.filter(\n (link) =>\n link.value > 0 &&\n nodeIds.has(link.source) &&\n nodeIds.has(link.target),\n ),\n [links, nodeIds],\n )\n\n const visibleNodes = useMemo(() => {\n const referencedIds = new Set<string>()\n for (const link of visibleLinks) {\n referencedIds.add(link.source)\n referencedIds.add(link.target)\n }\n return nodes.filter((node) => referencedIds.has(node.id))\n }, [nodes, visibleLinks])\n\n const colorById = useMemo(() => {\n const map = new Map<string, string>()\n for (const node of nodes) {\n map.set(node.id, resolveNodeColor(node, categoryColors, defaultColor))\n }\n return map\n }, [categoryColors, defaultColor, nodes])\n\n const orderById = useMemo(() => {\n const map = new Map<string, number>()\n visibleNodes.forEach((node, index) => map.set(node.id, index))\n return map\n }, [visibleNodes])\n\n const nodeSort = useMemo(\n () =>\n (\n a: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n b: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n ) =>\n (orderById.get(a.id) ?? 0) - (orderById.get(b.id) ?? 0),\n [orderById],\n )\n\n const graph = useMemo(\n () => ({\n nodes: visibleNodes.map((node) => ({ ...node })),\n links: visibleLinks.map((link) => ({ ...link })),\n }),\n [visibleNodes, visibleLinks],\n )\n\n const nodeAlign = useMemo(() => {\n const keep = new Set(keepAtDepthIds)\n return (node: SankeyNode<SankeyDatumNode, SankeyDatumLink>, n: number) =>\n keep.has(node.id) ? (node.depth ?? 0) : sankeyJustify(node, n)\n }, [keepAtDepthIds])\n\n const innerWidth = Math.max(0, width - labelPadding - LEFT_INSET)\n const innerHeight = Math.max(0, height - TOP_INSET * 2)\n const halfLabelStack = LABEL_STACK_HEIGHT / 2\n const labelWidth = Math.max(40, labelPadding - LABEL_GAP - 4)\n\n return (\n <div className={cn('flex w-full flex-col gap-2 py-2', className)}>\n {title && <SectionTitle>{title}</SectionTitle>}\n\n <div ref={containerRef} className=\"relative w-full\" style={{ height }}>\n {width > 0 && innerWidth > 0 && (\n <Sankey<SankeyDatumNode, SankeyDatumLink>\n root={graph}\n size={[innerWidth, innerHeight]}\n nodeWidth={nodeWidth}\n nodePadding={nodePadding}\n nodeAlign={nodeAlign}\n nodeId={(node) => node.id}\n nodeSort={nodeSort}\n >\n {({ graph: laidOut, createPath }) => (\n <>\n <svg\n aria-hidden=\"true\"\n className=\"block\"\n height={height}\n width={width}\n >\n <Group left={LEFT_INSET} top={TOP_INSET}>\n <Group className=\"visx-sankey-links\">\n {laidOut.links.map((link, index) => {\n const sourceId =\n typeof link.source === 'object'\n ? (\n link.source as SankeyNode<\n SankeyDatumNode,\n SankeyDatumLink\n >\n ).id\n : String(link.source)\n const stroke =\n link.color ?? colorById.get(sourceId) ?? defaultColor\n\n return (\n <path\n // oxlint-disable-next-line react/no-array-index-key -- stateless SVG paths; source/target pairs may repeat\n key={`sankey-link-${index}`}\n d={createPath(link) ?? ''}\n fill=\"none\"\n stroke={stroke}\n strokeOpacity={0.32}\n strokeWidth={Math.max(1, link.width ?? 0)}\n />\n )\n })}\n </Group>\n\n <Group className=\"visx-sankey-nodes\">\n {laidOut.nodes.map((node) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const w = node.x1 - node.x0\n const h = node.y1 - node.y0\n const fill = colorById.get(node.id) ?? defaultColor\n\n return (\n <rect\n key={node.id}\n x={node.x0}\n y={node.y0}\n width={w}\n height={Math.max(1, h)}\n fill={fill}\n rx={2}\n />\n )\n })}\n </Group>\n </Group>\n </svg>\n\n <div className=\"pointer-events-none absolute inset-0 [-webkit-text-size-adjust:100%] [text-size-adjust:100%]\">\n {laidOut.nodes.map((node, index) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const h = node.y1 - node.y0\n const nodeCenterY = TOP_INSET + node.y0 + h / 2\n const labelCenterY = Math.min(\n Math.max(nodeCenterY, halfLabelStack),\n height - halfLabelStack,\n )\n\n return (\n <div\n key={`sankey-label-${node.id ?? index}`}\n className=\"absolute flex -translate-y-1/2 flex-col items-start gap-1\"\n style={{\n left: LEFT_INSET + node.x1 + LABEL_GAP,\n top: labelCenterY,\n width: labelWidth,\n }}\n >\n <span className=\"text-[11px] leading-none font-semibold whitespace-nowrap text-gray\">\n {node.label}\n </span>\n <NumberFlow\n value={node.value ?? 0}\n {...(valueSuffix !== undefined && {\n suffix: valueSuffix,\n })}\n {...(valueFormat !== undefined && {\n format: valueFormat,\n })}\n className=\"block min-h-[10px] text-[10px] leading-none text-gray-light tabular-nums\"\n />\n </div>\n )\n })}\n </div>\n </>\n )}\n </Sankey>\n )}\n </div>\n </div>\n )\n}\n\nexport type { SankeyLink, SankeyNode }\nexport { SankeyChart, SANKEY_CATEGORY_COLORS }\n"],"mappings":"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"}
@@ -118,7 +118,7 @@ function SankeyChart({ nodes, links, categoryColors = SANKEY_CATEGORY_COLORS, de
118
118
  })
119
119
  }), /* @__PURE__ */ jsx(Group, {
120
120
  className: "visx-sankey-nodes",
121
- children: laidOut.nodes.map((node, index) => {
121
+ children: laidOut.nodes.map((node) => {
122
122
  if (node.x0 === void 0 || node.x1 === void 0 || node.y0 === void 0 || node.y1 === void 0) return null;
123
123
  const w = node.x1 - node.x0;
124
124
  const h = node.y1 - node.y0;
@@ -130,7 +130,7 @@ function SankeyChart({ nodes, links, categoryColors = SANKEY_CATEGORY_COLORS, de
130
130
  height: Math.max(1, h),
131
131
  fill,
132
132
  rx: 2
133
- }, `sankey-node-${index}`);
133
+ }, node.id);
134
134
  })
135
135
  })]
136
136
  })
@@ -1 +1 @@
1
- {"version":3,"file":"sankey-chart.js","names":[],"sources":["../../src/components/sankey-chart.tsx"],"sourcesContent":["import { Group } from '@visx/group'\nimport {\n Sankey,\n sankeyJustify,\n type SankeyLink,\n type SankeyNode,\n} from '@visx/sankey'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { cn } from '../utils/cn'\nimport NumberFlow, { type Format } from './number-flow'\nimport { SectionTitle } from './typography'\n\nconst SANKEY_CATEGORY_COLORS: Record<string, string> = {\n solar: 'var(--orange-a9)',\n battery: 'var(--green-a9)',\n grid: 'var(--blue-9)',\n gridFeedIn: 'var(--blue-7)',\n home: 'var(--spark-9)',\n floor: 'var(--neutral-a9)',\n device: 'var(--neutral-a8)',\n}\n\nexport interface SankeyChartNode {\n id: string\n label: string\n /** Optional category — used to colour the node and any outgoing links. */\n category?: string\n /** Override the colour resolved from `category`. */\n color?: string\n}\n\nexport interface SankeyChartLink {\n source: string\n target: string\n value: number\n /** Override the colour of the link. Defaults to the source node colour. */\n color?: string\n}\n\nexport interface SankeyChartProps {\n nodes: SankeyChartNode[]\n links: SankeyChartLink[]\n /** Resolves a category to a colour. */\n categoryColors?: Record<string, string>\n /** Fallback colour for nodes/links without a category. */\n defaultColor?: string\n /** Suffix appended to each animated value label (e.g. \" W\"). */\n valueSuffix?: string\n /** NumberFlow `format` for value labels. */\n valueFormat?: Format\n height?: number\n nodeWidth?: number\n nodePadding?: number\n /** Reserved horizontal space on the right for labels. */\n labelPadding?: number\n /**\n * Node ids kept at their natural (computed) column instead of being pushed to\n * the last column. By default `sankeyJustify` sends every node with no\n * outgoing link to the right edge; use this to hold a terminal node (e.g. grid\n * feed-in) in a middle column alongside its siblings.\n */\n keepAtDepthIds?: readonly string[]\n title?: string\n className?: string\n}\n\ntype SankeyDatumNode = SankeyChartNode & {\n /** Computed by d3-sankey; present after layout. */\n}\n\ntype SankeyDatumLink = SankeyChartLink\n\nconst DEFAULT_HEIGHT = 260\nconst DEFAULT_NODE_WIDTH = 12\nconst DEFAULT_NODE_PADDING = 10\nconst DEFAULT_LABEL_PADDING = 96\nconst LEFT_INSET = 4\nconst TOP_INSET = 8\nconst LABEL_GAP = 8\nconst LABEL_STACK_HEIGHT = 26\nconst DEFAULT_COLOR = 'var(--neutral-a9)'\n\nfunction resolveNodeColor(\n node: SankeyChartNode,\n categoryColors: Record<string, string> | undefined,\n defaultColor: string,\n) {\n if (node.color) return node.color\n if (node.category && categoryColors?.[node.category]) {\n return categoryColors[node.category] as string\n }\n return defaultColor\n}\n\nfunction SankeyChart({\n nodes,\n links,\n categoryColors = SANKEY_CATEGORY_COLORS,\n defaultColor = DEFAULT_COLOR,\n valueSuffix,\n valueFormat,\n height = DEFAULT_HEIGHT,\n nodeWidth = DEFAULT_NODE_WIDTH,\n nodePadding = DEFAULT_NODE_PADDING,\n labelPadding = DEFAULT_LABEL_PADDING,\n keepAtDepthIds,\n title,\n className,\n}: SankeyChartProps): React.ReactElement {\n const containerRef = useRef<HTMLDivElement>(null)\n const [width, setWidth] = useState(0)\n\n useEffect(() => {\n const element = containerRef.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n setWidth(entry.contentRect.width)\n })\n\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [])\n\n const nodeIds = useMemo(() => new Set(nodes.map((node) => node.id)), [nodes])\n\n // Drop zero-value links and any whose endpoints aren't in the current node\n // set (a stale link referencing a removed node makes d3-sankey throw\n // `missing: <id>`).\n const visibleLinks = useMemo(\n () =>\n links.filter(\n (link) =>\n link.value > 0 &&\n nodeIds.has(link.source) &&\n nodeIds.has(link.target),\n ),\n [links, nodeIds],\n )\n\n const visibleNodes = useMemo(() => {\n const referencedIds = new Set<string>()\n for (const link of visibleLinks) {\n referencedIds.add(link.source)\n referencedIds.add(link.target)\n }\n return nodes.filter((node) => referencedIds.has(node.id))\n }, [nodes, visibleLinks])\n\n const colorById = useMemo(() => {\n const map = new Map<string, string>()\n for (const node of nodes) {\n map.set(node.id, resolveNodeColor(node, categoryColors, defaultColor))\n }\n return map\n }, [categoryColors, defaultColor, nodes])\n\n const orderById = useMemo(() => {\n const map = new Map<string, number>()\n visibleNodes.forEach((node, index) => map.set(node.id, index))\n return map\n }, [visibleNodes])\n\n const nodeSort = useMemo(\n () =>\n (\n a: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n b: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n ) =>\n (orderById.get(a.id) ?? 0) - (orderById.get(b.id) ?? 0),\n [orderById],\n )\n\n const graph = useMemo(\n () => ({\n nodes: visibleNodes.map((node) => ({ ...node })),\n links: visibleLinks.map((link) => ({ ...link })),\n }),\n [visibleNodes, visibleLinks],\n )\n\n const nodeAlign = useMemo(() => {\n const keep = new Set(keepAtDepthIds)\n return (node: SankeyNode<SankeyDatumNode, SankeyDatumLink>, n: number) =>\n keep.has(node.id) ? (node.depth ?? 0) : sankeyJustify(node, n)\n }, [keepAtDepthIds])\n\n const innerWidth = Math.max(0, width - labelPadding - LEFT_INSET)\n const innerHeight = Math.max(0, height - TOP_INSET * 2)\n const halfLabelStack = LABEL_STACK_HEIGHT / 2\n const labelWidth = Math.max(40, labelPadding - LABEL_GAP - 4)\n\n return (\n <div className={cn('flex w-full flex-col gap-2 py-2', className)}>\n {title && <SectionTitle>{title}</SectionTitle>}\n\n <div ref={containerRef} className=\"relative w-full\" style={{ height }}>\n {width > 0 && innerWidth > 0 && (\n <Sankey<SankeyDatumNode, SankeyDatumLink>\n root={graph}\n size={[innerWidth, innerHeight]}\n nodeWidth={nodeWidth}\n nodePadding={nodePadding}\n nodeAlign={nodeAlign}\n nodeId={(node) => node.id}\n nodeSort={nodeSort}\n >\n {({ graph: laidOut, createPath }) => (\n <>\n <svg\n aria-hidden=\"true\"\n className=\"block\"\n height={height}\n width={width}\n >\n <Group left={LEFT_INSET} top={TOP_INSET}>\n <Group className=\"visx-sankey-links\">\n {laidOut.links.map((link, index) => {\n const sourceId =\n typeof link.source === 'object'\n ? (\n link.source as SankeyNode<\n SankeyDatumNode,\n SankeyDatumLink\n >\n ).id\n : String(link.source)\n const stroke =\n link.color ?? colorById.get(sourceId) ?? defaultColor\n\n return (\n <path\n key={`sankey-link-${index}`}\n d={createPath(link) ?? ''}\n fill=\"none\"\n stroke={stroke}\n strokeOpacity={0.32}\n strokeWidth={Math.max(1, link.width ?? 0)}\n />\n )\n })}\n </Group>\n\n <Group className=\"visx-sankey-nodes\">\n {laidOut.nodes.map((node, index) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const w = node.x1 - node.x0\n const h = node.y1 - node.y0\n const fill = colorById.get(node.id) ?? defaultColor\n\n return (\n <rect\n key={`sankey-node-${index}`}\n x={node.x0}\n y={node.y0}\n width={w}\n height={Math.max(1, h)}\n fill={fill}\n rx={2}\n />\n )\n })}\n </Group>\n </Group>\n </svg>\n\n <div className=\"pointer-events-none absolute inset-0 [-webkit-text-size-adjust:100%] [text-size-adjust:100%]\">\n {laidOut.nodes.map((node, index) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const h = node.y1 - node.y0\n const nodeCenterY = TOP_INSET + node.y0 + h / 2\n const labelCenterY = Math.min(\n Math.max(nodeCenterY, halfLabelStack),\n height - halfLabelStack,\n )\n\n return (\n <div\n key={`sankey-label-${node.id ?? index}`}\n className=\"absolute flex -translate-y-1/2 flex-col items-start gap-1\"\n style={{\n left: LEFT_INSET + node.x1 + LABEL_GAP,\n top: labelCenterY,\n width: labelWidth,\n }}\n >\n <span className=\"text-[11px] leading-none font-semibold whitespace-nowrap text-gray\">\n {node.label}\n </span>\n <NumberFlow\n value={node.value ?? 0}\n {...(valueSuffix !== undefined && {\n suffix: valueSuffix,\n })}\n {...(valueFormat !== undefined && {\n format: valueFormat,\n })}\n className=\"block min-h-[10px] text-[10px] leading-none text-gray-light tabular-nums\"\n />\n </div>\n )\n })}\n </div>\n </>\n )}\n </Sankey>\n )}\n </div>\n </div>\n )\n}\n\nexport type { SankeyLink, SankeyNode }\nexport { SankeyChart, SANKEY_CATEGORY_COLORS }\n"],"mappings":";;;;;;;;;AAYA,MAAM,yBAAiD;CACrD,OAAO;CACP,SAAS;CACT,MAAM;CACN,YAAY;CACZ,MAAM;CACN,OAAO;CACP,QAAQ;AACV;AAoDA,MAAM,iBAAiB;AACvB,MAAM,qBAAqB;AAC3B,MAAM,uBAAuB;AAC7B,MAAM,wBAAwB;AAC9B,MAAM,aAAa;AACnB,MAAM,YAAY;AAClB,MAAM,YAAY;AAClB,MAAM,qBAAqB;AAC3B,MAAM,gBAAgB;AAEtB,SAAS,iBACP,MACA,gBACA,cACA;CACA,IAAI,KAAK,OAAO,OAAO,KAAK;CAC5B,IAAI,KAAK,YAAY,iBAAiB,KAAK,WACzC,OAAO,eAAe,KAAK;CAE7B,OAAO;AACT;AAEA,SAAS,YAAY,EACnB,OACA,OACA,iBAAiB,wBACjB,eAAe,eACf,aACA,aACA,SAAS,gBACT,YAAY,oBACZ,cAAc,sBACd,eAAe,uBACf,gBACA,OACA,aACuC;CACvC,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,CAAC,OAAO,YAAY,SAAS,CAAC;CAEpC,gBAAgB;EACd,MAAM,UAAU,aAAa;EAC7B,IAAI,CAAC,SAAS;EAEd,MAAM,iBAAiB,IAAI,gBAAgB,YAAY;GACrD,MAAM,QAAQ,QAAQ;GACtB,IAAI,CAAC,OAAO;GACZ,SAAS,MAAM,YAAY,KAAK;EAClC,CAAC;EAED,eAAe,QAAQ,OAAO;EAC9B,aAAa,eAAe,WAAW;CACzC,GAAG,CAAC,CAAC;CAEL,MAAM,UAAU,cAAc,IAAI,IAAI,MAAM,KAAK,SAAS,KAAK,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC;CAK5E,MAAM,eAAe,cAEjB,MAAM,QACH,SACC,KAAK,QAAQ,KACb,QAAQ,IAAI,KAAK,MAAM,KACvB,QAAQ,IAAI,KAAK,MAAM,CAC3B,GACF,CAAC,OAAO,OAAO,CACjB;CAEA,MAAM,eAAe,cAAc;EACjC,MAAM,gCAAgB,IAAI,IAAY;EACtC,KAAK,MAAM,QAAQ,cAAc;GAC/B,cAAc,IAAI,KAAK,MAAM;GAC7B,cAAc,IAAI,KAAK,MAAM;EAC/B;EACA,OAAO,MAAM,QAAQ,SAAS,cAAc,IAAI,KAAK,EAAE,CAAC;CAC1D,GAAG,CAAC,OAAO,YAAY,CAAC;CAExB,MAAM,YAAY,cAAc;EAC9B,MAAM,sBAAM,IAAI,IAAoB;EACpC,KAAK,MAAM,QAAQ,OACjB,IAAI,IAAI,KAAK,IAAI,iBAAiB,MAAM,gBAAgB,YAAY,CAAC;EAEvE,OAAO;CACT,GAAG;EAAC;EAAgB;EAAc;CAAK,CAAC;CAExC,MAAM,YAAY,cAAc;EAC9B,MAAM,sBAAM,IAAI,IAAoB;EACpC,aAAa,SAAS,MAAM,UAAU,IAAI,IAAI,KAAK,IAAI,KAAK,CAAC;EAC7D,OAAO;CACT,GAAG,CAAC,YAAY,CAAC;CAEjB,MAAM,WAAW,eAGX,GACA,OAEC,UAAU,IAAI,EAAE,EAAE,KAAK,MAAM,UAAU,IAAI,EAAE,EAAE,KAAK,IACzD,CAAC,SAAS,CACZ;CAEA,MAAM,QAAQ,eACL;EACL,OAAO,aAAa,KAAK,UAAU,EAAE,GAAG,KAAK,EAAE;EAC/C,OAAO,aAAa,KAAK,UAAU,EAAE,GAAG,KAAK,EAAE;CACjD,IACA,CAAC,cAAc,YAAY,CAC7B;CAEA,MAAM,YAAY,cAAc;EAC9B,MAAM,OAAO,IAAI,IAAI,cAAc;EACnC,QAAQ,MAAoD,MAC1D,KAAK,IAAI,KAAK,EAAE,IAAK,KAAK,SAAS,IAAK,cAAc,MAAM,CAAC;CACjE,GAAG,CAAC,cAAc,CAAC;CAEnB,MAAM,aAAa,KAAK,IAAI,GAAG,QAAQ,eAAe,UAAU;CAChE,MAAM,cAAc,KAAK,IAAI,GAAG,SAAS,YAAY,CAAC;CACtD,MAAM,iBAAiB,qBAAqB;CAC5C,MAAM,aAAa,KAAK,IAAI,IAAI,eAAe,YAAY,CAAC;CAE5D,OACE,qBAAC,OAAD;EAAK,WAAW,GAAG,mCAAmC,SAAS;YAA/D,CACG,SAAS,oBAAC,cAAD,EAAA,UAAe,MAAoB,CAAA,GAE7C,oBAAC,OAAD;GAAK,KAAK;GAAc,WAAU;GAAkB,OAAO,EAAE,OAAO;aACjE,QAAQ,KAAK,aAAa,KACzB,oBAAC,QAAD;IACE,MAAM;IACN,MAAM,CAAC,YAAY,WAAW;IACnB;IACE;IACF;IACX,SAAS,SAAS,KAAK;IACb;eAER,EAAE,OAAO,SAAS,iBAClB,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,OAAD;KACE,eAAY;KACZ,WAAU;KACF;KACD;eAEP,qBAAC,OAAD;MAAO,MAAM;MAAY,KAAK;gBAA9B,CACE,oBAAC,OAAD;OAAO,WAAU;iBACd,QAAQ,MAAM,KAAK,MAAM,UAAU;QAClC,MAAM,WACJ,OAAO,KAAK,WAAW,WAEjB,KAAK,OAIL,KACF,OAAO,KAAK,MAAM;QACxB,MAAM,SACJ,KAAK,SAAS,UAAU,IAAI,QAAQ,KAAK;QAE3C,OACE,oBAAC,QAAD;SAEE,GAAG,WAAW,IAAI,KAAK;SACvB,MAAK;SACG;SACR,eAAe;SACf,aAAa,KAAK,IAAI,GAAG,KAAK,SAAS,CAAC;QACzC,GANM,eAAe,OAMrB;OAEL,CAAC;MACI,CAAA,GAEP,oBAAC,OAAD;OAAO,WAAU;iBACd,QAAQ,MAAM,KAAK,MAAM,UAAU;QAClC,IACE,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,GAEZ,OAAO;QAGT,MAAM,IAAI,KAAK,KAAK,KAAK;QACzB,MAAM,IAAI,KAAK,KAAK,KAAK;QACzB,MAAM,OAAO,UAAU,IAAI,KAAK,EAAE,KAAK;QAEvC,OACE,oBAAC,QAAD;SAEE,GAAG,KAAK;SACR,GAAG,KAAK;SACR,OAAO;SACP,QAAQ,KAAK,IAAI,GAAG,CAAC;SACf;SACN,IAAI;QACL,GAPM,eAAe,OAOrB;OAEL,CAAC;MACI,CAAA,CACF;;IACJ,CAAA,GAEL,oBAAC,OAAD;KAAK,WAAU;eACZ,QAAQ,MAAM,KAAK,MAAM,UAAU;MAClC,IACE,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,KACZ,KAAK,OAAO,KAAA,GAEZ,OAAO;MAGT,MAAM,IAAI,KAAK,KAAK,KAAK;MACzB,MAAM,cAAc,YAAY,KAAK,KAAK,IAAI;MAC9C,MAAM,eAAe,KAAK,IACxB,KAAK,IAAI,aAAa,cAAc,GACpC,SAAS,cACX;MAEA,OACE,qBAAC,OAAD;OAEE,WAAU;OACV,OAAO;QACL,MAAM,aAAa,KAAK,KAAK;QAC7B,KAAK;QACL,OAAO;OACT;iBAPF,CASE,oBAAC,QAAD;QAAM,WAAU;kBACb,KAAK;OACF,CAAA,GACN,oBAAC,YAAD;QACE,OAAO,KAAK,SAAS;QACrB,GAAK,gBAAgB,KAAA,KAAa,EAChC,QAAQ,YACV;QACA,GAAK,gBAAgB,KAAA,KAAa,EAChC,QAAQ,YACV;QACA,WAAU;OACX,CAAA,CACE;SArBE,gBAAgB,KAAK,MAAM,OAqB7B;KAET,CAAC;IACE,CAAA,CACL,EAAA,CAAA;GAEE,CAAA;EAEP,CAAA,CACF;;AAET"}
1
+ {"version":3,"file":"sankey-chart.js","names":[],"sources":["../../src/components/sankey-chart.tsx"],"sourcesContent":["import { Group } from '@visx/group'\nimport {\n Sankey,\n sankeyJustify,\n type SankeyLink,\n type SankeyNode,\n} from '@visx/sankey'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { cn } from '../utils/cn'\nimport NumberFlow, { type Format } from './number-flow'\nimport { SectionTitle } from './typography'\n\nconst SANKEY_CATEGORY_COLORS: Record<string, string> = {\n solar: 'var(--orange-a9)',\n battery: 'var(--green-a9)',\n grid: 'var(--blue-9)',\n gridFeedIn: 'var(--blue-7)',\n home: 'var(--spark-9)',\n floor: 'var(--neutral-a9)',\n device: 'var(--neutral-a8)',\n}\n\nexport interface SankeyChartNode {\n id: string\n label: string\n /** Optional category — used to colour the node and any outgoing links. */\n category?: string\n /** Override the colour resolved from `category`. */\n color?: string\n}\n\nexport interface SankeyChartLink {\n source: string\n target: string\n value: number\n /** Override the colour of the link. Defaults to the source node colour. */\n color?: string\n}\n\nexport interface SankeyChartProps {\n nodes: SankeyChartNode[]\n links: SankeyChartLink[]\n /** Resolves a category to a colour. */\n categoryColors?: Record<string, string>\n /** Fallback colour for nodes/links without a category. */\n defaultColor?: string\n /** Suffix appended to each animated value label (e.g. \" W\"). */\n valueSuffix?: string\n /** NumberFlow `format` for value labels. */\n valueFormat?: Format\n height?: number\n nodeWidth?: number\n nodePadding?: number\n /** Reserved horizontal space on the right for labels. */\n labelPadding?: number\n /**\n * Node ids kept at their natural (computed) column instead of being pushed to\n * the last column. By default `sankeyJustify` sends every node with no\n * outgoing link to the right edge; use this to hold a terminal node (e.g. grid\n * feed-in) in a middle column alongside its siblings.\n */\n keepAtDepthIds?: readonly string[]\n title?: string\n className?: string\n}\n\ntype SankeyDatumNode = SankeyChartNode & {\n /** Computed by d3-sankey; present after layout. */\n}\n\ntype SankeyDatumLink = SankeyChartLink\n\nconst DEFAULT_HEIGHT = 260\nconst DEFAULT_NODE_WIDTH = 12\nconst DEFAULT_NODE_PADDING = 10\nconst DEFAULT_LABEL_PADDING = 96\nconst LEFT_INSET = 4\nconst TOP_INSET = 8\nconst LABEL_GAP = 8\nconst LABEL_STACK_HEIGHT = 26\nconst DEFAULT_COLOR = 'var(--neutral-a9)'\n\nfunction resolveNodeColor(\n node: SankeyChartNode,\n categoryColors: Record<string, string> | undefined,\n defaultColor: string,\n) {\n if (node.color) return node.color\n if (node.category && categoryColors?.[node.category]) {\n return categoryColors[node.category] as string\n }\n return defaultColor\n}\n\nfunction SankeyChart({\n nodes,\n links,\n categoryColors = SANKEY_CATEGORY_COLORS,\n defaultColor = DEFAULT_COLOR,\n valueSuffix,\n valueFormat,\n height = DEFAULT_HEIGHT,\n nodeWidth = DEFAULT_NODE_WIDTH,\n nodePadding = DEFAULT_NODE_PADDING,\n labelPadding = DEFAULT_LABEL_PADDING,\n keepAtDepthIds,\n title,\n className,\n}: SankeyChartProps): React.ReactElement {\n const containerRef = useRef<HTMLDivElement>(null)\n const [width, setWidth] = useState(0)\n\n useEffect(() => {\n const element = containerRef.current\n if (!element) return\n\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n setWidth(entry.contentRect.width)\n })\n\n resizeObserver.observe(element)\n return () => resizeObserver.disconnect()\n }, [])\n\n const nodeIds = useMemo(() => new Set(nodes.map((node) => node.id)), [nodes])\n\n // Drop zero-value links and any whose endpoints aren't in the current node\n // set (a stale link referencing a removed node makes d3-sankey throw\n // `missing: <id>`).\n const visibleLinks = useMemo(\n () =>\n links.filter(\n (link) =>\n link.value > 0 &&\n nodeIds.has(link.source) &&\n nodeIds.has(link.target),\n ),\n [links, nodeIds],\n )\n\n const visibleNodes = useMemo(() => {\n const referencedIds = new Set<string>()\n for (const link of visibleLinks) {\n referencedIds.add(link.source)\n referencedIds.add(link.target)\n }\n return nodes.filter((node) => referencedIds.has(node.id))\n }, [nodes, visibleLinks])\n\n const colorById = useMemo(() => {\n const map = new Map<string, string>()\n for (const node of nodes) {\n map.set(node.id, resolveNodeColor(node, categoryColors, defaultColor))\n }\n return map\n }, [categoryColors, defaultColor, nodes])\n\n const orderById = useMemo(() => {\n const map = new Map<string, number>()\n visibleNodes.forEach((node, index) => map.set(node.id, index))\n return map\n }, [visibleNodes])\n\n const nodeSort = useMemo(\n () =>\n (\n a: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n b: SankeyNode<SankeyDatumNode, SankeyDatumLink>,\n ) =>\n (orderById.get(a.id) ?? 0) - (orderById.get(b.id) ?? 0),\n [orderById],\n )\n\n const graph = useMemo(\n () => ({\n nodes: visibleNodes.map((node) => ({ ...node })),\n links: visibleLinks.map((link) => ({ ...link })),\n }),\n [visibleNodes, visibleLinks],\n )\n\n const nodeAlign = useMemo(() => {\n const keep = new Set(keepAtDepthIds)\n return (node: SankeyNode<SankeyDatumNode, SankeyDatumLink>, n: number) =>\n keep.has(node.id) ? (node.depth ?? 0) : sankeyJustify(node, n)\n }, [keepAtDepthIds])\n\n const innerWidth = Math.max(0, width - labelPadding - LEFT_INSET)\n const innerHeight = Math.max(0, height - TOP_INSET * 2)\n const halfLabelStack = LABEL_STACK_HEIGHT / 2\n const labelWidth = Math.max(40, labelPadding - LABEL_GAP - 4)\n\n return (\n <div className={cn('flex w-full flex-col gap-2 py-2', className)}>\n {title && <SectionTitle>{title}</SectionTitle>}\n\n <div ref={containerRef} className=\"relative w-full\" style={{ height }}>\n {width > 0 && innerWidth > 0 && (\n <Sankey<SankeyDatumNode, SankeyDatumLink>\n root={graph}\n size={[innerWidth, innerHeight]}\n nodeWidth={nodeWidth}\n nodePadding={nodePadding}\n nodeAlign={nodeAlign}\n nodeId={(node) => node.id}\n nodeSort={nodeSort}\n >\n {({ graph: laidOut, createPath }) => (\n <>\n <svg\n aria-hidden=\"true\"\n className=\"block\"\n height={height}\n width={width}\n >\n <Group left={LEFT_INSET} top={TOP_INSET}>\n <Group className=\"visx-sankey-links\">\n {laidOut.links.map((link, index) => {\n const sourceId =\n typeof link.source === 'object'\n ? (\n link.source as SankeyNode<\n SankeyDatumNode,\n SankeyDatumLink\n >\n ).id\n : String(link.source)\n const stroke =\n link.color ?? colorById.get(sourceId) ?? defaultColor\n\n return (\n <path\n // oxlint-disable-next-line react/no-array-index-key -- stateless SVG paths; source/target pairs may repeat\n key={`sankey-link-${index}`}\n d={createPath(link) ?? ''}\n fill=\"none\"\n stroke={stroke}\n strokeOpacity={0.32}\n strokeWidth={Math.max(1, link.width ?? 0)}\n />\n )\n })}\n </Group>\n\n <Group className=\"visx-sankey-nodes\">\n {laidOut.nodes.map((node) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const w = node.x1 - node.x0\n const h = node.y1 - node.y0\n const fill = colorById.get(node.id) ?? defaultColor\n\n return (\n <rect\n key={node.id}\n x={node.x0}\n y={node.y0}\n width={w}\n height={Math.max(1, h)}\n fill={fill}\n rx={2}\n />\n )\n })}\n </Group>\n </Group>\n </svg>\n\n <div className=\"pointer-events-none absolute inset-0 [-webkit-text-size-adjust:100%] [text-size-adjust:100%]\">\n {laidOut.nodes.map((node, index) => {\n if (\n node.x0 === undefined ||\n node.x1 === undefined ||\n node.y0 === undefined ||\n node.y1 === undefined\n ) {\n return null\n }\n\n const h = node.y1 - node.y0\n const nodeCenterY = TOP_INSET + node.y0 + h / 2\n const labelCenterY = Math.min(\n Math.max(nodeCenterY, halfLabelStack),\n height - halfLabelStack,\n )\n\n return (\n <div\n key={`sankey-label-${node.id ?? index}`}\n className=\"absolute flex -translate-y-1/2 flex-col items-start gap-1\"\n style={{\n left: LEFT_INSET + node.x1 + LABEL_GAP,\n top: labelCenterY,\n width: labelWidth,\n }}\n >\n <span className=\"text-[11px] leading-none font-semibold whitespace-nowrap text-gray\">\n {node.label}\n </span>\n <NumberFlow\n value={node.value ?? 0}\n {...(valueSuffix !== undefined && {\n suffix: valueSuffix,\n })}\n {...(valueFormat !== undefined && {\n format: valueFormat,\n })}\n className=\"block min-h-[10px] text-[10px] leading-none text-gray-light tabular-nums\"\n />\n </div>\n )\n })}\n </div>\n </>\n )}\n </Sankey>\n )}\n </div>\n </div>\n )\n}\n\nexport type { SankeyLink, SankeyNode }\nexport { SankeyChart, SANKEY_CATEGORY_COLORS }\n"],"mappings":"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"}