mirror of
https://github.com/home-assistant/frontend.git
synced 2025-10-13 05:39:54 +00:00
Compare commits
1 Commits
button-hea
...
add-automa
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f831f876de |
@@ -220,7 +220,7 @@ export class HaResizableBottomSheet extends LitElement {
|
||||
min-height: var(--min-height, 30dvh);
|
||||
background-color: var(
|
||||
--ha-bottom-sheet-surface-background,
|
||||
var(--ha-color-surface-default)
|
||||
var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)),
|
||||
);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "@home-assistant/webawesome/dist/components/dialog/dialog";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import "./ha-dialog-header";
|
||||
import "./ha-icon-button";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { haStyleScrollbar } from "../resources/styles";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-dialog-header";
|
||||
import "./ha-icon-button";
|
||||
|
||||
export type DialogWidth = "small" | "medium" | "large" | "full";
|
||||
|
||||
@@ -90,6 +90,8 @@ export class HaWaDialog extends LitElement {
|
||||
@state()
|
||||
private _open = false;
|
||||
|
||||
@query(".body") public bodyContainer!: HTMLDivElement;
|
||||
|
||||
protected updated(
|
||||
changedProperties: Map<string | number | symbol, unknown>
|
||||
): void {
|
||||
@@ -107,6 +109,7 @@ export class HaWaDialog extends LitElement {
|
||||
.lightDismiss=${!this.preventScrimClose}
|
||||
without-header
|
||||
@wa-show=${this._handleShow}
|
||||
@wa-after-show=${this._handleAfterShow}
|
||||
@wa-after-hide=${this._handleAfterHide}
|
||||
>
|
||||
<slot name="header">
|
||||
@@ -146,6 +149,10 @@ export class HaWaDialog extends LitElement {
|
||||
(this.querySelector("[autofocus]") as HTMLElement | null)?.focus();
|
||||
};
|
||||
|
||||
private _handleAfterShow = () => {
|
||||
fireEvent(this, "after-show");
|
||||
};
|
||||
|
||||
private _handleAfterHide = () => {
|
||||
this._open = false;
|
||||
fireEvent(this, "closed");
|
||||
@@ -172,7 +179,7 @@ export class HaWaDialog extends LitElement {
|
||||
)
|
||||
)
|
||||
);
|
||||
--width: var(--ha-dialog-width-md, min(580px, var(--full-width)));
|
||||
--width: min(var(--ha-dialog-width-md, 580px), var(--full-width));
|
||||
--spacing: var(--dialog-content-padding, var(--ha-space-6));
|
||||
--show-duration: var(--ha-dialog-show-duration, 200ms);
|
||||
--hide-duration: var(--ha-dialog-hide-duration, 200ms);
|
||||
@@ -193,11 +200,11 @@ export class HaWaDialog extends LitElement {
|
||||
}
|
||||
|
||||
:host([width="small"]) wa-dialog {
|
||||
--width: var(--ha-dialog-width-sm, min(320px, var(--full-width)));
|
||||
--width: min(var(--ha-dialog-width-sm, 320px), var(--full-width));
|
||||
}
|
||||
|
||||
:host([width="large"]) wa-dialog {
|
||||
--width: var(--ha-dialog-width-lg, min(720px, var(--full-width)));
|
||||
--width: min(var(--ha-dialog-width-lg, 720px), var(--full-width));
|
||||
}
|
||||
|
||||
:host([width="full"]) wa-dialog {
|
||||
@@ -315,6 +322,7 @@ declare global {
|
||||
|
||||
interface HASSDomEvents {
|
||||
opened: undefined;
|
||||
"after-show": undefined;
|
||||
closed: undefined;
|
||||
}
|
||||
}
|
||||
|
@@ -17,8 +17,6 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stringCompare } from "../../../common/string/compare";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import { deepEqual } from "../../../common/util/deep-equal";
|
||||
import "../../../components/ha-dialog";
|
||||
import type { HaDialog } from "../../../components/ha-dialog";
|
||||
import "../../../components/ha-dialog-header";
|
||||
import "../../../components/ha-domain-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
@@ -26,8 +24,11 @@ import "../../../components/ha-icon-button-prev";
|
||||
import "../../../components/ha-icon-next";
|
||||
import "../../../components/ha-md-divider";
|
||||
import "../../../components/ha-md-list";
|
||||
import type { HaMdList } from "../../../components/ha-md-list";
|
||||
import "../../../components/ha-md-list-item";
|
||||
import "../../../components/ha-service-icon";
|
||||
import "../../../components/ha-wa-dialog";
|
||||
import type { HaWaDialog } from "../../../components/ha-wa-dialog";
|
||||
import "../../../components/search-input";
|
||||
import {
|
||||
ACTION_GROUPS,
|
||||
@@ -111,12 +112,12 @@ class DialogAddAutomationElement
|
||||
|
||||
@state() private _domains?: Set<string>;
|
||||
|
||||
@query("ha-dialog") private _dialog?: HaDialog;
|
||||
@query("#content") private _contentElement?: HaMdList;
|
||||
|
||||
@query("ha-wa-dialog") private _dialogElement?: HaWaDialog;
|
||||
|
||||
private _fullScreen = false;
|
||||
|
||||
@state() private _width?: number;
|
||||
|
||||
@state() private _height?: number;
|
||||
|
||||
@state() private _narrow = false;
|
||||
@@ -146,7 +147,6 @@ class DialogAddAutomationElement
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
this._height = undefined;
|
||||
this._width = undefined;
|
||||
this._params = undefined;
|
||||
this._group = undefined;
|
||||
this._prev = undefined;
|
||||
@@ -461,10 +461,8 @@ class DialogAddAutomationElement
|
||||
}
|
||||
|
||||
protected _opened(): void {
|
||||
// Store the width and height so that when we search, box doesn't jump
|
||||
const boundingRect =
|
||||
this.shadowRoot!.querySelector("ha-md-list")?.getBoundingClientRect();
|
||||
this._width = boundingRect?.width;
|
||||
// Store the height so that when we search, box doesn't jump
|
||||
const boundingRect = this._contentElement?.getBoundingClientRect();
|
||||
this._height = boundingRect?.height;
|
||||
}
|
||||
|
||||
@@ -502,6 +500,15 @@ class DialogAddAutomationElement
|
||||
this._manifests
|
||||
);
|
||||
|
||||
const groupItems = this._getGroupItems(
|
||||
this._params.type,
|
||||
undefined,
|
||||
undefined,
|
||||
this.hass.localize,
|
||||
this.hass.services,
|
||||
this._manifests
|
||||
);
|
||||
|
||||
const groupName = isService(this._group)
|
||||
? domainToName(
|
||||
this.hass.localize,
|
||||
@@ -514,14 +521,13 @@ class DialogAddAutomationElement
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
<ha-wa-dialog
|
||||
open
|
||||
hideActions
|
||||
@opened=${this._opened}
|
||||
persist-initial-height
|
||||
@after-show=${this._opened}
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${true}
|
||||
>
|
||||
<div slot="heading">
|
||||
<div slot="header">
|
||||
<ha-dialog-header>
|
||||
<span slot="title"
|
||||
>${this._group
|
||||
@@ -538,7 +544,7 @@ class DialogAddAutomationElement
|
||||
: html`<ha-icon-button
|
||||
.path=${mdiClose}
|
||||
slot="navigationIcon"
|
||||
dialogAction="cancel"
|
||||
data-dialog="close"
|
||||
></ha-icon-button>`}
|
||||
</ha-dialog-header>
|
||||
<search-input
|
||||
@@ -556,100 +562,206 @@ class DialogAddAutomationElement
|
||||
)}
|
||||
></search-input>
|
||||
</div>
|
||||
<ha-md-list
|
||||
dialogInitialFocus=${ifDefined(this._fullScreen ? "" : undefined)}
|
||||
<div
|
||||
id="content"
|
||||
style=${styleMap({
|
||||
width: this._width ? `${this._width}px` : "auto",
|
||||
height: this._height ? `${Math.min(468, this._height)}px` : "auto",
|
||||
height: this._height ? `${Math.min(468, this._height)}px` : "100vh",
|
||||
})}
|
||||
>
|
||||
${this._params.clipboardItem &&
|
||||
!this._filter &&
|
||||
(!this._group ||
|
||||
items.find((item) => item.key === this._params!.clipboardItem))
|
||||
? html`<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
class="paste"
|
||||
.value=${PASTE_VALUE}
|
||||
@click=${this._selected}
|
||||
>
|
||||
<div class="shortcut-label">
|
||||
<div class="label">
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.${this._params.type}s.paste`
|
||||
)}
|
||||
</div>
|
||||
<div class="supporting-text">
|
||||
${this.hass.localize(
|
||||
// @ts-ignore
|
||||
`ui.panel.config.automation.editor.${this._params.type}s.type.${this._params.clipboardItem}.label`
|
||||
)}
|
||||
<ha-md-list
|
||||
class="groups"
|
||||
dialogInitialFocus=${ifDefined(this._fullScreen ? "" : undefined)}
|
||||
>
|
||||
${this._params.clipboardItem &&
|
||||
!this._filter &&
|
||||
(!this._group ||
|
||||
items.find((item) => item.key === this._params!.clipboardItem))
|
||||
? html`<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
class="paste"
|
||||
.value=${PASTE_VALUE}
|
||||
@click=${this._selected}
|
||||
>
|
||||
<div class="shortcut-label">
|
||||
<div class="label">
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.${this._params.type}s.paste`
|
||||
)}
|
||||
</div>
|
||||
<div class="supporting-text">
|
||||
${this.hass.localize(
|
||||
// @ts-ignore
|
||||
`ui.panel.config.automation.editor.${this._params.type}s.type.${this._params.clipboardItem}.label`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
${!this._narrow
|
||||
? html`<span class="shortcut">
|
||||
<span
|
||||
>${isMac
|
||||
? html`<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiAppleKeyboardCommand}
|
||||
></ha-svg-icon>`
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.ctrl"
|
||||
)}</span
|
||||
>
|
||||
<span>+</span>
|
||||
<span>V</span>
|
||||
</span>`
|
||||
: nothing}
|
||||
</div>
|
||||
${!this._narrow
|
||||
? html`<span class="shortcut">
|
||||
<span
|
||||
>${isMac
|
||||
? html`<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiAppleKeyboardCommand}
|
||||
></ha-svg-icon>`
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.ctrl"
|
||||
)}</span
|
||||
>
|
||||
<span>+</span>
|
||||
<span>V</span>
|
||||
</span>`
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiContentPaste}
|
||||
></ha-svg-icon
|
||||
><ha-svg-icon slot="end" .path=${mdiPlus}></ha-svg-icon>
|
||||
</ha-md-list-item>
|
||||
<ha-md-divider role="separator" tabindex="-1"></ha-md-divider>`
|
||||
: nothing}
|
||||
${repeat(
|
||||
items,
|
||||
(item) => item.key,
|
||||
(item) => html`
|
||||
<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
.value=${item.key}
|
||||
.group=${item.group}
|
||||
@click=${this._selected}
|
||||
>
|
||||
<div slot="headline">${item.name}</div>
|
||||
<div slot="supporting-text">${item.description}</div>
|
||||
${item.icon
|
||||
? html`<span slot="start">${item.icon}</span>`
|
||||
: item.iconPath
|
||||
? html`<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${item.iconPath}
|
||||
></ha-svg-icon>`
|
||||
: nothing}
|
||||
${item.group
|
||||
? html`<ha-icon-next slot="end"></ha-icon-next>`
|
||||
: html`<ha-svg-icon
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiContentPaste}
|
||||
></ha-svg-icon
|
||||
><ha-svg-icon
|
||||
class="plus"
|
||||
slot="end"
|
||||
.path=${mdiPlus}
|
||||
></ha-svg-icon>`}
|
||||
</ha-md-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-md-list>
|
||||
</ha-dialog>
|
||||
></ha-svg-icon>
|
||||
</ha-md-list-item>
|
||||
<ha-md-divider
|
||||
role="separator"
|
||||
tabindex="-1"
|
||||
></ha-md-divider>`
|
||||
: nothing}
|
||||
${repeat(
|
||||
groupItems,
|
||||
(item) => item.key,
|
||||
(item) => html`
|
||||
<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
.value=${item.key}
|
||||
.group=${item.group}
|
||||
@click=${this._selected}
|
||||
class=${item.key === this._group ? "selected" : ""}
|
||||
>
|
||||
<div slot="headline">${item.name}</div>
|
||||
${item.icon
|
||||
? html`<span slot="start">${item.icon}</span>`
|
||||
: item.iconPath
|
||||
? html`<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${item.iconPath}
|
||||
></ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-md-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-md-list>
|
||||
<div class="items ${!this._group ? "blank" : ""}">
|
||||
${!this._group
|
||||
? "Select a group"
|
||||
: html`<h3>${this._params.type}</h3>
|
||||
<ha-md-list
|
||||
dialogInitialFocus=${ifDefined(
|
||||
this._fullScreen ? "" : undefined
|
||||
)}
|
||||
>
|
||||
${this._params.clipboardItem &&
|
||||
!this._filter &&
|
||||
(!this._group ||
|
||||
items.find(
|
||||
(item) => item.key === this._params!.clipboardItem
|
||||
))
|
||||
? html`<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
class="paste"
|
||||
.value=${PASTE_VALUE}
|
||||
@click=${this._selected}
|
||||
>
|
||||
<div class="shortcut-label">
|
||||
<div class="label">
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.automation.editor.${this._params.type}s.paste`
|
||||
)}
|
||||
</div>
|
||||
<div class="supporting-text">
|
||||
${this.hass.localize(
|
||||
// @ts-ignore
|
||||
`ui.panel.config.automation.editor.${this._params.type}s.type.${this._params.clipboardItem}.label`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
${!this._narrow
|
||||
? html`<span class="shortcut">
|
||||
<span
|
||||
>${isMac
|
||||
? html`<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiAppleKeyboardCommand}
|
||||
></ha-svg-icon>`
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.ctrl"
|
||||
)}</span
|
||||
>
|
||||
<span>+</span>
|
||||
<span>V</span>
|
||||
</span>`
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${mdiContentPaste}
|
||||
></ha-svg-icon
|
||||
><ha-svg-icon
|
||||
class="plus"
|
||||
slot="end"
|
||||
.path=${mdiPlus}
|
||||
></ha-svg-icon>
|
||||
</ha-md-list-item>
|
||||
<ha-md-divider
|
||||
role="separator"
|
||||
tabindex="-1"
|
||||
></ha-md-divider>`
|
||||
: nothing}
|
||||
${repeat(
|
||||
items,
|
||||
(item) => item.key,
|
||||
(item) => html`
|
||||
<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
.value=${item.key}
|
||||
.group=${item.group}
|
||||
@click=${this._selected}
|
||||
>
|
||||
<div slot="headline">${item.name}</div>
|
||||
<div slot="supporting-text">${item.description}</div>
|
||||
${item.icon
|
||||
? html`<span slot="start">${item.icon}</span>`
|
||||
: item.iconPath
|
||||
? html`<ha-svg-icon
|
||||
slot="start"
|
||||
.path=${item.iconPath}
|
||||
></ha-svg-icon>`
|
||||
: nothing}
|
||||
${item.group
|
||||
? html`<ha-icon-next slot="end"></ha-icon-next>`
|
||||
: html`<ha-svg-icon
|
||||
slot="end"
|
||||
class="plus"
|
||||
.path=${mdiPlus}
|
||||
></ha-svg-icon>`}
|
||||
</ha-md-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-md-list>`}
|
||||
</div>
|
||||
</div>
|
||||
</ha-wa-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _back() {
|
||||
this._dialog!.scrollToPos(0, 0);
|
||||
this._dialogElement?.bodyContainer.scrollTo(0, 0);
|
||||
if (this._filter) {
|
||||
this._filter = "";
|
||||
return;
|
||||
@@ -663,7 +775,7 @@ class DialogAddAutomationElement
|
||||
}
|
||||
|
||||
private _selected(ev) {
|
||||
this._dialog!.scrollToPos(0, 0);
|
||||
this._dialogElement?.bodyContainer.scrollTo(0, 0);
|
||||
const item = ev.currentTarget;
|
||||
if (item.group) {
|
||||
this._prev = this._group;
|
||||
@@ -707,29 +819,92 @@ class DialogAddAutomationElement
|
||||
haStyle,
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-dialog {
|
||||
ha-wa-dialog {
|
||||
--dialog-content-padding: 0;
|
||||
--ha-dialog-width-md: 888px;
|
||||
--mdc-dialog-max-height: 60vh;
|
||||
--mdc-dialog-max-height: 60dvh;
|
||||
}
|
||||
@media all and (min-width: 550px) {
|
||||
ha-dialog {
|
||||
ha-wa-dialog {
|
||||
--mdc-dialog-min-width: 500px;
|
||||
}
|
||||
}
|
||||
ha-icon-next {
|
||||
width: 24px;
|
||||
}
|
||||
ha-md-list {
|
||||
|
||||
#content {
|
||||
max-height: 468px;
|
||||
max-width: 100vw;
|
||||
--md-list-item-leading-space: 24px;
|
||||
--md-list-item-trailing-space: 24px;
|
||||
--md-list-item-supporting-text-font: var(--ha-font-size-s);
|
||||
display: flex;
|
||||
gap: var(--ha-space-3);
|
||||
padding: var(--ha-space-3) var(--ha-space-4);
|
||||
}
|
||||
|
||||
ha-md-list.groups {
|
||||
overflow: auto;
|
||||
flex: 3;
|
||||
border-radius: var(--ha-border-radius-xl);
|
||||
border: 1px solid var(--ha-color-border-neutral-quiet);
|
||||
--md-list-item-leading-space: var(--ha-space-3);
|
||||
--md-list-item-trailing-space: var(--md-list-item-leading-space);
|
||||
--md-list-item-bottom-space: var(--ha-space-1);
|
||||
--md-list-item-top-space: var(--md-list-item-bottom-space);
|
||||
--md-list-item-supporting-text-font: var(--ha-font-size-s);
|
||||
--md-list-item-one-line-container-height: var(--ha-space-8);
|
||||
}
|
||||
|
||||
ha-md-list.groups ha-md-list-item.selected {
|
||||
background-color: var(--ha-color-fill-primary-normal-active);
|
||||
--md-list-item-label-text-color: var(--primary-color);
|
||||
--icon-primary-color: var(--primary-color);
|
||||
}
|
||||
ha-md-list.groups ha-md-list-item.selected ha-svg-icon {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
#content .items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
flex: 7;
|
||||
}
|
||||
|
||||
#content .items.blank {
|
||||
border-radius: var(--ha-border-radius-xl);
|
||||
background-color: var(--ha-color-surface-default);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--ha-color-text-secondary);
|
||||
}
|
||||
|
||||
#content .items ha-md-list {
|
||||
--md-list-item-two-line-container-height: var(--ha-space-12);
|
||||
--md-list-item-leading-space: var(--ha-space-3);
|
||||
--md-list-item-trailing-space: var(--md-list-item-leading-space);
|
||||
--md-list-item-bottom-space: var(--ha-space-2);
|
||||
--md-list-item-top-space: var(--md-list-item-bottom-space);
|
||||
--md-list-item-supporting-text-font: var(--ha-font-size-s);
|
||||
gap: var(--ha-space-2);
|
||||
}
|
||||
|
||||
#content .items ha-md-list ha-md-list-item {
|
||||
border-radius: var(--ha-border-radius-lg);
|
||||
border: 1px solid var(--ha-color-border-neutral-quiet);
|
||||
}
|
||||
|
||||
ha-md-list-item img {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
ha-md-list-item.paste {
|
||||
border-bottom: 1px solid var(--ha-color-border-neutral-quiet);
|
||||
}
|
||||
|
||||
ha-svg-icon.plus {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
search-input {
|
||||
display: block;
|
||||
margin: 0 16px;
|
||||
|
@@ -302,8 +302,6 @@ export default class HaAutomationSidebar extends LitElement {
|
||||
--ha-bottom-sheet-border-style: solid;
|
||||
--ha-bottom-sheet-border-color: var(--primary-color);
|
||||
margin-top: var(--safe-area-inset-top);
|
||||
|
||||
--ha-bottom-sheet-surface-background: var(--card-background-color);
|
||||
}
|
||||
|
||||
@media all and (max-width: 870px) {
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import "../heading-badges/hui-entity-heading-badge";
|
||||
import "../heading-badges/hui-button-heading-badge";
|
||||
|
||||
import {
|
||||
createLovelaceElement,
|
||||
@@ -7,7 +6,7 @@ import {
|
||||
} from "./create-element-base";
|
||||
import type { LovelaceHeadingBadgeConfig } from "../heading-badges/types";
|
||||
|
||||
const ALWAYS_LOADED_TYPES = new Set(["error", "entity", "button"]);
|
||||
const ALWAYS_LOADED_TYPES = new Set(["error", "entity"]);
|
||||
|
||||
export const createHeadingBadgeElement = (config: LovelaceHeadingBadgeConfig) =>
|
||||
createLovelaceElement(
|
||||
|
@@ -1,238 +0,0 @@
|
||||
import { mdiEye, mdiGestureTap, mdiTextShort } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { any, array, assert, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type {
|
||||
HaFormSchema,
|
||||
SchemaUnion,
|
||||
} from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { Condition } from "../../common/validate-condition";
|
||||
import type { UiAction } from "../../components/hui-action-editor";
|
||||
import type { ButtonHeadingBadgeConfig } from "../../heading-badges/types";
|
||||
import type { LovelaceGenericElementEditor } from "../../types";
|
||||
import "../conditions/ha-card-conditions-editor";
|
||||
import { configElementStyle } from "../config-elements/config-elements-style";
|
||||
import { actionConfigStruct } from "../structs/action-struct";
|
||||
|
||||
export const DEFAULT_CONFIG: Partial<ButtonHeadingBadgeConfig> = {
|
||||
type: "button",
|
||||
};
|
||||
|
||||
const entityConfigStruct = object({
|
||||
type: optional(string()),
|
||||
text: optional(string()),
|
||||
icon: optional(string()),
|
||||
tap_action: optional(actionConfigStruct),
|
||||
hold_action: optional(actionConfigStruct),
|
||||
double_tap_action: optional(actionConfigStruct),
|
||||
visibility: optional(array(any())),
|
||||
});
|
||||
|
||||
const ALLOWED_ACTIONS: UiAction[] = [
|
||||
"navigate",
|
||||
"url",
|
||||
"assist",
|
||||
"call-service",
|
||||
"none",
|
||||
];
|
||||
|
||||
@customElement("hui-heading-button-editor")
|
||||
export class HuiHeadingButtonEditor
|
||||
extends LitElement
|
||||
implements LovelaceGenericElementEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public preview = false;
|
||||
|
||||
@state() private _config?: ButtonHeadingBadgeConfig;
|
||||
|
||||
public setConfig(config: ButtonHeadingBadgeConfig): void {
|
||||
assert(config, entityConfigStruct);
|
||||
this._config = {
|
||||
...DEFAULT_CONFIG,
|
||||
...config,
|
||||
};
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
() =>
|
||||
[
|
||||
{
|
||||
name: "content",
|
||||
type: "expandable",
|
||||
flatten: true,
|
||||
iconPath: mdiTextShort,
|
||||
schema: [
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "text",
|
||||
selector: {
|
||||
text: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "icon",
|
||||
selector: { icon: {} },
|
||||
context: { icon_entity: "entity" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "interactions",
|
||||
type: "expandable",
|
||||
flatten: true,
|
||||
iconPath: mdiGestureTap,
|
||||
schema: [
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "none",
|
||||
actions: ALLOWED_ACTIONS,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
type: "optional_actions",
|
||||
flatten: true,
|
||||
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||
(action) => ({
|
||||
name: action,
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "none" as const,
|
||||
actions: ALLOWED_ACTIONS,
|
||||
},
|
||||
},
|
||||
})
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[]
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const schema = this._schema();
|
||||
|
||||
const conditions = this._config.visibility ?? [];
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<ha-expansion-panel outlined>
|
||||
<ha-svg-icon slot="leading-icon" .path=${mdiEye}></ha-svg-icon>
|
||||
<h3 slot="header">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.heading.entity_config.visibility"
|
||||
)}
|
||||
</h3>
|
||||
<div class="content">
|
||||
<p class="intro">
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.heading.entity_config.visibility_explanation"
|
||||
)}
|
||||
</p>
|
||||
<ha-card-conditions-editor
|
||||
.hass=${this.hass}
|
||||
.conditions=${conditions}
|
||||
@value-changed=${this._conditionChanged}
|
||||
>
|
||||
</ha-card-conditions-editor>
|
||||
</div>
|
||||
</ha-expansion-panel>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const config = { ...ev.detail.value } as FormData;
|
||||
|
||||
fireEvent(this, "config-changed", { config });
|
||||
}
|
||||
|
||||
private _conditionChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const conditions = ev.detail.value as Condition[];
|
||||
|
||||
const newConfig: ButtonHeadingBadgeConfig = {
|
||||
...this._config,
|
||||
visibility: conditions,
|
||||
};
|
||||
if (newConfig.visibility?.length === 0) {
|
||||
delete newConfig.visibility;
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: newConfig });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
case "text":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.heading.button_config.${schema.name}`
|
||||
);
|
||||
default:
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
static get styles() {
|
||||
return [
|
||||
configElementStyle,
|
||||
css`
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
ha-form {
|
||||
display: block;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.intro {
|
||||
margin: 0;
|
||||
color: var(--secondary-text-color);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-heading-button-editor": HuiHeadingButtonEditor;
|
||||
}
|
||||
}
|
@@ -1,96 +0,0 @@
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../components/ha-state-icon";
|
||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||
import "../../../state-display/state-display";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
import { handleAction } from "../common/handle-action";
|
||||
import { hasAction } from "../common/has-action";
|
||||
import { DEFAULT_CONFIG } from "../editor/heading-badge-editor/hui-entity-heading-badge-editor";
|
||||
import type {
|
||||
LovelaceHeadingBadge,
|
||||
LovelaceHeadingBadgeEditor,
|
||||
} from "../types";
|
||||
import type { ButtonHeadingBadgeConfig } from "./types";
|
||||
|
||||
const DEFAULT_ACTIONS: Pick<
|
||||
ButtonHeadingBadgeConfig,
|
||||
"tap_action" | "hold_action" | "double_tap_action"
|
||||
> = {
|
||||
tap_action: { action: "none" },
|
||||
hold_action: { action: "none" },
|
||||
double_tap_action: { action: "none" },
|
||||
};
|
||||
|
||||
@customElement("hui-button-heading-badge")
|
||||
export class HuiButtonHeadingBadge
|
||||
extends LitElement
|
||||
implements LovelaceHeadingBadge
|
||||
{
|
||||
public static async getConfigElement(): Promise<LovelaceHeadingBadgeEditor> {
|
||||
await import(
|
||||
"../editor/heading-badge-editor/hui-button-heading-badge-editor"
|
||||
);
|
||||
return document.createElement("hui-heading-button-editor");
|
||||
}
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _config?: ButtonHeadingBadgeConfig;
|
||||
|
||||
@property({ type: Boolean }) public preview = false;
|
||||
|
||||
public setConfig(config): void {
|
||||
this._config = {
|
||||
...DEFAULT_CONFIG,
|
||||
...DEFAULT_ACTIONS,
|
||||
...config,
|
||||
};
|
||||
}
|
||||
|
||||
get hasAction() {
|
||||
return (
|
||||
hasAction(this._config?.tap_action) ||
|
||||
hasAction(this._config?.hold_action) ||
|
||||
hasAction(this._config?.double_tap_action)
|
||||
);
|
||||
}
|
||||
|
||||
private _handleAction(ev: ActionHandlerEvent) {
|
||||
handleAction(this, this.hass!, this._config!, ev.detail.action!);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const config = this._config;
|
||||
|
||||
return html`
|
||||
<ha-button
|
||||
@action=${this._handleAction}
|
||||
.actionHandler=${actionHandler({
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
>
|
||||
<ha-icon .icon=${config.icon}></ha-icon>
|
||||
${this._config.text}
|
||||
</ha-button>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-button-heading-badge": HuiButtonHeadingBadge;
|
||||
}
|
||||
}
|
@@ -26,12 +26,3 @@ export interface EntityHeadingBadgeConfig extends LovelaceHeadingBadgeConfig {
|
||||
hold_action?: ActionConfig;
|
||||
double_tap_action?: ActionConfig;
|
||||
}
|
||||
|
||||
export interface ButtonHeadingBadgeConfig extends LovelaceHeadingBadgeConfig {
|
||||
type?: "button";
|
||||
icon: string;
|
||||
text?: string;
|
||||
tap_action: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
double_tap_action?: ActionConfig;
|
||||
}
|
||||
|
@@ -7766,9 +7766,6 @@
|
||||
"state": "[%key:ui::panel::lovelace::editor::badge::entity::displayed_elements_options::state%]"
|
||||
}
|
||||
},
|
||||
"button_config": {
|
||||
"text": "Text"
|
||||
},
|
||||
"default_heading": "Kitchen"
|
||||
},
|
||||
"map": {
|
||||
|
Reference in New Issue
Block a user