diff --git a/src/models/lineWithFocusChart.js b/src/models/lineWithFocusChart.js index 1bbfe61..a105b6f 100644 --- a/src/models/lineWithFocusChart.js +++ b/src/models/lineWithFocusChart.js @@ -7,6 +7,7 @@ nv.models.lineWithFocusChart = function() { height = null, height2 = 100, showLegend = true, + brushExtent = null, tooltips = true, tooltip = function(key, x, y, e, graph) { return '

' + key + '

' + @@ -21,7 +22,7 @@ nv.models.lineWithFocusChart = function() { y2 = lines2.yScale(), xAxis = nv.models.axis().scale(x).orient('bottom').tickPadding(5), yAxis = nv.models.axis().scale(y).orient('left'), - x2Axis = nv.models.axis().scale(x).orient('bottom').tickPadding(5), + x2Axis = nv.models.axis().scale(x2).orient('bottom').tickPadding(5), y2Axis = nv.models.axis().scale(y2).orient('left'), legend = nv.models.legend().height(30), dispatch = d3.dispatch('tooltipShow', 'tooltipHide'), @@ -57,6 +58,8 @@ nv.models.lineWithFocusChart = function() { var wrap = container.selectAll('g.nv-wrap.nv-lineWithFocusChart').data([data]); var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineWithFocusChart').append('g'); + gEnter.append('g').attr('class', 'nv-legendWrap'); + var focusEnter = gEnter.append('g').attr('class', 'nv-focus'); focusEnter.append('g').attr('class', 'nv-x nv-axis'); focusEnter.append('g').attr('class', 'nv-y nv-axis'); @@ -66,9 +69,9 @@ nv.models.lineWithFocusChart = function() { contextEnter.append('g').attr('class', 'nv-x nv-axis'); contextEnter.append('g').attr('class', 'nv-y nv-axis'); contextEnter.append('g').attr('class', 'nv-linesWrap'); + contextEnter.append('g').attr('class', 'nv-brushBackground'); contextEnter.append('g').attr('class', 'nv-x nv-brush'); - gEnter.append('g').attr('class', 'nv-legendWrap'); var g = wrap.select('g'); @@ -86,7 +89,7 @@ nv.models.lineWithFocusChart = function() { if ( margin.top != legend.height()) { margin.top = legend.height(); availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; + - margin.top - margin.bottom - height2; } g.select('.nv-legendWrap') @@ -115,28 +118,34 @@ nv.models.lineWithFocusChart = function() { g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + /* var focusLinesWrap = g.select('.nv-focus .nv-linesWrap') .datum(data.filter(function(d) { return !d.disabled })) d3.transition(focusLinesWrap).call(lines); + */ xAxis .ticks( availableWidth / 100 ) .tickSize(-availableHeight, 0); + yAxis + .ticks( availableHeight / 36 ) + .tickSize( -availableWidth, 0); + g.select('.nv-focus .nv-x.nv-axis') .attr('transform', 'translate(0,' + y.range()[0] + ')'); + + +/* d3.transition(g.select('.nv-focus .nv-x.nv-axis')) .call(xAxis); - yAxis - .ticks( availableHeight / 36 ) - .tickSize( -availableWidth, 0); - d3.transition(g.select('.nv-focus .nv-y.nv-axis')) .call(yAxis); + */ @@ -149,6 +158,48 @@ nv.models.lineWithFocusChart = function() { d3.transition(contextLinesWrap).call(lines2); + + if (brushExtent) brush.extent(brushExtent); + + var brushBG = g.select('.nv-brushBackground').selectAll('g') + .data([brushExtent || brush.extent()]) + + var brushBGenter = brushBG.enter() + .append('g'); + + brushBGenter.append('rect') + .attr('class', 'left') + .attr('x', 0) + .attr('y', 0) + .attr('height', availableHeight2); + + brushBGenter.append('rect') + .attr('class', 'right') + .attr('x', 0) + .attr('y', 0) + .attr('height', availableHeight2); + + + function updateBrushBG() { + //nv.log('test', brush.empty(), brush.extent()); + if (!brush.empty()) brush.extent(brushExtent); + brushBG + .data([brush.empty() ? x2.domain() : brushExtent]) + //.data([brush.empty() ? xi.domain() : brush.extent()]) + .each(function(d,i) { + var leftWidth = x2(d[0]) - x.range()[0], + rightWidth = x.range()[1] - x2(d[1]); + d3.select(this).select('.left') + .attr('width', leftWidth < 0 ? 0 : leftWidth); + + d3.select(this).select('.right') + .attr('x', x2(d[1])) + .attr('width', rightWidth < 0 ? 0 : rightWidth); + }); + } + + + gBrush = g.select('.nv-x.nv-brush') .call(brush); gBrush.selectAll('rect') @@ -157,6 +208,10 @@ nv.models.lineWithFocusChart = function() { gBrush.selectAll('.resize').append('path').attr('d', resizePath); + //if (brushExtent) onBrush(); + onBrush(); + + x2Axis //.tickFormat(xAxis.tickFormat()) //exposing x2Axis so user can set differently @@ -178,8 +233,11 @@ nv.models.lineWithFocusChart = function() { .call(y2Axis); - updateFocus(); + g.select('.nv-focus .nv-x.nv-axis') + .attr('transform', 'translate(0,' + y.range()[0] + ')'); + g.select('.nv-context .nv-x.nv-axis') + .attr('transform', 'translate(0,' + y2.range()[0] + ')'); legend.dispatch.on('legendClick', function(d,i) { @@ -240,35 +298,54 @@ nv.models.lineWithFocusChart = function() { } + function onBrush() { - updateFocus(); + //nv.log(brush.empty(), brush.extent(), x2(brush.extent()[0]), x2(brush.extent()[1])); + /* focusLinesWrap.call(lines) //var focusLinesWrap = g.select('.focus .linesWrap') g.select('.nv-focus .nv-x.nv-axis').call(xAxis); g.select('.nv-focus .nv-y.nv-axis').call(yAxis); } + */ + brushExtent = brush.empty() ? null : brush.extent(); + extent = brush.empty() ? x2.domain() : brush.extent(); + + updateBrushBG(); - function updateFocus() { - var yDomain = brush.empty() ? y2.domain() : d3.extent(d3.merge(data.filter(function(d) { return !d.disabled }).map(function(d) { return d.values })).filter(function(d) { - return lines.x()(d) >= brush.extent()[0] && lines.x()(d) <= brush.extent()[1]; - }), lines.y()); //This doesn't account for the 1 point before and the 1 point after the domain. Would fix, but likely need to change entire methodology here - if (typeof yDomain[0] == 'undefined') yDomain = y2.domain(); //incase the brush doesn't cover a single point + var focusLinesWrap = g.select('.nv-focus .nv-linesWrap') + .datum( + data + .filter(function(d) { return !d.disabled }) + .map(function(d,i) { + return { + key: d.key, + values: d.values.filter(function(d,i) { + return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1]; + }) + } + }) + ) - x.domain(brush.empty() ? x2.domain() : brush.extent()); - y.domain(yDomain); + d3.transition(focusLinesWrap).call(lines); - //TODO: Rethink this... performance is horrible, likely need to cut off focus data to within the range - // If I limit the data for focusLines would want to include 1 point before and after the extent, - // Need to figure out an optimized way to accomplish this. - // ***One concern is to try not to make the assumption that all lines are of the same length, and - // points with the same index have the same x value (while this is true in our test cases, may - // not always be) - lines.xDomain(x.domain()); - lines.yDomain(y.domain()); + /* + var contextLinesWrap = g.select('.context .linesWrap') + .datum(data.filter(function(d) { return !d.disabled })) + + d3.transition(contextLinesWrap).call(lines2); + */ + + + d3.transition(g.select('.nv-focus .nv-x.nv-axis')) + .call(xAxis); + + d3.transition(g.select('.nv-focus .nv-y.nv-axis')) + .call(yAxis); } @@ -338,7 +415,7 @@ nv.models.lineWithFocusChart = function() { tooltip = _; return chart; }; - + chart.interpolate = function(_) { if (!arguments.length) return lines.interpolate(); lines.interpolate(_); diff --git a/src/nv.d3.css b/src/nv.d3.css index 8efe790..33ca6a7 100644 --- a/src/nv.d3.css +++ b/src/nv.d3.css @@ -536,18 +536,18 @@ svg .title { cursor: move; } -.nvd3.nv-historicalStockChart .nv-brush .extent { +.nvd3 .nv-brush .extent { /* cursor: ew-resize !important; */ fill-opacity: 0 !important; } -.nvd3.nv-historicalStockChart .nv-brushBackground rect { +.nvd3 .nv-brushBackground rect { stroke: #000; stroke-width: .4; fill: #fff; - fill-opacity: .5; + fill-opacity: .7; }