Show sankey chart in vertical layout on mobile (#26439)

* Show sankey chart in vertical layout on mobile

* ts fix
This commit is contained in:
Petar Petrov 2025-08-08 16:37:44 +03:00 committed by GitHub
parent 49c7dad6eb
commit a7db401b62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 84 additions and 37 deletions

View File

@ -387,24 +387,25 @@ export class HaChartBase extends LitElement {
lastTipX = e.x; lastTipX = e.x;
lastTipY = e.y; lastTipY = e.y;
this.chart?.setOption({ this.chart?.setOption({
xAxis: ensureArray(this.chart?.getOption().xAxis as any).map( xAxis: ensureArray(
(axis: XAXisOption) => (this.chart?.getOption().xAxis as any) ?? []
axis.show ).map((axis: XAXisOption) =>
? { axis.show
...axis, ? {
axisPointer: { ...axis,
...axis.axisPointer, axisPointer: {
status: "show", ...axis.axisPointer,
handle: { status: "show",
color: style.getPropertyValue("primary-color"), handle: {
margin: 0, color: style.getPropertyValue("primary-color"),
size: 20, margin: 0,
...axis.axisPointer?.handle, size: 20,
show: true, ...axis.axisPointer?.handle,
}, show: true,
}, },
} },
: axis }
: axis
), ),
}); });
}); });
@ -417,21 +418,22 @@ export class HaChartBase extends LitElement {
return; return;
} }
this.chart?.setOption({ this.chart?.setOption({
xAxis: ensureArray(this.chart?.getOption().xAxis as any).map( xAxis: ensureArray(
(axis: XAXisOption) => (this.chart?.getOption().xAxis as any) ?? []
axis.show ).map((axis: XAXisOption) =>
? { axis.show
...axis, ? {
axisPointer: { ...axis,
...axis.axisPointer, axisPointer: {
handle: { ...axis.axisPointer,
...axis.axisPointer?.handle, handle: {
show: false, ...axis.axisPointer?.handle,
}, show: false,
status: "hide",
}, },
} status: "hide",
: axis },
}
: axis
), ),
}); });
this.chart?.dispatchAction({ this.chart?.dispatchAction({

View File

@ -186,14 +186,16 @@ export class HaSankeyChart extends LitElement {
"" ""
); );
const wordWidth = measureTextWidth(longestWord, FONT_SIZE); const wordWidth = measureTextWidth(longestWord, FONT_SIZE);
const availableWidth = params.rect.width + 6;
const fontSize = Math.min( const fontSize = Math.min(
FONT_SIZE, FONT_SIZE,
(params.rect.width / wordWidth) * FONT_SIZE (availableWidth / wordWidth) * FONT_SIZE
); );
return { return {
fontSize: fontSize > 1 ? fontSize : 0, fontSize: fontSize > 1 ? fontSize : 0,
width: params.rect.width, width: availableWidth,
align: "center", align: "center",
dy: -2, // shift up or the lowest row labels may be cut off
}; };
} }

View File

@ -0,0 +1,34 @@
import type { LitElement } from "lit";
import { state } from "lit/decorators";
import type { Constructor } from "../types";
import { isMobileClient } from "../util/is_mobile";
import { listenMediaQuery } from "../common/dom/media_query";
export const MobileAwareMixin = <T extends Constructor<LitElement>>(
superClass: T
) => {
class MobileAwareClass extends superClass {
@state() protected _isMobileSize = false;
protected _isMobileClient = isMobileClient;
private _unsubMql?: () => void;
public connectedCallback() {
super.connectedCallback();
this._unsubMql = listenMediaQuery(
"all and (max-width: 450px), all and (max-height: 500px)",
(matches) => {
this._isMobileSize = matches;
}
);
}
public disconnectedCallback() {
super.disconnectedCallback();
this._unsubMql?.();
this._unsubMql = undefined;
}
}
return MobileAwareClass;
};

View File

@ -23,6 +23,7 @@ import type { Link, Node } from "../../../../components/chart/ha-sankey-chart";
import { getGraphColorByIndex } from "../../../../common/color/colors"; import { getGraphColorByIndex } from "../../../../common/color/colors";
import { formatNumber } from "../../../../common/number/format_number"; import { formatNumber } from "../../../../common/number/format_number";
import { getEntityContext } from "../../../../common/entity/context/get_entity_context"; import { getEntityContext } from "../../../../common/entity/context/get_entity_context";
import { MobileAwareMixin } from "../../../../mixins/mobile-aware-mixin";
const DEFAULT_CONFIG: Partial<EnergySankeyCardConfig> = { const DEFAULT_CONFIG: Partial<EnergySankeyCardConfig> = {
group_by_floor: true, group_by_floor: true,
@ -31,7 +32,7 @@ const DEFAULT_CONFIG: Partial<EnergySankeyCardConfig> = {
@customElement("hui-energy-sankey-card") @customElement("hui-energy-sankey-card")
class HuiEnergySankeyCard class HuiEnergySankeyCard
extends SubscribeMixin(LitElement) extends SubscribeMixin(MobileAwareMixin(LitElement))
implements LovelaceCard implements LovelaceCard
{ {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@ -70,7 +71,11 @@ class HuiEnergySankeyCard
} }
protected shouldUpdate(changedProps: PropertyValues): boolean { protected shouldUpdate(changedProps: PropertyValues): boolean {
return changedProps.has("_config") || changedProps.has("_data"); return (
changedProps.has("_config") ||
changedProps.has("_data") ||
changedProps.has("_isMobileSize")
);
} }
protected render() { protected render() {
@ -373,13 +378,17 @@ class HuiEnergySankeyCard
const hasData = nodes.some((node) => node.value > 0); const hasData = nodes.some((node) => node.value > 0);
const vertical =
this._config.layout === "vertical" ||
(this._config.layout !== "horizontal" && this._isMobileSize);
return html` return html`
<ha-card .header=${this._config.title}> <ha-card .header=${this._config.title}>
<div class="card-content"> <div class="card-content">
${hasData ${hasData
? html`<ha-sankey-chart ? html`<ha-sankey-chart
.data=${{ nodes, links }} .data=${{ nodes, links }}
.vertical=${this._config.layout === "vertical"} .vertical=${vertical}
.valueFormatter=${this._valueFormatter} .valueFormatter=${this._valueFormatter}
></ha-sankey-chart>` ></ha-sankey-chart>`
: html`${this.hass.localize( : html`${this.hass.localize(

View File

@ -213,7 +213,7 @@ export interface EnergyCarbonGaugeCardConfig extends EnergyCardBaseConfig {
export interface EnergySankeyCardConfig extends EnergyCardBaseConfig { export interface EnergySankeyCardConfig extends EnergyCardBaseConfig {
type: "energy-sankey"; type: "energy-sankey";
title?: string; title?: string;
layout?: "vertical" | "horizontal"; layout?: "vertical" | "horizontal" | "auto";
group_by_floor?: boolean; group_by_floor?: boolean;
group_by_area?: boolean; group_by_area?: boolean;
} }