384 lines
11 KiB
TypeScript
384 lines
11 KiB
TypeScript
import { preventDefault } from "@fullcalendar/core/internal";
|
|
import { ActionDetail } from "@material/mwc-list";
|
|
import { mdiCheck, mdiDelete, mdiDotsVertical, mdiFlask } from "@mdi/js";
|
|
import { LitElement, PropertyValues, css, html, nothing } from "lit";
|
|
import { customElement, property, state } from "lit/decorators";
|
|
import { classMap } from "lit/directives/class-map";
|
|
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
|
import { handleStructError } from "../../../../common/structs/handle-errors";
|
|
import "../../../../components/ha-alert";
|
|
import "../../../../components/ha-button-menu";
|
|
import "../../../../components/ha-card";
|
|
import "../../../../components/ha-expansion-panel";
|
|
import "../../../../components/ha-icon-button";
|
|
import "../../../../components/ha-list-item";
|
|
import "../../../../components/ha-svg-icon";
|
|
import "../../../../components/ha-yaml-editor";
|
|
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
|
|
import { haStyle } from "../../../../resources/styles";
|
|
import type { HomeAssistant } from "../../../../types";
|
|
import { LovelaceCondition } from "../../common/conditions/types";
|
|
import { ICON_CONDITION } from "../../common/icon-condition";
|
|
import {
|
|
LegacyCondition,
|
|
checkConditionsMet,
|
|
validateConditionalConfig,
|
|
} from "../../common/validate-condition";
|
|
import type { LovelaceConditionEditorConstructor } from "./types";
|
|
|
|
@customElement("ha-card-condition-editor")
|
|
export class HaCardConditionEditor extends LitElement {
|
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
|
|
@property({ attribute: false }) condition!:
|
|
| LovelaceCondition
|
|
| LegacyCondition;
|
|
|
|
@state() public _yamlMode = false;
|
|
|
|
@state() public _uiAvailable = false;
|
|
|
|
@state() public _uiWarnings: string[] = [];
|
|
|
|
@state() _condition?: LovelaceCondition;
|
|
|
|
@state() private _testingResult?: boolean;
|
|
|
|
private get _editor() {
|
|
if (!this._condition) return undefined;
|
|
return customElements.get(
|
|
`ha-card-condition-${this._condition.condition}`
|
|
) as LovelaceConditionEditorConstructor | undefined;
|
|
}
|
|
|
|
public expand() {
|
|
this.updateComplete.then(() => {
|
|
this.shadowRoot!.querySelector("ha-expansion-panel")!.expanded = true;
|
|
});
|
|
}
|
|
|
|
protected willUpdate(changedProperties: PropertyValues): void {
|
|
if (changedProperties.has("condition")) {
|
|
this._condition = {
|
|
condition: "state",
|
|
...this.condition,
|
|
};
|
|
const validator = this._editor?.validateUIConfig;
|
|
if (validator) {
|
|
try {
|
|
validator(this._condition, this.hass);
|
|
this._uiAvailable = true;
|
|
this._uiWarnings = [];
|
|
} catch (err) {
|
|
this._uiWarnings = handleStructError(
|
|
this.hass,
|
|
err as Error
|
|
).warnings;
|
|
this._uiAvailable = false;
|
|
}
|
|
} else {
|
|
this._uiAvailable = false;
|
|
this._uiWarnings = [];
|
|
}
|
|
|
|
if (!this._uiAvailable && !this._yamlMode) {
|
|
this._yamlMode = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected render() {
|
|
const condition = this._condition;
|
|
|
|
if (!condition) return nothing;
|
|
|
|
return html`
|
|
<ha-card outlined>
|
|
<ha-expansion-panel leftChevron>
|
|
<h3 slot="header">
|
|
<ha-svg-icon
|
|
class="condition-icon"
|
|
.path=${ICON_CONDITION[condition.condition]}
|
|
></ha-svg-icon>
|
|
${
|
|
this.hass.localize(
|
|
`ui.panel.lovelace.editor.condition-editor.condition.${condition.condition}.label`
|
|
) || condition.condition
|
|
}
|
|
</h3>
|
|
<ha-button-menu
|
|
slot="icons"
|
|
@action=${this._handleAction}
|
|
@click=${preventDefault}
|
|
@closed=${stopPropagation}
|
|
fixed
|
|
.corner=${"BOTTOM_END"}
|
|
.menuCorner=${"END"}
|
|
>
|
|
<ha-icon-button
|
|
slot="trigger"
|
|
.label=${this.hass.localize("ui.common.menu")}
|
|
.path=${mdiDotsVertical}
|
|
>
|
|
</ha-icon-button>
|
|
|
|
<ha-list-item graphic="icon">
|
|
${this.hass.localize(
|
|
"ui.panel.lovelace.editor.condition-editor.test"
|
|
)}
|
|
<ha-svg-icon slot="graphic" .path=${mdiFlask}></ha-svg-icon>
|
|
</ha-list-item>
|
|
|
|
<ha-list-item graphic="icon" .disabled=${!this._uiAvailable}>
|
|
${this.hass.localize(
|
|
"ui.panel.lovelace.editor.edit_card.edit_ui"
|
|
)}
|
|
${
|
|
!this._yamlMode
|
|
? html`
|
|
<ha-svg-icon
|
|
class="selected_menu_item"
|
|
slot="graphic"
|
|
.path=${mdiCheck}
|
|
></ha-svg-icon>
|
|
`
|
|
: ``
|
|
}
|
|
</ha-list-item>
|
|
|
|
<ha-list-item graphic="icon">
|
|
${this.hass.localize(
|
|
"ui.panel.lovelace.editor.edit_card.edit_yaml"
|
|
)}
|
|
${
|
|
this._yamlMode
|
|
? html`
|
|
<ha-svg-icon
|
|
class="selected_menu_item"
|
|
slot="graphic"
|
|
.path=${mdiCheck}
|
|
></ha-svg-icon>
|
|
`
|
|
: ``
|
|
}
|
|
</ha-list-item>
|
|
|
|
<li divider role="separator"></li>
|
|
|
|
<ha-list-item class="warning" graphic="icon">
|
|
${this.hass!.localize("ui.common.delete")}
|
|
<ha-svg-icon
|
|
class="warning"
|
|
slot="graphic"
|
|
.path=${mdiDelete}
|
|
></ha-svg-icon>
|
|
</ha-list-item>
|
|
</ha-button-menu>
|
|
</div>
|
|
${
|
|
!this._uiAvailable
|
|
? html`
|
|
<ha-alert
|
|
alert-type="warning"
|
|
.title=${this.hass.localize(
|
|
"ui.errors.config.editor_not_supported"
|
|
)}
|
|
>
|
|
${this._uiWarnings!.length > 0 &&
|
|
this._uiWarnings![0] !== undefined
|
|
? html`
|
|
<ul>
|
|
${this._uiWarnings!.map(
|
|
(warning) => html`<li>${warning}</li>`
|
|
)}
|
|
</ul>
|
|
`
|
|
: nothing}
|
|
${this.hass.localize(
|
|
"ui.errors.config.edit_in_yaml_supported"
|
|
)}
|
|
</ha-alert>
|
|
`
|
|
: nothing
|
|
}
|
|
<div class="content">
|
|
${
|
|
this._yamlMode
|
|
? html`
|
|
<ha-yaml-editor
|
|
.hass=${this.hass}
|
|
.defaultValue=${this.condition}
|
|
@value-changed=${this._onYamlChange}
|
|
></ha-yaml-editor>
|
|
`
|
|
: html`
|
|
${dynamicElement(
|
|
`ha-card-condition-${condition.condition}`,
|
|
{
|
|
hass: this.hass,
|
|
condition: condition,
|
|
}
|
|
)}
|
|
`
|
|
}
|
|
</div>
|
|
</ha-expansion-panel>
|
|
<div
|
|
class="testing ${classMap({
|
|
active: this._testingResult !== undefined,
|
|
pass: this._testingResult === true,
|
|
error: this._testingResult === false,
|
|
})}"
|
|
>
|
|
${
|
|
this._testingResult
|
|
? this.hass.localize(
|
|
"ui.panel.lovelace.editor.condition-editor.testing_pass"
|
|
)
|
|
: this.hass.localize(
|
|
"ui.panel.lovelace.editor.condition-editor.testing_error"
|
|
)
|
|
}
|
|
</div>
|
|
</ha-card>
|
|
`;
|
|
}
|
|
|
|
private async _handleAction(ev: CustomEvent<ActionDetail>) {
|
|
switch (ev.detail.index) {
|
|
case 0:
|
|
await this._testCondition();
|
|
break;
|
|
case 1:
|
|
this._yamlMode = false;
|
|
break;
|
|
case 2:
|
|
this._yamlMode = true;
|
|
break;
|
|
case 3:
|
|
this._delete();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private _timeout?: number;
|
|
|
|
private async _testCondition() {
|
|
if (this._timeout) {
|
|
window.clearTimeout(this._timeout);
|
|
this._timeout = undefined;
|
|
}
|
|
this._testingResult = undefined;
|
|
const condition = this.condition;
|
|
|
|
const validateResult = validateConditionalConfig([this.condition]);
|
|
|
|
if (!validateResult) {
|
|
showAlertDialog(this, {
|
|
title: this.hass.localize(
|
|
"ui.panel.lovelace.editor.condition-editor.invalid_config_title"
|
|
),
|
|
text: this.hass.localize(
|
|
"ui.panel.lovelace.editor.condition-editor.invalid_config_text"
|
|
),
|
|
});
|
|
return;
|
|
}
|
|
|
|
this._testingResult = checkConditionsMet([condition], this.hass);
|
|
|
|
this._timeout = window.setTimeout(() => {
|
|
this._testingResult = undefined;
|
|
}, 2500);
|
|
}
|
|
|
|
private _delete() {
|
|
fireEvent(this, "value-changed", { value: null });
|
|
}
|
|
|
|
private _onYamlChange(ev: CustomEvent) {
|
|
ev.stopPropagation();
|
|
if (!ev.detail.isValid) {
|
|
return;
|
|
}
|
|
// @ts-ignore
|
|
fireEvent(this, "value-changed", { value: ev.detail.value });
|
|
}
|
|
|
|
static styles = [
|
|
haStyle,
|
|
css`
|
|
ha-button-menu {
|
|
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
|
}
|
|
|
|
ha-expansion-panel {
|
|
--expansion-panel-summary-padding: 0 0 0 8px;
|
|
--expansion-panel-content-padding: 0;
|
|
}
|
|
.condition-icon {
|
|
display: none;
|
|
}
|
|
@media (min-width: 870px) {
|
|
.condition-icon {
|
|
display: inline-block;
|
|
color: var(--secondary-text-color);
|
|
opacity: 0.9;
|
|
margin-right: 8px;
|
|
margin-inline-end: 8px;
|
|
margin-inline-start: initial;
|
|
}
|
|
}
|
|
h3 {
|
|
margin: 0;
|
|
font-size: inherit;
|
|
font-weight: inherit;
|
|
}
|
|
.content {
|
|
padding: 12px;
|
|
}
|
|
.selected_menu_item {
|
|
color: var(--primary-color);
|
|
}
|
|
.disabled {
|
|
opacity: 0.5;
|
|
pointer-events: none;
|
|
}
|
|
.testing {
|
|
position: absolute;
|
|
top: 0px;
|
|
right: 0px;
|
|
left: 0px;
|
|
text-transform: uppercase;
|
|
font-weight: bold;
|
|
font-size: 14px;
|
|
background-color: var(--divider-color, #e0e0e0);
|
|
color: var(--text-primary-color);
|
|
max-height: 0px;
|
|
overflow: hidden;
|
|
transition: max-height 0.3s;
|
|
text-align: center;
|
|
border-top-right-radius: var(--ha-card-border-radius, 12px);
|
|
border-top-left-radius: var(--ha-card-border-radius, 12px);
|
|
}
|
|
.testing.active {
|
|
max-height: 100px;
|
|
}
|
|
.testing.error {
|
|
background-color: var(--accent-color);
|
|
}
|
|
.testing.pass {
|
|
background-color: var(--success-color);
|
|
}
|
|
`,
|
|
];
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"ha-card-condition-editor": HaCardConditionEditor;
|
|
}
|
|
}
|