Use fire-event for copy, cut and duplicate (#24486)

* Add event for duplicate

* Update new events in card option and card edit mode

* Add copy event
This commit is contained in:
Paul Bottein 2025-03-04 10:14:22 +01:00 committed by GitHub
parent dbe5bffe22
commit 5a37087231
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 108 additions and 74 deletions

View File

@ -1,4 +1,3 @@
import "@material/mwc-button";
import { import {
mdiContentCopy, mdiContentCopy,
mdiContentCut, mdiContentCut,
@ -8,29 +7,18 @@ import {
mdiPencil, mdiPencil,
mdiPlusCircleMultipleOutline, mdiPlusCircleMultipleOutline,
} from "@mdi/js"; } from "@mdi/js";
import deepClone from "deep-clone-simple";
import type { CSSResultGroup, TemplateResult } from "lit"; import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit"; import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { storage } from "../../../common/decorators/storage";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button-menu"; import "../../../components/ha-button-menu";
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
import "../../../components/ha-list-item"; import "../../../components/ha-list-item";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
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 { addCard } from "../editor/config-util";
import type { LovelaceCardPath } from "../editor/lovelace-path"; import type { LovelaceCardPath } from "../editor/lovelace-path";
import {
findLovelaceContainer,
findLovelaceItems,
getLovelaceContainerPath,
parseLovelaceCardPath,
} from "../editor/lovelace-path";
import type { Lovelace } from "../types"; import type { Lovelace } from "../types";
@customElement("hui-card-edit-mode") @customElement("hui-card-edit-mode")
@ -62,19 +50,6 @@ export class HuiCardEditMode extends LitElement {
@state() @state()
public _focused = false; public _focused = false;
@storage({
key: "dashboardCardClipboard",
state: false,
subscribe: false,
storage: "sessionStorage",
})
protected _clipboard?: LovelaceCardConfig;
private get _cards() {
const containerPath = getLovelaceContainerPath(this.path!);
return findLovelaceItems("cards", this.lovelace!.config, containerPath)!;
}
private _touchStarted = false; private _touchStarted = false;
protected firstUpdated(): void { protected firstUpdated(): void {
@ -274,25 +249,7 @@ export class HuiCardEditMode extends LitElement {
} }
private _duplicateCard(): void { private _duplicateCard(): void {
const { cardIndex, sectionIndex } = parseLovelaceCardPath(this.path!); fireEvent(this, "ll-duplicate-card", { path: this.path! });
const containerPath = getLovelaceContainerPath(this.path!);
const sectionConfig =
sectionIndex !== undefined
? findLovelaceContainer(this.lovelace!.config, containerPath)
: undefined;
const cardConfig = this._cards![cardIndex];
showEditCardDialog(this, {
lovelaceConfig: this.lovelace!.config,
saveCardConfig: async (config) => {
const newConfig = addCard(this.lovelace!.config, containerPath, config);
await this.lovelace!.saveConfig(newConfig);
},
cardConfig,
sectionConfig,
isNew: true,
});
} }
private _editCard(): void { private _editCard(): void {
@ -300,14 +257,12 @@ export class HuiCardEditMode extends LitElement {
} }
private _cutCard(): void { private _cutCard(): void {
this._copyCard(); fireEvent(this, "ll-copy-card", { path: this.path! });
fireEvent(this, "ll-delete-card", { path: this.path!, silent: true }); fireEvent(this, "ll-delete-card", { path: this.path!, silent: true });
} }
private _copyCard(): void { private _copyCard(): void {
const { cardIndex } = parseLovelaceCardPath(this.path!); fireEvent(this, "ll-copy-card", { path: this.path! });
const cardConfig = this._cards[cardIndex];
this._clipboard = deepClone(cardConfig);
} }
private _deleteCard(): void { private _deleteCard(): void {

View File

@ -10,13 +10,13 @@ import {
mdiPlus, mdiPlus,
mdiPlusCircleMultipleOutline, mdiPlusCircleMultipleOutline,
} from "@mdi/js"; } from "@mdi/js";
import deepClone from "deep-clone-simple";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit"; import { LitElement, css, html, nothing } from "lit";
import { customElement, property, queryAssignedNodes } from "lit/decorators"; import { customElement, property, queryAssignedNodes } from "lit/decorators";
import { storage } from "../../../common/decorators/storage"; import { storage } from "../../../common/decorators/storage";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-button-menu"; import "../../../components/ha-button-menu";
import "../../../components/ha-card";
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
import "../../../components/ha-list-item"; import "../../../components/ha-list-item";
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
@ -29,7 +29,6 @@ import {
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import { computeCardSize } from "../common/compute-card-size"; import { computeCardSize } from "../common/compute-card-size";
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
import { import {
addCard, addCard,
deleteCard, deleteCard,
@ -267,24 +266,13 @@ export class HuiCardOptions extends LitElement {
this._cutCard(); this._cutCard();
break; break;
case 4: case 4:
this._deleteCard({ silent: false }); this._deleteCard();
break; break;
} }
} }
private _duplicateCard(): void { private _duplicateCard(): void {
const { cardIndex } = parseLovelaceCardPath(this.path!); fireEvent(this, "ll-duplicate-card", { path: this.path! });
const containerPath = getLovelaceContainerPath(this.path!);
const cardConfig = this._cards![cardIndex];
showEditCardDialog(this, {
lovelaceConfig: this.lovelace!.config,
saveCardConfig: async (config) => {
const newConfig = addCard(this.lovelace!.config, containerPath, config);
await this.lovelace!.saveConfig(newConfig);
},
cardConfig,
isNew: true,
});
} }
private _editCard(): void { private _editCard(): void {
@ -292,14 +280,16 @@ export class HuiCardOptions extends LitElement {
} }
private _cutCard(): void { private _cutCard(): void {
this._copyCard(); fireEvent(this, "ll-copy-card", { path: this.path! });
this._deleteCard({ silent: true }); fireEvent(this, "ll-delete-card", { path: this.path!, silent: true });
} }
private _copyCard(): void { private _copyCard(): void {
const { cardIndex } = parseLovelaceCardPath(this.path!); fireEvent(this, "ll-copy-card", { path: this.path! });
const cardConfig = this._cards[cardIndex]; }
this._clipboard = deepClone(cardConfig);
private _deleteCard(): void {
fireEvent(this, "ll-delete-card", { path: this.path!, silent: false });
} }
private _decreaseCardPosiion(): void { private _decreaseCardPosiion(): void {
@ -420,10 +410,6 @@ export class HuiCardOptions extends LitElement {
}, },
}); });
} }
private _deleteCard({ silent }: { silent: boolean }): void {
fireEvent(this, "ll-delete-card", { path: this.path!, silent });
}
} }
declare global { declare global {

View File

@ -7,6 +7,7 @@ import { repeat } from "lit/directives/repeat";
import { styleMap } from "lit/directives/style-map"; import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-ripple"; import "../../../components/ha-ripple";
import "../../../components/ha-sortable";
import type { HaSortableOptions } from "../../../components/ha-sortable"; import type { HaSortableOptions } from "../../../components/ha-sortable";
import type { LovelaceSectionElement } from "../../../data/lovelace"; import type { LovelaceSectionElement } from "../../../data/lovelace";
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";

View File

@ -1,6 +1,8 @@
import deepClone from "deep-clone-simple";
import type { PropertyValues } from "lit"; import type { PropertyValues } from "lit";
import { ReactiveElement } from "lit"; import { ReactiveElement } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { storage } from "../../../common/decorators/storage";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import type { MediaQueriesListener } from "../../../common/dom/media_query"; import type { MediaQueriesListener } from "../../../common/dom/media_query";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
@ -21,7 +23,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 { addCard, 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";
@ -59,6 +61,14 @@ export class HuiSection extends ReactiveElement {
private _listeners: MediaQueriesListener[] = []; private _listeners: MediaQueriesListener[] = [];
@storage({
key: "dashboardCardClipboard",
state: false,
subscribe: false,
storage: "sessionStorage",
})
protected _clipboard?: LovelaceCardConfig;
private _createCardElement(cardConfig: LovelaceCardConfig) { private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = document.createElement("hui-card"); const element = document.createElement("hui-card");
element.hass = this.hass; element.hass = this.hass;
@ -278,6 +288,43 @@ export class HuiSection extends ReactiveElement {
if (!this.lovelace) return; if (!this.lovelace) return;
performDeleteCard(this.hass, this.lovelace, ev.detail); performDeleteCard(this.hass, this.lovelace, ev.detail);
}); });
this._layoutElement.addEventListener("ll-duplicate-card", (ev) => {
ev.stopPropagation();
if (!this.lovelace) return;
const { cardIndex } = parseLovelaceCardPath(ev.detail.path);
const sectionConfig = this.config;
if (isStrategySection(sectionConfig)) {
return;
}
const cardConfig = sectionConfig.cards![cardIndex];
showEditCardDialog(this, {
lovelaceConfig: this.lovelace!.config,
saveCardConfig: async (newCardConfig) => {
const newConfig = addCard(
this.lovelace!.config,
[this.viewIndex, this.index],
newCardConfig
);
await this.lovelace!.saveConfig(newConfig);
},
cardConfig,
sectionConfig,
isNew: true,
});
});
this._layoutElement.addEventListener("ll-copy-card", (ev) => {
ev.stopPropagation();
if (!this.lovelace) return;
const { cardIndex } = parseLovelaceCardPath(ev.detail.path);
const sectionConfig = this.config;
if (isStrategySection(sectionConfig)) {
return;
}
const cardConfig = sectionConfig.cards![cardIndex];
this._clipboard = deepClone(cardConfig);
});
} }
private _createCards(config: LovelaceSectionConfig): void { private _createCards(config: LovelaceSectionConfig): void {

View File

@ -1,6 +1,8 @@
import deepClone from "deep-clone-simple";
import type { PropertyValues } from "lit"; import type { PropertyValues } from "lit";
import { ReactiveElement } from "lit"; import { ReactiveElement } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { storage } from "../../../common/decorators/storage";
import type { HASSDomEvent } from "../../../common/dom/fire_event"; import type { HASSDomEvent } from "../../../common/dom/fire_event";
import "../../../components/entity/ha-state-label-badge"; import "../../../components/entity/ha-state-label-badge";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
@ -21,7 +23,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 { addCard, replaceCard } from "../editor/config-util";
import { import {
type DeleteBadgeParams, type DeleteBadgeParams,
performDeleteBadge, performDeleteBadge,
@ -45,6 +47,8 @@ declare global {
"ll-create-card": { suggested?: string[] } | undefined; "ll-create-card": { suggested?: string[] } | undefined;
"ll-edit-card": { path: LovelaceCardPath }; "ll-edit-card": { path: LovelaceCardPath };
"ll-delete-card": DeleteCardParams; "ll-delete-card": DeleteCardParams;
"ll-duplicate-card": { path: LovelaceCardPath };
"ll-copy-card": { path: LovelaceCardPath };
"ll-create-badge": undefined; "ll-create-badge": undefined;
"ll-edit-badge": { path: LovelaceCardPath }; "ll-edit-badge": { path: LovelaceCardPath };
"ll-delete-badge": DeleteBadgeParams; "ll-delete-badge": DeleteBadgeParams;
@ -53,6 +57,8 @@ declare global {
"ll-create-card": HASSDomEvent<HASSDomEvents["ll-create-card"]>; "ll-create-card": HASSDomEvent<HASSDomEvents["ll-create-card"]>;
"ll-edit-card": HASSDomEvent<HASSDomEvents["ll-edit-card"]>; "ll-edit-card": HASSDomEvent<HASSDomEvents["ll-edit-card"]>;
"ll-delete-card": HASSDomEvent<HASSDomEvents["ll-delete-card"]>; "ll-delete-card": HASSDomEvent<HASSDomEvents["ll-delete-card"]>;
"ll-duplicate-card": HASSDomEvent<HASSDomEvents["ll-duplicate-card"]>;
"ll-copy-card": HASSDomEvent<HASSDomEvents["ll-copy-card"]>;
"ll-create-badge": HASSDomEvent<HASSDomEvents["ll-create-badge"]>; "ll-create-badge": HASSDomEvent<HASSDomEvents["ll-create-badge"]>;
"ll-edit-badge": HASSDomEvent<HASSDomEvents["ll-edit-badge"]>; "ll-edit-badge": HASSDomEvent<HASSDomEvents["ll-edit-badge"]>;
"ll-delete-badge": HASSDomEvent<HASSDomEvents["ll-delete-badge"]>; "ll-delete-badge": HASSDomEvent<HASSDomEvents["ll-delete-badge"]>;
@ -79,6 +85,14 @@ export class HUIView extends ReactiveElement {
private _layoutElement?: LovelaceViewElement; private _layoutElement?: LovelaceViewElement;
@storage({
key: "dashboardCardClipboard",
state: false,
subscribe: false,
storage: "sessionStorage",
})
protected _clipboard?: LovelaceCardConfig;
private _createCardElement(cardConfig: LovelaceCardConfig) { private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = document.createElement("hui-card"); const element = document.createElement("hui-card");
element.hass = this.hass; element.hass = this.hass;
@ -313,6 +327,37 @@ export class HUIView extends ReactiveElement {
if (!this.lovelace) return; if (!this.lovelace) return;
performDeleteBadge(this.hass, this.lovelace, ev.detail); performDeleteBadge(this.hass, this.lovelace, ev.detail);
}); });
this._layoutElement.addEventListener("ll-duplicate-card", (ev) => {
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, {
lovelaceConfig: this.lovelace!.config,
saveCardConfig: async (newCardConfig) => {
const newConfig = addCard(
this.lovelace!.config,
[this.index],
newCardConfig
);
await this.lovelace!.saveConfig(newConfig);
},
cardConfig,
isNew: true,
});
});
this._layoutElement.addEventListener("ll-copy-card", (ev) => {
if (!this.lovelace) return;
const { cardIndex } = parseLovelaceCardPath(ev.detail.path);
const viewConfig = this.lovelace!.config.views[this.index];
if (isStrategyView(viewConfig)) {
return;
}
const cardConfig = viewConfig.cards![cardIndex];
this._clipboard = deepClone(cardConfig);
});
} }
private _createBadges(config: LovelaceViewConfig): void { private _createBadges(config: LovelaceViewConfig): void {