Bump ChartJS to version 4 (#15531)

This commit is contained in:
Bram Kragten 2023-08-30 15:27:24 +02:00 committed by GitHub
parent e06bd41b5e
commit 24dd45c8cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 334 additions and 104 deletions

View File

@ -103,7 +103,7 @@
"@webcomponents/scoped-custom-element-registry": "0.0.9",
"@webcomponents/webcomponentsjs": "2.8.0",
"app-datepicker": "5.1.1",
"chart.js": "3.3.2",
"chart.js": "4.3.3",
"comlink": "4.4.1",
"core-js": "3.32.1",
"cropperjs": "1.6.0",

View File

@ -15,13 +15,20 @@ import { HomeAssistant } from "../../types";
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;
left: string;
}
@customElement("ha-chart-base")
export default class HaChartBase extends LitElement {
export class HaChartBase extends LitElement {
public chart?: Chart;
@property({ attribute: false }) public hass!: HomeAssistant;
@ -45,14 +52,6 @@ export default class HaChartBase extends LitElement {
@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() {
this._releaseCanvas();
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() {
this._setupChart();
this.data.datasets.forEach((dataset, index) => {
@ -80,14 +109,11 @@ export default class HaChartBase extends LitElement {
if (!this.hasUpdated || !this.chart) {
return;
}
if (changedProps.has("plugins")) {
if (changedProps.has("plugins") || changedProps.has("chartType")) {
this.chart.destroy();
this._setupChart();
return;
}
if (changedProps.has("chartType")) {
this.chart.config.type = this.chartType;
}
if (changedProps.has("data")) {
if (this._hiddenDatasets.size) {
this.data.datasets.forEach((dataset, index) => {
@ -131,55 +157,70 @@ export default class HaChartBase extends LitElement {
</div>`
: ""}
<div
class="chartContainer"
class="animationContainer"
style=${styleMap({
height: `${this.height ?? this._chartHeight}px`,
height: `${this.height || this._chartHeight || 0}px`,
overflow: this._chartHeight ? "initial" : "hidden",
"padding-left": `${computeRTL(this.hass) ? 0 : this.paddingYAxis}px`,
"padding-right": `${computeRTL(this.hass) ? this.paddingYAxis : 0}px`,
})}
>
<canvas></canvas>
${this._tooltip
? html`<div
class="chartTooltip ${classMap({ [this._tooltip.yAlign]: true })}"
style=${styleMap({
top: this._tooltip.top,
left: this._tooltip.left,
})}
>
<div class="title">${this._tooltip.title}</div>
${this._tooltip.beforeBody
? html`<div class="beforeBody">
${this._tooltip.beforeBody}
</div>`
: ""}
<div>
<ul>
${this._tooltip.body.map(
(item, i) =>
html`<li>
<div
class="bullet"
style=${styleMap({
backgroundColor: this._tooltip!.labelColors[i]
.backgroundColor as string,
borderColor: this._tooltip!.labelColors[i]
.borderColor as string,
})}
></div>
${item.lines.join("\n")}
</li>`
)}
</ul>
</div>
${this._tooltip.footer.length
? html`<div class="footer">
${this._tooltip.footer.map((item) => html`${item}<br />`)}
</div>`
: ""}
</div>`
: ""}
<div
class="chartContainer"
style=${styleMap({
height: `${
this.height ?? this._chartHeight ?? this.clientWidth / 2
}px`,
"padding-left": `${
computeRTL(this.hass) ? 0 : this.paddingYAxis
}px`,
"padding-right": `${
computeRTL(this.hass) ? this.paddingYAxis : 0
}px`,
})}
>
<canvas></canvas>
${this._tooltip
? html`<div
class="chartTooltip ${classMap({
[this._tooltip.yAlign]: true,
})}"
style=${styleMap({
top: this._tooltip.top,
left: this._tooltip.left,
})}
>
<div class="title">${this._tooltip.title}</div>
${this._tooltip.beforeBody
? html`<div class="beforeBody">
${this._tooltip.beforeBody}
</div>`
: ""}
<div>
<ul>
${this._tooltip.body.map(
(item, i) =>
html`<li>
<div
class="bullet"
style=${styleMap({
backgroundColor: this._tooltip!.labelColors[i]
.backgroundColor as string,
borderColor: this._tooltip!.labelColors[i]
.borderColor as string,
})}
></div>
${item.lines.join("\n")}
</li>`
)}
</ul>
</div>
${this._tooltip.footer.length
? html`<div class="footer">
${this._tooltip.footer.map((item) => html`${item}<br />`)}
</div>`
: ""}
</div>`
: ""}
</div>
</div>
`;
}
@ -213,6 +254,7 @@ export default class HaChartBase extends LitElement {
private _createOptions() {
return {
maintainAspectRatio: false,
...this.options,
plugins: {
...this.options?.plugins,
@ -233,10 +275,10 @@ export default class HaChartBase extends LitElement {
return [
...(this.plugins || []),
{
id: "afterRenderHook",
afterRender: (chart) => {
id: "resizeHook",
resize: (chart) => {
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
this._chartHeight = chart.height;
}
@ -288,21 +330,13 @@ export default class HaChartBase extends LitElement {
};
}
public updateChart = (
mode:
| "resize"
| "reset"
| "none"
| "hide"
| "show"
| "normal"
| "active"
| undefined
): void => {
private _releaseCanvas() {
// release the canvas memory to prevent
// safari from running out of memory.
if (this.chart) {
this.chart.update(mode);
this.chart.destroy();
}
};
}
static get styles(): CSSResultGroup {
return css`
@ -310,11 +344,14 @@ export default class HaChartBase extends LitElement {
display: block;
position: var(--chart-base-position, relative);
}
.chartContainer {
.animationContainer {
overflow: hidden;
height: 0;
transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1);
}
.chartContainer {
position: relative;
}
canvas {
max-height: var(--chart-max-height, 400px);
}

View File

@ -1,6 +1,6 @@
import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
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 { fireEvent } from "../../common/dom/fire_event";
import { computeRTL } from "../../common/util/compute_rtl";
@ -11,14 +11,18 @@ import {
} from "../../common/number/format_number";
import { LineChartEntity, LineChartState } from "../../data/history";
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 parsed = parseFloat(value);
return isFinite(parsed) ? parsed : null;
};
class StateHistoryChartLine extends LitElement {
export class StateHistoryChartLine extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public data: LineChartEntity[] = [];
@ -47,6 +51,12 @@ class StateHistoryChartLine extends LitElement {
private _chartTime: Date = new Date();
@query("ha-chart-base") private _chart?: HaChartBase;
public resize = (options?: ChartResizeOptions): void => {
this._chart?.resize(options);
};
protected render() {
return html`
<ha-chart-base

View File

@ -1,6 +1,6 @@
import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
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 millisecondsToDuration from "../../common/datetime/milliseconds_to_duration";
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 { TimelineEntity } from "../../data/history";
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 { computeTimelineColor } from "./timeline-chart/timeline-color";
@ -46,6 +50,12 @@ export class StateHistoryChartTimeline extends LitElement {
private _chartTime: Date = new Date();
@query("ha-chart-base") private _chart?: HaChartBase;
public resize = (options?: ChartResizeOptions): void => {
this._chart?.resize(options);
};
protected render() {
return html`
<ha-chart-base

View File

@ -6,7 +6,13 @@ import {
nothing,
PropertyValues,
} 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 { restoreScroll } from "../../common/decorators/restore-scroll";
import {
@ -18,6 +24,9 @@ import { loadVirtualizer } from "../../resources/virtualizer";
import type { HomeAssistant } from "../../types";
import "./state-history-chart-line";
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
@ -75,6 +84,16 @@ export class StateHistoryCharts extends LitElement {
// @ts-ignore
@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() {
if (!isComponentLoaded(this.hass, "history")) {
return html`<div class="info">

View File

@ -12,7 +12,7 @@ import {
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, property, state, query } from "lit/decorators";
import memoizeOne from "memoize-one";
import { getGraphColorByIndex } from "../../common/color/colors";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
@ -31,6 +31,7 @@ import {
} from "../../data/recorder";
import type { HomeAssistant } from "../../types";
import "./ha-chart-base";
import type { ChartResizeOptions, HaChartBase } from "./ha-chart-base";
export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
mean: "mean",
@ -42,7 +43,7 @@ export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
};
@customElement("statistics-chart")
class StatisticsChart extends LitElement {
export class StatisticsChart extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public statisticsData?: Statistics;
@ -75,8 +76,14 @@ class StatisticsChart extends LitElement {
@state() private _chartOptions?: ChartOptions;
@query("ha-chart-base") private _chart?: HaChartBase;
private _computedStyle?: CSSStyleDeclaration;
public resize = (options?: ChartResizeOptions): void => {
this._chart?.resize(options);
};
protected shouldUpdate(changedProps: PropertyValues): boolean {
return changedProps.size > 1 || !changedProps.has("hass");
}

View File

@ -1,3 +1,8 @@
import type {
BarControllerChartOptions,
BarControllerDatasetOptions,
} from "chart.js";
export interface TimeLineData {
start: Date;
end: Date;

View File

@ -16,7 +16,7 @@ export interface TextBaroptions extends BarOptions {
export class TextBarElement extends BarElement {
static id = "textbar";
draw(ctx) {
draw(ctx: CanvasRenderingContext2D) {
super.draw(ctx);
const options = this.options as TextBaroptions;
const { x, y, base, width, text } = (

View File

@ -2,6 +2,95 @@ import { BarController, BarElement } from "chart.js";
import { TimeLineData } from "./const";
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) {
const startValue = vScale.parse(entry.start, i);
const endValue = vScale.parse(entry.end, i);
@ -97,7 +186,7 @@ export class TimelineController extends BarController {
bars: BarElement[],
start: 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 iScale = this._cachedMeta.iScale!;
@ -114,15 +203,15 @@ export class TimelineController extends BarController {
for (let index = start; index < start + count; index++) {
const data = dataset.data[index] as TimeLineData;
// @ts-ignore
const y = vScale.getPixelForValue(this.index);
// @ts-ignore
const xStart = iScale.getPixelForValue(data.start.getTime());
// @ts-ignore
const xEnd = iScale.getPixelForValue(data.end.getTime());
const width = xEnd - xStart;
const parsed = this.getParsed(index);
const stack = (parsed._stacks || {})[vScale.axis];
const height = 10;
const properties: TextBarProps = {
@ -145,7 +234,10 @@ export class TimelineController extends BarController {
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);
}
}

View File

@ -10,8 +10,8 @@ import {
mdiPencilOutline,
} from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, nothing, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { LitElement, PropertyValues, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { cache } from "lit/directives/cache";
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../common/dom/fire_event";
@ -38,15 +38,17 @@ import { haStyleDialog } from "../../resources/styles";
import "../../state-summary/state-card-content";
import { HomeAssistant } from "../../types";
import {
computeShowHistoryComponent,
computeShowLogBookComponent,
DOMAINS_WITH_MORE_INFO,
EDITABLE_DOMAINS_WITH_ID,
EDITABLE_DOMAINS_WITH_UNIQUE_ID,
computeShowHistoryComponent,
computeShowLogBookComponent,
} from "./const";
import "./controls/more-info-default";
import "./ha-more-info-history-and-logbook";
import type { MoreInfoHistoryAndLogbook } from "./ha-more-info-history-and-logbook";
import "./ha-more-info-info";
import type { MoreInfoInfo } from "./ha-more-info-info";
import "./ha-more-info-settings";
import "./more-info-content";
@ -91,6 +93,9 @@ export class MoreInfoDialog extends LitElement {
@state() private _infoEditMode = false;
@query("ha-more-info-info, ha-more-info-history-and-logbook")
private _history?: MoreInfoInfo | MoreInfoHistoryAndLogbook;
public showDialog(params: MoreInfoDialogParams) {
this._entityId = params.entityId;
if (!this._entityId) {
@ -263,6 +268,7 @@ export class MoreInfoDialog extends LitElement {
<ha-dialog
open
@closed=${this.closeDialog}
@opened=${this._handleOpened}
.heading=${title}
hideActions
flexContent
@ -485,6 +491,10 @@ export class MoreInfoDialog extends LitElement {
this.large = !this.large;
}
private _handleOpened() {
this._history?.resize({ aspectRatio: 2 });
}
static get styles() {
return [
haStyleDialog,

View File

@ -1,11 +1,13 @@
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 {
computeShowHistoryComponent,
computeShowLogBookComponent,
} from "./const";
import "./ha-more-info-history";
import type { MoreInfoHistory } from "./ha-more-info-history";
import "./ha-more-info-logbook";
@customElement("ha-more-info-history-and-logbook")
@ -14,6 +16,13 @@ export class MoreInfoHistoryAndLogbook extends LitElement {
@property() public entityId!: string;
@query("ha-more-info-history")
private _history?: MoreInfoHistory;
public resize(options?: ChartResizeOptions) {
this._history?.resize(options);
}
protected render() {
return html`
${computeShowHistoryComponent(this.hass, this.entityId)

View File

@ -1,11 +1,12 @@
import { startOfYesterday, subHours } from "date-fns/esm";
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 { fireEvent } from "../../common/dom/fire_event";
import { computeDomain } from "../../common/entity/compute_domain";
import { createSearchParam } from "../../common/url/search-params";
import "../../components/chart/state-history-charts";
import type { StateHistoryCharts } from "../../components/chart/state-history-charts";
import "../../components/chart/statistics-chart";
import {
computeHistory,
@ -20,6 +21,8 @@ import {
StatisticsTypes,
} from "../../data/recorder";
import { HomeAssistant } from "../../types";
import type { StatisticsChart } from "../../components/chart/statistics-chart";
import { ChartResizeOptions } from "../../components/chart/ha-chart-base";
declare global {
interface HASSDomEvents {
@ -51,12 +54,22 @@ export class MoreInfoHistory extends LitElement {
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() {
if (!this.entityId) {
return nothing;
}
return html` ${isComponentLoaded(this.hass, "history")
return html`${isComponentLoaded(this.hass, "history")
? html`<div class="header">
<div class="title">
${this.hass.localize("ui.dialogs.more_info_control.history")}

View File

@ -1,7 +1,8 @@
import { HassEntity } from "home-assistant-js-websocket";
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 { ChartResizeOptions } from "../../components/chart/ha-chart-base";
import { ExtEntityRegistryEntry } from "../../data/entity_registry";
import type { HomeAssistant } from "../../types";
import {
@ -12,6 +13,7 @@ import {
DOMAINS_WITH_MORE_INFO,
} from "./const";
import "./ha-more-info-history";
import type { MoreInfoHistory } from "./ha-more-info-history";
import "./ha-more-info-logbook";
import "./more-info-content";
@ -25,6 +27,13 @@ export class MoreInfoInfo extends LitElement {
@property({ attribute: false }) public editMode?: boolean;
@query("ha-more-info-history")
private _history?: MoreInfoHistory;
public resize(options?: ChartResizeOptions) {
this._history?.resize(options);
}
protected render() {
const entityId = this.entityId;
const stateObj = this.hass.states[entityId] as HassEntity | undefined;

View File

@ -19,7 +19,7 @@ import {
numberFormatToLocale,
} from "../../../../common/number/format_number";
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 { EnergyData, getEnergyDataCollection } from "../../../../data/energy";
import {

View File

@ -2039,6 +2039,13 @@ __metadata:
languageName: node
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":
version: 2.0.4
resolution: "@leichtgewicht/ip-codec@npm:2.0.4"
@ -6570,10 +6577,12 @@ __metadata:
languageName: node
linkType: hard
"chart.js@npm:3.3.2":
version: 3.3.2
resolution: "chart.js@npm:3.3.2"
checksum: 0aaebab52cb5eacfc969210f7c304cad7e20b03a37b33f4e0332b5eeb9319e2929b085e0a8654ec33752566a55982ffe6bb3f2ba620c2a9b3bc71806ca7b9cb7
"chart.js@npm:4.3.3":
version: 4.3.3
resolution: "chart.js@npm:4.3.3"
dependencies:
"@kurkle/color": ^0.3.0
checksum: 548605fc0a0a64fdc591a41159a2be86c1b408339d6e57b566c04dc157331b003db285a6139801d1700596acde8f0521bc6a156da29388025581619db6600073
languageName: node
linkType: hard
@ -9720,7 +9729,7 @@ __metadata:
babel-loader: 9.1.3
babel-plugin-template-html-minifier: 4.1.0
chai: 4.3.8
chart.js: 3.3.2
chart.js: 4.3.3
comlink: 4.4.1
core-js: 3.32.1
cropperjs: 1.6.0