feat v1.9.0: перетаскивание значков всегда (убран режим правки) + UX-правки (расталкивание, фиксация позиций, нумерация дублей, «показать все», подсказка разметки, адаптивный тулбар); README и скриншоты обновлены
@@ -105,6 +105,8 @@ title: План дома
|
|||||||
|
|
||||||
Как только вы сохранили комнату с привязкой к зоне, **устройства этой зоны автоматически расставляются внутри контура**. Берутся те же устройства, что показаны на странице **Настройки → Устройства → (фильтр по нужной комнате)** — только осмысленные, без служебных записей, мостов и дубликатов.
|
Как только вы сохранили комнату с привязкой к зоне, **устройства этой зоны автоматически расставляются внутри контура**. Берутся те же устройства, что показаны на странице **Настройки → Устройства → (фильтр по нужной комнате)** — только осмысленные, без служебных записей, мостов и дубликатов.
|
||||||
|
|
||||||
|
По умолчанию на план попадают только осмысленные устройства — служебные записи, мосты и дубликаты отфильтрованы. Если нужно видеть **вообще все** устройства зоны, включите в шапке кнопку **👁 «Показать все устройства»**.
|
||||||
|
|
||||||
Дальше можно просто пользоваться планом: клик по иконке открывает карточку устройства с моделью, ссылкой и кнопкой перехода в Home Assistant.
|
Дальше можно просто пользоваться планом: клик по иконке открывает карточку устройства с моделью, ссылкой и кнопкой перехода в Home Assistant.
|
||||||
|
|
||||||

|

|
||||||
@@ -115,15 +117,15 @@ title: План дома
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Шаг 5. Правка расположения
|
### Шаг 5. Расставьте значки по местам
|
||||||
|
|
||||||
Кнопка **✥ (режим правки)** позволяет перетаскивать иконки мышью, ставя их точно по местам. Позиции сохраняются на сервере и одинаковы во всех браузерах.
|
Значки устройств можно **перетаскивать мышью в любой момент** — отдельный «режим правки» включать не нужно. Позиции сохраняются на сервере и одинаковы во всех браузерах и устройствах. Кнопка **↺** в шапке возвращает автоматическую раскладку.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Шаг 6. Добавление своих устройств вручную
|
### Шаг 6. Добавление своих устройств вручную
|
||||||
|
|
||||||
Не всё нужно оставлять на автоматику. Кнопкой **+ (добавить устройство)** в режиме правки можно поставить на план любое устройство, группу или **виртуальную точку** (например, «Вентиль на вводе», которого нет как устройства). Задайте имя, иконку, модель, ссылку, описание и при желании приложите **PDF-инструкцию**.
|
Не всё нужно оставлять на автоматику. Кнопкой **+** в шапке можно поставить на план любое устройство, группу или **виртуальную точку** (например, «Вентиль на вводе», которого нет как устройства). Задайте имя, иконку, модель, ссылку, описание и при желании приложите **PDF-инструкцию**.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -142,9 +144,9 @@ title: План дома
|
|||||||
|
|
||||||
**Нужно ли что-то писать в YAML?** Нет. Единственная строчка — это добавление карточки на дашборд; всё остальное делается мышкой.
|
**Нужно ли что-то писать в YAML?** Нет. Единственная строчка — это добавление карточки на дашборд; всё остальное делается мышкой.
|
||||||
|
|
||||||
**Мои устройства не появились на плане.** Устройство появляется, только если его зона в Home Assistant привязана к нарисованной комнате. Проверьте, что у устройства задана комната (Настройки → Устройства), а комната обведена и привязана к этой зоне.
|
**Мои устройства не появились на плане.** Устройство появляется, только если его зона в Home Assistant привязана к нарисованной комнате. Проверьте, что у устройства задана комната (Настройки → Устройства), а комната обведена и привязана к этой зоне. Если устройство есть, но скрыто курированием (мосты, служебные, дубликаты) — включите в шапке кнопку **👁 «Показать все устройства»**.
|
||||||
|
|
||||||
**Можно ли скрыть лишнее устройство или переименовать его?** Да — в режиме правки откройте устройство и отредактируйте имя, иконку или скройте его.
|
**Можно ли скрыть лишнее устройство или переименовать его?** Да — кликните по устройству на плане и в его карточке нажмите «Редактировать»: там можно сменить имя, иконку, модель или скрыть значок.
|
||||||
|
|
||||||
**Данные хранятся в облаке?** Нет. Всё хранится локально в вашем Home Assistant.
|
**Данные хранятся в облаке?** Нет. Всё хранится локально в вашем Home Assistant.
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ PLANS_DIR = "houseplan/plans" # относительно каталога ко
|
|||||||
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.8.4"
|
VERSION = "1.9.0"
|
||||||
|
|
||||||
DEFAULT_CONFIG: dict = {
|
DEFAULT_CONFIG: dict = {
|
||||||
"spaces": [],
|
"spaces": [],
|
||||||
|
|||||||
@@ -15,5 +15,5 @@
|
|||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"issue_tracker": "https://github.com/justbusiness/houseplan-card/issues",
|
"issue_tracker": "https://github.com/justbusiness/houseplan-card/issues",
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"version": "1.8.4"
|
"version": "1.9.0"
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,22 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v1.9.0 — 2026-07-05 (UX: перетаскивание всегда, расталкивание, показать все, адаптив)
|
||||||
|
- **Перетаскивание значков доступно всегда** — отдельный «режим правки» убран (кнопка ✥ удалена).
|
||||||
|
Клик по значку открывает карточку устройства (правка метаданных — через кнопку «Редактировать»
|
||||||
|
в ней), перетаскивание работает в любой момент. `_pointerDown` больше не требует `_edit`.
|
||||||
|
- **Значки не скучиваются** (`_declump`): после авто-раскладки по сетке выполняется расталкивание
|
||||||
|
в пределах комнаты, чтобы иконки не налезали друг на друга.
|
||||||
|
- **Позиции фиксируются при сохранении комнаты**: авто-позиции устройств зоны один раз пишутся
|
||||||
|
в раскладку, поэтому при смене порядка в реестре HA значки не перетасовываются.
|
||||||
|
- **Дубли не скрываются, а нумеруются**: устройства с одинаковым «имя|зона» получают суффикс
|
||||||
|
(« 2», « 3»…) вместо тихого исчезновения.
|
||||||
|
- **Кнопка 👁 «Показать все устройства»** в шапке: временно отключает курирование и показывает
|
||||||
|
все устройства зоны (мосты, служебные, дубли) — на случай, если нужное скрыто. Флаг `show_all`
|
||||||
|
в настройках конфига.
|
||||||
|
- **Подсказка продолжения разметки**: после сохранения комнаты тост показывает счётчик комнат и
|
||||||
|
предлагает обвести следующую или выйти из разметки.
|
||||||
|
- **Адаптивная шапка**: тулбар переносится и ужимается на узких экранах (media-query ≤620px).
|
||||||
|
|
||||||
## v1.8.4 — 2026-07-04 (онбординг: обязательные поля + ведение пользователя)
|
## v1.8.4 — 2026-07-04 (онбординг: обязательные поля + ведение пользователя)
|
||||||
- Первичная инициализация приведена к целевому сценарию: установка → диалог пространства →
|
- Первичная инициализация приведена к целевому сценарию: установка → диалог пространства →
|
||||||
комнаты → авто-значки устройств.
|
комнаты → авто-значки устройств.
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 326 KiB After Width: | Height: | Size: 323 KiB |
|
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 290 KiB |
|
Before Width: | Height: | Size: 1010 KiB After Width: | Height: | Size: 1012 KiB |
|
Before Width: | Height: | Size: 820 KiB After Width: | Height: | Size: 823 KiB |
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 198 KiB |
|
Before Width: | Height: | Size: 321 KiB After Width: | Height: | Size: 299 KiB |
|
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 292 KiB |
|
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 223 KiB |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "houseplan-card",
|
"name": "houseplan-card",
|
||||||
"version": "1.8.4",
|
"version": "1.9.0",
|
||||||
"description": "Interactive house plan Lovelace card for Home Assistant (dacha Kirillovskoe)",
|
"description": "Interactive house plan Lovelace card for Home Assistant (dacha Kirillovskoe)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
} from './logic';
|
} from './logic';
|
||||||
import './editor';
|
import './editor';
|
||||||
|
|
||||||
const CARD_VERSION = '1.8.4';
|
const CARD_VERSION = '1.9.0';
|
||||||
const LS_KEY = 'houseplan_card_layout_v1';
|
const LS_KEY = 'houseplan_card_layout_v1';
|
||||||
const LS_CFG = 'houseplan_card_cfg_v1'; // кэш серверного конфига+раскладки для мгновенного рендера
|
const LS_CFG = 'houseplan_card_cfg_v1'; // кэш серверного конфига+раскладки для мгновенного рендера
|
||||||
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
||||||
@@ -64,7 +64,7 @@ interface Marker {
|
|||||||
interface ServerConfig {
|
interface ServerConfig {
|
||||||
spaces: any[];
|
spaces: any[];
|
||||||
markers: Marker[];
|
markers: Marker[];
|
||||||
settings: { exclude_integrations?: string[]; group_lights?: boolean };
|
settings: { exclude_integrations?: string[]; group_lights?: boolean; show_all?: boolean };
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DevItem {
|
interface DevItem {
|
||||||
@@ -363,6 +363,19 @@ class HouseplanCard extends LitElement {
|
|||||||
return this._serverCfg?.settings || {};
|
return this._serverCfg?.settings || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get _showAll(): boolean {
|
||||||
|
return !!this._settings.show_all;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleShowAll(): void {
|
||||||
|
if (!this._serverCfg) return;
|
||||||
|
this._serverCfg = { ...this._serverCfg, settings: { ...this._serverCfg.settings, show_all: !this._showAll } };
|
||||||
|
this._regSignature = '';
|
||||||
|
this._maybeRebuildDevices();
|
||||||
|
this._saveConfig();
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
private get _excluded(): Set<string> {
|
private get _excluded(): Set<string> {
|
||||||
const list = this._settings.exclude_integrations;
|
const list = this._settings.exclude_integrations;
|
||||||
return list ? new Set(list) : EXCLUDED_DOMAINS;
|
return list ? new Set(list) : EXCLUDED_DOMAINS;
|
||||||
@@ -636,9 +649,10 @@ class HouseplanCard extends LitElement {
|
|||||||
const groupLights = this._settings.group_lights !== false;
|
const groupLights = this._settings.group_lights !== false;
|
||||||
const groupedAreas = this._groupedLightAreas();
|
const groupedAreas = this._groupedLightAreas();
|
||||||
const excluded = this._excluded;
|
const excluded = this._excluded;
|
||||||
|
const showAll = this._showAll;
|
||||||
const entsBy = this._entitiesByDevice();
|
const entsBy = this._entitiesByDevice();
|
||||||
const claimed = this._claimed;
|
const claimed = this._claimed;
|
||||||
const seen: Record<string, 1> = {};
|
const seen: Record<string, number> = {};
|
||||||
const rest: DevItem[] = [];
|
const rest: DevItem[] = [];
|
||||||
|
|
||||||
// 1) авто-устройства HA (не занятые маркером, не скрытые)
|
// 1) авто-устройства HA (не занятые маркером, не скрытые)
|
||||||
@@ -651,21 +665,25 @@ class HouseplanCard extends LitElement {
|
|||||||
if (marker && marker.hidden) continue;
|
if (marker && marker.hidden) continue;
|
||||||
const entIds = entsBy[dev.id] || [];
|
const entIds = entsBy[dev.id] || [];
|
||||||
const dom = this._domainOfDevice(dev, entIds);
|
const dom = this._domainOfDevice(dev, entIds);
|
||||||
if (excluded.has(dom)) continue;
|
// курирование (можно отключить переключателем «показать все»)
|
||||||
if (dev.model === 'Group') continue;
|
if (!showAll) {
|
||||||
if (/scene/i.test(dev.model || '')) continue;
|
if (excluded.has(dom)) continue;
|
||||||
if (/bridge/i.test((dev.model || '') + (dev.name || ''))) continue;
|
if (dev.model === 'Group') continue;
|
||||||
if (dom === 'myheat' && dev.via_device_id) continue;
|
if (/scene/i.test(dev.model || '')) continue;
|
||||||
|
if (/bridge/i.test((dev.model || '') + (dev.name || ''))) continue;
|
||||||
|
if (dom === 'myheat' && dev.via_device_id) continue;
|
||||||
|
}
|
||||||
const name = (dev.name_by_user || dev.name || 'без имени').trim();
|
const name = (dev.name_by_user || dev.name || 'без имени').trim();
|
||||||
const key = name + '|' + area;
|
const key = name + '|' + area;
|
||||||
if (seen[key]) continue;
|
|
||||||
seen[key] = 1;
|
|
||||||
let icon = iconFor(name, dev.model);
|
let icon = iconFor(name, dev.model);
|
||||||
if (entIds.some((e) => e.startsWith('lock.'))) icon = 'mdi:lock';
|
if (entIds.some((e) => e.startsWith('lock.'))) icon = 'mdi:lock';
|
||||||
if (groupLights && icon === 'mdi:lightbulb' && groupedAreas.has(area)) continue;
|
if (!showAll && groupLights && icon === 'mdi:lightbulb' && groupedAreas.has(area)) continue;
|
||||||
|
// дубли по «имя|зона» не скрываем, а нумеруем
|
||||||
|
seen[key] = (seen[key] || 0) + 1;
|
||||||
|
const dispName = seen[key] > 1 ? name + ' ' + seen[key] : name;
|
||||||
const item: DevItem = {
|
const item: DevItem = {
|
||||||
id: dev.id,
|
id: dev.id,
|
||||||
name,
|
name: dispName,
|
||||||
model: dev.model || '',
|
model: dev.model || '',
|
||||||
area,
|
area,
|
||||||
space: areaMap[area].space,
|
space: areaMap[area].space,
|
||||||
@@ -782,29 +800,64 @@ class HouseplanCard extends LitElement {
|
|||||||
|
|
||||||
private _defaultPositions(): Record<string, { x: number; y: number }> {
|
private _defaultPositions(): Record<string, { x: number; y: number }> {
|
||||||
const map: Record<string, { x: number; y: number }> = {};
|
const map: Record<string, { x: number; y: number }> = {};
|
||||||
|
const iconPct = this._config?.icon_size ?? 2.5;
|
||||||
|
const minDist = (iconPct / 100) * NORM_W * 1.3; // не ближе диаметра значка + запас
|
||||||
for (const s of this._model) {
|
for (const s of this._model) {
|
||||||
for (const r of s.rooms) {
|
for (const r of s.rooms) {
|
||||||
if (!r.area) continue;
|
if (!r.area) continue;
|
||||||
const ds = this._devices.filter((d) => d.area === r.area && d.space === s.id);
|
const ds = this._devices.filter((d) => d.area === r.area && d.space === s.id);
|
||||||
if (!ds.length) continue;
|
if (!ds.length) continue;
|
||||||
const b = this._roomBounds(r);
|
const b = this._roomBounds(r);
|
||||||
const pad = Math.min(b.w, b.h) * 0.12;
|
const pad = Math.min(b.w, b.h) * 0.1;
|
||||||
const iw = b.w - pad * 2;
|
const iw = b.w - pad * 2;
|
||||||
const ih = b.h - pad * 2;
|
const ih = b.h - pad * 2;
|
||||||
const cols = Math.max(1, Math.round(Math.sqrt((ds.length * iw) / Math.max(ih, 1))));
|
const cols = Math.max(1, Math.round(Math.sqrt((ds.length * iw) / Math.max(ih, 1))));
|
||||||
const rows = Math.ceil(ds.length / cols);
|
const rows = Math.ceil(ds.length / cols);
|
||||||
const cw = iw / cols;
|
const cw = iw / cols;
|
||||||
const ch = ih / Math.max(rows, 1);
|
const ch = ih / Math.max(rows, 1);
|
||||||
ds.forEach((d, i) => {
|
const pts = ds.map((_, i) => ({
|
||||||
const c = i % cols;
|
x: b.x + pad + cw * ((i % cols) + 0.5),
|
||||||
const rw = Math.floor(i / cols);
|
y: b.y + pad + ch * (Math.floor(i / cols) + 0.5),
|
||||||
map[d.id] = { x: b.x + pad + cw * (c + 0.5), y: b.y + pad + ch * (rw + 0.5) };
|
}));
|
||||||
});
|
this._declump(pts, b, minDist, pad * 0.5);
|
||||||
|
ds.forEach((d, i) => (map[d.id] = pts[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Расталкивание значков, чтобы не скучивались (в пределах комнаты). */
|
||||||
|
private _declump(
|
||||||
|
pts: { x: number; y: number }[],
|
||||||
|
b: { x: number; y: number; w: number; h: number },
|
||||||
|
minDist: number,
|
||||||
|
pad: number,
|
||||||
|
): void {
|
||||||
|
if (pts.length < 2) return;
|
||||||
|
const minX = b.x + pad, maxX = b.x + b.w - pad, minY = b.y + pad, maxY = b.y + b.h - pad;
|
||||||
|
for (let it = 0; it < 60; it++) {
|
||||||
|
let moved = false;
|
||||||
|
for (let i = 0; i < pts.length; i++) {
|
||||||
|
for (let j = i + 1; j < pts.length; j++) {
|
||||||
|
const dx = pts[j].x - pts[i].x, dy = pts[j].y - pts[i].y;
|
||||||
|
const dist = Math.hypot(dx, dy) || 0.001;
|
||||||
|
if (dist < minDist) {
|
||||||
|
const push = (minDist - dist) / 2;
|
||||||
|
const ux = dx / dist, uy = dy / dist;
|
||||||
|
pts[i].x -= ux * push; pts[i].y -= uy * push;
|
||||||
|
pts[j].x += ux * push; pts[j].y += uy * push;
|
||||||
|
moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const q of pts) {
|
||||||
|
q.x = Math.max(minX, Math.min(maxX, q.x));
|
||||||
|
q.y = Math.max(minY, Math.min(maxY, q.y));
|
||||||
|
}
|
||||||
|
if (!moved) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Позиция устройства в рендер-единицах текущего пространства. */
|
/** Позиция устройства в рендер-единицах текущего пространства. */
|
||||||
private _pos(d: DevItem): { x: number; y: number } {
|
private _pos(d: DevItem): { x: number; y: number } {
|
||||||
const s = this._spaceModel(d.space);
|
const s = this._spaceModel(d.space);
|
||||||
@@ -883,11 +936,6 @@ class HouseplanCard extends LitElement {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
if (this._drag?.moved || this._suppressClick) return;
|
if (this._drag?.moved || this._suppressClick) return;
|
||||||
if (this._markup) return;
|
if (this._markup) return;
|
||||||
if (this._edit) {
|
|
||||||
this._selId = d.id;
|
|
||||||
this._openMarkerDialog(d);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._infoCard = d;
|
this._infoCard = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1086,12 +1134,12 @@ class HouseplanCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _clickRoom(r: RoomCfg): void {
|
private _clickRoom(r: RoomCfg): void {
|
||||||
if (this._suppressClick || this._edit || !r.area) return;
|
if (this._suppressClick || !r.area) return;
|
||||||
navigate('/config/areas/area/' + r.area);
|
navigate('/config/areas/area/' + r.area);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _pointerDown(ev: PointerEvent, d: DevItem): void {
|
private _pointerDown(ev: PointerEvent, d: DevItem): void {
|
||||||
if (!this._edit) return;
|
if (this._markup) return; // в режиме разметки значки не тащим
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const p = this._pos(d);
|
const p = this._pos(d);
|
||||||
this._drag = { id: d.id, sx: ev.clientX, sy: ev.clientY, ox: p.x, oy: p.y, moved: false };
|
this._drag = { id: d.id, sx: ev.clientX, sy: ev.clientY, ox: p.x, oy: p.y, moved: false };
|
||||||
@@ -1363,12 +1411,30 @@ class HouseplanCard extends LitElement {
|
|||||||
this._roomDialog = false;
|
this._roomDialog = false;
|
||||||
this._regSignature = '';
|
this._regSignature = '';
|
||||||
this._maybeRebuildDevices();
|
this._maybeRebuildDevices();
|
||||||
// авто-добавление значков устройств зоны — через _defaultPositions (сетка внутри контура)
|
// авто-добавление значков устройств зоны + ФИКСАЦИЯ их позиций в раскладке,
|
||||||
const added = boundArea ? this._devices.filter((d) => d.area === boundArea).length : 0;
|
// чтобы при смене порядка в реестре HA значки не перетасовывались.
|
||||||
|
let added = 0;
|
||||||
|
if (boundArea) {
|
||||||
|
const aspect = this._serverCfg?.spaces.find((x: any) => x.id === this._space)?.aspect || 1;
|
||||||
|
const H2 = NORM_W / aspect;
|
||||||
|
const next = { ...this._layout };
|
||||||
|
for (const d of this._devices) {
|
||||||
|
if (d.area !== boundArea || d.space !== this._space) continue;
|
||||||
|
added++;
|
||||||
|
if (this._layout[d.id]) continue; // размещено вручную — не трогаем
|
||||||
|
const dp = this._defPos[d.id];
|
||||||
|
if (!dp) continue;
|
||||||
|
next[d.id] = { s: this._space, x: dp.x / NORM_W, y: dp.y / H2 };
|
||||||
|
this._dirtyPos.add(d.id);
|
||||||
|
}
|
||||||
|
this._layout = next;
|
||||||
|
this._persistLayout();
|
||||||
|
}
|
||||||
|
const roomsN = this._model.find((s) => s.id === this._space)?.rooms.length || 0;
|
||||||
this._showToast(
|
this._showToast(
|
||||||
boundArea
|
boundArea
|
||||||
? `Комната сохранена — добавлено устройств: ${added}`
|
? `Комната сохранена (${roomsN}). Устройств добавлено: ${added}. Обведите следующую или выйдите из разметки.`
|
||||||
: 'Комната сохранена (без зоны)',
|
: `Комната сохранена (${roomsN}, без зоны). Обведите следующую или выйдите из разметки.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1863,23 +1929,24 @@ class HouseplanCard extends LitElement {
|
|||||||
<ha-icon icon="mdi:plus-box-outline"></ha-icon>
|
<ha-icon icon="mdi:plus-box-outline"></ha-icon>
|
||||||
</button>`
|
</button>`
|
||||||
: nothing}
|
: nothing}
|
||||||
<button class="btn ${this._edit ? 'on' : ''}" @click=${() => {
|
${this._norm
|
||||||
this._edit = !this._edit;
|
? html`<button class="btn ${this._showAll ? 'on' : ''}" @click=${this._toggleShowAll}
|
||||||
this._markup = false;
|
title="Показывать все устройства зоны (без курирования)">
|
||||||
this._selId = null;
|
<ha-icon icon="${this._showAll ? 'mdi:eye' : 'mdi:eye-off-outline'}"></ha-icon>
|
||||||
}} title="Режим правки: клик по значку — карточка, перетаскивание — двигать">
|
</button>
|
||||||
<ha-icon icon="mdi:cursor-move"></ha-icon>
|
<button class="btn" @click=${this._resetLayout} title="Сбросить позиции значков к авто-раскладке">
|
||||||
</button>
|
<ha-icon icon="mdi:backup-restore"></ha-icon>
|
||||||
|
</button>`
|
||||||
|
: nothing}
|
||||||
<button class="btn ${this._markup ? 'on' : ''}" @click=${this._toggleMarkup}
|
<button class="btn ${this._markup ? 'on' : ''}" @click=${this._toggleMarkup}
|
||||||
title="Разметка комнат: сетка, линии, контуры">
|
title="Разметка комнат: сетка, линии, контуры">
|
||||||
<ha-icon icon="mdi:vector-square-edit"></ha-icon>
|
<ha-icon icon="mdi:vector-square-edit"></ha-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
${this._edit ? this._renderEditbar() : nothing}
|
|
||||||
${this._markup ? this._renderMarkupBar() : nothing}
|
${this._markup ? this._renderMarkupBar() : nothing}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stage ${this._edit ? 'edit' : ''} ${this._markup ? 'markup' : ''}"
|
<div class="stage ${this._markup ? 'markup' : ''}"
|
||||||
style="height:calc(100dvh - 118px)"
|
style="height:calc(100dvh - 118px)"
|
||||||
@click=${(e: MouseEvent) => this._markupClick(e)}
|
@click=${(e: MouseEvent) => this._markupClick(e)}
|
||||||
@wheel=${(e: WheelEvent) => this._onWheel(e)}
|
@wheel=${(e: WheelEvent) => this._onWheel(e)}
|
||||||
@@ -2355,6 +2422,12 @@ class HouseplanCard extends LitElement {
|
|||||||
background: rgba(127, 127, 127, 0.12);
|
background: rgba(127, 127, 127, 0.12);
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
@media (max-width: 620px) {
|
||||||
|
.head { gap: 6px; padding: 8px 10px; }
|
||||||
|
.head .count { display: none; }
|
||||||
|
.head .title { font-size: 14px; }
|
||||||
}
|
}
|
||||||
.tab {
|
.tab {
|
||||||
border: 0;
|
border: 0;
|
||||||
@@ -2569,7 +2642,7 @@ class HouseplanCard extends LitElement {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: var(--hp-txt);
|
color: var(--hp-txt);
|
||||||
cursor: pointer;
|
cursor: grab;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
transition: background 0.15s, border-color 0.15s, opacity 0.2s;
|
transition: background 0.15s, border-color 0.15s, opacity 0.2s;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
|
||||||
@@ -2578,6 +2651,9 @@ class HouseplanCard extends LitElement {
|
|||||||
.dev ha-icon {
|
.dev ha-icon {
|
||||||
--mdc-icon-size: calc(var(--icon-size, 2.5cqw) * 0.62);
|
--mdc-icon-size: calc(var(--icon-size, 2.5cqw) * 0.62);
|
||||||
}
|
}
|
||||||
|
.dev:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
.dev:hover {
|
.dev:hover {
|
||||||
background: var(--hp-accent);
|
background: var(--hp-accent);
|
||||||
color: var(--text-primary-color, #fff);
|
color: var(--text-primary-color, #fff);
|
||||||
@@ -2604,9 +2680,7 @@ class HouseplanCard extends LitElement {
|
|||||||
border-color: #ffc14d;
|
border-color: #ffc14d;
|
||||||
box-shadow: 0 0 0 3px rgba(255, 193, 77, 0.35);
|
box-shadow: 0 0 0 3px rgba(255, 193, 77, 0.35);
|
||||||
}
|
}
|
||||||
.stage.edit .dev {
|
|
||||||
cursor: grab;
|
|
||||||
}
|
|
||||||
.dev .tval {
|
.dev .tval {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
|
|||||||