Skip to content

Instantly share code, notes, and snippets.

@onlymejosh
Last active February 23, 2017 17:37
Show Gist options
  • Save onlymejosh/507a18e1705319126f57c71d41cec3cc to your computer and use it in GitHub Desktop.
Save onlymejosh/507a18e1705319126f57c71d41cec3cc to your computer and use it in GitHub Desktop.
New Twiddle
import Ember from 'ember';
export default Ember.Controller.extend({
appName: 'Ember Twiddle'
});
import Component from 'ember-component';
import computed from 'ember-computed';
import EmberObject from 'ember-object';
import { htmlSafe } from 'ember-string';
import layout from './template';
import set from 'ember-metal/set';
import run from 'ember-runloop';
import { stack, stackOrderNone, stackOffsetNone} from 'd3-shape';
import { scaleBand, scaleLinear, scaleOrdinal } from 'd3-scale';
import { max, range } from 'd3-array';
export default Component.extend({
layout,
width: 600,
height: 300,
color: scaleOrdinal([
'#E9008B',
'#CE3C1D',
'#F05522',
'#F4891E',
'#FFC60A',
'#DECD20',
'#BBD530',
'#94C73B',
'#00823C',
'#46B85C',
'#00B38C',
'#5FC3B0',
'#478CCB',
'#26C4F4',
'#3D66B0',
'#4151A3',
'#69236B',
'#59439B',
'#6E3E7B',
'#894772',
'#BF5A88',
'#ED589F'
]),
/**
*
* @property keys
* @return Returns an array of keys for which the data is distributed
* Example:
* keys: computed(() => ['0.0-20.0', '20.0-40.0', '40.0-60.0', '60.0-80.0', '80.0-101.0']),
*
*/
keys: computed(() => ["apples", "bananas", "cherries", "dates"]),
/**
*
* @property data
* @return Returns a flat array of distributed data.
* Example:
* data: computed(() => [
* { timestamp: new Date(2015, 2, 1), apples: 200, bananas: 100, cherries: 300, dates: 300 },
* ]),
*
*/
data: computed(() => [
{timestamp: new Date(2015, 0, 1), apples: 3840, bananas: 1920, cherries: 960, dates: 400},
{timestamp: new Date(2015, 1, 1), apples: 1600, bananas: 1440, cherries: 960, dates: 400},
{timestamp: new Date(2015, 2, 1), apples: 640, bananas: 960, cherries: 640, dates: 400},
{timestamp: new Date(2015, 3, 1), apples: 320, bananas: 480, cherries: 640, dates: 400}
]),
xScale: computed('data.[]', 'width', function() {
return scaleBand()
.domain(range(this.get('data.length')))
.rangeRound([0, this.get('width')])
.padding(0.05);
}),
yScale: computed('data.[]', 'height', function() {
return scaleLinear().range([0, this.get('height')]);
}),
d3Data: computed('data.[]', 'keys.[]', 'yScale', 'xScale', function() {
let { data, keys } = this.getProperties('data', 'keys');
if (!(data && data.length || keys && keys.length)) { return false; }
let stackedData = stack()
.keys(this.get('keys'))
.order(stackOrderNone)
.offset(stackOffsetNone);
let layers = stackedData(this.get('data'));
console.log('data change')
this.get('yScale').domain([0, max(layers[layers.length - 1], (d) => d[1]) ]).nice();
let bandwidth = this.get('xScale').bandwidth();
let d3Data = layers.map((layer) => {
return EmberObject.create({
key: layer.key,
isLowLight: false, // TODO: More descriptive naming? Used to define whether the series should be
values: layer.map((item, i) => {
let [y0, y1] = item;
let { data } = item;
let height = this.get('yScale')(y1 - y0);
let x = this.get('xScale')(i);
return {
color: this.get('color')(layer.index),
width: bandwidth,
height,
x,
y: this.get('height') - height - this.get('yScale')(y0),
label: `${layer.key} x ${data[layer.key]} on ${data.timestamp}`,
};
}),
});
});
return d3Data;
}),
_positionTooltip(tooltipOffset) {
let $tooltip = this.$('.tooltip');
let width = $tooltip.width();
tooltipOffset.left -= width / 2; // position tooltip in the middle
let delta = window.innerWidth - (tooltipOffset.left + width);
// ensure we're not off the left or right
if (delta < 0) {
tooltipOffset.left += delta;
}
if (tooltipOffset.left < 0) {
tooltipOffset.arrowPosition = 0.5 - (Math.abs(tooltipOffset.left) / width);
tooltipOffset.left = 0;
}
return tooltipOffset;
},
setActive(key) {
this.get('d3Data').setEach('isLowLight', true);
const activeSeries = this.get('d3Data').findBy('key',key)
console.log(activeSeries)
activeSeries.set('isLowLight', false)
console.log(this.get('d3Data'))
},
actions: {
showTooltip(tooltipText, left, top) {
const position = this._positionTooltip({ left, top });
this.setProperties({
tooltipLocation: position,
tooltipStyle: htmlSafe(`left: ${position.left}px; top: ${position.top}px; display: block;`),
tooltipText,
});
},
hideTooltip() {
this.set('tooltipLocation', null);
this.set('tooltipStyle', null);
},
onSeriesMouseEnter(key) {
run.scheduleOnce('afterRender', this, 'setActive', key)
},
onSeriesMouseLeave(key) {
this.get('d3Data').setEach('isLowLight', false);
}
}
});
<svg width={{width}} height={{height}}>
{{#each d3Data as |series|}}
{{series.isLowLight}}
{{series.key}}
<g id={{series.key}} class="{{if series.isLowLight 'lighten'}}"
{{action (action 'onSeriesMouseEnter' series.key) on="mouseEnter"}}
{{action (action 'onSeriesMouseLeave' series.key) on="mouseLeave"}}
>
{{#each series.values as |point| }}
{{vis-bar
color=point.color
width=point.width
height=point.height
x=point.x
y=point.y
tooltip=point.label
onMouseEnter=(action 'showTooltip' point.label)
onMouseLeave=(action 'hideTooltip')
}}
{{/each}}
</g>
{{/each}}
</svg>
<div class="tooltip" style={{tooltipStyle}}>
{{tooltipText}}
<div class="arrow"></div>
</div>
body {
margin: 12px 16px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 12pt;
}
.lighten {
opacity: 0.1;
}
{
"version": "0.11.0",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": true,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js",
"ember": "2.10.2",
"ember-data": "2.11.0",
"ember-template-compiler": "2.10.2",
"ember-testing": "2.10.2"
},
"addons": {
"ember-d3": "0.3.2"
}
}
import Component from 'ember-component';
import layout from './template';
import run from 'ember-runloop';
export default Component.extend({
layout,
attributeBindings: ['color:fill', 'height', 'width', 'x', 'y'],
tagName: 'rect',
// Default Properties
color: 'red',
height: 0,
width: 0,
x: 0,
y: 0,
mouseEnter(event) {
const x = this.$().position().left + (this.get('width') / 2);
this.get('onMouseEnter')(x, event.pageY);
},
mouseLeave() {
this.get('onMouseLeave')();
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment