diff --git a/src/panels/config/helpers/dialog-helper-detail.ts b/src/panels/config/helpers/dialog-helper-detail.ts index 7bf463fb0e..73612dd096 100644 --- a/src/panels/config/helpers/dialog-helper-detail.ts +++ b/src/panels/config/helpers/dialog-helper-detail.ts @@ -1,13 +1,12 @@ import "@material/mwc-button/mwc-button"; -import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item-base"; import "@lrnwebcomponents/simple-tooltip/simple-tooltip"; import type { CSSResultGroup, TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; +import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { dynamicElement } from "../../../common/dom/dynamic-element-directive"; -import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event"; import "../../../components/ha-circular-progress"; import { createCloseHeading } from "../../../components/ha-dialog"; import "../../../components/ha-list-item"; @@ -26,13 +25,14 @@ import { import { createSchedule } from "../../../data/schedule"; import { createTimer } from "../../../data/timer"; import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; -import { haStyleDialog } from "../../../resources/styles"; +import { haStyleDialog, haStyleScrollbar } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; import { brandsUrl } from "../../../util/brands-url"; import type { Helper, HelperDomain } from "./const"; import { isHelperDomain } from "./const"; import type { ShowDialogHelperDetailParams } from "./show-dialog-helper-detail"; import { fireEvent } from "../../../common/dom/fire_event"; +import { stringCompare } from "../../../common/string/compare"; type HelperCreators = Record< HelperDomain, @@ -46,6 +46,7 @@ type HelperCreators = Record< params: any ) => Promise; import: () => Promise; + alias?: string[]; } >; @@ -53,6 +54,7 @@ const HELPERS: HelperCreators = { input_boolean: { create: createInputBoolean, import: () => import("./forms/ha-input_boolean-form"), + alias: ["switch", "toggle"], }, input_button: { create: createInputButton, @@ -73,6 +75,7 @@ const HELPERS: HelperCreators = { input_select: { create: createInputSelect, import: () => import("./forms/ha-input_select-form"), + alias: ["select", "dropdown"], }, counter: { create: createCounter, @@ -81,6 +84,7 @@ const HELPERS: HelperCreators = { timer: { create: createTimer, import: () => import("./forms/ha-timer-form"), + alias: ["countdown"], }, schedule: { create: createSchedule, @@ -108,6 +112,8 @@ export class DialogHelperDetail extends LitElement { @state() private _loading = false; + @state() private _filter?: string; + private _params?: ShowDialogHelperDetailParams; public async showDialog(params: ShowDialogHelperDetailParams): Promise { @@ -172,24 +178,24 @@ export class DialogHelperDetail extends LitElement { indeterminate >`; } else { - const items: [string, string][] = []; - - for (const helper of Object.keys(HELPERS) as (keyof typeof HELPERS)[]) { - items.push([ - helper, - this.hass.localize(`ui.panel.config.helpers.types.${helper}`) || - helper, - ]); - } - - for (const domain of this._helperFlows) { - items.push([domain, domainToName(this.hass.localize, domain)]); - } - - items.sort((a, b) => a[1].localeCompare(b[1])); + const items = this._filterHelpers( + HELPERS, + this._helperFlows, + this._filter + ); content = html` + { + const items: [string, string][] = []; + + for (const helper of Object.keys( + predefinedHelpers + ) as (keyof typeof predefinedHelpers)[]) { + items.push([ + helper, + this.hass.localize(`ui.panel.config.helpers.types.${helper}`) || + helper, + ]); + } + + if (flowHelpers) { + for (const domain of flowHelpers) { + items.push([domain, domainToName(this.hass.localize, domain)]); + } + } + + return items + .filter(([domain, label]) => { + if (filter) { + const lowerFilter = filter.toLowerCase(); + return ( + label.toLowerCase().includes(lowerFilter) || + domain.toLowerCase().includes(lowerFilter) || + (predefinedHelpers[domain as HelperDomain]?.alias || []).some( + (alias) => alias.toLowerCase().includes(lowerFilter) + ) + ); + } + return true; + }) + .sort((a, b) => stringCompare(a[1], b[1], this.hass.locale.language)); + } + ); + + private async _filterChanged(e) { + this._filter = e.detail.value; + } + private _valueChanged(ev: CustomEvent): void { this._item = ev.detail.value; } @@ -303,13 +355,8 @@ export class DialogHelperDetail extends LitElement { } } - private async _domainPicked( - ev: CustomEvent - ): Promise { - if (!shouldHandleRequestSelectedEvent(ev)) { - return; - } - const domain = (ev.currentTarget! as any).domain; + private async _domainPicked(ev): Promise { + const domain = ev.target.closest("ha-list-item").domain; if (domain in HELPERS) { this._loading = true; @@ -343,6 +390,7 @@ export class DialogHelperDetail extends LitElement { static get styles(): CSSResultGroup { return [ + haStyleScrollbar, haStyleDialog, css` ha-dialog.button-left { @@ -364,6 +412,18 @@ export class DialogHelperDetail extends LitElement { .form { padding: 24px; } + search-input { + display: block; + margin: 16px 16px 0; + } + mwc-list { + height: calc(60vh - 184px); + } + @media all and (max-width: 450px), all and (max-height: 500px) { + mwc-list { + height: calc(100vh - 184px); + } + } `, ]; } diff --git a/src/translations/en.json b/src/translations/en.json index d2b64c37b8..7e530420c2 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4936,6 +4936,7 @@ "rename_input_label": "Entry name", "search": "Search integrations", "search_brand": "Search for a brand name", + "search_helper": "Search for a helper", "add_zwave_js_device": "Add Z-Wave device", "add_zha_device": "Add Zigbee device", "add_matter_device": "Add Matter device",