Fix multiple sortable (#13522)

This commit is contained in:
Paul Bottein 2022-08-31 12:01:36 +02:00 committed by GitHub
parent 1e19799da9
commit c116ad67ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 98 additions and 81 deletions

View File

@ -49,6 +49,7 @@ import { subscribeRepairsIssueRegistry } from "../data/repairs";
import { updateCanInstall, UpdateEntity } from "../data/update"; import { updateCanInstall, UpdateEntity } from "../data/update";
import { SubscribeMixin } from "../mixins/subscribe-mixin"; import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive"; import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive";
import { loadSortable, SortableInstance } from "../resources/sortable.ondemand";
import { haStyleScrollbar } from "../resources/styles"; import { haStyleScrollbar } from "../resources/styles";
import type { HomeAssistant, PanelInfo, Route } from "../types"; import type { HomeAssistant, PanelInfo, Route } from "../types";
import "./ha-icon"; import "./ha-icon";
@ -177,8 +178,6 @@ const computePanels = memoizeOne(
} }
); );
let Sortable;
@customElement("ha-sidebar") @customElement("ha-sidebar")
class HaSidebar extends SubscribeMixin(LitElement) { class HaSidebar extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@ -205,6 +204,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
private _recentKeydownActiveUntil = 0; private _recentKeydownActiveUntil = 0;
private sortableStyleLoaded = false;
// @ts-ignore // @ts-ignore
@LocalStorage("sidebarPanelOrder", true, { @LocalStorage("sidebarPanelOrder", true, {
attribute: false, attribute: false,
@ -217,7 +218,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
}) })
private _hiddenPanels: string[] = []; private _hiddenPanels: string[] = [];
private _sortable?; private _sortable?: SortableInstance;
public hassSubscribe(): UnsubscribeFunc[] { public hassSubscribe(): UnsubscribeFunc[] {
return [ return [
@ -658,36 +659,36 @@ class HaSidebar extends SubscribeMixin(LitElement) {
} }
private async _activateEditMode() { private async _activateEditMode() {
if (!Sortable) { await Promise.all([this._loadSortableStyle(), this._createSortable()]);
const [sortableImport, sortStylesImport] = await Promise.all([ }
import("sortablejs/modular/sortable.core.esm"),
import("../resources/ha-sortable-style"), private async _loadSortableStyle() {
]); if (this.sortableStyleLoaded) return;
const sortStylesImport = await import("../resources/ha-sortable-style");
const style = document.createElement("style"); const style = document.createElement("style");
style.innerHTML = (sortStylesImport.sortableStyles as CSSResult).cssText; style.innerHTML = (sortStylesImport.sortableStyles as CSSResult).cssText;
this.shadowRoot!.appendChild(style); this.shadowRoot!.appendChild(style);
Sortable = sortableImport.Sortable; this.sortableStyleLoaded = true;
Sortable.mount(sortableImport.OnSpill);
Sortable.mount(sortableImport.AutoScroll());
}
await this.updateComplete; await this.updateComplete;
this._createSortable();
} }
private _createSortable() { private async _createSortable() {
this._sortable = new Sortable(this.shadowRoot!.getElementById("sortable"), { const Sortable = await loadSortable();
this._sortable = new Sortable(
this.shadowRoot!.getElementById("sortable")!,
{
animation: 150, animation: 150,
fallbackClass: "sortable-fallback", fallbackClass: "sortable-fallback",
dataIdAttr: "data-panel", dataIdAttr: "data-panel",
handle: "paper-icon-item", handle: "paper-icon-item",
onSort: async () => { onSort: async () => {
this._panelOrder = this._sortable.toArray(); this._panelOrder = this._sortable!.toArray();
}, },
}); }
);
} }
private _deactivateEditMode() { private _deactivateEditMode() {

View File

@ -41,7 +41,7 @@ export const addItem = (
export const reorderItems = ( export const reorderItems = (
hass: HomeAssistant, hass: HomeAssistant,
itemIds: [string] itemIds: string[]
): Promise<ShoppingListItem> => ): Promise<ShoppingListItem> =>
hass.callWS({ hass.callWS({
type: "shopping_list/items/reorder", type: "shopping_list/items/reorder",

View File

@ -30,8 +30,10 @@ import { HomeAssistant } from "../../../types";
import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LovelaceCard, LovelaceCardEditor } from "../types";
import { SensorCardConfig, ShoppingListCardConfig } from "./types"; import { SensorCardConfig, ShoppingListCardConfig } from "./types";
import type { HaTextField } from "../../../components/ha-textfield"; import type { HaTextField } from "../../../components/ha-textfield";
import {
let Sortable; loadSortable,
SortableInstance,
} from "../../../resources/sortable.ondemand";
@customElement("hui-shopping-list-card") @customElement("hui-shopping-list-card")
class HuiShoppingListCard class HuiShoppingListCard
@ -59,7 +61,7 @@ class HuiShoppingListCard
@state() private _renderEmptySortable = false; @state() private _renderEmptySortable = false;
private _sortable?; private _sortable?: SortableInstance;
@query("#sortable") private _sortableEl?: HTMLElement; @query("#sortable") private _sortableEl?: HTMLElement;
@ -299,12 +301,6 @@ class HuiShoppingListCard
} }
private async _toggleReorder() { private async _toggleReorder() {
if (!Sortable) {
const sortableImport = await import(
"sortablejs/modular/sortable.core.esm"
);
Sortable = sortableImport.Sortable;
}
this._reordering = !this._reordering; this._reordering = !this._reordering;
await this.updateComplete; await this.updateComplete;
if (this._reordering) { if (this._reordering) {
@ -315,18 +311,22 @@ class HuiShoppingListCard
} }
} }
private _createSortable() { private async _createSortable() {
const Sortable = await loadSortable();
const sortableEl = this._sortableEl; const sortableEl = this._sortableEl;
this._sortable = new Sortable(sortableEl, { this._sortable = new Sortable(sortableEl!, {
animation: 150, animation: 150,
fallbackClass: "sortable-fallback", fallbackClass: "sortable-fallback",
dataIdAttr: "item-id", dataIdAttr: "item-id",
handle: "ha-svg-icon", handle: "ha-svg-icon",
onEnd: async (evt) => { onEnd: async (evt) => {
if (evt.newIndex === undefined || evt.oldIndex === undefined) {
return;
}
// Since this is `onEnd` event, it's possible that // Since this is `onEnd` event, it's possible that
// an item wa dragged away and was put back to its original position. // an item wa dragged away and was put back to its original position.
if (evt.oldIndex !== evt.newIndex) { if (evt.oldIndex !== evt.newIndex) {
reorderItems(this.hass!, this._sortable.toArray()).catch(() => reorderItems(this.hass!, this._sortable!.toArray()).catch(() =>
this._fetchData() this._fetchData()
); );
// Move the shopping list item in memory. // Move the shopping list item in memory.

View File

@ -15,11 +15,13 @@ import "../../../components/entity/ha-entity-picker";
import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker"; import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker";
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
import { sortableStyles } from "../../../resources/ha-sortable-style"; import { sortableStyles } from "../../../resources/ha-sortable-style";
import {
loadSortable,
SortableInstance,
} from "../../../resources/sortable.ondemand";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { EntityConfig } from "../entity-rows/types"; import { EntityConfig } from "../entity-rows/types";
let Sortable;
@customElement("hui-entity-editor") @customElement("hui-entity-editor")
export class HuiEntityEditor extends LitElement { export class HuiEntityEditor extends LitElement {
@property({ attribute: false }) protected hass?: HomeAssistant; @property({ attribute: false }) protected hass?: HomeAssistant;
@ -32,7 +34,7 @@ export class HuiEntityEditor extends LitElement {
@state() private _renderEmptySortable = false; @state() private _renderEmptySortable = false;
private _sortable?; private _sortable?: SortableInstance;
public connectedCallback() { public connectedCallback() {
super.connectedCallback(); super.connectedCallback();
@ -123,23 +125,18 @@ export class HuiEntityEditor extends LitElement {
} }
private async _createSortable() { private async _createSortable() {
if (!Sortable) { const Sortable = await loadSortable();
const sortableImport = await import(
"sortablejs/modular/sortable.core.esm"
);
Sortable = sortableImport.Sortable; this._sortable = new Sortable(
Sortable.mount(sortableImport.OnSpill); this.shadowRoot!.querySelector(".entities")!,
Sortable.mount(sortableImport.AutoScroll()); {
}
this._sortable = new Sortable(this.shadowRoot!.querySelector(".entities"), {
animation: 150, animation: 150,
fallbackClass: "sortable-fallback", fallbackClass: "sortable-fallback",
handle: "ha-svg-icon", handle: "ha-svg-icon",
dataIdAttr: "data-entity-id", dataIdAttr: "data-entity-id",
onEnd: async (evt: SortableEvent) => this._entityMoved(evt), onEnd: async (evt: SortableEvent) => this._entityMoved(evt),
}); }
);
} }
private async _addEntity(ev: CustomEvent): Promise<void> { private async _addEntity(ev: CustomEvent): Promise<void> {

View File

@ -16,11 +16,13 @@ import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker
import "../../../components/ha-icon-button"; import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon"; import "../../../components/ha-svg-icon";
import { sortableStyles } from "../../../resources/ha-sortable-style"; import { sortableStyles } from "../../../resources/ha-sortable-style";
import {
loadSortable,
SortableInstance,
} from "../../../resources/sortable.ondemand";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types"; import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types";
let Sortable;
declare global { declare global {
interface HASSDomEvents { interface HASSDomEvents {
"entities-changed": { "entities-changed": {
@ -41,7 +43,7 @@ export class HuiEntitiesCardRowEditor extends LitElement {
@state() private _renderEmptySortable = false; @state() private _renderEmptySortable = false;
private _sortable?; private _sortable?: SortableInstance;
public connectedCallback() { public connectedCallback() {
super.connectedCallback(); super.connectedCallback();
@ -173,22 +175,17 @@ export class HuiEntitiesCardRowEditor extends LitElement {
} }
private async _createSortable() { private async _createSortable() {
if (!Sortable) { const Sortable = await loadSortable();
const sortableImport = await import(
"sortablejs/modular/sortable.core.esm"
);
Sortable = sortableImport.Sortable; this._sortable = new Sortable(
Sortable.mount(sortableImport.OnSpill); this.shadowRoot!.querySelector(".entities")!,
Sortable.mount(sortableImport.AutoScroll()); {
}
this._sortable = new Sortable(this.shadowRoot!.querySelector(".entities"), {
animation: 150, animation: 150,
fallbackClass: "sortable-fallback", fallbackClass: "sortable-fallback",
handle: ".handle", handle: ".handle",
onEnd: async (evt: SortableEvent) => this._rowMoved(evt), onEnd: async (evt: SortableEvent) => this._rowMoved(evt),
}); }
);
} }
private async _addEntity(ev: CustomEvent): Promise<void> { private async _addEntity(ev: CustomEvent): Promise<void> {

View File

@ -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;
};

11
src/resources/sortable.ts Normal file
View File

@ -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 };