diff --git a/TODO.md b/TODO.md index 57f94f6..f1e9486 100644 --- a/TODO.md +++ b/TODO.md @@ -48,9 +48,9 @@ - [ ] Stacked Area - [X] Transition Enter State with Previous Scale - [X] How to handle Ordinal Scales where value didn't exist previously? (closest index?) -- [ ] Add Animation Easing Functions - - [ ] Basic Quad Easing - - [ ] Elastic Easing - - [ ] Linear Easing +- [X] Add Animation Easing Functions + - [X] Basic Quad Easing + - [X] Elastic Easing + - [X] Linear Easing - [ ] Auto Height/Width options for -- [ ] Canvas High DPI: http://www.html5rocks.com/en/tutorials/canvas/hidpi/ \ No newline at end of file +- [ ] Canvas High DPI: http://www.html5rocks.com/en/tutorials/canvas/hidpi/ diff --git a/addon/components/e3-layout.js b/addon/components/e3-layout.js new file mode 100644 index 0000000..cc92baa --- /dev/null +++ b/addon/components/e3-layout.js @@ -0,0 +1,72 @@ +import Ember from 'ember'; +const {guidFor} = Ember; + +let e3Layout = Ember.Component.extend({ + tagName: '', + + /* + At minimum, we should provide data array to a layout. + */ + attrs: { + data: null + }, + + /* + Any attributes changed; recalculate the layout. + */ + didUpdateAttrs() { + this.getAttr('_e3Context').updateMeta( + 'layouts', + this.getAttr('name'), + this._generateLayout() + ); + }, + + /* + Internal private hook to trigger the layout's implementation of the + using component's `generateLayout()` + */ + _generateLayout() { + let data = this.getData(); + return this.generateLayout(data); + }, + + /* + Hook to get the data that will be used to generate the layout. + */ + getData() { + return this.getAttr('data'); + }, + + /* + Generate item layout + */ + _generateItemLayout(data) { + return this.generateItemLayout(data); + }, + + /* + Actually generate the layout + */ + generateLayout(data) { + let guidMap = Object.create(null); + + // For each item, create a layout. + data.forEach(item => { + let guid = guidFor(item); + guidMap[guid] = this._generateItemLayout(item); + }); + + return function(item) { + return guidMap[guidFor(item)]; + }; + }, + + generateItemLayout(/*data*/) {} +}); + +e3Layout.reopenClass({ + positionalParams: ['_e3Context', 'name'] +}); + +export default e3Layout; diff --git a/addon/components/e3-layout/identity.js b/addon/components/e3-layout/identity.js new file mode 100644 index 0000000..e9aff51 --- /dev/null +++ b/addon/components/e3-layout/identity.js @@ -0,0 +1,28 @@ +import layout from '../e3-layout'; + +/* + This is more or less a utility layout that will take x & y scales and + create a layout for objects based on that layout. + */ +export default layout.extend({ + name: 'identity', + + attrs: { + x: null, + y: null + }, + + generateItemLayout(item) { + let xScale = this.getAttr('x'); + let yScale = this.getAttr('y'); + + if(!xScale || !yScale) { + return; + } + + return { + x: xScale(item), + y: yScale(item) + }; + } +}); diff --git a/addon/helpers/e3-bind-layout.js b/addon/helpers/e3-bind-layout.js new file mode 100644 index 0000000..b20eb28 --- /dev/null +++ b/addon/helpers/e3-bind-layout.js @@ -0,0 +1,25 @@ +import Ember from 'ember'; + +/* + A layout is a function that takes a model as an input, and returns an object + that represents the position for that object. Most likey, the returned result + of the layout is something like + { + x: {INTEGER}, + y: {INTEGER} + } + */ +export function e3BindLayout(params/*, hash*/) { + let [layout, layoutProp] = params; + + if(layout) { + return function(data) { + let itemLayout = layout(data); + if(itemLayout) { + return itemLayout[layoutProp]; + } + }; + } +} + +export default Ember.Helper.helper(e3BindLayout); diff --git a/addon/utils/shadow/scales/ordinal.js b/addon/utils/shadow/scales/ordinal.js index 6ce2fc5..1e317cb 100644 --- a/addon/utils/shadow/scales/ordinal.js +++ b/addon/utils/shadow/scales/ordinal.js @@ -48,7 +48,7 @@ export default function shadowScalesOrdinal(range = [0,1], domain = [0,1], optio } // Create the lookup map. - let map = {}; + let map = Object.create(null); let stepSpacing = (spaceBetween / max(1, (bands - 1))); // Create a lookup in the map for each item in the domain. @@ -95,4 +95,4 @@ function calculateMissingPosition(lookedUpVal, domain, sort) { } return found; -} \ No newline at end of file +} diff --git a/app/components/e3-layout.js b/app/components/e3-layout.js new file mode 100644 index 0000000..7f2872b --- /dev/null +++ b/app/components/e3-layout.js @@ -0,0 +1 @@ +export { default } from 'ember-e3/components/e3-layout'; \ No newline at end of file diff --git a/app/components/e3-layout/identity.js b/app/components/e3-layout/identity.js new file mode 100644 index 0000000..7a19399 --- /dev/null +++ b/app/components/e3-layout/identity.js @@ -0,0 +1 @@ +export { default } from 'ember-e3/components/e3-layout/identity'; \ No newline at end of file diff --git a/app/helpers/e3-bind-layout.js b/app/helpers/e3-bind-layout.js new file mode 100644 index 0000000..c739da6 --- /dev/null +++ b/app/helpers/e3-bind-layout.js @@ -0,0 +1 @@ +export { default, e3BindLayout } from 'ember-e3/helpers/e3-bind-layout'; diff --git a/tests/dummy/app/router.js b/tests/dummy/app/router.js index 593863c..4e1ca2a 100644 --- a/tests/dummy/app/router.js +++ b/tests/dummy/app/router.js @@ -16,6 +16,7 @@ Router.map(function() { this.route('grouped-bars'); this.route('invert'); this.route('nytimes-strikeouts'); + this.route('force-direction'); }); export default Router; diff --git a/tests/dummy/app/routes/bubble-1.js b/tests/dummy/app/routes/bubble-1.js index 0fca7c0..9876a5e 100644 --- a/tests/dummy/app/routes/bubble-1.js +++ b/tests/dummy/app/routes/bubble-1.js @@ -32,7 +32,6 @@ export default Ember.Route.extend({ return g(10); }, setupController(controller, model) { - window.c = this; controller.set('model', model); } }); diff --git a/tests/dummy/app/routes/force-direction.js b/tests/dummy/app/routes/force-direction.js new file mode 100644 index 0000000..9876a5e --- /dev/null +++ b/tests/dummy/app/routes/force-direction.js @@ -0,0 +1,37 @@ +import Ember from 'ember'; +let ID = 0; +// TODO: Use faker or something here. +function g(number) { + let res = []; + while(--number >= 0) { + res.push(o()); + } + return res; +} + +function o() { + return { + id: ++ID, + value: Math.random() * 100, + temperature: Math.random() * 100 + }; +} + +export default Ember.Route.extend({ + actions: { + add() { + let model = this.controller.get('model'); + model.push(o()); + this.controller.set('model', model.slice(0)); + }, + clear() { + this.controller.set('model', []); + } + }, + model() { + return g(10); + }, + setupController(controller, model) { + controller.set('model', model); + } +}); diff --git a/tests/dummy/app/templates/force-direction.hbs b/tests/dummy/app/templates/force-direction.hbs new file mode 100644 index 0000000..79687f8 --- /dev/null +++ b/tests/dummy/app/templates/force-direction.hbs @@ -0,0 +1,28 @@ +