diff --git a/packages/core/ui/core/properties/index.ts b/packages/core/ui/core/properties/index.ts index c4410e7f3..3d2b40ef9 100644 --- a/packages/core/ui/core/properties/index.ts +++ b/packages/core/ui/core/properties/index.ts @@ -12,9 +12,16 @@ import { calc } from '@csstools/css-calc'; export { unsetValue } from './property-shared'; const cssPropertyNames: string[] = []; +const HAS_OWN = Object.prototype.hasOwnProperty; const symbolPropertyMap = {}; const cssSymbolPropertyMap = {}; +// Hoisted regex/constants for hot paths to avoid re-allocation +const CSS_VARIABLE_NAME_RE = /^--[^,\s]+?$/; +const DIP_RE = /([0-9]+(\.[0-9]+)?)dip\b/g; +const UNSET_RE = /unset/g; +const INFINITY_RE = /infinity/g; + const inheritableProperties = new Array>(); const inheritableCssProperties = new Array>(); @@ -50,7 +57,7 @@ export function _getStyleProperties(): CssProperty[] { } export function isCssVariable(property: string) { - return /^--[^,\s]+?$/.test(property); + return CSS_VARIABLE_NAME_RE.test(property); } export function isCssCalcExpression(value: string) { @@ -119,27 +126,31 @@ export function _evaluateCssCalcExpression(value: string) { } else { return value; } + return value; } function _replaceDip(value: string) { - return value.replace(/([0-9]+(\.[0-9]+)?)dip\b/g, '$1'); + return value.replace(DIP_RE, '$1'); } function _replaceKeywordsWithValues(value: string) { let cssValue = value; if (cssValue.includes('unset')) { - cssValue = cssValue.replace(/unset/g, '0'); + cssValue = cssValue.replace(UNSET_RE, '0'); } if (cssValue.includes('infinity')) { - cssValue = cssValue.replace(/infinity/g, '999999'); + cssValue = cssValue.replace(INFINITY_RE, '999999'); } return cssValue; } function getPropertiesFromMap(map): Property[] | CssProperty[] { - const props = []; - Object.getOwnPropertySymbols(map).forEach((symbol) => props.push(map[symbol])); - + const symbols = Object.getOwnPropertySymbols(map); + const len = symbols.length; + const props = new Array(len); + for (let i = 0; i < len; i++) { + props[i] = map[symbols[i]]; + } return props; } @@ -240,13 +251,11 @@ export class Property implements TypedPropertyDescriptor< if (this._suspendedUpdates) { this._suspendedUpdates[propertyName] = property; } + } else if (defaultValueKey in this) { + this[setNative](this[defaultValueKey]); + delete this[defaultValueKey]; } else { - if (defaultValueKey in this) { - this[setNative](this[defaultValueKey]); - delete this[defaultValueKey]; - } else { - this[setNative](defaultValue); - } + this[setNative](defaultValue); } } } else { @@ -424,13 +433,11 @@ export class CoercibleProperty extends Property imp if (this._suspendedUpdates) { this._suspendedUpdates[propertyName] = property; } + } else if (defaultValueKey in this) { + this[setNative](this[defaultValueKey]); + delete this[defaultValueKey]; } else { - if (defaultValueKey in this) { - this[setNative](this[defaultValueKey]); - delete this[defaultValueKey]; - } else { - this[setNative](defaultValue); - } + this[setNative](defaultValue); } } } else { @@ -577,7 +584,10 @@ export class CssProperty { const propertyName = options.name; this.name = propertyName; - cssPropertyNames.push(options.cssName); + // Guard against undefined cssName + if (options.cssName) { + cssPropertyNames.push(options.cssName); + } this.cssName = `css:${options.cssName}`; this.cssLocalName = options.cssName; @@ -657,13 +667,11 @@ export class CssProperty { if (view._suspendedUpdates) { view._suspendedUpdates[propertyName] = property; } + } else if (defaultValueKey in this) { + view[setNative](this[defaultValueKey]); + delete this[defaultValueKey]; } else { - if (defaultValueKey in this) { - view[setNative](this[defaultValueKey]); - delete this[defaultValueKey]; - } else { - view[setNative](defaultValue); - } + view[setNative](defaultValue); } } } else { @@ -743,13 +751,11 @@ export class CssProperty { if (view._suspendedUpdates) { view._suspendedUpdates[propertyName] = property; } + } else if (defaultValueKey in this) { + view[setNative](this[defaultValueKey]); + delete this[defaultValueKey]; } else { - if (defaultValueKey in this) { - view[setNative](this[defaultValueKey]); - delete this[defaultValueKey]; - } else { - view[setNative](defaultValue); - } + view[setNative](defaultValue); } } } else { @@ -856,7 +862,9 @@ export class CssAnimationProperty implements CssAnimationPro const propertyName = options.name; this.name = propertyName; - cssPropertyNames.push(options.cssName); + if (options.cssName) { + cssPropertyNames.push(options.cssName); + } CssAnimationProperty.properties[propertyName] = this; if (options.cssName && options.cssName !== propertyName) { @@ -1143,21 +1151,19 @@ export class InheritedCssProperty extends CssProperty if (view._suspendedUpdates) { view._suspendedUpdates[propertyName] = property; } - } else { - if (unsetNativeValue) { - if (defaultValueKey in this) { - view[setNative](this[defaultValueKey]); - delete this[defaultValueKey]; - } else { - view[setNative](defaultValue); - } + } else if (unsetNativeValue) { + if (defaultValueKey in this) { + view[setNative](this[defaultValueKey]); + delete this[defaultValueKey]; } else { - if (!(defaultValueKey in this)) { - this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue; - } - - view[setNative](value); + view[setNative](defaultValue); } + } else { + if (!(defaultValueKey in this)) { + this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue; + } + + view[setNative](value); } } @@ -1356,6 +1362,7 @@ export function applyPendingNativeSetters(view: ViewBase): void { // TODO: Check what happens if a view was suspended and its value was reset, or set back to default! const suspendedUpdates = view._suspendedUpdates; for (const propertyName in suspendedUpdates) { + if (!HAS_OWN.call(suspendedUpdates, propertyName)) continue; const property = suspendedUpdates[propertyName]; const setNative = property.setNative; if (view[setNative]) { @@ -1523,9 +1530,11 @@ export function propagateInheritableCssProperties(parentStyle: Style, childStyle export function getSetProperties(view: ViewBase): [string, any][] { const result = []; - Object.getOwnPropertyNames(view).forEach((prop) => { + const ownProps = Object.getOwnPropertyNames(view); + for (let i = 0; i < ownProps.length; i++) { + const prop = ownProps[i]; result.push([prop, view[prop]]); - }); + } const symbols = Object.getOwnPropertySymbols(view); for (const symbol of symbols) { @@ -1545,7 +1554,9 @@ export function getComputedCssValues(view: ViewBase): [string, any][] { const result = []; const style = view.style; for (const prop of cssPropertyNames) { - result.push([prop, style[prop]]); + if (prop !== undefined && prop !== null) { + result.push([prop, style[prop]]); + } } // Add these to enable box model in chrome-devtools styles tab