From 0b2779266e60d70cd182db54dc62086ac1b4fc04 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Thu, 17 May 2018 14:04:24 -0700 Subject: [PATCH 1/4] fix: simplify style logic in template compiler --- .../lwc-engine/src/3rdparty/snabbdom/types.ts | 2 +- .../src/framework/modules/styles.ts | 22 +- packages/lwc-template-compiler/package.json | 2 - .../fixtures/base/style-static/expected.js | 7 +- .../src/__tests__/parser.spec.ts | 7 +- .../src/parser/__tests__/style.spec.ts | 68 +++++++ .../lwc-template-compiler/src/parser/index.ts | 4 +- .../lwc-template-compiler/src/parser/style.ts | 189 +++--------------- .../typings/camelcase.d.ts | 4 - .../typings/css-parse.d.ts | 70 ------- yarn.lock | 2 +- 11 files changed, 111 insertions(+), 266 deletions(-) create mode 100644 packages/lwc-template-compiler/src/parser/__tests__/style.spec.ts delete mode 100644 packages/lwc-template-compiler/typings/camelcase.d.ts delete mode 100644 packages/lwc-template-compiler/typings/css-parse.d.ts diff --git a/packages/lwc-engine/src/3rdparty/snabbdom/types.ts b/packages/lwc-engine/src/3rdparty/snabbdom/types.ts index bda96a01f3..a50539714e 100644 --- a/packages/lwc-engine/src/3rdparty/snabbdom/types.ts +++ b/packages/lwc-engine/src/3rdparty/snabbdom/types.ts @@ -76,7 +76,7 @@ export interface VNodeData { props?: Props; attrs?: Attrs; class?: Classes; - style?: VNodeStyle; + style?: VNodeStyle | string; // dataset?: Dataset; on?: On; // hero?: Hero; diff --git a/packages/lwc-engine/src/framework/modules/styles.ts b/packages/lwc-engine/src/framework/modules/styles.ts index 2c0a80d9b0..26b5448f10 100644 --- a/packages/lwc-engine/src/framework/modules/styles.ts +++ b/packages/lwc-engine/src/framework/modules/styles.ts @@ -5,17 +5,15 @@ import { StringCharCodeAt, } from '../language'; import { EmptyObject } from '../utils'; -import { VNode, Module } from "../../3rdparty/snabbdom/types"; +import { VNode, Module, VNodeStyle } from "../../3rdparty/snabbdom/types"; import { removeAttribute } from '../dom'; -const DashCharCode = 45; - function updateStyle(oldVnode: VNode, vnode: VNode) { - const { data: { style: newStyle } } = vnode; + const { style: newStyle } = vnode.data; if (isUndefined(newStyle)) { return; } - let { data: { style: oldStyle } } = oldVnode; + let { style: oldStyle } = oldVnode.data; if (oldStyle === newStyle) { return; } @@ -27,13 +25,13 @@ function updateStyle(oldVnode: VNode, vnode: VNode) { let name: string; const elm = (vnode.elm as HTMLElement); const { style } = elm; - if (isUndefined(newStyle) || newStyle as any === '') { + if (isUndefined(newStyle) || newStyle === '') { removeAttribute.call(elm, 'style'); } else if (isString(newStyle)) { style.cssText = newStyle; } else { if (!isUndefined(oldStyle)) { - for (name in oldStyle) { + for (name in oldStyle as VNodeStyle) { if (!(name in newStyle)) { style.removeProperty(name); } @@ -44,14 +42,8 @@ function updateStyle(oldVnode: VNode, vnode: VNode) { for (name in newStyle) { const cur = newStyle[name]; - if (cur !== (oldStyle as any)[name]) { - if (StringCharCodeAt.call(name, 0) === DashCharCode && StringCharCodeAt.call(name, 1) === DashCharCode) { - // if the name is prefixed with --, it will be considered a variable, and setProperty() is needed - style.setProperty(name, cur); - } else { - // @ts-ignore - style[name] = cur; - } + if (cur !== (oldStyle as VNodeStyle)[name]) { + style.setProperty(name, cur); } } } diff --git a/packages/lwc-template-compiler/package.json b/packages/lwc-template-compiler/package.json index e86885af00..e0eaf1a597 100644 --- a/packages/lwc-template-compiler/package.json +++ b/packages/lwc-template-compiler/package.json @@ -26,8 +26,6 @@ "babel-traverse": "^6.26.0", "babel-types": "^6.26.0", "babylon": "^6.17.0", - "camelcase": "^4.1.0", - "css-parse": "^2.0.0", "decamelize": "^1.2.0", "he": "^1.1.1", "parse5-with-errors": "^4.0.1" diff --git a/packages/lwc-template-compiler/src/__tests__/fixtures/base/style-static/expected.js b/packages/lwc-template-compiler/src/__tests__/fixtures/base/style-static/expected.js index ba29af8c45..95ef1c38fe 100644 --- a/packages/lwc-template-compiler/src/__tests__/fixtures/base/style-static/expected.js +++ b/packages/lwc-template-compiler/src/__tests__/fixtures/base/style-static/expected.js @@ -6,12 +6,9 @@ export default function tmpl($api, $cmp, $slotset, $ctx) { 'section', { styleMap: { - fontSize: '12px', + 'font-size': '12px', color: 'red', - marginLeft: '5px', - marginRight: '5px', - marginTop: '10px', - marginBottom: '10px' + margin: '10px 5px 10px' }, key: 1 }, diff --git a/packages/lwc-template-compiler/src/__tests__/parser.spec.ts b/packages/lwc-template-compiler/src/__tests__/parser.spec.ts index 98b58f870f..4c2bc4c00b 100644 --- a/packages/lwc-template-compiler/src/__tests__/parser.spec.ts +++ b/packages/lwc-template-compiler/src/__tests__/parser.spec.ts @@ -73,12 +73,9 @@ describe('class and style', () => {
`); expect(root.children[0].styleMap).toEqual({ - fontSize: '12px', + ['font-size']: '12px', color: 'red', - marginLeft: '5px', - marginRight: '5px', - marginTop: '10px', - marginBottom: '10px', + margin: '10px 5px 10px' }); }); diff --git a/packages/lwc-template-compiler/src/parser/__tests__/style.spec.ts b/packages/lwc-template-compiler/src/parser/__tests__/style.spec.ts new file mode 100644 index 0000000000..0949c97d8f --- /dev/null +++ b/packages/lwc-template-compiler/src/parser/__tests__/style.spec.ts @@ -0,0 +1,68 @@ +import { parseStyleText, parseClassNames } from '../style'; + +describe('parseStyleText', () => { + it('should parse simple style text', () => { + const res = parseStyleText('color: blue'); + expect(res).toEqual({ color: 'blue' }); + }); + + it('should parse simple style text with trailing coma', () => { + const res = parseStyleText('color: blue;'); + expect(res).toEqual({ + color: 'blue' + }); + }); + + it('should parse simple style with multiple values', () => { + const res = parseStyleText('box-shadow: 10px 5px 5px black;'); + expect(res).toEqual({ + ['box-shadow']: '10px 5px 5px black' + }); + }); + + it('should parse multiple declaration', () => { + const res = parseStyleText(`font-size: 12px;background: blue; color:red ;`); + expect(res).toEqual({ + ['font-size']: '12px', + background: 'blue', + color: 'red' + }); + }); + + it('should parse css functions', () => { + const res = parseStyleText(`background-color:rgba(255,0,0,0.3)`); + expect(res).toEqual({ + ['background-color']: 'rgba(255,0,0,0.3)' + }); + }); + + it('should support base 64 encoded strings', () => { + const image = 'url("data:image/webp;base64,AAAAAAAAAAA")'; + const res = parseStyleText(`background: ${image}`); + expect(res).toEqual({ + background: image + }); + }); +}); + +describe('parseClassNames', () => { + it('should support a single class', () => { + const res = parseClassNames('foo'); + expect(res).toEqual({ foo: true }); + }); + + it('should support simple class list', () => { + const res = parseClassNames('foo bar'); + expect(res).toEqual({ foo: true, bar: true }); + }); + + it('should support simple class list with trailing spaces', () => { + const res = parseClassNames(' foo bar '); + expect(res).toEqual({ foo: true, bar: true }); + }); + + it('should support simple class list multiple spaces', () => { + const res = parseClassNames('foo bar'); + expect(res).toEqual({ foo: true, bar: true }); + }); +}); diff --git a/packages/lwc-template-compiler/src/parser/index.ts b/packages/lwc-template-compiler/src/parser/index.ts index 293728adcc..0078bb8c64 100644 --- a/packages/lwc-template-compiler/src/parser/index.ts +++ b/packages/lwc-template-compiler/src/parser/index.ts @@ -29,7 +29,7 @@ import { } from './expression'; import { - parseStyle, + parseStyleText, parseClassNames, } from './style'; @@ -266,7 +266,7 @@ export default function parse(source: string, state: State): { if (styleAttribute.type === IRAttributeType.Expression) { element.style = styleAttribute.value; } else if (styleAttribute.type === IRAttributeType.String) { - element.styleMap = parseStyle(styleAttribute.value); + element.styleMap = parseStyleText(styleAttribute.value); } } } diff --git a/packages/lwc-template-compiler/src/parser/style.ts b/packages/lwc-template-compiler/src/parser/style.ts index 86acb9b380..d7de98fb06 100644 --- a/packages/lwc-template-compiler/src/parser/style.ts +++ b/packages/lwc-template-compiler/src/parser/style.ts @@ -1,178 +1,45 @@ -import * as parseCSS from 'css-parse'; -import * as toCamelCase from 'camelcase'; - -const NOT_SUPPORTED = ['display']; - -const DIRECTIONS = ['top', 'right', 'bottom', 'left']; -const CHANGE_ARR = ['margin', 'padding', 'border-width', 'border-radius']; - -const NUMBERIZE = ['width', 'height', 'font-size', 'line-height'].concat(DIRECTIONS); -DIRECTIONS.forEach((dir) => { - NUMBERIZE.push(`border-${dir}-width`); - CHANGE_ARR.forEach((prop) => { - NUMBERIZE.push(`${prop}-${dir}`); - }); -}); - -// Special properties and shorthands that need to be broken down separately -const SPECIAL_PROPS: { [name: string]: { regex: RegExp, map: { [key: number]: string | null } } } = {}; -['border', 'border-top', 'border-right', 'border-bottom', 'border-left'].forEach((name) => { - SPECIAL_PROPS[name] = { - /* uncomment to remove `px` */ - // regex: /^\s*([0-9]+)(px)?\s+(solid|dotted|dashed)?\s*([a-z0-9#,\(\)\.\s]+)\s*$/i, - regex: /^\s*([0-9]+px?)\s+(solid|dotted|dashed)?\s*([a-z0-9#,\(\)\.\s]+)\s*$/i, - map: { - 1: `${name}-width`, - 3: name === 'border' ? `${name}-style` : null, - 4: `${name}-color`, - }, - }; -}); - -// Map of properties that when expanded use different directions than the default Top,Right,Bottom,Left. -const DIRECTION_MAPS: { [name: string]: { [direction: string]: string } } = { - 'border-radius': { - Top: 'top-left', - Right: 'top-right', - Bottom: 'bottom-right', - Left: 'bottom-left', - }, -}; - -function clean(value: string): string { - return value.replace(/\r?\n|\r/g, ''); +export interface StyleMap { + [name: string]: string; } -// Convert the shorthand property to the individual directions, handles edge cases. -// i.e. border-width and border-radius -function directionToPropertyName(property: string, direction: string): string { - const names = property.split('-'); - names.splice(1, 0, DIRECTION_MAPS[property] ? DIRECTION_MAPS[property][direction] : direction); - return toCamelCase(names.join('-')); +export interface ClassMap { + [name: string]: true; } -// FIXME: This function is crap and need to be better tested -function parse(styleString: string): any { - const stylesheetString = `body { ${styleString} }`; - - const { stylesheet } = parseCSS(clean(stylesheetString)); - - const JSONResult: any = {}; - - for (const rule of stylesheet.rules) { - if (rule.type !== 'rule') { - continue; - } - - for (let selector of rule.selectors) { - selector = selector.replace(/\.|#/g, ''); - const styles = (JSONResult[selector] = JSONResult[selector] || {}); - - for (const declaration of rule.declarations) { - if (declaration.type !== 'declaration') { - continue; - } - - const { value, property } = declaration; +const DECLARATION_DELIMITER = /;(?![^(]*\))/g; +const PROPERTY_DELIMITER = /:(.+)/; - if (SPECIAL_PROPS[property]) { - const special: any = SPECIAL_PROPS[property]; - const matches = special.regex.exec(value as string); - if (matches) { - if (typeof special.map === 'function') { - special.map(matches, styles, rule.declarations); - } else { - for (const key in special.map) { - if (matches[key] && special.map[key]) { - rule.declarations.push({ - position: declaration.position, - parent: rule, - property: special.map[key] || '', - value: matches[key], - type: 'declaration', - }); - } - } - } - continue; - } - } +// Borrowed from Vue template compiler: +// https://github.com/vuejs/vue/blob/531371b818b0e31a989a06df43789728f23dc4e8/src/platforms/web/util/style.js#L5-L16 +export function parseStyleText(cssText: string): StyleMap { + const styleMap: StyleMap = {}; - if (NOT_SUPPORTED.includes(property)) { - continue; - } + const declarations = cssText.split(DECLARATION_DELIMITER); + for (const declaration of declarations) { + if (declaration) { + const [prop, value] = declaration.split(PROPERTY_DELIMITER); - if (NUMBERIZE.includes(property)) { - // uncomment to remove `px` - // value = value.replace(/px|\s*/g, ''); - styles[toCamelCase(property)] = /*parseFloat(value);*/ value; /* uncomment to remove `px` */ - } else if (CHANGE_ARR.includes(property)) { - const values = (value as string)/*.replace(/px/g, '')*/.split(/[\s,]+/); - - /* uncomment to remove `px` */ - // values.forEach((value, index, arr) => { - // arr[index] = parseInt(value); - // }); - - const length = values.length; - - if (length === 1) { - styles[toCamelCase(property)] = values[0]; - } - - if (length === 2) { - for (const prop of ['Top', 'Bottom']) { - styles[directionToPropertyName(property, prop)] = values[0]; - } - - for (const prop of ['Left', 'Right']) { - styles[directionToPropertyName(property, prop)] = values[1]; - } - } - - if (length === 3) { - - for (const prop of ['Left', 'Right']) { - styles[directionToPropertyName(property, prop)] = values[1]; - } - - styles[directionToPropertyName(property, 'Top')] = values[0]; - styles[directionToPropertyName(property, 'Bottom')] = values[2]; - } - - if (length === 4) { - ['Top', 'Right', 'Bottom', 'Left'].forEach((prop, index) => { - styles[directionToPropertyName(property, prop)] = values[index]; - }); - } - } else { - const shouldParseFloat = (typeof declaration.value === 'number' && !isNaN(declaration.value)) - && property !== 'font-weight'; - if (shouldParseFloat) { - declaration.value = parseFloat(declaration.value as string); - } - - styles[toCamelCase(property)] = declaration.value; - } + if (prop !== undefined && value !== undefined) { + styleMap[prop.trim()] = value.trim(); } } } - return JSONResult.body; + return styleMap; } -export function parseStyle(style: string): { [name: string]: string } { - return parse(style); -} +const CLASSNAME_DELIMITER = /\s+/; + +export function parseClassNames(classNames: string): ClassMap { + const classMap: ClassMap = {}; -export function parseClassNames(classNames: string): { [name: string]: true } { - const splitted = classNames.trim() - .split(/\s+/) - .filter((className) => className.length); + const classList = classNames.split(CLASSNAME_DELIMITER); + for (const className of classList) { + const normalizedClassName = className.trim(); - const classObj: { [name: string]: true } = {}; - for (const className of splitted) { - classObj[className] = true; + if (normalizedClassName.length > 0) { + classMap[className] = true; + } } - return classObj; + return classMap; } diff --git a/packages/lwc-template-compiler/typings/camelcase.d.ts b/packages/lwc-template-compiler/typings/camelcase.d.ts deleted file mode 100644 index c4a34bbd45..0000000000 --- a/packages/lwc-template-compiler/typings/camelcase.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module 'camelcase' { - const toCamelcase: (...str: string[]) => string; - export = toCamelcase; -} diff --git a/packages/lwc-template-compiler/typings/css-parse.d.ts b/packages/lwc-template-compiler/typings/css-parse.d.ts deleted file mode 100644 index 0fccb97584..0000000000 --- a/packages/lwc-template-compiler/typings/css-parse.d.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Partial type definition for css-parse - * From: https://github.com/reworkcss/css - */ - -declare namespace Errors { - interface ParsingError { - message: string; - reason: string; - filename?: string; - line?: number; - column: number; - source: string; - } -} - -declare namespace AST { - interface Location { - line: number; - column: number; - } - - interface ASTNode { - type: string; - parent: ASTNode | null; - position: { - start: Location; - end: Location; - source?: string; - content: string; - } - } - - interface Stylesheet extends ASTNode { - type: 'stylesheet'; - rules: (Rule | Comment)[]; - parsingErrors: Errors.ParsingError[]; - } - - interface Rule extends ASTNode { - type: 'rule'; - selectors: string[]; - declarations: (Declaration | Comment)[]; - } - - interface Declaration extends ASTNode { - type: 'declaration'; - property: string; - value: string | number | null; - } - - interface Comment extends ASTNode { - type: 'comment'; - comment: 'string'; - } - - interface Root { - stylesheet: Stylesheet; - } -} - -declare module 'css-parse' { - interface ParsingOptions { - silent?: boolean; - source?: string; - } - - const parse: (code: string, options?: ParsingOptions) => AST.Root; - export = parse; -} diff --git a/yarn.lock b/yarn.lock index a1e15c7f7c..f40e15df36 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3107,7 +3107,7 @@ css-loader@^0.9.1: loader-utils "~0.2.2" source-map "~0.1.38" -css-parse@^2.0.0, css-parse@~2.0.0: +css-parse@~2.0.0: version "2.0.0" resolved "http://npm.lwcjs.org/css-parse/-/css-parse-2.0.0/a468ee667c16d81ccf05c58c38d2a97c780dbfd4.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4" dependencies: From 33236d3567d28ed306511875c3240796fd60d30e Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Thu, 17 May 2018 15:05:40 -0700 Subject: [PATCH 2/4] fix: remove unused API to fix linting error --- packages/lwc-engine/src/framework/modules/styles.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/lwc-engine/src/framework/modules/styles.ts b/packages/lwc-engine/src/framework/modules/styles.ts index 26b5448f10..8773e7345f 100644 --- a/packages/lwc-engine/src/framework/modules/styles.ts +++ b/packages/lwc-engine/src/framework/modules/styles.ts @@ -2,7 +2,6 @@ import assert from "../assert"; import { isString, isUndefined, - StringCharCodeAt, } from '../language'; import { EmptyObject } from '../utils'; import { VNode, Module, VNodeStyle } from "../../3rdparty/snabbdom/types"; From 48f9cf447dfc41dab826bf3c43b552ba59a41213 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 15:01:32 -0700 Subject: [PATCH 3/4] fix: use camelCase instead of kebab-case for css properties --- .../src/framework/modules/styles.ts | 2 +- .../fixtures/base/style-static/expected.js | 2 +- .../src/__tests__/parser.spec.ts | 2 +- .../src/parser/__tests__/style.spec.ts | 6 ++--- .../lwc-template-compiler/src/parser/style.ts | 27 +++++++++++++++++-- 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/packages/lwc-engine/src/framework/modules/styles.ts b/packages/lwc-engine/src/framework/modules/styles.ts index 8773e7345f..fe45fc5bc0 100644 --- a/packages/lwc-engine/src/framework/modules/styles.ts +++ b/packages/lwc-engine/src/framework/modules/styles.ts @@ -42,7 +42,7 @@ function updateStyle(oldVnode: VNode, vnode: VNode) { for (name in newStyle) { const cur = newStyle[name]; if (cur !== (oldStyle as VNodeStyle)[name]) { - style.setProperty(name, cur); + style[name] = cur; } } } diff --git a/packages/lwc-template-compiler/src/__tests__/fixtures/base/style-static/expected.js b/packages/lwc-template-compiler/src/__tests__/fixtures/base/style-static/expected.js index 95ef1c38fe..d7f13b9965 100644 --- a/packages/lwc-template-compiler/src/__tests__/fixtures/base/style-static/expected.js +++ b/packages/lwc-template-compiler/src/__tests__/fixtures/base/style-static/expected.js @@ -6,7 +6,7 @@ export default function tmpl($api, $cmp, $slotset, $ctx) { 'section', { styleMap: { - 'font-size': '12px', + fontSize: '12px', color: 'red', margin: '10px 5px 10px' }, diff --git a/packages/lwc-template-compiler/src/__tests__/parser.spec.ts b/packages/lwc-template-compiler/src/__tests__/parser.spec.ts index 4c2bc4c00b..23760854f3 100644 --- a/packages/lwc-template-compiler/src/__tests__/parser.spec.ts +++ b/packages/lwc-template-compiler/src/__tests__/parser.spec.ts @@ -73,7 +73,7 @@ describe('class and style', () => {
`); expect(root.children[0].styleMap).toEqual({ - ['font-size']: '12px', + fontSize: '12px', color: 'red', margin: '10px 5px 10px' }); diff --git a/packages/lwc-template-compiler/src/parser/__tests__/style.spec.ts b/packages/lwc-template-compiler/src/parser/__tests__/style.spec.ts index 0949c97d8f..79bc7eaf13 100644 --- a/packages/lwc-template-compiler/src/parser/__tests__/style.spec.ts +++ b/packages/lwc-template-compiler/src/parser/__tests__/style.spec.ts @@ -16,14 +16,14 @@ describe('parseStyleText', () => { it('should parse simple style with multiple values', () => { const res = parseStyleText('box-shadow: 10px 5px 5px black;'); expect(res).toEqual({ - ['box-shadow']: '10px 5px 5px black' + boxShadow: '10px 5px 5px black' }); }); it('should parse multiple declaration', () => { const res = parseStyleText(`font-size: 12px;background: blue; color:red ;`); expect(res).toEqual({ - ['font-size']: '12px', + fontSize: '12px', background: 'blue', color: 'red' }); @@ -32,7 +32,7 @@ describe('parseStyleText', () => { it('should parse css functions', () => { const res = parseStyleText(`background-color:rgba(255,0,0,0.3)`); expect(res).toEqual({ - ['background-color']: 'rgba(255,0,0,0.3)' + backgroundColor: 'rgba(255,0,0,0.3)' }); }); diff --git a/packages/lwc-template-compiler/src/parser/style.ts b/packages/lwc-template-compiler/src/parser/style.ts index d7de98fb06..ba7e992097 100644 --- a/packages/lwc-template-compiler/src/parser/style.ts +++ b/packages/lwc-template-compiler/src/parser/style.ts @@ -6,10 +6,32 @@ export interface ClassMap { [name: string]: true; } +const DASH_CHAR_CODE = 45; // "-" + +// Implementation of the CSS property to IDL attribute algorithm. +// https://drafts.csswg.org/cssom/#idl-attribute-to-css-property +function cssPropertyToIdlAttribute(property: string): string { + let output = ''; + let uppercaseNext = false; + + for (let i = 0; i < property.length; i++) { + if (property.charCodeAt(i) === DASH_CHAR_CODE) { + uppercaseNext = true; + } else if (uppercaseNext) { + uppercaseNext = false; + output += property[i].toUpperCase(); + } else { + output += property[i]; + } + } + + return output; +} + const DECLARATION_DELIMITER = /;(?![^(]*\))/g; const PROPERTY_DELIMITER = /:(.+)/; -// Borrowed from Vue template compiler: +// Borrowed from Vue template compiler. // https://github.com/vuejs/vue/blob/531371b818b0e31a989a06df43789728f23dc4e8/src/platforms/web/util/style.js#L5-L16 export function parseStyleText(cssText: string): StyleMap { const styleMap: StyleMap = {}; @@ -20,7 +42,8 @@ export function parseStyleText(cssText: string): StyleMap { const [prop, value] = declaration.split(PROPERTY_DELIMITER); if (prop !== undefined && value !== undefined) { - styleMap[prop.trim()] = value.trim(); + const camelCasedAttribute = cssPropertyToIdlAttribute(prop.trim()); + styleMap[camelCasedAttribute] = value.trim(); } } } From 5d377130884b71f2f6a014f6595f9e46a11eae7e Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Tue, 29 May 2018 17:39:36 -0700 Subject: [PATCH 4/4] docs: add comments to clarify the style changes --- packages/lwc-engine/src/framework/modules/styles.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/lwc-engine/src/framework/modules/styles.ts b/packages/lwc-engine/src/framework/modules/styles.ts index f78c92f474..220f11fed2 100644 --- a/packages/lwc-engine/src/framework/modules/styles.ts +++ b/packages/lwc-engine/src/framework/modules/styles.ts @@ -27,6 +27,7 @@ function updateStyle(oldVnode: VNode, vnode: VNode) { if (isUndefined(newStyle) || newStyle === '') { removeAttribute.call(elm, 'style'); } else if (isString(newStyle)) { + // The style property is a string when defined via an expression in the template. style.cssText = newStyle; } else { if (!isUndefined(oldStyle)) { @@ -39,6 +40,9 @@ function updateStyle(oldVnode: VNode, vnode: VNode) { oldStyle = EmptyObject; } + // The style property is an object when defined as a string in the template. The compiler + // takes care of transforming the inline style into an object. It's faster to set the + // different style properties individually instead of via a string. for (name in newStyle) { const cur = newStyle[name]; if (cur !== (oldStyle as VNodeStyle)[name]) {