Skip to content

Instantly share code, notes, and snippets.

@Danny-Engelman
Created July 15, 2023 07:29
Show Gist options
  • Select an option

  • Save Danny-Engelman/267b965cfcaa1d529f80f81e1490f8c0 to your computer and use it in GitHub Desktop.

Select an option

Save Danny-Engelman/267b965cfcaa1d529f80f81e1490f8c0 to your computer and use it in GitHub Desktop.

Revisions

  1. Danny-Engelman created this gist Jul 15, 2023.
    201 changes: 201 additions & 0 deletions ACME BaseClass
    Original 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