karwosts 2025-06-16 10:09:07 +02:00 committed by GitHub
commit 793f485a95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 179 additions and 3 deletions

View File

@ -20,6 +20,8 @@ class HaEntityStatePicker extends LitElement {
@property({ attribute: false }) public extraOptions?: any[];
@property({ attribute: false }) public excludeOptions?: string[];
// eslint-disable-next-line lit/no-native-attributes
@property({ type: Boolean }) public autofocus = false;
@ -49,7 +51,8 @@ class HaEntityStatePicker extends LitElement {
(changedProps.has("_opened") && this._opened) ||
changedProps.has("entityId") ||
changedProps.has("attribute") ||
changedProps.has("extraOptions")
changedProps.has("extraOptions") ||
changedProps.has("excludeOptions")
) {
const stateObj = this.entityId
? this.hass.states[this.entityId]
@ -68,7 +71,7 @@ class HaEntityStatePicker extends LitElement {
),
}))
: []),
];
].filter((item) => !(this.excludeOptions || []).includes(item.value));
}
}

View File

@ -0,0 +1,145 @@
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { keyed } from "lit/directives/keyed";
import { repeat } from "lit/directives/repeat";
import { fireEvent } from "../../common/dom/fire_event";
import { ensureArray } from "../../common/array/ensure-array";
import type { HomeAssistant } from "../../types";
import "./ha-entity-state-picker";
@customElement("ha-entity-states-picker")
export class HaEntityStatesPicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public entityId?: string;
@property() public attribute?: string;
@property({ attribute: false }) public extraOptions?: any[];
@property({ type: Boolean, attribute: "allow-custom-value" })
public allowCustomValue;
@property() public label?: string;
@property({ type: Array }) public value?: string[];
@property() public helper?: string;
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public required = false;
private _keys: string[] = [];
private _getKey(index: number) {
if (!this._keys[index]) {
this._keys[index] = Math.random().toString();
}
return this._keys[index];
}
protected willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps);
if (changedProps.has("value")) {
this.value = ensureArray(this.value);
}
}
protected render() {
if (!this.hass) {
return nothing;
}
const value = this.value || [];
return html`
${repeat(
value,
(_state, index) => this._getKey(index),
(state, index) => html`
<div>
<ha-entity-state-picker
.index=${index}
.hass=${this.hass}
.entityId=${this.entityId}
.attribute=${this.attribute}
.extraOptions=${this.extraOptions}
.excludeOptions=${value.filter((v) => v !== state)}
.allowCustomValue=${this.allowCustomValue}
.label=${this.label}
.value=${state}
.disabled=${this.disabled}
.helper=${this.disabled && index === value.length - 1
? this.helper
: undefined}
@value-changed=${this._valueChanged}
></ha-entity-state-picker>
</div>
`
)}
<div>
${this.disabled && value.length
? nothing
: keyed(
value.length,
html`<ha-entity-state-picker
.hass=${this.hass}
.entityId=${this.entityId}
.attribute=${this.attribute}
.extraOptions=${this.extraOptions}
.excludeOptions=${value}
.allowCustomValue=${this.allowCustomValue}
.label=${this.label}
.helper=${this.helper}
.disabled=${this.disabled}
.required=${this.required && !value.length}
@value-changed=${this._addValue}
></ha-entity-state-picker>`
)}
</div>
`;
}
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
const newState = ev.detail.value;
const newValue = [...this.value!];
const index = (ev.currentTarget as any)?.index;
if (!index) {
return;
}
if (newState === undefined) {
newValue.splice(index, 1);
this._keys.splice(index, 1);
fireEvent(this, "value-changed", {
value: newValue,
});
return;
}
newValue[index] = newState;
fireEvent(this, "value-changed", {
value: newValue,
});
}
private _addValue(ev: CustomEvent) {
ev.stopPropagation();
fireEvent(this, "value-changed", {
value: [...(this.value || []), ev.detail.value],
});
}
static override styles = css`
div {
margin-top: 8px;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-entity-states-picker": HaEntityStatesPicker;
}
}

View File

@ -208,6 +208,7 @@ export class HaComboBox extends LitElement {
aria-label=${ifDefined(this.hass?.localize("ui.common.clear"))}
class="clear-button"
.path=${mdiClose}
?disabled=${this.disabled}
@click=${this._clearValue}
></ha-svg-icon>`
: ""}
@ -386,7 +387,8 @@ export class HaComboBox extends LitElement {
:host([opened]) .toggle-button {
color: var(--primary-color);
}
.toggle-button[disabled] {
.toggle-button[disabled],
.clear-button[disabled] {
color: var(--disabled-text-color);
pointer-events: none;
}

View File

@ -106,6 +106,8 @@ export const computeInitialHaFormData = (
data[field.name] = [];
} else if ("media" in selector || "target" in selector) {
data[field.name] = {};
} else if ("state" in selector) {
data[field.name] = selector.state?.multiple ? [] : "";
} else {
throw new Error(
`Selector ${Object.keys(selector)[0]} not supported in initial form data`

View File

@ -112,6 +112,10 @@ const SELECTOR_SCHEMAS = {
name: "entity_id",
selector: { entity: {} },
},
{
name: "multiple",
selector: { boolean: {} },
},
] as const,
target: [] as const,
template: [] as const,

View File

@ -4,6 +4,7 @@ import type { StateSelector } from "../../data/selector";
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../types";
import "../entity/ha-entity-state-picker";
import "../entity/ha-entity-states-picker";
@customElement("ha-selector-state")
export class HaSelectorState extends SubscribeMixin(LitElement) {
@ -27,6 +28,24 @@ export class HaSelectorState extends SubscribeMixin(LitElement) {
};
protected render() {
if (this.selector.state?.multiple) {
return html`
<ha-entity-states-picker
.hass=${this.hass}
.entityId=${this.selector.state?.entity_id ||
this.context?.filter_entity}
.attribute=${this.selector.state?.attribute ||
this.context?.filter_attribute}
.extraOptions=${this.selector.state?.extra_options}
.value=${this.value}
.label=${this.label}
.helper=${this.helper}
.disabled=${this.disabled}
.required=${this.required}
allow-custom-value
></ha-entity-states-picker>
`;
}
return html`
<ha-entity-state-picker
.hass=${this.hass}

View File

@ -380,6 +380,7 @@ export interface StateSelector {
extra_options?: { label: string; value: any }[];
entity_id?: string;
attribute?: string;
multiple?: boolean;
} | null;
}