diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 8be862f4a3..3425cb35d4 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -49,6 +49,7 @@ import { subscribeRepairsIssueRegistry } from "../data/repairs"; import { updateCanInstall, UpdateEntity } from "../data/update"; import { SubscribeMixin } from "../mixins/subscribe-mixin"; import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive"; +import { loadSortable, SortableInstance } from "../resources/sortable.ondemand"; import { haStyleScrollbar } from "../resources/styles"; import type { HomeAssistant, PanelInfo, Route } from "../types"; import "./ha-icon"; @@ -177,8 +178,6 @@ const computePanels = memoizeOne( } ); -let Sortable; - @customElement("ha-sidebar") class HaSidebar extends SubscribeMixin(LitElement) { @property({ attribute: false }) public hass!: HomeAssistant; @@ -205,6 +204,8 @@ class HaSidebar extends SubscribeMixin(LitElement) { private _recentKeydownActiveUntil = 0; + private sortableStyleLoaded = false; + // @ts-ignore @LocalStorage("sidebarPanelOrder", true, { attribute: false, @@ -217,7 +218,7 @@ class HaSidebar extends SubscribeMixin(LitElement) { }) private _hiddenPanels: string[] = []; - private _sortable?; + private _sortable?: SortableInstance; public hassSubscribe(): UnsubscribeFunc[] { return [ @@ -658,36 +659,36 @@ class HaSidebar extends SubscribeMixin(LitElement) { } private async _activateEditMode() { - if (!Sortable) { - const [sortableImport, sortStylesImport] = await Promise.all([ - import("sortablejs/modular/sortable.core.esm"), - import("../resources/ha-sortable-style"), - ]); - - const style = document.createElement("style"); - style.innerHTML = (sortStylesImport.sortableStyles as CSSResult).cssText; - this.shadowRoot!.appendChild(style); - - Sortable = sortableImport.Sortable; - Sortable.mount(sortableImport.OnSpill); - Sortable.mount(sortableImport.AutoScroll()); - } - - await this.updateComplete; - - this._createSortable(); + await Promise.all([this._loadSortableStyle(), this._createSortable()]); } - private _createSortable() { - this._sortable = new Sortable(this.shadowRoot!.getElementById("sortable"), { - animation: 150, - fallbackClass: "sortable-fallback", - dataIdAttr: "data-panel", - handle: "paper-icon-item", - onSort: async () => { - this._panelOrder = this._sortable.toArray(); - }, - }); + private async _loadSortableStyle() { + if (this.sortableStyleLoaded) return; + + const sortStylesImport = await import("../resources/ha-sortable-style"); + + const style = document.createElement("style"); + style.innerHTML = (sortStylesImport.sortableStyles as CSSResult).cssText; + this.shadowRoot!.appendChild(style); + + this.sortableStyleLoaded = true; + await this.updateComplete; + } + + private async _createSortable() { + const Sortable = await loadSortable(); + this._sortable = new Sortable( + this.shadowRoot!.getElementById("sortable")!, + { + animation: 150, + fallbackClass: "sortable-fallback", + dataIdAttr: "data-panel", + handle: "paper-icon-item", + onSort: async () => { + this._panelOrder = this._sortable!.toArray(); + }, + } + ); } private _deactivateEditMode() { diff --git a/src/data/shopping-list.ts b/src/data/shopping-list.ts index 1036cde3f1..1d1d16b26e 100644 --- a/src/data/shopping-list.ts +++ b/src/data/shopping-list.ts @@ -41,7 +41,7 @@ export const addItem = ( export const reorderItems = ( hass: HomeAssistant, - itemIds: [string] + itemIds: string[] ): Promise => hass.callWS({ type: "shopping_list/items/reorder", diff --git a/src/panels/lovelace/cards/hui-shopping-list-card.ts b/src/panels/lovelace/cards/hui-shopping-list-card.ts index 9aadb1a0c9..8886fdddbd 100644 --- a/src/panels/lovelace/cards/hui-shopping-list-card.ts +++ b/src/panels/lovelace/cards/hui-shopping-list-card.ts @@ -30,8 +30,10 @@ import { HomeAssistant } from "../../../types"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { SensorCardConfig, ShoppingListCardConfig } from "./types"; import type { HaTextField } from "../../../components/ha-textfield"; - -let Sortable; +import { + loadSortable, + SortableInstance, +} from "../../../resources/sortable.ondemand"; @customElement("hui-shopping-list-card") class HuiShoppingListCard @@ -59,7 +61,7 @@ class HuiShoppingListCard @state() private _renderEmptySortable = false; - private _sortable?; + private _sortable?: SortableInstance; @query("#sortable") private _sortableEl?: HTMLElement; @@ -299,12 +301,6 @@ class HuiShoppingListCard } private async _toggleReorder() { - if (!Sortable) { - const sortableImport = await import( - "sortablejs/modular/sortable.core.esm" - ); - Sortable = sortableImport.Sortable; - } this._reordering = !this._reordering; await this.updateComplete; if (this._reordering) { @@ -315,18 +311,22 @@ class HuiShoppingListCard } } - private _createSortable() { + private async _createSortable() { + const Sortable = await loadSortable(); const sortableEl = this._sortableEl; - this._sortable = new Sortable(sortableEl, { + this._sortable = new Sortable(sortableEl!, { animation: 150, fallbackClass: "sortable-fallback", dataIdAttr: "item-id", handle: "ha-svg-icon", onEnd: async (evt) => { + if (evt.newIndex === undefined || evt.oldIndex === undefined) { + return; + } // Since this is `onEnd` event, it's possible that // an item wa dragged away and was put back to its original position. if (evt.oldIndex !== evt.newIndex) { - reorderItems(this.hass!, this._sortable.toArray()).catch(() => + reorderItems(this.hass!, this._sortable!.toArray()).catch(() => this._fetchData() ); // Move the shopping list item in memory. diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index 5aa461b9b8..e5219ca990 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -15,11 +15,13 @@ import "../../../components/entity/ha-entity-picker"; import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker"; import "../../../components/ha-icon-button"; import { sortableStyles } from "../../../resources/ha-sortable-style"; +import { + loadSortable, + SortableInstance, +} from "../../../resources/sortable.ondemand"; import { HomeAssistant } from "../../../types"; import { EntityConfig } from "../entity-rows/types"; -let Sortable; - @customElement("hui-entity-editor") export class HuiEntityEditor extends LitElement { @property({ attribute: false }) protected hass?: HomeAssistant; @@ -32,7 +34,7 @@ export class HuiEntityEditor extends LitElement { @state() private _renderEmptySortable = false; - private _sortable?; + private _sortable?: SortableInstance; public connectedCallback() { super.connectedCallback(); @@ -123,23 +125,18 @@ export class HuiEntityEditor extends LitElement { } private async _createSortable() { - if (!Sortable) { - const sortableImport = await import( - "sortablejs/modular/sortable.core.esm" - ); + const Sortable = await loadSortable(); - Sortable = sortableImport.Sortable; - Sortable.mount(sortableImport.OnSpill); - Sortable.mount(sortableImport.AutoScroll()); - } - - this._sortable = new Sortable(this.shadowRoot!.querySelector(".entities"), { - animation: 150, - fallbackClass: "sortable-fallback", - handle: "ha-svg-icon", - dataIdAttr: "data-entity-id", - onEnd: async (evt: SortableEvent) => this._entityMoved(evt), - }); + this._sortable = new Sortable( + this.shadowRoot!.querySelector(".entities")!, + { + animation: 150, + fallbackClass: "sortable-fallback", + handle: "ha-svg-icon", + dataIdAttr: "data-entity-id", + onEnd: async (evt: SortableEvent) => this._entityMoved(evt), + } + ); } private async _addEntity(ev: CustomEvent): Promise { diff --git a/src/panels/lovelace/editor/hui-entities-card-row-editor.ts b/src/panels/lovelace/editor/hui-entities-card-row-editor.ts index 6f492d23e4..3d3925054c 100644 --- a/src/panels/lovelace/editor/hui-entities-card-row-editor.ts +++ b/src/panels/lovelace/editor/hui-entities-card-row-editor.ts @@ -16,11 +16,13 @@ import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker import "../../../components/ha-icon-button"; import "../../../components/ha-svg-icon"; import { sortableStyles } from "../../../resources/ha-sortable-style"; +import { + loadSortable, + SortableInstance, +} from "../../../resources/sortable.ondemand"; import { HomeAssistant } from "../../../types"; import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types"; -let Sortable; - declare global { interface HASSDomEvents { "entities-changed": { @@ -41,7 +43,7 @@ export class HuiEntitiesCardRowEditor extends LitElement { @state() private _renderEmptySortable = false; - private _sortable?; + private _sortable?: SortableInstance; public connectedCallback() { super.connectedCallback(); @@ -173,22 +175,17 @@ export class HuiEntitiesCardRowEditor extends LitElement { } private async _createSortable() { - if (!Sortable) { - const sortableImport = await import( - "sortablejs/modular/sortable.core.esm" - ); + const Sortable = await loadSortable(); - Sortable = sortableImport.Sortable; - Sortable.mount(sortableImport.OnSpill); - Sortable.mount(sortableImport.AutoScroll()); - } - - this._sortable = new Sortable(this.shadowRoot!.querySelector(".entities"), { - animation: 150, - fallbackClass: "sortable-fallback", - handle: ".handle", - onEnd: async (evt: SortableEvent) => this._rowMoved(evt), - }); + this._sortable = new Sortable( + this.shadowRoot!.querySelector(".entities")!, + { + animation: 150, + fallbackClass: "sortable-fallback", + handle: ".handle", + onEnd: async (evt: SortableEvent) => this._rowMoved(evt), + } + ); } private async _addEntity(ev: CustomEvent): Promise { diff --git a/src/resources/sortable.ondemand.ts b/src/resources/sortable.ondemand.ts new file mode 100644 index 0000000000..a33f39d233 --- /dev/null +++ b/src/resources/sortable.ondemand.ts @@ -0,0 +1,11 @@ +let loaded: typeof import("./sortable").default; + +export type { SortableInstance } from "./sortable"; +export const loadSortable = async (): Promise< + typeof import("./sortable").default +> => { + if (!loaded) { + loaded = (await import("./sortable")).default; + } + return loaded; +}; diff --git a/src/resources/sortable.ts b/src/resources/sortable.ts new file mode 100644 index 0000000000..32514c4aa3 --- /dev/null +++ b/src/resources/sortable.ts @@ -0,0 +1,11 @@ +import Sortable from "sortablejs"; +import SortableCore, { + OnSpill, + AutoScroll, +} from "sortablejs/modular/sortable.core.esm"; + +SortableCore.mount(OnSpill, new AutoScroll()); + +export default SortableCore as typeof Sortable; + +export type { Sortable as SortableInstance };