Skip to content

Commit

Permalink
Merge pull request #53 from twjohnston/stacked-bar
Browse files Browse the repository at this point in the history
Add stacked-bar chart type
  • Loading branch information
twjohnston committed May 4, 2018
2 parents 50eeb1c + 00ba172 commit 596dae9
Show file tree
Hide file tree
Showing 24 changed files with 1,438 additions and 1,266 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ dist: trusty
language: node_js

node_js:
- '8.1.2'
- '8.6.0'
- 'stable'

addons:
Expand Down Expand Up @@ -63,7 +63,7 @@ deploy:
on:
all_branches: true
condition: "$EMBER_TRY_SCENARIO = 'ember-default'"
node: '8.1.2'
node: '8.6.0'
tags: true

after_deploy:
Expand Down
2 changes: 1 addition & 1 deletion .travis/maybe-bump-version.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

if [[ ! "${PUBLISH_NODE_VERSION:-8.1.2}" =~ ^$TRAVIS_NODE_VERSION ]]
if [[ ! "${PUBLISH_NODE_VERSION:-8.6.0}" =~ ^$TRAVIS_NODE_VERSION ]]
then
echo "Skipping pr-bumper bump step for TRAVIS_NODE_VERSION [${TRAVIS_NODE_VERSION}]"
exit 0
Expand Down
2 changes: 1 addition & 1 deletion .travis/maybe-publish-coverage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ then
exit 0
fi

if [[ ! "${PUBLISH_NODE_VERSION:-8.1.2}" =~ ^$TRAVIS_NODE_VERSION ]]
if [[ ! "${PUBLISH_NODE_VERSION:-8.6.0}" =~ ^$TRAVIS_NODE_VERSION ]]
then
echo "Skipping pr-bumper coverage publish step for TRAVIS_NODE_VERSION [${TRAVIS_NODE_VERSION}]"
exit 0
Expand Down
24 changes: 17 additions & 7 deletions addon/components/frost-chart-axis-tick.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export default Component.extend({
propTypes: {
// options
axis: PropTypes.string.isRequired,
coordinate: PropTypes.number
coordinate: PropTypes.number,
width: PropTypes.number,
height: PropTypes.number

// state
},
Expand All @@ -38,8 +40,8 @@ export default Component.extend({
// == Computed Properties ===================================================

@readOnly
@computed('coordinate')
style (coordinate) {
@computed('coordinate', 'width', 'height')
style (coordinate, width, height) {
if (isNone(coordinate)) {
return EmberString.htmlSafe('')
}
Expand All @@ -48,25 +50,33 @@ export default Component.extend({
if (this.get('axis') === 'x') {
return EmberString.htmlSafe(`
position: absolute;
left: calc(${coordinate}px - ${this.$().outerWidth()}px / 2);
left: calc(${coordinate}px - ${width}px / 2);
`)
} else {
return EmberString.htmlSafe(`
position: absolute;
top: calc(${coordinate}px - ${this.$().outerHeight()}px / 2);
top: calc(${coordinate}px - ${height}px / 2);
`)
}
},

// == Functions =============================================================

_dispatchRenderedTick () {
const width = this.$().outerWidth()
const height = this.$().outerHeight()

this.setProperties({
width,
height
})

this.dispatch({
type: 'RENDERED_TICK',
axis: this.get('axis'),
tick: {
height: this.$().outerHeight(true),
width: this.$().outerWidth(true)
height,
width
}
})
},
Expand Down
87 changes: 87 additions & 0 deletions addon/components/frost-chart-svg-plot-stacked-bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {stack as d3Stack} from 'd3-shape'
import computed, {readOnly} from 'ember-computed-decorators'
import {Component} from 'ember-frost-core'
import {PropTypes} from 'ember-prop-types'
import layout from '../templates/components/frost-chart-svg-plot-stacked-bar'

export default Component.extend({

// == Dependencies ==========================================================

// == Keyword Properties ====================================================

layout,
tagName: 'g',

// == PropTypes =============================================================

propTypes: {
// options
chartState: PropTypes.EmberObject.isRequired,
data: PropTypes.array.isRequired,
x: PropTypes.string.isRequired,
y: PropTypes.string.isRequired,
seriesKeys: PropTypes.arrayOf(PropTypes.string).isRequired,
seriesColors: PropTypes.array

// state
},

getDefaultProps () {
return {
// options
seriesColors: [
'#009eef',
'#a183db',
'#009999',
'#a1e7ff',
'#e8dffb',
'#9aefea'
]

// state
}
},

// == Computed Properties ===================================================

@readOnly
@computed('data.[]', 'x', 'chartState.range.x', 'chartState.range.y',
'chartState.domain.x', 'chartState.domain.y', 'seriesKeys', 'seriesColors')
_bars (data, xProp, xRange, yRange, xDomain, yDomain, seriesKeys, seriesColors) {
if (!xRange || !yRange || !xDomain || !yDomain) {
return []
}

const stackLayout = d3Stack().keys(seriesKeys)
const stackedData = stackLayout(data)

const xScale = this.get('chartState.scale.x')
const xTransform = xScale({domain: xDomain, range: xRange})

const yScale = this.get('chartState.scale.y')
const yTransform = yScale({domain: yDomain, range: yRange})

return stackedData.reduce((arr, layer, index) => {
return arr.concat(layer.map(entry => {
return {
x: xTransform(entry.data[xProp]),
y: yTransform(entry[1]),
width: xTransform.bandwidth(),
height: yTransform(entry[0]) - yTransform(entry[1]),
color: seriesColors[index % seriesColors.length], // Repeat colors if too few are provided
seriesIndex: index
}
}))
}, [])
}

// == Functions =============================================================

// == DOM Events ============================================================

// == Lifecycle Hooks =======================================================

// == Actions ===============================================================

})
20 changes: 15 additions & 5 deletions addon/components/frost-chart-x-axis.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
*/

import Ember from 'ember'
const {String: EmberString, assign, get} = Ember
import computed, {readOnly} from 'ember-computed-decorators'
import {Component} from 'ember-frost-core'
import {PropTypes} from 'ember-prop-types'
import {linearTicks} from '../helpers/linear-ticks'

import layout from '../templates/components/frost-chart-x-axis'

const {String: EmberString, assign, get, isPresent} = Ember

export default Component.extend({

// == Dependencies ==========================================================
Expand Down Expand Up @@ -61,8 +61,8 @@ export default Component.extend({
},

@readOnly
@computed('_ticks', 'chartState.range.x', 'chartState.domain.x')
_positionedTicks (ticks, range, domain) {
@computed('_ticks', 'chartState.range.x', 'chartState.domain.x', '_tickOffset')
_positionedTicks (ticks, range, domain, tickOffset) {
if (!range || !domain) {
return ticks
}
Expand All @@ -72,7 +72,7 @@ export default Component.extend({

return ticks.map(tick => {
return assign({}, tick, {
coordinate: transform(get(tick, 'value'))
coordinate: transform(get(tick, 'value')) + tickOffset
})
})
},
Expand Down Expand Up @@ -106,6 +106,16 @@ export default Component.extend({
return EmberString.htmlSafe(`height: ${tickHeight}px;`)
},

@readOnly
@computed('chartState.scale.x', 'chartState.domain.x', 'chartState.range.x')
_tickOffset (scale, domain, range) {
if (!isPresent(scale) || !isPresent(domain) || !isPresent(range)) {
return 0
}
const transform = scale({domain, range})
return isPresent(transform.bandwidth) ? transform.bandwidth() / 2 : 0
},

// == Functions =============================================================

_dispatchRenderedAxis () {
Expand Down
1 change: 0 additions & 1 deletion addon/components/frost-chart-y-axis.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import computed, {readOnly} from 'ember-computed-decorators'
import {Component} from 'ember-frost-core'
import {linearTicks} from '../helpers/linear-ticks'
import {PropTypes} from 'ember-prop-types'

import layout from '../templates/components/frost-chart-y-axis'

export default Component.extend({
Expand Down
5 changes: 4 additions & 1 deletion addon/components/frost-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ export default Component.extend({

propTypes: {
// options
xDomain: PropTypes.arrayOf(PropTypes.number).isRequired,
xDomain: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.number),
PropTypes.arrayOf(PropTypes.string)
]).isRequired,
xRange: PropTypes.arrayOf(PropTypes.number),
xScale: PropTypes.func,
yDomain: PropTypes.arrayOf(PropTypes.number).isRequired,
Expand Down
14 changes: 14 additions & 0 deletions addon/helpers/d3-band-scale.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Helper definition for the d3-band-scale helper
*/
import {scaleBand} from 'd3-scale'
import Ember from 'ember'
const {Helper} = Ember

export function bandScale () {
return function ({domain, range}) {
return scaleBand().domain(domain).rangeRound(range).paddingInner(0.2)
}
}

export default Helper.helper(bandScale)
20 changes: 20 additions & 0 deletions addon/helpers/ordinal-ticks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Ember from 'ember'
const {A, Helper, Object: EmberObject} = Ember

export function ordinalTicks () {
return function (domain) {
const _ticks = A()
const numTicks = domain.length
_ticks.addObjects(Array.from({length: numTicks}, (entry, index) => {
const tick = domain[index]
return EmberObject.create({
index: index + 1,
value: tick
})
}))

return _ticks
}
}

export default Helper.helper(ordinalTicks)
5 changes: 5 additions & 0 deletions addon/styles/_frost-chart-svg-plot-stacked-bar.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.frost-chart-svg-plot-stacked-bar {
rect {
fill-opacity: .6;
}
}
4 changes: 4 additions & 0 deletions addon/styles/_frost-chart.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.frost-chart {
position: relative;

.frost-chart-svg {
overflow: visible;
}
}
1 change: 1 addition & 0 deletions addon/styles/addon.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
@import './frost-chart-svg-plot-line';
@import './frost-chart-svg-plot-scatter';
@import './frost-chart-svg-plot-grid';
@import './frost-chart-svg-plot-stacked-bar';
@import './frost-gauge';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{#each _bars as |bar|}}
{{yield bar}}
{{/each}}
6 changes: 6 additions & 0 deletions addon/templates/components/frost-chart-svg.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
hook=(concat hookPrefix '-bar')
)

stacked-bar=(component 'frost-chart-svg-plot-stacked-bar'
bar=(vertical-bar)
chartState=chartState
hook=(concat hookPrefix '-stacked-bar')
)

x-grid=(component 'frost-chart-svg-plot-x-grid'
chartState=chartState
hook=(concat hookPrefix '-xGrid')
Expand Down
4 changes: 4 additions & 0 deletions app/components/frost-chart-svg-plot-stacked-bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Simple re-export of frost-chart-svg-plot-stacked-bar in the app namespace
*/
export {default} from 'ember-frost-chart/components/frost-chart-svg-plot-stacked-bar'
4 changes: 4 additions & 0 deletions app/helpers/d3-band-scale.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Re-export the d3-band-scale helper into the app namespace
*/
export {default} from 'ember-frost-chart/helpers/d3-band-scale'
4 changes: 4 additions & 0 deletions app/helpers/ordinal-ticks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Re-export the ordinal-ticks helper into the app namespace
*/
export {default} from 'ember-frost-chart/helpers/ordinal-ticks'
Loading

0 comments on commit 596dae9

Please sign in to comment.