export default (...args) => { const { xScale, yScale, xAxis, yAxis } = getArguments(...args); let xLabel = functor(''); let yLabel = functor(''); let xAxisHeight = functor(null); let yAxisWidth = functor(null); let yOrient = functor('right'); let xOrient = functor('bottom'); let canvasPlotArea = seriesCanvasMulti(); let svgPlotArea = seriesSvgMulti(); let xAxisStore = store('tickFormat', 'ticks', 'tickArguments', 'tickSize', 'tickSizeInner', 'tickSizeOuter', 'tickValues', 'tickPadding', 'tickCenterLabel'); let xDecorate = () => { }; let yAxisStore = store('tickFormat', 'ticks', 'tickArguments', 'tickSize', 'tickSizeInner', 'tickSizeOuter', 'tickValues', 'tickPadding', 'tickCenterLabel'); let yDecorate = () => { }; let decorate = () => { }; const containerDataJoin = dataJoin('d3fc-group', 'cartesian-chart'); const xAxisDataJoin = dataJoin('d3fc-svg', 'x-axis') .key(d => d); const yAxisDataJoin = dataJoin('d3fc-svg', 'y-axis') .key(d => d); const xLabelDataJoin = dataJoin('div', 'x-label') .key(d => d); const yLabelDataJoin = dataJoin('div', 'y-label') .key(d => d); const propagateTransition = maybeTransition => selection => maybeTransition.selection ? selection.transition(maybeTransition) : selection; const cartesian = (selection) => { const transitionPropagator = propagateTransition(selection); selection.each((data, index, group) => { const container = containerDataJoin(select(group[index]), [data]); container.enter() .attr('auto-resize', '') .html( '<d3fc-svg class="plot-area"></d3fc-svg>' + '<d3fc-canvas class="plot-area"></d3fc-canvas>' ); xLabelDataJoin(container, [xOrient(data)]) .attr('class', d => `x-label ${d}-label`) .text(xLabel(data)); yLabelDataJoin(container, [yOrient(data)]) .attr('class', d => `y-label ${d}-label`) .text(yLabel(data)); xAxisDataJoin(container, [xOrient(data)]) .attr('class', d => `x-axis ${d}-axis`) .style('height', xAxisHeight(data)) .on('measure', (d, i, nodes) => { const { width, height } = event.detail; if (d === 'top') { select(nodes[i]) .select('svg') .attr('viewBox', `0 ${-height} ${width} ${height}`); } xScale.range([0, width]); }) .on('draw', (d, i, nodes) => { const xAxisComponent = d === 'top' ? xAxis.top(xScale) : xAxis.bottom(xScale); xAxisComponent.decorate(xDecorate); transitionPropagator(select(nodes[i])) .select('svg') .call(xAxisStore(xAxisComponent)); }); yAxisDataJoin(container, [yOrient(data)]) .attr('class', d => `y-axis ${d}-axis`) .style('width', yAxisWidth(data)) .on('measure', (d, i, nodes) => { const { width, height } = event.detail; if (d === 'left') { select(nodes[i]) .select('svg') .attr('viewBox', `${-width} 0 ${width} ${height}`); } yScale.range([height, 0]); }) .on('draw', (d, i, nodes) => { const yAxisComponent = d === 'left' ? yAxis.left(yScale) : yAxis.right(yScale); yAxisComponent.decorate(yDecorate); transitionPropagator(select(nodes[i])) .select('svg') .call(yAxisStore(yAxisComponent)); }); container.select('d3fc-canvas.plot-area') .on('draw', (d, i, nodes) => { const canvas = select(nodes[i]) .select('canvas') .node(); canvasPlotArea.context(canvas.getContext('2d')) .xScale(xScale) .yScale(yScale); canvasPlotArea(d); }); container.select('d3fc-svg.plot-area') .on('draw', (d, i, nodes) => { svgPlotArea.xScale(xScale) .yScale(yScale); transitionPropagator(select(nodes[i])) .select('svg') .call(svgPlotArea); }); container.each((d, i, nodes) => nodes[i].requestRedraw()); decorate(container, data, index); }); }; const 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, prefix('x')); rebindAll(cartesian, yScale, scaleExclusions, prefix('y')); rebindAll(cartesian, xAxisStore, prefix('x')); rebindAll(cartesian, yAxisStore, prefix('y')); cartesian.xOrient = (...args) => { if (!args.length) { return xOrient; } xOrient = functor(args[0]); return cartesian; }; cartesian.yOrient = (...args) => { if (!args.length) { return yOrient; } yOrient = functor(args[0]); return cartesian; }; cartesian.xDecorate = (...args) => { if (!args.length) { return xDecorate; } xDecorate = args[0]; return cartesian; }; cartesian.yDecorate = (...args) => { if (!args.length) { return yDecorate; } yDecorate = args[0]; return cartesian; }; cartesian.xLabel = (...args) => { if (!args.length) { return xLabel; } xLabel = functor(args[0]); return cartesian; }; cartesian.yLabel = (...args) => { if (!args.length) { return yLabel; } yLabel = functor(args[0]); return cartesian; }; cartesian.xAxisHeight = (...args) => { if (!args.length) { return xAxisHeight; } xAxisHeight = functor(args[0]); return cartesian; }; cartesian.yAxisWidth = (...args) => { if (!args.length) { return yAxisWidth; } yAxisWidth = functor(args[0]); return cartesian; }; cartesian.canvasPlotArea = (...args) => { if (!args.length) { return canvasPlotArea; } canvasPlotArea = args[0]; return cartesian; }; cartesian.svgPlotArea = (...args) => { if (!args.length) { return svgPlotArea; } svgPlotArea = args[0]; return cartesian; }; cartesian.decorate = (...args) => { if (!args.length) { return decorate; } decorate = args[0]; return cartesian; }; return cartesian; };
export default () => { let x = (d) => d.x; let y = (d) => d.y; let xScale = scaleIdentity(); let yScale = scaleIdentity(); const point = seriesCanvasPoint(); 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 = seriesCanvasMulti() .series([horizontalLine, verticalLine, point]) .xScale(xIdentity) .yScale(yIdentity) .mapping((data) => [data]); const instance = (data) => { data.forEach(d => { // 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.crossValue(x) .mainValue(y); horizontalLine.value(y); verticalLine.value(x); multi(d); }); }; // 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; }; const lineIncludes = include('label', 'decorate'); rebindAll(instance, horizontalLine, lineIncludes, prefix('y')); rebindAll(instance, verticalLine, lineIncludes, prefix('x')); rebind(instance, point, 'decorate'); rebind(instance, multi, 'context'); return instance; };