Skip to content

Instantly share code, notes, and snippets.

@enjalot
Last active January 28, 2016 05:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save enjalot/5442d07cb2d3a147ed36 to your computer and use it in GitHub Desktop.
Save enjalot/5442d07cb2d3a147ed36 to your computer and use it in GitHub Desktop.
infinite blocks
node_modules
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Intro to Data Vis with D3</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.13/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css" rel="stylesheet" type="text/css">
<link href="styles.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="app-container"></div>
<script src="main-build.js"></script>
</body>
</html>
[
{ "title": "Bar Chart Populations", "id": "6cd1e224d76811b68df4" },
{ "title": "Bar Chart Religions", "id": "123f860373b442c7de0e" },
{ "title": "Horizontal Bar Chart", "id": "4df29e2f8c6e20ed2baf" },
{ "title": "Colored Bar Chart", "id": "fea34ca9b3b8886e3ab8" },
{ "title": "Grouped Bar Chart", "id": "d4e2b2854f25429a06aa" },
{ "title": "Bar", "id": "a364f9c0b9a114c90efa" },
{ "title": "Stacked Bar", "id": "ab098389dd80e4a6eb58" },
{ "title": "Stacked Bar Chart", "id": "805413fb3b2efaada1ce" },
{ "title": "Scatter Plot", "id": "134ed87c99257e3f2e31" },
{ "title": "Line Chart", "id": "60b40877ef898f19aeb8" },
{"title": "Infinite Blocks", "id": "5442d07cb2d3a147ed36" }
]
var NavItem = React.createClass({
displayName: "NavItem",
click: function (e) {
this.props.controller.setCurrentIndex(this.props.item.index);
},
render: function () {
var item = this.props.item;
var blocksUrl = "http://bl.ocks.org/curran/" + item.id;
var thumbnailUrl = "http://bl.ocks.org/curran/raw/" + item.id + "/thumbnail.png";
var imgClass = "nav-item-thumbnail" + (this.props.active ? " active" : "");
return React.createElement(
"div",
{ className: "nav-item", onClick: this.click },
React.createElement(
"span",
{ className: "nav-item-title" },
item.title
),
React.createElement("img", { className: imgClass, src: thumbnailUrl })
);
}
});
var NavList = React.createClass({
displayName: "NavList",
render: function () {
return React.createElement(
"div",
{ className: "nav" },
this.props.items.map(item => {
return React.createElement(NavItem, { item: item,
key: item.index,
active: item.index === this.props.currentIndex,
controller: this.props.controller });
})
);
}
});
var ContentPane = React.createClass({
displayName: "ContentPane",
render: function () {
var blockbuilderUrl = "http://blockbuilder.org/curran/" + this.props.item.id + "#embed=true";
return React.createElement("iframe", { className: "content", src: blockbuilderUrl });
}
});
var App = React.createClass({
displayName: "App",
getInitialState() {
return {
items: [],
item: {},
currentIndex: 0
};
},
render() {
return React.createElement(
"div",
{ className: "app" },
React.createElement(NavList, { items: this.state.items,
currentIndex: this.state.currentIndex,
controller: this.props.controller }),
React.createElement(ContentPane, { item: this.state.item })
);
}
});
var mountNode = document.getElementById("app-container");
var controller = {};
var app = ReactDOM.render(React.createElement(App, { controller: controller }), mountNode);
controller.setItems = items => {
app.setState(() => {
return { items: items };
});
controller.setCurrentIndex(0);
};
controller.setCurrentIndex = currentIndex => {
app.setState(previousState => {
return {
currentIndex: currentIndex,
item: previousState.items[currentIndex]
};
});
};
// Increment (offset == 1) or decrement (offset == -1) the current index.
controller.incrementCurrentIndex = offset => {
app.setState(previousState => {
var currentIndex = previousState.currentIndex + offset;
// Guard against going out of bounds.
var n = previousState.items.length;
currentIndex = currentIndex < 0 ? 0 : currentIndex;
currentIndex = currentIndex >= n ? n - 1 : currentIndex;
return {
currentIndex: currentIndex,
item: previousState.items[currentIndex]
};
});
};
// Load the file that configures the items and their order.
d3.json("items.json", (err, items) => {
// Assign an index to each item.
items.forEach((item, i) => item.index = i);
// Set the state from the loaded data.
controller.setItems(items);
});
// Set up navigation with arrow keys.
window.addEventListener("keydown", function (e) {
var offsets = {
// Decrement slide on UP (38) and LEFT (37)
37: -1,
38: -1,
// Increment slide on DOWN (40) and RIGHT (39);
39: 1,
40: 1
};
controller.incrementCurrentIndex(offsets[e.keyCode]);
});
var NavItem = React.createClass({
click: function (e){
this.props.controller.setCurrentIndex(this.props.item.index);
},
render: function (){
var item = this.props.item;
var blocksUrl = "http://bl.ocks.org/curran/" + item.id;
var thumbnailUrl = "http://bl.ocks.org/curran/raw/" + item.id + "/thumbnail.png";
var imgClass = "nav-item-thumbnail" + (this.props.active ? " active" : "");
return (
<div className="nav-item" onClick={this.click} >
<span className="nav-item-title">{item.title}</span>
<img className={imgClass} src={thumbnailUrl}/>
</div>
)
}
});
var NavList = React.createClass({
render: function (){
return (
<div className="nav">
{this.props.items.map((item) => {
return <NavItem item={item}
key={item.index}
active={item.index === this.props.currentIndex}
controller={this.props.controller}/>
})}
</div>
);
}
});
var ContentPane = React.createClass({
render: function (){
var blockbuilderUrl = "http://blockbuilder.org/curran/" + this.props.item.id;
return <iframe className="content" src={blockbuilderUrl} />;
}
});
var App = React.createClass({
getInitialState() {
return {
items: [],
item: {},
currentIndex: 0
};
},
render() {
return (
<div className="app">
<NavList items={this.state.items}
currentIndex={this.state.currentIndex}
controller={this.props.controller}/>
<ContentPane item={this.state.item} />
</div>
);
}
});
var mountNode = document.getElementById("app-container");
var controller = {};
var app = ReactDOM.render(<App controller={controller}/>, mountNode);
controller.setItems = (items) => {
app.setState(() => {
return { items: items };
});
controller.setCurrentIndex(0);
}
controller.setCurrentIndex = (currentIndex) => {
app.setState((previousState) => {
return {
currentIndex: currentIndex,
item: previousState.items[currentIndex]
};
});
}
// Increment (offset == 1) or decrement (offset == -1) the current index.
controller.incrementCurrentIndex = (offset) => {
app.setState((previousState) => {
var currentIndex = previousState.currentIndex + offset;
// Guard against going out of bounds.
var n = previousState.items.length;
currentIndex = (currentIndex < 0) ? 0 : currentIndex;
currentIndex = (currentIndex >= n) ? (n - 1) : currentIndex;
return {
currentIndex: currentIndex,
item: previousState.items[currentIndex]
};
});
}
// Load the file that configures the items and their order.
d3.json("items.json", (err, items) => {
// Assign an index to each item.
items.forEach((item, i) => item.index = i);
// Set the state from the loaded data.
controller.setItems(items);
});
// Set up navigation with arrow keys.
window.addEventListener("keydown", function (e){
var offsets = {
// Decrement slide on UP (38) and LEFT (37)
37: -1,
38: -1,
// Increment slide on DOWN (40) and RIGHT (39);
39: 1,
40: 1
};
controller.incrementCurrentIndex(offsets[e.keyCode]);
});
{
"name": "intro-data-vis-d3",
"version": "0.0.1",
"description": "A React app that displays a presentation.",
"main": "index.js",
"scripts": {
"build": "babel main.js --presets react --out-file=main-build.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-cli": "^6.4.5",
"babel-preset-react": "^6.3.13"
}
}
/* http://www.paulirish.com/2012/box-sizing-border-box-ftw/ */
html {
box-sizing: border-box;
height: 100%;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
font-family: "Open Sans", sans-serif;
height: 100%;
}
#app-container {
height: 100%;
}
.app {
height: 100%;
display: flex;
flex-direction: row;
}
.nav {
/* Original bl.ocks.org thumbnails are (230 X 120). */
width: 200px;
background-color: rgb(247, 247, 247);
overflow-y: scroll;
}
.nav-item {
position: relative;
cursor: pointer;
margin: 8px;
}
.nav-item-title {
pointer-events:none;
position: absolute;
top: 2px;
/* Center the text inside the box. */
width: 100%;
text-align: center;
/* This trick adds a heavy white shadow around the text. */
text-shadow:
0px 0px 2px white,
0px 0px 2px white,
0px 0px 2px white,
0px 0px 2px white,
0px 0px 3px white,
0px 0px 3px white,
0px 0px 3px white,
0px 0px 3px white;
}
.nav-item-thumbnail {
transition: box-shadow 0.3s, border-color 0.3s;
width: 100%;
border-radius: 5px;
border-style: solid;
border-color: white;
border-width: 1px;
box-shadow: 0px 0px 5px #888888;
}
.nav-item-thumbnail:hover {
box-shadow: 1px 1px 5px #888888;
}
.nav-item-thumbnail.active {
border-color: gray;
box-shadow: 1px 1px 6px #888888;
}
.content {
width: 100%;
height: 100%;
}
/* Custom scrollbars. Draws from https://css-tricks.com/custom-scrollbars-in-webkit/ */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
}
::-webkit-scrollbar-thumb {
border-radius: 5px;
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment