Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | 7x 7x 7x | import type { ElementRef } from '@angular/core';
import * as d3 from 'd3';
import type { IPieChartDataNode, IPieChartOptions } from '../interfaces/pie-chart.interface';
import { generateConfiguration } from './configuration.util';
/**
* The pie chart default configuration.
*/
export const defaultPieChartConfig: IPieChartOptions = Object.freeze({
chartTitle: '',
width: 600,
height: 600,
margin: {
top: 20,
right: 20,
bottom: 20,
left: 20,
},
innerRadius: 0, // increase inner radius to render a donut chart
showLabels: true,
labelRadiusModifier: 50,
labelTextWrapWidth: 60,
transitionDuration: 1000,
color: d3.scaleOrdinal(d3.schemeCategory10),
} as IPieChartOptions);
/**
* Creates a container for the pie chart.
* @param container the chart container
* @param config the chart configuration
* @returns the object with the svg element and the g element
*/
const createContainer = (container: ElementRef<HTMLDivElement>, config: IPieChartOptions) => {
const id = container.nativeElement.id ?? 'pie-0';
d3.select(`#${id}`).select('svg').remove();
const svg = d3
.select(`#${id}`)
.append('svg')
.attr('width', config.width + config.margin.left + config.margin.right)
.attr('height', config.height + config.margin.top + config.margin.bottom)
.attr('class', id);
const g = svg
.append('g')
.attr('transform', `translate(${config.width / 2 + config.margin.left},${config.height / 2 + config.margin.top})`);
return { svg, g };
};
/**
* Draws the pie chart.
* @param container the chart container
* @param data the chart data
* @param options the chart options
* @returns the chart configuration
*/
export const drawPieChart = (container: ElementRef<HTMLDivElement>, data: IPieChartDataNode[], options?: Partial<IPieChartOptions>) => {
const config: IPieChartOptions = generateConfiguration<IPieChartOptions>(defaultPieChartConfig, options, {});
const { g } = createContainer(container, config);
const pie = d3.pie<IPieChartDataNode>().value(datum => datum.y);
const radius = Math.min(config.width, config.height) / 2;
const arc = d3.arc<d3.PieArcDatum<IPieChartDataNode>>().innerRadius(config.innerRadius).outerRadius(radius);
const arcs = g
.selectAll('arc')
.data(pie(data))
.enter()
.append('g')
.attr('class', 'arc')
.on('mouseover', function (this, event: MouseEvent, d) {
this.style.opacity = '0.8';
const tooltipText = `${d.data.key}: ${d.data.y}`;
g.append('text')
.attr('class', 'chart-tooltip')
.style('opacity', 0)
.attr('dx', -config.width / (2 * 2 * 2))
.attr('dy', config.height / 2 + config.margin.top)
.text(tooltipText)
.transition()
.duration(config.transitionDuration)
.style('opacity', 1);
})
.on('mouseout', function (this, event, d) {
this.style.opacity = 'unset';
d3.selectAll('.chart-tooltip')
.transition()
.duration(config.transitionDuration / 2)
.style('opacity', 0)
.remove();
});
arcs
.append('path')
.attr('fill', (d, i) => config.color(i.toString()))
.attr('d', arc);
if (config.showLabels) {
const label = d3
.arc<d3.PieArcDatum<IPieChartDataNode>>()
.innerRadius(radius)
.outerRadius(radius + config.labelRadiusModifier);
const textDy = 5;
arcs
.append('text')
.attr('class', 'legend')
.attr('text-anchor', 'middle')
.attr('dy', textDy)
.attr('transform', d => `translate(${label.centroid(d)})`)
.style('font-size', '12px')
.text(d => d.data.y);
}
return config;
};
|