8000 compile: support classes on inline components · sveltejs/svelte@5214c75 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5214c75

Browse files
committed
compile: support classes on inline components
Closes #2870
1 parent 242c3d3 commit 5214c75

File tree

27 files changed

+510
-8
lines changed

27 files changed

+510
-8
lines changed

src/compile/css/Stylesheet.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ import MagicString from 'magic-string';
22
import { walk } from 'estree-walker';
33
import Selector from './Selector';
44
import Element from '../nodes/Element';
5+
import InlineComponent from '../nodes/InlineComponent';
56
import { Node, Ast } from '../../interfaces';
67
import Component from '../Component';
78

9+
type ClassTarget = Element
10+
| InlineComponent;
11+
12+
813
function remove_css_prefix(name: string): string {
914
return name.replace(/^-((webkit)|(moz)|(o)|(ms))-/, '');
1015
}
@@ -33,7 +38,7 @@ class Rule {
3338
this.declarations = node.block.children.map((node: Node) => new Declaration(node));
3439
}
3540

36-
apply(node: Element, stack: Element[]) {
41+
apply(node: ClassTarget, stack: ClassTarget[]) {
3742
this.selectors.forEach(selector => selector.apply(node, stack)); // TODO move the logic in here?
3843
}
3944

@@ -147,7 +152,7 @@ class Atrule {
147152
this.children = [];
148153
}
149154

150-
apply(node: Element, stack: Element[]) {
155+
apply(node: ClassTarget, stack: ClassTarget[]) {
151156
if (this.node.name === 'media' || this.node.name === 'supports') {
152157
this.children.forEach(child => {
153158
child.apply(node, stack);
@@ -323,10 +328,10 @@ export default class Stylesheet {
323328
}
324329
}
325330

326-
apply(node: Element) {
331+
apply(node: ClassTarget) {
327332
if (!this.has_styles) return;
328333

329-
const stack: Element[] = [];
334+
const stack: ClassTarget[] = [];
330335
let parent: Node = node;
331336
while (parent = parent.parent) {
332337
if (parent.type === 'Element') stack.unshift(parent as Element);

src/compile/nodes/InlineComponent.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import Node from './shared/Node';
22
import Attribute from './Attribute';
3+
import Class from './Class';
4+
import Text from './Text';
35
import map_children from './shared/map_children';
46
import Binding from './Binding';
57
import EventHandler from './EventHandler';
@@ -15,6 +17,7 @@ export default class InlineComponent extends Node {
1517
expression: Expression;
1618
attributes: Attribute[] = [];
1719
bindings: Binding[] = [];
20+
classes: Class[] = [];
1821
handlers: EventHandler[] = [];
1922
lets: Let[] = [];
2023
children: INode[];
@@ -60,10 +63,8 @@ export default class InlineComponent extends Node {
6063
break;
6164

6265
case 'Class':
63-
component.error(node, {
64-
code: `invalid-class`,
65-
message: `Classes can only be applied to DOM elements, not components`
66-
});
66+
this.classes.push(new Class(component, this, scope, node));
67+
break;
6768

6869
case 'EventHandler':
6970
this.handlers.push(new EventHandler(component, this, scope, node));
@@ -99,5 +100,31 @@ export default class InlineComponent extends Node {
99100
}
100101

101102
this.children = map_children(component, this, this.scope, info.children);
103+
104+
component.stylesheet.apply(this);
105+
}
106+
107+
add_css_class(class_name = this.component.stylesheet.id) {
108+
const class_attribute = this.attributes.find(a => a.name === 'class');
109+
if (class_attribute && !class_attribute.is_true) {
110+
if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') {
111+
(class_attribute.chunks[0] as Text).data += ` ${class_name}`;
112+
} else {
113+
(<Node[]>class_attribute.chunks E377 ).push(
114+
new Text(this.component, this, this.scope, {
115+
type: 'Text',
116+
data: ` ${class_name}`
117+
})
118+
);
119+
}
120+
} else {
121+
this.attributes.push(
122+
new Attribute(this.component, this, this.scope, {
123+
type: 'Attribute',
124+
name: 'class',
125+
value: [{ type: 'Text', data: class_name }]
126+
})
127+
);
128+
}
102129
}
103130
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.foo.svelte-xyz{color:red}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script>
2+
const Nested = window.Nested;
3+
4+
export let clazz = '';
5+
</script>
6+
7+
<div class="foo">
8+
<Nested class={ clazz } />
9+
</div>
10+
11+
<style>
12+
.foo {
13+
color: red;
14+
}
15+
</style>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.custom-class.svelte-xyz{color:red}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
const Nested = window.Nested;
3+
4+
export let prop;
5+
</script>
6+
7+
<Nested class="custom-class" prop={ prop } />
8+
9+
<style>
10+
.custom-class {
11+
color: red;
12+
}
13+
</style>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.foo.svelte-xyz{color:red}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
const Nested = window.Nested;
3+
</script>
4+
5+
<div class="foo">
6+
<Nested class="no-definition" />
7+
</div>
8+
9+
<style>
10+
.foo {
11+
color: red;
12+
}
13+
</style>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export default {
2+
warnings: [{
3+
filename: "SvelteComponent.svelte",
4+
code: `css-unused-selector`,
5+
message: "Unused CSS selector",
6+
start: {
7+
line: 12,
8+
column: 1,
9+
character: 110
10+
},
11+
end: {
12+
line: 12,
13+
column: 5,
14+
character: 114
15+
},
16+
pos: 110,
17+
frame: `
18+
10: }
19+
11:
20+
12: .bar {
21+
^
22+
13: color: blue;
23+
14: }`
24+
}]
25+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.foo.svelte-xyz{color:red}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script>
2+
const Nested = window.Nested;
3+
</script>
4+
5+
<Nested class="foo" />
6+
7+
<style>
8+
.foo {
9+
color: red;
10+
}
11+
12+
.bar {
13+
color: blue;
14+
}
15+
</style>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.custom-class.svelte-xyz{color:red}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
const Nested = window.Nested;
3+
</script>
4+
5+
<Nested class="custom-class" />
6+
7+
<style>
8+
.custom-class {
9+
color: red;
10+
}
11+
</style>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.custom.svelte-xyz{color:red}.custom2.svelte-xyz{background:yellow}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script>
2+
const Nested = window.Nested;
3+
4+
export let clazz = 'foo';
5+
</script>
6+
7+
<Nested class="custom { clazz } custom2" />
8+
9+
<style>
10+
.custom {
11+
color: red;
12+
}
13+
14+
.custom2 {
15+
background: yellow;
16+
}
17+
</style>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/* generated by Svelte vX.Y.Z */
2+
import {
3+
SvelteComponent,
4+
init,
5+
mount_component,
6+
noop,
7+
safe_not_equal
8+
} from "svelte/internal";
9+
10+
function create_fragment(ctx) {
11+
var current;
12+
13+
var nested = new ctx.Nested({ props: { class: "custom" } });
14+
nested.$on("click", ctx.handleClick);
15+
16+
return {
17+
c() {
18+
nested.$$.fragment.c();
19+
},
20+
21+
m(target, anchor) {
22+
mount_component(nested, target, anchor);
23+
current = true;
24+
},
25+
26+
p: noop,
27+
28+
i(local) {
29+
if (current) return;
30+
nested.$$.fragment.i(local);
31+
32+
current = true;
33+
},
34+
35+
o(local) {
36+
nested.$$.fragment.o(local);
37+
current = false;
38+
},
39+
40+
d(detaching) {
41+
nested.$destroy(detaching);
42+
}
43+
};
44+
}
45+
46+
function instance($$self) {
47+
const Nested = window.Nested;
48+
49+
let handleClick = () => {};
50+
51+
return { Nested, handleClick };
52+
}
53+
54+
class Component extends SvelteComponent {
55+
constructor(options) {
56+
super();
57+
init(this, options, instance, create_fragment, safe_not_equal, []);
58+
}
59+
}
60+
61+
export default Component;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
const Nested = window.Nested;
3+
4+
let handleClick = () => {};
5+
</script>
6+
7+
<Nested class="custom" on:click={ handleClick } />
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/* generated by Svelte vX.Y.Z */
2+
import {
3+
SvelteComponent,
4+
append,
5< F438 /td>+
detach,
6+
element,
7+
init,
8+
insert,
9+
mount_component,
10+
noop,
11+
safe_not_equal
12+
} from "svelte/internal";
13+
14+
function add_css() {
15+
var style = element("style");
16+
style.id = 'svelte-sg04hs-style';
17+
style.textContent = ".foo.svelte-sg04hs{color:red}";
18+
append(document.head, style);
19+
}
20+
21+
function create_fragment(ctx) {
22+
var div, current;
23+
24+
var nested = new ctx.Nested({ props: { class: "no-definition" } });
25+
26+
return {
27+
c() {
28+
div = element("div");
29+
nested.$$.fragment.c();
30+
div.className = "foo svelte-sg04hs";
31+
},
32+
33+
m(target, anchor) {
34+
insert(target, div, anchor);
35+
mount_component(nested, div, null);
36+
current = true;
37+
},
38+
39+
p: noop,
40+
41+
i(local) {
42+
if (current) return;
43+
nested.$$.fragment.i(local);
44+
45+
current = true;
46+
},
47+
48+
o(local) {
49+
nested.$$.fragment.o(local);
50+
current = false;
51+
},
52+
53+
d(detaching) {
54+
if (detaching) {
55+
detach(div);
56+
}
57+
58+
nested.$destroy();
59+
}
60+
};
61+
}
62+
63+
function instance($$self) {
64+
const Nested = window.Nested;
65+
66+
return { Nested };
67+
}
68+
69+
class Component extends SvelteComponent {
70+
constructor(options) {
71+
super();
72+
if (!document.getElementById("svelte-sg04hs-style")) add_css();
73+
init(this, options, instance, create_fragment, safe_not_equal, []);
74+
}
75+
}
76+
77+
export default Component;

0 commit comments

Comments
 (0)
0