diff --git a/.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch b/.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch new file mode 100644 index 0000000000..be555a3f9d --- /dev/null +++ b/.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch @@ -0,0 +1,39 @@ +diff --git a/modular/sortable.complete.esm.js b/modular/sortable.complete.esm.js +index 02e9f2d6bebeb430fe6e7c1cc3f9c3c9df051f14..bb8268b0844a1faa4108cc92c0be2a3dbaf23f83 100644 +--- a/modular/sortable.complete.esm.js ++++ b/modular/sortable.complete.esm.js +@@ -1657,7 +1657,7 @@ Sortable.prototype = + target = parent; // store last element + } + /* jshint boss:true */ +- while (parent = parent.parentNode); ++ while (parent = parent.parentNode || parent.getRootNode().host); + } + + _unhideGhostForTarget(); +diff --git a/modular/sortable.core.esm.js b/modular/sortable.core.esm.js +index b04c8b4634f7c6b4ef1aadbb48afe6564306dea9..39a107163c8c336ebd669b5ea8a936af87e1c1e7 100644 +--- a/modular/sortable.core.esm.js ++++ b/modular/sortable.core.esm.js +@@ -1657,7 +1657,7 @@ Sortable.prototype = + target = parent; // store last element + } + /* jshint boss:true */ +- while (parent = parent.parentNode); ++ while (parent = parent.parentNode || parent.getRootNode().host); + } + + _unhideGhostForTarget(); +diff --git a/modular/sortable.esm.js b/modular/sortable.esm.js +index 6ec7ed1bb557e21c2578200161e989c65d23150b..0a05475a22904472fac6c13f524c674da76584b0 100644 +--- a/modular/sortable.esm.js ++++ b/modular/sortable.esm.js +@@ -1657,7 +1657,7 @@ Sortable.prototype = + target = parent; // store last element + } + /* jshint boss:true */ +- while (parent = parent.parentNode); ++ while (parent = parent.parentNode || parent.getRootNode().host); + } + + _unhideGhostForTarget(); diff --git a/package.json b/package.json index 1ea42597f9..3c2795454f 100644 --- a/package.json +++ b/package.json @@ -248,7 +248,8 @@ "_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch", "resolutions": { "@polymer/polymer": "patch:@polymer/polymer@3.5.1#./.yarn/patches/@polymer/polymer/pr-5569.patch", - "@material/mwc-button@^0.25.3": "^0.27.0" + "@material/mwc-button@^0.25.3": "^0.27.0", + "sortablejs@1.15.0": "patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch" }, "prettier": { "trailingComma": "es5", diff --git a/src/components/ha-outlined-icon-button.ts b/src/components/ha-outlined-icon-button.ts index c4d758e4b5..967575977d 100644 --- a/src/components/ha-outlined-icon-button.ts +++ b/src/components/ha-outlined-icon-button.ts @@ -24,6 +24,11 @@ export class HaOutlinedIconButton extends IconButton { --md-sys-color-on-surface-variant: var(--secondary-text-color); --md-sys-color-on-surface-rgb: var(--rgb-secondary-text-color); } + :host([no-ripple]) .outlined { + --md-ripple-focus-opacity: 0; + --md-ripple-hover-opacity: 0; + --md-ripple-pressed-opacity: 0; + } button { /* Fix md-outlined-icon-button padding for iOS */ padding: 0; diff --git a/src/data/entity_registry.ts b/src/data/entity_registry.ts index a4bdafd949..cf6530b946 100644 --- a/src/data/entity_registry.ts +++ b/src/data/entity_registry.ts @@ -76,7 +76,7 @@ export interface SensorEntityOptions { } export interface LightEntityOptions { - favorites_colors?: LightColor[]; + favorite_colors?: LightColor[]; } export interface NumberEntityOptions { diff --git a/src/data/light.ts b/src/data/light.ts index 3b7371b7b0..8d794423ff 100644 --- a/src/data/light.ts +++ b/src/data/light.ts @@ -107,7 +107,7 @@ export type LightColor = rgbww_color: [number, number, number, number, number]; }; -const FAVORITE_COLOR_COUNT = 6; +const FAVORITE_COLOR_COUNT = 8; export const computeDefaultFavoriteColors = ( stateObj: LightEntity @@ -119,8 +119,6 @@ export const computeDefaultFavoriteColors = ( LightColorMode.COLOR_TEMP ); - const supportsWhite = lightSupportsColorMode(stateObj, LightColorMode.WHITE); - const supportsColor = lightSupportsColor(stateObj); const colorPerMode = @@ -149,9 +147,5 @@ export const computeDefaultFavoriteColors = ( } } - // Remove last color by white mode if supported - if (supportsWhite) { - colors.pop(); - } return colors; }; diff --git a/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts b/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts index 39a6ce6cc1..a5ab759e58 100644 --- a/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts +++ b/src/dialogs/more-info/components/lights/dialog-light-color-favorite.ts @@ -33,6 +33,7 @@ class DialogLightColorFavorite extends LitElement { public closeDialog(): void { this._dialogParams = undefined; this._entry = undefined; + this._color = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -59,10 +60,13 @@ class DialogLightColorFavorite extends LitElement { return nothing; } - const title = this.hass.localize("ui.dialogs.light-color-favorite.title"); - return html` - + - ${title} + ${this._dialogParams?.title} - ${this.editMode - ? html`` - : nothing} - + > `; } static get styles(): CSSResultGroup { return [ css` + :host { + display: block; + } ha-outlined-icon-button { --ha-icon-display: block; --md-sys-color-on-surface: var( @@ -101,6 +101,9 @@ class MoreInfoViewLightColorPicker extends LitElement { var(--rgb-secondary-text-color) ); --md-sys-color-outline: var(--divider-color); + --md-ripple-focus-color: 0; + --md-ripple-hover-opacity: 0; + --md-ripple-pressed-opacity: 0; border-radius: 9999px; } :host([disabled]) { diff --git a/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts b/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts new file mode 100644 index 0000000000..5828777f1c --- /dev/null +++ b/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts @@ -0,0 +1,377 @@ +import { mdiCheck, mdiMinus, mdiPlus } from "@mdi/js"; +import { + css, + CSSResultGroup, + html, + LitElement, + nothing, + PropertyValues, + TemplateResult, +} from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import type { SortableEvent } from "sortablejs"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-control-slider"; +import { UNAVAILABLE } from "../../../../data/entity"; +import { + ExtEntityRegistryEntry, + updateEntityRegistryEntry, +} from "../../../../data/entity_registry"; +import { + computeDefaultFavoriteColors, + LightColor, + LightEntity, +} from "../../../../data/light"; +import { actionHandler } from "../../../../panels/lovelace/common/directives/action-handler-directive"; +import { + loadSortable, + SortableInstance, +} from "../../../../resources/sortable.ondemand"; +import { HomeAssistant } from "../../../../types"; +import { showConfirmationDialog } from "../../../generic/show-dialog-box"; +import "./ha-favorite-color-button"; +import { showLightColorFavoriteDialog } from "./show-dialog-light-color-favorite"; + +@customElement("ha-more-info-light-favorite-colors") +export class HaMoreInfoLightFavoriteColors extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public stateObj!: LightEntity; + + @property({ attribute: false }) public entry?: ExtEntityRegistryEntry | null; + + @property({ attribute: false }) public editMode?: boolean; + + @state() private _favoriteColors: LightColor[] = []; + + private _sortable?: SortableInstance; + + protected updated(changedProps: PropertyValues): void { + if (changedProps.has("editMode")) { + if (this.editMode) { + this._createSortable(); + } else { + this._destroySortable(); + } + } + if (changedProps.has("entry")) { + if (this.entry) { + if (this.entry.options?.light?.favorite_colors) { + this._favoriteColors = this.entry.options.light.favorite_colors; + } else if (this.stateObj) { + this._favoriteColors = computeDefaultFavoriteColors(this.stateObj); + } + } + } + } + + private async _createSortable() { + const Sortable = await loadSortable(); + this._sortable = new Sortable( + this.shadowRoot!.querySelector(".container")!, + { + animation: 150, + fallbackClass: "sortable-fallback", + draggable: ".color", + onChoose: (evt: SortableEvent) => { + (evt.item as any).placeholder = + document.createComment("sort-placeholder"); + evt.item.after((evt.item as any).placeholder); + }, + onEnd: (evt: SortableEvent) => { + // put back in original location + if ((evt.item as any).placeholder) { + (evt.item as any).placeholder.replaceWith(evt.item); + delete (evt.item as any).placeholder; + } + this._dragged(evt); + }, + } + ); + } + + private _dragged(ev: SortableEvent): void { + if (ev.oldIndex === ev.newIndex) return; + this._move(ev.oldIndex!, ev.newIndex!); + } + + private _move(index: number, newIndex: number) { + const favoriteColors = this._favoriteColors.concat(); + const action = favoriteColors.splice(index, 1)[0]; + favoriteColors.splice(newIndex, 0, action); + this._favoriteColors = favoriteColors; + this._save(favoriteColors); + } + + private _destroySortable() { + this._sortable?.destroy(); + this._sortable = undefined; + } + + private _apply = (index: number) => { + const favorite = this._favoriteColors[index]; + this.hass.callService("light", "turn_on", { + entity_id: this.stateObj!.entity_id, + ...favorite, + }); + }; + + private async _save(newFavoriteColors: LightColor[]) { + const result = await updateEntityRegistryEntry( + this.hass, + this.entry!.entity_id, + { + options_domain: "light", + options: { + favorite_colors: newFavoriteColors, + }, + } + ); + fireEvent(this, "entity-entry-updated", result.entity_entry); + } + + private _add = async () => { + const color = await showLightColorFavoriteDialog(this, { + entry: this.entry!, + title: this.hass.localize( + "ui.dialogs.more_info_control.light.favorite_color.add_title" + ), + }); + if (color) { + const newFavoriteColors = [...this._favoriteColors, color]; + this._save(newFavoriteColors); + } + }; + + private _edit = async (index) => { + // Make sure the current favorite color is set + await this._apply(index); + const color = await showLightColorFavoriteDialog(this, { + entry: this.entry!, + title: this.hass.localize( + "ui.dialogs.more_info_control.light.favorite_color.edit_title" + ), + }); + + if (color) { + const newFavoriteColors = [...this._favoriteColors]; + newFavoriteColors[index] = color; + this._save(newFavoriteColors); + } else { + this._apply(index); + } + }; + + private _remove = async (index) => { + const confirm = await showConfirmationDialog(this, { + destructive: true, + title: this.hass.localize( + `ui.dialogs.more_info_control.light.favorite_color.remove_confirm_title` + ), + text: this.hass.localize( + `ui.dialogs.more_info_control.light.favorite_color.remove_confirm_text` + ), + confirmText: this.hass.localize( + `ui.dialogs.more_info_control.light.favorite_color.remove_confirm_action` + ), + }); + if (!confirm) { + return; + } + const newFavoriteColors = this._favoriteColors.filter( + (_, i) => index !== i + ); + this._save(newFavoriteColors); + }; + + private _handleDeleteButton = (ev) => { + ev.stopPropagation(); + const index = ev.target.index; + this._remove(index); + }; + + private _handleAddButton = (ev) => { + ev.stopPropagation(); + this._add(); + }; + + private _handleColorAction = (ev) => { + ev.stopPropagation(); + if (ev.detail.action === "hold" && this.hass.user?.is_admin) { + fireEvent(this, "toggle-edit-mode", true); + return; + } + + const index = ev.target.index; + if (this.editMode) { + this._edit(index); + return; + } + this._apply(index); + }; + + private _exitEditMode = (ev) => { + ev.stopPropagation(); + fireEvent(this, "toggle-edit-mode", false); + }; + + protected render(): TemplateResult { + return html` +
+ ${this._favoriteColors.map( + (color, index) => html` +
+
+ + + ${this.editMode + ? html` + + ` + : nothing} +
+
+ ` + )} + ${this.editMode + ? html` + + + + + + + ` + : nothing} +
+ `; + } + + static get styles(): CSSResultGroup { + return css` + .container { + position: relative; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 12px; + flex-wrap: wrap; + max-width: 250px; + user-select: none; + } + + .container > * { + margin: 8px; + } + + .color { + display: block; + } + + .color .color-bubble.shake { + position: relative; + display: block; + animation: shake 0.45s linear infinite; + } + .color:nth-child(3n + 1) .color-bubble.shake { + animation-delay: 0.15s; + } + .color:nth-child(3n + 2) .color-bubble.shake { + animation-delay: 0.3s; + } + + .sortable-ghost { + opacity: 0.4; + } + .sortable-fallback { + display: none; + } + + @keyframes shake { + 0% { + transform: rotateZ(0deg) translateX(-1px) translateY(0) scale(1); + } + 20% { + transform: rotateZ(-3deg) translateX(0) translateY(); + } + 40% { + transform: rotateZ(0deg) translateX(1px) translateY(0); + } + 60% { + transform: rotateZ(3deg) translateX(0) translateY(0); + } + 100% { + transform: rotateZ(0deg) translateX(-1px) translateY(0); + } + } + + .delete { + position: absolute; + top: -6px; + right: -6px; + width: 20px; + height: 20px; + outline: none; + background-color: var(--secondary-background-color); + padding: 0; + border-radius: 10px; + border: none; + cursor: pointer; + display: block; + } + .delete { + --mdc-icon-size: 12px; + color: var(--primary-text-color); + } + .delete * { + pointer-events: none; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-more-info-light-favorite-colors": HaMoreInfoLightFavoriteColors; + } +} diff --git a/src/dialogs/more-info/components/lights/ha-more-info-view-light-color-picker.ts b/src/dialogs/more-info/components/lights/ha-more-info-view-light-color-picker.ts index 42b7040c57..b556443f5e 100644 --- a/src/dialogs/more-info/components/lights/ha-more-info-view-light-color-picker.ts +++ b/src/dialogs/more-info/components/lights/ha-more-info-view-light-color-picker.ts @@ -16,7 +16,11 @@ class MoreInfoViewLightColorPicker extends LitElement { } return html` - + `; } diff --git a/src/dialogs/more-info/components/lights/light-color-picker.ts b/src/dialogs/more-info/components/lights/light-color-picker.ts index 6d0bf0d3b0..61a9714a3b 100644 --- a/src/dialogs/more-info/components/lights/light-color-picker.ts +++ b/src/dialogs/more-info/components/lights/light-color-picker.ts @@ -42,6 +42,8 @@ class LightColorPicker extends LitElement { @property() public entityId!: string; + @property() public defaultMode!: Mode; + @state() private _cwSliderValue?: number; @state() private _wwSliderValue?: number; @@ -274,11 +276,13 @@ class LightColorPicker extends LitElement { } this._modes = modes; - this._mode = this.stateObj!.attributes.color_mode - ? this.stateObj!.attributes.color_mode === LightColorMode.COLOR_TEMP - ? LightColorMode.COLOR_TEMP - : "color" - : this._modes[0]; + this._mode = + this.defaultMode ?? + (this.stateObj!.attributes.color_mode + ? this.stateObj!.attributes.color_mode === LightColorMode.COLOR_TEMP + ? LightColorMode.COLOR_TEMP + : "color" + : this._modes[0]); } this._updateSliderValues(); diff --git a/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts b/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts index ed6e36cbc8..9af56e6080 100644 --- a/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts +++ b/src/dialogs/more-info/components/lights/show-dialog-light-color-favorite.ts @@ -4,6 +4,7 @@ import { LightColor } from "../../../../data/light"; export interface LightColorFavoriteDialogParams { entry: ExtEntityRegistryEntry; + title: string; submit?: (color?: LightColor) => void; cancel?: () => void; } diff --git a/src/dialogs/more-info/components/lights/show-view-light-color-picker.ts b/src/dialogs/more-info/components/lights/show-view-light-color-picker.ts index c59cc031e9..5f1d6b4e5b 100644 --- a/src/dialogs/more-info/components/lights/show-view-light-color-picker.ts +++ b/src/dialogs/more-info/components/lights/show-view-light-color-picker.ts @@ -2,6 +2,7 @@ import { fireEvent } from "../../../../common/dom/fire_event"; export interface LightColorPickerViewParams { entityId: string; + defaultMode: "color" | "color_temp"; } export const loadLightColorPickerView = () => diff --git a/src/dialogs/more-info/controls/more-info-light.ts b/src/dialogs/more-info/controls/more-info-light.ts index 7889d66a3a..db541897a9 100644 --- a/src/dialogs/more-info/controls/more-info-light.ts +++ b/src/dialogs/more-info/controls/more-info-light.ts @@ -15,8 +15,6 @@ import { PropertyValues, } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { classMap } from "lit/directives/class-map"; -import { fireEvent } from "../../../common/dom/fire_event"; import { stopPropagation } from "../../../common/dom/stop_propagation"; import { computeAttributeNameDisplay, @@ -29,15 +27,10 @@ import "../../../components/ha-button-menu"; import "../../../components/ha-outlined-button"; import "../../../components/ha-outlined-icon-button"; import "../../../components/ha-select"; -import { ON, UNAVAILABLE } from "../../../data/entity"; -import { - ExtEntityRegistryEntry, - updateEntityRegistryEntry, -} from "../../../data/entity_registry"; +import { UNAVAILABLE } from "../../../data/entity"; +import { ExtEntityRegistryEntry } from "../../../data/entity_registry"; import { forwardHaptic } from "../../../data/haptics"; import { - computeDefaultFavoriteColors, - LightColor, LightColorMode, LightEntity, LightEntityFeature, @@ -51,7 +44,7 @@ import "../components/ha-more-info-state-header"; import "../components/ha-more-info-toggle"; import "../components/lights/ha-favorite-color-button"; import "../components/lights/ha-more-info-light-brightness"; -import { showLightColorFavoriteDialog } from "../components/lights/show-dialog-light-color-favorite"; +import "../components/lights/ha-more-info-light-favorite-colors"; import { showLightColorPickerView } from "../components/lights/show-view-light-color-picker"; @customElement("more-info-light") @@ -62,12 +55,12 @@ class MoreInfoLight extends LitElement { @property({ attribute: false }) public entry?: ExtEntityRegistryEntry | null; + @property({ attribute: false }) public editMode?: boolean; + @state() private _effect?: string; @state() private _selectedBrightness?: number; - @state() private _focusedFavoriteIndex?: number; - private _brightnessChanged(ev) { const value = (ev.detail as any).value; if (isNaN(value)) return; @@ -80,25 +73,9 @@ class MoreInfoLight extends LitElement { ? Math.round((this.stateObj.attributes.brightness * 100) / 255) : undefined; this._effect = this.stateObj?.attributes.effect; - - if (this.stateObj?.state !== ON) { - this._focusedFavoriteIndex = undefined; - } } } - private get _favoriteColors(): LightColor[] { - if (this.entry) { - if (this.entry.options?.light?.favorites_colors) { - return this.entry.options.light.favorites_colors; - } - if (this.stateObj) { - return computeDefaultFavoriteColors(this.stateObj); - } - } - return []; - } - protected render() { if (!this.hass || !this.stateObj) { return nothing; @@ -155,74 +132,87 @@ class MoreInfoLight extends LitElement { `} ${supportsColorTemp || supportsColor || supportsBrightness ? html` -
- ${supportsBrightness - ? html` - - ` - : nothing} - ${supportsColor || supportsColorTemp - ? html` - ` - : nothing} - ${supportsWhite +
+
+ ${supportsBrightness + ? html` + + + + ` + : nothing} + ${supportsColor + ? html` + + + ` + : nothing} + ${supportsColorTemp + ? html` + + + ` + : nothing} + ${supportsWhite + ? html` + + + + ` + : nothing} +
+ ${this.editMode || + (this.entry?.options?.light?.favorite_colors?.length ?? 0) > 0 ? html` - - - + ` : nothing} - ${this._favoriteColors.map((color, index) => { - const editMode = this._focusedFavoriteIndex === index; - return html` - - - `; - })}
` : nothing} @@ -285,10 +275,6 @@ class MoreInfoLight extends LitElement { `; } - private _removeFocus = () => { - this._focusedFavoriteIndex = undefined; - }; - private _toggle = () => { const service = this.stateObj?.state === "on" ? "turn_off" : "turn_on"; forwardHaptic("light"); @@ -297,7 +283,7 @@ class MoreInfoLight extends LitElement { }); }; - private _showLightColorPickerView = () => { + private _showLightColorPickerView = (ev) => { showLightColorPickerView( this, this.hass.localize( @@ -305,48 +291,11 @@ class MoreInfoLight extends LitElement { ), { entityId: this.stateObj!.entity_id, + defaultMode: ev.target.mode, } ); }; - private _editFavoriteColor = async (index) => { - // Make sure the current favorite color is set - this._applyFavoriteColor(index); - const color = await showLightColorFavoriteDialog(this, { - entry: this.entry!, - }); - - if (color) { - const newFavoriteColors = [...this._favoriteColors]; - - newFavoriteColors[index] = color; - - const result = await updateEntityRegistryEntry( - this.hass, - this.entry!.entity_id, - { - options_domain: "light", - options: { - favorites_colors: newFavoriteColors, - }, - } - ); - - fireEvent(this, "entity-entry-updated", result.entity_entry); - } else { - this._applyFavoriteColor(index); - } - this._focusedFavoriteIndex = index; - }; - - private _applyFavoriteColor = (index: number) => { - const favorite = this._favoriteColors[index]; - this.hass.callService("light", "turn_on", { - entity_id: this.stateObj!.entity_id, - ...favorite, - }); - }; - private _setWhite = () => { this.hass.callService("light", "turn_on", { entity_id: this.stateObj!.entity_id, @@ -354,19 +303,6 @@ class MoreInfoLight extends LitElement { }); }; - private _handleFavoriteButton = (ev) => { - ev.stopPropagation(); - const index = ev.target.index; - if (this._focusedFavoriteIndex === index) { - this._editFavoriteColor(index); - return; - } - if (this.hass.user?.is_admin) { - this._focusedFavoriteIndex = index; - } - this._applyFavoriteColor(index); - }; - private _handleEffectButton(ev) { ev.stopPropagation(); ev.preventDefault(); @@ -391,12 +327,12 @@ class MoreInfoLight extends LitElement { flex-wrap: wrap; max-width: 250px; } - .color-rgb-mode, + .color-mode, .color-temp-mode { border-radius: 9999px; --md-sys-color-outline: var(--divider-color); } - .color-rgb-mode { + .color-mode { background-image: url("/static/images/color_wheel.png"); background-size: cover; } diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index a35f1d7e4e..b2f56e82a5 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -5,6 +5,8 @@ import { mdiDevices, mdiDotsVertical, mdiInformationOutline, + mdiPencil, + mdiPencilOff, mdiPencilOutline, } from "@mdi/js"; import type { HassEntity } from "home-assistant-js-websocket"; @@ -67,6 +69,9 @@ declare global { interface HASSDomEvents { "show-child-view": ChildView; } + interface HASSDomEvents { + "toggle-edit-mode": boolean; + } } @customElement("ha-more-info-dialog") @@ -83,6 +88,8 @@ export class MoreInfoDialog extends LitElement { @state() private _entry?: ExtEntityRegistryEntry | null; + @state() private _infoEditMode = false; + public showDialog(params: MoreInfoDialogParams) { this._entityId = params.entityId; if (!this._entityId) { @@ -218,6 +225,15 @@ export class MoreInfoDialog extends LitElement { this.closeDialog(); } + private _toggleInfoEditMode(ev) { + if (!shouldHandleRequestSelectedEvent(ev)) return; + this._infoEditMode = !this._infoEditMode; + } + + private _handleToggleInfoEditModeEvent(ev) { + this._infoEditMode = ev.detail; + } + private _goToRelated(ev): void { if (!shouldHandleRequestSelectedEvent(ev)) return; this.setView("related"); @@ -342,6 +358,28 @@ export class MoreInfoDialog extends LitElement { ` : nothing} + ${this._entry && domain === "light" + ? html` + + ${this._infoEditMode + ? this.hass.localize( + `ui.dialogs.more_info_control.exit_edit_mode` + ) + : this.hass.localize( + `ui.dialogs.more_info_control.${domain}.edit_mode` + )} + + + ` + : nothing} ${this._childView ? html` @@ -385,6 +424,7 @@ export class MoreInfoDialog extends LitElement { .hass=${this.hass} .entityId=${this._entityId} .entry=${this._entry} + .editMode=${this._infoEditMode} > ` : this._currView === "history" @@ -428,6 +468,7 @@ export class MoreInfoDialog extends LitElement { super.updated(changedProps); if (changedProps.has("_currView")) { this._childView = undefined; + this._infoEditMode = false; } } diff --git a/src/dialogs/more-info/ha-more-info-info.ts b/src/dialogs/more-info/ha-more-info-info.ts index c690c654a2..cf9fa430aa 100644 --- a/src/dialogs/more-info/ha-more-info-info.ts +++ b/src/dialogs/more-info/ha-more-info-info.ts @@ -19,9 +19,11 @@ import "./more-info-content"; export class MoreInfoInfo extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public entityId!: string; + @property({ attribute: false }) public entityId!: string; - @property() public entry?: ExtEntityRegistryEntry | null; + @property({ attribute: false }) public entry?: ExtEntityRegistryEntry | null; + + @property({ attribute: false }) public editMode?: boolean; protected render() { const entityId = this.entityId; @@ -78,6 +80,7 @@ export class MoreInfoInfo extends LitElement { .stateObj=${stateObj} .hass=${this.hass} .entry=${this.entry} + .editMode=${this.editMode} >
diff --git a/src/dialogs/more-info/more-info-content.ts b/src/dialogs/more-info/more-info-content.ts index 2c72f5a6b1..ead8379b1e 100644 --- a/src/dialogs/more-info/more-info-content.ts +++ b/src/dialogs/more-info/more-info-content.ts @@ -14,6 +14,8 @@ class MoreInfoContent extends ReactiveElement { @property({ attribute: false }) public entry?: ExtEntityRegistryEntry | null; + @property({ attribute: false }) public editMode?: boolean; + private _detachedChild?: ChildNode; protected createRenderRoot() { @@ -26,6 +28,7 @@ class MoreInfoContent extends ReactiveElement { const stateObj = this.stateObj; const entry = this.entry; const hass = this.hass; + const editMode = this.editMode; if (!stateObj || !hass) { if (this.lastChild) { @@ -59,6 +62,7 @@ class MoreInfoContent extends ReactiveElement { hass, stateObj, entry, + editMode, }); } } diff --git a/src/translations/en.json b/src/translations/en.json index 9a9da01be8..411cef44d4 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -869,6 +869,7 @@ "turn_on": "Turn on", "turn_off": "Turn off", "toggle": "Toggle", + "exit_edit_mode": "Exit edit mode", "script": { "last_action": "Last action", "last_triggered": "Last triggered" @@ -926,6 +927,7 @@ "graph_unit": "People in zone" }, "light": { + "edit_mode": "Edit favorite colors", "toggle": "Toggle", "change_color": "Change color", "set_white": "Set white", @@ -940,7 +942,14 @@ }, "favorite_color": { "set": "Set favorite color {number}", - "edit": "Edit favorite color {number}" + "edit": "Edit favorite color {number}", + "remove": "Remove favorite color {number}", + "remove_confirm_title": "Remove favorite color?", + "remove_confirm_text": "This favorite color will be permanently removed.", + "remove_confirm_action": "Remove", + "add": "Add new favorite color", + "edit_title": "Edit favorite color", + "add_title": "Add favorite color" } }, "fan": { @@ -1336,9 +1345,6 @@ "title": "Enter code", "input_label": "Code" }, - "light-color-favorite": { - "title": "Edit favorite color" - }, "tts-try": { "header": "Try text-to-speech", "message": "Message", diff --git a/yarn.lock b/yarn.lock index eb0c885b56..e4da57f172 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14473,6 +14473,13 @@ __metadata: languageName: node linkType: hard +"sortablejs@patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch::locator=home-assistant-frontend%40workspace%3A.": + version: 1.15.0 + resolution: "sortablejs@patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch::version=1.15.0&hash=29e891&locator=home-assistant-frontend%40workspace%3A." + checksum: 9a5c899bc4ec1a99732a44511ef7188ae0aba59b98bce66b952590a33a12e0779cebe8664a5565815facfeb0a32072418a2447eb690a5d3c234981126462da7e + languageName: node + linkType: hard + "source-list-map@npm:^2.0.1": version: 2.0.1 resolution: "source-list-map@npm:2.0.1"