From c8a39021376b17ba4ebd87441fc2cf757bb1b340 Mon Sep 17 00:00:00 2001 From: Johan Groth Date: Tue, 24 Jun 2025 15:20:49 +0200 Subject: [PATCH] perf: avoid calling getPropertyValue during render --- src/components/date-picker/date-picker.tsx | 19 +++++++-- src/components/input-field/input-field.tsx | 15 +++++-- src/components/menu/menu.tsx | 48 +++++++++++++--------- src/components/picker/picker.tsx | 18 ++++++-- src/components/popover/popover.tsx | 41 ++++++++++-------- src/components/select/select.tsx | 15 +++++-- src/components/tooltip/tooltip.tsx | 15 +++++-- src/util/computed-styles.ts | 16 ++++++++ 8 files changed, 135 insertions(+), 52 deletions(-) create mode 100644 src/util/computed-styles.ts diff --git a/src/components/date-picker/date-picker.tsx b/src/components/date-picker/date-picker.tsx index 3b3dfdbb2a..6ba01f3856 100644 --- a/src/components/date-picker/date-picker.tsx +++ b/src/components/date-picker/date-picker.tsx @@ -13,6 +13,7 @@ import { DateType, Languages } from '../date-picker/date.types'; import { InputType } from '../input-field/input-field.types'; import { DateFormatter } from './dateFormatter'; import { MDCTextField } from '@material/textfield'; +import { getComputedStyles } from '../../util/computed-styles'; // tslint:disable:no-duplicate-string const nativeTypeForConsumerType: { [key: string]: InputType } = { @@ -153,6 +154,9 @@ export class DatePicker { @State() private showPortal = false; + @State() + private computedStyles: Record = {}; + private useNative: boolean; private nativeType: InputType; private nativeFormat: string; @@ -185,6 +189,16 @@ export class DatePicker { this.updateInternalFormatAndType(); } + public connectedCallback() { + this.setComputedStyles(); + } + + private async setComputedStyles() { + this.computedStyles = await getComputedStyles(this.host, [ + '--dropdown-z-index', + ]); + } + public disconnectedCallback() { this.hideCalendar(); } @@ -214,10 +228,7 @@ export class DatePicker { ); } - const dropdownZIndex = getComputedStyle(this.host).getPropertyValue( - '--dropdown-z-index', - ); - + const dropdownZIndex = this.computedStyles['--dropdown-z-index']; const formatter = this.formatter || this.formatValue; return [ diff --git a/src/components/input-field/input-field.tsx b/src/components/input-field/input-field.tsx index 6ea07fd0fd..ed206deff0 100644 --- a/src/components/input-field/input-field.tsx +++ b/src/components/input-field/input-field.tsx @@ -25,6 +25,7 @@ import { JSXBase } from '@stencil/core/internal'; import { createRandomString } from '../../util/random-string'; import { LimelListCustomEvent } from '../../components'; import { globalConfig } from '../../global/config'; +import { getComputedStyles } from '../../util/computed-styles'; interface LinkProperties { href: string; @@ -247,6 +248,9 @@ export class InputField { @State() public showCompletions: boolean = false; + @State() + private computedStyles: Record = {}; + private inputElement?: HTMLInputElement | HTMLTextAreaElement; private mdcTextField: MDCTextField; private completionsList: ListItem[] = []; @@ -264,6 +268,13 @@ export class InputField { public connectedCallback() { this.initialize(); + this.setComputedStyles(); + } + + private async setComputedStyles() { + this.computedStyles = await getComputedStyles(this.limelInputField, [ + '--dropdown-z-index', + ]); } public componentDidLoad() { @@ -861,9 +872,7 @@ export class InputField { return; } - const dropdownZIndex = getComputedStyle( - this.limelInputField, - ).getPropertyValue('--dropdown-z-index'); + const dropdownZIndex = this.computedStyles['--dropdown-z-index']; return ( | null; + @State() + private computedStyles: Record = {}; + private list: HTMLLimelMenuListElement; private searchInput: HTMLLimelInputFieldElement; private portalId: string; @@ -202,6 +206,24 @@ export class Menu { this.portalId = createRandomString(); } + public connectedCallback() { + this.setComputedStyles(); + } + + private async setComputedStyles() { + const propertyNames = [ + '--menu-surface-width', + '--list-grid-item-max-width', + '--list-grid-item-min-width', + '--list-grid-gap', + '--notification-badge-background-color', + '--notification-badge-text-color', + '--dropdown-z-index', + ] as const; + + this.computedStyles = await getComputedStyles(this.host, propertyNames); + } + public componentDidRender() { const slotElement = this.host.shadowRoot.querySelector('slot'); slotElement.assignedElements().forEach(this.setTriggerAttributes); @@ -210,9 +232,7 @@ export class Menu { public render() { const cssProperties = this.getCssProperties(); - const dropdownZIndex = getComputedStyle(this.host).getPropertyValue( - '--dropdown-z-index', - ); + const dropdownZIndex = this.computedStyles['--dropdown-z-index']; const menuSurfaceWidth = this.getMenuSurfaceWidth( cssProperties['--menu-surface-width'], @@ -653,22 +673,10 @@ export class Menu { }; private getCssProperties() { - const propertyNames = [ - '--menu-surface-width', - '--list-grid-item-max-width', - '--list-grid-item-min-width', - '--list-grid-gap', - '--notification-badge-background-color', - '--notification-badge-text-color', - ] as const; - const style = getComputedStyle(this.host); - const values = propertyNames.map((property) => { - return style.getPropertyValue(property); - }); - - type PropName = (typeof propertyNames)[number]; - - return zipObject(propertyNames, values) as Record; + return { + ...this.computedStyles, + '--dropdown-z-index': undefined, + }; } private setListElement = (element: HTMLLimelMenuListElement) => { diff --git a/src/components/picker/picker.tsx b/src/components/picker/picker.tsx index d0da42907f..345c1418fb 100644 --- a/src/components/picker/picker.tsx +++ b/src/components/picker/picker.tsx @@ -23,6 +23,7 @@ import { import { getIconFillColor, getIconName } from '../icon/get-icon-props'; import { PickerValue } from './value.types'; import { DebouncedFunc, debounce } from 'lodash-es'; +import { getComputedStyles } from '../../util/computed-styles'; const SEARCH_DEBOUNCE = 300; const CHIP_SET_TAG_NAME = 'limel-chip-set'; @@ -205,6 +206,9 @@ export class Picker { @State() private chips: Chip[] = []; + @State() + private computedStyles: Record = {}; + @Element() private host: HTMLLimelPickerElement; @@ -241,6 +245,16 @@ export class Picker { this.chipSet = this.host.shadowRoot.querySelector(CHIP_SET_TAG_NAME); } + public connectedCallback() { + this.setComputedStyles(); + } + + private async setComputedStyles() { + this.computedStyles = await getComputedStyles(this.host, [ + '--dropdown-z-index', + ]); + } + public disconnectedCallback() { this.debouncedSearch.cancel(); } @@ -489,9 +503,7 @@ export class Picker { } private renderPortal(content: any[] = []) { - const dropdownZIndex = getComputedStyle(this.host).getPropertyValue( - '--dropdown-z-index', - ); + const dropdownZIndex = this.computedStyles['--dropdown-z-index']; return ( ; + @State() + private computedStyles: Record = {}; + @Element() private host: HTMLLimelPopoverElement; @@ -97,6 +101,21 @@ export class Popover { this.setupGlobalHandlers(); } + public connectedCallback() { + this.setComputedStyles(); + } + + private async setComputedStyles() { + const propertyNames = [ + '--popover-surface-width', + '--popover-body-background-color', + '--popover-border-radius', + '--popover-box-shadow', + '--popover-z-index', + ] as const; + this.computedStyles = await getComputedStyles(this.host, propertyNames); + } + public componentWillLoad() { this.setupGlobalHandlers(); } @@ -120,9 +139,7 @@ export class Popover { public render() { const cssProperties = this.getCssProperties(); - const popoverZIndex = getComputedStyle(this.host).getPropertyValue( - '--popover-z-index', - ); + const popoverZIndex = this.computedStyles['--popover-z-index']; return (
@@ -153,18 +170,10 @@ export class Popover { } private getCssProperties() { - const propertyNames = [ - '--popover-surface-width', - '--popover-body-background-color', - '--popover-border-radius', - '--popover-box-shadow', - ]; - const style = getComputedStyle(this.host); - const values = propertyNames.map((property) => { - return style.getPropertyValue(property); - }); - - return zipObject(propertyNames, values); + return { + ...this.computedStyles, + '--popover-z-index': undefined, + }; } private handleGlobalKeyPress = (event: KeyboardEvent) => { diff --git a/src/components/select/select.tsx b/src/components/select/select.tsx index 48a1651986..ba1d75a060 100644 --- a/src/components/select/select.tsx +++ b/src/components/select/select.tsx @@ -17,6 +17,7 @@ import { ENTER, SPACE } from '../../util/keycodes'; import { isMultiple } from '../../util/multiple'; import { createRandomString } from '../../util/random-string'; import { SelectTemplate, triggerIconColorWarning } from './select.template'; +import { getComputedStyles } from '../../util/computed-styles'; /** * @exampleComponent limel-example-select @@ -110,6 +111,9 @@ export class Select { @State() private menuOpen: boolean = false; + @State() + private computedStyles: Record = {}; + private hasChanged: boolean = false; private checkValid: boolean = false; private mdcSelectHelperText: MDCSelectHelperText; @@ -130,6 +134,13 @@ export class Select { public connectedCallback() { this.initialize(); + this.setComputedStyles(); + } + + private async setComputedStyles() { + this.computedStyles = await getComputedStyles(this.host, [ + '--dropdown-z-index', + ]); } public componentWillLoad() { @@ -179,9 +190,7 @@ export class Select { } public render() { - const dropdownZIndex = getComputedStyle(this.host).getPropertyValue( - '--dropdown-z-index', - ); + const dropdownZIndex = this.computedStyles['--dropdown-z-index']; return ( = {}; + private portalId: string; private tooltipId: string; private ownerElement: HTMLElement; @@ -116,6 +120,13 @@ export class Tooltip { this.ownerElement = getOwnerElement(this.elementId, this.host); this.setOwnerAriaLabel(); this.addListeners(); + this.setComputesdStyles(); + } + + private async setComputesdStyles() { + this.computedStyles = await getComputedStyles(this.host, [ + '--tooltip-z-index', + ]); } public disconnectedCallback() { @@ -123,9 +134,7 @@ export class Tooltip { } public render(): JSX.Element { - const tooltipZIndex = getComputedStyle(this.host).getPropertyValue( - '--tooltip-z-index', - ); + const tooltipZIndex = this.computedStyles['--tooltip-z-index']; return (
diff --git a/src/util/computed-styles.ts b/src/util/computed-styles.ts new file mode 100644 index 0000000000..b0e9a5b88a --- /dev/null +++ b/src/util/computed-styles.ts @@ -0,0 +1,16 @@ +export async function getComputedStyles( + element: HTMLElement, + styles: T, +): Promise> { + return new Promise((resolve) => { + requestAnimationFrame(() => { + const computedStyles: Record = {} as any; + const style = getComputedStyle(element); + styles.forEach((name) => { + computedStyles[name] = style.getPropertyValue(name).trim(); + }); + + resolve(computedStyles); + }); + }); +}