implement shadowdom option

pull/8457/head
Simon Holthausen 3 years ago
parent 96e97682e0
commit 840a7bed4b

@ -46,6 +46,7 @@ interface ComponentOptions {
accessors?: boolean;
preserveWhitespace?: boolean;
ceProps?: Record<string, { reflect?: boolean; type?: 'String' | 'Boolean' | 'Number' | 'Array' | 'Object', attribute?: string }>;
shadowdom?: 'open' | 'none';
}
const regex_leading_directory_separator = /^[/\\]/;
@ -1574,6 +1575,17 @@ function process_component_options(component: Component, nodes) {
break;
}
case 'shadowdom': {
const shadowdom = get_value(attribute, compiler_errors.invalid_shadowdom_attribute);
if (shadowdom !== 'open' && shadowdom !== 'none') {
return component.error(attribute, compiler_errors.invalid_shadowdom_attribute);
}
component_options.shadowdom = shadowdom;
break;
}
case 'ceProps': {
const error = () => component.error(attribute, compiler_errors.invalid_ceProps_attribute);
const { value } = attribute;

@ -206,6 +206,10 @@ export default {
code: 'invalid-tag-attribute',
message: "'tag' must be a string literal"
},
invalid_shadowdom_attribute: {
code: 'invalid-shadowdom-attribute',
message: "'shadowdom' must be either 'open' or 'none'"
},
invalid_ceProps_attribute: {
code: 'invalid-ceProps-attribute',
message: "'ceProps' must be a statically analyzable object literal of the form " +

@ -555,9 +555,10 @@ export default function dom(
.filter(accessor => !writable_props.some(prop => prop.export_name === accessor.key.name))
.map(accessor => `"${accessor.key.name}"`)
.join(',');
const use_shadow_dom = component.component_options.shadowdom !== 'none' ? 'true' : 'false';
body.push(
b`@_customElements.define("${component.tag}", @create_custom_element(${name}, ${JSON.stringify(props_str)}, [${slots_str}], [${accessors_str}]));`
b`@_customElements.define("${component.tag}", @create_custom_element(${name}, ${JSON.stringify(props_str)}, [${slots_str}], [${accessors_str}], ${use_shadow_dom}));`
);
}

@ -157,10 +157,13 @@ if (typeof HTMLElement === 'function') {
constructor(
private $$componentCtor: ComponentType,
private $$slots: string[]
private $$slots: string[],
use_shadow_dom: boolean
) {
super();
this.attachShadow({ mode: 'open' });
if (use_shadow_dom) {
this.attachShadow({ mode: 'open' });
}
}
addEventListener(type: string, listener: any, options?: any): void {
@ -237,7 +240,7 @@ if (typeof HTMLElement === 'function') {
}
this.$$component = new this.$$componentCtor({
target: this.shadowRoot!,
target: this.shadowRoot || this,
props: {
...this.$$data,
$$slots,
@ -330,17 +333,19 @@ interface CustomElementPropDefinition {
* @param props_definition The props to observe
* @param slots The slots to create
* @param accessors Other accessors besides the ones for props the component has
* @param use_shadow_dom Whether to use shadow DOM
* @returns A custom element class
*/
export function create_custom_element(
Component: ComponentType,
props_definition: Record<string, CustomElementPropDefinition>,
slots: string[],
accessors: string[]
accessors: string[],
use_shadow_dom: boolean
) {
const Class = class extends SvelteElement {
constructor() {
super(Component, slots);
super(Component, slots, use_shadow_dom);
this.$$props_definition = props_definition;
}

@ -0,0 +1,13 @@
<svelte:options tag="custom-element" shadowdom="none" />
<script>
export let name;
</script>
<h1>Hello {name}!</h1>
<style>
h1 {
color: red;
}
</style>

@ -0,0 +1,16 @@
import * as assert from 'assert';
import { tick } from 'svelte';
import './main.svelte';
export default async function (target) {
target.innerHTML = '<custom-element name="world"></custom-element>';
await tick();
const el = target.querySelector('custom-element');
const h1 = el.querySelector('h1');
assert.equal(el.name, 'world');
assert.equal(el.shadowRoot, null);
assert.equal(h1.innerHTML, 'Hello world!');
assert.equal(getComputedStyle(h1).color, 'rgb(255, 0, 0)');
}

@ -4,7 +4,7 @@ import CustomElement from './main.svelte';
import { create_custom_element } from 'svelte/internal';
export default async function (target) {
customElements.define('no-tag', create_custom_element(CustomElement, {name: {}}, [], []));
customElements.define('no-tag', create_custom_element(CustomElement, {name: {}}, [], [], true));
target.innerHTML = '<no-tag name="world"></no-tag>';
await tick();

@ -4,7 +4,7 @@ import CustomElement from './main.svelte';
import { create_custom_element } from 'svelte/internal';
export default async function (target) {
customElements.define('no-tag', create_custom_element(CustomElement, { name: {}}, [], []));
customElements.define('no-tag', create_custom_element(CustomElement, { name: {}}, [], [], true));
target.innerHTML = '<no-tag name="world"></no-tag>';
await tick();

@ -4,7 +4,7 @@ import CustomElement from './main.svelte';
import { create_custom_element } from 'svelte/internal';
export default async function (target) {
customElements.define('no-tag', create_custom_element(CustomElement, { name: {} }, [], []));
customElements.define('no-tag', create_custom_element(CustomElement, { name: {} }, [], [], true));
target.innerHTML = '<no-tag name="world"></no-tag>';
await tick();

@ -44,5 +44,5 @@ class Component extends SvelteComponent {
}
}
customElements.define("custom-element", create_custom_element(Component, {}, [], []));
customElements.define("custom-element", create_custom_element(Component, {}, [], [], true));
export default Component;
Loading…
Cancel
Save