mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-20 02:38:53 +00:00
Generic picker warn unknown selected item (#28372)
* Add unknown item text localization to various pickers * Review
This commit is contained in:
@@ -216,6 +216,9 @@ export class HaDevicePicker extends LitElement {
|
|||||||
.getItems=${this._getItems}
|
.getItems=${this._getItems}
|
||||||
.hideClearIcon=${this.hideClearIcon}
|
.hideClearIcon=${this.hideClearIcon}
|
||||||
.valueRenderer=${valueRenderer}
|
.valueRenderer=${valueRenderer}
|
||||||
|
.unknownItemText=${this.hass.localize(
|
||||||
|
"ui.components.device-picker.unknown"
|
||||||
|
)}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
>
|
>
|
||||||
</ha-generic-picker>
|
</ha-generic-picker>
|
||||||
|
|||||||
@@ -288,10 +288,13 @@ export class HaEntityPicker extends LitElement {
|
|||||||
.hideClearIcon=${this.hideClearIcon}
|
.hideClearIcon=${this.hideClearIcon}
|
||||||
.searchFn=${this._searchFn}
|
.searchFn=${this._searchFn}
|
||||||
.valueRenderer=${this._valueRenderer}
|
.valueRenderer=${this._valueRenderer}
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
.addButtonLabel=${this.addButton
|
.addButtonLabel=${this.addButton
|
||||||
? this.hass.localize("ui.components.entity.entity-picker.add")
|
? this.hass.localize("ui.components.entity.entity-picker.add")
|
||||||
: undefined}
|
: undefined}
|
||||||
|
.unknownItemText=${this.hass.localize(
|
||||||
|
"ui.components.entity.entity-picker.unknown"
|
||||||
|
)}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
>
|
>
|
||||||
</ha-generic-picker>
|
</ha-generic-picker>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -475,6 +475,9 @@ export class HaStatisticPicker extends LitElement {
|
|||||||
.searchFn=${this._searchFn}
|
.searchFn=${this._searchFn}
|
||||||
.valueRenderer=${this._valueRenderer}
|
.valueRenderer=${this._valueRenderer}
|
||||||
.helper=${this.helper}
|
.helper=${this.helper}
|
||||||
|
.unknownItemText=${this.hass.localize(
|
||||||
|
"ui.components.statistic-picker.unknown"
|
||||||
|
)}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
>
|
>
|
||||||
</ha-generic-picker>
|
</ha-generic-picker>
|
||||||
|
|||||||
@@ -379,6 +379,9 @@ export class HaAreaPicker extends LitElement {
|
|||||||
.getAdditionalItems=${this._getAdditionalItems}
|
.getAdditionalItems=${this._getAdditionalItems}
|
||||||
.valueRenderer=${valueRenderer}
|
.valueRenderer=${valueRenderer}
|
||||||
.addButtonLabel=${this.addButtonLabel}
|
.addButtonLabel=${this.addButtonLabel}
|
||||||
|
.unknownItemText=${this.hass.localize(
|
||||||
|
"ui.components.area-picker.unknown"
|
||||||
|
)}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
>
|
>
|
||||||
</ha-generic-picker>
|
</ha-generic-picker>
|
||||||
|
|||||||
@@ -393,6 +393,9 @@ export class HaFloorPicker extends LitElement {
|
|||||||
.getAdditionalItems=${this._getAdditionalItems}
|
.getAdditionalItems=${this._getAdditionalItems}
|
||||||
.valueRenderer=${valueRenderer}
|
.valueRenderer=${valueRenderer}
|
||||||
.rowRenderer=${this._rowRenderer}
|
.rowRenderer=${this._rowRenderer}
|
||||||
|
.unknownItemText=${this.hass.localize(
|
||||||
|
"ui.components.floor-picker.unknown"
|
||||||
|
)}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
>
|
>
|
||||||
</ha-generic-picker>
|
</ha-generic-picker>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { mdiPlaylistPlus } from "@mdi/js";
|
|||||||
import { css, html, LitElement, nothing, type CSSResultGroup } from "lit";
|
import { css, html, LitElement, nothing, type CSSResultGroup } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import { tinykeys } from "tinykeys";
|
import { tinykeys } from "tinykeys";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
@@ -46,8 +47,9 @@ export class HaGenericPicker extends LitElement {
|
|||||||
@property({ attribute: "hide-clear-icon", type: Boolean })
|
@property({ attribute: "hide-clear-icon", type: Boolean })
|
||||||
public hideClearIcon = false;
|
public hideClearIcon = false;
|
||||||
|
|
||||||
|
/** To prevent lags, getItems needs to be memoized */
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public getItems?: (
|
public getItems!: (
|
||||||
searchString?: string,
|
searchString?: string,
|
||||||
section?: string
|
section?: string
|
||||||
) => (PickerComboBoxItem | string)[];
|
) => (PickerComboBoxItem | string)[];
|
||||||
@@ -107,6 +109,8 @@ export class HaGenericPicker extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: "selected-section" }) public selectedSection?: string;
|
@property({ attribute: "selected-section" }) public selectedSection?: string;
|
||||||
|
|
||||||
|
@property({ attribute: "unknown-item-text" }) public unknownItemText?: string;
|
||||||
|
|
||||||
@query(".container") private _containerElement?: HTMLDivElement;
|
@query(".container") private _containerElement?: HTMLDivElement;
|
||||||
|
|
||||||
@query("ha-picker-combo-box") private _comboBox?: HaPickerComboBox;
|
@query("ha-picker-combo-box") private _comboBox?: HaPickerComboBox;
|
||||||
@@ -156,6 +160,8 @@ export class HaGenericPicker extends LitElement {
|
|||||||
type="button"
|
type="button"
|
||||||
class=${this._opened ? "opened" : ""}
|
class=${this._opened ? "opened" : ""}
|
||||||
compact
|
compact
|
||||||
|
.unknown=${this._unknownValue(this.value, this.getItems())}
|
||||||
|
.unknownItemText=${this.unknownItemText}
|
||||||
aria-label=${ifDefined(this.label)}
|
aria-label=${ifDefined(this.label)}
|
||||||
@click=${this.open}
|
@click=${this.open}
|
||||||
@clear=${this._clear}
|
@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() {
|
private _renderHelper() {
|
||||||
return this.helper
|
return this.helper
|
||||||
? html`<ha-input-helper-text .disabled=${this.disabled}
|
? html`<ha-input-helper-text .disabled=${this.disabled}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
|||||||
@state() private _listScrolled = false;
|
@state() private _listScrolled = false;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public getItems?: (
|
public getItems!: (
|
||||||
searchString?: string,
|
searchString?: string,
|
||||||
section?: string
|
section?: string
|
||||||
) => (PickerComboBoxItem | string)[];
|
) => (PickerComboBoxItem | string)[];
|
||||||
@@ -264,11 +264,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
|||||||
this.getAdditionalItems?.(searchString) || [];
|
this.getAdditionalItems?.(searchString) || [];
|
||||||
|
|
||||||
private _getItems = () => {
|
private _getItems = () => {
|
||||||
let items = [
|
let items = [...this.getItems(this._search, this.selectedSection)];
|
||||||
...(this.getItems
|
|
||||||
? this.getItems(this._search, this.selectedSection)
|
|
||||||
: []),
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!this.sections?.length) {
|
if (!this.sections?.length) {
|
||||||
items = items.sort((entityA, entityB) =>
|
items = items.sort((entityA, entityB) =>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { consume } from "@lit/context";
|
||||||
import { mdiClose, mdiMenuDown } from "@mdi/js";
|
import { mdiClose, mdiMenuDown } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@@ -7,8 +8,10 @@ import {
|
|||||||
type CSSResultGroup,
|
type CSSResultGroup,
|
||||||
type TemplateResult,
|
type TemplateResult,
|
||||||
} from "lit";
|
} 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 { fireEvent } from "../common/dom/fire_event";
|
||||||
|
import { localizeContext } from "../data/context";
|
||||||
|
import type { HomeAssistant } from "../types";
|
||||||
import "./ha-combo-box-item";
|
import "./ha-combo-box-item";
|
||||||
import type { HaComboBoxItem } from "./ha-combo-box-item";
|
import type { HaComboBoxItem } from "./ha-combo-box-item";
|
||||||
import "./ha-icon-button";
|
import "./ha-icon-button";
|
||||||
@@ -33,6 +36,10 @@ export class HaPickerField extends LitElement {
|
|||||||
|
|
||||||
@property() public placeholder?: string;
|
@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 })
|
@property({ attribute: "hide-clear-icon", type: Boolean })
|
||||||
public hideClearIcon = false;
|
public hideClearIcon = false;
|
||||||
|
|
||||||
@@ -41,6 +48,10 @@ export class HaPickerField extends LitElement {
|
|||||||
|
|
||||||
@query("ha-combo-box-item", true) public item!: HaComboBoxItem;
|
@query("ha-combo-box-item", true) public item!: HaComboBoxItem;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: localizeContext, subscribe: true })
|
||||||
|
private localize!: HomeAssistant["localize"];
|
||||||
|
|
||||||
public async focus() {
|
public async focus() {
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
await this.item?.focus();
|
await this.item?.focus();
|
||||||
@@ -61,6 +72,12 @@ export class HaPickerField extends LitElement {
|
|||||||
${this.placeholder}
|
${this.placeholder}
|
||||||
</span>
|
</span>
|
||||||
`}
|
`}
|
||||||
|
${this.unknown
|
||||||
|
? html`<div slot="supporting-text" class="unknown">
|
||||||
|
${this.unknownItemText ||
|
||||||
|
this.localize("ui.components.combo-box.unknown_item")}
|
||||||
|
</div>`
|
||||||
|
: nothing}
|
||||||
${showClearIcon
|
${showClearIcon
|
||||||
? html`
|
? html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
@@ -142,6 +159,10 @@ export class HaPickerField extends LitElement {
|
|||||||
background-color: var(--mdc-theme-primary);
|
background-color: var(--mdc-theme-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host([unknown]) ha-combo-box-item {
|
||||||
|
background-color: var(--ha-color-fill-warning-quiet-resting);
|
||||||
|
}
|
||||||
|
|
||||||
.clear {
|
.clear {
|
||||||
margin: 0 -8px;
|
margin: 0 -8px;
|
||||||
--mdc-icon-button-size: 32px;
|
--mdc-icon-button-size: 32px;
|
||||||
@@ -156,6 +177,10 @@ export class HaPickerField extends LitElement {
|
|||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.unknown {
|
||||||
|
color: var(--ha-color-on-warning-normal);
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,6 +141,9 @@ class HaServicePicker extends LitElement {
|
|||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
this.hass.services
|
this.hass.services
|
||||||
)}
|
)}
|
||||||
|
.unknownItemText=${this.hass.localize(
|
||||||
|
"ui.components.service-picker.unknown"
|
||||||
|
)}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
>
|
>
|
||||||
</ha-generic-picker>
|
</ha-generic-picker>
|
||||||
|
|||||||
@@ -134,6 +134,9 @@ class HaUserPicker extends LitElement {
|
|||||||
.getItems=${this._getItems}
|
.getItems=${this._getItems}
|
||||||
.valueRenderer=${this._valueRenderer}
|
.valueRenderer=${this._valueRenderer}
|
||||||
.rowRenderer=${this._rowRenderer}
|
.rowRenderer=${this._rowRenderer}
|
||||||
|
.unknownItemText=${this.hass.localize(
|
||||||
|
"ui.components.user-picker.unknown"
|
||||||
|
)}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
>
|
>
|
||||||
</ha-generic-picker>
|
</ha-generic-picker>
|
||||||
|
|||||||
@@ -203,6 +203,9 @@ export class HaCategoryPicker extends SubscribeMixin(LitElement) {
|
|||||||
.getItems=${this._getItems}
|
.getItems=${this._getItems}
|
||||||
.getAdditionalItems=${this._getAdditionalItems}
|
.getAdditionalItems=${this._getAdditionalItems}
|
||||||
.valueRenderer=${valueRenderer}
|
.valueRenderer=${valueRenderer}
|
||||||
|
.unknownItemText=${this.hass.localize(
|
||||||
|
"ui.components.category-picker.unknown"
|
||||||
|
)}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
>
|
>
|
||||||
</ha-generic-picker>
|
</ha-generic-picker>
|
||||||
|
|||||||
@@ -658,7 +658,8 @@
|
|||||||
"show_entities": "Show entities",
|
"show_entities": "Show entities",
|
||||||
"new_entity": "Create a new entity",
|
"new_entity": "Create a new entity",
|
||||||
"placeholder": "Select an 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": {
|
"entity-name-picker": {
|
||||||
"types": {
|
"types": {
|
||||||
@@ -779,7 +780,8 @@
|
|||||||
"user-picker": {
|
"user-picker": {
|
||||||
"no_match": "No users found for {term}",
|
"no_match": "No users found for {term}",
|
||||||
"user": "User",
|
"user": "User",
|
||||||
"add_user": "Add user"
|
"add_user": "Add user",
|
||||||
|
"unknown": "Unknown user selected"
|
||||||
},
|
},
|
||||||
"blueprint-picker": {
|
"blueprint-picker": {
|
||||||
"select_blueprint": "Select a blueprint"
|
"select_blueprint": "Select a blueprint"
|
||||||
@@ -793,7 +795,8 @@
|
|||||||
"device": "Device",
|
"device": "Device",
|
||||||
"unnamed_device": "Unnamed device",
|
"unnamed_device": "Unnamed device",
|
||||||
"no_area": "No area",
|
"no_area": "No area",
|
||||||
"placeholder": "Select a device"
|
"placeholder": "Select a device",
|
||||||
|
"unknown": "Unknown device selected"
|
||||||
},
|
},
|
||||||
"category-picker": {
|
"category-picker": {
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
@@ -805,6 +808,7 @@
|
|||||||
"add_new": "Add new category…",
|
"add_new": "Add new category…",
|
||||||
"no_categories": "No categories available",
|
"no_categories": "No categories available",
|
||||||
"no_match": "No categories found for {term}",
|
"no_match": "No categories found for {term}",
|
||||||
|
"unknown": "Unknown category selected",
|
||||||
"add_dialog": {
|
"add_dialog": {
|
||||||
"title": "Add new category",
|
"title": "Add new category",
|
||||||
"text": "Enter the name of the new category.",
|
"text": "Enter the name of the new category.",
|
||||||
@@ -831,7 +835,8 @@
|
|||||||
"add_new": "Add new area…",
|
"add_new": "Add new area…",
|
||||||
"no_areas": "No areas available",
|
"no_areas": "No areas available",
|
||||||
"no_match": "No areas found for {term}",
|
"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": {
|
"floor-picker": {
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
@@ -841,7 +846,8 @@
|
|||||||
"add_new": "Add new floor…",
|
"add_new": "Add new floor…",
|
||||||
"no_floors": "No floors available",
|
"no_floors": "No floors available",
|
||||||
"no_match": "No floors found for {term}",
|
"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": {
|
"area-filter": {
|
||||||
"title": "Areas",
|
"title": "Areas",
|
||||||
@@ -858,7 +864,8 @@
|
|||||||
"no_match": "No statistics found for {term}",
|
"no_match": "No statistics found for {term}",
|
||||||
"no_state": "Entity without state",
|
"no_state": "Entity without state",
|
||||||
"missing_entity": "Why is my entity not listed?",
|
"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-picker": {
|
||||||
"addon": "Add-on",
|
"addon": "Add-on",
|
||||||
@@ -998,7 +1005,8 @@
|
|||||||
},
|
},
|
||||||
"service-picker": {
|
"service-picker": {
|
||||||
"action": "Action",
|
"action": "Action",
|
||||||
"no_match": "No matching actions found"
|
"no_match": "No matching actions found",
|
||||||
|
"unknown": "Unknown action selected"
|
||||||
},
|
},
|
||||||
"service-control": {
|
"service-control": {
|
||||||
"required": "This field is required",
|
"required": "This field is required",
|
||||||
@@ -1296,7 +1304,8 @@
|
|||||||
},
|
},
|
||||||
"combo-box": {
|
"combo-box": {
|
||||||
"no_match": "No matching items found",
|
"no_match": "No matching items found",
|
||||||
"no_items": "No items available"
|
"no_items": "No items available",
|
||||||
|
"unknown_item": "Unknown item"
|
||||||
},
|
},
|
||||||
"suggest_with_ai": {
|
"suggest_with_ai": {
|
||||||
"label": "Suggest",
|
"label": "Suggest",
|
||||||
|
|||||||
Reference in New Issue
Block a user