in src/TornadoChart.ts [273:1144]
tooltipData: buildTooltip(null),
highlight: hasHighlights && !!highlight,
});
}
}
return {
categories: categoriesLabels,
series: series,
settings: settings,
legend: TornadoChart.getLegendData(series, hasDynamicSeries),
dataPoints: dataPoints,
highlightedDataPoints: highlightedDataPoints,
maxLabelsWidth: Math.max(...categoriesLabels.map(x => x.width)),
hasDynamicSeries: hasDynamicSeries,
hasHighlights: hasHighlights,
labelHeight: labelHeight,
legendObjectProperties: dataViewObjects.getObject(dataView.metadata.objects, "legend", {}),
categoriesObjectProperties: dataViewObjects.getObject(dataView.metadata.objects, "categories", {}),
};
}
public static PARSE_SERIES(
dataView: DataView,
dataViewValueColumns: DataViewValueColumns,
hostService: IVisualHost,
index: number,
isGrouped: boolean,
columnGroup: DataViewValueColumnGroup,
colors: IColorPalette): TornadoChartSeries {
if (!dataView) {
return;
}
let dataViewValueColumn: DataViewValueColumn = dataViewValueColumns ? dataViewValueColumns[index] : null,
source: DataViewMetadataColumn = dataViewValueColumn ? dataViewValueColumn.source : null,
identity: any = columnGroup ? columnGroup.identity : null,
queryName: string = source ? source.queryName : null;
let selectionId: ISelectionId = hostService.createSelectionIdBuilder()
.withSeries(dataViewValueColumns, columnGroup)
.withMeasure(queryName)
.createSelectionId();
let sourceGroupName: string = null;
if (source.groupName !== undefined && source.groupName !== null) {
sourceGroupName = "" + source.groupName;
}
let objects: DataViewObjects,
categoryAxisObject: DataViewObject | DataViewObjectWithId[],
displayName: PrimitiveValue = source ? sourceGroupName
? sourceGroupName : source.displayName
: null;
if (isGrouped && columnGroup && columnGroup.objects) {
categoryAxisObject = columnGroup.objects ? columnGroup.objects["categoryAxis"] : null;
objects = columnGroup.objects;
} else if (source && source.objects) {
objects = source.objects;
categoryAxisObject = objects ? objects["categoryAxis"] : null;
} else if (dataView && dataView.metadata && dataView.metadata.objects) {
objects = dataView.metadata.objects;
}
let fillColor: string = TornadoChart.getColor(
tornadoChartProperties.dataPoint.fill,
["purple", "teal"][index],
objects, colors);
let categoryAxisEnd: number = categoryAxisObject ? categoryAxisObject["end"] : null;
return <TornadoChartSeries>{
fill: fillColor,
name: displayName,
selectionId: selectionId,
categoryAxisEnd: categoryAxisEnd,
};
}
private static getColor(properties: any, defaultColor: string, objects: DataViewObjects, colors: IColorPalette, convertToHighContrastMode: boolean = true): string {
let colorHelper: ColorHelper = new ColorHelper(colors, properties, defaultColor);
if (colorHelper.isHighContrast && convertToHighContrastMode)
return colorHelper.getColorForMeasure(objects, "", "foreground");
return colorHelper.getColorForMeasure(objects, "");
}
private static getTextData(
text: string,
textOptions: TornadoChartTextOptions,
measureWidth: boolean = false,
measureHeight: boolean = false,
overrideFontSize?: number): TextData {
let width: number = 0,
height: number = 0,
fontSize: string,
textProperties: TextProperties;
text = text || "";
fontSize = overrideFontSize
? PixelConverter.fromPoint(overrideFontSize)
: PixelConverter.fromPoint(textOptions.fontSize);
textProperties = {
text: text,
fontFamily: textOptions.fontFamily,
fontSize: fontSize
};
if (measureWidth) {
width = textMeasurementService.measureSvgTextWidth(textProperties);
}
if (measureHeight) {
height = textMeasurementService.estimateSvgTextHeight(textProperties);
}
return {
text: text,
width: width,
height: height,
textProperties: textProperties
};
}
public colors: IColorPalette;
public colorHelper: ColorHelper;
public textOptions: TornadoChartTextOptions = {};
private columnPadding: number = 5;
private leftLabelMargin: number = 4;
private InnerTextHeightDelta: number = 2;
private margin: IMargin = {
top: 10,
right: 5,
bottom: 10,
left: 10
};
private root: Selection<any>;
private main: Selection<any>;
private columns: Selection<any>;
private axes: Selection<any>;
private labels: Selection<any>;
private categories: Selection<any>;
private clearCatcher: Selection<any>;
private selectionManager: ISelectionManager;
private legend: ILegend;
private behavior: IInteractiveBehavior;
private interactivityService: IInteractivityServiceSelectable;
private hostService: IVisualHost;
private localizationManager: ILocalizationManager;
private scrolling: TornadoChartScrolling;
private viewport: IViewport;
private dataView: TornadoChartDataView;
private heightColumn: number = 0;
private tooltipServiceWrapper: ITooltipServiceWrapper;
private get allLabelsWidth(): number {
let labelsWidth: number = this.dataView.settings.showCategories
? Math.min(this.dataView.maxLabelsWidth, this.scrolling.scrollViewport.width / 2)
: TornadoChart.DefaultLabelsWidth;
return labelsWidth + TornadoChart.CategoryLabelMargin;
}
private get allColumnsWidth(): number {
return this.scrolling.scrollViewport.width - this.allLabelsWidth;
}
private get columnWidth(): number {
return this.dataView.series.length === TornadoChart.MaxSeries
? this.allColumnsWidth / 2
: this.allColumnsWidth;
}
constructor(options: VisualConstructorOptions) {
let fontSize: string;
this.hostService = options.host;
this.localizationManager = this.hostService.createLocalizationManager();
this.colors = options.host.colorPalette;
this.colorHelper = new ColorHelper(this.colors);
this.selectionManager = options.host.createSelectionManager();
this.tooltipServiceWrapper = createTooltipServiceWrapper(
options.host.tooltipService,
options.element);
this.interactivityService = createInteractivitySelectionService(this.hostService);
let interactiveBehavior: IInteractiveBehavior = this.colorHelper.isHighContrast ? <IInteractiveBehavior>(new OpacityLegendBehavior()) : null;
this.legend = createLegend(options.element, false, this.interactivityService, true, null, interactiveBehavior);
let root: Selection<any> = this.root = d3.select(options.element)
.append("svg");
root
.classed(TornadoChart.ClassName, true);
fontSize = root.style("font-size");
this.textOptions.fontSize = Number(fontSize.slice(0, fontSize.length - 2));
this.textOptions.fontFamily = root.style("font-family");
this.scrolling = new TornadoChartScrolling(
() => root,
() => this.viewport,
() => this.margin,
() => this.dataView.categories.length * TornadoChart.CategoryMinHeight,
true);
let main: Selection<any> = this.main = root.append("g");
this.clearCatcher = appendClearCatcher(main);
this.columns = main
.append("g")
.classed(TornadoChart.Columns.className, true);
this.axes = main
.append("g")
.classed(TornadoChart.Axes.className, true);
this.labels = main
.append("g")
.classed(TornadoChart.Labels.className, true);
this.categories = main
.append("g")
.classed(TornadoChart.Categories.className, true);
this.behavior = new TornadoWebBehavior();
}
public update(options: VisualUpdateOptions): void {
if (!options ||
!options.dataViews ||
!options.dataViews[0] ||
!options.dataViews[0].categorical ||
!options.dataViews[0].categorical.categories ||
!options.dataViews[0].categorical.categories[0] ||
!options.dataViews[0].categorical.categories[0].source ||
!options.dataViews[0].categorical.values ||
!options.dataViews[0].categorical.values[0] ||
!options.dataViews[0].categorical.values[0].values ||
!options.dataViews[0].categorical.values[0].values.length) {
this.clearData();
return;
}
this.viewport = {
height: Math.max(0, options.viewport.height - this.margin.top - this.margin.bottom),
width: Math.max(0, options.viewport.width - this.margin.left - this.margin.right)
};
this.dataView = TornadoChart.CONVERTER(this.validateDataView(options.dataViews[0]), this.hostService, this.textOptions, this.colors, this.localizationManager);
if (!this.dataView || this.scrolling.scrollViewport.height < TornadoChart.CategoryMinHeight) {
this.clearData();
return;
}
this.root.on("contextmenu", () => {
const mouseEvent: MouseEvent = getEvent();
const eventTarget: EventTarget = mouseEvent.target;
let dataPoint: any = d3.select(<d3.BaseType>eventTarget).datum();
this.selectionManager.showContextMenu(dataPoint ? dataPoint.selectionId : {}, {
x: mouseEvent.clientX,
y: mouseEvent.clientY
});
mouseEvent.preventDefault();
});
this.render();
}
private validateDataView(dataView: DataView): DataView {
if (!dataView || !dataView.categorical || !dataView.categorical.values) {
return null;
}
return dataView;
}
private updateElements(): void {
let translateX: number = 0,
position: string = dataViewObject.getValue(this.dataView.categoriesObjectProperties, "position", legendPosition.left);
if (position === "Left") {
translateX = this.allLabelsWidth;
}
let elementsTranslate: string = translate(translateX, 0);
this.root
.attr("height", this.viewport.height + this.margin.top + this.margin.bottom)
.attr("width", this.viewport.width + this.margin.left + this.margin.right);
this.columns
.attr("transform", elementsTranslate);
this.labels
.attr("transform", elementsTranslate);
this.axes
.attr("transform", elementsTranslate);
}
private static parseSettings(objects: DataViewObjects, value: number, colors: IColorPalette): TornadoChartSettings {
let precision: number = TornadoChart.getPrecision(objects);
let displayUnits: number = dataViewObjects.getValue<number>(
objects,
tornadoChartProperties.labels.labelDisplayUnits,
TornadoChart.DefaultTornadoChartSettings.labelSettings.displayUnits);
let labelSettings: VisualDataLabelsSettings = TornadoChart.DefaultTornadoChartSettings.labelSettings;
let getLabelValueFormatter = (formatString: string) => valueFormatter.create({
format: formatString,
precision: precision,
value: (displayUnits === 0) && (value != null) ? value : displayUnits,
});
return {
labelOutsideFillColor: TornadoChart.getColor(
tornadoChartProperties.labels.outsideFill,
TornadoChart.DefaultTornadoChartSettings.labelOutsideFillColor,
objects,
colors),
labelSettings: {
show: dataViewObjects.getValue<boolean>(
objects,
tornadoChartProperties.labels.show,
labelSettings.show),
precision: precision,
fontSize: dataViewObjects.getValue<number>(
objects,
tornadoChartProperties.labels.fontSize,
labelSettings.fontSize),
displayUnits: displayUnits,
labelColor: TornadoChart.getColor(
tornadoChartProperties.labels.insideFill,
labelSettings.labelColor,
objects,
colors),
},
showCategories: dataViewObjects.getValue<boolean>(
objects,
tornadoChartProperties.categories.show,
TornadoChart.DefaultTornadoChartSettings.showCategories),
showLegend: dataViewObjects.getValue<boolean>(
objects,
tornadoChartProperties.legend.show,
TornadoChart.DefaultTornadoChartSettings.showLegend),
legendFontSize: dataViewObjects.getValue<number>(
objects,
tornadoChartProperties.legend.fontSize,
TornadoChart.DefaultTornadoChartSettings.legendFontSize),
legendColor: TornadoChart.getColor(
tornadoChartProperties.legend.labelColor,
TornadoChart.DefaultTornadoChartSettings.legendColor,
objects,
colors),
categoriesFillColor: TornadoChart.getColor(
tornadoChartProperties.categories.fill,
TornadoChart.DefaultTornadoChartSettings.categoriesFillColor,
objects,
colors),
categoriesFontSize: dataViewObjects.getValue<number>(
objects,
tornadoChartProperties.categories.fontSize,
TornadoChart.DefaultTornadoChartSettings.legendFontSize),
categoriesPosition: dataViewObject.getValue<string>(
objects,
"position",
legendPosition.left),
getLabelValueFormatter: getLabelValueFormatter
};
}
private static getPrecision(objects: DataViewObjects): number {
let precision: number = dataViewObjects.getValue<number>(
objects,
tornadoChartProperties.labels.labelPrecision,
TornadoChart.DefaultTornadoChartSettings.labelSettings.precision);
return Math.min(Math.max(0, precision), TornadoChart.MaxPrecision);
}
private static getLegendData(series: TornadoChartSeries[], hasDynamicSeries: boolean): LegendData {
let legendDataPoints: LegendDataPoint[] = [];
if (hasDynamicSeries)
legendDataPoints = series.map((series: TornadoChartSeries) => {
return <LegendDataPoint>{
label: series.name,
color: series.fill,
icon: MarkerShape.circle,
selected: false,
identity: series.selectionId
};
});
return {
dataPoints: legendDataPoints
};
}
private render(): void {
this.updateElements();
this.renderLegend();
this.scrolling.renderY(this.dataView, this.renderWithScrolling.bind(this));
}
private clearData(): void {
this.columns.selectAll("*").remove();
this.axes.selectAll("*").remove();
this.labels.selectAll("*").remove();
this.categories.selectAll("*").remove();
this.legend.reset();
this.legend.drawLegend({ dataPoints: [] }, this.viewport);
this.scrolling.clearData();
}
public onClearSelection(): void {
if (this.interactivityService) {
this.interactivityService.clearSelection();
}
}
private renderWithScrolling(tornadoChartDataView: TornadoChartDataView, scrollStart: number, scrollEnd: number): void {
if (!this.dataView || !this.dataView.settings) {
return;
}
let categoriesLength: number = tornadoChartDataView.categories.length;
let startIndex: number = scrollStart * categoriesLength;
let endIndex: number = scrollEnd * categoriesLength;
let startIndexRound: number = Math.floor(startIndex);
let endIndexRound: number = Math.floor(endIndex);
let maxValues: number = Math.floor(this.scrolling.scrollViewport.height / TornadoChart.CategoryMinHeight);
if (scrollEnd - scrollStart < 1 && maxValues < endIndexRound - startIndexRound) {
if (startIndex - startIndexRound > endIndex - endIndexRound) {
startIndexRound++;
}
else {
endIndex--;
}
}
if (!this.dataView.hasHighlights) {
this.interactivityService.applySelectionStateToData(tornadoChartDataView.dataPoints);
this.interactivityService.applySelectionStateToData(tornadoChartDataView.highlightedDataPoints);
}
// Filter data according to the visible visual area
tornadoChartDataView.categories = tornadoChartDataView.categories.slice(startIndexRound, endIndexRound);
tornadoChartDataView.dataPoints = tornadoChartDataView.dataPoints.filter((d: TornadoChartPoint) => d.categoryIndex >= startIndexRound && d.categoryIndex < endIndexRound);
tornadoChartDataView.highlightedDataPoints = tornadoChartDataView.highlightedDataPoints.filter((d: TornadoChartPoint) => d.categoryIndex >= startIndexRound && d.categoryIndex < endIndexRound);
this.dataView = tornadoChartDataView;
this.computeHeightColumn();
this.renderMiddleSection();
this.renderAxes();
this.renderCategories();
}
private updateViewport(): void {
let legendMargins: IViewport = this.legend.getMargins(),
legendPosition: LegendPosition;
legendPosition = LegendPosition[<string>this.dataView.legendObjectProperties[legendProps.position]];
switch (legendPosition) {
case LegendPosition.Top:
case LegendPosition.TopCenter:
case LegendPosition.Bottom:
case LegendPosition.BottomCenter: {
this.viewport.height -= legendMargins.height;
break;
}
case LegendPosition.Left:
case LegendPosition.LeftCenter:
case LegendPosition.Right:
case LegendPosition.RightCenter: {
this.viewport.width -= legendMargins.width;
break;
}
}
}
private computeHeightColumn(): void {
let length: number = this.dataView.categories.length;
this.heightColumn = (this.scrolling.scrollViewport.height - ((length - 1) * this.columnPadding)) / length;
}
private renderMiddleSection(): void {
let tornadoChartDataView: TornadoChartDataView = this.dataView;
this.calculateDataPoints(tornadoChartDataView.dataPoints);
this.calculateDataPoints(tornadoChartDataView.highlightedDataPoints);
let dataPointsWithHighlights: TornadoChartPoint[] = this.dataView.hasHighlights ? tornadoChartDataView.highlightedDataPoints : tornadoChartDataView.dataPoints;
this.renderColumns(dataPointsWithHighlights, tornadoChartDataView.series.length === 2);
this.renderLabels(this.dataView.hasHighlights ? tornadoChartDataView.highlightedDataPoints : tornadoChartDataView.dataPoints, tornadoChartDataView.settings.labelSettings);
}
/**
* Calculate the width, dx value and label info for every data point
*/
private calculateDataPoints(dataPoints: TornadoChartPoint[]): void {
let categoriesLength: number = this.dataView.categories.length;
let settings: TornadoChartSettings = this.dataView.settings;
let heightColumn: number = Math.max(this.heightColumn, 0);
let py: number = heightColumn / 2;
let pyHighlighted: number = heightColumn * TornadoChart.HighlightedShapeFactor / 2;
let maxSeries: boolean = this.dataView.series.length === TornadoChart.MaxSeries;
for (let i: number = 0; i < dataPoints.length; i++) {
let dataPoint: TornadoChartPoint = dataPoints[i];
let shiftToMiddle: boolean = i < categoriesLength && maxSeries;
let shiftToRight: boolean = i > categoriesLength - 1;
let widthOfColumn: number = this.getColumnWidth(dataPoint.value, dataPoint.minValue, dataPoint.maxValue, this.columnWidth);
let dx: number = (this.columnWidth - widthOfColumn) * Number(shiftToMiddle) + this.columnWidth * Number(shiftToRight)/* - scrollBarWidth*/;
dx = Math.max(dx, 0);
let highlighted: boolean = this.dataView.hasHighlights && dataPoint.highlight;
let highlightOffset: number = highlighted ? heightColumn * (1 - TornadoChart.HighlightedShapeFactor) / 2 : 0;
let dy: number = (heightColumn + this.columnPadding) * (i % categoriesLength) + highlightOffset;
let label: LabelData = this.getLabelData(
dataPoint.value,
dx,
widthOfColumn,
shiftToMiddle,
dataPoint.formatString,
settings);
dataPoint.dx = dx;
dataPoint.dy = dy;
dataPoint.px = widthOfColumn / 2;
dataPoint.py = highlighted ? pyHighlighted : py;
dataPoint.angle = shiftToMiddle ? TornadoChart.MaxAngle : TornadoChart.MinAngle;
dataPoint.width = widthOfColumn;
dataPoint.height = highlighted ? heightColumn * TornadoChart.HighlightedShapeFactor : heightColumn;
dataPoint.label = label;
}
}
private renderColumns(columnsData: TornadoChartPoint[], selectSecondSeries: boolean = false): void {
let hasSelection: boolean = this.interactivityService && this.interactivityService.hasSelection();
let columnsSelection: Selection<any> = this.columns
.selectAll(TornadoChart.Column.selectorName)
.data(columnsData);
let columnsSelectionMerged = columnsSelection
.enter()
.append("svg:rect")
.merge(columnsSelection);
columnsSelectionMerged.classed(TornadoChart.Column.className, true);
columnsSelectionMerged
.style("fill", (p: TornadoChartPoint) => this.colorHelper.isHighContrast ? this.colorHelper.getThemeColor() : p.color)
.style("stroke", (p: TornadoChartPoint) => p.color)
.style("fill-opacity", (p: TornadoChartPoint) => tornadoChartUtils.getOpacity(
p.selected,
p.highlight,
hasSelection,
this.dataView.hasHighlights))
.style("stroke-opacity", (p: TornadoChartPoint) => tornadoChartUtils.getOpacity(
p.selected,
p.highlight,
hasSelection,
this.dataView.hasHighlights))
.attr("transform", (p: TornadoChartPoint) => translateAndRotate(p.dx, p.dy, p.px, p.py, p.angle))
.attr("height", (p: TornadoChartPoint) => p.height)
.attr("width", (p: TornadoChartPoint) => p.width);
columnsSelection
.exit()
.remove();
let interactivityService = this.interactivityService;
if (interactivityService) {
interactivityService.applySelectionStateToData(columnsData);
let behaviorOptions: TornadoBehaviorOptions = {
columns: columnsSelectionMerged,
clearCatcher: this.clearCatcher,
interactivityService: this.interactivityService,
behavior: this.behavior,
dataPoints: columnsData
};
interactivityService.bind(behaviorOptions);
}
this.renderTooltip(columnsSelectionMerged);
}
private renderTooltip(selection: Selection<any>): void {
this.tooltipServiceWrapper.addTooltip(
selection,
(tooltipEvent: TooltipEventArgs<TornadoChartPoint>) => {
return (<TornadoChartPoint>tooltipEvent.data).tooltipData;
},
null,
true);
}
private getColumnWidth(value: number, minValue: number, maxValue: number, width: number): number {
if (minValue === maxValue) {
return width;
}
let columnWidth = width * (value - minValue) / (maxValue - minValue);
// In case the user specifies a custom category axis end we limit the
// column width to the maximum available width
return Math.max(0, Math.min(width, columnWidth));
}
private getLabelData(
value: number,
dxColumn: number,
columnWidth: number,
isColumnPositionLeft: boolean,
formatStringProp: string,
settings?: TornadoChartSettings): LabelData {
let dx: number,
tornadoChartSettings: TornadoChartSettings = settings ? settings : this.dataView.settings,
labelSettings: VisualDataLabelsSettings = tornadoChartSettings.labelSettings,
fontSize: number = labelSettings.fontSize,
color: string = labelSettings.labelColor;
let maxOutsideLabelWidth: number = isColumnPositionLeft
? dxColumn - this.leftLabelMargin
: this.allColumnsWidth - (dxColumn + columnWidth + this.leftLabelMargin);
let maxLabelWidth: number = Math.max(maxOutsideLabelWidth, columnWidth - this.leftLabelMargin);
let textProperties: TextProperties = {
fontFamily: dataLabelUtils.StandardFontFamily,
fontSize: PixelConverter.fromPoint(fontSize),
text: tornadoChartSettings.getLabelValueFormatter(formatStringProp).format(value)
};
let valueAfterValueFormatter: string = textMeasurementService.getTailoredTextOrDefault(textProperties, maxLabelWidth);
let textDataAfterValueFormatter: TextData = TornadoChart.getTextData(valueAfterValueFormatter, this.textOptions, true, false, fontSize);
if (columnWidth > textDataAfterValueFormatter.width + TornadoChart.LabelPadding) {
dx = dxColumn + columnWidth / 2 - textDataAfterValueFormatter.width / 2;
} else {
if (isColumnPositionLeft) {
dx = dxColumn - this.leftLabelMargin - textDataAfterValueFormatter.width;
} else {
dx = dxColumn + columnWidth + this.leftLabelMargin;
}
color = tornadoChartSettings.labelOutsideFillColor;
}
return {
dx: dx,
source: value,
value: valueAfterValueFormatter,
color: color
};
}
private renderAxes(): void {
let linesData: LineData[],
axesSelection: Selection<any>,
axesElements: Selection<any> = this.main
.select(TornadoChart.Axes.selectorName)
.selectAll(TornadoChart.Axis.selectorName);
if (this.dataView.series.length !== TornadoChart.MaxSeries) {
axesElements.remove();
return;
}
linesData = this.generateAxesData();
axesSelection = axesElements.data(linesData);
let axesSelectionMerged = axesSelection
.enter()
.append("svg:line")
.merge(axesSelection);
axesSelectionMerged
.classed(TornadoChart.Axis.className, true)
.style("stroke", this.colorHelper.getHighContrastColor());
axesSelectionMerged
.attr("x1", (data: LineData) => data.x1)
.attr("y1", (data: LineData) => data.y1)
.attr("x2", (data: LineData) => data.x2)
.attr("y2", (data: LineData) => data.y2);
axesSelection
.exit()
.remove();
}
private generateAxesData(): LineData[] {
let x: number,
y1: number,
y2: number;
x = this.allColumnsWidth / 2;
y1 = 0;
y2 = this.scrolling.scrollViewport.height;
return [{
x1: x,
y1: y1,
x2: x,
y2: y2
}];
}
private renderLabels(dataPoints: TornadoChartPoint[], labelsSettings: VisualDataLabelsSettings): void {
let labelSelectionMerged: Selection<TornadoChartPoint>,
labelSelection: Selection<TornadoChartPoint> = this.main
.select(TornadoChart.Labels.selectorName)
.selectAll(TornadoChart.Label.selectorName)
.data(dataPoints.filter((p: TornadoChartPoint) => p.label.dx >= 0));
// Check if labels can be displayed
if (!labelsSettings.show || this.dataView.labelHeight >= this.heightColumn) {
this.labels.selectAll("*").remove();
return;
}
let fontSizeInPx: string = PixelConverter.fromPoint(labelsSettings.fontSize);
let labelYOffset: number = this.heightColumn / 2 + this.dataView.labelHeight / 2 - this.InnerTextHeightDelta;
let categoriesLength: number = this.dataView.categories.length;
labelSelectionMerged = labelSelection
.enter()
.append("g")
.merge(labelSelection);
labelSelectionMerged
.append("svg:title")
.classed(TornadoChart.LabelTitle.className, true);
labelSelectionMerged
.append("svg:text")
.attr("dy", dataLabelUtils.DefaultDy)
.classed(TornadoChart.LabelText.className, true);
labelSelectionMerged
.attr("pointer-events", "none")
.classed(TornadoChart.Label.className, true);
labelSelectionMerged
.select(TornadoChart.LabelTitle.selectorName)
.text((p: TornadoChartPoint) => p.label.source);
labelSelectionMerged
.attr("transform", (p: TornadoChartPoint, index: number) => {
let dy: number = (this.heightColumn + this.columnPadding) * (index % categoriesLength);
return translate(p.label.dx, dy + labelYOffset);
});
labelSelectionMerged
.select(TornadoChart.LabelText.selectorName)
.attr("fill", (p: TornadoChartPoint) => p.label.color)
.attr("font-size", fontSizeInPx)
.text((p: TornadoChartPoint) => p.label.value);
labelSelection
.exit()
.remove();
}
private renderCategories(): void {
let settings: TornadoChartSettings = this.dataView.settings,
color: string = settings.categoriesFillColor,
fontSizeInPx: string = PixelConverter.fromPoint(settings.categoriesFontSize),
position: string = dataViewObject.getValue(this.dataView.categoriesObjectProperties, "position", legendPosition.left),
categoriesSelectionMerged: Selection<any>,
categoriesSelection: Selection<any>,
categoryElements: Selection<any> = this.main
.select(TornadoChart.Categories.selectorName)
.selectAll(TornadoChart.Category.selectorName);
if (!settings.showCategories) {
categoryElements.remove();
return;
}
categoriesSelection = categoryElements.data(this.dataView.categories);
categoriesSelectionMerged = categoriesSelection
.enter()
.append("g")
.merge(categoriesSelection);
categoriesSelectionMerged
.append("svg:title")
.classed(TornadoChart.CategoryTitle.className, true);
categoriesSelectionMerged
.append("svg:text")
.classed(TornadoChart.CategoryText.className, true);
let xShift: number = 0;
if (position === "Right") {
let width: number = this.viewport.width + this.margin.left + this.margin.right;
xShift = width - this.allLabelsWidth;
}
categoriesSelectionMerged
.attr("transform", (text: string, index: number) => {
let shift: number = (this.heightColumn + this.columnPadding) * index + this.heightColumn / 2,
textData: TextData = TornadoChart.getTextData(text, this.textOptions, false, true);
shift = shift + textData.height / 2 - this.InnerTextHeightDelta;
return translate(xShift, shift);
})
.classed(TornadoChart.Category.className, true);
categoriesSelectionMerged
.select(TornadoChart.CategoryTitle.selectorName)
.text((text: TextData) => text.text);
categoriesSelectionMerged
.select(TornadoChart.CategoryText.selectorName)
.attr("fill", color)
.attr("font-size", fontSizeInPx)
.text((data: TextData) => this.dataView.settings.showCategories
? textMeasurementService.getTailoredTextOrDefault(
TornadoChart.getTextData(data.text, this.textOptions).textProperties, this.allLabelsWidth)
: "");
categoriesSelection
.exit()
.remove();
}
private renderLegend(): void {
let settings: TornadoChartSettings = this.dataView.settings;
if (settings.showLegend) {
let legend: LegendData = this.dataView.legend;
if (!legend) {
return;
}
let legendData: LegendData = {
title: legend.title,
dataPoints: legend.dataPoints,
fontSize: settings.legendFontSize,
labelColor: settings.legendColor
};
if (this.dataView.legendObjectProperties) {
let position: string;
LegendDataModule.update(legendData, this.dataView.legendObjectProperties);
position = <string>this.dataView.legendObjectProperties[legendProps.position];
if (position) {