UNPKG

wj-elements

Version:

WebJET Elements is a modern set of user interface tools harnessing the power of web components designed to simplify web application development.

576 lines (575 loc) 21.1 kB
var __defProp = Object.defineProperty; var __typeError = (msg) => { throw TypeError(msg); }; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); var _drawingStatus, _isAttached, _isRendering, _originalVisibility, _pristine, _WJElement_instances, enqueueUpdate_fn, refresh_fn, resolveRender_fn; import { UniversalService } from "./universal-service.js"; import { Permissions } from "./permissions.js"; import { WjElementUtils } from "./element-utils.js"; import { event } from "./event.js"; import { store, defaultStoreActions } from "./wje-store.js"; const template = document.createElement("template"); template.innerHTML = ``; const _WJElement = class _WJElement extends HTMLElement { /** * Initializes a new instance of the WJElement class. */ constructor() { super(); __privateAdd(this, _WJElement_instances); __privateAdd(this, _drawingStatus); __privateAdd(this, _isAttached); __privateAdd(this, _isRendering); __privateAdd(this, _originalVisibility); __privateAdd(this, _pristine); /** * Initializes the component, setting up attributes and rendering. * @param [force] Whether to force initialization. * @returns A promise that resolves when initialization is complete. */ __publicField(this, "initWjElement", (force = false) => { return new Promise(async (resolve, reject) => { var _a; __privateSet(this, _drawingStatus, this.drawingStatuses.BEGINING); (_a = this.setupAttributes) == null ? void 0 : _a.call(this); if (this.hasShadowRoot) { if (!this.shadowRoot) this.attachShadow({ mode: this.shadowType || "open" }); } this.setUpAccessors(); __privateSet(this, _drawingStatus, this.drawingStatuses.START); await this.display(force); const sheet = new CSSStyleSheet(); sheet.replaceSync(this.constructor.cssStyleSheet); this.context.adoptedStyleSheets = [sheet]; resolve(); }); }); __privateSet(this, _isAttached, false); this.service = new UniversalService({ store }); this.defineDependencies(); __privateSet(this, _isRendering, false); this._dependencies = {}; this.drawingStatuses = { CREATED: 0, ATTACHED: 1, BEGINING: 2, START: 3, DRAWING: 4, DONE: 5, DISCONNECTED: 6 }; __privateSet(this, _drawingStatus, this.drawingStatuses.CREATED); __privateSet(this, _pristine, true); __privateSet(this, _isRendering, false); this.rafId = null; __privateSet(this, _originalVisibility, null); this.params = {}; this.updateComplete = new Promise((resolve, reject) => { this.finisPromise = resolve; this.rejectPromise = reject; }); } get drawingStatus() { return __privateGet(this, _drawingStatus); } /** * Sets the value of the 'permission' attribute. * @param {string[]} value The value to set for the 'permission' attribute. */ set permission(value) { this.setAttribute("permission", value.join(",")); } /** * Gets the value of the 'permission-check' attribute. * @returns {string[]} The value of the 'permission' attribute. */ get permission() { var _a; return ((_a = this.getAttribute("permission")) == null ? void 0 : _a.split(",")) || []; } /** * Sets the 'permission-check' attribute. * @param {boolean} value The value to set for the 'permission-check' attribute. */ set isPermissionCheck(value) { if (value) this.setAttribute("permission-check", ""); else this.removeAttribute("permission-check"); } /** * Checks if the 'permission-check' attribute is present. * @returns {boolean} True if the 'permission-check' attribute is present. */ get isPermissionCheck() { return this.hasAttribute("permission-check"); } set noShow(value) { if (value) this.setAttribute("no-show", ""); else this.removeAttribute("no-show"); } /** * Checks if the 'show' attribute is present. * @returns {boolean} True if the 'show' attribute is present. */ get noShow() { return this.hasAttribute("no-show"); } /** * Sets the 'shadow' attribute. * @param {string} value The value to set for the 'shadow' attribute. */ set isShadowRoot(value) { return this.setAttribute("shadow", value); } get isShadowRoot() { return this.getAttribute("shadow"); } /** * Checks if the 'shadow' attribute is present. * @returns {boolean} True if the 'shadow' attribute is present. */ get hasShadowRoot() { return this.hasAttribute("shadow"); } /** * Gets the value of the 'shadow' attribute or 'open' if not set. * @returns {string} The value of the 'shadow' attribute or 'open'. */ get shadowType() { return this.getAttribute("shadow") || "open"; } /** * Gets the rendering context, either the shadow root or the component itself. * @returns The rendering context. */ get context() { if (this.hasShadowRoot) { return this.shadowRoot; } else { return this; } } /** * Gets the store instance. * @returns {object} The store instance. */ get store() { return store; } /** * @typedef {object} ArrayActions * @property {Function} addAction - Adds an item to the array. * @property {Function} deleteAction - Deletes an item from the array. * @property {Function} loadAction - Loads an array. * @property {Function} updateAction - Updates an item in the array. * @property {Function} addManyAction - Adds many items to the array. */ /** * @typedef {object} ObjectActions * @property {Function} addAction - Replace old object with new object * @property {Function} deleteAction - Delete item based on key * @property {Function} updateAction - Update item based on key */ /** * Gets the default store actions. * @returns The default store actions for arrays and objects. */ get defaultStoreActions() { return defaultStoreActions; } /** * Gets the classes to be removed after the component is connected. * @returns An array of class names to remove. */ get removeClassAfterConnect() { var _a; return (_a = this.getAttribute("remove-class-after-connect")) == null ? void 0 : _a.split(" "); } /** * Sets the component dependencies. * @param value The dependencies to set. */ set dependencies(value) { this._dependencies = value; } /** * Gets the component dependencies. * @returns The component dependencies. */ get dependencies() { return this._dependencies; } /** * Defines a custom element if not already defined. * @param name The name of the custom element. * @param [elementConstructor] The constructor for the custom element. * @param [options] Additional options for defining the element. */ static define(name, elementConstructor = this, options = {}) { const definedElement = customElements.get(name); if (!definedElement) { customElements.define(name, elementConstructor, options); } } /** * Defines component dependencies by registering custom elements. */ defineDependencies() { if (this.dependencies) Object.entries(this.dependencies).forEach((name, component) => _WJElement.define(name, component)); } /** * Hook for extending behavior before drawing the component. * @param context The rendering context, usually the element's shadow root or main DOM element. * @param appStoreObj The global application store for managing state. * @param params Additional parameters or attributes for rendering the component. */ beforeDraw(context, appStoreObj, params) { } /** * Renders the component within the provided context. * @param context The rendering context, usually the element's shadow root or main DOM element. * @param appStoreObj * @param params Additional parameters or attributes for rendering the component. * @returns This implementation does not render anything and returns `null`. * @description * The `draw` method is responsible for rendering the component's content. * Override this method in subclasses to define custom rendering logic. * @example * class MyComponent extends WJElement { * draw(context, appStoreObj, params) { * const div = document.createElement('div'); * div.textContent = 'Hello, world!'; * context.appendChild(div); * } * } */ draw(context, appStoreObj, params) { return null; } /** * Hook for extending behavior after drawing the component. * @param context The rendering context, usually the element's shadow root or main DOM element. * @param appStoreObj The global application store for managing state. * @param params Additional parameters or attributes for rendering the component. */ afterDraw(context, appStoreObj, params) { } /** * Lifecycle method invoked when the component is connected to the DOM. */ connectedCallback() { var _a; if (!__privateGet(this, _isRendering)) { __privateSet(this, _originalVisibility, __privateGet(this, _originalVisibility) ?? this.style.visibility); this.style.visibility = "hidden"; (_a = this.setupAttributes) == null ? void 0 : _a.call(this); this.setUpAccessors(); __privateSet(this, _drawingStatus, this.drawingStatuses.ATTACHED); __privateSet(this, _pristine, false); __privateMethod(this, _WJElement_instances, enqueueUpdate_fn).call(this); } } /** * Sets up attributes and event listeners for the component. * This method retrieves all custom events defined for the component * and adds event listeners for each of them. When an event is triggered, * it calls the corresponding method on the host element. */ setupAttributes() { let allEvents = WjElementUtils.getEvents(this); allEvents.forEach((customEvent, domEvent) => { this.addEventListener(domEvent, (e) => { var _a, _b; (_b = (_a = this.getRootNode().host)[customEvent]) == null ? void 0 : _b.call(_a); }); }); } /** * Hook for extending behavior before disconnecting the component. */ beforeDisconnect() { } /** * Hook for extending behavior after disconnecting the component. */ afterDisconnect() { } /** * Hook for extending behavior before redrawing the component. */ beforeRedraw() { } /** * Cleans up resources and event listeners for the component. */ componentCleanup() { } /** * Lifecycle method invoked when the component is disconnected from the DOM. */ disconnectedCallback() { var _a, _b; if (__privateGet(this, _isAttached)) { (_a = this.beforeDisconnect) == null ? void 0 : _a.call(this); this.context.innerHTML = ""; (_b = this.afterDisconnect) == null ? void 0 : _b.call(this); __privateSet(this, _isAttached, false); this.style.visibility = __privateGet(this, _originalVisibility); __privateSet(this, _originalVisibility, null); } if (__privateGet(this, _isRendering)) { this.stopRenderLoop(); } __privateSet(this, _drawingStatus, this.drawingStatuses.DISCONNECTED); this.componentCleanup(); } /** * Lifecycle method invoked when an observed attribute changes. * @param name The name of the attribute that changed. * @param old The old value of the attribute. * @param newName The new value of the attribute. */ attributeChangedCallback(name, old, newName) { if (old !== newName) { __privateSet(this, _pristine, false); __privateMethod(this, _WJElement_instances, enqueueUpdate_fn).call(this); } } refresh() { this.updateComplete = new Promise((resolve, reject) => { this.finisPromise = resolve; this.rejectPromise = reject; }); __privateSet(this, _pristine, false); __privateMethod(this, _WJElement_instances, enqueueUpdate_fn).call(this); } stopRenderLoop() { if (this.rafId) { cancelAnimationFrame(this.rafId); this.rafId = null; } } /** * Renders the component within the provided context. * @param context The rendering context, usually the element's shadow root or main DOM element. * @param appStore The global application store for managing state. * @param params Additional parameters or attributes for rendering the component. * @returns This implementation does not render anything and returns `null`. * @description * The `draw` method is responsible for rendering the component's content. * Override this method in subclasses to define custom rendering logic. * @example * class MyComponent extends WJElement { * draw(context, appStore, params) { * const div = document.createElement('div'); * div.textContent = 'Hello, world!'; * context.appendChild(div); * } * } */ draw(context, appStore, params) { return null; } /** * Displays the component's content, optionally forcing a re-render. * @param [force] Whether to force a re-render. * @returns A promise that resolves when the display is complete. */ display(force = false) { this.template = this.constructor.customTemplate || document.createElement("template"); if (force) { [...this.context.childNodes].forEach(this.context.removeChild.bind(this.context)); } this.context.append(this.template.content.cloneNode(true)); if (this.noShow || this.isPermissionCheck && !Permissions.isPermissionFulfilled(this.permission)) { this.remove(); return Promise.resolve(); } return __privateMethod(this, _WJElement_instances, resolveRender_fn).call(this); } /** * Renders the component's content. */ async render() { __privateSet(this, _drawingStatus, this.drawingStatuses.DRAWING); let _draw = this.draw(this.context, this.store, WjElementUtils.getAttributes(this)); if (_draw instanceof Promise || (_draw == null ? void 0 : _draw.constructor.name) === "Promise") { _draw = await _draw; } let rend = _draw; let element; if (rend instanceof HTMLElement || rend instanceof DocumentFragment) { element = rend; } else { let inputTemplate = document.createElement("template"); inputTemplate.innerHTML = rend; element = inputTemplate.content.cloneNode(true); } let rendered = element; this.context.appendChild(rendered); } /** * Sanitizes a given name by converting it from kebab-case to camelCase. * @param {string} name The name in kebab-case format (e.g., "example-name"). * @returns {string} The sanitized name in camelCase format (e.g., "exampleName"). * @example * sanitizeName('example-name'); * @example * sanitizeName('my-custom-component'); */ sanitizeName(name) { let parts = name.split("-"); return [parts.shift(), ...parts.map((n) => n[0].toUpperCase() + n.slice(1))].join(""); } /** * Checks if a property on an object has a getter or setter method defined. * @param {object} obj The object on which the property is defined. * @param {string} property The name of the property to check. * @returns {object} An object indicating the presence of getter and setter methods. * @property {Function|null} hasGetter The getter function if it exists, otherwise `null`. * @property {Function|null} hasSetter The setter function if it exists, otherwise `null`. * @example * const obj = { * get name() { return 'value'; }, * set name(val) { console.log(val); } * }; * checkGetterSetter(obj, 'name'); * @example * const obj = { prop: 42 }; * checkGetterSetter(obj, 'prop'); */ checkGetterSetter(obj, property) { let descriptor = Object.getOwnPropertyDescriptor(obj, property); if (descriptor) { return { hasGetter: typeof descriptor.get === "function" ? descriptor.get : null, hasSetter: typeof descriptor.set === "function" ? descriptor.set : null }; } let proto = Object.getPrototypeOf(obj); if (proto) { return this.checkGetterSetter(proto, property); } return { hasGetter: null, hasSetter: null }; } /** * Sets up property accessors for the component's attributes. */ setUpAccessors() { let attrs = this.getAttributeNames(); attrs.forEach((name) => { const sanitizedName = this.sanitizeName(name); const { hasGetter, hasSetter } = this.checkGetterSetter(this, sanitizedName); Object.defineProperty(this, sanitizedName, { set: hasSetter ?? ((value) => this.setAttribute(name, value)), get: hasGetter ?? (() => this.getAttribute(name)) }); }); } }; _drawingStatus = new WeakMap(); _isAttached = new WeakMap(); _isRendering = new WeakMap(); _originalVisibility = new WeakMap(); _pristine = new WeakMap(); _WJElement_instances = new WeakSet(); /** * Enqueues an update for the component. * This method processes the current render promise and then refreshes the component. */ enqueueUpdate_fn = function() { if (!__privateGet(this, _isRendering)) { this.rafId = requestAnimationFrame(() => __privateMethod(this, _WJElement_instances, refresh_fn).call(this)); } }; refresh_fn = async function() { var _a, _b, _c; if (__privateGet(this, _isRendering)) { this.rafId = requestAnimationFrame(() => __privateMethod(this, _WJElement_instances, refresh_fn).call(this)); return; } if (!__privateGet(this, _pristine)) { __privateSet(this, _pristine, true); __privateSet(this, _isRendering, true); if (__privateGet(this, _isAttached)) { (_a = this.beforeRedraw) == null ? void 0 : _a.call(this); (_b = this.beforeDisconnect) == null ? void 0 : _b.call(this); (_c = this.afterDisconnect) == null ? void 0 : _c.call(this); } else { this.stopRenderLoop(); } try { await this.initWjElement(true); } catch (error) { console.error("Render error:", error); } finally { __privateSet(this, _isRendering, false); if (!__privateGet(this, _pristine)) { __privateSet(this, _pristine, false); __privateMethod(this, _WJElement_instances, enqueueUpdate_fn).call(this); } else { this.finisPromise(); this.style.visibility = __privateGet(this, _originalVisibility); } } } }; /** * Resolves the rendering process of the component. * @returns A promise that resolves when rendering is complete. * @private */ resolveRender_fn = function() { this.params = WjElementUtils.getAttributes(this); return new Promise(async (resolve, reject) => { var _a; const __beforeDraw = this.beforeDraw(this.context, this.store, WjElementUtils.getAttributes(this)); if (__beforeDraw instanceof Promise || (__beforeDraw == null ? void 0 : __beforeDraw.constructor.name) === "Promise") { await __beforeDraw; } await this.render(); const __afterDraw = (_a = this.afterDraw) == null ? void 0 : _a.call(this, this.context, this.store, WjElementUtils.getAttributes(this)); if (__afterDraw instanceof Promise || (__afterDraw == null ? void 0 : __afterDraw.constructor.name) === "Promise") { await __afterDraw; } __privateSet(this, _isRendering, false); __privateSet(this, _isAttached, true); if (this.removeClassAfterConnect) { this.classList.remove(...this.removeClassAfterConnect); } __privateSet(this, _drawingStatus, this.drawingStatuses.DONE); resolve(); }).catch((e) => { console.log(e); }); }; /** * Processes and combines two templates into one. * @param pTemplate The primary template. * @param inputTemplate The secondary template. * @returns The combined template. */ __publicField(_WJElement, "processTemplates", (pTemplate, inputTemplate) => { const newTemplate = document.createElement("template"); newTemplate.innerHTML = [inputTemplate.innerHTML, (pTemplate == null ? void 0 : pTemplate.innerHTML) || ""].join(""); return newTemplate; }); let WJElement = _WJElement; let __esModule = "true"; export { Permissions, WjElementUtils, __esModule, WJElement as default, event }; //# sourceMappingURL=wje-element.js.map