diff --git a/src/compiler/compile/nodes/InlineComponent.ts b/src/compiler/compile/nodes/InlineComponent.ts index 9990a8302d..c78f434527 100644 --- a/src/compiler/compile/nodes/InlineComponent.ts +++ b/src/compiler/compile/nodes/InlineComponent.ts @@ -18,6 +18,7 @@ export default class InlineComponent extends Node { bindings: Binding[] = []; handlers: EventHandler[] = []; lets: Let[] = []; + css_custom_properties: Attribute[] = []; children: INode[]; scope: TemplateScope; @@ -46,6 +47,10 @@ export default class InlineComponent extends Node { }); case 'Attribute': + if (node.name.startsWith('--')) { + this.css_custom_properties.push(new Attribute(component, this, scope, node)); + break; + } // fallthrough case 'Spread': this.attributes.push(new Attribute(component, this, scope, node)); diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index c6299321f4..2ea149a848 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -18,6 +18,7 @@ import { extract_names } from 'periscopic'; import mark_each_block_bindings from '../shared/mark_each_block_bindings'; import { string_to_member_expression } from '../../../utils/string_to_member_expression'; import SlotTemplate from '../../../nodes/SlotTemplate'; +import { is_head } from '../shared/is_head'; type SlotDefinition = { block: Block; scope: TemplateScope; get_context?: Node; get_changes?: Node }; @@ -63,6 +64,10 @@ export default class InlineComponentWrapper extends Wrapper { } }); + this.node.css_custom_properties.forEach(attr => { + block.add_dependencies(attr.dependencies); + }); + this.var = { type: 'Identifier', name: ( @@ -146,6 +151,12 @@ export default class InlineComponentWrapper extends Wrapper { } } + const has_css_custom_properties = this.node.css_custom_properties.length > 0; + const css_custom_properties_wrapper = has_css_custom_properties ? block.get_unique_name('div') : null; + if (has_css_custom_properties) { + block.add_variable(css_custom_properties_wrapper); + } + const initial_props = this.slots.size > 0 ? [ p`$$slots: { @@ -491,17 +502,64 @@ export default class InlineComponentWrapper extends Wrapper { ${munged_handlers} `); + 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}); + } + `); + } + }); + } 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, ${parent_nodes});` + b`@claim_component(${name}.$$.fragment, ${nodes});` ); } - block.chunks.mount.push( - b`@mount_component(${name}, ${parent_node || '#target'}, ${parent_node ? 'null' : '#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'});` + ); + } block.chunks.intro.push(b` @transition_in(${name}.$$.fragment, #local); diff --git a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts index c4116e6591..7fb1067dba 100644 --- a/src/compiler/compile/render_ssr/handlers/InlineComponent.ts +++ b/src/compiler/compile/render_ssr/handlers/InlineComponent.ts @@ -1,4 +1,5 @@ import { string_literal } from '../../utils/stringify'; +import { get_attribute_value } from './shared/get_attribute_value'; import Renderer, { RenderOptions } from '../Renderer'; import InlineComponent from '../../nodes/InlineComponent'; import { p, x } from 'code-red'; @@ -86,5 +87,21 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend ${slot_fns} }`; + if (node.css_custom_properties.length > 0) { + renderer.add_string('
'); + } + renderer.add_expression(x`@validate_component(${expression}, "${node.name}").$$render($$result, ${props}, ${bindings}, ${slots})`); + + if (node.css_custom_properties.length > 0) { + renderer.add_string('
'); + } }