compile: support classes on inline components

Closes #2870
pull/2888/head
Nico Rehwaldt 6 years ago
parent baa5fc2625
commit cd433408ac

@ -2,9 +2,14 @@ import MagicString from 'magic-string';
import { walk } from 'estree-walker';
import Selector from './Selector';
import Element from '../nodes/Element';
import InlineComponent from '../nodes/InlineComponent';
import { Node, Ast } from '../../interfaces';
import Component from '../Component';
type ClassTarget = Element
| InlineComponent;
function remove_css_prefix(name: string): string {
return name.replace(/^-((webkit)|(moz)|(o)|(ms))-/, '');
}
@ -33,7 +38,7 @@ class Rule {
this.declarations = node.block.children.map((node: Node) => new Declaration(node));
}
apply(node: Element, stack: Element[]) {
apply(node: ClassTarget, stack: ClassTarget[]) {
this.selectors.forEach(selector => selector.apply(node, stack)); // TODO move the logic in here?
}
@ -147,7 +152,7 @@ class Atrule {
this.children = [];
}
apply(node: Element, stack: Element[]) {
apply(node: ClassTarget, stack: ClassTarget[]) {
if (this.node.name === 'media' || this.node.name === 'supports') {
this.children.forEach(child => {
child.apply(node, stack);
@ -323,10 +328,10 @@ export default class Stylesheet {
}
}
apply(node: Element) {
apply(node: ClassTarget) {
if (!this.has_styles) return;
const stack: Element[] = [];
const stack: ClassTarget[] = [];
let parent: Node = node;
while (parent = parent.parent) {
if (parent.type === 'Element') stack.unshift(parent as Element);

@ -1,5 +1,7 @@
import Node from './shared/Node';
import Attribute from './Attribute';
import Class from './Class';
import Text from './Text';
import map_children from './shared/map_children';
import Binding from './Binding';
import EventHandler from './EventHandler';
@ -15,6 +17,7 @@ export default class InlineComponent extends Node {
expression: Expression;
attributes: Attribute[] = [];
bindings: Binding[] = [];
classes: Class[] = [];
handlers: EventHandler[] = [];
lets: Let[] = [];
children: INode[];
@ -60,10 +63,8 @@ export default class InlineComponent extends Node {
break;
case 'Class':
component.error(node, {
code: `invalid-class`,
message: `Classes can only be applied to DOM elements, not components`
});
this.classes.push(new Class(component, this, scope, node));
break;
case 'EventHandler':
this.handlers.push(new EventHandler(component, this, scope, node));
@ -99,5 +100,31 @@ export default class InlineComponent extends Node {
}
this.children = map_children(component, this, this.scope, info.children);
component.stylesheet.apply(this);
}
add_css_class(class_name = this.component.stylesheet.id) {
const class_attribute = this.attributes.find(a => a.name === 'class');
if (class_attribute && !class_attribute.is_true) {
if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') {
(class_attribute.chunks[0] as Text).data += ` ${class_name}`;
} else {
(<Node[]>class_attribute.chunks).push(
new Text(this.component, this, this.scope, {
type: 'Text',
data: ` ${class_name}`
})
);
}
} else {
this.attributes.push(
new Attribute(this.component, this, this.scope, {
type: 'Attribute',
name: 'class',
value: [{ type: 'Text', data: class_name }]
})
);
}
}
}

@ -0,0 +1,15 @@
<script>
const Nested = window.Nested;
export let clazz = '';
</script>
<div class="foo">
<Nested class={ clazz } />
</div>
<style>
.foo {
color: red;
}
</style>

@ -0,0 +1 @@
.custom-class.svelte-xyz{color:red}

@ -0,0 +1,13 @@
<script>
const Nested = window.Nested;
export let prop;
</script>
<Nested class="custom-class" prop={ prop } />
<style>
.custom-class {
color: red;
}
</style>

@ -0,0 +1,13 @@
<script>
const Nested = window.Nested;
</script>
<div class="foo">
<Nested class="no-definition" />
</div>
<style>
.foo {
color: red;
}
</style>

@ -0,0 +1,25 @@
export default {
warnings: [{
filename: "SvelteComponent.svelte",
code: `css-unused-selector`,
message: "Unused CSS selector",
start: {
line: 12,
column: 1,
character: 110
},
end: {
line: 12,
column: 5,
character: 114
},
pos: 110,
frame: `
10: }
11:
12: .bar {
^
13: color: blue;
14: }`
}]
};

@ -0,0 +1,15 @@
<script>
const Nested = window.Nested;
</script>
<Nested class="foo" />
<style>
.foo {
color: red;
}
.bar {
color: blue;
}
</style>

@ -0,0 +1 @@
.custom-class.svelte-xyz{color:red}

@ -0,0 +1,11 @@
<script>
const Nested = window.Nested;
</script>
<Nested class="custom-class" />
<style>
.custom-class {
color: red;
}
</style>

@ -0,0 +1 @@
.custom.svelte-xyz{color:red}.custom2.svelte-xyz{background:yellow}

@ -0,0 +1,17 @@
<script>
const Nested = window.Nested;
export let clazz = 'foo';
</script>
<Nested class="custom { clazz } custom2" />
<style>
.custom {
color: red;
}
.custom2 {
background: yellow;
}
</style>

@ -0,0 +1,61 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent,
init,
mount_component,
noop,
safe_not_equal
} from "svelte/internal";
function create_fragment(ctx) {
var current;
var nested = new ctx.Nested({ props: { class: "custom" } });
nested.$on("click", ctx.handleClick);
return {
c() {
nested.$$.fragment.c();
},
m(target, anchor) {
mount_component(nested, target, anchor);
current = true;
},
p: noop,
i(local) {
if (current) return;
nested.$$.fragment.i(local);
current = true;
},
o(local) {
nested.$$.fragment.o(local);
current = false;
},
d(detaching) {
nested.$destroy(detaching);
}
};
}
function instance($$self) {
const Nested = window.Nested;
let handleClick = () => {};
return { Nested, handleClick };
}
class Component extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, []);
}
}
export default Component;

@ -0,0 +1,7 @@
<script>
const Nested = window.Nested;
let handleClick = () => {};
</script>
<Nested class="custom" on:click={ handleClick } />

@ -0,0 +1,77 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent,
append,
detach,
element,
init,
insert,
mount_component,
noop,
safe_not_equal
} from "svelte/internal";
function add_css() {
var style = element("style");
style.id = 'svelte-sg04hs-style';
style.textContent = ".foo.svelte-sg04hs{color:red}";
append(document.head, style);
}
function create_fragment(ctx) {
var div, current;
var nested = new ctx.Nested({ props: { class: "no-definition" } });
return {
c() {
div = element("div");
nested.$$.fragment.c();
div.className = "foo svelte-sg04hs";
},
m(target, anchor) {
insert(target, div, anchor);
mount_component(nested, div, null);
current = true;
},
p: noop,
i(local) {
if (current) return;
nested.$$.fragment.i(local);
current = true;
},
o(local) {
nested.$$.fragment.o(local);
current = false;
},
d(detaching) {
if (detaching) {
detach(div);
}
nested.$destroy();
}
};
}
function instance($$self) {
const Nested = window.Nested;
return { Nested };
}
class Component extends SvelteComponent {
constructor(options) {
super();
if (!document.getElementById("svelte-sg04hs-style")) add_css();
init(this, options, instance, create_fragment, safe_not_equal, []);
}
}
export default Component;

@ -0,0 +1,13 @@
<script>
const Nested = window.Nested;
</script>
<div class="foo">
<Nested class="no-definition" />
</div>
<style>
.foo {
color: red;
}
</style>

@ -0,0 +1,70 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent,
append,
element,
init,
mount_component,
noop,
safe_not_equal
} from "svelte/internal";
function add_css() {
var style = element("style");
style.id = 'svelte-1rk2jaa-style';
style.textContent = ".custom.svelte-1rk2jaa{color:'blue'}";
append(document.head, style);
}
function create_fragment(ctx) {
var current;
var nested = new ctx.Nested({
props: { class: "custom svelte-1rk2jaa" }
});
return {
c() {
nested.$$.fragment.c();
},
m(target, anchor) {
mount_component(nested, target, anchor);
current = true;
},
p: noop,
i(local) {
if (current) return;
nested.$$.fragment.i(local);
current = true;
},
o(local) {
nested.$$.fragment.o(local);
current = false;
},
d(detaching) {
nested.$destroy(detaching);
}
};
}
function instance($$self) {
const Nested = window.Nested;
return { Nested };
}
class Component extends SvelteComponent {
constructor(options) {
super();
if (!document.getElementById("svelte-1rk2jaa-style")) add_css();
init(this, options, instance, create_fragment, safe_not_equal, []);
}
}
export default Component;

@ -0,0 +1,11 @@
<script>
const Nested = window.Nested;
</script>
<Nested class="custom" />
<style>
.custom {
color: 'blue';
}
</style>

@ -0,0 +1,77 @@
/* generated by Svelte vX.Y.Z */
import {
SvelteComponent,
append,
element,
init,
mount_component,
safe_not_equal
} from "svelte/internal";
function add_css() {
var style = element("style");
style.id = 'svelte-167wvy9-style';
style.textContent = ".custom.svelte-167wvy9{color:'blue'}.custom2.svelte-167wvy9{background:'fuchsia'}";
append(document.head, style);
}
function create_fragment(ctx) {
var current;
var nested = new ctx.Nested({
props: {
class: "custom custom2 " + otherCls + " svelte-167wvy9"
}
});
return {
c() {
nested.$$.fragment.c();
},
m(target, anchor) {
mount_component(nested, target, anchor);
current = true;
},
p(changed, ctx) {
var nested_changes = {};
if (changed.otherCls) nested_changes.class = "custom custom2 " + otherCls + " svelte-167wvy9";
nested.$set(nested_changes);
},
i(local) {
if (current) return;
nested.$$.fragment.i(local);
current = true;
},
o(local) {
nested.$$.fragment.o(local);
current = false;
},
d(detaching) {
nested.$destroy(detaching);
}
};
}
let otherCls = 'asd';
function instance($$self) {
const Nested = window.Nested;
return { Nested };
}
class Component extends SvelteComponent {
constructor(options) {
super();
if (!document.getElementById("svelte-167wvy9-style")) add_css();
init(this, options, instance, create_fragment, safe_not_equal, []);
}
}
export default Component;

@ -0,0 +1,17 @@
<script>
const Nested = window.Nested;
let otherCls = 'asd';
</script>
<Nested class="custom custom2 {otherCls}" />
<style>
.custom {
color: 'blue';
}
.custom2 {
background: 'fuchsia';
}
</style>

@ -0,0 +1,7 @@
<script>
let clazz;
export { clazz as class };
</script>
<p class={ clazz }>i am a widget</p>

@ -0,0 +1 @@
<div><p class="foo svelte-40y5y5">i am a widget</p></div>

@ -0,0 +1,13 @@
<script>
import Widget from './Widget.svelte';
</script>
<style>
.foo {
color: yellow;
}
</style>
<div>
<Widget class="foo" />
</div>
Loading…
Cancel
Save