Skip to content
Snippets Groups Projects
Commit 4a7340f8 authored by Reiter, Christoph's avatar Reiter, Christoph :snake:
Browse files

Merge branch 'template-import-async-slots' into 'master'

template slots: handle slots being added after the first render

See merge request !106
parents 81eef212 92b69c65
No related branches found
No related tags found
1 merge request!106template slots: handle slots being added after the first render
Pipeline #67945 passed
...@@ -4,12 +4,12 @@ export default class DBPLitElement extends AdapterLitElement { ...@@ -4,12 +4,12 @@ export default class DBPLitElement extends AdapterLitElement {
constructor() { constructor() {
super(); super();
this.htmlOverrides = ''; this.htmlOverrides = '';
this._rootContainer = null; this._localTemplateSlotsImported = false;
this._globalSlotsContainer = null;
this._globalTemplateSlotsImported = false;
this._renderDone = false;
} }
/**
* See: https://lit-element.polymer-project.org/guide/properties#initialize
*/
static get properties() { static get properties() {
return { return {
...super.properties, ...super.properties,
...@@ -18,20 +18,11 @@ export default class DBPLitElement extends AdapterLitElement { ...@@ -18,20 +18,11 @@ export default class DBPLitElement extends AdapterLitElement {
} }
connectedCallback() {
this.updateComplete.then(() => {
// transform all global override templates or named template slots in the light DOM to named div slots
this.transformSlots();
});
super.connectedCallback();
}
disconnectedCallback() { disconnectedCallback() {
super.disconnectedCallback(); super.disconnectedCallback();
if (this._rootContainer !== null) { if (this._globalSlotsContainer !== null) {
this._rootContainer.remove(); this._globalSlotsContainer.remove();
} }
} }
...@@ -39,91 +30,106 @@ export default class DBPLitElement extends AdapterLitElement { ...@@ -39,91 +30,106 @@ export default class DBPLitElement extends AdapterLitElement {
return this.shadowRoot === null ? this.querySelector(selector) : this.shadowRoot.querySelector(selector); return this.shadowRoot === null ? this.querySelector(selector) : this.shadowRoot.querySelector(selector);
} }
getTagName() { firstUpdated() {
const tagName = this.dataset ? (this.dataset.tagName || '') : ''; super.firstUpdated();
this._renderDone = true;
this._importTemplateSlots();
}
return tagName !== '' ? tagName : this.tagName.toLowerCase(); update(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
switch (propName) {
case "html-overrides":
this._importTemplateSlots();
break;
}
});
super.update(changedProperties);
} }
/** /**
* Transforms all global override templates or named template slots in the light DOM to named div slots * Transforms all global override templates or named template slots in the light DOM to named div slots
* on the first render.
*
* Global overrides will replace all existing slotted elements with the same slot name.
*/ */
transformSlots() { _importTemplateSlots() {
// query all named slots of the component if (!this._renderDone) {
const slots = this.shadowRoot.querySelectorAll("slot[name]");
// if there are no slots we can exit
if (slots.length === 0) {
return; return;
} }
this._importLocalTemplateSlots();
this._importGlobalTemplateSlots();
}
slots.forEach((slot) => { _importLocalTemplateSlots() {
const slotName = slot.name; if(this._localTemplateSlotsImported ) {
return;
// search if there is a template with the name of the slot in the light DOM of the component }
const templateElem = this.querySelector("template[slot=" + slotName + "]");
if (!templateElem) {
return;
}
// Now extract slots from templates contained in the light dom
let lightTemplateSlots = this.querySelectorAll(':scope > template[slot]:not([slot=""]');
for(let templateElem of lightTemplateSlots) {
// create a slot div container to put in the cloned template content // create a slot div container to put in the cloned template content
const divElem = document.createElement('div'); const divElem = document.createElement('div');
divElem.slot = slotName; divElem.slot = templateElem.getAttribute('slot');
divElem.appendChild(templateElem.content.cloneNode(true)); divElem.appendChild(templateElem.content.cloneNode(true));
// remove the old template // remove the old template
templateElem.remove(); templateElem.remove();
// put the slot div container with the cloned template in the light DOM // put the slot div container with the cloned template in the light DOM
this.appendChild(divElem); this.appendChild(divElem);
}); }
this._localTemplateSlotsImported = true;
}
_importGlobalTemplateSlots() {
if(this.htmlOverrides === '' || this._globalTemplateSlotsImported) {
return;
}
// check if we have an "html-override" attribute set so we need to check for the global override template // First add global override templates as light dom slots
if (this.htmlOverrides !== '') { let globalOverrideTemplateElem = document.querySelector('template#' + this.htmlOverrides);
const globalOverrideTemplateElem = document.querySelector('template#' + this.htmlOverrides); if (globalOverrideTemplateElem !== null) {
// we need to clone the element so we can access the content
if (globalOverrideTemplateElem) { const overrideTemplateElemClone = globalOverrideTemplateElem.content.cloneNode(true);
// we need to clone the element so we can access the content const templateOverrideElem = overrideTemplateElemClone.querySelector('template#' + this.tagName.toLowerCase());
const overrideTemplateElemClone = globalOverrideTemplateElem.content.cloneNode(true); if (templateOverrideElem !== null) {
const tagName = this.getTagName(); const templateOverrideElemClone = templateOverrideElem.content.cloneNode(true);
// then we will look if there is an override for the current tag // Find all slots which are direct children (somehow :scope doesn't work here so check parentNode)
const templateOverrideElem = overrideTemplateElemClone.querySelector('template#' + tagName); let globalTemplateSlots = [];
for(let e of templateOverrideElemClone.querySelectorAll('[slot]:not([slot=""]')) {
if (templateOverrideElem) { if (e.parentNode === templateOverrideElemClone) {
// if there is an override we again need to clone that template so we can access the content globalTemplateSlots.push(e);
const templateOverrideElemClone = templateOverrideElem.content.cloneNode(true);
// Create a dummy node and add it to the the same shadow root the templates are from
// By adding it into the template we have the nice side effect that it is not visible
let container = document.createElement("div");
globalOverrideTemplateElem.append(container);
container.appendChild(templateOverrideElemClone);
this._rootContainer = container;
// now we need to look for slots in the override
slots.forEach((slot) => {
const slotName = slot.name;
// if a slot is found we need to remove the current slot in the light DOM
// so we are not showing the old and new content at the same time
if (templateOverrideElemClone.querySelector('[slot="' + slotName + '"]')) {
const currentSlotElement = this.querySelector('[slot="' + slotName + '"]');
if (currentSlotElement) {
currentSlotElement.remove();
}
}
});
// Now move the slots into the light dom of the target.
// The parent node in the other shadow root has to stay around for this to work
while (container.childNodes.length) {
this.appendChild(container.removeChild(container.childNodes[0]));
} }
} }
// Global overrides will replace local ones.
// Either normal slotted elements or the ones we create from templates.
for(let slotElem of globalTemplateSlots) {
for (let elm of this.querySelectorAll('[slot="' + slotElem.slot + '"]')) {
elm.remove();
}
}
// Create a dummy node and add it to the the same shadow root the templates are from
// By adding it into the template we have the nice side effect that it is not visible
let container = document.createElement("div");
globalOverrideTemplateElem.append(container);
this._globalSlotsContainer = container;
for(let slotElem of globalTemplateSlots) {
container.appendChild(slotElem);
}
// Now move the slots into the light dom of the target.
// The parent node in the other shadow root has to stay around for this to work
while (container.childNodes.length) {
this.appendChild(container.removeChild(container.childNodes[0]));
}
} }
} }
this._globalTemplateSlotsImported = true;
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment