diff --git a/packages/core/src/style_manager/config/config.ts b/packages/core/src/style_manager/config/config.ts index f8462567c..22ece72f6 100644 --- a/packages/core/src/style_manager/config/config.ts +++ b/packages/core/src/style_manager/config/config.ts @@ -1,4 +1,5 @@ import { PropertyTypes } from '..'; +import type { PropertyParseValue } from '../model/Property'; import { SectorProperties } from '../model/Sector'; export interface StyleManagerConfig { @@ -7,6 +8,16 @@ export interface StyleManagerConfig { */ sectors?: (Omit & { properties?: (string | PropertyTypes)[] })[]; + /** + * Pass a custom function to parse property values globally. + * + * @example + * parseValue: ({ value, parse }) => { + * return value.startsWith('var(--') ? { value, unit: '' } : parse(); + * } + */ + parseValue?: PropertyParseValue; + /** * Specify the element to use as a container, string (query) or HTMLElement. * With the empty value, nothing will be rendered. diff --git a/packages/core/src/style_manager/model/Property.ts b/packages/core/src/style_manager/model/Property.ts index e13cc5b6a..a8ef35150 100644 --- a/packages/core/src/style_manager/model/Property.ts +++ b/packages/core/src/style_manager/model/Property.ts @@ -1,6 +1,7 @@ import { isUndefined, isString, isArray, result, keys, each, includes, isFunction } from 'underscore'; import { Model } from '../../common'; import Component from '../../dom_components/model/Component'; +import type Editor from '../../editor'; import EditorModel from '../../editor/model/Editor'; import { capitalize, camelCase, hasWin } from '../../utils/mixins'; import Sector from './Sector'; @@ -15,6 +16,13 @@ export type IsVisibleFn = (props: { component?: Component; }) => boolean | void; +export type PropertyParseValue = (props: { + property: Property; + editor?: Editor; + parse: () => Partial; + value: string; +}) => Partial; + /** @private */ export interface PropertyProps { name?: string; @@ -33,6 +41,15 @@ export interface PropertyProps { fixedValues?: string[]; className?: string; extend?: string; + /** + * Pass a custom function to parse the property value. + * + * @example + * parseValue: ({ value, parse }) => { + * return value.startsWith('var(--') ? { value, unit: '' } : parse(); + * } + */ + parseValue?: PropertyParseValue; onChange?: (data: { property: Property; from: PartialPropertyProps; @@ -373,7 +390,20 @@ export default class Property this.parseValue(value, opts)); + } + + __parseValueCustom(value: string, parse: () => Partial) { + const customValueParser = this.get('parseValue') || this.em?.Styles.getConfig().parseValue; + + return customValueParser + ? customValueParser({ + property: this, + editor: this.em?.getEditor(), + parse, + value, + }) + : parse(); } __getClearProps() { @@ -388,11 +418,11 @@ export default class Property } __parseValue(value: string) { - const result = this.parseValue(value); - result.__layers = value - .split(VALUES_REG) - .map((v) => v.trim()) - .map((v) => this.__parseLayer(v)) - .filter(Boolean); - - return result; + return this.__parseValueCustom(value, () => { + const result = this.parseValue(value); + result.__layers = value + .split(VALUES_REG) + .map((v) => v.trim()) + .map((v) => this.__parseLayer(v)) + .filter(Boolean); + + return result; + }); } __parseLayer(value: string) { diff --git a/packages/core/test/specs/style_manager/model/Models.ts b/packages/core/test/specs/style_manager/model/Models.ts index 1f700eada..89850f70f 100644 --- a/packages/core/test/specs/style_manager/model/Models.ts +++ b/packages/core/test/specs/style_manager/model/Models.ts @@ -250,4 +250,40 @@ describe('PropertyNumber', () => { expect(obj.parseValue('200px')).toEqual(result); expect(obj.parseValue('95px')).toEqual({ value: 95, unit: 'px' }); }); + + test('supports per-property parseValue overrides', () => { + obj = new PropertyNumber({ + units: ['px'], + property: 'width', + parseValue: ({ value, parse }) => (value.startsWith('var(--') ? { value, unit: '' } : parse()), + }); + + expect(obj.__parseValue('var(--size)', {})).toEqual({ value: 'var(--size)', unit: '' }); + expect(obj.__parseValue('20px', {})).toEqual({ value: 20, unit: 'px' }); + }); + + test('supports global styleManager parseValue overrides', () => { + const em = new Editor({ + styleManager: { + parseValue: ({ value, parse }) => (value.startsWith('var(--') ? { value, unit: '' } : parse()), + }, + }); + const sm = em.Styles; + sm.onLoad(); + sm.addSector('test', { + name: 'Test', + properties: [{ type: 'number', property: 'width', units: ['px'] }], + }); + obj = sm.getProperty('test', 'width') as PropertyNumber; + + obj.upValue('var(--size)', { noTarget: true }); + expect(obj.get('value')).toEqual('var(--size)'); + expect(obj.get('unit')).toEqual(''); + + obj.upValue('20px', { noTarget: true }); + expect(obj.get('value')).toEqual(20); + expect(obj.get('unit')).toEqual('px'); + + em.destroy(); + }); });