Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/common/dom/stop_propagation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const stopPropagation = (ev) => ev.stopPropagation();
2 changes: 1 addition & 1 deletion src/data/input-select.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HomeAssistant } from "../types";

export const setOption = (
export const setInputSelectOption = (
hass: HomeAssistant,
entity: string,
option: string
Expand Down
75 changes: 50 additions & 25 deletions src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
customElement,
PropertyValues,
} from "lit-element";
import { repeat } from "lit-html/directives/repeat";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";

Expand All @@ -18,11 +17,12 @@ import "../components/hui-warning";

import computeStateName from "../../../common/entity/compute_state_name";

import { HomeAssistant } from "../../../types";
import { HomeAssistant, InputSelectEntity } from "../../../types";
import { EntityRow, EntityConfig } from "./types";
import { setOption } from "../../../data/input-select";
import { setInputSelectOption } from "../../../data/input-select";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import { forwardHaptic } from "../../../util/haptics";
import { stopPropagation } from "../../../common/dom/stop_propagation";

@customElement("hui-input-select-entity-row")
class HuiInputSelectEntityRow extends LitElement implements EntityRow {
Expand All @@ -47,7 +47,9 @@ class HuiInputSelectEntityRow extends LitElement implements EntityRow {
return html``;
}

const stateObj = this.hass.states[this._config.entity];
const stateObj = this.hass.states[this._config.entity] as
| InputSelectEntity
| undefined;

if (!stateObj) {
return html`
Expand All @@ -64,26 +66,43 @@ class HuiInputSelectEntityRow extends LitElement implements EntityRow {
return html`
<state-badge .stateObj="${stateObj}"></state-badge>
<ha-paper-dropdown-menu
selected-item-label="${stateObj.state}"
@selected-item-label-changed="${this._selectedChanged}"
label="${this._config.name || computeStateName(stateObj)}"
.label=${this._config.name || computeStateName(stateObj)}
.value=${stateObj.state}
@iron-select=${this._selectedChanged}
@click=${stopPropagation}
>
<paper-listbox
slot="dropdown-content"
selected="${stateObj.attributes.options.indexOf(stateObj.state)}"
>
${repeat(
stateObj.attributes.options,
(option) =>
html`
<paper-item>${option}</paper-item>
`
<paper-listbox slot="dropdown-content">
${stateObj.attributes.options.map(
(option) => html`
<paper-item>${option}</paper-item>
`
)}
</paper-listbox>
</ha-paper-dropdown-menu>
`;
}

protected updated(changedProps: PropertyValues) {
super.updated(changedProps);

if (!this.hass || !this._config) {
return;
}

const stateObj = this.hass.states[this._config.entity] as
| InputSelectEntity
| undefined;

if (!stateObj) {
return;
}

// Update selected after rendering the items or else it won't work in Firefox
this.shadowRoot!.querySelector(
"paper-listbox"
)!.selected = stateObj.attributes.options.indexOf(stateObj.state);
}

static get styles(): CSSResult {
return css`
:host {
Expand All @@ -94,22 +113,28 @@ class HuiInputSelectEntityRow extends LitElement implements EntityRow {
margin-left: 16px;
flex: 1;
}

paper-item {
cursor: pointer;
min-width: 200px;
}
`;
}

private _selectedChanged(ev): void {
forwardHaptic(this, "light");
// Selected Option will transition to '' before transitioning to new value
const stateObj = this.hass!.states[this._config!.entity];
if (
!ev.target.selectedItem ||
ev.target.selectedItem.innerText === "" ||
ev.target.selectedItem.innerText === stateObj.state
) {
const option = ev.detail.item.innerText;
if (option === stateObj.state) {
return;
}

setOption(this.hass!, stateObj.entity_id, ev.target.selectedItem.innerText);
forwardHaptic(this, "light");

setInputSelectOption(
this.hass!,
stateObj.entity_id,
ev.target.selectedItem.innerText
);
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/polymer-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ export interface PolymerChangedEvent<T> extends Event {
};
}

export interface PolymerIronSelectEvent<T> extends Event {
detail: {
item: T;
};
}

declare global {
// for fire event
interface HASSDomEvents {
Expand Down
96 changes: 0 additions & 96 deletions src/state-summary/state-card-input_select.js

This file was deleted.

96 changes: 96 additions & 0 deletions src/state-summary/state-card-input_select.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {
LitElement,
customElement,
TemplateResult,
html,
CSSResult,
css,
property,
PropertyValues,
} from "lit-element";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
import "@polymer/paper-item/paper-item";
// tslint:disable-next-line: no-duplicate-imports
import { PaperItemElement } from "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";

import "../components/entity/state-badge";

import computeStateName from "../common/entity/compute_state_name";
import { HomeAssistant, InputSelectEntity } from "../types";
import { setInputSelectOption } from "../data/input-select";
import { PolymerIronSelectEvent } from "../polymer-types";
import { stopPropagation } from "../common/dom/stop_propagation";

@customElement("state-card-input_select")
class StateCardInputSelect extends LitElement {
@property() public hass!: HomeAssistant;
@property() public stateObj!: InputSelectEntity;

protected render(): TemplateResult | void {
return html`
<state-badge .stateObj=${this.stateObj}></state-badge>
<paper-dropdown-menu-light
.label=${computeStateName(this.stateObj)}
.value="${this.stateObj.state}"
@iron-select=${this._selectedOptionChanged}
@click=${stopPropagation}
>
<paper-listbox slot="dropdown-content">
${this.stateObj.attributes.options.map(
(option) => html`
<paper-item>${option}</paper-item>
`
)}
</paper-listbox>
</paper-dropdown-menu-light>
`;
}

protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
// Update selected after rendering the items or else it won't work in Firefox
this.shadowRoot!.querySelector(
"paper-listbox"
)!.selected = this.stateObj.attributes.options.indexOf(this.stateObj.state);
}

private async _selectedOptionChanged(
ev: PolymerIronSelectEvent<PaperItemElement>
) {
const option = ev.detail.item.innerText;
if (option === this.stateObj.state) {
return;
}
await setInputSelectOption(this.hass, this.stateObj.entity_id, option);
}

static get styles(): CSSResult {
return css`
:host {
display: block;
}

state-badge {
float: left;
margin-top: 10px;
}

paper-dropdown-menu-light {
display: block;
margin-left: 53px;
}

paper-item {
cursor: pointer;
min-width: 200px;
}
`;
}
}

declare global {
interface HTMLElementTagNameMap {
"state-card-input_select": StateCardInputSelect;
}
}
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ export type CameraEntity = HassEntityBase & {
};
};

export type InputSelectEntity = HassEntityBase & {
attributes: HassEntityAttributeBase & {
options: string[];
};
};

export interface Route {
prefix: string;
path: string;
Expand Down