UNPKG

@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

131 lines 5.81 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.KSLCustomElement = void 0; const errors_1 = require("../../utils/errors"); // Custom elements API as well as all Web Component definitions can only be run in a browser environment, // because they require build-in browser APIs, which are not available on the backend (during SSR). // There are several libraries that allow server-side rendering of Web Components. It is usually done by // redefining the whole DOM API on the backend. For the purpose of this SDK it is not required to // support server-side rendering of Web Components, because whole page must be loaded for SDK to work properly. // However, some SSR frameworks (such as Next.js) will try to run this code on the server side first during SSR, // which could result in a crash because HTMLElement is not defined on the backend. That is why we are setting // this property to null in a global Nodejs scope. const isNotInBrowser = typeof window === 'undefined'; if (isNotInBrowser) { // eslint-disable-next-line // @ts-ignore: required to support SSR frameworks global['HTMLElement'] = null; } /** * This is a base class for all kontent-smart-link custom elements. */ class KSLCustomElement extends HTMLElement { static _template = null; /** * Name of the custom element that will be added to the CustomElementRegistry. * This name will be used to add a custom element to the page. * * @type {string} */ static get is() { throw (0, errors_1.NotImplementedError)('KSLCustomElement: "is" getter is not implemented for this custom element.'); } /** * Template is usually stored in the scope of custom element file, but this does not work with SSR, * since `document` is not available on the backend. That is why we are storing the template in a static * constructor property and initialize it when we are sure we are in a browser environment. * * @type {HTMLTemplateElement} */ static get template() { if (!this._template) { this._template = this.initializeTemplate(); } return this._template; } constructor() { super(); // We are using an "open" mode here, so that it is easier for users to manipulate the shadow root // of our custom elements if they want to change or customize something. const shadowRootConfig = { mode: 'open' }; const selfClass = Object.getPrototypeOf(this).constructor; const shadowRoot = this.attachShadow(shadowRootConfig); shadowRoot.appendChild(selfClass.template.content.cloneNode(true)); } /** * Add this custom element to the CustomElementRegistry so that it can be used on the page. * Usually customElements.define is called inline right at the bottom of the custom element file, * but this would not work with SSR, since custom elements can't be defined on the backend. */ static define() { if (typeof window === 'undefined') { throw (0, errors_1.InvalidEnvironmentError)('KSLCustomElement: custom elements can only be defined in a browser environment.'); } if (!window.customElements) { throw (0, errors_1.InvalidEnvironmentError)('KSLCustomElement: custom elements API is not available.'); } // The 'this' keyword refers to current constructor in static methods. // Conversion to unknown is needed so that we can explicitly converse // this class to CustomElementConstructor. const self = this; const constructor = window.customElements.get(this.is); if (!constructor) { customElements.define(this.is, self); return customElements.whenDefined(this.is); } return Promise.resolve(constructor); } /** * Initialize a template for the custom element. * Each KSL custom element class should implement this static method. * * @returns {HTMLTemplateElement} */ static initializeTemplate() { throw (0, errors_1.NotImplementedError)('KSLCustomElement: "initializeTemplate" method should be implemented for every component.'); } /** * Update attribute value on the custom element. * * @param {string} attributeName * @param {string | number | boolean | null} attributeValue */ updateAttribute(attributeName, attributeValue) { if (attributeValue) { this.setAttribute(attributeName, attributeValue.toString()); } else { this.removeAttribute(attributeName); } } /** * Dispatch an asynchronous event from component. Dispatching this event returns Promise * which resolves if event was successful and rejects if events is not successful. * * @param {string} eventType * @param {TEventData} eventData * @param {number | null} timeout * @protected */ dispatchAsyncEvent(eventType, eventData, timeout = null) { return new Promise((resolve, reject) => { const timeoutId = timeout ? window.setTimeout(reject, timeout) : 0; const customEvent = new CustomEvent(eventType, { detail: { eventData: eventData, onResolve: (data) => { window.clearTimeout(timeoutId); resolve(data); }, onReject: (reason) => { window.clearTimeout(timeoutId); reject(reason); }, }, }); this.dispatchEvent(customEvent); }); } } exports.KSLCustomElement = KSLCustomElement; //# sourceMappingURL=KSLCustomElement.js.map