mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Add picker for badges (#21436)
This commit is contained in:
parent
345000a0e9
commit
179245e1aa
@ -1,12 +1,25 @@
|
|||||||
import { Condition } from "../../../panels/lovelace/common/validate-condition";
|
import { Condition } from "../../../panels/lovelace/common/validate-condition";
|
||||||
|
|
||||||
export interface LovelaceBadgeConfig {
|
export interface LovelaceBadgeConfig {
|
||||||
type?: string;
|
type: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
visibility?: Condition[];
|
visibility?: Condition[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultBadgeConfig = (entity_id: string): LovelaceBadgeConfig => ({
|
export const ensureBadgeConfig = (
|
||||||
type: "entity",
|
config: Partial<LovelaceBadgeConfig> | string
|
||||||
entity: entity_id,
|
): LovelaceBadgeConfig => {
|
||||||
});
|
if (typeof config === "string") {
|
||||||
|
return {
|
||||||
|
type: "entity",
|
||||||
|
entity: config,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ("type" in config && config.type) {
|
||||||
|
return config as LovelaceBadgeConfig;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: "entity",
|
||||||
|
...config,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -27,7 +27,7 @@ export interface LovelaceBaseViewConfig {
|
|||||||
|
|
||||||
export interface LovelaceViewConfig extends LovelaceBaseViewConfig {
|
export interface LovelaceViewConfig extends LovelaceBaseViewConfig {
|
||||||
type?: string;
|
type?: string;
|
||||||
badges?: (string | LovelaceBadgeConfig)[]; // Badge can be just an entity_id
|
badges?: (string | Partial<LovelaceBadgeConfig>)[]; // Badge can be just an entity_id or without type
|
||||||
cards?: LovelaceCardConfig[];
|
cards?: LovelaceCardConfig[];
|
||||||
sections?: LovelaceSectionRawConfig[];
|
sections?: LovelaceSectionRawConfig[];
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,10 @@ export class HuiEntityFilterBadge
|
|||||||
const element = document.createElement("hui-badge");
|
const element = document.createElement("hui-badge");
|
||||||
element.hass = this.hass;
|
element.hass = this.hass;
|
||||||
element.preview = this.preview;
|
element.preview = this.preview;
|
||||||
element.config = badgeConfig;
|
element.config = {
|
||||||
|
type: "entity",
|
||||||
|
...badgeConfig,
|
||||||
|
};
|
||||||
element.load();
|
element.load();
|
||||||
this._elements.push(element);
|
this._elements.push(element);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ import {
|
|||||||
} from "../cards/types";
|
} from "../cards/types";
|
||||||
import { EntityConfig } from "../entity-rows/types";
|
import { EntityConfig } from "../entity-rows/types";
|
||||||
import { ButtonsHeaderFooterConfig } from "../header-footer/types";
|
import { ButtonsHeaderFooterConfig } from "../header-footer/types";
|
||||||
|
import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
|
||||||
|
import { EntityBadgeConfig } from "../badges/types";
|
||||||
|
|
||||||
const HIDE_DOMAIN = new Set([
|
const HIDE_DOMAIN = new Set([
|
||||||
"automation",
|
"automation",
|
||||||
@ -310,6 +312,23 @@ export const computeCards = (
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const computeBadges = (
|
||||||
|
_states: HassEntities,
|
||||||
|
entityIds: string[]
|
||||||
|
): LovelaceBadgeConfig[] => {
|
||||||
|
const badges: LovelaceBadgeConfig[] = [];
|
||||||
|
|
||||||
|
for (const entityId of entityIds) {
|
||||||
|
const config: EntityBadgeConfig = {
|
||||||
|
type: "entity",
|
||||||
|
entity: entityId,
|
||||||
|
};
|
||||||
|
|
||||||
|
badges.push(config);
|
||||||
|
}
|
||||||
|
return badges;
|
||||||
|
};
|
||||||
|
|
||||||
const computeDefaultViewStates = (
|
const computeDefaultViewStates = (
|
||||||
entities: HassEntities,
|
entities: HassEntities,
|
||||||
entityEntries: HomeAssistant["entities"]
|
entityEntries: HomeAssistant["entities"]
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||||
import {
|
import {
|
||||||
|
mdiContentCopy,
|
||||||
|
mdiContentCut,
|
||||||
mdiContentDuplicate,
|
mdiContentDuplicate,
|
||||||
mdiDelete,
|
mdiDelete,
|
||||||
mdiDotsVertical,
|
mdiDotsVertical,
|
||||||
mdiPencil,
|
mdiPencil,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
|
import deepClone from "deep-clone-simple";
|
||||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
@ -25,6 +28,7 @@ import {
|
|||||||
parseLovelaceCardPath,
|
parseLovelaceCardPath,
|
||||||
} from "../editor/lovelace-path";
|
} from "../editor/lovelace-path";
|
||||||
import { Lovelace } from "../types";
|
import { Lovelace } from "../types";
|
||||||
|
import { ensureBadgeConfig } from "../../../data/lovelace/config/badge";
|
||||||
|
|
||||||
@customElement("hui-badge-edit-mode")
|
@customElement("hui-badge-edit-mode")
|
||||||
export class HuiBadgeEditMode extends LitElement {
|
export class HuiBadgeEditMode extends LitElement {
|
||||||
@ -46,7 +50,7 @@ export class HuiBadgeEditMode extends LitElement {
|
|||||||
public _focused: boolean = false;
|
public _focused: boolean = false;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "lovelaceClipboard",
|
key: "dashboardBadgeClipboard",
|
||||||
state: false,
|
state: false,
|
||||||
subscribe: false,
|
subscribe: false,
|
||||||
storage: "sessionStorage",
|
storage: "sessionStorage",
|
||||||
@ -134,6 +138,14 @@ export class HuiBadgeEditMode extends LitElement {
|
|||||||
"ui.panel.lovelace.editor.edit_card.duplicate"
|
"ui.panel.lovelace.editor.edit_card.duplicate"
|
||||||
)}
|
)}
|
||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
|
<ha-list-item graphic="icon">
|
||||||
|
<ha-svg-icon slot="graphic" .path=${mdiContentCopy}></ha-svg-icon>
|
||||||
|
${this.hass.localize("ui.panel.lovelace.editor.edit_card.copy")}
|
||||||
|
</ha-list-item>
|
||||||
|
<ha-list-item graphic="icon">
|
||||||
|
<ha-svg-icon slot="graphic" .path=${mdiContentCut}></ha-svg-icon>
|
||||||
|
${this.hass.localize("ui.panel.lovelace.editor.edit_card.cut")}
|
||||||
|
</ha-list-item>
|
||||||
<li divider role="separator"></li>
|
<li divider role="separator"></li>
|
||||||
<ha-list-item graphic="icon" class="warning">
|
<ha-list-item graphic="icon" class="warning">
|
||||||
${this.hass.localize("ui.panel.lovelace.editor.edit_card.delete")}
|
${this.hass.localize("ui.panel.lovelace.editor.edit_card.delete")}
|
||||||
@ -159,18 +171,35 @@ export class HuiBadgeEditMode extends LitElement {
|
|||||||
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
||||||
switch (ev.detail.index) {
|
switch (ev.detail.index) {
|
||||||
case 0:
|
case 0:
|
||||||
this._duplicateCard();
|
this._duplicateBadge();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
this._deleteCard();
|
this._copyBadge();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this._cutBadge();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
this._deleteBadge();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _duplicateCard(): void {
|
private _cutBadge(): void {
|
||||||
|
this._copyBadge();
|
||||||
|
this._deleteBadge();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _copyBadge(): void {
|
||||||
|
const { cardIndex } = parseLovelaceCardPath(this.path!);
|
||||||
|
const cardConfig = this._badges[cardIndex];
|
||||||
|
this._clipboard = deepClone(cardConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _duplicateBadge(): void {
|
||||||
const { cardIndex } = parseLovelaceCardPath(this.path!);
|
const { cardIndex } = parseLovelaceCardPath(this.path!);
|
||||||
const containerPath = getLovelaceContainerPath(this.path!);
|
const containerPath = getLovelaceContainerPath(this.path!);
|
||||||
const badgeConfig = this._badges![cardIndex];
|
const badgeConfig = ensureBadgeConfig(this._badges![cardIndex]);
|
||||||
showEditBadgeDialog(this, {
|
showEditBadgeDialog(this, {
|
||||||
lovelaceConfig: this.lovelace!.config,
|
lovelaceConfig: this.lovelace!.config,
|
||||||
saveConfig: this.lovelace!.saveConfig,
|
saveConfig: this.lovelace!.saveConfig,
|
||||||
@ -191,7 +220,7 @@ export class HuiBadgeEditMode extends LitElement {
|
|||||||
fireEvent(this, "ll-edit-badge", { path: this.path! });
|
fireEvent(this, "ll-edit-badge", { path: this.path! });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _deleteCard(): void {
|
private _deleteBadge(): void {
|
||||||
fireEvent(this, "ll-delete-badge", { path: this.path! });
|
fireEvent(this, "ll-delete-badge", { path: this.path! });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ export class HuiCardEditMode extends LitElement {
|
|||||||
public _focused: boolean = false;
|
public _focused: boolean = false;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "lovelaceClipboard",
|
key: "dashboardCardClipboard",
|
||||||
state: false,
|
state: false,
|
||||||
subscribe: false,
|
subscribe: false,
|
||||||
storage: "sessionStorage",
|
storage: "sessionStorage",
|
||||||
|
@ -67,7 +67,7 @@ export class HuiCardOptions extends LitElement {
|
|||||||
@property({ type: Boolean }) public hidePosition = false;
|
@property({ type: Boolean }) public hidePosition = false;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "lovelaceClipboard",
|
key: "dashboardCardClipboard",
|
||||||
state: false,
|
state: false,
|
||||||
subscribe: false,
|
subscribe: false,
|
||||||
storage: "sessionStorage",
|
storage: "sessionStorage",
|
||||||
|
@ -4,6 +4,7 @@ import "../badges/hui-state-label-badge";
|
|||||||
import {
|
import {
|
||||||
createLovelaceElement,
|
createLovelaceElement,
|
||||||
getLovelaceElementClass,
|
getLovelaceElementClass,
|
||||||
|
tryCreateLovelaceElement,
|
||||||
} from "./create-element-base";
|
} from "./create-element-base";
|
||||||
|
|
||||||
const ALWAYS_LOADED_TYPES = new Set(["error", "state-label", "entity"]);
|
const ALWAYS_LOADED_TYPES = new Set(["error", "state-label", "entity"]);
|
||||||
@ -11,6 +12,17 @@ const LAZY_LOAD_TYPES = {
|
|||||||
"entity-filter": () => import("../badges/hui-entity-filter-badge"),
|
"entity-filter": () => import("../badges/hui-entity-filter-badge"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This will not return an error card but will throw the error
|
||||||
|
export const tryCreateBadgeElement = (config: LovelaceBadgeConfig) =>
|
||||||
|
tryCreateLovelaceElement(
|
||||||
|
"badge",
|
||||||
|
config,
|
||||||
|
ALWAYS_LOADED_TYPES,
|
||||||
|
LAZY_LOAD_TYPES,
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
export const createBadgeElement = (config: LovelaceBadgeConfig) =>
|
export const createBadgeElement = (config: LovelaceBadgeConfig) =>
|
||||||
createLovelaceElement(
|
createLovelaceElement(
|
||||||
"badge",
|
"badge",
|
||||||
|
590
src/panels/lovelace/editor/badge-editor/hui-badge-picker.ts
Normal file
590
src/panels/lovelace/editor/badge-editor/hui-badge-picker.ts
Normal file
@ -0,0 +1,590 @@
|
|||||||
|
import Fuse, { IFuseOptions } from "fuse.js";
|
||||||
|
import {
|
||||||
|
CSSResultGroup,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
nothing,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { styleMap } from "lit/directives/style-map";
|
||||||
|
import { until } from "lit/directives/until";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { storage } from "../../../../common/decorators/storage";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { stringCompare } from "../../../../common/string/compare";
|
||||||
|
import { stripDiacritics } from "../../../../common/string/strip-diacritics";
|
||||||
|
import "../../../../components/ha-circular-progress";
|
||||||
|
import "../../../../components/search-input";
|
||||||
|
import { isUnavailableState } from "../../../../data/entity";
|
||||||
|
import { LovelaceBadgeConfig } from "../../../../data/lovelace/config/badge";
|
||||||
|
import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
|
||||||
|
import {
|
||||||
|
CUSTOM_TYPE_PREFIX,
|
||||||
|
CustomBadgeEntry,
|
||||||
|
customBadges,
|
||||||
|
getCustomBadgeEntry,
|
||||||
|
} from "../../../../data/lovelace_custom_cards";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import { getStripDiacriticsFn } from "../../../../util/fuse";
|
||||||
|
import {
|
||||||
|
calcUnusedEntities,
|
||||||
|
computeUsedEntities,
|
||||||
|
} from "../../common/compute-unused-entities";
|
||||||
|
import { tryCreateBadgeElement } from "../../create-element/create-badge-element";
|
||||||
|
import type { LovelaceBadge } from "../../types";
|
||||||
|
import { getBadgeStubConfig } from "../get-badge-stub-config";
|
||||||
|
import { coreBadges } from "../lovelace-badges";
|
||||||
|
import type { Badge, BadgePickTarget } from "../types";
|
||||||
|
|
||||||
|
interface BadgeElement {
|
||||||
|
badge: Badge;
|
||||||
|
element: TemplateResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("hui-badge-picker")
|
||||||
|
export class HuiBadgePicker extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public suggestedBadges?: string[];
|
||||||
|
|
||||||
|
@storage({
|
||||||
|
key: "dashboardBadgeClipboard",
|
||||||
|
state: true,
|
||||||
|
subscribe: true,
|
||||||
|
storage: "sessionStorage",
|
||||||
|
})
|
||||||
|
private _clipboard?: LovelaceBadgeConfig;
|
||||||
|
|
||||||
|
@state() private _badges: BadgeElement[] = [];
|
||||||
|
|
||||||
|
public lovelace?: LovelaceConfig;
|
||||||
|
|
||||||
|
public badgePicked?: (badgeConf: LovelaceBadgeConfig) => void;
|
||||||
|
|
||||||
|
@state() private _filter = "";
|
||||||
|
|
||||||
|
@state() private _width?: number;
|
||||||
|
|
||||||
|
@state() private _height?: number;
|
||||||
|
|
||||||
|
private _unusedEntities?: string[];
|
||||||
|
|
||||||
|
private _usedEntities?: string[];
|
||||||
|
|
||||||
|
private _filterBadges = memoizeOne(
|
||||||
|
(badgeElements: BadgeElement[], filter?: string): BadgeElement[] => {
|
||||||
|
if (!filter) {
|
||||||
|
return badgeElements;
|
||||||
|
}
|
||||||
|
let badges = badgeElements.map(
|
||||||
|
(badgeElement: BadgeElement) => badgeElement.badge
|
||||||
|
);
|
||||||
|
const options: IFuseOptions<Badge> = {
|
||||||
|
keys: ["type", "name", "description"],
|
||||||
|
isCaseSensitive: false,
|
||||||
|
minMatchCharLength: Math.min(filter.length, 2),
|
||||||
|
threshold: 0.2,
|
||||||
|
getFn: getStripDiacriticsFn,
|
||||||
|
};
|
||||||
|
const fuse = new Fuse(badges, options);
|
||||||
|
badges = fuse
|
||||||
|
.search(stripDiacritics(filter))
|
||||||
|
.map((result) => result.item);
|
||||||
|
return badgeElements.filter((badgeElement: BadgeElement) =>
|
||||||
|
badges.includes(badgeElement.badge)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
private _suggestedBadges = memoizeOne(
|
||||||
|
(badgeElements: BadgeElement[]): BadgeElement[] =>
|
||||||
|
badgeElements.filter(
|
||||||
|
(badgeElement: BadgeElement) => badgeElement.badge.isSuggested
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
private _customBadges = memoizeOne(
|
||||||
|
(badgeElements: BadgeElement[]): BadgeElement[] =>
|
||||||
|
badgeElements.filter(
|
||||||
|
(badgeElement: BadgeElement) =>
|
||||||
|
badgeElement.badge.isCustom && !badgeElement.badge.isSuggested
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
private _otherBadges = memoizeOne(
|
||||||
|
(badgeElements: BadgeElement[]): BadgeElement[] =>
|
||||||
|
badgeElements.filter(
|
||||||
|
(badgeElement: BadgeElement) =>
|
||||||
|
!badgeElement.badge.isSuggested && !badgeElement.badge.isCustom
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (
|
||||||
|
!this.hass ||
|
||||||
|
!this.lovelace ||
|
||||||
|
!this._unusedEntities ||
|
||||||
|
!this._usedEntities
|
||||||
|
) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const suggestedBadges = this._suggestedBadges(this._badges);
|
||||||
|
const otherBadges = this._otherBadges(this._badges);
|
||||||
|
const customBadgesItems = this._customBadges(this._badges);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<search-input
|
||||||
|
.hass=${this.hass}
|
||||||
|
.filter=${this._filter}
|
||||||
|
@value-changed=${this._handleSearchChange}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.lovelace.editor.edit_badge.search_badgess"
|
||||||
|
)}
|
||||||
|
></search-input>
|
||||||
|
<div
|
||||||
|
id="content"
|
||||||
|
style=${styleMap({
|
||||||
|
width: this._width ? `${this._width}px` : "auto",
|
||||||
|
height: this._height ? `${this._height}px` : "auto",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div class="badges-container">
|
||||||
|
${this._filter
|
||||||
|
? this._filterBadges(this._badges, this._filter).map(
|
||||||
|
(badgeElement: BadgeElement) => badgeElement.element
|
||||||
|
)
|
||||||
|
: html`
|
||||||
|
${suggestedBadges.length > 0
|
||||||
|
? html`
|
||||||
|
<div class="badges-container-header">
|
||||||
|
${this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.badge.generic.suggested_badges`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
${this._renderClipboardBadge()}
|
||||||
|
${suggestedBadges.map(
|
||||||
|
(badgeElement: BadgeElement) => badgeElement.element
|
||||||
|
)}
|
||||||
|
${suggestedBadges.length > 0
|
||||||
|
? html`
|
||||||
|
<div class="badges-container-header">
|
||||||
|
${this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.badge.generic.other_badges`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
${otherBadges.map(
|
||||||
|
(badgeElement: BadgeElement) => badgeElement.element
|
||||||
|
)}
|
||||||
|
${customBadgesItems.length > 0
|
||||||
|
? html`
|
||||||
|
<div class="badges-container-header">
|
||||||
|
${this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.badge.generic.custom_badges`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
${customBadgesItems.map(
|
||||||
|
(badgeElement: BadgeElement) => badgeElement.element
|
||||||
|
)}
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
<div class="badges-container">
|
||||||
|
<div
|
||||||
|
class="badge manual"
|
||||||
|
@click=${this._badgePicked}
|
||||||
|
.config=${{ type: "" }}
|
||||||
|
>
|
||||||
|
<div class="badge-header">
|
||||||
|
${this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.badge.generic.manual`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div class="preview description">
|
||||||
|
${this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.badge.generic.manual_description`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
|
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||||
|
if (!oldHass) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldHass.locale !== this.hass!.locale) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(): void {
|
||||||
|
if (!this.hass || !this.lovelace) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const usedEntities = computeUsedEntities(this.lovelace);
|
||||||
|
const unusedEntities = calcUnusedEntities(this.hass, usedEntities);
|
||||||
|
|
||||||
|
this._usedEntities = [...usedEntities].filter(
|
||||||
|
(eid) =>
|
||||||
|
this.hass!.states[eid] &&
|
||||||
|
!isUnavailableState(this.hass!.states[eid].state)
|
||||||
|
);
|
||||||
|
this._unusedEntities = [...unusedEntities].filter(
|
||||||
|
(eid) =>
|
||||||
|
this.hass!.states[eid] &&
|
||||||
|
!isUnavailableState(this.hass!.states[eid].state)
|
||||||
|
);
|
||||||
|
|
||||||
|
this._loadBages();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loadBages() {
|
||||||
|
let badges = coreBadges.map<Badge>((badge) => ({
|
||||||
|
name: this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.badge.${badge.type}.name`
|
||||||
|
),
|
||||||
|
description: this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.badge.${badge.type}.description`
|
||||||
|
),
|
||||||
|
isSuggested: this.suggestedBadges?.includes(badge.type) || false,
|
||||||
|
...badge,
|
||||||
|
}));
|
||||||
|
|
||||||
|
badges = badges.sort((a, b) => {
|
||||||
|
if (a.isSuggested && !b.isSuggested) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!a.isSuggested && b.isSuggested) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return stringCompare(
|
||||||
|
a.name || a.type,
|
||||||
|
b.name || b.type,
|
||||||
|
this.hass?.language
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (customBadges.length > 0) {
|
||||||
|
badges = badges.concat(
|
||||||
|
customBadges
|
||||||
|
.map((cbadge: CustomBadgeEntry) => ({
|
||||||
|
type: cbadge.type,
|
||||||
|
name: cbadge.name,
|
||||||
|
description: cbadge.description,
|
||||||
|
showElement: cbadge.preview,
|
||||||
|
isCustom: true,
|
||||||
|
}))
|
||||||
|
.sort((a, b) =>
|
||||||
|
stringCompare(
|
||||||
|
a.name || a.type,
|
||||||
|
b.name || b.type,
|
||||||
|
this.hass?.language
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this._badges = badges.map((badge) => ({
|
||||||
|
badge: badge,
|
||||||
|
element: html`${until(
|
||||||
|
this._renderBadgeElement(badge),
|
||||||
|
html`
|
||||||
|
<div class="badge spinner">
|
||||||
|
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}`,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderClipboardBadge() {
|
||||||
|
if (!this._clipboard) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html` ${until(
|
||||||
|
this._renderBadgeElement(
|
||||||
|
{
|
||||||
|
type: this._clipboard.type,
|
||||||
|
showElement: true,
|
||||||
|
isCustom: false,
|
||||||
|
name: this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.badge.generic.paste"
|
||||||
|
),
|
||||||
|
description: `${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.badge.generic.paste_description",
|
||||||
|
{
|
||||||
|
type: this._clipboard.type,
|
||||||
|
}
|
||||||
|
)}`,
|
||||||
|
},
|
||||||
|
this._clipboard
|
||||||
|
),
|
||||||
|
html`
|
||||||
|
<div class="badge spinner">
|
||||||
|
<ha-circular-progress indeterminate></ha-circular-progress>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleSearchChange(ev: CustomEvent) {
|
||||||
|
const value = ev.detail.value;
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
// Reset when we no longer filter
|
||||||
|
this._width = undefined;
|
||||||
|
this._height = undefined;
|
||||||
|
} else if (!this._width || !this._height) {
|
||||||
|
// Save height and width so the dialog doesn't jump while searching
|
||||||
|
const div = this.shadowRoot!.getElementById("content");
|
||||||
|
if (div && !this._width) {
|
||||||
|
const width = div.clientWidth;
|
||||||
|
if (width) {
|
||||||
|
this._width = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (div && !this._height) {
|
||||||
|
const height = div.clientHeight;
|
||||||
|
if (height) {
|
||||||
|
this._height = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._filter = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _badgePicked(ev: Event): void {
|
||||||
|
const config: LovelaceBadgeConfig = (ev.currentTarget! as BadgePickTarget)
|
||||||
|
.config;
|
||||||
|
|
||||||
|
fireEvent(this, "config-changed", { config });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _tryCreateBadgeElement(badge: LovelaceBadgeConfig) {
|
||||||
|
const element = tryCreateBadgeElement(badge) as LovelaceBadge;
|
||||||
|
element.hass = this.hass;
|
||||||
|
element.addEventListener(
|
||||||
|
"ll-rebuild",
|
||||||
|
(ev) => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._rebuildBadge(element, badge);
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _rebuildBadge(
|
||||||
|
badgeElToReplace: LovelaceBadge,
|
||||||
|
config: LovelaceBadgeConfig
|
||||||
|
): void {
|
||||||
|
let newBadgeEl: LovelaceBadge;
|
||||||
|
try {
|
||||||
|
newBadgeEl = this._tryCreateBadgeElement(config);
|
||||||
|
} catch (err: any) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (badgeElToReplace.parentElement) {
|
||||||
|
badgeElToReplace.parentElement!.replaceChild(
|
||||||
|
newBadgeEl,
|
||||||
|
badgeElToReplace
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _renderBadgeElement(
|
||||||
|
badge: Badge,
|
||||||
|
config?: LovelaceBadgeConfig
|
||||||
|
): Promise<TemplateResult> {
|
||||||
|
let { type } = badge;
|
||||||
|
const { showElement, isCustom, name, description } = badge;
|
||||||
|
const customBadge = isCustom ? getCustomBadgeEntry(type) : undefined;
|
||||||
|
if (isCustom) {
|
||||||
|
type = `${CUSTOM_TYPE_PREFIX}${type}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let element: LovelaceBadge | undefined;
|
||||||
|
let badgeConfig: LovelaceBadgeConfig = config ?? { type };
|
||||||
|
|
||||||
|
if (this.hass && this.lovelace) {
|
||||||
|
if (!config) {
|
||||||
|
badgeConfig = await getBadgeStubConfig(
|
||||||
|
this.hass,
|
||||||
|
type,
|
||||||
|
this._unusedEntities!,
|
||||||
|
this._usedEntities!
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showElement) {
|
||||||
|
try {
|
||||||
|
element = this._tryCreateBadgeElement(badgeConfig);
|
||||||
|
} catch (err: any) {
|
||||||
|
element = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="badge">
|
||||||
|
<div
|
||||||
|
class="overlay"
|
||||||
|
@click=${this._badgePicked}
|
||||||
|
.config=${badgeConfig}
|
||||||
|
></div>
|
||||||
|
<div class="badge-header">
|
||||||
|
${customBadge
|
||||||
|
? `${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.badge_picker.custom_badge"
|
||||||
|
)}: ${customBadge.name || customBadge.type}`
|
||||||
|
: name}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="preview ${classMap({
|
||||||
|
description: !element || element.tagName === "HUI-ERROR-BADGE",
|
||||||
|
})}"
|
||||||
|
>
|
||||||
|
${element && element.tagName !== "HUI-ERROR-BADGE"
|
||||||
|
? element
|
||||||
|
: customBadge
|
||||||
|
? customBadge.description ||
|
||||||
|
this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.badge_picker.no_description`
|
||||||
|
)
|
||||||
|
: description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
css`
|
||||||
|
search-input {
|
||||||
|
display: block;
|
||||||
|
--mdc-shape-small: var(--badge-picker-search-shape);
|
||||||
|
margin: var(--badge-picker-search-margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badges-container-header {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 12px 8px 4px 8px;
|
||||||
|
margin: 0;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badges-container {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 8px 8px;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
height: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: var(--ha-card-border-radius, 12px);
|
||||||
|
background: var(--primary-background-color, #fafafa);
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border: var(--ha-card-border-width, 1px) solid
|
||||||
|
var(--ha-card-border-color, var(--divider-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-header {
|
||||||
|
color: var(--ha-card-header-color, --primary-text-color);
|
||||||
|
font-family: var(--ha-card-header-font-family, inherit);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: -0.012em;
|
||||||
|
line-height: 20px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
background: var(
|
||||||
|
--ha-card-background,
|
||||||
|
var(--card-background-color, white)
|
||||||
|
);
|
||||||
|
border-bottom: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
pointer-events: none;
|
||||||
|
margin: 20px;
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: var(--ha-card-border-radius, 12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
inset-inline-start: 8px;
|
||||||
|
inset-inline-end: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
--mdc-icon-size: 16px;
|
||||||
|
line-height: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: var(--text-primary-color);
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
.icon.custom {
|
||||||
|
background: var(--warning-color);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-badge-picker": HuiBadgePicker;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,289 @@
|
|||||||
|
import "@material/mwc-tab-bar/mwc-tab-bar";
|
||||||
|
import "@material/mwc-tab/mwc-tab";
|
||||||
|
import { mdiClose } from "@mdi/js";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { cache } from "lit/directives/cache";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import memoize from "memoize-one";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||||
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
|
import { DataTableRowData } from "../../../../components/data-table/ha-data-table";
|
||||||
|
import "../../../../components/ha-dialog";
|
||||||
|
import "../../../../components/ha-dialog-header";
|
||||||
|
import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
|
||||||
|
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
|
||||||
|
import { haStyleDialog } from "../../../../resources/styles";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import { computeBadges } from "../../common/generate-lovelace-config";
|
||||||
|
import "../card-editor/hui-entity-picker-table";
|
||||||
|
import { findLovelaceContainer } from "../lovelace-path";
|
||||||
|
import "./hui-badge-picker";
|
||||||
|
import { CreateBadgeDialogParams } from "./show-create-badge-dialog";
|
||||||
|
import { showEditBadgeDialog } from "./show-edit-badge-dialog";
|
||||||
|
import { showSuggestBadgeDialog } from "./show-suggest-badge-dialog";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"selected-changed": SelectedChangedEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SelectedChangedEvent {
|
||||||
|
selectedEntities: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("hui-dialog-create-badge")
|
||||||
|
export class HuiCreateDialogBadge
|
||||||
|
extends LitElement
|
||||||
|
implements HassDialog<CreateBadgeDialogParams>
|
||||||
|
{
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _params?: CreateBadgeDialogParams;
|
||||||
|
|
||||||
|
@state() private _containerConfig!: LovelaceViewConfig;
|
||||||
|
|
||||||
|
@state() private _selectedEntities: string[] = [];
|
||||||
|
|
||||||
|
@state() private _currTabIndex = 0;
|
||||||
|
|
||||||
|
public async showDialog(params: CreateBadgeDialogParams): Promise<void> {
|
||||||
|
this._params = params;
|
||||||
|
|
||||||
|
const containerConfig = findLovelaceContainer(
|
||||||
|
params.lovelaceConfig,
|
||||||
|
params.path
|
||||||
|
);
|
||||||
|
|
||||||
|
if ("strategy" in containerConfig) {
|
||||||
|
throw new Error("Can't edit strategy");
|
||||||
|
}
|
||||||
|
|
||||||
|
this._containerConfig = containerConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(): boolean {
|
||||||
|
this._params = undefined;
|
||||||
|
this._currTabIndex = 0;
|
||||||
|
this._selectedEntities = [];
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this._params) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = this._containerConfig.title
|
||||||
|
? this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.edit_badge.pick_badge_title",
|
||||||
|
{ name: `"${this._containerConfig.title}"` }
|
||||||
|
)
|
||||||
|
: this.hass!.localize("ui.panel.lovelace.editor.edit_badge.pick_badge");
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-dialog
|
||||||
|
open
|
||||||
|
scrimClickAction
|
||||||
|
@keydown=${this._ignoreKeydown}
|
||||||
|
@closed=${this._cancel}
|
||||||
|
.heading=${title}
|
||||||
|
class=${classMap({ table: this._currTabIndex === 1 })}
|
||||||
|
>
|
||||||
|
<ha-dialog-header show-border slot="heading">
|
||||||
|
<ha-icon-button
|
||||||
|
slot="navigationIcon"
|
||||||
|
dialogAction="cancel"
|
||||||
|
.label=${this.hass.localize("ui.common.close")}
|
||||||
|
.path=${mdiClose}
|
||||||
|
></ha-icon-button>
|
||||||
|
<span slot="title"> ${title} </span>
|
||||||
|
<mwc-tab-bar
|
||||||
|
.activeIndex=${this._currTabIndex}
|
||||||
|
@MDCTabBar:activated=${this._handleTabChanged}
|
||||||
|
>
|
||||||
|
<mwc-tab
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.badge_picker.by_badge"
|
||||||
|
)}
|
||||||
|
dialogInitialFocus
|
||||||
|
></mwc-tab>
|
||||||
|
<mwc-tab
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.badge_picker.by_entity"
|
||||||
|
)}
|
||||||
|
></mwc-tab>
|
||||||
|
</mwc-tab-bar>
|
||||||
|
</ha-dialog-header>
|
||||||
|
${cache(
|
||||||
|
this._currTabIndex === 0
|
||||||
|
? html`
|
||||||
|
<hui-badge-picker
|
||||||
|
.suggestedBadges=${this._params.suggestedBadges}
|
||||||
|
.lovelace=${this._params.lovelaceConfig}
|
||||||
|
.hass=${this.hass}
|
||||||
|
@config-changed=${this._handleBadgePicked}
|
||||||
|
></hui-badge-picker>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<hui-entity-picker-table
|
||||||
|
no-label-float
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${true}
|
||||||
|
.entities=${this._allEntities(this.hass.states)}
|
||||||
|
@selected-changed=${this._handleSelectedChanged}
|
||||||
|
></hui-entity-picker-table>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div slot="primaryAction">
|
||||||
|
<mwc-button @click=${this._cancel}>
|
||||||
|
${this.hass!.localize("ui.common.cancel")}
|
||||||
|
</mwc-button>
|
||||||
|
${this._selectedEntities.length
|
||||||
|
? html`
|
||||||
|
<mwc-button @click=${this._suggestBadges}>
|
||||||
|
${this.hass!.localize("ui.common.continue")}
|
||||||
|
</mwc-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _ignoreKeydown(ev: KeyboardEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
|
/* overrule the ha-style-dialog max-height on small screens */
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-max-height: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width: 850px) {
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-min-width: 845px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-max-width: 845px;
|
||||||
|
--dialog-content-padding: 2px 24px 20px 24px;
|
||||||
|
--dialog-z-index: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-dialog.table {
|
||||||
|
--dialog-content-padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-max-width: calc(100vw - 32px);
|
||||||
|
--mdc-dialog-min-width: 1000px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hui-badge-picker {
|
||||||
|
--badge-picker-search-shape: 0;
|
||||||
|
--badge-picker-search-margin: -2px -24px 0;
|
||||||
|
}
|
||||||
|
hui-entity-picker-table {
|
||||||
|
display: block;
|
||||||
|
height: calc(100vh - 198px);
|
||||||
|
--mdc-shape-small: 0;
|
||||||
|
}
|
||||||
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
|
hui-entity-picker-table {
|
||||||
|
height: calc(100vh - 158px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleBadgePicked(ev) {
|
||||||
|
const config = ev.detail.config;
|
||||||
|
if (this._params!.entities && this._params!.entities.length) {
|
||||||
|
if (Object.keys(config).includes("entities")) {
|
||||||
|
config.entities = this._params!.entities;
|
||||||
|
} else if (Object.keys(config).includes("entity")) {
|
||||||
|
config.entity = this._params!.entities[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showEditBadgeDialog(this, {
|
||||||
|
lovelaceConfig: this._params!.lovelaceConfig,
|
||||||
|
saveConfig: this._params!.saveConfig,
|
||||||
|
path: this._params!.path,
|
||||||
|
badgeConfig: config,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleTabChanged(ev: CustomEvent): void {
|
||||||
|
const newTab = ev.detail.index;
|
||||||
|
if (newTab === this._currTabIndex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._currTabIndex = ev.detail.index;
|
||||||
|
this._selectedEntities = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleSelectedChanged(ev: CustomEvent): void {
|
||||||
|
this._selectedEntities = ev.detail.selectedEntities;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cancel(ev?: Event) {
|
||||||
|
if (ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _suggestBadges(): void {
|
||||||
|
const badgeConfig = computeBadges(this.hass.states, this._selectedEntities);
|
||||||
|
|
||||||
|
showSuggestBadgeDialog(this, {
|
||||||
|
lovelaceConfig: this._params!.lovelaceConfig,
|
||||||
|
saveConfig: this._params!.saveConfig,
|
||||||
|
path: this._params!.path as [number],
|
||||||
|
entities: this._selectedEntities,
|
||||||
|
badgeConfig,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _allEntities = memoize((entities) =>
|
||||||
|
Object.keys(entities).map((entity) => {
|
||||||
|
const stateObj = this.hass.states[entity];
|
||||||
|
return {
|
||||||
|
icon: "",
|
||||||
|
entity_id: entity,
|
||||||
|
stateObj,
|
||||||
|
name: computeStateName(stateObj),
|
||||||
|
domain: computeDomain(entity),
|
||||||
|
last_changed: stateObj!.last_changed,
|
||||||
|
} as DataTableRowData;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-dialog-create-badge": HuiCreateDialogBadge;
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@ import "../../../../components/ha-dialog";
|
|||||||
import "../../../../components/ha-dialog-header";
|
import "../../../../components/ha-dialog-header";
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
import {
|
import {
|
||||||
defaultBadgeConfig,
|
ensureBadgeConfig,
|
||||||
LovelaceBadgeConfig,
|
LovelaceBadgeConfig,
|
||||||
} from "../../../../data/lovelace/config/badge";
|
} from "../../../../data/lovelace/config/badge";
|
||||||
import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
|
import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
|
||||||
@ -106,8 +106,7 @@ export class HuiDialogEditBadge
|
|||||||
this._dirty = true;
|
this._dirty = true;
|
||||||
} else {
|
} else {
|
||||||
const badge = this._containerConfig.badges?.[params.badgeIndex];
|
const badge = this._containerConfig.badges?.[params.badgeIndex];
|
||||||
this._badgeConfig =
|
this._badgeConfig = badge != null ? ensureBadgeConfig(badge) : badge;
|
||||||
typeof badge === "string" ? defaultBadgeConfig(badge) : badge;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.large = false;
|
this.large = false;
|
||||||
|
@ -0,0 +1,204 @@
|
|||||||
|
import deepFreeze from "deep-freeze";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/ha-yaml-editor";
|
||||||
|
|
||||||
|
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
|
||||||
|
import { LovelaceBadgeConfig } from "../../../../data/lovelace/config/badge";
|
||||||
|
import { LovelaceConfig } from "../../../../data/lovelace/config/types";
|
||||||
|
import { haStyleDialog } from "../../../../resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
|
||||||
|
import "../../badges/hui-badge";
|
||||||
|
import { addBadges } from "../config-util";
|
||||||
|
import {
|
||||||
|
LovelaceContainerPath,
|
||||||
|
parseLovelaceContainerPath,
|
||||||
|
} from "../lovelace-path";
|
||||||
|
import { SuggestBadgeDialogParams } from "./show-suggest-badge-dialog";
|
||||||
|
|
||||||
|
@customElement("hui-dialog-suggest-badge")
|
||||||
|
export class HuiDialogSuggestBadge extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _params?: SuggestBadgeDialogParams;
|
||||||
|
|
||||||
|
@state() private _badgeConfig?: LovelaceBadgeConfig[];
|
||||||
|
|
||||||
|
@state() private _saving = false;
|
||||||
|
|
||||||
|
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
|
||||||
|
|
||||||
|
public showDialog(params: SuggestBadgeDialogParams): void {
|
||||||
|
this._params = params;
|
||||||
|
this._badgeConfig = params.badgeConfig;
|
||||||
|
if (!Object.isFrozen(this._badgeConfig)) {
|
||||||
|
this._badgeConfig = deepFreeze(this._badgeConfig);
|
||||||
|
}
|
||||||
|
if (this._yamlEditor) {
|
||||||
|
this._yamlEditor.setValue(this._badgeConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this._params = undefined;
|
||||||
|
this._badgeConfig = undefined;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderPreview() {
|
||||||
|
if (this._badgeConfig) {
|
||||||
|
return html`
|
||||||
|
<div class="element-preview">
|
||||||
|
${this._badgeConfig.map(
|
||||||
|
(badgeConfig) => html`
|
||||||
|
<hui-badge
|
||||||
|
.hass=${this.hass}
|
||||||
|
.config=${badgeConfig}
|
||||||
|
preview
|
||||||
|
></hui-badge>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this._params) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-dialog
|
||||||
|
open
|
||||||
|
scrimClickAction
|
||||||
|
@closed=${this.closeDialog}
|
||||||
|
.heading=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.suggest_badge.header"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
${this._renderPreview()}
|
||||||
|
${this._params.yaml && this._badgeConfig
|
||||||
|
? html`
|
||||||
|
<div class="editor">
|
||||||
|
<ha-yaml-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.defaultValue=${this._badgeConfig}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
</div>
|
||||||
|
<mwc-button
|
||||||
|
slot="secondaryAction"
|
||||||
|
@click=${this.closeDialog}
|
||||||
|
dialogInitialFocus
|
||||||
|
>
|
||||||
|
${this._params.yaml
|
||||||
|
? this.hass!.localize("ui.common.close")
|
||||||
|
: this.hass!.localize("ui.common.cancel")}
|
||||||
|
</mwc-button>
|
||||||
|
${!this._params.yaml
|
||||||
|
? html`
|
||||||
|
<mwc-button
|
||||||
|
slot="primaryAction"
|
||||||
|
.disabled=${this._saving}
|
||||||
|
@click=${this._save}
|
||||||
|
>
|
||||||
|
${this._saving
|
||||||
|
? html`
|
||||||
|
<ha-circular-progress
|
||||||
|
indeterminate
|
||||||
|
aria-label="Saving"
|
||||||
|
size="small"
|
||||||
|
></ha-circular-progress>
|
||||||
|
`
|
||||||
|
: this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.suggest_badge.add"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
|
/* overrule the ha-style-dialog max-height on small screens */
|
||||||
|
ha-dialog {
|
||||||
|
max-height: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media all and (min-width: 850px) {
|
||||||
|
ha-dialog {
|
||||||
|
width: 845px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ha-dialog {
|
||||||
|
max-width: 845px;
|
||||||
|
--dialog-z-index: 6;
|
||||||
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.element-preview {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.editor {
|
||||||
|
padding-top: 16px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computeNewConfig(
|
||||||
|
config: LovelaceConfig,
|
||||||
|
path: LovelaceContainerPath
|
||||||
|
): LovelaceConfig {
|
||||||
|
const { viewIndex } = parseLovelaceContainerPath(path);
|
||||||
|
|
||||||
|
const newBadges = this._badgeConfig!;
|
||||||
|
return addBadges(config, [viewIndex], newBadges);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _save(): Promise<void> {
|
||||||
|
if (
|
||||||
|
!this._params?.lovelaceConfig ||
|
||||||
|
!this._params?.path ||
|
||||||
|
!this._params?.saveConfig ||
|
||||||
|
!this._badgeConfig
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._saving = true;
|
||||||
|
|
||||||
|
const newConfig = this._computeNewConfig(
|
||||||
|
this._params.lovelaceConfig,
|
||||||
|
this._params.path
|
||||||
|
);
|
||||||
|
await this._params!.saveConfig(newConfig);
|
||||||
|
this._saving = false;
|
||||||
|
showSaveSuccessToast(this, this.hass);
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-dialog-suggest-badge": HuiDialogSuggestBadge;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
|
||||||
|
import { LovelaceContainerPath } from "../lovelace-path";
|
||||||
|
|
||||||
|
export interface CreateBadgeDialogParams {
|
||||||
|
lovelaceConfig: LovelaceConfig;
|
||||||
|
saveConfig: (config: LovelaceConfig) => void;
|
||||||
|
path: LovelaceContainerPath;
|
||||||
|
suggestedBadges?: string[];
|
||||||
|
entities?: string[]; // We can pass entity id's that will be added to the config when a badge is picked
|
||||||
|
}
|
||||||
|
|
||||||
|
export const importCreateBadgeDialog = () =>
|
||||||
|
import("./hui-dialog-create-badge");
|
||||||
|
|
||||||
|
export const showCreateBadgeDialog = (
|
||||||
|
element: HTMLElement,
|
||||||
|
createBadgeDialogParams: CreateBadgeDialogParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "hui-dialog-create-badge",
|
||||||
|
dialogImport: importCreateBadgeDialog,
|
||||||
|
dialogParams: createBadgeDialogParams,
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,26 @@
|
|||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { LovelaceBadgeConfig } from "../../../../data/lovelace/config/badge";
|
||||||
|
import { LovelaceConfig } from "../../../../data/lovelace/config/types";
|
||||||
|
import { LovelaceContainerPath } from "../lovelace-path";
|
||||||
|
|
||||||
|
export interface SuggestBadgeDialogParams {
|
||||||
|
lovelaceConfig?: LovelaceConfig;
|
||||||
|
yaml?: boolean;
|
||||||
|
saveConfig?: (config: LovelaceConfig) => void;
|
||||||
|
path?: LovelaceContainerPath;
|
||||||
|
entities?: string[]; // We pass this to create dialog when user chooses "Pick own"
|
||||||
|
badgeConfig: LovelaceBadgeConfig[]; // We can pass a suggested config
|
||||||
|
}
|
||||||
|
|
||||||
|
const importSuggestBadgeDialog = () => import("./hui-dialog-suggest-badge");
|
||||||
|
|
||||||
|
export const showSuggestBadgeDialog = (
|
||||||
|
element: HTMLElement,
|
||||||
|
suggestBadgeDialogParams: SuggestBadgeDialogParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "hui-dialog-suggest-badge",
|
||||||
|
dialogImport: importSuggestBadgeDialog,
|
||||||
|
dialogParams: suggestBadgeDialogParams,
|
||||||
|
});
|
||||||
|
};
|
@ -52,7 +52,7 @@ export class HuiCardPicker extends LitElement {
|
|||||||
@property({ attribute: false }) public suggestedCards?: string[];
|
@property({ attribute: false }) public suggestedCards?: string[];
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "lovelaceClipboard",
|
key: "dashboardCardClipboard",
|
||||||
state: true,
|
state: true,
|
||||||
subscribe: true,
|
subscribe: true,
|
||||||
storage: "sessionStorage",
|
storage: "sessionStorage",
|
||||||
@ -490,7 +490,7 @@ export class HuiCardPicker extends LitElement {
|
|||||||
.cards-container {
|
.cards-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: 8px 8px;
|
grid-gap: 8px 8px;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,6 +560,7 @@ export class HuiCardPicker extends LitElement {
|
|||||||
|
|
||||||
.manual {
|
.manual {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
|
@ -45,7 +45,7 @@ export class HuiConditionalCardEditor
|
|||||||
@property({ attribute: false }) public lovelace?: LovelaceConfig;
|
@property({ attribute: false }) public lovelace?: LovelaceConfig;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "lovelaceClipboard",
|
key: "dashboardCardClipboard",
|
||||||
state: false,
|
state: false,
|
||||||
subscribe: false,
|
subscribe: false,
|
||||||
storage: "sessionStorage",
|
storage: "sessionStorage",
|
||||||
|
@ -67,7 +67,7 @@ export class HuiStackCardEditor
|
|||||||
@property({ attribute: false }) public lovelace?: LovelaceConfig;
|
@property({ attribute: false }) public lovelace?: LovelaceConfig;
|
||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
key: "lovelaceClipboard",
|
key: "dashboardCardClipboard",
|
||||||
state: false,
|
state: false,
|
||||||
subscribe: false,
|
subscribe: false,
|
||||||
storage: "sessionStorage",
|
storage: "sessionStorage",
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
|
import {
|
||||||
|
ensureBadgeConfig,
|
||||||
|
LovelaceBadgeConfig,
|
||||||
|
} from "../../../data/lovelace/config/badge";
|
||||||
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
||||||
import { LovelaceSectionRawConfig } from "../../../data/lovelace/config/section";
|
import { LovelaceSectionRawConfig } from "../../../data/lovelace/config/section";
|
||||||
import { LovelaceConfig } from "../../../data/lovelace/config/types";
|
import { LovelaceConfig } from "../../../data/lovelace/config/types";
|
||||||
@ -331,6 +334,17 @@ export const addBadge = (
|
|||||||
return newConfig;
|
return newConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const addBadges = (
|
||||||
|
config: LovelaceConfig,
|
||||||
|
path: LovelaceContainerPath,
|
||||||
|
badgeConfig: LovelaceBadgeConfig[]
|
||||||
|
): LovelaceConfig => {
|
||||||
|
const badges = findLovelaceItems("badges", config, path);
|
||||||
|
const newBadges = badges ? [...badges, ...badgeConfig] : [...badgeConfig];
|
||||||
|
const newConfig = updateLovelaceItems("badges", config, path, newBadges);
|
||||||
|
return newConfig;
|
||||||
|
};
|
||||||
|
|
||||||
export const replaceBadge = (
|
export const replaceBadge = (
|
||||||
config: LovelaceConfig,
|
config: LovelaceConfig,
|
||||||
path: LovelaceCardPath,
|
path: LovelaceCardPath,
|
||||||
@ -410,7 +424,7 @@ export const moveBadge = (
|
|||||||
const badge = badges![fromCardIndex];
|
const badge = badges![fromCardIndex];
|
||||||
|
|
||||||
let newConfig = deleteBadge(config, fromPath);
|
let newConfig = deleteBadge(config, fromPath);
|
||||||
newConfig = insertBadge(newConfig, toPath, badge);
|
newConfig = insertBadge(newConfig, toPath, ensureBadgeConfig(badge));
|
||||||
|
|
||||||
return newConfig;
|
return newConfig;
|
||||||
};
|
};
|
||||||
|
@ -32,6 +32,7 @@ import type { HuiFormEditor } from "./config-elements/hui-form-editor";
|
|||||||
import "./config-elements/hui-generic-entity-row-editor";
|
import "./config-elements/hui-generic-entity-row-editor";
|
||||||
import { GUISupportError } from "./gui-support-error";
|
import { GUISupportError } from "./gui-support-error";
|
||||||
import { EditSubElementEvent, GUIModeChangedEvent } from "./types";
|
import { EditSubElementEvent, GUIModeChangedEvent } from "./types";
|
||||||
|
import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
|
||||||
|
|
||||||
export interface ConfigChangedEvent {
|
export interface ConfigChangedEvent {
|
||||||
config:
|
config:
|
||||||
@ -39,7 +40,8 @@ export interface ConfigChangedEvent {
|
|||||||
| LovelaceRowConfig
|
| LovelaceRowConfig
|
||||||
| LovelaceHeaderFooterConfig
|
| LovelaceHeaderFooterConfig
|
||||||
| LovelaceCardFeatureConfig
|
| LovelaceCardFeatureConfig
|
||||||
| LovelaceStrategyConfig;
|
| LovelaceStrategyConfig
|
||||||
|
| LovelaceBadgeConfig;
|
||||||
error?: string;
|
error?: string;
|
||||||
guiModeAvailable?: boolean;
|
guiModeAvailable?: boolean;
|
||||||
}
|
}
|
||||||
|
8
src/panels/lovelace/editor/lovelace-badges.ts
Normal file
8
src/panels/lovelace/editor/lovelace-badges.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Badge } from "./types";
|
||||||
|
|
||||||
|
export const coreBadges: Badge[] = [
|
||||||
|
{
|
||||||
|
type: "entity",
|
||||||
|
showElement: true,
|
||||||
|
},
|
||||||
|
];
|
@ -127,7 +127,7 @@ export const updateLovelaceContainer = (
|
|||||||
|
|
||||||
type LovelaceItemKeys = {
|
type LovelaceItemKeys = {
|
||||||
cards: LovelaceCardConfig[];
|
cards: LovelaceCardConfig[];
|
||||||
badges: LovelaceBadgeConfig[];
|
badges: (Partial<LovelaceBadgeConfig> | string)[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateLovelaceItems = <T extends keyof LovelaceItemKeys>(
|
export const updateLovelaceItems = <T extends keyof LovelaceItemKeys>(
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types";
|
import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types";
|
||||||
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
|
||||||
import { LovelaceCardFeatureConfig } from "../card-features/types";
|
import { LovelaceCardFeatureConfig } from "../card-features/types";
|
||||||
|
import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
|
||||||
|
|
||||||
export interface YamlChangedEvent extends Event {
|
export interface YamlChangedEvent extends Event {
|
||||||
detail: {
|
detail: {
|
||||||
@ -65,6 +66,15 @@ export interface Card {
|
|||||||
isSuggested?: boolean;
|
isSuggested?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Badge {
|
||||||
|
type: string;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
showElement?: boolean;
|
||||||
|
isCustom?: boolean;
|
||||||
|
isSuggested?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface HeaderFooter {
|
export interface HeaderFooter {
|
||||||
type: LovelaceHeaderFooterConfig["type"];
|
type: LovelaceHeaderFooterConfig["type"];
|
||||||
icon?: string;
|
icon?: string;
|
||||||
@ -74,6 +84,10 @@ export interface CardPickTarget extends EventTarget {
|
|||||||
config: LovelaceCardConfig;
|
config: LovelaceCardConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BadgePickTarget extends EventTarget {
|
||||||
|
config: LovelaceBadgeConfig;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SubElementEditorConfig {
|
export interface SubElementEditorConfig {
|
||||||
index?: number;
|
index?: number;
|
||||||
elementConfig?:
|
elementConfig?:
|
||||||
|
@ -6,7 +6,7 @@ import "../../../components/entity/ha-state-label-badge";
|
|||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import type { LovelaceViewElement } from "../../../data/lovelace";
|
import type { LovelaceViewElement } from "../../../data/lovelace";
|
||||||
import {
|
import {
|
||||||
defaultBadgeConfig,
|
ensureBadgeConfig,
|
||||||
LovelaceBadgeConfig,
|
LovelaceBadgeConfig,
|
||||||
} from "../../../data/lovelace/config/badge";
|
} from "../../../data/lovelace/config/badge";
|
||||||
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
|
||||||
@ -26,7 +26,6 @@ import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dia
|
|||||||
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
|
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
|
||||||
import { deleteBadge, deleteCard } from "../editor/config-util";
|
import { deleteBadge, deleteCard } from "../editor/config-util";
|
||||||
import { confDeleteCard } from "../editor/delete-card";
|
import { confDeleteCard } from "../editor/delete-card";
|
||||||
import { getBadgeStubConfig } from "../editor/get-badge-stub-config";
|
|
||||||
import {
|
import {
|
||||||
LovelaceCardPath,
|
LovelaceCardPath,
|
||||||
parseLovelaceCardPath,
|
parseLovelaceCardPath,
|
||||||
@ -37,6 +36,7 @@ import type { HuiSection } from "../sections/hui-section";
|
|||||||
import { generateLovelaceViewStrategy } from "../strategies/get-strategy";
|
import { generateLovelaceViewStrategy } from "../strategies/get-strategy";
|
||||||
import type { Lovelace } from "../types";
|
import type { Lovelace } from "../types";
|
||||||
import { DEFAULT_VIEW_LAYOUT, PANEL_VIEW_LAYOUT } from "./const";
|
import { DEFAULT_VIEW_LAYOUT, PANEL_VIEW_LAYOUT } from "./const";
|
||||||
|
import { showCreateBadgeDialog } from "../editor/badge-editor/show-create-badge-dialog";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
@ -332,18 +332,10 @@ export class HUIView extends ReactiveElement {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this._layoutElement.addEventListener("ll-create-badge", async () => {
|
this._layoutElement.addEventListener("ll-create-badge", async () => {
|
||||||
const defaultConfig = await getBadgeStubConfig(
|
showCreateBadgeDialog(this, {
|
||||||
this.hass,
|
|
||||||
"entity",
|
|
||||||
Object.keys(this.hass.entities),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
showEditBadgeDialog(this, {
|
|
||||||
lovelaceConfig: this.lovelace.config,
|
lovelaceConfig: this.lovelace.config,
|
||||||
saveConfig: this.lovelace.saveConfig,
|
saveConfig: this.lovelace.saveConfig,
|
||||||
path: [this.index],
|
path: [this.index],
|
||||||
badgeConfig: defaultConfig,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._layoutElement.addEventListener("ll-edit-badge", (ev) => {
|
this._layoutElement.addEventListener("ll-edit-badge", (ev) => {
|
||||||
@ -368,8 +360,7 @@ export class HUIView extends ReactiveElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._badges = config.badges.map((badge) => {
|
this._badges = config.badges.map((badge) => {
|
||||||
const badgeConfig =
|
const badgeConfig = ensureBadgeConfig(badge);
|
||||||
typeof badge === "string" ? defaultBadgeConfig(badge) : badge;
|
|
||||||
const element = this._createBadgeElement(badgeConfig);
|
const element = this._createBadgeElement(badgeConfig);
|
||||||
return element;
|
return element;
|
||||||
});
|
});
|
||||||
|
@ -5586,6 +5586,10 @@
|
|||||||
"explanation": "The badge will be shown when ALL conditions below are fulfilled. If no conditions are set, the badge will always be shown."
|
"explanation": "The badge will be shown when ALL conditions below are fulfilled. If no conditions are set, the badge will always be shown."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"suggest_badge": {
|
||||||
|
"header": "[%key:ui::panel::lovelace::editor::suggest_card::header%]",
|
||||||
|
"add": "[%key:ui::panel::lovelace::editor::suggest_card::add%]"
|
||||||
|
},
|
||||||
"move_card": {
|
"move_card": {
|
||||||
"header": "Choose a view to move the card to",
|
"header": "Choose a view to move the card to",
|
||||||
"error_title": "Impossible to move the card",
|
"error_title": "Impossible to move the card",
|
||||||
@ -6047,6 +6051,15 @@
|
|||||||
"standard": "Standard (icon and state)",
|
"standard": "Standard (icon and state)",
|
||||||
"complete": "Complete (icon, name and state)"
|
"complete": "Complete (icon, name and state)"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"generic": {
|
||||||
|
"manual": "Manual",
|
||||||
|
"manual_description": "Need to add a custom badge or just want to manually write the YAML?",
|
||||||
|
"paste": "Paste from clipboard",
|
||||||
|
"paste_description": "Paste a {type} badge from the clipboard",
|
||||||
|
"suggested_badges": "Suggested badges",
|
||||||
|
"other_badges": "Other badges",
|
||||||
|
"custom_badges": "Custom badges"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
@ -6236,7 +6249,15 @@
|
|||||||
"domain": "Domain",
|
"domain": "Domain",
|
||||||
"entity": "Entity",
|
"entity": "Entity",
|
||||||
"by_entity": "By entity",
|
"by_entity": "By entity",
|
||||||
"by_card": "By Card"
|
"by_card": "By card"
|
||||||
|
},
|
||||||
|
"badge_picker": {
|
||||||
|
"no_description": "No description available.",
|
||||||
|
"custom_badge": "Custom",
|
||||||
|
"domain": "Domain",
|
||||||
|
"entity": "Entity",
|
||||||
|
"by_entity": "By entity",
|
||||||
|
"by_badge": "By badge"
|
||||||
},
|
},
|
||||||
"header-footer": {
|
"header-footer": {
|
||||||
"header": "Header",
|
"header": "Header",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user