1
0
mirror of https://github.com/home-assistant/frontend.git synced 2025-12-19 18:28:42 +00:00

Generic picker warn unknown selected item (#28372)

* Add unknown item text localization to various pickers

* Review
This commit is contained in:
Wendelin
2025-12-09 13:24:30 +01:00
committed by GitHub
parent 74b51b77fe
commit 6322c19a45
12 changed files with 89 additions and 17 deletions

View File

@@ -216,6 +216,9 @@ export class HaDevicePicker extends LitElement {
.getItems=${this._getItems}
.hideClearIcon=${this.hideClearIcon}
.valueRenderer=${valueRenderer}
.unknownItemText=${this.hass.localize(
"ui.components.device-picker.unknown"
)}
@value-changed=${this._valueChanged}
>
</ha-generic-picker>

View File

@@ -288,10 +288,13 @@ export class HaEntityPicker extends LitElement {
.hideClearIcon=${this.hideClearIcon}
.searchFn=${this._searchFn}
.valueRenderer=${this._valueRenderer}
@value-changed=${this._valueChanged}
.addButtonLabel=${this.addButton
? this.hass.localize("ui.components.entity.entity-picker.add")
: undefined}
.unknownItemText=${this.hass.localize(
"ui.components.entity.entity-picker.unknown"
)}
@value-changed=${this._valueChanged}
>
</ha-generic-picker>
`;

View File

@@ -475,6 +475,9 @@ export class HaStatisticPicker extends LitElement {
.searchFn=${this._searchFn}
.valueRenderer=${this._valueRenderer}
.helper=${this.helper}
.unknownItemText=${this.hass.localize(
"ui.components.statistic-picker.unknown"
)}
@value-changed=${this._valueChanged}
>
</ha-generic-picker>

View File

@@ -379,6 +379,9 @@ export class HaAreaPicker extends LitElement {
.getAdditionalItems=${this._getAdditionalItems}
.valueRenderer=${valueRenderer}
.addButtonLabel=${this.addButtonLabel}
.unknownItemText=${this.hass.localize(
"ui.components.area-picker.unknown"
)}
@value-changed=${this._valueChanged}
>
</ha-generic-picker>

View File

@@ -393,6 +393,9 @@ export class HaFloorPicker extends LitElement {
.getAdditionalItems=${this._getAdditionalItems}
.valueRenderer=${valueRenderer}
.rowRenderer=${this._rowRenderer}
.unknownItemText=${this.hass.localize(
"ui.components.floor-picker.unknown"
)}
@value-changed=${this._valueChanged}
>
</ha-generic-picker>

View File

@@ -4,6 +4,7 @@ import { mdiPlaylistPlus } from "@mdi/js";
import { css, html, LitElement, nothing, type CSSResultGroup } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import memoizeOne from "memoize-one";
import { tinykeys } from "tinykeys";
import { fireEvent } from "../common/dom/fire_event";
import type { HomeAssistant } from "../types";
@@ -46,8 +47,9 @@ export class HaGenericPicker extends LitElement {
@property({ attribute: "hide-clear-icon", type: Boolean })
public hideClearIcon = false;
/** To prevent lags, getItems needs to be memoized */
@property({ attribute: false })
public getItems?: (
public getItems!: (
searchString?: string,
section?: string
) => (PickerComboBoxItem | string)[];
@@ -107,6 +109,8 @@ export class HaGenericPicker extends LitElement {
@property({ attribute: "selected-section" }) public selectedSection?: string;
@property({ attribute: "unknown-item-text" }) public unknownItemText?: string;
@query(".container") private _containerElement?: HTMLDivElement;
@query("ha-picker-combo-box") private _comboBox?: HaPickerComboBox;
@@ -156,6 +160,8 @@ export class HaGenericPicker extends LitElement {
type="button"
class=${this._opened ? "opened" : ""}
compact
.unknown=${this._unknownValue(this.value, this.getItems())}
.unknownItemText=${this.unknownItemText}
aria-label=${ifDefined(this.label)}
@click=${this.open}
@clear=${this._clear}
@@ -233,6 +239,18 @@ export class HaGenericPicker extends LitElement {
`;
}
private _unknownValue = memoizeOne(
(value?: string, items?: (PickerComboBoxItem | string)[]) => {
if (value === undefined || !items) {
return false;
}
return !items.some(
(item) => typeof item !== "string" && item.id === value
);
}
);
private _renderHelper() {
return this.helper
? html`<ha-input-helper-text .disabled=${this.disabled}

View File

@@ -80,7 +80,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
@state() private _listScrolled = false;
@property({ attribute: false })
public getItems?: (
public getItems!: (
searchString?: string,
section?: string
) => (PickerComboBoxItem | string)[];
@@ -264,11 +264,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
this.getAdditionalItems?.(searchString) || [];
private _getItems = () => {
let items = [
...(this.getItems
? this.getItems(this._search, this.selectedSection)
: []),
];
let items = [...this.getItems(this._search, this.selectedSection)];
if (!this.sections?.length) {
items = items.sort((entityA, entityB) =>

View File

@@ -1,3 +1,4 @@
import { consume } from "@lit/context";
import { mdiClose, mdiMenuDown } from "@mdi/js";
import {
css,
@@ -7,8 +8,10 @@ import {
type CSSResultGroup,
type TemplateResult,
} from "lit";
import { customElement, property, query } from "lit/decorators";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { localizeContext } from "../data/context";
import type { HomeAssistant } from "../types";
import "./ha-combo-box-item";
import type { HaComboBoxItem } from "./ha-combo-box-item";
import "./ha-icon-button";
@@ -33,6 +36,10 @@ export class HaPickerField extends LitElement {
@property() public placeholder?: string;
@property({ type: Boolean, reflect: true }) public unknown = false;
@property({ attribute: "unknown-item-text" }) public unknownItemText?: string;
@property({ attribute: "hide-clear-icon", type: Boolean })
public hideClearIcon = false;
@@ -41,6 +48,10 @@ export class HaPickerField extends LitElement {
@query("ha-combo-box-item", true) public item!: HaComboBoxItem;
@state()
@consume({ context: localizeContext, subscribe: true })
private localize!: HomeAssistant["localize"];
public async focus() {
await this.updateComplete;
await this.item?.focus();
@@ -61,6 +72,12 @@ export class HaPickerField extends LitElement {
${this.placeholder}
</span>
`}
${this.unknown
? html`<div slot="supporting-text" class="unknown">
${this.unknownItemText ||
this.localize("ui.components.combo-box.unknown_item")}
</div>`
: nothing}
${showClearIcon
? html`
<ha-icon-button
@@ -142,6 +159,10 @@ export class HaPickerField extends LitElement {
background-color: var(--mdc-theme-primary);
}
:host([unknown]) ha-combo-box-item {
background-color: var(--ha-color-fill-warning-quiet-resting);
}
.clear {
margin: 0 -8px;
--mdc-icon-button-size: 32px;
@@ -156,6 +177,10 @@ export class HaPickerField extends LitElement {
color: var(--secondary-text-color);
padding: 0 8px;
}
.unknown {
color: var(--ha-color-on-warning-normal);
}
`,
];
}

View File

@@ -141,6 +141,9 @@ class HaServicePicker extends LitElement {
this.hass.localize,
this.hass.services
)}
.unknownItemText=${this.hass.localize(
"ui.components.service-picker.unknown"
)}
@value-changed=${this._valueChanged}
>
</ha-generic-picker>

View File

@@ -134,6 +134,9 @@ class HaUserPicker extends LitElement {
.getItems=${this._getItems}
.valueRenderer=${this._valueRenderer}
.rowRenderer=${this._rowRenderer}
.unknownItemText=${this.hass.localize(
"ui.components.user-picker.unknown"
)}
@value-changed=${this._valueChanged}
>
</ha-generic-picker>

View File

@@ -203,6 +203,9 @@ export class HaCategoryPicker extends SubscribeMixin(LitElement) {
.getItems=${this._getItems}
.getAdditionalItems=${this._getAdditionalItems}
.valueRenderer=${valueRenderer}
.unknownItemText=${this.hass.localize(
"ui.components.category-picker.unknown"
)}
@value-changed=${this._valueChanged}
>
</ha-generic-picker>

View File

@@ -658,7 +658,8 @@
"show_entities": "Show entities",
"new_entity": "Create a new entity",
"placeholder": "Select an entity",
"create_helper": "Create a new {domain, select, \n undefined {} \n other {{domain} }\n } helper."
"create_helper": "Create a new {domain, select, \n undefined {} \n other {{domain} }\n } helper.",
"unknown": "Unknown entity selected"
},
"entity-name-picker": {
"types": {
@@ -779,7 +780,8 @@
"user-picker": {
"no_match": "No users found for {term}",
"user": "User",
"add_user": "Add user"
"add_user": "Add user",
"unknown": "Unknown user selected"
},
"blueprint-picker": {
"select_blueprint": "Select a blueprint"
@@ -793,7 +795,8 @@
"device": "Device",
"unnamed_device": "Unnamed device",
"no_area": "No area",
"placeholder": "Select a device"
"placeholder": "Select a device",
"unknown": "Unknown device selected"
},
"category-picker": {
"clear": "Clear",
@@ -805,6 +808,7 @@
"add_new": "Add new category…",
"no_categories": "No categories available",
"no_match": "No categories found for {term}",
"unknown": "Unknown category selected",
"add_dialog": {
"title": "Add new category",
"text": "Enter the name of the new category.",
@@ -831,7 +835,8 @@
"add_new": "Add new area…",
"no_areas": "No areas available",
"no_match": "No areas found for {term}",
"failed_create_area": "Failed to create area."
"failed_create_area": "Failed to create area.",
"unknown": "Unknown area selected"
},
"floor-picker": {
"clear": "Clear",
@@ -841,7 +846,8 @@
"add_new": "Add new floor…",
"no_floors": "No floors available",
"no_match": "No floors found for {term}",
"failed_create_floor": "Failed to create floor."
"failed_create_floor": "Failed to create floor.",
"unknown": "Unknown floor selected"
},
"area-filter": {
"title": "Areas",
@@ -858,7 +864,8 @@
"no_match": "No statistics found for {term}",
"no_state": "Entity without state",
"missing_entity": "Why is my entity not listed?",
"learn_more": "Learn more about statistics"
"learn_more": "Learn more about statistics",
"unknown": "Unknown statistic selected"
},
"addon-picker": {
"addon": "Add-on",
@@ -998,7 +1005,8 @@
},
"service-picker": {
"action": "Action",
"no_match": "No matching actions found"
"no_match": "No matching actions found",
"unknown": "Unknown action selected"
},
"service-control": {
"required": "This field is required",
@@ -1296,7 +1304,8 @@
},
"combo-box": {
"no_match": "No matching items found",
"no_items": "No items available"
"no_items": "No items available",
"unknown_item": "Unknown item"
},
"suggest_with_ai": {
"label": "Suggest",