mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 05:08:53 +00:00
fix: openings and markers keep their identity across a space switch (#525)
Lit reuses list nodes by position. The opening list and the device markers had no keys, so on a space switch the leaf that held a slot kept its DOM node and only changed values — and `.op-leaf` (transform) and `.op-arc` (stroke-dashoffset) carry a 0.6 s transition, so the browser animated a door that never moved: the new floor's leaf drove in from the previous floor's opening angle. Measured on two spaces with a door in the same place and opposite contact states: the node is reused, transform goes `rotate(-90deg) → rotate(0deg)`, dash offset `0 → 125.66`, both transitions `running`. The marker shell adds two more with its `box-shadow`. Both lists are now rendered through `repeat(…, (item) => item.id, …)`, the same lesson `glow-scene.ts` already learned for the Glow spots. The trap is written where it starts — above the two transitions in `plan.styles.ts` — because that is the file someone edits when adding the next animated property. `houseplan-card.ts` is at its line ceiling, and the note would have cost the budget a dozen lines for nothing: the swap itself is line-for-line. The witness walks the shadow tree per element. `document.getAnimations()` is empty here EVEN ON THE BROKEN CODE — the card lives in a shadow root and the document-level call does not reach into it, and the issue proposed exactly that call. The smoke also builds its own fixture: the demo home has no openings at all, so two doors in two spaces are prepared in the smoke, and it asserts the other half of the contract as well — a real contact change inside one space still animates the leaf. On `origin/dev` the smoke fails on five facts, naming the offenders: `op-arc:stroke-dashoffset`, `op-leaf:transform`, `device-shell-frame:box-shadow` twice. Mutants `openings-rendered-without-keys` and `device-markers-rendered-without-keys` put each `map` back. Perf, 7 samples against `19e421b3`: spaceSwitchMs 524.8 (limit 769.35, base 512.9), switchCycleMs 1293.9 (1696.28, 1256.5), firstStableRenderMs 2533.8 (3000, 2529.2), modelReadyMs 732.7 (944.97, 726.9), longTask.maxSingleMs 663 (910, 660) — `benchmark:compare` green in full. The initial View graph grows 241 B gzip: `repeat` enters it for the first time. The #438 ceiling is recentred 300 400 → 300 700 with the usual dated note; measured 300 059 B keeps 641 B above and 1 359 B below the band. The 301 066 B budget is untouched, but only 366 B now separate the ceiling from it — the #367 headroom debt has stopped being theoretical. Issue: #525 User-Visible: yes
This commit is contained in:
@@ -11784,7 +11784,7 @@ export class HouseplanCard extends LitElement {
|
||||
view,
|
||||
(point) => this._scenePoint(point),
|
||||
)}
|
||||
${devs.map((d) => this._renderDevice(
|
||||
${repeat(devs, (d) => d.id, (d) => this._renderDevice(
|
||||
d, view, showLqi, isoOverlays?.devices.get(d.id),
|
||||
))}
|
||||
${this._renderVacuums(this._renderVacuumDevices, view, space.id)}
|
||||
@@ -12993,7 +12993,7 @@ export class HouseplanCard extends LitElement {
|
||||
const walls = this._spaceWalls;
|
||||
const openCuts = this._openCuts();
|
||||
const openingWallIndex = this._openingWallIndexFor(space, openCuts).value;
|
||||
return svg`<g class="openinglayer">${items.map((o) => {
|
||||
return svg`<g class="openinglayer">${repeat(items, (o) => o.id, (o) => {
|
||||
if (o.orphanReason) return svg`<g class="opening orphan" data-hp="opening-orphan"
|
||||
data-id=${o.id} role="button" tabindex="0"
|
||||
aria-label=${this._t('opening.partition_orphan')}
|
||||
|
||||
@@ -519,7 +519,18 @@ export const planStyles = css`
|
||||
stroke: var(--hp-accent);
|
||||
stroke-opacity: 1;
|
||||
}
|
||||
/* doors, windows & gates */
|
||||
/* doors, windows & gates
|
||||
|
||||
#525: these two transitions are the reason the opening list is rendered
|
||||
with repeat(items, (o) => o.id, ...) and not a bare map(). Lit reuses
|
||||
list nodes by POSITION, so on a space switch the leaf that held a slot
|
||||
kept its DOM node and only changed values — and the browser dutifully
|
||||
animated a door that never moved: the new floor's leaf drove in from the
|
||||
previous floor's opening angle, 0.6 s of an event that did not happen.
|
||||
Anything animated that is rendered from a list needs a key. The Glow
|
||||
spots learned it first (glow-scene.ts, opacity), the openings and the
|
||||
device markers (.device-shell-frame, box-shadow) with this issue.
|
||||
Witness: demo/smoke_space_switch_transitions.mjs. */
|
||||
.op-leaf {
|
||||
transition: transform 0.6s ease;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user