359 lines
10 KiB
TypeScript
359 lines
10 KiB
TypeScript
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;
|
||
}
|
||
}
|