Files
frontend/src/components/ha-generic-picker.ts

193 lines
5.5 KiB
TypeScript

import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import type { ComboBoxLightOpenedChangedEvent } from "@vaadin/combo-box/vaadin-combo-box-light";
import { css, html, LitElement, nothing, type CSSResultGroup } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { fireEvent } from "../common/dom/fire_event";
import type { HomeAssistant } from "../types";
import "./ha-combo-box-item";
import "./ha-icon-button";
import "./ha-input-helper-text";
import "./ha-picker-combo-box";
import type {
HaPickerComboBox,
PickerComboBoxItem,
PickerComboBoxSearchFn,
} from "./ha-picker-combo-box";
import "./ha-picker-field";
import type { HaPickerField, PickerValueRenderer } from "./ha-picker-field";
import "./ha-svg-icon";
@customElement("ha-generic-picker")
export class HaGenericPicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
// eslint-disable-next-line lit/no-native-attributes
@property({ type: Boolean }) public autofocus = false;
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public required = false;
@property({ type: Boolean, attribute: "allow-custom-value" })
public allowCustomValue;
@property() public label?: string;
@property() public value?: string;
@property() public helper?: string;
@property() public placeholder?: string;
@property({ type: String, attribute: "search-label" })
public searchLabel?: string;
@property({ attribute: "hide-clear-icon", type: Boolean })
public hideClearIcon = false;
@property({ attribute: false, type: Array })
public getItems?: () => PickerComboBoxItem[];
@property({ attribute: false, type: Array })
public getAdditionalItems?: (searchString?: string) => PickerComboBoxItem[];
@property({ attribute: false })
public rowRenderer?: ComboBoxLitRenderer<PickerComboBoxItem>;
@property({ attribute: false })
public valueRenderer?: PickerValueRenderer;
@property({ attribute: false })
public searchFn?: PickerComboBoxSearchFn<PickerComboBoxItem>;
@property({ attribute: "not-found-label", type: String })
public notFoundLabel?: string;
@query("ha-picker-field") private _field?: HaPickerField;
@query("ha-picker-combo-box") private _comboBox?: HaPickerComboBox;
@state() private _opened = false;
protected render() {
return html`
${this.label
? html`<label ?disabled=${this.disabled}>${this.label}</label>`
: nothing}
<div class="container">
${!this._opened
? html`
<ha-picker-field
type="button"
compact
aria-label=${ifDefined(this.label)}
@click=${this.open}
@clear=${this._clear}
.placeholder=${this.placeholder}
.value=${this.value}
.required=${this.required}
.disabled=${this.disabled}
.hideClearIcon=${this.hideClearIcon}
.valueRenderer=${this.valueRenderer}
>
</ha-picker-field>
`
: html`
<ha-picker-combo-box
.hass=${this.hass}
.autofocus=${this.autofocus}
.allowCustomValue=${this.allowCustomValue}
.label=${this.searchLabel ??
this.hass.localize("ui.common.search")}
.value=${this.value}
hide-clear-icon
@opened-changed=${this._openedChanged}
@value-changed=${this._valueChanged}
.rowRenderer=${this.rowRenderer}
.notFoundLabel=${this.notFoundLabel}
.getItems=${this.getItems}
.getAdditionalItems=${this.getAdditionalItems}
.searchFn=${this.searchFn}
></ha-picker-combo-box>
`}
</div>
${this._renderHelper()}
`;
}
private _renderHelper() {
return this.helper
? html`<ha-input-helper-text .disabled=${this.disabled}
>${this.helper}</ha-input-helper-text
>`
: nothing;
}
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
const value = ev.detail.value;
if (!value) {
return;
}
fireEvent(this, "value-changed", { value });
}
private _clear(e) {
e.stopPropagation();
this._setValue(undefined);
}
private _setValue(value: string | undefined) {
this.value = value;
fireEvent(this, "value-changed", { value });
}
public async open() {
if (this.disabled) {
return;
}
this._opened = true;
await this.updateComplete;
this._comboBox?.focus();
this._comboBox?.open();
}
private async _openedChanged(ev: ComboBoxLightOpenedChangedEvent) {
const opened = ev.detail.value;
if (this._opened && !opened) {
this._opened = false;
await this.updateComplete;
this._field?.focus();
}
}
static get styles(): CSSResultGroup {
return [
css`
.container {
position: relative;
display: block;
}
label[disabled] {
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.6));
}
label {
display: block;
margin: 0 0 8px;
}
ha-input-helper-text {
display: block;
margin: 8px 0 0;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-generic-picker": HaGenericPicker;
}
}