From cd4aacf6fd9ee7cbd9e760b90de9dbb31d610ec4 Mon Sep 17 00:00:00 2001 From: Torsten Date: Thu, 11 Jun 2026 10:23:11 +0000 Subject: [PATCH] unique_bubble_graph.yaml aktualisiert --- unique_bubble_graph.yaml | 158 ++++++++++++++++++++++++++++++++------- 1 file changed, 130 insertions(+), 28 deletions(-) diff --git a/unique_bubble_graph.yaml b/unique_bubble_graph.yaml index 09a4458..3e494b3 100644 --- a/unique_bubble_graph.yaml +++ b/unique_bubble_graph.yaml @@ -1,6 +1,6 @@ unique_bubble_graph: name: Unique Bubble Multi History Background Graph - version: 2.8.6 + version: 2.8.7 creator: Torsten supported: - button @@ -120,6 +120,26 @@ unique_bubble_graph: pointer-events: none; } + .bubble-history-point-markers { + position: absolute !important; + inset: 0 !important; + z-index: 8 !important; + display: none; + pointer-events: none; + } + + .bubble-history-point-marker { + position: absolute !important; + width: 9px; + height: 9px; + border-radius: 50%; + transform: translate(-50%, -50%); + box-shadow: + 0 0 0 2px rgba(20, 20, 20, 0.55), + 0 0 8px rgba(255, 255, 255, 0.65); + pointer-events: none; + } + .bubble-button-card-container, .bubble-button-card, .bubble-name-container, @@ -175,6 +195,9 @@ unique_bubble_graph: const extendToNow = cfg.extend_to_now !== false; const extendThresholdMinutes = Number(cfg.extend_threshold_minutes ?? 2); + const tooltipIndicator = ["points", "line", "both", "none"].includes(cfg.tooltip_indicator) + ? cfg.tooltip_indicator + : "points"; const sameScale = cfg.same_scale === true; const numberOrNull = (value) => { @@ -360,6 +383,7 @@ unique_bubble_graph: let bg = host.querySelector(".bubble-history-background"); let tooltip = host.querySelector(".bubble-history-tooltip"); let marker = host.querySelector(".bubble-history-marker"); + let pointLayer = host.querySelector(".bubble-history-point-markers"); let loader = host.querySelector(".bubble-history-loader"); if (!bg) { @@ -381,6 +405,12 @@ unique_bubble_graph: host.appendChild(marker); } + if (!pointLayer) { + pointLayer = document.createElement("div"); + pointLayer.className = "bubble-history-point-markers"; + host.appendChild(pointLayer); + } + if (!loader) { loader = document.createElement("div"); loader.className = "bubble-history-loader"; @@ -537,7 +567,10 @@ unique_bubble_graph: host.__bubbleHistoryTooltip = tooltip; host.__bubbleHistoryMarker = marker; + host.__bubbleHistoryPointLayer = pointLayer; host.__bubbleHistoryTooltipTop = tooltipTop; + host.__bubbleHistoryTooltipIndicator = tooltipIndicator; + host.__bubbleHistoryPaddingY = paddingY; if (host.__bubbleHistoryTooltipAttached) return; @@ -551,8 +584,9 @@ unique_bubble_graph: const tooltipElement = host.__bubbleHistoryTooltip; const markerElement = host.__bubbleHistoryMarker; + const pointLayerElement = host.__bubbleHistoryPointLayer; - if (!tooltipElement || !markerElement) return; + if (!tooltipElement || !markerElement || !pointLayerElement) return; const rect = host.getBoundingClientRect(); const xRatio = Math.min(1, Math.max(0, (event.clientX - rect.left) / rect.width)); @@ -568,11 +602,15 @@ unique_bubble_graph: : Math.max(...usableSeries.map((series) => series.points[series.points.length - 1].t)); const targetTime = globalMinTime + xRatio * (globalMaxTime - globalMinTime); + const timeRange = globalMaxTime - globalMinTime || 1; + const indicatorMode = host.__bubbleHistoryTooltipIndicator || "points"; - const rows = usableSeries.map((series) => { - const nearest = findNearestPoint(series.points, targetTime); - if (!nearest) return ""; + const nearestBySeries = usableSeries.map((series) => ({ + series, + nearest: findNearestPoint(series.points, targetTime) + })).filter((entry) => entry.nearest); + const rows = nearestBySeries.map(({ series, nearest }) => { return `
@@ -581,31 +619,68 @@ unique_bubble_graph: `; }).join(""); - const firstNearest = findNearestPoint(usableSeries[0].points, targetTime); + const firstNearest = nearestBySeries[0]?.nearest; const tooltipTime = firstNearest ? firstNearest.t : targetTime; - const x = ((targetTime - globalMinTime) / (globalMaxTime - globalMinTime || 1)) * rect.width; + const x = ((targetTime - globalMinTime) / timeRange) * rect.width; const safeX = Math.min(rect.width - 60, Math.max(60, x)); + const pointMarkers = nearestBySeries.map(({ series, nearest }) => { + const minValue = Number.isFinite(series.__minValue) ? series.__minValue : 0; + const maxValue = Number.isFinite(series.__maxValue) ? series.__maxValue : 1; + const valueRange = maxValue - minValue || 1; + const pointX = ((nearest.t - globalMinTime) / timeRange) * rect.width; + const pointYView = + 100 - + host.__bubbleHistoryPaddingY - + ((nearest.v - minValue) / valueRange) * + (100 - host.__bubbleHistoryPaddingY * 2); + const pointY = (pointYView / 100) * rect.height; + + return ` + + `; + }).join(""); + tooltipElement.innerHTML = ` ${rows}
${formatTime(tooltipTime)}
`; tooltipElement.style.display = "block"; - markerElement.style.display = "block"; - tooltipElement.style.left = `${safeX}px`; tooltipElement.style.top = `${host.__bubbleHistoryTooltipTop}px`; - markerElement.style.left = `${x}px`; + + if (indicatorMode === "line" || indicatorMode === "both") { + markerElement.style.display = "block"; + markerElement.style.left = `${x}px`; + } else { + markerElement.style.display = "none"; + } + + if (indicatorMode === "points" || indicatorMode === "both") { + pointLayerElement.innerHTML = pointMarkers; + pointLayerElement.style.display = "block"; + } else { + pointLayerElement.innerHTML = ""; + pointLayerElement.style.display = "none"; + } }; host.onmouseleave = () => { const tooltipElement = host.__bubbleHistoryTooltip; const markerElement = host.__bubbleHistoryMarker; + const pointLayerElement = host.__bubbleHistoryPointLayer; if (tooltipElement) tooltipElement.style.display = "none"; if (markerElement) markerElement.style.display = "none"; + if (pointLayerElement) { + pointLayerElement.style.display = "none"; + pointLayerElement.innerHTML = ""; + } }; }; @@ -645,25 +720,39 @@ unique_bubble_graph: const globalMaxTime = Math.max(...usableSeries.map((series) => series.points[series.points.length - 1].t)); const sameScaleRange = sameScale ? getSameScaleRange(usableSeries) : null; - const buildPath = (series) => { - let minValue; - let maxValue; - + const getValueRange = (series) => { if (sameScaleRange) { - minValue = sameScaleRange.minValue; - maxValue = sameScaleRange.maxValue; - } else { - const rawMinValue = Math.min(...series.points.map((point) => point.v)); - const rawMaxValue = Math.max(...series.points.map((point) => point.v)); - - const rawRange = rawMaxValue - rawMinValue; - const safeRange = rawRange || Math.max(Math.abs(rawMaxValue), 1); - const valuePadding = safeRange * (valuePaddingPercent / 100); - - minValue = rawMinValue - valuePadding; - maxValue = rawMaxValue + valuePadding; + return { + minValue: sameScaleRange.minValue, + maxValue: sameScaleRange.maxValue + }; } + const rawMinValue = Math.min(...series.points.map((point) => point.v)); + const rawMaxValue = Math.max(...series.points.map((point) => point.v)); + const rawRange = rawMaxValue - rawMinValue; + const safeRange = rawRange || Math.max(Math.abs(rawMaxValue), 1); + const valuePadding = safeRange * (valuePaddingPercent / 100); + + return { + minValue: rawMinValue - valuePadding, + maxValue: rawMaxValue + valuePadding + }; + }; + + const renderSeries = usableSeries.map((series) => { + const range = getValueRange(series); + + return { + ...series, + __minValue: range.minValue, + __maxValue: range.maxValue + }; + }); + + const buildPath = (series) => { + const minValue = series.__minValue; + const maxValue = series.__maxValue; const timeRange = globalMaxTime - globalMinTime || 1; const valueRange = maxValue - minValue || 1; @@ -685,7 +774,7 @@ unique_bubble_graph: .join(" "); }; - const paths = usableSeries.map((series, index) => { + const paths = renderSeries.map((series, index) => { const linePath = buildPath(series); const areaPath = `${linePath} L ${width} ${height} L 0 ${height} Z`; @@ -714,7 +803,7 @@ unique_bubble_graph: `; - host.__bubbleHistorySeries = usableSeries; + host.__bubbleHistorySeries = renderSeries; host.__bubbleHistoryMinTime = globalMinTime; host.__bubbleHistoryMaxTime = globalMaxTime; attachTooltipOnce(); @@ -1197,6 +1286,19 @@ unique_bubble_graph: label: Tooltip anzeigen selector: boolean: null + - name: tooltip_indicator + label: Tooltip Markierung + selector: + select: + options: + - label: Punkte auf dem Graphen + value: points + - label: Vertikale Linie + value: line + - label: Punkte und Linie + value: both + - label: Keine Markierung + value: none - name: tooltip_top label: Tooltip Position oben selector: