mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-06 14:39:22 +00:00
fix: satisfy render performance validation gates
Issue: #451 User-Visible: no
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
* 2) LEGACY fallback — baked-in country-house data (src/data/*), coordinates in a 1489×1053 canvas.
|
||||
* The icon layout is stored on the server (houseplan/layout/*), fallback — localStorage.
|
||||
*/
|
||||
import { LitElement, html, svg, nothing, noChange, TemplateResult, PropertyValues } from 'lit';
|
||||
import { LitElement, html, svg, nothing, noChange, TemplateResult, PropertyValues, type PropertyDeclaration } from 'lit';
|
||||
import { guard } from 'lit/directives/guard.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import './hp-dialog';
|
||||
@@ -306,6 +306,7 @@ import {
|
||||
type RenderDeviceSnapshot,
|
||||
} from './render-device-snapshot';
|
||||
import { RenderLifecycle, intakeHass } from './houseplan-render-lifecycle';
|
||||
import type { HassRenderSnapshot } from './render-invalidation';
|
||||
import { deviceFaceStyle, deviceThemeClass, renderDeviceFace } from './device-face';
|
||||
import { effectiveDeviceBaseSize } from './device-marker-geometry'; import { renderZigbeeTopologyOverlay } from './zigbee-topology-overlay-bridge';
|
||||
import {
|
||||
@@ -657,11 +658,12 @@ type FixedFloorState = FixedFloorSelection | { kind: 'pending'; value: unknown }
|
||||
const HANDLE_PAINT_ORDER = ['edges', 'corners'] as const;
|
||||
|
||||
export class HouseplanCard extends LitElement {
|
||||
public requestUpdate(name?: PropertyKey, oldValue?: unknown, options?: any): void {
|
||||
public requestUpdate(name?: PropertyKey, oldValue?: unknown, options?: PropertyDeclaration): void {
|
||||
if (name === 'hass' && this.hass && this._liveRt) {
|
||||
const snapshot = this._visibleDeviceSnapshot || this._candidateDeviceSnapshot;
|
||||
const render = this._liveRt.hass(
|
||||
oldValue, this.hass, snapshot ? { entityIds: snapshot.entityIds } : null,
|
||||
oldValue as HassRenderSnapshot | null | undefined, this.hass,
|
||||
snapshot ? { entityIds: snapshot.entityIds } : null,
|
||||
() => intakeHass(this),
|
||||
);
|
||||
if (!render) return;
|
||||
@@ -4042,7 +4044,7 @@ export class HouseplanCard extends LitElement {
|
||||
}
|
||||
if (changed.has('hass') && this.hass) {
|
||||
const snapshot = this._visibleDeviceSnapshot || this._candidateDeviceSnapshot;
|
||||
this._renderLife.observe(changed.get('hass'), this.hass,
|
||||
this._renderLife.observe(changed.get('hass') as HassRenderSnapshot | null | undefined, this.hass,
|
||||
snapshot ? { entityIds: snapshot.entityIds } : null, () => intakeHass(this));
|
||||
}
|
||||
if (this._continuity.hasCompleteFrame && this._continuity.state === 'steady') {
|
||||
@@ -4500,7 +4502,7 @@ export class HouseplanCard extends LitElement {
|
||||
private _haRegistryConnection: any = null;
|
||||
private _haRegistryRev = -1;
|
||||
private _haBindingCacheKey = '';
|
||||
private _planHassMemo: { hass: any; sig: string; active: any; full: any } | null = null;
|
||||
private _planHassMemo: { hass: unknown; sig: string; active: unknown; full: unknown } | null = null;
|
||||
private _renderLife = new RenderLifecycle();
|
||||
private _liveEditorPaintCount = 0;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
haRegistryDiagnostics,
|
||||
type HaBindingStatus,
|
||||
} from './ha-binding-status';
|
||||
import type { HassRenderDependencies } from './render-invalidation';
|
||||
import type { HassRenderDependencies, HassRenderSnapshot } from './render-invalidation';
|
||||
|
||||
export interface HouseplanDiagnosticsCore {
|
||||
registry: ReturnType<typeof haRegistryDiagnostics>;
|
||||
@@ -13,12 +13,12 @@ type DiagnosticMarker = { removed?: boolean; binding: string };
|
||||
|
||||
/** State kept outside the already oversized card element (#34). */
|
||||
export class RenderLifecycle {
|
||||
private seen: any = null;
|
||||
private seen: unknown = null;
|
||||
private diagnosticsCache: HouseplanDiagnosticsCore | null = null;
|
||||
|
||||
observe(
|
||||
before: any,
|
||||
after: any,
|
||||
before: HassRenderSnapshot | null | undefined,
|
||||
after: HassRenderSnapshot | null | undefined,
|
||||
dependencies: HassRenderDependencies | null,
|
||||
intake: () => void,
|
||||
): void {
|
||||
@@ -26,7 +26,7 @@ export class RenderLifecycle {
|
||||
this.intake(after, intake);
|
||||
}
|
||||
|
||||
intake(snapshot: any, run: () => void): void {
|
||||
intake(snapshot: unknown, run: () => void): void {
|
||||
if (!snapshot || this.seen === snapshot) return;
|
||||
this.seen = snapshot;
|
||||
run();
|
||||
@@ -37,7 +37,9 @@ export class RenderLifecycle {
|
||||
}
|
||||
|
||||
private invalidateDiagnosticsForHass(
|
||||
before: any, after: any, dependencies: HassRenderDependencies | null,
|
||||
before: HassRenderSnapshot | null | undefined,
|
||||
after: HassRenderSnapshot | null | undefined,
|
||||
dependencies: HassRenderDependencies | null,
|
||||
): void {
|
||||
if (!this.diagnosticsCache) return;
|
||||
if (before?.entities !== after?.entities || before?.devices !== after?.devices
|
||||
@@ -48,7 +50,7 @@ export class RenderLifecycle {
|
||||
}
|
||||
|
||||
diagnostics(
|
||||
hass: any,
|
||||
hass: unknown,
|
||||
markers: readonly DiagnosticMarker[],
|
||||
resolve: (binding: string) => HaBindingStatus,
|
||||
): HouseplanDiagnosticsCore {
|
||||
@@ -66,7 +68,24 @@ export class RenderLifecycle {
|
||||
}
|
||||
|
||||
/** Operational work that must survive a deliberately skipped visual update. */
|
||||
export function intakeHass(host: any): void {
|
||||
interface HassIntakeHost {
|
||||
_hassSequence: number;
|
||||
_renderSnapshotAt: number;
|
||||
_continuity: { note: (reason: string) => void };
|
||||
_ensureHaRegistryAuthority: () => void;
|
||||
_planHassMemo: unknown;
|
||||
_hookConnection: () => void;
|
||||
_loadOk: boolean;
|
||||
_loading: boolean;
|
||||
_loadTries: number;
|
||||
_loadFromServer: () => unknown;
|
||||
_maybeRebuildDevices: () => void;
|
||||
_vacTick: () => void;
|
||||
_activityTick: () => void;
|
||||
}
|
||||
|
||||
export function intakeHass(value: object): void {
|
||||
const host = value as HassIntakeHost;
|
||||
host._hassSequence++;
|
||||
host._renderSnapshotAt = Date.now();
|
||||
host._continuity.note('hass-snapshot');
|
||||
|
||||
+63
-15
@@ -6,6 +6,48 @@ interface LiveEditorState {
|
||||
hidden: HTMLElement[];
|
||||
}
|
||||
|
||||
interface LiveEditorHost {
|
||||
isConnected: boolean;
|
||||
renderRoot: ParentNode;
|
||||
requestUpdate: () => void;
|
||||
_mode: 'view' | 'plan' | 'devices' | 'decor';
|
||||
_deviceDrag: { id: string } | null;
|
||||
_physicalDrag?: unknown;
|
||||
_physicalRotate?: unknown;
|
||||
_decorDraft?: unknown;
|
||||
_decorMove?: unknown;
|
||||
_dtDrag?: unknown;
|
||||
_bdDrag?: unknown;
|
||||
_opDrag?: unknown;
|
||||
_resize?: { dragging: boolean };
|
||||
_layout: unknown;
|
||||
_dtBox: { id: string; x: number; y: number; w: number; h: number } | null;
|
||||
_dtSel: { id: string; kind: string } | null;
|
||||
_opMeasureView: { guide: unknown } | null;
|
||||
_devices: { id: string }[];
|
||||
_liveEditorPaintCount: number;
|
||||
_baseVb: () => number[];
|
||||
_viewOr: (viewBox: number[]) => { x: number; y: number; w: number; h: number };
|
||||
_spaceDisplayForRender: () => unknown;
|
||||
_renderWallBodies: (display: unknown) => unknown;
|
||||
_renderResizeMeasurements: () => unknown;
|
||||
_renderOpenings: (display: unknown) => unknown;
|
||||
_renderResizeLayer: (view: unknown) => unknown;
|
||||
_renderMarkupLayer: (viewBox: number[]) => unknown;
|
||||
_renderHiddenWallDiagnosticOverlay: () => unknown;
|
||||
_renderOpeningPlacementPreview: () => unknown;
|
||||
_renderOpeningDimensionGuides: (measure: unknown) => unknown;
|
||||
_renderOpeningCenterTick: (guide: unknown) => unknown;
|
||||
_renderActiveChainInk: () => unknown;
|
||||
_renderPlanSnapOverlay: () => unknown;
|
||||
_renderWallThickUi: () => unknown;
|
||||
_renderDecorLayer: () => unknown;
|
||||
_renderBackdropFrame: (view: unknown) => unknown;
|
||||
_renderTextFrame: (view: unknown) => unknown;
|
||||
_livePos: (device: { id: string }) => { x: number; y: number };
|
||||
_scenePoint: (point: number[]) => number[];
|
||||
}
|
||||
|
||||
const states = new WeakMap<object, LiveEditorState>();
|
||||
|
||||
const liveProperties = new Set<PropertyKey>([
|
||||
@@ -26,7 +68,7 @@ const stateOf = (host: object): LiveEditorState => {
|
||||
return state;
|
||||
};
|
||||
|
||||
const activeEditorGesture = (host: any): boolean => host._mode !== 'view' && (
|
||||
const activeEditorGesture = (host: LiveEditorHost): boolean => host._mode !== 'view' && (
|
||||
!!host._deviceDrag || !!host._physicalDrag || !!host._physicalRotate
|
||||
|| !!host._decorDraft || !!host._decorMove || !!host._dtDrag || !!host._bdDrag
|
||||
|| !!host._opDrag
|
||||
@@ -39,16 +81,19 @@ const activeEditorGesture = (host: any): boolean => host._mode !== 'view' && (
|
||||
* and terminal null assignments still receive a normal host update.
|
||||
*/
|
||||
export function routeHouseplanEditorUpdate(
|
||||
host: any,
|
||||
value: object,
|
||||
name?: PropertyKey,
|
||||
oldValue?: unknown,
|
||||
): boolean {
|
||||
const host = value as LiveEditorHost;
|
||||
if (!host?.isConnected || host._mode === 'view') return false;
|
||||
const namedLive = name !== undefined && liveProperties.has(name);
|
||||
if (name === '_layout' && !host._deviceDrag) return false;
|
||||
if (name === '_dtBox' && !host._dtDrag) return false;
|
||||
if (name !== undefined && gestureProperties.has(name)
|
||||
&& oldValue == null && host[name] != null && activeEditorGesture(host)) {
|
||||
&& oldValue == null
|
||||
&& (host as unknown as Record<PropertyKey, unknown>)[name] != null
|
||||
&& activeEditorGesture(host)) {
|
||||
// Pointerdown may change selection/chrome once. Subsequent moves stay live.
|
||||
return false;
|
||||
}
|
||||
@@ -74,7 +119,7 @@ const restore = (state: LiveEditorState): void => {
|
||||
state.hidden.length = 0;
|
||||
};
|
||||
|
||||
const planTemplate = (host: any): TemplateResult => {
|
||||
const planTemplate = (host: LiveEditorHost): TemplateResult => {
|
||||
const vb = host._baseVb();
|
||||
const view = host._viewOr(vb);
|
||||
const measure = host._opMeasureView;
|
||||
@@ -99,7 +144,7 @@ const planTemplate = (host: any): TemplateResult => {
|
||||
</g>`;
|
||||
};
|
||||
|
||||
const editorTemplate = (host: any): TemplateResult | typeof nothing => {
|
||||
const editorTemplate = (host: LiveEditorHost): TemplateResult | typeof nothing => {
|
||||
if (host._mode === 'plan') return planTemplate(host);
|
||||
if (host._mode === 'decor') {
|
||||
const view = host._viewOr(host._baseVb());
|
||||
@@ -112,10 +157,10 @@ const editorTemplate = (host: any): TemplateResult | typeof nothing => {
|
||||
return nothing;
|
||||
};
|
||||
|
||||
const paintDevice = (host: any, root: ParentNode): void => {
|
||||
const paintDevice = (host: LiveEditorHost, root: ParentNode): void => {
|
||||
const drag = host._deviceDrag;
|
||||
if (!drag) return;
|
||||
const device = host._devices.find((candidate: any) => candidate.id === drag.id);
|
||||
const device = host._devices.find((candidate) => candidate.id === drag.id);
|
||||
const element = [...root.querySelectorAll<HTMLElement>('[data-hp="device"]')]
|
||||
.find((candidate) => candidate.dataset.id === drag.id);
|
||||
if (!device || !element) return;
|
||||
@@ -127,8 +172,9 @@ const paintDevice = (host: any, root: ParentNode): void => {
|
||||
};
|
||||
|
||||
/** One editor-only render root per card; the settled scene remains untouched. */
|
||||
export function paintHouseplanEditor(host: any): void {
|
||||
const root = host.renderRoot as ParentNode | undefined;
|
||||
export function paintHouseplanEditor(value: object): void {
|
||||
const host = value as LiveEditorHost;
|
||||
const root = host.renderRoot;
|
||||
if (!root) return;
|
||||
const state = stateOf(host);
|
||||
restore(state);
|
||||
@@ -146,17 +192,18 @@ export function paintHouseplanEditor(host: any): void {
|
||||
host._liveEditorPaintCount = (host._liveEditorPaintCount || 0) + 1;
|
||||
}
|
||||
|
||||
export function scheduleHouseplanEditor(host: any): void {
|
||||
const state = stateOf(host);
|
||||
export function scheduleHouseplanEditor(value: object): void {
|
||||
const state = stateOf(value);
|
||||
if (state.raf || typeof requestAnimationFrame !== 'function') return;
|
||||
state.raf = requestAnimationFrame(() => {
|
||||
state.raf = 0;
|
||||
paintHouseplanEditor(host);
|
||||
paintHouseplanEditor(value);
|
||||
});
|
||||
}
|
||||
|
||||
/** Only text needs a DOM measurement; every box-like item owns its config box. */
|
||||
export function measureHouseplanDecorText(host: any): void {
|
||||
export function measureHouseplanDecorText(value: object): void {
|
||||
const host = value as LiveEditorHost;
|
||||
const shape = host._dtSel;
|
||||
if (!shape) {
|
||||
if (host._dtBox) { host._dtBox = null; host.requestUpdate(); }
|
||||
@@ -182,8 +229,9 @@ export function measureHouseplanDecorText(host: any): void {
|
||||
host.requestUpdate();
|
||||
}
|
||||
|
||||
export function commitHouseplanEditor(host: any): void {
|
||||
const state = stateOf(host);
|
||||
export function commitHouseplanEditor(value: object): void {
|
||||
const host = value as LiveEditorHost;
|
||||
const state = stateOf(value);
|
||||
if (state.raf && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(state.raf);
|
||||
state.raf = 0;
|
||||
restore(state);
|
||||
|
||||
+15
-5
@@ -16,6 +16,15 @@ interface HoverState {
|
||||
space: string;
|
||||
}
|
||||
|
||||
interface LiveHoverHost {
|
||||
renderRoot: ParentNode;
|
||||
_tip: LiveTip | null;
|
||||
_t: (key: 'tip.temp_avg' | 'tip.hum_avg' | 'tip.lqi') => string;
|
||||
_hoverRoom: { space: string; room: unknown } | null;
|
||||
_spaceModel: () => unknown;
|
||||
_roomHoverPaths: (model: unknown) => { fillD: string; outlineD: string } | null;
|
||||
}
|
||||
|
||||
const states = new WeakMap<object, HoverState>();
|
||||
|
||||
const appendMeta = (tip: HTMLElement, label: string, value?: string, color?: string): void => {
|
||||
@@ -33,7 +42,7 @@ const appendMeta = (tip: HTMLElement, label: string, value?: string, color?: str
|
||||
tip.append(row);
|
||||
};
|
||||
|
||||
const syncTip = (host: any, root: ParentNode): void => {
|
||||
const syncTip = (host: LiveHoverHost, root: ParentNode): void => {
|
||||
const element = root.querySelector<HTMLElement>('[data-hp-live-tip]');
|
||||
if (!element) return;
|
||||
const tip = host._tip as LiveTip | null;
|
||||
@@ -64,16 +73,17 @@ const setRoomPath = (root: ParentNode, selector: string, d: string): void => {
|
||||
};
|
||||
|
||||
/** Update the ordinary mouse hover without scheduling a full card render. */
|
||||
export function syncHouseplanHover(host: any): void {
|
||||
const root = host.renderRoot as ParentNode | undefined;
|
||||
export function syncHouseplanHover(value: object): void {
|
||||
const host = value as LiveHoverHost;
|
||||
const root = host.renderRoot;
|
||||
if (!root) return;
|
||||
syncTip(host, root);
|
||||
const hover = host._hoverRoom;
|
||||
const previous = states.get(host);
|
||||
const previous = states.get(value);
|
||||
const space = hover?.space || '';
|
||||
const room = hover?.room || null;
|
||||
if (previous?.room === room && previous?.space === space) return;
|
||||
states.set(host, { room, space });
|
||||
states.set(value, { room, space });
|
||||
const model = hover && host._spaceModel();
|
||||
const paths = model ? host._roomHoverPaths(model) : null;
|
||||
setRoomPath(root, '[data-hp-live-room-hover="fill"]', paths?.fillD || '');
|
||||
|
||||
@@ -3,20 +3,39 @@ import {
|
||||
} from './live-viewport';
|
||||
import { resetHouseplanHover, syncHouseplanHover } from './live-hover';
|
||||
import {
|
||||
classifyHassRenderChange, type HassRenderDependencies,
|
||||
classifyHassRenderChange, type HassRenderDependencies, type HassRenderSnapshot,
|
||||
} from './render-invalidation';
|
||||
import type { RenderLifecycle } from './houseplan-render-lifecycle';
|
||||
|
||||
interface LiveRuntimeHost {
|
||||
_renderLife: RenderLifecycle;
|
||||
_pointers: { size: number };
|
||||
_cameraTransition: { active: boolean };
|
||||
_deviceDrag?: unknown;
|
||||
_physicalDrag?: unknown;
|
||||
_physicalRotate?: unknown;
|
||||
_decorMove?: unknown;
|
||||
_decorDraft?: unknown;
|
||||
_dtDrag?: unknown;
|
||||
_bdDrag?: unknown;
|
||||
_opDrag?: unknown;
|
||||
_resize?: { dragging: boolean };
|
||||
}
|
||||
|
||||
/** Lazily loaded DOM-only fast paths; the card keeps a full-render fallback. */
|
||||
export class LiveRuntime {
|
||||
private deferredHass = false;
|
||||
public constructor(private readonly host: any) {}
|
||||
public constructor(private readonly host: object) {}
|
||||
public hass(
|
||||
before: any, after: any, dependencies: HassRenderDependencies | null, intake: () => void,
|
||||
before: HassRenderSnapshot | null | undefined,
|
||||
after: HassRenderSnapshot | null | undefined,
|
||||
dependencies: HassRenderDependencies | null,
|
||||
intake: () => void,
|
||||
): boolean {
|
||||
const change = classifyHassRenderChange(before, after, dependencies);
|
||||
const defer = change === 'state' && this.active();
|
||||
if (defer) this.deferredHass = true;
|
||||
this.host._renderLife.observe(before, after, dependencies, intake);
|
||||
(this.host as LiveRuntimeHost)._renderLife.observe(before, after, dependencies, intake);
|
||||
return change !== 'none' && !defer;
|
||||
}
|
||||
public clear(): void { this.deferredHass = false; }
|
||||
@@ -28,7 +47,7 @@ export class LiveRuntime {
|
||||
public viewport(): void { scheduleHouseplanViewport(this.host); }
|
||||
public hover(): void { syncHouseplanHover(this.host); }
|
||||
public active(): boolean {
|
||||
const host = this.host;
|
||||
const host = this.host as LiveRuntimeHost;
|
||||
return host._pointers.size > 0 || host._cameraTransition.active || !!host._deviceDrag
|
||||
|| !!host._physicalDrag || !!host._physicalRotate || !!host._decorMove
|
||||
|| !!host._decorDraft || !!host._dtDrag || !!host._bdDrag || !!host._opDrag
|
||||
|
||||
+15
-5
@@ -17,6 +17,14 @@ interface LiveViewportState {
|
||||
raf: number;
|
||||
}
|
||||
|
||||
interface LiveViewportHost {
|
||||
_viewOr: (viewBox: number[]) => LiveViewBox;
|
||||
_baseVb: () => number[];
|
||||
_floorView: (view: LiveViewBox) => LiveViewBox;
|
||||
_zoom: number;
|
||||
renderRoot: ParentNode;
|
||||
}
|
||||
|
||||
const states = new WeakMap<object, LiveViewportState>();
|
||||
const stateOf = (host: object): LiveViewportState => {
|
||||
let state = states.get(host);
|
||||
@@ -95,14 +103,15 @@ export function paintLiveViewport(
|
||||
}
|
||||
}
|
||||
|
||||
const frameOf = (host: any): LiveViewportFrame => {
|
||||
const frameOf = (host: LiveViewportHost): LiveViewportFrame => {
|
||||
const view = host._viewOr(host._baseVb());
|
||||
return { view: { ...view }, floor: { ...host._floorView(view) }, zoom: host._zoom };
|
||||
};
|
||||
|
||||
/** Coalesce camera input into one lightweight paint per animation frame. */
|
||||
export function scheduleHouseplanViewport(host: any): void {
|
||||
const state = stateOf(host);
|
||||
export function scheduleHouseplanViewport(value: object): void {
|
||||
const host = value as LiveViewportHost;
|
||||
const state = stateOf(value);
|
||||
state.pending = frameOf(host);
|
||||
if (state.raf || typeof requestAnimationFrame !== 'function') return;
|
||||
state.raf = requestAnimationFrame(() => {
|
||||
@@ -117,8 +126,9 @@ export function scheduleHouseplanViewport(host: any): void {
|
||||
}
|
||||
|
||||
/** Record a complete Lit frame and remove any temporary HTML projection. */
|
||||
export function commitHouseplanViewport(host: any): void {
|
||||
const state = stateOf(host);
|
||||
export function commitHouseplanViewport(value: object): void {
|
||||
const host = value as LiveViewportHost;
|
||||
const state = stateOf(value);
|
||||
if (state.raf && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(state.raf);
|
||||
state.raf = 0;
|
||||
state.pending = null;
|
||||
|
||||
+22
-14
@@ -5,14 +5,22 @@ export interface HassRenderDependencies {
|
||||
|
||||
export type HassRenderChange = 'none' | 'state' | 'structural';
|
||||
|
||||
const scalarChanged = (before: any, after: any, path: readonly string[]): boolean => {
|
||||
let a = before, b = after;
|
||||
for (const part of path) {
|
||||
a = a?.[part];
|
||||
b = b?.[part];
|
||||
}
|
||||
return a !== b;
|
||||
};
|
||||
export interface HassRenderSnapshot {
|
||||
readonly states?: Readonly<Record<string, unknown>>;
|
||||
readonly connection?: unknown;
|
||||
readonly entities?: unknown;
|
||||
readonly devices?: unknown;
|
||||
readonly areas?: unknown;
|
||||
readonly themes?: unknown;
|
||||
readonly user?: unknown;
|
||||
readonly config?: unknown;
|
||||
readonly floors?: unknown;
|
||||
readonly services?: unknown;
|
||||
readonly panels?: unknown;
|
||||
readonly language?: unknown;
|
||||
readonly locale?: Readonly<Record<string, unknown>> | null;
|
||||
readonly [key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify one HA assignment without walking the complete `hass.states` map.
|
||||
@@ -24,8 +32,8 @@ const scalarChanged = (before: any, after: any, path: readonly string[]): boolea
|
||||
* rare and can alter formatting, registry resolution, permissions or actions.
|
||||
*/
|
||||
export function classifyHassRenderChange(
|
||||
before: any,
|
||||
after: any,
|
||||
before: HassRenderSnapshot | null | undefined,
|
||||
after: HassRenderSnapshot | null | undefined,
|
||||
dependencies: HassRenderDependencies | null,
|
||||
): HassRenderChange {
|
||||
if (before === after) return 'none';
|
||||
@@ -42,10 +50,10 @@ export function classifyHassRenderChange(
|
||||
|| before.services !== after.services
|
||||
|| before.panels !== after.panels
|
||||
|| before.language !== after.language
|
||||
|| scalarChanged(before, after, ['locale', 'language'])
|
||||
|| scalarChanged(before, after, ['locale', 'number_format'])
|
||||
|| scalarChanged(before, after, ['locale', 'time_format'])
|
||||
|| scalarChanged(before, after, ['locale', 'date_format'])) {
|
||||
|| before.locale?.language !== after.locale?.language
|
||||
|| before.locale?.number_format !== after.locale?.number_format
|
||||
|| before.locale?.time_format !== after.locale?.time_format
|
||||
|| before.locale?.date_format !== after.locale?.date_format) {
|
||||
return 'structural';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user