Skip to content

Instantly share code, notes, and snippets.

@YoungElPaso
Created February 28, 2017 19:37
Show Gist options
  • Save YoungElPaso/e1ac3e0c4db62a806a0fbfb63109c816 to your computer and use it in GitHub Desktop.
Save YoungElPaso/e1ac3e0c4db62a806a0fbfb63109c816 to your computer and use it in GitHub Desktop.
D3 Demos // source https://jsbin.com/quwokus
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>D3 Demos</title>
<script src="https://fb.me/react-15.1.0.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.min.js"></script>
<script src="https://fb.me/react-dom-15.1.0.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.string/3.3.4/underscore.string.js"></script>
<link rel="stylesheet" href="https://unpkg.com/tachyons@4.6.1/css/tachyons.min.css"/>
<style id="jsbin-css">
h1, h2, h3, h4, h5, h6, p {
font-family: sans-serif;
}
h1, h2, h3, h4, h5, h6, div, p, nav, section, hr {
/* margin: 0 0 1em 0; */
margin: 0 0 10px 0;
}
nav, section, div, p {
/* padding: 0.3em; */
padding: 10px;
}
/* Stealing from Tachyon, for svg */
.bg-blue-svg {
fill: #357EDD;
}
svg path:hover,
svg path.selected {
opacity: 1;
stroke: white;
stroke-width: 2px;
}
</style>
</head>
<body>
<h1>D3 Demos</h1>
<div id="app"> Here was pie </div>
<script id="jsbin-javascript">
// Some default data.
'use strict';
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var data = {
title: 'Some Data',
desc: 'Maps values to color on \'magma \' scale and maps values to a font-size on a linear scale.',
values: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
};
var textNumData = {
title: 'Text & Number',
desc: 'This illustrates how numbers and basic text can go together.',
values: [50, 100, 500],
label: 'we\'ve grown in leaps and bounds!'
};
var donationData = {
title: 'Donations 2016',
desc: 'Shows donation totals in a dynamic way, with the total highlighted along with a basic label or description that can support HTML.',
values: [1000],
label: 'in <b>donations</b> raised by McGill U in 2016.<br/ > More on the way!',
symbol: '$',
// TODO: have a CTA available?
cta: null
};
var stepCountData = {
title: 'Step Count Today',
desc: 'Shows a simple step counter.',
values: [1700, 1000]
};
var pieData = {
title: 'Academic Community',
desc: 'A pie chart that shows the demographic breakdown of the academic community at McGill',
values: [{ 'label': 'Undergraduate', 'number': 10000 }, { 'label': 'Post-graduate', 'number': 3000 }, { 'label': 'Graduate', 'number': 8000 }, { 'label': 'Other', 'number': 800 }, { 'label': 'Faculty', 'number': 1500 }, { 'label': 'TA\'s', 'number': 7000 }]
};
var percentageData = {
title: 'Advanced preparation!',
label: '55% of McGill undergraduates will go on to get a PhD. More than any other Canadian university.',
values: [55],
desc: 'Number of undergraduate students who later in a PhD'
};
// TODO : style this component (text to the left or right or bottom of % and big label, see one of those xample websites Joyce sent.)
// TODO: put this stuff in a document and put that in the ticket.
var Charts = (function (_React$Component) {
_inherits(Charts, _React$Component);
function Charts(props) {
_classCallCheck(this, Charts);
_get(Object.getPrototypeOf(Charts.prototype), 'constructor', this).call(this, props);
this.state = {
data: data
};
}
// A generic text based infographic class.
// Renders some text w/ d3js in a fun way.
// Takes data, colors text based on values.
// This component doesn't even need to use SVG
// But it could!
// Handles loading the data and viz.
_createClass(Charts, [{
key: 'componentDidMount',
value: function componentDidMount() {}
}, {
key: 'render',
value: function render() {
return React.createElement(
'div',
{ className: 'all-charts-render' },
React.createElement(StepCount, { type: 'Text Number svg infographic', data: stepCountData }),
React.createElement(Percentage, { type: 'Percentage', data: percentageData }),
React.createElement(Pie, { type: 'Pie', data: pieData }),
React.createElement(Bar, { type: 'Bar', data: this.state.data }),
React.createElement(GenText, { type: 'Text infographic', data: this.state.data }),
React.createElement(TextNumber, { type: 'Text Number infographic', data: textNumData }),
React.createElement(Donation, { type: 'Donation', data: donationData })
);
}
}]);
return Charts;
})(React.Component);
var GenText = (function (_React$Component2) {
_inherits(GenText, _React$Component2);
function GenText(props) {
_classCallCheck(this, GenText);
_get(Object.getPrototypeOf(GenText.prototype), 'constructor', this).call(this, props);
// Set up a font scale for all text components ot have.
var scale = d3.scaleLinear();
scale.domain([this.props.data.values[0], this.props.data.values[this.props.data.values.length - 1]]);
// A reasonable font-size scale range.
scale.range([16, 60]);
this.fontScale = scale;
}
// A generic chart class.
// Handles loading the data and viz.
_createClass(GenText, [{
key: 'componentDidMount',
value: function componentDidMount() {
var fontScale = this.fontScale;
// TODO: get a pie-chart or some other graph going.
// console.log(this.props.type);
// Fire up d3.
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.selectAll('div').data(this.props.data.values).enter().append('div').text(function (d) {
return d;
})
// Enhance the font-size based on value.
.style('font-size', function (d) {
// Run the values through scale, any number will be between scale.range value.
return fontScale(d) + "px";
}) // Enhance the color based on value.
.style('background-color', function (d) {
// Try out D3 color interpolation...
var color = d3.interpolateInferno(100);
return color;
}).style('color', function (d) {
// Try out D3 color interpolation...
// Need rainbow to offset inferno.
var color = d3.interpolateRainbow(d / 100);
return color;
}).classed('value', true);
}
// Renders the basic markup
}, {
key: 'render',
value: function render() {
var scale = d3.scaleLinear();
scale.domain([this.props.data.values[0], this.props.data.values[this.props.data.values.length - 1]]);
// A reasonable font-size scale range.
scale.range([16, 60]);
return React.createElement(
'div',
{ className: 'gen-text-render bg-mid-gray ' + s.slugify(this.props.type) },
React.createElement(
'h2',
{ className: 'hot-pink' },
this.props.type,
' Text: ',
React.createElement(
'span',
{ className: 'dark-pink' },
this.props.data.title
),
' '
),
React.createElement('div', { className: 'text-info light-gray' }),
React.createElement(
'div',
null,
this.props.data.desc || this.props.data.label
)
);
}
}]);
return GenText;
})(React.Component);
var GenChart = (function (_React$Component3) {
_inherits(GenChart, _React$Component3);
function GenChart(props) {
_classCallCheck(this, GenChart);
_get(Object.getPrototypeOf(GenChart.prototype), 'constructor', this).call(this, props);
// Set up a basic scale for chart.
var scale = d3.scaleLinear();
scale.domain([0, 10000]);
// A reasonable font-size scale range.
scale.range([0, 100]);
this.scale = scale;
}
// A pie chart.
// Handles loading the data and viz.
_createClass(GenChart, [{
key: 'componentDidMount',
value: function componentDidMount() {}
// Renders the basic markup
}, {
key: 'render',
value: function render() {
return React.createElement(
'div',
{ className: 'gen-chart-render bg-mid-gray ' + s.slugify(this.props.type) },
React.createElement(
'h2',
null,
this.props.type,
' Chart: ',
this.props.data.title,
' '
),
React.createElement('div', { className: 'chart' })
);
}
}]);
return GenChart;
})(React.Component);
var Pie = (function (_GenChart) {
_inherits(Pie, _GenChart);
function Pie() {
_classCallCheck(this, Pie);
_get(Object.getPrototypeOf(Pie.prototype), 'constructor', this).apply(this, arguments);
}
// A bar chart.
_createClass(Pie, [{
key: 'componentDidMount',
value: function componentDidMount() {
var scale = this.scale;
// Need to convert number to scale on 0-1.
var fillScale = d3.scaleLinear();
fillScale.domain([0, 15000]);true;
fillScale.range([0, 1]);
var selector = '.' + s.slugify(this.props.type) + ' .chart';
var chart = d3.select(selector);
chart.classed(' tc bg-light-blue cf', true);
var svg = chart.append('svg').attr('width', 200).attr('height', 200);
// .classed('cf', true);
svg.selectAll('path').data(this.props.data.values).enter();
// Use d3 pie utility to construct an arc made of pie segments.
var pie = d3.pie();
var arcs = pie.value(function (d) {
return d.number;
})(this.props.data.values);
var arc = d3.arc().outerRadius(100).padAngle(0.03).innerRadius(50);
arcs.forEach(function (d, i) {
var data = d.data;
svg.append('path').attr('d', arc(d)).classed('centersvg ' + s.slugify(data.label), true).attr('transform', 'translate(100, 100)').attr('opacity', 0.85).attr('fill', function (data) {
var newD = fillScale(d.value);
var color = d3.interpolatePlasma(newD);
return color;
});
});
// Sort the labels according to biggest number value.
var sorted = this.props.data.values.sort(function (a, b) {
return a.number < b.number;
});
// TODO: need to add some labels or a legend!
svg.exit();
// Lets try a legend!
chart.append('div').classed('cf w-100', true);
chart.selectAll('div').select('.w-100').data(sorted).enter().append('div').classed('f6 dib mr1 white b grow', true).text(function (d) {
return d.label;
}).style('background-color', function (d) {
return d3.interpolatePlasma(fillScale(d.number));
}).on('mouseover', function (e) {
var pathC = s.slugify(this.__data__.label);
var p = chart.select('path.' + pathC)
// .attr('opacity', 1)
// .attr('stroke', 'white')
// .attr('stroke-width', 2);
.classed('selected', true);
}).on('mouseout', function (e) {
var pathC = s.slugify(this.__data__.label);
var p = chart.select('path.' + pathC)
// .attr('opacity', 0.8)
// .attr('stroke-width', 0);
.classed('selected', false);
});
}
}]);
return Pie;
})(GenChart);
var Bar = (function (_GenChart2) {
_inherits(Bar, _GenChart2);
function Bar() {
_classCallCheck(this, Bar);
_get(Object.getPrototypeOf(Bar.prototype), 'constructor', this).apply(this, arguments);
}
// A special statement type of text-based viz.
_createClass(Bar, [{
key: 'componentDidMount',
value: function componentDidMount() {}
}]);
return Bar;
})(GenChart);
var TextNumber = (function (_GenText) {
_inherits(TextNumber, _GenText);
function TextNumber() {
_classCallCheck(this, TextNumber);
_get(Object.getPrototypeOf(TextNumber.prototype), 'constructor', this).apply(this, arguments);
}
// A special donation centric text viz.
_createClass(TextNumber, [{
key: 'componentDidMount',
value: function componentDidMount() {
// Load the generic fontScale.
var fontScale = this.fontScale;
// Change the range a bit.
fontScale.range([24, 100]);
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.classed('tc bg-blue white pt3 br3 hide-child hover-bg-dark-blue', true);
textBox.selectAll('div').data(this.props.data.values).enter().append('div').text(function (d) {
return d;
})
// Enhance the font-size based on value.
.style('font-size', function (d) {
// var d = d > 100 ? 80 : d; // replace this with scale.
var d = fontScale(d);
return d + "px";
}).style('color', function (d) {
// Need to convert number to scale on 0-1.
var d = Math.log(d) / 10;
// Try out D3 color interpolation...
var color = d3.interpolateWarm(d);
return color;
}).classed('garamond fw9 underline', true);
// Add the description.
textBox.append('div').text(this.props.data.label || this.props.data.desc).classed('child f2', true);
}
}]);
return TextNumber;
})(GenText);
var Donation = (function (_GenText2) {
_inherits(Donation, _GenText2);
function Donation() {
_classCallCheck(this, Donation);
_get(Object.getPrototypeOf(Donation.prototype), 'constructor', this).apply(this, arguments);
}
// A big fat juicy percentage!
_createClass(Donation, [{
key: 'componentDidMount',
value: function componentDidMount() {
// Load the symbol from the data.
var sym = this.props.data.symbol;
// Load the generic fontScale.
var fontScale = this.fontScale;
// Changing default range, want bigger fonts, not as much variation.
fontScale.range([80, 100]);
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.classed(' tc bg-dark-gray hover-bg-dark-green', true);
textBox.selectAll('div').data(this.props.data.values).enter().append('div').text(function (d) {
return sym + d;
})
// Enhance the font-size based on value.
.style('font-size', function (d) {
var d = fontScale(d);
return d + "px";
}).style('color', function (d) {
// Need to convert number to scale on 0-1.
var d = Math.log(d) / 10;
// Try out D3 color interpolation...
var color = d3.interpolateMagma(d);
return color;
}).classed('garamond fw9', true);
// Add the description.
textBox.append('div').html(this.props.data.label || this.props.data.desc).classed('f3 garamond', true);
}
}]);
return Donation;
})(GenText);
var Percentage = (function (_GenText3) {
_inherits(Percentage, _GenText3);
function Percentage() {
_classCallCheck(this, Percentage);
_get(Object.getPrototypeOf(Percentage.prototype), 'constructor', this).apply(this, arguments);
}
// A common example of a more complex text-based viz.
_createClass(Percentage, [{
key: 'componentDidMount',
value: function componentDidMount() {}
}]);
return Percentage;
})(GenText);
var StepCount = (function (_GenText4) {
_inherits(StepCount, _GenText4);
function StepCount() {
_classCallCheck(this, StepCount);
_get(Object.getPrototypeOf(StepCount.prototype), 'constructor', this).apply(this, arguments);
}
// TODO:
// What other types do we want?
// Pie Chart (svg) -- done
// Big fancy percentage!
// Bar Chart (svg)
// Donations (css/html) -- kinda done
// Student body Martlett thing? (css/html?)
// Earthquake? (css/html?)
// Text pyramid? (css/html?)
// (See notes in Google photos sketch)
_createClass(StepCount, [{
key: 'componentDidMount',
value: function componentDidMount() {
// Create a good scale for the circles.
var scale = d3.scaleLinear();
scale.domain([0, 2000]);
// A reasonable font-size scale range.
scale.range([10, 100]);
var radialScale = d3.scaleLinear();
radialScale.domain([0, 2000]);
radialScale.range([0, 8]);
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.classed(' tc bg-light-blue', true);
var svg = textBox.append('svg').attr('width', 200).attr('height', 200);
svg.selectAll('path').data(this.props.data.values).enter()
// Add a circle that scales well.
.append('path')
// 'd' attribute is the SVtrueG attr. for the shape of path.
.attr('d', function (d) {
// Lets try an arc.
var arc = d3.arc();
var arcPath = arc({
innerRadius: 0,
// Need a nice scale for d...
outerRadius: scale(d),
startAngle: 0,
endAngle: radialScale(d)
});
return arcPath;
}).attr('transform', 'translate(100, 100)').attr('opacity', 0.8).attr('fill', function (d) {
// Need to convert number to scale on 0-1.
var fillScale = d3.scaleLinear();
fillScale.domain([0, 2000]);true;
fillScale.range([0, 1]);
var newD = fillScale(d);
var color = d3.interpolatePlasma(newD);
return color;
});
// Add a label?
var doSvgtext = false;
// Do the text in the svg.
if (doSvgtext == true) {
svg.selectAll('text').data(this.props.data.values).enter().append('text').text(function (d) {
return d;
}).attr('class', 'f4 Helvetica').attr('text-anchor', 'middle').attr('fill', 'white').attr('opacity', 0.75).attr('transform', function (d) {
y = scale(d);
return 'translate(100,' + (130 - y) + ')';
});
} else {
// Doing text in html/css.
textBox.selectAll('div');
textBox.append('div').text(this.props.data.values[1] + ' out of ' + this.props.data.values[0] + ' steps today!').classed('f5 Helvetica blue', true);
}
}
}]);
return StepCount;
})(GenText);
ReactDOM.render(React.createElement(Charts), document.getElementById('app'));
</script>
<script id="jsbin-source-css" type="text/css">h1,h2,h3,h4,h5,h6, p {
font-family: sans-serif;
}
h1,h2,h3,h4,h5,h6,div,p,nav,section,hr {
/* margin: 0 0 1em 0; */
margin: 0 0 10px 0;
}
nav, section, div, p {
/* padding: 0.3em; */
padding: 10px;
}
/* Stealing from Tachyon, for svg */
.bg-blue-svg {
fill: #357EDD;
}
svg path:hover,
svg path.selected {
opacity: 1;
stroke: white;
stroke-width: 2px;
}
svg .centersvg {
}</script>
<script id="jsbin-source-javascript" type="text/javascript">// Some default data.
var data = {
title: 'Some Data',
desc: 'Maps values to color on \'magma \' scale and maps values to a font-size on a linear scale.',
values: [10,20,30,40,50, 60, 70, 80, 90, 100]
}
var textNumData = {
title: 'Text & Number',
desc: 'This illustrates how numbers and basic text can go together.',
values: [50, 100, 500],
label: 'we\'ve grown in leaps and bounds!'
}
var donationData = {
title: 'Donations 2016',
desc: 'Shows donation totals in a dynamic way, with the total highlighted along with a basic label or description that can support HTML.',
values: [1000],
label: 'in <b>donations</b> raised by McGill U in 2016.<br/ > More on the way!',
symbol: '$',
// TODO: have a CTA available?
cta: null
}
var stepCountData = {
title: 'Step Count Today',
desc: 'Shows a simple step counter.',
values: [1700, 1000]
}
var pieData = {
title: 'Academic Community',
desc: 'A pie chart that shows the demographic breakdown of the academic community at McGill',
values: [
{'label':'Undergraduate', 'number': 10000},
{'label':'Post-graduate', 'number': 3000},
{'label':'Graduate', 'number': 8000},
{'label':'Other', 'number': 800},
{'label':'Faculty', 'number': 1500},
{'label':'TA\'s', 'number': 7000}
]
}
var percentageData = {
title: 'Advanced preparation!',
label: '55% of McGill undergraduates will go on to get a PhD. More than any other Canadian university.',
values: [55],
desc: 'Number of undergraduate students who later in a PhD'
}
// TODO : style this component (text to the left or right or bottom of % and big label, see one of those xample websites Joyce sent.)
// TODO: put this stuff in a document and put that in the ticket.
class Charts extends React.Component {
constructor(props) {
super(props)
this.state = {
data: data
}
}
// Handles loading the data and viz.
componentDidMount() {
}
render() {
return (
<div className="all-charts-render">
<StepCount type="Text Number svg infographic" data={stepCountData} />
<Percentage type="Percentage" data={percentageData} />
<Pie type="Pie" data={pieData} />
<Bar type="Bar" data={this.state.data} />
<GenText type="Text infographic" data={this.state.data} />
<TextNumber type="Text Number infographic" data={textNumData} />
<Donation type="Donation" data={donationData} />
</div>
)
}
}
// A generic text based infographic class.
// Renders some text w/ d3js in a fun way.
// Takes data, colors text based on values.
// This component doesn't even need to use SVG
// But it could!
class GenText extends React.Component {
constructor(props) {
super(props);
// Set up a font scale for all text components ot have.
var scale = d3.scaleLinear();
scale.domain([this.props.data.values[0], this.props.data.values[this.props.data.values.length-1]]);
// A reasonable font-size scale range.
scale.range([16, 60]);
this.fontScale = scale;
}
// Handles loading the data and viz.
componentDidMount() {
var fontScale = this.fontScale;
// TODO: get a pie-chart or some other graph going.
// console.log(this.props.type);
// Fire up d3.
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.selectAll('div')
.data(this.props.data.values)
.enter()
.append('div')
.text(function(d){
return d;
})
// Enhance the font-size based on value.
.style('font-size', function(d) {
// Run the values through scale, any number will be between scale.range value.
return fontScale(d) + "px"
})// Enhance the color based on value.
.style('background-color', function(d) {
// Try out D3 color interpolation...
var color = d3.interpolateInferno(100)
return color;
})
.style('color', function(d) {
// Try out D3 color interpolation...
// Need rainbow to offset inferno.
var color = d3.interpolateRainbow(d/100)
return color;
})
.classed('value', true);
}
// Renders the basic markup
render() {var scale = d3.scaleLinear();
scale.domain([this.props.data.values[0], this.props.data.values[this.props.data.values.length-1]]);
// A reasonable font-size scale range.
scale.range([16, 60]);
return (
<div className={'gen-text-render bg-mid-gray ' + s.slugify(this.props.type)}>
<h2 className="hot-pink">{this.props.type} Text: <span className="dark-pink">{this.props.data.title}</span> </h2>
<div className="text-info light-gray">
</div>
<div>
{this.props.data.desc || this.props.data.label}
</div>
</div>
)
}
}
// A generic chart class.
class GenChart extends React.Component {
constructor(props) {
super(props);
// Set up a basic scale for chart.
var scale = d3.scaleLinear();
scale.domain([0, 10000]);
// A reasonable font-size scale range.
scale.range([0, 100]);
this.scale = scale;
}
// Handles loading the data and viz.
componentDidMount() {
}
// Renders the basic markup
render() {
return (
<div className={'gen-chart-render bg-mid-gray ' + s.slugify(this.props.type)}>
<h2>{this.props.type} Chart: {this.props.data.title} </h2>
<div className="chart"></div>
</div>
)
}
}
// A pie chart.
class Pie extends GenChart {
componentDidMount() {
let scale = this.scale;
// Need to convert number to scale on 0-1.
let fillScale = d3.scaleLinear();
fillScale.domain([0, 15000]);true
fillScale.range([0,1]);
var selector = '.' + s.slugify(this.props.type) + ' .chart';
var chart = d3.select(selector);
chart.classed(' tc bg-light-blue cf', true);
var svg = chart.append('svg')
.attr('width', 200)
.attr('height', 200);
// .classed('cf', true);
svg.selectAll('path')
.data(this.props.data.values)
.enter();
// Use d3 pie utility to construct an arc made of pie segments.
var pie = d3.pie();
var arcs = pie.value(function(d){
return d.number;
})(this.props.data.values);
var arc = d3.arc()
.outerRadius(100)
.padAngle(0.03)
.innerRadius(50);
arcs.forEach(function(d, i){
var data = d.data;
svg.append('path')
.attr('d', arc(d))
.classed('centersvg ' + s.slugify(data.label) , true)
.attr('transform', 'translate(100, 100)')
.attr('opacity', 0.85)
.attr('fill', function(data){
var newD = fillScale(d.value);
var color = d3.interpolatePlasma(newD);
return color;
});
});
// Sort the labels according to biggest number value.
var sorted = this.props.data.values.sort(function(a,b){
return a.number < b.number;
})
// TODO: need to add some labels or a legend!
svg.exit();
// Lets try a legend!
chart.append('div').classed('cf w-100', true)
;
chart.selectAll('div').select('.w-100')
.data(sorted)
.enter()
.append('div')
.classed('f6 dib mr1 white b grow', true)
.text(function(d){
return d.label;
})
.style('background-color', function(d){
return d3.interpolatePlasma(fillScale(d.number));
})
.on('mouseover', function(e){
var pathC = s.slugify(this.__data__.label);
var p = chart.select('path.'+pathC)
// .attr('opacity', 1)
// .attr('stroke', 'white')
// .attr('stroke-width', 2);
.classed('selected', true);
})
.on('mouseout', function(e){
var pathC = s.slugify(this.__data__.label);
var p = chart.select('path.'+pathC)
// .attr('opacity', 0.8)
// .attr('stroke-width', 0);
.classed('selected', false);
})
;
}
}
// A bar chart.
class Bar extends GenChart {
componentDidMount() {
}
}
// A special statement type of text-based viz.
class TextNumber extends GenText {
componentDidMount() {
// Load the generic fontScale.
var fontScale = this.fontScale;
// Change the range a bit.
fontScale.range([24, 100]);
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.classed('tc bg-blue white pt3 br3 hide-child hover-bg-dark-blue', true);
textBox.selectAll('div')
.data(this.props.data.values)
.enter()
.append('div')
.text(function(d){
return d;
})
// Enhance the font-size based on value.
.style('font-size', function(d) {
// var d = d > 100 ? 80 : d; // replace this with scale.
var d = fontScale(d);
return d + "px"
})
.style('color', function(d) {
// Need to convert number to scale on 0-1.
var d = Math.log(d) / 10;
// Try out D3 color interpolation...
var color = d3.interpolateWarm(d)
return color;
})
.classed('garamond fw9 underline', true);
// Add the description.
textBox
.append('div')
.text(this.props.data.label || this.props.data.desc)
.classed('child f2', true)
;
}
}
// A special donation centric text viz.
class Donation extends GenText {
componentDidMount() {
// Load the symbol from the data.
var sym = this.props.data.symbol;
// Load the generic fontScale.
var fontScale = this.fontScale;
// Changing default range, want bigger fonts, not as much variation.
fontScale.range([80, 100]);
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.classed(' tc bg-dark-gray hover-bg-dark-green', true);
textBox.selectAll('div')
.data(this.props.data.values)
.enter()
.append('div')
.text(function(d){
return sym + d;
})
// Enhance the font-size based on value.
.style('font-size', function(d) {
var d = fontScale(d);
return d + "px"
})
.style('color', function(d) {
// Need to convert number to scale on 0-1.
var d = Math.log(d) / 10;
// Try out D3 color interpolation...
var color = d3.interpolateMagma(d)
return color;
})
.classed('garamond fw9', true);
// Add the description.
textBox
.append('div')
.html(this.props.data.label || this.props.data.desc)
.classed('f3 garamond', true)
;
}
}
// A big fat juicy percentage!
class Percentage extends GenText {
componentDidMount() {
}
}
// A common example of a more complex text-based viz.
class StepCount extends GenText {
componentDidMount() {
// Create a good scale for the circles.
var scale = d3.scaleLinear();
scale.domain([0, 2000]);
// A reasonable font-size scale range.
scale.range([10, 100]);
var radialScale = d3.scaleLinear();
radialScale.domain([0,2000]);
radialScale.range([0, 8]);
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.classed(' tc bg-light-blue', true);
var svg = textBox.append('svg')
.attr('width', 200)
.attr('height', 200);
svg.selectAll('path')
.data(this.props.data.values)
.enter()
// Add a circle that scales well.
.append('path')
// 'd' attribute is the SVtrueG attr. for the shape of path.
.attr('d', function(d){
// Lets try an arc.
var arc = d3.arc();
var arcPath = arc({
innerRadius: 0,
// Need a nice scale for d...
outerRadius: scale(d),
startAngle: 0,
endAngle: radialScale(d)
});
return arcPath;
})
.attr('transform', 'translate(100, 100)')
.attr('opacity', 0.8)
.attr('fill', function(d){
// Need to convert number to scale on 0-1.
var fillScale = d3.scaleLinear();
fillScale.domain([0, 2000]);true
fillScale.range([0,1]);
var newD = fillScale(d);
var color = d3.interpolatePlasma(newD);
return color;
});
// Add a label?
var doSvgtext = false;
// Do the text in the svg.
if (doSvgtext== true) {
svg.selectAll('text')
.data(this.props.data.values)
.enter()
.append('text')
.text(function(d){return d})
.attr('class', 'f4 Helvetica')
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.attr('opacity', 0.75)
.attr('transform', function(d){
y = scale(d);
return 'translate(100,' + (130 - y) + ')';
});
} else {
// Doing text in html/css.
textBox.selectAll('div')
textBox.append('div').text(this.props.data.values[1] + ' out of ' + this.props.data.values[0] + ' steps today!').classed('f5 Helvetica blue', true)
}
}
}
// TODO:
// What other types do we want?
// Pie Chart (svg) -- done
// Big fancy percentage!
// Bar Chart (svg)
// Donations (css/html) -- kinda done
// Student body Martlett thing? (css/html?)
// Earthquake? (css/html?)
// Text pyramid? (css/html?)
// (See notes in Google photos sketch)
ReactDOM.render(
React.createElement(Charts),
document.getElementById('app')
);</script></body>
</html>
h1, h2, h3, h4, h5, h6, p {
font-family: sans-serif;
}
h1, h2, h3, h4, h5, h6, div, p, nav, section, hr {
/* margin: 0 0 1em 0; */
margin: 0 0 10px 0;
}
nav, section, div, p {
/* padding: 0.3em; */
padding: 10px;
}
/* Stealing from Tachyon, for svg */
.bg-blue-svg {
fill: #357EDD;
}
svg path:hover,
svg path.selected {
opacity: 1;
stroke: white;
stroke-width: 2px;
}
// Some default data.
'use strict';
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var data = {
title: 'Some Data',
desc: 'Maps values to color on \'magma \' scale and maps values to a font-size on a linear scale.',
values: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
};
var textNumData = {
title: 'Text & Number',
desc: 'This illustrates how numbers and basic text can go together.',
values: [50, 100, 500],
label: 'we\'ve grown in leaps and bounds!'
};
var donationData = {
title: 'Donations 2016',
desc: 'Shows donation totals in a dynamic way, with the total highlighted along with a basic label or description that can support HTML.',
values: [1000],
label: 'in <b>donations</b> raised by McGill U in 2016.<br/ > More on the way!',
symbol: '$',
// TODO: have a CTA available?
cta: null
};
var stepCountData = {
title: 'Step Count Today',
desc: 'Shows a simple step counter.',
values: [1700, 1000]
};
var pieData = {
title: 'Academic Community',
desc: 'A pie chart that shows the demographic breakdown of the academic community at McGill',
values: [{ 'label': 'Undergraduate', 'number': 10000 }, { 'label': 'Post-graduate', 'number': 3000 }, { 'label': 'Graduate', 'number': 8000 }, { 'label': 'Other', 'number': 800 }, { 'label': 'Faculty', 'number': 1500 }, { 'label': 'TA\'s', 'number': 7000 }]
};
var percentageData = {
title: 'Advanced preparation!',
label: '55% of McGill undergraduates will go on to get a PhD. More than any other Canadian university.',
values: [55],
desc: 'Number of undergraduate students who later in a PhD'
};
// TODO : style this component (text to the left or right or bottom of % and big label, see one of those xample websites Joyce sent.)
// TODO: put this stuff in a document and put that in the ticket.
var Charts = (function (_React$Component) {
_inherits(Charts, _React$Component);
function Charts(props) {
_classCallCheck(this, Charts);
_get(Object.getPrototypeOf(Charts.prototype), 'constructor', this).call(this, props);
this.state = {
data: data
};
}
// A generic text based infographic class.
// Renders some text w/ d3js in a fun way.
// Takes data, colors text based on values.
// This component doesn't even need to use SVG
// But it could!
// Handles loading the data and viz.
_createClass(Charts, [{
key: 'componentDidMount',
value: function componentDidMount() {}
}, {
key: 'render',
value: function render() {
return React.createElement(
'div',
{ className: 'all-charts-render' },
React.createElement(StepCount, { type: 'Text Number svg infographic', data: stepCountData }),
React.createElement(Percentage, { type: 'Percentage', data: percentageData }),
React.createElement(Pie, { type: 'Pie', data: pieData }),
React.createElement(Bar, { type: 'Bar', data: this.state.data }),
React.createElement(GenText, { type: 'Text infographic', data: this.state.data }),
React.createElement(TextNumber, { type: 'Text Number infographic', data: textNumData }),
React.createElement(Donation, { type: 'Donation', data: donationData })
);
}
}]);
return Charts;
})(React.Component);
var GenText = (function (_React$Component2) {
_inherits(GenText, _React$Component2);
function GenText(props) {
_classCallCheck(this, GenText);
_get(Object.getPrototypeOf(GenText.prototype), 'constructor', this).call(this, props);
// Set up a font scale for all text components ot have.
var scale = d3.scaleLinear();
scale.domain([this.props.data.values[0], this.props.data.values[this.props.data.values.length - 1]]);
// A reasonable font-size scale range.
scale.range([16, 60]);
this.fontScale = scale;
}
// A generic chart class.
// Handles loading the data and viz.
_createClass(GenText, [{
key: 'componentDidMount',
value: function componentDidMount() {
var fontScale = this.fontScale;
// TODO: get a pie-chart or some other graph going.
// console.log(this.props.type);
// Fire up d3.
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.selectAll('div').data(this.props.data.values).enter().append('div').text(function (d) {
return d;
})
// Enhance the font-size based on value.
.style('font-size', function (d) {
// Run the values through scale, any number will be between scale.range value.
return fontScale(d) + "px";
}) // Enhance the color based on value.
.style('background-color', function (d) {
// Try out D3 color interpolation...
var color = d3.interpolateInferno(100);
return color;
}).style('color', function (d) {
// Try out D3 color interpolation...
// Need rainbow to offset inferno.
var color = d3.interpolateRainbow(d / 100);
return color;
}).classed('value', true);
}
// Renders the basic markup
}, {
key: 'render',
value: function render() {
var scale = d3.scaleLinear();
scale.domain([this.props.data.values[0], this.props.data.values[this.props.data.values.length - 1]]);
// A reasonable font-size scale range.
scale.range([16, 60]);
return React.createElement(
'div',
{ className: 'gen-text-render bg-mid-gray ' + s.slugify(this.props.type) },
React.createElement(
'h2',
{ className: 'hot-pink' },
this.props.type,
' Text: ',
React.createElement(
'span',
{ className: 'dark-pink' },
this.props.data.title
),
' '
),
React.createElement('div', { className: 'text-info light-gray' }),
React.createElement(
'div',
null,
this.props.data.desc || this.props.data.label
)
);
}
}]);
return GenText;
})(React.Component);
var GenChart = (function (_React$Component3) {
_inherits(GenChart, _React$Component3);
function GenChart(props) {
_classCallCheck(this, GenChart);
_get(Object.getPrototypeOf(GenChart.prototype), 'constructor', this).call(this, props);
// Set up a basic scale for chart.
var scale = d3.scaleLinear();
scale.domain([0, 10000]);
// A reasonable font-size scale range.
scale.range([0, 100]);
this.scale = scale;
}
// A pie chart.
// Handles loading the data and viz.
_createClass(GenChart, [{
key: 'componentDidMount',
value: function componentDidMount() {}
// Renders the basic markup
}, {
key: 'render',
value: function render() {
return React.createElement(
'div',
{ className: 'gen-chart-render bg-mid-gray ' + s.slugify(this.props.type) },
React.createElement(
'h2',
null,
this.props.type,
' Chart: ',
this.props.data.title,
' '
),
React.createElement('div', { className: 'chart' })
);
}
}]);
return GenChart;
})(React.Component);
var Pie = (function (_GenChart) {
_inherits(Pie, _GenChart);
function Pie() {
_classCallCheck(this, Pie);
_get(Object.getPrototypeOf(Pie.prototype), 'constructor', this).apply(this, arguments);
}
// A bar chart.
_createClass(Pie, [{
key: 'componentDidMount',
value: function componentDidMount() {
var scale = this.scale;
// Need to convert number to scale on 0-1.
var fillScale = d3.scaleLinear();
fillScale.domain([0, 15000]);true;
fillScale.range([0, 1]);
var selector = '.' + s.slugify(this.props.type) + ' .chart';
var chart = d3.select(selector);
chart.classed(' tc bg-light-blue cf', true);
var svg = chart.append('svg').attr('width', 200).attr('height', 200);
// .classed('cf', true);
svg.selectAll('path').data(this.props.data.values).enter();
// Use d3 pie utility to construct an arc made of pie segments.
var pie = d3.pie();
var arcs = pie.value(function (d) {
return d.number;
})(this.props.data.values);
var arc = d3.arc().outerRadius(100).padAngle(0.03).innerRadius(50);
arcs.forEach(function (d, i) {
var data = d.data;
svg.append('path').attr('d', arc(d)).classed('centersvg ' + s.slugify(data.label), true).attr('transform', 'translate(100, 100)').attr('opacity', 0.85).attr('fill', function (data) {
var newD = fillScale(d.value);
var color = d3.interpolatePlasma(newD);
return color;
});
});
// Sort the labels according to biggest number value.
var sorted = this.props.data.values.sort(function (a, b) {
return a.number < b.number;
});
// TODO: need to add some labels or a legend!
svg.exit();
// Lets try a legend!
chart.append('div').classed('cf w-100', true);
chart.selectAll('div').select('.w-100').data(sorted).enter().append('div').classed('f6 dib mr1 white b grow', true).text(function (d) {
return d.label;
}).style('background-color', function (d) {
return d3.interpolatePlasma(fillScale(d.number));
}).on('mouseover', function (e) {
var pathC = s.slugify(this.__data__.label);
var p = chart.select('path.' + pathC)
// .attr('opacity', 1)
// .attr('stroke', 'white')
// .attr('stroke-width', 2);
.classed('selected', true);
}).on('mouseout', function (e) {
var pathC = s.slugify(this.__data__.label);
var p = chart.select('path.' + pathC)
// .attr('opacity', 0.8)
// .attr('stroke-width', 0);
.classed('selected', false);
});
}
}]);
return Pie;
})(GenChart);
var Bar = (function (_GenChart2) {
_inherits(Bar, _GenChart2);
function Bar() {
_classCallCheck(this, Bar);
_get(Object.getPrototypeOf(Bar.prototype), 'constructor', this).apply(this, arguments);
}
// A special statement type of text-based viz.
_createClass(Bar, [{
key: 'componentDidMount',
value: function componentDidMount() {}
}]);
return Bar;
})(GenChart);
var TextNumber = (function (_GenText) {
_inherits(TextNumber, _GenText);
function TextNumber() {
_classCallCheck(this, TextNumber);
_get(Object.getPrototypeOf(TextNumber.prototype), 'constructor', this).apply(this, arguments);
}
// A special donation centric text viz.
_createClass(TextNumber, [{
key: 'componentDidMount',
value: function componentDidMount() {
// Load the generic fontScale.
var fontScale = this.fontScale;
// Change the range a bit.
fontScale.range([24, 100]);
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.classed('tc bg-blue white pt3 br3 hide-child hover-bg-dark-blue', true);
textBox.selectAll('div').data(this.props.data.values).enter().append('div').text(function (d) {
return d;
})
// Enhance the font-size based on value.
.style('font-size', function (d) {
// var d = d > 100 ? 80 : d; // replace this with scale.
var d = fontScale(d);
return d + "px";
}).style('color', function (d) {
// Need to convert number to scale on 0-1.
var d = Math.log(d) / 10;
// Try out D3 color interpolation...
var color = d3.interpolateWarm(d);
return color;
}).classed('garamond fw9 underline', true);
// Add the description.
textBox.append('div').text(this.props.data.label || this.props.data.desc).classed('child f2', true);
}
}]);
return TextNumber;
})(GenText);
var Donation = (function (_GenText2) {
_inherits(Donation, _GenText2);
function Donation() {
_classCallCheck(this, Donation);
_get(Object.getPrototypeOf(Donation.prototype), 'constructor', this).apply(this, arguments);
}
// A big fat juicy percentage!
_createClass(Donation, [{
key: 'componentDidMount',
value: function componentDidMount() {
// Load the symbol from the data.
var sym = this.props.data.symbol;
// Load the generic fontScale.
var fontScale = this.fontScale;
// Changing default range, want bigger fonts, not as much variation.
fontScale.range([80, 100]);
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.classed(' tc bg-dark-gray hover-bg-dark-green', true);
textBox.selectAll('div').data(this.props.data.values).enter().append('div').text(function (d) {
return sym + d;
})
// Enhance the font-size based on value.
.style('font-size', function (d) {
var d = fontScale(d);
return d + "px";
}).style('color', function (d) {
// Need to convert number to scale on 0-1.
var d = Math.log(d) / 10;
// Try out D3 color interpolation...
var color = d3.interpolateMagma(d);
return color;
}).classed('garamond fw9', true);
// Add the description.
textBox.append('div').html(this.props.data.label || this.props.data.desc).classed('f3 garamond', true);
}
}]);
return Donation;
})(GenText);
var Percentage = (function (_GenText3) {
_inherits(Percentage, _GenText3);
function Percentage() {
_classCallCheck(this, Percentage);
_get(Object.getPrototypeOf(Percentage.prototype), 'constructor', this).apply(this, arguments);
}
// A common example of a more complex text-based viz.
_createClass(Percentage, [{
key: 'componentDidMount',
value: function componentDidMount() {}
}]);
return Percentage;
})(GenText);
var StepCount = (function (_GenText4) {
_inherits(StepCount, _GenText4);
function StepCount() {
_classCallCheck(this, StepCount);
_get(Object.getPrototypeOf(StepCount.prototype), 'constructor', this).apply(this, arguments);
}
// TODO:
// What other types do we want?
// Pie Chart (svg) -- done
// Big fancy percentage!
// Bar Chart (svg)
// Donations (css/html) -- kinda done
// Student body Martlett thing? (css/html?)
// Earthquake? (css/html?)
// Text pyramid? (css/html?)
// (See notes in Google photos sketch)
_createClass(StepCount, [{
key: 'componentDidMount',
value: function componentDidMount() {
// Create a good scale for the circles.
var scale = d3.scaleLinear();
scale.domain([0, 2000]);
// A reasonable font-size scale range.
scale.range([10, 100]);
var radialScale = d3.scaleLinear();
radialScale.domain([0, 2000]);
radialScale.range([0, 8]);
var selector = '.' + s.slugify(this.props.type) + ' .text-info';
var textBox = d3.select(selector);
textBox.classed(' tc bg-light-blue', true);
var svg = textBox.append('svg').attr('width', 200).attr('height', 200);
svg.selectAll('path').data(this.props.data.values).enter()
// Add a circle that scales well.
.append('path')
// 'd' attribute is the SVtrueG attr. for the shape of path.
.attr('d', function (d) {
// Lets try an arc.
var arc = d3.arc();
var arcPath = arc({
innerRadius: 0,
// Need a nice scale for d...
outerRadius: scale(d),
startAngle: 0,
endAngle: radialScale(d)
});
return arcPath;
}).attr('transform', 'translate(100, 100)').attr('opacity', 0.8).attr('fill', function (d) {
// Need to convert number to scale on 0-1.
var fillScale = d3.scaleLinear();
fillScale.domain([0, 2000]);true;
fillScale.range([0, 1]);
var newD = fillScale(d);
var color = d3.interpolatePlasma(newD);
return color;
});
// Add a label?
var doSvgtext = false;
// Do the text in the svg.
if (doSvgtext == true) {
svg.selectAll('text').data(this.props.data.values).enter().append('text').text(function (d) {
return d;
}).attr('class', 'f4 Helvetica').attr('text-anchor', 'middle').attr('fill', 'white').attr('opacity', 0.75).attr('transform', function (d) {
y = scale(d);
return 'translate(100,' + (130 - y) + ')';
});
} else {
// Doing text in html/css.
textBox.selectAll('div');
textBox.append('div').text(this.props.data.values[1] + ' out of ' + this.props.data.values[0] + ' steps today!').classed('f5 Helvetica blue', true);
}
}
}]);
return StepCount;
})(GenText);
ReactDOM.render(React.createElement(Charts), document.getElementById('app'));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment