mirror of
https://github.com/home-assistant/frontend.git
synced 2026-05-07 18:03:29 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db5cf12fe3 |
@@ -1,40 +0,0 @@
|
||||
import type { TooltipPositionCallback } from "echarts/types/dist/shared";
|
||||
|
||||
export const TOOLTIP_GAP_PX = 12;
|
||||
export const TOOLTIP_TOP_OFFSET_PX = 10;
|
||||
|
||||
/**
|
||||
* Pins the tooltip near the top of the chart and offsets it horizontally
|
||||
* from the cursor so it never covers the data point being inspected.
|
||||
* For axis-trigger time-series tooltips where the cursor's Y is uncorrelated
|
||||
* with the displayed content.
|
||||
*/
|
||||
export const sideTooltipPosition: TooltipPositionCallback = (
|
||||
point,
|
||||
_params,
|
||||
dom,
|
||||
_rect,
|
||||
size
|
||||
) => {
|
||||
const [cursorX] = point;
|
||||
const [viewW, viewH] = size.viewSize;
|
||||
const [tipW, tipH] = size.contentSize;
|
||||
|
||||
const rtl =
|
||||
dom instanceof HTMLElement && getComputedStyle(dom).direction === "rtl";
|
||||
|
||||
const rightOfCursor = cursorX + TOOLTIP_GAP_PX;
|
||||
const leftOfCursor = cursorX - TOOLTIP_GAP_PX - tipW;
|
||||
|
||||
let x = rtl ? leftOfCursor : rightOfCursor;
|
||||
const overflowsRight = x + tipW > viewW;
|
||||
const overflowsLeft = x < 0;
|
||||
if (overflowsRight || overflowsLeft) {
|
||||
x = rtl ? rightOfCursor : leftOfCursor;
|
||||
}
|
||||
x = Math.max(0, Math.min(x, viewW - tipW));
|
||||
|
||||
const y = Math.max(0, Math.min(TOOLTIP_TOP_OFFSET_PX, viewH - tipH));
|
||||
|
||||
return [x, y];
|
||||
};
|
||||
@@ -11,7 +11,6 @@ import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import type { LineChartEntity, LineChartState } from "../../data/history";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
|
||||
import { sideTooltipPosition } from "./chart-tooltip-position";
|
||||
import type { ECOption } from "../../resources/echarts/echarts";
|
||||
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
||||
import {
|
||||
@@ -411,7 +410,8 @@ export class StateHistoryChartLine extends LitElement {
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
renderMode: "html",
|
||||
position: sideTooltipPosition,
|
||||
position: "bottom",
|
||||
align: "center",
|
||||
confine: true,
|
||||
formatter: this._renderTooltip,
|
||||
},
|
||||
|
||||
@@ -14,7 +14,6 @@ import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import type { TimelineEntity } from "../../data/history";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
|
||||
import { sideTooltipPosition } from "./chart-tooltip-position";
|
||||
import { computeTimelineColor } from "./timeline-color";
|
||||
import type { ECOption } from "../../resources/echarts/echarts";
|
||||
import echarts from "../../resources/echarts/echarts";
|
||||
@@ -257,7 +256,8 @@ export class StateHistoryChartTimeline extends LitElement {
|
||||
},
|
||||
tooltip: {
|
||||
renderMode: "html",
|
||||
position: sideTooltipPosition,
|
||||
position: "bottom",
|
||||
align: "center",
|
||||
confine: true,
|
||||
formatter: this._renderTooltip,
|
||||
},
|
||||
|
||||
@@ -37,7 +37,6 @@ import type { HomeAssistant } from "../../types";
|
||||
import { getPeriodicAxisLabelConfig } from "./axis-label";
|
||||
import type { CustomLegendOption } from "./ha-chart-base";
|
||||
import "./ha-chart-base";
|
||||
import { sideTooltipPosition } from "./chart-tooltip-position";
|
||||
import { fillDataGapsAndRoundCaps } from "./round-caps";
|
||||
|
||||
export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
|
||||
@@ -399,7 +398,8 @@ export class StatisticsChart extends LitElement {
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
renderMode: "html",
|
||||
position: sideTooltipPosition,
|
||||
position: "bottom",
|
||||
align: "center",
|
||||
confine: true,
|
||||
formatter: this._renderTooltip,
|
||||
},
|
||||
|
||||
@@ -315,14 +315,18 @@ export class HuiPowerSourcesGraphCard
|
||||
typeof item === "object" && "value" in item!
|
||||
? item.value![0]
|
||||
: item![0];
|
||||
usageData[i] = [x, 0];
|
||||
let sum = 0;
|
||||
this._chartData.forEach((dataset) => {
|
||||
const y =
|
||||
typeof dataset.data![i] === "object" && "value" in dataset.data![i]!
|
||||
? dataset.data![i].value![1]
|
||||
: dataset.data![i]![1];
|
||||
usageData[i]![1] += y as number;
|
||||
sum += y as number;
|
||||
});
|
||||
// Consumption can't be negative; sources unaccounted for in the
|
||||
// configuration (e.g. solar exporting to grid without a configured
|
||||
// solar source) would otherwise drag the usage line below zero.
|
||||
usageData[i] = [x, Math.max(0, sum)];
|
||||
});
|
||||
this._chartData.push({
|
||||
...commonSeriesOptions,
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
TOOLTIP_GAP_PX,
|
||||
TOOLTIP_TOP_OFFSET_PX,
|
||||
sideTooltipPosition,
|
||||
} from "../../../src/components/chart/chart-tooltip-position";
|
||||
|
||||
const callPosition = (
|
||||
cursorX: number,
|
||||
options: {
|
||||
viewSize?: [number, number];
|
||||
contentSize?: [number, number];
|
||||
rtl?: boolean;
|
||||
} = {}
|
||||
) => {
|
||||
const dom = document.createElement("div");
|
||||
if (options.rtl) {
|
||||
dom.setAttribute("dir", "rtl");
|
||||
document.body.appendChild(dom);
|
||||
}
|
||||
const result = sideTooltipPosition([cursorX, 0], [], dom, null, {
|
||||
viewSize: options.viewSize ?? [800, 400],
|
||||
contentSize: options.contentSize ?? [200, 120],
|
||||
}) as [number, number];
|
||||
if (options.rtl) {
|
||||
document.body.removeChild(dom);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
describe("sideTooltipPosition", () => {
|
||||
it("places tooltip to the right of the cursor by default", () => {
|
||||
const [x, y] = callPosition(100);
|
||||
expect(x).toBe(100 + TOOLTIP_GAP_PX);
|
||||
expect(y).toBe(TOOLTIP_TOP_OFFSET_PX);
|
||||
});
|
||||
|
||||
it("flips to the left when right side overflows the chart", () => {
|
||||
const [x] = callPosition(700, {
|
||||
viewSize: [800, 400],
|
||||
contentSize: [200, 120],
|
||||
});
|
||||
expect(x).toBe(700 - TOOLTIP_GAP_PX - 200);
|
||||
});
|
||||
|
||||
it("clamps to chart bounds when neither side fits", () => {
|
||||
const [x] = callPosition(50, {
|
||||
viewSize: [120, 400],
|
||||
contentSize: [200, 120],
|
||||
});
|
||||
expect(x).toBe(0);
|
||||
});
|
||||
|
||||
it("clamps Y when chart is shorter than the tooltip", () => {
|
||||
const [, y] = callPosition(100, {
|
||||
viewSize: [800, 100],
|
||||
contentSize: [200, 120],
|
||||
});
|
||||
expect(y).toBe(0);
|
||||
});
|
||||
|
||||
it("prefers the left of the cursor in RTL mode", () => {
|
||||
const [x] = callPosition(400, { rtl: true });
|
||||
expect(x).toBe(400 - TOOLTIP_GAP_PX - 200);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user