diff --git a/src/components/entity/ha-entity-name-picker.ts b/src/components/entity/ha-entity-name-picker.ts index 297ff82c24..608752aeef 100644 --- a/src/components/entity/ha-entity-name-picker.ts +++ b/src/components/entity/ha-entity-name-picker.ts @@ -18,10 +18,7 @@ import "../ha-combo-box-item"; import "../ha-generic-picker"; import type { HaGenericPicker } from "../ha-generic-picker"; import "../ha-input-helper-text"; -import { - NO_ITEMS_AVAILABLE_ID, - type PickerComboBoxItem, -} from "../ha-picker-combo-box"; +import type { PickerComboBoxItem } from "../ha-picker-combo-box"; import "../ha-sortable"; const rowRenderer: RenderItemFunction = (item) => html` @@ -184,18 +181,17 @@ export class HaEntityNamePicker extends LitElement { .disabled=${this.disabled} .required=${this.required && !value.length} .getItems=${this._getFilteredItems} - .getAdditionalItems=${this._getAdditionalItems} .rowRenderer=${rowRenderer} - .searchFn=${this._searchFn} - .notFoundLabel=${this.hass.localize( - "ui.components.entity.entity-name-picker.no_match" - )} .value=${this._getPickerValue()} allow-custom-value .customValueLabel=${this.hass.localize( "ui.components.entity.entity-name-picker.custom_name" )} @value-changed=${this._pickerValueChanged} + .searchFn=${this._searchFn} + .searchLabel=${this.hass.localize( + "ui.components.entity.entity-name-picker.search" + )} >
{ + private _getFilteredItems = (): PickerComboBoxItem[] => { const items = this._getItems(this.entityId); const currentItem = this._editIndex != null ? this._items[this._editIndex] : undefined; @@ -336,49 +329,27 @@ export class HaEntityNamePicker extends LitElement { ); // When editing an existing text item, include it in the base items - if (currentItem?.type === "text" && currentItem.text && !searchString) { + if (currentItem?.type === "text" && currentItem.text) { filteredItems.push(this._customNameOption(currentItem.text)); } return filteredItems; }; - private _getAdditionalItems = ( - searchString?: string + private _searchFn = ( + searchString: string, + filteredItems: PickerComboBoxItem[] ): PickerComboBoxItem[] => { - if (!searchString) { - return []; - } - const currentItem = this._editIndex != null ? this._items[this._editIndex] : undefined; + const currentId = + currentItem?.type === "text" && currentItem.text + ? this._customNameOption(currentItem.text).id + : undefined; - // Don't add if it's the same as the current item being edited - if ( - currentItem?.type === "text" && - currentItem.text && - currentItem.text === searchString - ) { - return []; - } - - // Always return custom name option when there's a search string - // This prevents "No matching items found" from showing - return [this._customNameOption(searchString)]; - }; - - private _searchFn = ( - search: string, - filteredItems: PickerComboBoxItem[], - _allItems: PickerComboBoxItem[] - ): PickerComboBoxItem[] => { - // Remove NO_ITEMS_AVAILABLE_ID if we have additional items (custom name option) - // This prevents "No matching items found" from showing when custom values are allowed - const hasAdditionalItems = this._getAdditionalItems(search).length > 0; - if (hasAdditionalItems) { - return filteredItems.filter( - (item) => typeof item !== "string" || item !== NO_ITEMS_AVAILABLE_ID - ); + // Remove custom name option if search string is present to avoid duplicates + if (searchString && currentId) { + return filteredItems.filter((item) => item.id !== currentId); } return filteredItems; }; diff --git a/src/components/entity/ha-entity-state-content-picker.ts b/src/components/entity/ha-entity-state-content-picker.ts index 74c42a9d22..85a13d8a1d 100644 --- a/src/components/entity/ha-entity-state-content-picker.ts +++ b/src/components/entity/ha-entity-state-content-picker.ts @@ -19,7 +19,10 @@ import "../ha-combo-box-item"; import "../ha-generic-picker"; import type { HaGenericPicker } from "../ha-generic-picker"; import "../ha-input-helper-text"; -import type { PickerComboBoxItem } from "../ha-picker-combo-box"; +import { + NO_ITEMS_AVAILABLE_ID, + type PickerComboBoxItem, +} from "../ha-picker-combo-box"; import "../ha-sortable"; const HIDDEN_ATTRIBUTES = [ @@ -199,11 +202,7 @@ export class HaStateContentPicker extends LitElement { .value=${this._getPickerValue()} .getItems=${this._getFilteredItems} .getAdditionalItems=${this._getAdditionalItems} - .notFoundLabel=${this.hass.localize("ui.components.combo-box.no_match")} - allow-custom-value - .customValueLabel=${this.hass.localize( - "ui.components.entity.entity-state-content-picker.custom_state" - )} + .searchFn=${this._searchFn} @value-changed=${this._pickerValueChanged} >
@@ -328,7 +327,7 @@ export class HaStateContentPicker extends LitElement { (text: string): PickerComboBoxItem => ({ id: text, primary: this.hass.localize( - "ui.components.entity.entity-state-content-picker.custom_state" + "ui.components.entity.entity-state-content-picker.custom_attribute" ), secondary: `"${text}"`, search_labels: { @@ -340,10 +339,7 @@ export class HaStateContentPicker extends LitElement { }) ); - private _getFilteredItems = ( - searchString?: string, - _section?: string - ): PickerComboBoxItem[] => { + private _getFilteredItems = (): PickerComboBoxItem[] => { const stateObj = this.entityId ? this.hass.states[this.entityId] : undefined; @@ -358,11 +354,7 @@ export class HaStateContentPicker extends LitElement { ); // When editing an existing custom value, include it in the base items - if ( - currentValue && - !items.find((item) => item.id === currentValue) && - !searchString - ) { + if (currentValue && !items.find((item) => item.id === currentValue)) { filteredItems.push(this._customValueOption(currentValue)); } @@ -372,33 +364,34 @@ export class HaStateContentPicker extends LitElement { private _getAdditionalItems = ( searchString?: string ): PickerComboBoxItem[] => { - if (!searchString) { - return []; - } - - const currentValue = - this._editIndex != null ? this._value[this._editIndex] : undefined; - - // Don't add if it's the same as the current item being edited - if (currentValue && currentValue === searchString) { - return []; - } - - // Check if the search string matches an existing item const stateObj = this.entityId ? this.hass.states[this.entityId] : undefined; const items = this._getItems(this.entityId, stateObj, this.allowName); - const existingItem = items.find((item) => item.id === searchString); - // Only return custom value option if it doesn't match an existing item - if (!existingItem) { + // If the search string does not match with the id of any of the items, + // offer to add it as a custom attribute + const existingItem = items.find((item) => item.id === searchString); + if (searchString && !existingItem) { return [this._customValueOption(searchString)]; } return []; }; + private _searchFn = ( + search: string, + filteredItems: PickerComboBoxItem[], + _allItems: PickerComboBoxItem[] + ): PickerComboBoxItem[] => { + if (!search) { + return filteredItems; + } + + // Always exclude NO_ITEMS_AVAILABLE_ID (since custom values are allowed) and currentValue (the custom value being edited) + return filteredItems.filter((item) => item.id !== NO_ITEMS_AVAILABLE_ID); + }; + private async _moveItem(ev: CustomEvent) { ev.stopPropagation(); const { oldIndex, newIndex } = ev.detail; diff --git a/src/components/ha-icon-picker.ts b/src/components/ha-icon-picker.ts index 4268c65f3b..c6a1ae7ce9 100644 --- a/src/components/ha-icon-picker.ts +++ b/src/components/ha-icon-picker.ts @@ -124,9 +124,6 @@ export class HaIconPicker extends LitElement { .label=${this.label} .value=${this._value} .searchFn=${this._filterIcons} - .notFoundLabel=${this.hass?.localize( - "ui.components.icon-picker.no_match" - )} popover-placement="bottom-start" @value-changed=${this._valueChanged} > diff --git a/src/components/ha-picker-combo-box.ts b/src/components/ha-picker-combo-box.ts index ae80ae34d3..349d5c4720 100644 --- a/src/components/ha-picker-combo-box.ts +++ b/src/components/ha-picker-combo-box.ts @@ -324,7 +324,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) { }); } - if (!items.length) { + if (!items.length && !this.allowCustomValue) { items.push({ id: NO_ITEMS_AVAILABLE_ID, primary: "" }); } @@ -430,7 +430,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) { index ); - if (!filteredItems.length) { + if (!filteredItems.length && !this.allowCustomValue) { filteredItems.push({ id: NO_ITEMS_AVAILABLE_ID, primary: "" }); } diff --git a/src/translations/en.json b/src/translations/en.json index 1718e3a1a8..ca8e1811e1 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -672,8 +672,8 @@ "device_missing": "No related device" }, "add": "Add", - "custom_name": "Custom name", - "no_match": "No entities found" + "search": "Search or enter custom name", + "custom_name": "Custom name" }, "entity-attribute-picker": { "attribute": "Attribute", @@ -685,7 +685,7 @@ }, "entity-state-content-picker": { "add": "Add", - "custom_state": "Custom state" + "custom_attribute": "Custom attribute" } }, "target-picker": { @@ -772,9 +772,6 @@ "no_match": "No languages found for {term}", "no_languages": "No languages available" }, - "icon-picker": { - "no_match": "No matching icons found" - }, "tts-picker": { "tts": "Text-to-speech", "none": "None"