export default function() { let x = (d) => d.x; let y = (d) => d.y; let xScale = scaleIdentity(); let yScale = scaleIdentity(); let decorate = () => {}; const join = dataJoin('g', 'annotation-crosshair'); const point = seriesSvgPoint(); const horizontalLine = annotationLine(); const verticalLine = annotationLine() .orient('vertical'); // The line annotations and point series used to render the crosshair are positioned using // screen coordinates. This function constructs an identity scale for these components. const xIdentity = scaleIdentity(); const yIdentity = scaleIdentity(); const multi = seriesSvgMulti() .series([horizontalLine, verticalLine, point]) .xScale(xIdentity) .yScale(yIdentity) .mapping((data) => [data]); const instance = (selection) => { selection.each((data, index, nodes) => { const g = join(select(nodes[index]), data); // Prevent the crosshair triggering pointer events on itself g.enter() .style('pointer-events', 'none'); // Assign the identity scales an accurate range to allow the line annotations to cover // the full width/height of the chart. xIdentity.range(xScale.range()); yIdentity.range(yScale.range()); point.xValue(x) .yValue(y); horizontalLine.value(y); verticalLine.value(x); g.call(multi); decorate(g, data, index); }); }; // Don't use the xValue/yValue convention to indicate that these values are in screen // not domain co-ordinates and are therefore not scaled. instance.x = (...args) => { if (!args.length) { return x; } x = args[0]; return instance; }; instance.y = (...args) => { if (!args.length) { return y; } y = args[0]; return instance; }; instance.xScale = (...args) => { if (!args.length) { return xScale; } xScale = args[0]; return instance; }; instance.yScale = (...args) => { if (!args.length) { return yScale; } yScale = args[0]; return instance; }; instance.decorate = (...args) => { if (!args.length) { return decorate; } decorate = args[0]; return instance; }; const lineIncludes = include('label'); rebindAll(instance, horizontalLine, lineIncludes, prefix('y')); rebindAll(instance, verticalLine, lineIncludes, prefix('x')); return instance; }
export default function() { // creates an array with four elements, representing the high, low, open and close // values of the given array function highLowOpenClose(data) { var xValueAccessor = sparkline.xValue(), yValueAccessor = sparkline.yValue(); var high = d3.max(data, yValueAccessor); var low = d3.min(data, yValueAccessor); function elementWithYValue(value) { return data.filter(function(d) { return yValueAccessor(d) === value; })[0]; } return [{ x: xValueAccessor(data[0]), y: yValueAccessor(data[0]) }, { x: xValueAccessor(elementWithYValue(high)), y: high }, { x: xValueAccessor(elementWithYValue(low)), y: low }, { x: xValueAccessor(data[data.length - 1]), y: yValueAccessor(data[data.length - 1]) }]; } var xScale = dateTime(); var yScale = d3.scale.linear(); var radius = 2; var line = _line(); // configure the point series to render the data from the // highLowOpenClose function var point = _point() .xValue(function(d) { return d.x; }) .yValue(function(d) { return d.y; }) .decorate(function(sel) { sel.attr('class', function(d, i) { switch (i) { case 0: return 'open'; case 1: return 'high'; case 2: return 'low'; case 3: return 'close'; } }); }); var multi = _multi() .series([line, point]) .mapping(function(series) { switch (series) { case point: return highLowOpenClose(this); default: return this; } }); var sparkline = function(selection) { point.size(radius * radius * Math.PI); selection.each(function(data) { var container = d3.select(this); var dimensions = innerDimensions(this); var margin = radius; xScale.range([margin, dimensions.width - margin]); yScale.range([dimensions.height - margin, margin]); multi.xScale(xScale) .yScale(yScale); container.call(multi); }); }; rebindAll(sparkline, xScale, include('discontinuityProvider', 'domain'), prefix('x')); rebindAll(sparkline, yScale, include('domain'), prefix('y')); rebindAll(sparkline, line, include('xValue', 'yValue')); sparkline.xScale = function() { return xScale; }; sparkline.yScale = function() { return yScale; }; sparkline.radius = function(x) { if (!arguments.length) { return radius; } radius = x; return sparkline; }; return sparkline; }
export default function() { var x = function(d) { return d.x; }, y = function(d) { return d.y; }, xScale = d3.scale.identity(), yScale = d3.scale.identity(), decorate = noop; var dataJoin = dataJoinUtil() .children(true) .selector('g.crosshair') .element('g') .attr('class', 'crosshair'); var pointSeries = point() .xValue(x) .yValue(y); var horizontalLine = line() .value(y) .label(y); var verticalLine = line() .orient('vertical') .value(x) .label(x); // The line annotations and point series used to render the crosshair are positioned using // screen coordinates. This function constructs an identity scale for these components. var identityXScale = d3.scale.identity(); var identityYScale = d3.scale.identity(); var multi = multiSeries() .series([horizontalLine, verticalLine, pointSeries]) .xScale(identityXScale) .yScale(identityYScale) .mapping(function() { return [this]; }); var crosshair = function(selection) { selection.each(function(data, index) { var container = d3.select(this); var crosshairElement = dataJoin(container, data); crosshairElement.enter() .style('pointer-events', 'none'); // Assign the identity scales an accurate range to allow the line annotations to cover // the full width/height of the chart. identityXScale.range(range(xScale)); identityYScale.range(range(yScale)); crosshairElement.call(multi); decorate(crosshairElement, data, index); }); }; // Don't use the xValue/yValue convention to indicate that these values are in screen // not domain co-ordinates and are therefore not scaled. crosshair.x = function(_x) { if (!arguments.length) { return x; } x = _x; return crosshair; }; crosshair.y = function(_x) { if (!arguments.length) { return y; } y = _x; return crosshair; }; crosshair.xScale = function(_x) { if (!arguments.length) { return xScale; } xScale = _x; return crosshair; }; crosshair.yScale = function(_x) { if (!arguments.length) { return yScale; } yScale = _x; return crosshair; }; crosshair.decorate = function(_x) { if (!arguments.length) { return decorate; } decorate = _x; return crosshair; }; var lineIncludes = include('label'); rebindAll(crosshair, horizontalLine, lineIncludes, prefix('y')); rebindAll(crosshair, verticalLine, lineIncludes, prefix('x')); return crosshair; }
export default function(xScale, yScale) { xScale = xScale || d3.scale.linear(); yScale = yScale || d3.scale.linear(); var margin = { bottom: 30, right: 30 }, yLabel = '', xLabel = '', xBaseline = null, yBaseline = null, chartLabel = '', plotArea = line(), decorate = noop; // Each axis-series has a cross-scale which is defined as an identity // scale. If no baseline function is supplied, the axis is positioned // using the cross-scale range extents. If a baseline function is supplied // it is transformed via the respective scale. var xAxis = axis() .orient('bottom') .baseline(function() { if (xBaseline !== null) { return yScale(xBaseline.apply(this, arguments)); } else { var r = range(yScale); return xAxis.orient() === 'bottom' ? r[0] : r[1]; } }); var yAxis = axis() .orient('right') .baseline(function() { if (yBaseline !== null) { return xScale(yBaseline.apply(this, arguments)); } else { var r = range(xScale); return yAxis.orient() === 'left' ? r[0] : r[1]; } }); var containerDataJoin = dataJoin() .selector('svg.cartesian-chart') .element('svg') .attr({'class': 'cartesian-chart', 'layout-style': 'flex: 1'}); var cartesian = function(selection) { selection.each(function(data, index) { var container = d3.select(this); var svg = containerDataJoin(container, [data]); svg.enter().html( '<g class="plot-area-container"> \ <rect class="background" \ layout-style="position: absolute; top: 0; bottom: 0; left: 0; right: 0"/> \ <g class="axes-container" \ layout-style="position: absolute; top: 0; bottom: 0; left: 0; right: 0"> \ <g class="x-axis" layout-style="height: 0; width: 0"/> \ <g class="y-axis" layout-style="height: 0; width: 0"/> \ </g> \ <svg class="plot-area" \ layout-style="position: absolute; top: 0; bottom: 0; left: 0; right: 0"/> \ </g> \ <g class="x-axis label-container"> \ <g layout-style="height: 0; width: 0"> \ <text class="label" dy="-0.5em"/> \ </g> \ </g> \ <g class="y-axis label-container"> \ <g layout-style="height: 0; width: 0"> \ <text class="label"/> \ </g> \ </g> \ <g class="title label-container"> \ <g layout-style="height: 0; width: 0"> \ <text class="label"/> \ </g> \ </g>'); var expandedMargin = expandRect(margin); svg.select('.plot-area-container') .layout({ position: 'absolute', top: expandedMargin.top, left: expandedMargin.left, bottom: expandedMargin.bottom, right: expandedMargin.right }); svg.select('.title') .layout({ position: 'absolute', top: 0, alignItems: 'center', left: expandedMargin.left, right: expandedMargin.right }); var yAxisLayout = { position: 'absolute', top: expandedMargin.top, bottom: expandedMargin.bottom, alignItems: 'center', flexDirection: 'row' }; yAxisLayout[yAxis.orient()] = 0; svg.select('.y-axis.label-container') .attr('class', 'y-axis label-container ' + yAxis.orient()) .layout(yAxisLayout); var xAxisLayout = { position: 'absolute', left: expandedMargin.left, right: expandedMargin.right, alignItems: 'center' }; xAxisLayout[xAxis.orient()] = 0; svg.select('.x-axis.label-container') .attr('class', 'x-axis label-container ' + xAxis.orient()) .layout(xAxisLayout); // perform the flexbox / css layout container.layout(); // update the label text svg.select('.title .label') .text(chartLabel); svg.select('.y-axis.label-container .label') .text(yLabel) .attr('transform', yAxis.orient() === 'right' ? 'rotate(90)' : 'rotate(-90)'); svg.select('.x-axis.label-container .label') .text(xLabel); // set the axis ranges var plotAreaContainer = svg.select('.plot-area'); setRange(xScale, [0, plotAreaContainer.layout('width')]); setRange(yScale, [plotAreaContainer.layout('height'), 0]); // render the axes xAxis.xScale(xScale) .yScale(d3.scale.identity()); yAxis.yScale(yScale) .xScale(d3.scale.identity()); svg.select('.axes-container .x-axis') .call(xAxis); svg.select('.axes-container .y-axis') .call(yAxis); // render the plot area plotArea.xScale(xScale) .yScale(yScale); plotAreaContainer.call(plotArea); decorate(svg, data, index); }); }; var scaleExclusions = exclude( /range\w*/, // the scale range is set via the component layout /tickFormat/ // use axis.tickFormat instead (only present on linear scales) ); rebindAll(cartesian, xScale, scaleExclusions, exclude('ticks'), prefix('x')); rebindAll(cartesian, yScale, scaleExclusions, exclude('ticks'), prefix('y')); // The scale ticks method is a stateless method that returns (roughly) the number of ticks // requested. This is subtley different from the axis ticks methods that simply stores the given arguments // for invocation of the scale method at some point in the future. // Here we expose the underling scale ticks method in case the user want to generate their own ticks. if (!isOrdinal(xScale)) { rebindAll(cartesian, xScale, includeMap({'ticks': 'xScaleTicks'})); } if (!isOrdinal(yScale)) { rebindAll(cartesian, yScale, includeMap({'ticks': 'yScaleTicks'})); } var axisExclusions = exclude( 'baseline', // the axis baseline is adapted so is not exposed directly 'xScale', 'yScale' // these are set by this components ); rebindAll(cartesian, xAxis, axisExclusions, prefix('x')); rebindAll(cartesian, yAxis, axisExclusions, prefix('y')); cartesian.xBaseline = function(x) { if (!arguments.length) { return xBaseline; } xBaseline = d3.functor(x); return cartesian; }; cartesian.yBaseline = function(x) { if (!arguments.length) { return yBaseline; } yBaseline = d3.functor(x); return cartesian; }; cartesian.chartLabel = function(x) { if (!arguments.length) { return chartLabel; } chartLabel = x; return cartesian; }; cartesian.plotArea = function(x) { if (!arguments.length) { return plotArea; } plotArea = x; return cartesian; }; cartesian.xLabel = function(x) { if (!arguments.length) { return xLabel; } xLabel = x; return cartesian; }; cartesian.margin = function(x) { if (!arguments.length) { return margin; } margin = x; return cartesian; }; cartesian.yLabel = function(x) { if (!arguments.length) { return yLabel; } yLabel = x; return cartesian; }; cartesian.decorate = function(x) { if (!arguments.length) { return decorate; } decorate = x; return cartesian; }; return cartesian; }