From ae9fcebfd5898b17ec6ea2fdd2ef8bf8579ca908 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 30 Aug 2023 00:59:06 -0700 Subject: [PATCH] Add sortable options to input_select settings menu (#17706) * Sortable options in input_select settings menu * fix lint * no ripple, default cursor * Update src/panels/config/helpers/forms/ha-input_select-form.ts Co-authored-by: Paul Bottein * sortableStyles * Use ha-list-item and mwc-list --------- Co-authored-by: Paul Bottein --- .../helpers/forms/ha-input_select-form.ts | 143 ++++++++++++++---- 1 file changed, 114 insertions(+), 29 deletions(-) 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 55217382bd..d314bbfa99 100644 --- a/src/panels/config/helpers/forms/ha-input_select-form.ts +++ b/src/panels/config/helpers/forms/ha-input_select-form.ts @@ -1,10 +1,14 @@ -import "@material/mwc-button/mwc-button"; -import "@material/mwc-list/mwc-list-item"; -import { mdiDelete } from "@mdi/js"; +import "@material/mwc-list/mwc-list"; +import { mdiDelete, mdiDrag } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, 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-textfield"; import type { HaTextField } from "../../../../components/ha-textfield"; @@ -12,6 +16,10 @@ 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 { + loadSortable, + SortableInstance, +} from "../../../../resources/sortable.ondemand"; @customElement("ha-input_select-form") class HaInputSelectForm extends LitElement { @@ -27,8 +35,59 @@ 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 loadSortable(); + 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; + + const options = this._options.concat(); + const option = options.splice(ev.oldIndex!, 1)[0]; + options.splice(ev.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) { @@ -86,30 +145,39 @@ class HaInputSelectForm extends LitElement { "ui.dialogs.helper_settings.input_select.options" )}: - ${this._options.length - ? this._options.map( - (option, index) => html` - - ${option} - - - ` - ) - : html` - - ${this.hass!.localize( - "ui.dialogs.helper_settings.input_select.no_options" - )} - - `} + + ${this._options.length + ? repeat( + this._options, + (option) => option, + (option, index) => html` + +
+
+ +
+ ${option} +
+ +
+ ` + ) + : html` + + ${this.hass!.localize( + "ui.dialogs.helper_settings.input_select.no_options" + )} + + `} +
- ${this.hass!.localize( "ui.dialogs.helper_settings.input_select.add" - )}
@@ -190,6 +258,7 @@ class HaInputSelectForm extends LitElement { static get styles(): CSSResultGroup { return [ haStyle, + sortableStyles, css` .form { color: var(--primary-text-color); @@ -199,6 +268,10 @@ class HaInputSelectForm extends LitElement { border-radius: 4px; margin-top: 4px; --mdc-icon-button-size: 24px; + --mdc-ripple-color: transparent; + --mdc-list-side-padding: 16px; + cursor: default; + background-color: var(--card-background-color); } mwc-button { margin-left: 8px; @@ -214,6 +287,18 @@ class HaInputSelectForm extends LitElement { margin-top: 8px; margin-bottom: 8px; } + .handle { + cursor: move; + padding-right: 12px; + } + .handle ha-svg-icon { + pointer-events: none; + height: 24px; + } + .optioncontent { + display: flex; + align-items: center; + } `, ]; }