mirror of https://github.com/sveltejs/svelte
[feat] Style directives (#5923)
* add Style node, interfaces * style-directives: add parser and runtime test * style-directives: push styles in to styles array on Element * style-directives: minimal ssr work * style-directives: ssr add_styles * style-directive: tests * style-directives: work in progress * obviously incorrect hard-coded color * tweak * style directive interface * style-directives: get text from info in Style node * style-directives: add_styles func in ElementWrapper * style-directives: ssr rendering * handle text-only style directive in tag.ts * style-directives: handle spread styles in ssr * style-directives: more parser tests * style-directives: more inline tests * style-directives: remove solo tests * style-directives: cleanup * style-directives: tweak spread ssr internal * style-directives: push updater into update chunks; add dynamic test; * remove .solo * check for dynamic dependencies before adding update chunk * add test of multiple styles; remove null styles; * style-directives: docs; more tests of multiple styles * style-directives: use camelcase * style-directives: cleanup * style-directives: fix mustache template case with template literal * style-directives: use ternary * style-directives: linting * style-directives: remove "text" from interface * style-directives: actually, remove StyleDirective interface entriely * add more test, fix test for ssr * fix lint and tidy up * add test for css variables * fix linting errors Co-authored-by: pmurray73 <pmurray73@bloomberg.net> Co-authored-by: Tan Li Hau <lhtan93@gmail.com>pull/7113/head
parent
deed340cf5
commit
8a47b5ec9e
@ -0,0 +1,22 @@
|
||||
import Node from './shared/Node';
|
||||
import Expression from './shared/Expression';
|
||||
import { TemplateNode } from '../../interfaces';
|
||||
import TemplateScope from './shared/TemplateScope';
|
||||
import Component from '../Component';
|
||||
|
||||
export default class Style extends Node {
|
||||
type: 'Style';
|
||||
name: string;
|
||||
expression: Expression;
|
||||
should_cache: boolean;
|
||||
|
||||
constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) {
|
||||
super(component, parent, scope, info);
|
||||
|
||||
this.name = info.name;
|
||||
|
||||
this.expression = new Expression(component, this, scope, info.expression);
|
||||
|
||||
this.should_cache = info.expression.type === 'TemplateLiteral' && info.expression.expressions.length > 0;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
<div class:foo={isFoo}></div>
|
@ -0,0 +1,41 @@
|
||||
{
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 29,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 29,
|
||||
"type": "Element",
|
||||
"name": "div",
|
||||
"attributes": [
|
||||
{
|
||||
"start": 5,
|
||||
"end": 22,
|
||||
"type": "Class",
|
||||
"name": "foo",
|
||||
"modifiers": [],
|
||||
"expression": {
|
||||
"type": "Identifier",
|
||||
"start": 16,
|
||||
"end": 21,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 16
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 21
|
||||
}
|
||||
},
|
||||
"name": "isFoo"
|
||||
}
|
||||
}
|
||||
],
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
<div style:color></div>
|
@ -0,0 +1,31 @@
|
||||
{
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 23,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 23,
|
||||
"type": "Element",
|
||||
"name": "div",
|
||||
"attributes": [
|
||||
{
|
||||
"start": 5,
|
||||
"end": 16,
|
||||
"type": "Style",
|
||||
"name": "color",
|
||||
"modifiers": [],
|
||||
"expression": {
|
||||
"start": 11,
|
||||
"end": 16,
|
||||
"name": "color",
|
||||
"type": "Identifier"
|
||||
}
|
||||
}
|
||||
],
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
<div style:color="red"></div>
|
@ -0,0 +1,39 @@
|
||||
{
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 29,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 29,
|
||||
"type": "Element",
|
||||
"name": "div",
|
||||
"attributes": [
|
||||
{
|
||||
"start": 5,
|
||||
"end": 22,
|
||||
"type": "Style",
|
||||
"name": "color",
|
||||
"modifiers": [],
|
||||
"expression": {
|
||||
"type": "TemplateLiteral",
|
||||
"expressions": [],
|
||||
"quasis": [
|
||||
{
|
||||
"type": "TemplateElement",
|
||||
"value": {
|
||||
"raw": "red",
|
||||
"cooked": null
|
||||
},
|
||||
"tail": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
<div style:color={myColor}></div>
|
@ -0,0 +1,41 @@
|
||||
{
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 33,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 33,
|
||||
"type": "Element",
|
||||
"name": "div",
|
||||
"attributes": [
|
||||
{
|
||||
"start": 5,
|
||||
"end": 26,
|
||||
"type": "Style",
|
||||
"name": "color",
|
||||
"modifiers": [],
|
||||
"expression": {
|
||||
"type": "Identifier",
|
||||
"start": 18,
|
||||
"end": 25,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 18
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 25
|
||||
}
|
||||
},
|
||||
"name": "myColor"
|
||||
}
|
||||
}
|
||||
],
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
<div style="color: red;">red</div>
|
@ -0,0 +1,41 @@
|
||||
{
|
||||
"html": {
|
||||
"start": 0,
|
||||
"end": 34,
|
||||
"type": "Fragment",
|
||||
"children": [
|
||||
{
|
||||
"start": 0,
|
||||
"end": 34,
|
||||
"type": "Element",
|
||||
"name": "div",
|
||||
"attributes": [
|
||||
{
|
||||
"start": 5,
|
||||
"end": 24,
|
||||
"type": "Attribute",
|
||||
"name": "style",
|
||||
"value": [
|
||||
{
|
||||
"start": 12,
|
||||
"end": 23,
|
||||
"type": "Text",
|
||||
"raw": "color: red;",
|
||||
"data": "color: red;"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"start": 25,
|
||||
"end": 28,
|
||||
"type": "Text",
|
||||
"raw": "red",
|
||||
"data": "red"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
export default {
|
||||
html: `
|
||||
<p style="height: 40px; color: red;"></p>
|
||||
`,
|
||||
|
||||
test({ assert, target, window }) {
|
||||
const p = target.querySelector('p');
|
||||
|
||||
const styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'red');
|
||||
assert.equal(styles.height, '40px');
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let color = 'red';
|
||||
</script>
|
||||
|
||||
<p style="height: 40px; color: blue;" style:color={color} />
|
@ -0,0 +1,13 @@
|
||||
export default {
|
||||
html: `
|
||||
<p style="height: 40px; color: red;"></p>
|
||||
`,
|
||||
|
||||
test({ assert, target, window }) {
|
||||
const p = target.querySelector('p');
|
||||
|
||||
const styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'red');
|
||||
assert.equal(styles.height, '40px');
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let color = 'red';
|
||||
</script>
|
||||
|
||||
<p style="height: 40px;" style:color={color} />
|
@ -0,0 +1,9 @@
|
||||
export default {
|
||||
html: '<p style="--border-color: red;"></p>',
|
||||
|
||||
test({ assert, component, target }) {
|
||||
component.myColor = 'blue';
|
||||
|
||||
assert.htmlEqual(target.innerHTML, '<p style="--border-color: blue;"></p>');
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let myColor = "red";
|
||||
</script>
|
||||
|
||||
<p style:--border-color={myColor} />
|
@ -0,0 +1,10 @@
|
||||
export default {
|
||||
html: `
|
||||
<p style="color: red;"></p>
|
||||
`,
|
||||
|
||||
test({ assert, component, target }) {
|
||||
component.myColor = 'blue';
|
||||
assert.htmlEqual(target.innerHTML, '<p style="color: blue;"></p>');
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let myColor = "red";
|
||||
</script>
|
||||
|
||||
<p style:color={myColor} />
|
@ -0,0 +1,27 @@
|
||||
export default {
|
||||
html: `
|
||||
<p style="color: red; width: 65px; font-weight: 700;"></p>
|
||||
`,
|
||||
|
||||
test({ assert, component, target, window }) {
|
||||
const p = target.querySelector('p');
|
||||
|
||||
let styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'red');
|
||||
|
||||
component.myColor = 'pink';
|
||||
component.width = '100vh';
|
||||
component.absolute = true;
|
||||
component.bold = false;
|
||||
|
||||
styles = window.getComputedStyle(p);
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<p style="color: pink; width: 100vh; font-weight: 100; position: absolute;"></p>'
|
||||
);
|
||||
assert.equal(styles.color, 'pink');
|
||||
assert.equal(styles.width, '100vh');
|
||||
assert.equal(styles.fontWeight, '100');
|
||||
assert.equal(styles.position, 'absolute');
|
||||
}
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
<script>
|
||||
export let myColor = "red";
|
||||
export let width = "65px";
|
||||
export let absolute = false;
|
||||
export let bold = true;
|
||||
</script>
|
||||
|
||||
<p
|
||||
style:color={myColor}
|
||||
style:width
|
||||
style:position={absolute ? "absolute" : null}
|
||||
style:font-weight={bold ? 700 : 100}
|
||||
/>
|
@ -0,0 +1,18 @@
|
||||
export default {
|
||||
html: `
|
||||
<p style="color: red;"></p>
|
||||
`,
|
||||
|
||||
test({ assert, component, target, window }) {
|
||||
const p = target.querySelector('p');
|
||||
|
||||
let styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'red');
|
||||
|
||||
component.color = 'blue';
|
||||
assert.htmlEqual(target.innerHTML, '<p style="color: blue;"></p>');
|
||||
|
||||
styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'blue');
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let color = "red";
|
||||
</script>
|
||||
|
||||
<p style:color />
|
@ -0,0 +1,5 @@
|
||||
export default {
|
||||
html: `
|
||||
<p style=""></p>
|
||||
`
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
export let spread = { style: 'color: red;' };
|
||||
export let color = null;
|
||||
export let style = 'color: blue';
|
||||
</script>
|
||||
|
||||
<p
|
||||
style:color
|
||||
{...spread}
|
||||
{style}
|
||||
/>
|
@ -0,0 +1,43 @@
|
||||
export default {
|
||||
html: `
|
||||
<p style="color: green;"></p>
|
||||
`,
|
||||
|
||||
test({ assert, component, target, window }) {
|
||||
const p = target.querySelector('p');
|
||||
|
||||
let styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'green');
|
||||
|
||||
component.color = null;
|
||||
assert.htmlEqual(target.innerHTML, '<p style=""></p>');
|
||||
styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, '');
|
||||
|
||||
component.spread = { style: 'color: yellow; padding: 30px;' };
|
||||
|
||||
assert.htmlEqual(target.innerHTML, '<p style="padding: 30px;"></p>');
|
||||
styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, '');
|
||||
assert.equal(styles.padding, '30px');
|
||||
|
||||
component.spread = {};
|
||||
component.style = 'color: blue; background-color: green;';
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<p style="background-color: green;"></p>'
|
||||
);
|
||||
styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, '');
|
||||
assert.equal(styles.backgroundColor, 'green');
|
||||
|
||||
component.color = 'purple';
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<p style="background-color: green; color: purple;"></p>'
|
||||
);
|
||||
styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'purple');
|
||||
assert.equal(styles.backgroundColor, 'green');
|
||||
}
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
export let spread = { style: 'color: red;' };
|
||||
export let color = 'green';
|
||||
export let style = 'color: blue;';
|
||||
</script>
|
||||
|
||||
<p
|
||||
style:color
|
||||
{style}
|
||||
{...spread}
|
||||
/>
|
@ -0,0 +1,39 @@
|
||||
export default {
|
||||
html: `
|
||||
<p id="my-id" style="width: 65px; color: blue;"></p>
|
||||
`,
|
||||
|
||||
test({ assert, component, target, window }) {
|
||||
const p = target.querySelector('p');
|
||||
|
||||
const styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'blue');
|
||||
assert.equal(styles.width, '65px');
|
||||
assert.equal(p.id, 'my-id');
|
||||
|
||||
component.color = 'red';
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<p id="my-id" style="width: 65px; color: red;"></p>'
|
||||
);
|
||||
|
||||
component.obj = { style: 'height: 72px;' };
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<p style="height: 72px; color: red;"></p>'
|
||||
);
|
||||
|
||||
component.obj = { style: 'border-radius: 2px; color: orange' };
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<p style="border-radius: 2px; color: red;"></p>'
|
||||
);
|
||||
|
||||
component.obj = {};
|
||||
|
||||
assert.htmlEqual(target.innerHTML, '<p style="color: red;"></p>');
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let color = 'blue';
|
||||
export let obj = { id: 'my-id', style: 'width: 65px' };
|
||||
</script>
|
||||
<p {...obj} style:color={color} />
|
@ -0,0 +1,14 @@
|
||||
export default {
|
||||
html: `
|
||||
<p id="my-id" style="width: 65px; color: blue;"></p>
|
||||
`,
|
||||
|
||||
test({ assert, target, window }) {
|
||||
const p = target.querySelector('p');
|
||||
|
||||
const styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'blue');
|
||||
assert.equal(styles.width, '65px');
|
||||
assert.equal(p.id, 'my-id');
|
||||
}
|
||||
};
|
@ -0,0 +1 @@
|
||||
<p {...{ id: "my-id", style: "width: 65px" }} style:color="blue" />
|
@ -0,0 +1,23 @@
|
||||
export default {
|
||||
html: `
|
||||
<p style="color: green; transform: translateX(45px); border: 100px solid pink;"></p>
|
||||
`,
|
||||
|
||||
test({ assert, component, target, window }) {
|
||||
const p = target.querySelector('p');
|
||||
|
||||
const styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'green');
|
||||
assert.equal(styles.transform, 'translateX(45px)');
|
||||
assert.equal(styles.border, '100px solid pink');
|
||||
|
||||
component.translate_x = '100%';
|
||||
component.border_width = 20;
|
||||
component.border_color = 'yellow';
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<p style="color: green; transform: translateX(100%); border: 20px solid yellow;"></p>'
|
||||
);
|
||||
}
|
||||
};
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
export let translate_x = "45px";
|
||||
export let border_width = 100;
|
||||
export let border_color;
|
||||
</script>
|
||||
|
||||
<p style:color={"green"} style:transform="translateX({translate_x})" style:border="{border_width}px solid {border_color || 'pink'}" />
|
@ -0,0 +1,12 @@
|
||||
export default {
|
||||
html: `
|
||||
<p style="color: red;"></p>
|
||||
`,
|
||||
|
||||
test({ assert, target, window }) {
|
||||
const p = target.querySelector('p');
|
||||
|
||||
const styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'red');
|
||||
}
|
||||
};
|
@ -0,0 +1 @@
|
||||
<p style:color="red" />
|
@ -0,0 +1,12 @@
|
||||
export default {
|
||||
html: `
|
||||
<p style="color: red;"></p>
|
||||
`,
|
||||
|
||||
test({ assert, target, window }) {
|
||||
const p = target.querySelector('p');
|
||||
|
||||
const styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'red');
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
export let myColor = "red";
|
||||
</script>
|
||||
|
||||
<p style:color={myColor} />
|
@ -0,0 +1,12 @@
|
||||
export default {
|
||||
html: `
|
||||
<div style="color: red;"></div>
|
||||
`,
|
||||
|
||||
test({ assert, component, target, window }) {
|
||||
const p = target.querySelector('div');
|
||||
|
||||
const styles = window.getComputedStyle(p);
|
||||
assert.equal(styles.color, 'red');
|
||||
}
|
||||
};
|
@ -0,0 +1 @@
|
||||
<div style="color: red;"></div>
|
Loading…
Reference in new issue