Home Reference Source Repository

src/main/scheduler/drawSchedule.js

import moduleKeys from './moduleKeys';
import { schQ } from './pipeline';
import id from './id';

const { PRE } = moduleKeys();
/**
 * drawSchedule:
 *  exactly the same as draw except to be used with load.  drawSchedule
 *  causes nothing to happen on its own.  It returns an object to be
 *  rendered by load.
 *  ```
 *  const axis = drawSchedule(...args);
 *  const bar = drawSchedule(...args);
 *  load('barAnimation', axis, bar);
 *  ```
 *
 *  The big difference is there needs to be a second argument to pass to 
 *  the function during rendering.  Under the hood an event listener is
 *  used to know when the function completes asynchronously. Notice in the
 *  main example the following code.
 *  ```
 *  (selection, done) =>
 *    ...
 *    .call(done)
 * ```
 * In order for the sequence to complete an argument must be given to the 
 * function and it must be called at the end.
 *
 * @todo have a verification to make sure an argument is given to the
 *  function that is called by .call.
 *
 * @see {@link draw}
 * @see {@link load}
 *
 * @param {String} what - what will be picked in the selection 
 * @param {D3Object} parent - the SVG that will be render new images
 * @param {Object} settings - All of the functionality of the view of
 *   component. must include properties data, and is.
 * @return {Object} this is description.
 * @property {String} string type used internally
 * @property {Function} bind function that will bind or not bind data to 
 *  the DOM
 * @property {Object} is functions render DOM nodes with D3 
 *
 * @example
 * function bar(parent, data, helpers) {
 *   let {size, x, y} = helpers;
 * 
 *   return drawSchedule('.bar', parent, {
 *     data: data,
 *     is:{
 *       enter: (selection, done) => {
 *         return selection.enter().append('rect')
 *           .attr({
 *             'class': 'bar',
 *             'x': (d) => x(d.letter),
 *             'width': x.rangeBand(),
 *             'y': (d) => y(d.frequency),
 *             'height': (d) => size.height - y(d.frequency)
 *           })
 *           .style({
 *             'opacity': 0
 *           })
 *           .transition().delay(100)
 *           .style({
 *             'opacity': 1
 *           })
 *           .call(done);
 *       }
 *     }
 *   });
 * }
 *
 */
function drawSchedule(what, parent, settings) {
  let {data, is} = settings;
  let applyArgs = undefined;
  let dataBinder = undefined;

  parent = data === false ? parent : parent.selectAll(what);

  if (!(data instanceof Array 
        && data[1] instanceof Function)){
    applyArgs = [data];
  } else {
    applyArgs = data;
  }

  if (data) {
    dataBinder = () => {
      return parent.data.apply(parent, applyArgs);
    };
  } else {
    dataBinder = () => {
      return parent;
    };
  }

  return {
    type: PRE,
    bind: {dataBinder},
    is: is
  };
}

/**
 * load
 *  what each transition to schq to be rendered in sequential order.  Any
 *  time this function is call all previous transition are canceled.
 *
 * @see {@link drawSchedule}
 *
 * @param {String} stringId - a unique id so schq can use an event listener
 *  to know when transitions have ended.
 * @param {...Function} transitions - a series of functions that return 
 *  transition objects
 */
function load(stringId, ...transitions) {
  let key = id(stringId);

  schQ.loader(transitions, key);
}

export {drawSchedule, load};