CSNW/d3.compose

View on GitHub
src/chart.js

Summary

Maintainability
B
5 hrs
Test Coverage
import {
  assign,
  inherits,
  objectEach,
  isObject,
  isUndefined
} from './utils';
import {
  checkProp,
  createChart,
  isChart,
  types
} from './helpers';
import {constraint} from './layout';

var defaultProps = {};
var defaultMargin = {top: 0, right: 0, bottom: 0, left: 0};

export function Chart(selection, props, context) {
  this.base = selection;
  this.context = context;
  this.setProps(props);
}

assign(Chart.prototype, {
  setProps: function setProps(props) {
    props = props || defaultProps;
    var properties = this.constructor && this.constructor.properties;
    if (!properties) {
      this.props = props;
      return;
    }

    // TODO Skip in production
    checkProps(properties, props);

    var defaults = getDefaults(properties, props);
    this.props = assign(defaults, props);
  },

  prepareLayout: function prepareLayout(layout) {
    var width = layout.width;
    var height = layout.height;
    var margin = layout.margin;

    // Load width/height (if necessary)
    if (isUndefined(width) || isUndefined(height)) {
      var dimensions = this.getDimensions();
      if (isUndefined(width)) {
        width = dimensions.width;
      }
      if (isUndefined(height)) {
        height = dimensions.height;
      }
    }

    // Load default margins
    if (!isUndefined(margin) && !isObject(margin)) {
      margin = {top: margin, right: margin, bottom: margin, left: margin};
    } else {
      margin = assign({}, this.getMargin(), margin);
    }

    return assign({}, layout, {width: width, height: height, margin: margin});
  },

  getDimensions: function getDimensions() {
    return {
      width: constraint.flex(),
      height: constraint.flex()
    };
  },

  getMargin: function getMargin() {
    return defaultMargin;
  },

  render: function render() {},
  remove: function remove() {}
});

Chart.properties = {
  top: types.any,
  right: types.any,
  bottom: types.any,
  left: types.any,
  width: types.any,
  height: types.any,
  margin: types.any,
  zIndex: types.any
};

Chart.layerType = 'g';

/**
  Create extension with given prototype and static properties

  @method extend
  @static
  @param {Object} protoProps
  @param {Object} staticProps
  @return {Class} extension
*/
Chart.extend = function extend(protoProps, staticProps) {
  var Parent = this;
  var Child;
  
  if (protoProps && Object.prototype.hasOwnProperty.call(protoProps, "constructor")) {
    Child = protoProps.constructor;

    // inherits sets constructor, remove from protoProps
    protoProps = assign({}, protoProps);
    delete protoProps.constructor;
  } else {
    Child = function() { return Parent.apply(this, arguments); };
  }

  inherits(Child, Parent);

  if (staticProps) {
    assign(Child, staticProps);
  }
  if (protoProps) {
    assign(Child.prototype, protoProps);
  }

  return Child;
}

export default function chart(Type) {
  if (!isChart(Type)) {
    Type = createChart(Type, Chart);
  }

  return function(id, props) {
    if (!props) {
      props = id;
      id = undefined;
    }

    return {type: Type, id: id, props: props};
  };
}

// Helpers
export function getDefaults(definitions, props) {
  var defaults = Object.keys(definitions).reduce(function(memo, key) {
    var definition = definitions[key];
    if (isUndefined(props[key]) && definition.getDefault) {
      memo[key] = definition.getDefault(props);
    }
    return memo;
  }, {});

  return defaults;
}

export function checkProps(definitions, props) {
  objectEach(definitions, function(definition, key) {
    checkProp(props[key], definition);
  });
}