function AreaChart() { var my = ChiasmComponent({ margin: { left: 20, top: 20, right: 20, bottom: 20 }, xColumn: Model.None, yColumn: Model.None, xScaleDomain: Model.None, yScaleDomain: Model.None, fill: "black", stroke: "none", strokeWidth: "1px", backgroundColor: "lightgray", border: "black", brushEnabled: false, brushIntervalX: Model.None }); var xScale = d3.time.scale(); var yScale = d3.scale.linear(); var brush = d3.svg.brush() .x(xScale) .on("brush", onBrush); var svg = d3.select(my.initSVG()); var clipRect = svg .append("clipPath") .attr("id", "clip") .append("rect"); var g = svg.append("g"); var borderRect = g.append("rect") .style("stroke-width", 2); var areaG = g.append("g") .style("clip-path", "url(#clip)"); var path = areaG.append("path"); var area = d3.svg.area() .interpolate("monotone"); var brushG = g.append("g") .attr("class", "brush"); my.when("backgroundColor", function (backgroundColor){ borderRect.style("fill", backgroundColor); }); my.when("border", function (border){ borderRect.style("stroke", border) }); // Respond to changes in size and margin. // Inspired by D3 margin convention from http://bl.ocks.org/mbostock/3019563 my.when(["box", "margin"], function(box, margin){ my.innerBox = { width: box.width - margin.left - margin.right, height: box.height - margin.top - margin.bottom }; g.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); }); my.when(["innerBox"], function (innerBox){ borderRect .attr("width", innerBox.width) .attr("height", innerBox.height); clipRect .attr("width", innerBox.width) .attr("height", innerBox.height); }); my.when(["data", "innerBox", "xColumn", "xScaleDomain"], function (data, innerBox, xColumn, xScaleDomain){ if(xColumn !== Model.None){ xScale.range([0, innerBox.width]); if(xScaleDomain !== Model.None){ xScale.domain(parseDates(xScaleDomain)); } else { xScale.domain(d3.extent(data, function (d){ return d[xColumn]; })); } my.x = function (d){ return xScale(d[xColumn]); }; } }); my.when(["data", "innerBox", "yColumn", "yScaleDomain"], function (data, innerBox, yColumn, yScaleDomain){ if(yColumn !== Model.None){ if(yScaleDomain !== Model.None){ yScale.domain(yScaleDomain); } else { yScale.domain([0, d3.max(data, function (d){ return d[yColumn]; })]); } yScale.range([innerBox.height, 0]); my.y = function (d){ return yScale(d[yColumn]); }; } }); my.when([ "data", "x", "y", "innerBox", "fill", "stroke", "strokeWidth" ], function (data, x, y, innerBox, fill, stroke, strokeWidth){ area .x(x) .y0(innerBox.height) .y1(y) path .datum(data) .attr("d", area) .attr("fill", fill) .attr("stroke", stroke) .attr("stroke-width", strokeWidth); }); my.when("brushEnabled", function (brushEnabled){ brushG.remove(); if(brushEnabled){ g.node().appendChild(brushG.node()); } }); function onBrush() { my.brushIntervalX = brush.empty() ? Model.None : brush.extent(); } function parseDates(dates){ return dates.map(function (date){ if(typeof date === Date){ return date; } else { return new Date(date); } }); } my.when(["brushIntervalX", "innerBox", "x", "y"], function (brushIntervalX, innerBox){ if(brushIntervalX !== Model.None){ brush.extent(parseDates(brushIntervalX)); // Uncomment this to see what the brush interval is as you drag. //console.log(brushIntervalX.map(function (date){ // return date.toUTCString(); //})); } brushG.call(brush); brushG.selectAll("rect") .attr("y", 0) .attr("height", innerBox.height); }); return my; }