mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-07 23:19:14 +00:00
fix(space-card): key room shapes by space and id so a config change never repaints a stranger (#745)
The space card drew its rooms with a bare map(), so Lit reused room nodes by
position, and `.room { transition: 0.12s }` (planStyles is part of this card's
styles too) drew a node's fill and stroke in from whichever room held it
before. Two paths change the room set in the same DOM: a new `space` in
setConfig of the same element (the card editor's preview), and a config event
from any device that inserts, removes, reorders or re-zones a room of the
shown space. Filled rooms faded out and back in for ~0.12 s, unfilled ones
briefly darkened in a filled room's place.
The list is now keyed(space.id, repeat(rooms, (r, i) => r.id || i, ...)),
the same shape as the full card after #742: the outer key handles the space
change, the inner one keeps a node bound to its room inside a space. An
id-less room keys by its numeric index, which never equals a string id. The
transition itself stays: it smooths a real fill change on the same room. The
#742 note in plan.styles.ts now names the space card as well.
Witness: a new section of smoke_space_card. The config is delivered by a
server push (__hpTest.setServerConfig), the event the card subscribes to.
Red on dev: node r1 reused for g1 with fill/fill-opacity transitions and a
first-frame fill of rgba(0, 0, 0, 0) / 0; a room inserted first shifts all
four nodes and replays fill transitions. A real custom_fill change still runs
a fill transition on the same node (catches `transition: none`). One mutant:
inner key replaced by map(), guarded by AC2 (checked by hand: red).
Issue: #745
User-Visible: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
This commit is contained in:
+8
-4
@@ -7,6 +7,8 @@
|
||||
* Geometry/model math lives in space-geometry.ts (pure, unit-tested).
|
||||
*/
|
||||
import { html, svg, nothing, type TemplateResult } from 'lit';
|
||||
import { keyed } from 'lit/directives/keyed.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import {
|
||||
buildDevices, areaLqi, effectiveExcludedIntegrations, roomClimateKey, roomClimateMap, sourceValue,
|
||||
resolvedLightSources, resolvedLightState,
|
||||
@@ -531,9 +533,11 @@ export function renderSpaceStatic(o: StaticRenderOpts): TemplateResult | null {
|
||||
for (const room of space.rooms) if (room.id) {
|
||||
roomFillsById.set(room.id, resolvedRoomFills.get(room) || null);
|
||||
}
|
||||
const roomShapes = space.rooms
|
||||
.filter((r) => r.area || disp.showBorders || roomFillModeOf(disp.fill, r) !== 'none')
|
||||
.map((r) => {
|
||||
const shownRooms = space.rooms
|
||||
.filter((r) => r.area || disp.showBorders || roomFillModeOf(disp.fill, r) !== 'none');
|
||||
// #745: keyed like the full card's rooms (plan.styles.ts, #525/#534/#742/#745): a bare map() handed a
|
||||
// room node to a stranger on a new `space` or a config event, and `.room`'s transition drew its fill in.
|
||||
const roomShapes = keyed(space.id, repeat(shownRooms, (r, index) => r.id || index, (r) => {
|
||||
let cls = 'room ' + (space.bg ? 'overlay' : 'yard');
|
||||
let style = '';
|
||||
// tier 3 wins over the space, exactly as on the full card (HP-1454-07)
|
||||
@@ -563,7 +567,7 @@ export function renderSpaceStatic(o: StaticRenderOpts): TemplateResult | null {
|
||||
: svg`<rect class="${cls}" style="${style}" data-hp="room" data-id=${hpId} data-area=${hpArea}
|
||||
x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="${Math.min(r.w!, r.h!) * 0.03}"></rect>`;
|
||||
return shape;
|
||||
});
|
||||
}));
|
||||
|
||||
// Base projection is independent of radial pools: opt-in pools are painted
|
||||
// above it through the same room-level Glow gates as the full plan.
|
||||
|
||||
Reference in New Issue
Block a user