diff --git a/gallery/src/pages/components/ha-selector.ts b/gallery/src/pages/components/ha-selector.ts index 3a456cae66..af52d5c640 100644 --- a/gallery/src/pages/components/ha-selector.ts +++ b/gallery/src/pages/components/ha-selector.ts @@ -1,20 +1,20 @@ /* eslint-disable lit/no-template-arrow */ import "@material/mwc-button"; -import { LitElement, TemplateResult, css, html } from "lit"; +import { css, html, LitElement, TemplateResult } from "lit"; import { customElement, state } from "lit/decorators"; +import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry"; +import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry"; +import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry"; +import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor"; import "../../../../src/components/ha-selector/ha-selector"; import "../../../../src/components/ha-settings-row"; +import { BlueprintInput } from "../../../../src/data/blueprint"; +import { showDialog } from "../../../../src/dialogs/make-dialog-manager"; +import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; +import { ProvideHassElement } from "../../../../src/mixins/provide-hass-lit-mixin"; import type { HomeAssistant } from "../../../../src/types"; import "../../components/demo-black-white-row"; -import { BlueprintInput } from "../../../../src/data/blueprint"; -import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry"; -import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry"; -import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry"; -import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor"; -import { getEntity } from "../../../../src/fake_data/entity"; -import { ProvideHassElement } from "../../../../src/mixins/provide-hass-lit-mixin"; -import { showDialog } from "../../../../src/dialogs/make-dialog-manager"; const ENTITIES = [ getEntity("alarm_control_panel", "alarm", "disarmed", { @@ -202,6 +202,7 @@ const SCHEMAS: { input: { entity: { name: "Entity", selector: { entity: { multiple: true } } }, device: { name: "Device", selector: { device: { multiple: true } } }, + area: { name: "Area", selector: { area: { multiple: true } } }, }, }, ]; diff --git a/src/components/ha-areas-picker.ts b/src/components/ha-areas-picker.ts new file mode 100644 index 0000000000..fbf4a9287f --- /dev/null +++ b/src/components/ha-areas-picker.ts @@ -0,0 +1,160 @@ +import { css, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import { fireEvent } from "../common/dom/fire_event"; +import type { EntityRegistryEntry } from "../data/entity_registry"; +import { SubscribeMixin } from "../mixins/subscribe-mixin"; +import type { HomeAssistant } from "../types"; +import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker"; +import "./ha-area-picker"; + +@customElement("ha-areas-picker") +export class HaAreasPicker extends SubscribeMixin(LitElement) { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public label?: string; + + @property() public value?: string[]; + + @property() public placeholder?: string; + + @property({ type: Boolean, attribute: "no-add" }) + public noAdd?: boolean; + + /** + * Show only areas with entities from specific domains. + * @type {Array} + * @attr include-domains + */ + @property({ type: Array, attribute: "include-domains" }) + public includeDomains?: string[]; + + /** + * Show no areas with entities of these domains. + * @type {Array} + * @attr exclude-domains + */ + @property({ type: Array, attribute: "exclude-domains" }) + public excludeDomains?: string[]; + + /** + * Show only areas with entities of these device classes. + * @type {Array} + * @attr include-device-classes + */ + @property({ type: Array, attribute: "include-device-classes" }) + public includeDeviceClasses?: string[]; + + @property() public deviceFilter?: HaDevicePickerDeviceFilterFunc; + + @property() public entityFilter?: (entity: EntityRegistryEntry) => boolean; + + @property({ attribute: "picked-area-label" }) + public pickedAreaLabel?: string; + + @property({ attribute: "pick-area-label" }) + public pickAreaLabel?: string; + + @property({ type: Boolean }) public disabled?: boolean; + + protected render(): TemplateResult { + if (!this.hass) { + return html``; + } + + const currentAreas = this._currentAreas; + return html` + ${currentAreas.map( + (area) => html` +
+ +
+ ` + )} +
+ +
+ `; + } + + private get _currentAreas(): string[] { + return this.value || []; + } + + private async _updateAreas(areas) { + this.value = areas; + + fireEvent(this, "value-changed", { + value: areas, + }); + } + + private _areaChanged(ev: CustomEvent) { + ev.stopPropagation(); + const curValue = (ev.currentTarget as any).curValue; + const newValue = ev.detail.value; + if (newValue === curValue) { + return; + } + const currentAreas = this._currentAreas; + if (!newValue || currentAreas.includes(newValue)) { + this._updateAreas(currentAreas.filter((ent) => ent !== curValue)); + return; + } + this._updateAreas( + currentAreas.map((ent) => (ent === curValue ? newValue : ent)) + ); + } + + private _addArea(ev: CustomEvent) { + ev.stopPropagation(); + + const toAdd = ev.detail.value; + if (!toAdd) { + return; + } + (ev.currentTarget as any).value = ""; + const currentAreas = this._currentAreas; + if (currentAreas.includes(toAdd)) { + return; + } + + this._updateAreas([...currentAreas, toAdd]); + } + + static override styles = css` + div { + margin-top: 8px; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-areas-picker": HaAreasPicker; + } +} diff --git a/src/components/ha-selector/ha-selector-area.ts b/src/components/ha-selector/ha-selector-area.ts index 704c79282a..e6926c9701 100644 --- a/src/components/ha-selector/ha-selector-area.ts +++ b/src/components/ha-selector/ha-selector-area.ts @@ -6,6 +6,7 @@ import { EntityRegistryEntry } from "../../data/entity_registry"; import { AreaSelector } from "../../data/selector"; import { HomeAssistant } from "../../types"; import "../ha-area-picker"; +import "../ha-areas-picker"; @customElement("ha-selector-area") export class HaAreaSelector extends LitElement { @@ -38,21 +39,43 @@ export class HaAreaSelector extends LitElement { } protected render() { - return html``; + if (!this.selector.area.multiple) { + return html` + + `; + } + + return html` + + `; } private _filterEntities = (entity: EntityRegistryEntry): boolean => { diff --git a/src/data/selector.ts b/src/data/selector.ts index ae985f1343..076fa78aa3 100644 --- a/src/data/selector.ts +++ b/src/data/selector.ts @@ -46,6 +46,7 @@ export interface AreaSelector { manufacturer?: DeviceSelector["device"]["manufacturer"]; model?: DeviceSelector["device"]["model"]; }; + multiple?: boolean; }; }