import * as d3 from "d3";
type Selection<T extends d3.BaseType> = d3.Selection<T, any, any, any>;
export class Visual implements IVisual {
private svg: Selection<SVGElement>;
private container: Selection<SVGElement>;
private host: IVisualHost;
constructor(options: VisualConstructorOptions) {
this.host = options.host;
this.svg = d3.select(options.element)
.append('svg')
.classed('visual-svg', true);
this.container = this.svg
.append('g')
.classed('visual-container', true);
}
public update(options: VisualUpdateOptions) {
const dataView = options.dataViews[0];
if (!dataView) {
return;
}
const width = options.viewport.width;
const height = options.viewport.height;
this.svg
.attr('width', width)
.attr('height', height);
// D3 data binding and visualization logic
this.renderChart(dataView, width, height);
}
private renderChart(dataView: DataView, width: number, height: number): void {
const data = this.transformData(dataView);
// Create scales
const xScale = d3.scaleBand()
.domain(data.map(d => d.category))
.range([0, width])
.padding(0.1);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([height, 0]);
// Bind data and create bars
const bars = this.container.selectAll('.bar')
.data(data);
bars.enter()
.append('rect')
.classed('bar', true)
.merge(bars)
.attr('x', d => xScale(d.category))
.attr('y', d => yScale(d.value))
.attr('width', xScale.bandwidth())
.attr('height', d => height - yScale(d.value))
.style('fill', '#3498db');
bars.exit().remove();
}
private transformData(dataView: DataView): any[] {
// Transform Power BI DataView to D3-friendly format
const categorical = dataView.categorical;
const categories = categorical.categories[0];
const values = categorical.values[0];
return categories.values.map((category, index) => ({
category: category.toString(),
value: values.values[index] as number
}));
}
}