From 8d2ec8098c34a10bc7dbd708a2a1d484623a77a0 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 25 Oct 2023 13:09:28 +0200 Subject: [PATCH 1/3] Fix state not condition for condition card (#18397) --- src/panels/lovelace/common/validate-condition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/common/validate-condition.ts b/src/panels/lovelace/common/validate-condition.ts index 57c2eab65eb2..b6059292dbaa 100644 --- a/src/panels/lovelace/common/validate-condition.ts +++ b/src/panels/lovelace/common/validate-condition.ts @@ -49,7 +49,7 @@ function checkStateCondition( return condition.state != null ? ensureArray(condition.state).includes(state) - : ensureArray(condition.state_not).includes(state); + : !ensureArray(condition.state_not).includes(state); } function checkStateNumericCondition( From 0c2531a7eeb181f34e8b11f70bcaf24d6a43337d Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 25 Oct 2023 13:57:50 +0200 Subject: [PATCH 2/3] Update todo list card (#18396) --- .../ha-two-pane-top-app-bar-fixed.ts | 1 + .../lovelace/cards/hui-todo-list-card.ts | 110 ++++++++---------- 2 files changed, 52 insertions(+), 59 deletions(-) diff --git a/src/components/ha-two-pane-top-app-bar-fixed.ts b/src/components/ha-two-pane-top-app-bar-fixed.ts index ec64e4c2f9b4..995079aec0f8 100644 --- a/src/components/ha-two-pane-top-app-bar-fixed.ts +++ b/src/components/ha-two-pane-top-app-bar-fixed.ts @@ -302,6 +302,7 @@ export abstract class TopAppBarBaseBase extends BaseElement { } .pane .footer { border-top: 1px solid var(--divider-color); + padding-bottom: 8px; } .main { min-height: 100%; diff --git a/src/panels/lovelace/cards/hui-todo-list-card.ts b/src/panels/lovelace/cards/hui-todo-list-card.ts index 9bed15e5e11a..76cc977129e2 100644 --- a/src/panels/lovelace/cards/hui-todo-list-card.ts +++ b/src/panels/lovelace/cards/hui-todo-list-card.ts @@ -11,8 +11,9 @@ import { } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import { guard } from "lit/directives/guard"; import { repeat } from "lit/directives/repeat"; +import memoizeOne from "memoize-one"; +import type { SortableEvent } from "sortablejs"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { supportsFeature } from "../../../common/entity/supports-feature"; import "../../../components/ha-card"; @@ -75,17 +76,11 @@ export class HuiTodoListCard @state() private _items?: Record; - @state() private _uncheckedItems?: TodoItem[]; - - @state() private _checkedItems?: TodoItem[]; - @state() private _reordering = false; - @state() private _renderEmptySortable = false; - private _sortable?: SortableInstance; - @query("#sortable") private _sortableEl?: HTMLElement; + @query("#unchecked") private _uncheckedContainer?: HTMLElement; public getCardSize(): number { return (this._config ? (this._config.title ? 2 : 0) : 0) + 3; @@ -109,6 +104,24 @@ export class HuiTodoListCard return undefined; } + private _getCheckedItems = memoizeOne( + (items?: Record): TodoItem[] => + items + ? Object.values(items).filter( + (item) => item.status === TodoItemStatus.Completed + ) + : [] + ); + + private _getUncheckedItems = memoizeOne( + (items?: Record): TodoItem[] => + items + ? Object.values(items).filter( + (item) => item.status === TodoItemStatus.NeedsAction + ) + : [] + ); + public willUpdate( changedProperties: PropertyValueMap | Map ): void { @@ -118,8 +131,7 @@ export class HuiTodoListCard } this._fetchData(); } else if (changedProperties.has("_entityId") || !this._items) { - this._uncheckedItems = []; - this._checkedItems = []; + this._items = undefined; this._fetchData(); } } @@ -157,6 +169,9 @@ export class HuiTodoListCard return nothing; } + const checkedItems = this._getCheckedItems(this._items); + const uncheckedItems = this._getUncheckedItems(this._items); + return html` - ${this._reordering - ? html` -
- ${guard( - [this._uncheckedItems, this._renderEmptySortable], - () => - this._renderEmptySortable - ? "" - : this._renderItems(this._uncheckedItems!) - )} -
- ` - : this._renderItems(this._uncheckedItems!)} - ${this._checkedItems!.length > 0 +
${this._renderItems(uncheckedItems)}
+ ${checkedItems.length ? html`
@@ -224,7 +227,7 @@ export class HuiTodoListCard ${this.todoListSupportsFeature( TodoListEntityFeature.DELETE_TODO_ITEM ) - ? html` ${repeat( - this._checkedItems!, + checkedItems, (item) => item.uid, (item) => html`
@@ -322,21 +325,12 @@ export class HuiTodoListCard if (!this.hass || !this._entityId) { return; } - const checkedItems: TodoItem[] = []; - const uncheckedItems: TodoItem[] = []; const items = await fetchItems(this.hass!, this._entityId!); const records: Record = {}; items.forEach((item) => { records[item.uid!] = item; - if (item.status === TodoItemStatus.Completed) { - checkedItems.push(item); - } else { - uncheckedItems.push(item); - } }); this._items = records; - this._checkedItems = checkedItems; - this._uncheckedItems = uncheckedItems; } private _completeItem(ev): void { @@ -373,7 +367,7 @@ export class HuiTodoListCard return; } const deleteActions: Array> = []; - this._checkedItems!.forEach((item: TodoItem) => { + this._getCheckedItems(this._items).forEach((item: TodoItem) => { deleteActions.push(deleteItem(this.hass!, this._entityId!, item.uid!)); }); await Promise.all(deleteActions).finally(() => this._fetchData()); @@ -416,43 +410,41 @@ export class HuiTodoListCard private async _createSortable() { const Sortable = (await import("../../../resources/sortable")).default; - const sortableEl = this._sortableEl; - this._sortable = new Sortable(sortableEl!, { + this._sortable = new Sortable(this._uncheckedContainer!, { animation: 150, fallbackClass: "sortable-fallback", dataIdAttr: "item-id", handle: "ha-svg-icon", - onEnd: async (evt) => { + 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 wa dragged away and was put back to its original position. + // an item was dragged away and was put back to its original position. if (evt.oldIndex !== evt.newIndex) { - const item = this._uncheckedItems![evt.oldIndex]; - moveItem( - this.hass!, - this._entityId!, - item.uid!, - evt.newIndex - ).finally(() => this._fetchData()); - // Move the shopping list item in memory. - this._uncheckedItems!.splice( - evt.newIndex, - 0, - this._uncheckedItems!.splice(evt.oldIndex, 1)[0] - ); - } - this._renderEmptySortable = true; - await this.updateComplete; - while (sortableEl?.lastElementChild) { - sortableEl.removeChild(sortableEl.lastElementChild); + this._moveItem(evt.oldIndex, evt.newIndex); } - this._renderEmptySortable = false; }, }); } + private async _moveItem(oldIndex, newIndex) { + const item = this._getUncheckedItems(this._items)[oldIndex]; + await moveItem(this.hass!, this._entityId!, item.uid!, newIndex).finally( + () => this._fetchData() + ); + } + static get styles(): CSSResultGroup { return css` ha-card { From 763f80b46a7b8920be1921bd7c649438c934f40a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 25 Oct 2023 13:59:11 +0200 Subject: [PATCH 3/3] Bumped version to 20231025.1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f599efbe4009..88e5452ec2e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20231025.0" +version = "20231025.1" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md"