mirror of
https://github.com/home-assistant/frontend.git
synced 2025-10-18 16:19:46 +00:00
Compare commits
9 Commits
copilot/fi
...
wa-dialog-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f6c6835de5 | ||
![]() |
13346a21d7 | ||
![]() |
fe364b0687 | ||
![]() |
eb760d27b9 | ||
![]() |
b62b7c6167 | ||
![]() |
61238aad3a | ||
![]() |
b4b7348646 | ||
![]() |
f51d37f972 | ||
![]() |
bca926f36c |
@@ -25,7 +25,6 @@ import "../ha-sortable";
|
||||
interface EntityNameOption {
|
||||
primary: string;
|
||||
secondary?: string;
|
||||
field_label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
@@ -42,23 +41,6 @@ const KNOWN_TYPES = new Set(["entity", "device", "area", "floor"]);
|
||||
|
||||
const UNIQUE_TYPES = new Set(["entity", "device", "area", "floor"]);
|
||||
|
||||
const formatOptionValue = (item: EntityNameItem) => {
|
||||
if (item.type === "text" && item.text) {
|
||||
return item.text;
|
||||
}
|
||||
return `___${item.type}___`;
|
||||
};
|
||||
|
||||
const parseOptionValue = (value: string): EntityNameItem => {
|
||||
if (value.startsWith("___") && value.endsWith("___")) {
|
||||
const type = value.slice(3, -3);
|
||||
if (KNOWN_TYPES.has(type)) {
|
||||
return { type: type as EntityNameType };
|
||||
}
|
||||
}
|
||||
return { type: "text", text: value };
|
||||
};
|
||||
|
||||
@customElement("ha-entity-name-picker")
|
||||
export class HaEntityNamePicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -139,23 +121,13 @@ export class HaEntityNamePicker extends LitElement {
|
||||
return {
|
||||
primary,
|
||||
secondary,
|
||||
field_label: primary,
|
||||
value: formatOptionValue({ type: name }),
|
||||
value: name,
|
||||
};
|
||||
});
|
||||
|
||||
return items;
|
||||
});
|
||||
|
||||
private _customNameOption = memoizeOne((text: string) => ({
|
||||
primary: this.hass.localize(
|
||||
"ui.components.entity.entity-name-picker.custom_name"
|
||||
),
|
||||
secondary: `"${text}"`,
|
||||
field_label: text,
|
||||
value: formatOptionValue({ type: "text", text }),
|
||||
}));
|
||||
|
||||
private _formatItem = (item: EntityNameItem) => {
|
||||
if (item.type === "text") {
|
||||
return `"${item.text}"`;
|
||||
@@ -242,7 +214,7 @@ export class HaEntityNamePicker extends LitElement {
|
||||
allow-custom-value
|
||||
item-id-path="value"
|
||||
item-value-path="value"
|
||||
item-label-path="field_label"
|
||||
item-label-path="primary"
|
||||
.renderer=${rowRenderer}
|
||||
@opened-changed=${this._openedChanged}
|
||||
@value-changed=${this._comboBoxValueChanged}
|
||||
@@ -314,13 +286,14 @@ export class HaEntityNamePicker extends LitElement {
|
||||
const initialItem =
|
||||
this._editIndex != null ? this._value[this._editIndex] : undefined;
|
||||
|
||||
const initialValue = initialItem ? formatOptionValue(initialItem) : "";
|
||||
const initialValue = initialItem
|
||||
? initialItem.type === "text"
|
||||
? initialItem.text
|
||||
: initialItem.type
|
||||
: "";
|
||||
|
||||
const filteredItems = this._filterSelectedOptions(options, initialValue);
|
||||
|
||||
if (initialItem && initialItem.type === "text" && initialItem.text) {
|
||||
filteredItems.push(this._customNameOption(initialItem.text));
|
||||
}
|
||||
this._comboBox.filteredItems = filteredItems;
|
||||
this._comboBox.setInputValue(initialValue);
|
||||
} else {
|
||||
@@ -353,7 +326,11 @@ export class HaEntityNamePicker extends LitElement {
|
||||
const currentItem =
|
||||
this._editIndex != null ? this._value[this._editIndex] : undefined;
|
||||
|
||||
const currentValue = currentItem ? formatOptionValue(currentItem) : "";
|
||||
const currentValue = currentItem
|
||||
? currentItem.type === "text"
|
||||
? currentItem.text
|
||||
: currentItem.type
|
||||
: "";
|
||||
|
||||
this._comboBox.filteredItems = this._filterSelectedOptions(
|
||||
options,
|
||||
@@ -375,7 +352,6 @@ export class HaEntityNamePicker extends LitElement {
|
||||
const fuse = new Fuse(this._comboBox.filteredItems, fuseOptions);
|
||||
const filteredItems = fuse.search(filter).map((result) => result.item);
|
||||
|
||||
filteredItems.push(this._customNameOption(input));
|
||||
this._comboBox.filteredItems = filteredItems;
|
||||
}
|
||||
|
||||
@@ -409,7 +385,9 @@ export class HaEntityNamePicker extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const item: EntityNameItem = parseOptionValue(value);
|
||||
const item: EntityNameItem = KNOWN_TYPES.has(value as any)
|
||||
? { type: value as EntityNameType }
|
||||
: { type: "text", text: value };
|
||||
|
||||
const newValue = [...this._value];
|
||||
|
||||
|
@@ -107,15 +107,14 @@ export class HaMediaSelector extends LitElement {
|
||||
supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA));
|
||||
|
||||
if (this.selector.media?.image_upload && !this.value) {
|
||||
return html`${this.label ? html`<label>${this.label}</label>` : nothing}
|
||||
<ha-picture-upload
|
||||
.hass=${this.hass}
|
||||
.value=${null}
|
||||
.contentIdHelper=${this.selector.media?.content_id_helper}
|
||||
select-media
|
||||
full-media
|
||||
@media-picked=${this._pictureUploadMediaPicked}
|
||||
></ha-picture-upload>`;
|
||||
return html`<ha-picture-upload
|
||||
.hass=${this.hass}
|
||||
.value=${null}
|
||||
.contentIdHelper=${this.selector.media?.content_id_helper}
|
||||
select-media
|
||||
full-media
|
||||
@media-picked=${this._pictureUploadMediaPicked}
|
||||
></ha-picture-upload>`;
|
||||
}
|
||||
|
||||
return html`
|
||||
@@ -142,7 +141,6 @@ export class HaMediaSelector extends LitElement {
|
||||
`}
|
||||
${!supportsBrowse
|
||||
? html`
|
||||
${this.label ? html`<label>${this.label}</label>` : nothing}
|
||||
<ha-alert>
|
||||
${this.hass.localize(
|
||||
"ui.components.selectors.media.browse_not_supported"
|
||||
@@ -156,8 +154,7 @@ export class HaMediaSelector extends LitElement {
|
||||
.computeHelper=${this._computeHelperCallback}
|
||||
></ha-form>
|
||||
`
|
||||
: html`${this.label ? html`<label>${this.label}</label>` : nothing}
|
||||
<ha-card
|
||||
: html`<ha-card
|
||||
outlined
|
||||
tabindex="0"
|
||||
role="button"
|
||||
|
@@ -79,7 +79,6 @@ export interface DataEntryFlowStepAbort {
|
||||
reason: string;
|
||||
description_placeholders?: Record<string, string>;
|
||||
translation_domain?: string;
|
||||
next_flow?: [FlowType, string]; // [flow_type, flow_id]
|
||||
}
|
||||
|
||||
export interface DataEntryFlowStepProgress {
|
||||
|
@@ -472,10 +472,7 @@ class DataEntryFlowDialog extends LitElement {
|
||||
this._step = undefined;
|
||||
await this.updateComplete;
|
||||
this._step = _step;
|
||||
if (
|
||||
(_step.type === "create_entry" || _step.type === "abort") &&
|
||||
_step.next_flow
|
||||
) {
|
||||
if (_step.type === "create_entry" && _step.next_flow) {
|
||||
// skip device rename if there is a chained flow
|
||||
this._step = undefined;
|
||||
this._handler = undefined;
|
||||
@@ -489,36 +486,32 @@ class DataEntryFlowDialog extends LitElement {
|
||||
carryOverDevices: this._devices(
|
||||
this._params!.flowConfig.showDevices,
|
||||
Object.values(this.hass.devices),
|
||||
_step.type === "create_entry" ? _step.result?.entry_id : undefined,
|
||||
_step.result?.entry_id,
|
||||
this._params!.carryOverDevices
|
||||
).map((device) => device.id),
|
||||
dialogClosedCallback: this._params!.dialogClosedCallback,
|
||||
});
|
||||
} else if (_step.next_flow[0] === "options_flow") {
|
||||
if (_step.type === "create_entry") {
|
||||
showOptionsFlowDialog(
|
||||
this._params!.dialogParentElement!,
|
||||
_step.result!,
|
||||
{
|
||||
continueFlowId: _step.next_flow[1],
|
||||
navigateToResult: this._params!.navigateToResult,
|
||||
dialogClosedCallback: this._params!.dialogClosedCallback,
|
||||
}
|
||||
);
|
||||
}
|
||||
showOptionsFlowDialog(
|
||||
this._params!.dialogParentElement!,
|
||||
_step.result!,
|
||||
{
|
||||
continueFlowId: _step.next_flow[1],
|
||||
navigateToResult: this._params!.navigateToResult,
|
||||
dialogClosedCallback: this._params!.dialogClosedCallback,
|
||||
}
|
||||
);
|
||||
} else if (_step.next_flow[0] === "config_subentries_flow") {
|
||||
if (_step.type === "create_entry") {
|
||||
showSubConfigFlowDialog(
|
||||
this._params!.dialogParentElement!,
|
||||
_step.result!,
|
||||
_step.next_flow[0],
|
||||
{
|
||||
continueFlowId: _step.next_flow[1],
|
||||
navigateToResult: this._params!.navigateToResult,
|
||||
dialogClosedCallback: this._params!.dialogClosedCallback,
|
||||
}
|
||||
);
|
||||
}
|
||||
showSubConfigFlowDialog(
|
||||
this._params!.dialogParentElement!,
|
||||
_step.result!,
|
||||
_step.next_flow[0],
|
||||
{
|
||||
continueFlowId: _step.next_flow[1],
|
||||
navigateToResult: this._params!.navigateToResult,
|
||||
dialogClosedCallback: this._params!.dialogClosedCallback,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.closeDialog();
|
||||
showAlertDialog(this._params!.dialogParentElement!, {
|
||||
|
@@ -1,17 +1,16 @@
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state, query } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-bottom-sheet";
|
||||
import "../../../../components/ha-button";
|
||||
import {
|
||||
getMobileOpenFromBottomAnimation,
|
||||
getMobileCloseToBottomAnimation,
|
||||
} from "../../../../components/ha-md-dialog";
|
||||
import type { HaMdDialog } from "../../../../components/ha-md-dialog";
|
||||
import "../../../../components/ha-dialog-header";
|
||||
import "../../../../components/ha-dialog-footer";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-icon-button-toggle";
|
||||
import "../../../../components/ha-wa-dialog";
|
||||
import type { EntityRegistryEntry } from "../../../../data/entity_registry";
|
||||
import type { LightColor, LightEntity } from "../../../../data/light";
|
||||
import {
|
||||
@@ -41,7 +40,11 @@ class DialogLightColorFavorite extends LitElement {
|
||||
|
||||
@state() private _modes: LightPickerMode[] = [];
|
||||
|
||||
@query("ha-md-dialog") private _dialog?: HaMdDialog;
|
||||
@state() private _open = false;
|
||||
|
||||
@state() private _narrow = false;
|
||||
|
||||
private _mediaQuery?: MediaQueryList;
|
||||
|
||||
public async showDialog(
|
||||
dialogParams: LightColorFavoriteDialogParams
|
||||
@@ -50,12 +53,22 @@ class DialogLightColorFavorite extends LitElement {
|
||||
this._dialogParams = dialogParams;
|
||||
this._color = dialogParams.initialColor ?? this._computeCurrentColor();
|
||||
this._updateModes();
|
||||
this._mediaQuery = matchMedia(
|
||||
"all and (max-width: 450px), all and (max-height: 500px)"
|
||||
);
|
||||
this._narrow = this._mediaQuery.matches;
|
||||
this._mediaQuery.addEventListener("change", this._handleMediaChange);
|
||||
this._open = true;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._dialog?.close();
|
||||
this._open = false;
|
||||
}
|
||||
|
||||
private _handleMediaChange = (ev: MediaQueryListEvent) => {
|
||||
this._narrow = ev.matches;
|
||||
};
|
||||
|
||||
private _updateModes() {
|
||||
const supportsTemp = lightSupportsColorMode(
|
||||
this.stateObj!,
|
||||
@@ -120,16 +133,16 @@ class DialogLightColorFavorite extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private async _cancel() {
|
||||
private _cancel() {
|
||||
this._dialogParams?.cancel?.();
|
||||
}
|
||||
|
||||
private _cancelDialog() {
|
||||
this._cancel();
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
private _dialogClosed(): void {
|
||||
if (this._mediaQuery) {
|
||||
this._mediaQuery.removeEventListener("change", this._handleMediaChange);
|
||||
this._mediaQuery = undefined;
|
||||
}
|
||||
this._dialogParams = undefined;
|
||||
this._entry = undefined;
|
||||
this._color = undefined;
|
||||
@@ -138,7 +151,6 @@ class DialogLightColorFavorite extends LitElement {
|
||||
|
||||
private async _save() {
|
||||
if (!this._color) {
|
||||
this._cancel();
|
||||
return;
|
||||
}
|
||||
this._dialogParams?.submit?.(this._color);
|
||||
@@ -153,89 +165,126 @@ class DialogLightColorFavorite extends LitElement {
|
||||
this._mode = newMode;
|
||||
}
|
||||
|
||||
private _renderModes() {
|
||||
if (this._modes.length <= 1) {
|
||||
return nothing;
|
||||
}
|
||||
return html`
|
||||
<div class="modes">
|
||||
${this._modes.map(
|
||||
(value) => html`
|
||||
<ha-icon-button-toggle
|
||||
border-only
|
||||
.selected=${value === this._mode}
|
||||
.label=${this.hass.localize(
|
||||
`ui.dialogs.more_info_control.light.color_picker.mode.${value}`
|
||||
)}
|
||||
.mode=${value}
|
||||
@click=${this._modeChanged}
|
||||
>
|
||||
<span class="wheel ${classMap({ [value]: true })}"></span>
|
||||
</ha-icon-button-toggle>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderColorPicker() {
|
||||
return html`
|
||||
${this._mode === "color_temp"
|
||||
? html`
|
||||
<light-color-temp-picker
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
@color-changed=${this._colorChanged}
|
||||
>
|
||||
</light-color-temp-picker>
|
||||
`
|
||||
: nothing}
|
||||
${this._mode === "color"
|
||||
? html`
|
||||
<light-color-rgb-picker
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
@color-changed=${this._colorChanged}
|
||||
>
|
||||
</light-color-rgb-picker>
|
||||
`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderButtons() {
|
||||
return html`
|
||||
<ha-button
|
||||
slot="secondaryAction"
|
||||
appearance="plain"
|
||||
@click=${this._cancel}
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
appearance="accent"
|
||||
@click=${this._save}
|
||||
.disabled=${!this._color}
|
||||
>
|
||||
${this.hass.localize("ui.common.save")}
|
||||
</ha-button>
|
||||
`;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._entry || !this.stateObj) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
if (this._narrow) {
|
||||
return html`
|
||||
<ha-bottom-sheet .open=${this._open} @closed=${this._dialogClosed}>
|
||||
<div class="bottom-sheet-container">
|
||||
<ha-dialog-header>
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
@click=${this.closeDialog}
|
||||
.label=${this.hass.localize("ui.common.close")}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
<span slot="title">${this._dialogParams?.title}</span>
|
||||
</ha-dialog-header>
|
||||
<div class="header">${this._renderModes()}</div>
|
||||
<div class="content">${this._renderColorPicker()}</div>
|
||||
<div class="buttons">
|
||||
<ha-button appearance="plain" @click=${this._cancel}>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
appearance="accent"
|
||||
@click=${this._save}
|
||||
.disabled=${!this._color}
|
||||
>
|
||||
${this.hass.localize("ui.common.save")}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
</ha-bottom-sheet>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-md-dialog
|
||||
open
|
||||
@cancel=${this._cancel}
|
||||
<ha-wa-dialog
|
||||
.hass=${this.hass}
|
||||
.open=${this._open}
|
||||
.headerTitle=${this._dialogParams?.title ?? ""}
|
||||
@closed=${this._dialogClosed}
|
||||
aria-labelledby="dialog-light-color-favorite-title"
|
||||
.getOpenAnimation=${getMobileOpenFromBottomAnimation}
|
||||
.getCloseAnimation=${getMobileCloseToBottomAnimation}
|
||||
>
|
||||
<ha-dialog-header slot="headline">
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
@click=${this.closeDialog}
|
||||
.label=${this.hass.localize("ui.common.close")}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
<span slot="title" id="dialog-light-color-favorite-title"
|
||||
>${this._dialogParams?.title}</span
|
||||
>
|
||||
</ha-dialog-header>
|
||||
<div slot="content">
|
||||
<div class="header">
|
||||
${this._modes.length > 1
|
||||
? html`
|
||||
<div class="modes">
|
||||
${this._modes.map(
|
||||
(value) => html`
|
||||
<ha-icon-button-toggle
|
||||
border-only
|
||||
.selected=${value === this._mode}
|
||||
.label=${this.hass.localize(
|
||||
`ui.dialogs.more_info_control.light.color_picker.mode.${value}`
|
||||
)}
|
||||
.mode=${value}
|
||||
@click=${this._modeChanged}
|
||||
>
|
||||
<span
|
||||
class="wheel ${classMap({ [value]: true })}"
|
||||
></span>
|
||||
</ha-icon-button-toggle>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
<div class="content">
|
||||
${this._mode === "color_temp"
|
||||
? html`
|
||||
<light-color-temp-picker
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
@color-changed=${this._colorChanged}
|
||||
>
|
||||
</light-color-temp-picker>
|
||||
`
|
||||
: nothing}
|
||||
${this._mode === "color"
|
||||
? html`
|
||||
<light-color-rgb-picker
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
@color-changed=${this._colorChanged}
|
||||
>
|
||||
</light-color-rgb-picker>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
</div>
|
||||
<div slot="actions">
|
||||
<ha-button appearance="plain" @click=${this._cancelDialog}>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button @click=${this._save} .disabled=${!this._color}
|
||||
>${this.hass.localize("ui.common.save")}</ha-button
|
||||
>
|
||||
</div>
|
||||
</ha-md-dialog>
|
||||
<div class="header">${this._renderModes()}</div>
|
||||
<div class="content">${this._renderColorPicker()}</div>
|
||||
<ha-dialog-footer slot="footer"
|
||||
>${this._renderButtons()}</ha-dialog-footer
|
||||
>
|
||||
</ha-wa-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -243,24 +292,39 @@ class DialogLightColorFavorite extends LitElement {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-md-dialog {
|
||||
min-width: 420px; /* prevent width jumps when switching modes */
|
||||
max-height: min(
|
||||
600px,
|
||||
100% - 48px
|
||||
); /* prevent scrolling on desktop */
|
||||
ha-wa-dialog {
|
||||
--ha-dialog-width-md: 420px;
|
||||
--dialog-content-padding: 0;
|
||||
--dialog-surface-position: fixed;
|
||||
}
|
||||
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
ha-md-dialog {
|
||||
min-width: 100%;
|
||||
min-height: auto;
|
||||
max-height: calc(100% - 100px);
|
||||
margin-bottom: 0;
|
||||
ha-bottom-sheet {
|
||||
--ha-bottom-sheet-max-width: 560px;
|
||||
--ha-bottom-sheet-padding: 0;
|
||||
--ha-bottom-sheet-surface-background: var(--card-background-color);
|
||||
}
|
||||
|
||||
--md-dialog-container-shape-start-start: 28px;
|
||||
--md-dialog-container-shape-start-end: 28px;
|
||||
}
|
||||
.bottom-sheet-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bottom-sheet-container .header {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.bottom-sheet-container .content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--ha-space-2);
|
||||
padding: var(--ha-space-4) var(--ha-space-6);
|
||||
padding-bottom: max(var(--ha-space-4), env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.content {
|
||||
@@ -268,14 +332,14 @@ class DialogLightColorFavorite extends LitElement {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
padding: var(--ha-space-6);
|
||||
flex: 1;
|
||||
}
|
||||
.modes {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
padding: 0 24px;
|
||||
padding: 0 var(--ha-space-6);
|
||||
}
|
||||
.wheel {
|
||||
width: 30px;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import { consume } from "@lit/context";
|
||||
import {
|
||||
mdiAppleKeyboardCommand,
|
||||
mdiContentCopy,
|
||||
@@ -14,7 +13,6 @@ import {
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { keyed } from "lit/directives/keyed";
|
||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
||||
@@ -22,16 +20,7 @@ import "../../../../components/ha-md-divider";
|
||||
import "../../../../components/ha-md-menu-item";
|
||||
import { ACTION_BUILDING_BLOCKS } from "../../../../data/action";
|
||||
import type { ActionSidebarConfig } from "../../../../data/automation";
|
||||
import {
|
||||
floorsContext,
|
||||
fullEntitiesContext,
|
||||
labelsContext,
|
||||
} from "../../../../data/context";
|
||||
import type { EntityRegistryEntry } from "../../../../data/entity_registry";
|
||||
import type { FloorRegistryEntry } from "../../../../data/floor_registry";
|
||||
import type { LabelRegistryEntry } from "../../../../data/label_registry";
|
||||
import type { RepeatAction } from "../../../../data/script";
|
||||
import { describeAction } from "../../../../data/script_i18n";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { isMac } from "../../../../util/is_mac";
|
||||
import type HaAutomationConditionEditor from "../action/ha-automation-action-editor";
|
||||
@@ -59,18 +48,6 @@ export default class HaAutomationSidebarAction extends LitElement {
|
||||
|
||||
@state() private _warnings?: string[];
|
||||
|
||||
@state()
|
||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||
_entityReg!: EntityRegistryEntry[];
|
||||
|
||||
@state()
|
||||
@consume({ context: labelsContext, subscribe: true })
|
||||
_labelReg!: LabelRegistryEntry[];
|
||||
|
||||
@state()
|
||||
@consume({ context: floorsContext, subscribe: true })
|
||||
_floorReg!: Record<string, FloorRegistryEntry>;
|
||||
|
||||
@query(".sidebar-editor")
|
||||
public editor?: HaAutomationConditionEditor;
|
||||
|
||||
@@ -101,20 +78,15 @@ export default class HaAutomationSidebarAction extends LitElement {
|
||||
|
||||
const isBuildingBlock = ACTION_BUILDING_BLOCKS.includes(type || "");
|
||||
|
||||
const title = capitalizeFirstLetter(
|
||||
describeAction(
|
||||
this.hass,
|
||||
this._entityReg,
|
||||
this._labelReg,
|
||||
this._floorReg,
|
||||
actionConfig
|
||||
)
|
||||
);
|
||||
|
||||
const subtitle = this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.action"
|
||||
);
|
||||
|
||||
const title =
|
||||
this.hass.localize(
|
||||
`ui.panel.config.automation.editor.actions.type.${type}.label` as LocalizeKeys
|
||||
) || type;
|
||||
|
||||
const description = isBuildingBlock
|
||||
? this.hass.localize(
|
||||
`ui.panel.config.automation.editor.actions.type.${type}.description.picker` as LocalizeKeys
|
||||
|
@@ -126,16 +126,7 @@ class HuiPictureElementsCard extends LitElement implements LovelaceCard {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
let image: string | undefined =
|
||||
(typeof this._config?.image === "object" &&
|
||||
this._config.image.media_content_id) ||
|
||||
(this._config.image as string | undefined);
|
||||
|
||||
const darkModeImage: string | undefined =
|
||||
(typeof this._config?.dark_mode_image === "object" &&
|
||||
this._config.dark_mode_image.media_content_id) ||
|
||||
(this._config.dark_mode_image as string | undefined);
|
||||
|
||||
let image: string | undefined = this._config.image;
|
||||
if (this._config.image_entity) {
|
||||
const stateObj: ImageEntity | PersonEntity | undefined =
|
||||
this.hass.states[this._config.image_entity];
|
||||
@@ -165,7 +156,7 @@ class HuiPictureElementsCard extends LitElement implements LovelaceCard {
|
||||
.entity=${this._config.entity}
|
||||
.aspectRatio=${this._config.aspect_ratio}
|
||||
.darkModeFilter=${this._config.dark_mode_filter}
|
||||
.darkModeImage=${darkModeImage}
|
||||
.darkModeImage=${this._config.dark_mode_image}
|
||||
></hui-image>
|
||||
${this._elements}
|
||||
</div>
|
||||
|
@@ -179,10 +179,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
let image: string | undefined =
|
||||
(typeof this._config?.image === "object" &&
|
||||
this._config.image.media_content_id) ||
|
||||
(this._config.image as string | undefined);
|
||||
let image: string | undefined = this._config.image;
|
||||
if (this._config.image_entity) {
|
||||
const stateObj: ImageEntity | PersonEntity | undefined =
|
||||
this.hass.states[this._config.image_entity];
|
||||
|
@@ -459,7 +459,7 @@ export interface PictureCardConfig extends LovelaceCardConfig {
|
||||
|
||||
export interface PictureElementsCardConfig extends LovelaceCardConfig {
|
||||
title?: string;
|
||||
image?: string | MediaSelectorValue;
|
||||
image?: string;
|
||||
image_entity?: string;
|
||||
camera_image?: string;
|
||||
camera_view?: HuiImage["cameraView"];
|
||||
@@ -469,7 +469,7 @@ export interface PictureElementsCardConfig extends LovelaceCardConfig {
|
||||
entity?: string;
|
||||
elements: LovelaceElementConfig[];
|
||||
theme?: string;
|
||||
dark_mode_image?: string | MediaSelectorValue;
|
||||
dark_mode_image?: string;
|
||||
dark_mode_filter?: string;
|
||||
}
|
||||
|
||||
@@ -494,7 +494,7 @@ export interface PictureEntityCardConfig extends LovelaceCardConfig {
|
||||
export interface PictureGlanceCardConfig extends LovelaceCardConfig {
|
||||
entities: (string | PictureGlanceEntityConfig)[];
|
||||
title?: string;
|
||||
image?: string | MediaSelectorValue;
|
||||
image?: string;
|
||||
image_entity?: string;
|
||||
camera_image?: string;
|
||||
camera_view?: HuiImage["cameraView"];
|
||||
|
@@ -2,15 +2,7 @@ import memoizeOne from "memoize-one";
|
||||
import { mdiGestureTap } from "@mdi/js";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
any,
|
||||
assert,
|
||||
literal,
|
||||
object,
|
||||
optional,
|
||||
string,
|
||||
union,
|
||||
} from "superstruct";
|
||||
import { any, assert, literal, object, optional, string } from "superstruct";
|
||||
import type { LocalizeFunc } from "../../../../../common/translations/localize";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
@@ -23,7 +15,7 @@ import { actionConfigStruct } from "../../structs/action-struct";
|
||||
const imageElementConfigStruct = object({
|
||||
type: literal("image"),
|
||||
entity: optional(string()),
|
||||
image: optional(union([string(), object()])),
|
||||
image: optional(string()),
|
||||
style: optional(any()),
|
||||
title: optional(string()),
|
||||
tap_action: optional(actionConfigStruct),
|
||||
@@ -95,20 +87,7 @@ export class HuiImageElementEditor
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "image",
|
||||
selector: {
|
||||
media: {
|
||||
accept: ["image/*"] as string[],
|
||||
clearable: true,
|
||||
image_upload: true,
|
||||
hide_content_type: true,
|
||||
content_id_helper: localize(
|
||||
"ui.panel.lovelace.editor.card.picture.content_id_helper"
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: "image", selector: { image: {} } },
|
||||
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
||||
{
|
||||
name: "camera_view",
|
||||
@@ -140,7 +119,7 @@ export class HuiImageElementEditor
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._processData(this._config)}
|
||||
.data=${this._config}
|
||||
.schema=${this._schema(this.hass.localize)}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
@@ -148,13 +127,6 @@ export class HuiImageElementEditor
|
||||
`;
|
||||
}
|
||||
|
||||
private _processData = memoizeOne((config: ImageElementConfig) => ({
|
||||
...config,
|
||||
...(typeof config.image === "string"
|
||||
? { image: { media_content_id: config.image } }
|
||||
: {}),
|
||||
}));
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
@@ -11,7 +11,6 @@ import {
|
||||
optional,
|
||||
string,
|
||||
type,
|
||||
union,
|
||||
} from "superstruct";
|
||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
@@ -38,14 +37,14 @@ const genericElementConfigStruct = type({
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
object({
|
||||
image: optional(union([string(), object()])),
|
||||
image: optional(string()),
|
||||
camera_image: optional(string()),
|
||||
camera_view: optional(string()),
|
||||
elements: array(genericElementConfigStruct),
|
||||
title: optional(string()),
|
||||
state_filter: optional(any()),
|
||||
theme: optional(string()),
|
||||
dark_mode_image: optional(union([string(), object()])),
|
||||
dark_mode_image: optional(string()),
|
||||
dark_mode_filter: optional(any()),
|
||||
})
|
||||
);
|
||||
@@ -77,34 +76,8 @@ export class HuiPictureElementsCardEditor
|
||||
),
|
||||
schema: [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "image",
|
||||
selector: {
|
||||
media: {
|
||||
accept: ["image/*"] as string[],
|
||||
clearable: true,
|
||||
image_upload: true,
|
||||
hide_content_type: true,
|
||||
content_id_helper: localize(
|
||||
"ui.panel.lovelace.editor.card.picture.content_id_helper"
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dark_mode_image",
|
||||
selector: {
|
||||
media: {
|
||||
accept: ["image/*"] as string[],
|
||||
clearable: true,
|
||||
image_upload: true,
|
||||
hide_content_type: true,
|
||||
content_id_helper: localize(
|
||||
"ui.panel.lovelace.editor.card.picture.content_id_helper"
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: "image", selector: { image: {} } },
|
||||
{ name: "dark_mode_image", selector: { image: {} } },
|
||||
{
|
||||
name: "camera_image",
|
||||
selector: { entity: { domain: "camera" } },
|
||||
@@ -151,7 +124,7 @@ export class HuiPictureElementsCardEditor
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._processData(this._config)}
|
||||
.data=${this._config}
|
||||
.schema=${this._schema(this.hass.localize)}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._formChanged}
|
||||
@@ -165,16 +138,6 @@ export class HuiPictureElementsCardEditor
|
||||
`;
|
||||
}
|
||||
|
||||
private _processData = memoizeOne((config: PictureElementsCardConfig) => ({
|
||||
...config,
|
||||
...(typeof config.image === "string"
|
||||
? { image: { media_content_id: config.image } }
|
||||
: {}),
|
||||
...(typeof config.dark_mode_image === "string"
|
||||
? { dark_mode_image: { media_content_id: config.dark_mode_image } }
|
||||
: {}),
|
||||
}));
|
||||
|
||||
private _formChanged(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
if (!this._config || !this.hass) {
|
||||
|
@@ -11,7 +11,6 @@ import {
|
||||
object,
|
||||
optional,
|
||||
string,
|
||||
union,
|
||||
} from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
@@ -41,7 +40,7 @@ const cardConfigStruct = assign(
|
||||
object({
|
||||
title: optional(string()),
|
||||
entity: optional(string()),
|
||||
image: optional(union([string(), object()])),
|
||||
image: optional(string()),
|
||||
image_entity: optional(string()),
|
||||
camera_image: optional(string()),
|
||||
camera_view: optional(enums(["auto", "live"])),
|
||||
@@ -72,20 +71,7 @@ export class HuiPictureGlanceCardEditor
|
||||
(localize: LocalizeFunc) =>
|
||||
[
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "image",
|
||||
selector: {
|
||||
media: {
|
||||
accept: ["image/*"] as string[],
|
||||
clearable: true,
|
||||
image_upload: true,
|
||||
hide_content_type: true,
|
||||
content_id_helper: localize(
|
||||
"ui.panel.lovelace.editor.card.picture.content_id_helper"
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: "image", selector: { image: {} } },
|
||||
{
|
||||
name: "image_entity",
|
||||
selector: { entity: { domain: ["image", "person"] } },
|
||||
@@ -246,10 +232,12 @@ export class HuiPictureGlanceCardEditor
|
||||
`;
|
||||
}
|
||||
|
||||
const data = { camera_view: "auto", fit_mode: "cover", ...this._config };
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._processData(this._config)}
|
||||
.data=${data}
|
||||
.schema=${this._schema(this.hass.localize)}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
.computeHelper=${this._computeHelperCallback}
|
||||
@@ -267,15 +255,6 @@ export class HuiPictureGlanceCardEditor
|
||||
`;
|
||||
}
|
||||
|
||||
private _processData = memoizeOne((config: PictureGlanceCardConfig) => ({
|
||||
camera_view: "auto",
|
||||
fit_mode: "cover",
|
||||
...config,
|
||||
...(typeof config.image === "string"
|
||||
? { image: { media_content_id: config.image } }
|
||||
: {}),
|
||||
}));
|
||||
|
||||
private _goBack(): void {
|
||||
this._subElementEditorConfig = undefined;
|
||||
}
|
||||
|
@@ -153,21 +153,13 @@ export class HuiPictureElementsCardRowEditor extends LitElement {
|
||||
(element as ServiceButtonElementConfig).service ??
|
||||
""
|
||||
);
|
||||
case "image": {
|
||||
if (element.title) {
|
||||
return element.title;
|
||||
}
|
||||
const config = element as ImageElementConfig;
|
||||
if (config.image) {
|
||||
if (typeof config.image === "string") {
|
||||
return config.image;
|
||||
}
|
||||
return (
|
||||
config.image.metadata?.title || config.image.media_content_id || ""
|
||||
);
|
||||
}
|
||||
return config.camera_image || "";
|
||||
}
|
||||
case "image":
|
||||
return (
|
||||
element.title ??
|
||||
(element as ImageElementConfig).image ??
|
||||
(element as ImageElementConfig).camera_image ??
|
||||
""
|
||||
);
|
||||
case "conditional":
|
||||
return (
|
||||
element.title ??
|
||||
|
@@ -50,12 +50,6 @@ export class HuiImageElement extends LitElement implements LovelaceElement {
|
||||
stateObj = this.hass.states[this._config.image_entity] as ImageEntity;
|
||||
}
|
||||
|
||||
const image = stateObj
|
||||
? computeImageUrl(stateObj)
|
||||
: (typeof this._config?.image === "object" &&
|
||||
this._config.image.media_content_id) ||
|
||||
(this._config.image as string | undefined);
|
||||
|
||||
return html`
|
||||
<div
|
||||
@action=${this._handleAction}
|
||||
@@ -73,7 +67,7 @@ export class HuiImageElement extends LitElement implements LovelaceElement {
|
||||
<hui-image
|
||||
.hass=${this.hass}
|
||||
.entity=${this._config.entity}
|
||||
.image=${image}
|
||||
.image=${stateObj ? computeImageUrl(stateObj) : this._config.image}
|
||||
.stateImage=${this._config.state_image}
|
||||
.cameraImage=${this._config.camera_image}
|
||||
.cameraView=${this._config.camera_view}
|
||||
|
@@ -3,7 +3,6 @@ import type { ActionConfig } from "../../../data/lovelace/config/action";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { Condition } from "../common/validate-condition";
|
||||
import type { HuiImage } from "../components/hui-image";
|
||||
import type { MediaSelectorValue } from "../../../data/selector";
|
||||
|
||||
interface LovelaceElementConfigBase {
|
||||
type: string;
|
||||
@@ -46,7 +45,7 @@ export interface ImageElementConfig extends LovelaceElementConfigBase {
|
||||
tap_action?: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
double_tap_action?: ActionConfig;
|
||||
image?: string | MediaSelectorValue;
|
||||
image?: string;
|
||||
image_entity?: string;
|
||||
state_image?: string;
|
||||
camera_image?: string;
|
||||
|
@@ -518,7 +518,6 @@ class HUIRoot extends LitElement {
|
||||
${isSubview
|
||||
? html`
|
||||
<ha-icon-button-arrow-prev
|
||||
.hass=${this.hass}
|
||||
slot="navigationIcon"
|
||||
@click=${this._goBack}
|
||||
></ha-icon-button-arrow-prev>
|
||||
|
@@ -667,8 +667,7 @@
|
||||
"floor_missing": "No floor assigned",
|
||||
"device_missing": "No related device"
|
||||
},
|
||||
"add": "Add",
|
||||
"custom_name": "Custom name"
|
||||
"add": "Add"
|
||||
},
|
||||
"entity-attribute-picker": {
|
||||
"attribute": "Attribute",
|
||||
|
Reference in New Issue
Block a user