@kontent-ai/smart-link
Version:
Kontent.ai Smart Link SDK allowing to automatically inject [smart links](https://docs.kontent.ai/tutorials/develop-apps/build-strong-foundation/set-up-editing-from-preview#a-using-smart-links) to Kontent.ai according to manually specified [HTML data attri
118 lines • 5.66 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SmartLinkRenderer = void 0;
const domElement_1 = require("../utils/domElement");
const KSLContainerElement_1 = require("../web-components/KSLContainerElement");
const customElements_1 = require("../utils/customElements");
class SmartLinkRenderer {
configuration;
defaultContainer;
containerByRenderingRoot;
highlightByElement;
addButtonByElement;
constructor(configuration) {
this.configuration = configuration;
this.containerByRenderingRoot = new Map();
this.highlightByElement = new Map();
this.addButtonByElement = new Map();
this.defaultContainer = createAndMountDefaultContainer();
}
render = (visibleElements, observedElements) => {
if (observedElements.size === 0) {
this.clear();
return;
}
const { newAddButtonByElement, newHighlightByElement } = this.processVisibleElements(visibleElements, this.addButtonByElement, this.highlightByElement);
// Remove highlights that are not observed anymore as they might be removed from the DOM.
for (const [element, highlight] of newHighlightByElement) {
if (!observedElements.has(element)) {
highlight.remove();
newHighlightByElement.delete(element);
}
}
// Remove add buttons that are not observed anymore as they might be removed from the DOM.
for (const [element, addButton] of newAddButtonByElement) {
if (!observedElements.has(element)) {
addButton.remove();
newAddButtonByElement.delete(element);
}
}
// All containers that have no children can be removed because they are not used by any highlight, or a add button.
for (const [parent, container] of this.containerByRenderingRoot.entries()) {
if (container.children.length === 0) {
container.remove();
this.containerByRenderingRoot.delete(parent);
}
}
this.highlightByElement = newHighlightByElement;
this.addButtonByElement = newAddButtonByElement;
};
destroy = () => {
this.clear();
this.defaultContainer.remove();
};
clear = () => {
for (const [, addButton] of this.addButtonByElement.entries()) {
addButton.remove();
}
for (const [, highlight] of this.highlightByElement.entries()) {
highlight.remove();
}
for (const [, container] of this.containerByRenderingRoot.entries()) {
container.remove();
}
this.highlightByElement = new Map();
this.containerByRenderingRoot = new Map();
this.addButtonByElement = new Map();
this.defaultContainer.innerHTML = '';
};
processVisibleElements = (visibleElements, addButtonByElement, highlightByElement) => {
// Group elements by their rendering roots to avoid unnecessary re-calculations (e.g. reposition container only once
// instead of repositioning it for every child, calculating bounding client rects, etc.).
const elementsByRenderingRoot = (0, domElement_1.groupElementsByRenderingRoot)(visibleElements);
return Array.from(elementsByRenderingRoot.entries()).reduce((acc, [root, elements]) => {
const container = this.createContainerIfNotExist(root);
container.adjustPosition();
for (const element of elements) {
// This check is needed to prevent highlight rendering for the "flat" elements (height or/and width === 0),
// because those elements are basically invisible and cannot be clicked.
const isFlat = element.offsetHeight === 0 || element.offsetWidth === 0;
if (!isFlat && (0, customElements_1.shouldElementHaveHighlight)(element, this.configuration)) {
const highlight = acc.newHighlightByElement.get(element) ?? container.createHighlightForElement(element);
highlight.adjustPosition();
acc.newHighlightByElement.set(element, highlight);
}
if ((0, customElements_1.shouldElementHaveAddButton)(element, this.configuration)) {
const addButton = acc.newAddButtonByElement.get(element) ?? container.createAddButtonForElement(element);
addButton.adjustPosition();
acc.newAddButtonByElement.set(element, addButton);
}
}
return acc;
}, {
newAddButtonByElement: new Map(addButtonByElement),
newHighlightByElement: new Map(highlightByElement),
});
};
createContainerIfNotExist = (root) => {
// if root is not specified or root is body
if (!root || root === document.body) {
return this.defaultContainer;
}
const container = this.containerByRenderingRoot.get(root);
if (container) {
return container;
}
const newContainer = document.createElement(KSLContainerElement_1.KSLContainerElement.is);
root.appendChild(newContainer);
this.containerByRenderingRoot.set(root, newContainer);
return newContainer;
};
}
exports.SmartLinkRenderer = SmartLinkRenderer;
const createAndMountDefaultContainer = () => {
const container = document.createElement(KSLContainerElement_1.KSLContainerElement.is);
window.document.body.appendChild(container);
return container;
};
//# sourceMappingURL=SmartLinkRenderer.js.map