|
|
@ -107,13 +107,13 @@ const DEFAULT_OPTIONS: Options = { |
|
|
|
|
|
|
|
|
|
|
|
abstract class ChartwerkPod<T extends TimeSerie, O extends Options> { |
|
|
|
abstract class ChartwerkPod<T extends TimeSerie, O extends Options> { |
|
|
|
protected d3Node?: d3.Selection<HTMLElement, unknown, null, undefined>; |
|
|
|
protected d3Node?: d3.Selection<HTMLElement, unknown, null, undefined>; |
|
|
|
protected chartContainer?: d3.Selection<SVGGElement, unknown, null, undefined>; |
|
|
|
|
|
|
|
protected customOverlay?: d3.Selection<SVGRectElement, unknown, null, undefined>; |
|
|
|
protected customOverlay?: d3.Selection<SVGRectElement, unknown, null, undefined>; |
|
|
|
protected crosshair?: d3.Selection<SVGGElement, unknown, null, undefined>; |
|
|
|
protected crosshair?: d3.Selection<SVGGElement, unknown, null, undefined>; |
|
|
|
protected brush?: d3.BrushBehavior<unknown>; |
|
|
|
protected brush?: d3.BrushBehavior<unknown>; |
|
|
|
protected zoom?: any; |
|
|
|
protected zoom?: any; |
|
|
|
protected svg?: d3.Selection<SVGElement, unknown, null, undefined>; |
|
|
|
protected svg?: d3.Selection<SVGElement, unknown, null, undefined>; |
|
|
|
protected state?: PodState<T, O>; |
|
|
|
protected state: PodState<T, O>; |
|
|
|
|
|
|
|
protected pan?: any; // TODO: not any;
|
|
|
|
protected clipPath?: any; |
|
|
|
protected clipPath?: any; |
|
|
|
protected isPanning = false; |
|
|
|
protected isPanning = false; |
|
|
|
protected isBrushing = false; |
|
|
|
protected isBrushing = false; |
|
|
@ -131,7 +131,10 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> { |
|
|
|
protected readonly d3: typeof d3; |
|
|
|
protected readonly d3: typeof d3; |
|
|
|
protected deltaYTransform = 0; |
|
|
|
protected deltaYTransform = 0; |
|
|
|
protected debouncedRender = debounce(this.render.bind(this), 100); |
|
|
|
protected debouncedRender = debounce(this.render.bind(this), 100); |
|
|
|
|
|
|
|
// containers
|
|
|
|
|
|
|
|
// TODO: add better names
|
|
|
|
|
|
|
|
protected chartContainer: d3.Selection<SVGGElement, unknown, null, undefined>; |
|
|
|
|
|
|
|
protected metricContainer: d3.Selection<SVGGElement, unknown, null, undefined>; |
|
|
|
// components
|
|
|
|
// components
|
|
|
|
protected grid: Grid; |
|
|
|
protected grid: Grid; |
|
|
|
|
|
|
|
|
|
|
@ -175,6 +178,7 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> { |
|
|
|
this.addEvents(); |
|
|
|
this.addEvents(); |
|
|
|
|
|
|
|
|
|
|
|
this.renderCrosshair(); |
|
|
|
this.renderCrosshair(); |
|
|
|
|
|
|
|
this.renderMetricsContainer(); |
|
|
|
this.renderMetrics(); |
|
|
|
this.renderMetrics(); |
|
|
|
|
|
|
|
|
|
|
|
this.renderLegend(); |
|
|
|
this.renderLegend(); |
|
|
@ -201,7 +205,7 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> { |
|
|
|
let options = cloneDeep(newOptions); |
|
|
|
let options = cloneDeep(newOptions); |
|
|
|
defaultsDeep(options, DEFAULT_OPTIONS); |
|
|
|
defaultsDeep(options, DEFAULT_OPTIONS); |
|
|
|
this.options = options; |
|
|
|
this.options = options; |
|
|
|
|
|
|
|
// TODO: update state if axis ranges were changed
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected updateSeries(newSeries: T[]): void { |
|
|
|
protected updateSeries(newSeries: T[]): void { |
|
|
@ -240,6 +244,22 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> { |
|
|
|
this.grid = new Grid(this.d3, this.chartContainer, svgElParams, this.options.grid); |
|
|
|
this.grid = new Grid(this.d3, this.chartContainer, svgElParams, this.options.grid); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected renderMetricsContainer(): void { |
|
|
|
|
|
|
|
this.d3.select('.metrics-container').remove(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: it should be a class with svgElParams as fields
|
|
|
|
|
|
|
|
// container for clip path
|
|
|
|
|
|
|
|
const clipContatiner = this.chartContainer |
|
|
|
|
|
|
|
.append('g') |
|
|
|
|
|
|
|
.attr('clip-path', `url(#${this.rectClipId})`) |
|
|
|
|
|
|
|
.attr('class', 'metrics-container'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// container for panning
|
|
|
|
|
|
|
|
this.metricContainer = clipContatiner |
|
|
|
|
|
|
|
.append('g') |
|
|
|
|
|
|
|
.attr('class', 'metrics-rect'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected createSvg(): void { |
|
|
|
protected createSvg(): void { |
|
|
|
this.d3Node.select('svg').remove(); |
|
|
|
this.d3Node.select('svg').remove(); |
|
|
|
this.svg = this.d3Node |
|
|
|
this.svg = this.d3Node |
|
|
@ -329,6 +349,8 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected renderCrosshair(): void { |
|
|
|
protected renderCrosshair(): void { |
|
|
|
|
|
|
|
this.d3.selectAll('#crosshair-container').remove(); |
|
|
|
|
|
|
|
|
|
|
|
this.crosshair = this.chartContainer.append('g') |
|
|
|
this.crosshair = this.chartContainer.append('g') |
|
|
|
.attr('id', 'crosshair-container') |
|
|
|
.attr('id', 'crosshair-container') |
|
|
|
.style('display', 'none'); |
|
|
|
.style('display', 'none'); |
|
|
@ -463,11 +485,11 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> { |
|
|
|
if(this.options.axis.y1.isActive === true) { |
|
|
|
if(this.options.axis.y1.isActive === true) { |
|
|
|
this.initScaleY1 = this.y1Scale.copy(); |
|
|
|
this.initScaleY1 = this.y1Scale.copy(); |
|
|
|
} |
|
|
|
} |
|
|
|
const pan = this.d3.zoom() |
|
|
|
this.pan = this.d3.zoom() |
|
|
|
.on('zoom', this.onPanning.bind(this)) |
|
|
|
.on('zoom', this.onPanning.bind(this)) |
|
|
|
.on('end', this.onPanningEnd.bind(this)); |
|
|
|
.on('end', this.onPanningEnd.bind(this)); |
|
|
|
|
|
|
|
|
|
|
|
this.chartContainer.call(pan); |
|
|
|
this.chartContainer.call(this.pan); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected renderClipPath(): void { |
|
|
|
protected renderClipPath(): void { |
|
|
@ -592,13 +614,11 @@ abstract class ChartwerkPod<T extends TimeSerie, O extends Options> { |
|
|
|
public rescaleMetricAndAxis(event: d3.D3ZoomEvent<any, any>): void { |
|
|
|
public rescaleMetricAndAxis(event: d3.D3ZoomEvent<any, any>): void { |
|
|
|
this.isPanning = true; |
|
|
|
this.isPanning = true; |
|
|
|
this.onMouseOut(); |
|
|
|
this.onMouseOut(); |
|
|
|
|
|
|
|
|
|
|
|
this.onPanningRescale(event); |
|
|
|
this.onPanningRescale(event); |
|
|
|
|
|
|
|
|
|
|
|
// TODO: check clear state for necessity
|
|
|
|
// TODO: check clear state for necessity
|
|
|
|
this.renderYAxis(); |
|
|
|
this.renderYAxis(); |
|
|
|
this.renderXAxis(); |
|
|
|
this.renderXAxis(); |
|
|
|
|
|
|
|
|
|
|
|
// metrics-rect wrapper is required for panning
|
|
|
|
// metrics-rect wrapper is required for panning
|
|
|
|
this.chartContainer.select('.metrics-rect') |
|
|
|
this.chartContainer.select('.metrics-rect') |
|
|
|
.attr('transform', `translate(${this.state.transform.x},${this.state.transform.y}), scale(${this.state.transform.k})`); |
|
|
|
.attr('transform', `translate(${this.state.transform.x},${this.state.transform.y}), scale(${this.state.transform.k})`); |
|
|
|