|
|
|
@ -17,15 +17,25 @@ const DEFAULT_GAUGE_OPTIONS: GaugeOptions = {
|
|
|
|
|
type: ZoomType.NONE |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
colors: ['green', 'yellow', 'red'], |
|
|
|
|
stops: [10, 30], |
|
|
|
|
stops: [ |
|
|
|
|
{ |
|
|
|
|
color: 'green', |
|
|
|
|
value: 10 |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
color: 'yellow', |
|
|
|
|
value: 20 |
|
|
|
|
} |
|
|
|
|
], |
|
|
|
|
defaultColor: 'red', |
|
|
|
|
stat: Stat.CURRENT, |
|
|
|
|
innerRadius: 50, |
|
|
|
|
outerRadius: 80 |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
export class ChartwerkGaugePod extends ChartwerkPod<GaugeTimeSerie, GaugeOptions> { |
|
|
|
|
gaugeTransform = ''; |
|
|
|
|
// TODO: better name
|
|
|
|
|
private _gaugeTransform = ''; |
|
|
|
|
|
|
|
|
|
constructor(el: HTMLElement, _series: GaugeTimeSerie[] = [], _options: GaugeOptions = {}) { |
|
|
|
|
super( |
|
|
|
@ -34,74 +44,17 @@ export class ChartwerkGaugePod extends ChartwerkPod<GaugeTimeSerie, GaugeOptions
|
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get valueRange(): number[] { |
|
|
|
|
if(this.stops.length < 2) { |
|
|
|
|
return this.stops; |
|
|
|
|
} |
|
|
|
|
let range = [this.stops[0]]; |
|
|
|
|
for(let i = 1; i < this.stops.length; i++) { |
|
|
|
|
range.push(this.stops[i] - this.stops[i-1]); |
|
|
|
|
} |
|
|
|
|
return range; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get colors(): string[] { |
|
|
|
|
return this.options.colors; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get stat(): Stat { |
|
|
|
|
return this.options.stat; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get stops(): number[] { |
|
|
|
|
return [...this.options.stops, this.options.maxValue || this.maxValue]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get innerRadius(): number { |
|
|
|
|
return this.options.innerRadius; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get outerRadius(): number { |
|
|
|
|
return this.options.outerRadius; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get aggregatedValue(): number { |
|
|
|
|
switch(this.stat) { |
|
|
|
|
case Stat.CURRENT: |
|
|
|
|
return _.last(this.series[0].datapoints)[0]; |
|
|
|
|
default: |
|
|
|
|
throw new Error(`Unsupported stat: ${this.stat}`); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
renderNeedle(): void { |
|
|
|
|
const maxValue = this.options.maxValue || this.maxValue; |
|
|
|
|
|
|
|
|
|
let scale = d3.scaleLinear() |
|
|
|
|
.domain([0, maxValue]) |
|
|
|
|
.range([0, 180]) |
|
|
|
|
.clamp(true); |
|
|
|
|
|
|
|
|
|
this.chartContainer.selectAll('.needle').data([this.aggregatedValue]) |
|
|
|
|
.transition() |
|
|
|
|
.ease(d3.easeElasticOut) |
|
|
|
|
.duration(1000) |
|
|
|
|
.attr('transform', (d: number) => { |
|
|
|
|
return this.gaugeTransform + 'rotate(' + scale(d) + ')' |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
renderMetrics(): void { |
|
|
|
|
if(this.series.length === 0 || this.series[0].datapoints.length === 0) { |
|
|
|
|
if (this.series.length === 0 || this.series[0].datapoints.length === 0) { |
|
|
|
|
this.renderNoDataPointsMessage(); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.gaugeTransform = `translate(${this.width / 2},${this.height - 10})`; |
|
|
|
|
this._gaugeTransform = `translate(${this.width / 2},${this.height - 10})`; |
|
|
|
|
|
|
|
|
|
const arc = d3.arc() |
|
|
|
|
.innerRadius(this.innerRadius) |
|
|
|
|
.outerRadius(this.outerRadius) |
|
|
|
|
.innerRadius(this._innerRadius) |
|
|
|
|
.outerRadius(this._outerRadius) |
|
|
|
|
.padAngle(0); |
|
|
|
|
|
|
|
|
|
const pie = d3.pie() |
|
|
|
@ -109,17 +62,17 @@ export class ChartwerkGaugePod extends ChartwerkPod<GaugeTimeSerie, GaugeOptions
|
|
|
|
|
.endAngle(Math.PI / 2) |
|
|
|
|
.sort(null); |
|
|
|
|
|
|
|
|
|
const arcs = pie(this.valueRange); |
|
|
|
|
const arcs = pie(this._valueRange); |
|
|
|
|
|
|
|
|
|
this.chartContainer.selectAll('path') |
|
|
|
|
.data(arcs) |
|
|
|
|
.enter() |
|
|
|
|
.append('path') |
|
|
|
|
.style('fill', (d: object, i: number) => { |
|
|
|
|
return this.colors[i]; |
|
|
|
|
return this._colors[i]; |
|
|
|
|
}) |
|
|
|
|
.attr('d', arc as any) |
|
|
|
|
.attr('transform', this.gaugeTransform) |
|
|
|
|
.attr('transform', this._gaugeTransform) |
|
|
|
|
|
|
|
|
|
const needle = this.chartContainer.selectAll('.needle') |
|
|
|
|
.data([0]) |
|
|
|
@ -132,12 +85,72 @@ export class ChartwerkGaugePod extends ChartwerkPod<GaugeTimeSerie, GaugeOptions
|
|
|
|
|
.classed('needle', true) |
|
|
|
|
.style('stroke', 'black') |
|
|
|
|
.attr('transform', (d: number) => { |
|
|
|
|
return this.gaugeTransform + 'rotate(' + d + ')' |
|
|
|
|
return this._gaugeTransform + 'rotate(' + d + ')' |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.renderNeedle(); |
|
|
|
|
this._renderNeedle(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: better name
|
|
|
|
|
private get _valueRange(): number[] { |
|
|
|
|
// TODO: refactor
|
|
|
|
|
const stopValues = [...this.options.stops.map(stop => stop.value), this.options.maxValue || this.maxValue] |
|
|
|
|
|
|
|
|
|
if(stopValues.length < 2) { |
|
|
|
|
return stopValues; |
|
|
|
|
} |
|
|
|
|
let range = [stopValues[0]]; |
|
|
|
|
for(let i = 1; i < stopValues.length; i++) { |
|
|
|
|
range.push(stopValues[i] - stopValues[i-1]); |
|
|
|
|
} |
|
|
|
|
return range; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private get _colors(): string[] { |
|
|
|
|
// TODO: refactor
|
|
|
|
|
return [...this.options.stops.map(stop => stop.color), this.options.defaultColor]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private get _stat(): Stat { |
|
|
|
|
return this.options.stat; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private get _innerRadius(): number { |
|
|
|
|
return this.options.innerRadius; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private get _outerRadius(): number { |
|
|
|
|
return this.options.outerRadius; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private get aggregatedValue(): number { |
|
|
|
|
switch(this._stat) { |
|
|
|
|
case Stat.CURRENT: |
|
|
|
|
return _.last(this.series[0].datapoints)[0]; |
|
|
|
|
// TODO: support other stats
|
|
|
|
|
default: |
|
|
|
|
throw new Error(`Unsupported stat: ${this._stat}`); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private get _maxValue(): number { |
|
|
|
|
return this.options.maxValue || this.maxValue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private _renderNeedle(): void { |
|
|
|
|
let scale = d3.scaleLinear() |
|
|
|
|
.domain([0, this._maxValue]) |
|
|
|
|
.range([0, 180]) |
|
|
|
|
.clamp(true); |
|
|
|
|
|
|
|
|
|
this.chartContainer.selectAll('.needle').data([this.aggregatedValue]) |
|
|
|
|
.transition() |
|
|
|
|
.ease(d3.easeElasticOut) |
|
|
|
|
.duration(1000) |
|
|
|
|
.attr('transform', (d: number) => { |
|
|
|
|
return this._gaugeTransform + 'rotate(' + scale(d) + ')' |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* handlers and overloads */ |
|
|
|
|
onMouseOver(): void {} |
|
|
|
|