diff --git a/projects/swimlane/ngx-charts/src/lib/area-chart/area-chart.component.ts b/projects/swimlane/ngx-charts/src/lib/area-chart/area-chart.component.ts index 1f7d7082f..ef2d80f98 100644 --- a/projects/swimlane/ngx-charts/src/lib/area-chart/area-chart.component.ts +++ b/projects/swimlane/ngx-charts/src/lib/area-chart/area-chart.component.ts @@ -23,6 +23,7 @@ import { Series } from '../models/chart-data.model'; import { LegendOptions, LegendPosition } from '../common/types/legend.model'; import { ViewDimensions } from '../common/types/view-dimension.interface'; import { ScaleType } from '../common/types/scale-type.enum'; +import { select } from 'd3-selection'; @Component({ selector: 'ngx-charts-area-chart', @@ -75,6 +76,9 @@ import { ScaleType } from '../common/types/scale-type.enum'; [maxTickLength]="maxYAxisTickLength" [tickFormatting]="yAxisTickFormatting" [ticks]="yAxisTicks" + [referenceLines]="referenceLines" + [showRefLines]="showRefLines" + [showRefLabels]="showRefLabels" [wrapTicks]="wrapTicks" (dimensionsChanged)="updateYAxisWidth($event)" > @@ -192,6 +196,9 @@ export class AreaChartComponent extends BaseChartComponent { @Input() yAxisTicks: any[]; @Input() roundDomains: boolean = false; @Input() tooltipDisabled: boolean = false; + @Input() referenceLines: any[]; + @Input() showRefLines: boolean = false; + @Input() showRefLabels: boolean = false; @Input() xScaleMin: any; @Input() xScaleMax: any; @Input() yScaleMin: number; @@ -274,6 +281,10 @@ export class AreaChartComponent extends BaseChartComponent { this.clipPathId = 'clip' + id().toString(); this.clipPath = `url(#${this.clipPathId})`; + + const parent = select(this.chartElement.nativeElement).select('.area-chart').node() as HTMLElement; + const refLines = select(this.chartElement.nativeElement).selectAll('.ref-line').nodes() as HTMLElement[]; + refLines.forEach(line => parent.appendChild(line)); } updateTimeline(): void { diff --git a/projects/swimlane/ngx-charts/src/lib/bar-chart/bar-horizontal.component.ts b/projects/swimlane/ngx-charts/src/lib/bar-chart/bar-horizontal.component.ts index 9b398f410..5e39f2a8b 100644 --- a/projects/swimlane/ngx-charts/src/lib/bar-chart/bar-horizontal.component.ts +++ b/projects/swimlane/ngx-charts/src/lib/bar-chart/bar-horizontal.component.ts @@ -16,6 +16,7 @@ import { BaseChartComponent } from '../common/base-chart.component'; import { LegendOptions, LegendPosition } from '../common/types/legend.model'; import { ScaleType } from '../common/types/scale-type.enum'; import { ViewDimensions } from '../common/types/view-dimension.interface'; +import { select } from 'd3-selection'; @Component({ selector: 'ngx-charts-bar-horizontal', @@ -44,6 +45,9 @@ import { ViewDimensions } from '../common/types/view-dimension.interface'; [maxTickLength]="maxXAxisTickLength" [tickFormatting]="xAxisTickFormatting" [ticks]="xAxisTicks" + [referenceLines]="referenceLines" + [showRefLines]="showRefLines" + [showRefLabels]="showRefLabels" [wrapTicks]="wrapTicks" (dimensionsChanged)="updateXAxisHeight($event)" > @@ -101,6 +105,9 @@ export class BarHorizontalComponent extends BaseChartComponent { @Input() xAxisLabel: string; @Input() yAxisLabel: string; @Input() tooltipDisabled: boolean = false; + @Input() referenceLines: any; + @Input() showRefLines: boolean = false; + @Input() showRefLabels: boolean = false; @Input() gradient: boolean; @Input() showGridLines: boolean = true; @Input() activeEntries: any[] = []; @@ -175,6 +182,12 @@ export class BarHorizontalComponent extends BaseChartComponent { this.legendOptions = this.getLegendOptions(); this.transform = `translate(${this.dims.xOffset} , ${this.margin[0]})`; + + if (this.showRefLines) { + const parent = select(this.chartElement.nativeElement).select('.bar-chart').node() as HTMLElement; + const refLines = select(this.chartElement.nativeElement).selectAll('.ref-line').nodes() as HTMLElement[]; + refLines.forEach(line => parent.appendChild(line)); + } } getXScale(): any { diff --git a/projects/swimlane/ngx-charts/src/lib/bar-chart/bar-vertical.component.ts b/projects/swimlane/ngx-charts/src/lib/bar-chart/bar-vertical.component.ts index cf2e9e353..09b3d4347 100644 --- a/projects/swimlane/ngx-charts/src/lib/bar-chart/bar-vertical.component.ts +++ b/projects/swimlane/ngx-charts/src/lib/bar-chart/bar-vertical.component.ts @@ -17,6 +17,7 @@ import { DataItem } from '../models/chart-data.model'; import { LegendOptions, LegendPosition } from '../common/types/legend.model'; import { ScaleType } from '../common/types/scale-type.enum'; import { ViewDimensions } from '../common/types/view-dimension.interface'; +import { select } from 'd3-selection'; @Component({ selector: 'ngx-charts-bar-vertical', @@ -61,6 +62,9 @@ import { ViewDimensions } from '../common/types/view-dimension.interface'; [maxTickLength]="maxYAxisTickLength" [tickFormatting]="yAxisTickFormatting" [ticks]="yAxisTicks" + [referenceLines]="referenceLines" + [showRefLines]="showRefLines" + [showRefLabels]="showRefLabels" [wrapTicks]="wrapTicks" (dimensionsChanged)="updateYAxisWidth($event)" > @@ -104,6 +108,9 @@ export class BarVerticalComponent extends BaseChartComponent { @Input() yAxisLabel: string; @Input() tooltipDisabled: boolean = false; @Input() gradient: boolean; + @Input() referenceLines: any[]; + @Input() showRefLines; + @Input() showRefLabels; @Input() showGridLines: boolean = true; @Input() activeEntries: any[] = []; @Input() schemeType: ScaleType; @@ -179,6 +186,12 @@ export class BarVerticalComponent extends BaseChartComponent { this.legendOptions = this.getLegendOptions(); this.transform = `translate(${this.dims.xOffset} , ${this.margin[0] + this.dataLabelMaxHeight.negative})`; + + if (this.showRefLines) { + const parent = select(this.chartElement.nativeElement).select('.bar-chart').node() as HTMLElement; + const refLines = select(this.chartElement.nativeElement).selectAll('.ref-line').nodes() as HTMLElement[]; + refLines.forEach(line => parent.appendChild(line)); + } } getXScale(): any { diff --git a/projects/swimlane/ngx-charts/src/lib/common/axes/x-axis-ticks.component.ts b/projects/swimlane/ngx-charts/src/lib/common/axes/x-axis-ticks.component.ts index 6ff130af9..fdeb05b96 100644 --- a/projects/swimlane/ngx-charts/src/lib/common/axes/x-axis-ticks.component.ts +++ b/projects/swimlane/ngx-charts/src/lib/common/axes/x-axis-ticks.component.ts @@ -17,6 +17,7 @@ import { trimLabel } from '../trim-label.helper'; import { getTickLines, reduceTicks } from './ticks.helper'; import { Orientation } from '../types/orientation.enum'; import { TextAnchor } from '../types/text-anchor.enum'; +import { roundedRect } from '../../common/shape.helper'; @Component({ selector: 'g[ngx-charts-x-axis-ticks]', @@ -54,6 +55,30 @@ import { TextAnchor } from '../types/text-anchor.enum'; + + + + + + + + {{ tickTrim(tickFormat(refLine.value)) }} + + {{ refLine.name }} + + + + `, changeDetection: ChangeDetectionStrategy.OnPush }) @@ -71,6 +96,9 @@ export class XAxisTicksComponent implements OnChanges, AfterViewInit { @Input() width: number; @Input() rotateTicks: boolean = true; @Input() wrapTicks = false; + @Input() referenceLines: any[]; + @Input() showRefLabels: boolean = false; + @Input() showRefLines: boolean = false; @Output() dimensionsChanged = new EventEmitter(); @@ -89,6 +117,17 @@ export class XAxisTicksComponent implements OnChanges, AfterViewInit { height: number = 0; approxHeight: number = 10; maxPossibleLengthForTickIfWrapped = 16; + transform: (o: any) => string; + refMax: number; + refMin: number; + referenceLineLength: number = 0; + referenceAreaPath: string; + dx: string; + x1: number; + x2: number; + y1: number; + y2: number; + tickSpacing: number; @ViewChild('ticksel') ticksElement: ElementRef; @@ -123,7 +162,55 @@ export class XAxisTicksComponent implements OnChanges, AfterViewInit { update(): void { const scale = this.scale; + this.adjustedScale = this.scale.bandwidth + ? function (d) { + return this.scale(d) + this.scale.bandwidth() * 0.5; + } + : this.scale; this.ticks = this.getTicks(); + const sign = this.orient === Orientation.Top || this.orient === Orientation.Right ? -1 : 1; + this.tickSpacing = Math.max(this.innerTickSize, 0) + this.tickPadding; + + switch (this.orient) { + case Orientation.Bottom: + this.transform = function (tick) { + return 'translate(' + this.adjustedScale(tick) + ',0)'; + }; + this.textAnchor = TextAnchor.Middle; + this.x2 = this.innerTickSize * sign; + this.x1 = this.tickSpacing * sign; + this.dx = sign < 0 ? '0em' : '.71em'; + break; + case Orientation.Left: + this.transform = function (tick) { + return 'translate(0,' + this.adjustedScale(tick) + ')'; + }; + this.textAnchor = TextAnchor.End; + console.log('ANCHOR' + this.textAnchor); + this.y2 = this.innerTickSize * -sign; + this.y1 = this.tickSpacing * -sign; + this.dx = '.32em'; + break; + case Orientation.Top: + this.transform = function (tick) { + return 'translate(' + this.adjustedScale(tick) + ',0)'; + }; + this.textAnchor = TextAnchor.Middle; + this.y2 = this.innerTickSize * sign; + this.y1 = this.tickSpacing * sign; + this.dx = sign < 0 ? '0em' : '.71em'; + break; + case Orientation.Right: + this.transform = function (tick) { + return 'translate(0,' + this.adjustedScale(tick) + ')'; + }; + this.textAnchor = TextAnchor.Start; + this.x2 = this.innerTickSize * -sign; + this.x1 = this.tickSpacing * -sign; + this.dx = '.32em'; + break; + default: + } if (this.tickFormatting) { this.tickFormat = this.tickFormatting; @@ -141,12 +228,6 @@ export class XAxisTicksComponent implements OnChanges, AfterViewInit { const angle = this.rotateTicks ? this.getRotationAngle(this.ticks) : null; - this.adjustedScale = this.scale.bandwidth - ? function (d) { - return this.scale(d) + this.scale.bandwidth() * 0.5; - } - : this.scale; - this.textTransform = ''; if (angle && angle !== 0) { this.textTransform = `rotate(${angle})`; @@ -159,6 +240,31 @@ export class XAxisTicksComponent implements OnChanges, AfterViewInit { setTimeout(() => this.updateDims()); } + setReferencelines(): void { + this.refMin = this.adjustedScale( + Math.min.apply( + null, + this.referenceLines.map(item => item.value) + ) + ); + this.refMax = this.adjustedScale( + Math.max.apply( + null, + this.referenceLines.map(item => item.value) + ) + ); + this.referenceLineLength = this.referenceLines.length; + + this.referenceAreaPath = roundedRect( + this.refMax, + -this.gridLineHeight + 25, + this.refMin - this.refMax, + this.gridLineHeight, + 0, + [false, false, false, false] + ); + } + getRotationAngle(ticks: any[]): number { let angle = 0; this.maxTicksLength = 0; @@ -207,6 +313,10 @@ export class XAxisTicksComponent implements OnChanges, AfterViewInit { this.approxHeight = Math.min(requiredHeight, 200); + if (this.showRefLines && this.referenceLines) { + this.setReferencelines(); + } + return angle; } diff --git a/projects/swimlane/ngx-charts/src/lib/common/axes/x-axis.component.ts b/projects/swimlane/ngx-charts/src/lib/common/axes/x-axis.component.ts index d652c630c..bf69c6348 100644 --- a/projects/swimlane/ngx-charts/src/lib/common/axes/x-axis.component.ts +++ b/projects/swimlane/ngx-charts/src/lib/common/axes/x-axis.component.ts @@ -30,6 +30,9 @@ import { ViewDimensions } from '../types/view-dimension.interface'; [orient]="xOrient" [showGridLines]="showGridLines" [gridLineHeight]="dims.height" + [referenceLines]="referenceLines" + [showRefLines]="showRefLines" + [showRefLabels]="showRefLabels" [width]="dims.width" [tickValues]="ticks" [wrapTicks]="wrapTicks" @@ -61,6 +64,9 @@ export class XAxisComponent implements OnChanges { @Input() ticks: any[]; @Input() xAxisTickCount: number; @Input() xOrient: Orientation = Orientation.Bottom; + @Input() referenceLines: any[]; + @Input() showRefLines: boolean; + @Input() showRefLabels: boolean; @Input() xAxisOffset: number = 0; @Input() wrapTicks = false; diff --git a/projects/swimlane/ngx-charts/src/lib/common/axes/y-axis-ticks.component.ts b/projects/swimlane/ngx-charts/src/lib/common/axes/y-axis-ticks.component.ts index 50eb1d611..f3d310b2d 100644 --- a/projects/swimlane/ngx-charts/src/lib/common/axes/y-axis-ticks.component.ts +++ b/projects/swimlane/ngx-charts/src/lib/common/axes/y-axis-ticks.component.ts @@ -77,14 +77,9 @@ import { TextAnchor } from '../types/text-anchor.enum'; - + - + {{ tickTrim(tickFormat(refLine.value)) }}