diff --git a/src/panels/lovelace/components/hui-card-edit-mode.ts b/src/panels/lovelace/components/hui-card-edit-mode.ts index 19d4a7918a..0711cf5849 100644 --- a/src/panels/lovelace/components/hui-card-edit-mode.ts +++ b/src/panels/lovelace/components/hui-card-edit-mode.ts @@ -1,4 +1,3 @@ -import "@material/mwc-button"; import { mdiContentCopy, mdiContentCut, @@ -8,29 +7,18 @@ import { mdiPencil, mdiPlusCircleMultipleOutline, } from "@mdi/js"; -import deepClone from "deep-clone-simple"; import type { CSSResultGroup, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import { storage } from "../../../common/decorators/storage"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-button-menu"; import "../../../components/ha-icon-button"; import "../../../components/ha-list-item"; import "../../../components/ha-svg-icon"; -import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; import { haStyle } from "../../../resources/styles"; 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 { - findLovelaceContainer, - findLovelaceItems, - getLovelaceContainerPath, - parseLovelaceCardPath, -} from "../editor/lovelace-path"; import type { Lovelace } from "../types"; @customElement("hui-card-edit-mode") @@ -62,19 +50,6 @@ export class HuiCardEditMode extends LitElement { @state() 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; protected firstUpdated(): void { @@ -274,25 +249,7 @@ export class HuiCardEditMode extends LitElement { } private _duplicateCard(): void { - const { cardIndex, sectionIndex } = parseLovelaceCardPath(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, - }); + fireEvent(this, "ll-duplicate-card", { path: this.path! }); } private _editCard(): void { @@ -300,14 +257,12 @@ export class HuiCardEditMode extends LitElement { } private _cutCard(): void { - this._copyCard(); + fireEvent(this, "ll-copy-card", { path: this.path! }); fireEvent(this, "ll-delete-card", { path: this.path!, silent: true }); } private _copyCard(): void { - const { cardIndex } = parseLovelaceCardPath(this.path!); - const cardConfig = this._cards[cardIndex]; - this._clipboard = deepClone(cardConfig); + fireEvent(this, "ll-copy-card", { path: this.path! }); } private _deleteCard(): void { diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts index b74d609f96..3f3f1a91ad 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -10,13 +10,13 @@ import { mdiPlus, mdiPlusCircleMultipleOutline, } from "@mdi/js"; -import deepClone from "deep-clone-simple"; import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, queryAssignedNodes } from "lit/decorators"; import { storage } from "../../../common/decorators/storage"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-button-menu"; +import "../../../components/ha-card"; import "../../../components/ha-icon-button"; import "../../../components/ha-list-item"; import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; @@ -29,7 +29,6 @@ import { import { haStyle } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; import { computeCardSize } from "../common/compute-card-size"; -import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; import { addCard, deleteCard, @@ -267,24 +266,13 @@ export class HuiCardOptions extends LitElement { this._cutCard(); break; case 4: - this._deleteCard({ silent: false }); + this._deleteCard(); break; } } private _duplicateCard(): void { - const { cardIndex } = parseLovelaceCardPath(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, - }); + fireEvent(this, "ll-duplicate-card", { path: this.path! }); } private _editCard(): void { @@ -292,14 +280,16 @@ export class HuiCardOptions extends LitElement { } private _cutCard(): void { - this._copyCard(); - this._deleteCard({ silent: true }); + fireEvent(this, "ll-copy-card", { path: this.path! }); + fireEvent(this, "ll-delete-card", { path: this.path!, silent: true }); } private _copyCard(): void { - const { cardIndex } = parseLovelaceCardPath(this.path!); - const cardConfig = this._cards[cardIndex]; - this._clipboard = deepClone(cardConfig); + fireEvent(this, "ll-copy-card", { path: this.path! }); + } + + private _deleteCard(): void { + fireEvent(this, "ll-delete-card", { path: this.path!, silent: false }); } 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 { diff --git a/src/panels/lovelace/sections/hui-grid-section.ts b/src/panels/lovelace/sections/hui-grid-section.ts index 4906c4d107..c30b3484b2 100644 --- a/src/panels/lovelace/sections/hui-grid-section.ts +++ b/src/panels/lovelace/sections/hui-grid-section.ts @@ -7,6 +7,7 @@ import { repeat } from "lit/directives/repeat"; import { styleMap } from "lit/directives/style-map"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-ripple"; +import "../../../components/ha-sortable"; import type { HaSortableOptions } from "../../../components/ha-sortable"; import type { LovelaceSectionElement } from "../../../data/lovelace"; import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; diff --git a/src/panels/lovelace/sections/hui-section.ts b/src/panels/lovelace/sections/hui-section.ts index 5155e996ea..5bcd11b56a 100644 --- a/src/panels/lovelace/sections/hui-section.ts +++ b/src/panels/lovelace/sections/hui-section.ts @@ -1,6 +1,8 @@ +import deepClone from "deep-clone-simple"; import type { PropertyValues } from "lit"; import { ReactiveElement } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { storage } from "../../../common/decorators/storage"; import { fireEvent } from "../../../common/dom/fire_event"; import type { MediaQueriesListener } from "../../../common/dom/media_query"; import "../../../components/ha-svg-icon"; @@ -21,7 +23,7 @@ import { import { createSectionElement } from "../create-element/create-section-element"; import { showCreateCardDialog } from "../editor/card-editor/show-create-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 { parseLovelaceCardPath } from "../editor/lovelace-path"; import { generateLovelaceSectionStrategy } from "../strategies/get-strategy"; @@ -59,6 +61,14 @@ export class HuiSection extends ReactiveElement { private _listeners: MediaQueriesListener[] = []; + @storage({ + key: "dashboardCardClipboard", + state: false, + subscribe: false, + storage: "sessionStorage", + }) + protected _clipboard?: LovelaceCardConfig; + private _createCardElement(cardConfig: LovelaceCardConfig) { const element = document.createElement("hui-card"); element.hass = this.hass; @@ -278,6 +288,43 @@ export class HuiSection extends ReactiveElement { if (!this.lovelace) return; 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 { diff --git a/src/panels/lovelace/views/hui-view.ts b/src/panels/lovelace/views/hui-view.ts index f862915c86..6b2f19739b 100644 --- a/src/panels/lovelace/views/hui-view.ts +++ b/src/panels/lovelace/views/hui-view.ts @@ -1,6 +1,8 @@ +import deepClone from "deep-clone-simple"; import type { PropertyValues } from "lit"; import { ReactiveElement } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { storage } from "../../../common/decorators/storage"; import type { HASSDomEvent } from "../../../common/dom/fire_event"; import "../../../components/entity/ha-state-label-badge"; 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 { showCreateCardDialog } from "../editor/card-editor/show-create-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 { type DeleteBadgeParams, performDeleteBadge, @@ -45,6 +47,8 @@ declare global { "ll-create-card": { suggested?: string[] } | undefined; "ll-edit-card": { path: LovelaceCardPath }; "ll-delete-card": DeleteCardParams; + "ll-duplicate-card": { path: LovelaceCardPath }; + "ll-copy-card": { path: LovelaceCardPath }; "ll-create-badge": undefined; "ll-edit-badge": { path: LovelaceCardPath }; "ll-delete-badge": DeleteBadgeParams; @@ -53,6 +57,8 @@ declare global { "ll-create-card": HASSDomEvent; "ll-edit-card": HASSDomEvent; "ll-delete-card": HASSDomEvent; + "ll-duplicate-card": HASSDomEvent; + "ll-copy-card": HASSDomEvent; "ll-create-badge": HASSDomEvent; "ll-edit-badge": HASSDomEvent; "ll-delete-badge": HASSDomEvent; @@ -79,6 +85,14 @@ export class HUIView extends ReactiveElement { private _layoutElement?: LovelaceViewElement; + @storage({ + key: "dashboardCardClipboard", + state: false, + subscribe: false, + storage: "sessionStorage", + }) + protected _clipboard?: LovelaceCardConfig; + private _createCardElement(cardConfig: LovelaceCardConfig) { const element = document.createElement("hui-card"); element.hass = this.hass; @@ -313,6 +327,37 @@ export class HUIView extends ReactiveElement { if (!this.lovelace) return; 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 {