All files / packages/react-tao/src Adapter.js

100% Statements 59/59
100% Branches 29/29
100% Functions 14/14
100% Lines 57/57
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141            52x       46x               46x         88x 88x 88x 88x 88x       104x       86x       20x       6x 6x               58x             2x       56x 30x 54x 52x 28x   60x 59x 58x         59x 59x 60x 38x 35x 36x   35x 42x 34x 34x       28x             6x 2x   4x 4x 4x   3x 29x   6x 1x   2x 2x 2x 2x 2x 2x 1x   1x         1x 1x 1x   2x       21x       5x          
import cartesian from 'cartesian';
import { Component } from 'react';
import { AppCtx } from '@tao.js/core';
 
import { noop, normalizeClean } from './helpers';
 
const wrappedHandler = (ComponentHandler = null, props, _adapter) => (
  tao,
  data
) => {
  _adapter._current = {
    ComponentHandler,
    tao,
    props: {
      ...props,
      ...data
    }
  };
  _adapter._reactors.forEach(notify => notify());
};
 
class Adapter {
  constructor(TAO) {
    this._tao = TAO;
    this._current = null;
    this._default = {};
    this._reactors = new Map();
    this._components = new Map();
  }
 
  get current() {
    return this._current;
  }
 
  get defaultCtx() {
    return { ...this._default };
  }
 
  set defaultCtx({ t, term, a, action, o, orient } = {}) {
    this._default = normalizeClean({ t, term, a, action, o, orient });
  }
 
  setDefaultCtx({ t, term, a, action, o, orient } = {}) {
    this.defaultCtx = { t, term, a, action, o, orient };
    return this;
  }
 
  addComponentHandler(
    { t, term, a, action, o, orient } = {},
    ComponentHandler,
    props
  ) {
    if (
      ComponentHandler &&
      !(
        ComponentHandler instanceof Component ||
        ComponentHandler instanceof Function
      )
    ) {
      throw new Error(
        'cannot add a Component handler that is not a React.Component or Function'
      );
    }
    const tao = normalizeClean({ t, term, a, action, o, orient });
    const ctx = Object.assign(this.defaultCtx, tao);
    const permutations = cartesian(ctx);
    if (!permutations.length) {
      return this;
    }
    const handler = wrappedHandler(ComponentHandler, props, this);
    if (!this._components.has(ComponentHandler)) {
      this._components.set(ComponentHandler, {
        handlers: new Map(),
        index: new Map()
      });
    }
    const componentHandlers = this._components.get(ComponentHandler);
    permutations.forEach(tao => {
      const { term, action, orient } = tao;
      const acKey = AppCtx.getKey(term, action, orient);
      if (!componentHandlers.index.has(acKey)) {
        componentHandlers.index.set(acKey, new AppCtx(term, action, orient));
      }
      const ac = componentHandlers.index.get(acKey);
      if (!componentHandlers.handlers.has(ac)) {
        componentHandlers.handlers.set(ac, handler);
        this._tao.addInlineHandler(ac.unwrapCtx(), handler);
      }
    });
 
    return this;
  }
 
  removeComponentHandler(
    { t, term, a, action, o, orient } = {},
    ComponentHandler
  ) {
    if (!this._components.has(ComponentHandler)) {
      return this;
    }
    const componentHandlers = this._components.get(ComponentHandler);
    const tao = normalizeClean({ t, term, a, action, o, orient });
    if (!tao.term && !tao.action && !tao.orient) {
      // remove all handlers
      for (let [ac, handler] of componentHandlers.handlers) {
        this._tao.removeInlineHandler(ac.unwrapCtx(), handler);
      }
      this._components.delete(ComponentHandler);
      return this;
    }
    const ctx = Object.assign(this.defaultCtx, tao);
    const permutations = cartesian(ctx);
    permutations.forEach(({ term: t, action: a, orient: o }) => {
      const acKey = AppCtx.getKey(t, a, o);
      const ac = componentHandlers.index.get(acKey);
      if (!ac) {
        return;
      }
      componentHandlers.index.delete(acKey);
      // currently cannot hit this guard
      // if (!componentHandlers.handlers.has(ac)) {
      //   return;
      // }
      const handler = componentHandlers.handlers.get(ac);
      this._tao.removeInlineHandler(ac.unwrapCtx(), handler);
      componentHandlers.handlers.delete(ac);
    });
    return this;
  }
 
  registerReactor(reactor, notify = noop) {
    this._reactors.set(reactor, notify);
  }
 
  unregisterReactor(reactor) {
    this._reactors.delete(reactor);
  }
}
 
export default Adapter;