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]) {