Show card preview inside section in card editor (#21065)

* Show card inside section in card editor

* Replace edit mode by preview

* Add backward compatibility for custom cards

* Re-order props
This commit is contained in:
Paul Bottein 2024-06-21 11:12:18 +02:00 committed by GitHub
parent eb0579ddc5
commit f78946447f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 101 additions and 45 deletions

View File

@ -25,7 +25,7 @@ declare global {
export class HuiCard extends ReactiveElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public editMode = false;
@property({ type: Boolean }) public preview = false;
@property({ type: Boolean }) public isPanel = false;
@ -89,7 +89,9 @@ export class HuiCard extends ReactiveElement {
private _createElement(config: LovelaceCardConfig) {
const element = createCardElement(config);
element.hass = this.hass;
element.editMode = this.editMode;
element.preview = this.preview;
// For backwards compatibility
(element as any).editMode = this.preview;
// Update element when the visibility of the card changes (e.g. conditional card or filter card)
element.addEventListener("card-visibility-changed", (ev: Event) => {
ev.stopPropagation();
@ -135,9 +137,11 @@ export class HuiCard extends ReactiveElement {
this._buildElement(createErrorCardConfig(e.message, null));
}
}
if (changedProps.has("editMode")) {
if (changedProps.has("preview")) {
try {
this._element.editMode = this.editMode;
this._element.preview = this.preview;
// For backwards compatibility
(this._element as any).editMode = this.preview;
} catch (e: any) {
this._buildElement(createErrorCardConfig(e.message, null));
}
@ -192,7 +196,7 @@ export class HuiCard extends ReactiveElement {
const visible =
forceVisible ||
this.editMode ||
this.preview ||
!this.config?.visibility ||
checkConditionsMet(this.config.visibility, this.hass);
this._setElementVisibility(visible);

View File

@ -39,13 +39,13 @@ class HuiConditionalCard extends HuiConditionalBase implements LovelaceCard {
private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = document.createElement("hui-card");
element.hass = this.hass;
element.editMode = this.editMode;
element.preview = this.preview;
element.config = cardConfig;
return element;
}
protected setVisibility(conditionMet: boolean): void {
const visible = this.editMode || conditionMet;
const visible = this.preview || conditionMet;
const previouslyHidden = this.hidden;
super.setVisibility(conditionMet);
if (previouslyHidden !== this.hidden) {

View File

@ -55,7 +55,7 @@ export class HuiEntityFilterCard
@property({ type: Boolean }) public isPanel = false;
@property({ type: Boolean }) public editMode = false;
@property({ type: Boolean }) public preview = false;
@state() private _config?: EntityFilterCardConfig;
@ -117,7 +117,7 @@ export class HuiEntityFilterCard
protected shouldUpdate(changedProps: PropertyValues): boolean {
if (this._element) {
this._element.hass = this.hass;
this._element.editMode = this.editMode;
this._element.preview = this.preview;
this._element.isPanel = this.isPanel;
}
@ -247,7 +247,7 @@ export class HuiEntityFilterCard
private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = document.createElement("hui-card");
element.hass = this.hass;
element.editMode = this.editMode;
element.preview = this.preview;
element.config = cardConfig;
return element;
}

View File

@ -10,7 +10,7 @@ import { ErrorCardConfig } from "./types";
export class HuiErrorCard extends LitElement implements LovelaceCard {
public hass?: HomeAssistant;
@property({ attribute: false }) public editMode = false;
@property({ attribute: false }) public preview = false;
@state() private _config?: ErrorCardConfig;

View File

@ -38,7 +38,7 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public editMode = false;
@property({ type: Boolean }) public preview = false;
@state() private _config?: MarkdownCardConfig;
@ -163,12 +163,12 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
user: this.hass.user!.name,
},
strict: true,
report_errors: this.editMode,
report_errors: this.preview,
}
);
await this._unsubRenderTemplate;
} catch (e: any) {
if (this.editMode) {
if (this.preview) {
this._error = e.message;
this._errorLevel = undefined;
}

View File

@ -23,7 +23,7 @@ export abstract class HuiStackCard<T extends StackCardConfig = StackCardConfig>
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public editMode = false;
@property({ type: Boolean }) public preview = false;
@state() protected _cards?: HuiCard[];
@ -58,7 +58,7 @@ export abstract class HuiStackCard<T extends StackCardConfig = StackCardConfig>
}
if (changedProperties.has("editMode")) {
this._cards.forEach((card) => {
card.editMode = this.editMode;
card.preview = this.preview;
});
}
}
@ -67,7 +67,7 @@ export abstract class HuiStackCard<T extends StackCardConfig = StackCardConfig>
private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = document.createElement("hui-card");
element.hass = this.hass;
element.editMode = this.editMode;
element.preview = this.preview;
element.config = cardConfig;
return element;
}

View File

@ -24,7 +24,7 @@ declare global {
export class HuiConditionalBase extends ReactiveElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public editMode = false;
@property({ type: Boolean }) public preview = false;
@state() protected _config?: ConditionalCardConfig | ConditionalRowConfig;
@ -116,7 +116,7 @@ export class HuiConditionalBase extends ReactiveElement {
changed.has("_element") ||
changed.has("_config") ||
changed.has("hass") ||
changed.has("editMode")
changed.has("preview")
) {
this._listenMediaQueries();
this._updateVisibility();
@ -128,7 +128,7 @@ export class HuiConditionalBase extends ReactiveElement {
return;
}
this._element.editMode = this.editMode;
this._element.preview = this.preview;
const conditionMet = checkConditionsMet(
this._config!.conditions,
@ -142,7 +142,7 @@ export class HuiConditionalBase extends ReactiveElement {
if (!this._element || !this.hass) {
return;
}
const visible = this.editMode || conditionMet;
const visible = this.preview || conditionMet;
if (this.hidden !== !visible) {
this.toggleAttribute("hidden", !visible);
this.style.setProperty("display", visible ? "" : "none");

View File

@ -48,7 +48,7 @@ export class HuiDialogDeleteCard extends LitElement {
<hui-card
.hass=${this.hass}
.config=${this._cardConfig}
editMode
preview
></hui-card>
</div>
`

View File

@ -9,6 +9,7 @@ import {
nothing,
} from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
@ -29,6 +30,7 @@ import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
import "../../sections/hui-section";
import { addCard, replaceCard } from "../config-util";
import { getCardDocumentationURL } from "../get-card-documentation-url";
import type { ConfigChangedEvent } from "../hui-element-editor";
@ -245,12 +247,23 @@ export class HuiDialogEditCard
></hui-card-element-editor>
</div>
<div class="element-preview">
<hui-card
.hass=${this.hass}
.config=${this._cardConfig}
editMode
class=${this._error ? "blur" : ""}
></hui-card>
${this._isInSection
? html`
<hui-section
.hass=${this.hass}
.config=${this._cardConfigInSection(this._cardConfig)}
preview
class=${this._error ? "blur" : ""}
></hui-section>
`
: html`
<hui-card
.hass=${this.hass}
.config=${this._cardConfig}
preview
class=${this._error ? "blur" : ""}
></hui-card>
`}
${this._error
? html`
<ha-circular-progress
@ -335,6 +348,22 @@ export class HuiDialogEditCard
this._cardEditorEl?.focusYamlEditor();
}
private get _isInSection() {
return this._params!.path.length === 2;
}
private _cardConfigInSection = memoizeOne(
(cardConfig?: LovelaceCardConfig) => {
const { cards, title, ...containerConfig } = this
._containerConfig as LovelaceSectionConfig;
return {
...containerConfig,
cards: cardConfig ? [cardConfig] : [],
};
}
);
private get _canSave(): boolean {
if (this._saving) {
return false;
@ -454,9 +483,17 @@ export class HuiDialogEditCard
}
.content hui-card {
margin: 4px auto;
display: block;
padding: 4px;
margin: 0 auto;
max-width: 390px;
}
.content hui-section {
display: block;
padding: 4px;
margin: 0 auto;
max-width: var(--ha-view-sections-column-max-width, 500px);
}
.content .element-editor {
margin: 0 10px;
}
@ -476,6 +513,11 @@ export class HuiDialogEditCard
margin: auto 0px;
max-width: 500px;
}
.content hui-section {
padding: 8px 10px;
margin: auto 0px;
max-width: var(--ha-view-sections-column-max-width, 500px);
}
}
.hidden {
display: none;

View File

@ -75,6 +75,7 @@ export class HuiDialogSuggestCard extends LitElement {
<hui-section
.hass=${this.hass}
.config=${this._sectionConfig}
preview
></hui-section>
</div>
`;
@ -84,7 +85,11 @@ export class HuiDialogSuggestCard extends LitElement {
<div class="element-preview">
${this._cardConfig.map(
(cardConfig) => html`
<hui-card .hass=${this.hass} .config=${cardConfig}></hui-card>
<hui-card
.hass=${this.hass}
.config=${cardConfig}
preview
></hui-card>
`
)}
</div>

View File

@ -81,7 +81,7 @@ export type LovelaceRowConfig =
export interface LovelaceRow extends HTMLElement {
hass?: HomeAssistant;
editMode?: boolean;
preview?: boolean;
setConfig(config: LovelaceRowConfig);
}

View File

@ -41,6 +41,8 @@ export class HuiSection extends ReactiveElement {
@property({ attribute: false }) public lovelace?: Lovelace;
@property({ type: Boolean, reflect: true }) public preview = false;
@property({ type: Number }) public index!: number;
@property({ type: Number }) public viewIndex!: number;
@ -56,7 +58,7 @@ export class HuiSection extends ReactiveElement {
private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = document.createElement("hui-card");
element.hass = this.hass;
element.editMode = this.lovelace?.editMode || false;
element.preview = this.preview;
element.config = cardConfig;
element.addEventListener("card-updated", (ev: Event) => {
ev.stopPropagation();
@ -118,8 +120,10 @@ export class HuiSection extends ReactiveElement {
}
if (changedProperties.has("lovelace")) {
this._layoutElement.lovelace = this.lovelace;
}
if (changedProperties.has("preview")) {
this._cards.forEach((element) => {
element.editMode = this.lovelace?.editMode || false;
element.preview = this.preview;
});
}
if (changedProperties.has("_cards")) {
@ -205,7 +209,7 @@ export class HuiSection extends ReactiveElement {
}
const visible =
forceVisible ||
this.lovelace?.editMode ||
this.preview ||
!this.config.visibility ||
checkConditionsMet(this.config.visibility, this.hass);

View File

@ -48,7 +48,7 @@ export type LovelaceLayoutOptions = {
export interface LovelaceCard extends HTMLElement {
hass?: HomeAssistant;
isPanel?: boolean;
editMode?: boolean;
preview?: boolean;
getCardSize(): number | Promise<number>;
getLayoutOptions?(): LovelaceLayoutOptions;
setConfig(config: LovelaceCardConfig): void;

View File

@ -248,17 +248,17 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
});
}
private _addCardToColumn(columnEl, index, editMode) {
private _addCardToColumn(columnEl, index, preview) {
const card: HuiCard = this.cards[index];
if (!editMode || this.isStrategy) {
card.editMode = false;
if (!preview || this.isStrategy) {
card.preview = false;
columnEl.appendChild(card);
} else {
const wrapper = document.createElement("hui-card-options");
wrapper.hass = this.hass;
wrapper.lovelace = this.lovelace;
wrapper.path = [this.index!, index];
card.editMode = true;
card.preview = true;
wrapper.appendChild(card);
columnEl.appendChild(wrapper);
}

View File

@ -108,7 +108,7 @@ export class PanelView extends LitElement implements LovelaceViewElement {
card.isPanel = true;
if (this.isStrategy || !this.lovelace?.editMode) {
card.editMode = false;
card.preview = false;
this._card = card;
return;
}
@ -118,7 +118,7 @@ export class PanelView extends LitElement implements LovelaceViewElement {
wrapper.lovelace = this.lovelace;
wrapper.path = [this.index!, 0];
wrapper.hidePosition = true;
card.editMode = true;
card.preview = true;
wrapper.appendChild(card);
this._card = wrapper;
}

View File

@ -144,14 +144,14 @@ export class SideBarView extends LitElement implements LovelaceViewElement {
const cardConfig = this._config?.cards?.[idx];
let element: HuiCard | HuiCardOptions;
if (this.isStrategy || !this.lovelace?.editMode) {
card.editMode = false;
card.preview = false;
element = card;
} else {
element = document.createElement("hui-card-options");
element.hass = this.hass;
element.lovelace = this.lovelace;
element.path = [this.index!, idx];
card.editMode = true;
card.preview = true;
const movePositionButton = document.createElement("ha-icon-button");
movePositionButton.slot = "buttons";
const moveIcon = document.createElement("ha-svg-icon");

View File

@ -76,7 +76,7 @@ export class HUIView extends ReactiveElement {
private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = document.createElement("hui-card");
element.hass = this.hass;
element.editMode = this.lovelace.editMode;
element.preview = this.lovelace.editMode;
element.config = cardConfig;
element.addEventListener("card-updated", (ev: Event) => {
ev.stopPropagation();
@ -109,6 +109,7 @@ export class HUIView extends ReactiveElement {
element.lovelace = this.lovelace;
element.config = sectionConfig;
element.viewIndex = this.index;
element.preview = this.lovelace.editMode;
element.addEventListener(
"ll-rebuild",
(ev: Event) => {
@ -214,7 +215,7 @@ export class HUIView extends ReactiveElement {
}
});
this._cards.forEach((element) => {
element.editMode = this.lovelace.editMode;
element.preview = this.lovelace.editMode;
});
}
if (changedProperties.has("_cards")) {