fix: make devices inert in the Background editor

Issue: #362
User-Visible: yes
This commit is contained in:
Sergey Matyunin
2026-08-29 06:39:28 +00:00
committed by claude[bot]
parent f6da1ff57c
commit 5d37438e11
23 changed files with 351 additions and 158 deletions
+20 -5
View File
@@ -5177,9 +5177,12 @@ export class HouseplanCard extends LitElement {
}
private _clickDevice(ev: Event, d: DevItem): void {
// Devices are passive landmarks in Plan/Background. Guard before stopping
// propagation so a Background tool still owns the same canvas point even
// if a future CSS change accidentally makes a marker a hit target again.
if (this._mode !== 'view' && this._mode !== 'devices') return;
ev.stopPropagation();
if (this._drag?.moved || this._suppressClick || this._holdFired) return;
if (this._mode === 'plan') return;
if (this._mode === 'devices') {
this._openMarkerDialog(d);
return;
@@ -6335,7 +6338,10 @@ export class HouseplanCard extends LitElement {
}
private _pointerDown(ev: PointerEvent, d: DevItem): void {
if (this._mode === 'plan') return; // icons are hidden in plan mode anyway
// Only View and the Devices editor own device input. In Background the
// event must keep bubbling to the active decor tool; do not prevent it,
// capture it or create a layout drag.
if (this._mode !== 'view' && this._mode !== 'devices') return;
if (this._mode === 'view') {
// view: no drag, no capture — panning may start on an icon; only the
// long-press timer runs (cancelled by stage movement)
@@ -6361,6 +6367,7 @@ export class HouseplanCard extends LitElement {
}
private _pointerMove(ev: PointerEvent, d: DevItem): void {
if (this._mode !== 'devices') return;
if (!this._drag || this._drag.id !== d.id) return;
const stage = this.renderRoot.querySelector('.stage') as HTMLElement;
if (!stage) return;
@@ -6387,6 +6394,7 @@ export class HouseplanCard extends LitElement {
private _pointerUp(_ev: PointerEvent, d: DevItem): void {
clearTimeout(this._holdTimer);
if (this._mode !== 'devices') return;
if (!this._drag || this._drag.id !== d.id) return;
const moved = this._drag.moved;
this._drag = moved ? this._drag : null;
@@ -12017,11 +12025,14 @@ export class HouseplanCard extends LitElement {
@click=${(e: MouseEvent) => this._clickDevice(e, d)}
@keydown=${(e: KeyboardEvent) => this._keyDevice(e, d)}
@contextmenu=${(e: MouseEvent) => this._ctxDevice(e, d)}
@pointerover=${(e: PointerEvent) =>
this._showTip(e, d.name, presentation.haDisabled ? ghostLabel : metrics)}
@pointerover=${(e: PointerEvent) => {
if (this._mode !== 'view' && this._mode !== 'devices') return;
this._showTip(e, d.name, presentation.haDisabled ? ghostLabel : metrics);
}}
@pointerleave=${() => this._clearTransientHover()}
@pointerdown=${(e: PointerEvent) => this._pointerDown(e, d)}
@pointermove=${(e: PointerEvent) => {
if (this._mode !== 'view' && this._mode !== 'devices') return;
this._pointerMove(e, d);
this._showTip(e, d.name, presentation.haDisabled ? ghostLabel : metrics);
}}
@@ -12527,7 +12538,11 @@ export class HouseplanCard extends LitElement {
const top = ((point[1] - view.y) / view.h) * 100;
return html`<div class="oplock ${deviceThemeClass(this._renderPlanHass)} ${locked ? 'locked' : known ? 'unlocked' : 'unknown'}"
style="left:${left}%;top:${top}%"
@click=${(e: MouseEvent) => { e.stopPropagation(); if (this._mode === 'view') this._openingInfo = o; }}>
@click=${(e: MouseEvent) => {
if (this._mode !== 'view') return;
e.stopPropagation();
this._openingInfo = o;
}}>
<span class="oplock-shell" aria-hidden="true">
<span class="oplock-core">
<ha-icon icon="${locked ? 'mdi:lock' : known ? 'mdi:lock-open-variant' : 'mdi:lock-question'}"></ha-icon>
+12 -2
View File
@@ -853,8 +853,18 @@ export const planStyles = css`
.stage.mode-decor.dtool-erase .decorlayer .dshape {
cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg transform='rotate(-45 12 12)'%3E%3Crect x='7' y='2' width='10' height='18' rx='2' fill='%23fff' stroke='%23111' stroke-width='1.5'/%3E%3Cpath d='M7 13h10v5a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2z' fill='%23ff9f43' stroke='%23111' stroke-width='1.5'/%3E%3C/g%3E%3C/svg%3E") 5 22, pointer;
}
.stage.mode-decor .room, .stage.mode-decor .devlayer { pointer-events: none; }
.stage.mode-decor .oplock { pointer-events: none; }
.stage.mode-decor .room { pointer-events: none; }
/* Devices are landmarks in Background, never editing targets. The marker
package deliberately re-enables pointer events on the core, its 44 px
pseudo hit area and the visible capsule for View/Devices. A none on
devlayer alone therefore does not make its HTML descendants inert.
Scope the boundary to the whole subtree (including the pseudo element)
so a Background tool receives the exact point below every visible part. */
.stage.mode-decor .devlayer,
.stage.mode-decor .devlayer *,
.stage.mode-decor .dev::before {
pointer-events: none;
}
/* Backdrop-editor de-emphasis is a shared mode-transition coordinate.
It multiplies whole presentation groups and never changes Glow source
alpha, additive blending, or the underlying resolved state. */