perf: Various rendering performance improvements (#2359)
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { LitElement } from 'lit';
|
import { LitElement } from 'lit';
|
||||||
import { orderBy } from 'lodash-es';
|
import { isEqual, orderBy } from 'lodash-es';
|
||||||
import { dispatchActionExecutionRequest } from '../card-controller/actions/utils/execution-request.js';
|
import { dispatchActionExecutionRequest } from '../card-controller/actions/utils/execution-request.js';
|
||||||
import { SubmenuInteraction } from '../components/submenu/types.js';
|
import { SubmenuInteraction } from '../components/submenu/types.js';
|
||||||
import { ActionConfig, ActionsConfig } from '../config/schema/actions/types.js';
|
import { ActionConfig, ActionsConfig } from '../config/schema/actions/types.js';
|
||||||
@@ -46,6 +46,9 @@ export class MenuController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setButtons(buttons: MenuItem[]): void {
|
public setButtons(buttons: MenuItem[]): void {
|
||||||
|
if (isEqual(buttons, this._buttons)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this._buttons = buttons;
|
this._buttons = buttons;
|
||||||
this._sortButtons();
|
this._sortButtons();
|
||||||
this._host.requestUpdate();
|
this._host.requestUpdate();
|
||||||
|
|||||||
@@ -12,9 +12,13 @@ import 'side-drawer';
|
|||||||
import { SideDrawer } from 'side-drawer';
|
import { SideDrawer } from 'side-drawer';
|
||||||
import drawerInjectStyle from '../scss/drawer-inject.scss';
|
import drawerInjectStyle from '../scss/drawer-inject.scss';
|
||||||
import drawerStyle from '../scss/drawer.scss';
|
import drawerStyle from '../scss/drawer.scss';
|
||||||
import { stopEventFromActivatingCardWideActions } from '../utils/action';
|
import { stopEventFromActivatingCardWideActions } from '../utils/action.js';
|
||||||
import { getChildrenFromElement, isHoverableDevice } from '../utils/basic';
|
import {
|
||||||
import './icon';
|
contentsChanged,
|
||||||
|
getChildrenFromElement,
|
||||||
|
isHoverableDevice,
|
||||||
|
} from '../utils/basic.js';
|
||||||
|
import './icon.js';
|
||||||
|
|
||||||
export interface DrawerIcons {
|
export interface DrawerIcons {
|
||||||
open?: string;
|
open?: string;
|
||||||
@@ -32,7 +36,7 @@ export class AdvancedCameraCardDrawer extends LitElement {
|
|||||||
@property({ type: Boolean, reflect: true, attribute: true })
|
@property({ type: Boolean, reflect: true, attribute: true })
|
||||||
public open = false;
|
public open = false;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
public icons?: DrawerIcons;
|
public icons?: DrawerIcons;
|
||||||
|
|
||||||
// The 'empty' attribute is used in the styling to change the drawer
|
// The 'empty' attribute is used in the styling to change the drawer
|
||||||
|
|||||||
@@ -10,14 +10,15 @@ import { customElement, property } from 'lit/decorators.js';
|
|||||||
import { IconController } from '../components-lib/icon-controller';
|
import { IconController } from '../components-lib/icon-controller';
|
||||||
import { HomeAssistant } from '../ha/types';
|
import { HomeAssistant } from '../ha/types';
|
||||||
import iconStyle from '../scss/icon.scss';
|
import iconStyle from '../scss/icon.scss';
|
||||||
import { Icon } from '../types';
|
import { Icon } from '../types.js';
|
||||||
|
import { contentsChanged } from '../utils/basic.js';
|
||||||
|
|
||||||
@customElement('advanced-camera-card-icon')
|
@customElement('advanced-camera-card-icon')
|
||||||
export class AdvancedCameraCardIcon extends LitElement {
|
export class AdvancedCameraCardIcon extends LitElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
public icon?: Icon;
|
public icon?: Icon;
|
||||||
|
|
||||||
// Note: This attribute will allow non-active entity state styles (e.g. 'off',
|
// Note: This attribute will allow non-active entity state styles (e.g. 'off',
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { LiveConfig } from '../../config/schema/live.js';
|
|||||||
import { CardWideConfig } from '../../config/schema/types.js';
|
import { CardWideConfig } from '../../config/schema/types.js';
|
||||||
import { HomeAssistant } from '../../ha/types.js';
|
import { HomeAssistant } from '../../ha/types.js';
|
||||||
import liveGridStyle from '../../scss/live-grid.scss';
|
import liveGridStyle from '../../scss/live-grid.scss';
|
||||||
|
import { contentsChanged } from '../../utils/basic.js';
|
||||||
import './carousel.js';
|
import './carousel.js';
|
||||||
|
|
||||||
@customElement('advanced-camera-card-live-grid')
|
@customElement('advanced-camera-card-live-grid')
|
||||||
@@ -38,7 +39,7 @@ export class AdvancedCameraCardLiveGrid extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public microphoneState?: MicrophoneState;
|
public microphoneState?: MicrophoneState;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
public triggeredCameraIDs?: Set<string>;
|
public triggeredCameraIDs?: Set<string>;
|
||||||
|
|
||||||
private _renderCarousel(cameraID?: string): TemplateResult {
|
private _renderCarousel(cameraID?: string): TemplateResult {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { LiveConfig } from '../../config/schema/live.js';
|
|||||||
import { CardWideConfig } from '../../config/schema/types.js';
|
import { CardWideConfig } from '../../config/schema/types.js';
|
||||||
import { HomeAssistant } from '../../ha/types.js';
|
import { HomeAssistant } from '../../ha/types.js';
|
||||||
import basicBlockStyle from '../../scss/basic-block.scss';
|
import basicBlockStyle from '../../scss/basic-block.scss';
|
||||||
|
import { contentsChanged } from '../../utils/basic.js';
|
||||||
import './grid.js';
|
import './grid.js';
|
||||||
|
|
||||||
@customElement('advanced-camera-card-live')
|
@customElement('advanced-camera-card-live')
|
||||||
@@ -30,7 +31,7 @@ export class AdvancedCameraCardLive extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public microphoneState?: MicrophoneState;
|
public microphoneState?: MicrophoneState;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
public triggeredCameraIDs?: Set<string>;
|
public triggeredCameraIDs?: Set<string>;
|
||||||
|
|
||||||
private _controller = new LiveController(this);
|
private _controller = new LiveController(this);
|
||||||
|
|||||||
@@ -14,13 +14,14 @@ import { StatusBarItem } from '../config/schema/actions/types.js';
|
|||||||
import { StatusBarConfig } from '../config/schema/status-bar.js';
|
import { StatusBarConfig } from '../config/schema/status-bar.js';
|
||||||
import statusStyle from '../scss/status.scss';
|
import statusStyle from '../scss/status.scss';
|
||||||
import { hasAction } from '../utils/action';
|
import { hasAction } from '../utils/action';
|
||||||
|
import { contentsChanged } from '../utils/basic.js';
|
||||||
import './icon.js';
|
import './icon.js';
|
||||||
|
|
||||||
@customElement('advanced-camera-card-status-bar')
|
@customElement('advanced-camera-card-status-bar')
|
||||||
export class AdvancedCameraCardStatusBar extends LitElement {
|
export class AdvancedCameraCardStatusBar extends LitElement {
|
||||||
private _controller = new StatusBarController(this);
|
private _controller = new StatusBarController(this);
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
public items?: StatusBarItem[];
|
public items?: StatusBarItem[];
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
hasAction,
|
hasAction,
|
||||||
stopEventFromActivatingCardWideActions,
|
stopEventFromActivatingCardWideActions,
|
||||||
} from '../../utils/action.js';
|
} from '../../utils/action.js';
|
||||||
|
import { contentsChanged } from '../../utils/basic.js';
|
||||||
import '../icon.js';
|
import '../icon.js';
|
||||||
import { SubmenuInteraction, SubmenuItem } from './types.js';
|
import { SubmenuInteraction, SubmenuItem } from './types.js';
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ export class AdvancedCameraCardSubmenu extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
public items?: SubmenuItem[];
|
public items?: SubmenuItem[];
|
||||||
|
|
||||||
private _renderItem(item: SubmenuItem): TemplateResult | void {
|
private _renderItem(item: SubmenuItem): TemplateResult | void {
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit';
|
||||||
import { createRef, ref, Ref } from 'lit/directives/ref.js';
|
|
||||||
import { customElement, property } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
import { DrawerIcons, AdvancedCameraCardDrawer } from './drawer.js';
|
import { createRef, ref, Ref } from 'lit/directives/ref.js';
|
||||||
|
|
||||||
import './drawer.js';
|
|
||||||
|
|
||||||
import surroundBasicStyle from '../scss/surround-basic.scss';
|
import surroundBasicStyle from '../scss/surround-basic.scss';
|
||||||
|
import { contentsChanged } from '../utils/basic.js';
|
||||||
|
import './drawer.js';
|
||||||
|
import { AdvancedCameraCardDrawer, DrawerIcons } from './drawer.js';
|
||||||
|
|
||||||
interface AdvancedCameraCardDrawerOpen {
|
interface AdvancedCameraCardDrawerOpen {
|
||||||
drawer: 'left' | 'right';
|
drawer: 'left' | 'right';
|
||||||
@@ -13,7 +12,7 @@ interface AdvancedCameraCardDrawerOpen {
|
|||||||
|
|
||||||
@customElement('advanced-camera-card-surround-basic')
|
@customElement('advanced-camera-card-surround-basic')
|
||||||
export class AdvancedCameraCardSurroundBasic extends LitElement {
|
export class AdvancedCameraCardSurroundBasic extends LitElement {
|
||||||
@property({ attribute: false })
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
public drawerIcons?: {
|
public drawerIcons?: {
|
||||||
left?: DrawerIcons;
|
left?: DrawerIcons;
|
||||||
right?: DrawerIcons;
|
right?: DrawerIcons;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { DeviceRegistryManager } from '../ha/registry/device/index.js';
|
|||||||
import { ResolvedMediaCache } from '../ha/resolved-media.js';
|
import { ResolvedMediaCache } from '../ha/resolved-media.js';
|
||||||
import { HomeAssistant } from '../ha/types.js';
|
import { HomeAssistant } from '../ha/types.js';
|
||||||
import viewsStyle from '../scss/views.scss';
|
import viewsStyle from '../scss/views.scss';
|
||||||
|
import { contentsChanged } from '../utils/basic.js';
|
||||||
import './surround.js';
|
import './surround.js';
|
||||||
|
|
||||||
// As a special case: The diagnostics view is not dynamically loaded in case
|
// As a special case: The diagnostics view is not dynamically loaded in case
|
||||||
@@ -61,7 +62,7 @@ export class AdvancedCameraCardViews extends LitElement {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
public microphoneState?: MicrophoneState;
|
public microphoneState?: MicrophoneState;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false, hasChanged: contentsChanged })
|
||||||
public triggeredCameraIDs?: Set<string>;
|
public triggeredCameraIDs?: Set<string>;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
|
|||||||
@@ -252,6 +252,29 @@ describe('MenuController', () => {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('with identical buttons to avoid unnecessary updates', () => {
|
||||||
|
const host = createLitElement();
|
||||||
|
const controller = new MenuController(host);
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
type: 'custom:advanced-camera-card-menu-icon' as const,
|
||||||
|
icon: 'mdi:cow',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
controller.setButtons(buttons);
|
||||||
|
expect(host.requestUpdate).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
controller.setButtons([
|
||||||
|
{
|
||||||
|
type: 'custom:advanced-camera-card-menu-icon' as const,
|
||||||
|
icon: 'mdi:cow',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(host.requestUpdate).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('should get buttons', () => {
|
describe('should get buttons', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user