mirror of
https://github.com/home-assistant/frontend.git
synced 2025-09-05 03:06:40 +00:00
Compare commits
5 Commits
20240424.0
...
add-no-dev
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e3be190b36 | ||
![]() |
87bcd3e471 | ||
![]() |
7e9b01b56d | ||
![]() |
713763fc21 | ||
![]() |
5b7ab1bfcb |
@@ -102,7 +102,7 @@
|
||||
"color-name": "2.0.0",
|
||||
"comlink": "4.4.1",
|
||||
"core-js": "3.37.0",
|
||||
"cropperjs": "1.6.1",
|
||||
"cropperjs": "1.6.2",
|
||||
"date-fns": "3.6.0",
|
||||
"date-fns-tz": "3.1.3",
|
||||
"deep-clone-simple": "1.1.1",
|
||||
|
@@ -20,6 +20,8 @@ import "./ha-check-list-item";
|
||||
import "./ha-expansion-panel";
|
||||
import "./search-input-outlined";
|
||||
|
||||
export const FILTER_NO_DEVICE = "__NO_DEVICE__";
|
||||
|
||||
@customElement("ha-filter-devices")
|
||||
export class HaFilterDevices extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -32,6 +34,9 @@ export class HaFilterDevices extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "no-device-option" })
|
||||
public noDeviceOption = false;
|
||||
|
||||
@state() private _shouldRender = false;
|
||||
|
||||
@state() private _filter?: string;
|
||||
@@ -74,6 +79,7 @@ export class HaFilterDevices extends LitElement {
|
||||
.items=${this._devices(
|
||||
this.hass.devices,
|
||||
this._filter || "",
|
||||
this.noDeviceOption,
|
||||
this.value
|
||||
)}
|
||||
.keyFunction=${this._keyFunction}
|
||||
@@ -137,9 +143,13 @@ export class HaFilterDevices extends LitElement {
|
||||
}
|
||||
|
||||
private _devices = memoizeOne(
|
||||
(devices: HomeAssistant["devices"], filter: string, _value) => {
|
||||
const values = Object.values(devices);
|
||||
return values
|
||||
(
|
||||
devices: HomeAssistant["devices"],
|
||||
filter: string,
|
||||
noDeviceOption: boolean,
|
||||
_value
|
||||
) => {
|
||||
const values = Object.values(devices)
|
||||
.filter(
|
||||
(device) =>
|
||||
!filter ||
|
||||
@@ -152,6 +162,28 @@ export class HaFilterDevices extends LitElement {
|
||||
this.hass.locale.language
|
||||
)
|
||||
);
|
||||
if (noDeviceOption) {
|
||||
values.unshift({
|
||||
id: FILTER_NO_DEVICE,
|
||||
name: this.hass.localize("ui.panel.config.devices.no_device"),
|
||||
area_id: null,
|
||||
configuration_url: null,
|
||||
config_entries: [],
|
||||
connections: [],
|
||||
disabled_by: null,
|
||||
entry_type: null,
|
||||
identifiers: [],
|
||||
manufacturer: null,
|
||||
model: null,
|
||||
name_by_user: null,
|
||||
sw_version: null,
|
||||
hw_version: null,
|
||||
via_device_id: null,
|
||||
serial_number: null,
|
||||
labels: [],
|
||||
});
|
||||
}
|
||||
return values;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -171,7 +203,7 @@ export class HaFilterDevices extends LitElement {
|
||||
|
||||
for (const deviceId of this.value) {
|
||||
value.push(deviceId);
|
||||
if (this.type) {
|
||||
if (this.type && deviceId !== FILTER_NO_DEVICE) {
|
||||
relatedPromises.push(findRelated(this.hass, "device", deviceId));
|
||||
}
|
||||
}
|
||||
|
@@ -133,7 +133,7 @@ export class HaLocationsEditor extends LitElement {
|
||||
.layers=${this._getLayers(this._circles, this._locationMarkers)}
|
||||
.zoom=${this.zoom}
|
||||
.autoFit=${this.autoFit}
|
||||
?darkMode=${this.darkMode}
|
||||
?forceDarkMode=${this.darkMode}
|
||||
></ha-map>
|
||||
${this.helper
|
||||
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
|
||||
|
@@ -69,7 +69,9 @@ export class HaMap extends ReactiveElement {
|
||||
|
||||
@property({ type: Boolean }) public fitZones = false;
|
||||
|
||||
@property({ type: Boolean }) public darkMode = false;
|
||||
@property({ type: Boolean }) public forceDarkMode = false;
|
||||
|
||||
@property({ type: Boolean }) public forceLightMode = false;
|
||||
|
||||
@property({ type: Number }) public zoom = 14;
|
||||
|
||||
@@ -154,7 +156,8 @@ export class HaMap extends ReactiveElement {
|
||||
}
|
||||
|
||||
if (
|
||||
!changedProps.has("darkMode") &&
|
||||
!changedProps.has("forceDarkMode") &&
|
||||
!changedProps.has("forceLightMode") &&
|
||||
(!changedProps.has("hass") ||
|
||||
(oldHass && oldHass.themes?.darkMode === this.hass.themes?.darkMode))
|
||||
) {
|
||||
@@ -164,11 +167,13 @@ export class HaMap extends ReactiveElement {
|
||||
}
|
||||
|
||||
private _updateMapStyle(): void {
|
||||
const darkMode = this.darkMode || (this.hass.themes.darkMode ?? false);
|
||||
const forcedDark = this.darkMode;
|
||||
const darkMode =
|
||||
!this.forceLightMode &&
|
||||
(this.forceDarkMode || (this.hass.themes.darkMode ?? false));
|
||||
const map = this.renderRoot.querySelector("#map");
|
||||
map!.classList.toggle("dark", darkMode);
|
||||
map!.classList.toggle("forced-dark", forcedDark);
|
||||
map!.classList.toggle("forced-dark", this.forceDarkMode);
|
||||
map!.classList.toggle("forced-light", this.forceLightMode);
|
||||
}
|
||||
|
||||
private async _loadMap(): Promise<void> {
|
||||
@@ -398,8 +403,13 @@ export class HaMap extends ReactiveElement {
|
||||
"--dark-primary-color"
|
||||
);
|
||||
|
||||
const className =
|
||||
this.darkMode || this.hass.themes.darkMode ? "dark" : "light";
|
||||
const className = this.forceLightMode
|
||||
? "light"
|
||||
: this.forceDarkMode
|
||||
? "dark"
|
||||
: this.hass.themes.darkMode
|
||||
? "dark"
|
||||
: "light";
|
||||
|
||||
for (const entity of this.entities) {
|
||||
const stateObj = hass.states[getEntityId(entity)];
|
||||
@@ -543,27 +553,30 @@ export class HaMap extends ReactiveElement {
|
||||
background: #090909;
|
||||
}
|
||||
#map.forced-dark {
|
||||
color: #ffffff;
|
||||
--map-filter: invert(0.9) hue-rotate(170deg) brightness(1.5)
|
||||
contrast(1.2) saturate(0.3);
|
||||
}
|
||||
#map.forced-light {
|
||||
background: #ffffff;
|
||||
color: #000000;
|
||||
--map-filter: invert(0);
|
||||
}
|
||||
#map:active {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
.light {
|
||||
color: #000000;
|
||||
}
|
||||
.dark {
|
||||
color: #ffffff;
|
||||
}
|
||||
.leaflet-tile-pane {
|
||||
filter: var(--map-filter);
|
||||
}
|
||||
.dark .leaflet-bar a {
|
||||
background-color: var(--card-background-color, #1c1c1c);
|
||||
background-color: #1c1c1c;
|
||||
color: #ffffff;
|
||||
}
|
||||
.dark .leaflet-bar a:hover {
|
||||
background-color: #313131;
|
||||
}
|
||||
.leaflet-marker-draggable {
|
||||
cursor: move !important;
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ import {
|
||||
mdiPlus,
|
||||
mdiRobotHappy,
|
||||
mdiTag,
|
||||
mdiTextureBox,
|
||||
mdiToggleSwitch,
|
||||
mdiToggleSwitchOffOutline,
|
||||
mdiTransitConnection,
|
||||
@@ -69,6 +70,7 @@ import type { HaMenu } from "../../../components/ha-menu";
|
||||
import "../../../components/ha-menu-item";
|
||||
import "../../../components/ha-sub-menu";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||
import {
|
||||
AutomationEntity,
|
||||
deleteAutomation,
|
||||
@@ -106,6 +108,7 @@ import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route, ServiceCallResponse } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
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 { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
@@ -403,6 +406,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||
</div>
|
||||
</ha-menu-item>`;
|
||||
|
||||
const labelItems = html`${this._labels?.map((label) => {
|
||||
const color = label.color ? computeCssColor(label.color) : undefined;
|
||||
const selected = this._selected.every((entityId) =>
|
||||
@@ -440,10 +444,45 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
</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");
|
||||
|
||||
const labelsInOverflow =
|
||||
areasInOverflow &&
|
||||
(!this._sizeController.value || this._sizeController.value < 700);
|
||||
|
||||
const automations = this._automations(
|
||||
this.automations,
|
||||
this._entityReg,
|
||||
@@ -598,6 +637,22 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${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>`}`
|
||||
: nothing
|
||||
}
|
||||
@@ -662,6 +717,24 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
</ha-sub-menu>`
|
||||
: 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-svg-icon slot="start" .path=${mdiToggleSwitch}></ha-svg-icon>
|
||||
<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() {
|
||||
const promises: Promise<ServiceCallResponse>[] = [];
|
||||
this._selected.forEach((entityId) => {
|
||||
|
@@ -52,7 +52,6 @@ import "../../../components/data-table/ha-data-table-labels";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-button-menu";
|
||||
import "../../../components/ha-check-list-item";
|
||||
import "../../../components/ha-filter-devices";
|
||||
import "../../../components/ha-filter-floor-areas";
|
||||
import "../../../components/ha-filter-integrations";
|
||||
import "../../../components/ha-filter-labels";
|
||||
@@ -97,6 +96,7 @@ import { configSections } from "../ha-panel-config";
|
||||
import "../integrations/ha-integration-overflow-menu";
|
||||
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
|
||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||
import { FILTER_NO_DEVICE } from "../../../components/ha-filter-devices";
|
||||
|
||||
export interface StateEntity
|
||||
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
|
||||
@@ -448,8 +448,12 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
entity.labels.some((lbl) => filter.value!.includes(lbl))
|
||||
);
|
||||
} else if (filter.items) {
|
||||
filteredEntities = filteredEntities.filter((entity) =>
|
||||
filter.items!.has(entity.entity_id)
|
||||
filteredEntities = filteredEntities.filter(
|
||||
(entity) =>
|
||||
filter.items!.has(entity.entity_id) ||
|
||||
(key === "ha-filter-devices" &&
|
||||
filter.value?.includes(FILTER_NO_DEVICE) &&
|
||||
!entity.device_id)
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -781,6 +785,7 @@ ${
|
||||
.expanded=${this._expandedFilter === "ha-filter-devices"}
|
||||
.narrow=${this.narrow}
|
||||
@expanded-changed=${this._filterExpanded}
|
||||
no-device-option
|
||||
></ha-filter-devices>
|
||||
<ha-filter-integrations
|
||||
.hass=${this.hass}
|
||||
|
@@ -15,6 +15,7 @@ import {
|
||||
mdiPlay,
|
||||
mdiPlus,
|
||||
mdiTag,
|
||||
mdiTextureBox,
|
||||
} from "@mdi/js";
|
||||
import { differenceInDays } from "date-fns";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
@@ -61,6 +62,7 @@ import "../../../components/ha-menu-item";
|
||||
import "../../../components/ha-state-icon";
|
||||
import "../../../components/ha-sub-menu";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||
import {
|
||||
CategoryRegistryEntry,
|
||||
createCategoryRegistryEntry,
|
||||
@@ -97,6 +99,7 @@ import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry-detail";
|
||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
@@ -406,6 +409,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||
</div>
|
||||
</ha-menu-item>`;
|
||||
|
||||
const labelItems = html` ${this._labels?.map((label) => {
|
||||
const color = label.color ? computeCssColor(label.color) : undefined;
|
||||
const selected = this._selected.every((entityId) =>
|
||||
@@ -442,9 +446,46 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||
</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");
|
||||
|
||||
const labelsInOverflow =
|
||||
areasInOverflow &&
|
||||
(!this._sizeController.value || this._sizeController.value < 700);
|
||||
|
||||
const scenes = this._scenes(
|
||||
this.scenes,
|
||||
this._entityReg,
|
||||
@@ -453,6 +494,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
this._labels,
|
||||
this._filteredScenes
|
||||
);
|
||||
|
||||
return html`
|
||||
<hass-tabs-subpage-data-table
|
||||
.hass=${this.hass}
|
||||
@@ -582,9 +624,25 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${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>`}`
|
||||
: nothing}
|
||||
${this.narrow || labelsInOverflow
|
||||
${this.narrow || areasInOverflow
|
||||
? html`
|
||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
||||
${
|
||||
@@ -630,8 +688,8 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
: nothing
|
||||
}
|
||||
${
|
||||
this.narrow || this.hass.dockedSidebar === "docked"
|
||||
? html` <ha-sub-menu>
|
||||
this.narrow || labelsInOverflow
|
||||
? html`<ha-sub-menu>
|
||||
<ha-menu-item slot="item">
|
||||
<div slot="headline">
|
||||
${this.hass.localize(
|
||||
@@ -647,6 +705,24 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
</ha-sub-menu>`
|
||||
: 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>`
|
||||
: nothing}
|
||||
${!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) {
|
||||
const entityReg = this._entityReg.find(
|
||||
(reg) => reg.entity_id === scene.entity_id
|
||||
|
@@ -13,6 +13,7 @@ import {
|
||||
mdiPlus,
|
||||
mdiScriptText,
|
||||
mdiTag,
|
||||
mdiTextureBox,
|
||||
mdiTransitConnection,
|
||||
} from "@mdi/js";
|
||||
import { differenceInDays } from "date-fns";
|
||||
@@ -61,6 +62,7 @@ import "../../../components/ha-icon-overflow-menu";
|
||||
import "../../../components/ha-menu-item";
|
||||
import "../../../components/ha-sub-menu";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||
import {
|
||||
CategoryRegistryEntry,
|
||||
createCategoryRegistryEntry,
|
||||
@@ -98,6 +100,7 @@ import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry-detail";
|
||||
import { showNewAutomationDialog } from "../automation/show-dialog-new-automation";
|
||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||
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")}
|
||||
</div>
|
||||
</ha-menu-item>`;
|
||||
|
||||
const labelItems = html`${this._labels?.map((label) => {
|
||||
const color = label.color ? computeCssColor(label.color) : undefined;
|
||||
const selected = this._selected.every((entityId) =>
|
||||
@@ -454,9 +458,46 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||
</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");
|
||||
|
||||
const labelsInOverflow =
|
||||
areasInOverflow &&
|
||||
(!this._sizeController.value || this._sizeController.value < 700);
|
||||
|
||||
const scripts = this._scripts(
|
||||
this.scripts,
|
||||
this._entityReg,
|
||||
@@ -608,9 +649,25 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
||||
></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${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>`}`
|
||||
: nothing}
|
||||
${this.narrow || labelsInOverflow
|
||||
${this.narrow || areasInOverflow
|
||||
? html`
|
||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
||||
${
|
||||
@@ -656,8 +713,8 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
||||
: nothing
|
||||
}
|
||||
${
|
||||
this.narrow || this.hass.dockedSidebar === "docked"
|
||||
? html` <ha-sub-menu>
|
||||
this.narrow || labelsInOverflow
|
||||
? html`<ha-sub-menu>
|
||||
<ha-menu-item slot="item">
|
||||
<div slot="headline">
|
||||
${this.hass.localize(
|
||||
@@ -673,6 +730,24 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
||||
</ha-sub-menu>`
|
||||
: 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>`
|
||||
: nothing}
|
||||
${!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) {
|
||||
this._activeSorting = ev.detail;
|
||||
}
|
||||
|
@@ -90,12 +90,10 @@ class HuiLockOpenDoorCardFeature
|
||||
return html`
|
||||
${this._buttonState === "success"
|
||||
? html`
|
||||
<div class="buttons">
|
||||
<p class="open-success">
|
||||
<ha-svg-icon path=${mdiCheck}></ha-svg-icon>
|
||||
${this.hass.localize("ui.card.lock.open_door_success")}
|
||||
</p>
|
||||
</div>
|
||||
<p class="open-success">
|
||||
<ha-svg-icon path=${mdiCheck}></ha-svg-icon>
|
||||
${this.hass.localize("ui.card.lock.open_door_success")}
|
||||
</p>
|
||||
`
|
||||
: html`
|
||||
<ha-control-button-group>
|
||||
@@ -115,12 +113,6 @@ class HuiLockOpenDoorCardFeature
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 0;
|
||||
}
|
||||
ha-control-button {
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -139,10 +131,14 @@ class HuiLockOpenDoorCardFeature
|
||||
line-height: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
font-weight: 500;
|
||||
color: var(--success-color);
|
||||
margin: 0 12px 12px 12px;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
ha-control-button-group + ha-attributes:not([empty]) {
|
||||
margin-top: 16px;
|
||||
|
@@ -138,7 +138,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
||||
includeDomains
|
||||
);
|
||||
|
||||
return { type: "map", entities: foundEntities };
|
||||
return { type: "map", entities: foundEntities, theme_mode: "auto" };
|
||||
}
|
||||
|
||||
protected render() {
|
||||
@@ -151,6 +151,14 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
||||
(${this._error.code})
|
||||
</ha-alert>`;
|
||||
}
|
||||
|
||||
const isDarkMode =
|
||||
this._config.dark_mode || this._config.theme_mode === "dark"
|
||||
? true
|
||||
: this._config.theme_mode === "light"
|
||||
? false
|
||||
: this.hass.themes.darkMode;
|
||||
|
||||
return html`
|
||||
<ha-card id="card" .header=${this._config.title}>
|
||||
<div id="root">
|
||||
@@ -161,7 +169,9 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
||||
.paths=${this._getHistoryPaths(this._config, this._stateHistory)}
|
||||
.autoFit=${this._config.auto_fit || false}
|
||||
.fitZones=${this._config.fit_zones}
|
||||
?darkMode=${this._config.dark_mode}
|
||||
?forceDarkMode=${this._config.theme_mode === "dark" ||
|
||||
this._config.dark_mode}
|
||||
?forceLightMode=${this._config.theme_mode === "light"}
|
||||
interactiveZones
|
||||
renderPassive
|
||||
></ha-map>
|
||||
@@ -170,6 +180,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
||||
"ui.panel.lovelace.cards.map.reset_focus"
|
||||
)}
|
||||
.path=${mdiImageFilterCenterFocus}
|
||||
style=${isDarkMode ? "color:#ffffff" : "color:#000000"}
|
||||
@click=${this._fitMap}
|
||||
tabindex="0"
|
||||
></ha-icon-button>
|
||||
|
@@ -3,7 +3,7 @@ import { ActionConfig } from "../../../data/lovelace/config/action";
|
||||
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
||||
import { Statistic, StatisticType } from "../../../data/recorder";
|
||||
import { ForecastType } from "../../../data/weather";
|
||||
import { FullCalendarView, TranslationDict } from "../../../types";
|
||||
import { FullCalendarView, ThemeMode, TranslationDict } from "../../../types";
|
||||
import { LovelaceCardFeatureConfig } from "../card-features/types";
|
||||
import { LegacyStateFilter } from "../common/evaluate-filter";
|
||||
import { Condition, LegacyCondition } from "../common/validate-condition";
|
||||
@@ -314,6 +314,7 @@ export interface MapCardConfig extends LovelaceCardConfig {
|
||||
hours_to_show?: number;
|
||||
geo_location_sources?: string[];
|
||||
dark_mode?: boolean;
|
||||
theme_mode?: ThemeMode;
|
||||
}
|
||||
|
||||
export interface MarkdownCardConfig extends LovelaceCardConfig {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { mdiPalette } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
string,
|
||||
union,
|
||||
} from "superstruct";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { hasLocation } from "../../../../common/entity/has_location";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
@@ -28,6 +30,7 @@ import { processEditorEntities } from "../process-editor-entities";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { EntitiesEditorEvent } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
import { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
|
||||
export const mapEntitiesConfigStruct = union([
|
||||
object({
|
||||
@@ -50,30 +53,11 @@ const cardConfigStruct = assign(
|
||||
hours_to_show: optional(number()),
|
||||
geo_location_sources: optional(array(string())),
|
||||
auto_fit: optional(boolean()),
|
||||
theme_mode: optional(string()),
|
||||
})
|
||||
);
|
||||
|
||||
const SCHEMA = [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{ name: "aspect_ratio", selector: { text: {} } },
|
||||
{
|
||||
name: "default_zoom",
|
||||
default: DEFAULT_ZOOM,
|
||||
selector: { number: { mode: "box", min: 0 } },
|
||||
},
|
||||
{ name: "dark_mode", selector: { boolean: {} } },
|
||||
{
|
||||
name: "hours_to_show",
|
||||
default: DEFAULT_HOURS_TO_SHOW,
|
||||
selector: { number: { mode: "box", min: 0 } },
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const;
|
||||
const themeModes = ["auto", "light", "dark"] as const;
|
||||
|
||||
@customElement("hui-map-card-editor")
|
||||
export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
@@ -83,8 +67,68 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
|
||||
@state() private _configEntities?: EntityConfig[];
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(localize: LocalizeFunc) =>
|
||||
[
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "",
|
||||
type: "expandable",
|
||||
iconPath: mdiPalette,
|
||||
title: localize(`ui.panel.lovelace.editor.card.map.appearance`),
|
||||
schema: [
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{ name: "aspect_ratio", selector: { text: {} } },
|
||||
{
|
||||
name: "default_zoom",
|
||||
default: DEFAULT_ZOOM,
|
||||
selector: { number: { mode: "box", min: 0 } },
|
||||
},
|
||||
{
|
||||
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",
|
||||
default: DEFAULT_HOURS_TO_SHOW,
|
||||
selector: { number: { mode: "box", min: 0 } },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const
|
||||
);
|
||||
|
||||
public setConfig(config: MapCardConfig): void {
|
||||
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._configEntities = config.entities
|
||||
? processEditorEntities(config.entities)
|
||||
@@ -104,33 +148,32 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.schema=${this._schema(this.hass.localize)}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<hui-entity-editor
|
||||
.hass=${this.hass}
|
||||
.entities=${this._configEntities}
|
||||
.entityFilter=${hasLocation}
|
||||
@entities-changed=${this._entitiesValueChanged}
|
||||
></hui-entity-editor>
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.map.geo_location_sources"
|
||||
)}
|
||||
</h3>
|
||||
<div class="geo_location_sources">
|
||||
<hui-input-list-editor
|
||||
.inputLabel=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.map.source"
|
||||
)}
|
||||
.hass=${this.hass}
|
||||
.value=${this._geo_location_sources}
|
||||
@value-changed=${this._geoSourcesChanged}
|
||||
></hui-input-list-editor>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hui-entity-editor
|
||||
.hass=${this.hass}
|
||||
.entities=${this._configEntities}
|
||||
.entityFilter=${hasLocation}
|
||||
@entities-changed=${this._entitiesValueChanged}
|
||||
></hui-entity-editor>
|
||||
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.map.geo_location_sources"
|
||||
)}
|
||||
</h3>
|
||||
|
||||
<hui-input-list-editor
|
||||
.inputLabel=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.map.source"
|
||||
)}
|
||||
.hass=${this.hass}
|
||||
.value=${this._geo_location_sources}
|
||||
@value-changed=${this._geoSourcesChanged}
|
||||
></hui-input-list-editor>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -170,9 +213,14 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
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) {
|
||||
case "dark_mode":
|
||||
case "theme_mode":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.map.${schema.name}`
|
||||
);
|
||||
case "default_zoom":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.map.${schema.name}`
|
||||
@@ -185,16 +233,7 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
};
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
configElementStyle,
|
||||
css`
|
||||
.geo_location_sources {
|
||||
padding-left: 20px;
|
||||
padding-inline-start: 20px;
|
||||
direction: var(--direction);
|
||||
}
|
||||
`,
|
||||
];
|
||||
return [configElementStyle, css``];
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3941,6 +3941,7 @@
|
||||
"name": "Name",
|
||||
"update": "Update",
|
||||
"no_devices": "No devices",
|
||||
"no_device": "No device",
|
||||
"enabled_label": "Enable {type}",
|
||||
"enabled_cause": "The {type} is disabled by {cause}.",
|
||||
"disabled_by": {
|
||||
@@ -5835,7 +5836,14 @@
|
||||
"name": "Map",
|
||||
"geo_location_sources": "Geolocation sources",
|
||||
"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",
|
||||
"description": "The Map card that allows you to display entities on a map."
|
||||
},
|
||||
|
@@ -139,6 +139,8 @@ export type FullCalendarView =
|
||||
| "dayGridDay"
|
||||
| "listWeek";
|
||||
|
||||
export type ThemeMode = "auto" | "light" | "dark";
|
||||
|
||||
export interface ToggleButton {
|
||||
label: string;
|
||||
iconPath?: string;
|
||||
|
10
yarn.lock
10
yarn.lock
@@ -7028,10 +7028,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cropperjs@npm:1.6.1":
|
||||
version: 1.6.1
|
||||
resolution: "cropperjs@npm:1.6.1"
|
||||
checksum: 10/3ecd895ba8820021d6efd8effda54fb40a418e6940c307d46c62812d7d0ee10aacde473b44b7cfb315ec0fc2e5c5c573f4de06e8c2e8b7cb134f12f65c1d4aa7
|
||||
"cropperjs@npm:1.6.2":
|
||||
version: 1.6.2
|
||||
resolution: "cropperjs@npm:1.6.2"
|
||||
checksum: 10/4b97ac27b7fd65316a531372dd96ce1073b4620c1b012fbad5acb0fdc28952112466bc333a98c510fe2bc9dcdeee58f5c8619522c045c6166300621673e3d759
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9674,7 +9674,7 @@ __metadata:
|
||||
color-name: "npm:2.0.0"
|
||||
comlink: "npm:4.4.1"
|
||||
core-js: "npm:3.37.0"
|
||||
cropperjs: "npm:1.6.1"
|
||||
cropperjs: "npm:1.6.2"
|
||||
date-fns: "npm:3.6.0"
|
||||
date-fns-tz: "npm:3.1.3"
|
||||
deep-clone-simple: "npm:1.1.1"
|
||||
|
Reference in New Issue
Block a user