diff --git a/src/components/ha-selector/ha-selector-select.ts b/src/components/ha-selector/ha-selector-select.ts index a20559f91b..0f1301d286 100644 --- a/src/components/ha-selector/ha-selector-select.ts +++ b/src/components/ha-selector/ha-selector-select.ts @@ -1,16 +1,13 @@ import "@material/mwc-list/mwc-list-item"; import { mdiDrag } from "@mdi/js"; -import { LitElement, PropertyValues, css, html, nothing } from "lit"; +import { LitElement, css, html, nothing } from "lit"; import { customElement, property, query } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; -import { SortableEvent } from "sortablejs"; import { ensureArray } from "../../common/array/ensure-array"; import { fireEvent } from "../../common/dom/fire_event"; import { stopPropagation } from "../../common/dom/stop_propagation"; import { caseInsensitiveStringCompare } from "../../common/string/compare"; import type { SelectOption, SelectSelector } from "../../data/selector"; -import { sortableStyles } from "../../resources/ha-sortable-style"; -import { SortableInstance } from "../../resources/sortable"; import type { HomeAssistant } from "../../types"; import "../chips/ha-chip-set"; import "../chips/ha-input-chip"; @@ -21,6 +18,7 @@ import "../ha-formfield"; import "../ha-input-helper-text"; import "../ha-radio"; import "../ha-select"; +import "../ha-sortable"; @customElement("ha-selector-select") export class HaSelectSelector extends LitElement { @@ -42,50 +40,10 @@ export class HaSelectSelector extends LitElement { @query("ha-combo-box", true) private comboBox!: HaComboBox; - private _sortable?: SortableInstance; - - protected updated(changedProps: PropertyValues): void { - if (changedProps.has("value") || changedProps.has("selector")) { - const sortableNeeded = - this.selector.select?.multiple && - this.selector.select.reorder && - this.value?.length; - if (!this._sortable && sortableNeeded) { - this._createSortable(); - } else if (this._sortable && !sortableNeeded) { - this._destroySortable(); - } - } - } - - private async _createSortable() { - const Sortable = (await import("../../resources/sortable")).default; - this._sortable = new Sortable( - this.shadowRoot!.querySelector("ha-chip-set")!, - { - animation: 150, - fallbackClass: "sortable-fallback", - draggable: "ha-input-chip", - onChoose: (evt: SortableEvent) => { - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - this._dragged(evt); - }, - } - ); - } - - private _dragged(ev: SortableEvent): void { - if (ev.oldIndex === ev.newIndex) return; - this._move(ev.oldIndex!, ev.newIndex!); + private _itemMoved(ev: CustomEvent): void { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; + this._move(oldIndex!, newIndex); } private _move(index: number, newIndex: number) { @@ -99,11 +57,6 @@ export class HaSelectSelector extends LitElement { }); } - private _destroySortable() { - this._sortable?.destroy(); - this._sortable = undefined; - } - private _filter = ""; protected render() { @@ -195,37 +148,43 @@ export class HaSelectSelector extends LitElement { return html` ${value?.length ? html` - - ${repeat( - value, - (item) => item, - (item, idx) => { - const label = - options.find((option) => option.value === item)?.label || - item; - return html` - - ${this.selector.select?.reorder - ? html` - - ` - : nothing} - ${options.find((option) => option.value === item) - ?.label || item} - - `; - } - )} - + + + ${repeat( + value, + (item) => item, + (item, idx) => { + const label = + options.find((option) => option.value === item) + ?.label || item; + return html` + + ${this.selector.select?.reorder + ? html` + + ` + : nothing} + ${options.find((option) => option.value === item) + ?.label || item} + + `; + } + )} + + ` : nothing} @@ -419,25 +378,35 @@ export class HaSelectSelector extends LitElement { this.comboBox.filteredItems = filteredItems; } - static styles = [ - sortableStyles, - css` - :host { - position: relative; - } - ha-select, - mwc-formfield, - ha-formfield { - display: block; - } - mwc-list-item[disabled] { - --mdc-theme-text-primary-on-background: var(--disabled-text-color); - } - ha-chip-set { - padding: 8px 0; - } - `, - ]; + static styles = css` + :host { + position: relative; + } + ha-select, + mwc-formfield, + ha-formfield { + display: block; + } + mwc-list-item[disabled] { + --mdc-theme-text-primary-on-background: var(--disabled-text-color); + } + ha-chip-set { + padding: 8px 0; + } + + .sortable-fallback { + display: none; + opacity: 0; + } + + .sortable-ghost { + opacity: 0.4; + } + + .sortable-drag { + cursor: grabbing; + } + `; } declare global { diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 50b5e1cba9..352f0130f9 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -33,7 +33,6 @@ import { } from "lit"; import { customElement, eventOptions, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import { guard } from "lit/directives/guard"; import memoizeOne from "memoize-one"; import { storage } from "../common/decorators/storage"; import { fireEvent } from "../common/dom/fire_event"; @@ -50,12 +49,12 @@ import { subscribeRepairsIssueRegistry } from "../data/repairs"; import { UpdateEntity, updateCanInstall } from "../data/update"; import { SubscribeMixin } from "../mixins/subscribe-mixin"; import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive"; -import type { SortableInstance } from "../resources/sortable"; import { haStyleScrollbar } from "../resources/styles"; import type { HomeAssistant, PanelInfo, Route } from "../types"; import "./ha-icon"; import "./ha-icon-button"; import "./ha-menu-button"; +import "./ha-sortable"; import "./ha-svg-icon"; import "./user/ha-user-badge"; @@ -204,15 +203,13 @@ class HaSidebar extends SubscribeMixin(LitElement) { @state() private _issuesCount = 0; - @state() private _renderEmptySortable = false; - private _mouseLeaveTimeout?: number; private _tooltipHideTimeout?: number; private _recentKeydownActiveUntil = 0; - private sortableStyleLoaded = false; + private _editStyleLoaded = false; @storage({ key: "sidebarPanelOrder", @@ -228,8 +225,6 @@ class HaSidebar extends SubscribeMixin(LitElement) { }) private _hiddenPanels: string[] = []; - private _sortable?: SortableInstance; - public hassSubscribe(): UnsubscribeFunc[] { return this.hass.user?.is_admin ? [ @@ -264,14 +259,13 @@ class HaSidebar extends SubscribeMixin(LitElement) { changedProps.has("expanded") || changedProps.has("narrow") || changedProps.has("alwaysExpand") || + changedProps.has("editMode") || changedProps.has("_externalConfig") || changedProps.has("_updatesCount") || changedProps.has("_issuesCount") || changedProps.has("_notifications") || - changedProps.has("editMode") || - changedProps.has("_renderEmptySortable") || changedProps.has("_hiddenPanels") || - (changedProps.has("_panelOrder") && !this.editMode) + changedProps.has("_panelOrder") ) { return true; } @@ -306,12 +300,8 @@ class HaSidebar extends SubscribeMixin(LitElement) { if (changedProps.has("alwaysExpand")) { toggleAttribute(this, "expanded", this.alwaysExpand); } - if (changedProps.has("editMode")) { - if (this.editMode) { - this._activateEditMode(); - } else { - this._deactivateEditMode(); - } + if (changedProps.has("editMode") && this.editMode) { + this._editModeActivated(); } if (!changedProps.has("hass")) { return; @@ -470,15 +460,36 @@ class HaSidebar extends SubscribeMixin(LitElement) { `; } + private _panelMoved(ev: CustomEvent) { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; + + const [beforeSpacer] = computePanels( + this.hass.panels, + this.hass.defaultPanel, + this._panelOrder, + this._hiddenPanels, + this.hass.locale + ); + + const panelOrder = beforeSpacer.map((panel) => panel.url_path); + const panel = panelOrder.splice(oldIndex, 1)[0]; + panelOrder.splice(newIndex, 0, panel); + + this._panelOrder = panelOrder; + } + private _renderPanelsEdit(beforeSpacer: PanelInfo[]) { - // prettier-ignore - return html`
- ${guard([this._hiddenPanels, this._renderEmptySortable], () => - this._renderEmptySortable ? "" : this._renderPanels(beforeSpacer) - )} -
- ${this._renderSpacer()} - ${this._renderHiddenPanels()} `; + return html` + +
${this._renderPanels(beforeSpacer)}
+
+ ${this._renderSpacer()}${this._renderHiddenPanels()} + `; } private _renderHiddenPanels() { @@ -674,44 +685,22 @@ class HaSidebar extends SubscribeMixin(LitElement) { fireEvent(this, "hass-edit-sidebar", { editMode: true }); } - private async _activateEditMode() { - await Promise.all([this._loadSortableStyle(), this._createSortable()]); + private async _editModeActivated() { + await this._loadEditStyle(); } - private async _loadSortableStyle() { - if (this.sortableStyleLoaded) return; + private async _loadEditStyle() { + if (this._editStyleLoaded) return; - const sortStylesImport = await import("../resources/ha-sortable-style"); + const editStylesImport = await import("../resources/ha-sidebar-edit-style"); const style = document.createElement("style"); - style.innerHTML = (sortStylesImport.sortableStyles as CSSResult).cssText; + style.innerHTML = (editStylesImport.sidebarEditStyle as CSSResult).cssText; this.shadowRoot!.appendChild(style); - this.sortableStyleLoaded = true; await this.updateComplete; } - private async _createSortable() { - const Sortable = (await import("../resources/sortable")).default; - 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() { - this._sortable?.destroy(); - this._sortable = undefined; - } - private _closeEditMode() { fireEvent(this, "hass-edit-sidebar", { editMode: false }); } @@ -724,13 +713,8 @@ class HaSidebar extends SubscribeMixin(LitElement) { } // Make a copy for Memoize this._hiddenPanels = [...this._hiddenPanels, panel]; - this._renderEmptySortable = true; - await this.updateComplete; - const container = this.shadowRoot!.getElementById("sortable")!; - while (container.lastElementChild) { - container.removeChild(container.lastElementChild); - } - this._renderEmptySortable = false; + // Remove it from the panel order + this._panelOrder = this._panelOrder.filter((order) => order !== panel); } private async _unhidePanel(ev: Event) { @@ -739,13 +723,6 @@ class HaSidebar extends SubscribeMixin(LitElement) { this._hiddenPanels = this._hiddenPanels.filter( (hidden) => hidden !== panel ); - this._renderEmptySortable = true; - await this.updateComplete; - const container = this.shadowRoot!.getElementById("sortable")!; - while (container.lastElementChild) { - container.removeChild(container.lastElementChild); - } - this._renderEmptySortable = false; } private _itemMouseEnter(ev: MouseEvent) { @@ -910,7 +887,7 @@ class HaSidebar extends SubscribeMixin(LitElement) { .menu mwc-button { width: 100%; } - #sortable, + .reorder-list, .hidden-panel { display: none; } diff --git a/src/components/ha-sortable.ts b/src/components/ha-sortable.ts new file mode 100644 index 0000000000..aed068a83a --- /dev/null +++ b/src/components/ha-sortable.ts @@ -0,0 +1,153 @@ +/* eslint-disable lit/prefer-static-styles */ +import { html, LitElement, nothing, PropertyValues } from "lit"; +import { customElement, property } from "lit/decorators"; +import type { SortableEvent } from "sortablejs"; +import { fireEvent } from "../common/dom/fire_event"; +import type { SortableInstance } from "../resources/sortable"; + +declare global { + interface HASSDomEvents { + "item-moved": { + oldIndex: number; + newIndex: number; + }; + } +} + +@customElement("ha-sortable") +export class HaSortable extends LitElement { + private _sortable?: SortableInstance; + + @property({ type: Boolean }) + public disabled = false; + + @property({ type: Boolean, attribute: "no-style" }) + public noStyle: boolean = false; + + @property({ type: String, attribute: "draggable-selector" }) + public draggableSelector?: string; + + @property({ type: String, attribute: "handle-selector" }) + public handleSelector?: string; + + protected updated(changedProperties: PropertyValues) { + if (changedProperties.has("disabled")) { + if (this.disabled) { + this._destroySortable(); + } else { + this._createSortable(); + } + } + } + + // Workaround for connectedCallback just after disconnectedCallback (when dragging sortable with sortable children) + private _shouldBeDestroy = false; + + public disconnectedCallback() { + super.disconnectedCallback(); + this._shouldBeDestroy = true; + setTimeout(() => { + if (this._shouldBeDestroy) { + this._destroySortable(); + this._shouldBeDestroy = false; + } + }, 1); + } + + public connectedCallback() { + super.connectedCallback(); + this._shouldBeDestroy = false; + } + + protected createRenderRoot() { + return this; + } + + protected render() { + if (this.noStyle) return nothing; + return html` + + `; + } + + private async _createSortable() { + if (this._sortable) return; + const container = this.children[0] as HTMLElement | undefined; + + if (!container) return; + + const Sortable = (await import("../resources/sortable")).default; + + const options: SortableInstance.Options = { + animation: 150, + onChoose: this._handleChoose, + onEnd: this._handleEnd, + }; + + if (this.draggableSelector) { + options.draggable = this.draggableSelector; + } + if (this.handleSelector) { + options.handle = this.handleSelector; + } + this._sortable = new Sortable(container, options); + } + + private _handleEnd = (evt: SortableEvent) => { + // put back in original location + if ((evt.item as any).placeholder) { + (evt.item as any).placeholder.replaceWith(evt.item); + delete (evt.item as any).placeholder; + } + // if item was not moved, ignore + if ( + evt.oldIndex === undefined || + evt.newIndex === undefined || + evt.oldIndex === evt.newIndex + ) { + return; + } + + fireEvent(this, "item-moved", { + oldIndex: evt.oldIndex!, + newIndex: evt.newIndex!, + }); + }; + + private _handleChoose = (evt: SortableEvent) => { + (evt.item as any).placeholder = document.createComment("sort-placeholder"); + evt.item.after((evt.item as any).placeholder); + }; + + private _destroySortable() { + if (!this._sortable) return; + this._sortable.destroy(); + this._sortable = undefined; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-sortable": HaSortable; + } +} diff --git a/src/dialogs/area-filter/area-filter-dialog.ts b/src/dialogs/area-filter/area-filter-dialog.ts index 2dc7cacadc..d82a515c09 100644 --- a/src/dialogs/area-filter/area-filter-dialog.ts +++ b/src/dialogs/area-filter/area-filter-dialog.ts @@ -4,15 +4,13 @@ import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { repeat } from "lit/directives/repeat"; -import type { SortableEvent } from "sortablejs"; import { fireEvent } from "../../common/dom/fire_event"; import type { AreaFilterValue } from "../../components/ha-area-filter"; import "../../components/ha-button"; import "../../components/ha-icon-button"; import "../../components/ha-list-item"; +import "../../components/ha-sortable"; import { areaCompare } from "../../data/area_registry"; -import { sortableStyles } from "../../resources/ha-sortable-style"; -import type { SortableInstance } from "../../resources/sortable"; import { haStyleDialog } from "../../resources/styles"; import { HomeAssistant } from "../../types"; import { HassDialog } from "../make-dialog-manager"; @@ -31,23 +29,18 @@ export class DialogAreaFilter @state() private _areas: string[] = []; - private _sortable?: SortableInstance; - - public async showDialog(dialogParams: AreaFilterDialogParams): Promise { + public showDialog(dialogParams: AreaFilterDialogParams): void { this._dialogParams = dialogParams; this._hidden = dialogParams.initialValue?.hidden ?? []; const order = dialogParams.initialValue?.order ?? []; const allAreas = Object.keys(this.hass!.areas); this._areas = allAreas.concat().sort(areaCompare(this.hass!.areas, order)); - await this.updateComplete; - this._createSortable(); } public closeDialog(): void { this._dialogParams = undefined; this._hidden = []; this._areas = []; - this._destroySortable(); fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -66,42 +59,14 @@ export class DialogAreaFilter this.closeDialog(); } - private async _createSortable() { - const Sortable = (await import("../../resources/sortable")).default; - if (this._sortable) return; - this._sortable = new Sortable(this.shadowRoot!.querySelector(".areas")!, { - animation: 150, - fallbackClass: "sortable-fallback", - handle: ".handle", - draggable: ".draggable", - onChoose: (evt: SortableEvent) => { - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - this._dragged(evt); - }, - }); - } - - private _destroySortable() { - this._sortable?.destroy(); - this._sortable = undefined; - } - - private _dragged(ev: SortableEvent): void { - if (ev.oldIndex === ev.newIndex) return; + private _areaMoved(ev: CustomEvent): void { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; const areas = this._areas.concat(); - const option = areas.splice(ev.oldIndex!, 1)[0]; - areas.splice(ev.newIndex!, 0, option); + const option = areas.splice(oldIndex, 1)[0]; + areas.splice(newIndex, 0, option); this._areas = areas; } @@ -120,50 +85,56 @@ export class DialogAreaFilter .heading=${this._dialogParams.title ?? this.hass.localize("ui.components.area-filter.title")} > - - ${repeat( - allAreas, - (area) => area, - (area, _idx) => { - const isVisible = !this._hidden.includes(area); - const name = this.hass!.areas[area]?.name || area; - return html` - - ${isVisible - ? html`` - : nothing} - ${name} - - - `; - } - )} - + + + ${repeat( + allAreas, + (area) => area, + (area, _idx) => { + const isVisible = !this._hidden.includes(area); + const name = this.hass!.areas[area]?.name || area; + return html` + + ${isVisible + ? html`` + : nothing} + ${name} + + + `; + } + )} + + ${this.hass.localize("ui.common.cancel")} @@ -192,7 +163,6 @@ export class DialogAreaFilter static get styles(): CSSResultGroup { return [ - sortableStyles, haStyleDialog, css` ha-dialog { diff --git a/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts b/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts index 9a2cbd6caf..93a6b1528f 100644 --- a/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts +++ b/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts @@ -10,9 +10,9 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import type { SortableEvent } from "sortablejs"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-control-slider"; +import "../../../../components/ha-sortable"; import { UNAVAILABLE } from "../../../../data/entity"; import { ExtEntityRegistryEntry, @@ -24,7 +24,6 @@ import { computeDefaultFavoriteColors, } from "../../../../data/light"; import { actionHandler } from "../../../../panels/lovelace/common/directives/action-handler-directive"; -import type { SortableInstance } from "../../../../resources/sortable"; import { HomeAssistant } from "../../../../types"; import { showConfirmationDialog } from "../../../generic/show-dialog-box"; import "./ha-favorite-color-button"; @@ -48,16 +47,7 @@ export class HaMoreInfoLightFavoriteColors extends LitElement { @state() private _favoriteColors: LightColor[] = []; - private _sortable?: SortableInstance; - protected updated(changedProps: PropertyValues): void { - if (changedProps.has("editMode")) { - if (this.editMode) { - this._createSortable(); - } else { - this._destroySortable(); - } - } if (changedProps.has("entry")) { if (this.entry) { if (this.entry.options?.light?.favorite_colors) { @@ -69,34 +59,10 @@ export class HaMoreInfoLightFavoriteColors extends LitElement { } } - private async _createSortable() { - const Sortable = (await import("../../../../resources/sortable")).default; - this._sortable = new Sortable( - this.shadowRoot!.querySelector(".container")!, - { - animation: 150, - fallbackClass: "sortable-fallback", - draggable: ".color", - onChoose: (evt: SortableEvent) => { - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - this._dragged(evt); - }, - } - ); - } - - private _dragged(ev: SortableEvent): void { - if (ev.oldIndex === ev.newIndex) return; - this._move(ev.oldIndex!, ev.newIndex!); + private _colorMoved(ev: CustomEvent): void { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; + this._move(oldIndex, newIndex); } private _move(index: number, newIndex: number) { @@ -107,11 +73,6 @@ export class HaMoreInfoLightFavoriteColors extends LitElement { this._save(favoriteColors); } - private _destroySortable() { - this._sortable?.destroy(); - this._sortable = undefined; - } - private _apply = (index: number) => { const favorite = this._favoriteColors[index]; this.hass.callService("light", "turn_on", { @@ -223,72 +184,79 @@ export class HaMoreInfoLightFavoriteColors extends LitElement { protected render(): TemplateResult { return html` -
- ${this._favoriteColors.map( - (color, index) => html` -
-
- +
+ ${this._favoriteColors.map( + (color, index) => html` +
+
- - ${this.editMode - ? html` - - ` - : nothing} + + + ${this.editMode + ? html` + + ` + : nothing} +
-
- ` - )} - ${this.editMode - ? html` - - - - - - ` - : nothing} -
+ )} + ${this.editMode + ? html` + + + + + + + ` + : nothing} +
+ `; } diff --git a/src/panels/config/automation/action/ha-automation-action.ts b/src/panels/config/automation/action/ha-automation-action.ts index ce312a7bd3..ba12089307 100644 --- a/src/panels/config/automation/action/ha-automation-action.ts +++ b/src/panels/config/automation/action/ha-automation-action.ts @@ -1,19 +1,16 @@ -import "@material/mwc-button"; import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js"; import deepClone from "deep-clone-simple"; import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit"; import { customElement, property } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; -import type { SortableEvent } from "sortablejs"; import { storage } from "../../../../common/decorators/storage"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-button"; import "../../../../components/ha-svg-icon"; +import "../../../../components/ha-sortable"; import { getService, isService } from "../../../../data/action"; import type { AutomationClipboard } from "../../../../data/automation"; import { Action } from "../../../../data/script"; -import { sortableStyles } from "../../../../resources/ha-sortable-style"; -import type { SortableInstance } from "../../../../resources/sortable"; import { HomeAssistant } from "../../../../types"; import { PASTE_VALUE, @@ -48,8 +45,6 @@ export default class HaAutomationAction extends LitElement { private _actionKeys = new WeakMap(); - private _sortable?: SortableInstance; - protected render() { return html` ${this.reOrderMode && !this.nested @@ -63,62 +58,68 @@ export default class HaAutomationAction extends LitElement { ${this.hass.localize( "ui.panel.config.automation.editor.re_order_mode.description_actions" )} - + ${this.hass.localize( "ui.panel.config.automation.editor.re_order_mode.exit" )} - + ` : null} -
- ${repeat( - this.actions, - (action) => this._getKey(action), - (action, idx) => html` - - ${this.reOrderMode - ? html` - - -
- -
- ` - : ""} -
- ` - )} -
+ +
+ ${repeat( + this.actions, + (action) => this._getKey(action), + (action, idx) => html` + + ${this.reOrderMode + ? html` + + +
+ +
+ ` + : ""} +
+ ` + )} +
+
{ - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - this._dragged(evt); - }, - }); - } - - private _destroySortable() { - this._sortable?.destroy(); - this._sortable = undefined; - } - private _getKey(action: Action) { if (!this._actionKeys.has(action)) { this._actionKeys.set(action, Math.random().toString()); @@ -262,11 +229,6 @@ export default class HaAutomationAction extends LitElement { this._move(index, newIndex); } - private _dragged(ev: SortableEvent): void { - if (ev.oldIndex === ev.newIndex) return; - this._move(ev.oldIndex!, ev.newIndex!); - } - private _move(index: number, newIndex: number) { const actions = this.actions.concat(); const action = actions.splice(index, 1)[0]; @@ -274,6 +236,12 @@ export default class HaAutomationAction extends LitElement { fireEvent(this, "value-changed", { value: actions }); } + private _actionMoved(ev: CustomEvent): void { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; + this._move(oldIndex, newIndex); + } + private _actionChanged(ev: CustomEvent) { ev.stopPropagation(); const actions = [...this.actions]; @@ -302,39 +270,36 @@ export default class HaAutomationAction extends LitElement { } static get styles(): CSSResultGroup { - return [ - sortableStyles, - css` - ha-automation-action-row { - display: block; - margin-bottom: 16px; - scroll-margin-top: 48px; - } - ha-svg-icon { - height: 20px; - } - ha-alert { - display: block; - margin-bottom: 16px; - border-radius: var(--ha-card-border-radius, 12px); - overflow: hidden; - } - .handle { - cursor: move; /* fallback if grab cursor is unsupported */ - cursor: grab; - padding: 12px; - } - .handle ha-svg-icon { - pointer-events: none; - height: 24px; - } - .buttons { - display: flex; - flex-wrap: wrap; - gap: 8px; - } - `, - ]; + return css` + ha-automation-action-row { + display: block; + margin-bottom: 16px; + scroll-margin-top: 48px; + } + ha-svg-icon { + height: 20px; + } + ha-alert { + display: block; + margin-bottom: 16px; + border-radius: var(--ha-card-border-radius, 12px); + overflow: hidden; + } + .handle { + padding: 12px; + cursor: move; /* fallback if grab cursor is unsupported */ + cursor: grab; + } + .handle ha-svg-icon { + pointer-events: none; + height: 24px; + } + .buttons { + display: flex; + flex-wrap: wrap; + gap: 8px; + } + `; } } diff --git a/src/panels/config/automation/action/types/ha-automation-action-choose.ts b/src/panels/config/automation/action/types/ha-automation-action-choose.ts index 5c0ee07637..1b4bebb273 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-choose.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-choose.ts @@ -1,29 +1,31 @@ import { consume } from "@lit-labs/context"; -import type { SortableEvent } from "sortablejs"; +import type { ActionDetail } from "@material/mwc-list"; import { - mdiDotsVertical, - mdiRenameBox, - mdiSort, + mdiArrowDown, + mdiArrowUp, mdiContentDuplicate, mdiDelete, - mdiPlus, - mdiArrowUp, - mdiArrowDown, + mdiDotsVertical, mdiDrag, + mdiPlus, + mdiRenameBox, + mdiSort, } from "@mdi/js"; import deepClone from "deep-clone-simple"; import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit"; import { customElement, property, state } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; -import type { ActionDetail } from "@material/mwc-list"; -import type { SortableInstance } from "../../../../../resources/sortable"; import { ensureArray } from "../../../../../common/array/ensure-array"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { capitalizeFirstLetter } from "../../../../../common/string/capitalize-first-letter"; import "../../../../../components/ha-button"; -import "../../../../../components/ha-icon-button"; import "../../../../../components/ha-button-menu"; +import "../../../../../components/ha-icon-button"; +import "../../../../../components/ha-sortable"; import { Condition } from "../../../../../data/automation"; +import { describeCondition } from "../../../../../data/automation_i18n"; +import { fullEntitiesContext } from "../../../../../data/context"; +import { EntityRegistryEntry } from "../../../../../data/entity_registry"; import { Action, ChooseAction, @@ -36,10 +38,6 @@ import { import { haStyle } from "../../../../../resources/styles"; import { HomeAssistant } from "../../../../../types"; import { ActionElement } from "../ha-automation-action-row"; -import { describeCondition } from "../../../../../data/automation_i18n"; -import { fullEntitiesContext } from "../../../../../data/context"; -import { EntityRegistryEntry } from "../../../../../data/entity_registry"; -import { sortableStyles } from "../../../../../resources/ha-sortable-style"; const preventDefault = (ev) => ev.preventDefault(); @@ -63,8 +61,6 @@ export class HaChooseAction extends LitElement implements ActionElement { private _expandLast = false; - private _sortable?: SortableInstance; - public static get defaultConfig() { return { choose: [{ conditions: [], sequence: [] }] }; } @@ -100,157 +96,166 @@ export class HaChooseAction extends LitElement implements ActionElement { const action = this.action; return html` -
- ${repeat( - action.choose ? ensureArray(action.choose) : [], - (option) => option, - (option, idx) => - html` - -

- ${this.hass.localize( - "ui.panel.config.automation.editor.actions.type.choose.option", - { number: idx + 1 } - )}: - ${option.alias || - (this._expandedStates[idx] - ? "" - : this._getDescription(option))} -

- ${this.reOrderMode - ? html` - +
+ ${repeat( + action.choose ? ensureArray(action.choose) : [], + (option) => option, + (option, idx) => html` +
+ + +

+ ${this.hass.localize( + "ui.panel.config.automation.editor.actions.type.choose.option", + { number: idx + 1 } + )}: + ${option.alias || + (this._expandedStates[idx] + ? "" + : this._getDescription(option))} +

+ ${this.reOrderMode + ? html` + + +
+ +
+ ` + : html` + + + + ${this.hass.localize( + "ui.panel.config.automation.editor.actions.rename" + )} + + + + ${this.hass.localize( + "ui.panel.config.automation.editor.actions.re_order" + )} + + + + + ${this.hass.localize( + "ui.panel.config.automation.editor.actions.duplicate" + )} + + + + + ${this.hass.localize( + "ui.panel.config.automation.editor.actions.type.choose.remove_option" + )} + + + + `} +
+

+ ${this.hass.localize( + "ui.panel.config.automation.editor.actions.type.choose.conditions" + )}: +

+ ( + option.conditions )} - .path=${mdiArrowUp} - @click=${this._moveUp} - .disabled=${idx === 0} - > - -
- -
- ` - : html` - - - - ${this.hass.localize( - "ui.panel.config.automation.editor.actions.rename" - )} - - - - ${this.hass.localize( - "ui.panel.config.automation.editor.actions.re_order" - )} - - - - - ${this.hass.localize( - "ui.panel.config.automation.editor.actions.duplicate" - )} - - - - - ${this.hass.localize( - "ui.panel.config.automation.editor.actions.type.choose.remove_option" - )} - - - - `} -
-

- ${this.hass.localize( - "ui.panel.config.automation.editor.actions.type.choose.conditions" - )}: -

- ( - option.conditions - )} - .reOrderMode=${this.reOrderMode} - .disabled=${this.disabled} - .hass=${this.hass} - .idx=${idx} - @value-changed=${this._conditionChanged} - > -

- ${this.hass.localize( - "ui.panel.config.automation.editor.actions.type.choose.sequence" - )}: -

- -
- - ` - )} -
+ @value-changed=${this._conditionChanged} + > +

+ ${this.hass.localize( + "ui.panel.config.automation.editor.actions.type.choose.sequence" + )}: +

+ +
+ + +
+ ` + )} +
+ { - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - this._dragged(evt); - }, - }); - } - - private _destroySortable() { - this._sortable?.destroy(); - this._sortable = undefined; - } - static get styles(): CSSResultGroup { return [ haStyle, - sortableStyles, css` - ha-card { + .option { margin: 0 0 16px 0; } .add-card mwc-button { @@ -543,9 +512,9 @@ export class HaChooseAction extends LitElement implements ActionElement { padding: 0 16px 16px 16px; } .handle { + padding: 12px; cursor: move; /* fallback if grab cursor is unsupported */ cursor: grab; - padding: 12px; } .handle ha-svg-icon { pointer-events: none; diff --git a/src/panels/config/automation/condition/ha-automation-condition.ts b/src/panels/config/automation/condition/ha-automation-condition.ts index 27364b92a3..c240c2ae6c 100644 --- a/src/panels/config/automation/condition/ha-automation-condition.ts +++ b/src/panels/config/automation/condition/ha-automation-condition.ts @@ -1,4 +1,3 @@ -import "@material/mwc-button"; import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js"; import deepClone from "deep-clone-simple"; import { @@ -11,18 +10,16 @@ import { } from "lit"; import { customElement, property } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; -import type { SortableEvent } from "sortablejs"; import { storage } from "../../../../common/decorators/storage"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-button"; import "../../../../components/ha-button-menu"; +import "../../../../components/ha-sortable"; import "../../../../components/ha-svg-icon"; import type { AutomationClipboard, Condition, } from "../../../../data/automation"; -import { sortableStyles } from "../../../../resources/ha-sortable-style"; -import type { SortableInstance } from "../../../../resources/sortable"; import type { HomeAssistant } from "../../../../types"; import { PASTE_VALUE, @@ -55,17 +52,7 @@ export default class HaAutomationCondition extends LitElement { private _conditionKeys = new WeakMap(); - private _sortable?: SortableInstance; - protected updated(changedProperties: PropertyValues) { - if (changedProperties.has("reOrderMode")) { - if (this.reOrderMode) { - this._createSortable(); - } else { - this._destroySortable(); - } - } - if (!changedProperties.has("conditions")) { return; } @@ -118,63 +105,70 @@ export default class HaAutomationCondition extends LitElement { ${this.hass.localize( "ui.panel.config.automation.editor.re_order_mode.description_conditions" )} - + ${this.hass.localize( "ui.panel.config.automation.editor.re_order_mode.exit" )} - + ` : null} -
- ${repeat( - this.conditions.filter((c) => typeof c === "object"), - (condition) => this._getKey(condition), - (cond, idx) => html` - - ${this.reOrderMode - ? html` - - -
- -
- ` - : ""} -
- ` - )} -
+ + +
+ ${repeat( + this.conditions.filter((c) => typeof c === "object"), + (condition) => this._getKey(condition), + (cond, idx) => html` + + ${this.reOrderMode + ? html` + + +
+ +
+ ` + : ""} +
+ ` + )} +
+
{ - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - this._dragged(evt); - }, - } - ); - } - - private _destroySortable() { - this._sortable?.destroy(); - this._sortable = undefined; - } - private _getKey(condition: Condition) { if (!this._conditionKeys.has(condition)) { this._conditionKeys.set(condition, Math.random().toString()); @@ -298,11 +262,6 @@ export default class HaAutomationCondition extends LitElement { this._move(index, newIndex); } - private _dragged(ev: SortableEvent): void { - if (ev.oldIndex === ev.newIndex) return; - this._move(ev.oldIndex!, ev.newIndex!); - } - private _move(index: number, newIndex: number) { const conditions = this.conditions.concat(); const condition = conditions.splice(index, 1)[0]; @@ -310,6 +269,12 @@ export default class HaAutomationCondition extends LitElement { fireEvent(this, "value-changed", { value: conditions }); } + private _conditionMoved(ev: CustomEvent): void { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; + this._move(oldIndex, newIndex); + } + private _conditionChanged(ev: CustomEvent) { ev.stopPropagation(); const conditions = [...this.conditions]; @@ -340,39 +305,36 @@ export default class HaAutomationCondition extends LitElement { } static get styles(): CSSResultGroup { - return [ - sortableStyles, - css` - ha-automation-condition-row { - display: block; - margin-bottom: 16px; - scroll-margin-top: 48px; - } - ha-svg-icon { - height: 20px; - } - ha-alert { - display: block; - margin-bottom: 16px; - border-radius: var(--ha-card-border-radius, 12px); - overflow: hidden; - } - .handle { - cursor: move; /* fallback if grab cursor is unsupported */ - cursor: grab; - padding: 12px; - } - .handle ha-svg-icon { - pointer-events: none; - height: 24px; - } - .buttons { - display: flex; - flex-wrap: wrap; - gap: 8px; - } - `, - ]; + return css` + ha-automation-condition-row { + display: block; + margin-bottom: 16px; + scroll-margin-top: 48px; + } + ha-svg-icon { + height: 20px; + } + ha-alert { + display: block; + margin-bottom: 16px; + border-radius: var(--ha-card-border-radius, 12px); + overflow: hidden; + } + .handle { + padding: 12px; + cursor: move; /* fallback if grab cursor is unsupported */ + cursor: grab; + } + .handle ha-svg-icon { + pointer-events: none; + height: 24px; + } + .buttons { + display: flex; + flex-wrap: wrap; + gap: 8px; + } + `; } } diff --git a/src/panels/config/automation/trigger/ha-automation-trigger.ts b/src/panels/config/automation/trigger/ha-automation-trigger.ts index 90467f61e0..167a9ab253 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger.ts @@ -1,25 +1,22 @@ -import "@material/mwc-button"; import { mdiArrowDown, mdiArrowUp, mdiDrag, mdiPlus } from "@mdi/js"; import deepClone from "deep-clone-simple"; import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit"; import { customElement, property } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; -import type { SortableEvent } from "sortablejs"; import { storage } from "../../../../common/decorators/storage"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-button"; import "../../../../components/ha-button-menu"; +import "../../../../components/ha-sortable"; import "../../../../components/ha-svg-icon"; import { AutomationClipboard, Trigger } from "../../../../data/automation"; -import { sortableStyles } from "../../../../resources/ha-sortable-style"; -import type { SortableInstance } from "../../../../resources/sortable"; import { HomeAssistant } from "../../../../types"; -import "./ha-automation-trigger-row"; -import type HaAutomationTriggerRow from "./ha-automation-trigger-row"; import { PASTE_VALUE, showAddAutomationElementDialog, } from "../show-add-automation-element-dialog"; +import "./ha-automation-trigger-row"; +import type HaAutomationTriggerRow from "./ha-automation-trigger-row"; @customElement("ha-automation-trigger") export default class HaAutomationTrigger extends LitElement { @@ -45,8 +42,6 @@ export default class HaAutomationTrigger extends LitElement { private _triggerKeys = new WeakMap(); - private _sortable?: SortableInstance; - protected render() { return html` ${this.reOrderMode && !this.nested @@ -60,70 +55,76 @@ export default class HaAutomationTrigger extends LitElement { ${this.hass.localize( "ui.panel.config.automation.editor.re_order_mode.description_triggers" )} - + ${this.hass.localize( "ui.panel.config.automation.editor.re_order_mode.exit" )} - + ` : null} -
- ${repeat( - this.triggers, - (trigger) => this._getKey(trigger), - (trg, idx) => html` - - ${this.reOrderMode - ? html` - - -
- -
- ` - : ""} -
- ` - )} - +
+ ${repeat( + this.triggers, + (trigger) => this._getKey(trigger), + (trg, idx) => html` + + ${this.reOrderMode + ? html` + + +
+ +
+ ` + : ""} +
+ ` )} - .disabled=${this.disabled} - @click=${this._addTriggerDialog} - > - - -
+
+ + + + `; } @@ -158,14 +159,6 @@ export default class HaAutomationTrigger extends LitElement { protected updated(changedProps: PropertyValues) { super.updated(changedProps); - if (changedProps.has("reOrderMode")) { - if (this.reOrderMode) { - this._createSortable(); - } else { - this._destroySortable(); - } - } - if (changedProps.has("triggers") && this._focusLastTriggerOnChange) { this._focusLastTriggerOnChange = false; @@ -190,36 +183,6 @@ export default class HaAutomationTrigger extends LitElement { this.reOrderMode = false; } - private async _createSortable() { - const Sortable = (await import("../../../../resources/sortable")).default; - this._sortable = new Sortable( - this.shadowRoot!.querySelector(".triggers")!, - { - animation: 150, - fallbackClass: "sortable-fallback", - handle: ".handle", - onChoose: (evt: SortableEvent) => { - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - this._dragged(evt); - }, - } - ); - } - - private _destroySortable() { - this._sortable?.destroy(); - this._sortable = undefined; - } - private _getKey(action: Trigger) { if (!this._triggerKeys.has(action)) { this._triggerKeys.set(action, Math.random().toString()); @@ -240,11 +203,6 @@ export default class HaAutomationTrigger extends LitElement { this._move(index, newIndex); } - private _dragged(ev: SortableEvent): void { - if (ev.oldIndex === ev.newIndex) return; - this._move(ev.oldIndex!, ev.newIndex!); - } - private _move(index: number, newIndex: number) { const triggers = this.triggers.concat(); const trigger = triggers.splice(index, 1)[0]; @@ -252,6 +210,12 @@ export default class HaAutomationTrigger extends LitElement { fireEvent(this, "value-changed", { value: triggers }); } + private _triggerMoved(ev: CustomEvent): void { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; + this._move(oldIndex, newIndex); + } + private _triggerChanged(ev: CustomEvent) { ev.stopPropagation(); const triggers = [...this.triggers]; @@ -280,34 +244,31 @@ export default class HaAutomationTrigger extends LitElement { } static get styles(): CSSResultGroup { - return [ - sortableStyles, - css` - ha-automation-trigger-row { - display: block; - margin-bottom: 16px; - scroll-margin-top: 48px; - } - ha-svg-icon { - height: 20px; - } - ha-alert { - display: block; - margin-bottom: 16px; - border-radius: var(--ha-card-border-radius, 16px); - overflow: hidden; - } - .handle { - cursor: move; /* fallback if grab cursor is unsupported */ - cursor: grab; - padding: 12px; - } - .handle ha-svg-icon { - pointer-events: none; - height: 24px; - } - `, - ]; + return css` + ha-automation-trigger-row { + display: block; + margin-bottom: 16px; + scroll-margin-top: 48px; + } + ha-svg-icon { + height: 20px; + } + ha-alert { + display: block; + margin-bottom: 16px; + border-radius: var(--ha-card-border-radius, 16px); + overflow: hidden; + } + .handle { + padding: 12px; + cursor: move; /* fallback if grab cursor is unsupported */ + cursor: grab; + } + .handle ha-svg-icon { + pointer-events: none; + height: 24px; + } + `; } } diff --git a/src/panels/config/helpers/forms/ha-input_select-form.ts b/src/panels/config/helpers/forms/ha-input_select-form.ts index e8be0fe504..69c328f2a0 100644 --- a/src/panels/config/helpers/forms/ha-input_select-form.ts +++ b/src/panels/config/helpers/forms/ha-input_select-form.ts @@ -1,22 +1,20 @@ import "@material/mwc-list/mwc-list"; import { mdiDelete, mdiDrag } from "@mdi/js"; -import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; -import type { SortableEvent } from "sortablejs"; -import { sortableStyles } from "../../../../resources/ha-sortable-style"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-button"; import "../../../../components/ha-icon-button"; -import "../../../../components/ha-list-item"; import "../../../../components/ha-icon-picker"; +import "../../../../components/ha-list-item"; +import "../../../../components/ha-sortable"; import "../../../../components/ha-textfield"; import type { HaTextField } from "../../../../components/ha-textfield"; import type { InputSelect } from "../../../../data/input_select"; import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; import { haStyle } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; -import type { SortableInstance } from "../../../../resources/sortable"; @customElement("ha-input_select-form") class HaInputSelectForm extends LitElement { @@ -32,59 +30,20 @@ class HaInputSelectForm extends LitElement { @state() private _options: string[] = []; - private _sortable?: SortableInstance; - @query("#option_input", true) private _optionInput?: HaTextField; - public connectedCallback() { - super.connectedCallback(); - this._createSortable(); - } - - public disconnectedCallback() { - super.disconnectedCallback(); - this._destroySortable(); - } - - private async _createSortable() { - const Sortable = (await import("../../../../resources/sortable")).default; - this._sortable = new Sortable(this.shadowRoot!.querySelector(".options")!, { - animation: 150, - fallbackClass: "sortable-fallback", - handle: ".handle", - onChoose: (evt: SortableEvent) => { - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - this._dragged(evt); - }, - }); - } - - private _dragged(ev: SortableEvent): void { - if (ev.oldIndex === ev.newIndex) return; - + private _optionMoved(ev: CustomEvent): void { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; const options = this._options.concat(); - const option = options.splice(ev.oldIndex!, 1)[0]; - options.splice(ev.newIndex!, 0, option); + const option = options.splice(oldIndex, 1)[0]; + options.splice(newIndex, 0, option); fireEvent(this, "value-changed", { value: { ...this._item, options }, }); } - private _destroySortable() { - this._sortable?.destroy(); - this._sortable = undefined; - } - set item(item: InputSelect) { this._item = item; if (item) { @@ -142,39 +101,41 @@ class HaInputSelectForm extends LitElement { "ui.dialogs.helper_settings.input_select.options" )}:
- - ${this._options.length - ? repeat( - this._options, - (option) => option, - (option, index) => html` - -
-
- + + + ${this._options.length + ? repeat( + this._options, + (option) => option, + (option, index) => html` + +
+
+ +
+ ${option}
- ${option} -
- + + + ` + ) + : html` + + ${this.hass!.localize( + "ui.dialogs.helper_settings.input_select.no_options" + )} - ` - ) - : html` - - ${this.hass!.localize( - "ui.dialogs.helper_settings.input_select.no_options" - )} - - `} - + `} + +
; - private _sortable?: SortableInstance; - - @query("#unchecked") private _uncheckedContainer?: HTMLElement; - connectedCallback(): void { super.connectedCallback(); if (this.hasUpdated) { @@ -264,9 +258,15 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard { ` : nothing}
- - ${this._renderItems(uncheckedItems, unavailable)} - ` + + + ${this._renderItems(uncheckedItems, unavailable)} + + ` : html`

${this.hass.localize( "ui.panel.lovelace.cards.todo-list.no_unchecked_items" @@ -553,43 +553,12 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard { private async _toggleReorder() { this._reordering = !this._reordering; - await this.updateComplete; - if (this._reordering) { - this._createSortable(); - } else { - this._sortable?.destroy(); - this._sortable = undefined; - } } - private async _createSortable() { - const Sortable = (await import("../../../resources/sortable")).default; - this._sortable = new Sortable(this._uncheckedContainer!, { - animation: 150, - fallbackClass: "sortable-fallback", - dataIdAttr: "item-id", - handle: "ha-svg-icon", - onChoose: (evt: SortableEvent) => { - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - if (evt.newIndex === undefined || evt.oldIndex === undefined) { - return; - } - // Since this is `onEnd` event, it's possible that - // an item was dragged away and was put back to its original position. - if (evt.oldIndex !== evt.newIndex) { - this._moveItem(evt.oldIndex, evt.newIndex); - } - }, - }); + private async _itemMoved(ev: CustomEvent) { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; + this._moveItem(oldIndex, newIndex); } private async _moveItem(oldIndex: number, newIndex: number) { @@ -621,165 +590,162 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard { } static get styles(): CSSResultGroup { - return [ - sortableStyles, - css` - ha-card { - height: 100%; - box-sizing: border-box; - } + return css` + ha-card { + height: 100%; + box-sizing: border-box; + } - .has-header { - padding-top: 0; - } + .has-header { + padding-top: 0; + } - .addRow { - padding: 16px; - padding-bottom: 0; - position: relative; - } + .addRow { + padding: 16px; + padding-bottom: 0; + position: relative; + } - .addRow ha-icon-button { - position: absolute; - right: 16px; - inset-inline-start: initial; - inset-inline-end: 16px; - } + .addRow ha-icon-button { + position: absolute; + right: 16px; + inset-inline-start: initial; + inset-inline-end: 16px; + } - .addRow, - .header { - display: flex; - flex-direction: row; - align-items: center; - } + .addRow, + .header { + display: flex; + flex-direction: row; + align-items: center; + } - .header { - padding-left: 30px; - padding-right: 16px; - padding-inline-start: 30px; - padding-inline-end: 16px; - margin-top: 8px; - justify-content: space-between; - direction: var(--direction); - } + .header { + padding-left: 30px; + padding-right: 16px; + padding-inline-start: 30px; + padding-inline-end: 16px; + margin-top: 8px; + justify-content: space-between; + direction: var(--direction); + } - .header span { - color: var(--primary-text-color); - font-weight: 500; - } + .header span { + color: var(--primary-text-color); + font-weight: 500; + } - .empty { - padding: 16px 32px; - } + .empty { + padding: 16px 32px; + } - .item { - margin-top: 8px; - } + .item { + margin-top: 8px; + } - ha-check-list-item { - --mdc-list-item-meta-size: 56px; - min-height: 56px; - height: auto; - } + ha-check-list-item { + --mdc-list-item-meta-size: 56px; + min-height: 56px; + height: auto; + } - ha-check-list-item.multiline { - align-items: flex-start; - --check-list-item-graphic-margin-top: 8px; - } + ha-check-list-item.multiline { + align-items: flex-start; + --check-list-item-graphic-margin-top: 8px; + } - .row { - display: flex; - justify-content: space-between; - } + .row { + display: flex; + justify-content: space-between; + } - .multiline .column { - display: flex; - flex-direction: column; - margin-top: 18px; - margin-bottom: 12px; - } + .multiline .column { + display: flex; + flex-direction: column; + margin-top: 18px; + margin-bottom: 12px; + } - .completed .summary { - text-decoration: line-through; - } + .completed .summary { + text-decoration: line-through; + } - .description, - .due { - font-size: 12px; - color: var(--secondary-text-color); - } + .description, + .due { + font-size: 12px; + color: var(--secondary-text-color); + } - .description { - white-space: initial; - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 3; - line-clamp: 3; - -webkit-box-orient: vertical; - } + .description { + white-space: initial; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 3; + line-clamp: 3; + -webkit-box-orient: vertical; + } - .description p { - margin: 0; - } + .description p { + margin: 0; + } - .description a { - color: var(--primary-color); - } + .description a { + color: var(--primary-color); + } - .due { - display: flex; - align-items: center; - } + .due { + display: flex; + align-items: center; + } - .due ha-svg-icon { - margin-right: 4px; - --mdc-icon-size: 14px; - } + .due ha-svg-icon { + margin-right: 4px; + --mdc-icon-size: 14px; + } - .due.overdue { - color: var(--warning-color); - } + .due.overdue { + color: var(--warning-color); + } - .completed .due.overdue { - color: var(--secondary-text-color); - } + .completed .due.overdue { + color: var(--secondary-text-color); + } - .handle { - cursor: move; /* fallback if grab cursor is unsupported */ - cursor: grab; - height: 24px; - padding: 16px 4px; - } + .handle { + cursor: move; /* fallback if grab cursor is unsupported */ + cursor: grab; + height: 24px; + padding: 16px 4px; + } - .deleteItemButton { - position: relative; - left: 8px; - } + .deleteItemButton { + position: relative; + left: 8px; + } - ha-textfield { - flex-grow: 1; - } + ha-textfield { + flex-grow: 1; + } - .divider { - height: 1px; - background-color: var(--divider-color); - margin: 10px 0; - } + .divider { + height: 1px; + background-color: var(--divider-color); + margin: 10px 0; + } - .clearall { - cursor: pointer; - } + .clearall { + cursor: pointer; + } - .todoList { - display: block; - padding: 8px; - } + .todoList { + display: block; + padding: 8px; + } - .warning { - color: var(--error-color); - } - `, - ]; + .warning { + color: var(--error-color); + } + `; } } diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index 22ac4f8aa0..0af08f1180 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -2,7 +2,6 @@ import { mdiDrag } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; -import type { SortableEvent } from "sortablejs"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/entity/ha-entity-picker"; import type { @@ -10,8 +9,7 @@ import type { HaEntityPickerEntityFilterFunc, } from "../../../components/entity/ha-entity-picker"; import "../../../components/ha-icon-button"; -import { sortableStyles } from "../../../resources/ha-sortable-style"; -import type { SortableInstance } from "../../../resources/sortable"; +import "../../../components/ha-sortable"; import { HomeAssistant } from "../../../types"; import { EntityConfig } from "../entity-rows/types"; @@ -27,13 +25,6 @@ export class HuiEntityEditor extends LitElement { private _entityKeys = new WeakMap(); - private _sortable?: SortableInstance; - - public disconnectedCallback() { - super.disconnectedCallback(); - this._destroySortable(); - } - private _getKey(action: EntityConfig) { if (!this._entityKeys.has(action)) { this._entityKeys.set(action, Math.random().toString()); @@ -55,27 +46,29 @@ export class HuiEntityEditor extends LitElement { this.hass!.localize("ui.panel.lovelace.editor.card.config.required") + ")"} -

- ${repeat( - this.entities, - (entityConf) => this._getKey(entityConf), - (entityConf, index) => html` -
-
- + +
+ ${repeat( + this.entities, + (entityConf) => this._getKey(entityConf), + (entityConf, index) => html` +
+
+ +
+
- -
- ` - )} -
+ ` + )} +
+ { - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - this._entityMoved(evt); - }, - } - ); - } - - private _destroySortable() { - this._sortable?.destroy(); - this._sortable = undefined; - } - private async _addEntity(ev: CustomEvent): Promise { const value = ev.detail.value; if (value === "") { @@ -132,14 +90,13 @@ export class HuiEntityEditor extends LitElement { fireEvent(this, "entities-changed", { entities: newConfigEntities }); } - private _entityMoved(ev: SortableEvent): void { - if (ev.oldIndex === ev.newIndex) { - return; - } + private _entityMoved(ev: CustomEvent): void { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; const newEntities = this.entities!.concat(); - newEntities.splice(ev.newIndex!, 0, newEntities.splice(ev.oldIndex!, 1)[0]); + newEntities.splice(newIndex, 0, newEntities.splice(oldIndex, 1)[0]); fireEvent(this, "entities-changed", { entities: newEntities }); } @@ -162,39 +119,36 @@ export class HuiEntityEditor extends LitElement { } static get styles(): CSSResultGroup { - return [ - sortableStyles, - css` - ha-entity-picker { - margin-top: 8px; - } - .add-entity { - display: block; - margin-left: 31px; - margin-inline-start: 31px; - margin-inline-end: initial; - direction: var(--direction); - } - .entity { - display: flex; - align-items: center; - } - .entity .handle { - padding-right: 8px; - cursor: move; /* fallback if grab cursor is unsupported */ - cursor: grab; - padding-inline-end: 8px; - padding-inline-start: initial; - direction: var(--direction); - } - .entity .handle > * { - pointer-events: none; - } - .entity ha-entity-picker { - flex-grow: 1; - } - `, - ]; + return css` + ha-entity-picker { + margin-top: 8px; + } + .add-entity { + display: block; + margin-left: 31px; + margin-inline-start: 31px; + margin-inline-end: initial; + direction: var(--direction); + } + .entity { + display: flex; + align-items: center; + } + .entity .handle { + padding-right: 8px; + cursor: move; /* fallback if grab cursor is unsupported */ + cursor: grab; + padding-inline-end: 8px; + padding-inline-start: initial; + direction: var(--direction); + } + .entity .handle > * { + pointer-events: none; + } + .entity ha-entity-picker { + flex-grow: 1; + } + `; } } diff --git a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts index 8693be890d..d8fe23d0a7 100644 --- a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts @@ -3,13 +3,13 @@ import { HassEntity } from "home-assistant-js-websocket"; import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; -import type { SortableEvent } from "sortablejs"; import { fireEvent } from "../../../../common/dom/fire_event"; import { stopPropagation } from "../../../../common/dom/stop_propagation"; import "../../../../components/entity/ha-entity-picker"; import "../../../../components/ha-button"; import "../../../../components/ha-icon-button"; import "../../../../components/ha-list-item"; +import "../../../../components/ha-sortable"; import "../../../../components/ha-svg-icon"; import { CUSTOM_TYPE_PREFIX, @@ -18,8 +18,6 @@ import { isCustomType, stripCustomPrefix, } from "../../../../data/lovelace_custom_cards"; -import { sortableStyles } from "../../../../resources/ha-sortable-style"; -import type { SortableInstance } from "../../../../resources/sortable"; import { HomeAssistant } from "../../../../types"; import { supportsAlarmModesCardFeature } from "../../card-features/hui-alarm-modes-card-feature"; import { supportsClimateFanModesCardFeature } from "../../card-features/hui-climate-fan-modes-card-feature"; @@ -39,11 +37,11 @@ import { supportsNumericInputCardFeature } from "../../card-features/hui-numeric import { supportsSelectOptionsCardFeature } from "../../card-features/hui-select-options-card-feature"; import { supportsTargetHumidityCardFeature } from "../../card-features/hui-target-humidity-card-feature"; import { supportsTargetTemperatureCardFeature } from "../../card-features/hui-target-temperature-card-feature"; +import { supportsUpdateActionsCardFeature } from "../../card-features/hui-update-actions-card-feature"; import { supportsVacuumCommandsCardFeature } from "../../card-features/hui-vacuum-commands-card-feature"; import { supportsWaterHeaterOperationModesCardFeature } from "../../card-features/hui-water-heater-operation-modes-card-feature"; import { LovelaceCardFeatureConfig } from "../../card-features/types"; import { getCardFeatureElementClass } from "../../create-element/create-card-feature-element"; -import { supportsUpdateActionsCardFeature } from "../../card-features/hui-update-actions-card-feature"; export type FeatureType = LovelaceCardFeatureConfig["type"]; type SupportsFeature = (stateObj: HassEntity) => boolean; @@ -149,13 +147,6 @@ export class HuiCardFeaturesEditor extends LitElement { private _featuresKeys = new WeakMap(); - private _sortable?: SortableInstance; - - public disconnectedCallback() { - super.disconnectedCallback(); - this._destroySortable(); - } - private _supportsFeatureType(type: string): boolean { if (!this.stateObj) return false; @@ -205,10 +196,6 @@ export class HuiCardFeaturesEditor extends LitElement { return this._featuresKeys.get(feature)!; } - protected firstUpdated() { - this._createSortable(); - } - private _getSupportedFeaturesType() { const featuresTypes = UI_FEATURE_TYPES.filter( (type) => !this.featuresTypes || this.featuresTypes.includes(type) @@ -249,61 +236,66 @@ export class HuiCardFeaturesEditor extends LitElement { ` : nothing} -
- ${repeat( - this.features, - (featureConf) => this._getKey(featureConf), - (featureConf, index) => { - const type = featureConf.type; - const supported = this._supportsFeatureType(type); - const editable = this._isFeatureTypeEditable(type); - return html` -
-
- -
-
-
- ${this._getFeatureTypeLabel(type)} - ${this.stateObj && !supported - ? html` - - ${this.hass!.localize( - "ui.panel.lovelace.editor.features.not_compatible" - )} - - ` - : nothing} + +
+ ${repeat( + this.features, + (featureConf) => this._getKey(featureConf), + (featureConf, index) => { + const type = featureConf.type; + const supported = this._supportsFeatureType(type); + const editable = this._isFeatureTypeEditable(type); + return html` +
+
+
+
+
+ ${this._getFeatureTypeLabel(type)} + ${this.stateObj && !supported + ? html` + + ${this.hass!.localize( + "ui.panel.lovelace.editor.features.not_compatible" + )} + + ` + : nothing} +
+
+ ${editable + ? html` + + ` + : nothing} +
- ${editable - ? html` - - ` - : nothing} - -
- `; - } - )} -
+ `; + } + )} +
+ ${supportedFeaturesType.length > 0 ? html` { - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - this._rowMoved(evt); - }, - } - ); - } - - private _destroySortable() { - this._sortable?.destroy(); - this._sortable = undefined; - } - private async _addFeature(ev: CustomEvent): Promise { const index = ev.detail.index as number; @@ -395,14 +357,13 @@ export class HuiCardFeaturesEditor extends LitElement { fireEvent(this, "features-changed", { features: newConfigFeature }); } - private _rowMoved(ev: SortableEvent): void { - if (ev.oldIndex === ev.newIndex) { - return; - } + private _featureMoved(ev: CustomEvent): void { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; const newFeatures = this.features!.concat(); - newFeatures.splice(ev.newIndex!, 0, newFeatures.splice(ev.oldIndex!, 1)[0]); + newFeatures.splice(newIndex, 0, newFeatures.splice(oldIndex, 1)[0]); fireEvent(this, "features-changed", { features: newFeatures }); } @@ -428,79 +389,76 @@ export class HuiCardFeaturesEditor extends LitElement { } static get styles(): CSSResultGroup { - return [ - sortableStyles, - css` - :host { - display: flex !important; - flex-direction: column; - } - .content { - padding: 12px; - } - ha-expansion-panel { - display: block; - --expansion-panel-content-padding: 0; - border-radius: 6px; - } - h3 { - margin: 0; - font-size: inherit; - font-weight: inherit; - } - ha-svg-icon, - ha-icon { - color: var(--secondary-text-color); - } - ha-button-menu { - margin-top: 8px; - } - .feature { - display: flex; - align-items: center; - } - .feature .handle { - padding-right: 8px; - cursor: move; /* fallback if grab cursor is unsupported */ - cursor: grab; - padding-inline-end: 8px; - padding-inline-start: initial; - direction: var(--direction); - } - .feature .handle > * { - pointer-events: none; - } + return css` + :host { + display: flex !important; + flex-direction: column; + } + .content { + padding: 12px; + } + ha-expansion-panel { + display: block; + --expansion-panel-content-padding: 0; + border-radius: 6px; + } + h3 { + margin: 0; + font-size: inherit; + font-weight: inherit; + } + ha-svg-icon, + ha-icon { + color: var(--secondary-text-color); + } + ha-button-menu { + margin-top: 8px; + } + .feature { + display: flex; + align-items: center; + } + .feature .handle { + cursor: move; /* fallback if grab cursor is unsupported */ + cursor: grab; + padding-right: 8px; + padding-inline-end: 8px; + padding-inline-start: initial; + direction: var(--direction); + } + .feature .handle > * { + pointer-events: none; + } - .feature-content { - height: 60px; - font-size: 16px; - display: flex; - align-items: center; - justify-content: space-between; - flex-grow: 1; - } + .feature-content { + height: 60px; + font-size: 16px; + display: flex; + align-items: center; + justify-content: space-between; + flex-grow: 1; + } - .feature-content div { - display: flex; - flex-direction: column; - } + .feature-content div { + display: flex; + flex-direction: column; + } - .remove-icon, - .edit-icon { - --mdc-icon-button-size: 36px; - color: var(--secondary-text-color); - } + .remove-icon, + .edit-icon { + --mdc-icon-button-size: 36px; + color: var(--secondary-text-color); + } - .secondary { - font-size: 12px; - color: var(--secondary-text-color); - } + .secondary { + font-size: 12px; + color: var(--secondary-text-color); + } - li[divider] { - border-bottom-color: var(--divider-color); - } - `, - ]; + li[divider] { + border-bottom-color: var(--divider-color); + } + `; } } 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 231305a1a3..58891055fc 100644 --- a/src/panels/lovelace/editor/hui-entities-card-row-editor.ts +++ b/src/panels/lovelace/editor/hui-entities-card-row-editor.ts @@ -1,15 +1,13 @@ import { mdiClose, mdiDrag, mdiPencil } from "@mdi/js"; -import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; -import type { SortableEvent } from "sortablejs"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/entity/ha-entity-picker"; import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker"; import "../../../components/ha-icon-button"; +import "../../../components/ha-sortable"; import "../../../components/ha-svg-icon"; -import { sortableStyles } from "../../../resources/ha-sortable-style"; -import type { SortableInstance } from "../../../resources/sortable"; import { HomeAssistant } from "../../../types"; import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types"; @@ -31,13 +29,6 @@ export class HuiEntitiesCardRowEditor extends LitElement { private _entityKeys = new WeakMap(); - private _sortable?: SortableInstance; - - public disconnectedCallback() { - super.disconnectedCallback(); - this._destroySortable(); - } - private _getKey(action: LovelaceRowConfig) { if (!this._entityKeys.has(action)) { this._entityKeys.set(action, Math.random().toString()); @@ -60,64 +51,66 @@ export class HuiEntitiesCardRowEditor extends LitElement { "ui.panel.lovelace.editor.card.config.required" )})`} -
- ${repeat( - this.entities, - (entityConf) => this._getKey(entityConf), - (entityConf, index) => html` -
-
- -
- ${entityConf.type - ? html` -
-
- - ${this.hass!.localize( - `ui.panel.lovelace.editor.card.entities.entity_row.${entityConf.type}` - )} - - ${this.hass!.localize( - "ui.panel.lovelace.editor.card.entities.edit_special_row" - )} + +
+ ${repeat( + this.entities, + (entityConf) => this._getKey(entityConf), + (entityConf, index) => html` +
+
+ +
+ ${entityConf.type + ? html` +
+
+ + ${this.hass!.localize( + `ui.panel.lovelace.editor.card.entities.entity_row.${entityConf.type}` + )} + + ${this.hass!.localize( + "ui.panel.lovelace.editor.card.entities.edit_special_row" + )} +
-
- ` - : html` - - `} - - -
- ` - )} -
+ ` + : html` + + `} + + +
+ ` + )} +
+ { - (evt.item as any).placeholder = - document.createComment("sort-placeholder"); - evt.item.after((evt.item as any).placeholder); - }, - onEnd: (evt: SortableEvent) => { - // put back in original location - if ((evt.item as any).placeholder) { - (evt.item as any).placeholder.replaceWith(evt.item); - delete (evt.item as any).placeholder; - } - this._rowMoved(evt); - }, - } - ); - } - - private _destroySortable() { - this._sortable?.destroy(); - this._sortable = undefined; - } - private async _addEntity(ev: CustomEvent): Promise { const value = ev.detail.value; if (value === "") { @@ -172,14 +131,13 @@ export class HuiEntitiesCardRowEditor extends LitElement { fireEvent(this, "entities-changed", { entities: newConfigEntities }); } - private _rowMoved(ev: SortableEvent): void { - if (ev.oldIndex === ev.newIndex) { - return; - } + private _rowMoved(ev: CustomEvent): void { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; const newEntities = this.entities!.concat(); - newEntities.splice(ev.newIndex!, 0, newEntities.splice(ev.oldIndex!, 1)[0]); + newEntities.splice(newIndex, 0, newEntities.splice(oldIndex, 1)[0]); fireEvent(this, "entities-changed", { entities: newEntities }); } @@ -222,67 +180,64 @@ export class HuiEntitiesCardRowEditor extends LitElement { } static get styles(): CSSResultGroup { - return [ - sortableStyles, - css` - ha-entity-picker { - margin-top: 8px; - } - .add-entity { - display: block; - margin-left: 31px; - margin-right: 71px; - margin-inline-start: 31px; - margin-inline-end: 71px; - direction: var(--direction); - } - .entity { - display: flex; - align-items: center; - } + return css` + ha-entity-picker { + margin-top: 8px; + } + .add-entity { + display: block; + margin-left: 31px; + margin-right: 71px; + margin-inline-start: 31px; + margin-inline-end: 71px; + direction: var(--direction); + } + .entity { + display: flex; + align-items: center; + } - .entity .handle { - padding-right: 8px; - cursor: move; /* fallback if grab cursor is unsupported */ - cursor: grab; - padding-inline-end: 8px; - padding-inline-start: initial; - direction: var(--direction); - } - .entity .handle > * { - pointer-events: none; - } + .entity .handle { + padding-right: 8px; + cursor: move; /* fallback if grab cursor is unsupported */ + cursor: grab; + padding-inline-end: 8px; + padding-inline-start: initial; + direction: var(--direction); + } + .entity .handle > * { + pointer-events: none; + } - .entity ha-entity-picker { - flex-grow: 1; - } + .entity ha-entity-picker { + flex-grow: 1; + } - .special-row { - height: 60px; - font-size: 16px; - display: flex; - align-items: center; - justify-content: space-between; - flex-grow: 1; - } + .special-row { + height: 60px; + font-size: 16px; + display: flex; + align-items: center; + justify-content: space-between; + flex-grow: 1; + } - .special-row div { - display: flex; - flex-direction: column; - } + .special-row div { + display: flex; + flex-direction: column; + } - .remove-icon, - .edit-icon { - --mdc-icon-button-size: 36px; - color: var(--secondary-text-color); - } + .remove-icon, + .edit-icon { + --mdc-icon-button-size: 36px; + color: var(--secondary-text-color); + } - .secondary { - font-size: 12px; - color: var(--secondary-text-color); - } - `, - ]; + .secondary { + font-size: 12px; + color: var(--secondary-text-color); + } + `; } } diff --git a/src/resources/ha-sortable-style.ts b/src/resources/ha-sidebar-edit-style.ts similarity index 74% rename from src/resources/ha-sortable-style.ts rename to src/resources/ha-sidebar-edit-style.ts index a1fcfe9209..50bf37c756 100644 --- a/src/resources/ha-sortable-style.ts +++ b/src/resources/ha-sidebar-edit-style.ts @@ -1,7 +1,7 @@ import { css } from "lit"; -export const sortableStyles = css` - #sortable a:nth-of-type(2n) paper-icon-item { +export const sidebarEditStyle = css` + .reorder-list a:nth-of-type(2n) paper-icon-item { animation-name: keyframes1; animation-iteration-count: infinite; transform-origin: 50% 10%; @@ -9,7 +9,7 @@ export const sortableStyles = css` animation-duration: 0.25s; } - #sortable a:nth-of-type(2n-1) paper-icon-item { + .reorder-list a:nth-of-type(2n-1) paper-icon-item { animation-name: keyframes2; animation-iteration-count: infinite; animation-direction: alternate; @@ -18,12 +18,12 @@ export const sortableStyles = css` animation-duration: 0.33s; } - #sortable a { + .reorder-list a { height: 48px; display: flex; } - #sortable { + .reorder-list { outline: none; display: block !important; } @@ -32,26 +32,6 @@ export const sortableStyles = css` display: flex !important; } - .sortable-fallback { - display: none; - opacity: 0; - } - - .sortable-ghost { - border: 2px solid var(--primary-color); - background: rgba(var(--rgb-primary-color), 0.25); - border-radius: 4px; - opacity: 0.4; - } - - .sortable-drag { - border-radius: 4px; - opacity: 1; - background: var(--card-background-color); - box-shadow: 0px 4px 8px 3px #00000026; - cursor: grabbing; - } - @keyframes keyframes1 { 0% { transform: rotate(-1deg);