mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
fix v1.15.3: center device icon badge on its anchor (box-sizing: border-box; 1px border no longer shifts it down-right). +demo/smoke_icon_center. Docs in same commit.
This commit is contained in:
@@ -11,7 +11,7 @@ PLANS_DIR = "houseplan/plans" # relative to the HA configuration directory
|
|||||||
FILES_URL = "/houseplan_files/files"
|
FILES_URL = "/houseplan_files/files"
|
||||||
FILES_DIR = "houseplan/files"
|
FILES_DIR = "houseplan/files"
|
||||||
CONF_ADMIN_ONLY = "admin_only"
|
CONF_ADMIN_ONLY = "admin_only"
|
||||||
VERSION = "1.15.2"
|
VERSION = "1.15.3"
|
||||||
|
|
||||||
DEFAULT_CONFIG: dict = {
|
DEFAULT_CONFIG: dict = {
|
||||||
"spaces": [],
|
"spaces": [],
|
||||||
|
|||||||
@@ -315,6 +315,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
|
|||||||
}
|
}
|
||||||
.dev {
|
.dev {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
box-sizing: border-box; /* рамка 1px не должна смещать центр значка от точки привязки */
|
||||||
width: var(--icon-size, 2.5cqw);
|
width: var(--icon-size, 2.5cqw);
|
||||||
height: var(--icon-size, 2.5cqw);
|
height: var(--icon-size, 2.5cqw);
|
||||||
margin: calc(var(--icon-size, 2.5cqw) / -2) 0 0 calc(var(--icon-size, 2.5cqw) / -2);
|
margin: calc(var(--icon-size, 2.5cqw) / -2) 0 0 calc(var(--icon-size, 2.5cqw) / -2);
|
||||||
@@ -1310,4 +1311,4 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`}}Yt.properties={hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_markup:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},Yt.styles=Lt,customElements.get("houseplan-card")||customElements.define("houseplan-card",Yt),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info("%c HOUSEPLAN-CARD %c v1.15.2 ","background:#3ea6ff;color:#04121f;font-weight:700","");
|
</div>`}}Yt.properties={hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_markup:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},Yt.styles=Lt,customElements.get("houseplan-card")||customElements.define("houseplan-card",Yt),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info("%c HOUSEPLAN-CARD %c v1.15.3 ","background:#3ea6ff;color:#04121f;font-weight:700","");
|
||||||
|
|||||||
@@ -16,5 +16,5 @@
|
|||||||
"issue_tracker": "https://github.com/Matysh/houseplan-card/issues",
|
"issue_tracker": "https://github.com/Matysh/houseplan-card/issues",
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"single_config_entry": true,
|
"single_config_entry": true,
|
||||||
"version": "1.15.2"
|
"version": "1.15.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Asserts device icon badges sit exactly on their anchor point (no content-box/border drift).
|
||||||
|
import { launch } from './serve.mjs';
|
||||||
|
const { page, browser } = await launch({ width: 900, height: 1000 }, 2);
|
||||||
|
const bad = await page.evaluate(() => {
|
||||||
|
const c = window.__card;
|
||||||
|
c._space = 'f1'; c._resetZoom(); c.requestUpdate();
|
||||||
|
const layer = c.renderRoot.querySelector('.devlayer');
|
||||||
|
const lr = layer.getBoundingClientRect();
|
||||||
|
const bad = [];
|
||||||
|
for (const d of layer.querySelectorAll('.dev')) {
|
||||||
|
const ax = lr.left + (parseFloat(d.style.left) / 100) * lr.width;
|
||||||
|
const ay = lr.top + (parseFloat(d.style.top) / 100) * lr.height;
|
||||||
|
const r = d.getBoundingClientRect();
|
||||||
|
const offX = r.left + r.width / 2 - ax, offY = r.top + r.height / 2 - ay;
|
||||||
|
if (Math.abs(offX) > 0.6 || Math.abs(offY) > 0.6)
|
||||||
|
bad.push({ icon: d.querySelector('ha-icon')?.getAttribute('icon'), offX: +offX.toFixed(2), offY: +offY.toFixed(2) });
|
||||||
|
}
|
||||||
|
return bad;
|
||||||
|
});
|
||||||
|
await browser.close();
|
||||||
|
if (bad.length) { console.error('FAIL icon-center: badges off anchor', JSON.stringify(bad.slice(0, 5))); process.exit(1); }
|
||||||
|
console.log('OK icon-center: all device badges centred on their anchor');
|
||||||
File diff suppressed because one or more lines are too long
Vendored
+2
-1
@@ -315,6 +315,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
|
|||||||
}
|
}
|
||||||
.dev {
|
.dev {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
box-sizing: border-box; /* рамка 1px не должна смещать центр значка от точки привязки */
|
||||||
width: var(--icon-size, 2.5cqw);
|
width: var(--icon-size, 2.5cqw);
|
||||||
height: var(--icon-size, 2.5cqw);
|
height: var(--icon-size, 2.5cqw);
|
||||||
margin: calc(var(--icon-size, 2.5cqw) / -2) 0 0 calc(var(--icon-size, 2.5cqw) / -2);
|
margin: calc(var(--icon-size, 2.5cqw) / -2) 0 0 calc(var(--icon-size, 2.5cqw) / -2);
|
||||||
@@ -1310,4 +1311,4 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`}}Yt.properties={hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_markup:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},Yt.styles=Lt,customElements.get("houseplan-card")||customElements.define("houseplan-card",Yt),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info("%c HOUSEPLAN-CARD %c v1.15.2 ","background:#3ea6ff;color:#04121f;font-weight:700","");
|
</div>`}}Yt.properties={hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_markup:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},Yt.styles=Lt,customElements.get("houseplan-card")||customElements.define("houseplan-card",Yt),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info("%c HOUSEPLAN-CARD %c v1.15.3 ","background:#3ea6ff;color:#04121f;font-weight:700","");
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v1.15.3 — 2026-07-08 (fix: device icon 1px off its anchor point)
|
||||||
|
- Device icon badges were sitting **1 px down-and-right of their true point**: `.dev`
|
||||||
|
used the default `content-box`, so the 1 px border made the rendered square 2 px
|
||||||
|
wider than the width the centering margin assumed. Added `box-sizing: border-box` —
|
||||||
|
the badge centre now lands exactly on the device coordinate (verified in the demo:
|
||||||
|
anchor offset 1 px → 0). The glyph itself was already centred within the square.
|
||||||
|
|
||||||
## v1.15.2 — 2026-07-08 (fix: room average temperature counted non-thermometers)
|
## v1.15.2 — 2026-07-08 (fix: room average temperature counted non-thermometers)
|
||||||
- **Average room temperature now uses only devices the card treats as thermometers**
|
- **Average room temperature now uses only devices the card treats as thermometers**
|
||||||
(`mdi:thermometer` / `mdi:air-filter`). Previously `areaTemp` swept every device in the
|
(`mdi:thermometer` / `mdi:air-filter`). Previously `areaTemp` swept every device in the
|
||||||
|
|||||||
+3
-1
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
| Item | State |
|
| Item | State |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Version | **v1.15.2** everywhere (manifest, const.py, package.json, CARD_VERSION) |
|
| Version | **v1.15.3** everywhere (manifest, const.py, package.json, CARD_VERSION) |
|
||||||
| GitHub | https://github.com/Matysh/houseplan-card — branch `main`, releases v1.9.3…v1.11.2 |
|
| GitHub | https://github.com/Matysh/houseplan-card — branch `main`, releases v1.9.3…v1.11.2 |
|
||||||
| CI | `.github/workflows/validate.yml` (hacs + hassfest + frontend + backend) — **fully green** since v1.11.1; `release.yml` auto-attaches the card bundle (needs `permissions: contents: write`, fixed) |
|
| CI | `.github/workflows/validate.yml` (hacs + hassfest + frontend + backend) — **fully green** since v1.11.1; `release.yml` auto-attaches the card bundle (needs `permissions: contents: write`, fixed) |
|
||||||
| HACS | Works as custom repository (id 1290210112 on the home instance). **Inclusion PR: https://github.com/hacs/default/pull/9004** (queue ≈2 months as of 2026-07). Lesson: #8995 was auto-closed by hacs-bot — the PR body MUST be their exact template with every checkbox ticked and all 3 links (release, HACS action run, hassfest run); a custom body gets closed without discussion |
|
| HACS | Works as custom repository (id 1290210112 on the home instance). **Inclusion PR: https://github.com/hacs/default/pull/9004** (queue ≈2 months as of 2026-07). Lesson: #8995 was auto-closed by hacs-bot — the PR body MUST be their exact template with every checkbox ticked and all 3 links (release, HACS action run, hassfest run); a custom body gets closed without discussion |
|
||||||
@@ -53,6 +53,8 @@
|
|||||||
darken-on-hover, wider space dialog.
|
darken-on-hover, wider space dialog.
|
||||||
- **v1.15.2** — fix: average room temperature (fill + tooltip) counted non-thermometers
|
- **v1.15.2** — fix: average room temperature (fill + tooltip) counted non-thermometers
|
||||||
(fridges/TRVs/chip `*_device_temperature`/diagnostic); now thermometer/air-monitor only.
|
(fridges/TRVs/chip `*_device_temperature`/diagnostic); now thermometer/air-monitor only.
|
||||||
|
- **v1.15.3** — fix: device icon badge sat 1 px off its anchor (content-box + 1 px
|
||||||
|
border); `box-sizing: border-box` centres it exactly on the device point.
|
||||||
|
|
||||||
## Where things live
|
## Where things live
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
|||||||
- [ ] Average room temperature counts ONLY thermometer/air-monitor devices — fridges, TRV heads,
|
- [ ] Average room temperature counts ONLY thermometer/air-monitor devices — fridges, TRV heads,
|
||||||
smart-plug chip temperatures (`*_device_temperature`) and diagnostic-category temps are excluded [auto]
|
smart-plug chip temperatures (`*_device_temperature`) and diagnostic-category temps are excluded [auto]
|
||||||
- [ ] Space dialog is 500 px wide; the comfort-bounds inputs are compact (56 px)
|
- [ ] Space dialog is 500 px wide; the comfort-bounds inputs are compact (56 px)
|
||||||
|
- [ ] Device icon badge is centred exactly on its point (no 1 px down-right drift) [auto]
|
||||||
- [ ] Room hover highlight still works when custom borders/fills are on
|
- [ ] Room hover highlight still works when custom borders/fills are on
|
||||||
- [ ] Settings persist across reload and other browsers (server-side)
|
- [ ] Settings persist across reload and other browsers (server-side)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "houseplan-card",
|
"name": "houseplan-card",
|
||||||
"version": "1.15.2",
|
"version": "1.15.3",
|
||||||
"description": "Interactive house plan Lovelace card for Home Assistant",
|
"description": "Interactive house plan Lovelace card for Home Assistant",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import './editor';
|
|||||||
import { cardStyles } from './styles';
|
import { cardStyles } from './styles';
|
||||||
import { langOf, t, type I18nKey } from './i18n';
|
import { langOf, t, type I18nKey } from './i18n';
|
||||||
|
|
||||||
const CARD_VERSION = '1.15.2';
|
const CARD_VERSION = '1.15.3';
|
||||||
const LS_KEY = 'houseplan_card_layout_v1';
|
const LS_KEY = 'houseplan_card_layout_v1';
|
||||||
const LS_CFG = 'houseplan_card_cfg_v1'; // cache of the server config+layout for instant rendering
|
const LS_CFG = 'houseplan_card_cfg_v1'; // cache of the server config+layout for instant rendering
|
||||||
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
||||||
|
|||||||
@@ -312,6 +312,7 @@ export const cardStyles = css`
|
|||||||
}
|
}
|
||||||
.dev {
|
.dev {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
box-sizing: border-box; /* рамка 1px не должна смещать центр значка от точки привязки */
|
||||||
width: var(--icon-size, 2.5cqw);
|
width: var(--icon-size, 2.5cqw);
|
||||||
height: var(--icon-size, 2.5cqw);
|
height: var(--icon-size, 2.5cqw);
|
||||||
margin: calc(var(--icon-size, 2.5cqw) / -2) 0 0 calc(var(--icon-size, 2.5cqw) / -2);
|
margin: calc(var(--icon-size, 2.5cqw) / -2) 0 0 calc(var(--icon-size, 2.5cqw) / -2);
|
||||||
|
|||||||
Reference in New Issue
Block a user