diff --git a/src/components/ha-form/ha-form-multi_select.ts b/src/components/ha-form/ha-form-multi_select.ts
new file mode 100644
index 0000000000..2f21740a14
--- /dev/null
+++ b/src/components/ha-form/ha-form-multi_select.ts
@@ -0,0 +1,157 @@
+import "@polymer/paper-checkbox/paper-checkbox";
+import "@polymer/paper-menu-button/paper-menu-button";
+import "@polymer/paper-input/paper-input";
+import "@polymer/paper-item/paper-icon-item";
+import "@polymer/paper-listbox/paper-listbox";
+import "@polymer/paper-ripple/paper-ripple";
+import {
+ customElement,
+ html,
+ LitElement,
+ property,
+ query,
+ TemplateResult,
+ CSSResult,
+ css,
+} from "lit-element";
+import { fireEvent } from "../../common/dom/fire_event";
+import {
+ HaFormElement,
+ HaFormMultiSelectData,
+ HaFormMultiSelectSchema,
+} from "./ha-form";
+
+@customElement("ha-form-multi_select")
+export class HaFormMultiSelect extends LitElement implements HaFormElement {
+ @property() public schema!: HaFormMultiSelectSchema;
+ @property() public data!: HaFormMultiSelectData;
+ @property() public label!: string;
+ @property() public suffix!: string;
+ @property() private _init = false;
+ @query("paper-menu-button") private _input?: HTMLElement;
+
+ public focus(): void {
+ if (this._input) {
+ this._input.focus();
+ }
+ }
+
+ protected render(): TemplateResult {
+ const options = Array.isArray(this.schema.options)
+ ? this.schema.options
+ : Object.entries(this.schema.options!);
+
+ return html`
+
+
+
+
this.schema.options![value] || value)
+ .join(", ")}
+ label=${this.label}
+ input-role="button"
+ input-aria-haspopup="listbox"
+ autocomplete="off"
+ >
+
+
+
+
+ ${// TS doesn't work with union array types https://github.com/microsoft/TypeScript/issues/36390
+ // @ts-ignore
+ options.map((item: string | [string, string]) => {
+ const value = this._optionValue(item);
+ return html`
+
+
+ ${this._optionLabel(item)}
+
+ `;
+ })}
+
+
+ `;
+ }
+
+ protected firstUpdated() {
+ this.updateComplete.then(() => {
+ const input = (this.shadowRoot?.querySelector("paper-input")
+ ?.inputElement as any)?.inputElement;
+ if (input) {
+ input.style.textOverflow = "ellipsis";
+ }
+ });
+ }
+
+ private _optionValue(item: string | string[]): string {
+ return Array.isArray(item) ? item[0] : item;
+ }
+
+ private _optionLabel(item: string | string[]): string {
+ return Array.isArray(item) ? item[1] || item[0] : item;
+ }
+
+ private _onSelect(ev: Event) {
+ ev.stopPropagation();
+ }
+
+ private _valueChanged(ev: CustomEvent): void {
+ if (!ev.detail.value || !this._init) {
+ // ignore first call because that is the init of the component
+ this._init = true;
+ return;
+ }
+
+ fireEvent(
+ this,
+ "value-changed",
+ {
+ value: ev.detail.value.map((element) => element.itemValue),
+ },
+ { bubbles: false }
+ );
+ }
+
+ static get styles(): CSSResult {
+ return css`
+ paper-menu-button {
+ display: block;
+ padding: 0;
+ --paper-item-icon-width: 34px;
+ }
+ paper-ripple {
+ top: 12px;
+ left: 0px;
+ bottom: 8px;
+ right: 0px;
+ }
+ paper-input {
+ text-overflow: ellipsis;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-form-multi_select": HaFormMultiSelect;
+ }
+}
diff --git a/src/components/ha-form/ha-form-select.ts b/src/components/ha-form/ha-form-select.ts
index d0a871af01..f650ac3bfd 100644
--- a/src/components/ha-form/ha-form-select.ts
+++ b/src/components/ha-form/ha-form-select.ts
@@ -5,6 +5,8 @@ import {
property,
TemplateResult,
query,
+ CSSResult,
+ css,
} from "lit-element";
import { HaFormElement, HaFormSelectData, HaFormSelectSchema } from "./ha-form";
import { fireEvent } from "../../common/dom/fire_event";
@@ -36,8 +38,10 @@ export class HaFormSelect extends LitElement implements HaFormElement {
.selected=${this.data}
@selected-item-changed=${this._valueChanged}
>
- ${this.schema.options!.map(
- (item) => html`
+ ${// TS doesn't work with union array types https://github.com/microsoft/TypeScript/issues/36390
+ // @ts-ignore
+ this.schema.options!.map(
+ (item: string | [string, string]) => html`
${this._optionLabel(item)}
@@ -48,12 +52,12 @@ export class HaFormSelect extends LitElement implements HaFormElement {
`;
}
- private _optionValue(item) {
+ private _optionValue(item: string | [string, string]) {
return Array.isArray(item) ? item[0] : item;
}
- private _optionLabel(item) {
- return Array.isArray(item) ? item[1] : item;
+ private _optionLabel(item: string | [string, string]) {
+ return Array.isArray(item) ? item[1] || item[0] : item;
}
private _valueChanged(ev: CustomEvent) {
@@ -64,6 +68,14 @@ export class HaFormSelect extends LitElement implements HaFormElement {
value: ev.detail.value.itemValue,
});
}
+
+ static get styles(): CSSResult {
+ return css`
+ paper-dropdown-menu {
+ display: block;
+ }
+ `;
+ }
}
declare global {
diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts
index 5925dee226..24102dff1a 100644
--- a/src/components/ha-form/ha-form.ts
+++ b/src/components/ha-form/ha-form.ts
@@ -12,6 +12,7 @@ import "./ha-form-integer";
import "./ha-form-float";
import "./ha-form-boolean";
import "./ha-form-select";
+import "./ha-form-multi_select";
import "./ha-form-positive_time_period_dict";
import { fireEvent } from "../../common/dom/fire_event";
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
@@ -22,6 +23,7 @@ export type HaFormSchema =
| HaFormFloatSchema
| HaFormBooleanSchema
| HaFormSelectSchema
+ | HaFormMultiSelectSchema
| HaFormTimeSchema;
export interface HaFormBaseSchema {
@@ -41,7 +43,12 @@ export interface HaFormIntegerSchema extends HaFormBaseSchema {
export interface HaFormSelectSchema extends HaFormBaseSchema {
type: "select";
- options?: string[];
+ options?: string[] | Array<[string, string]>;
+}
+
+export interface HaFormMultiSelectSchema extends HaFormBaseSchema {
+ type: "multi_select";
+ options?: { [key: string]: string } | string[] | Array<[string, string]>;
}
export interface HaFormFloatSchema extends HaFormBaseSchema {
@@ -71,6 +78,7 @@ export type HaFormData =
| HaFormFloatData
| HaFormBooleanData
| HaFormSelectData
+ | HaFormMultiSelectData
| HaFormTimeData;
export type HaFormStringData = string;
@@ -78,6 +86,7 @@ export type HaFormIntegerData = number;
export type HaFormFloatData = number;
export type HaFormBooleanData = boolean;
export type HaFormSelectData = string;
+export type HaFormMultiSelectData = string[];
export interface HaFormTimeData {
hours?: number;
minutes?: number;
diff --git a/src/data/data_entry_flow.ts b/src/data/data_entry_flow.ts
index 7f2e8ab894..dd2d09363f 100644
--- a/src/data/data_entry_flow.ts
+++ b/src/data/data_entry_flow.ts
@@ -1,3 +1,5 @@
+import { HaFormSchema } from "../components/ha-form/ha-form";
+
export interface DataEntryFlowProgressedEvent {
type: "data_entry_flow_progressed";
data: {
@@ -7,12 +9,6 @@ export interface DataEntryFlowProgressedEvent {
};
}
-export interface FieldSchema {
- name: string;
- default?: any;
- optional: boolean;
-}
-
export interface DataEntryFlowProgress {
flow_id: string;
handler: string;
@@ -27,7 +23,7 @@ export interface DataEntryFlowStepForm {
flow_id: string;
handler: string;
step_id: string;
- data_schema: FieldSchema[];
+ data_schema: HaFormSchema[];
errors: { [key: string]: string };
description_placeholders: { [key: string]: string };
}
diff --git a/src/dialogs/config-flow/show-dialog-data-entry-flow.ts b/src/dialogs/config-flow/show-dialog-data-entry-flow.ts
index 623732856a..490a6becc0 100644
--- a/src/dialogs/config-flow/show-dialog-data-entry-flow.ts
+++ b/src/dialogs/config-flow/show-dialog-data-entry-flow.ts
@@ -7,8 +7,8 @@ import {
DataEntryFlowStepForm,
DataEntryFlowStep,
DataEntryFlowStepAbort,
- FieldSchema,
} from "../../data/data_entry_flow";
+import { HaFormSchema } from "../../components/ha-form/ha-form";
export interface FlowConfig {
loadDevicesAndAreas: boolean;
@@ -45,7 +45,7 @@ export interface FlowConfig {
renderShowFormStepFieldLabel(
hass: HomeAssistant,
step: DataEntryFlowStepForm,
- field: FieldSchema
+ field: HaFormSchema
): string;
renderShowFormStepFieldError(
diff --git a/src/dialogs/config-flow/step-flow-form.ts b/src/dialogs/config-flow/step-flow-form.ts
index 172cf333a9..adf6a4ea5c 100644
--- a/src/dialogs/config-flow/step-flow-form.ts
+++ b/src/dialogs/config-flow/step-flow-form.ts
@@ -18,8 +18,10 @@ import "../../resources/ha-style";
import { HomeAssistant } from "../../types";
import { fireEvent } from "../../common/dom/fire_event";
import { configFlowContentStyles } from "./styles";
-import { DataEntryFlowStepForm, FieldSchema } from "../../data/data_entry_flow";
+import { DataEntryFlowStepForm } from "../../data/data_entry_flow";
import { FlowConfig } from "./show-dialog-data-entry-flow";
+// tslint:disable-next-line
+import { HaFormSchema } from "../../components/ha-form/ha-form";
@customElement("step-flow-form")
class StepFlowForm extends LitElement {
@@ -176,7 +178,7 @@ class StepFlowForm extends LitElement {
this._stepData = ev.detail.value;
}
- private _labelCallback = (field: FieldSchema): string =>
+ private _labelCallback = (field: HaFormSchema): string =>
this.flowConfig.renderShowFormStepFieldLabel(this.hass, this.step, field);
private _errorCallback = (error: string) =>