Widget.js

import { createElement, deleteElement } from './element';
import styles from './style.css';

function _applyProps(element, props, propMap) {
  // Put the props in the order defined in propMap, not the order in the props object.
  const propOrder = Object.keys(propMap);
  const propsIter = Object.keys(props).sort(
    (a, b) => propOrder.indexOf(a) - propOrder.indexOf(b)
  );
  for (let prop of propsIter) {
    if (props[prop]) {
      if (propMap[prop] != null) {
        propMap[prop](element, props[prop]);
      } else {
        console.error(`Unsupported prop '${prop}'`);
      }
    }
  }
}

export function applyProps(element, props, propMap, originalClasses) {
  const events = [];
  const proxy = {
    id: null,
    innerHTML: '',
    styles: [],
    classes: originalClasses,
    events,
    addEventListener: (type, handler, useCapture) => {
      events.push({type, handler, useCapture});
    }
  };
  _applyProps(proxy, props, propMap);
  if (proxy.id) element.id = proxy.id;
  if (proxy.innerHTML) element.innerHTML = proxy.innerHTML;
  if (proxy.classes) element.className = proxy.classes.join(' ');
  if (proxy.styles) element.style = proxy.styles.join(';');

  // TODO: remove the previous events, or figure out diffs
  for (let event of proxy.events) {
    element.addEventListener(event.type, event.handler, event.useCapture);
  }
}


/**
 * The base widget class for all widgets in this library.
 */
class Widget {
  constructor(type, props, propMap) {
    this.props = props || {};
    const id = this.props.id;
    const className = styles[type];
    this.element = createElement({ element: 'div', id, className });
    this.originalClassName = this.element.className;
    this.propMap = propMap;
    this.applyProps(this.props);
  }

  applyProps(props) {
    if (!this.element) {
      return;
    }
    applyProps(this.element, props, this.propMap, [this.originalClassName]);
  }

  updateProp(propName, value) {
    Object.assign(this.props, { [propName]: value });
    this.applyProps(this.props);
  }

  addChild(w) {
    w.parent = this;
    this.element.appendChild(w.element);
  }

  /**
   * Delete the widget and any child widgets.
   */
  delete() {
    if (this.element) {
      if (this.parent) {
        this.parent.element.removeChild(this.element);
        this.parent = null;
      } else {
        deleteElement(this.element);
      }
      this.element = null;
    }
  }
};

export default Widget;