diff --git a/.babelrc b/.babelrc
new file mode 100644
index 000000000..bd20dc179
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,6 @@
+{
+ "presets": [
+ "es2015",
+ "react"
+ ]
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..0ed1e792c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+logs
+*.log
+node_modules
+.idea
+lib
+npm-debug.log*
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 000000000..faa45389e
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,19 @@
+Copyright (c) Raphaël Benitte
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..f05174f7f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+# nivo
+
+[![License][license-image]][license-url]
+[![Travis CI][travis-image]][travis-url]
+[![NPM version][npm-image]][npm-url]
+[![Dependencies][gemnasium-image]][gemnasium-url]
+
+[license-image]: https://img.shields.io/github/license/nivo/mozaik.svg?style=flat-square
+[license-url]: https://github.com/plouc/nivo/blob/master/LICENSE.md
+[npm-image]: https://img.shields.io/npm/v/nivo.svg?style=flat-square
+[npm-url]: https://www.npmjs.com/package/nivo
+[travis-image]: https://img.shields.io/travis/plouc/nivo.svg?style=flat-square
+[travis-url]: https://travis-ci.org/plouc/nivo
+[gemnasium-image]: https://img.shields.io/gemnasium/plouc/nivo.svg?style=flat-square
+[gemnasium-url]: https://gemnasium.com/plouc/nivo
diff --git a/package.json b/package.json
new file mode 100644
index 000000000..854ddf601
--- /dev/null
+++ b/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "nivo",
+ "version": "0.0.0",
+ "author": {
+ "name": "Raphaël Benitte",
+ "url": "https://github.com/plouc"
+ },
+ "keywords": [],
+ "dependencies": {
+ "d3": "3.5.16",
+ "invariant": "2.2.1",
+ "lodash": "4.11.1",
+ "react-dimensions": "0.1.1"
+ },
+ "devDependencies": {
+ "babel-preset-es2015": "6.6.0",
+ "babel-preset-react": "6.5.0",
+ "react": "^0.13.3"
+ },
+ "peerDependencies": {
+ "react": "^0.13.3"
+ },
+ "main": "src/index.js",
+ "scripts": {}
+}
diff --git a/src/ArcUtils.js b/src/ArcUtils.js
new file mode 100644
index 000000000..4387113c5
--- /dev/null
+++ b/src/ArcUtils.js
@@ -0,0 +1,86 @@
+import d3 from 'd3';
+
+
+export const degreesToRadians = degrees => degrees * Math.PI / 180;
+
+export const radiansToDegrees = radians => 180 * radians / Math.PI;
+
+
+/**
+ * Try to get a neighbor arc, otherwise, returns null.
+ *
+ * @param {Number} i
+ * @param {String} keyProp
+ * @param {Array} prevData
+ * @param {Array} newData
+ * @returns {{startAngle: *, endAngle: *}}
+ */
+export const findNeighbor = (i, keyProp, prevData, newData) => {
+ const preceding = findPreceding(i, keyProp, prevData, newData);
+ if (preceding) {
+ return {
+ startAngle: preceding.endAngle,
+ endAngle: preceding.endAngle
+ };
+ }
+
+ const following = findFollowing(i, keyProp, prevData, newData);
+ if (following) {
+ return {
+ startAngle: following.startAngle,
+ endAngle: following.startAngle
+ };
+ }
+
+ return null;
+};
+
+/**
+ * Find the element in prevData that joins the highest preceding element in newData.
+ *
+ * @param {Number} i
+ * @param {String} keyProp
+ * @param {Array} prevData
+ * @param {Array} newData
+ * @returns {*}
+ */
+export const findPreceding = (i, keyProp, prevData, newData) => {
+ const m = prevData.length;
+
+ while (--i >= 0) {
+ let k = newData[i].data[keyProp];
+
+ for (let j = 0; j < m; ++j) {
+ if (prevData[j].data[keyProp] === k) {
+ return prevData[j];
+ }
+ }
+ }
+};
+
+/**
+ * Find the element in prevData that joins the lowest following element in newData.
+ *
+ * @param {Number} i
+ * @param {String} keyProp
+ * @param {Array} prevData
+ * @param {Array} newData
+ * @returns {*}
+ */
+export const findFollowing = (i, keyProp, prevData, newData) => {
+ const n = newData.length;
+ const m = prevData.length;
+
+ while (++i < n) {
+ let k = newData[i].data[keyProp];
+
+ for (let j = 0; j < m; ++j) {
+ if (prevData[j].data[keyProp] === k) {
+ return prevData[j];
+ }
+ }
+ }
+};
+
+
+export const midAngle = arc => arc.startAngle + (arc.endAngle - arc.startAngle) / 2;
diff --git a/src/components/Chart.jsx b/src/components/Chart.jsx
new file mode 100644
index 000000000..9ad4bf1e2
--- /dev/null
+++ b/src/components/Chart.jsx
@@ -0,0 +1,56 @@
+import React, { Component, PropTypes } from 'react';
+import Dimensions from 'react-dimensions';
+import _ from 'lodash';
+
+
+const defaultMargin = {
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0
+};
+
+class Chart extends Component {
+ render() {
+ const {
+ containerWidth,
+ containerHeight,
+ children
+ } = this.props;
+
+ const margin = _.assign({}, defaultMargin, this.props.margin);
+
+ const width = containerWidth - margin.left - margin.right;
+ const height = containerHeight - margin.top - margin.bottom;
+
+ return (
+
+ );
+ }
+}
+
+Chart.displayName = 'Chart';
+
+Chart.propTypes = {
+ containerWidth: PropTypes.number.isRequired,
+ containerHeight: PropTypes.number.isRequired,
+ margin: PropTypes.shape({
+ top: PropTypes.number,
+ right: PropTypes.number,
+ bottom: PropTypes.number,
+ left: PropTypes.number
+ }).isRequired
+};
+
+Chart.defaultProps = {
+ margin: {}
+};
+
+
+export default Dimensions()(Chart);
diff --git a/src/components/axes/AxisX.jsx b/src/components/axes/AxisX.jsx
new file mode 100644
index 000000000..a8e329242
--- /dev/null
+++ b/src/components/axes/AxisX.jsx
@@ -0,0 +1,19 @@
+import React, { Component, PropTypes } from 'react';
+import d3 from 'd3';
+
+class AxisX extends Component {
+ render() {
+ return null;
+ }
+}
+
+AxisX.displayName = 'AxisX';
+
+AxisX.propTypes = {
+};
+
+AxisX.defaultProps = {
+};
+
+
+export default AxisX;
diff --git a/src/components/axes/AxisY.jsx b/src/components/axes/AxisY.jsx
new file mode 100644
index 000000000..9f8c320b8
--- /dev/null
+++ b/src/components/axes/AxisY.jsx
@@ -0,0 +1,61 @@
+import React, { Component, PropTypes } from 'react';
+import d3 from 'd3';
+
+class AxisY extends Component {
+ shouldComponentUpdate(nextProps) {
+ const {
+ orient,
+ yScale,
+ tickMode,
+ tickPadding,
+ width,
+ transitionDuration,
+ transitionEasing
+ } = nextProps;
+
+ const element = d3.select(React.findDOMNode(this));
+
+ const axis = d3.svg.axis()
+ .scale(yScale)
+ .tickPadding(tickPadding)
+ .orient(orient)
+ ;
+
+ if (tickMode === 'grid') {
+ axis.tickSize(-width);
+ }
+
+ element
+ .transition()
+ .duration(transitionDuration)
+ .ease(transitionEasing)
+ .call(axis)
+ ;
+
+ return false;
+ }
+
+ render() {
+ return ;
+ }
+}
+
+AxisY.displayName = 'AxisY';
+
+AxisY.propTypes = {
+ orient: PropTypes.oneOf(['left', 'right']).isRequired,
+ yScale: PropTypes.func.isRequired,
+ tickMode: PropTypes.oneOf(['normal', 'grid']).isRequired,
+ tickPadding: PropTypes.number.isRequired
+};
+
+AxisY.defaultProps = {
+ orient: 'left',
+ transitionDuration: 600,
+ transitionEasing: 'cubic-out',
+ tickMode: 'normal',
+ tickPadding: 3
+};
+
+
+export default AxisY;
diff --git a/src/components/layouts/Pie.jsx b/src/components/layouts/Pie.jsx
new file mode 100644
index 000000000..d813a7a53
--- /dev/null
+++ b/src/components/layouts/Pie.jsx
@@ -0,0 +1,152 @@
+import React, { Component, PropTypes } from 'react';
+import _ from 'lodash';
+import d3 from 'd3';
+import { degreesToRadians, findNeighbor } from '../../ArcUtils';
+
+
+class Pie extends Component {
+ shouldComponentUpdate(nextProps) {
+ const {
+ data,
+ width, height,
+ sort,
+ keyProp, valueProp,
+ startAngle, endAngle, padAngle,
+ innerRadius,
+ transitionDuration, transitionEasing,
+ } = nextProps;
+
+ const element = d3.select(React.findDOMNode(this));
+ const container = element.select('.chart__layout__pie__slices');
+
+ element.attr('transform', `translate(${width / 2}, ${height / 2})`);
+
+ const pie = d3.layout.pie()
+ .sort(sort)
+ .value(d => d[valueProp])
+ .startAngle(degreesToRadians(startAngle))
+ .endAngle(degreesToRadians(endAngle))
+ .padAngle(degreesToRadians(padAngle))
+ ;
+
+ const radius = Math.min(width / 2, height / 2);
+ const arc = d3.svg.arc()
+ .outerRadius(radius)
+ .innerRadius(radius * innerRadius)
+ ;
+
+ element.select('.chart__layout__pie__outline').attr('d', arc({
+ startAngle: degreesToRadians(startAngle),
+ endAngle: degreesToRadians(endAngle)
+ }));
+
+ const color = d3.scale.category20();
+
+ let slices = container.selectAll('.chart__layout__pie__slice');
+ const previousData = slices.data();
+ const newData = pie(data.map((d, i) => {
+ if (!d.color) {
+ d.color = color(i);
+ }
+
+ return d;
+ }));
+
+ function arcTween(a) {
+ var i = d3.interpolate(this._current, a);
+ this._current = i(0);
+ return function (t) {
+ return arc(i(t));
+ };
+ }
+
+ slices = slices.data(newData, d => d.data[keyProp]);
+ slices.enter().append('path')
+ .attr('class', 'chart__layout__pie__slice')
+ .style('fill', d => d.data.color)
+ .each(function (d, i) {
+ this._current = findNeighbor(i, keyProp, previousData, newData) || _.assign({}, d, { endAngle: d.startAngle });
+ })
+ ;
+ slices.exit()
+ .datum((d, i) => {
+ return findNeighbor(i, keyProp, newData, previousData) || d;
+ })
+ .transition()
+ .duration(transitionDuration)
+ .ease(transitionEasing)
+ .attrTween('d', arcTween)
+ .remove()
+ ;
+ slices
+ .transition()
+ .duration(transitionDuration)
+ .ease(transitionEasing)
+ .attrTween('d', arcTween)
+ .style('fill', d => d.data.color)
+ ;
+
+ this.legends.forEach(legend => {
+ legend({ element, pie, arc, keyProp, data: newData, radius });
+ });
+
+ return false;
+ }
+
+ componentWillMount() {
+ const { children } = this.props;
+
+ const legends = [];
+
+ React.Children.forEach(children, element => {
+ if (React.isValidElement(element)) {
+ if (element.type.createLegendsFromReactElement) {
+ legends.push(element.type.createLegendsFromReactElement(element));
+ }
+ }
+ });
+
+ this.legends = legends;
+ }
+
+ render() {
+ return (
+
+
+
+
+ );
+ }
+}
+
+const { array, number, string, func } = PropTypes;
+
+Pie.propTypes = {
+ width: number.isRequired,
+ height: number.isRequired,
+ sort: func,
+ data: array.isRequired,
+ keyProp: string.isRequired,
+ valueProp: string.isRequired,
+ startAngle: number.isRequired,
+ endAngle: number.isRequired,
+ padAngle: number.isRequired,
+ transitionDuration: number.isRequired,
+ transitionEasing: string.isRequired,
+ innerRadius: number.isRequired
+};
+
+Pie.defaultProps = {
+ sort: null,
+ keyProp: 'label',
+ valueProp: 'value',
+ startAngle: 0,
+ endAngle: 360,
+ padAngle: 0,
+ transitionDuration: 1000,
+ transitionEasing: 'cubic-out',
+ innerRadius: 0
+};
+
+
+export default Pie;
diff --git a/src/components/layouts/PieColumnLegends.jsx b/src/components/layouts/PieColumnLegends.jsx
new file mode 100644
index 000000000..1bd770fc4
--- /dev/null
+++ b/src/components/layouts/PieColumnLegends.jsx
@@ -0,0 +1,92 @@
+import React, { Component, PropTypes } from 'react';
+import invariant from 'invariant';
+import d3 from 'd3';
+import { midAngle } from '../../ArcUtils';
+
+
+class PieColumnLegends extends Component {
+ static createLegendsFromReactElement(element) {
+ const { props } = element;
+
+ return ({ element, arc, keyProp, pie, data, radius }) => {
+
+ const labelFn = props.labelFn || (d => d.data[keyProp]);
+
+ const outerArc = d3.svg.arc()
+ .innerRadius(radius + props.radiusOffset)
+ .outerRadius(radius + props.radiusOffset)
+ ;
+
+ let lines = element.selectAll('.line').data(data, d => d.data[keyProp]);
+ lines.enter()
+ .append('polyline')
+ .attr('stroke', '#fff')
+ .attr('fill', 'none')
+ .attr('class', 'line')
+ ;
+ lines
+ .attr('points', d => {
+ const p0 = arc.centroid(d);
+ const p1 = outerArc.centroid(d);
+ const p2 = [0, p1[1]];
+
+ p2[0] = (radius + props.horizontalOffset) * (midAngle(d) < Math.PI ? 1 : -1);
+
+ return [p0, p1, p2];
+ })
+ ;
+ lines.exit()
+ .remove()
+ ;
+
+ let labels = element.selectAll('.column-label').data(data, d => d.data[keyProp]);
+ labels.enter()
+ .append('text')
+ .attr('fill', '#fff')
+ .attr('class', 'column-label')
+ ;
+ labels
+ .text(labelFn)
+ .attr('text-anchor', d => {
+ return midAngle(d) < Math.PI ? 'start' : 'end';
+ })
+ .attr('transform', d => {
+ const centroid = outerArc.centroid(d);
+ const position = [0, centroid[1]];
+
+ position[0] = (radius + props.horizontalOffset + props.textOffset) * (midAngle(d) < Math.PI ? 1 : -1);
+
+ return `translate(${position[0]}, ${position[1]})`;
+ })
+ ;
+ labels.exit()
+ .remove()
+ ;
+ };
+ }
+
+ render() {
+ invariant(
+ false,
+ ' element is for Pie configuration only and should not be rendered'
+ );
+ }
+}
+
+const { number, func } = PropTypes;
+
+PieColumnLegends.propTypes = {
+ labelFn: func,
+ radiusOffset: number.isRequired,
+ horizontalOffset: number.isRequired,
+ textOffset: number.isRequired
+};
+
+PieColumnLegends.defaultProps = {
+ radiusOffset: 16,
+ horizontalOffset: 30,
+ textOffset: 10
+};
+
+
+export default PieColumnLegends;
diff --git a/src/components/layouts/PieRadialLegends.jsx b/src/components/layouts/PieRadialLegends.jsx
new file mode 100644
index 000000000..6bdbc2dc6
--- /dev/null
+++ b/src/components/layouts/PieRadialLegends.jsx
@@ -0,0 +1,66 @@
+import React, { Component, PropTypes } from 'react';
+import invariant from 'invariant';
+import d3 from 'd3';
+import { midAngle, radiansToDegrees } from '../../ArcUtils';
+
+
+class PieRadialLegends extends Component {
+ static createLegendsFromReactElement(element) {
+ const { props } = element;
+
+ return ({ element, arc, keyProp, pie, data, radius }) => {
+
+ const labelFn = props.labelFn || (d => d.data[keyProp]);
+
+ const outerArc = d3.svg.arc()
+ .innerRadius(radius + props.radiusOffset)
+ .outerRadius(radius + props.radiusOffset)
+ ;
+
+ let labels = element.selectAll('.radial-label').data(data, d => d.data[keyProp]);
+ labels.enter()
+ .append('text')
+ .attr('fill', '#fff')
+ .attr('class', 'radial-label')
+ ;
+ labels
+ .text(labelFn)
+ .attr('text-anchor', d => {
+ return midAngle(d) < Math.PI ? 'start' : 'end';
+ })
+ .attr('transform', d => {
+ const centroid = outerArc.centroid(d);
+ const angle = midAngle(d);
+
+ const angleOffset = angle < Math.PI ? -90 : 90;
+
+ return `translate(${centroid[0]}, ${centroid[1]}) rotate(${radiansToDegrees(angle) + angleOffset}, 0, 0)`;
+ })
+ ;
+ labels.exit()
+ .remove()
+ ;
+ };
+ }
+
+ render() {
+ invariant(
+ false,
+ ' element is for Pie configuration only and should not be rendered'
+ );
+ }
+}
+
+const { number, func } = PropTypes;
+
+PieRadialLegends.propTypes = {
+ labelFn: func,
+ radiusOffset: number.isRequired
+};
+
+PieRadialLegends.defaultProps = {
+ radiusOffset: 16
+};
+
+
+export default PieRadialLegends;
diff --git a/src/components/layouts/PieSliceLegends.jsx b/src/components/layouts/PieSliceLegends.jsx
new file mode 100644
index 000000000..e11d917bb
--- /dev/null
+++ b/src/components/layouts/PieSliceLegends.jsx
@@ -0,0 +1,91 @@
+import React, { Component, PropTypes } from 'react';
+import invariant from 'invariant';
+import d3 from 'd3';
+import { midAngle, radiansToDegrees } from '../../ArcUtils';
+
+
+class PieSliceLegends extends Component {
+ static createLegendsFromReactElement(element) {
+ const { props } = element;
+
+ return ({ element, keyProp, arc, data }) => {
+ let legends = element.selectAll('.slice-legend').data(data, d => d.data[keyProp]);
+
+ const labelFn = props.labelFn || (d => d.data[keyProp]);
+
+ legends.enter()
+ .append('g')
+ .attr('class', 'slice-legend')
+ .attr('transform', d => {
+ const centroid = arc.centroid(d);
+
+ return `translate(${centroid[0]}, ${centroid[1]})`;
+ })
+ .each(function (d) {
+ d3.select(this)
+ .append('circle')
+ .attr('fill', d3.rgb(d.data.color).darker(1).toString())
+ .attr('r', props.radius)
+ ;
+
+ d3.select(this)
+ .append('text')
+ .attr('fill', d => d.data.color)
+ .attr('text-anchor', 'middle')
+ ;
+ })
+ ;
+
+ legends
+ .attr('transform', d => {
+ const centroid = arc.centroid(d);
+ let transform = `translate(${centroid[0]}, ${centroid[1]})`;
+
+ if (props.orient) {
+ const angle = midAngle(d);
+ transform = `${transform} rotate(${radiansToDegrees(angle)}, 0, 0)`;
+ }
+
+ return transform;
+ })
+ .each(function (d) {
+ d3.select(this).select('circle')
+ .attr('fill', d3.rgb(d.data.color).darker(1).toString())
+ ;
+
+ d3.select(this).select('text')
+ .attr('fill', d => d.data.color)
+ .text(labelFn(d))
+ ;
+ })
+ ;
+
+ legends.exit()
+ .remove()
+ ;
+ };
+ }
+
+ render() {
+ invariant(
+ false,
+ ' element is for Pie configuration only and should not be rendered'
+ );
+ }
+}
+
+const { number, bool, func } = PropTypes;
+
+PieSliceLegends.propTypes = {
+ labelFn: func,
+ radius: number.isRequired,
+ orient: bool.isRequired
+};
+
+PieSliceLegends.defaultProps = {
+ radius: 12,
+ orient: true
+};
+
+
+export default PieSliceLegends;
diff --git a/src/components/shapes/Area.jsx b/src/components/shapes/Area.jsx
new file mode 100644
index 000000000..bb443093d
--- /dev/null
+++ b/src/components/shapes/Area.jsx
@@ -0,0 +1,60 @@
+import React, { Component, PropTypes } from 'react';
+import d3 from 'd3';
+
+
+class Area extends Component {
+ shouldComponentUpdate(nextProps) {
+ const {
+ data,
+ xScale, yScale,
+ width, height,
+ interpolation,
+ transitionDuration,
+ transitionEasing
+ } = nextProps;
+
+ const element = d3.select(React.findDOMNode(this));
+
+ const area = d3.svg.area()
+ .x((d, i) => xScale(i))
+ .y0(height)
+ .y1(d => yScale(d))
+ .interpolate(interpolation)
+ ;
+
+ element.datum(data)
+ .transition()
+ .duration(transitionDuration)
+ .ease(transitionEasing)
+ .attr('d', area)
+ ;
+
+ return false;
+ }
+
+ render() {
+ return ;
+ }
+}
+
+Area.displayName = 'Area';
+
+Area.propTypes = {
+ data: PropTypes.array.isRequired,
+ xScale: PropTypes.func.isRequired,
+ yScale: PropTypes.func.isRequired,
+ width: PropTypes.number.isRequired,
+ height: PropTypes.number.isRequired,
+ interpolation: PropTypes.string.isRequired,
+ transitionDuration: PropTypes.number.isRequired,
+ transitionEasing: PropTypes.string.isRequired
+};
+
+Area.defaultProps = {
+ interpolation: 'linear',
+ transitionDuration: 600,
+ transitionEasing: 'cubic-out'
+};
+
+
+export default Area;
diff --git a/src/components/shapes/Line.jsx b/src/components/shapes/Line.jsx
new file mode 100644
index 000000000..4c19eede4
--- /dev/null
+++ b/src/components/shapes/Line.jsx
@@ -0,0 +1,56 @@
+import React, { Component, PropTypes } from 'react';
+import d3 from 'd3';
+
+
+class Line extends Component {
+ shouldComponentUpdate(nextProps) {
+ const {
+ data,
+ xScale, yScale,
+ interpolation,
+ transitionDuration,
+ transitionEasing
+ } = nextProps;
+
+ const element = d3.select(React.findDOMNode(this));
+
+ const line = d3.svg.line()
+ .x((d, i) => xScale(i))
+ .y(d => yScale(d))
+ .interpolate(interpolation)
+ ;
+
+ element.datum(data)
+ .transition()
+ .duration(transitionDuration)
+ .ease(transitionEasing)
+ .attr('d', line)
+ ;
+
+ return false;
+ }
+
+ render() {
+ return ;
+ }
+}
+
+Line.displayName = 'Line';
+
+Line.propTypes = {
+ data: PropTypes.array.isRequired,
+ xScale: PropTypes.func.isRequired,
+ yScale: PropTypes.func.isRequired,
+ interpolation: PropTypes.string.isRequired,
+ transitionDuration: PropTypes.number.isRequired,
+ transitionEasing: PropTypes.string.isRequired
+};
+
+Line.defaultProps = {
+ interpolation: 'linear',
+ transitionDuration: 600,
+ transitionEasing: 'cubic-out'
+};
+
+
+export default Line;
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 000000000..d43304de0
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,22 @@
+import Chart from './components/Chart.jsx';
+import Pie from './components/layouts/Pie.jsx';
+import PieColumnLegends from './components/layouts/PieColumnLegends.jsx';
+import PieRadialLegends from './components/layouts/PieRadialLegends.jsx';
+import PieSliceLegends from './components/layouts/PieSliceLegends.jsx';
+import AreaShape from './components/shapes/Area.jsx';
+import LineShape from './components/shapes/Line.jsx';
+import AxisX from './components/axes/AxisX.jsx';
+import AxisY from './components/axes/AxisY.jsx';
+
+
+export default {
+ Chart,
+ Pie,
+ PieColumnLegends,
+ PieRadialLegends,
+ PieSliceLegends,
+ AreaShape,
+ LineShape,
+ AxisX,
+ AxisY
+};