mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 09:16:38 +00:00
GUI editors for stacks (#4999)
* GUI editors for stacks * fix type checking * lint * Address review comments * Cleanup. Removing inline functions, combining others * Give the class a more fitting name * Final tweak * Update stack cards Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
e2de660bec
commit
3cc7deda04
@ -1,4 +1,4 @@
|
|||||||
import { html, TemplateResult } from "lit-element";
|
import { CSSResult, css } from "lit-element";
|
||||||
|
|
||||||
import { computeCardSize } from "../common/compute-card-size";
|
import { computeCardSize } from "../common/compute-card-size";
|
||||||
import { HuiStackCard } from "./hui-stack-card";
|
import { HuiStackCard } from "./hui-stack-card";
|
||||||
@ -17,9 +17,10 @@ class HuiHorizontalStackCard extends HuiStackCard {
|
|||||||
return totalSize;
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderStyle(): TemplateResult {
|
static get styles(): CSSResult[] {
|
||||||
return html`
|
return [
|
||||||
<style>
|
super.sharedStyles,
|
||||||
|
css`
|
||||||
#root {
|
#root {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@ -34,8 +35,8 @@ class HuiHorizontalStackCard extends HuiStackCard {
|
|||||||
#root > *:last-child {
|
#root > *:last-child {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
</style>
|
`,
|
||||||
`;
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,34 @@
|
|||||||
import { html, LitElement, TemplateResult, CSSResult, css } from "lit-element";
|
import {
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
CSSResult,
|
||||||
|
css,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
|
||||||
import { createCardElement } from "../create-element/create-card-element";
|
import { createCardElement } from "../create-element/create-card-element";
|
||||||
import { LovelaceCard } from "../types";
|
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { StackCardConfig } from "./types";
|
import { StackCardConfig } from "./types";
|
||||||
|
|
||||||
export abstract class HuiStackCard extends LitElement implements LovelaceCard {
|
export abstract class HuiStackCard extends LitElement implements LovelaceCard {
|
||||||
static get properties() {
|
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||||
return {
|
await import(
|
||||||
_config: {},
|
/* webpackChunkName: "hui-stack-card-editor" */ "../editor/config-elements/hui-stack-card-editor"
|
||||||
};
|
);
|
||||||
|
return document.createElement("hui-stack-card-editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static getStubConfig(): object {
|
||||||
|
return { cards: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
@property() protected _cards?: LovelaceCard[];
|
||||||
|
@property() private _config?: StackCardConfig;
|
||||||
|
private _hass?: HomeAssistant;
|
||||||
|
|
||||||
set hass(hass: HomeAssistant) {
|
set hass(hass: HomeAssistant) {
|
||||||
this._hass = hass;
|
this._hass = hass;
|
||||||
|
|
||||||
@ -24,9 +40,6 @@ export abstract class HuiStackCard extends LitElement implements LovelaceCard {
|
|||||||
element.hass = this._hass;
|
element.hass = this._hass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protected _cards?: LovelaceCard[];
|
|
||||||
private _config?: StackCardConfig;
|
|
||||||
private _hass?: HomeAssistant;
|
|
||||||
|
|
||||||
public abstract getCardSize(): number;
|
public abstract getCardSize(): number;
|
||||||
|
|
||||||
@ -42,12 +55,11 @@ export abstract class HuiStackCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._config) {
|
if (!this._config || !this._cards) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
|
||||||
${this._config.title
|
${this._config.title
|
||||||
? html`
|
? html`
|
||||||
<div class="card-header">${this._config.title}</div>
|
<div class="card-header">${this._config.title}</div>
|
||||||
@ -57,9 +69,7 @@ export abstract class HuiStackCard extends LitElement implements LovelaceCard {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract renderStyle(): TemplateResult;
|
static get sharedStyles(): CSSResult {
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
|
||||||
return css`
|
return css`
|
||||||
.card-header {
|
.card-header {
|
||||||
color: var(--ha-card-header-color, --primary-text-color);
|
color: var(--ha-card-header-color, --primary-text-color);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { html, TemplateResult } from "lit-element";
|
import { CSSResult, css } from "lit-element";
|
||||||
|
|
||||||
import { computeCardSize } from "../common/compute-card-size";
|
import { computeCardSize } from "../common/compute-card-size";
|
||||||
import { HuiStackCard } from "./hui-stack-card";
|
import { HuiStackCard } from "./hui-stack-card";
|
||||||
@ -18,9 +18,10 @@ class HuiVerticalStackCard extends HuiStackCard {
|
|||||||
return totalSize;
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderStyle(): TemplateResult {
|
static get styles(): CSSResult[] {
|
||||||
return html`
|
return [
|
||||||
<style>
|
super.sharedStyles,
|
||||||
|
css`
|
||||||
#root {
|
#root {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -34,8 +35,8 @@ class HuiVerticalStackCard extends HuiStackCard {
|
|||||||
#root > *:last-child {
|
#root > *:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
</style>
|
`,
|
||||||
`;
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,6 +243,7 @@ export interface ShoppingListCardConfig extends LovelaceCardConfig {
|
|||||||
|
|
||||||
export interface StackCardConfig extends LovelaceCardConfig {
|
export interface StackCardConfig extends LovelaceCardConfig {
|
||||||
cards: LovelaceCardConfig[];
|
cards: LovelaceCardConfig[];
|
||||||
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ThermostatCardConfig extends LovelaceCardConfig {
|
export interface ThermostatCardConfig extends LovelaceCardConfig {
|
||||||
|
@ -93,6 +93,10 @@ export class HuiCardOptions extends LitElement {
|
|||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
|
:host(:hover) {
|
||||||
|
outline: 2px solid var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
ha-card {
|
ha-card {
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
|
@ -0,0 +1,201 @@
|
|||||||
|
import {
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
customElement,
|
||||||
|
property,
|
||||||
|
CSSResult,
|
||||||
|
css,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@polymer/paper-tabs";
|
||||||
|
|
||||||
|
import { struct } from "../../common/structs/struct";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import { LovelaceCardEditor } from "../../types";
|
||||||
|
import { StackCardConfig } from "../../cards/types";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
|
||||||
|
const cardConfigStruct = struct({
|
||||||
|
type: "string",
|
||||||
|
cards: ["any"],
|
||||||
|
title: "string?",
|
||||||
|
});
|
||||||
|
|
||||||
|
@customElement("hui-stack-card-editor")
|
||||||
|
export class HuiStackCardEditor extends LitElement
|
||||||
|
implements LovelaceCardEditor {
|
||||||
|
@property() public hass?: HomeAssistant;
|
||||||
|
@property() private _config?: StackCardConfig;
|
||||||
|
@property() private _selectedCard: number = 0;
|
||||||
|
|
||||||
|
public setConfig(config: StackCardConfig): void {
|
||||||
|
this._config = cardConfigStruct(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.hass || !this._config) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
const selected = this._selectedCard!;
|
||||||
|
const numcards = this._config.cards.length;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="card-config">
|
||||||
|
<div class="toolbar">
|
||||||
|
<paper-tabs
|
||||||
|
.selected=${selected}
|
||||||
|
scrollable
|
||||||
|
@iron-select=${this._handleSelectedCard}
|
||||||
|
>
|
||||||
|
${this._config.cards.map((_card, i) => {
|
||||||
|
return html`
|
||||||
|
<paper-tab>
|
||||||
|
${i + 1}
|
||||||
|
</paper-tab>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
</paper-tabs>
|
||||||
|
<paper-tabs
|
||||||
|
id="add-card"
|
||||||
|
.selected=${selected === numcards ? "0" : undefined}
|
||||||
|
@iron-select=${this._handleSelectedCard}
|
||||||
|
>
|
||||||
|
<paper-tab>
|
||||||
|
<ha-icon icon="hass:plus"></ha-icon>
|
||||||
|
</paper-tab>
|
||||||
|
<paper-tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="editor">
|
||||||
|
${
|
||||||
|
selected < numcards
|
||||||
|
? html`
|
||||||
|
<div id="card-options">
|
||||||
|
<paper-icon-button
|
||||||
|
id="move-before"
|
||||||
|
title="Move card before"
|
||||||
|
icon="hass:arrow-left"
|
||||||
|
?disabled=${selected === 0}
|
||||||
|
@click=${this._handleMove}
|
||||||
|
></paper-icon-button>
|
||||||
|
|
||||||
|
<paper-icon-button
|
||||||
|
id="move-after"
|
||||||
|
title="Move card after"
|
||||||
|
icon="hass:arrow-right"
|
||||||
|
?disabled=${selected === numcards - 1}
|
||||||
|
@click=${this._handleMove}
|
||||||
|
></paper-icon-button>
|
||||||
|
|
||||||
|
<paper-icon-button
|
||||||
|
icon="hass:delete"
|
||||||
|
@click=${this._handleDeleteCard}
|
||||||
|
></paper-icon-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hui-card-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value="${this._config.cards[selected]}"
|
||||||
|
@config-changed="${this._handleConfigChanged}"
|
||||||
|
></hui-card-editor>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<hui-card-picker
|
||||||
|
.hass="${this.hass}"
|
||||||
|
@config-changed="${this._handleCardPicked}"
|
||||||
|
></hui-card-picker>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleSelectedCard(ev) {
|
||||||
|
this._selectedCard =
|
||||||
|
ev.target.id === "add-card"
|
||||||
|
? this._config!.cards.length
|
||||||
|
: parseInt(ev.target.selected, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleConfigChanged(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!this._config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._config.cards[this._selectedCard] = ev.detail.config;
|
||||||
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleCardPicked(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!this._config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const config = ev.detail.config;
|
||||||
|
this._config.cards.push(config);
|
||||||
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleDeleteCard() {
|
||||||
|
if (!this._config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._config.cards.splice(this._selectedCard, 1);
|
||||||
|
this._selectedCard = Math.max(0, this._selectedCard - 1);
|
||||||
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleMove(ev) {
|
||||||
|
if (!this._config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const source = this._selectedCard;
|
||||||
|
const target = ev.target.id === "move-before" ? source - 1 : source + 1;
|
||||||
|
const card = this._config.cards.splice(this._selectedCard, 1)[0];
|
||||||
|
this._config.cards.splice(target, 0, card);
|
||||||
|
this._selectedCard = target;
|
||||||
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
--paper-tabs-selection-bar-color: var(--primary-color);
|
||||||
|
--paper-tab-ink: var(--primary-color);
|
||||||
|
}
|
||||||
|
paper-tabs {
|
||||||
|
display: flex;
|
||||||
|
font-size: 14px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
#add-card {
|
||||||
|
max-width: 32px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#card-options {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor {
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
@media (max-width: 450px) {
|
||||||
|
#editor {
|
||||||
|
margin: 0 -12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-stack-card-editor": HuiStackCardEditor;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user