unique_bubble_graph.yaml aktualisiert
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
unique_bubble_graph:
|
unique_bubble_graph:
|
||||||
name: Unique Bubble Multi History Background Graph
|
name: Unique Bubble Multi History Background Graph
|
||||||
version: 2.8.6
|
version: 2.8.7
|
||||||
creator: Torsten
|
creator: Torsten
|
||||||
supported:
|
supported:
|
||||||
- button
|
- button
|
||||||
@@ -120,6 +120,26 @@ unique_bubble_graph:
|
|||||||
pointer-events: none;
|
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-container,
|
||||||
.bubble-button-card,
|
.bubble-button-card,
|
||||||
.bubble-name-container,
|
.bubble-name-container,
|
||||||
@@ -175,6 +195,9 @@ unique_bubble_graph:
|
|||||||
|
|
||||||
const extendToNow = cfg.extend_to_now !== false;
|
const extendToNow = cfg.extend_to_now !== false;
|
||||||
const extendThresholdMinutes = Number(cfg.extend_threshold_minutes ?? 2);
|
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 sameScale = cfg.same_scale === true;
|
||||||
const numberOrNull = (value) => {
|
const numberOrNull = (value) => {
|
||||||
@@ -360,6 +383,7 @@ unique_bubble_graph:
|
|||||||
let bg = host.querySelector(".bubble-history-background");
|
let bg = host.querySelector(".bubble-history-background");
|
||||||
let tooltip = host.querySelector(".bubble-history-tooltip");
|
let tooltip = host.querySelector(".bubble-history-tooltip");
|
||||||
let marker = host.querySelector(".bubble-history-marker");
|
let marker = host.querySelector(".bubble-history-marker");
|
||||||
|
let pointLayer = host.querySelector(".bubble-history-point-markers");
|
||||||
let loader = host.querySelector(".bubble-history-loader");
|
let loader = host.querySelector(".bubble-history-loader");
|
||||||
|
|
||||||
if (!bg) {
|
if (!bg) {
|
||||||
@@ -381,6 +405,12 @@ unique_bubble_graph:
|
|||||||
host.appendChild(marker);
|
host.appendChild(marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!pointLayer) {
|
||||||
|
pointLayer = document.createElement("div");
|
||||||
|
pointLayer.className = "bubble-history-point-markers";
|
||||||
|
host.appendChild(pointLayer);
|
||||||
|
}
|
||||||
|
|
||||||
if (!loader) {
|
if (!loader) {
|
||||||
loader = document.createElement("div");
|
loader = document.createElement("div");
|
||||||
loader.className = "bubble-history-loader";
|
loader.className = "bubble-history-loader";
|
||||||
@@ -537,7 +567,10 @@ unique_bubble_graph:
|
|||||||
|
|
||||||
host.__bubbleHistoryTooltip = tooltip;
|
host.__bubbleHistoryTooltip = tooltip;
|
||||||
host.__bubbleHistoryMarker = marker;
|
host.__bubbleHistoryMarker = marker;
|
||||||
|
host.__bubbleHistoryPointLayer = pointLayer;
|
||||||
host.__bubbleHistoryTooltipTop = tooltipTop;
|
host.__bubbleHistoryTooltipTop = tooltipTop;
|
||||||
|
host.__bubbleHistoryTooltipIndicator = tooltipIndicator;
|
||||||
|
host.__bubbleHistoryPaddingY = paddingY;
|
||||||
|
|
||||||
if (host.__bubbleHistoryTooltipAttached) return;
|
if (host.__bubbleHistoryTooltipAttached) return;
|
||||||
|
|
||||||
@@ -551,8 +584,9 @@ unique_bubble_graph:
|
|||||||
|
|
||||||
const tooltipElement = host.__bubbleHistoryTooltip;
|
const tooltipElement = host.__bubbleHistoryTooltip;
|
||||||
const markerElement = host.__bubbleHistoryMarker;
|
const markerElement = host.__bubbleHistoryMarker;
|
||||||
|
const pointLayerElement = host.__bubbleHistoryPointLayer;
|
||||||
|
|
||||||
if (!tooltipElement || !markerElement) return;
|
if (!tooltipElement || !markerElement || !pointLayerElement) return;
|
||||||
|
|
||||||
const rect = host.getBoundingClientRect();
|
const rect = host.getBoundingClientRect();
|
||||||
const xRatio = Math.min(1, Math.max(0, (event.clientX - rect.left) / rect.width));
|
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));
|
: Math.max(...usableSeries.map((series) => series.points[series.points.length - 1].t));
|
||||||
|
|
||||||
const targetTime = globalMinTime + xRatio * (globalMaxTime - globalMinTime);
|
const targetTime = globalMinTime + xRatio * (globalMaxTime - globalMinTime);
|
||||||
|
const timeRange = globalMaxTime - globalMinTime || 1;
|
||||||
|
const indicatorMode = host.__bubbleHistoryTooltipIndicator || "points";
|
||||||
|
|
||||||
const rows = usableSeries.map((series) => {
|
const nearestBySeries = usableSeries.map((series) => ({
|
||||||
const nearest = findNearestPoint(series.points, targetTime);
|
series,
|
||||||
if (!nearest) return "";
|
nearest: findNearestPoint(series.points, targetTime)
|
||||||
|
})).filter((entry) => entry.nearest);
|
||||||
|
|
||||||
|
const rows = nearestBySeries.map(({ series, nearest }) => {
|
||||||
return `
|
return `
|
||||||
<div class="bubble-history-tooltip-row">
|
<div class="bubble-history-tooltip-row">
|
||||||
<span class="bubble-history-tooltip-dot" style="background:${series.color};"></span>
|
<span class="bubble-history-tooltip-dot" style="background:${series.color};"></span>
|
||||||
@@ -581,31 +619,68 @@ unique_bubble_graph:
|
|||||||
`;
|
`;
|
||||||
}).join("");
|
}).join("");
|
||||||
|
|
||||||
const firstNearest = findNearestPoint(usableSeries[0].points, targetTime);
|
const firstNearest = nearestBySeries[0]?.nearest;
|
||||||
const tooltipTime = firstNearest ? firstNearest.t : targetTime;
|
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 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 `
|
||||||
|
<span
|
||||||
|
class="bubble-history-point-marker"
|
||||||
|
style="left:${pointX}px;top:${pointY}px;background:${series.color};"
|
||||||
|
></span>
|
||||||
|
`;
|
||||||
|
}).join("");
|
||||||
|
|
||||||
tooltipElement.innerHTML = `
|
tooltipElement.innerHTML = `
|
||||||
${rows}
|
${rows}
|
||||||
<div style="opacity:0.75;margin-top:3px;">${formatTime(tooltipTime)}</div>
|
<div style="opacity:0.75;margin-top:3px;">${formatTime(tooltipTime)}</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
tooltipElement.style.display = "block";
|
tooltipElement.style.display = "block";
|
||||||
markerElement.style.display = "block";
|
|
||||||
|
|
||||||
tooltipElement.style.left = `${safeX}px`;
|
tooltipElement.style.left = `${safeX}px`;
|
||||||
tooltipElement.style.top = `${host.__bubbleHistoryTooltipTop}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 = () => {
|
host.onmouseleave = () => {
|
||||||
const tooltipElement = host.__bubbleHistoryTooltip;
|
const tooltipElement = host.__bubbleHistoryTooltip;
|
||||||
const markerElement = host.__bubbleHistoryMarker;
|
const markerElement = host.__bubbleHistoryMarker;
|
||||||
|
const pointLayerElement = host.__bubbleHistoryPointLayer;
|
||||||
|
|
||||||
if (tooltipElement) tooltipElement.style.display = "none";
|
if (tooltipElement) tooltipElement.style.display = "none";
|
||||||
if (markerElement) markerElement.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 globalMaxTime = Math.max(...usableSeries.map((series) => series.points[series.points.length - 1].t));
|
||||||
const sameScaleRange = sameScale ? getSameScaleRange(usableSeries) : null;
|
const sameScaleRange = sameScale ? getSameScaleRange(usableSeries) : null;
|
||||||
|
|
||||||
const buildPath = (series) => {
|
const getValueRange = (series) => {
|
||||||
let minValue;
|
|
||||||
let maxValue;
|
|
||||||
|
|
||||||
if (sameScaleRange) {
|
if (sameScaleRange) {
|
||||||
minValue = sameScaleRange.minValue;
|
return {
|
||||||
maxValue = sameScaleRange.maxValue;
|
minValue: sameScaleRange.minValue,
|
||||||
} else {
|
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);
|
|
||||||
|
|
||||||
minValue = rawMinValue - valuePadding;
|
|
||||||
maxValue = rawMaxValue + valuePadding;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 timeRange = globalMaxTime - globalMinTime || 1;
|
||||||
const valueRange = maxValue - minValue || 1;
|
const valueRange = maxValue - minValue || 1;
|
||||||
|
|
||||||
@@ -685,7 +774,7 @@ unique_bubble_graph:
|
|||||||
.join(" ");
|
.join(" ");
|
||||||
};
|
};
|
||||||
|
|
||||||
const paths = usableSeries.map((series, index) => {
|
const paths = renderSeries.map((series, index) => {
|
||||||
const linePath = buildPath(series);
|
const linePath = buildPath(series);
|
||||||
const areaPath = `${linePath} L ${width} ${height} L 0 ${height} Z`;
|
const areaPath = `${linePath} L ${width} ${height} L 0 ${height} Z`;
|
||||||
|
|
||||||
@@ -714,7 +803,7 @@ unique_bubble_graph:
|
|||||||
</svg>
|
</svg>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
host.__bubbleHistorySeries = usableSeries;
|
host.__bubbleHistorySeries = renderSeries;
|
||||||
host.__bubbleHistoryMinTime = globalMinTime;
|
host.__bubbleHistoryMinTime = globalMinTime;
|
||||||
host.__bubbleHistoryMaxTime = globalMaxTime;
|
host.__bubbleHistoryMaxTime = globalMaxTime;
|
||||||
attachTooltipOnce();
|
attachTooltipOnce();
|
||||||
@@ -1197,6 +1286,19 @@ unique_bubble_graph:
|
|||||||
label: Tooltip anzeigen
|
label: Tooltip anzeigen
|
||||||
selector:
|
selector:
|
||||||
boolean: null
|
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
|
- name: tooltip_top
|
||||||
label: Tooltip Position oben
|
label: Tooltip Position oben
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
Reference in New Issue
Block a user