diff --git a/site/content/docs/03-template-syntax.md b/site/content/docs/03-template-syntax.md index 5d84827494..9afe399469 100644 --- a/site/content/docs/03-template-syntax.md +++ b/site/content/docs/03-template-syntax.md @@ -874,6 +874,9 @@ The `style:` directive provides a shorthand for setting multiple styles on an el
...
+ + +
...
``` --- diff --git a/src/compiler/compile/compiler_errors.ts b/src/compiler/compile/compiler_errors.ts index c1a7d8bc5c..e4d4ffddad 100644 --- a/src/compiler/compile/compiler_errors.ts +++ b/src/compiler/compile/compiler_errors.ts @@ -281,5 +281,9 @@ export default { invalid_component_style_directive: { code: 'invalid-component-style-directive', message: 'Style directives cannot be used on components' - } + }, + invalid_style_directive_modifier: (valid: string) => ({ + code: 'invalid-style-directive-modifier', + message: `Valid modifiers for style directives are: ${valid}` + }) }; diff --git a/src/compiler/compile/nodes/StyleDirective.ts b/src/compiler/compile/nodes/StyleDirective.ts index ea340cf2dc..01af8cc62e 100644 --- a/src/compiler/compile/nodes/StyleDirective.ts +++ b/src/compiler/compile/nodes/StyleDirective.ts @@ -1,20 +1,42 @@ import { TemplateNode } from '../../interfaces'; +import list from '../../utils/list'; +import compiler_errors from '../compiler_errors'; import Component from '../Component'; import { nodes_to_template_literal } from '../utils/nodes_to_template_literal'; import Expression from './shared/Expression'; import Node from './shared/Node'; import TemplateScope from './shared/TemplateScope'; +const valid_modifiers = new Set(['important']); + export default class StyleDirective extends Node { type: 'StyleDirective'; name: string; + modifiers: Set; expression: Expression; should_cache: boolean; - constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) { + constructor( + component: Component, + parent: Node, + scope: TemplateScope, + info: TemplateNode + ) { super(component, parent, scope, info); this.name = info.name; + this.modifiers = new Set(info.modifiers); + + for (const modifier of this.modifiers) { + if (!valid_modifiers.has(modifier)) { + component.error( + this, + compiler_errors.invalid_style_directive_modifier( + list([...valid_modifiers]) + ) + ); + } + } // Convert the value array to an expression so it's easier to handle // the StyleDirective going forward. @@ -34,6 +56,9 @@ export default class StyleDirective extends Node { this.expression = new Expression(component, this, scope, raw_expression); this.should_cache = raw_expression.expressions.length > 0; } + } + get important() { + return this.modifiers.has('important'); } } diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 97984748c3..8d0429879e 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -1100,7 +1100,7 @@ export default class ElementWrapper extends Wrapper { add_styles(block: Block) { const has_spread = this.node.attributes.some(attr => attr.is_spread); this.node.styles.forEach((style_directive) => { - const { name, expression, should_cache } = style_directive; + const { name, expression, should_cache, important } = style_directive; const snippet = expression.manipulate(block); let cached_snippet: Identifier | undefined; @@ -1109,7 +1109,7 @@ export default class ElementWrapper extends Wrapper { block.add_variable(cached_snippet, snippet); } - const updater = b`@set_style(${this.var}, "${name}", ${should_cache ? cached_snippet : snippet}, false)`; + const updater = b`@set_style(${this.var}, "${name}", ${should_cache ? cached_snippet : snippet}, ${important ? 1 : null})`; block.chunks.hydrate.push(updater); diff --git a/src/compiler/compile/render_ssr/handlers/Element.ts b/src/compiler/compile/render_ssr/handlers/Element.ts index 081988b567..f1dd86dd2b 100644 --- a/src/compiler/compile/render_ssr/handlers/Element.ts +++ b/src/compiler/compile/render_ssr/handlers/Element.ts @@ -44,7 +44,10 @@ export default function (node: Element, renderer: Renderer, options: RenderOptio class_expression_list.reduce((lhs, rhs) => x`${lhs} + ' ' + ${rhs}`); const style_expression_list = node.styles.map(style_directive => { - const { name, expression: { node: expression } } = style_directive; + let { name, important, expression: { node: expression } } = style_directive; + if (important) { + expression = x`${expression} + ' !important'`; + } return p`"${name}": ${expression}`; }); diff --git a/src/compiler/parse/state/tag.ts b/src/compiler/parse/state/tag.ts index bf50c6c774..9d1bf5e5c2 100644 --- a/src/compiler/parse/state/tag.ts +++ b/src/compiler/parse/state/tag.ts @@ -395,6 +395,7 @@ function read_attribute(parser: Parser, unique_names: Set) { end, type, name: directive_name, + modifiers, value }; } diff --git a/test/parser/samples/attribute-style-directive-modifiers/input.svelte b/test/parser/samples/attribute-style-directive-modifiers/input.svelte new file mode 100644 index 0000000000..1ec740107d --- /dev/null +++ b/test/parser/samples/attribute-style-directive-modifiers/input.svelte @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/parser/samples/attribute-style-directive-modifiers/output.json b/test/parser/samples/attribute-style-directive-modifiers/output.json new file mode 100644 index 0000000000..3c607fb5ea --- /dev/null +++ b/test/parser/samples/attribute-style-directive-modifiers/output.json @@ -0,0 +1,50 @@ +{ + "html": { + "start": 0, + "end": 43, + "type": "Fragment", + "children": [ + { + "start": 0, + "end": 43, + "type": "Element", + "name": "div", + "attributes": [ + { + "start": 5, + "end": 36, + "type": "StyleDirective", + "name": "color", + "modifiers": [ + "important" + ], + "value": [ + { + "start": 27, + "end": 36, + "type": "MustacheTag", + "expression": { + "type": "Identifier", + "start": 28, + "end": 35, + "loc": { + "start": { + "line": 1, + "column": 28 + }, + "end": { + "line": 1, + "column": 35 + } + }, + "name": "myColor" + } + } + ] + } + ], + "children": [] + } + ] + } +} \ No newline at end of file diff --git a/test/parser/samples/attribute-style-directive-shorthand/output.json b/test/parser/samples/attribute-style-directive-shorthand/output.json index ac658408e6..80fb4f741f 100644 --- a/test/parser/samples/attribute-style-directive-shorthand/output.json +++ b/test/parser/samples/attribute-style-directive-shorthand/output.json @@ -15,6 +15,7 @@ "end": 16, "type": "StyleDirective", "name": "color", + "modifiers": [], "value": true } ], @@ -22,4 +23,4 @@ } ] } -} \ No newline at end of file +} diff --git a/test/parser/samples/attribute-style-directive-string/output.json b/test/parser/samples/attribute-style-directive-string/output.json index e81e7be3c9..505e9333e1 100644 --- a/test/parser/samples/attribute-style-directive-string/output.json +++ b/test/parser/samples/attribute-style-directive-string/output.json @@ -15,6 +15,7 @@ "end": 22, "type": "StyleDirective", "name": "color", + "modifiers": [], "value": [ { "start": 18, @@ -45,6 +46,7 @@ "start": 35, "end": 52, "type": "StyleDirective", + "modifiers": [], "name": "color", "value": [ { @@ -77,6 +79,7 @@ "end": 80, "type": "StyleDirective", "name": "color", + "modifiers": [], "value": [ { "start": 77, @@ -108,6 +111,7 @@ "end": 120, "type": "StyleDirective", "name": "color", + "modifiers": [], "value": [ { "start": 106, @@ -160,6 +164,7 @@ "end": 160, "type": "StyleDirective", "name": "color", + "modifiers": [], "value": [ { "start": 146, @@ -212,6 +217,7 @@ "end": 198, "type": "StyleDirective", "name": "color", + "modifiers": [], "value": [ { "start": 185, @@ -264,6 +270,7 @@ "end": 245, "type": "StyleDirective", "name": "color", + "modifiers": [], "value": [ { "start": 223, @@ -352,4 +359,4 @@ } ] } -} \ No newline at end of file +} diff --git a/test/parser/samples/attribute-style-directive/output.json b/test/parser/samples/attribute-style-directive/output.json index f36ce6b01e..d80d61bddb 100644 --- a/test/parser/samples/attribute-style-directive/output.json +++ b/test/parser/samples/attribute-style-directive/output.json @@ -15,6 +15,7 @@ "end": 26, "type": "StyleDirective", "name": "color", + "modifiers": [], "value": [ { "start": 17, diff --git a/test/runtime-puppeteer/samples/inline-style-directive-important/_config.js b/test/runtime-puppeteer/samples/inline-style-directive-important/_config.js new file mode 100644 index 0000000000..d6323b82e3 --- /dev/null +++ b/test/runtime-puppeteer/samples/inline-style-directive-important/_config.js @@ -0,0 +1,22 @@ +export default { + html: ` +

hello

+

hello

+ `, + + ssrHtml: ` +

hello

+

hello

+ `, + + test({ assert, target, window, component }) { + const h1s = target.querySelectorAll('h1'); + + assert.equal(window.getComputedStyle(h1s[0])['backgroundColor'], 'rgb(0, 0, 255)'); + assert.equal(window.getComputedStyle(h1s[1])['backgroundColor'], 'rgb(255, 0, 0)'); + + component.color = 'yellow'; + assert.equal(window.getComputedStyle(h1s[0])['backgroundColor'], 'rgb(0, 0, 255)'); + assert.equal(window.getComputedStyle(h1s[1])['backgroundColor'], 'rgb(255, 255, 0)'); + } +}; diff --git a/test/runtime-puppeteer/samples/inline-style-directive-important/main.svelte b/test/runtime-puppeteer/samples/inline-style-directive-important/main.svelte new file mode 100644 index 0000000000..e869a9a473 --- /dev/null +++ b/test/runtime-puppeteer/samples/inline-style-directive-important/main.svelte @@ -0,0 +1,12 @@ + + +

hello

+

hello

+ + \ No newline at end of file diff --git a/test/validator/samples/style-directive-modifiers-invalid/errors.json b/test/validator/samples/style-directive-modifiers-invalid/errors.json new file mode 100644 index 0000000000..245dfc63a8 --- /dev/null +++ b/test/validator/samples/style-directive-modifiers-invalid/errors.json @@ -0,0 +1,15 @@ +[{ + "message": "Valid modifiers for style directives are: important", + "code": "invalid-style-directive-modifier", + "start": { + "line": 1, + "column": 8, + "character": 8 + }, + "end": { + "line": 1, + "column": 29, + "character": 29 + }, + "pos": 8 +}] diff --git a/test/validator/samples/style-directive-modifiers-invalid/input.svelte b/test/validator/samples/style-directive-modifiers-invalid/input.svelte new file mode 100644 index 0000000000..5fd7b1b1b5 --- /dev/null +++ b/test/validator/samples/style-directive-modifiers-invalid/input.svelte @@ -0,0 +1 @@ + \ No newline at end of file