mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-26 10:46:35 +00:00
Bump ChartJS to version 4 (#15531)
This commit is contained in:
parent
e06bd41b5e
commit
24dd45c8cd
@ -103,7 +103,7 @@
|
|||||||
"@webcomponents/scoped-custom-element-registry": "0.0.9",
|
"@webcomponents/scoped-custom-element-registry": "0.0.9",
|
||||||
"@webcomponents/webcomponentsjs": "2.8.0",
|
"@webcomponents/webcomponentsjs": "2.8.0",
|
||||||
"app-datepicker": "5.1.1",
|
"app-datepicker": "5.1.1",
|
||||||
"chart.js": "3.3.2",
|
"chart.js": "4.3.3",
|
||||||
"comlink": "4.4.1",
|
"comlink": "4.4.1",
|
||||||
"core-js": "3.32.1",
|
"core-js": "3.32.1",
|
||||||
"cropperjs": "1.6.0",
|
"cropperjs": "1.6.0",
|
||||||
|
@ -15,13 +15,20 @@ import { HomeAssistant } from "../../types";
|
|||||||
|
|
||||||
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
|
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
|
||||||
|
|
||||||
interface Tooltip extends TooltipModel<any> {
|
export interface ChartResizeOptions {
|
||||||
|
aspectRatio?: number;
|
||||||
|
height?: number;
|
||||||
|
width?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Tooltip
|
||||||
|
extends Omit<TooltipModel<any>, "tooltipPosition" | "hasValue" | "getProps"> {
|
||||||
top: string;
|
top: string;
|
||||||
left: string;
|
left: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-chart-base")
|
@customElement("ha-chart-base")
|
||||||
export default class HaChartBase extends LitElement {
|
export class HaChartBase extends LitElement {
|
||||||
public chart?: Chart;
|
public chart?: Chart;
|
||||||
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -45,14 +52,6 @@ export default class HaChartBase extends LitElement {
|
|||||||
|
|
||||||
@state() private _hiddenDatasets: Set<number> = new Set();
|
@state() private _hiddenDatasets: Set<number> = new Set();
|
||||||
|
|
||||||
private _releaseCanvas() {
|
|
||||||
// release the canvas memory to prevent
|
|
||||||
// safari from running out of memory.
|
|
||||||
if (this.chart) {
|
|
||||||
this.chart.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public disconnectedCallback() {
|
public disconnectedCallback() {
|
||||||
this._releaseCanvas();
|
this._releaseCanvas();
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
@ -65,6 +64,36 @@ export default class HaChartBase extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public updateChart = (
|
||||||
|
mode:
|
||||||
|
| "resize"
|
||||||
|
| "reset"
|
||||||
|
| "none"
|
||||||
|
| "hide"
|
||||||
|
| "show"
|
||||||
|
| "default"
|
||||||
|
| "active"
|
||||||
|
| undefined
|
||||||
|
): void => {
|
||||||
|
this.chart?.update(mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
public resize = (options?: ChartResizeOptions): void => {
|
||||||
|
if (options?.aspectRatio && !options.height) {
|
||||||
|
options.height = Math.round(
|
||||||
|
(options.width ?? this.clientWidth) / options.aspectRatio
|
||||||
|
);
|
||||||
|
} else if (options?.aspectRatio && !options.width) {
|
||||||
|
options.width = Math.round(
|
||||||
|
(options.height ?? this.clientHeight) * options.aspectRatio
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.chart?.resize(
|
||||||
|
options?.width ?? this.clientWidth,
|
||||||
|
options?.height ?? this.clientHeight
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
protected firstUpdated() {
|
protected firstUpdated() {
|
||||||
this._setupChart();
|
this._setupChart();
|
||||||
this.data.datasets.forEach((dataset, index) => {
|
this.data.datasets.forEach((dataset, index) => {
|
||||||
@ -80,14 +109,11 @@ export default class HaChartBase extends LitElement {
|
|||||||
if (!this.hasUpdated || !this.chart) {
|
if (!this.hasUpdated || !this.chart) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (changedProps.has("plugins")) {
|
if (changedProps.has("plugins") || changedProps.has("chartType")) {
|
||||||
this.chart.destroy();
|
this.chart.destroy();
|
||||||
this._setupChart();
|
this._setupChart();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (changedProps.has("chartType")) {
|
|
||||||
this.chart.config.type = this.chartType;
|
|
||||||
}
|
|
||||||
if (changedProps.has("data")) {
|
if (changedProps.has("data")) {
|
||||||
if (this._hiddenDatasets.size) {
|
if (this._hiddenDatasets.size) {
|
||||||
this.data.datasets.forEach((dataset, index) => {
|
this.data.datasets.forEach((dataset, index) => {
|
||||||
@ -130,19 +156,33 @@ export default class HaChartBase extends LitElement {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>`
|
</div>`
|
||||||
: ""}
|
: ""}
|
||||||
|
<div
|
||||||
|
class="animationContainer"
|
||||||
|
style=${styleMap({
|
||||||
|
height: `${this.height || this._chartHeight || 0}px`,
|
||||||
|
overflow: this._chartHeight ? "initial" : "hidden",
|
||||||
|
})}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="chartContainer"
|
class="chartContainer"
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
height: `${this.height ?? this._chartHeight}px`,
|
height: `${
|
||||||
overflow: this._chartHeight ? "initial" : "hidden",
|
this.height ?? this._chartHeight ?? this.clientWidth / 2
|
||||||
"padding-left": `${computeRTL(this.hass) ? 0 : this.paddingYAxis}px`,
|
}px`,
|
||||||
"padding-right": `${computeRTL(this.hass) ? this.paddingYAxis : 0}px`,
|
"padding-left": `${
|
||||||
|
computeRTL(this.hass) ? 0 : this.paddingYAxis
|
||||||
|
}px`,
|
||||||
|
"padding-right": `${
|
||||||
|
computeRTL(this.hass) ? this.paddingYAxis : 0
|
||||||
|
}px`,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<canvas></canvas>
|
<canvas></canvas>
|
||||||
${this._tooltip
|
${this._tooltip
|
||||||
? html`<div
|
? html`<div
|
||||||
class="chartTooltip ${classMap({ [this._tooltip.yAlign]: true })}"
|
class="chartTooltip ${classMap({
|
||||||
|
[this._tooltip.yAlign]: true,
|
||||||
|
})}"
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
top: this._tooltip.top,
|
top: this._tooltip.top,
|
||||||
left: this._tooltip.left,
|
left: this._tooltip.left,
|
||||||
@ -181,6 +221,7 @@ export default class HaChartBase extends LitElement {
|
|||||||
</div>`
|
</div>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,6 +254,7 @@ export default class HaChartBase extends LitElement {
|
|||||||
|
|
||||||
private _createOptions() {
|
private _createOptions() {
|
||||||
return {
|
return {
|
||||||
|
maintainAspectRatio: false,
|
||||||
...this.options,
|
...this.options,
|
||||||
plugins: {
|
plugins: {
|
||||||
...this.options?.plugins,
|
...this.options?.plugins,
|
||||||
@ -233,10 +275,10 @@ export default class HaChartBase extends LitElement {
|
|||||||
return [
|
return [
|
||||||
...(this.plugins || []),
|
...(this.plugins || []),
|
||||||
{
|
{
|
||||||
id: "afterRenderHook",
|
id: "resizeHook",
|
||||||
afterRender: (chart) => {
|
resize: (chart) => {
|
||||||
const change = chart.height - (this._chartHeight ?? 0);
|
const change = chart.height - (this._chartHeight ?? 0);
|
||||||
if (!this._chartHeight || change > 0 || change < -12) {
|
if (!this._chartHeight || change > 12 || change < -12) {
|
||||||
// hysteresis to prevent infinite render loops
|
// hysteresis to prevent infinite render loops
|
||||||
this._chartHeight = chart.height;
|
this._chartHeight = chart.height;
|
||||||
}
|
}
|
||||||
@ -288,21 +330,13 @@ export default class HaChartBase extends LitElement {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateChart = (
|
private _releaseCanvas() {
|
||||||
mode:
|
// release the canvas memory to prevent
|
||||||
| "resize"
|
// safari from running out of memory.
|
||||||
| "reset"
|
|
||||||
| "none"
|
|
||||||
| "hide"
|
|
||||||
| "show"
|
|
||||||
| "normal"
|
|
||||||
| "active"
|
|
||||||
| undefined
|
|
||||||
): void => {
|
|
||||||
if (this.chart) {
|
if (this.chart) {
|
||||||
this.chart.update(mode);
|
this.chart.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
@ -310,11 +344,14 @@ export default class HaChartBase extends LitElement {
|
|||||||
display: block;
|
display: block;
|
||||||
position: var(--chart-base-position, relative);
|
position: var(--chart-base-position, relative);
|
||||||
}
|
}
|
||||||
.chartContainer {
|
.animationContainer {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 0;
|
height: 0;
|
||||||
transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
.chartContainer {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
canvas {
|
canvas {
|
||||||
max-height: var(--chart-max-height, 400px);
|
max-height: var(--chart-max-height, 400px);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
||||||
import { html, LitElement, PropertyValues } from "lit";
|
import { html, LitElement, PropertyValues } from "lit";
|
||||||
import { property, state } from "lit/decorators";
|
import { property, query, state } from "lit/decorators";
|
||||||
import { getGraphColorByIndex } from "../../common/color/colors";
|
import { getGraphColorByIndex } from "../../common/color/colors";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
@ -11,14 +11,18 @@ import {
|
|||||||
} from "../../common/number/format_number";
|
} from "../../common/number/format_number";
|
||||||
import { LineChartEntity, LineChartState } from "../../data/history";
|
import { LineChartEntity, LineChartState } from "../../data/history";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
|
import {
|
||||||
|
ChartResizeOptions,
|
||||||
|
HaChartBase,
|
||||||
|
MIN_TIME_BETWEEN_UPDATES,
|
||||||
|
} from "./ha-chart-base";
|
||||||
|
|
||||||
const safeParseFloat = (value) => {
|
const safeParseFloat = (value) => {
|
||||||
const parsed = parseFloat(value);
|
const parsed = parseFloat(value);
|
||||||
return isFinite(parsed) ? parsed : null;
|
return isFinite(parsed) ? parsed : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StateHistoryChartLine extends LitElement {
|
export class StateHistoryChartLine extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public data: LineChartEntity[] = [];
|
@property({ attribute: false }) public data: LineChartEntity[] = [];
|
||||||
@ -47,6 +51,12 @@ class StateHistoryChartLine extends LitElement {
|
|||||||
|
|
||||||
private _chartTime: Date = new Date();
|
private _chartTime: Date = new Date();
|
||||||
|
|
||||||
|
@query("ha-chart-base") private _chart?: HaChartBase;
|
||||||
|
|
||||||
|
public resize = (options?: ChartResizeOptions): void => {
|
||||||
|
this._chart?.resize(options);
|
||||||
|
};
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<ha-chart-base
|
<ha-chart-base
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
||||||
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
||||||
import millisecondsToDuration from "../../common/datetime/milliseconds_to_duration";
|
import millisecondsToDuration from "../../common/datetime/milliseconds_to_duration";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
@ -8,7 +8,11 @@ import { numberFormatToLocale } from "../../common/number/format_number";
|
|||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
import { TimelineEntity } from "../../data/history";
|
import { TimelineEntity } from "../../data/history";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
|
import {
|
||||||
|
ChartResizeOptions,
|
||||||
|
HaChartBase,
|
||||||
|
MIN_TIME_BETWEEN_UPDATES,
|
||||||
|
} from "./ha-chart-base";
|
||||||
import type { TimeLineData } from "./timeline-chart/const";
|
import type { TimeLineData } from "./timeline-chart/const";
|
||||||
import { computeTimelineColor } from "./timeline-chart/timeline-color";
|
import { computeTimelineColor } from "./timeline-chart/timeline-color";
|
||||||
|
|
||||||
@ -46,6 +50,12 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
|
|
||||||
private _chartTime: Date = new Date();
|
private _chartTime: Date = new Date();
|
||||||
|
|
||||||
|
@query("ha-chart-base") private _chart?: HaChartBase;
|
||||||
|
|
||||||
|
public resize = (options?: ChartResizeOptions): void => {
|
||||||
|
this._chart?.resize(options);
|
||||||
|
};
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<ha-chart-base
|
<ha-chart-base
|
||||||
|
@ -6,7 +6,13 @@ import {
|
|||||||
nothing,
|
nothing,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, eventOptions, property, state } from "lit/decorators";
|
import {
|
||||||
|
customElement,
|
||||||
|
eventOptions,
|
||||||
|
property,
|
||||||
|
queryAll,
|
||||||
|
state,
|
||||||
|
} from "lit/decorators";
|
||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||||
import {
|
import {
|
||||||
@ -18,6 +24,9 @@ import { loadVirtualizer } from "../../resources/virtualizer";
|
|||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "./state-history-chart-line";
|
import "./state-history-chart-line";
|
||||||
import "./state-history-chart-timeline";
|
import "./state-history-chart-timeline";
|
||||||
|
import type { StateHistoryChartLine } from "./state-history-chart-line";
|
||||||
|
import type { StateHistoryChartTimeline } from "./state-history-chart-timeline";
|
||||||
|
import { ChartResizeOptions } from "./ha-chart-base";
|
||||||
|
|
||||||
const CANVAS_TIMELINE_ROWS_CHUNK = 10; // Split up the canvases to avoid hitting the render limit
|
const CANVAS_TIMELINE_ROWS_CHUNK = 10; // Split up the canvases to avoid hitting the render limit
|
||||||
|
|
||||||
@ -75,6 +84,16 @@ export class StateHistoryCharts extends LitElement {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@restoreScroll(".container") private _savedScrollPos?: number;
|
@restoreScroll(".container") private _savedScrollPos?: number;
|
||||||
|
|
||||||
|
@queryAll("state-history-chart-line, state-history-chart-timeline")
|
||||||
|
private _charts?: StateHistoryChartLine[] | StateHistoryChartTimeline[];
|
||||||
|
|
||||||
|
public resize = (options?: ChartResizeOptions): void => {
|
||||||
|
this._charts?.forEach(
|
||||||
|
(chart: StateHistoryChartLine | StateHistoryChartTimeline) =>
|
||||||
|
chart.resize(options)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!isComponentLoaded(this.hass, "history")) {
|
if (!isComponentLoaded(this.hass, "history")) {
|
||||||
return html`<div class="info">
|
return html`<div class="info">
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state, query } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { getGraphColorByIndex } from "../../common/color/colors";
|
import { getGraphColorByIndex } from "../../common/color/colors";
|
||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
@ -31,6 +31,7 @@ import {
|
|||||||
} from "../../data/recorder";
|
} from "../../data/recorder";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "./ha-chart-base";
|
import "./ha-chart-base";
|
||||||
|
import type { ChartResizeOptions, HaChartBase } from "./ha-chart-base";
|
||||||
|
|
||||||
export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
|
export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
|
||||||
mean: "mean",
|
mean: "mean",
|
||||||
@ -42,7 +43,7 @@ export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@customElement("statistics-chart")
|
@customElement("statistics-chart")
|
||||||
class StatisticsChart extends LitElement {
|
export class StatisticsChart extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public statisticsData?: Statistics;
|
@property({ attribute: false }) public statisticsData?: Statistics;
|
||||||
@ -75,8 +76,14 @@ class StatisticsChart extends LitElement {
|
|||||||
|
|
||||||
@state() private _chartOptions?: ChartOptions;
|
@state() private _chartOptions?: ChartOptions;
|
||||||
|
|
||||||
|
@query("ha-chart-base") private _chart?: HaChartBase;
|
||||||
|
|
||||||
private _computedStyle?: CSSStyleDeclaration;
|
private _computedStyle?: CSSStyleDeclaration;
|
||||||
|
|
||||||
|
public resize = (options?: ChartResizeOptions): void => {
|
||||||
|
this._chart?.resize(options);
|
||||||
|
};
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
return changedProps.size > 1 || !changedProps.has("hass");
|
return changedProps.size > 1 || !changedProps.has("hass");
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
import type {
|
||||||
|
BarControllerChartOptions,
|
||||||
|
BarControllerDatasetOptions,
|
||||||
|
} from "chart.js";
|
||||||
|
|
||||||
export interface TimeLineData {
|
export interface TimeLineData {
|
||||||
start: Date;
|
start: Date;
|
||||||
end: Date;
|
end: Date;
|
||||||
|
@ -16,7 +16,7 @@ export interface TextBaroptions extends BarOptions {
|
|||||||
export class TextBarElement extends BarElement {
|
export class TextBarElement extends BarElement {
|
||||||
static id = "textbar";
|
static id = "textbar";
|
||||||
|
|
||||||
draw(ctx) {
|
draw(ctx: CanvasRenderingContext2D) {
|
||||||
super.draw(ctx);
|
super.draw(ctx);
|
||||||
const options = this.options as TextBaroptions;
|
const options = this.options as TextBaroptions;
|
||||||
const { x, y, base, width, text } = (
|
const { x, y, base, width, text } = (
|
||||||
|
@ -2,6 +2,95 @@ import { BarController, BarElement } from "chart.js";
|
|||||||
import { TimeLineData } from "./const";
|
import { TimeLineData } from "./const";
|
||||||
import { TextBarProps } from "./textbar-element";
|
import { TextBarProps } from "./textbar-element";
|
||||||
|
|
||||||
|
function borderProps(properties) {
|
||||||
|
let reverse;
|
||||||
|
let start;
|
||||||
|
let end;
|
||||||
|
let top;
|
||||||
|
let bottom;
|
||||||
|
if (properties.horizontal) {
|
||||||
|
reverse = properties.base > properties.x;
|
||||||
|
start = "left";
|
||||||
|
end = "right";
|
||||||
|
} else {
|
||||||
|
reverse = properties.base < properties.y;
|
||||||
|
start = "bottom";
|
||||||
|
end = "top";
|
||||||
|
}
|
||||||
|
if (reverse) {
|
||||||
|
top = "end";
|
||||||
|
bottom = "start";
|
||||||
|
} else {
|
||||||
|
top = "start";
|
||||||
|
bottom = "end";
|
||||||
|
}
|
||||||
|
return { start, end, reverse, top, bottom };
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBorderSkipped(properties, options, stack, index) {
|
||||||
|
let edge = options.borderSkipped;
|
||||||
|
const res = {};
|
||||||
|
|
||||||
|
if (!edge) {
|
||||||
|
properties.borderSkipped = res;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edge === true) {
|
||||||
|
properties.borderSkipped = {
|
||||||
|
top: true,
|
||||||
|
right: true,
|
||||||
|
bottom: true,
|
||||||
|
left: true,
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { start, end, reverse, top, bottom } = borderProps(properties);
|
||||||
|
|
||||||
|
if (edge === "middle" && stack) {
|
||||||
|
properties.enableBorderRadius = true;
|
||||||
|
if ((stack._top || 0) === index) {
|
||||||
|
edge = top;
|
||||||
|
} else if ((stack._bottom || 0) === index) {
|
||||||
|
edge = bottom;
|
||||||
|
} else {
|
||||||
|
res[parseEdge(bottom, start, end, reverse)] = true;
|
||||||
|
edge = top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res[parseEdge(edge, start, end, reverse)] = true;
|
||||||
|
properties.borderSkipped = res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEdge(edge, a, b, reverse) {
|
||||||
|
if (reverse) {
|
||||||
|
edge = swap(edge, a, b);
|
||||||
|
edge = startEnd(edge, b, a);
|
||||||
|
} else {
|
||||||
|
edge = startEnd(edge, a, b);
|
||||||
|
}
|
||||||
|
return edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
function swap(orig, v1, v2) {
|
||||||
|
return orig === v1 ? v2 : orig === v2 ? v1 : orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startEnd(v, start, end) {
|
||||||
|
return v === "start" ? start : v === "end" ? end : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setInflateAmount(
|
||||||
|
properties,
|
||||||
|
{ inflateAmount }: { inflateAmount?: string | number },
|
||||||
|
ratio
|
||||||
|
) {
|
||||||
|
properties.inflateAmount =
|
||||||
|
inflateAmount === "auto" ? (ratio === 1 ? 0.33 : 0) : inflateAmount;
|
||||||
|
}
|
||||||
|
|
||||||
function parseValue(entry, item, vScale, i) {
|
function parseValue(entry, item, vScale, i) {
|
||||||
const startValue = vScale.parse(entry.start, i);
|
const startValue = vScale.parse(entry.start, i);
|
||||||
const endValue = vScale.parse(entry.end, i);
|
const endValue = vScale.parse(entry.end, i);
|
||||||
@ -97,7 +186,7 @@ export class TimelineController extends BarController {
|
|||||||
bars: BarElement[],
|
bars: BarElement[],
|
||||||
start: number,
|
start: number,
|
||||||
count: number,
|
count: number,
|
||||||
mode: "reset" | "resize" | "none" | "hide" | "show" | "normal" | "active"
|
mode: "reset" | "resize" | "none" | "hide" | "show" | "default" | "active"
|
||||||
) {
|
) {
|
||||||
const vScale = this._cachedMeta.vScale!;
|
const vScale = this._cachedMeta.vScale!;
|
||||||
const iScale = this._cachedMeta.iScale!;
|
const iScale = this._cachedMeta.iScale!;
|
||||||
@ -114,15 +203,15 @@ export class TimelineController extends BarController {
|
|||||||
for (let index = start; index < start + count; index++) {
|
for (let index = start; index < start + count; index++) {
|
||||||
const data = dataset.data[index] as TimeLineData;
|
const data = dataset.data[index] as TimeLineData;
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const y = vScale.getPixelForValue(this.index);
|
const y = vScale.getPixelForValue(this.index);
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const xStart = iScale.getPixelForValue(data.start.getTime());
|
const xStart = iScale.getPixelForValue(data.start.getTime());
|
||||||
// @ts-ignore
|
|
||||||
const xEnd = iScale.getPixelForValue(data.end.getTime());
|
const xEnd = iScale.getPixelForValue(data.end.getTime());
|
||||||
const width = xEnd - xStart;
|
const width = xEnd - xStart;
|
||||||
|
|
||||||
|
const parsed = this.getParsed(index);
|
||||||
|
const stack = (parsed._stacks || {})[vScale.axis];
|
||||||
|
|
||||||
const height = 10;
|
const height = 10;
|
||||||
|
|
||||||
const properties: TextBarProps = {
|
const properties: TextBarProps = {
|
||||||
@ -145,7 +234,10 @@ export class TimelineController extends BarController {
|
|||||||
backgroundColor: data.color,
|
backgroundColor: data.color,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const options = properties.options || bars[index].options;
|
||||||
|
|
||||||
|
setBorderSkipped(properties, options, stack, index);
|
||||||
|
setInflateAmount(properties, options, 1);
|
||||||
this.updateElement(bars[index], index, properties as any, mode);
|
this.updateElement(bars[index], index, properties as any, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ import {
|
|||||||
mdiPencilOutline,
|
mdiPencilOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { css, html, LitElement, nothing, PropertyValues } from "lit";
|
import { LitElement, PropertyValues, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { cache } from "lit/directives/cache";
|
import { cache } from "lit/directives/cache";
|
||||||
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
|
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
@ -38,15 +38,17 @@ import { haStyleDialog } from "../../resources/styles";
|
|||||||
import "../../state-summary/state-card-content";
|
import "../../state-summary/state-card-content";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import {
|
import {
|
||||||
computeShowHistoryComponent,
|
|
||||||
computeShowLogBookComponent,
|
|
||||||
DOMAINS_WITH_MORE_INFO,
|
DOMAINS_WITH_MORE_INFO,
|
||||||
EDITABLE_DOMAINS_WITH_ID,
|
EDITABLE_DOMAINS_WITH_ID,
|
||||||
EDITABLE_DOMAINS_WITH_UNIQUE_ID,
|
EDITABLE_DOMAINS_WITH_UNIQUE_ID,
|
||||||
|
computeShowHistoryComponent,
|
||||||
|
computeShowLogBookComponent,
|
||||||
} from "./const";
|
} from "./const";
|
||||||
import "./controls/more-info-default";
|
import "./controls/more-info-default";
|
||||||
import "./ha-more-info-history-and-logbook";
|
import "./ha-more-info-history-and-logbook";
|
||||||
|
import type { MoreInfoHistoryAndLogbook } from "./ha-more-info-history-and-logbook";
|
||||||
import "./ha-more-info-info";
|
import "./ha-more-info-info";
|
||||||
|
import type { MoreInfoInfo } from "./ha-more-info-info";
|
||||||
import "./ha-more-info-settings";
|
import "./ha-more-info-settings";
|
||||||
import "./more-info-content";
|
import "./more-info-content";
|
||||||
|
|
||||||
@ -91,6 +93,9 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
|
|
||||||
@state() private _infoEditMode = false;
|
@state() private _infoEditMode = false;
|
||||||
|
|
||||||
|
@query("ha-more-info-info, ha-more-info-history-and-logbook")
|
||||||
|
private _history?: MoreInfoInfo | MoreInfoHistoryAndLogbook;
|
||||||
|
|
||||||
public showDialog(params: MoreInfoDialogParams) {
|
public showDialog(params: MoreInfoDialogParams) {
|
||||||
this._entityId = params.entityId;
|
this._entityId = params.entityId;
|
||||||
if (!this._entityId) {
|
if (!this._entityId) {
|
||||||
@ -263,6 +268,7 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
<ha-dialog
|
<ha-dialog
|
||||||
open
|
open
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
|
@opened=${this._handleOpened}
|
||||||
.heading=${title}
|
.heading=${title}
|
||||||
hideActions
|
hideActions
|
||||||
flexContent
|
flexContent
|
||||||
@ -485,6 +491,10 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
this.large = !this.large;
|
this.large = !this.large;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleOpened() {
|
||||||
|
this._history?.resize({ aspectRatio: 2 });
|
||||||
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import { ChartResizeOptions } from "../../components/chart/ha-chart-base";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import {
|
import {
|
||||||
computeShowHistoryComponent,
|
computeShowHistoryComponent,
|
||||||
computeShowLogBookComponent,
|
computeShowLogBookComponent,
|
||||||
} from "./const";
|
} from "./const";
|
||||||
import "./ha-more-info-history";
|
import "./ha-more-info-history";
|
||||||
|
import type { MoreInfoHistory } from "./ha-more-info-history";
|
||||||
import "./ha-more-info-logbook";
|
import "./ha-more-info-logbook";
|
||||||
|
|
||||||
@customElement("ha-more-info-history-and-logbook")
|
@customElement("ha-more-info-history-and-logbook")
|
||||||
@ -14,6 +16,13 @@ export class MoreInfoHistoryAndLogbook extends LitElement {
|
|||||||
|
|
||||||
@property() public entityId!: string;
|
@property() public entityId!: string;
|
||||||
|
|
||||||
|
@query("ha-more-info-history")
|
||||||
|
private _history?: MoreInfoHistory;
|
||||||
|
|
||||||
|
public resize(options?: ChartResizeOptions) {
|
||||||
|
this._history?.resize(options);
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
${computeShowHistoryComponent(this.hass, this.entityId)
|
${computeShowHistoryComponent(this.hass, this.entityId)
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { startOfYesterday, subHours } from "date-fns/esm";
|
import { startOfYesterday, subHours } from "date-fns/esm";
|
||||||
import { css, html, LitElement, PropertyValues, nothing } from "lit";
|
import { css, html, LitElement, PropertyValues, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state, query } from "lit/decorators";
|
||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
import { createSearchParam } from "../../common/url/search-params";
|
import { createSearchParam } from "../../common/url/search-params";
|
||||||
import "../../components/chart/state-history-charts";
|
import "../../components/chart/state-history-charts";
|
||||||
|
import type { StateHistoryCharts } from "../../components/chart/state-history-charts";
|
||||||
import "../../components/chart/statistics-chart";
|
import "../../components/chart/statistics-chart";
|
||||||
import {
|
import {
|
||||||
computeHistory,
|
computeHistory,
|
||||||
@ -20,6 +21,8 @@ import {
|
|||||||
StatisticsTypes,
|
StatisticsTypes,
|
||||||
} from "../../data/recorder";
|
} from "../../data/recorder";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
|
import type { StatisticsChart } from "../../components/chart/statistics-chart";
|
||||||
|
import { ChartResizeOptions } from "../../components/chart/ha-chart-base";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
@ -51,12 +54,22 @@ export class MoreInfoHistory extends LitElement {
|
|||||||
|
|
||||||
private _metadata?: Record<string, StatisticsMetaData>;
|
private _metadata?: Record<string, StatisticsMetaData>;
|
||||||
|
|
||||||
|
@query("statistics-chart, state-history-charts") private _chart?:
|
||||||
|
| StateHistoryCharts
|
||||||
|
| StatisticsChart;
|
||||||
|
|
||||||
|
public resize = (options?: ChartResizeOptions): void => {
|
||||||
|
if (this._chart) {
|
||||||
|
this._chart.resize(options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.entityId) {
|
if (!this.entityId) {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html` ${isComponentLoaded(this.hass, "history")
|
return html`${isComponentLoaded(this.hass, "history")
|
||||||
? html`<div class="header">
|
? html`<div class="header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
${this.hass.localize("ui.dialogs.more_info_control.history")}
|
${this.hass.localize("ui.dialogs.more_info_control.history")}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
|
import { ChartResizeOptions } from "../../components/chart/ha-chart-base";
|
||||||
import { ExtEntityRegistryEntry } from "../../data/entity_registry";
|
import { ExtEntityRegistryEntry } from "../../data/entity_registry";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import {
|
import {
|
||||||
@ -12,6 +13,7 @@ import {
|
|||||||
DOMAINS_WITH_MORE_INFO,
|
DOMAINS_WITH_MORE_INFO,
|
||||||
} from "./const";
|
} from "./const";
|
||||||
import "./ha-more-info-history";
|
import "./ha-more-info-history";
|
||||||
|
import type { MoreInfoHistory } from "./ha-more-info-history";
|
||||||
import "./ha-more-info-logbook";
|
import "./ha-more-info-logbook";
|
||||||
import "./more-info-content";
|
import "./more-info-content";
|
||||||
|
|
||||||
@ -25,6 +27,13 @@ export class MoreInfoInfo extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public editMode?: boolean;
|
@property({ attribute: false }) public editMode?: boolean;
|
||||||
|
|
||||||
|
@query("ha-more-info-history")
|
||||||
|
private _history?: MoreInfoHistory;
|
||||||
|
|
||||||
|
public resize(options?: ChartResizeOptions) {
|
||||||
|
this._history?.resize(options);
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const entityId = this.entityId;
|
const entityId = this.entityId;
|
||||||
const stateObj = this.hass.states[entityId] as HassEntity | undefined;
|
const stateObj = this.hass.states[entityId] as HassEntity | undefined;
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
numberFormatToLocale,
|
numberFormatToLocale,
|
||||||
} from "../../../../common/number/format_number";
|
} from "../../../../common/number/format_number";
|
||||||
import "../../../../components/chart/ha-chart-base";
|
import "../../../../components/chart/ha-chart-base";
|
||||||
import type HaChartBase from "../../../../components/chart/ha-chart-base";
|
import type { HaChartBase } from "../../../../components/chart/ha-chart-base";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import { EnergyData, getEnergyDataCollection } from "../../../../data/energy";
|
import { EnergyData, getEnergyDataCollection } from "../../../../data/energy";
|
||||||
import {
|
import {
|
||||||
|
19
yarn.lock
19
yarn.lock
@ -2039,6 +2039,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@kurkle/color@npm:^0.3.0":
|
||||||
|
version: 0.3.2
|
||||||
|
resolution: "@kurkle/color@npm:0.3.2"
|
||||||
|
checksum: 79e97b31f8f6efb28c69d373f94b0c7480226fe8ec95221f518ac998e156444a496727ce47de6d728eb5c3369288e794cba82cae34253deb0d472d3bfe080e49
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@leichtgewicht/ip-codec@npm:^2.0.1":
|
"@leichtgewicht/ip-codec@npm:^2.0.1":
|
||||||
version: 2.0.4
|
version: 2.0.4
|
||||||
resolution: "@leichtgewicht/ip-codec@npm:2.0.4"
|
resolution: "@leichtgewicht/ip-codec@npm:2.0.4"
|
||||||
@ -6570,10 +6577,12 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"chart.js@npm:3.3.2":
|
"chart.js@npm:4.3.3":
|
||||||
version: 3.3.2
|
version: 4.3.3
|
||||||
resolution: "chart.js@npm:3.3.2"
|
resolution: "chart.js@npm:4.3.3"
|
||||||
checksum: 0aaebab52cb5eacfc969210f7c304cad7e20b03a37b33f4e0332b5eeb9319e2929b085e0a8654ec33752566a55982ffe6bb3f2ba620c2a9b3bc71806ca7b9cb7
|
dependencies:
|
||||||
|
"@kurkle/color": ^0.3.0
|
||||||
|
checksum: 548605fc0a0a64fdc591a41159a2be86c1b408339d6e57b566c04dc157331b003db285a6139801d1700596acde8f0521bc6a156da29388025581619db6600073
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -9720,7 +9729,7 @@ __metadata:
|
|||||||
babel-loader: 9.1.3
|
babel-loader: 9.1.3
|
||||||
babel-plugin-template-html-minifier: 4.1.0
|
babel-plugin-template-html-minifier: 4.1.0
|
||||||
chai: 4.3.8
|
chai: 4.3.8
|
||||||
chart.js: 3.3.2
|
chart.js: 4.3.3
|
||||||
comlink: 4.4.1
|
comlink: 4.4.1
|
||||||
core-js: 3.32.1
|
core-js: 3.32.1
|
||||||
cropperjs: 1.6.0
|
cropperjs: 1.6.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user