From 4b02a116344aa4ba34a54fd4cd830ef6fb83d28d Mon Sep 17 00:00:00 2001
From: Wendelin <12148533+wendevlin@users.noreply.github.com>
Date: Fri, 19 Dec 2025 11:06:20 +0100
Subject: [PATCH] Migrate ha-selector-select to use ha-generic-picker component
(#28614)
---
.../ha-selector/ha-selector-select.ts | 118 ++++++------------
1 file changed, 39 insertions(+), 79 deletions(-)
diff --git a/src/components/ha-selector/ha-selector-select.ts b/src/components/ha-selector/ha-selector-select.ts
index 845df8c7cf..4a93bf095e 100644
--- a/src/components/ha-selector/ha-selector-select.ts
+++ b/src/components/ha-selector/ha-selector-select.ts
@@ -1,7 +1,8 @@
import { mdiDragHorizontalVariant } from "@mdi/js";
import { LitElement, css, html, nothing } from "lit";
-import { customElement, property, query } from "lit/decorators";
+import { customElement, property } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
+import memoizeOne from "memoize-one";
import { ensureArray } from "../../common/array/ensure-array";
import { fireEvent } from "../../common/dom/fire_event";
import { stopPropagation } from "../../common/dom/stop_propagation";
@@ -11,9 +12,8 @@ import type { HomeAssistant } from "../../types";
import "../chips/ha-chip-set";
import "../chips/ha-input-chip";
import "../ha-checkbox";
-import "../ha-combo-box";
-import type { HaComboBox } from "../ha-combo-box";
import "../ha-formfield";
+import "../ha-generic-picker";
import "../ha-input-helper-text";
import "../ha-list-item";
import "../ha-radio";
@@ -40,8 +40,6 @@ export class HaSelectSelector extends LitElement {
@property({ type: Boolean }) public required = true;
- @query("ha-combo-box", true) private comboBox!: HaComboBox;
-
private _itemMoved(ev: CustomEvent): void {
ev.stopPropagation();
const { oldIndex, newIndex } = ev.detail;
@@ -59,15 +57,8 @@ export class HaSelectSelector extends LitElement {
});
}
- private _filter = "";
-
protected render() {
- const options =
- this.selector.select?.options?.map((option) =>
- typeof option === "object"
- ? (option as SelectOption)
- : ({ value: option, label: option } as SelectOption)
- ) || [];
+ const options = this._getOptions(this.selector);
const translationKey = this.selector.select?.translation_key;
@@ -165,10 +156,6 @@ export class HaSelectSelector extends LitElement {
const value =
!this.value || this.value === "" ? [] : ensureArray(this.value);
- const optionItems = options.filter(
- (option) => !option.disabled && !value?.includes(option.value)
- );
-
return html`
${value?.length
? html`
@@ -212,50 +199,33 @@ export class HaSelectSelector extends LitElement {
`
: nothing}
-
+ >
`;
}
if (this.selector.select?.custom_value) {
- if (
- this.value !== undefined &&
- !Array.isArray(this.value) &&
- !options.find((option) => option.value === this.value)
- ) {
- options.unshift({ value: this.value, label: this.value });
- }
-
- const optionItems = options.filter((option) => !option.disabled);
-
return html`
-
+ allow-custom-value
+ >
`;
}
@@ -274,7 +244,7 @@ export class HaSelectSelector extends LitElement {
>
${options.map(
(item: SelectOption) => html`
- ${item.label}
`
@@ -291,6 +261,30 @@ export class HaSelectSelector extends LitElement {
: "";
}
+ private _getOptions = memoizeOne(
+ (selector: SelectSelector) =>
+ selector.select?.options?.map((option) =>
+ typeof option === "object"
+ ? (option as SelectOption)
+ : ({ value: option, label: option } as SelectOption)
+ ) || []
+ );
+
+ private _getItems = memoizeOne(
+ (options: SelectOption[], value?: string[], multiple = false) => {
+ const filteredOptions = options.filter((option) =>
+ !option.disabled && !multiple ? true : !value?.includes(option.value)
+ );
+
+ return () =>
+ filteredOptions.map((option) => ({
+ id: option.value,
+ primary: option.label,
+ sorting_label: option.label,
+ }));
+ }
+ );
+
private get _mode(): "list" | "dropdown" | "box" {
return (
this.selector.select?.mode ||
@@ -355,8 +349,6 @@ export class HaSelectSelector extends LitElement {
fireEvent(this, "value-changed", {
value,
});
- await this.updateComplete;
- this._filterChanged();
}
private _comboBoxValueChanged(ev: CustomEvent): void {
@@ -374,49 +366,17 @@ export class HaSelectSelector extends LitElement {
return;
}
- const currentValue =
- !this.value || this.value === "" ? [] : ensureArray(this.value);
+ const currentValue = !this.value ? [] : ensureArray(this.value);
if (newValue !== undefined && currentValue.includes(newValue)) {
return;
}
- setTimeout(() => {
- this._filterChanged();
- this.comboBox.setInputValue("");
- }, 0);
-
fireEvent(this, "value-changed", {
value: [...currentValue, newValue],
});
}
- private _openedChanged(ev?: CustomEvent): void {
- if (ev?.detail.value) {
- this._filterChanged();
- }
- }
-
- private _filterChanged(ev?: CustomEvent): void {
- this._filter = ev?.detail.value || "";
-
- const filteredItems = this.comboBox.items?.filter((item) => {
- const label = item.label || item.value;
- return label.toLowerCase().includes(this._filter?.toLowerCase());
- });
-
- if (
- this._filter &&
- this.selector.select?.custom_value &&
- filteredItems &&
- !filteredItems.some((item) => (item.label || item.value) === this._filter)
- ) {
- filteredItems.unshift({ label: this._filter, value: this._filter });
- }
-
- this.comboBox.filteredItems = filteredItems;
- }
-
static styles = css`
:host {
position: relative;