Extract saving card config from card editor (#24319)

* Extract saving card config from card editor

* Await

* Add try/catch

* Remove unused translations

* Remove duration
This commit is contained in:
Paul Bottein 2025-02-20 12:27:39 +01:00 committed by GitHub
parent fb1ee2ed1d
commit f84ad92356
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 100 additions and 83 deletions

View File

@ -23,8 +23,10 @@ import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
import { addCard } from "../editor/config-util";
import type { LovelaceCardPath } from "../editor/lovelace-path"; import type { LovelaceCardPath } from "../editor/lovelace-path";
import { import {
findLovelaceContainer,
findLovelaceItems, findLovelaceItems,
getLovelaceContainerPath, getLovelaceContainerPath,
parseLovelaceCardPath, parseLovelaceCardPath,
@ -253,14 +255,24 @@ export class HuiCardEditMode extends LitElement {
} }
private _duplicateCard(): void { private _duplicateCard(): void {
const { cardIndex } = parseLovelaceCardPath(this.path!); const { cardIndex, sectionIndex } = parseLovelaceCardPath(this.path!);
const containerPath = getLovelaceContainerPath(this.path!); const containerPath = getLovelaceContainerPath(this.path!);
const sectionConfig =
sectionIndex !== undefined
? findLovelaceContainer(this.lovelace!.config, containerPath)
: undefined;
const cardConfig = this._cards![cardIndex]; const cardConfig = this._cards![cardIndex];
showEditCardDialog(this, { showEditCardDialog(this, {
lovelaceConfig: this.lovelace!.config, lovelaceConfig: this.lovelace!.config,
saveConfig: this.lovelace!.saveConfig, saveCardConfig: async (config) => {
path: containerPath, const newConfig = addCard(this.lovelace!.config, containerPath, config);
await this.lovelace!.saveConfig(newConfig);
},
cardConfig, cardConfig,
sectionConfig,
isNew: true,
}); });
} }

View File

@ -278,9 +278,12 @@ export class HuiCardOptions extends LitElement {
const cardConfig = this._cards![cardIndex]; const cardConfig = this._cards![cardIndex];
showEditCardDialog(this, { showEditCardDialog(this, {
lovelaceConfig: this.lovelace!.config, lovelaceConfig: this.lovelace!.config,
saveConfig: this.lovelace!.saveConfig, saveCardConfig: async (config) => {
path: containerPath, const newConfig = addCard(this.lovelace!.config, containerPath, config);
await this.lovelace!.saveConfig(newConfig);
},
cardConfig, cardConfig,
isNew: true,
}); });
} }

View File

@ -3,10 +3,10 @@ import "@material/mwc-tab/mwc-tab";
import { mdiClose } from "@mdi/js"; import { mdiClose } from "@mdi/js";
import type { CSSResultGroup } from "lit"; import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { ifDefined } from "lit/directives/if-defined";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { cache } from "lit/directives/cache"; import { cache } from "lit/directives/cache";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import memoize from "memoize-one"; import memoize from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { computeDomain } from "../../../../common/entity/compute_domain"; import { computeDomain } from "../../../../common/entity/compute_domain";
@ -24,6 +24,7 @@ import {
computeCards, computeCards,
computeSection, computeSection,
} from "../../common/generate-lovelace-config"; } from "../../common/generate-lovelace-config";
import { addCard } from "../config-util";
import { import {
findLovelaceContainer, findLovelaceContainer,
parseLovelaceContainerPath, parseLovelaceContainerPath,
@ -241,11 +242,24 @@ export class HuiCreateDialogCard
} }
} }
const lovelaceConfig = this._params!.lovelaceConfig;
const containerPath = this._params!.path;
const saveConfig = this._params!.saveConfig;
const sectionConfig =
containerPath.length === 2
? findLovelaceContainer(lovelaceConfig, containerPath)
: undefined;
showEditCardDialog(this, { showEditCardDialog(this, {
lovelaceConfig: this._params!.lovelaceConfig, lovelaceConfig,
saveConfig: this._params!.saveConfig, saveCardConfig: async (newCardConfig) => {
path: this._params!.path, const newConfig = addCard(lovelaceConfig, containerPath, newCardConfig);
await saveConfig(newConfig);
},
cardConfig: config, cardConfig: config,
sectionConfig,
isNew: true,
}); });
this.closeDialog(); this.closeDialog();

View File

@ -13,7 +13,6 @@ import "../../../../components/ha-dialog-header";
import "../../../../components/ha-icon-button"; import "../../../../components/ha-icon-button";
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section"; import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
import { import {
getCustomCardEntry, getCustomCardEntry,
isCustomType, isCustomType,
@ -23,13 +22,12 @@ import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-
import type { HassDialog } from "../../../../dialogs/make-dialog-manager"; import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyleDialog } from "../../../../resources/styles"; import { haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
import { showToast } from "../../../../util/toast";
import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
import "../../cards/hui-card"; import "../../cards/hui-card";
import "../../sections/hui-section"; import "../../sections/hui-section";
import { addCard, replaceCard } from "../config-util";
import { getCardDocumentationURL } from "../get-dashboard-documentation-url"; import { getCardDocumentationURL } from "../get-dashboard-documentation-url";
import type { ConfigChangedEvent } from "../hui-element-editor"; import type { ConfigChangedEvent } from "../hui-element-editor";
import { findLovelaceContainer } from "../lovelace-path";
import type { GUIModeChangedEvent } from "../types"; import type { GUIModeChangedEvent } from "../types";
import "./hui-card-element-editor"; import "./hui-card-element-editor";
import type { HuiCardElementEditor } from "./hui-card-element-editor"; import type { HuiCardElementEditor } from "./hui-card-element-editor";
@ -59,9 +57,7 @@ export class HuiDialogEditCard
@state() private _cardConfig?: LovelaceCardConfig; @state() private _cardConfig?: LovelaceCardConfig;
@state() private _containerConfig!: @state() private _sectionConfig?: LovelaceSectionConfig;
| LovelaceViewConfig
| LovelaceSectionConfig;
@state() private _saving = false; @state() private _saving = false;
@ -85,23 +81,10 @@ export class HuiDialogEditCard
this._GUImode = true; this._GUImode = true;
this._guiModeAvailable = true; this._guiModeAvailable = true;
const containerConfig = findLovelaceContainer( this._sectionConfig = this._params.sectionConfig;
params.lovelaceConfig,
params.path
);
if ("strategy" in containerConfig) {
throw new Error("Can't edit strategy");
}
this._containerConfig = containerConfig;
if ("cardConfig" in params) {
this._cardConfig = params.cardConfig; this._cardConfig = params.cardConfig;
this._dirty = true; this._dirty = Boolean(this._params.isNew);
} else {
this._cardConfig = this._containerConfig.cards?.[params.cardIndex];
}
this.large = false; this.large = false;
if (this._cardConfig && !Object.isFrozen(this._cardConfig)) { if (this._cardConfig && !Object.isFrozen(this._cardConfig)) {
@ -156,12 +139,12 @@ export class HuiDialogEditCard
}; };
protected render() { protected render() {
if (!this._params) { if (!this._params || !this._cardConfig) {
return nothing; return nothing;
} }
let heading: string; let heading: string;
if (this._cardConfig && this._cardConfig.type) { if (this._cardConfig.type) {
let cardName: string | undefined; let cardName: string | undefined;
if (isCustomType(this._cardConfig.type)) { if (isCustomType(this._cardConfig.type)) {
// prettier-ignore // prettier-ignore
@ -181,13 +164,6 @@ export class HuiDialogEditCard
"ui.panel.lovelace.editor.edit_card.typed_header", "ui.panel.lovelace.editor.edit_card.typed_header",
{ type: cardName } { type: cardName }
); );
} else if (!this._cardConfig) {
heading = this._containerConfig.title
? this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.pick_card_view_title",
{ name: this._containerConfig.title }
)
: this.hass!.localize("ui.panel.lovelace.editor.edit_card.pick_card");
} else { } else {
heading = this.hass!.localize( heading = this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.header" "ui.panel.lovelace.editor.edit_card.header"
@ -230,10 +206,8 @@ export class HuiDialogEditCard
<div class="content"> <div class="content">
<div class="element-editor"> <div class="element-editor">
<hui-card-element-editor <hui-card-element-editor
.showVisibilityTab=${this._cardConfig?.type !== "conditional"} .showVisibilityTab=${this._cardConfig.type !== "conditional"}
.sectionConfig=${this._isInSection .sectionConfig=${this._sectionConfig}
? this._containerConfig
: undefined}
.hass=${this.hass} .hass=${this.hass}
.lovelace=${this._params.lovelaceConfig} .lovelace=${this._params.lovelaceConfig}
.value=${this._cardConfig} .value=${this._cardConfig}
@ -244,7 +218,7 @@ export class HuiDialogEditCard
></hui-card-element-editor> ></hui-card-element-editor>
</div> </div>
<div class="element-preview"> <div class="element-preview">
${this._isInSection ${this._sectionConfig
? html` ? html`
<hui-section <hui-section
.hass=${this.hass} .hass=${this.hass}
@ -345,14 +319,10 @@ export class HuiDialogEditCard
this._cardEditorEl?.focusYamlEditor(); this._cardEditorEl?.focusYamlEditor();
} }
private get _isInSection() {
return this._params!.path.length === 2;
}
private _cardConfigInSection = memoizeOne( private _cardConfigInSection = memoizeOne(
(cardConfig?: LovelaceCardConfig) => { (cardConfig: LovelaceCardConfig) => {
const { cards, title, ...containerConfig } = this const { cards, title, ...containerConfig } = this
._containerConfig as LovelaceSectionConfig; ._sectionConfig as LovelaceSectionConfig;
return { return {
...containerConfig, ...containerConfig,
@ -411,20 +381,18 @@ export class HuiDialogEditCard
return; return;
} }
this._saving = true; this._saving = true;
const path = this._params!.path; try {
await this._params!.saveConfig( await this._params!.saveCardConfig(this._cardConfig!);
"cardConfig" in this._params!
? addCard(this._params!.lovelaceConfig, path, this._cardConfig!)
: replaceCard(
this._params!.lovelaceConfig,
[...path, this._params!.cardIndex],
this._cardConfig!
)
);
this._saving = false; this._saving = false;
this._dirty = false; this._dirty = false;
showSaveSuccessToast(this, this.hass); showSaveSuccessToast(this, this.hass);
this.closeDialog(); this.closeDialog();
} catch (err: any) {
showToast(this, {
message: err.message,
});
this._saving = false;
}
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {

View File

@ -1,20 +1,15 @@
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
import type { LovelaceConfig } from "../../../../data/lovelace/config/types"; import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
import type { LovelaceContainerPath } from "../lovelace-path";
export type EditCardDialogParams = { export interface EditCardDialogParams {
lovelaceConfig: LovelaceConfig; lovelaceConfig: LovelaceConfig;
saveConfig: (config: LovelaceConfig) => void; saveCardConfig: (config: LovelaceCardConfig) => void;
path: LovelaceContainerPath;
} & (
| {
cardIndex: number;
}
| {
cardConfig: LovelaceCardConfig; cardConfig: LovelaceCardConfig;
sectionConfig?: LovelaceSectionConfig;
isNew?: boolean;
} }
);
export const importEditCardDialog = () => import("./hui-dialog-edit-card"); export const importEditCardDialog = () => import("./hui-dialog-edit-card");

View File

@ -21,6 +21,7 @@ import {
import { createSectionElement } from "../create-element/create-section-element"; import { createSectionElement } from "../create-element/create-section-element";
import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dialog"; import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dialog";
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
import { replaceCard } from "../editor/config-util";
import { performDeleteCard } from "../editor/delete-card"; import { performDeleteCard } from "../editor/delete-card";
import { parseLovelaceCardPath } from "../editor/lovelace-path"; import { parseLovelaceCardPath } from "../editor/lovelace-path";
import { generateLovelaceSectionStrategy } from "../strategies/get-strategy"; import { generateLovelaceSectionStrategy } from "../strategies/get-strategy";
@ -253,11 +254,23 @@ export class HuiSection extends ReactiveElement {
ev.stopPropagation(); ev.stopPropagation();
if (!this.lovelace) return; if (!this.lovelace) return;
const { cardIndex } = parseLovelaceCardPath(ev.detail.path); const { cardIndex } = parseLovelaceCardPath(ev.detail.path);
const sectionConfig = this.config;
if (isStrategySection(sectionConfig)) {
return;
}
const cardConfig = sectionConfig.cards![cardIndex];
showEditCardDialog(this, { showEditCardDialog(this, {
lovelaceConfig: this.lovelace.config, lovelaceConfig: this.lovelace.config,
saveConfig: this.lovelace.saveConfig, saveCardConfig: async (newCardConfig) => {
path: [this.viewIndex, this.index], const newConfig = replaceCard(
cardIndex, this.lovelace!.config,
[this.viewIndex, this.index, cardIndex],
newCardConfig
);
await this.lovelace!.saveConfig(newConfig);
},
sectionConfig,
cardConfig,
}); });
}); });
this._layoutElement.addEventListener("ll-delete-card", (ev) => { this._layoutElement.addEventListener("ll-delete-card", (ev) => {

View File

@ -21,6 +21,7 @@ import { showCreateBadgeDialog } from "../editor/badge-editor/show-create-badge-
import { showEditBadgeDialog } from "../editor/badge-editor/show-edit-badge-dialog"; import { showEditBadgeDialog } from "../editor/badge-editor/show-edit-badge-dialog";
import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dialog"; import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dialog";
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
import { replaceCard } from "../editor/config-util";
import { import {
type DeleteBadgeParams, type DeleteBadgeParams,
performDeleteBadge, performDeleteBadge,
@ -270,11 +271,22 @@ export class HUIView extends ReactiveElement {
}); });
this._layoutElement.addEventListener("ll-edit-card", (ev) => { this._layoutElement.addEventListener("ll-edit-card", (ev) => {
const { cardIndex } = parseLovelaceCardPath(ev.detail.path); const { cardIndex } = parseLovelaceCardPath(ev.detail.path);
const viewConfig = this.lovelace!.config.views[this.index];
if (isStrategyView(viewConfig)) {
return;
}
const cardConfig = viewConfig.cards![cardIndex];
showEditCardDialog(this, { showEditCardDialog(this, {
lovelaceConfig: this.lovelace.config, lovelaceConfig: this.lovelace.config,
saveConfig: this.lovelace.saveConfig, saveCardConfig: async (newCardConfig) => {
path: [this.index], const newConfig = replaceCard(
cardIndex, this.lovelace!.config,
[this.index, cardIndex],
newCardConfig
);
await this.lovelace.saveConfig(newConfig);
},
cardConfig,
}); });
}); });
this._layoutElement.addEventListener("ll-delete-card", (ev) => { this._layoutElement.addEventListener("ll-delete-card", (ev) => {