A version which uses a Model(which coordinates multiple scales), and the parent creates the axes.
Last active
August 9, 2016 15:23
-
-
Save gtb104/d2b8d443109d797fa9923defd2071bf8 to your computer and use it in GitHub Desktop.
Declarative D3 v2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
const { | |
Component, | |
computed | |
} = Ember; | |
const log = console.log; | |
export default Component.extend({ | |
tagName: 'g', | |
classNames: ['graph-content'], | |
attributeBindings: ['clip-path', 'transform'], | |
'clip-path': computed('model.clipPathId', function() { | |
var clipPathId = this.get('model.clipPathId'); | |
return `url(#${clipPathId})`; | |
}), | |
transform: computed('model.graphTransform', function() { | |
return this.get('model.graphTransform'); | |
}), | |
textX: computed('model.graphWidth', function() { | |
return this.get('model.graphWidth') * 0.5; | |
}), | |
textY: computed('model.graphHeight', function() { | |
return this.get('model.graphHeight') * 0.5; | |
}), | |
init() { | |
this._super(...arguments); | |
}, | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
const { | |
Component, | |
computed, | |
run | |
} = Ember; | |
const log = console.log; | |
export default Component.extend({ | |
tagName: 'g', | |
seriesToRender: null, | |
lineFn: computed('xScale', 'yScale', function() { | |
var xScale = this.get('xScale'); | |
var yScale = this.get('yScale'); | |
return d3.line() | |
.x(d => xScale(d.x)) | |
.y(d => yScale(d.y)) | |
.curve(d3.curveLinear); | |
}), | |
didReceiveAttrs(attrs) { | |
this._super(...arguments); | |
if (attrs.newAttrs && attrs.oldAttrs) { | |
let series = this.get('seriesToRender'); | |
let data = this.get('data'); | |
let line = this.get('lineFn'); | |
this.update(data[series], line); | |
} | |
}, | |
didInsertElement() { | |
this._super(...arguments); | |
let series = this.get('seriesToRender'); | |
let data = this.get('data'); | |
let line = this.get('lineFn'); | |
if (data && data[series]) { | |
this.draw(data[series], line); | |
} else { | |
this.draw({}, line); | |
} | |
}, | |
draw(data, line) { | |
let path = d3.select(this.element).append('path') | |
.datum(data) | |
.attr('class', 'chart-line') | |
.attr('d', line); | |
this.set('path', path); | |
}, | |
update(data, line) { | |
this.get('path') | |
.datum(data) | |
.transition().duration(750) | |
.attr('d', line); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
const { | |
computed, | |
warn | |
} = Ember; | |
const log = console.log; | |
const scaleFactoryProperty = function(axis) { | |
let scaleTypeKey = axis + 'ScaleType'; | |
let powExponentKey = axis + 'PowerExponent'; | |
return computed(scaleTypeKey, powExponentKey, function() { | |
let type = this.get(scaleTypeKey).toLowerCase(); | |
let powExp = this.get(powExponentKey); | |
if (type === 'linear') { | |
return d3.scaleLinear; | |
} | |
else if (type === 'time') { | |
return d3.scaleTime; | |
} | |
else if (type === 'ordinal') { | |
return function() { | |
let scale = d3.scaleOrdinal(); | |
// ordinal scales don't have an invert function, so we need to add one | |
scale.invert = function(rv) { | |
let [min, max] = d3.extent(scale.range()); | |
let domain = scale.domain(); | |
let i = Math.round((domain.length - 1) * (rv - min) / (max - min)); | |
return domain[i]; | |
}; | |
return scale; | |
} | |
} | |
else if (type === 'power' || type === 'pow') { | |
return function(){ | |
return d3.scalePow().exponent(powExp); | |
}; | |
} | |
else if (type === 'log') { | |
return d3.scaleLog; | |
} | |
else { | |
warn('unknown scale type: ' + type); | |
return d3.scaleLinear; | |
} | |
}); | |
}; | |
const scaleProperty = function(axis) { | |
let scaleFactoryKey = axis + 'ScaleFactory'; | |
let rangeKey = axis + 'Range'; | |
let domainKey = axis + 'Domain'; | |
let scaleTypeKey = axis + 'ScaleType'; | |
let ordinalPaddingKey = axis + 'OrdinalPadding'; | |
let ordinalOuterPaddingKey = axis + 'OrdinalOuterPadding'; | |
return computed(scaleFactoryKey, rangeKey, scaleTypeKey, ordinalPaddingKey, domainKey, ordinalOuterPaddingKey, function() { | |
let scaleFactory = this.get(scaleFactoryKey); | |
let range = this.get(rangeKey); | |
let domain = this.get(domainKey); | |
let scaleType = this.get(scaleTypeKey); | |
let ordinalPadding = this.get(ordinalPaddingKey); | |
let ordinalOuterPadding = this.get(ordinalOuterPaddingKey); | |
let scale = scaleFactory().domain(domain); | |
if (scaleType === 'ordinal') { | |
scale.rangeBands(range, ordinalPadding, ordinalOuterPadding); | |
} else { | |
scale.range(range).clamp(true); | |
} | |
return scale; | |
}); | |
}; | |
const domainProperty = function(axis) { | |
let minKey = axis + 'Min'; | |
let maxKey = axis + 'Max'; | |
return computed(minKey, maxKey, function() { | |
let min = this.get(minKey); | |
let max = this.get(maxKey); | |
return [min, max]; | |
}); | |
}; | |
const minProperty = function(axis) { | |
var _DataExtent = axis + 'DataExtent'; | |
return computed(_DataExtent, function() { | |
return this.get(_DataExtent)[0]; | |
}); | |
}; | |
const maxProperty = function(axis) { | |
var _DataExtent = axis + 'DataExtent'; | |
return computed(_DataExtent, function() { | |
return this.get(_DataExtent)[1]; | |
}); | |
}; | |
export default Ember.Mixin.create({ | |
seriesData: (function() { | |
let _data = null; | |
return computed({ | |
get() { | |
return _data; | |
}, | |
set(key, value) { | |
_data = value; | |
this.updateExtents(value); | |
return value; | |
} | |
}); | |
})(), | |
clipPathId: computed(function() { | |
return null; | |
}), | |
graphTransform: computed(function() { | |
return null; | |
}), | |
dataExtents: (function() { | |
let _extents = { | |
xMin: Number.MAX_VALUE, | |
xMax: Number.MIN_VALUE, | |
yMin: Number.MAX_VALUE, | |
yMax: Number.MIN_VALUE | |
}; | |
return computed({ | |
get(key) { | |
return _extents; | |
}, | |
set(key, value) { | |
//log(`${key}.set()`, value); | |
_extents = value; | |
return value; | |
} | |
}); | |
})(), | |
updateExtents(dataObj) { | |
let extents = this.get('dataExtents'); | |
let series = Object.keys(dataObj); | |
let newExtents = series.reduce((a, v) => a.concat(dataObj[v]), []).reduce((a, v) => { | |
let x = v.x; | |
let y = v.y; | |
Ember.set(a, 'xMin', a.xMin < x ? a.xMin : x); | |
Ember.set(a, 'xMax', a.xMax > x ? a.xMax : x); | |
Ember.set(a, 'yMin', a.yMin < y ? a.yMin : y); | |
Ember.set(a, 'yMax', a.yMax > y ? a.yMax : y); | |
return a; | |
}, extents); | |
this.set('dataExtents', newExtents); | |
}, | |
xDataExtent: computed('dataExtents.xMin', 'dataExtents.xMax', function() { | |
let { xMin, xMax } = this.get('dataExtents'); | |
return [xMin, xMax]; | |
}), | |
yDataExtent: computed('dataExtents.yMin','dataExtents.yMax', function(){ | |
let { yMin, yMax } = this.get('dataExtents'); | |
return [yMin, yMax]; | |
}), | |
xOrdinalPadding: 0.1, | |
yOrdinalPadding: 0.1, | |
xOrdinalOuterPadding: 0.1, | |
yOrdinalOuterPadding: 0.1, | |
xPowerExponent: 3, | |
yPowerExponent: 3, | |
xScaleType: 'linear', | |
yScaleType: 'linear', | |
xScaleFactory: scaleFactoryProperty('x'), | |
yScaleFactory: scaleFactoryProperty('y'), | |
xScale: scaleProperty('x'), | |
yScale: scaleProperty('y'), | |
xDomain: domainProperty('x'), | |
yDomain: domainProperty('y'), | |
xRange: computed('graphWidth', function() { | |
return [0, this.get('graphWidth')]; | |
}), | |
yRange: computed('graphHeight', function() { | |
return [this.get('graphHeight'), 0]; | |
}), | |
xMin: minProperty('x'), | |
xMax: maxProperty('x'), | |
yMin: minProperty('y'), | |
yMax: maxProperty('y'), | |
hasData: computed.bool('seriesData'), | |
actions: { | |
updateDomain(data) { | |
console.log('updateDomain() called', data); | |
} | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
import ChartData from '../models/chart-data'; | |
const data = { | |
A: [ | |
{x:1469385570980, y:3}, | |
{x:1469731166074, y:6}, | |
{x:1469903961473, y:5}, | |
{x:1470076750392, y:2} | |
], | |
B: [ | |
{x:1469385570980, y:4}, | |
{x:1469731166074, y:3.5}, | |
{x:1469903961473, y:3}, | |
{x:1470076750392, y:4} | |
] | |
}; | |
export default Ember.Controller.extend({ | |
appName: 'Declarative D3 v2', | |
init() { | |
this._super(...arguments); | |
this.set('model', ChartData.create()); | |
//this.set('model', ChartData.create({seriesData: data})); | |
Ember.run.later(this, () => { | |
console.log('ADD DATA!!!!!'); | |
this.set('model.seriesData', data); | |
}, 1000); | |
Ember.run.later(this, () => { | |
console.log('ADD DATA!!!!!'); | |
let now = new Date().getTime(); | |
let copy = Object.assign({}, data); | |
copy.A.push({x: now, y: 4}); | |
copy.B.push({x: now, y: 7}); | |
this.set('model.seriesData', copy); | |
}, 3000); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
import ChartMixin from '../chart-model-mixin'; | |
const { | |
Object | |
} = Ember; | |
export default Object.extend(ChartMixin, { | |
// Overrides | |
xScaleType: 'time' | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
const { | |
Component, | |
computed | |
} = Ember; | |
const log = console.log; | |
export default Component.extend({ | |
tagName: 'svg', | |
classNames: ['rsa-chart'], | |
attributeBindings: ['width', 'height'], | |
width: 400, | |
height: 200, | |
marginTop: 10, | |
marginRight: 20, | |
marginBottom: 10, | |
marginLeft: 20, | |
xTickCount: computed(function() { | |
let width = this.get('graphWidth'); | |
return width / 80; | |
}), | |
showAxisY: false, | |
yAxis: null, | |
yAxisWidth: 20, | |
showAxisX: true, | |
xAxis: computed('model.xScale', function() { | |
let scale = this.get('model.xScale'); | |
let t = this.get('xTickCount'); | |
return d3.axisBottom(scale).ticks(t); | |
}), | |
xAxisHeight: 20, | |
clipPathId: computed('elementId', function() { | |
let id = this.get('elementId') + '-content-mask'; | |
this.set('model.clipPathId', id); | |
return id; | |
}), | |
graphX: computed('marginLeft', 'yAxis.width', function() { | |
let marginLeft = this.get('marginLeft'); | |
let yAxisWidth = this.get('yAxis.width') || 0; | |
return marginLeft + yAxisWidth; | |
}), | |
graphY: computed('marginTop', function() { | |
return this.get('marginTop'); | |
}), | |
graphWidth: computed('width', 'marginRight', 'marginLeft', 'yAxis.width', function() { | |
let marginRight = this.get('marginRight') || 0; | |
let marginLeft = this.get('marginLeft') || 0; | |
let yAxisWidth = this.get('yAxis.width') || 0; | |
let width = this.get('width') || 0; | |
let w = Math.max(0, width - marginRight - marginLeft - yAxisWidth); | |
this.set('model.graphWidth', w); | |
return w; | |
}), | |
graphHeight: computed('height', 'marginTop', 'marginBottom', function(){ | |
let marginTop = this.get('marginTop') || 0; | |
let marginBottom = this.get('marginBottom') || 0; | |
let xAxisHeight = this.get('showAxisX') ? this.get('xAxisHeight') : 0; | |
let height = this.get('height') || 0; | |
let h = Math.max(0, height - marginTop - marginBottom - xAxisHeight); | |
this.set('model.graphHeight', h); | |
return h; | |
}), | |
graphTransform: computed('graphX', 'graphY', function() { | |
let graphX = this.get('graphX'); | |
let graphY = this.get('graphY'); | |
let t = `translate(${graphX} ${graphY})`; | |
this.set('model.graphTransform', t); | |
return t; | |
}), | |
seriesDataWatcher: Ember.observer('model.seriesData', function() { | |
this.updateAxes(); | |
}), | |
init() { | |
this._super(...arguments); | |
this.get('graphTransform'); | |
}, | |
didInsertElement() { | |
this._super(...arguments); | |
this.set('svg', this.element); | |
if (this.get('showAxisX')) { | |
let axis = this.get('xAxis'); | |
let y = this.get('graphHeight')+10; | |
d3.select('.xAxis') | |
.attr('transform', 'translate(20,' + y + ')') | |
.call(axis); | |
} | |
}, | |
updateAxes() { | |
if (this.get('showAxisX')) { | |
let axis = this.get('xAxis'); | |
d3.select('.xAxis') | |
.transition().duration(750) | |
.call(axis); | |
} | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
margin: 12px 16px; | |
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
font-size: 12pt; | |
} | |
.rsa-chart-background { | |
fill: #eee; | |
} | |
.chart-content-background { | |
fill: #f3f3f3; | |
} | |
.chart-line { | |
fill: none; | |
stroke: steelblue; | |
stroke-width: 2; | |
} | |
.no-data { | |
font-size: 2em; | |
fill: #ddd; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"version": "0.10.5", | |
"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.7.0", | |
"ember-template-compiler": "2.7.0", | |
"d3": "https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.1/d3.min.js" | |
}, | |
"addons": {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment