function newExtensions(services) {
return {
newExtension,
newExtensionPoint,
newExtensionsBinder
};
function newExtension(key, value, invalidation) {
const provider = services.newProvider(key);
invalidation && invalidation.then(provider.close);
value !== undefined && provider(value);
return provider;
}
function newExtensionPoint({
element,
key,
renderAll,
render,
invalidation
}) {
if (!element) element = "div";
if (typeof element === "string") element = document.createElement(element);
if (!renderAll) {
renderAll = ({ key, extensions, element }) => {
element.innerHTML = "";
extensions.forEach((extension, index) => {
if (!extension) return;
const options = {
key,
parent: element,
extension,
extensions,
index
};
let child;
if (typeof render === "function") {
child = render(options);
} else if (typeof extension === "function") {
child = extension(options);
} else if (extension && typeof extension === "object") {
if (typeof extension.render === "function") {
child = extension.render(options);
} else {
child = extension;
}
}
if (child instanceof Node) element.appendChild(child);
});
};
}
const consumer = services.newConsumer(key, (extensions) => {
renderAll({
key,
element,
extensions
});
});
invalidation && invalidation.then(consumer.close);
return element;
}
function newExtensionsBinder(
types = {},
newDefaultExtensionPoint = newExtensionPoint
) {
return function bindExtensions(element, invalidation) {
if (typeof element === "function") element = element();
const selectAll = (selector) => [
...[element].filter((el) => el.matches(selector)),
...element.querySelectorAll(selector)
];
const extensionPoints = selectAll("[data-extension-point]");
for (const elm of extensionPoints) {
const key = elm.dataset.extensionPoint;
const type = elm.dataset.extensionType || "default";
const newExtPoint = types[type] || newDefaultExtensionPoint;
if (!newExtPoint) {
throw new Error(
`No renderings found for this extension point. Extension type: "${type}". Extension point key: "${key}".`
);
}
newExtPoint({ key, element: elm, invalidation });
}
const extensionsElm = selectAll("[data-extension]");
for (const elm of extensionsElm) {
const key = elm.dataset.extension;
let extension = elm.render || elm.onrender;
if (!extension) {
const template = elm; // elm.firstChild;
extension = template
? () => {
const clone = template.cloneNode(true);
clone.removeAttribute("data-extension");
return clone;
}
: () => {};
}
elm.parentElement && elm.parentElement.removeChild(elm);
extension && newExtension(key, extension, invalidation);
}
return element;
};
}
}