From a0fc4aad7a68b12e098b7ea5cc06773e5f235fac Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Sun, 6 Sep 2020 19:44:06 -0500 Subject: [PATCH 1/9] Use Sortable to move entities in entities editor --- package.json | 1 + .../lovelace/components/hui-entity-editor.ts | 171 +++++++++++------- yarn.lock | 5 + 3 files changed, 115 insertions(+), 62 deletions(-) diff --git a/package.json b/package.json index 1410d6bf3b0c..1db467294c02 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "@polymer/polymer": "3.1.0", "@thomasloven/round-slider": "0.5.0", "@types/chromecast-caf-sender": "^1.0.3", + "@types/sortablejs": "^1.10.6", "@vaadin/vaadin-combo-box": "^5.0.10", "@vaadin/vaadin-date-picker": "^4.0.7", "@vue/web-component-wrapper": "^1.2.0", diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index 23ab6eb6f108..187a872c3c43 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -1,33 +1,60 @@ -import "../../../components/ha-icon-button"; +import { mdiDrag } from "@mdi/js"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, + PropertyValues, TemplateResult, } from "lit-element"; +import { guard } from "lit-html/directives/guard"; +import { SortableEvent } from "sortablejs"; +import Sortable, { + AutoScroll, + OnSpill, +} from "sortablejs/modular/sortable.core.esm"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/entity/ha-entity-picker"; +import "../../../components/ha-icon-button"; +import { sortStyles } from "../../../components/ha-sidebar-sort-styles"; import { HomeAssistant } from "../../../types"; import { EditorTarget } from "../editor/types"; import { EntityConfig } from "../entity-rows/types"; @customElement("hui-entity-editor") export class HuiEntityEditor extends LitElement { - @property() protected hass?: HomeAssistant; + @property({ attribute: false }) protected hass?: HomeAssistant; - @property() protected entities?: EntityConfig[]; + @property({ attribute: false }) protected entities?: EntityConfig[]; @property() protected label?: string; + @internalProperty() private _attached = false; + + @internalProperty() private _renderEmptySortable = false; + + private _sortable?; + + public connectedCallback() { + super.connectedCallback(); + this._attached = true; + } + + public disconnectedCallback() { + super.disconnectedCallback(); + this._attached = false; + } + protected render(): TemplateResult { if (!this.entities) { return html``; } return html` + ${sortStyles}

${this.label || this.hass!.localize("ui.panel.lovelace.editor.card.generic.entities") + @@ -36,42 +63,65 @@ export class HuiEntityEditor extends LitElement { ")"}

- ${this.entities.map((entityConf, index) => { - return html` -
- - - -
- `; - })} - + ${guard([this.entities, this._renderEmptySortable], () => + this.entities!.map((entityConf, index) => { + return html` +
+ + +
+ `; + }) + )}
+ `; } - private _addEntity(ev: Event): void { + protected updated(changedProps: PropertyValues): void { + super.updated(changedProps); + + const attachedChanged = changedProps.has("_attached"); + const entitiesChanged = changedProps.has("entities"); + + if (!entitiesChanged && !attachedChanged) { + return; + } + + if (attachedChanged && !this._attached) { + // Tear down existing polyfill, if available + this._sortable?.destroy(); + this._sortable = undefined; + return; + } + + if (!this._sortable && this.entities) { + Sortable.mount(OnSpill); + Sortable.mount(new AutoScroll()); + + this._createSortable(); + } + } + + private _createSortable() { + this._sortable = new Sortable(this.shadowRoot!.querySelector(".entities"), { + animation: 150, + fallbackClass: "sortable-fallback", + handle: "ha-svg-icon", + onEnd: async (evt: SortableEvent) => this._entityMoved(evt), + }); + } + + private async _addEntity(ev: Event): Promise { const target = ev.target! as EditorTarget; if (target.value === "") { return; @@ -81,28 +131,19 @@ export class HuiEntityEditor extends LitElement { }); target.value = ""; fireEvent(this, "entities-changed", { entities: newConfigEntities }); + this._renderEmptySortable = true; + await this.updateComplete; + this._renderEmptySortable = false; } - private _entityUp(ev: Event): void { - const target = ev.target! as EditorTarget; - const newEntities = this.entities!.concat(); - - [newEntities[target.index! - 1], newEntities[target.index!]] = [ - newEntities[target.index!], - newEntities[target.index! - 1], - ]; - - fireEvent(this, "entities-changed", { entities: newEntities }); - } + private _entityMoved(ev: SortableEvent): void { + if (ev.oldIndex === ev.newIndex) { + return; + } - private _entityDown(ev: Event): void { - const target = ev.target! as EditorTarget; const newEntities = this.entities!.concat(); - [newEntities[target.index! + 1], newEntities[target.index!]] = [ - newEntities[target.index!], - newEntities[target.index! + 1], - ]; + newEntities.splice(ev.newIndex!, 0, newEntities.splice(ev.oldIndex!, 1)[0]); fireEvent(this, "entities-changed", { entities: newEntities }); } @@ -123,16 +164,22 @@ export class HuiEntityEditor extends LitElement { fireEvent(this, "entities-changed", { entities: newConfigEntities }); } - static get styles(): CSSResult { - return css` - .entity { - display: flex; - align-items: flex-end; - } - .entity ha-entity-picker { - flex-grow: 1; - } - `; + static get styles(): CSSResult[] { + return [ + css` + .entity { + display: flex; + align-items: center; + } + .entity ha-svg-icon { + padding-right: 8px; + cursor: move; + } + .entity ha-entity-picker { + flex-grow: 1; + } + `, + ]; } } diff --git a/yarn.lock b/yarn.lock index 8b8451ef9c8f..4a0450044e52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2710,6 +2710,11 @@ dependencies: "@types/node" "*" +"@types/sortablejs@^1.10.6": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@types/sortablejs/-/sortablejs-1.10.6.tgz#98725ae08f1dfe28b8da0fdf302c417f5ff043c0" + integrity sha512-QRz8Z+uw2Y4Gwrtxw8hD782zzuxxugdcq8X/FkPsXUa1kfslhGzy13+4HugO9FXNo+jlWVcE6DYmmegniIQ30A== + "@types/tern@*": version "0.23.3" resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.3.tgz#4b54538f04a88c9ff79de1f6f94f575a7f339460" From 05edc8501d84468f2249101007a738385b515c8d Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Mon, 7 Sep 2020 01:55:36 -0500 Subject: [PATCH 2/9] Make it actually work --- .../lovelace/components/hui-entity-editor.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index 187a872c3c43..69aff2867b94 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -11,7 +11,7 @@ import { TemplateResult, } from "lit-element"; import { guard } from "lit-html/directives/guard"; -import { SortableEvent } from "sortablejs"; +import type { SortableEvent } from "sortablejs"; import Sortable, { AutoScroll, OnSpill, @@ -34,8 +34,6 @@ export class HuiEntityEditor extends LitElement { @internalProperty() private _attached = false; - @internalProperty() private _renderEmptySortable = false; - private _sortable?; public connectedCallback() { @@ -63,10 +61,10 @@ export class HuiEntityEditor extends LitElement { ")"}
- ${guard([this.entities, this._renderEmptySortable], () => + ${guard([this.entities], () => this.entities!.map((entityConf, index) => { return html` -
+
entity.entity)); } } @@ -117,6 +120,7 @@ export class HuiEntityEditor extends LitElement { animation: 150, fallbackClass: "sortable-fallback", handle: "ha-svg-icon", + dataIdAttr: "data-entity-id", onEnd: async (evt: SortableEvent) => this._entityMoved(evt), }); } @@ -131,9 +135,6 @@ export class HuiEntityEditor extends LitElement { }); target.value = ""; fireEvent(this, "entities-changed", { entities: newConfigEntities }); - this._renderEmptySortable = true; - await this.updateComplete; - this._renderEmptySortable = false; } private _entityMoved(ev: SortableEvent): void { From c25b398dea005f9159bf32fcfa46c74cd5d6942e Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Mon, 7 Sep 2020 02:01:22 -0500 Subject: [PATCH 3/9] comment --- src/panels/lovelace/components/hui-entity-editor.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index 69aff2867b94..8cdf9d91901b 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -85,6 +85,11 @@ export class HuiEntityEditor extends LitElement { `; } + protected firstUpdated(): void { + Sortable.mount(OnSpill); + Sortable.mount(new AutoScroll()); + } + protected updated(changedProps: PropertyValues): void { super.updated(changedProps); @@ -103,9 +108,6 @@ export class HuiEntityEditor extends LitElement { } if (!this._sortable && this.entities) { - Sortable.mount(OnSpill); - Sortable.mount(new AutoScroll()); - this._createSortable(); return; } From 6325183f69250a131db72338d4137772ccc621fa Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Mon, 7 Sep 2020 02:06:16 -0500 Subject: [PATCH 4/9] Remove array result --- .../lovelace/components/hui-entity-editor.ts | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index 8cdf9d91901b..5c1315c0ebd2 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -167,22 +167,20 @@ export class HuiEntityEditor extends LitElement { fireEvent(this, "entities-changed", { entities: newConfigEntities }); } - static get styles(): CSSResult[] { - return [ - css` - .entity { - display: flex; - align-items: center; - } - .entity ha-svg-icon { - padding-right: 8px; - cursor: move; - } - .entity ha-entity-picker { - flex-grow: 1; - } - `, - ]; + static get styles(): CSSResult { + return css` + .entity { + display: flex; + align-items: center; + } + .entity ha-svg-icon { + padding-right: 8px; + cursor: move; + } + .entity ha-entity-picker { + flex-grow: 1; + } + `; } } From acba1ee4d0f274bb83a8510e245c324398ae64bf Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Mon, 7 Sep 2020 02:06:55 -0500 Subject: [PATCH 5/9] Change comment from copy pasta --- src/panels/lovelace/components/hui-entity-editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index 5c1315c0ebd2..9e7ade90364a 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -101,7 +101,7 @@ export class HuiEntityEditor extends LitElement { } if (attachedChanged && !this._attached) { - // Tear down existing polyfill, if available + // Tear down sortable, if available this._sortable?.destroy(); this._sortable = undefined; return; From 5fc31266738d175ddd0b5a5fc06da2347f5dfd29 Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Mon, 7 Sep 2020 02:40:35 -0500 Subject: [PATCH 6/9] make styles a css result - Not working --- src/components/ha-sidebar-sort-styles.ts | 122 +++++++++--------- src/components/ha-sidebar.ts | 14 +- .../lovelace/components/hui-entity-editor.ts | 34 ++--- 3 files changed, 87 insertions(+), 83 deletions(-) diff --git a/src/components/ha-sidebar-sort-styles.ts b/src/components/ha-sidebar-sort-styles.ts index b87b3c92ba4f..aa5ffe778947 100644 --- a/src/components/ha-sidebar-sort-styles.ts +++ b/src/components/ha-sidebar-sort-styles.ts @@ -1,77 +1,75 @@ -import { html } from "lit-element"; +import { css } from "lit-element"; -export const sortStyles = html` - + .hide-panel { + display: none; + position: absolute; + right: 8px; + } + + :host([expanded]) .hide-panel { + display: inline-flex; + } + + paper-icon-item.hidden-panel, + paper-icon-item.hidden-panel span, + paper-icon-item.hidden-panel ha-icon[slot="item-icon"] { + color: var(--secondary-text-color); + cursor: pointer; + } `; diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 12b1c17d391f..33578b92f33d 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -23,7 +23,7 @@ import { LitElement, property, PropertyValues, - TemplateResult, + unsafeCSS, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; import { guard } from "lit-html/directives/guard"; @@ -160,7 +160,7 @@ const computePanels = memoizeOne( let Sortable; -let sortStyles: TemplateResult; +let sortStyles: CSSResult; @customElement("ha-sidebar") class HaSidebar extends LitElement { @@ -228,7 +228,9 @@ class HaSidebar extends LitElement { } return html` - ${this._editMode ? sortStyles : ""} +