mirror of
https://github.com/home-assistant/frontend.git
synced 2026-10-27 08:04:18 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b07a0b5a37 |
No files matched your search
@@ -1,27 +1,17 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { consume } from "../../common/decorators/consume";
|
||||
import {
|
||||
consumeEntityStates,
|
||||
consumeLocalize,
|
||||
} from "../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../common/decorators/transform";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../../common/translations/localize";
|
||||
import { formattersContext } from "../../data/context";
|
||||
import type { HomeAssistantFormatters, ValueChangedEvent } from "../../types";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../../types";
|
||||
import "../ha-generic-picker";
|
||||
import type { PickerComboBoxItem } from "../ha-picker-combo-box";
|
||||
import type { PickerValueRenderer } from "../ha-picker-field";
|
||||
|
||||
type FormatEntityAttributeName =
|
||||
HomeAssistantFormatters["formatEntityAttributeName"];
|
||||
|
||||
@customElement("ha-entity-attribute-picker")
|
||||
class HaEntityAttributePicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public entityId?: string | string[];
|
||||
|
||||
/**
|
||||
@@ -48,32 +38,18 @@ class HaEntityAttributePicker extends LitElement {
|
||||
|
||||
@property() public helper?: string;
|
||||
|
||||
@state()
|
||||
@consumeEntityStates({ entityIdPath: ["entityId"] })
|
||||
private _stateObjs?: Record<string, HassEntity>;
|
||||
|
||||
@state()
|
||||
@consume({ context: formattersContext, subscribe: true })
|
||||
@transform<HomeAssistantFormatters, FormatEntityAttributeName>({
|
||||
transformer: (formatters) => formatters?.formatEntityAttributeName,
|
||||
})
|
||||
private _formatEntityAttributeName!: FormatEntityAttributeName;
|
||||
|
||||
@state() @consumeLocalize() private _localize!: LocalizeFunc;
|
||||
|
||||
private _getItemsMemoized = memoizeOne(
|
||||
(
|
||||
entityId: string | string[] | undefined,
|
||||
stateObjs: Record<string, HassEntity> | undefined,
|
||||
hideAttributes: string[] | undefined,
|
||||
formatEntityAttributeName: FormatEntityAttributeName
|
||||
hass: HomeAssistant
|
||||
): PickerComboBoxItem[] => {
|
||||
const entityIds = entityId ? ensureArray(entityId) : [];
|
||||
const options: PickerComboBoxItem[] = [];
|
||||
const optionsSet = new Set<string>();
|
||||
|
||||
for (const id of entityIds) {
|
||||
const stateObj = stateObjs?.[id];
|
||||
const stateObj = hass.states[id];
|
||||
if (!stateObj) {
|
||||
continue;
|
||||
}
|
||||
@@ -87,7 +63,7 @@ class HaEntityAttributePicker extends LitElement {
|
||||
optionsSet.add(attribute);
|
||||
options.push({
|
||||
id: attribute,
|
||||
primary: formatEntityAttributeName(stateObj, attribute),
|
||||
primary: hass.formatEntityAttributeName(stateObj, attribute),
|
||||
sorting_label: attribute,
|
||||
});
|
||||
}
|
||||
@@ -99,28 +75,19 @@ class HaEntityAttributePicker extends LitElement {
|
||||
);
|
||||
|
||||
private _getItems = () =>
|
||||
this._getItemsMemoized(
|
||||
this.entityId,
|
||||
this._stateObjs,
|
||||
this.hideAttributes,
|
||||
this._formatEntityAttributeName
|
||||
);
|
||||
|
||||
private _valueRenderer = memoizeOne(
|
||||
(items: PickerComboBoxItem[]): PickerValueRenderer =>
|
||||
(value: string) => {
|
||||
const item = items.find((option) => option.id === value);
|
||||
return html`<span slot="headline">${item?.primary ?? value}</span>`;
|
||||
}
|
||||
);
|
||||
this._getItemsMemoized(this.entityId, this.hideAttributes, this.hass);
|
||||
|
||||
protected render() {
|
||||
if (!this.hass) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-generic-picker
|
||||
.value=${this.value}
|
||||
.label=${
|
||||
this.label ??
|
||||
this._localize(
|
||||
this.hass.localize(
|
||||
"ui.components.entity.entity-attribute-picker.attribute"
|
||||
)
|
||||
}
|
||||
@@ -129,13 +96,19 @@ class HaEntityAttributePicker extends LitElement {
|
||||
.helper=${this.helper}
|
||||
.allowCustomValue=${this.allowCustomValue}
|
||||
.getItems=${this._getItems}
|
||||
.valueRenderer=${this._valueRenderer(this._getItems())}
|
||||
.valueRenderer=${this._valueRenderer}
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</ha-generic-picker>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueRenderer: PickerValueRenderer = (value: string) => {
|
||||
const items = this._getItems();
|
||||
const item = items.find((option) => option.id === value);
|
||||
return html`<span slot="headline">${item?.primary ?? value}</span>`;
|
||||
};
|
||||
|
||||
private _valueChanged(ev: ValueChangedEvent<string>) {
|
||||
ev.stopPropagation();
|
||||
const newValue = ev.detail.value;
|
||||
|
||||
@@ -1,27 +1,18 @@
|
||||
import type { ContextType } from "@lit/context";
|
||||
import { mdiDragHorizontalVariant, mdiPlus } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { consume } from "../../common/decorators/consume";
|
||||
import {
|
||||
consumeEntityState,
|
||||
consumeLocalize,
|
||||
} from "../../common/decorators/consume-context-entry";
|
||||
import { transform } from "../../common/decorators/transform";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { getEntityContext } from "../../common/entity/context/get_entity_context";
|
||||
import type { LocalizeFunc } from "../../common/translations/localize";
|
||||
import { formattersContext, registriesContext } from "../../data/context";
|
||||
import {
|
||||
STATE_DISPLAY_SPECIAL_CONTENT,
|
||||
STATE_DISPLAY_SPECIAL_CONTENT_DOMAINS,
|
||||
} from "../../state-display/state-display";
|
||||
import type { HomeAssistantFormatters, ValueChangedEvent } from "../../types";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../../types";
|
||||
import "../chips/ha-assist-chip";
|
||||
import "../chips/ha-chip-set";
|
||||
import "../chips/ha-input-chip";
|
||||
@@ -88,11 +79,10 @@ const HIDDEN_ATTRIBUTES = [
|
||||
"wind_speed_unit",
|
||||
];
|
||||
|
||||
type FormatEntityAttributeName =
|
||||
HomeAssistantFormatters["formatEntityAttributeName"];
|
||||
|
||||
@customElement("ha-entity-state-content-picker")
|
||||
export class HaStateContentPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public entityId?: string;
|
||||
|
||||
// eslint-disable-next-line lit/no-native-attributes
|
||||
@@ -116,48 +106,34 @@ export class HaStateContentPicker extends LitElement {
|
||||
|
||||
@query("ha-generic-picker", true) private _picker?: HaGenericPicker;
|
||||
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["entityId"] })
|
||||
private _stateObj?: HassEntity;
|
||||
|
||||
@state()
|
||||
@consume({ context: registriesContext, subscribe: true })
|
||||
private _registries!: ContextType<typeof registriesContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: formattersContext, subscribe: true })
|
||||
@transform<HomeAssistantFormatters, FormatEntityAttributeName>({
|
||||
transformer: (formatters) => formatters?.formatEntityAttributeName,
|
||||
})
|
||||
private _formatEntityAttributeName!: FormatEntityAttributeName;
|
||||
|
||||
@state() @consumeLocalize() private _localize!: LocalizeFunc;
|
||||
|
||||
private _editIndex?: number;
|
||||
|
||||
private _getItems = memoizeOne(
|
||||
(
|
||||
entityId: string | undefined,
|
||||
stateObj: HassEntity | undefined,
|
||||
allowName: boolean,
|
||||
allowContext: boolean,
|
||||
localize: LocalizeFunc,
|
||||
registries: ContextType<typeof registriesContext>,
|
||||
formatEntityAttributeName: FormatEntityAttributeName
|
||||
entityId?: string,
|
||||
stateObj?: HassEntity,
|
||||
allowName?: boolean,
|
||||
allowContext?: boolean
|
||||
) => {
|
||||
const domain = entityId ? computeDomain(entityId) : undefined;
|
||||
const items: PickerComboBoxItem[] = [
|
||||
{
|
||||
id: "state",
|
||||
primary: localize("ui.components.state-content-picker.state"),
|
||||
sorting_label: localize("ui.components.state-content-picker.state"),
|
||||
primary: this.hass.localize(
|
||||
"ui.components.state-content-picker.state"
|
||||
),
|
||||
sorting_label: this.hass.localize(
|
||||
"ui.components.state-content-picker.state"
|
||||
),
|
||||
},
|
||||
...(allowName
|
||||
? [
|
||||
{
|
||||
id: "name",
|
||||
primary: localize("ui.components.state-content-picker.name"),
|
||||
sorting_label: localize(
|
||||
primary: this.hass.localize(
|
||||
"ui.components.state-content-picker.name"
|
||||
),
|
||||
sorting_label: this.hass.localize(
|
||||
"ui.components.state-content-picker.name"
|
||||
),
|
||||
} satisfies PickerComboBoxItem,
|
||||
@@ -165,15 +141,19 @@ export class HaStateContentPicker extends LitElement {
|
||||
: []),
|
||||
{
|
||||
id: "last_changed",
|
||||
primary: localize("ui.components.state-content-picker.last_changed"),
|
||||
sorting_label: localize(
|
||||
primary: this.hass.localize(
|
||||
"ui.components.state-content-picker.last_changed"
|
||||
),
|
||||
sorting_label: this.hass.localize(
|
||||
"ui.components.state-content-picker.last_changed"
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "last_updated",
|
||||
primary: localize("ui.components.state-content-picker.last_updated"),
|
||||
sorting_label: localize(
|
||||
primary: this.hass.localize(
|
||||
"ui.components.state-content-picker.last_updated"
|
||||
),
|
||||
sorting_label: this.hass.localize(
|
||||
"ui.components.state-content-picker.last_updated"
|
||||
),
|
||||
},
|
||||
@@ -181,19 +161,19 @@ export class HaStateContentPicker extends LitElement {
|
||||
? (() => {
|
||||
const context = getEntityContext(
|
||||
stateObj,
|
||||
registries.entities,
|
||||
registries.devices,
|
||||
registries.areas,
|
||||
registries.floors
|
||||
this.hass.entities,
|
||||
this.hass.devices,
|
||||
this.hass.areas,
|
||||
this.hass.floors
|
||||
);
|
||||
const contextItems: PickerComboBoxItem[] = [];
|
||||
if (context.device) {
|
||||
contextItems.push({
|
||||
id: "device_name",
|
||||
primary: localize(
|
||||
primary: this.hass.localize(
|
||||
"ui.components.state-content-picker.device_name"
|
||||
),
|
||||
sorting_label: localize(
|
||||
sorting_label: this.hass.localize(
|
||||
"ui.components.state-content-picker.device_name"
|
||||
),
|
||||
});
|
||||
@@ -201,10 +181,10 @@ export class HaStateContentPicker extends LitElement {
|
||||
if (context.parentDevice) {
|
||||
contextItems.push({
|
||||
id: "parent_device_name",
|
||||
primary: localize(
|
||||
primary: this.hass.localize(
|
||||
"ui.components.state-content-picker.parent_device_name"
|
||||
),
|
||||
sorting_label: localize(
|
||||
sorting_label: this.hass.localize(
|
||||
"ui.components.state-content-picker.parent_device_name"
|
||||
),
|
||||
});
|
||||
@@ -212,10 +192,10 @@ export class HaStateContentPicker extends LitElement {
|
||||
if (context.area) {
|
||||
contextItems.push({
|
||||
id: "area_name",
|
||||
primary: localize(
|
||||
primary: this.hass.localize(
|
||||
"ui.components.state-content-picker.area_name"
|
||||
),
|
||||
sorting_label: localize(
|
||||
sorting_label: this.hass.localize(
|
||||
"ui.components.state-content-picker.area_name"
|
||||
),
|
||||
});
|
||||
@@ -223,10 +203,10 @@ export class HaStateContentPicker extends LitElement {
|
||||
if (context.floor) {
|
||||
contextItems.push({
|
||||
id: "floor_name",
|
||||
primary: localize(
|
||||
primary: this.hass.localize(
|
||||
"ui.components.state-content-picker.floor_name"
|
||||
),
|
||||
sorting_label: localize(
|
||||
sorting_label: this.hass.localize(
|
||||
"ui.components.state-content-picker.floor_name"
|
||||
),
|
||||
});
|
||||
@@ -241,10 +221,10 @@ export class HaStateContentPicker extends LitElement {
|
||||
(content) =>
|
||||
({
|
||||
id: content,
|
||||
primary: localize(
|
||||
primary: this.hass.localize(
|
||||
`ui.components.state-content-picker.${content}`
|
||||
),
|
||||
sorting_label: localize(
|
||||
sorting_label: this.hass.localize(
|
||||
`ui.components.state-content-picker.${content}`
|
||||
),
|
||||
}) satisfies PickerComboBoxItem
|
||||
@@ -256,8 +236,14 @@ export class HaStateContentPicker extends LitElement {
|
||||
(attribute) =>
|
||||
({
|
||||
id: attribute,
|
||||
primary: formatEntityAttributeName(stateObj!, attribute),
|
||||
sorting_label: formatEntityAttributeName(stateObj!, attribute),
|
||||
primary: this.hass.formatEntityAttributeName(
|
||||
stateObj!,
|
||||
attribute
|
||||
),
|
||||
sorting_label: this.hass.formatEntityAttributeName(
|
||||
stateObj!,
|
||||
attribute
|
||||
),
|
||||
}) satisfies PickerComboBoxItem
|
||||
),
|
||||
];
|
||||
@@ -265,21 +251,13 @@ export class HaStateContentPicker extends LitElement {
|
||||
}
|
||||
);
|
||||
|
||||
private _items() {
|
||||
return this._getItems(
|
||||
this.entityId,
|
||||
this._stateObj,
|
||||
this.allowName,
|
||||
this.allowContext,
|
||||
this._localize,
|
||||
this._registries,
|
||||
this._formatEntityAttributeName
|
||||
);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const value = this._value;
|
||||
|
||||
const stateObj = this.entityId
|
||||
? this.hass.states[this.entityId]
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
${this.label ? html`<label>${this.label}</label>` : nothing}
|
||||
<ha-generic-picker
|
||||
@@ -304,7 +282,7 @@ export class HaStateContentPicker extends LitElement {
|
||||
this._value,
|
||||
(item) => item,
|
||||
(item: string, idx) => {
|
||||
const label = this._getItemLabel(item);
|
||||
const label = this._getItemLabel(item, stateObj);
|
||||
const isValid = !!label;
|
||||
return html`
|
||||
<ha-input-chip
|
||||
@@ -331,7 +309,7 @@ export class HaStateContentPicker extends LitElement {
|
||||
<ha-assist-chip
|
||||
@click=${this._addItem}
|
||||
.disabled=${this.disabled}
|
||||
label=${this._localize(
|
||||
label=${this.hass.localize(
|
||||
"ui.components.entity.entity-state-content-picker.add"
|
||||
)}
|
||||
class="add"
|
||||
@@ -376,9 +354,20 @@ export class HaStateContentPicker extends LitElement {
|
||||
return !this.value ? [] : ensureArray(this.value);
|
||||
}
|
||||
|
||||
private _getItemLabel(value: string): string | undefined {
|
||||
return this._items().find((item) => item.id === value)?.primary;
|
||||
}
|
||||
private _getItemLabel = memoizeOne(
|
||||
(value: string, stateObj?: HassEntity): string | undefined => {
|
||||
const stateObjForItems = this.entityId
|
||||
? this.hass.states[this.entityId]
|
||||
: stateObj;
|
||||
const items = this._getItems(
|
||||
this.entityId,
|
||||
stateObjForItems,
|
||||
this.allowName,
|
||||
this.allowContext
|
||||
);
|
||||
return items.find((item) => item.id === value)?.primary;
|
||||
}
|
||||
);
|
||||
|
||||
private _toValue = memoizeOne((value: string[]): typeof this.value => {
|
||||
if (value.length === 0) {
|
||||
@@ -398,9 +387,9 @@ export class HaStateContentPicker extends LitElement {
|
||||
}
|
||||
|
||||
private _customValueOption = memoizeOne(
|
||||
(text: string, localize: LocalizeFunc): PickerComboBoxItem => ({
|
||||
(text: string): PickerComboBoxItem => ({
|
||||
id: text,
|
||||
primary: localize(
|
||||
primary: this.hass.localize(
|
||||
"ui.components.entity.entity-state-content-picker.custom_attribute"
|
||||
),
|
||||
secondary: `"${text}"`,
|
||||
@@ -414,7 +403,15 @@ export class HaStateContentPicker extends LitElement {
|
||||
);
|
||||
|
||||
private _getFilteredItems = (): PickerComboBoxItem[] => {
|
||||
const items = this._items();
|
||||
const stateObj = this.entityId
|
||||
? this.hass.states[this.entityId]
|
||||
: undefined;
|
||||
const items = this._getItems(
|
||||
this.entityId,
|
||||
stateObj,
|
||||
this.allowName,
|
||||
this.allowContext
|
||||
);
|
||||
const currentValue =
|
||||
this._editIndex != null ? this._value[this._editIndex] : undefined;
|
||||
|
||||
@@ -426,7 +423,7 @@ export class HaStateContentPicker extends LitElement {
|
||||
|
||||
// When editing an existing custom value, include it in the base items
|
||||
if (currentValue && !items.find((item) => item.id === currentValue)) {
|
||||
filteredItems.push(this._customValueOption(currentValue, this._localize));
|
||||
filteredItems.push(this._customValueOption(currentValue));
|
||||
}
|
||||
|
||||
return filteredItems;
|
||||
@@ -435,13 +432,21 @@ export class HaStateContentPicker extends LitElement {
|
||||
private _getAdditionalItems = (
|
||||
searchString?: string
|
||||
): PickerComboBoxItem[] => {
|
||||
const items = this._items();
|
||||
const stateObj = this.entityId
|
||||
? this.hass.states[this.entityId]
|
||||
: undefined;
|
||||
const items = this._getItems(
|
||||
this.entityId,
|
||||
stateObj,
|
||||
this.allowName,
|
||||
this.allowContext
|
||||
);
|
||||
|
||||
// If the search string does not match with the id of any of the items,
|
||||
// offer to add it as a custom attribute
|
||||
const existingItem = items.find((item) => item.id === searchString);
|
||||
if (searchString && !existingItem) {
|
||||
return [this._customValueOption(searchString, this._localize)];
|
||||
return [this._customValueOption(searchString)];
|
||||
}
|
||||
|
||||
return [];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ContextType } from "@lit/context";
|
||||
import type { RenderItemFunction } from "@lit-labs/virtualizer/virtualize";
|
||||
import { mdiPlus } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
@@ -15,7 +16,13 @@ import memoizeOne from "memoize-one";
|
||||
import { consume } from "../common/decorators/consume";
|
||||
import { computeCssColor } from "../common/color/compute-color";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { labelsContext } from "../data/context";
|
||||
import {
|
||||
apiContext,
|
||||
internationalizationContext,
|
||||
labelsContext,
|
||||
registriesContext,
|
||||
statesContext,
|
||||
} from "../data/context";
|
||||
import {
|
||||
getLabels,
|
||||
labelComboBoxKeys,
|
||||
@@ -27,7 +34,7 @@ import {
|
||||
} from "../data/label/label_registry";
|
||||
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
|
||||
import { showLabelDetailDialog } from "../panels/config/labels/show-dialog-label-detail";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import type { ValueChangedEvent } from "../types";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||
import "./ha-generic-picker";
|
||||
import type { HaGenericPicker } from "./ha-generic-picker";
|
||||
@@ -53,8 +60,6 @@ export const renderLabelColorBadge = (color: string | undefined) =>
|
||||
|
||||
@customElement("ha-label-picker")
|
||||
export class HaLabelPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public value?: string;
|
||||
@@ -112,6 +117,19 @@ export class HaLabelPicker extends LitElement {
|
||||
@state()
|
||||
private _labels?: LabelRegistryEntry[];
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@consume({ context: statesContext, subscribe: true })
|
||||
private _states?: ContextType<typeof statesContext>;
|
||||
|
||||
@consume({ context: registriesContext, subscribe: true })
|
||||
private _registries?: ContextType<typeof registriesContext>;
|
||||
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: ContextType<typeof apiContext>;
|
||||
|
||||
@queryAssignedElements({ flatten: true })
|
||||
private _slotNodes?: NodeListOf<HTMLElement>;
|
||||
|
||||
@@ -149,12 +167,16 @@ export class HaLabelPicker extends LitElement {
|
||||
|
||||
private _getLabelsMemoized = memoizeOne(getLabels);
|
||||
|
||||
private _getItems = () =>
|
||||
this._getLabelsMemoized(
|
||||
this.hass.states,
|
||||
this.hass.areas,
|
||||
this.hass.devices,
|
||||
this.hass.entities,
|
||||
private _getItems = () => {
|
||||
const { areas, devices, entities } = this._registries ?? {};
|
||||
if (!this._states || !areas || !devices || !entities) {
|
||||
return [];
|
||||
}
|
||||
return this._getLabelsMemoized(
|
||||
this._states,
|
||||
areas,
|
||||
devices,
|
||||
entities,
|
||||
this._labels,
|
||||
this.includeDomains,
|
||||
this.excludeDomains,
|
||||
@@ -163,6 +185,7 @@ export class HaLabelPicker extends LitElement {
|
||||
this.entityFilter,
|
||||
this.excludeLabels
|
||||
);
|
||||
};
|
||||
|
||||
private _allLabelNames = memoizeOne((labels?: LabelRegistryEntry[]) => {
|
||||
if (!labels) {
|
||||
@@ -190,7 +213,7 @@ export class HaLabelPicker extends LitElement {
|
||||
return [
|
||||
{
|
||||
id: ADD_NEW_ID + searchString,
|
||||
primary: this.hass.localize(
|
||||
primary: this._i18n.localize(
|
||||
"ui.components.label-picker.add_new_suggestion",
|
||||
{
|
||||
name: searchString,
|
||||
@@ -204,7 +227,7 @@ export class HaLabelPicker extends LitElement {
|
||||
return [
|
||||
{
|
||||
id: ADD_NEW_ID,
|
||||
primary: this.hass.localize("ui.components.label-picker.add_new"),
|
||||
primary: this._i18n.localize("ui.components.label-picker.add_new"),
|
||||
icon_path: mdiPlus,
|
||||
},
|
||||
];
|
||||
@@ -213,7 +236,7 @@ export class HaLabelPicker extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
const placeholder =
|
||||
this.placeholder ??
|
||||
this.hass.localize("ui.components.label-picker.label");
|
||||
this._i18n.localize("ui.components.label-picker.label");
|
||||
|
||||
return html`
|
||||
<ha-generic-picker
|
||||
@@ -222,10 +245,10 @@ export class HaLabelPicker extends LitElement {
|
||||
.label=${this.label}
|
||||
.helper=${this.helper}
|
||||
.notFoundLabel=${this._notFoundLabel}
|
||||
.emptyLabel=${this.hass.localize(
|
||||
.emptyLabel=${this._i18n.localize(
|
||||
"ui.components.label-picker.no_labels"
|
||||
)}
|
||||
.addButtonLabel=${this.hass.localize("ui.components.label-picker.add")}
|
||||
.addButtonLabel=${this._i18n.localize("ui.components.label-picker.add")}
|
||||
.placeholder=${placeholder}
|
||||
.value=${this.value}
|
||||
.getItems=${this._getItems}
|
||||
@@ -253,7 +276,7 @@ export class HaLabelPicker extends LitElement {
|
||||
}
|
||||
|
||||
if (value.startsWith(ADD_NEW_ID)) {
|
||||
this.hass.loadFragmentTranslation("config");
|
||||
this._i18n.loadFragmentTranslation("config");
|
||||
|
||||
const suggestedName = value.substring(ADD_NEW_ID.length);
|
||||
|
||||
@@ -261,7 +284,10 @@ export class HaLabelPicker extends LitElement {
|
||||
suggestedName: suggestedName,
|
||||
createEntry: async (values) => {
|
||||
try {
|
||||
const label = await createLabelRegistryEntry(this.hass, values);
|
||||
const label = await createLabelRegistryEntry(
|
||||
this._api.callWS,
|
||||
values
|
||||
);
|
||||
if (this._labels?.some((l) => l.label_id === label.label_id)) {
|
||||
this._setValue(label.label_id);
|
||||
} else {
|
||||
@@ -269,7 +295,7 @@ export class HaLabelPicker extends LitElement {
|
||||
}
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
title: this._i18n.localize(
|
||||
"ui.components.label-picker.failed_create_label"
|
||||
),
|
||||
text: err.message,
|
||||
@@ -292,7 +318,7 @@ export class HaLabelPicker extends LitElement {
|
||||
}
|
||||
|
||||
private _notFoundLabel = (search: string) =>
|
||||
this.hass.localize("ui.components.label-picker.no_match", {
|
||||
this._i18n.localize("ui.components.label-picker.no_match", {
|
||||
term: html`<b>‘${search}’</b>`,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ContextType } from "@lit/context";
|
||||
import { mdiPlaylistPlus } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { TemplateResult } from "lit";
|
||||
@@ -8,11 +9,15 @@ import memoizeOne from "memoize-one";
|
||||
import { consume } from "../common/decorators/consume";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
import { labelsContext } from "../data/context";
|
||||
import {
|
||||
apiContext,
|
||||
internationalizationContext,
|
||||
labelsContext,
|
||||
} from "../data/context";
|
||||
import type { LabelRegistryEntry } from "../data/label/label_registry";
|
||||
import { updateLabelRegistryEntry } from "../data/label/label_registry";
|
||||
import { showLabelDetailDialog } from "../panels/config/labels/show-dialog-label-detail";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import type { ValueChangedEvent } from "../types";
|
||||
import "./chips/ha-chip-set";
|
||||
import "./chips/ha-input-chip";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||
@@ -23,8 +28,6 @@ import "./ha-tooltip";
|
||||
|
||||
@customElement("ha-labels-picker")
|
||||
export class HaLabelsPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property({ attribute: false }) public value?: string[];
|
||||
@@ -82,6 +85,13 @@ export class HaLabelsPicker extends LitElement {
|
||||
@state()
|
||||
private _labels?: LabelRegistryEntry[];
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: ContextType<typeof apiContext>;
|
||||
|
||||
@query("ha-label-picker", true) public labelPicker!: HaLabelPicker;
|
||||
|
||||
public async open() {
|
||||
@@ -114,12 +124,11 @@ export class HaLabelsPicker extends LitElement {
|
||||
const labels = this._sortedLabels(
|
||||
this.value,
|
||||
this._labels,
|
||||
this.hass.locale.language
|
||||
this._i18n.locale.language
|
||||
);
|
||||
return html`
|
||||
${this.label ? html`<label>${this.label}</label>` : nothing}
|
||||
<ha-label-picker
|
||||
.hass=${this.hass}
|
||||
.helper=${this.helper}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
@@ -175,7 +184,7 @@ export class HaLabelsPicker extends LitElement {
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlaylistPlus} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize("ui.components.label-picker.add")}
|
||||
${this._i18n.localize("ui.components.label-picker.add")}
|
||||
</ha-button>
|
||||
</ha-chip-set>
|
||||
</ha-label-picker>
|
||||
@@ -196,7 +205,11 @@ export class HaLabelsPicker extends LitElement {
|
||||
showLabelDetailDialog(this, {
|
||||
entry: label,
|
||||
updateEntry: async (values) => {
|
||||
await updateLabelRegistryEntry(this.hass, label.label_id, values);
|
||||
await updateLabelRegistryEntry(
|
||||
this._api.callWS,
|
||||
label.label_id,
|
||||
values
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,10 +6,13 @@ import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { consumeEntityStates } from "../../common/decorators/consume-context-entry";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { AttributeSelector } from "../../data/selector";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../entity/ha-entity-attribute-picker";
|
||||
|
||||
@customElement("ha-selector-attribute")
|
||||
export class HaSelectorAttribute extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public selector!: AttributeSelector;
|
||||
|
||||
@property() public value?: any;
|
||||
@@ -33,6 +36,7 @@ export class HaSelectorAttribute extends LitElement {
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-entity-attribute-picker
|
||||
.hass=${this.hass}
|
||||
.entityId=${
|
||||
this.selector.attribute?.entity_id || this.context?.filter_entity
|
||||
}
|
||||
|
||||
@@ -3,13 +3,10 @@ import { customElement, property } from "lit/decorators";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { LabelSelector } from "../../data/selector";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../ha-labels-picker";
|
||||
|
||||
@customElement("ha-selector-label")
|
||||
export class HaLabelSelector extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property() public value?: string | string[];
|
||||
|
||||
@property() public name?: string;
|
||||
@@ -31,7 +28,6 @@ export class HaLabelSelector extends LitElement {
|
||||
return html`
|
||||
<ha-labels-picker
|
||||
no-add
|
||||
.hass=${this.hass}
|
||||
.value=${ensureArray(this.value ?? [])}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@@ -45,7 +41,6 @@ export class HaLabelSelector extends LitElement {
|
||||
return html`
|
||||
<ha-label-picker
|
||||
no-add
|
||||
.hass=${this.hass}
|
||||
.value=${this.value}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { UiStateContentSelector } from "../../data/selector";
|
||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../entity/ha-entity-state-content-picker";
|
||||
|
||||
@customElement("ha-selector-ui_state_content")
|
||||
export class HaSelectorUiStateContent extends LitElement {
|
||||
export class HaSelectorUiStateContent extends SubscribeMixin(LitElement) {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public selector!: UiStateContentSelector;
|
||||
|
||||
@property() public value?: string | string[];
|
||||
@@ -24,6 +28,7 @@ export class HaSelectorUiStateContent extends LitElement {
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-entity-state-content-picker
|
||||
.hass=${this.hass}
|
||||
.entityId=${
|
||||
this.selector.ui_state_content?.entity_id ||
|
||||
this.context?.filter_entity
|
||||
|
||||
@@ -61,30 +61,30 @@ export const subscribeLabelRegistry = (
|
||||
);
|
||||
|
||||
export const createLabelRegistryEntry = (
|
||||
hass: HomeAssistant,
|
||||
callWS: HomeAssistant["callWS"],
|
||||
values: LabelRegistryEntryMutableParams
|
||||
) =>
|
||||
hass.callWS<LabelRegistryEntry>({
|
||||
callWS<LabelRegistryEntry>({
|
||||
type: "config/label_registry/create",
|
||||
...values,
|
||||
});
|
||||
|
||||
export const updateLabelRegistryEntry = (
|
||||
hass: HomeAssistant,
|
||||
callWS: HomeAssistant["callWS"],
|
||||
labelId: string,
|
||||
updates: Partial<LabelRegistryEntryMutableParams>
|
||||
) =>
|
||||
hass.callWS<LabelRegistryEntry>({
|
||||
callWS<LabelRegistryEntry>({
|
||||
type: "config/label_registry/update",
|
||||
label_id: labelId,
|
||||
...updates,
|
||||
});
|
||||
|
||||
export const deleteLabelRegistryEntry = (
|
||||
hass: HomeAssistant,
|
||||
callWS: HomeAssistant["callWS"],
|
||||
labelId: string
|
||||
) =>
|
||||
hass.callWS({
|
||||
callWS({
|
||||
type: "config/label_registry/delete",
|
||||
label_id: labelId,
|
||||
});
|
||||
@@ -204,7 +204,6 @@ class DialogAreaDetail
|
||||
|
||||
<ha-labels-picker
|
||||
.label=${this.hass.localize("ui.components.label-picker.labels")}
|
||||
.hass=${this.hass}
|
||||
.value=${this._labels}
|
||||
@value-changed=${this._labelsChanged}
|
||||
.placeholder=${this.hass.localize(
|
||||
|
||||
@@ -218,7 +218,6 @@ class DialogAutomationSave
|
||||
this._visibleOptionals.includes("labels")
|
||||
? html` <ha-labels-picker
|
||||
id="labels"
|
||||
.hass=${this.hass}
|
||||
.value=${this._entryUpdates.labels}
|
||||
@value-changed=${this._registryEntryChanged}
|
||||
></ha-labels-picker>`
|
||||
|
||||
@@ -1416,7 +1416,7 @@ ${rejected
|
||||
private _bulkCreateLabel = () => {
|
||||
showLabelDetailDialog(this, {
|
||||
createEntry: async (values) => {
|
||||
const label = await createLabelRegistryEntry(this.hass, values);
|
||||
const label = await createLabelRegistryEntry(this.hass.callWS, values);
|
||||
this._bulkLabel(label.label_id, "add");
|
||||
},
|
||||
});
|
||||
|
||||
@@ -114,7 +114,6 @@ class DialogDeviceRegistryDetail extends DirtyStateProviderMixin<DeviceFormState
|
||||
@value-changed=${this._areaPicked}
|
||||
></ha-area-picker>
|
||||
<ha-labels-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this._labels}
|
||||
@value-changed=${this._labelsChanged}
|
||||
></ha-labels-picker>
|
||||
|
||||
@@ -1247,7 +1247,7 @@ ${rejected
|
||||
private _bulkCreateLabel = () => {
|
||||
showLabelDetailDialog(this, {
|
||||
createEntry: async (values) => {
|
||||
const label = await createLabelRegistryEntry(this.hass, values);
|
||||
const label = await createLabelRegistryEntry(this.hass.callWS, values);
|
||||
this._bulkLabel(label.label_id, "add");
|
||||
},
|
||||
});
|
||||
|
||||
@@ -990,7 +990,6 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
: nothing
|
||||
}
|
||||
<ha-labels-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this._labels}
|
||||
.disabled=${!!this.disabled}
|
||||
@value-changed=${this._labelsChanged}
|
||||
|
||||
@@ -1447,7 +1447,7 @@ ${rejected
|
||||
private _bulkCreateLabel = () => {
|
||||
showLabelDetailDialog(this, {
|
||||
createEntry: async (values) => {
|
||||
const label = await createLabelRegistryEntry(this.hass, values);
|
||||
const label = await createLabelRegistryEntry(this.hass.callWS, values);
|
||||
this._bulkLabel(label.label_id, "add");
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1563,7 +1563,7 @@ ${rejected
|
||||
private _bulkCreateLabel = () => {
|
||||
showLabelDetailDialog(this, {
|
||||
createEntry: async (values) => {
|
||||
const label = await createLabelRegistryEntry(this.hass, values);
|
||||
const label = await createLabelRegistryEntry(this.hass.callWS, values);
|
||||
this._bulkLabel(label.label_id, "add");
|
||||
},
|
||||
});
|
||||
|
||||
@@ -358,7 +358,7 @@ export class HaConfigLabels extends LitElement {
|
||||
private async _createLabel(
|
||||
values: LabelRegistryEntryMutableParams
|
||||
): Promise<LabelRegistryEntry> {
|
||||
const newTag = await createLabelRegistryEntry(this.hass, values);
|
||||
const newTag = await createLabelRegistryEntry(this.hass.callWS, values);
|
||||
this._labels = [...this._labels, newTag];
|
||||
return newTag;
|
||||
}
|
||||
@@ -368,7 +368,7 @@ export class HaConfigLabels extends LitElement {
|
||||
values: Partial<LabelRegistryEntryMutableParams>
|
||||
): Promise<LabelRegistryEntry> {
|
||||
const updated = await updateLabelRegistryEntry(
|
||||
this.hass,
|
||||
this.hass.callWS,
|
||||
selectedLabel.label_id,
|
||||
values
|
||||
);
|
||||
@@ -399,7 +399,7 @@ export class HaConfigLabels extends LitElement {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
await deleteLabelRegistryEntry(this.hass, selectedLabel.label_id);
|
||||
await deleteLabelRegistryEntry(this.hass.callWS, selectedLabel.label_id);
|
||||
this._labels = this._labels.filter(
|
||||
(label) => label.label_id !== selectedLabel.label_id
|
||||
);
|
||||
@@ -431,7 +431,7 @@ export class HaConfigLabels extends LitElement {
|
||||
action: async () => {
|
||||
const results = await Promise.allSettled(
|
||||
this._selected.map((labelId) =>
|
||||
deleteLabelRegistryEntry(this.hass, labelId)
|
||||
deleteLabelRegistryEntry(this.hass.callWS, labelId)
|
||||
)
|
||||
);
|
||||
const deletedIds = new Set(
|
||||
|
||||
@@ -1158,7 +1158,7 @@ ${rejected
|
||||
private _bulkCreateLabel = () => {
|
||||
showLabelDetailDialog(this, {
|
||||
createEntry: async (values) => {
|
||||
const label = await createLabelRegistryEntry(this.hass, values);
|
||||
const label = await createLabelRegistryEntry(this.hass.callWS, values);
|
||||
this._bulkLabel(label.label_id, "add");
|
||||
},
|
||||
});
|
||||
|
||||
@@ -175,7 +175,6 @@ class DialogSceneSave extends DirtyStateProviderMixin<SceneSaveState>()(
|
||||
this._visibleOptionals.includes("labels")
|
||||
? html` <ha-labels-picker
|
||||
id="labels"
|
||||
.hass=${this.hass}
|
||||
.value=${this._entryUpdates.labels}
|
||||
@value-changed=${this._registryEntryChanged}
|
||||
></ha-labels-picker>`
|
||||
|
||||
@@ -1136,7 +1136,7 @@ ${rejected
|
||||
private _bulkCreateLabel = () => {
|
||||
showLabelDetailDialog(this, {
|
||||
createEntry: async (values) => {
|
||||
const label = await createLabelRegistryEntry(this.hass, values);
|
||||
const label = await createLabelRegistryEntry(this.hass.callWS, values);
|
||||
this._bulkLabel(label.label_id, "add");
|
||||
},
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user