Browse Source

Merge branch 'update-line-with-models' into 'main'

Update line with models

See merge request chartwerk/line-pod!24
pull/1/head
Alexander Velikiy 2 years ago
parent
commit
9bf20c4169
  1. 16
      examples/demo_live.html
  2. 3
      package.json
  3. 274
      src/index.ts
  4. 23
      src/models/line_series.ts
  5. 12
      src/types.ts
  6. 6
      yarn.lock

16
examples/demo_live.html

@ -15,9 +15,9 @@
const startTime = 1590590148; const startTime = 1590590148;
const arrayLength = 100; const arrayLength = 100;
this.isZoomed = false; // TODO: temporary hack to have zoomin|zoomout with `appendData`. It will be moved to Pod. this.isZoomed = false; // TODO: temporary hack to have zoomin|zoomout with `appendData`. It will be moved to Pod.
const data1 = Array.from({ length: arrayLength }, (el, idx) => [startTime + idx * 10000, Math.floor(Math.random() * 40)]); var data1 = Array.from({ length: arrayLength }, (el, idx) => [startTime + idx * 10000, Math.floor(Math.random() * 40)]);
const data2 = Array.from({ length: arrayLength }, (el, idx) => [startTime + idx * 10000, Math.floor(Math.random() * 100)]); var data2 = Array.from({ length: arrayLength }, (el, idx) => [startTime + idx * 10000, Math.floor(Math.random() * 100)]);
const data3 = Array.from({ length: arrayLength }, (el, idx) => [startTime + idx * 10000, Math.floor(Math.random() * 20) + 90]); var data3 = Array.from({ length: arrayLength }, (el, idx) => [startTime + idx * 10000, Math.floor(Math.random() * 20) + 90]);
const zoomIn = (ranges) => { const xRange = ranges[0]; options.axis.x.range = xRange; pod.updateData(undefined, options); this.isZoomed = true } const zoomIn = (ranges) => { const xRange = ranges[0]; options.axis.x.range = xRange; pod.updateData(undefined, options); this.isZoomed = true }
const zoomOut = (ranges) => { options.axis.x.range = undefined; pod.updateData(undefined, options); this.isZoomed = false } const zoomOut = (ranges) => { options.axis.x.range = undefined; pod.updateData(undefined, options); this.isZoomed = false }
let options = { renderLegend: false, axis: { y: { invert: false, range: [0, 350] }, x: { format: 'time' } }, eventsCallbacks: { zoomIn: zoomIn, zoomOut } }; let options = { renderLegend: false, axis: { y: { invert: false, range: [0, 350] }, x: { format: 'time' } }, eventsCallbacks: { zoomIn: zoomIn, zoomOut } };
@ -37,9 +37,17 @@
const d1 = [startTime + rerenderIdx * 10000, Math.floor(Math.random() * 20) + 90]; const d1 = [startTime + rerenderIdx * 10000, Math.floor(Math.random() * 20) + 90];
const d2 = [startTime + rerenderIdx * 10000, Math.floor(Math.random() * 100)]; const d2 = [startTime + rerenderIdx * 10000, Math.floor(Math.random() * 100)];
const d3 = [startTime + rerenderIdx * 10000, Math.floor(Math.random() * 20) + 90]; const d3 = [startTime + rerenderIdx * 10000, Math.floor(Math.random() * 20) + 90];
console.log('d1', data1)
const shouldRerender = !this.isZoomed; const shouldRerender = !this.isZoomed;
console.time('rerender'); console.time('rerender');
pod.appendData([d1, d2, d3], shouldRerender); data1.push(d1);
data2.push(d2);
data3.push(d3);
pod.updateData([
{ target: 'test1', datapoints: data1, color: 'green', maxLength: arrayLength + 30, renderDots: false },
{ target: 'test2', datapoints: data2, color: 'blue', maxLength: arrayLength + 30, renderDots: false },
{ target: 'test3', datapoints: data3, color: 'orange', maxLength: arrayLength + 30, renderDots: false },
]);
console.timeEnd('rerender'); console.timeEnd('rerender');
if(rerenderIdx > arrayLength + 100) { if(rerenderIdx > arrayLength + 100) {
clearInterval(test); clearInterval(test);

3
package.json

@ -6,7 +6,8 @@
"scripts": { "scripts": {
"build": "webpack --config build/webpack.prod.conf.js && webpack --config build/webpack.dev.conf.js", "build": "webpack --config build/webpack.prod.conf.js && webpack --config build/webpack.dev.conf.js",
"dev": "webpack --watch --config build/webpack.dev.conf.js", "dev": "webpack --watch --config build/webpack.dev.conf.js",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1",
"update-core": "yarn up @chartwerk/core && yarn up @chartwerk/core@latest"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

274
src/index.ts

@ -1,5 +1,7 @@
import { ChartwerkPod, VueChartwerkPodMixin, TickOrientation, TimeFormat, CrosshairOrientation, BrushOrientation } from '@chartwerk/core'; import { ChartwerkPod, VueChartwerkPodMixin, TimeFormat, CrosshairOrientation, BrushOrientation } from '@chartwerk/core';
import { LineTimeSerie, LineOptions, Mode } from './types'; import { LineTimeSerie, LineOptions } from './types';
import { LineSeries } from './models/line_series';
import * as d3 from 'd3'; import * as d3 from 'd3';
import * as _ from 'lodash'; import * as _ from 'lodash';
@ -15,6 +17,7 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
constructor(_el: HTMLElement, _series: LineTimeSerie[] = [], _options: LineOptions = {}) { constructor(_el: HTMLElement, _series: LineTimeSerie[] = [], _options: LineOptions = {}) {
super(_el, _series, _options); super(_el, _series, _options);
this.series = new LineSeries(_series);
} }
renderMetrics(): void { renderMetrics(): void {
@ -24,35 +27,13 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
this.initLineGenerator(); this.initLineGenerator();
this.initAreaGenerator(); this.initAreaGenerator();
// TODO: seems that renderMetrics is not correct name if(!this.series.isSeriesAvailable) {
if(this.series.length === 0) {
this.renderNoDataPointsMessage(); this.renderNoDataPointsMessage();
return; return;
} }
for(let idx = 0; idx < this.series.length; ++idx) { for(const serie of this.series.visibleSeries) {
if(this.series[idx].visible === false) { this._renderMetric(serie);
continue;
}
// TODO: use _.defaults same as in core
const confidence = this.series[idx].confidence || 0;
const mode = this.series[idx].mode || Mode.STANDARD;
const target = this.series[idx].target;
const renderDots = this.series[idx].renderDots !== undefined ? this.series[idx].renderDots : false;
const renderLines = this.series[idx].renderLines !== undefined ? this.series[idx].renderLines : true;
this._renderMetric(
this.series[idx].datapoints,
{
color: this.getSerieColor(idx),
confidence,
target,
mode,
serieIdx: idx,
renderDots,
renderLines,
}
);
} }
} }
@ -63,141 +44,62 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
initLineGenerator(): void { initLineGenerator(): void {
this.lineGenerator = d3.line() this.lineGenerator = d3.line()
.x(d => this.xScale(d[0])) .x(d => this.state.xScale(d[0]))
.y(d => this.yScale(d[1])); .y(d => this.state.yScale(d[1]));
} }
initAreaGenerator(): void { initAreaGenerator(): void {
this.areaGenerator = d3.area() this.areaGenerator = d3.area()
.x(d => this.xScale(d[0])) .x(d => this.state.xScale(d[0]))
.y1(d => this.yScale(d[1])) .y1(d => this.state.yScale(d[1]))
.y0(d => this.height); .y0(d => this.height);
} }
getRenderGenerator(serieIdx: number): any { getRenderGenerator(renderArea: boolean): any {
const renderArea = this.series[serieIdx].renderArea;
if(renderArea) { if(renderArea) {
return this.areaGenerator; return this.areaGenerator;
} }
return this.lineGenerator; return this.lineGenerator;
} }
public appendData(data: [number, number][], shouldRerender = true): void { _renderDots(serie: LineTimeSerie): void {
for(let idx = 0; idx < this.series.length; ++idx) {
if(this.series[idx].visible === false) {
continue;
}
this.series[idx].datapoints.push(data[idx]);
const maxLength = this.series[idx].maxLength;
if(maxLength !== undefined && this.series[idx].datapoints.length > maxLength) {
this.series[idx].datapoints.shift();
}
}
for(let idx = 0; idx < this.series.length; ++idx) {
this.metricContainer.select(`.metric-path-${idx}`)
.datum(this.series[idx].datapoints)
.attr('d', this.getRenderGenerator(idx));
if(this.series[idx].renderDots === true) {
this.metricContainer.selectAll(`.metric-circle-${idx}`)
.data(this.series[idx].datapoints)
.attr('cx', d => this.xScale(d[0]))
.attr('cy', d => this.yScale(d[1]));
this._renderDots([data[idx]], idx);
}
}
if(shouldRerender) {
const rightBorder = _.last(data)[0];
this.state.xValueRange = [this.state.getMinValueX(), rightBorder];
this.renderXAxis();
this.renderYAxis();
this.renderGrid();
}
}
_renderDots(datapoints: number[][], serieIdx: number): void {
const customClass = this.series[serieIdx].class || '';
this.metricContainer.selectAll(null) this.metricContainer.selectAll(null)
.data(datapoints) .data(serie.datapoints)
.enter() .enter()
.append('circle') .append('circle')
.attr('class', `metric-circle-${serieIdx} metric-el ${customClass}`) .attr('class', `metric-circle-${serie.idx} metric-el ${serie.class}`)
.attr('fill', this.getSerieColor(serieIdx)) .attr('fill', serie.color)
.attr('r', METRIC_CIRCLE_RADIUS) .attr('r', METRIC_CIRCLE_RADIUS)
.style('pointer-events', 'none') .style('pointer-events', 'none')
.attr('cx', d => this.xScale(d[0])) .attr('cx', d => this.state.xScale(d[0]))
.attr('cy', d => this.yScale(d[1])); .attr('cy', d => this.state.yScale(d[1]));
} }
_renderLines(datapoints: number[][], serieIdx: number): void { _renderLines(serie: LineTimeSerie): void {
const dashArray = this.series[serieIdx].dashArray !== undefined ? this.series[serieIdx].dashArray : '0'; const fillColor = serie.renderArea ? serie.color : 'none';
const customClass = this.series[serieIdx].class || ''; const fillOpacity = serie.renderArea ? 0.5 : 'none';
const fillColor = this.series[serieIdx].renderArea ? this.getSerieColor(serieIdx) : 'none';
const fillOpacity = this.series[serieIdx].renderArea ? 0.5 : 'none';
this.metricContainer this.metricContainer
.append('path') .append('path')
.datum(datapoints) .datum(serie.datapoints)
.attr('class', `metric-path-${serieIdx} metric-el ${customClass}`) .attr('class', `metric-path-${serie.idx} metric-el ${serie.class}`)
.attr('fill', fillColor) .attr('fill', fillColor)
.attr('fill-opacity', fillOpacity) .attr('fill-opacity', fillOpacity)
.attr('stroke', this.getSerieColor(serieIdx)) .attr('stroke', serie.color)
.attr('stroke-width', 1) .attr('stroke-width', 1)
.attr('stroke-opacity', 0.7) .attr('stroke-opacity', 0.7)
.attr('pointer-events', 'none') .attr('pointer-events', 'none')
.style('stroke-dasharray', dashArray) .style('stroke-dasharray', serie.dashArray)
.attr('d', this.getRenderGenerator(serieIdx)); .attr('d', this.getRenderGenerator(serie.renderArea));
}
_renderMetric(
datapoints: number[][],
metricOptions: {
color: string,
confidence: number,
target: string,
mode: Mode,
serieIdx: number,
renderDots: boolean,
renderLines: boolean,
}
): void {
if(_.includes(this.seriesTargetsWithBounds, metricOptions.target)) {
return;
} }
if(metricOptions.mode === Mode.CHARGE) { _renderMetric(serie: LineTimeSerie): void {
const dataPairs = d3.pairs(datapoints); if(serie.renderLines === true) {
this.metricContainer.selectAll(null) this._renderLines(serie);
.data(dataPairs)
.enter()
.append('line')
.attr('x1', d => this.xScale(d[0][0]))
.attr('x2', d => this.xScale(d[1][0]))
.attr('y1', d => this.yScale(d[0][1]))
.attr('y2', d => this.yScale(d[1][1]))
.attr('stroke-opacity', 0.7)
.style('stroke-width', 1)
.style('stroke', d => {
if(d[1][0] > d[0][0]) {
return 'green';
} else if (d[1][0] < d[0][0]) {
return 'red';
} else {
return 'gray';
}
});
return;
}
if(metricOptions.renderLines === true) {
this._renderLines(datapoints, metricOptions.serieIdx);
} }
if(metricOptions.renderDots === true) { if(serie.renderDots === true) {
this._renderDots(datapoints, metricOptions.serieIdx); this._renderDots(serie);
} }
} }
@ -209,7 +111,7 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
appendCrosshairCircles(): void { appendCrosshairCircles(): void {
// circle for each serie // circle for each serie
this.series.forEach((serie: LineTimeSerie, serieIdx: number) => { this.series.visibleSeries.forEach((serie: LineTimeSerie, serieIdx: number) => {
this.appendCrosshairCircle(serieIdx); this.appendCrosshairCircle(serieIdx);
}); });
} }
@ -222,7 +124,7 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
.attr('cx', -CROSSHAIR_BACKGROUND_RAIDUS) .attr('cx', -CROSSHAIR_BACKGROUND_RAIDUS)
.attr('cy', -CROSSHAIR_BACKGROUND_RAIDUS) .attr('cy', -CROSSHAIR_BACKGROUND_RAIDUS)
.attr('clip-path', `url(#${this.rectClipId})`) .attr('clip-path', `url(#${this.rectClipId})`)
.attr('fill', this.getSerieColor(serieIdx)) .attr('fill', this.series.visibleSeries[serieIdx].color)
.style('opacity', CROSSHAIR_BACKGROUND_OPACITY) .style('opacity', CROSSHAIR_BACKGROUND_OPACITY)
.style('pointer-events', 'none'); .style('pointer-events', 'none');
@ -232,23 +134,19 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
.attr('cy', -CROSSHAIR_CIRCLE_RADIUS) .attr('cy', -CROSSHAIR_CIRCLE_RADIUS)
.attr('class', `crosshair-circle-${serieIdx}`) .attr('class', `crosshair-circle-${serieIdx}`)
.attr('clip-path', `url(#${this.rectClipId})`) .attr('clip-path', `url(#${this.rectClipId})`)
.attr('fill', this.getSerieColor(serieIdx)) .attr('fill', this.series.visibleSeries[serieIdx].color)
.attr('r', CROSSHAIR_CIRCLE_RADIUS) .attr('r', CROSSHAIR_CIRCLE_RADIUS)
.style('pointer-events', 'none'); .style('pointer-events', 'none');
} }
public renderSharedCrosshair(values: { x?: number, y?: number }): void { public renderSharedCrosshair(values: { x?: number, y?: number }): void {
this.onMouseOver(); // TODO: refactor to use it once this.onMouseOver(); // TODO: refactor to use it once
const eventX = this.xScale(values.x); const eventX = this.state.xScale(values.x);
const eventY = this.yScale(values.y); const eventY = this.state.yScale(values.y);
this.moveCrosshairLine(eventX, eventY); this.moveCrosshairLine(eventX, eventY);
const datapoints = this.findAndHighlightDatapoints(values.x, values.y); const datapoints = this.findAndHighlightDatapoints(values.x, values.y);
if(this.options.eventsCallbacks === undefined || this.options.eventsCallbacks.sharedCrosshairMove === undefined) { this.options.callbackSharedCrosshairMove({
return;
}
this.options.eventsCallbacks.sharedCrosshairMove({
datapoints: datapoints, datapoints: datapoints,
eventX, eventY eventX, eventY
}); });
@ -350,7 +248,7 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
// columnIdx: 1 for y, 0 for x // columnIdx: 1 for y, 0 for x
// inverval: x/y value interval between data points // inverval: x/y value interval between data points
// TODO: move it to base/state instead of timeInterval // TODO: move it to base/state instead of timeInterval
const intervals = _.map(this.series, serie => { const intervals = _.map(this.series.visibleSeries, serie => {
if(serie.datapoints.length < 2) { if(serie.datapoints.length < 2) {
return undefined; return undefined;
} }
@ -366,23 +264,15 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
onMouseMove(): void { onMouseMove(): void {
const eventX = d3.mouse(this.chartContainer.node())[0]; const eventX = d3.mouse(this.chartContainer.node())[0];
const eventY = d3.mouse(this.chartContainer.node())[1]; const eventY = d3.mouse(this.chartContainer.node())[1];
const xValue = this.xScale.invert(eventX); // mouse x position in xScale const xValue = this.state.xScale.invert(eventX); // mouse x position in xScale
const yValue = this.yScale.invert(eventY); const yValue = this.state.yScale.invert(eventY);
// TODO: isOutOfChart is a hack, use clip path correctly
if(this.isOutOfChart() === true) {
this.crosshair.style('display', 'none');
return;
}
this.moveCrosshairLine(eventX, eventY); this.moveCrosshairLine(eventX, eventY);
const datapoints = this.findAndHighlightDatapoints(xValue, yValue); const datapoints = this.findAndHighlightDatapoints(xValue, yValue);
if(this.options.eventsCallbacks === undefined || this.options.eventsCallbacks.mouseMove === undefined) {
return;
}
// TDOO: is shift key pressed // TDOO: is shift key pressed
// TODO: need to refactor this object // TODO: need to refactor this object
this.options.eventsCallbacks.mouseMove({ this.options.callbackMouseMove({
x: d3.event.pageX, x: d3.event.pageX,
y: d3.event.pageY, y: d3.event.pageY,
xVal: xValue, xVal: xValue,
@ -394,67 +284,30 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
} }
findAndHighlightDatapoints(xValue: number, yValue: number): { value: [number, number], color: string, label: string }[] { findAndHighlightDatapoints(xValue: number, yValue: number): { value: [number, number], color: string, label: string }[] {
if(this.series === undefined || this.series.length === 0) { if(!this.series.isSeriesAvailable) {
return []; return [];
} }
let points = []; // datapoints in each metric that is closest to xValue/yValue position let points = []; // datapoints in each metric that is closest to xValue/yValue position
this.series.forEach((serie: LineTimeSerie, serieIdx: number) => { this.series.visibleSeries.forEach((serie: LineTimeSerie) => {
if(
serie.visible === false ||
_.includes(this.seriesTargetsWithBounds, serie.target)
) {
this.hideCrosshairCircle(serieIdx);
return;
}
const closestDatapoint = this.getClosestDatapoint(serie, xValue, yValue); const closestDatapoint = this.getClosestDatapoint(serie, xValue, yValue);
if(closestDatapoint === undefined || this.isOutOfRange(closestDatapoint, xValue, yValue, serie.useOutOfRange)) { if(closestDatapoint === undefined) {
this.hideCrosshairCircle(serieIdx); this.hideCrosshairCircle(serie.idx);
return; return;
} }
const xPosition = this.xScale(closestDatapoint[0]); const xPosition = this.state.xScale(closestDatapoint[0]);
const yPosition = this.yScale(closestDatapoint[1]); const yPosition = this.state.yScale(closestDatapoint[1]);
this.moveCrosshairCircle(xPosition, yPosition, serieIdx); this.moveCrosshairCircle(xPosition, yPosition, serie.idx);
points.push({ points.push({
value: closestDatapoint, value: closestDatapoint,
color: this.getSerieColor(serieIdx), color: serie.color,
label: serie.alias || serie.target label: serie.alias || serie.target
}); });
}); });
return points; return points;
} }
isOutOfRange(closestDatapoint: [number, number], xValue: number, yValue: number, useOutOfRange = true): boolean {
// find is mouse position more than xRange/yRange from closest point
// TODO: refactor getValueInterval to remove this!
if(useOutOfRange === false) {
return false;
}
let columnIdx; // 1 for y value, 0 for x value
let value; // xValue ot y Value
switch(this.options.crosshair.orientation) {
case CrosshairOrientation.VERTICAL:
columnIdx = 0;
value = xValue;
break;
case CrosshairOrientation.HORIZONTAL:
columnIdx = 1;
value = yValue;
break;
case CrosshairOrientation.BOTH:
// TODO: maybe use voronoi
columnIdx = 1;
value = yValue;
default:
throw new Error(`Unknown type of crosshair orientaion: ${this.options.crosshair.orientation}`);
}
const range = Math.abs(closestDatapoint[columnIdx] - value);
const interval = this.getValueInterval(columnIdx); // interval between points
// do not move crosshair circles, it mouse to far from closest point
return interval === undefined || range > interval / 2;
}
onMouseOver(): void { onMouseOver(): void {
this.crosshair.style('display', null); this.crosshair.style('display', null);
this.crosshair.selectAll('.crosshair-circle') this.crosshair.selectAll('.crosshair-circle')
@ -462,30 +315,21 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
} }
onMouseOut(): void { onMouseOut(): void {
if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.mouseOut !== undefined) { this.options.callbackMouseOut();
this.options.eventsCallbacks.mouseOut();
}
this.crosshair.style('display', 'none'); this.crosshair.style('display', 'none');
} }
isDoubleClickActive(): boolean { isDoubleClickActive(): boolean {
if(this.options.zoomEvents.mouse.doubleClick === undefined) { return this.options.doubleClickEvent.isActive;
return false;
}
return this.options.zoomEvents.mouse.doubleClick.isActive;
} }
// methods below rewrite cores, (move more methods here) // methods below rewrite s, (move more methods here)
protected zoomOut(): void { protected zoomOut(): void {
// TODO: test to remove, seems its depricated
if(this.isOutOfChart() === true) {
return;
}
if(d3.event.type === 'dblclick' && !this.isDoubleClickActive()) { if(d3.event.type === 'dblclick' && !this.isDoubleClickActive()) {
return; return;
} }
// TODO: its not clear, why we use this orientation here. Mb its better to use separate option // TODO: its not clear, why we use this orientation here. Mb its better to use separate option
const orientation: BrushOrientation = this.options.zoomEvents.mouse.zoom.orientation; const orientation: BrushOrientation = this.options.mouseZoomEvent.orientation;
const xInterval = this.state.xValueRange[1] - this.state.xValueRange[0]; const xInterval = this.state.xValueRange[1] - this.state.xValueRange[0];
const yInterval = this.state.yValueRange[1] - this.state.yValueRange[0]; const yInterval = this.state.yValueRange[1] - this.state.yValueRange[0];
switch(orientation) { switch(orientation) {
@ -514,15 +358,13 @@ export class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
this.renderGrid(); this.renderGrid();
this.onMouseOver(); this.onMouseOver();
if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.zoomOut !== undefined) { let xAxisMiddleValue: number = this.state.xScale.invert(this.width / 2);
let xAxisMiddleValue: number = this.xScale.invert(this.width / 2); let yAxisMiddleValue: number = this.state.yScale.invert(this.height / 2);
let yAxisMiddleValue: number = this.yScale.invert(this.height / 2);
const centers = { const centers = {
x: xAxisMiddleValue, x: xAxisMiddleValue,
y: yAxisMiddleValue y: yAxisMiddleValue
} }
this.options.eventsCallbacks.zoomOut(centers); this.options.callbackZoomOut(centers);
}
} }
} }
@ -557,4 +399,4 @@ export const VueChartwerkLinePod = {
} }
}; };
export { LineTimeSerie, LineOptions, Mode, TickOrientation, TimeFormat }; export { LineTimeSerie, LineOptions, TimeFormat };

23
src/models/line_series.ts

@ -0,0 +1,23 @@
import { CoreSeries } from '@chartwerk/core';
import { LineTimeSerie } from '../types';
const LINE_SERIE_DEFAULTS = {
maxLength: undefined,
renderDots: false,
renderLines: true,
dashArray: '0',
class: '',
renderArea: false,
};
export class LineSeries extends CoreSeries<LineTimeSerie> {
constructor(series: LineTimeSerie[]) {
super(series);
}
protected get defaults(): LineTimeSerie {
return { ...this._coreDefaults, ...LINE_SERIE_DEFAULTS };
}
}

12
src/types.ts

@ -1,19 +1,13 @@
import { TimeSerie, Options } from '@chartwerk/core'; import { Serie, Options } from '@chartwerk/core';
type LineTimeSerieParams = { type LineTimeSerieParams = {
confidence: number,
mode: Mode,
maxLength: number, maxLength: number,
renderDots: boolean, renderDots: boolean,
renderLines: boolean, // TODO: refactor same as scatter-pod renderLines: boolean, // TODO: refactor same as scatter-pod
useOutOfRange: boolean, // It's temporary hack. Need to refactor getValueInterval() method
dashArray: string; // dasharray attr, only for lines dashArray: string; // dasharray attr, only for lines
class: string; // option to add custom class to each serie element class: string; // option to add custom class to each serie element
renderArea: boolean; // TODO: move to render type renderArea: boolean; // TODO: move to render type
} }
export enum Mode {
STANDARD = 'Standard', export type LineTimeSerie = Serie & Partial<LineTimeSerieParams>;
CHARGE = 'Charge'
}
export type LineTimeSerie = TimeSerie & Partial<LineTimeSerieParams>;
export type LineOptions = Options; export type LineOptions = Options;

6
yarn.lock

@ -6,12 +6,12 @@ __metadata:
cacheKey: 8 cacheKey: 8
"@chartwerk/core@npm:latest": "@chartwerk/core@npm:latest":
version: 0.5.4 version: 0.6.0
resolution: "@chartwerk/core@npm:0.5.4" resolution: "@chartwerk/core@npm:0.6.0"
dependencies: dependencies:
d3: ^5.7.2 d3: ^5.7.2
lodash: ^4.14.149 lodash: ^4.14.149
checksum: 0d686409377d6880f0012db3d9c1e1910cf3660885ac13196dabe93f57eca53984cf52dcf781b2876373fe9efc7b9d0209117cbcd811c50892b1de4f38a4a37f checksum: db368ea1ba1f9b48583c3da953998206329ea552eaff4e9f13cb02b25e0289464eca268baa86f045d62552a0859fc3ee2ccf70e7df95b58327bba217f9888169
languageName: node languageName: node
linkType: hard linkType: hard

Loading…
Cancel
Save