Skip to content

Commit

Permalink
[feat] support --style-props for <svelte:component> (#7468)
Browse files Browse the repository at this point in the history
* 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 <lhtan93@gmail.com>
  • Loading branch information
baseballyama and tanhauhau committed Sep 13, 2022
1 parent 75a7c3e commit e2538c5
Show file tree
Hide file tree
Showing 23 changed files with 808 additions and 65 deletions.
157 changes: 92 additions & 65 deletions src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,12 +399,21 @@ export default class InlineComponentWrapper extends Wrapper {
return b`${name}.$on("${handler.name}", ${snippet});`;
});

const mount_target = has_css_custom_properties ? css_custom_properties_wrapper : (parent_node || '#target');
const mount_anchor = has_css_custom_properties ? 'null' : (parent_node ? 'null' : '#anchor');
const to_claim = parent_nodes && this.renderer.options.hydratable;
let claim_nodes = parent_nodes;

if (this.node.name === 'svelte:component') {
const switch_value = block.get_unique_name('switch_value');
const switch_props = block.get_unique_name('switch_props');

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

if (has_css_custom_properties) {
this.set_css_custom_properties(block, css_custom_properties_wrapper);
}

block.chunks.init.push(b`
var ${switch_value} = ${snippet};
Expand All @@ -427,39 +436,43 @@ export default class InlineComponentWrapper extends Wrapper {
b`if (${name}) @create_component(${name}.$$.fragment);`
);

if (parent_nodes && this.renderer.options.hydratable) {
block.chunks.claim.push(
b`if (${name}) @claim_component(${name}.$$.fragment, ${parent_nodes});`
);
}

block.chunks.mount.push(b`
if (${name}) {
@mount_component(${name}, ${parent_node || '#target'}, ${parent_node ? 'null' : '#anchor'});
}
`);
if (css_custom_properties_wrapper) this.create_css_custom_properties_wrapper_mount_chunk(block, parent_node, css_custom_properties_wrapper);
block.chunks.mount.push(b`if (${name}) @mount_component(${name}, ${mount_target}, ${mount_anchor});`);

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

if (updates.length) {
block.chunks.update.push(b`
${updates}
`);
}

const tmp_anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
const anchor = has_css_custom_properties ? 'null' : tmp_anchor;
const update_mount_node = has_css_custom_properties ? css_custom_properties_wrapper : this.get_update_mount_node(tmp_anchor);
const update_insert =
css_custom_properties_wrapper &&
(tmp_anchor.name !== 'null'
? b`@insert(${tmp_anchor}.parentNode, ${css_custom_properties_wrapper}, ${tmp_anchor});`
: b`@insert(${parent_node}, ${css_custom_properties_wrapper}, ${tmp_anchor});`);

block.chunks.update.push(b`
if (${switch_value} !== (${switch_value} = ${snippet})) {
if (${name}) {
@group_outros();
const old_component = ${name};
@transition_out(old_component.$$.fragment, 1, 0, () => {
@destroy_component(old_component, 1);
${has_css_custom_properties ? b`@detach(${update_mount_node})` : null}
});
@check_outros();
}
if (${switch_value}) {
${update_insert}
${name} = new ${switch_value}(${switch_props}(#ctx));
${munged_bindings}
Expand Down Expand Up @@ -501,62 +514,16 @@ export default class InlineComponentWrapper extends Wrapper {
`);

if (has_css_custom_properties) {
block.chunks.create.push(b`${css_custom_properties_wrapper} = @element("div");`);
block.chunks.hydrate.push(b`@set_style(${css_custom_properties_wrapper}, "display", "contents");`);
this.node.css_custom_properties.forEach(attr => {
const dependencies = attr.get_dependencies();
const should_cache = attr.should_cache();
const last = should_cache && block.get_unique_name(`${attr.name.replace(/[^a-zA-Z_$]/g, '_')}_last`);
if (should_cache) block.add_variable(last);
const value = attr.get_value(block);
const init = should_cache ? x`${last} = ${value}` : value;

block.chunks.hydrate.push(b`@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${init});`);
if (dependencies.length > 0) {
let condition = block.renderer.dirty(dependencies);
if (should_cache) condition = x`${condition} && (${last} !== (${last} = ${value}))`;

block.chunks.update.push(b`
if (${condition}) {
@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${should_cache ? last : value});
}
`);
}
});
this.set_css_custom_properties(block, css_custom_properties_wrapper);
}
block.chunks.create.push(b`@create_component(${name}.$$.fragment);`);

if (parent_nodes && this.renderer.options.hydratable) {
let nodes = parent_nodes;
if (has_css_custom_properties) {
nodes = block.get_unique_name(`${css_custom_properties_wrapper.name}_nodes`);
block.chunks.claim.push(b`
${css_custom_properties_wrapper} = @claim_element(${parent_nodes}, "DIV", { style: true })
var ${nodes} = @children(${css_custom_properties_wrapper});
`);
}
block.chunks.claim.push(
b`@claim_component(${name}.$$.fragment, ${nodes});`
);
}
if (css_custom_properties_wrapper) this.create_css_custom_properties_wrapper_mount_chunk(block, parent_node, css_custom_properties_wrapper);
block.chunks.mount.push(b`@mount_component(${name}, ${mount_target}, ${mount_anchor});`);

if (has_css_custom_properties) {
if (parent_node) {
block.chunks.mount.push(b`@append(${parent_node}, ${css_custom_properties_wrapper})`);
if (is_head(parent_node)) {
block.chunks.destroy.push(b`@detach(${css_custom_properties_wrapper});`);
}
} else {
block.chunks.mount.push(b`@insert(#target, ${css_custom_properties_wrapper}, #anchor);`);
// TODO we eventually need to consider what happens to elements
// that belong to the same outgroup as an outroing element...
block.chunks.destroy.push(b`if (detaching) @detach(${css_custom_properties_wrapper});`);
}
block.chunks.mount.push(b`@mount_component(${name}, ${css_custom_properties_wrapper}, null);`);
} else {
block.chunks.mount.push(
b`@mount_component(${name}, ${parent_node || '#target'}, ${parent_node ? 'null' : '#anchor'});`
);
if (to_claim) {
if (css_custom_properties_wrapper) claim_nodes = this.create_css_custom_properties_wrapper_claim_chunk(block, claim_nodes, css_custom_properties_wrapper);
block.chunks.claim.push(b`@claim_component(${name}.$$.fragment, ${claim_nodes});`);
}

block.chunks.intro.push(b`
Expand All @@ -579,4 +546,64 @@ export default class InlineComponentWrapper extends Wrapper {
);
}
}

private create_css_custom_properties_wrapper_mount_chunk(
block: Block,
parent_node: Identifier,
css_custom_properties_wrapper: Identifier | null
) {
if (parent_node) {
block.chunks.mount.push(b`@append(${parent_node}, ${css_custom_properties_wrapper})`);
if (is_head(parent_node)) {
block.chunks.destroy.push(b`@detach(${css_custom_properties_wrapper});`);
}
} else {
block.chunks.mount.push(b`@insert(#target, ${css_custom_properties_wrapper}, #anchor);`);
// TODO we eventually need to consider what happens to elements
// that belong to the same outgroup as an outroing element...
block.chunks.destroy.push(b`if (detaching && ${this.var}) @detach(${css_custom_properties_wrapper});`);
}
}

private create_css_custom_properties_wrapper_claim_chunk(
block: Block,
parent_nodes: Identifier,
css_custom_properties_wrapper: Identifier | null
) {
const nodes = block.get_unique_name(`${css_custom_properties_wrapper.name}_nodes`);
block.chunks.claim.push(b`
${css_custom_properties_wrapper} = @claim_element(${parent_nodes}, "DIV", { style: true })
var ${nodes} = @children(${css_custom_properties_wrapper});
`);
return nodes;
}

private set_css_custom_properties(
block: Block,
css_custom_properties_wrapper: Identifier
) {
block.chunks.create.push(b`${css_custom_properties_wrapper} = @element("div");`);
block.chunks.hydrate.push(b`@set_style(${css_custom_properties_wrapper}, "display", "contents");`);
this.node.css_custom_properties.forEach((attr) => {
const dependencies = attr.get_dependencies();
const should_cache = attr.should_cache();
const last = should_cache && block.get_unique_name(`${attr.name.replace(/[^a-zA-Z_$]/g, '_')}_last`);
if (should_cache) block.add_variable(last);
const value = attr.get_value(block);
const init = should_cache ? x`${last} = ${value}` : value;

block.chunks.hydrate.push(
b`@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${init});`
);
if (dependencies.length > 0) {
let condition = block.renderer.dirty(dependencies);
if (should_cache) condition = x`${condition} && (${last} !== (${last} = ${value}))`;
block.chunks.update.push(b`
if (${condition}) {
@set_style(${css_custom_properties_wrapper}, "${attr.name}", ${should_cache ? last : value});
}
`);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script>
export let id;
</script>

<div {id}>
<p>Slider</p>
<span>Track</span>
</div>

<style>
p {
color: var(--rail-color);
}
span {
color: var(--track-color);
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export default {
props: {
railColor1: 'black',
trackColor1: 'red',
railColor2: 'green',
trackColor2: 'blue'
},
html: `
<div style="display: contents; --rail-color:black; --track-color:red;">
<div id="slider-1">
<p class="svelte-17ay6rc">Slider</p>
<span class="svelte-17ay6rc">Track</span>
</div>
</div>
<div style="display: contents; --rail-color:green; --track-color:blue;">
<div id="slider-2">
<p class="svelte-17ay6rc">Slider</p>
<span class="svelte-17ay6rc">Track</span>
</div>
</div>
`,
test({ component, assert, target }) {
component.railColor1 = 'yellow';
component.trackColor2 = 'orange';

assert.htmlEqual(target.innerHTML, `
<div style="display: contents; --rail-color:yellow; --track-color:red;">
<div id="slider-1">
<p class="svelte-17ay6rc">Slider</p>
<span class="svelte-17ay6rc">Track</span>
</div>
</div>
<div style="display: contents; --rail-color:green; --track-color:orange;">
<div id="slider-2">
<p class="svelte-17ay6rc">Slider</p>
<span class="svelte-17ay6rc">Track</span>
</div>
</div>
`);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script>
import Slider from './Slider.svelte';
export let railColor1;
export let railColor2;
export let trackColor1;
export let trackColor2;
function identity(color) {
return color;
}
</script>

<svelte:component
this={Slider}
id="slider-1"
--rail-color={railColor1}
--track-color={trackColor1}
/>

<svelte:component
this={Slider}
id="slider-2"
--rail-color={railColor2}
--track-color={identity(trackColor2)}
/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script>
export let id;
</script>

<div {id}>
<p>Slider1</p>
<span>Track</span>
</div>

<style>
p {
color: var(--rail-color);
}
span {
color: var(--track-color);
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script>
export let id;
</script>

<div {id}>
<p>Slider2</p>
<span>Track</span>
</div>

<style>
p {
color: var(--rail-color);
}
span {
color: var(--track-color);
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
export default {
props: {
componentName: 'Slider1'
},
html: `
<div style="display: contents; --rail-color:rgb(0, 0, 0); --track-color:rgb(255, 0, 0);">
<div id="component1">
<p class="svelte-17ay6rc">Slider1</p>
<span class="svelte-17ay6rc">Track</span>
</div>
</div>
<div style="display: contents; --rail-color:rgb(0, 255, 0); --track-color:rgb(0, 0, 255);">
<div id="component2">
<p class="svelte-17ay6rc">Slider1</p>
<span class="svelte-17ay6rc">Track</span>
</div>
</div>
`,
test({ target, window, assert, component }) {

function assert_slider_1() {
const railColor1 = target.querySelector('#component1 p');
const trackColor1 = target.querySelector('#component1 span');
const railColor2 = target.querySelector('#component2 p');
const trackColor2 = target.querySelector('#component2 span');

assert.equal(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
assert.equal(railColor1.textContent, 'Slider1');
assert.equal(railColor2.textContent, 'Slider1');
}

function assert_slider_2() {
const railColor1 = target.querySelector('#component1 p');
const trackColor1 = target.querySelector('#component1 span');
const railColor2 = target.querySelector('#component2 p');
const trackColor2 = target.querySelector('#component2 span');

assert.equal(window.getComputedStyle(railColor1).color, 'rgb(0, 0, 0)');
assert.equal(window.getComputedStyle(trackColor1).color, 'rgb(255, 0, 0)');
assert.equal(window.getComputedStyle(railColor2).color, 'rgb(0, 255, 0)');
assert.equal(window.getComputedStyle(trackColor2).color, 'rgb(0, 0, 255)');
assert.equal(railColor1.textContent, 'Slider2');
assert.equal(railColor2.textContent, 'Slider2');
}

assert_slider_1();
component.componentName = 'Slider2';
assert_slider_2();
component.componentName = undefined;
assert.equal(window.document.querySelector('div'), null);
component.componentName = 'Slider1';
assert_slider_1();
}
};

0 comments on commit e2538c5

Please sign in to comment.