ha-frontend/src/panels/lovelace/components/hui-energy-period-selector.ts

359 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import "@material/mwc-button/mwc-button";
import { mdiCompare, mdiCompareRemove } from "@mdi/js";
import {
addDays,
addMonths,
addWeeks,
addYears,
differenceInDays,
endOfDay,
endOfMonth,
endOfToday,
endOfWeek,
endOfYear,
isWithinInterval,
startOfDay,
startOfMonth,
startOfToday,
startOfWeek,
startOfYear,
} from "date-fns/esm";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import {
formatDate,
formatDateMonthYear,
formatDateShort,
formatDateYear,
} from "../../../common/datetime/format_date";
import { toggleAttribute } from "../../../common/dom/toggle_attribute";
import "../../../components/ha-button-toggle-group";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-button-prev";
import "../../../components/ha-icon-button-next";
import { EnergyData, getEnergyDataCollection } from "../../../data/energy";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { HomeAssistant, ToggleButton } from "../../../types";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { firstWeekdayIndex } from "../../../common/datetime/first_weekday";
@customElement("hui-energy-period-selector")
export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public collectionKey?: string;
@property({ type: Boolean, reflect: true }) public narrow = false;
@state() _startDate?: Date;
@state() _endDate?: Date;
@state() private _period?: "day" | "week" | "month" | "year";
@state() private _compare = false;
public connectedCallback() {
super.connectedCallback();
toggleAttribute(this, "narrow", this.offsetWidth < 600);
}
public hassSubscribe(): UnsubscribeFunc[] {
return [
getEnergyDataCollection(this.hass, {
key: this.collectionKey,
}).subscribe((data) => this._updateDates(data)),
];
}
protected render(): TemplateResult {
if (!this.hass || !this._startDate) {
return html``;
}
const viewButtons: ToggleButton[] = [
{
label: this.hass.localize(
"ui.panel.lovelace.components.energy_period_selector.day"
),
value: "day",
},
{
label: this.hass.localize(
"ui.panel.lovelace.components.energy_period_selector.week"
),
value: "week",
},
{
label: this.hass.localize(
"ui.panel.lovelace.components.energy_period_selector.month"
),
value: "month",
},
{
label: this.hass.localize(
"ui.panel.lovelace.components.energy_period_selector.year"
),
value: "year",
},
];
return html`
<div class="row">
<div class="label">
${this._period === "day"
? formatDate(this._startDate, this.hass.locale)
: this._period === "month"
? formatDateMonthYear(this._startDate, this.hass.locale)
: this._period === "year"
? formatDateYear(this._startDate, this.hass.locale)
: `${formatDateShort(
this._startDate,
this.hass.locale
)} ${formatDateShort(
this._endDate || new Date(),
this.hass.locale
)}`}
<ha-icon-button-prev
.label=${this.hass.localize(
"ui.panel.lovelace.components.energy_period_selector.previous"
)}
@click=${this._pickPrevious}
></ha-icon-button-prev>
<ha-icon-button-next
.label=${this.hass.localize(
"ui.panel.lovelace.components.energy_period_selector.next"
)}
@click=${this._pickNext}
></ha-icon-button-next>
<mwc-button dense outlined @click=${this._pickToday}>
${this.hass.localize(
"ui.panel.lovelace.components.energy_period_selector.today"
)}
</mwc-button>
</div>
<div class="period">
<ha-button-toggle-group
.buttons=${viewButtons}
.active=${this._period}
dense
@value-changed=${this._handleView}
.dir=${computeRTLDirection(this.hass)}
></ha-button-toggle-group>
${this.narrow
? html`<ha-icon-button
class="compare ${this._compare ? "active" : ""}"
.path=${this._compare ? mdiCompareRemove : mdiCompare}
@click=${this._toggleCompare}
dense
outlined
>
${this.hass.localize(
"ui.panel.lovelace.components.energy_period_selector.compare"
)}
</ha-icon-button>`
: html`<mwc-button
class="compare ${this._compare ? "active" : ""}"
@click=${this._toggleCompare}
dense
outlined
>
${this.hass.localize(
"ui.panel.lovelace.components.energy_period_selector.compare"
)}
</mwc-button>`}
</div>
</div>
`;
}
private _handleView(ev: CustomEvent): void {
this._period = ev.detail.value;
const today = startOfToday();
const start =
!this._startDate ||
isWithinInterval(today, {
start: this._startDate,
end: this._endDate || endOfToday(),
})
? today
: this._startDate;
this._setDate(
this._period === "day"
? startOfDay(start)
: this._period === "week"
? startOfWeek(start, {
weekStartsOn: firstWeekdayIndex(this.hass.locale),
})
: this._period === "month"
? startOfMonth(start)
: startOfYear(start)
);
}
private _pickToday() {
this._setDate(
this._period === "day"
? startOfToday()
: this._period === "week"
? startOfWeek(new Date(), {
weekStartsOn: firstWeekdayIndex(this.hass.locale),
})
: this._period === "month"
? startOfMonth(new Date())
: startOfYear(new Date())
);
}
private _pickPrevious() {
const newStart =
this._period === "day"
? addDays(this._startDate!, -1)
: this._period === "week"
? addWeeks(this._startDate!, -1)
: this._period === "month"
? addMonths(this._startDate!, -1)
: addYears(this._startDate!, -1);
this._setDate(newStart);
}
private _pickNext() {
const newStart =
this._period === "day"
? addDays(this._startDate!, 1)
: this._period === "week"
? addWeeks(this._startDate!, 1)
: this._period === "month"
? addMonths(this._startDate!, 1)
: addYears(this._startDate!, 1);
this._setDate(newStart);
}
private _setDate(startDate: Date) {
const endDate =
this._period === "day"
? endOfDay(startDate)
: this._period === "week"
? endOfWeek(startDate, {
weekStartsOn: firstWeekdayIndex(this.hass.locale),
})
: this._period === "month"
? endOfMonth(startDate)
: endOfYear(startDate);
const energyCollection = getEnergyDataCollection(this.hass, {
key: this.collectionKey,
});
energyCollection.setPeriod(startDate, endDate);
energyCollection.refresh();
}
private _updateDates(energyData: EnergyData): void {
this._compare = energyData.startCompare !== undefined;
this._startDate = energyData.start;
this._endDate = energyData.end || endOfToday();
const dayDifference = differenceInDays(this._endDate, this._startDate);
this._period =
dayDifference < 1
? "day"
: dayDifference === 6
? "week"
: dayDifference > 26 && dayDifference < 31 // 28, 29, 30 or 31 days in a month
? "month"
: dayDifference === 364 || dayDifference === 365 // Leap year
? "year"
: undefined;
}
private _toggleCompare() {
this._compare = !this._compare;
const energyCollection = getEnergyDataCollection(this.hass, {
key: "energy_dashboard",
});
energyCollection.setCompare(this._compare);
energyCollection.refresh();
}
static get styles(): CSSResultGroup {
return css`
.row {
display: flex;
justify-content: flex-end;
}
:host([narrow]) .row {
flex-direction: column-reverse;
}
.label {
display: flex;
justify-content: flex-end;
align-items: center;
font-size: 20px;
}
.period {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
align-items: center;
}
:host([narrow]) .period {
margin-bottom: 8px;
}
mwc-button {
margin-left: 8px;
}
ha-icon-button {
margin-left: 4px;
--mdc-icon-size: 20px;
}
ha-icon-button.active::before,
mwc-button.active::before {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
background-color: currentColor;
opacity: 0;
pointer-events: none;
content: "";
transition: opacity 15ms linear, background-color 15ms linear;
opacity: var(--mdc-icon-button-ripple-opacity, 0.12);
}
ha-icon-button.active::before {
border-radius: 50%;
}
.compare {
position: relative;
}
:host {
--mdc-button-outline-color: currentColor;
--primary-color: currentColor;
--mdc-theme-primary: currentColor;
--mdc-theme-on-primary: currentColor;
--mdc-button-disabled-outline-color: var(--disabled-text-color);
--mdc-button-disabled-ink-color: var(--disabled-text-color);
--mdc-icon-button-ripple-opacity: 0.2;
}
ha-icon-button {
--mdc-icon-button-size: 28px;
}
ha-button-toggle-group {
padding-left: 8px;
padding-inline-start: 8px;
direction: var(--direction);
}
mwc-button {
flex-shrink: 0;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-energy-period-selector": HuiEnergyPeriodSelector;
}
}