mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Fix visual map issues (#20541)
* Fix visual map issues (#15587) - hover background color of zoom control in dark mode - map light mode when dark theme is used - background color of zoom control with map dark mode when light theme is used * Fix breaking change (#15587) * Change theme mode selection to use dropdown (#15587) - Additionally fixed unpleasant horizontal scrollbar in map editor * Add yaml migration, fix force light/dark options --------- Co-authored-by: Christoph Wen <wen.christoph@gmail.com>
This commit is contained in:
parent
5b7ab1bfcb
commit
713763fc21
@ -133,7 +133,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}
|
?forceDarkMode=${this.darkMode}
|
||||||
></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>`
|
||||||
|
@ -69,7 +69,9 @@ export class HaMap extends ReactiveElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public fitZones = false;
|
@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;
|
@property({ type: Number }) public zoom = 14;
|
||||||
|
|
||||||
@ -154,7 +156,8 @@ export class HaMap extends ReactiveElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!changedProps.has("darkMode") &&
|
!changedProps.has("forceDarkMode") &&
|
||||||
|
!changedProps.has("forceLightMode") &&
|
||||||
(!changedProps.has("hass") ||
|
(!changedProps.has("hass") ||
|
||||||
(oldHass && oldHass.themes?.darkMode === this.hass.themes?.darkMode))
|
(oldHass && oldHass.themes?.darkMode === this.hass.themes?.darkMode))
|
||||||
) {
|
) {
|
||||||
@ -164,11 +167,13 @@ export class HaMap extends ReactiveElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _updateMapStyle(): void {
|
private _updateMapStyle(): void {
|
||||||
const darkMode = this.darkMode || (this.hass.themes.darkMode ?? false);
|
const darkMode =
|
||||||
const forcedDark = this.darkMode;
|
!this.forceLightMode &&
|
||||||
|
(this.forceDarkMode || (this.hass.themes.darkMode ?? false));
|
||||||
const map = this.renderRoot.querySelector("#map");
|
const map = this.renderRoot.querySelector("#map");
|
||||||
map!.classList.toggle("dark", darkMode);
|
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> {
|
private async _loadMap(): Promise<void> {
|
||||||
@ -398,8 +403,13 @@ export class HaMap extends ReactiveElement {
|
|||||||
"--dark-primary-color"
|
"--dark-primary-color"
|
||||||
);
|
);
|
||||||
|
|
||||||
const className =
|
const className = this.forceLightMode
|
||||||
this.darkMode || this.hass.themes.darkMode ? "dark" : "light";
|
? "light"
|
||||||
|
: this.forceDarkMode
|
||||||
|
? "dark"
|
||||||
|
: 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 +553,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;
|
||||||
}
|
}
|
||||||
|
@ -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,14 @@ 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;
|
||||||
|
|
||||||
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 +169,9 @@ 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}
|
?forceDarkMode=${this._config.theme_mode === "dark" ||
|
||||||
|
this._config.dark_mode}
|
||||||
|
?forceLightMode=${this._config.theme_mode === "light"}
|
||||||
interactiveZones
|
interactiveZones
|
||||||
renderPassive
|
renderPassive
|
||||||
></ha-map>
|
></ha-map>
|
||||||
@ -170,6 +180,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,30 +53,11 @@ 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;
|
||||||
{ 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;
|
|
||||||
|
|
||||||
@customElement("hui-map-card-editor")
|
@customElement("hui-map-card-editor")
|
||||||
export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||||
@ -83,8 +67,68 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
|||||||
|
|
||||||
@state() private _configEntities?: EntityConfig[];
|
@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 {
|
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,33 +148,32 @@ 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>
|
|
||||||
${this.hass.localize(
|
<h3>
|
||||||
"ui.panel.lovelace.editor.card.map.geo_location_sources"
|
${this.hass.localize(
|
||||||
)}
|
"ui.panel.lovelace.editor.card.map.geo_location_sources"
|
||||||
</h3>
|
)}
|
||||||
<div class="geo_location_sources">
|
</h3>
|
||||||
<hui-input-list-editor
|
|
||||||
.inputLabel=${this.hass.localize(
|
<hui-input-list-editor
|
||||||
"ui.panel.lovelace.editor.card.map.source"
|
.inputLabel=${this.hass.localize(
|
||||||
)}
|
"ui.panel.lovelace.editor.card.map.source"
|
||||||
.hass=${this.hass}
|
)}
|
||||||
.value=${this._geo_location_sources}
|
.hass=${this.hass}
|
||||||
@value-changed=${this._geoSourcesChanged}
|
.value=${this._geo_location_sources}
|
||||||
></hui-input-list-editor>
|
@value-changed=${this._geoSourcesChanged}
|
||||||
</div>
|
></hui-input-list-editor>
|
||||||
</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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user