JSFiddle - React, Tailwind, and code Playground

by elclanrs

HTML

<my-popover>
  <p>Title Goes Here</p>
</my-popover>

JavaScript

// https://stackoverflow.com/questions/9368538/getting-an-array-of-all-dom-events-possible
const events = Object.getOwnPropertyNames(document)
  .concat(Object.getOwnPropertyNames(Object.getPrototypeOf(Object.getPrototypeOf(document))))
  .concat(Object.getOwnPropertyNames(Object.getPrototypeOf(window)))
  .filter((elem, idx, self) => {
    return !elem.indexOf('on') &&
      (document[elem] === null || typeof document[elem] === 'function') &&
      self.indexOf(elem) === idx;
  })
  .map(evt => evt.replace(/^on/, ''));

function createComponent(selector, config) {
	const { template = '', style = '', attributes = {} } = config;
	const templateElem = document.createElement('template');
  
  delete config.template;
  delete config.style;
  delete config.attributes;
  
  templateElem.innerHTML = `
  <style>${style}</style>
  ${template}
  `.trim();
  
  const comp = class extends HTMLElement {
    static get observedAttributes() {
      return Object.keys(attributes);
    }
    
  	constructor() {
    	super();
      const templateClone = templateElem.content.cloneNode(true);
      this.attachShadow({ mode: 'open' }).appendChild(templateClone);
      
      events.forEach(evtName => {
        Array.from(this.shadowRoot.querySelectorAll(`[on-${evtName}]`)).forEach(elem => {
          const methodName = elem.getAttribute([`on-${evtName}`]);
          const method = this[methodName].bind(this);
          elem.addEventListener(evtName, method);
        });
      });
      
      Object.entries(attributes).forEach(([name, attr]) => {
      	if (!this.hasAttribute(name)) {
        	this[name] = attr;
        }
      })
    }
  };
  
  Object.entries(attributes).forEach(([name, attr]) => {
    Object.defineProperty(comp.prototype, name, {
    	get() {
      	return this[`_${name}`];
      },
      set(value) {
      	this[`_${name}`] = value;
        
        if (typeof value === 'boolean') {
        	if (value) {
          	this.setAttribute(name, value);
          } else...