Skip to content

Commit e2538c5

Browse files
[feat] support --style-props for <svelte:component> (#7468)
* add test * support --style-props for <svelte:component> * refactor * add more test * support switching instance * add test with svelte:self * merge duplicated if statement * slight refactor * remove unnecessary anchor * reorder insertion Co-authored-by: tanhauhau <[email protected]>
1 parent 75a7c3e commit e2538c5

File tree

23 files changed

+808
-65
lines changed

23 files changed

+808
-65
lines changed

src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts

Lines changed: 92 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -399,12 +399,21 @@ export default class InlineComponentWrapper extends Wrapper {
399399
return b`${name}.$on("${handler.name}", ${snippet});`;
400400
});
401401

402+
const mount_target = has_css_custom_properties ? css_custom_properties_wrapper : (parent_node || '#target');
403+
const mount_anchor = has_css_custom_properties ? 'null' : (parent_node ? 'null' : '#anchor');
404+
const to_claim = parent_nodes && this.renderer.options.hydratable;
405+
let claim_nodes = parent_nodes;
406+
402407
if (this.node.name === 'svelte:component') {
403408
const switch_value = block.get_unique_name('switch_value');
404409
const switch_props = block.get_unique_name('switch_props');
405410

406411
const snippet = this.node.expression.manipulate(block);
407412

413+
if (has_css_custom_properties) {
414+
this.set_css_custom_properties(block, css_custom_properties_wrapper);
415+
}
416+
408417
block.chunks.init.push(b`
409418
var ${switch_value} = ${snippet};
410419
@@ -427,39 +436,43 @@ export default class InlineComponentWrapper extends Wrapper {
427436
b`if (${name}) @create_component(${name}.$$.fragment);`
428437
);
429438

430-
if (parent_nodes && this.renderer.options.hydratable) {
431-
block.chunks.claim.push(
432-
b`if (${name}) @claim_component(${name}.$$.fragment, ${parent_nodes});`
433-
);
434-
}
435-
436-
block.chunks.mount.push(b`
437-
if (${name}) {
438-
@mount_component(${name}, ${parent_node || '#target'}, ${parent_node ? 'null' : '#anchor'});
439-
}
440-
`);
439+
if (css_custom_properties_wrapper) this.create_css_custom_properties_wrapper_mount_chunk(block, parent_node, css_custom_properties_wrapper);
440+
block.chunks.mount.push(b`if (${name}) @mount_component(${name}, ${mount_target}, ${mount_anchor});`);
441441

442-
const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
443-
const update_mount_node = this.get_update_mount_node(anchor);
442+
if (to_claim) {
443+
if (css_custom_properties_wrapper) claim_nodes = this.create_css_custom_properties_wrapper_claim_chunk(block, claim_nodes, css_custom_properties_wrapper);
444+
block.chunks.claim.push(b`if (${name}) @claim_component(${name}.$$.fragment, ${claim_nodes});`);
445+
}
444446

445447
if (updates.length) {
446448
block.chunks.update.push(b`
447449
${updates}
448450
`);
449451
}
450452

453+
const tmp_anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
454+
const anchor = has_css_custom_properties ? 'null' : tmp_anchor;
455+
const update_mount_node = has_css_custom_properties ? css_custom_properties_wrapper : this.get_update_mount_node(tmp_anchor);
456+
const update_insert =
457+
css_custom_properties_wrapper &&
458+
(tmp_anchor.name !== 'null'
459+
? b`@insert(${tmp_anchor}.parentNode, ${css_custom_properties_wrapper}, ${tmp_anchor});`
460+
: b`@insert(${parent_node}, ${css_custom_properties_wrapper}, ${tmp_anchor});`);
461+
451462
block.chunks.update.push(b`
452463
if (${switch_value} !== (${switch_value} = ${snippet})) {
453464
if (${name}) {
454465
@group_outros();
455466
const old_component = ${name};
456467
@transition_out(old_component.$$.fragment, 1, 0, () => {
457468
@destroy_component(old_component, 1);
469+
${has_css_custom_properties ? b`@detach(${update_mount_node})` : null}
458470
});
459471
@check_outros();
460472
}
461473
462474
if (${switch_value}) {
475+
${update_insert}
463476
${name} = new ${switch_value}(${switch_props}(#ctx));
464477
465478
${munged_bindings}
@@ -501,62 +514,16 @@ export default class InlineComponentWrapper extends Wrapper {
501514
`);
502515

503516
if (has_css_custom_properties) {
504-
block.chunks.create.push(b`${css_custom_properties_wrapper} = @element("div");`);
505-
block.chunks.hydrate.push(b`@set_style(${css_custom_properties_wrapper}, "display", "contents");`);
506-
this.node.css_custom_properties.forEach(attr => {
507-
const dependencies = attr.get_dependencies();
508-
const should_cache = attr.should_cache();
509-
const last = should_cache && block.get_unique_name(`${attr.name.replace(/[^a-zA-Z_$]/g, '_')}_last`);
510-
if (should_cache) block.add_variable(last);
511-
const value = attr.get_value(block);
512-
const init = should_cache ? x`${last} = ${value}` : value;
513-
514-
block.chunks.hydrate.push(b`@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${init});`);
515-
if (dependencies.length > 0) {
516-
let condition = block.renderer.dirty(dependencies);
517-
if (should_cache) condition = x`${condition} && (${last} !== (${last} = ${value}))`;
518-
519-
block.chunks.update.push(b`
520-
if (${condition}) {
521-
@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${should_cache ? last : value});
522-
}
523-
`);
524-
}
525-
});
517+
this.set_css_custom_properties(block, css_custom_properties_wrapper);
526518
}
527519
block.chunks.create.push(b`@create_component(${name}.$$.fragment);`);
528520

529-
if (parent_nodes && this.renderer.options.hydratable) {
530-
let nodes = parent_nodes;
531-
if (has_css_custom_properties) {
532-
nodes = block.get_unique_name(`${css_custom_properties_wrapper.name}_nodes`);
533-
block.chunks.claim.push(b`
534-
${css_custom_properties_wrapper} = @claim_element(${parent_nodes}, "DIV", { style: true })
535-
var ${nodes} = @children(${css_custom_properties_wrapper});
536-
`);
537-
}
538-
block.chunks.claim.push(
539-
b`@claim_component(${name}.$$.fragment, ${nodes});`
540-
);
541-
}
521+
if (css_custom_properties_wrapper) this.create_css_custom_properties_wrapper_mount_chunk(block, parent_node, css_custom_properties_wrapper);
522+
block.chunks.mount.push(b`@mount_component(${name}, ${mount_target}, ${mount_anchor});`);
542523

543-
if (has_css_custom_properties) {
544-
if (parent_node) {
545-
block.chunks.mount.push(b`@append(${parent_node}, ${css_custom_properties_wrapper})`);
546-
if (is_head(parent_node)) {
547-
block.chunks.destroy.push(b`@detach(${css_custom_properties_wrapper});`);
548-
}
549-
} else {
550-
block.chunks.mount.push(b`@insert(#target, ${css_custom_properties_wrapper}, #anchor);`);
551-
// TODO we eventually need to consider what happens to elements
552-
// that belong to the same outgroup as an outroing element...
553-
block.chunks.destroy.push(b`if (detaching) @detach(${css_custom_properties_wrapper});`);
554-
}
555-
block.chunks.mount.push(b`@mount_component(${name}, ${css_custom_properties_wrapper}, null);`);
556-
} else {
557-
block.chunks.mount.push(
558-
b`@mount_component(${name}, ${parent_node || '#target'}, ${parent_node ? 'null' : '#anchor'});`
559-
);
524+
if (to_claim) {
525+
if (css_custom_properties_wrapper) claim_nodes = this.create_css_custom_properties_wrapper_claim_chunk(block, claim_nodes, css_custom_properties_wrapper);
526+
block.chunks.claim.push(b`@claim_component(${name}.$$.fragment, ${claim_nodes});`);
560527
}
561528

562529
block.chunks.intro.push(b`
@@ -579,4 +546,64 @@ export default class InlineComponentWrapper extends Wrapper {
579546
);
580547
}
581548
}
549+
550+
private create_css_custom_properties_wrapper_mount_chunk(
551+
block: Block,
552+
parent_node: Identifier,
553+
css_custom_properties_wrapper: Identifier | null
554+
) {
555+
if (parent_node) {
556+
block.chunks.mount.push(b`@append(${parent_node}, ${css_custom_properties_wrapper})`);
557+
if (is_head(parent_node)) {
558+
block.chunks.destroy.push(b`@detach(${css_custom_properties_wrapper});`);
559+
}
560+
} else {
561+
block.chunks.mount.push(b`@insert(#target, ${css_custom_properties_wrapper}, #anchor);`);
562+
// TODO we eventually need to consider what happens to elements
563+
// that belong to the same outgroup as an outroing element...
564+
block.chunks.destroy.push(b`if (detaching && ${this.var}) @detach(${css_custom_properties_wrapper});`);
565+
}
566+
}
567+
568+
private create_css_custom_properties_wrapper_claim_chunk(
569+
block: Block,
570+
parent_nodes: Identifier,
571+
css_custom_properties_wrapper: Identifier | null
572+
) {
573+
const nodes = block.get_unique_name(`${css_custom_properties_wrapper.name}_nodes`);
574+
block.chunks.claim.push(b`
575+
${css_custom_properties_wrapper} = @claim_element(${parent_nodes}, "DIV", { style: true })
576+
var ${nodes} = @children(${css_custom_properties_wrapper});
577+
`);
578+
return nodes;
579+
}
580+
581+
private set_css_custom_properties(
582+
block: Block,
583+
css_custom_properties_wrapper: Identifier
584+
) {
585+
block.chunks.create.push(b`${css_custom_properties_wrapper} = @element("div");`);
586+
block.chunks.hydrate.push(b`@set_style(${css_custom_properties_wrapper}, "display", "contents");`);
587+
this.node.css_custom_properties.forEach((attr) => {
588+
const dependencies = attr.get_dependencies();
589+
const should_cache = attr.should_cache();
590+
const last = should_cache && block.get_unique_name(`${attr.name.replace(/[^a-zA-Z_$]/g, '_')}_last`);
591+
if (should_cache) block.add_variable(last);
592+
const value = attr.get_value(block);
593+
const init = should_cache ? x`${last} = ${value}` : value;
594+
595+
block.chunks.hydrate.push(
596+
b`@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${init});`
597+
);
598+
if (dependencies.length > 0) {
599+
let condition = block.renderer.dirty(dependencies);
600+
if (should_cache) condition = x`${condition} && (${last} !== (${last} = ${value}))`;
601+
block.chunks.update.push(b`
602+
if (${condition}) {
603+
@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${should_cache ? last : value});
604+
}
605+
`);
606+
}
607+
});
608+
}
582609
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script>
2+
export let id;
3+
</script>
4+
5+
<div {id}>
6+
<p>Slider</p>
7+
<span>Track</span>
8+
</div>
9+
10+
<style>
11+
p {
12+
color: var(--rail-color);
13+
}
14+
span {
15+
color: var(--track-color);
16+
}
17+
</style>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
export default {
2+
props: {
3+
railColor1: 'black',
4+
trackColor1: 'red',
5+
railColor2: 'green',
6+
trackColor2: 'blue'
7+
},
8+
html: `
9+
<div style="display: contents; --rail-color:black; --track-color:red;">
10+
<div id="slider-1">
11+
<p class="svelte-17ay6rc">Slider</p>
12+
<span class="svelte-17ay6rc">Track</span>
13+
</div>
14+
</div>
15+
<div style="display: contents; --rail-color:green; --track-color:blue;">
16+
<div id="slider-2">
17+
<p class="svelte-17ay6rc">Slider</p>
18+
<span class="svelte-17ay6rc">Track</span>
19+
</div>
20+
</div>
21+
`,
22+
test({ component, assert, target }) {
23+
component.railColor1 = 'yellow';
24+
component.trackColor2 = 'orange';
25+
26+
assert.htmlEqual(target.innerHTML, `
27+
<div style="display: contents; --rail-color:yellow; --track-color:red;">
28+
<div id="slider-1">
29+
<p class="svelte-17ay6rc">Slider</p>
30+
<span class="svelte-17ay6rc">Track</span>
31+
</div>
32+
</div>
33+
<div style="display: contents; --rail-color:green; --track-color:orange;">
34+
<div id="slider-2">
35+
<p class="svelte-17ay6rc">Slider</p>
36+
<span class="svelte-17ay6rc">Track</span>
37+
</div>
38+
</div>
39+
`);
40+
}
41+
};
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<script>
2+
import Slider from './Slider.svelte';
3+
export let railColor1;
4+
export let railColor2;
5+
export let trackColor1;
6+
export let trackColor2;
7+
8+
function identity(color) {
9+
return color;
10+
}
11+
</script>
12+
13+
<svelte:component
14+
this={Slider}
15+
id="slider-1"
16+
--rail-color={railColor1}
17+
--track-color={trackColor1}
18+
/>
19+
20+
<svelte:component
21+
this={Slider}
22+
id="slider-2"
23+
--rail-color={railColor2}
24+
--track-color={identity(trackColor2)}
25+
/>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script>
2+
export let id;
3+
</script>
4+
5+
<div {id}>
6+
<p>Slider1</p>
7+
<span>Track</span>
8+
</div>
9+
10+
<style>
11+
p {
12+
color: var(--rail-color);
13+
}
14+
span {
15+
color: var(--track-color);
16+
}
17+
</style>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script>
2+
export let id;
3+
</script>
4+
5+
<div {id}>
6+
<p>Slider2</p>
7+
<span>Track</span>
8+
</div>
9+
10+
<style>
11+
p {
12+
color: var(--rail-color);
13+
}
14+
span {
15+
color: var(--track-color);
16+
}
17+
</style>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
export default {
2+
props: {
3+
componentName: 'Slider1'
4+
},
5+
html: `
6+
<div style="display: contents; --rail-color:rgb(0, 0, 0); --track-color:rgb(255, 0, 0);">
7+
<div id="component1">
8+
<p class="svelte-17ay6rc">Slider1</p>
9+
<span class="svelte-17ay6rc">Track</span>
10+
</div>
11+
</div>
12+
<div style="display: contents; --rail-color:rgb(0, 255, 0); --track-color:rgb(0, 0, 255);">
13+
<div id="component2">
14+
<p class="svelte-17ay6rc">Slider1</p>
15+
<span class="svelte-17ay6rc">Track</span>
16+
</div>
17+
</div>
18+
`,
19+
test({ target, window, assert, component }) {
20+
21+
function assert_slider_1() {
22+
const railColor1 = target.querySelector('#component1 p');
23+
const trackColor1 = target.querySelector('#component1 span');
24+
const railColor2 = target.querySelector('#component2 p');
25+
const trackColor2 = target.querySelector('#component2 span');
26+
27+
assert.equal(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
28+
assert.equal(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
29+
assert.equal(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
30+
assert.equal(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
31+
assert.equal(railColor1.textContent, 'Slider1');
32+
assert.equal(railColor2.textContent, 'Slider1');
33+
}
34+
35+
function assert_slider_2() {
36+
const railColor1 = target.querySelector('#component1 p');
37+
const trackColor1 = target.querySelector('#component1 span');
38+
const railColor2 = target.querySelector('#component2 p');
39+
const trackColor2 = target.querySelector('#component2 span');
40+
41+
assert.equal(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
42+
assert.equal(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
43+
assert.equal(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
44+
assert.equal(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
45+
assert.equal(railColor1.textContent, 'Slider2');
46+
assert.equal(railColor2.textContent, 'Slider2');
47+
}
48+
49+
assert_slider_1();
50+
component.componentName = 'Slider2';
51+
assert_slider_2();
52+
component.componentName = undefined;
53+
assert.equal(window.document.querySelector('div'), null);
54+
component.componentName = 'Slider1';
55+
assert_slider_1();
56+
}
57+
};

0 commit comments

Comments
 (0)