diff --git a/src/components/ChargeVizPanel.tsx b/src/components/ChargeVizPanel.tsx index f3a1bc2..fcc527d 100644 --- a/src/components/ChargeVizPanel.tsx +++ b/src/components/ChargeVizPanel.tsx @@ -1,6 +1,5 @@ // --------------------------------------------------------------------------- // ChargeVizPanel.tsx -// Responsive Grafana battery visual with trimmed SVG padding. // --------------------------------------------------------------------------- import React from 'react'; @@ -19,11 +18,13 @@ interface ChargeVizOptions { interface Props extends PanelProps {} +// Utility: safely convert any value to a finite number. const toFinite = (v: any): number | undefined => { const n = Number(v); return Number.isFinite(n) ? n : undefined; }; +// Compute least-squares slope (% per minute) for stable trend estimation. function slopePercentPerMinute(times: number[], values: number[]): number { const pts: Array<[number, number]> = []; for (let i = 0; i < times.length; i++) { @@ -60,6 +61,9 @@ export const ChargeVizPanel: React.FC = ({ data, width, height, options } const useRegression = options.useRegressionForRate ?? false; const hideText = options.hideText ?? false; + // ----------------------------------------------------------------------- + // Data extraction and basic stats + // ----------------------------------------------------------------------- let currentValue = 0; let rate = 0; let low = 0; @@ -101,11 +105,17 @@ export const ChargeVizPanel: React.FC = ({ data, width, height, options } } } + // Clamp the percentage range. const fillLevel = Math.max(0, Math.min(100, currentValue)); + + // Select colour by thresholds. let fillColour = highColour; if (fillLevel < 30) fillColour = lowColour; else if (fillLevel < 70) fillColour = midColour; + // ----------------------------------------------------------------------- + // Gradient setup for charge/discharge effect + // ----------------------------------------------------------------------- const gradientId = 'batteryGradient'; let gradientDef: React.ReactNode = null; if (gradientFill) { @@ -129,115 +139,111 @@ export const ChargeVizPanel: React.FC = ({ data, width, height, options } ); } - // Dynamic scaling (Option B) - const aspectRatio = 0.5; - const panelRatio = height / width; - let batteryWidth: number; - let batteryHeight: number; - - if (panelRatio < aspectRatio) { - batteryHeight = height * 0.9; - batteryWidth = batteryHeight / aspectRatio; - } else { - batteryWidth = width * (hideText ? 0.95 : 0.6); - batteryHeight = batteryWidth * aspectRatio; - } - + // The SVG’s internal fill height (for the battery graphic) const fillHeightPx = (fillLevel / 100) * 220; // ----------------------------------------------------------------------- - // Render + // Render layout // ----------------------------------------------------------------------- return (
- {/* Battery SVG with reduced horizontal padding */} - - {gradientDef} - - {/* Outline - starts closer to edges */} - - - {/* Terminal */} - - - {/* Fill - adjusted for new inner width */} - - - {/* Text inside battery */} - - {fillLevel.toFixed(0)}% - - + {gradientDef} - {/* Optional stats text */} + {/* Battery outline */} + + + {/* Terminal */} + + + {/* Fill area */} + + + {/* Percentage text inside the battery */} + + {fillLevel.toFixed(0)}% + + +
+ + {/* Optional side text – vertically centred next to battery */} {!hideText && (
-
+
Rate: {rate.toFixed(dp)} %/min
Low: {low.toFixed(dp)}%