diff --git a/src/components/ha-filter-categories.ts b/src/components/ha-filter-categories.ts index 571c6b27ff..9b5a7c539e 100644 --- a/src/components/ha-filter-categories.ts +++ b/src/components/ha-filter-categories.ts @@ -12,8 +12,10 @@ import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; import { CategoryRegistryEntry, + createCategoryRegistryEntry, deleteCategoryRegistryEntry, subscribeCategoryRegistry, + updateCategoryRegistryEntry, } from "../data/category_registry"; import { showConfirmationDialog } from "../dialogs/generic/show-dialog-box"; import { SubscribeMixin } from "../mixins/subscribe-mixin"; @@ -174,6 +176,8 @@ export class HaFilterCategories extends SubscribeMixin(LitElement) { showCategoryRegistryDetailDialog(this, { scope: this.scope!, entry: this._categories.find((cat) => cat.category_id === id), + updateEntry: (updates) => + updateCategoryRegistryEntry(this.hass, this.scope!, id, updates), }); } @@ -206,7 +210,11 @@ export class HaFilterCategories extends SubscribeMixin(LitElement) { if (!this.scope) { return; } - showCategoryRegistryDetailDialog(this, { scope: this.scope }); + showCategoryRegistryDetailDialog(this, { + scope: this.scope, + createEntry: (values) => + createCategoryRegistryEntry(this.hass, this.scope!, values), + }); } private _expandedWillChange(ev) { diff --git a/src/panels/config/category/dialog-category-registry-detail.ts b/src/panels/config/category/dialog-category-registry-detail.ts index 508e5b9b42..74f89f0bd7 100644 --- a/src/panels/config/category/dialog-category-registry-detail.ts +++ b/src/panels/config/category/dialog-category-registry-detail.ts @@ -1,6 +1,6 @@ import "@material/mwc-button"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; -import { property, state } from "lit/decorators"; +import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-alert"; import { createCloseHeading } from "../../../components/ha-dialog"; @@ -8,14 +8,14 @@ import "../../../components/ha-icon-picker"; import "../../../components/ha-settings-row"; import "../../../components/ha-textfield"; import { + CategoryRegistryEntry, CategoryRegistryEntryMutableParams, - createCategoryRegistryEntry, - updateCategoryRegistryEntry, } from "../../../data/category_registry"; import { haStyleDialog } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; import { CategoryRegistryDetailDialogParams } from "./show-dialog-category-registry-detail"; +@customElement("dialog-category-registry-detail") class DialogCategoryDetail extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -34,8 +34,13 @@ class DialogCategoryDetail extends LitElement { ): Promise { this._params = params; this._error = undefined; - this._name = this._params.entry ? this._params.entry.name : ""; - this._icon = this._params.entry?.icon || null; + if (this._params.entry) { + this._name = this._params.entry.name || ""; + this._icon = this._params.entry.icon || null; + } else { + this._name = this._params.suggestedName || ""; + this._icon = null; + } await this.updateComplete; } @@ -123,24 +128,16 @@ class DialogCategoryDetail extends LitElement { private async _updateEntry() { const create = !this._params!.entry; this._submitting = true; + let newValue: CategoryRegistryEntry | undefined; try { const values: CategoryRegistryEntryMutableParams = { name: this._name.trim(), icon: this._icon || (create ? undefined : null), }; if (create) { - await createCategoryRegistryEntry( - this.hass, - this._params!.scope, - values - ); + newValue = await this._params!.createEntry!(values); } else { - await updateCategoryRegistryEntry( - this.hass, - this._params!.scope, - this._params!.entry!.category_id, - values - ); + newValue = await this._params!.updateEntry!(values); } this.closeDialog(); } catch (err: any) { @@ -150,6 +147,7 @@ class DialogCategoryDetail extends LitElement { } finally { this._submitting = false; } + return newValue; } static get styles(): CSSResultGroup { @@ -171,5 +169,3 @@ declare global { "dialog-category-registry-detail": DialogCategoryDetail; } } - -customElements.define("dialog-category-registry-detail", DialogCategoryDetail); diff --git a/src/panels/config/category/ha-category-picker.ts b/src/panels/config/category/ha-category-picker.ts index 583eaf4f44..529db65b2c 100644 --- a/src/panels/config/category/ha-category-picker.ts +++ b/src/panels/config/category/ha-category-picker.ts @@ -20,19 +20,20 @@ import { createCategoryRegistryEntry, subscribeCategoryRegistry, } from "../../../data/category_registry"; -import { - showAlertDialog, - showPromptDialog, -} from "../../../dialogs/generic/show-dialog-box"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { HomeAssistant, ValueChangedEvent } from "../../../types"; +import { showCategoryRegistryDetailDialog } from "./show-dialog-category-registry-detail"; type ScorableCategoryRegistryEntry = ScorableTextItem & CategoryRegistryEntry; +const ADD_NEW_ID = "___ADD_NEW___"; +const NO_CATEGORIES_ID = "___NO_CATEGORIES___"; +const ADD_NEW_SUGGESTION_ID = "___ADD_NEW_SUGGESTION___"; + const rowRenderer: ComboBoxLitRenderer = (item) => html` ${item.icon ? html`` @@ -103,7 +104,7 @@ export class HaCategoryPicker extends SubscribeMixin(LitElement) { const result = categories ? [...categories] : []; if (!result?.length) { result.push({ - category_id: "no_categories", + category_id: NO_CATEGORIES_ID, name: this.hass.localize( "ui.components.category-picker.no_categories" ), @@ -116,7 +117,7 @@ export class HaCategoryPicker extends SubscribeMixin(LitElement) { : [ ...result, { - category_id: "add_new", + category_id: ADD_NEW_ID, name: this.hass.localize("ui.components.category-picker.add_new"), icon: "mdi:plus", }, @@ -130,7 +131,12 @@ export class HaCategoryPicker extends SubscribeMixin(LitElement) { (this._init && changedProps.has("_opened") && this._opened) ) { this._init = true; - const categories = this._getCategories(this._categories, this.noAdd); + const categories = this._getCategories(this._categories, this.noAdd).map( + (label) => ({ + ...label, + strings: [label.name], + }) + ); this.comboBox.items = categories; this.comboBox.filteredItems = categories; } @@ -175,18 +181,30 @@ export class HaCategoryPicker extends SubscribeMixin(LitElement) { filterString, target.items || [] ); - if (!this.noAdd && filteredItems?.length === 0) { - this._suggestion = filterString; - this.comboBox.filteredItems = [ - { - category_id: "add_new_suggestion", - name: this.hass.localize( - "ui.components.category-picker.add_new_sugestion", - { name: this._suggestion } - ), - picture: null, - }, - ]; + if (filteredItems?.length === 0) { + if (this.noAdd) { + this.comboBox.filteredItems = [ + { + category_id: NO_CATEGORIES_ID, + name: this.hass.localize( + "ui.components.category-picker.no_categories" + ), + icon: null, + }, + ] as ScorableCategoryRegistryEntry[]; + } else { + this._suggestion = filterString; + this.comboBox.filteredItems = [ + { + category_id: ADD_NEW_SUGGESTION_ID, + name: this.hass.localize( + "ui.components.category-picker.add_new_sugestion", + { name: this._suggestion } + ), + icon: "mdi:plus", + }, + ]; + } } else { this.comboBox.filteredItems = filteredItems; } @@ -204,11 +222,11 @@ export class HaCategoryPicker extends SubscribeMixin(LitElement) { ev.stopPropagation(); let newValue = ev.detail.value; - if (newValue === "no_categories") { + if (newValue === NO_CATEGORIES_ID) { newValue = ""; } - if (!["add_new_suggestion", "add_new"].includes(newValue)) { + if (![ADD_NEW_SUGGESTION_ID, ADD_NEW_ID].includes(newValue)) { if (newValue !== this._value) { this._setValue(newValue); } @@ -216,54 +234,30 @@ export class HaCategoryPicker extends SubscribeMixin(LitElement) { } (ev.target as any).value = this._value; - showPromptDialog(this, { - title: this.hass.localize( - "ui.components.category-picker.add_dialog.title" - ), - text: this.hass.localize("ui.components.category-picker.add_dialog.text"), - confirmText: this.hass.localize( - "ui.components.category-picker.add_dialog.add" - ), - inputLabel: this.hass.localize( - "ui.components.category-picker.add_dialog.name" - ), - defaultValue: - newValue === "add_new_suggestion" ? this._suggestion : undefined, - confirm: async (name) => { - if (!name) { - return; - } - try { - const category = await createCategoryRegistryEntry( - this.hass, - this.scope!, - { - name, - } - ); - this._categories = [...this._categories!, category]; - this.comboBox.filteredItems = this._getCategories( - this._categories, - this.noAdd - ); - await this.updateComplete; - await this.comboBox.updateComplete; - this._setValue(category.category_id); - } catch (err: any) { - showAlertDialog(this, { - title: this.hass.localize( - "ui.components.category-picker.add_dialog.failed_create_category" - ), - text: err.message, - }); - } - }, - cancel: () => { - this._setValue(undefined); - this._suggestion = undefined; - this.comboBox.setInputValue(""); + + showCategoryRegistryDetailDialog(this, { + scope: this.scope!, + suggestedName: newValue === ADD_NEW_SUGGESTION_ID ? this._suggestion : "", + createEntry: async (values) => { + const category = await createCategoryRegistryEntry( + this.hass, + this.scope!, + values + ); + this._categories = [...this._categories!, category]; + this.comboBox.filteredItems = this._getCategories( + this._categories, + this.noAdd + ); + await this.updateComplete; + await this.comboBox.updateComplete; + this._setValue(category.category_id); + return category; }, }); + + this._suggestion = undefined; + this.comboBox.setInputValue(""); } private _setValue(value?: string) { diff --git a/src/panels/config/category/show-dialog-category-registry-detail.ts b/src/panels/config/category/show-dialog-category-registry-detail.ts index 00ac4686e8..7adaff611f 100644 --- a/src/panels/config/category/show-dialog-category-registry-detail.ts +++ b/src/panels/config/category/show-dialog-category-registry-detail.ts @@ -1,9 +1,19 @@ import { fireEvent } from "../../../common/dom/fire_event"; -import { CategoryRegistryEntry } from "../../../data/category_registry"; +import { + CategoryRegistryEntry, + CategoryRegistryEntryMutableParams, +} from "../../../data/category_registry"; export interface CategoryRegistryDetailDialogParams { entry?: CategoryRegistryEntry; scope: string; + suggestedName?: string; + createEntry?: ( + values: CategoryRegistryEntryMutableParams + ) => Promise; + updateEntry?: ( + updates: Partial + ) => Promise; } export const loadCategoryRegistryDetailDialog = () =>