Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] support --style-props for <svelte:component> #7468

Merged
merged 10 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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});`);
}
tanhauhau marked this conversation as resolved.
Show resolved Hide resolved
}

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>
`);
}
};
tanhauhau marked this conversation as resolved.
Show resolved Hide resolved
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();
}
};