Full Code of AnSavvides/d3act for AI

master 2521d0e2e59b cached
16 files
32.6 KB
8.4k tokens
60 symbols
1 requests
Download .txt
Repository: AnSavvides/d3act
Branch: master
Commit: 2521d0e2e59b
Files: 16
Total size: 32.6 KB

Directory structure:
gitextract_jf5houkv/

├── .eslintrc
├── .gitignore
├── .jshintrc
├── .npmignore
├── Gruntfile.js
├── LICENSE
├── README.md
├── examples/
│   ├── app.jsx
│   ├── index.html
│   └── webpack.config.js
├── package.json
└── src/
    └── components/
        ├── BarChart.js
        ├── BaseChart.js
        ├── BubbleChart.js
        ├── Chart.js
        └── PieChart.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintrc
================================================
{
 "parser": "babel-eslint",
  "rules": {
    "strict": 0,
    "global-strict": 0
  },
  "plugins": [
    "react"
  ],
  "globals": {
    "document": true,
    "event": true
  }
}

================================================
FILE: .gitignore
================================================
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules

lib
dist

================================================
FILE: .jshintrc
================================================
{
  "node": true,
  "browser": true,
  "esnext": true,
  "bitwise": true,
  "camelcase": false,
  "curly": true,
  "eqeqeq": true,
  "immed": true,
  "indent": 4,
  "latedef": true,
  "newcap": true,
  "noarg": true,
  "quotmark": "false",
  "regexp": true,
  "undef": true,
  "unused": false,
  "strict": true,
  "trailing": true,
  "smarttabs": true,
  "white": true,
  "newcap": false,
  "globals": {
    "React": true
  }
}



================================================
FILE: .npmignore
================================================
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules



================================================
FILE: Gruntfile.js
================================================
"use strict";

module.exports = function (grunt) {
    require("load-grunt-tasks")(grunt);

    var pkgConfig = grunt.file.readJSON("package.json");

    grunt.initConfig({
        pkg: pkgConfig,

        webpack: {
            options: {
                entry: "./src/components/Chart.js",
                output: {
                    path: "./dist/",
                    filename: "dist.js",
                },
                stats: {
                    colors: true,
                    modules: true,
                    reasons: true,
                },
                module: {
                    loaders: [
                        {
                            test: /\.js$/,
                            exclude: /node_modules/,
                            loader: "babel-loader"
                        }
                    ]
                },
            },
            dist: {
                cache: false
            }
        },

        watch: {
            scripts: {
                files: ["./src/components/*.js"],
                tasks: ["build"],
                options: {
                    atBegin: true
                }
            }
        },

        eslint: {
            target: ["./src/components/*.js"],
        }

    });

    grunt.loadNpmTasks("grunt-contrib-watch");

    grunt.registerTask("lint", ["eslint"]);

    grunt.registerTask("build", ["webpack", "eslint"]);

};


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015 Andreas Savvides

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.

================================================
FILE: README.md
================================================
# d3act - d3 with React

## Background

[`d3`](http://d3js.org/) has been the de facto standard when it comes to data visualisations for a while now and [`React`](http://facebook.github.io/react/) has recently emerged as the go-to library for building user interfaces. `d3` and `React` are both data-centric libraries, making them a natural fit; `d3` takes a data-driven approach and `React` aims to solve the problem of data changing over time in the context of building large applications.

In recent months, there have been various approaches documented & implemented in order to showcase how to effectively use `d3` and `React` together, especially with a focus on letting `React` do the SVG rendering:
- ["D3 and React - the future of charting components?" by 10consulting](http://10consulting.com/2014/02/19/d3-plus-reactjs-for-charting/)
- ["ReactJS and D3 Part I: Layouts" by Colin Megill](http://formidablelabs.com/blog/2015/05/21/react-d3-layouts/)
- ["react-d3 by Eric S. Bullington"](https://github.com/esbullington/react-d3)
- ["react-d3-components by Neri Marschik"](https://github.com/codesuki/react-d3-components)

[Nicolas Hery's "Integrating D3.js visualizations in a React app"](http://nicolashery.com/integrating-d3js-visualizations-in-a-react-app/) talks about letting `d3` do all the work after React has finished rendering components in the DOM. `d3act` is inspired by [@nicolas_hery](https://twitter.com/nicolas_hery)'s approach; create a lightweight `React` component wrapper that maps `React`'s lifecycle methods `componentDidMount`, `componentDidUpdate` and `componentWillUnmount` into a `d3` chart's `create`, `update` and `unmount` methods respectively.

`d3act` and the idea behind it was first presented at [ReactEurope 2015](https://www.react-europe.org/2015.html) as a five minute lightning talk; you can watch it [here](https://www.youtube.com/watch?v=6Pbf0n85HH8).

## How does this improve DX?

During the [keynote](https://www.youtube.com/watch?v=PAA9O4E1IM4) at ReactEurope 2015, [vjeux](https://twitter.com/vjeux) introduced the idea of DX (Developer Experience) - akin to UX (User Experience), but with a focus on enabling developers to build great UX with the best possible tools & technologies. Things like [Babel](babeljs.io), [Redux](https://github.com/gaearon/redux) and [Radium](https://github.com/FormidableLabs/radium) definitely tick the DX box.

So how does `d3act` improve DX? The need for creating `d3act` first emerged while building multiple `d3`-based visualisations to handle very large data sets in a single page in the context of a `React` app. Letting `React` manipulate and render `SVG` elements proved to slow things down - not by much, but at the scale of data that was being handled, this became a bottleneck.

At the same time, all existing visualisations were already written in the standard `d3` way which performed best - so in order to improve the DX let's wrap these existing `d3` visualisations in a lightweight `React` component that can extend to any such visualisations.

As a result, `d3act` aims to enable a better DX by enabling:
- Use of existing `d3` visualisations
- Keep creating `d3` visualisations using the same syntax
- Don't worry about taking apart `d3`'s approach and reimplement the same things in `React`
- Integrate seamlessly with `React` applications
- Utilise what each library is best at

## Getting Started

```bash
npm install d3act
```

### Examples

#### It's as simple as...

```js
<Chart
    type={"pie"}
    width={300}
    height={300}
    showTooltips={true}
    data={
        {
            "React": 2,
            "Relay": 12,
            "GraphQL": 5,
        }
    }
/>
```

#### ...and you can use already existing charts

Ensure your existing charts have `constructor`, `create`, `update` and `unmount` functions.

```js
class PreExistingChart {
    constructor(el, props) {
        this.el = el;
        this.props = props;
    }

    create(data) {
        // Create your chart
    }

    update(data) {
        // Update your chart
    }

    unmount() {
        this.el.remove();
    }
}

<Chart
    type={"custom"}
    customChart={PreExistingChart}
    data={
        {
            "React": 2,
            "Relay": 12,
            "GraphQL": 5,
        }
    }
/>
```

To see examples in action locally:

```bash
npm install
npm run examples
```

Head to http://localhost:8080/ and see charts in action as below:

![Bubble chart](bubble.png)
![Bar chart](bar.png)
![Pie chart](pie.png)

### How do I update charts?

Just update your `data` state and all else is handled for you.

## What's next?

- Improve API so that you can customise charts beyond just dimensions and margins
- Better error handling
- Manage state better
- Improve tooltips & make them more extensible
- Think about making expected data for each visualisation a little more uniform
- Add tests

## Is this the best way to combine `d3` and `React`?

This is one way of combining `d3` with `React`; there are plenty of other ways as already outlined - pick what works best for you. I don't think there is such a thing as a global 'best way' of combining `d3` with `React`, it very much comes down to what you want to do with it.

## Contributing

All contributions are welcome; fork the repository, make your changes and open a pull request. Try to stick to the coding conventions that are already in place.

To build things locally, you can just `grunt watch` from the project's root directory. This will also lint code using [ESLint](http://eslint.org/); if you would like to just lint the code, you could also just use `grunt lint`.

## License

[MIT](LICENSE)


================================================
FILE: examples/app.jsx
================================================
import * as d3 from "d3";
import React from "react";
import { render } from "react-dom";

import Chart from "../lib/components/Chart";

class ExampleBarChart extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            data: [
                { xValue: "React", yValue: 2 },
                { xValue: "Relay", yValue: 12 },
                { xValue: "GraphQL", yValue: 5 },
                { xValue: "Radium", yValue: 7 },
                { xValue: "Babel", yValue: 5 },
            ]
        };
    }

    componentDidMount() {
        setTimeout(() => {
            this.setState({
                data: [
                    { xValue: "React", yValue: 2 },
                    { xValue: "Relay", yValue: 8 },
                    { xValue: "GraphQL", yValue: 15 },
                    { xValue: "Radium", yValue: 27 },
                    { xValue: "Babel", yValue: 5 },
                ]
            })
        }, 3000);
    }

    render() {
        return (
            <div>
                <h2>Bar Chart</h2>
                <Chart
                    type={"bar"}
                    width={500}
                    height={500}
                    margin={{ top: 40, right: 40, bottom: 40, left: 40 }}
                    showTooltips={true}
                    data={this.state.data}
                />
            </div>
        );
    }
}

class ExamplePieChart extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            data: {
                "React": 2,
                "Relay": 12,
                "GraphQL": 5,
                "Radium": 7,
                "Babel": 5,
            }
        };
    }

    render() {
        return (
            <div>
                <h2>Pie Chart</h2>
                <Chart
                    type={"pie"}
                    width={300}
                    height={300}
                    showTooltips={true}
                    data={this.state.data}
                />
            </div>
        );
    }

}

class ExampleDonutChart extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            data: {
                "React": 2,
                "Relay": 12,
                "GraphQL": 5,
                "Radium": 7,
                "Babel": 5,
            }
        };
    }

    render() {
        return (
            <div>
                <h2>Donut Chart</h2>
                <Chart
                    type={"pie"}
                    width={300}
                    height={300}
                    innerRadius={100}
                    showTooltips={true}
                    showLegend={true}
                    data={this.state.data}
                />
            </div>
        );
    }

}

class ExampleBubbleChart extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            data: {
                children: [
                    { name: "Alaa", value: 1 },
                    { name: "Zaid", value: 1 },
                    { name: "Kareem", value: 2 },
                    { name: "Mahmoud", value: 1 },
                    { name: "Tariq", value: 1 },
                    { name: "Shareef", value: 1 },
                    { name: "Tom", value: 41 },
                    { name: "Forest", value: 2 },
                    { name: "John", value: 84 },
                    { name: "Alex", value: 11 },
                    { name: "Donald", value: 7 },
                    { name: "Mark", value: 29 },
                    { name: "Charles", value: 20 },
                    { name: "Quincy", value: 5 },
                    { name: "Alvan", value: 1 },
                    { name: "Don", value: 32 },
                    { name: "Hassan", value: 2 },
                    { name: "Jordan", value: 8 },
                    { name: "Michael", value: 32 },
                    { name: "Steven", value: 5 },
                    { name: "Rafael", value: 2 },
                    { name: "Rick", value: 12 },
                ]
            }
        }
    }

    render () {
        return (
            <div>
                <h2>Bubble Chart</h2>
                <Chart
                    type={"bubble"}
                    diameter={500}
                    showTooltips={true}
                    data={this.state.data}
                />
            </div>
        );
    }
}

class SomeCustomChart {
    constructor(el, props) {
        this.el = el;
        this.props = props;
    }

    getColor() {
        return d3.scaleOrdinal(d3.schemeCategory20c);
    }

    create(data) {
        const width = 400;
        const height = 400;

        const color = this.getColor();

        const radius = Math.min(width, height) / 2;
        const halfWidth = width / 2;
        const halfHeight = height / 2;

        const arc = d3.arc()
            .outerRadius(radius - 10)
            .innerRadius(0);

        const pie = d3.pie()
            .sort(null)
            .value(d => { return d.value; });

        const svg = d3.select(this.el).append("svg")
            .attr("width", width)
            .attr("height", height)
            .append("g")
                .attr("transform", `translate(${halfWidth}, ${halfHeight})`);

        const path = svg.selectAll("path")
            .data(pie(d3.entries(data)))
            .enter().append("path");

        path
            .attr("fill", (_d, i) => { return color(i); })
            .attr("d", arc);
    }

    update() {
        // We don't want to do anything with
        // updates in this instance.
    }

    unmount() {
        this.el.remove();
    }
}

class ExampleCustomChart extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            data: {
                "React": 2,
                "Relay": 12,
                "GraphQL": 5,
                "Radium": 7,
                "Babel": 5,
            }
        };
    }

    render() {
        return (
            <div>
                <h2>Custom Chart</h2>
                <Chart
                    type={"custom"}
                    customChart={SomeCustomChart}
                    data={this.state.data}
                />
            </div>
        );
    }

}

class App extends React.Component {
    render() {
        return (
            <div>
                <ExampleBarChart />
                <ExamplePieChart />
                <ExampleDonutChart />
                <ExampleBubbleChart />
                <ExampleCustomChart />
            </div>
        );
    }
}

render(<App />, document.getElementById("app"));


================================================
FILE: examples/index.html
================================================
<!doctype html>
<html>
<head>
    <title>d3act Examples</title>
</head>
<body>
    <h1>d3act Examples</h1>
    <div id="app"></div>
    <script src="app.js"></script>
</body>
</html>


================================================
FILE: examples/webpack.config.js
================================================
var path = require("path");
var webpack = require("webpack");

module.exports = {
    cache: true,
    entry: {
        app: "./examples/app.jsx"
    },
    output: {
        path: path.join(__dirname),
        publicPath: "/",
        filename: "[name].js",
        chunkFilename: "[chunkhash].js"
    },
    module: {
        loaders: [
            {
                test: /\.jsx$/,
                loader: "babel-loader?stage=0"
            }
        ]
    }
};


================================================
FILE: package.json
================================================
{
  "name": "d3act",
  "version": "3.1.0",
  "description": "Visualising data using d3 with React.js",
  "main": "./lib/components/Chart.js",
  "scripts": {
    "build": "babel src --out-dir lib",
    "prepublish": "npm run build",
    "examples": "npm run build && webpack-dev-server --config examples/webpack.config.js --no-info --content-base examples/"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/AnSavvides/d3act.git"
  },
  "keywords": [
    "d3",
    "React",
    "visualisations",
    "data",
    "svg"
  ],
  "author": "Andreas Savvides",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/AnSavvides/d3act/issues"
  },
  "homepage": "https://github.com/AnSavvides/d3act#readme",
  "dependencies": {
    "d3": "^4.2.2",
    "react": "^15.3.1",
    "react-dom": "^15.3.1"
  },
  "devDependencies": {
    "babel": "^5.6.23",
    "babel-core": "^5.6.15",
    "babel-loader": "^5.2.2",
    "babel-eslint": "^3.1.23",
    "eslint": "^0.24.0",
    "eslint-plugin-react": "^3.2.3",
    "grunt-eslint": "^16.0.0",
    "grunt": "^0.4.5",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-webpack": "^1.0.10",
    "load-grunt-tasks": "^3.2.0",
    "node-libs-browser": "^0.5.2",
    "webpack": "^1.10.0",
    "webpack-dev-server": "^1.10.1"
  }
}


================================================
FILE: src/components/BarChart.js
================================================
import * as d3 from "d3";
import BaseChart from "./BaseChart";

export default class BarChart extends BaseChart {
    getScaleX() {
        return d3.scaleBand().range([0, this.props.width], 0.1);
    }

    getScaleY() {
        return d3.scaleLinear().range([this.props.height, 0]);
    }

    createAxisX(x) {
        return d3.axisBottom(x);
    }

    createAxisY(y) {
        return d3.axisLeft(y);
    }

    onMouseOver(d) {
        return this.tooltip
            .style("visibility", "visible")
            .text(`${d.xValue} (${d.yValue})`);
    }

    create(data) {
        this.x = this.getScaleX();
        this.y = this.getScaleY();

        const xAxis = this.createAxisX(this.x);
        const yAxis = this.createAxisY(this.y);

        const width = this.props.width + this.props.margin.left + this.props.margin.right;
        const height = this.props.height + this.props.margin.top + this.props.margin.bottom;

        this.svg = d3.select(this.el).append("svg")
            .attr("width", width)
            .attr("height", height)
            .append("g")
                .attr("transform", `translate(${this.props.margin.left}, ${this.props.margin.top})`);

        this.x.domain(data.map(d => { return d.xValue; }));
        this.y.domain([0, d3.max(data, d => { return d.yValue; })]);

        this.svg.append("g")
            .attr("class", "x axis")
            .attr("transform", `translate(0, ${this.props.height})`)
            .call(xAxis);

        this.svg.append("g")
            .attr("class", "y axis")
            .call(yAxis)
        .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 6)
            .attr("dy", ".71em")
            .style("text-anchor", "end");

        this.svg.selectAll(".bar")
            .data(data)
        .enter().append("rect")
            .attr("class", "bar")
            .attr("x", d => { return this.x(d.xValue); })
            .attr("width", this.x.bandwidth())
            .attr("y", d => { return this.y(d.yValue); })
            .attr("height", d => { return this.props.height - this.y(d.yValue); })
            .on("mouseover", this.onMouseOver.bind(this))
            .on("mousemove", this.onMouseMove.bind(this))
            .on("mouseout", this.onMouseOut.bind(this))
            .style("fill", "steelblue");

        this.svg.selectAll("path")
            .style("fill", "none")
            .style("stroke", "#000")
            .style("shape-rendering", "crispEdges");

        if (this.showTooltips) {
            this.addTooltips();
        }
    }

    update(data) {
        // Recalculate domain given new data
        this.y.domain([0, d3.max(data, d => { return d.yValue; })]);
        this.x.domain(data.map(d => { return d.xValue; }));

        // We now have an updated Y axis
        const updatedAxisY = this.createAxisY(this.y);
        const updatedAxisX = this.createAxisX(this.x);

        // Let's update the x & y axis
        this.svg.selectAll("g.y.axis").call(updatedAxisY);
        this.svg.selectAll("g.x.axis").call(updatedAxisX);

        this.svg.selectAll(".bar")
            .data(data)
        .enter().append("rect")
            .on("mouseover", this.onMouseOver.bind(this))
            .on("mousemove", this.onMouseMove.bind(this))
            .on("mouseout", this.onMouseOut.bind(this))
            .style("fill", "steelblue");

        this.svg.selectAll("rect")
            .data(data)
            .transition().duration(this.transitionDuration)
                .attr("class", "bar")
                .attr("y", d => { return this.y(d.yValue); })
                .attr("height", d => { return this.props.height - this.y(d.yValue); })
                .attr("x", d => { return this.x(d.xValue); })
                .attr("width", this.x.bandwidth());
    }
}


================================================
FILE: src/components/BaseChart.js
================================================
import * as d3 from "d3";

const chartConfig = {
    showTooltips: true,
    transitionDuration: 1000,
    innerRadius: 0,
    showLegend: false
};

export default class BaseChart {
    constructor(el, props) {
        this.el = el;
        this.props = props;
        this.color = this.getColor();

        Object.keys(chartConfig).forEach(configKey => {
            // If a prop is defined, let's just use it, otherwise
            // fall back to the default.
            if (this.props[configKey] !== undefined) {
                this[configKey] = this.props[configKey];
            } else {
                this[configKey] = chartConfig[configKey];
            }
        });
    }

    // Overwrite this function to apply your own color scheme
    getColor() {
        return d3.scaleOrdinal(d3.schemeCategory20c);
    }

    // We don't show tooltips by default
    addTooltips() {
        this.tooltip = d3.select(this.el)
            .append("div")
            .classed("d3act-tooltip", true)
            .style("position", "absolute")
            .style("z-index", "10")
            .style("visibility", "hidden")
            .style("border", "1px solid grey")
            .style("border-radius", "3px")
            .style("text-align", "center")
            .style("padding", "8px 0")
            .style("width", "100px")
            .style("background-color", "#000")
            .style("color", "#FFF");
    }

    onMouseMove() {
        if (!this.showTooltips) {
            return;
        }

        const top = (d3.event.pageY - 10);
        const left = (d3.event.pageX + 10);

        this.tooltip
            .style("top", `${top}px`)
            .style("left", `${left}px`);
    }

    onMouseOut() {
        if (!this.showTooltips) {
            return;
        }

        this.tooltip.style("visibility", "hidden");
    }

    // Overwrite this function to apply your own removal logic
    unmount() {
        this.el.remove();
    }

    create() {
        // To be implemented by class extending BaseChart.
        // `data` is passed as an argument to this function.
    }

    update() {
        // To be implemented by class extending BaseChart.
        // `data` is passed as an argument to this function.
    }
}


================================================
FILE: src/components/BubbleChart.js
================================================
import * as d3 from "d3";
import BaseChart from "./BaseChart";

export default class BubbleChart extends BaseChart {
    addText() {
        this.text = this.node.append("text")
            .attr("dy", ".3em")
            .attr("class", "bubble-text")
            .style("text-anchor", "middle")
            .style("pointer-events", "none")
            .text(this.setText);
    }

    setText(node) {
        if (node.data.value > 15) { return node.data.name; }
    }

    onMouseOver(node) {
        return this.tooltip
            .style("visibility", "visible")
            .text(`${node.data.name} (${node.data.value})`);
    }

    create(data) {
        this.bubble = d3.pack()
            .size([this.props.diameter, this.props.diameter])
            .padding(1.5);

        this.root = d3.hierarchy(data).sum(nodeData => nodeData.value);

        this.svg = d3.select(this.el).append("svg")
            .attr("width", this.props.diameter)
            .attr("height", this.props.diameter)
            .attr("class", "bubble");

        this.node = this.svg.selectAll(".node")
            .data(this.bubble(this.root).children)
            .enter().append("g")
                .attr("class", "node")
                .attr("transform", d => { return `translate(${d.x}, ${d.y})`; });

        this.node.append("circle")
            .attr("r", node => { return node.r; })
            .style("fill", node => { return this.color(node.data.name); })
            .on("mouseover", this.onMouseOver.bind(this))
            .on("mousemove", this.onMouseMove.bind(this))
            .on("mouseout", this.onMouseOut.bind(this));

        d3.select(document.frameElement).style("height", `${this.props.diameter} px`);

        this.addText();

        if (this.showTooltips) {
            this.addTooltips();
        }
    }

    update(data) {
        const formattedData = this.bubble(this.root).children;

        this.node = this.svg.selectAll(".node")
            .data(formattedData);

        const nodeEnter = this.node.enter()
            .append("g")
            .attr("class", "node")
            .attr("transform", d => { return `translate(${d.x}, ${d.y})`; });

        nodeEnter
            .append("circle")
            .attr("r", d => { return d.r; })
            .style("fill", (d, i) => { return this.color(i); })
            .on("mouseover", this.onMouseOver.bind(this))
            .on("mousemove", this.onMouseMove.bind(this))
            .on("mouseout", this.onMouseOut.bind(this));

        this.text
            .data(formattedData)
            .text(this.setText);

        this.node.select("circle")
            .transition().duration(this.transitionDuration)
            .attr("r", d => {
                return d.r;
            })
            .style("fill", (d, i) => {
                return this.color(i);
            });

        this.node.transition().attr("class", "node")
            .attr("transform", d => {
                return `translate(${d.x}, ${d.y})`;
            });

        this.node.exit().remove();
    }
}


================================================
FILE: src/components/Chart.js
================================================
import React from "react";
import { findDOMNode } from "react-dom";

import BubbleChart from "./BubbleChart";
import BarChart from "./BarChart";
import PieChart from "./PieChart";

export default class Chart extends React.Component {
    constructor(props) {
        super(props);

        this.chartToClassMappings = {
            bubble: BubbleChart,
            bar: BarChart,
            pie: PieChart
        };
    }

    componentDidMount() {
        if (Object.keys(this.props.data).length === 0) {
            return;
        }

        const el = findDOMNode(this);

        if (this.props.type === "custom") {
            this.chart = new this.props.customChart(el, this.props);
        } else {
            this.chart = new this.chartToClassMappings[this.props.type](el, this.props);
        }

        this.chart.create(this.props.data);
    }

    componentDidUpdate() {
        this.chart.update(this.props.data);
    }

    componentWillUnmount() {
        this.chart.unmount();
    }

    render() {
        return (<div className="chart"></div>);
    }
}


================================================
FILE: src/components/PieChart.js
================================================
import * as d3 from "d3";
import BaseChart from "./BaseChart";

export default class PieChart extends BaseChart {

    // d3's default transition does not interpolate pie chart
    // segments correctly; all intermediate `d` values for
    // the `path`s are invalid, so will throw errors.
    // What we are doing here is specifying to d3 how to
    // correctly generate all those previously invalid
    // values.
    arcTween(a) {
        var interpolated = d3.interpolate(this.originalAngles, a);
        this.originalAngles = interpolated(0);

        return t => {
            return this.arc(interpolated(t));
        };
    }

    onMouseOver(d) {
        return this.tooltip
            .style("visibility", "visible")
            .text(`${d.data.key} (${d.data.value})`);
    }

    addLegend() {
        this.legend = this.svg.selectAll(".d3act-legend")
            .data(this.color.domain())
            .enter()
            .append("g")
            .attr("class", "d3act-legend")
            .attr("transform", (d, i) => {
                const height = this.legendRectSize + this.legendSpacing;
                const offset = 20;
                const horz = -2 * this.legendRectSize;
                const vert = (this.props.height / 2) + (i * height) + offset;

                return "translate(" + horz + "," + vert + ")";
            });

        this.legend.append("rect")
            .attr("width", this.legendRectSize)
            .attr("height", this.legendRectSize)
            .style("fill", this.color)
            .style("stroke", this.color);

        this.legend.append("text")
            .attr("x", this.legendRectSize + this.legendSpacing)
            .attr("y", this.legendRectSize - this.legendSpacing)
            .text(d => {
                const name = Object.keys(this.data)[d];
                const value = this.data[name];

                return `${name} (${value})`;
            });
    }

    getHeight(numberOfItems) {
        if (this.showLegend) {
            const legendRectExtraHeight = (numberOfItems * this.legendRectSize);
            const legendSpacingExtraHeight = (numberOfItems * this.legendSpacing);

            return (this.props.height * 1.5) + legendSpacingExtraHeight + legendRectExtraHeight;
        }

        return this.props.height;
    }

    create(data) {
        this.legendRectSize = 18;
        this.legendSpacing = 4;
        this.data = data;

        const numberOfItems = Object.keys(data).length;

        const width = this.props.width;
        const height = this.props.height;
        const svgHeight = this.getHeight(numberOfItems);
        const radius = Math.min(width, height) / 2;
        const halfWidth = width / 2;
        const halfHeight = height / 2;

        this.arc = d3.arc()
            .outerRadius(radius - 10)
            .innerRadius(this.innerRadius);

        this.pie = d3.pie()
            .sort(null)
            .value(d => { return d.value; });

        this.svg = d3.select(this.el).append("svg")
            .attr("width", width)
            .attr("height", svgHeight)
            .append("g")
                .attr("transform", `translate(${halfWidth}, ${halfHeight})`);

        this.path = this.svg.selectAll("path")
            .data(this.pie(d3.entries(data)))
            .enter().append("path");

        this.path
            .attr("fill", (d, i) => { return this.color(i); })
            .attr("d", this.arc)
            .each(d => {
                // Let's keep a reference to the
                // original angles to make use of
                // in our arcTween helper.
                this.originalAngles = d;
            })
            .on("mouseover", this.onMouseOver.bind(this))
            .on("mousemove", this.onMouseMove.bind(this))
            .on("mouseout", this.onMouseOut.bind(this));

        if (this.showTooltips) {
            this.addTooltips();
        }

        if (this.showLegend) {
            this.addLegend();
        }
    }

    update(data) {
        this.path
            .data(this.pie(d3.entries(data)))
            .transition().duration(this.transitionDuration)
            .attrTween("d", this.arcTween.bind(this));

        this.path
            .data(this.pie(d3.entries(data)))
            .enter().append("path")
            .attr("fill", (d, i) => { return this.color(i); })
            .attr("d", this.arc)
            .each(d => {
                // Let's keep a reference to the
                // original angles to make use of
                // in our arcTween helper.
                this.originalAngles = d;
            })
            .on("mouseover", this.onMouseOver.bind(this))
            .on("mousemove", this.onMouseMove.bind(this))
            .on("mouseout", this.onMouseOut.bind(this));
    }
}
Download .txt
gitextract_jf5houkv/

├── .eslintrc
├── .gitignore
├── .jshintrc
├── .npmignore
├── Gruntfile.js
├── LICENSE
├── README.md
├── examples/
│   ├── app.jsx
│   ├── index.html
│   └── webpack.config.js
├── package.json
└── src/
    └── components/
        ├── BarChart.js
        ├── BaseChart.js
        ├── BubbleChart.js
        ├── Chart.js
        └── PieChart.js
Download .txt
SYMBOL INDEX (60 symbols across 6 files)

FILE: examples/app.jsx
  class ExampleBarChart (line 7) | class ExampleBarChart extends React.Component {
    method constructor (line 9) | constructor(props) {
    method componentDidMount (line 23) | componentDidMount() {
    method render (line 37) | render() {
  class ExamplePieChart (line 54) | class ExamplePieChart extends React.Component {
    method constructor (line 56) | constructor(props) {
    method render (line 70) | render() {
  class ExampleDonutChart (line 87) | class ExampleDonutChart extends React.Component {
    method constructor (line 89) | constructor(props) {
    method render (line 103) | render() {
  class ExampleBubbleChart (line 122) | class ExampleBubbleChart extends React.Component {
    method constructor (line 124) | constructor(props) {
    method render (line 157) | render () {
  class SomeCustomChart (line 172) | class SomeCustomChart {
    method constructor (line 173) | constructor(el, props) {
    method getColor (line 178) | getColor() {
    method create (line 182) | create(data) {
    method update (line 215) | update() {
    method unmount (line 220) | unmount() {
  class ExampleCustomChart (line 225) | class ExampleCustomChart extends React.Component {
    method constructor (line 227) | constructor(props) {
    method render (line 241) | render() {
  class App (line 256) | class App extends React.Component {
    method render (line 257) | render() {

FILE: src/components/BarChart.js
  class BarChart (line 4) | class BarChart extends BaseChart {
    method getScaleX (line 5) | getScaleX() {
    method getScaleY (line 9) | getScaleY() {
    method createAxisX (line 13) | createAxisX(x) {
    method createAxisY (line 17) | createAxisY(y) {
    method onMouseOver (line 21) | onMouseOver(d) {
    method create (line 27) | create(data) {
    method update (line 83) | update(data) {

FILE: src/components/BaseChart.js
  class BaseChart (line 10) | class BaseChart {
    method constructor (line 11) | constructor(el, props) {
    method getColor (line 28) | getColor() {
    method addTooltips (line 33) | addTooltips() {
    method onMouseMove (line 49) | onMouseMove() {
    method onMouseOut (line 62) | onMouseOut() {
    method unmount (line 71) | unmount() {
    method create (line 75) | create() {
    method update (line 80) | update() {

FILE: src/components/BubbleChart.js
  class BubbleChart (line 4) | class BubbleChart extends BaseChart {
    method addText (line 5) | addText() {
    method setText (line 14) | setText(node) {
    method onMouseOver (line 18) | onMouseOver(node) {
    method create (line 24) | create(data) {
    method update (line 58) | update(data) {

FILE: src/components/Chart.js
  class Chart (line 8) | class Chart extends React.Component {
    method constructor (line 9) | constructor(props) {
    method componentDidMount (line 19) | componentDidMount() {
    method componentDidUpdate (line 35) | componentDidUpdate() {
    method componentWillUnmount (line 39) | componentWillUnmount() {
    method render (line 43) | render() {

FILE: src/components/PieChart.js
  class PieChart (line 4) | class PieChart extends BaseChart {
    method arcTween (line 12) | arcTween(a) {
    method onMouseOver (line 21) | onMouseOver(d) {
    method addLegend (line 27) | addLegend() {
    method getHeight (line 59) | getHeight(numberOfItems) {
    method create (line 70) | create(data) {
    method update (line 124) | update(data) {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (36K chars).
[
  {
    "path": ".eslintrc",
    "chars": 179,
    "preview": "{\n \"parser\": \"babel-eslint\",\n  \"rules\": {\n    \"strict\": 0,\n    \"global-strict\": 0\n  },\n  \"plugins\": [\n    \"react\"\n  ],\n "
  },
  {
    "path": ".gitignore",
    "chars": 523,
    "preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nl"
  },
  {
    "path": ".jshintrc",
    "chars": 429,
    "preview": "{\n  \"node\": true,\n  \"browser\": true,\n  \"esnext\": true,\n  \"bitwise\": true,\n  \"camelcase\": false,\n  \"curly\": true,\n  \"eqeq"
  },
  {
    "path": ".npmignore",
    "chars": 515,
    "preview": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nl"
  },
  {
    "path": "Gruntfile.js",
    "chars": 1417,
    "preview": "\"use strict\";\n\nmodule.exports = function (grunt) {\n    require(\"load-grunt-tasks\")(grunt);\n\n    var pkgConfig = grunt.fi"
  },
  {
    "path": "LICENSE",
    "chars": 1082,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Andreas Savvides\n\nPermission is hereby granted, free of charge, to any person "
  },
  {
    "path": "README.md",
    "chars": 5647,
    "preview": "# d3act - d3 with React\n\n## Background\n\n[`d3`](http://d3js.org/) has been the de facto standard when it comes to data vi"
  },
  {
    "path": "examples/app.jsx",
    "chars": 6666,
    "preview": "import * as d3 from \"d3\";\nimport React from \"react\";\nimport { render } from \"react-dom\";\n\nimport Chart from \"../lib/comp"
  },
  {
    "path": "examples/index.html",
    "chars": 183,
    "preview": "<!doctype html>\n<html>\n<head>\n    <title>d3act Examples</title>\n</head>\n<body>\n    <h1>d3act Examples</h1>\n    <div id=\""
  },
  {
    "path": "examples/webpack.config.js",
    "chars": 465,
    "preview": "var path = require(\"path\");\nvar webpack = require(\"webpack\");\n\nmodule.exports = {\n    cache: true,\n    entry: {\n        "
  },
  {
    "path": "package.json",
    "chars": 1289,
    "preview": "{\n  \"name\": \"d3act\",\n  \"version\": \"3.1.0\",\n  \"description\": \"Visualising data using d3 with React.js\",\n  \"main\": \"./lib/"
  },
  {
    "path": "src/components/BarChart.js",
    "chars": 3802,
    "preview": "import * as d3 from \"d3\";\nimport BaseChart from \"./BaseChart\";\n\nexport default class BarChart extends BaseChart {\n    ge"
  },
  {
    "path": "src/components/BaseChart.js",
    "chars": 2243,
    "preview": "import * as d3 from \"d3\";\n\nconst chartConfig = {\n    showTooltips: true,\n    transitionDuration: 1000,\n    innerRadius: "
  },
  {
    "path": "src/components/BubbleChart.js",
    "chars": 3049,
    "preview": "import * as d3 from \"d3\";\nimport BaseChart from \"./BaseChart\";\n\nexport default class BubbleChart extends BaseChart {\n   "
  },
  {
    "path": "src/components/Chart.js",
    "chars": 1073,
    "preview": "import React from \"react\";\nimport { findDOMNode } from \"react-dom\";\n\nimport BubbleChart from \"./BubbleChart\";\nimport Bar"
  },
  {
    "path": "src/components/PieChart.js",
    "chars": 4782,
    "preview": "import * as d3 from \"d3\";\nimport BaseChart from \"./BaseChart\";\n\nexport default class PieChart extends BaseChart {\n\n    /"
  }
]

About this extraction

This page contains the full source code of the AnSavvides/d3act GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (32.6 KB), approximately 8.4k tokens, and a symbol index with 60 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!