UNPKG

d3-radarchart

Version:
266 lines (247 loc) 8.61 kB
import * as d3 from 'd3'; import { AREA_EVENT } from '../const.js'; class Axis { constructor (params) { Object.keys(params).forEach(key => { this[key] = params[key]; }); this.drawingContext = this.selectors.drawingContext; this.circularSegmentClass = d => `${this.chartRootName}${d.axisId}csegline`; this.axisOverlayRectClassName = d => `${this.chartRootName}${d.axisId}axisoverlayrect`; this.axisLabelParentClassName = d => `${this.chartRootName}${d.axisId}axislabelparent`; } render (axisRenderProps, datums) { const { width, levels, lineProps, ticksAttr, axisLabelProps, rotateTextWithAxis, wheelLabelAreaId } = axisRenderProps; const cEventHandler = this.eventHandlerFactory.createAreaEventHandler; // Circular segments for (let lvlInx = 0; lvlInx < levels.levelsFractions.length; lvlInx++) { const { levelsFractions } = levels; this.drawingContext() .selectAll('.levels') .data(datums) .enter() .append('svg:line') .attr('x1', d => { const tickValue = d.range * levelsFractions[lvlInx] + d.minValue; const cordsOnAxis = d.projectValueOnAxis(tickValue); return cordsOnAxis.x; }) .attr('y1', d => { const tickValue = d.range * levelsFractions[lvlInx] + d.minValue; const cordsOnAxis = d.projectValueOnAxis(tickValue); return cordsOnAxis.y; }) .attr('x2', (_, i) => { const nxtInx = i + 1 === datums.length ? 0 : i + 1; const nAxis = datums[nxtInx]; const nValue = nAxis.range * levelsFractions[lvlInx] + nAxis.minValue; const nCordAxis = nAxis.projectValueOnAxis(nValue); return nCordAxis.x; }) .attr('y2', (_, i) => { const nxtInx = i + 1 === datums.length ? 0 : i + 1; const nAxis = datums[nxtInx]; const nValue = nAxis.range * levelsFractions[lvlInx] + nAxis.minValue; const nCordAxis = nAxis.projectValueOnAxis(nValue); return nCordAxis.y; }) .attr('class', d => this.circularSegmentClass(d)) .style('stroke', '#DCDCDC') .style('stroke-opacity', '0.75') .style('stroke-width', '0.3px'); } const Format = d3.format('.2%'); const { setAxisPerecentLevelForAxis } = this.stateSetters; // Text indicating at what % each level is for (let lvlInx = 0; lvlInx < levels.levelsFractions.length; lvlInx++) { const { levelsFractions } = levels; this.drawingContext() .selectAll('.levels') .data(datums) .enter() .append('svg:text') .attr('x', d => { const tickValue = d.range * levelsFractions[lvlInx] + d.minValue; const cordsOnAxis = d.projectValueOnAxis(tickValue); return cordsOnAxis.x; }) .attr('y', d => { const tickValue = d.range * levelsFractions[lvlInx] + d.minValue; const cordsOnAxis = d.projectValueOnAxis(tickValue); return cordsOnAxis.y; }) .attr('class', 'legend') .style('font-family', ticksAttr['font-family']) .style('font-size', d => d.scaledTickSize + 'px') .style('opacity', 0.0) .attr('fill', ticksAttr.fill) .text(() => Format(levelsFractions[lvlInx])) .each(function (d) { setAxisPerecentLevelForAxis(d.axisId, this); }); } const axisLineGs = this.drawingContext() .selectAll('.axis') .data(datums) .enter() .append('g'); const { setAxisLineElForAxis } = this.stateSetters; axisLineGs .append('line') .attr('class', 'axisline') .attr('x1', d => d.x1) .attr('y1', d => d.y1) .attr('x2', d => d.x2) .attr('y2', d => d.y2) .attr('pointer-events', 'none') .style('stroke', lineProps.fill) .style('stroke-opacity', 0.75) .style('stroke-width', '0.3px') .each(function (d) { setAxisLineElForAxis(d.axisId, this); }); const { setAxisOverlayRectForAxis } = this.stateSetters; axisLineGs .append('rect') .attr('class', d => this.axisOverlayRectClassName(d)) .attr('x', d => d.x1) .attr('y', d => d.y1) .attr( 'transform', d => 'rotate(' + d.angleFromNorth + ',' + d.x1 + ',' + d.y1 + ')' ) .attr('width', d => d.axisLength) .attr('height', 10) .attr('fill-opacity', 0.0) // .on('mouseover', d => d.onAxisLineRectOver()) // .on('mouseout', d => d.onAxisLineRectMouseOut()) .each(function (d) { setAxisOverlayRectForAxis(d.axisId, this); }); const { addLabelLineForAxis, addZoomedLabelLinesForAxis, setAxisLabelParentForAxis, setLabelValueForAxis } = this.stateSetters; axisLineGs .append('text') .attr('class', d => this.axisLabelParentClassName(d)) .attr('pointer-events', 'none') .attr('transform', d => { if (rotateTextWithAxis) { return ( 'rotate(' + d.axisLabelRotation() + ',' + d.x2 + ',' + d.y2 + ')' ); } return ''; }) .text('') .each(function (d) { setAxisLabelParentForAxis(d.axisId, this); const lines = d.lines; for (let i = 0; i < lines.length; i++) { d3.select(this) .append('tspan') .attr('x', dd => dd.axisLabelCords().x) .attr('y', dd => dd.axisLabelCords().y) .attr('dy', dd => { return dd.textLineSpacingPx(width) * i; }) .text(lines[i]) .style('font-family', axisLabelProps['font-family']) .style('font-size', dd => dd.axisTitleScale(width) + 'px') .style('fill', axisLabelProps.fill) .attr('text-anchor', 'middle') .each(function (dd) { addLabelLineForAxis(dd.axisId, this); }); } // Label value d3.select(this) .append('tspan') .attr('x', dd => dd.axisLabelCords().x) .attr('y', dd => dd.axisLabelCords().y) .attr('dy', dd => { return dd.textLineSpacingPx(width) * dd.lines.length; }) .text('') .style('font-family', axisLabelProps['font-family']) .style('font-size', dd => dd.axisTitleScale(width) + 'px') .style('fill', axisLabelProps['value-fill']) .attr('text-anchor', 'middle') .each(function (dd) { setLabelValueForAxis(dd.axisId, this); }); // Zoom label text for (let i = 0; i < d.zoomLines.length; i++) { d3.select(this) .append('tspan') .attr('x', dd => dd.axisLabelCords().x) .attr('y', dd => dd.axisLabelCords().y) .attr('dy', dd => { return dd.textLineSpacingPx(width) * i; }) .text(d.zoomLines[i]) .style('font-family', axisLabelProps['font-family']) .style('font-size', dd => dd.axisTitleScale(width) + 'px') .style('fill', axisLabelProps.fill) .style('fill-opacity', 0.0) .attr('text-anchor', 'middle') .each(function (dd) { addZoomedLabelLinesForAxis(dd.axisId, this); }); } }); axisLineGs .append('rect') .attr('class', d => this.selectors.axisTextOverlayClassName(d.axisId)) .attr('x', d => d.overLayx()) .attr('y', d => d.overLayy()) .attr('width', d => d.overLayWidth) .attr('height', d => d.overLayHeight()) .attr('fill-opacity', 0.0) .attr('transform', d => { if (rotateTextWithAxis) { return ( 'rotate(' + d.axisLabelRotation() + ',' + d.x2 + ',' + d.y2 + ')' ); } return ''; }) .on( 'wheel', (() => { if (wheelLabelAreaId) { return cEventHandler( AREA_EVENT.CIRCLE_WHEEL_SCROLL, wheelLabelAreaId ); } return null; })() ) .on('mouseover', this.eventHandlerFactory.onAxisLabelOver()) .on('mouseout', this.eventHandlerFactory.onAxisLabelOut()); } removeAxis (axisIds) { axisIds.forEach(axisId => { this.selectors .selectAxisTextForAxis(axisId) .on('wheel', null) .on('mouseover', null) .on('mouseout', null); }); } } export default Axis;