mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-26 02:36:37 +00:00
Changes for new storage (#2228)
* Changes for new storage * Fix lint * Use indexes for editing * Use lovelace object * Use lovelace object * Lit conversion panel lovelace * Lovelace obj * Fix edit cards * Fix some bugs * Fix last bugs * Lint * Don't drop view content * Move file * Add skip button to card picker * Correctly set lovelace mode
This commit is contained in:
parent
2a23487163
commit
4f0a965573
@ -1,22 +1,23 @@
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
export interface LovelaceConfig {
|
export interface LovelaceConfig {
|
||||||
_frontendAuto: boolean;
|
|
||||||
title?: string;
|
title?: string;
|
||||||
views: LovelaceViewConfig[];
|
views: LovelaceViewConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceViewConfig {
|
export interface LovelaceViewConfig {
|
||||||
|
index?: number;
|
||||||
title?: string;
|
title?: string;
|
||||||
badges?: string[];
|
badges?: string[];
|
||||||
cards?: LovelaceCardConfig[];
|
cards?: LovelaceCardConfig[];
|
||||||
id?: string;
|
path?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceCardConfig {
|
export interface LovelaceCardConfig {
|
||||||
id?: string;
|
index?: number;
|
||||||
|
view_index?: number;
|
||||||
type: string;
|
type: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
@ -60,95 +61,11 @@ export const fetchConfig = (
|
|||||||
force,
|
force,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const migrateConfig = (hass: HomeAssistant): Promise<void> =>
|
|
||||||
hass.callWS({
|
|
||||||
type: "lovelace/config/migrate",
|
|
||||||
});
|
|
||||||
|
|
||||||
export const saveConfig = (
|
export const saveConfig = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config: LovelaceConfig | string,
|
config: LovelaceConfig
|
||||||
format: "json" | "yaml"
|
|
||||||
): Promise<void> =>
|
): Promise<void> =>
|
||||||
hass.callWS({
|
hass.callWS({
|
||||||
type: "lovelace/config/save",
|
type: "lovelace/config/save",
|
||||||
config,
|
config,
|
||||||
format,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getCardConfig = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
cardId: string
|
|
||||||
): Promise<string> =>
|
|
||||||
hass.callWS({
|
|
||||||
type: "lovelace/config/card/get",
|
|
||||||
card_id: cardId,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateCardConfig = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
cardId: string,
|
|
||||||
config: LovelaceCardConfig | string,
|
|
||||||
format: "json" | "yaml"
|
|
||||||
): Promise<void> =>
|
|
||||||
hass.callWS({
|
|
||||||
type: "lovelace/config/card/update",
|
|
||||||
card_id: cardId,
|
|
||||||
card_config: config,
|
|
||||||
format,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const deleteCard = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
cardId: string
|
|
||||||
): Promise<void> =>
|
|
||||||
hass.callWS({
|
|
||||||
type: "lovelace/config/card/delete",
|
|
||||||
card_id: cardId,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const addCard = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
viewId: string,
|
|
||||||
config: LovelaceCardConfig | string,
|
|
||||||
format: "json" | "yaml"
|
|
||||||
): Promise<void> =>
|
|
||||||
hass.callWS({
|
|
||||||
type: "lovelace/config/card/add",
|
|
||||||
view_id: viewId,
|
|
||||||
card_config: config,
|
|
||||||
format,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateViewConfig = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
viewId: string,
|
|
||||||
config: LovelaceViewConfig | string,
|
|
||||||
format: "json" | "yaml"
|
|
||||||
): Promise<void> =>
|
|
||||||
hass.callWS({
|
|
||||||
type: "lovelace/config/view/update",
|
|
||||||
view_id: viewId,
|
|
||||||
view_config: config,
|
|
||||||
format,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const deleteView = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
viewId: string
|
|
||||||
): Promise<void> =>
|
|
||||||
hass.callWS({
|
|
||||||
type: "lovelace/config/view/delete",
|
|
||||||
view_id: viewId,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const addView = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
config: LovelaceViewConfig | string,
|
|
||||||
format: "json" | "yaml"
|
|
||||||
): Promise<void> =>
|
|
||||||
hass.callWS({
|
|
||||||
type: "lovelace/config/view/add",
|
|
||||||
view_config: config,
|
|
||||||
format,
|
|
||||||
});
|
});
|
||||||
|
@ -134,8 +134,14 @@ export class HuiLightCard extends hassLocalizeLitMixin(LitElement)
|
|||||||
this._roundSliderStyle = loaded.roundSliderStyle;
|
this._roundSliderStyle = loaded.roundSliderStyle;
|
||||||
this._jQuery = loaded.jQuery;
|
this._jQuery = loaded.jQuery;
|
||||||
|
|
||||||
const brightness = this.hass!.states[this._config!.entity].attributes
|
const stateObj = this.hass!.states[this._config!.entity] as LightEntity;
|
||||||
.brightness;
|
|
||||||
|
if (!stateObj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const brightness = stateObj.attributes.brightness || 0;
|
||||||
|
|
||||||
this._jQuery("#light", this.shadowRoot).roundSlider({
|
this._jQuery("#light", this.shadowRoot).roundSlider({
|
||||||
...lightConfig,
|
...lightConfig,
|
||||||
change: (value) => this._setBrightness(value),
|
change: (value) => this._setBrightness(value),
|
||||||
@ -152,7 +158,13 @@ export class HuiLightCard extends hassLocalizeLitMixin(LitElement)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const attrs = this.hass!.states[this._config!.entity].attributes;
|
const stateObj = this.hass!.states[this._config!.entity];
|
||||||
|
|
||||||
|
if (!stateObj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attrs = stateObj.attributes;
|
||||||
|
|
||||||
this._jQuery("#light", this.shadowRoot).roundSlider({
|
this._jQuery("#light", this.shadowRoot).roundSlider({
|
||||||
value: Math.round((attrs.brightness / 254) * 100) || 0,
|
value: Math.round((attrs.brightness / 254) * 100) || 0,
|
||||||
|
@ -94,7 +94,7 @@ const computeDefaultViewStates = (hass: HomeAssistant): HassEntities => {
|
|||||||
|
|
||||||
const generateViewConfig = (
|
const generateViewConfig = (
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
id: string,
|
path: string,
|
||||||
title: string | undefined,
|
title: string | undefined,
|
||||||
icon: string | undefined,
|
icon: string | undefined,
|
||||||
entities: HassEntities,
|
entities: HassEntities,
|
||||||
@ -158,7 +158,7 @@ const generateViewConfig = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
path,
|
||||||
title,
|
title,
|
||||||
icon,
|
icon,
|
||||||
badges,
|
badges,
|
||||||
@ -228,7 +228,6 @@ export const generateLovelaceConfig = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_frontendAuto: true,
|
|
||||||
title,
|
title,
|
||||||
views,
|
views,
|
||||||
};
|
};
|
||||||
|
@ -1,32 +1,22 @@
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
|
||||||
import { showEditCardDialog } from "../editor/show-edit-card-dialog";
|
|
||||||
|
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
import { confDeleteCard } from "../editor/delete-card";
|
import { confDeleteCard } from "../editor/delete-card";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||||
|
import { Lovelace } from "../types";
|
||||||
declare global {
|
|
||||||
// for fire event
|
|
||||||
interface HASSDomEvents {
|
|
||||||
"show-edit-card": {
|
|
||||||
cardConfig?: LovelaceCardConfig;
|
|
||||||
viewId?: string | number;
|
|
||||||
add: boolean;
|
|
||||||
reloadLovelace: () => void;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HuiCardOptions extends hassLocalizeLitMixin(LitElement) {
|
export class HuiCardOptions extends hassLocalizeLitMixin(LitElement) {
|
||||||
public cardConfig?: LovelaceCardConfig;
|
public cardConfig?: LovelaceCardConfig;
|
||||||
protected hass?: HomeAssistant;
|
protected hass?: HomeAssistant;
|
||||||
|
protected lovelace?: Lovelace;
|
||||||
|
protected path?: [number, number];
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return { hass: {} };
|
return { hass: {}, lovelace: {}, path: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
@ -66,26 +56,13 @@ export class HuiCardOptions extends hassLocalizeLitMixin(LitElement) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
private _editCard(): void {
|
private _editCard(): void {
|
||||||
if (!this.cardConfig) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showEditCardDialog(this, {
|
showEditCardDialog(this, {
|
||||||
cardConfig: this.cardConfig,
|
lovelace: this.lovelace!,
|
||||||
add: false,
|
path: this.path!,
|
||||||
reloadLovelace: () => fireEvent(this, "config-refresh"),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
private _deleteCard(): void {
|
private _deleteCard(): void {
|
||||||
if (!this.cardConfig) {
|
confDeleteCard(this.lovelace!, this.path!);
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.cardConfig.id) {
|
|
||||||
this._editCard();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
confDeleteCard(this.hass!, this.cardConfig.id, () =>
|
|
||||||
fireEvent(this, "config-refresh")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,22 +2,11 @@ import { html, LitElement } from "@polymer/lit-element";
|
|||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
import { getCardElementTag } from "../../common/get-card-element-tag";
|
||||||
import { getCardElementTag } from "../common/get-card-element-tag";
|
import { CardPickTarget } from "../types";
|
||||||
import { CardPickTarget } from "./types";
|
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
|
||||||
|
|
||||||
import { uid } from "../../../common/util/uid";
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HASSDomEvents {
|
|
||||||
"card-picked": {
|
|
||||||
config: LovelaceCardConfig;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cards = [
|
const cards = [
|
||||||
{ name: "Alarm panel", type: "alarm-panel" },
|
{ name: "Alarm panel", type: "alarm-panel" },
|
||||||
@ -47,7 +36,8 @@ const cards = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export class HuiCardPicker extends hassLocalizeLitMixin(LitElement) {
|
export class HuiCardPicker extends hassLocalizeLitMixin(LitElement) {
|
||||||
protected hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
|
public cardPicked?: (cardConf: LovelaceCardConfig) => void;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
@ -90,16 +80,14 @@ export class HuiCardPicker extends hassLocalizeLitMixin(LitElement) {
|
|||||||
const tag = getCardElementTag(type);
|
const tag = getCardElementTag(type);
|
||||||
|
|
||||||
const elClass = customElements.get(tag);
|
const elClass = customElements.get(tag);
|
||||||
let config: LovelaceCardConfig = { type, id: uid() };
|
let config: LovelaceCardConfig = { type };
|
||||||
|
|
||||||
if (elClass && elClass.getStubConfig) {
|
if (elClass && elClass.getStubConfig) {
|
||||||
const cardConfig = elClass.getStubConfig(this.hass);
|
const cardConfig = elClass.getStubConfig(this.hass);
|
||||||
config = { ...config, ...cardConfig };
|
config = { ...config, ...cardConfig };
|
||||||
}
|
}
|
||||||
|
|
||||||
fireEvent(this, "card-picked", {
|
this.cardPicked!(config);
|
||||||
config,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,12 @@
|
|||||||
import "@polymer/paper-input/paper-textarea";
|
import "@polymer/paper-input/paper-textarea";
|
||||||
|
|
||||||
import createCardElement from "../common/create-card-element";
|
import createCardElement from "../../common/create-card-element";
|
||||||
import createErrorCardConfig from "../common/create-error-card-config";
|
import createErrorCardConfig from "../../common/create-error-card-config";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||||
import { LovelaceCard } from "../types";
|
import { LovelaceCard } from "../../types";
|
||||||
import { ConfigError } from "./types";
|
import { ConfigError } from "../types";
|
||||||
import { getCardElementTag } from "../common/get-card-element-tag";
|
import { getCardElementTag } from "../../common/get-card-element-tag";
|
||||||
|
|
||||||
export class HuiCardPreview extends HTMLElement {
|
export class HuiCardPreview extends HTMLElement {
|
||||||
private _hass?: HomeAssistant;
|
private _hass?: HomeAssistant;
|
@ -1,11 +1,12 @@
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
import { HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||||
import "./hui-edit-card";
|
import "./hui-edit-card";
|
||||||
import "./hui-migrate-config";
|
import "./hui-dialog-pick-card";
|
||||||
|
import { EditCardDialogParams } from "./show-edit-card-dialog";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
@ -18,66 +19,67 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EditCardDialogParams {
|
|
||||||
cardConfig?: LovelaceCardConfig;
|
|
||||||
viewId?: string | number;
|
|
||||||
add: boolean;
|
|
||||||
reloadLovelace: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HuiDialogEditCard extends LitElement {
|
export class HuiDialogEditCard extends LitElement {
|
||||||
protected hass?: HomeAssistant;
|
protected hass?: HomeAssistant;
|
||||||
private _params?: EditCardDialogParams;
|
private _params?: EditCardDialogParams;
|
||||||
|
private _cardConfig?: LovelaceCardConfig;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
_params: {},
|
_params: {},
|
||||||
|
_cardConfig: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._cardPicked = this._cardPicked.bind(this);
|
||||||
|
this._cancel = this._cancel.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
public async showDialog(params: EditCardDialogParams): Promise<void> {
|
public async showDialog(params: EditCardDialogParams): Promise<void> {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
await this.updateComplete;
|
this._cardConfig =
|
||||||
(this.shadowRoot!.children[0] as any).showDialog();
|
params.path.length === 2
|
||||||
|
? (this._cardConfig = params.lovelace.config.views[
|
||||||
|
params.path[0]
|
||||||
|
].cards![params.path[1]])
|
||||||
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._params) {
|
if (!this._params) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
if (
|
if (!this._cardConfig) {
|
||||||
(!this._params.add &&
|
// Card picker
|
||||||
this._params.cardConfig &&
|
|
||||||
!("id" in this._params.cardConfig)) ||
|
|
||||||
(this._params.add && !this._params.viewId)
|
|
||||||
) {
|
|
||||||
return html`
|
return html`
|
||||||
<hui-migrate-config
|
<hui-dialog-pick-card
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
@reload-lovelace="${this._params.reloadLovelace}"
|
.cardPicked="${this._cardPicked}"
|
||||||
></hui-migrate-config>
|
></hui-dialog-pick-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<hui-edit-card
|
<hui-edit-card
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
.viewId="${this._params.viewId}"
|
.lovelace="${this._params.lovelace}"
|
||||||
.cardConfig="${this._params.cardConfig}"
|
.path="${this._params.path}"
|
||||||
@reload-lovelace="${this._params.reloadLovelace}"
|
.cardConfig="${this._cardConfig}"
|
||||||
@cancel-edit-card="${this._cancel}"
|
.closeDialog="${this._cancel}"
|
||||||
>
|
>
|
||||||
</hui-edit-card>
|
</hui-edit-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _cardPicked(cardConf: LovelaceCardConfig) {
|
||||||
|
this._cardConfig = cardConf;
|
||||||
|
}
|
||||||
|
|
||||||
private _cancel() {
|
private _cancel() {
|
||||||
this._params = {
|
this._params = undefined;
|
||||||
add: false,
|
this._cardConfig = undefined;
|
||||||
reloadLovelace: () => {
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
|||||||
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
|
import { TemplateResult } from "lit-html";
|
||||||
|
import "@polymer/paper-dialog/paper-dialog";
|
||||||
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
|
|
||||||
|
import "./hui-card-picker";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||||
|
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||||
|
|
||||||
|
export class HuiDialogPickCard extends hassLocalizeLitMixin(LitElement) {
|
||||||
|
public hass?: HomeAssistant;
|
||||||
|
public cardPicked?: (cardConf: LovelaceCardConfig) => void;
|
||||||
|
|
||||||
|
static get properties(): PropertyDeclarations {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<paper-dialog with-backdrop opened>
|
||||||
|
<h2>${this.localize("ui.panel.lovelace.editor.edit_card.header")}</h2>
|
||||||
|
<paper-dialog-scrollable>
|
||||||
|
<hui-card-picker
|
||||||
|
.hass="${this.hass}"
|
||||||
|
.cardPicked="${this.cardPicked}"
|
||||||
|
></hui-card-picker>
|
||||||
|
</paper-dialog-scrollable>
|
||||||
|
<div class="paper-dialog-buttons">
|
||||||
|
<paper-button @click="${this._skipPick}">SKIP</paper-button>
|
||||||
|
</div>
|
||||||
|
</paper-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _skipPick() {
|
||||||
|
this.cardPicked!({ type: "" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-dialog-pick-card": HuiDialogPickCard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("hui-dialog-pick-card", HuiDialogPickCard);
|
@ -15,31 +15,22 @@ import "@polymer/paper-dialog/paper-dialog";
|
|||||||
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import {
|
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||||
addCard,
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
updateCardConfig,
|
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||||
LovelaceCardConfig,
|
|
||||||
} from "../../../data/lovelace";
|
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
|
||||||
|
|
||||||
import "./hui-yaml-editor";
|
import "./hui-yaml-editor";
|
||||||
import "./hui-card-picker";
|
|
||||||
import "./hui-card-preview";
|
import "./hui-card-preview";
|
||||||
// This is not a duplicate import, one is for types, one is for element.
|
// This is not a duplicate import, one is for types, one is for element.
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
import { HuiCardPreview } from "./hui-card-preview";
|
import { HuiCardPreview } from "./hui-card-preview";
|
||||||
import { LovelaceCardEditor } from "../types";
|
import { LovelaceCardEditor, Lovelace } from "../../types";
|
||||||
import {
|
import { YamlChangedEvent, ConfigValue, ConfigError } from "../types";
|
||||||
YamlChangedEvent,
|
import { extYamlSchema } from "../yaml-ext-schema";
|
||||||
CardPickedEvent,
|
import { EntityConfig } from "../../entity-rows/types";
|
||||||
ConfigValue,
|
import { getCardElementTag } from "../../common/get-card-element-tag";
|
||||||
ConfigError,
|
import { addCard, replaceCard } from "../config-util";
|
||||||
} from "./types";
|
|
||||||
import { extYamlSchema } from "./yaml-ext-schema";
|
|
||||||
import { EntityConfig } from "../entity-rows/types";
|
|
||||||
import { getCardElementTag } from "../common/get-card-element-tag";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
@ -52,17 +43,30 @@ declare global {
|
|||||||
"config-changed": {
|
"config-changed": {
|
||||||
config: LovelaceCardConfig;
|
config: LovelaceCardConfig;
|
||||||
};
|
};
|
||||||
"cancel-edit-card": {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
||||||
|
public hass?: HomeAssistant;
|
||||||
|
public lovelace?: Lovelace;
|
||||||
|
public path?: [number] | [number, number];
|
||||||
|
public cardConfig?: LovelaceCardConfig;
|
||||||
|
public closeDialog?: () => void;
|
||||||
|
private _configElement?: LovelaceCardEditor | null;
|
||||||
|
private _uiEditor?: boolean;
|
||||||
|
private _configValue?: ConfigValue;
|
||||||
|
private _configState?: string;
|
||||||
|
private _loading?: boolean;
|
||||||
|
private _saving: boolean;
|
||||||
|
private _errorMsg?: TemplateResult;
|
||||||
|
private _cardType?: string;
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return {
|
return {
|
||||||
hass: {},
|
hass: {},
|
||||||
cardConfig: {},
|
cardConfig: {},
|
||||||
viewId: {},
|
viewIndex: {},
|
||||||
_cardId: {},
|
_cardIndex: {},
|
||||||
_configElement: {},
|
_configElement: {},
|
||||||
_configValue: {},
|
_configValue: {},
|
||||||
_configState: {},
|
_configState: {},
|
||||||
@ -81,38 +85,15 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
return this.shadowRoot!.querySelector("hui-card-preview")!;
|
return this.shadowRoot!.querySelector("hui-card-preview")!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public cardConfig?: LovelaceCardConfig;
|
|
||||||
public viewId?: string | number;
|
|
||||||
protected hass?: HomeAssistant;
|
|
||||||
private _cardId?: string;
|
|
||||||
private _configElement?: LovelaceCardEditor | null;
|
|
||||||
private _uiEditor?: boolean;
|
|
||||||
private _configValue?: ConfigValue;
|
|
||||||
private _configState?: string;
|
|
||||||
private _loading?: boolean;
|
|
||||||
private _saving: boolean;
|
|
||||||
private _errorMsg?: TemplateResult;
|
|
||||||
private _cardType?: string;
|
|
||||||
|
|
||||||
protected constructor() {
|
protected constructor() {
|
||||||
super();
|
super();
|
||||||
this._saving = false;
|
this._saving = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async showDialog(): Promise<void> {
|
|
||||||
// Wait till dialog is rendered.
|
|
||||||
if (this._dialog == null) {
|
|
||||||
await this.updateComplete;
|
|
||||||
}
|
|
||||||
this._dialog.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues): void {
|
protected updated(changedProperties: PropertyValues): void {
|
||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
if (
|
|
||||||
!changedProperties.has("cardConfig") &&
|
if (!changedProperties.has("cardConfig")) {
|
||||||
!changedProperties.has("viewId")
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,17 +103,8 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
this._errorMsg = undefined;
|
this._errorMsg = undefined;
|
||||||
this._configElement = undefined;
|
this._configElement = undefined;
|
||||||
|
|
||||||
if (this.cardConfig && String(this.cardConfig.id) !== this._cardId) {
|
this._loading = true;
|
||||||
this._loading = true;
|
this._loadConfigElement(this.cardConfig!);
|
||||||
this._cardId = String(this.cardConfig.id);
|
|
||||||
this._loadConfigElement(this.cardConfig);
|
|
||||||
} else {
|
|
||||||
this._cardId = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.viewId && !this.cardConfig) {
|
|
||||||
this._resizeDialog();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
@ -147,7 +119,6 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
content = html`
|
content = html`
|
||||||
<hui-yaml-editor
|
<hui-yaml-editor
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
.cardId="${this._cardId}"
|
|
||||||
.yaml="${this._configValue!.value}"
|
.yaml="${this._configValue!.value}"
|
||||||
@yaml-changed="${this._handleYamlChanged}"
|
@yaml-changed="${this._handleYamlChanged}"
|
||||||
></hui-yaml-editor>
|
></hui-yaml-editor>
|
||||||
@ -157,18 +128,11 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
<hr />
|
<hr />
|
||||||
<hui-card-preview .hass="${this.hass}"> </hui-card-preview>
|
<hui-card-preview .hass="${this.hass}"> </hui-card-preview>
|
||||||
`;
|
`;
|
||||||
} else if (this.viewId && !this.cardConfig) {
|
|
||||||
content = html`
|
|
||||||
<hui-card-picker
|
|
||||||
.hass="${this.hass}"
|
|
||||||
@card-picked="${this._handleCardPicked}"
|
|
||||||
></hui-card-picker>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<paper-dialog with-backdrop>
|
<paper-dialog with-backdrop opened>
|
||||||
<h2>${this.localize("ui.panel.lovelace.editor.edit_card.header")}</h2>
|
<h2>${this.localize("ui.panel.lovelace.editor.edit_card.header")}</h2>
|
||||||
<paper-spinner
|
<paper-spinner
|
||||||
?active="${this._loading}"
|
?active="${this._loading}"
|
||||||
@ -203,7 +167,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
)
|
)
|
||||||
}</paper-button
|
}</paper-button
|
||||||
>
|
>
|
||||||
<paper-button @click="${this._closeDialog}"
|
<paper-button @click="${this.closeDialog}"
|
||||||
>${this.localize("ui.common.cancel")}</paper-button
|
>${this.localize("ui.common.cancel")}</paper-button
|
||||||
>
|
>
|
||||||
<paper-button
|
<paper-button
|
||||||
@ -272,15 +236,6 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _save(): void {
|
|
||||||
this._saving = true;
|
|
||||||
this._updateConfigInBackend();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _saveDone(): void {
|
|
||||||
this._saving = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _loadedDialog(): Promise<void> {
|
private async _loadedDialog(): Promise<void> {
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
this._loading = false;
|
this._loading = false;
|
||||||
@ -292,58 +247,42 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
fireEvent(this._dialog, "iron-resize");
|
fireEvent(this._dialog, "iron-resize");
|
||||||
}
|
}
|
||||||
|
|
||||||
private _closeDialog(): void {
|
private async _save(): Promise<void> {
|
||||||
this.cardConfig = undefined;
|
|
||||||
this.viewId = undefined;
|
|
||||||
fireEvent(this, "cancel-edit-card");
|
|
||||||
this._dialog.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _updateConfigInBackend(): Promise<void> {
|
|
||||||
if (!this._isConfigValid()) {
|
if (!this._isConfigValid()) {
|
||||||
alert("Your config is not valid, please fix your config before saving.");
|
alert("Your config is not valid, please fix your config before saving.");
|
||||||
this._saveDone();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._isConfigChanged()) {
|
if (!this._isConfigChanged()) {
|
||||||
this._closeDialog();
|
this.closeDialog!();
|
||||||
this._saveDone();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._saving = true;
|
||||||
|
|
||||||
|
const cardConf: LovelaceCardConfig =
|
||||||
|
this._configValue!.format === "yaml"
|
||||||
|
? yaml.safeLoad(this._configValue!.value!, {
|
||||||
|
schema: extYamlSchema,
|
||||||
|
})
|
||||||
|
: this._configValue!.value!;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.viewId) {
|
const lovelace = this.lovelace!;
|
||||||
await addCard(
|
await lovelace.saveConfig(
|
||||||
this.hass!,
|
this._creatingCard
|
||||||
String(this.viewId),
|
? addCard(lovelace.config, this.path as [number], cardConf)
|
||||||
this._configValue!.value!,
|
: replaceCard(
|
||||||
this._configValue!.format
|
lovelace.config,
|
||||||
);
|
this.path as [number, number],
|
||||||
} else {
|
cardConf
|
||||||
await updateCardConfig(
|
)
|
||||||
this.hass!,
|
);
|
||||||
this._cardId!,
|
this.closeDialog!();
|
||||||
this._configValue!.value!,
|
|
||||||
this._configValue!.format
|
|
||||||
);
|
|
||||||
}
|
|
||||||
fireEvent(this, "reload-lovelace");
|
|
||||||
this._closeDialog();
|
|
||||||
this._saveDone();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(`Saving failed: ${err.message}`);
|
alert(`Saving failed: ${err.message}`);
|
||||||
this._saveDone();
|
} finally {
|
||||||
}
|
this._saving = false;
|
||||||
}
|
|
||||||
|
|
||||||
private async _handleCardPicked(ev: CardPickedEvent): Promise<void> {
|
|
||||||
const succes = await this._loadConfigElement(ev.detail.config);
|
|
||||||
if (!succes) {
|
|
||||||
this._configValue = {
|
|
||||||
format: "yaml",
|
|
||||||
value: yaml.safeDump(ev.detail.config),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,14 +333,10 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
|
|
||||||
private async _toggleEditor(): Promise<void> {
|
private async _toggleEditor(): Promise<void> {
|
||||||
if (this._uiEditor && this._configValue!.format === "json") {
|
if (this._uiEditor && this._configValue!.format === "json") {
|
||||||
if (this._isConfigChanged()) {
|
this._configValue = {
|
||||||
this._configValue = {
|
format: "yaml",
|
||||||
format: "yaml",
|
value: yaml.safeDump(this._configValue!.value),
|
||||||
value: yaml.safeDump(this._configValue!.value),
|
};
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this._configValue = { format: "yaml", value: undefined };
|
|
||||||
}
|
|
||||||
this._uiEditor = !this._uiEditor;
|
this._uiEditor = !this._uiEditor;
|
||||||
} else if (this._configElement && this._configValue!.format === "yaml") {
|
} else if (this._configElement && this._configValue!.format === "yaml") {
|
||||||
const yamlConfig = this._configValue!.value;
|
const yamlConfig = this._configValue!.value;
|
||||||
@ -438,12 +373,12 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _isConfigChanged(): boolean {
|
private _isConfigChanged(): boolean {
|
||||||
if (this.viewId) {
|
if (this._creatingCard) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const configValue =
|
const configValue =
|
||||||
this._configValue!.format === "yaml"
|
this._configValue!.format === "yaml"
|
||||||
? yaml.safeDump(this._configValue!.value)
|
? yaml.safeLoad(this._configValue!.value)
|
||||||
: this._configValue!.value;
|
: this._configValue!.value;
|
||||||
return JSON.stringify(configValue) !== JSON.stringify(this.cardConfig);
|
return JSON.stringify(configValue) !== JSON.stringify(this.cardConfig);
|
||||||
}
|
}
|
||||||
@ -465,6 +400,7 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
if (elClass && elClass.getConfigElement) {
|
if (elClass && elClass.getConfigElement) {
|
||||||
configElement = await elClass.getConfigElement();
|
configElement = await elClass.getConfigElement();
|
||||||
} else {
|
} else {
|
||||||
|
this._configValue = { format: "yaml", value: yaml.safeDump(conf) };
|
||||||
this._uiEditor = false;
|
this._uiEditor = false;
|
||||||
this._configElement = null;
|
this._configElement = null;
|
||||||
return false;
|
return false;
|
||||||
@ -477,6 +413,10 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
Your config is not supported by the UI editor:<br /><b>${err.message}</b
|
Your config is not supported by the UI editor:<br /><b>${err.message}</b
|
||||||
><br />Falling back to YAML editor.
|
><br />Falling back to YAML editor.
|
||||||
`;
|
`;
|
||||||
|
this._configValue = {
|
||||||
|
format: "yaml",
|
||||||
|
value: yaml.safeDump(conf),
|
||||||
|
};
|
||||||
this._uiEditor = false;
|
this._uiEditor = false;
|
||||||
this._configElement = null;
|
this._configElement = null;
|
||||||
return false;
|
return false;
|
||||||
@ -492,6 +432,10 @@ export class HuiEditCard extends hassLocalizeLitMixin(LitElement) {
|
|||||||
this._updatePreview(conf);
|
this._updatePreview(conf);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get _creatingCard(): boolean {
|
||||||
|
return this.path!.length === 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
@ -1,42 +1,29 @@
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
import "@polymer/paper-input/paper-textarea";
|
import "@polymer/paper-input/paper-textarea";
|
||||||
import "@polymer/paper-spinner/paper-spinner";
|
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { getCardConfig } from "../../../data/lovelace";
|
|
||||||
|
|
||||||
export class HuiYAMLEditor extends LitElement {
|
export class HuiYAMLEditor extends LitElement {
|
||||||
public cardId?: string;
|
|
||||||
protected hass?: HomeAssistant;
|
protected hass?: HomeAssistant;
|
||||||
private _yaml?: string;
|
private _yaml?: string;
|
||||||
private _loading?: boolean;
|
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
static get properties(): PropertyDeclarations {
|
||||||
return { _yaml: {}, cardId: {} };
|
return { _yaml: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
set yaml(yaml: string) {
|
set yaml(yaml: string) {
|
||||||
if (yaml === undefined) {
|
if (yaml === undefined) {
|
||||||
this._loading = true;
|
return;
|
||||||
this._loadConfig();
|
|
||||||
} else {
|
} else {
|
||||||
this._yaml = yaml;
|
this._yaml = yaml;
|
||||||
if (this._loading) {
|
|
||||||
this._loading = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<paper-spinner
|
|
||||||
?active="${this._loading}"
|
|
||||||
alt="Loading"
|
|
||||||
class="center"
|
|
||||||
></paper-spinner>
|
|
||||||
<paper-textarea
|
<paper-textarea
|
||||||
max-rows="10"
|
max-rows="10"
|
||||||
.value="${this._yaml}"
|
.value="${this._yaml}"
|
||||||
@ -51,31 +38,10 @@ export class HuiYAMLEditor extends LitElement {
|
|||||||
paper-textarea {
|
paper-textarea {
|
||||||
--paper-input-container-shared-input-style_-_font-family: monospace;
|
--paper-input-container-shared-input-style_-_font-family: monospace;
|
||||||
}
|
}
|
||||||
.center {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
paper-spinner {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
paper-spinner[active] {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadConfig(): Promise<void> {
|
|
||||||
if (!this.hass || !this.cardId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._yaml = await getCardConfig(this.hass, this.cardId);
|
|
||||||
if (this._loading) {
|
|
||||||
this._loading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _valueChanged(ev: Event): void {
|
private _valueChanged(ev: Event): void {
|
||||||
const target = ev.target! as any;
|
const target = ev.target! as any;
|
||||||
this._yaml = target.value;
|
this._yaml = target.value;
|
@ -1,5 +1,5 @@
|
|||||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { Lovelace } from "../../types";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
@ -13,10 +13,8 @@ const dialogShowEvent = "show-edit-card";
|
|||||||
const dialogTag = "hui-dialog-edit-card";
|
const dialogTag = "hui-dialog-edit-card";
|
||||||
|
|
||||||
export interface EditCardDialogParams {
|
export interface EditCardDialogParams {
|
||||||
cardConfig?: LovelaceCardConfig;
|
lovelace: Lovelace;
|
||||||
viewId?: string | number;
|
path: [number] | [number, number];
|
||||||
add: boolean;
|
|
||||||
reloadLovelace: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const registerEditCardDialog = (element: HTMLElement) =>
|
const registerEditCardDialog = (element: HTMLElement) =>
|
@ -33,7 +33,6 @@ const entitiesConfigStruct = struct.union([
|
|||||||
|
|
||||||
const cardConfigStruct = struct({
|
const cardConfigStruct = struct({
|
||||||
type: "string",
|
type: "string",
|
||||||
id: "string|number",
|
|
||||||
title: "string|number?",
|
title: "string|number?",
|
||||||
theme: "string?",
|
theme: "string?",
|
||||||
show_header_toggle: "boolean?",
|
show_header_toggle: "boolean?",
|
||||||
|
@ -32,7 +32,6 @@ const entitiesConfigStruct = struct.union([
|
|||||||
|
|
||||||
const cardConfigStruct = struct({
|
const cardConfigStruct = struct({
|
||||||
type: "string",
|
type: "string",
|
||||||
id: "string|number",
|
|
||||||
title: "string|number?",
|
title: "string|number?",
|
||||||
theme: "string?",
|
theme: "string?",
|
||||||
columns: "number?",
|
columns: "number?",
|
||||||
|
117
src/panels/lovelace/editor/config-util.ts
Normal file
117
src/panels/lovelace/editor/config-util.ts
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import {
|
||||||
|
LovelaceConfig,
|
||||||
|
LovelaceCardConfig,
|
||||||
|
LovelaceViewConfig,
|
||||||
|
} from "../../../data/lovelace";
|
||||||
|
|
||||||
|
export const addCard = (
|
||||||
|
config: LovelaceConfig,
|
||||||
|
path: [number],
|
||||||
|
cardConfig: LovelaceCardConfig
|
||||||
|
): LovelaceConfig => {
|
||||||
|
const [viewIndex] = path;
|
||||||
|
const views: LovelaceViewConfig[] = [];
|
||||||
|
|
||||||
|
config.views.forEach((viewConf, index) => {
|
||||||
|
if (index !== viewIndex) {
|
||||||
|
views.push(config.views[index]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cards = viewConf.cards
|
||||||
|
? [...viewConf.cards, cardConfig]
|
||||||
|
: [cardConfig];
|
||||||
|
|
||||||
|
views.push({
|
||||||
|
...viewConf,
|
||||||
|
cards,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
views,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const replaceCard = (
|
||||||
|
config: LovelaceConfig,
|
||||||
|
path: [number, number],
|
||||||
|
cardConfig: LovelaceCardConfig
|
||||||
|
): LovelaceConfig => {
|
||||||
|
const [viewIndex, cardIndex] = path;
|
||||||
|
const views: LovelaceViewConfig[] = [];
|
||||||
|
|
||||||
|
config.views.forEach((viewConf, index) => {
|
||||||
|
if (index !== viewIndex) {
|
||||||
|
views.push(config.views[index]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
views.push({
|
||||||
|
...viewConf,
|
||||||
|
cards: (viewConf.cards || []).map((origConf, ind) =>
|
||||||
|
ind === cardIndex ? cardConfig : origConf
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
views,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteCard = (
|
||||||
|
config: LovelaceConfig,
|
||||||
|
path: [number, number]
|
||||||
|
): LovelaceConfig => {
|
||||||
|
const [viewIndex, cardIndex] = path;
|
||||||
|
const views: LovelaceViewConfig[] = [];
|
||||||
|
|
||||||
|
config.views.forEach((viewConf, index) => {
|
||||||
|
if (index !== viewIndex) {
|
||||||
|
views.push(config.views[index]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
views.push({
|
||||||
|
...viewConf,
|
||||||
|
cards: (viewConf.cards || []).filter(
|
||||||
|
(_origConf, ind) => ind !== cardIndex
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
views,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addView = (
|
||||||
|
config: LovelaceConfig,
|
||||||
|
viewConfig: LovelaceViewConfig
|
||||||
|
): LovelaceConfig => ({
|
||||||
|
...config,
|
||||||
|
views: config.views.concat(viewConfig),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const replaceView = (
|
||||||
|
config: LovelaceConfig,
|
||||||
|
viewIndex: number,
|
||||||
|
viewConfig: LovelaceViewConfig
|
||||||
|
): LovelaceConfig => ({
|
||||||
|
...config,
|
||||||
|
views: config.views.map((origView, index) =>
|
||||||
|
index === viewIndex ? viewConfig : origView
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteView = (
|
||||||
|
config: LovelaceConfig,
|
||||||
|
viewIndex: number
|
||||||
|
): LovelaceConfig => ({
|
||||||
|
...config,
|
||||||
|
views: config.views.filter((_origView, index) => index !== viewIndex),
|
||||||
|
});
|
@ -1,17 +1,15 @@
|
|||||||
import { deleteCard } from "../../../data/lovelace";
|
import { Lovelace } from "../types";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { deleteCard } from "./config-util";
|
||||||
|
|
||||||
export async function confDeleteCard(
|
export async function confDeleteCard(
|
||||||
hass: HomeAssistant,
|
lovelace: Lovelace,
|
||||||
cardId: string,
|
path: [number, number]
|
||||||
reloadLovelace: () => void
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!confirm("Are you sure you want to delete this card?")) {
|
if (!confirm("Are you sure you want to delete this card?")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await deleteCard(hass, String(cardId));
|
await lovelace.saveConfig(deleteCard(lovelace.config, path));
|
||||||
reloadLovelace();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(`Deleting failed: ${err.message}`);
|
alert(`Deleting failed: ${err.message}`);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import { deleteView } from "../../../data/lovelace";
|
|
||||||
import { HomeAssistant } from "../../../types";
|
|
||||||
|
|
||||||
export async function confDeleteView(
|
|
||||||
hass: HomeAssistant,
|
|
||||||
viewId: string,
|
|
||||||
reloadLovelace: () => void
|
|
||||||
): Promise<void> {
|
|
||||||
if (!confirm("Are you sure you want to delete this view?")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await deleteView(hass, String(viewId));
|
|
||||||
reloadLovelace();
|
|
||||||
} catch (err) {
|
|
||||||
alert(`Deleting failed: ${err.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,40 +10,8 @@ import "@polymer/paper-button/paper-button";
|
|||||||
|
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
import {
|
|
||||||
saveConfig,
|
|
||||||
migrateConfig,
|
|
||||||
LovelaceConfig,
|
|
||||||
} from "../../../data/lovelace";
|
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
|
import { SaveDialogParams } from "./show-save-config-dialog";
|
||||||
declare global {
|
|
||||||
// for fire event
|
|
||||||
interface HASSDomEvents {
|
|
||||||
"show-save-config": SaveDialogParams;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const dialogShowEvent = "show-save-config";
|
|
||||||
const dialogTag = "hui-dialog-save-config";
|
|
||||||
|
|
||||||
export interface SaveDialogParams {
|
|
||||||
config: LovelaceConfig;
|
|
||||||
reloadLovelace: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const registerSaveDialog = (element: HTMLElement) =>
|
|
||||||
fireEvent(element, "register-dialog", {
|
|
||||||
dialogShowEvent,
|
|
||||||
dialogTag,
|
|
||||||
dialogImport: () => import("./hui-dialog-save-config"),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const showSaveDialog = (
|
|
||||||
element: HTMLElement,
|
|
||||||
saveDialogParams: SaveDialogParams
|
|
||||||
) => fireEvent(element, dialogShowEvent, saveDialogParams);
|
|
||||||
|
|
||||||
export class HuiSaveConfig extends hassLocalizeLitMixin(LitElement) {
|
export class HuiSaveConfig extends hassLocalizeLitMixin(LitElement) {
|
||||||
protected hass?: HomeAssistant;
|
protected hass?: HomeAssistant;
|
||||||
@ -137,13 +105,11 @@ export class HuiSaveConfig extends hassLocalizeLitMixin(LitElement) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._saving = true;
|
this._saving = true;
|
||||||
delete this._params.config._frontendAuto;
|
|
||||||
try {
|
try {
|
||||||
await saveConfig(this.hass, this._params.config, "json");
|
const lovelace = this._params!.lovelace;
|
||||||
await migrateConfig(this.hass);
|
await lovelace.saveConfig(lovelace.config);
|
||||||
this._saving = false;
|
this._saving = false;
|
||||||
this._closeDialog();
|
this._closeDialog();
|
||||||
this._params.reloadLovelace!();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(`Saving failed: ${err.message}`);
|
alert(`Saving failed: ${err.message}`);
|
||||||
this._saving = false;
|
this._saving = false;
|
||||||
@ -157,4 +123,4 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define(dialogTag, HuiSaveConfig);
|
customElements.define("hui-dialog-save-config", HuiSaveConfig);
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
|
||||||
import { TemplateResult } from "lit-html";
|
|
||||||
|
|
||||||
import "@polymer/paper-spinner/paper-spinner";
|
|
||||||
import "@polymer/paper-dialog/paper-dialog";
|
|
||||||
// This is not a duplicate import, one is for types, one is for element.
|
|
||||||
// tslint:disable-next-line
|
|
||||||
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
|
||||||
import "@polymer/paper-button/paper-button";
|
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../types";
|
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
|
||||||
import { migrateConfig } from "../../../data/lovelace";
|
|
||||||
|
|
||||||
export class HuiMigrateConfig extends hassLocalizeLitMixin(LitElement) {
|
|
||||||
protected hass?: HomeAssistant;
|
|
||||||
private _migrating?: boolean;
|
|
||||||
|
|
||||||
static get properties(): PropertyDeclarations {
|
|
||||||
return { _hass: {}, _migrating: {} };
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _dialog(): PaperDialogElement {
|
|
||||||
return this.shadowRoot!.querySelector("paper-dialog")!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async showDialog(): Promise<void> {
|
|
||||||
// Wait till dialog is rendered.
|
|
||||||
if (this._dialog == null) {
|
|
||||||
await this.updateComplete;
|
|
||||||
}
|
|
||||||
this._dialog.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
${this.renderStyle()}
|
|
||||||
<paper-dialog with-backdrop>
|
|
||||||
<h2>${this.localize("ui.panel.lovelace.editor.migrate.header")}</h2>
|
|
||||||
<paper-dialog-scrollable>
|
|
||||||
<p>${this.localize("ui.panel.lovelace.editor.migrate.para_no_id")}</p>
|
|
||||||
<p>
|
|
||||||
${this.localize("ui.panel.lovelace.editor.migrate.para_migrate")}
|
|
||||||
</p>
|
|
||||||
</paper-dialog-scrollable>
|
|
||||||
<div class="paper-dialog-buttons">
|
|
||||||
<paper-button @click="${this._closeDialog}"
|
|
||||||
>${this.localize("ui.common.cancel")}</paper-button
|
|
||||||
>
|
|
||||||
<paper-button
|
|
||||||
?disabled="${this._migrating}"
|
|
||||||
@click="${this._migrateConfig}"
|
|
||||||
>
|
|
||||||
<paper-spinner
|
|
||||||
?active="${this._migrating}"
|
|
||||||
alt="Saving"
|
|
||||||
></paper-spinner>
|
|
||||||
${
|
|
||||||
this.localize("ui.panel.lovelace.editor.migrate.migrate")
|
|
||||||
}</paper-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</paper-dialog>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderStyle(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
paper-dialog {
|
|
||||||
width: 650px;
|
|
||||||
}
|
|
||||||
paper-spinner {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
paper-spinner[active] {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
paper-button paper-spinner {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _closeDialog(): void {
|
|
||||||
this._dialog.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _migrateConfig(): Promise<void> {
|
|
||||||
this._migrating = true;
|
|
||||||
try {
|
|
||||||
await migrateConfig(this.hass!);
|
|
||||||
this._closeDialog();
|
|
||||||
this._migrating = false;
|
|
||||||
fireEvent(this, "reload-lovelace");
|
|
||||||
} catch (err) {
|
|
||||||
alert(`Migration failed: ${err.message}`);
|
|
||||||
this._migrating = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"hui-migrate-config": HuiMigrateConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("hui-migrate-config", HuiMigrateConfig);
|
|
33
src/panels/lovelace/editor/show-save-config-dialog.ts
Normal file
33
src/panels/lovelace/editor/show-save-config-dialog.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { Lovelace } from "../types";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// for fire event
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"show-save-config": SaveDialogParams;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogShowEvent = "show-save-config";
|
||||||
|
const dialogTag = "hui-dialog-save-config";
|
||||||
|
|
||||||
|
export interface SaveDialogParams {
|
||||||
|
lovelace: Lovelace;
|
||||||
|
}
|
||||||
|
|
||||||
|
let registeredDialog = false;
|
||||||
|
|
||||||
|
export const showSaveDialog = (
|
||||||
|
element: HTMLElement,
|
||||||
|
saveDialogParams: SaveDialogParams
|
||||||
|
) => {
|
||||||
|
if (!registeredDialog) {
|
||||||
|
registeredDialog = true;
|
||||||
|
fireEvent(element, "register-dialog", {
|
||||||
|
dialogShowEvent,
|
||||||
|
dialogTag,
|
||||||
|
dialogImport: () => import("./hui-dialog-save-config"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fireEvent(element, dialogShowEvent, saveDialogParams);
|
||||||
|
};
|
@ -8,12 +8,6 @@ export interface YamlChangedEvent extends Event {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CardPickedEvent extends Event {
|
|
||||||
detail: {
|
|
||||||
config: LovelaceCardConfig;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ViewEditEvent extends Event {
|
export interface ViewEditEvent extends Event {
|
||||||
detail: {
|
detail: {
|
||||||
config: LovelaceViewConfig;
|
config: LovelaceViewConfig;
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
import { HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||||
import "./hui-edit-view";
|
import "./hui-edit-view";
|
||||||
import "./hui-migrate-config";
|
|
||||||
import { EditViewDialogParams } from "./show-edit-view-dialog";
|
import { EditViewDialogParams } from "./show-edit-view-dialog";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@ -39,24 +38,11 @@ export class HuiDialogEditView extends LitElement {
|
|||||||
if (!this._params) {
|
if (!this._params) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
!this._params.add &&
|
|
||||||
this._params.viewConfig &&
|
|
||||||
!("id" in this._params.viewConfig)
|
|
||||||
) {
|
|
||||||
return html`
|
|
||||||
<hui-migrate-config
|
|
||||||
.hass="${this.hass}"
|
|
||||||
@reload-lovelace="${this._params.reloadLovelace}"
|
|
||||||
></hui-migrate-config>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
return html`
|
return html`
|
||||||
<hui-edit-view
|
<hui-edit-view
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
.viewConfig="${this._params.viewConfig}"
|
.lovelace="${this._params.lovelace}"
|
||||||
.add="${this._params.add}"
|
.viewIndex="${this._params.viewIndex}"
|
||||||
.reloadLovelace="${this._params.reloadLovelace}"
|
|
||||||
>
|
>
|
||||||
</hui-edit-view>
|
</hui-edit-view>
|
||||||
`;
|
`;
|
@ -1,9 +1,4 @@
|
|||||||
import {
|
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
PropertyDeclarations,
|
|
||||||
PropertyValues,
|
|
||||||
} from "@polymer/lit-element";
|
|
||||||
import { TemplateResult } from "lit-html";
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
import "@polymer/paper-spinner/paper-spinner";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
@ -16,40 +11,25 @@ import "@polymer/paper-icon-button/paper-icon-button.js";
|
|||||||
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
||||||
import "@polymer/paper-button/paper-button";
|
import "@polymer/paper-button/paper-button";
|
||||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
import "../components/hui-entity-editor";
|
import "../../components/hui-entity-editor";
|
||||||
import "./config-elements/hui-view-editor";
|
import "./hui-view-editor";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import {
|
import {
|
||||||
addView,
|
|
||||||
updateViewConfig,
|
|
||||||
LovelaceViewConfig,
|
LovelaceViewConfig,
|
||||||
LovelaceCardConfig,
|
LovelaceCardConfig,
|
||||||
} from "../../../data/lovelace";
|
} from "../../../../data/lovelace";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||||
import { EntitiesEditorEvent, ViewEditEvent } from "./types";
|
import { EntitiesEditorEvent, ViewEditEvent } from "../types";
|
||||||
import { processEditorEntities } from "./process-editor-entities";
|
import { processEditorEntities } from "../process-editor-entities";
|
||||||
import { EntityConfig } from "../entity-rows/types";
|
import { EntityConfig } from "../../entity-rows/types";
|
||||||
import { confDeleteView } from "./delete-view";
|
import { navigate } from "../../../../common/navigate";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { Lovelace } from "../../types";
|
||||||
|
import { deleteView, addView, replaceView } from "../config-util";
|
||||||
|
|
||||||
export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
|
export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
|
||||||
static get properties(): PropertyDeclarations {
|
public lovelace?: Lovelace;
|
||||||
return {
|
public viewIndex?: number;
|
||||||
hass: {},
|
|
||||||
viewConfig: {},
|
|
||||||
add: {},
|
|
||||||
_config: {},
|
|
||||||
_badges: {},
|
|
||||||
_cards: {},
|
|
||||||
_saving: {},
|
|
||||||
_curTab: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public viewConfig?: LovelaceViewConfig;
|
|
||||||
public add?: boolean;
|
|
||||||
public reloadLovelace?: () => {};
|
|
||||||
protected hass?: HomeAssistant;
|
protected hass?: HomeAssistant;
|
||||||
private _config?: LovelaceViewConfig;
|
private _config?: LovelaceViewConfig;
|
||||||
private _badges?: EntityConfig[];
|
private _badges?: EntityConfig[];
|
||||||
@ -58,6 +38,19 @@ export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
|
|||||||
private _curTabIndex: number;
|
private _curTabIndex: number;
|
||||||
private _curTab?: string;
|
private _curTab?: string;
|
||||||
|
|
||||||
|
static get properties(): PropertyDeclarations {
|
||||||
|
return {
|
||||||
|
hass: {},
|
||||||
|
lovelace: {},
|
||||||
|
viewIndex: {},
|
||||||
|
_config: {},
|
||||||
|
_badges: {},
|
||||||
|
_cards: {},
|
||||||
|
_saving: {},
|
||||||
|
_curTab: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected constructor() {
|
protected constructor() {
|
||||||
super();
|
super();
|
||||||
this._saving = false;
|
this._saving = false;
|
||||||
@ -69,30 +62,21 @@ export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
|
|||||||
if (this._dialog == null) {
|
if (this._dialog == null) {
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
}
|
}
|
||||||
this._dialog.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues): void {
|
if (this.viewIndex === undefined) {
|
||||||
super.updated(changedProperties);
|
|
||||||
if (!changedProperties.has("viewConfig") && !changedProperties.has("add")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
this.viewConfig &&
|
|
||||||
(!changedProperties.get("viewConfig") ||
|
|
||||||
this.viewConfig.id !==
|
|
||||||
(changedProperties.get("viewConfig") as LovelaceViewConfig).id)
|
|
||||||
) {
|
|
||||||
const { cards, badges, ...viewConfig } = this.viewConfig;
|
|
||||||
this._config = viewConfig;
|
|
||||||
this._badges = badges ? processEditorEntities(badges) : [];
|
|
||||||
this._cards = cards;
|
|
||||||
} else if (changedProperties.has("add")) {
|
|
||||||
this._config = {};
|
this._config = {};
|
||||||
this._badges = [];
|
this._badges = [];
|
||||||
this._cards = [];
|
this._cards = [];
|
||||||
|
} else {
|
||||||
|
const { cards, badges, ...viewConfig } = this.lovelace!.config.views[
|
||||||
|
this.viewIndex
|
||||||
|
];
|
||||||
|
this._config = viewConfig;
|
||||||
|
this._badges = badges ? processEditorEntities(badges) : [];
|
||||||
|
this._cards = cards;
|
||||||
}
|
}
|
||||||
this._resizeDialog();
|
|
||||||
|
this._dialog.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _dialog(): PaperDialogElement {
|
private get _dialog(): PaperDialogElement {
|
||||||
@ -142,7 +126,7 @@ export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
|
|||||||
<paper-dialog-scrollable> ${content} </paper-dialog-scrollable>
|
<paper-dialog-scrollable> ${content} </paper-dialog-scrollable>
|
||||||
<div class="paper-dialog-buttons">
|
<div class="paper-dialog-buttons">
|
||||||
${
|
${
|
||||||
!this.add
|
this.viewIndex !== undefined
|
||||||
? html`
|
? html`
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
class="delete"
|
class="delete"
|
||||||
@ -208,23 +192,27 @@ export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _save(): void {
|
private async _delete() {
|
||||||
this._saving = true;
|
|
||||||
this._updateConfigInBackend();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _delete() {
|
|
||||||
if (this._cards && this._cards.length > 0) {
|
if (this._cards && this._cards.length > 0) {
|
||||||
alert(
|
alert(
|
||||||
"You can't delete a view that has cards in it. Remove the cards first."
|
"You can't delete a view that has cards in it. Remove the cards first."
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
confDeleteView(this.hass!, String(this.viewConfig!.id!), () => {
|
|
||||||
|
if (!confirm("Are you sure you want to delete this view?")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.lovelace!.saveConfig(
|
||||||
|
deleteView(this.lovelace!.config, this.viewIndex!)
|
||||||
|
);
|
||||||
this._closeDialog();
|
this._closeDialog();
|
||||||
this.reloadLovelace!();
|
|
||||||
navigate(this, `/lovelace/0`);
|
navigate(this, `/lovelace/0`);
|
||||||
});
|
} catch (err) {
|
||||||
|
alert(`Deleting failed: ${err.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _resizeDialog(): Promise<void> {
|
private async _resizeDialog(): Promise<void> {
|
||||||
@ -234,9 +222,9 @@ export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
|
|||||||
|
|
||||||
private _closeDialog(): void {
|
private _closeDialog(): void {
|
||||||
this._curTabIndex = 0;
|
this._curTabIndex = 0;
|
||||||
|
this.lovelace = undefined;
|
||||||
this._config = {};
|
this._config = {};
|
||||||
this._badges = [];
|
this._badges = [];
|
||||||
this.viewConfig = undefined;
|
|
||||||
this._dialog.close();
|
this._dialog.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,39 +236,35 @@ export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
|
|||||||
this._resizeDialog();
|
this._resizeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _updateConfigInBackend(): Promise<void> {
|
private async _save(): Promise<void> {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this._isConfigChanged()) {
|
if (!this._isConfigChanged()) {
|
||||||
this._closeDialog();
|
this._closeDialog();
|
||||||
this._saving = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._badges) {
|
this._saving = true;
|
||||||
this._config.badges = this._badges.map((entityConf) => {
|
|
||||||
return entityConf.entity;
|
const viewConf: LovelaceViewConfig = {
|
||||||
});
|
cards: this._cards,
|
||||||
}
|
badges: this._badges!.map((entityConf) => entityConf.entity),
|
||||||
|
...this._config,
|
||||||
|
};
|
||||||
|
|
||||||
|
const lovelace = this.lovelace!;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.add) {
|
await lovelace.saveConfig(
|
||||||
this._config.cards = [];
|
this._creatingView
|
||||||
await addView(this.hass!, this._config, "json");
|
? addView(lovelace.config, viewConf)
|
||||||
} else {
|
: replaceView(lovelace.config, this.viewIndex!, viewConf)
|
||||||
await updateViewConfig(
|
);
|
||||||
this.hass!,
|
|
||||||
String(this.viewConfig!.id!),
|
|
||||||
this._config,
|
|
||||||
"json"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.reloadLovelace!();
|
|
||||||
this._closeDialog();
|
this._closeDialog();
|
||||||
this._saving = false;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(`Saving failed: ${err.message}`);
|
alert(`Saving failed: ${err.message}`);
|
||||||
|
} finally {
|
||||||
this._saving = false;
|
this._saving = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -299,10 +283,15 @@ export class HuiEditView extends hassLocalizeLitMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _isConfigChanged(): boolean {
|
private _isConfigChanged(): boolean {
|
||||||
if (!this.add) {
|
return (
|
||||||
return true;
|
this._creatingView ||
|
||||||
}
|
JSON.stringify(this._config) !==
|
||||||
return JSON.stringify(this._config) !== JSON.stringify(this.viewConfig);
|
JSON.stringify(this.lovelace!.config.views[this.viewIndex!])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _creatingView(): boolean {
|
||||||
|
return this.viewIndex === undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ import { EditorTarget } from "../types";
|
|||||||
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { configElementStyle } from "./config-elements-style";
|
import { configElementStyle } from "../config-elements/config-elements-style";
|
||||||
|
|
||||||
import "../../components/hui-theme-select-editor";
|
import "../../components/hui-theme-select-editor";
|
||||||
import { LovelaceViewConfig } from "../../../../data/lovelace";
|
import { LovelaceViewConfig } from "../../../../data/lovelace";
|
||||||
@ -24,12 +24,11 @@ export class HuiViewEditor extends hassLocalizeLitMixin(LitElement) {
|
|||||||
return { hass: {}, _config: {} };
|
return { hass: {}, _config: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
get _id(): string {
|
get _path(): string {
|
||||||
if (!this._config) {
|
if (!this._config) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
return this._config.path || "";
|
||||||
return "id" in this._config ? this._config.id! : "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get _title(): string {
|
get _title(): string {
|
||||||
@ -68,12 +67,6 @@ export class HuiViewEditor extends hassLocalizeLitMixin(LitElement) {
|
|||||||
return html`
|
return html`
|
||||||
${configElementStyle}
|
${configElementStyle}
|
||||||
<div class="card-config">
|
<div class="card-config">
|
||||||
<paper-input
|
|
||||||
label="ID"
|
|
||||||
value="${this._id}"
|
|
||||||
.configValue="${"id"}"
|
|
||||||
@value-changed="${this._valueChanged}"
|
|
||||||
></paper-input>
|
|
||||||
<paper-input
|
<paper-input
|
||||||
label="Title"
|
label="Title"
|
||||||
value="${this._title}"
|
value="${this._title}"
|
||||||
@ -86,6 +79,12 @@ export class HuiViewEditor extends hassLocalizeLitMixin(LitElement) {
|
|||||||
.configValue="${"icon"}"
|
.configValue="${"icon"}"
|
||||||
@value-changed="${this._valueChanged}"
|
@value-changed="${this._valueChanged}"
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
label="URL Path"
|
||||||
|
value="${this._path}"
|
||||||
|
.configValue="${"path"}"
|
||||||
|
@value-changed="${this._valueChanged}"
|
||||||
|
></paper-input>
|
||||||
<hui-theme-select-editor
|
<hui-theme-select-editor
|
||||||
.hass="${this.hass}"
|
.hass="${this.hass}"
|
||||||
.value="${this._theme}"
|
.value="${this._theme}"
|
@ -1,5 +1,5 @@
|
|||||||
import { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event";
|
import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { LovelaceViewConfig } from "../../../data/lovelace";
|
import { Lovelace } from "../../types";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
@ -18,9 +18,8 @@ const dialogShowEvent = "show-edit-view";
|
|||||||
const dialogTag = "hui-dialog-edit-view";
|
const dialogTag = "hui-dialog-edit-view";
|
||||||
|
|
||||||
export interface EditViewDialogParams {
|
export interface EditViewDialogParams {
|
||||||
viewConfig?: LovelaceViewConfig;
|
lovelace: Lovelace;
|
||||||
add?: boolean;
|
viewIndex?: number;
|
||||||
reloadLovelace: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const registerEditViewDialog = (element: HTMLElement) =>
|
const registerEditViewDialog = (element: HTMLElement) =>
|
@ -1,154 +0,0 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "@polymer/paper-button/paper-button";
|
|
||||||
|
|
||||||
import { registerSaveDialog } from "./editor/hui-dialog-save-config";
|
|
||||||
import { fetchConfig } from "../../data/lovelace";
|
|
||||||
import "../../layouts/hass-loading-screen";
|
|
||||||
import "../../layouts/hass-error-screen";
|
|
||||||
import "./hui-root";
|
|
||||||
import localizeMixin from "../../mixins/localize-mixin";
|
|
||||||
|
|
||||||
let registeredDialog = false;
|
|
||||||
|
|
||||||
class Lovelace extends localizeMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
paper-button {
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template is="dom-if" if="[[_equal(_state, "loaded")]]" restamp>
|
|
||||||
<hui-root
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
route="[[route]]"
|
|
||||||
config="[[_config]]"
|
|
||||||
columns="[[_columns]]"
|
|
||||||
on-config-refresh="_forceFetchConfig"
|
|
||||||
></hui-root>
|
|
||||||
</template>
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if="[[_equal(_state, "loading")]]"
|
|
||||||
restamp
|
|
||||||
>
|
|
||||||
<hass-loading-screen
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></hass-loading-screen>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[_equal(_state, "error")]]" restamp>
|
|
||||||
<hass-error-screen
|
|
||||||
title="Lovelace"
|
|
||||||
error="[[_errorMsg]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
>
|
|
||||||
<paper-button on-click="_forceFetchConfig"
|
|
||||||
>Reload ui-lovelace.yaml</paper-button
|
|
||||||
>
|
|
||||||
</hass-error-screen>
|
|
||||||
</template>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
|
|
||||||
narrow: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
showMenu: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
route: Object,
|
|
||||||
|
|
||||||
_columns: {
|
|
||||||
type: Number,
|
|
||||||
value: 1,
|
|
||||||
},
|
|
||||||
|
|
||||||
_state: {
|
|
||||||
type: String,
|
|
||||||
value: "loading",
|
|
||||||
},
|
|
||||||
|
|
||||||
_errorMsg: String,
|
|
||||||
|
|
||||||
_config: {
|
|
||||||
type: Object,
|
|
||||||
value: null,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static get observers() {
|
|
||||||
return ["_updateColumns(narrow, showMenu)"];
|
|
||||||
}
|
|
||||||
|
|
||||||
ready() {
|
|
||||||
this._fetchConfig(false);
|
|
||||||
this._updateColumns = this._updateColumns.bind(this);
|
|
||||||
this.mqls = [300, 600, 900, 1200].map((width) => {
|
|
||||||
const mql = matchMedia(`(min-width: ${width}px)`);
|
|
||||||
mql.addListener(this._updateColumns);
|
|
||||||
return mql;
|
|
||||||
});
|
|
||||||
this._updateColumns();
|
|
||||||
super.ready();
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateColumns() {
|
|
||||||
const matchColumns = this.mqls.reduce((cols, mql) => cols + mql.matches, 0);
|
|
||||||
// Do -1 column if the menu is docked and open
|
|
||||||
this._columns = Math.max(1, matchColumns - (!this.narrow && this.showMenu));
|
|
||||||
}
|
|
||||||
|
|
||||||
_forceFetchConfig() {
|
|
||||||
this._fetchConfig(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _fetchConfig(force) {
|
|
||||||
try {
|
|
||||||
const conf = await fetchConfig(this.hass, force);
|
|
||||||
this.setProperties({
|
|
||||||
_config: conf,
|
|
||||||
_state: "loaded",
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
if (err.code === "file_not_found") {
|
|
||||||
const {
|
|
||||||
generateLovelaceConfig,
|
|
||||||
} = await import("./common/generate-lovelace-config");
|
|
||||||
this.setProperties({
|
|
||||||
_config: generateLovelaceConfig(this.hass, this.localize),
|
|
||||||
_state: "loaded",
|
|
||||||
});
|
|
||||||
if (!registeredDialog) {
|
|
||||||
registeredDialog = true;
|
|
||||||
registerSaveDialog(this);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.setProperties({
|
|
||||||
_state: "error",
|
|
||||||
_errorMsg: err.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_equal(a, b) {
|
|
||||||
return a === b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-panel-lovelace", Lovelace);
|
|
185
src/panels/lovelace/ha-panel-lovelace.ts
Normal file
185
src/panels/lovelace/ha-panel-lovelace.ts
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
import "@polymer/paper-button/paper-button";
|
||||||
|
|
||||||
|
import { fetchConfig, LovelaceConfig, saveConfig } from "../../data/lovelace";
|
||||||
|
import "../../layouts/hass-loading-screen";
|
||||||
|
import "../../layouts/hass-error-screen";
|
||||||
|
import "./hui-root";
|
||||||
|
import { HomeAssistant, PanelInfo } from "../../types";
|
||||||
|
import { Lovelace } from "./types";
|
||||||
|
import { LitElement, html, PropertyValues } from "@polymer/lit-element";
|
||||||
|
import { hassLocalizeLitMixin } from "../../mixins/lit-localize-mixin";
|
||||||
|
import { TemplateResult } from "lit-html";
|
||||||
|
import { showSaveDialog } from "./editor/show-save-config-dialog";
|
||||||
|
|
||||||
|
interface LovelacePanelConfig {
|
||||||
|
mode: "yaml" | "storage";
|
||||||
|
}
|
||||||
|
|
||||||
|
class LovelacePanel extends hassLocalizeLitMixin(LitElement) {
|
||||||
|
public panel?: PanelInfo<LovelacePanelConfig>;
|
||||||
|
public hass?: HomeAssistant;
|
||||||
|
public narrow?: boolean;
|
||||||
|
public showMenu?: boolean;
|
||||||
|
public route?: object;
|
||||||
|
private _columns?: number;
|
||||||
|
private _state?: "loading" | "loaded" | "error";
|
||||||
|
private _errorMsg?: string;
|
||||||
|
private lovelace?: Lovelace;
|
||||||
|
private mqls?: MediaQueryList[];
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: {},
|
||||||
|
lovelace: {},
|
||||||
|
narrow: { type: Boolean, value: false },
|
||||||
|
showMenu: { type: Boolean, value: false },
|
||||||
|
route: {},
|
||||||
|
_columns: { type: Number, value: 1 },
|
||||||
|
_state: { type: String, value: "loading" },
|
||||||
|
_errorMsg: String,
|
||||||
|
_config: { type: {}, value: null },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): TemplateResult {
|
||||||
|
const state = this._state!;
|
||||||
|
|
||||||
|
if (state === "loaded") {
|
||||||
|
return html`
|
||||||
|
<hui-root
|
||||||
|
.narrow="${this.narrow}"
|
||||||
|
.showMenu="${this.showMenu}"
|
||||||
|
.hass="${this.hass}"
|
||||||
|
.lovelace="${this.lovelace}"
|
||||||
|
.route="${this.route}"
|
||||||
|
.columns="${this._columns}"
|
||||||
|
@config-refresh="${this._forceFetchConfig}"
|
||||||
|
></hui-root>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === "error") {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
paper-button {
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<hass-error-screen
|
||||||
|
title="Lovelace"
|
||||||
|
.error="${this._errorMsg}"
|
||||||
|
.narrow="${this.narrow}"
|
||||||
|
.showMenu="${this.showMenu}"
|
||||||
|
>
|
||||||
|
<paper-button on-click="_forceFetchConfig"
|
||||||
|
>Reload ui-lovelace.yaml</paper-button
|
||||||
|
>
|
||||||
|
</hass-error-screen>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<hass-loading-screen
|
||||||
|
.narrow="${this.narrow}"
|
||||||
|
.showMenu="${this.showMenu}"
|
||||||
|
></hass-loading-screen>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public updated(changedProps: PropertyValues): void {
|
||||||
|
if (changedProps.has("narrow") || changedProps.has("showMenu")) {
|
||||||
|
this._updateColumns();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public firstUpdated() {
|
||||||
|
this._fetchConfig(false);
|
||||||
|
this._updateColumns = this._updateColumns.bind(this);
|
||||||
|
this.mqls = [300, 600, 900, 1200].map((width) => {
|
||||||
|
const mql = matchMedia(`(min-width: ${width}px)`);
|
||||||
|
mql.addListener(this._updateColumns);
|
||||||
|
return mql;
|
||||||
|
});
|
||||||
|
this._updateColumns();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _updateColumns() {
|
||||||
|
const matchColumns = this.mqls!.reduce(
|
||||||
|
(cols, mql) => cols + Number(mql.matches),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
// Do -1 column if the menu is docked and open
|
||||||
|
this._columns = Math.max(
|
||||||
|
1,
|
||||||
|
matchColumns - Number(!this.narrow && this.showMenu)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _forceFetchConfig() {
|
||||||
|
this._fetchConfig(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _fetchConfig(force) {
|
||||||
|
let conf;
|
||||||
|
let gen: boolean;
|
||||||
|
|
||||||
|
try {
|
||||||
|
conf = await fetchConfig(this.hass!, force);
|
||||||
|
gen = false;
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code !== "config_not_found") {
|
||||||
|
// tslint:disable-next-line
|
||||||
|
console.log(err);
|
||||||
|
this._state = "error";
|
||||||
|
this._errorMsg = err.message;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
generateLovelaceConfig,
|
||||||
|
} = await import("./common/generate-lovelace-config");
|
||||||
|
conf = generateLovelaceConfig(this.hass!, this.localize);
|
||||||
|
gen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._state = "loaded";
|
||||||
|
this.lovelace = {
|
||||||
|
config: conf,
|
||||||
|
autoGen: gen,
|
||||||
|
editMode: this.lovelace ? this.lovelace.editMode : false,
|
||||||
|
mode: this.panel!.config.mode,
|
||||||
|
setEditMode: (editMode: boolean) => {
|
||||||
|
if (!editMode || !this.lovelace!.autoGen) {
|
||||||
|
this._updateLovelace({ editMode });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showSaveDialog(this, {
|
||||||
|
lovelace: this.lovelace!,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
saveConfig: async (newConfig: LovelaceConfig): Promise<void> => {
|
||||||
|
const { config, autoGen } = this.lovelace!;
|
||||||
|
try {
|
||||||
|
// Optimistic update
|
||||||
|
this._updateLovelace({ config: newConfig, autoGen: false });
|
||||||
|
await saveConfig(this.hass!, newConfig);
|
||||||
|
} catch (err) {
|
||||||
|
// tslint:disable-next-line
|
||||||
|
console.error(err);
|
||||||
|
// Rollback the optimistic update
|
||||||
|
this._updateLovelace({ config, autoGen });
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _updateLovelace(props: Partial<Lovelace>) {
|
||||||
|
this.lovelace = {
|
||||||
|
...this.lovelace!,
|
||||||
|
...props,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-panel-lovelace", LovelacePanel);
|
@ -32,8 +32,7 @@ import "./hui-unused-entities";
|
|||||||
import "./hui-view";
|
import "./hui-view";
|
||||||
import debounce from "../../common/util/debounce";
|
import debounce from "../../common/util/debounce";
|
||||||
import createCardElement from "./common/create-card-element";
|
import createCardElement from "./common/create-card-element";
|
||||||
import { showSaveDialog } from "./editor/hui-dialog-save-config";
|
import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
|
||||||
import { showEditViewDialog } from "./editor/show-edit-view-dialog";
|
|
||||||
|
|
||||||
// CSS and JS should only be imported once. Modules and HTML are safe.
|
// CSS and JS should only be imported once. Modules and HTML are safe.
|
||||||
const CSS_CACHE = {};
|
const CSS_CACHE = {};
|
||||||
@ -181,8 +180,12 @@ class HUIRoot extends NavigateMixin(
|
|||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
computed: "_computeConfig(lovelace)",
|
||||||
observer: "_configChanged",
|
observer: "_configChanged",
|
||||||
},
|
},
|
||||||
|
lovelace: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
columns: {
|
columns: {
|
||||||
type: Number,
|
type: Number,
|
||||||
observer: "_columnsChanged",
|
observer: "_columnsChanged",
|
||||||
@ -216,6 +219,7 @@ class HUIRoot extends NavigateMixin(
|
|||||||
_editMode: {
|
_editMode: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
|
computed: "_computeEditMode(lovelace)",
|
||||||
observer: "_editModeChanged",
|
observer: "_editModeChanged",
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -258,12 +262,12 @@ class HUIRoot extends NavigateMixin(
|
|||||||
_routeChanged(route) {
|
_routeChanged(route) {
|
||||||
const views = this.config && this.config.views;
|
const views = this.config && this.config.views;
|
||||||
if (route.path === "" && route.prefix === "/lovelace" && views) {
|
if (route.path === "" && route.prefix === "/lovelace" && views) {
|
||||||
this.navigate(`/lovelace/${views[0].id || 0}`, true);
|
this.navigate(`/lovelace/${views[0].path || 0}`, true);
|
||||||
} else if (this.routeData.view) {
|
} else if (this.routeData.view) {
|
||||||
const view = this.routeData.view;
|
const view = this.routeData.view;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
for (let i = 0; i < views.length; i++) {
|
for (let i = 0; i < views.length; i++) {
|
||||||
if (views[i].id === view || i === parseInt(view)) {
|
if (views[i].path === view || i === parseInt(view)) {
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -272,8 +276,8 @@ class HUIRoot extends NavigateMixin(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeViewId(id, index) {
|
_computeViewPath(path, index) {
|
||||||
return id || index;
|
return path || index;
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeTitle(config) {
|
_computeTitle(config) {
|
||||||
@ -305,17 +309,7 @@ class HUIRoot extends NavigateMixin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
_editModeEnable() {
|
_editModeEnable() {
|
||||||
if (this.config._frontendAuto) {
|
this.lovelace.setEditMode(true);
|
||||||
showSaveDialog(this, {
|
|
||||||
config: this.config,
|
|
||||||
reloadLovelace: () => {
|
|
||||||
this.fire("config-refresh");
|
|
||||||
this._editMode = true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._editMode = true;
|
|
||||||
if (this.config.views.length < 2) {
|
if (this.config.views.length < 2) {
|
||||||
this.$.view.classList.remove("tabs-hidden");
|
this.$.view.classList.remove("tabs-hidden");
|
||||||
this.fire("iron-resize");
|
this.fire("iron-resize");
|
||||||
@ -323,7 +317,7 @@ class HUIRoot extends NavigateMixin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
_editModeDisable() {
|
_editModeDisable() {
|
||||||
this._editMode = false;
|
this.lovelace.setEditMode(false);
|
||||||
if (this.config.views.length < 2) {
|
if (this.config.views.length < 2) {
|
||||||
this.$.view.classList.add("tabs-hidden");
|
this.$.view.classList.add("tabs-hidden");
|
||||||
this.fire("iron-resize");
|
this.fire("iron-resize");
|
||||||
@ -336,20 +330,14 @@ class HUIRoot extends NavigateMixin(
|
|||||||
|
|
||||||
_editView() {
|
_editView() {
|
||||||
showEditViewDialog(this, {
|
showEditViewDialog(this, {
|
||||||
viewConfig: this.config.views[this._curView],
|
lovelace: this.lovelace,
|
||||||
add: false,
|
viewIndex: this._curView,
|
||||||
reloadLovelace: () => {
|
|
||||||
this.fire("config-refresh");
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_addView() {
|
_addView() {
|
||||||
showEditViewDialog(this, {
|
showEditViewDialog(this, {
|
||||||
add: true,
|
lovelace: this.lovelace,
|
||||||
reloadLovelace: () => {
|
|
||||||
this.fire("config-refresh");
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,8 +348,8 @@ class HUIRoot extends NavigateMixin(
|
|||||||
|
|
||||||
_navigateView(viewIndex) {
|
_navigateView(viewIndex) {
|
||||||
if (viewIndex !== this._curView) {
|
if (viewIndex !== this._curView) {
|
||||||
const id = this.config.views[viewIndex].id || viewIndex;
|
const path = this.config.views[viewIndex].path || viewIndex;
|
||||||
this.navigate(`/lovelace/${id}`);
|
this.navigate(`/lovelace/${path}`);
|
||||||
}
|
}
|
||||||
scrollToTarget(this, this.$.layout.header.scrollTarget);
|
scrollToTarget(this, this.$.layout.header.scrollTarget);
|
||||||
}
|
}
|
||||||
@ -390,12 +378,12 @@ class HUIRoot extends NavigateMixin(
|
|||||||
if (viewConfig.panel) {
|
if (viewConfig.panel) {
|
||||||
view = createCardElement(viewConfig.cards[0]);
|
view = createCardElement(viewConfig.cards[0]);
|
||||||
view.isPanel = true;
|
view.isPanel = true;
|
||||||
view.editMode = this._editMode;
|
|
||||||
} else {
|
} else {
|
||||||
view = document.createElement("hui-view");
|
view = document.createElement("hui-view");
|
||||||
|
view.lovelace = this.lovelace;
|
||||||
view.config = viewConfig;
|
view.config = viewConfig;
|
||||||
view.columns = this.columns;
|
view.columns = this.columns;
|
||||||
view.editMode = this._editMode;
|
view.index = viewIndex;
|
||||||
}
|
}
|
||||||
if (viewConfig.background) background = viewConfig.background;
|
if (viewConfig.background) background = viewConfig.background;
|
||||||
}
|
}
|
||||||
@ -452,5 +440,13 @@ class HUIRoot extends NavigateMixin(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_computeConfig(lovelace) {
|
||||||
|
return lovelace ? lovelace.config : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_computeEditMode(lovelace) {
|
||||||
|
return lovelace ? lovelace.editMode : false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
customElements.define("hui-root", HUIRoot);
|
customElements.define("hui-root", HUIRoot);
|
||||||
|
@ -11,7 +11,7 @@ import EventsMixin from "../../mixins/events-mixin";
|
|||||||
import localizeMixin from "../../mixins/localize-mixin";
|
import localizeMixin from "../../mixins/localize-mixin";
|
||||||
import createCardElement from "./common/create-card-element";
|
import createCardElement from "./common/create-card-element";
|
||||||
import { computeCardSize } from "./common/compute-card-size";
|
import { computeCardSize } from "./common/compute-card-size";
|
||||||
import { showEditCardDialog } from "./editor/show-edit-card-dialog";
|
import { showEditCardDialog } from "./editor/card-editor/show-edit-card-dialog";
|
||||||
|
|
||||||
class HUIView extends localizeMixin(EventsMixin(PolymerElement)) {
|
class HUIView extends localizeMixin(EventsMixin(PolymerElement)) {
|
||||||
static get template() {
|
static get template() {
|
||||||
@ -82,7 +82,7 @@ class HUIView extends localizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
<div id="badges"></div>
|
<div id="badges"></div>
|
||||||
<div id="columns"></div>
|
<div id="columns"></div>
|
||||||
<paper-fab
|
<paper-fab
|
||||||
hidden$="{{!editMode}}"
|
hidden$="[[!lovelace.editMode]]"
|
||||||
elevated="2"
|
elevated="2"
|
||||||
icon="hass:plus"
|
icon="hass:plus"
|
||||||
title=[[localize("ui.panel.lovelace.editor.edit_card.add")]]
|
title=[[localize("ui.panel.lovelace.editor.edit_card.add")]]
|
||||||
@ -97,9 +97,11 @@ class HUIView extends localizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
type: Object,
|
type: Object,
|
||||||
observer: "_hassChanged",
|
observer: "_hassChanged",
|
||||||
},
|
},
|
||||||
|
lovelace: Object,
|
||||||
config: Object,
|
config: Object,
|
||||||
columns: Number,
|
columns: Number,
|
||||||
editMode: Boolean,
|
editMode: Boolean,
|
||||||
|
index: Number,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,11 +121,8 @@ class HUIView extends localizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
|
|
||||||
_addCard() {
|
_addCard() {
|
||||||
showEditCardDialog(this, {
|
showEditCardDialog(this, {
|
||||||
viewId: "id" in this.config ? String(this.config.id) : undefined,
|
lovelace: this.lovelace,
|
||||||
add: true,
|
path: [this.index],
|
||||||
reloadLovelace: () => {
|
|
||||||
this.fire("config-refresh");
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,23 +168,23 @@ class HUIView extends localizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
|
|
||||||
const elements = [];
|
const elements = [];
|
||||||
const elementsToAppend = [];
|
const elementsToAppend = [];
|
||||||
for (const cardConfig of config.cards) {
|
config.cards.forEach((cardConfig, cardIndex) => {
|
||||||
const element = createCardElement(cardConfig);
|
const element = createCardElement(cardConfig);
|
||||||
element.hass = this.hass;
|
element.hass = this.hass;
|
||||||
elements.push(element);
|
elements.push(element);
|
||||||
|
|
||||||
if (!this.editMode) {
|
if (!this.lovelace.editMode) {
|
||||||
elementsToAppend.push(element);
|
elementsToAppend.push(element);
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapper = document.createElement("hui-card-options");
|
const wrapper = document.createElement("hui-card-options");
|
||||||
wrapper.hass = this.hass;
|
wrapper.hass = this.hass;
|
||||||
wrapper.cardConfig = cardConfig;
|
wrapper.lovelace = this.lovelace;
|
||||||
wrapper.editMode = this.editMode;
|
wrapper.path = [this.index, cardIndex];
|
||||||
wrapper.appendChild(element);
|
wrapper.appendChild(element);
|
||||||
elementsToAppend.push(wrapper);
|
elementsToAppend.push(wrapper);
|
||||||
}
|
});
|
||||||
|
|
||||||
let columns = [];
|
let columns = [];
|
||||||
const columnEntityCount = [];
|
const columnEntityCount = [];
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { LovelaceCardConfig } from "../../data/lovelace";
|
import { LovelaceCardConfig, LovelaceConfig } from "../../data/lovelace";
|
||||||
|
|
||||||
|
export interface Lovelace {
|
||||||
|
config: LovelaceConfig;
|
||||||
|
editMode: boolean;
|
||||||
|
autoGen: boolean;
|
||||||
|
mode: "yaml" | "storage";
|
||||||
|
setEditMode: (editMode: boolean) => void;
|
||||||
|
saveConfig: (newConfig: LovelaceConfig) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface LovelaceCard extends HTMLElement {
|
export interface LovelaceCard extends HTMLElement {
|
||||||
hass?: HomeAssistant;
|
hass?: HomeAssistant;
|
||||||
|
@ -159,3 +159,11 @@ export type GroupEntity = HassEntityBase & {
|
|||||||
control?: "hidden";
|
control?: "hidden";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface PanelInfo<T = unknown> {
|
||||||
|
component_name: string;
|
||||||
|
icon?: string;
|
||||||
|
title?: string;
|
||||||
|
url_path: string;
|
||||||
|
config: T;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user