Compare commits

...

1 Commits

Author SHA1 Message Date
Paul Bottein
bd7b345fe2 Improve strategy edit mode 2025-08-18 13:43:18 +02:00
7 changed files with 57 additions and 25 deletions

View File

@@ -23,6 +23,7 @@ export interface LovelaceViewElement extends HTMLElement {
badges?: HuiBadge[]; badges?: HuiBadge[];
sections?: HuiSection[]; sections?: HuiSection[];
isStrategy: boolean; isStrategy: boolean;
allowEdit: boolean;
setConfig(config: LovelaceViewConfig): void; setConfig(config: LovelaceViewConfig): void;
} }
@@ -35,6 +36,7 @@ export interface LovelaceSectionElement extends HTMLElement {
cards?: HuiCard[]; cards?: HuiCard[];
isStrategy: boolean; isStrategy: boolean;
importOnly?: boolean; importOnly?: boolean;
allowEdit: boolean;
setConfig(config: LovelaceSectionConfig): void; setConfig(config: LovelaceSectionConfig): void;
} }

View File

@@ -983,6 +983,7 @@ class HUIRoot extends LitElement {
} else { } else {
view = document.createElement("hui-view"); view = document.createElement("hui-view");
view.index = viewIndex; view.index = viewIndex;
view.allowEdit = !isStrategyDashboard(this.lovelace!.rawConfig);
this._viewCache![viewIndex] = view; this._viewCache![viewIndex] = view;
} }

View File

@@ -50,6 +50,8 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
@property({ attribute: false }) public isStrategy = false; @property({ attribute: false }) public isStrategy = false;
@property({ attribute: false }) public allowEdit = false;
@property({ attribute: false }) public cards: HuiCard[] = []; @property({ attribute: false }) public cards: HuiCard[] = [];
@property({ attribute: "import-only", type: Boolean }) @property({ attribute: "import-only", type: Boolean })
@@ -80,7 +82,7 @@ export class GridSection extends LitElement implements LovelaceSectionElement {
const cardsConfig = this._config?.cards ?? []; const cardsConfig = this._config?.cards ?? [];
const editMode = Boolean(this.lovelace?.editMode && !this.isStrategy); const editMode = Boolean(this.allowEdit && this.lovelace?.editMode);
const sortableOptions = this.importOnly const sortableOptions = this.importOnly
? IMPORT_MODE_CARD_SORTABLE_OPTIONS ? IMPORT_MODE_CARD_SORTABLE_OPTIONS

View File

@@ -44,7 +44,8 @@ export class HuiSection extends ReactiveElement {
@property({ attribute: false }) public lovelace?: Lovelace; @property({ attribute: false }) public lovelace?: Lovelace;
@property({ type: Boolean, reflect: true }) public preview = false; @property({ type: Boolean, attribute: "force-preview" })
public forcePreview = false;
@property({ type: Boolean, attribute: "import-only" }) @property({ type: Boolean, attribute: "import-only" })
public importOnly = false; public importOnly = false;
@@ -53,6 +54,8 @@ export class HuiSection extends ReactiveElement {
@property({ attribute: false, type: Number }) public viewIndex!: number; @property({ attribute: false, type: Number }) public viewIndex!: number;
@property({ attribute: false }) public allowEdit = false;
@state() private _cards: HuiCard[] = []; @state() private _cards: HuiCard[] = [];
private _layoutElementType?: string; private _layoutElementType?: string;
@@ -136,10 +139,17 @@ export class HuiSection extends ReactiveElement {
if (changedProperties.has("lovelace")) { if (changedProperties.has("lovelace")) {
this._layoutElement.lovelace = this.lovelace; this._layoutElement.lovelace = this.lovelace;
} }
if (changedProperties.has("preview")) { if (
this._layoutElement.preview = this.preview; changedProperties.has("forcePreview") ||
changedProperties.has("lovelace") ||
changedProperties.has("allowEdit")
) {
const preview =
this.forcePreview ||
(this.allowEdit && Boolean(this.lovelace?.editMode));
this._layoutElement.preview = preview;
this._cards.forEach((element) => { this._cards.forEach((element) => {
element.preview = this.preview; element.preview = preview;
}); });
} }
if (changedProperties.has("importOnly")) { if (changedProperties.has("importOnly")) {
@@ -208,6 +218,7 @@ export class HuiSection extends ReactiveElement {
this._createCards(sectionConfig); this._createCards(sectionConfig);
this._layoutElement!.isStrategy = isStrategy; this._layoutElement!.isStrategy = isStrategy;
this._layoutElement!.allowEdit = this.allowEdit && !isStrategy;
this._layoutElement!.hass = this.hass; this._layoutElement!.hass = this.hass;
this._layoutElement!.lovelace = this.lovelace; this._layoutElement!.lovelace = this.lovelace;
this._layoutElement!.index = this.index; this._layoutElement!.index = this.index;

View File

@@ -25,7 +25,6 @@ import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";
import type { HuiBadge } from "../badges/hui-badge"; import type { HuiBadge } from "../badges/hui-badge";
import "./hui-view-header";
import type { HuiCard } from "../cards/hui-card"; import type { HuiCard } from "../cards/hui-card";
import "../components/hui-badge-edit-mode"; import "../components/hui-badge-edit-mode";
import { import {
@@ -44,6 +43,7 @@ import {
import { showEditSectionDialog } from "../editor/section-editor/show-edit-section-dialog"; import { showEditSectionDialog } from "../editor/section-editor/show-edit-section-dialog";
import type { HuiSection } from "../sections/hui-section"; import type { HuiSection } from "../sections/hui-section";
import type { Lovelace } from "../types"; import type { Lovelace } from "../types";
import "./hui-view-header";
export const DEFAULT_MAX_COLUMNS = 4; export const DEFAULT_MAX_COLUMNS = 4;
@@ -59,6 +59,8 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
@property({ attribute: false }) public isStrategy = false; @property({ attribute: false }) public isStrategy = false;
@property({ attribute: false }) public allowEdit = false;
@property({ attribute: false }) public sections: HuiSection[] = []; @property({ attribute: false }) public sections: HuiSection[] = [];
@property({ attribute: false }) public cards: HuiCard[] = []; @property({ attribute: false }) public cards: HuiCard[] = [];
@@ -149,7 +151,8 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
const sections = this.sections; const sections = this.sections;
const totalSectionCount = const totalSectionCount =
this._sectionColumnCount + (this.lovelace?.editMode ? 1 : 0); this._sectionColumnCount + (this.lovelace?.editMode ? 1 : 0);
const editMode = this.lovelace.editMode;
const editMode = Boolean(this.allowEdit && this.lovelace.editMode);
const maxColumnCount = this._columnsController.value ?? 1; const maxColumnCount = this._columnsController.value ?? 1;
@@ -163,6 +166,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
.hass=${this.hass} .hass=${this.hass}
.badges=${this.badges} .badges=${this.badges}
.lovelace=${this.lovelace} .lovelace=${this.lovelace}
.editMode=${editMode}
.viewIndex=${this.index} .viewIndex=${this.index}
.config=${this._config?.header} .config=${this._config?.header}
style=${styleMap({ style=${styleMap({
@@ -205,7 +209,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
})} })}
> >
${ ${
this.lovelace?.editMode editMode
? html` ? html`
<div class="section-header"> <div class="section-header">
${editMode ${editMode

View File

@@ -3,6 +3,7 @@ import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { DragScrollController } from "../../../common/controllers/drag-scroll-controller";
import "../../../components/ha-ripple"; import "../../../components/ha-ripple";
import "../../../components/ha-sortable"; import "../../../components/ha-sortable";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
@@ -16,11 +17,10 @@ import type { HuiBadge } from "../badges/hui-badge";
import "../badges/hui-view-badges"; import "../badges/hui-view-badges";
import type { HuiCard } from "../cards/hui-card"; import type { HuiCard } from "../cards/hui-card";
import "../components/hui-badge-edit-mode"; import "../components/hui-badge-edit-mode";
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
import { replaceView } from "../editor/config-util"; import { replaceView } from "../editor/config-util";
import { showEditViewHeaderDialog } from "../editor/view-header/show-edit-view-header-dialog"; import { showEditViewHeaderDialog } from "../editor/view-header/show-edit-view-header-dialog";
import type { Lovelace } from "../types"; import type { Lovelace } from "../types";
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
import { DragScrollController } from "../../../common/controllers/drag-scroll-controller";
export const DEFAULT_VIEW_HEADER_LAYOUT = "center"; export const DEFAULT_VIEW_HEADER_LAYOUT = "center";
export const DEFAULT_VIEW_HEADER_BADGES_POSITION = "bottom"; export const DEFAULT_VIEW_HEADER_BADGES_POSITION = "bottom";
@@ -32,6 +32,8 @@ export class HuiViewHeader extends LitElement {
@property({ attribute: false }) public lovelace!: Lovelace; @property({ attribute: false }) public lovelace!: Lovelace;
@property({ attribute: false }) public editMode = false;
@property({ attribute: false }) public card?: HuiCard; @property({ attribute: false }) public card?: HuiCard;
@property({ attribute: false }) public badges: HuiBadge[] = []; @property({ attribute: false }) public badges: HuiBadge[] = [];
@@ -43,7 +45,7 @@ export class HuiViewHeader extends LitElement {
private _checkHidden() { private _checkHidden() {
const allHidden = const allHidden =
!this.card && !this.card &&
!this.lovelace.editMode && !this.editMode &&
this.badges.every((badges) => badges.hidden); this.badges.every((badges) => badges.hidden);
this.toggleAttribute("hidden", allHidden); this.toggleAttribute("hidden", allHidden);
} }
@@ -76,15 +78,15 @@ export class HuiViewHeader extends LitElement {
willUpdate(changedProperties: PropertyValues<typeof this>): void { willUpdate(changedProperties: PropertyValues<typeof this>): void {
if ( if (
changedProperties.has("badges") || changedProperties.has("badges") ||
changedProperties.has("lovelace") || changedProperties.has("editMode") ||
changedProperties.has("card") changedProperties.has("card")
) { ) {
this._checkHidden(); this._checkHidden();
} }
if (changedProperties.has("config") || changedProperties.has("lovelace")) { if (changedProperties.has("config") || changedProperties.has("editMode")) {
this._dragScrollController.enabled = this._dragScrollController.enabled =
!this.lovelace.editMode && this.config?.badges_wrap === "scroll"; !this.editMode && this.config?.badges_wrap === "scroll";
} }
if (changedProperties.has("config")) { if (changedProperties.has("config")) {
@@ -101,8 +103,8 @@ export class HuiViewHeader extends LitElement {
if (changedProperties.has("hass")) { if (changedProperties.has("hass")) {
this.card.hass = this.hass; this.card.hass = this.hass;
} }
if (changedProperties.has("lovelace")) { if (changedProperties.has("editMode")) {
this.card.preview = this.lovelace.editMode; this.card.preview = this.editMode;
} }
} }
} }
@@ -110,7 +112,7 @@ export class HuiViewHeader extends LitElement {
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;
element.preview = this.lovelace.editMode; element.preview = this.editMode;
element.config = cardConfig; element.config = cardConfig;
element.load(); element.load();
return element; return element;
@@ -193,7 +195,7 @@ export class HuiViewHeader extends LitElement {
render() { render() {
if (!this.lovelace) return nothing; if (!this.lovelace) return nothing;
const editMode = Boolean(this.lovelace?.editMode); const editMode = this.editMode;
const card = this.card; const card = this.card;

View File

@@ -80,6 +80,8 @@ export class HUIView extends ReactiveElement {
@property({ type: Number }) public index!: number; @property({ type: Number }) public index!: number;
@property({ attribute: false }) public allowEdit = false;
@state() private _cards: HuiCard[] = []; @state() private _cards: HuiCard[] = [];
@state() private _badges: HuiBadge[] = []; @state() private _badges: HuiBadge[] = [];
@@ -105,7 +107,7 @@ export class HUIView extends ReactiveElement {
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;
element.preview = this.lovelace.editMode; element.preview = this.allowEdit && this.lovelace.editMode;
element.config = cardConfig; element.config = cardConfig;
element.addEventListener("card-updated", (ev: Event) => { element.addEventListener("card-updated", (ev: Event) => {
ev.stopPropagation(); ev.stopPropagation();
@@ -118,7 +120,7 @@ export class HUIView extends ReactiveElement {
public createBadgeElement(badgeConfig: LovelaceBadgeConfig) { public createBadgeElement(badgeConfig: LovelaceBadgeConfig) {
const element = document.createElement("hui-badge"); const element = document.createElement("hui-badge");
element.hass = this.hass; element.hass = this.hass;
element.preview = this.lovelace.editMode; element.preview = this.allowEdit && this.lovelace.editMode;
element.config = badgeConfig; element.config = badgeConfig;
element.addEventListener("badge-updated", (ev: Event) => { element.addEventListener("badge-updated", (ev: Event) => {
ev.stopPropagation(); ev.stopPropagation();
@@ -130,12 +132,13 @@ export class HUIView extends ReactiveElement {
// Public to make demo happy // Public to make demo happy
public createSectionElement(sectionConfig: LovelaceSectionConfig) { public createSectionElement(sectionConfig: LovelaceSectionConfig) {
const viewConfig = this.lovelace.config.views[this.index];
const element = document.createElement("hui-section"); const element = document.createElement("hui-section");
element.hass = this.hass; element.hass = this.hass;
element.lovelace = this.lovelace; element.lovelace = this.lovelace;
element.config = sectionConfig; element.config = sectionConfig;
element.viewIndex = this.index; element.viewIndex = this.index;
element.preview = this.lovelace.editMode; element.allowEdit = this.allowEdit && !isStrategyView(viewConfig);
element.addEventListener( element.addEventListener(
"ll-rebuild", "ll-rebuild",
(ev: Event) => { (ev: Event) => {
@@ -276,20 +279,26 @@ export class HUIView extends ReactiveElement {
} }
if (changedProperties.has("lovelace")) { if (changedProperties.has("lovelace")) {
this._layoutElement.lovelace = this.lovelace; this._layoutElement.lovelace = this.lovelace;
}
if (
changedProperties.has("lovelace") ||
changedProperties.has("allowEdit")
) {
const preview = this.allowEdit && this.lovelace.editMode;
this._sections.forEach((element) => { this._sections.forEach((element) => {
try { try {
element.hass = this.hass; element.hass = this.hass;
element.lovelace = this.lovelace; element.lovelace = this.lovelace;
element.preview = this.lovelace.editMode; element.preview = preview;
} catch (e: any) { } catch (e: any) {
this._rebuildSection(element, createErrorSectionConfig(e.message)); this._rebuildSection(element, createErrorSectionConfig(e.message));
} }
}); });
this._cards.forEach((element) => { this._cards.forEach((element) => {
element.preview = this.lovelace.editMode; element.preview = preview;
}); });
this._badges.forEach((element) => { this._badges.forEach((element) => {
element.preview = this.lovelace.editMode; element.preview = preview;
}); });
} }
if (changedProperties.has("_cards")) { if (changedProperties.has("_cards")) {
@@ -337,6 +346,7 @@ export class HUIView extends ReactiveElement {
this._createCards(viewConfig); this._createCards(viewConfig);
this._createSections(viewConfig); this._createSections(viewConfig);
this._layoutElement!.isStrategy = isStrategy; this._layoutElement!.isStrategy = isStrategy;
this._layoutElement!.allowEdit = this.allowEdit && !isStrategy;
this._layoutElement!.hass = this.hass; this._layoutElement!.hass = this.hass;
this._layoutElement!.narrow = this.narrow; this._layoutElement!.narrow = this.narrow;
this._layoutElement!.lovelace = this.lovelace; this._layoutElement!.lovelace = this.lovelace;
@@ -357,7 +367,7 @@ export class HUIView extends ReactiveElement {
const rawConfig = this.lovelace.config.views[this.index]; const rawConfig = this.lovelace.config.views[this.index];
const viewConfig = await this._generateConfig(rawConfig); const viewConfig = await this._generateConfig(rawConfig);
const isStrategy = isStrategyView(viewConfig); const isStrategy = isStrategyView(rawConfig);
this._setConfig(viewConfig, isStrategy); this._setConfig(viewConfig, isStrategy);
} }