mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 07:16:39 +00:00
20240424.1 (#20609)
This commit is contained in:
commit
64f54d9aaa
@ -102,7 +102,7 @@
|
|||||||
"color-name": "2.0.0",
|
"color-name": "2.0.0",
|
||||||
"comlink": "4.4.1",
|
"comlink": "4.4.1",
|
||||||
"core-js": "3.37.0",
|
"core-js": "3.37.0",
|
||||||
"cropperjs": "1.6.1",
|
"cropperjs": "1.6.2",
|
||||||
"date-fns": "3.6.0",
|
"date-fns": "3.6.0",
|
||||||
"date-fns-tz": "3.1.3",
|
"date-fns-tz": "3.1.3",
|
||||||
"deep-clone-simple": "1.1.1",
|
"deep-clone-simple": "1.1.1",
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20240424.0"
|
version = "20240424.1"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -69,7 +69,7 @@ export class HaFilterDevices extends LitElement {
|
|||||||
@value-changed=${this._handleSearchChange}
|
@value-changed=${this._handleSearchChange}
|
||||||
>
|
>
|
||||||
</search-input-outlined>
|
</search-input-outlined>
|
||||||
<mwc-list class="ha-scrollbar">
|
<mwc-list class="ha-scrollbar" multi>
|
||||||
<lit-virtualizer
|
<lit-virtualizer
|
||||||
.items=${this._devices(
|
.items=${this._devices(
|
||||||
this.hass.devices,
|
this.hass.devices,
|
||||||
|
198
src/components/ha-filter-domains.ts
Normal file
198
src/components/ha-filter-domains.ts
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
import { mdiFilterVariantRemove } from "@mdi/js";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { repeat } from "lit/directives/repeat";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import { stringCompare } from "../common/string/compare";
|
||||||
|
import { domainToName } from "../data/integration";
|
||||||
|
import { haStyleScrollbar } from "../resources/styles";
|
||||||
|
import type { HomeAssistant } from "../types";
|
||||||
|
import "./ha-domain-icon";
|
||||||
|
import "./search-input-outlined";
|
||||||
|
import { computeDomain } from "../common/entity/compute_domain";
|
||||||
|
|
||||||
|
@customElement("ha-filter-domains")
|
||||||
|
export class HaFilterDomains extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public value?: string[];
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true }) public expanded = false;
|
||||||
|
|
||||||
|
@state() private _shouldRender = false;
|
||||||
|
|
||||||
|
@state() private _filter?: string;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<ha-expansion-panel
|
||||||
|
leftChevron
|
||||||
|
.expanded=${this.expanded}
|
||||||
|
@expanded-will-change=${this._expandedWillChange}
|
||||||
|
@expanded-changed=${this._expandedChanged}
|
||||||
|
>
|
||||||
|
<div slot="header" class="header">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.entities.picker.headers.domain"
|
||||||
|
)}
|
||||||
|
${this.value?.length
|
||||||
|
? html`<div class="badge">${this.value?.length}</div>
|
||||||
|
<ha-icon-button
|
||||||
|
.path=${mdiFilterVariantRemove}
|
||||||
|
@click=${this._clearFilter}
|
||||||
|
></ha-icon-button>`
|
||||||
|
: nothing}
|
||||||
|
</div>
|
||||||
|
${this._shouldRender
|
||||||
|
? html`<search-input-outlined
|
||||||
|
.hass=${this.hass}
|
||||||
|
.filter=${this._filter}
|
||||||
|
@value-changed=${this._handleSearchChange}
|
||||||
|
>
|
||||||
|
</search-input-outlined>
|
||||||
|
<mwc-list
|
||||||
|
class="ha-scrollbar"
|
||||||
|
@click=${this._handleItemClick}
|
||||||
|
multi
|
||||||
|
>
|
||||||
|
${repeat(
|
||||||
|
this._domains(this.hass.states, this._filter),
|
||||||
|
(i) => i,
|
||||||
|
(domain) =>
|
||||||
|
html`<ha-check-list-item
|
||||||
|
.value=${domain}
|
||||||
|
.selected=${(this.value || []).includes(domain)}
|
||||||
|
graphic="icon"
|
||||||
|
>
|
||||||
|
<ha-domain-icon
|
||||||
|
slot="graphic"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.domain=${domain}
|
||||||
|
brandFallback
|
||||||
|
></ha-domain-icon>
|
||||||
|
${domainToName(this.hass.localize, domain)}
|
||||||
|
</ha-check-list-item>`
|
||||||
|
)}
|
||||||
|
</mwc-list> `
|
||||||
|
: nothing}
|
||||||
|
</ha-expansion-panel>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _domains = memoizeOne((states, filter) => {
|
||||||
|
const domains = new Set<string>();
|
||||||
|
Object.keys(states).forEach((entityId) => {
|
||||||
|
domains.add(computeDomain(entityId));
|
||||||
|
});
|
||||||
|
return Array.from(domains)
|
||||||
|
.filter((domain) => !filter || domain.toLowerCase().includes(filter))
|
||||||
|
.sort((a, b) => stringCompare(a, b, this.hass.locale.language));
|
||||||
|
});
|
||||||
|
|
||||||
|
protected updated(changed) {
|
||||||
|
if (changed.has("expanded") && this.expanded) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.expanded) return;
|
||||||
|
this.renderRoot.querySelector("mwc-list")!.style.height =
|
||||||
|
`${this.clientHeight - 49 - 32}px`; // 32px is the height of the search input
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _expandedWillChange(ev) {
|
||||||
|
this._shouldRender = ev.detail.expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _expandedChanged(ev) {
|
||||||
|
this.expanded = ev.detail.expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleItemClick(ev) {
|
||||||
|
const listItem = ev.target.closest("ha-check-list-item");
|
||||||
|
const value = listItem?.value;
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.value?.includes(value)) {
|
||||||
|
this.value = this.value?.filter((val) => val !== value);
|
||||||
|
} else {
|
||||||
|
this.value = [...(this.value || []), value];
|
||||||
|
}
|
||||||
|
|
||||||
|
listItem.selected = this.value.includes(value);
|
||||||
|
|
||||||
|
fireEvent(this, "data-table-filter-changed", {
|
||||||
|
value: this.value,
|
||||||
|
items: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clearFilter(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
this.value = undefined;
|
||||||
|
fireEvent(this, "data-table-filter-changed", {
|
||||||
|
value: undefined,
|
||||||
|
items: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleSearchChange(ev: CustomEvent) {
|
||||||
|
this._filter = ev.detail.value.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyleScrollbar,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
border-bottom: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
:host([expanded]) {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
ha-expansion-panel {
|
||||||
|
--ha-card-border-radius: 0;
|
||||||
|
--expansion-panel-content-padding: 0;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.header ha-icon-button {
|
||||||
|
margin-inline-start: auto;
|
||||||
|
margin-inline-end: 8px;
|
||||||
|
}
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-inline-start: 8px;
|
||||||
|
margin-inline-end: 0;
|
||||||
|
min-width: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 11px;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
line-height: 16px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0px 2px;
|
||||||
|
color: var(--text-primary-color);
|
||||||
|
}
|
||||||
|
search-input-outlined {
|
||||||
|
display: block;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-filter-domains": HaFilterDomains;
|
||||||
|
}
|
||||||
|
}
|
@ -71,7 +71,7 @@ export class HaFilterEntities extends LitElement {
|
|||||||
@value-changed=${this._handleSearchChange}
|
@value-changed=${this._handleSearchChange}
|
||||||
>
|
>
|
||||||
</search-input-outlined>
|
</search-input-outlined>
|
||||||
<mwc-list class="ha-scrollbar">
|
<mwc-list class="ha-scrollbar" multi>
|
||||||
<lit-virtualizer
|
<lit-virtualizer
|
||||||
.items=${this._entities(
|
.items=${this._entities(
|
||||||
this.hass.states,
|
this.hass.states,
|
||||||
|
@ -55,7 +55,11 @@ export class HaFilterIntegrations extends LitElement {
|
|||||||
@value-changed=${this._handleSearchChange}
|
@value-changed=${this._handleSearchChange}
|
||||||
>
|
>
|
||||||
</search-input-outlined>
|
</search-input-outlined>
|
||||||
<mwc-list class="ha-scrollbar" @click=${this._handleItemClick}>
|
<mwc-list
|
||||||
|
class="ha-scrollbar"
|
||||||
|
@click=${this._handleItemClick}
|
||||||
|
multi
|
||||||
|
>
|
||||||
${repeat(
|
${repeat(
|
||||||
this._integrations(this._manifests, this._filter, this.value),
|
this._integrations(this._manifests, this._filter, this.value),
|
||||||
(i) => i.domain,
|
(i) => i.domain,
|
||||||
|
@ -19,7 +19,7 @@ import { customElement, property, query, state } from "lit/decorators";
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import type { LeafletModuleType } from "../../common/dom/setup-leaflet-map";
|
import type { LeafletModuleType } from "../../common/dom/setup-leaflet-map";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant, ThemeMode } from "../../types";
|
||||||
import "../ha-input-helper-text";
|
import "../ha-input-helper-text";
|
||||||
import "./ha-map";
|
import "./ha-map";
|
||||||
import type { HaMap } from "./ha-map";
|
import type { HaMap } from "./ha-map";
|
||||||
@ -61,7 +61,8 @@ export class HaLocationsEditor extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Number }) public zoom = 16;
|
@property({ type: Number }) public zoom = 16;
|
||||||
|
|
||||||
@property({ type: Boolean }) public darkMode = false;
|
@property({ attribute: "theme-mode", type: String })
|
||||||
|
public themeMode: ThemeMode = "auto";
|
||||||
|
|
||||||
@state() private _locationMarkers?: Record<string, Marker | Circle>;
|
@state() private _locationMarkers?: Record<string, Marker | Circle>;
|
||||||
|
|
||||||
@ -133,7 +134,7 @@ export class HaLocationsEditor extends LitElement {
|
|||||||
.layers=${this._getLayers(this._circles, this._locationMarkers)}
|
.layers=${this._getLayers(this._circles, this._locationMarkers)}
|
||||||
.zoom=${this.zoom}
|
.zoom=${this.zoom}
|
||||||
.autoFit=${this.autoFit}
|
.autoFit=${this.autoFit}
|
||||||
?darkMode=${this.darkMode}
|
.themeMode=${this.themeMode}
|
||||||
></ha-map>
|
></ha-map>
|
||||||
${this.helper
|
${this.helper
|
||||||
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
|
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
|
import { isToday } from "date-fns";
|
||||||
import type {
|
import type {
|
||||||
Circle,
|
Circle,
|
||||||
CircleMarker,
|
CircleMarker,
|
||||||
LatLngTuple,
|
|
||||||
LatLngExpression,
|
LatLngExpression,
|
||||||
|
LatLngTuple,
|
||||||
Layer,
|
Layer,
|
||||||
Map,
|
Map,
|
||||||
Marker,
|
Marker,
|
||||||
Polyline,
|
Polyline,
|
||||||
} from "leaflet";
|
} from "leaflet";
|
||||||
import { isToday } from "date-fns";
|
import { CSSResultGroup, PropertyValues, ReactiveElement, css } from "lit";
|
||||||
import { css, CSSResultGroup, PropertyValues, ReactiveElement } from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { formatDateTime } from "../../common/datetime/format_date_time";
|
||||||
|
import {
|
||||||
|
formatTimeWeekday,
|
||||||
|
formatTimeWithSeconds,
|
||||||
|
} from "../../common/datetime/format_time";
|
||||||
import {
|
import {
|
||||||
LeafletModuleType,
|
LeafletModuleType,
|
||||||
setupLeafletMap,
|
setupLeafletMap,
|
||||||
} from "../../common/dom/setup-leaflet-map";
|
} from "../../common/dom/setup-leaflet-map";
|
||||||
import {
|
|
||||||
formatTimeWithSeconds,
|
|
||||||
formatTimeWeekday,
|
|
||||||
} from "../../common/datetime/format_time";
|
|
||||||
import { formatDateTime } from "../../common/datetime/format_date_time";
|
|
||||||
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
||||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||||
import { loadPolyfillIfNeeded } from "../../resources/resize-observer.polyfill";
|
import { loadPolyfillIfNeeded } from "../../resources/resize-observer.polyfill";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant, ThemeMode } from "../../types";
|
||||||
|
import { isTouch } from "../../util/is_touch";
|
||||||
import "../ha-icon-button";
|
import "../ha-icon-button";
|
||||||
import "./ha-entity-marker";
|
import "./ha-entity-marker";
|
||||||
import { isTouch } from "../../util/is_touch";
|
|
||||||
|
|
||||||
const getEntityId = (entity: string | HaMapEntity): string =>
|
const getEntityId = (entity: string | HaMapEntity): string =>
|
||||||
typeof entity === "string" ? entity : entity.entity_id;
|
typeof entity === "string" ? entity : entity.entity_id;
|
||||||
@ -69,7 +69,8 @@ export class HaMap extends ReactiveElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public fitZones = false;
|
@property({ type: Boolean }) public fitZones = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public darkMode = false;
|
@property({ attribute: "theme-mode", type: String })
|
||||||
|
public themeMode: ThemeMode = "auto";
|
||||||
|
|
||||||
@property({ type: Number }) public zoom = 14;
|
@property({ type: Number }) public zoom = 14;
|
||||||
|
|
||||||
@ -154,7 +155,7 @@ export class HaMap extends ReactiveElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!changedProps.has("darkMode") &&
|
!changedProps.has("themeMode") &&
|
||||||
(!changedProps.has("hass") ||
|
(!changedProps.has("hass") ||
|
||||||
(oldHass && oldHass.themes?.darkMode === this.hass.themes?.darkMode))
|
(oldHass && oldHass.themes?.darkMode === this.hass.themes?.darkMode))
|
||||||
) {
|
) {
|
||||||
@ -163,12 +164,18 @@ export class HaMap extends ReactiveElement {
|
|||||||
this._updateMapStyle();
|
this._updateMapStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get _darkMode() {
|
||||||
|
return (
|
||||||
|
this.themeMode === "dark" ||
|
||||||
|
(this.themeMode === "auto" && Boolean(this.hass.themes.darkMode))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private _updateMapStyle(): void {
|
private _updateMapStyle(): void {
|
||||||
const darkMode = this.darkMode || (this.hass.themes.darkMode ?? false);
|
|
||||||
const forcedDark = this.darkMode;
|
|
||||||
const map = this.renderRoot.querySelector("#map");
|
const map = this.renderRoot.querySelector("#map");
|
||||||
map!.classList.toggle("dark", darkMode);
|
map!.classList.toggle("dark", this._darkMode);
|
||||||
map!.classList.toggle("forced-dark", forcedDark);
|
map!.classList.toggle("forced-dark", this.themeMode === "dark");
|
||||||
|
map!.classList.toggle("forced-light", this.themeMode === "light");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadMap(): Promise<void> {
|
private async _loadMap(): Promise<void> {
|
||||||
@ -398,8 +405,7 @@ export class HaMap extends ReactiveElement {
|
|||||||
"--dark-primary-color"
|
"--dark-primary-color"
|
||||||
);
|
);
|
||||||
|
|
||||||
const className =
|
const className = this._darkMode ? "dark" : "light";
|
||||||
this.darkMode || this.hass.themes.darkMode ? "dark" : "light";
|
|
||||||
|
|
||||||
for (const entity of this.entities) {
|
for (const entity of this.entities) {
|
||||||
const stateObj = hass.states[getEntityId(entity)];
|
const stateObj = hass.states[getEntityId(entity)];
|
||||||
@ -543,27 +549,30 @@ export class HaMap extends ReactiveElement {
|
|||||||
background: #090909;
|
background: #090909;
|
||||||
}
|
}
|
||||||
#map.forced-dark {
|
#map.forced-dark {
|
||||||
|
color: #ffffff;
|
||||||
--map-filter: invert(0.9) hue-rotate(170deg) brightness(1.5)
|
--map-filter: invert(0.9) hue-rotate(170deg) brightness(1.5)
|
||||||
contrast(1.2) saturate(0.3);
|
contrast(1.2) saturate(0.3);
|
||||||
}
|
}
|
||||||
|
#map.forced-light {
|
||||||
|
background: #ffffff;
|
||||||
|
color: #000000;
|
||||||
|
--map-filter: invert(0);
|
||||||
|
}
|
||||||
#map:active {
|
#map:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
cursor: -moz-grabbing;
|
cursor: -moz-grabbing;
|
||||||
cursor: -webkit-grabbing;
|
cursor: -webkit-grabbing;
|
||||||
}
|
}
|
||||||
.light {
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
.dark {
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
.leaflet-tile-pane {
|
.leaflet-tile-pane {
|
||||||
filter: var(--map-filter);
|
filter: var(--map-filter);
|
||||||
}
|
}
|
||||||
.dark .leaflet-bar a {
|
.dark .leaflet-bar a {
|
||||||
background-color: var(--card-background-color, #1c1c1c);
|
background-color: #1c1c1c;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
.dark .leaflet-bar a:hover {
|
||||||
|
background-color: #313131;
|
||||||
|
}
|
||||||
.leaflet-marker-draggable {
|
.leaflet-marker-draggable {
|
||||||
cursor: move !important;
|
cursor: move !important;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ import type { HomeAssistant } from "../types";
|
|||||||
import { onBoardingStyles } from "./styles";
|
import { onBoardingStyles } from "./styles";
|
||||||
|
|
||||||
const AMSTERDAM: [number, number] = [52.3731339, 4.8903147];
|
const AMSTERDAM: [number, number] = [52.3731339, 4.8903147];
|
||||||
const mql = matchMedia("(prefers-color-scheme: dark)");
|
const darkMql = matchMedia("(prefers-color-scheme: dark)");
|
||||||
const LOCATION_MARKER_ID = "location";
|
const LOCATION_MARKER_ID = "location";
|
||||||
|
|
||||||
@customElement("onboarding-location")
|
@customElement("onboarding-location")
|
||||||
@ -199,7 +199,7 @@ class OnboardingLocation extends LitElement {
|
|||||||
this._highlightedMarker
|
this._highlightedMarker
|
||||||
)}
|
)}
|
||||||
zoom="14"
|
zoom="14"
|
||||||
.darkMode=${mql.matches}
|
.themeMode=${darkMql.matches ? "dark" : "light"}
|
||||||
.disabled=${this._working}
|
.disabled=${this._working}
|
||||||
@location-updated=${this._locationChanged}
|
@location-updated=${this._locationChanged}
|
||||||
@marker-clicked=${this._markerClicked}
|
@marker-clicked=${this._markerClicked}
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
mdiPlus,
|
mdiPlus,
|
||||||
mdiRobotHappy,
|
mdiRobotHappy,
|
||||||
mdiTag,
|
mdiTag,
|
||||||
|
mdiTextureBox,
|
||||||
mdiToggleSwitch,
|
mdiToggleSwitch,
|
||||||
mdiToggleSwitchOffOutline,
|
mdiToggleSwitchOffOutline,
|
||||||
mdiTransitConnection,
|
mdiTransitConnection,
|
||||||
@ -69,6 +70,7 @@ import type { HaMenu } from "../../../components/ha-menu";
|
|||||||
import "../../../components/ha-menu-item";
|
import "../../../components/ha-menu-item";
|
||||||
import "../../../components/ha-sub-menu";
|
import "../../../components/ha-sub-menu";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
|
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||||
import {
|
import {
|
||||||
AutomationEntity,
|
AutomationEntity,
|
||||||
deleteAutomation,
|
deleteAutomation,
|
||||||
@ -106,6 +108,7 @@ import { haStyle } from "../../../resources/styles";
|
|||||||
import { HomeAssistant, Route, ServiceCallResponse } from "../../../types";
|
import { HomeAssistant, Route, ServiceCallResponse } from "../../../types";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import { turnOnOffEntity } from "../../lovelace/common/entity/turn-on-off-entity";
|
import { turnOnOffEntity } from "../../lovelace/common/entity/turn-on-off-entity";
|
||||||
|
import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry-detail";
|
||||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||||
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
@ -403,6 +406,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-menu-item>`;
|
||||||
|
|
||||||
const labelItems = html`${this._labels?.map((label) => {
|
const labelItems = html`${this._labels?.map((label) => {
|
||||||
const color = label.color ? computeCssColor(label.color) : undefined;
|
const color = label.color ? computeCssColor(label.color) : undefined;
|
||||||
const selected = this._selected.every((entityId) =>
|
const selected = this._selected.every((entityId) =>
|
||||||
@ -440,10 +444,45 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
</div></ha-menu-item
|
</div></ha-menu-item
|
||||||
>`;
|
>`;
|
||||||
|
|
||||||
const labelsInOverflow =
|
const areaItems = html`${Object.values(this.hass.areas).map(
|
||||||
(this._sizeController.value && this._sizeController.value < 700) ||
|
(area) =>
|
||||||
|
html`<ha-menu-item
|
||||||
|
.value=${area.area_id}
|
||||||
|
@click=${this._handleBulkArea}
|
||||||
|
>
|
||||||
|
${area.icon
|
||||||
|
? html`<ha-icon slot="start" .icon=${area.icon}></ha-icon>`
|
||||||
|
: html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
|
.path=${mdiTextureBox}
|
||||||
|
></ha-svg-icon>`}
|
||||||
|
<div slot="headline">${area.name}</div>
|
||||||
|
</ha-menu-item>`
|
||||||
|
)}
|
||||||
|
<ha-menu-item .value=${null} @click=${this._handleBulkArea}>
|
||||||
|
<div slot="headline">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.no_area"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-menu-item>
|
||||||
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
|
<ha-menu-item @click=${this._bulkCreateArea}>
|
||||||
|
<div slot="headline">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.add_area"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-menu-item>`;
|
||||||
|
|
||||||
|
const areasInOverflow =
|
||||||
|
(this._sizeController.value && this._sizeController.value < 900) ||
|
||||||
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
||||||
|
|
||||||
|
const labelsInOverflow =
|
||||||
|
areasInOverflow &&
|
||||||
|
(!this._sizeController.value || this._sizeController.value < 700);
|
||||||
|
|
||||||
const automations = this._automations(
|
const automations = this._automations(
|
||||||
this.automations,
|
this.automations,
|
||||||
this._entityReg,
|
this._entityReg,
|
||||||
@ -598,6 +637,22 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${labelItems}
|
${labelItems}
|
||||||
|
</ha-button-menu-new>`}
|
||||||
|
${areasInOverflow
|
||||||
|
? nothing
|
||||||
|
: html`<ha-button-menu-new slot="selection-bar">
|
||||||
|
<ha-assist-chip
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="trailing-icon"
|
||||||
|
.path=${mdiMenuDown}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-assist-chip>
|
||||||
|
${areaItems}
|
||||||
</ha-button-menu-new>`}`
|
</ha-button-menu-new>`}`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
@ -662,6 +717,24 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
|
${
|
||||||
|
this.narrow || areasInOverflow
|
||||||
|
? html`<ha-sub-menu>
|
||||||
|
<ha-menu-item slot="item">
|
||||||
|
<div slot="headline">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="end"
|
||||||
|
.path=${mdiChevronRight}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-menu-item>
|
||||||
|
<ha-menu slot="menu">${areaItems}</ha-menu>
|
||||||
|
</ha-sub-menu>`
|
||||||
|
: nothing
|
||||||
|
}
|
||||||
<ha-menu-item @click=${this._handleBulkEnable}>
|
<ha-menu-item @click=${this._handleBulkEnable}>
|
||||||
<ha-svg-icon slot="start" .path=${mdiToggleSwitch}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiToggleSwitch}></ha-svg-icon>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
@ -1191,6 +1264,46 @@ ${rejected
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _handleBulkArea(ev) {
|
||||||
|
const area = ev.currentTarget.value;
|
||||||
|
this._bulkAddArea(area);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkAddArea(area: string) {
|
||||||
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
|
this._selected.forEach((entityId) => {
|
||||||
|
promises.push(
|
||||||
|
updateEntityRegistryEntry(this.hass, entityId, {
|
||||||
|
area_id: area,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const result = await Promise.allSettled(promises);
|
||||||
|
if (hasRejectedItems(result)) {
|
||||||
|
const rejected = rejectedItems(result);
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
|
||||||
|
number: rejected.length,
|
||||||
|
}),
|
||||||
|
text: html`<pre>
|
||||||
|
${rejected
|
||||||
|
.map((r) => r.reason.message || r.reason.code || r.reason)
|
||||||
|
.join("\r\n")}</pre
|
||||||
|
>`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkCreateArea() {
|
||||||
|
showAreaRegistryDetailDialog(this, {
|
||||||
|
createEntry: async (values) => {
|
||||||
|
const area = await createAreaRegistryEntry(this.hass, values);
|
||||||
|
this._bulkAddArea(area.area_id);
|
||||||
|
return area;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async _handleBulkEnable() {
|
private async _handleBulkEnable() {
|
||||||
const promises: Promise<ServiceCallResponse>[] = [];
|
const promises: Promise<ServiceCallResponse>[] = [];
|
||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
|
@ -53,6 +53,7 @@ import "../../../components/ha-alert";
|
|||||||
import "../../../components/ha-button-menu";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-check-list-item";
|
import "../../../components/ha-check-list-item";
|
||||||
import "../../../components/ha-filter-devices";
|
import "../../../components/ha-filter-devices";
|
||||||
|
import "../../../components/ha-filter-domains";
|
||||||
import "../../../components/ha-filter-floor-areas";
|
import "../../../components/ha-filter-floor-areas";
|
||||||
import "../../../components/ha-filter-integrations";
|
import "../../../components/ha-filter-integrations";
|
||||||
import "../../../components/ha-filter-labels";
|
import "../../../components/ha-filter-labels";
|
||||||
@ -443,6 +444,10 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
entryIds.includes(entity.config_entry_id))
|
entryIds.includes(entity.config_entry_id))
|
||||||
);
|
);
|
||||||
filter.value!.forEach((domain) => filteredDomains.add(domain));
|
filter.value!.forEach((domain) => filteredDomains.add(domain));
|
||||||
|
} else if (key === "ha-filter-domains" && filter.value?.length) {
|
||||||
|
filteredEntities = filteredEntities.filter((entity) =>
|
||||||
|
filter.value?.includes(computeDomain(entity.entity_id))
|
||||||
|
);
|
||||||
} else if (key === "ha-filter-labels" && filter.value?.length) {
|
} else if (key === "ha-filter-labels" && filter.value?.length) {
|
||||||
filteredEntities = filteredEntities.filter((entity) =>
|
filteredEntities = filteredEntities.filter((entity) =>
|
||||||
entity.labels.some((lbl) => filter.value!.includes(lbl))
|
entity.labels.some((lbl) => filter.value!.includes(lbl))
|
||||||
@ -782,6 +787,15 @@ ${
|
|||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
@expanded-changed=${this._filterExpanded}
|
@expanded-changed=${this._filterExpanded}
|
||||||
></ha-filter-devices>
|
></ha-filter-devices>
|
||||||
|
<ha-filter-domains
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._filters["ha-filter-domains"]?.value}
|
||||||
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
|
slot="filter-pane"
|
||||||
|
.expanded=${this._expandedFilter === "ha-filter-domains"}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@expanded-changed=${this._filterExpanded}
|
||||||
|
></ha-filter-domains>
|
||||||
<ha-filter-integrations
|
<ha-filter-integrations
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._filters["ha-filter-integrations"]?.value}
|
.value=${this._filters["ha-filter-integrations"]?.value}
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
mdiPlay,
|
mdiPlay,
|
||||||
mdiPlus,
|
mdiPlus,
|
||||||
mdiTag,
|
mdiTag,
|
||||||
|
mdiTextureBox,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import { differenceInDays } from "date-fns";
|
import { differenceInDays } from "date-fns";
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
@ -61,6 +62,7 @@ import "../../../components/ha-menu-item";
|
|||||||
import "../../../components/ha-state-icon";
|
import "../../../components/ha-state-icon";
|
||||||
import "../../../components/ha-sub-menu";
|
import "../../../components/ha-sub-menu";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
|
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||||
import {
|
import {
|
||||||
CategoryRegistryEntry,
|
CategoryRegistryEntry,
|
||||||
createCategoryRegistryEntry,
|
createCategoryRegistryEntry,
|
||||||
@ -97,6 +99,7 @@ import { haStyle } from "../../../resources/styles";
|
|||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import { showToast } from "../../../util/toast";
|
import { showToast } from "../../../util/toast";
|
||||||
|
import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry-detail";
|
||||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||||
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
@ -406,6 +409,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-menu-item>`;
|
||||||
|
|
||||||
const labelItems = html` ${this._labels?.map((label) => {
|
const labelItems = html` ${this._labels?.map((label) => {
|
||||||
const color = label.color ? computeCssColor(label.color) : undefined;
|
const color = label.color ? computeCssColor(label.color) : undefined;
|
||||||
const selected = this._selected.every((entityId) =>
|
const selected = this._selected.every((entityId) =>
|
||||||
@ -442,9 +446,46 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-menu-item
|
||||||
>`;
|
>`;
|
||||||
const labelsInOverflow =
|
|
||||||
(this._sizeController.value && this._sizeController.value < 700) ||
|
const areaItems = html`${Object.values(this.hass.areas).map(
|
||||||
|
(area) =>
|
||||||
|
html`<ha-menu-item
|
||||||
|
.value=${area.area_id}
|
||||||
|
@click=${this._handleBulkArea}
|
||||||
|
>
|
||||||
|
${area.icon
|
||||||
|
? html`<ha-icon slot="start" .icon=${area.icon}></ha-icon>`
|
||||||
|
: html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
|
.path=${mdiTextureBox}
|
||||||
|
></ha-svg-icon>`}
|
||||||
|
<div slot="headline">${area.name}</div>
|
||||||
|
</ha-menu-item>`
|
||||||
|
)}
|
||||||
|
<ha-menu-item .value=${null} @click=${this._handleBulkArea}>
|
||||||
|
<div slot="headline">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.no_area"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-menu-item>
|
||||||
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
|
<ha-menu-item @click=${this._bulkCreateArea}>
|
||||||
|
<div slot="headline">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.add_area"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-menu-item>`;
|
||||||
|
|
||||||
|
const areasInOverflow =
|
||||||
|
(this._sizeController.value && this._sizeController.value < 900) ||
|
||||||
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
||||||
|
|
||||||
|
const labelsInOverflow =
|
||||||
|
areasInOverflow &&
|
||||||
|
(!this._sizeController.value || this._sizeController.value < 700);
|
||||||
|
|
||||||
const scenes = this._scenes(
|
const scenes = this._scenes(
|
||||||
this.scenes,
|
this.scenes,
|
||||||
this._entityReg,
|
this._entityReg,
|
||||||
@ -453,6 +494,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
this._labels,
|
this._labels,
|
||||||
this._filteredScenes
|
this._filteredScenes
|
||||||
);
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -582,9 +624,25 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${labelItems}
|
${labelItems}
|
||||||
|
</ha-button-menu-new>`}
|
||||||
|
${areasInOverflow
|
||||||
|
? nothing
|
||||||
|
: html`<ha-button-menu-new slot="selection-bar">
|
||||||
|
<ha-assist-chip
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="trailing-icon"
|
||||||
|
.path=${mdiMenuDown}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-assist-chip>
|
||||||
|
${areaItems}
|
||||||
</ha-button-menu-new>`}`
|
</ha-button-menu-new>`}`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this.narrow || labelsInOverflow
|
${this.narrow || areasInOverflow
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
<ha-button-menu-new has-overflow slot="selection-bar">
|
||||||
${
|
${
|
||||||
@ -630,8 +688,8 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
${
|
${
|
||||||
this.narrow || this.hass.dockedSidebar === "docked"
|
this.narrow || labelsInOverflow
|
||||||
? html` <ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@ -647,6 +705,24 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
|
${
|
||||||
|
this.narrow || areasInOverflow
|
||||||
|
? html`<ha-sub-menu>
|
||||||
|
<ha-menu-item slot="item">
|
||||||
|
<div slot="headline">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="end"
|
||||||
|
.path=${mdiChevronRight}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-menu-item>
|
||||||
|
<ha-menu slot="menu">${areaItems}</ha-menu>
|
||||||
|
</ha-sub-menu>`
|
||||||
|
: nothing
|
||||||
|
}
|
||||||
</ha-button-menu-new>`
|
</ha-button-menu-new>`
|
||||||
: nothing}
|
: nothing}
|
||||||
${!this.scenes.length
|
${!this.scenes.length
|
||||||
@ -875,6 +951,46 @@ ${rejected
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _handleBulkArea(ev) {
|
||||||
|
const area = ev.currentTarget.value;
|
||||||
|
this._bulkAddArea(area);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkAddArea(area: string) {
|
||||||
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
|
this._selected.forEach((entityId) => {
|
||||||
|
promises.push(
|
||||||
|
updateEntityRegistryEntry(this.hass, entityId, {
|
||||||
|
area_id: area,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const result = await Promise.allSettled(promises);
|
||||||
|
if (hasRejectedItems(result)) {
|
||||||
|
const rejected = rejectedItems(result);
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
|
||||||
|
number: rejected.length,
|
||||||
|
}),
|
||||||
|
text: html`<pre>
|
||||||
|
${rejected
|
||||||
|
.map((r) => r.reason.message || r.reason.code || r.reason)
|
||||||
|
.join("\r\n")}</pre
|
||||||
|
>`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkCreateArea() {
|
||||||
|
showAreaRegistryDetailDialog(this, {
|
||||||
|
createEntry: async (values) => {
|
||||||
|
const area = await createAreaRegistryEntry(this.hass, values);
|
||||||
|
this._bulkAddArea(area.area_id);
|
||||||
|
return area;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _editCategory(scene: any) {
|
private _editCategory(scene: any) {
|
||||||
const entityReg = this._entityReg.find(
|
const entityReg = this._entityReg.find(
|
||||||
(reg) => reg.entity_id === scene.entity_id
|
(reg) => reg.entity_id === scene.entity_id
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
mdiPlus,
|
mdiPlus,
|
||||||
mdiScriptText,
|
mdiScriptText,
|
||||||
mdiTag,
|
mdiTag,
|
||||||
|
mdiTextureBox,
|
||||||
mdiTransitConnection,
|
mdiTransitConnection,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import { differenceInDays } from "date-fns";
|
import { differenceInDays } from "date-fns";
|
||||||
@ -61,6 +62,7 @@ import "../../../components/ha-icon-overflow-menu";
|
|||||||
import "../../../components/ha-menu-item";
|
import "../../../components/ha-menu-item";
|
||||||
import "../../../components/ha-sub-menu";
|
import "../../../components/ha-sub-menu";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
|
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||||
import {
|
import {
|
||||||
CategoryRegistryEntry,
|
CategoryRegistryEntry,
|
||||||
createCategoryRegistryEntry,
|
createCategoryRegistryEntry,
|
||||||
@ -98,6 +100,7 @@ import { haStyle } from "../../../resources/styles";
|
|||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import { showToast } from "../../../util/toast";
|
import { showToast } from "../../../util/toast";
|
||||||
|
import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry-detail";
|
||||||
import { showNewAutomationDialog } from "../automation/show-dialog-new-automation";
|
import { showNewAutomationDialog } from "../automation/show-dialog-new-automation";
|
||||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||||
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||||
@ -418,6 +421,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-menu-item>`;
|
||||||
|
|
||||||
const labelItems = html`${this._labels?.map((label) => {
|
const labelItems = html`${this._labels?.map((label) => {
|
||||||
const color = label.color ? computeCssColor(label.color) : undefined;
|
const color = label.color ? computeCssColor(label.color) : undefined;
|
||||||
const selected = this._selected.every((entityId) =>
|
const selected = this._selected.every((entityId) =>
|
||||||
@ -454,9 +458,46 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-menu-item
|
||||||
>`;
|
>`;
|
||||||
const labelsInOverflow =
|
|
||||||
(this._sizeController.value && this._sizeController.value < 700) ||
|
const areaItems = html`${Object.values(this.hass.areas).map(
|
||||||
|
(area) =>
|
||||||
|
html`<ha-menu-item
|
||||||
|
.value=${area.area_id}
|
||||||
|
@click=${this._handleBulkArea}
|
||||||
|
>
|
||||||
|
${area.icon
|
||||||
|
? html`<ha-icon slot="start" .icon=${area.icon}></ha-icon>`
|
||||||
|
: html`<ha-svg-icon
|
||||||
|
slot="start"
|
||||||
|
.path=${mdiTextureBox}
|
||||||
|
></ha-svg-icon>`}
|
||||||
|
<div slot="headline">${area.name}</div>
|
||||||
|
</ha-menu-item>`
|
||||||
|
)}
|
||||||
|
<ha-menu-item .value=${null} @click=${this._handleBulkArea}>
|
||||||
|
<div slot="headline">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.no_area"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-menu-item>
|
||||||
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
|
<ha-menu-item @click=${this._bulkCreateArea}>
|
||||||
|
<div slot="headline">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.add_area"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-menu-item>`;
|
||||||
|
|
||||||
|
const areasInOverflow =
|
||||||
|
(this._sizeController.value && this._sizeController.value < 900) ||
|
||||||
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
||||||
|
|
||||||
|
const labelsInOverflow =
|
||||||
|
areasInOverflow &&
|
||||||
|
(!this._sizeController.value || this._sizeController.value < 700);
|
||||||
|
|
||||||
const scripts = this._scripts(
|
const scripts = this._scripts(
|
||||||
this.scripts,
|
this.scripts,
|
||||||
this._entityReg,
|
this._entityReg,
|
||||||
@ -608,9 +649,25 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${labelItems}
|
${labelItems}
|
||||||
|
</ha-button-menu-new>`}
|
||||||
|
${areasInOverflow
|
||||||
|
? nothing
|
||||||
|
: html`<ha-button-menu-new slot="selection-bar">
|
||||||
|
<ha-assist-chip
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="trailing-icon"
|
||||||
|
.path=${mdiMenuDown}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-assist-chip>
|
||||||
|
${areaItems}
|
||||||
</ha-button-menu-new>`}`
|
</ha-button-menu-new>`}`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this.narrow || labelsInOverflow
|
${this.narrow || areasInOverflow
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
<ha-button-menu-new has-overflow slot="selection-bar">
|
||||||
${
|
${
|
||||||
@ -656,8 +713,8 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
${
|
${
|
||||||
this.narrow || this.hass.dockedSidebar === "docked"
|
this.narrow || labelsInOverflow
|
||||||
? html` <ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@ -673,6 +730,24 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
|
${
|
||||||
|
this.narrow || areasInOverflow
|
||||||
|
? html`<ha-sub-menu>
|
||||||
|
<ha-menu-item slot="item">
|
||||||
|
<div slot="headline">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="end"
|
||||||
|
.path=${mdiChevronRight}
|
||||||
|
></ha-svg-icon>
|
||||||
|
</ha-menu-item>
|
||||||
|
<ha-menu slot="menu">${areaItems}</ha-menu>
|
||||||
|
</ha-sub-menu>`
|
||||||
|
: nothing
|
||||||
|
}
|
||||||
</ha-button-menu-new>`
|
</ha-button-menu-new>`
|
||||||
: nothing}
|
: nothing}
|
||||||
${!this.scripts.length
|
${!this.scripts.length
|
||||||
@ -1111,6 +1186,46 @@ ${rejected
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _handleBulkArea(ev) {
|
||||||
|
const area = ev.currentTarget.value;
|
||||||
|
this._bulkAddArea(area);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkAddArea(area: string) {
|
||||||
|
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
|
||||||
|
this._selected.forEach((entityId) => {
|
||||||
|
promises.push(
|
||||||
|
updateEntityRegistryEntry(this.hass, entityId, {
|
||||||
|
area_id: area,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const result = await Promise.allSettled(promises);
|
||||||
|
if (hasRejectedItems(result)) {
|
||||||
|
const rejected = rejectedItems(result);
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
|
||||||
|
number: rejected.length,
|
||||||
|
}),
|
||||||
|
text: html`<pre>
|
||||||
|
${rejected
|
||||||
|
.map((r) => r.reason.message || r.reason.code || r.reason)
|
||||||
|
.join("\r\n")}</pre
|
||||||
|
>`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _bulkCreateArea() {
|
||||||
|
showAreaRegistryDetailDialog(this, {
|
||||||
|
createEntry: async (values) => {
|
||||||
|
const area = await createAreaRegistryEntry(this.hass, values);
|
||||||
|
this._bulkAddArea(area.area_id);
|
||||||
|
return area;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _handleSortingChanged(ev: CustomEvent) {
|
private _handleSortingChanged(ev: CustomEvent) {
|
||||||
this._activeSorting = ev.detail;
|
this._activeSorting = ev.detail;
|
||||||
}
|
}
|
||||||
|
@ -90,12 +90,10 @@ class HuiLockOpenDoorCardFeature
|
|||||||
return html`
|
return html`
|
||||||
${this._buttonState === "success"
|
${this._buttonState === "success"
|
||||||
? html`
|
? html`
|
||||||
<div class="buttons">
|
|
||||||
<p class="open-success">
|
<p class="open-success">
|
||||||
<ha-svg-icon path=${mdiCheck}></ha-svg-icon>
|
<ha-svg-icon path=${mdiCheck}></ha-svg-icon>
|
||||||
${this.hass.localize("ui.card.lock.open_door_success")}
|
${this.hass.localize("ui.card.lock.open_door_success")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-control-button-group>
|
<ha-control-button-group>
|
||||||
@ -115,12 +113,6 @@ class HuiLockOpenDoorCardFeature
|
|||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
.buttons {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
ha-control-button {
|
ha-control-button {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
@ -139,10 +131,14 @@ class HuiLockOpenDoorCardFeature
|
|||||||
line-height: 14px;
|
line-height: 14px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--success-color);
|
color: var(--success-color);
|
||||||
|
margin: 0 12px 12px 12px;
|
||||||
|
height: 40px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
ha-control-button-group + ha-attributes:not([empty]) {
|
ha-control-button-group + ha-attributes:not([empty]) {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
@ -138,7 +138,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||||||
includeDomains
|
includeDomains
|
||||||
);
|
);
|
||||||
|
|
||||||
return { type: "map", entities: foundEntities };
|
return { type: "map", entities: foundEntities, theme_mode: "auto" };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
@ -151,6 +151,17 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||||||
(${this._error.code})
|
(${this._error.code})
|
||||||
</ha-alert>`;
|
</ha-alert>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isDarkMode =
|
||||||
|
this._config.dark_mode || this._config.theme_mode === "dark"
|
||||||
|
? true
|
||||||
|
: this._config.theme_mode === "light"
|
||||||
|
? false
|
||||||
|
: this.hass.themes.darkMode;
|
||||||
|
|
||||||
|
const themeMode =
|
||||||
|
this._config.theme_mode || (this._config.dark_mode ? "dark" : "auto");
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card id="card" .header=${this._config.title}>
|
<ha-card id="card" .header=${this._config.title}>
|
||||||
<div id="root">
|
<div id="root">
|
||||||
@ -161,7 +172,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||||||
.paths=${this._getHistoryPaths(this._config, this._stateHistory)}
|
.paths=${this._getHistoryPaths(this._config, this._stateHistory)}
|
||||||
.autoFit=${this._config.auto_fit || false}
|
.autoFit=${this._config.auto_fit || false}
|
||||||
.fitZones=${this._config.fit_zones}
|
.fitZones=${this._config.fit_zones}
|
||||||
?darkMode=${this._config.dark_mode}
|
.themeMode=${themeMode}
|
||||||
interactiveZones
|
interactiveZones
|
||||||
renderPassive
|
renderPassive
|
||||||
></ha-map>
|
></ha-map>
|
||||||
@ -170,6 +181,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||||||
"ui.panel.lovelace.cards.map.reset_focus"
|
"ui.panel.lovelace.cards.map.reset_focus"
|
||||||
)}
|
)}
|
||||||
.path=${mdiImageFilterCenterFocus}
|
.path=${mdiImageFilterCenterFocus}
|
||||||
|
style=${isDarkMode ? "color:#ffffff" : "color:#000000"}
|
||||||
@click=${this._fitMap}
|
@click=${this._fitMap}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
@ -3,7 +3,7 @@ import { ActionConfig } from "../../../data/lovelace/config/action";
|
|||||||
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
||||||
import { Statistic, StatisticType } from "../../../data/recorder";
|
import { Statistic, StatisticType } from "../../../data/recorder";
|
||||||
import { ForecastType } from "../../../data/weather";
|
import { ForecastType } from "../../../data/weather";
|
||||||
import { FullCalendarView, TranslationDict } from "../../../types";
|
import { FullCalendarView, ThemeMode, TranslationDict } from "../../../types";
|
||||||
import { LovelaceCardFeatureConfig } from "../card-features/types";
|
import { LovelaceCardFeatureConfig } from "../card-features/types";
|
||||||
import { LegacyStateFilter } from "../common/evaluate-filter";
|
import { LegacyStateFilter } from "../common/evaluate-filter";
|
||||||
import { Condition, LegacyCondition } from "../common/validate-condition";
|
import { Condition, LegacyCondition } from "../common/validate-condition";
|
||||||
@ -314,6 +314,7 @@ export interface MapCardConfig extends LovelaceCardConfig {
|
|||||||
hours_to_show?: number;
|
hours_to_show?: number;
|
||||||
geo_location_sources?: string[];
|
geo_location_sources?: string[];
|
||||||
dark_mode?: boolean;
|
dark_mode?: boolean;
|
||||||
|
theme_mode?: ThemeMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarkdownCardConfig extends LovelaceCardConfig {
|
export interface MarkdownCardConfig extends LovelaceCardConfig {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { mdiPalette } from "@mdi/js";
|
||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import {
|
import {
|
||||||
@ -11,6 +12,7 @@ import {
|
|||||||
string,
|
string,
|
||||||
union,
|
union,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { hasLocation } from "../../../../common/entity/has_location";
|
import { hasLocation } from "../../../../common/entity/has_location";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
@ -28,6 +30,7 @@ import { processEditorEntities } from "../process-editor-entities";
|
|||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import { EntitiesEditorEvent } from "../types";
|
import { EntitiesEditorEvent } from "../types";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "./config-elements-style";
|
||||||
|
import { LocalizeFunc } from "../../../../common/translations/localize";
|
||||||
|
|
||||||
export const mapEntitiesConfigStruct = union([
|
export const mapEntitiesConfigStruct = union([
|
||||||
object({
|
object({
|
||||||
@ -50,11 +53,30 @@ const cardConfigStruct = assign(
|
|||||||
hours_to_show: optional(number()),
|
hours_to_show: optional(number()),
|
||||||
geo_location_sources: optional(array(string())),
|
geo_location_sources: optional(array(string())),
|
||||||
auto_fit: optional(boolean()),
|
auto_fit: optional(boolean()),
|
||||||
|
theme_mode: optional(string()),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const SCHEMA = [
|
const themeModes = ["auto", "light", "dark"] as const;
|
||||||
|
|
||||||
|
@customElement("hui-map-card-editor")
|
||||||
|
export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||||
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _config?: MapCardConfig;
|
||||||
|
|
||||||
|
@state() private _configEntities?: EntityConfig[];
|
||||||
|
|
||||||
|
private _schema = memoizeOne(
|
||||||
|
(localize: LocalizeFunc) =>
|
||||||
|
[
|
||||||
{ name: "title", selector: { text: {} } },
|
{ name: "title", selector: { text: {} } },
|
||||||
|
{
|
||||||
|
name: "",
|
||||||
|
type: "expandable",
|
||||||
|
iconPath: mdiPalette,
|
||||||
|
title: localize(`ui.panel.lovelace.editor.card.map.appearance`),
|
||||||
|
schema: [
|
||||||
{
|
{
|
||||||
name: "",
|
name: "",
|
||||||
type: "grid",
|
type: "grid",
|
||||||
@ -65,7 +87,21 @@ const SCHEMA = [
|
|||||||
default: DEFAULT_ZOOM,
|
default: DEFAULT_ZOOM,
|
||||||
selector: { number: { mode: "box", min: 0 } },
|
selector: { number: { mode: "box", min: 0 } },
|
||||||
},
|
},
|
||||||
{ name: "dark_mode", selector: { boolean: {} } },
|
{
|
||||||
|
name: "theme_mode",
|
||||||
|
default: "auto",
|
||||||
|
selector: {
|
||||||
|
select: {
|
||||||
|
mode: "dropdown",
|
||||||
|
options: themeModes.map((themeMode) => ({
|
||||||
|
value: themeMode,
|
||||||
|
label: localize(
|
||||||
|
`ui.panel.lovelace.editor.card.map.theme_modes.${themeMode}`
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "hours_to_show",
|
name: "hours_to_show",
|
||||||
default: DEFAULT_HOURS_TO_SHOW,
|
default: DEFAULT_HOURS_TO_SHOW,
|
||||||
@ -73,18 +109,26 @@ const SCHEMA = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
] as const;
|
],
|
||||||
|
},
|
||||||
@customElement("hui-map-card-editor")
|
] as const
|
||||||
export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
);
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
|
||||||
|
|
||||||
@state() private _config?: MapCardConfig;
|
|
||||||
|
|
||||||
@state() private _configEntities?: EntityConfig[];
|
|
||||||
|
|
||||||
public setConfig(config: MapCardConfig): void {
|
public setConfig(config: MapCardConfig): void {
|
||||||
assert(config, cardConfigStruct);
|
assert(config, cardConfigStruct);
|
||||||
|
|
||||||
|
// Migrate legacy dark_mode to theme_mode
|
||||||
|
if (!this._config && !("theme_mode" in config)) {
|
||||||
|
config = { ...config };
|
||||||
|
if (config.dark_mode) {
|
||||||
|
config.theme_mode = "dark";
|
||||||
|
} else {
|
||||||
|
config.theme_mode = "auto";
|
||||||
|
}
|
||||||
|
delete config.dark_mode;
|
||||||
|
fireEvent(this, "config-changed", { config: config });
|
||||||
|
}
|
||||||
|
|
||||||
this._config = config;
|
this._config = config;
|
||||||
this._configEntities = config.entities
|
this._configEntities = config.entities
|
||||||
? processEditorEntities(config.entities)
|
? processEditorEntities(config.entities)
|
||||||
@ -104,23 +148,24 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
|||||||
<ha-form
|
<ha-form
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.data=${this._config}
|
.data=${this._config}
|
||||||
.schema=${SCHEMA}
|
.schema=${this._schema(this.hass.localize)}
|
||||||
.computeLabel=${this._computeLabelCallback}
|
.computeLabel=${this._computeLabelCallback}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-form>
|
></ha-form>
|
||||||
<div class="card-config">
|
|
||||||
<hui-entity-editor
|
<hui-entity-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.entities=${this._configEntities}
|
.entities=${this._configEntities}
|
||||||
.entityFilter=${hasLocation}
|
.entityFilter=${hasLocation}
|
||||||
@entities-changed=${this._entitiesValueChanged}
|
@entities-changed=${this._entitiesValueChanged}
|
||||||
></hui-entity-editor>
|
></hui-entity-editor>
|
||||||
|
|
||||||
<h3>
|
<h3>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.map.geo_location_sources"
|
"ui.panel.lovelace.editor.card.map.geo_location_sources"
|
||||||
)}
|
)}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="geo_location_sources">
|
|
||||||
<hui-input-list-editor
|
<hui-input-list-editor
|
||||||
.inputLabel=${this.hass.localize(
|
.inputLabel=${this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.card.map.source"
|
"ui.panel.lovelace.editor.card.map.source"
|
||||||
@ -129,8 +174,6 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
|||||||
.value=${this._geo_location_sources}
|
.value=${this._geo_location_sources}
|
||||||
@value-changed=${this._geoSourcesChanged}
|
@value-changed=${this._geoSourcesChanged}
|
||||||
></hui-input-list-editor>
|
></hui-input-list-editor>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,9 +213,14 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
|||||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) => {
|
private _computeLabelCallback = (
|
||||||
|
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||||
|
) => {
|
||||||
switch (schema.name) {
|
switch (schema.name) {
|
||||||
case "dark_mode":
|
case "theme_mode":
|
||||||
|
return this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.card.map.${schema.name}`
|
||||||
|
);
|
||||||
case "default_zoom":
|
case "default_zoom":
|
||||||
return this.hass!.localize(
|
return this.hass!.localize(
|
||||||
`ui.panel.lovelace.editor.card.map.${schema.name}`
|
`ui.panel.lovelace.editor.card.map.${schema.name}`
|
||||||
@ -185,16 +233,7 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [configElementStyle, css``];
|
||||||
configElementStyle,
|
|
||||||
css`
|
|
||||||
.geo_location_sources {
|
|
||||||
padding-left: 20px;
|
|
||||||
padding-inline-start: 20px;
|
|
||||||
direction: var(--direction);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5835,7 +5835,14 @@
|
|||||||
"name": "Map",
|
"name": "Map",
|
||||||
"geo_location_sources": "Geolocation sources",
|
"geo_location_sources": "Geolocation sources",
|
||||||
"dark_mode": "Dark mode?",
|
"dark_mode": "Dark mode?",
|
||||||
"default_zoom": "Default zoom",
|
"appearance": "Appearance",
|
||||||
|
"theme_mode": "Theme Mode",
|
||||||
|
"theme_modes": {
|
||||||
|
"auto": "Auto",
|
||||||
|
"light": "Light",
|
||||||
|
"dark": "Dark"
|
||||||
|
},
|
||||||
|
"default_zoom": "Default Zoom",
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
"description": "The Map card that allows you to display entities on a map."
|
"description": "The Map card that allows you to display entities on a map."
|
||||||
},
|
},
|
||||||
|
@ -139,6 +139,8 @@ export type FullCalendarView =
|
|||||||
| "dayGridDay"
|
| "dayGridDay"
|
||||||
| "listWeek";
|
| "listWeek";
|
||||||
|
|
||||||
|
export type ThemeMode = "auto" | "light" | "dark";
|
||||||
|
|
||||||
export interface ToggleButton {
|
export interface ToggleButton {
|
||||||
label: string;
|
label: string;
|
||||||
iconPath?: string;
|
iconPath?: string;
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -7028,10 +7028,10 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"cropperjs@npm:1.6.1":
|
"cropperjs@npm:1.6.2":
|
||||||
version: 1.6.1
|
version: 1.6.2
|
||||||
resolution: "cropperjs@npm:1.6.1"
|
resolution: "cropperjs@npm:1.6.2"
|
||||||
checksum: 10/3ecd895ba8820021d6efd8effda54fb40a418e6940c307d46c62812d7d0ee10aacde473b44b7cfb315ec0fc2e5c5c573f4de06e8c2e8b7cb134f12f65c1d4aa7
|
checksum: 10/4b97ac27b7fd65316a531372dd96ce1073b4620c1b012fbad5acb0fdc28952112466bc333a98c510fe2bc9dcdeee58f5c8619522c045c6166300621673e3d759
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -9674,7 +9674,7 @@ __metadata:
|
|||||||
color-name: "npm:2.0.0"
|
color-name: "npm:2.0.0"
|
||||||
comlink: "npm:4.4.1"
|
comlink: "npm:4.4.1"
|
||||||
core-js: "npm:3.37.0"
|
core-js: "npm:3.37.0"
|
||||||
cropperjs: "npm:1.6.1"
|
cropperjs: "npm:1.6.2"
|
||||||
date-fns: "npm:3.6.0"
|
date-fns: "npm:3.6.0"
|
||||||
date-fns-tz: "npm:3.1.3"
|
date-fns-tz: "npm:3.1.3"
|
||||||
deep-clone-simple: "npm:1.1.1"
|
deep-clone-simple: "npm:1.1.1"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user