Created
July 15, 2023 07:29
-
-
Save Danny-Engelman/267b965cfcaa1d529f80f81e1490f8c0 to your computer and use it in GitHub Desktop.
Revisions
-
Danny-Engelman created this gist
Jul 15, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,201 @@ // ********************************************************** ACME_BaseClass class ACME_BaseClass extends HTMLElement { // ======================================================== ACME_BaseClass.$query $query( // selector is a DOM selector string eg. "div:not[id='1']" // if starts with * then return all elements as NodeList // if starts with ** then return all elements as Array selector, // optional 2nd parameter is the root element to query from root = this.shadowRoot || this ) { if (selector.charAt(0) === "*" || selector.charAt(1) === "*") { if (selector.charAt(1) === "*") { return [...root.querySelectorAll(selector.slice(2))]; } else { return root.querySelectorAll(selector.slice(1)); } } else return root.querySelector(selector); } // ======================================================== ACME_BaseClass.$elementHTML $elementHTML({ tag = "div", // HTML tag name html = "", attrs = "", }) { return `<${tag} ${attrs}>${html}</${tag}>`; } // ======================================================== ACME_BaseClass.$element $element({ tag = "div", // HTML tag name "div", if start with * or ** call $query to get element props = {}, // properties (and eventlisteners) attached to element attrs = [], // attributes attached to element classes = [], events = {}, // standard eventlisteners attached to element listeners = {}, // custom eventlisteners attached to element, when added to the DOM customevents = {}, // custom eventlisteners attached to element prepend = [], // element.prepend(...prepend) html, // element.innerHTML append = [], // element.append(...append) // optional override new element with existing element element = tag.charAt(0) === "*" ? this.$query(tag) // do not create a new tag, find existing element : document.createElement(tag), // else create a new tag styles = {}, //stuff any other properties into moreprops variable ...moreprops }) { // assign props,events and moreprops to element element = Object.assign(element, { ...props, ...events, ...moreprops }); // filter out empty classes classes = classes.filter((x) => x.length); if (classes.length) element.classList.add(...classes); element.prepend(...prepend.filter(Boolean)); if (html) element.innerHTML = html; element.append(...append.filter(Boolean)); (Array.isArray(attrs) ? attrs // if attrs is an Array, do a setAttribute for each attribute : Object.entries(attrs) ) // else proces as Object .map(([key, value]) => element.setAttribute(key, value)); // apply styles Object.entries(styles).map(([key, value]) => { element.style[key] = value; }); // apply customevents Object.entries(customevents).map(([name, handler]) => { console.log(name, handler, this); this.$listen_signal({ name, handler: handler.bind(element), // bind element scope to handler eventbus: document, }); }); // add listener to remove all eventlisteners on element element.addEventListener( new CustomEvent("removeEventListeners"), (evt) => { element.removeEventListeners(evt); } ); return element; } // ======================================================== ACME_BaseClass.disconnectedCallback disconnectedCallback() { console.warn("" + this.tagName + " disconnected"); } // ======================================================== ACME_BaseClass.eventbus get eventbus() { return this.__eventbus__ || document; } // ======================================================== ACME_BaseClass.connectedCallback connectedCallback(...args) { let scope = this; //! register all methods starting with event_ as $listener function registerEventMethods({ scope }) { Object.getOwnPropertyNames(Object.getPrototypeOf(scope)).map( (method) => { let eventbus; let [event, ...name] = method.split("_"); //! name becomes an Array! if (event == "event") { //! determine name and eventbus where to listen name = name.join("_"); // make sure second _ in event_nameX_nameY is possible if (name[0] === "$") { // $click is registered on scope/this element name = name.slice(1); // remove $ eventbus = scope; // listening at current element level } else { // event_nameX_nameY is registered on document eventbus = scope.eventbus; // listening at document level } // if (name == "borderColor") // log( // scope.nodeName + ` %c ${name} %c ${eventbus.nodeName}`, // "background:gold;", // "background:skyblue", // method, // eventbus // ); let useCapture = name.includes("_capture"); if (useCapture) name = name.replace("_capture", ""); // register the listener scope.$listen_signal({ name, // eventName eventbus, handler: scope[method].bind(scope), useCapture, }); } } ); // getOwnPropertyNames } // registerEventMethods registerEventMethods({ scope }); //if (scope["render_once"]) scope["render_once"].call(scope); //!if (scope["render"]) scope["render"].apply(scope,...args); } // ======================================================== ACME_BaseClass.$dispatch $dispatch_signal({ name, // EventName detail = {}, // event.detail // override options PER option: bubbles = true, // default, bubbles up the DOM composed = true, // default, escape shadowRoots cancelable = true, // default, cancelable event bubbling // optional overwrite whole options settings, or use already specified options options = { bubbles, composed, cancelable, }, eventbus = this, // default dispatch from current this element or use something like eventbus:document once = false, // default .dispatchEvent option to execute a Listener once }) { //console.warn("%c EventName:", "background:yellow", name, [detail]); window.HTMLColorPicker_Events = window.HTMLColorPicker_Events || new Set(); window.HTMLColorPicker_Events.add(name); eventbus.dispatchEvent( new CustomEvent(name, { ...options, // detail, }), once // default false ); } // ======================================================== ACME_BaseClass.$emit // shorthand code for $dispatch({}) $emit_signal(name, detail = {}, root = this) { root.$dispatch_signal({ name, // eventName detail, // evt.detail }); } // ======================================================== ACME_BaseClass.$listen_signal $listen_signal({ name = this.nodeName, // first element is String or configuration Object{} handler = () => { }, // optional handler FUNCTION, default empty function eventbus = this, // at what element in the DOM the listener should be attached useCapture = false, // optional, default false }) { eventbus.addEventListener( name, (evt) => handler(evt), useCapture // default false ); // record all listeners on this element this._listeners = this._listeners || []; this._listeners.push(() => eventbus.$removeEventListener(name, handler)); } // ======================================================== ACME_BaseClass.removeEventListeners $removeEventListeners() { this._listeners.map((x) => x()); } // ======================================================== ACME_BaseClass } // ********************************************************** ACME_BaseClass