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

template slots: handle slots being added after the first render

Instead of querying the slots in the shadow DOM after the first render
just add all available slots to the light DOM.

This fixes missing filled slots when slots where added after the first render,
for example if they were used only after some property is set.
parent 81eef212
No related branches found
No related tags found
No related merge requests found
Pipeline #67795 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,97 @@ export default class DBPLitElement extends AdapterLitElement { ...@@ -39,91 +30,97 @@ 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();
}
update(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
switch (propName) {
case "html-overrides":
this._importTemplateSlots();
break;
}
});
return tagName !== '' ? tagName : this.tagName.toLowerCase(); 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
*/ */
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]));
} }
} }
// 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 slot of globalTemplateSlots) {
container.appendChild(slot);
}
// 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.
Finish editing this message first!
Please register or to comment