mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 07:16:39 +00:00
Fix multiple sortable (#13522)
This commit is contained in:
parent
1e19799da9
commit
c116ad67ed
@ -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() {
|
||||||
|
@ -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",
|
||||||
|
@ -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.
|
||||||
|
@ -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> {
|
||||||
|
@ -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> {
|
||||||
|
11
src/resources/sortable.ondemand.ts
Normal file
11
src/resources/sortable.ondemand.ts
Normal 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
11
src/resources/sortable.ts
Normal 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 };
|
Loading…
x
Reference in New Issue
Block a user