From daf46e0c5e0898bfafa856d6a986b2bfcbb1d6f0 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Tue, 25 Nov 2025 20:45:11 +0000 Subject: [PATCH] fix: auto-hide sublabel when progress bar text overflows Use ResizeObserver to track container width and estimate text width based on character count. When the full text (percentage + sublabel) won't fit, only the percentage is shown to prevent text clipping. --- .../src/components/Dashboard/MetricBar.tsx | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/frontend-modern/src/components/Dashboard/MetricBar.tsx b/frontend-modern/src/components/Dashboard/MetricBar.tsx index d3c666102..1efc3d72b 100644 --- a/frontend-modern/src/components/Dashboard/MetricBar.tsx +++ b/frontend-modern/src/components/Dashboard/MetricBar.tsx @@ -1,4 +1,4 @@ -import { Show, createMemo } from 'solid-js'; +import { Show, createMemo, createSignal, onMount, onCleanup } from 'solid-js'; import { Sparkline } from '@/components/shared/Sparkline'; import { useMetricsViewMode } from '@/stores/metricsViewMode'; import { getMetricHistory } from '@/stores/metricsHistory'; @@ -11,10 +11,45 @@ interface MetricBarProps { resourceId?: string; // Required for sparkline mode to fetch history } +// Estimate text width based on character count (rough approximation for 10px font) +// Average char width ~6px at 10px font size +const estimateTextWidth = (text: string): number => { + return text.length * 5.5 + 8; // chars * avg width + padding +}; + export function MetricBar(props: MetricBarProps) { const { viewMode } = useMetricsViewMode(); const width = createMemo(() => Math.min(props.value, 100)); + // Track container width + const [containerWidth, setContainerWidth] = createSignal(100); + let containerRef: HTMLDivElement | undefined; + + // Set up ResizeObserver to track container width changes + onMount(() => { + if (!containerRef) return; + + setContainerWidth(containerRef.offsetWidth); + + const observer = new ResizeObserver((entries) => { + for (const entry of entries) { + setContainerWidth(entry.contentRect.width); + } + }); + + observer.observe(containerRef); + + onCleanup(() => observer.disconnect()); + }); + + // Determine if sublabel fits based on estimated text width + const showSublabel = createMemo(() => { + if (!props.sublabel) return false; + const fullText = `${props.label} (${props.sublabel})`; + const estimatedWidth = estimateTextWidth(fullText); + return containerWidth() >= estimatedWidth; + }); + // Get color based on percentage and metric type (matching original) const getColor = createMemo(() => { const percentage = props.value; @@ -67,18 +102,16 @@ export function MetricBar(props: MetricBarProps) { when={viewMode() === 'sparklines' && props.resourceId} fallback={ // Original progress bar mode -
+
{props.label} - - {(sublabel) => ( - - ({sublabel()}) - - )} + + + ({props.sublabel}) +