Put item at the top of picker result if there is an exact match with entity id (#25625)

This commit is contained in:
Paul Bottein 2025-05-28 22:33:42 +02:00 committed by Bram Kragten
parent 139c8b3702
commit 48a3e1fd63
4 changed files with 67 additions and 5 deletions

View File

@ -23,7 +23,10 @@ import type { HomeAssistant } from "../../types";
import "../ha-combo-box-item"; import "../ha-combo-box-item";
import "../ha-generic-picker"; import "../ha-generic-picker";
import type { HaGenericPicker } from "../ha-generic-picker"; import type { HaGenericPicker } from "../ha-generic-picker";
import type { PickerComboBoxItem } from "../ha-picker-combo-box"; import type {
PickerComboBoxItem,
PickerComboBoxSearchFn,
} from "../ha-picker-combo-box";
import type { PickerValueRenderer } from "../ha-picker-field"; import type { PickerValueRenderer } from "../ha-picker-field";
import "../ha-svg-icon"; import "../ha-svg-icon";
import "./state-badge"; import "./state-badge";
@ -406,6 +409,7 @@ export class HaEntityPicker extends LitElement {
.getItems=${this._getItems} .getItems=${this._getItems}
.getAdditionalItems=${this._getAdditionalItems} .getAdditionalItems=${this._getAdditionalItems}
.hideClearIcon=${this.hideClearIcon} .hideClearIcon=${this.hideClearIcon}
.searchFn=${this._searchFn}
.valueRenderer=${this._valueRenderer} .valueRenderer=${this._valueRenderer}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
> >
@ -413,6 +417,23 @@ export class HaEntityPicker extends LitElement {
`; `;
} }
private _searchFn: PickerComboBoxSearchFn<EntityComboBoxItem> = (
search,
filteredItems
) => {
// If there is exact match for entity id, put it first
const index = filteredItems.findIndex(
(item) => item.stateObj?.entity_id === search
);
if (index === -1) {
return filteredItems;
}
const [exactMatch] = filteredItems.splice(index, 1);
filteredItems.unshift(exactMatch);
return filteredItems;
};
public async open() { public async open() {
await this.updateComplete; await this.updateComplete;
await this._picker?.open(); await this._picker?.open();

View File

@ -25,7 +25,10 @@ import "../ha-generic-picker";
import type { HaGenericPicker } from "../ha-generic-picker"; import type { HaGenericPicker } from "../ha-generic-picker";
import "../ha-icon-button"; import "../ha-icon-button";
import "../ha-input-helper-text"; import "../ha-input-helper-text";
import type { PickerComboBoxItem } from "../ha-picker-combo-box"; import type {
PickerComboBoxItem,
PickerComboBoxSearchFn,
} from "../ha-picker-combo-box";
import type { PickerValueRenderer } from "../ha-picker-field"; import type { PickerValueRenderer } from "../ha-picker-field";
import "../ha-svg-icon"; import "../ha-svg-icon";
import "./state-badge"; import "./state-badge";
@ -470,6 +473,7 @@ export class HaStatisticPicker extends LitElement {
.getItems=${this._getItems} .getItems=${this._getItems}
.getAdditionalItems=${this._getAdditionalItems} .getAdditionalItems=${this._getAdditionalItems}
.hideClearIcon=${this.hideClearIcon} .hideClearIcon=${this.hideClearIcon}
.searchFn=${this._searchFn}
.valueRenderer=${this._valueRenderer} .valueRenderer=${this._valueRenderer}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
> >
@ -477,6 +481,24 @@ export class HaStatisticPicker extends LitElement {
`; `;
} }
private _searchFn: PickerComboBoxSearchFn<StatisticComboBoxItem> = (
search,
filteredItems
) => {
// If there is exact match for entity id or statistic id, put it first
const index = filteredItems.findIndex(
(item) =>
item.stateObj?.entity_id === search || item.statistic_id === search
);
if (index === -1) {
return filteredItems;
}
const [exactMatch] = filteredItems.splice(index, 1);
filteredItems.unshift(exactMatch);
return filteredItems;
};
private _valueChanged(ev: ValueChangedEvent<string>) { private _valueChanged(ev: ValueChangedEvent<string>) {
ev.stopPropagation(); ev.stopPropagation();
const value = ev.detail.value; const value = ev.detail.value;

View File

@ -12,6 +12,7 @@ import "./ha-picker-combo-box";
import type { import type {
HaPickerComboBox, HaPickerComboBox,
PickerComboBoxItem, PickerComboBoxItem,
PickerComboBoxSearchFn,
} from "./ha-picker-combo-box"; } from "./ha-picker-combo-box";
import "./ha-picker-field"; import "./ha-picker-field";
import type { HaPickerField, PickerValueRenderer } from "./ha-picker-field"; import type { HaPickerField, PickerValueRenderer } from "./ha-picker-field";
@ -57,6 +58,9 @@ export class HaGenericPicker extends LitElement {
@property({ attribute: false }) @property({ attribute: false })
public valueRenderer?: PickerValueRenderer; public valueRenderer?: PickerValueRenderer;
@property({ attribute: false })
public searchFn?: PickerComboBoxSearchFn<PickerComboBoxItem>;
@property({ attribute: "not-found-label", type: String }) @property({ attribute: "not-found-label", type: String })
public notFoundLabel?: string; public notFoundLabel?: string;
@ -102,6 +106,7 @@ export class HaGenericPicker extends LitElement {
.notFoundLabel=${this.notFoundLabel} .notFoundLabel=${this.notFoundLabel}
.getItems=${this.getItems} .getItems=${this.getItems}
.getAdditionalItems=${this.getAdditionalItems} .getAdditionalItems=${this.getAdditionalItems}
.searchFn=${this.searchFn}
></ha-picker-combo-box> ></ha-picker-combo-box>
`} `}
</div> </div>

View File

@ -49,6 +49,12 @@ const DEFAULT_ROW_RENDERER: ComboBoxLitRenderer<PickerComboBoxItem> = (
</ha-combo-box-item> </ha-combo-box-item>
`; `;
export type PickerComboBoxSearchFn<T extends PickerComboBoxItem> = (
search: string,
filteredItems: T[],
allItems: T[]
) => T[];
@customElement("ha-picker-combo-box") @customElement("ha-picker-combo-box")
export class HaPickerComboBox extends LitElement { export class HaPickerComboBox extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@ -84,6 +90,9 @@ export class HaPickerComboBox extends LitElement {
@property({ attribute: "not-found-label", type: String }) @property({ attribute: "not-found-label", type: String })
public notFoundLabel?: string; public notFoundLabel?: string;
@property({ attribute: false })
public searchFn?: PickerComboBoxSearchFn<PickerComboBoxItem>;
@state() private _opened = false; @state() private _opened = false;
@query("ha-combo-box", true) public comboBox!: HaComboBox; @query("ha-combo-box", true) public comboBox!: HaComboBox;
@ -237,6 +246,7 @@ export class HaPickerComboBox extends LitElement {
const fuse = new HaFuse(this._items, { shouldSort: false }, index); const fuse = new HaFuse(this._items, { shouldSort: false }, index);
const results = fuse.multiTermsSearch(searchString); const results = fuse.multiTermsSearch(searchString);
let filteredItems = this._items as PickerComboBoxItem[];
if (results) { if (results) {
const items = results.map((result) => result.item); const items = results.map((result) => result.item);
if (items.length === 0) { if (items.length === 0) {
@ -246,10 +256,14 @@ export class HaPickerComboBox extends LitElement {
} }
const additionalItems = this._getAdditionalItems(searchString); const additionalItems = this._getAdditionalItems(searchString);
items.push(...additionalItems); items.push(...additionalItems);
target.filteredItems = items; filteredItems = items;
} else {
target.filteredItems = this._items;
} }
if (this.searchFn) {
filteredItems = this.searchFn(searchString, filteredItems, this._items);
}
target.filteredItems = filteredItems;
} }
private _setValue(value: string | undefined) { private _setValue(value: string | undefined) {