From cbba1849e217541e4499f65e5a0a3f888dc50923 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 9 Dec 2019 10:59:52 +0100 Subject: [PATCH] Convert script and automation editor to lit (#4327) * Convert script and automation editor to lit * Update yarn.lock --- build-scripts/webpack.js | 2 +- package.json | 4 +- src/common/preact/event.ts | 28 -- src/common/preact/unmount.ts | 9 - src/data/script.ts | 15 + .../config/automation/ha-automation-editor.ts | 274 +++++++++----- src/panels/config/js/automation.tsx | 155 -------- src/panels/config/js/preact-types.ts | 35 -- src/panels/config/js/script.tsx | 80 ----- src/panels/config/script/ha-script-editor.js | 336 ------------------ src/panels/config/script/ha-script-editor.ts | 322 +++++++++++++++++ src/translations/en.json | 1 + tsconfig.json | 2 - yarn.lock | 2 +- 14 files changed, 521 insertions(+), 744 deletions(-) delete mode 100644 src/common/preact/event.ts delete mode 100644 src/common/preact/unmount.ts delete mode 100644 src/panels/config/js/automation.tsx delete mode 100644 src/panels/config/js/preact-types.ts delete mode 100644 src/panels/config/js/script.tsx delete mode 100644 src/panels/config/script/ha-script-editor.js create mode 100644 src/panels/config/script/ha-script-editor.ts diff --git a/build-scripts/webpack.js b/build-scripts/webpack.js index ac04587333..d3c57b3c2a 100644 --- a/build-scripts/webpack.js +++ b/build-scripts/webpack.js @@ -91,7 +91,7 @@ const createWebpackConfig = ({ ), ].filter(Boolean), resolve: { - extensions: [".ts", ".js", ".json", ".tsx"], + extensions: [".ts", ".js", ".json"], alias: { react: "preact-compat", "react-dom": "preact-compat", diff --git a/package.json b/package.json index 9f111f9e4e..b7302ff40f 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "version": "1.0.0", "scripts": { "build": "script/build_frontend", - "lint": "eslint src hassio/src gallery/src && tslint 'src/**/*.ts' 'src/**/*.tsx' 'hassio/src/**/*.ts' 'gallery/src/**/*.ts' 'cast/src/**/*.ts' 'test-mocha/**/*.ts' && tsc", + "lint": "eslint src hassio/src gallery/src && tslint 'src/**/*.ts' 'hassio/src/**/*.ts' 'gallery/src/**/*.ts' 'cast/src/**/*.ts' 'test-mocha/**/*.ts' && tsc", "mocha": "node_modules/.bin/ts-mocha -p test-mocha/tsconfig.test.json --opts test-mocha/mocha.opts", "test": "npm run lint && npm run mocha", "docker_build": "sh ./script/docker_run.sh build $npm_package_version", @@ -76,6 +76,7 @@ "chart.js": "~2.8.0", "chartjs-chart-timeline": "^0.3.0", "codemirror": "^5.49.0", + "copy-to-clipboard": "^1.0.9", "cpx": "^1.5.0", "deep-clone-simple": "^1.1.1", "es6-object-assign": "^1.1.0", @@ -99,7 +100,6 @@ "regenerator-runtime": "^0.13.2", "roboto-fontface": "^0.10.0", "superstruct": "^0.6.1", - "copy-to-clipboard": "^1.0.9", "tslib": "^1.10.0", "unfetch": "^4.1.0", "web-animations-js": "^2.3.1", diff --git a/src/common/preact/event.ts b/src/common/preact/event.ts deleted file mode 100644 index a35d1969e4..0000000000 --- a/src/common/preact/event.ts +++ /dev/null @@ -1,28 +0,0 @@ -// interface OnChangeComponent { -// props: { -// index: number; -// onChange(index: number, data: object); -// }; -// } - -// export function onChangeEvent(this: OnChangeComponent, prop, ev) { -export function onChangeEvent(this: any, prop, ev) { - if (!this.initialized) { - return; - } - - const origData = this.props[prop]; - if (ev.target.value === origData[ev.target.name]) { - return; - } - - const data = { ...origData }; - - if (ev.target.value) { - data[ev.target.name] = ev.target.value; - } else { - delete data[ev.target.name]; - } - - this.props.onChange(this.props.index, data); -} diff --git a/src/common/preact/unmount.ts b/src/common/preact/unmount.ts deleted file mode 100644 index 8d7fa510ea..0000000000 --- a/src/common/preact/unmount.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { render } from "preact"; - -export default function unmount(mountEl) { - render( - // @ts-ignore - () => null, - mountEl - ); -} diff --git a/src/data/script.ts b/src/data/script.ts index a4e5055acc..781ed79af5 100644 --- a/src/data/script.ts +++ b/src/data/script.ts @@ -1,6 +1,21 @@ import { HomeAssistant } from "../types"; import { computeObjectId } from "../common/entity/compute_object_id"; import { Condition } from "./automation"; +import { + HassEntityBase, + HassEntityAttributeBase, +} from "home-assistant-js-websocket"; + +export interface ScriptEntity extends HassEntityBase { + attributes: HassEntityAttributeBase & { + last_triggered: string; + }; +} + +export interface ScriptConfig { + alias: string; + sequence: Action[]; +} export interface EventAction { event: string; diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index ba8985252b..51980e7c88 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -1,42 +1,37 @@ -import { - LitElement, - TemplateResult, - html, - CSSResult, - css, - PropertyValues, - property, -} from "lit-element"; import "@polymer/app-layout/app-header/app-header"; import "@polymer/app-layout/app-toolbar/app-toolbar"; import "@polymer/paper-icon-button/paper-icon-button"; -import { classMap } from "lit-html/directives/class-map"; - -import { h, render } from "preact"; - -import "../../../components/ha-fab"; -import "../../../components/ha-paper-icon-button-arrow-prev"; -import "../../../layouts/ha-app-layout"; - -import Automation from "../js/automation"; -import unmountPreact from "../../../common/preact/unmount"; -import { computeStateName } from "../../../common/entity/compute_state_name"; - -import { haStyle } from "../../../resources/styles"; -import { HomeAssistant } from "../../../types"; import { - AutomationEntity, - AutomationConfig, - deleteAutomation, - getAutomationEditorInitData, -} from "../../../data/automation"; + css, + CSSResult, + html, + LitElement, + property, + PropertyValues, + TemplateResult, +} from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; +import { computeStateName } from "../../../common/entity/compute_state_name"; import { navigate } from "../../../common/navigate"; import { computeRTL } from "../../../common/util/compute_rtl"; +import "../../../components/ha-fab"; +import "../../../components/ha-paper-icon-button-arrow-prev"; +import { + AutomationConfig, + AutomationEntity, + Condition, + deleteAutomation, + getAutomationEditorInitData, + Trigger, +} from "../../../data/automation"; +import { Action } from "../../../data/script"; import { showConfirmationDialog } from "../../../dialogs/confirmation/show-dialog-confirmation"; - -function AutomationEditor(mountEl, props, mergeEl) { - return render(h(Automation, props), mountEl, mergeEl); -} +import "../../../layouts/ha-app-layout"; +import { haStyle } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; +import "./action/ha-automation-action"; +import "./condition/ha-automation-condition"; +import "./trigger/ha-automation-trigger"; export class HaAutomationEditor extends LitElement { @property() public hass!: HomeAssistant; @@ -45,26 +40,9 @@ export class HaAutomationEditor extends LitElement { @property() public creatingNew?: boolean; @property() private _config?: AutomationConfig; @property() private _dirty?: boolean; - private _rendered?: unknown; @property() private _errors?: string; - constructor() { - super(); - this._configChanged = this._configChanged.bind(this); - } - - public disconnectedCallback(): void { - super.disconnectedCallback(); - if (this._rendered) { - unmountPreact(this._rendered); - this._rendered = undefined; - } - } - protected render(): TemplateResult | void { - if (!this.hass) { - return; - } return html` @@ -100,11 +78,131 @@ export class HaAutomationEditor extends LitElement { ` : ""}
+ > + ${this._config + ? html` + + ${this._config.alias} + + ${this.hass.localize( + "ui.panel.config.automation.editor.introduction" + )} + + +
+ + + +
+
+
+ + + + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.header" + )} + + +

+ ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.introduction" + )} +

+ + ${this.hass.localize( + "ui.panel.config.automation.editor.triggers.learn_more" + )} + +
+ +
+ + + + ${this.hass.localize( + "ui.panel.config.automation.editor.conditions.header" + )} + + +

+ ${this.hass.localize( + "ui.panel.config.automation.editor.conditions.introduction" + )} +

+ + ${this.hass.localize( + "ui.panel.config.automation.editor.conditions.learn_more" + )} + +
+ +
+ + + + ${this.hass.localize( + "ui.panel.config.automation.editor.actions.header" + )} + + +

+ ${this.hass.localize( + "ui.panel.config.automation.editor.actions.introduction" + )} +

+ + ${this.hass.localize( + "ui.panel.config.automation.editor.actions.learn_more" + )} + +
+ +
+ ` + : ""} + { - constructor() { - super(); - - this.onChange = this.onChange.bind(this); - this.triggerChanged = this.triggerChanged.bind(this); - this.conditionChanged = this.conditionChanged.bind(this); - this.actionChanged = this.actionChanged.bind(this); - } - - public onChange(ev) { - this.props.onChange({ - ...this.props.automation, - [ev.target.name]: ev.target.value, - }); - } - - public triggerChanged(ev: CustomEvent) { - this.props.onChange({ ...this.props.automation, trigger: ev.detail.value }); - } - - public conditionChanged(ev: CustomEvent) { - this.props.onChange({ - ...this.props.automation, - condition: ev.detail.value, - }); - } - - public actionChanged(ev: CustomEvent) { - this.props.onChange({ ...this.props.automation, action: ev.detail.value }); - } - - public render({ automation, isWide, hass, localize }) { - const { alias, description, trigger, condition, action } = automation; - - return ( -
- - {alias} - - {localize("ui.panel.config.automation.editor.introduction")} - - -
- - -
-
-
- - - - {localize("ui.panel.config.automation.editor.triggers.header")} - - -

- {localize( - "ui.panel.config.automation.editor.triggers.introduction" - )} -

- - {localize( - "ui.panel.config.automation.editor.triggers.learn_more" - )} - -
- -
- - - - {localize("ui.panel.config.automation.editor.conditions.header")} - - -

- {localize( - "ui.panel.config.automation.editor.conditions.introduction" - )} -

- - {localize( - "ui.panel.config.automation.editor.conditions.learn_more" - )} - -
- -
- - - - {localize("ui.panel.config.automation.editor.actions.header")} - - -

- {localize( - "ui.panel.config.automation.editor.actions.introduction" - )} -

- - {localize("ui.panel.config.automation.editor.actions.learn_more")} - -
- -
-
- ); - } -} diff --git a/src/panels/config/js/preact-types.ts b/src/panels/config/js/preact-types.ts deleted file mode 100644 index bd1396513b..0000000000 --- a/src/panels/config/js/preact-types.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PaperInputElement } from "@polymer/paper-input/paper-input"; - -// Force file to be a module to augment global scope. -export {}; - -declare global { - namespace JSX { - interface IntrinsicElements { - "paper-input": Partial; - "ha-config-section": any; - "ha-card": any; - "paper-radio-button": any; - "paper-radio-group": any; - "ha-entity-picker": any; - "paper-listbox": any; - "paper-item": any; - "paper-menu-button": any; - "paper-dropdown-menu-light": any; - "paper-icon-button": any; - "ha-device-picker": any; - "ha-device-condition-picker": any; - "ha-textarea": any; - "ha-code-editor": any; - "ha-service-picker": any; - "mwc-button": any; - "ha-automation-trigger": any; - "ha-automation-condition": any; - "ha-automation-condition-editor": any; - "ha-automation-action": any; - "ha-device-trigger-picker": any; - "ha-device-action-picker": any; - "ha-form": any; - } - } -} diff --git a/src/panels/config/js/script.tsx b/src/panels/config/js/script.tsx deleted file mode 100644 index c6de67b420..0000000000 --- a/src/panels/config/js/script.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { h, Component } from "preact"; - -import "@polymer/paper-input/paper-input"; -import "../ha-config-section"; -import "../../../components/ha-card"; - -import "../automation/action/ha-automation-action"; - -export default class ScriptEditor extends Component<{ - onChange: (...args: any[]) => any; - script: any; - isWide: any; - hass: any; - localize: any; -}> { - constructor() { - super(); - - this.onChange = this.onChange.bind(this); - this.sequenceChanged = this.sequenceChanged.bind(this); - } - - public onChange(ev) { - this.props.onChange({ - ...this.props.script, - [ev.target.name]: ev.target.value, - }); - } - - public sequenceChanged(ev: CustomEvent) { - this.props.onChange({ ...this.props.script, sequence: ev.detail.value }); - } - - // @ts-ignore - public render({ script, isWide, hass, localize }) { - const { alias, sequence } = script; - - return ( -
- - {alias} - - {localize("ui.panel.config.script.editor.introduction")} - - -
- -
-
-
- - - - {localize("ui.panel.config.script.editor.sequence")} - - - {localize("ui.panel.config.script.editor.sequence_sentence")} -

- - {localize( - "ui.panel.config.script.editor.link_available_actions" - )} - -

-
- -
-
- ); - } -} diff --git a/src/panels/config/script/ha-script-editor.js b/src/panels/config/script/ha-script-editor.js deleted file mode 100644 index dd06a37aae..0000000000 --- a/src/panels/config/script/ha-script-editor.js +++ /dev/null @@ -1,336 +0,0 @@ -import "@polymer/app-layout/app-header/app-header"; -import "@polymer/app-layout/app-toolbar/app-toolbar"; -import "@polymer/paper-icon-button/paper-icon-button"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { h, render } from "preact"; - -import "../../../layouts/ha-app-layout"; -import "../../../components/ha-paper-icon-button-arrow-prev"; -import "../../../components/ha-fab"; - -import Script from "../js/script"; -import unmountPreact from "../../../common/preact/unmount"; - -import { computeObjectId } from "../../../common/entity/compute_object_id"; -import { computeStateName } from "../../../common/entity/compute_state_name"; -import NavigateMixin from "../../../mixins/navigate-mixin"; -import LocalizeMixin from "../../../mixins/localize-mixin"; - -import { computeRTL } from "../../../common/util/compute_rtl"; -import { deleteScript } from "../../../data/script"; - -function ScriptEditor(mountEl, props, mergeEl) { - return render(h(Script, props), mountEl, mergeEl); -} - -/* - * @appliesMixin LocalizeMixin - * @appliesMixin NavigateMixin - */ -class HaScriptEditor extends LocalizeMixin(NavigateMixin(PolymerElement)) { - static get template() { - return html` - - - - - -
[[computeHeader(script)]]
- -
-
-
- -
-
- -
- `; - } - - static get properties() { - return { - hass: { - type: Object, - }, - - errors: { - type: Object, - value: null, - }, - - dirty: { - type: Boolean, - value: false, - }, - - config: { - type: Object, - value: null, - }, - - script: { - type: Object, - observer: "scriptChanged", - }, - - creatingNew: { - type: Boolean, - observer: "creatingNewChanged", - }, - - isWide: { - type: Boolean, - observer: "_updateComponent", - }, - - _rendered: { - type: Object, - value: null, - }, - - _renderScheduled: { - type: Boolean, - value: false, - }, - - rtl: { - type: Boolean, - reflectToAttribute: true, - computed: "_computeRTL(hass)", - }, - }; - } - - ready() { - this.configChanged = this.configChanged.bind(this); - super.ready(); // This call will initialize preact. - } - - disconnectedCallback() { - super.disconnectedCallback(); - if (this._rendered) { - unmountPreact(this._rendered); - this._rendered = null; - } - } - - configChanged(config) { - // onChange gets called a lot during initial rendering causing recursing calls. - if (this._rendered === null) return; - this.config = config; - this.errors = null; - this.dirty = true; - this._updateComponent(); - } - - scriptChanged(newVal, oldVal) { - if (!newVal) return; - if (!this.hass) { - setTimeout(() => this.scriptChanged(newVal, oldVal), 0); - return; - } - if (oldVal && oldVal.entity_id === newVal.entity_id) { - return; - } - this.hass - .callApi( - "get", - "config/script/config/" + computeObjectId(newVal.entity_id) - ) - .then( - (config) => { - // Normalize data: ensure sequence is a list - // Happens when people copy paste their scripts into the config - var value = config.sequence; - if (value && !Array.isArray(value)) { - config.sequence = [value]; - } - - this.dirty = false; - this.config = config; - this._updateComponent(); - }, - () => { - alert( - this.hass.localize( - "ui.panel.config.script.editor.load_error_not_editable" - ) - ); - history.back(); - } - ); - } - - creatingNewChanged(newVal) { - if (!newVal) { - return; - } - this.dirty = false; - this.config = { - alias: this.hass.localize("ui.panel.config.script.editor.default_name"), - sequence: [{ service: "", data: {} }], - }; - this._updateComponent(); - } - - backTapped() { - if ( - this.dirty && - // eslint-disable-next-line - !confirm( - this.hass.localize("ui.panel.config.common.editor.confirm_unsaved") - ) - ) { - return; - } - history.back(); - } - - _updateComponent() { - if (this._renderScheduled || !this.hass || !this.config) return; - this._renderScheduled = true; - Promise.resolve().then(() => { - this._rendered = ScriptEditor( - this.$.root, - { - script: this.config, - onChange: this.configChanged, - isWide: this.isWide, - hass: this.hass, - localize: this.localize, - }, - this._rendered - ); - this._renderScheduled = false; - }); - } - - async _delete() { - if ( - !confirm( - this.hass.localize("ui.panel.config.script.editor.delete_confirm") - ) - ) { - return; - } - await deleteScript(this.hass, computeObjectId(this.script.entity_id)); - history.back(); - } - - saveScript() { - var id = this.creatingNew - ? "" + Date.now() - : computeObjectId(this.script.entity_id); - this.hass.callApi("post", "config/script/config/" + id, this.config).then( - () => { - this.dirty = false; - - if (this.creatingNew) { - this.navigate(`/config/script/edit/${id}`, true); - } - }, - (errors) => { - this.errors = errors.body.message; - throw errors; - } - ); - } - - computeHeader(script) { - return script - ? this.hass.localize( - "ui.panel.config.script.editor.header", - "name", - computeStateName(script) - ) - : this.hass.localize("ui.panel.config.script.editor.default_name"); - } - - _computeRTL(hass) { - return computeRTL(hass); - } -} - -customElements.define("ha-script-editor", HaScriptEditor); diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts new file mode 100644 index 0000000000..9787783706 --- /dev/null +++ b/src/panels/config/script/ha-script-editor.ts @@ -0,0 +1,322 @@ +import "@polymer/app-layout/app-header/app-header"; +import "@polymer/app-layout/app-toolbar/app-toolbar"; +import "@polymer/paper-icon-button/paper-icon-button"; +import { + css, + CSSResult, + html, + LitElement, + property, + PropertyValues, + TemplateResult, +} from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; +import { computeStateName } from "../../../common/entity/compute_state_name"; +import { navigate } from "../../../common/navigate"; +import { computeRTL } from "../../../common/util/compute_rtl"; +import "../../../components/ha-fab"; +import "../../../components/ha-paper-icon-button-arrow-prev"; +import { + Action, + ScriptEntity, + ScriptConfig, + deleteScript, +} from "../../../data/script"; +import { showConfirmationDialog } from "../../../dialogs/confirmation/show-dialog-confirmation"; +import "../../../layouts/ha-app-layout"; +import { haStyle } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; +import "../automation/action/ha-automation-action"; +import { computeObjectId } from "../../../common/entity/compute_object_id"; + +export class HaScriptEditor extends LitElement { + @property() public hass!: HomeAssistant; + @property() public script!: ScriptEntity; + @property() public isWide?: boolean; + @property() public creatingNew?: boolean; + @property() private _config?: ScriptConfig; + @property() private _dirty?: boolean; + @property() private _errors?: string; + + protected render(): TemplateResult | void { + return html` + + + + +
+ ${this.script + ? computeStateName(this.script) + : this.hass.localize( + "ui.panel.config.script.editor.default_name" + )} +
+ ${this.creatingNew + ? "" + : html` + + `} +
+
+ +
+ ${this._errors + ? html` +
${this._errors}
+ ` + : ""} +
+ ${this._config + ? html` + + ${this._config.alias} + + ${this.hass.localize( + "ui.panel.config.script.editor.introduction" + )} + + +
+ + +
+
+
+ + + + ${this.hass.localize( + "ui.panel.config.script.editor.sequence" + )} + + +

+ ${this.hass.localize( + "ui.panel.config.script.editor.sequence_sentence" + )} +

+ + ${this.hass.localize( + "ui.panel.config.script.editor.link_available_actions" + )} + +
+ +
+ ` + : ""} +
+
+ +
+ `; + } + + protected updated(changedProps: PropertyValues): void { + super.updated(changedProps); + + const oldScript = changedProps.get("script") as ScriptEntity; + if ( + changedProps.has("script") && + this.script && + this.hass && + // Only refresh config if we picked a new script. If same ID, don't fetch it. + (!oldScript || oldScript.entity_id !== this.script.entity_id) + ) { + this.hass + .callApi( + "GET", + `config/script/config/${computeObjectId(this.script.entity_id)}` + ) + .then( + (config) => { + // Normalize data: ensure sequence is a list + // Happens when people copy paste their scripts into the config + const value = config.sequence; + if (value && !Array.isArray(value)) { + config.sequence = [value]; + } + this._dirty = false; + this._config = config; + }, + (resp) => { + alert( + resp.status_code === 404 + ? this.hass.localize( + "ui.panel.config.script.editor.load_error_not_editable" + ) + : this.hass.localize( + "ui.panel.config.script.editor.load_error_unknown", + "err_no", + resp.status_code + ) + ); + history.back(); + } + ); + } + + if (changedProps.has("creatingNew") && this.creatingNew && this.hass) { + this._dirty = false; + this._config = { + alias: this.hass.localize("ui.panel.config.script.editor.default_name"), + sequence: [{ service: "" }], + }; + } + } + + private _valueChanged(ev: CustomEvent) { + ev.stopPropagation(); + const name = (ev.target as any)?.name; + if (!name) { + return; + } + const newVal = ev.detail.value; + + if ((this._config![name] || "") === newVal) { + return; + } + this._config = { ...this._config!, [name]: newVal }; + this._dirty = true; + } + + private _sequenceChanged(ev: CustomEvent): void { + this._config = { ...this._config!, sequence: ev.detail.value as Action[] }; + this._errors = undefined; + this._dirty = true; + } + + private _backTapped(): void { + if (this._dirty) { + showConfirmationDialog(this, { + text: this.hass!.localize( + "ui.panel.config.common.editor.confirm_unsaved" + ), + confirmBtnText: this.hass!.localize("ui.common.yes"), + cancelBtnText: this.hass!.localize("ui.common.no"), + confirm: () => history.back(), + }); + } else { + history.back(); + } + } + + private async _delete() { + if ( + !confirm( + this.hass.localize("ui.panel.config.script.editor.delete_confirm") + ) + ) { + return; + } + await deleteScript(this.hass, computeObjectId(this.script.entity_id)); + history.back(); + } + + private _saveScript(): void { + const id = this.creatingNew + ? "" + Date.now() + : computeObjectId(this.script.entity_id); + this.hass!.callApi("POST", "config/script/config/" + id, this._config).then( + () => { + this._dirty = false; + + if (this.creatingNew) { + navigate(this, `/config/script/edit/${id}`, true); + } + }, + (errors) => { + this._errors = errors.body.message; + throw errors; + } + ); + } + + static get styles(): CSSResult[] { + return [ + haStyle, + css` + ha-card { + overflow: hidden; + } + .errors { + padding: 20px; + font-weight: bold; + color: var(--google-red-500); + } + .content { + padding-bottom: 20px; + } + span[slot="introduction"] a { + color: var(--primary-color); + } + ha-fab { + position: fixed; + bottom: 16px; + right: 16px; + z-index: 1; + margin-bottom: -80px; + transition: margin-bottom 0.3s; + } + + ha-fab[is-wide] { + bottom: 24px; + right: 24px; + } + + ha-fab[dirty] { + margin-bottom: 0; + } + + ha-fab.rtl { + right: auto; + left: 16px; + } + + ha-fab[is-wide].rtl { + bottom: 24px; + right: auto; + left: 24px; + } + `, + ]; + } +} + +customElements.define("ha-script-editor", HaScriptEditor); diff --git a/src/translations/en.json b/src/translations/en.json index 823f16d563..04e15bda66 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -999,6 +999,7 @@ "edit_script": "Edit script" }, "editor": { + "alias": "Name", "introduction": "Use scripts to execute a sequence of actions.", "header": "Script: {name}", "default_name": "New Script", diff --git a/tsconfig.json b/tsconfig.json index 7ca6093963..da7442ff68 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,5 @@ { "compilerOptions": { - "jsx": "react", - "jsxFactory": "h", "target": "es2017", "module": "esnext", "moduleResolution": "node", diff --git a/yarn.lock b/yarn.lock index 44152d0c56..4d2a229d93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14457,4 +14457,4 @@ zip-stream@^1.2.0: archiver-utils "^1.3.0" compress-commons "^1.2.0" lodash "^4.8.0" - readable-stream "^2.0.0" + readable-stream "^2.0.0" \ No newline at end of file