2012-04-03 07:19:09 +00:00
nv . models . stackedAreaWithLegend = function ( ) {
var margin = { top : 30 , right : 20 , bottom : 50 , left : 60 } ,
2012-04-08 09:50:16 +00:00
getWidth = function ( ) { return 960 } ,
getHeight = function ( ) { return 500 } ,
2012-06-14 05:18:12 +00:00
color = d3 . scale . category20 ( ) . range ( ) ,
showControls = true ,
showLegend = true ;
2012-04-03 07:19:09 +00:00
var x = d3 . scale . linear ( ) ,
y = d3 . scale . linear ( ) ,
getX = function ( d ) { return d . x } ,
getY = function ( d ) { return d . y } ,
2012-05-17 06:04:33 +00:00
xAxis = nv . models . axis ( ) . scale ( x ) . orient ( 'bottom' ) ,
yAxis = nv . models . axis ( ) . scale ( y ) . orient ( 'left' ) ,
2012-04-03 07:19:09 +00:00
legend = nv . models . legend ( ) . height ( 30 ) ,
controls = nv . models . legend ( ) . height ( 30 ) ,
2012-05-28 22:31:26 +00:00
stacked = nv . models . stackedArea ( ) ,
dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' ) ;
2012-04-03 07:19:09 +00:00
2012-04-08 09:50:16 +00:00
//TODO: let user select default
2012-04-03 07:19:09 +00:00
var controlsData = [
{ key : 'Stacked' } ,
{ key : 'Stream' , disabled : true } ,
{ key : 'Expanded' , disabled : true }
] ;
function chart ( selection ) {
selection . each ( function ( data ) {
2012-04-08 09:50:16 +00:00
var width = getWidth ( ) ,
height = getHeight ( ) ,
availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ;
2012-05-30 19:18:18 +00:00
var seriesData = data . filter ( function ( d ) { return ! d . disabled } )
2012-04-03 07:19:09 +00:00
. reduce ( function ( prev , curr , index ) { //sum up all the y's
curr . values . forEach ( function ( d , i ) {
2012-05-28 23:08:12 +00:00
if ( ! index ) prev [ i ] = { x : getX ( d , i ) , y : 0 } ;
prev [ i ] . y += getY ( d , i ) ;
2012-04-03 07:19:09 +00:00
} ) ;
return prev ;
} , [ ] ) ;
2012-05-30 19:18:18 +00:00
x . domain ( d3 . extent ( d3 . merge ( seriesData ) , function ( d ) { return d . x } ) )
2012-04-08 09:50:16 +00:00
. range ( [ 0 , availableWidth ] ) ;
2012-04-03 07:19:09 +00:00
2012-04-03 20:35:59 +00:00
y . domain ( stacked . offset ( ) == 'zero' ?
2012-05-30 19:18:18 +00:00
[ 0 , d3 . max ( seriesData , function ( d ) { return d . y } ) ] :
2012-04-03 07:19:09 +00:00
[ 0 , 1 ] // 0 - 100%
)
2012-04-08 09:50:16 +00:00
. range ( [ availableHeight , 0 ] ) ;
2012-04-03 07:19:09 +00:00
stacked
2012-04-08 09:50:16 +00:00
. width ( availableWidth )
. height ( availableHeight )
2012-06-14 05:18:12 +00:00
//.color(color)
. color ( data . map ( function ( d , i ) {
return d . color || color [ i % 20 ] ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) )
2012-04-03 07:19:09 +00:00
2012-06-01 21:11:19 +00:00
var wrap = d3 . select ( this ) . selectAll ( 'g.wrap.stackedAreaWithLegend' ) . data ( [ data ] ) ;
2012-06-07 20:58:53 +00:00
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'wrap nvd3 stackedAreaWithLegend' ) . append ( 'g' ) ;
2012-04-03 07:19:09 +00:00
gEnter . append ( 'g' ) . attr ( 'class' , 'x axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'y axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'stackedWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'legendWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'controlsWrap' ) ;
2012-05-28 22:31:26 +00:00
2012-06-14 05:18:12 +00:00
var g = wrap . select ( 'g' ) ;
2012-05-28 22:31:26 +00:00
2012-06-14 05:18:12 +00:00
if ( showLegend ) {
//TODO: margins should be adjusted based on what components are used: axes, axis labels, legend
margin . top = legend . height ( ) ;
2012-05-28 22:31:26 +00:00
2012-06-14 05:18:12 +00:00
legend
. width ( width / 2 - margin . right )
. color ( color ) ;
2012-05-28 22:31:26 +00:00
2012-06-14 05:18:12 +00:00
g . select ( '.legendWrap' )
. datum ( data )
. attr ( 'transform' , 'translate(' + ( width / 2 - margin . left ) + ',' + ( - margin . top ) + ')' )
. call ( legend ) ;
}
2012-05-29 04:35:31 +00:00
2012-05-28 22:31:26 +00:00
2012-06-14 05:18:12 +00:00
if ( showControls ) {
controls . width ( 280 ) . color ( [ '#444' , '#444' , '#444' ] ) ;
g . select ( '.controlsWrap' )
. datum ( controlsData )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
. call ( controls ) ;
}
g . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
2012-05-28 22:31:26 +00:00
var stackedWrap = g . select ( '.stackedWrap' )
. datum ( data ) ;
d3 . transition ( stackedWrap ) . call ( stacked ) ;
xAxis
. domain ( x . domain ( ) )
. range ( x . range ( ) )
. ticks ( width / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.x.axis' )
. attr ( 'transform' , 'translate(0,' + availableHeight + ')' ) ;
d3 . transition ( g . select ( '.x.axis' ) )
. call ( xAxis ) ;
yAxis
. domain ( y . domain ( ) )
. range ( y . range ( ) )
. ticks ( stacked . offset ( ) == 'wiggle' ? 0 : height / 36 )
. tickSize ( - availableWidth , 0 )
. tickFormat ( stacked . offset ( ) == 'zero' ? d3 . format ( ',.2f' ) : d3 . format ( '%' ) ) ; //TODO: stacked format should be set by caller
d3 . transition ( g . select ( '.y.axis' ) )
. call ( yAxis ) ;
//TODO: FIX Logic error, screws up when series are disabled by clicking legend, then series are desiabled by clicking the area
stacked . dispatch . on ( 'areaClick.toggle' , function ( e ) {
2012-05-28 20:51:33 +00:00
if ( data . filter ( function ( d ) { return ! d . disabled } ) . length === 1 )
data = data . map ( function ( d ) {
if ( d . disabled )
d . values . map ( function ( p ) { p . y = p . _y || p . y ; return p } ) ; // ....
2012-06-09 07:58:14 +00:00
d . disabled = false ;
2012-05-28 20:51:33 +00:00
return d
} ) ;
else
data = data . map ( function ( d , i ) {
2012-06-09 07:58:14 +00:00
if ( ! d . disabled && i !== e . seriesIndex )
2012-05-28 20:51:33 +00:00
d . values . map ( function ( p ) { p . _y = p . y ; p . y = 0 ; return p } ) ; //TODO: need to use value from getY, not always d.y
2012-06-09 07:58:14 +00:00
if ( d . disabled && i === e . seriesIndex )
2012-05-28 20:51:33 +00:00
d . values . map ( function ( p ) { p . y = p . _y || p . y ; return p } ) ; // ....
2012-06-09 07:58:14 +00:00
d . disabled = ( i != e . seriesIndex ) ;
2012-05-28 20:51:33 +00:00
return d
} ) ;
2012-04-03 07:19:09 +00:00
2012-05-28 20:51:33 +00:00
selection . transition ( ) . call ( chart ) ;
} ) ;
2012-04-03 07:19:09 +00:00
legend . dispatch . on ( 'legendClick' , function ( d , i ) {
d . disabled = ! d . disabled ;
if ( d . disabled )
2012-04-08 09:50:16 +00:00
d . values . map ( function ( p ) { p . _y = p . y ; p . y = 0 ; return p } ) ; //TODO: need to use value from getY, not always d.y
2012-04-03 07:19:09 +00:00
else
2012-04-08 09:50:16 +00:00
d . values . map ( function ( p ) { p . y = p . _y ; return p } ) ; // ....
2012-04-03 07:19:09 +00:00
if ( ! data . filter ( function ( d ) { return ! d . disabled } ) . length ) {
data . map ( function ( d ) {
d . disabled = false ;
2012-04-08 09:50:16 +00:00
d . values . map ( function ( p ) { p . y = p . _y ; return p } ) ; // ....
2012-04-03 07:19:09 +00:00
return d ;
} ) ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
controls . dispatch . on ( 'legendClick' , function ( d , i ) {
if ( ! d . disabled ) return ;
controlsData = controlsData . map ( function ( s ) {
s . disabled = true ;
return s ;
} ) ;
d . disabled = false ;
switch ( d . key ) {
case 'Stacked' :
stacked . style ( 'stack' ) ;
break ;
case 'Stream' :
stacked . style ( 'stream' ) ;
break ;
case 'Expanded' :
stacked . style ( 'expand' ) ;
break ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
2012-04-08 09:50:16 +00:00
/ *
2012-04-03 07:19:09 +00:00
legend . dispatch . on ( 'legendMouseover' , function ( d , i ) {
d . hover = true ;
selection . transition ( ) . call ( chart )
} ) ;
legend . dispatch . on ( 'legendMouseout' , function ( d , i ) {
d . hover = false ;
selection . transition ( ) . call ( chart )
} ) ;
2012-04-08 09:50:16 +00:00
* /
2012-04-03 07:19:09 +00:00
2012-05-07 12:53:51 +00:00
stacked . dispatch . on ( 'tooltipShow' , function ( e ) {
2012-05-11 20:57:54 +00:00
//disable tooltips when value ~= 0
2012-05-14 19:23:11 +00:00
//// TODO: consider removing points from voronoi that have 0 value instead of this hack
2012-05-11 20:57:54 +00:00
if ( ! Math . round ( getY ( e . point ) * 100 ) ) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
setTimeout ( function ( ) { d3 . selectAll ( '.point.hover' ) . classed ( 'hover' , false ) } , 0 ) ;
return false ;
}
2012-06-09 07:58:14 +00:00
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ,
dispatch . tooltipShow ( e ) ;
2012-04-03 07:19:09 +00:00
} ) ;
2012-05-07 12:53:51 +00:00
stacked . dispatch . on ( 'tooltipHide' , function ( e ) {
2012-04-03 07:19:09 +00:00
dispatch . tooltipHide ( e ) ;
} ) ;
} ) ;
2012-06-09 07:58:14 +00:00
/ *
// If the legend changed the margin's height, need to recalc positions... should think of a better way to prevent duplicate work
if ( margin . top != legend . height ( ) )
chart ( selection ) ;
* /
2012-04-03 07:19:09 +00:00
return chart ;
}
2012-06-14 05:18:12 +00:00
2012-04-03 07:19:09 +00:00
chart . dispatch = dispatch ;
2012-06-14 05:18:12 +00:00
chart . stacked = stacked ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
2012-04-03 07:19:09 +00:00
2012-06-14 05:18:12 +00:00
d3 . rebind ( chart , stacked , 'interactive' , 'offset' , 'order' , 'style' , 'clipEdge' , 'size' , 'forceX' , 'forceY' , 'forceSize' ) ;
2012-05-28 23:08:12 +00:00
2012-04-03 07:19:09 +00:00
chart . x = function ( _ ) {
if ( ! arguments . length ) return getX ;
2012-05-22 23:16:42 +00:00
getX = d3 . functor ( _ ) ; //not used locally, so could jsut be a rebind
2012-04-08 09:50:16 +00:00
stacked . x ( getX ) ;
2012-04-03 07:19:09 +00:00
return chart ;
} ;
chart . y = function ( _ ) {
if ( ! arguments . length ) return getY ;
2012-04-08 09:50:16 +00:00
getY = d3 . functor ( _ ) ;
stacked . y ( getY ) ;
2012-04-03 07:19:09 +00:00
return chart ;
} ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin = _ ;
return chart ;
} ;
chart . width = function ( _ ) {
2012-04-08 09:50:16 +00:00
if ( ! arguments . length ) return getWidth ;
getWidth = d3 . functor ( _ ) ;
2012-04-03 07:19:09 +00:00
return chart ;
} ;
chart . height = function ( _ ) {
2012-04-08 09:50:16 +00:00
if ( ! arguments . length ) return getHeight ;
getHeight = d3 . functor ( _ ) ;
2012-04-03 07:19:09 +00:00
return chart ;
} ;
2012-06-14 05:18:12 +00:00
chart . showControls = function ( _ ) {
if ( ! arguments . length ) return showControls ;
showControls = _ ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
2012-04-03 07:19:09 +00:00
return chart ;
}