Browse Source

sync_890b61a13852b9f4b7f430954faf7c41e44183af

target-master
vargburz 3 years ago
parent
commit
ea743b0db7
  1. 1
      .gitignore
  2. 2
      README.md
  3. 35
      build/webpack.base.conf.js
  4. 8
      build/webpack.dev.conf.js
  5. 6
      build/webpack.prod.conf.js
  6. 86
      demo.html
  7. 15
      dist/delaunay.d.ts
  8. 88
      dist/index.d.ts
  9. 9
      dist/index.js
  10. 26
      dist/types.d.ts
  11. 4662
      package-lock.json
  12. 30
      package.json
  13. 89
      src/delaunay.ts
  14. 359
      src/index.ts
  15. 30
      src/types.ts
  16. 22
      tsconfig.json

1
.gitignore vendored

@ -0,0 +1 @@
node_modules

2
README.md

@ -1,3 +1 @@
# scatter-pod
Scatter Pod

35
build/webpack.base.conf.js

@ -0,0 +1,35 @@
const path = require('path');
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
context: resolve('src'),
entry: './index.ts',
plugins: [],
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
exclude: /node_modules/
}
],
},
resolve: {
extensions: ['.ts', '.js'],
},
output: {
filename: 'index.js',
path: resolve('dist'),
libraryTarget: 'umd',
umdNamedDefine: true
}
};

8
build/webpack.dev.conf.js

@ -0,0 +1,8 @@
const baseWebpackConfig = require('./webpack.base.conf');
var conf = baseWebpackConfig;
conf.devtool = 'inline-source-map';
conf.watch = true;
conf.mode = 'development';
module.exports = conf;

6
build/webpack.prod.conf.js

@ -0,0 +1,6 @@
const baseWebpackConfig = require('./webpack.base.conf');
var conf = baseWebpackConfig;
conf.mode = 'production';
module.exports = baseWebpackConfig;

86
demo.html

@ -0,0 +1,86 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<script src="./dist/index.js" type="text/javascript"></script>
</head>
<body>
<div id="chart" style="width: 500px; height: 500px;"></div>
<script type="text/javascript">
var pod = new ChartwerkScatterPod(
document.getElementById('chart'),
[
{
target: 'test1',
datapoints: [
[100, -50, 0],
[200, 150, 0],
[100, 160, 1],
[150, 170, 1],
[150, 180, 0],
[150, 250, 1]
],
color: 'red',
lineType: 'dashed',
pointType: 'circle'
},
{
target: 'test2',
datapoints: [
[200, 50, 1],
[175, 60, 0],
[150, 70, 1]
],
color: 'purple',
pointType: 'rectangle',
pointSize: 5,
yOrientation: 'right',
}
],
{
axis: {
x: {
format: 'numeric',
range: [-100, 300]
},
y: {
invert: true,
range: [-100, 250]
},
y1: {
isActive: true,
range: [0, 250]
}
},
zoomEvents: {
mouse: {
pan: { isActive: false, orientation: 'both', keyEvent: 'main' },
zoom: { isActive: true, keyEvent: 'shift' },
},
scroll: {
pan: { isActive: false },
zoom: { isActive: true, keyEvent: 'main' }
}
},
crosshair: {
orientation: 'both',
color: 'gray'
},
labelFormat: {
yAxis: 'y',
xAxis: 'x'
},
eventsCallbacks: {
zoomOut: () => { pod.render() }
},
margin: { top: 30, right: 30, bottom: 40, left: 30 },
circleView: true,
}
);
pod.render();
</script>
</body>
</html>

15
dist/delaunay.d.ts vendored

@ -0,0 +1,15 @@
import { ScatterData } from './types';
import * as d3 from 'd3';
export declare class DelaunayDiagram {
protected series: ScatterData[];
private _delaunayData;
private _delaunayDiagram;
constructor(series: ScatterData[], xScale: d3.ScaleLinear<number, number>, yScale: (string: any) => d3.ScaleLinear<number, number>);
get data(): number[][] | undefined;
setDelaunayDiagram(xScale: d3.ScaleLinear<number, number>, yScale: (string: any) => d3.ScaleLinear<number, number>): void;
findPointIndex(eventX: number, eventY: number): number | undefined;
getDataRowByIndex(index: number): number[] | undefined;
private getDatapointsForDelaunay;
private concatSeriesDatapoints;
private getSerieIdxByTarget;
}

88
dist/index.d.ts vendored

@ -0,0 +1,88 @@
import { ChartwerkPod, TickOrientation, TimeFormat, yAxisOrientation } from '@chartwerk/core';
import { ScatterData, ScatterOptions, PointType, LineType } from './types';
import { DelaunayDiagram } from './delaunay';
import * as d3 from 'd3';
export declare class ChartwerkScatterPod extends ChartwerkPod<ScatterData, ScatterOptions> {
_metricsContainer: any;
_delaunayDiagram: DelaunayDiagram;
constructor(el: HTMLElement, _series?: ScatterData[], _options?: ScatterOptions);
renderMetrics(): void;
renderMetricContainer(): void;
protected updateCrosshair(): void;
appendCrosshairPoints(): void;
protected appendCrosshairPoint(serieIdx: number): void;
protected renderLines(): void;
renderLine(datapoints: number[][], lineType: LineType, color: string, orientation: yAxisOrientation): void;
protected renderPoints(): void;
onPanningEnd(): void;
unhighlight(): void;
highlight(pointIdx: number): void;
protected getCrosshairCircleBackgroundSize(serieIdx: number): number;
renderSharedCrosshair(values: {
x?: number;
y?: number;
}): void;
moveCrosshairLine(xPosition: number, yPosition: number): void;
findAndHighlightDatapoints(eventX: number, eventY: number): {
values: any[];
pointIdx: number;
} | null;
protected getYScale(orientation: yAxisOrientation): d3.ScaleLinear<number, number>;
hideSharedCrosshair(): void;
onMouseMove(): void;
onMouseOver(): void;
onMouseOut(): void;
}
export declare const VueChartwerkScatterPodObject: {
render(createElement: any): any;
mixins: {
props: {
id: {
type: StringConstructor;
required: boolean;
};
series: {
type: ArrayConstructor;
required: boolean;
default: () => any[];
};
options: {
type: ObjectConstructor;
required: boolean;
default: () => {};
};
};
watch: {
id(): void;
series(): void;
options(): void;
};
mounted(): void;
destroyed(): void;
methods: {
render(): void;
renderSharedCrosshair(values: {
x?: number;
y?: number;
}): void;
hideSharedCrosshair(): void;
onPanningRescale(event: any): void;
renderChart(): void;
appendEvents(): void;
zoomIn(range: any): void;
zoomOut(centers: any): void;
mouseMove(evt: any): void;
mouseOut(): void;
onLegendClick(idx: any): void;
panningEnd(range: any): void;
panning(range: any): void;
contextMenu(evt: any): void;
sharedCrosshairMove(event: any): void;
renderEnd(): void;
};
}[];
methods: {
render(): void;
};
};
export { ScatterData, ScatterOptions, TickOrientation, TimeFormat, PointType, LineType };

9
dist/index.js vendored

File diff suppressed because one or more lines are too long

26
dist/types.d.ts vendored

@ -0,0 +1,26 @@
import { TimeSerie, Options } from '@chartwerk/core';
declare type ScatterDataParams = {
pointType: PointType;
lineType: LineType;
pointSize: number;
colorFormatter?: ColorFormatter;
};
declare type ScatterOptionsParams = {
voronoiRadius: number;
circleView: boolean;
renderGrid: boolean;
};
export declare type ScatterData = TimeSerie & Partial<ScatterDataParams>;
export declare type ScatterOptions = Options & Partial<ScatterOptionsParams>;
export declare enum PointType {
NONE = "none",
CIRCLE = "circle",
RECTANGLE = "rectangle"
}
export declare enum LineType {
NONE = "none",
SOLID = "solid",
DASHED = "dashed"
}
export declare type ColorFormatter = (datapoint: number[]) => string;
export {};

4662
package-lock.json generated

File diff suppressed because it is too large Load Diff

30
package.json

@ -0,0 +1,30 @@
{
"name": "@chartwerk/scatter-pod",
"version": "0.2.4",
"description": "Chartwerk scatter pod",
"main": "dist/index.js",
"scripts": {
"build": "webpack --config build/webpack.prod.conf.js",
"dev": "webpack --config build/webpack.dev.conf.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {},
"author": "CorpGlory",
"license": "Apache-2.0",
"dependencies": {
"@chartwerk/core": "github:chartwerk/core#532eddbc8ad938091b1d9ec1693cec5eddfdbfc2"
},
"devDependencies": {
"@types/d3": "^5.7.2",
"@types/lodash": "^4.14.149",
"css-loader": "^3.4.2",
"d3": "^5.15.0",
"d3-delaunay": "^6.0.2",
"lodash": "^4.17.15",
"style-loader": "^1.1.3",
"ts-loader": "^6.2.1",
"typescript": "^3.8.3",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11"
}
}

89
src/delaunay.ts

@ -0,0 +1,89 @@
import { ScatterData, PointType } from './types';
import { Delaunay } from 'd3-delaunay';
import * as _ from 'lodash';
import * as d3 from 'd3'; // only types
export class DelaunayDiagram {
private _delaunayData: number[][]; // [ 0:y, 1:x, ..., last:serieIdx ][]
private _delaunayDiagram: any;
constructor(
protected series: ScatterData[],
xScale: d3.ScaleLinear<number, number>, yScale: (string) => d3.ScaleLinear<number, number>, // TODO: bad, but idk how to do it better
) {
this._delaunayData = this.getDatapointsForDelaunay();
this.setDelaunayDiagram(xScale, yScale);
}
public get data(): number[][] | undefined {
if(!this._delaunayData || this._delaunayData.length === 0) {
return undefined;
}
return this._delaunayData;
}
public setDelaunayDiagram(xScale: d3.ScaleLinear<number, number>, yScale: (string) => d3.ScaleLinear<number, number>) {
if(!this._delaunayData) {
console.warn('No data for delaunay initialization');
return;
}
console.time('delaunay-init');
this._delaunayDiagram = Delaunay.from(
this._delaunayData,
(d: number[]) => xScale(d[1]),
(d: number[]) => yScale(this.series[_.last(d)].yOrientation)(d[0]),
);
console.timeEnd('delaunay-init');
}
public findPointIndex(eventX: number, eventY: number): number | undefined {
if(!this._delaunayDiagram) {
return undefined;
}
let pointIndex = this._delaunayDiagram.find(eventX, eventY);
if(pointIndex === -1) {
return undefined;
}
// TODO: add search radius via https://github.com/d3/d3-delaunay/issues/45
return pointIndex;
}
public getDataRowByIndex(index: number): number[] | undefined {
if(!this.data) {
return undefined;
}
return this.data[index];
}
private getDatapointsForDelaunay(): number[][] | undefined {
// here we union all datapoints with point render type(circle or rectangle)
// it means that circles and rectangles will be highlighted(not lines)
// TODO: set Defaults (if pointType === undefined, Circle type will be used futher)
const seriesForPointType = this.series.filter((serie: ScatterData) => serie.pointType !== PointType.NONE);
if(seriesForPointType.length === 0) {
return undefined; // to avoid ts error
}
return this.concatSeriesDatapoints(seriesForPointType);
}
private concatSeriesDatapoints(series: ScatterData[]): number[][] {
// return type row: [ 0:y, 1:x, 2?:custom value, last:serieIdx ]
const datapointsList = _.map(series, serie => {
const serieIdx = this.getSerieIdxByTarget(serie.target);
const datapointsWithOptions = _.map(serie.datapoints, row => _.concat(row, serieIdx));
return datapointsWithOptions;
});
return _.union(...datapointsList);
}
private getSerieIdxByTarget(target: string): number {
const idx = _.findIndex(this.series, serie => serie.target === target);
if(idx === -1) {
throw new Error(`Can't find serie with target: ${target}`);
}
return idx;
}
}

359
src/index.ts

@ -0,0 +1,359 @@
import { ChartwerkPod, VueChartwerkPodMixin, TickOrientation, TimeFormat, yAxisOrientation, CrosshairOrientation, PanOrientation } from '@chartwerk/core';
import { ScatterData, ScatterOptions, PointType, LineType, ColorFormatter } from './types';
import { DelaunayDiagram } from './delaunay';
import * as d3 from 'd3';
import * as _ from 'lodash';
// TODO: use pod state with defaults
const DEFAULT_POINT_SIZE = 4;
const POINT_HIGHLIGHT_DIAMETER = 4;
const CROSSHAIR_BACKGROUND_OPACITY = 0.3;
const DEFAULT_POINT_TYPE = PointType.CIRCLE;
const DEFAULT_LINE_TYPE = LineType.NONE;
const DEFAULT_LINE_DASHED_AMOUNT = 4;
export class ChartwerkScatterPod extends ChartwerkPod<ScatterData, ScatterOptions> {
_metricsContainer: any;
_delaunayDiagram: DelaunayDiagram;
constructor(el: HTMLElement, _series: ScatterData[] = [], _options: ScatterOptions = {}) {
super(d3, el, _series, _options);
}
renderMetrics(): void {
if(this.series.length === 0) {
this.renderNoDataPointsMessage();
return;
}
this.updateCrosshair();
this.renderMetricContainer();
this._delaunayDiagram = new DelaunayDiagram(this.series, this.xScale, this.getYScale.bind(this));
this.renderLines();
this.renderPoints();
}
renderMetricContainer(): void {
// container for clip path
const clipContatiner = this.chartContainer
.append('g')
.attr('clip-path', `url(#${this.rectClipId})`)
.attr('class', 'metrics-container');
// container for panning
this._metricsContainer = clipContatiner
.append('g')
.attr('class', ' metrics-rect');
}
protected updateCrosshair(): void {
// TODO: Crosshair class, which can be used as Pod
this.appendCrosshairPoints();
}
appendCrosshairPoints(): void {
this.series.forEach((serie: ScatterData, serieIdx: number) => {
this.appendCrosshairPoint(serieIdx);
});
}
protected appendCrosshairPoint(serieIdx: number): void {
// TODO: add Crosshair type options
const pointType = this.series[serieIdx].pointType || DEFAULT_POINT_TYPE;
switch(pointType) {
case PointType.NONE:
return;
case PointType.CIRCLE:
this.crosshair.append('circle')
.attr('class', `crosshair-point crosshair-point-${serieIdx} crosshair-background`)
.attr('r', this.getCrosshairCircleBackgroundSize(serieIdx))
.attr('clip-path', `url(#${this.rectClipId})`)
.style('opacity', CROSSHAIR_BACKGROUND_OPACITY)
.style('pointer-events', 'none')
.style('display', 'none');
return;
case PointType.RECTANGLE:
this.crosshair.append('rect')
.attr('class', `crosshair-point crosshair-point-${serieIdx} crosshair-background`)
.attr('width', this.getCrosshairCircleBackgroundSize(serieIdx))
.attr('height', this.getCrosshairCircleBackgroundSize(serieIdx))
.attr('clip-path', `url(#${this.rectClipId})`)
.style('opacity', CROSSHAIR_BACKGROUND_OPACITY)
.style('pointer-events', 'none')
.style('display', 'none');
return;
default:
throw new Error(`Unknown render point type: ${pointType}`);
}
}
protected renderLines(): void {
this.series.forEach((serie, serieIdx) => {
if(serie.visible === false) {
return;
}
const lineType = serie.lineType || DEFAULT_LINE_TYPE;
this.renderLine(serie.datapoints, lineType, this.getSerieColor(serieIdx), serie.yOrientation);
});
}
renderLine(datapoints: number[][], lineType: LineType, color: string, orientation: yAxisOrientation): void {
if(lineType === LineType.NONE) {
return;
}
let strokeDasharray;
// TODO: move to option
if(lineType === LineType.DASHED) {
strokeDasharray = DEFAULT_LINE_DASHED_AMOUNT;
}
const lineGenerator = this.d3.line()
.x((d: [number, number]) => this.xScale(d[1]))
.y((d: [number, number]) => this.getYScale(orientation)(d[0]));
this._metricsContainer
.append('path')
.datum(datapoints)
.attr('class', 'metric-path')
.attr('fill', 'none')
.style('pointer-events', 'none')
.attr('stroke', color)
.attr('stroke-width', 1)
.attr('stroke-opacity', 0.7)
.attr('stroke-dasharray', strokeDasharray)
.attr('d', lineGenerator);
}
protected renderPoints(): void {
if(!this._delaunayDiagram.data) {
return;
}
this._metricsContainer.selectAll(null)
.data(this._delaunayDiagram.data)
.enter()
.append('circle')
.filter((d: number[]) => this.series[_.last(d)].pointType !== PointType.RECTANGLE)
.attr('class', (d, i: number) => `metric-element metric-circle point-${i}`)
.attr('r', (d: number[]) => this.series[_.last(d)].pointSize || DEFAULT_POINT_SIZE)
.style('fill', (d: number[]) => this.getSerieColor(_.last(d)))
.style('pointer-events', 'none')
.attr('cx', (d: any[]) => this.xScale(d[1]))
.attr('cy', (d: any[]) => this.getYScale(this.series[_.last(d)].yOrientation)(d[0]));
this._metricsContainer.selectAll(null)
.data(this._delaunayDiagram.data)
.enter()
.append('rect')
.filter((d: number[]) => this.series[_.last(d)].pointType === PointType.RECTANGLE)
.attr('class', (d, i: number) => `metric-element metric-circle point-${i}`)
.attr('r', (d: number[]) => this.series[_.last(d)].pointSize || DEFAULT_POINT_SIZE)
.style('fill', (d: number[]) => this.getSerieColor(_.last(d)))
.style('pointer-events', 'none')
.attr('x', (d: number[]) => this.xScale(d[1]) - (this.series[_.last(d)].pointSize || DEFAULT_POINT_SIZE) / 2)
.attr('y', (d: number[]) => this.getYScale(this.series[_.last(d)].yOrientation)(d[0]) - (this.series[_.last(d)].pointSize || DEFAULT_POINT_SIZE) / 2)
.attr('width', (d: number[]) => this.series[_.last(d)].pointSize || DEFAULT_POINT_SIZE)
.attr('height', (d: number[]) => this.series[_.last(d)].pointSize || DEFAULT_POINT_SIZE);
}
onPanningEnd(): void {
this.isPanning = false;
this.onMouseOut();
this._delaunayDiagram.setDelaunayDiagram(this.xScale, this.getYScale.bind(this));
if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.panningEnd !== undefined) {
this.options.eventsCallbacks.panningEnd([this.state.xValueRange, this.state.yValueRange, this.state.y1ValueRange]);
} else {
console.log('on panning end, but there is no callback');
}
}
unhighlight(): void {
this.crosshair.selectAll('.crosshair-point').style('display', 'none');
}
highlight(pointIdx: number): void {
this.unhighlight();
const datapoint = this._delaunayDiagram.getDataRowByIndex(pointIdx);
if(datapoint === undefined || datapoint === null) {
return;
}
const serieIdx = _.last(datapoint);
const serieOrientation = this.series[serieIdx].yOrientation;
const size = this.getCrosshairCircleBackgroundSize(serieIdx);
const colorFormatter = this.series[serieIdx].colorFormatter;
this.crosshair.selectAll(`.crosshair-point-${serieIdx}`)
.attr('cx', this.xScale(datapoint[1]))
.attr('cy', this.getYScale(serieOrientation)(datapoint[0]))
.attr('x', this.xScale(datapoint[1]) - size / 2)
.attr('y', this.getYScale(serieOrientation)(datapoint[0]) - size / 2)
.attr('fill', colorFormatter !== undefined ? colorFormatter(datapoint) : this.series[serieIdx].color)
.style('display', null);
}
protected getCrosshairCircleBackgroundSize(serieIdx: number): number {
const seriePointSize = this.series[serieIdx].pointSize || DEFAULT_POINT_SIZE;
const pointType = this.series[serieIdx].pointType || DEFAULT_POINT_TYPE;
let highlightDiameter = POINT_HIGHLIGHT_DIAMETER;
if(pointType === PointType.RECTANGLE) {
highlightDiameter = highlightDiameter * 2;
}
return seriePointSize + highlightDiameter;
}
public renderSharedCrosshair(values: { x?: number, y?: number }): void {
this.onMouseOver(); // TODO: refactor to use it once
const eventX = this.xScale(values.x);
const eventY = this.yScale(values.y);
this.moveCrosshairLine(eventX, eventY);
const datapoints = this.findAndHighlightDatapoints(values.x, values.y);
if(this.options.eventsCallbacks === undefined || this.options.eventsCallbacks.sharedCrosshairMove === undefined) {
console.log('Shared crosshair move, but there is no callback');
return;
}
this.options.eventsCallbacks.sharedCrosshairMove({
datapoints,
eventX, eventY
});
}
moveCrosshairLine(xPosition: number, yPosition: number): void {
switch (this.options.crosshair.orientation) {
case CrosshairOrientation.VERTICAL:
this.crosshair.select('#crosshair-line-x')
.attr('x1', xPosition)
.attr('x2', xPosition);
return;
case CrosshairOrientation.HORIZONTAL:
this.crosshair.select('#crosshair-line-y')
.attr('y1', yPosition)
.attr('y2', yPosition);
return;
case CrosshairOrientation.BOTH:
this.crosshair.select('#crosshair-line-x')
.attr('x1', xPosition)
.attr('x2', xPosition);
this.crosshair.select('#crosshair-line-y')
.attr('y1', yPosition)
.attr('y2', yPosition);
return;
default:
throw new Error(`Unknown type of crosshair orientaion: ${this.options.crosshair.orientation}`);
}
}
findAndHighlightDatapoints(eventX: number, eventY: number): { values: any[], pointIdx: number } | null {
if(this.series === undefined || this.series.length === 0) {
return null;
}
const pointIndex = this._delaunayDiagram.findPointIndex(eventX, eventY);
if(pointIndex === undefined) {
this.unhighlight();
return null;
}
this.highlight(pointIndex);
return {
values: this._delaunayDiagram.data[pointIndex],
pointIdx: pointIndex,
};
}
protected getYScale(orientation: yAxisOrientation): d3.ScaleLinear<number, number> {
if(orientation === undefined || orientation === yAxisOrientation.BOTH) {
return this.yScale;
}
switch(orientation) {
case yAxisOrientation.LEFT:
return this.yScale;
case yAxisOrientation.RIGHT:
return this.y1Scale;
default:
throw new Error(`Unknown type of y axis orientation: ${orientation}`)
}
}
public hideSharedCrosshair(): void {
this.crosshair.style('display', 'none');
}
onMouseMove(): void {
const mousePosition = this.d3.mouse(this.chartContainer.node());
const eventX = mousePosition[0];
const eventY = mousePosition[1];
// TODO: seems isOutOfChart is deprecated (check clippath correctness)
if(this.isOutOfChart() === true || this.isPanning === true || this.isBrushing === true) {
this.crosshair.style('display', 'none');
return;
} else {
this.crosshair.style('display', null);
}
this.moveCrosshairLine(eventX, eventY);
// TOOD: it should be two different methods
const highlighted = this.findAndHighlightDatapoints(eventX, eventY);
if(this.options.eventsCallbacks === undefined || this.options.eventsCallbacks.mouseMove === undefined) {
console.log('Mouse move, but there is no callback');
return;
}
// TODO: group fields
this.options.eventsCallbacks.mouseMove({
x: this.d3.event.clientX,
y: this.d3.event.clientY,
xval: this.xScale.invert(eventX),
yval: this.xScale.invert(eventY),
highlighted,
chartX: eventX,
chartWidth: this.width
});
}
onMouseOver(): void {
if(this.isOutOfChart() === true || this.isPanning === true || this.isBrushing === true) {
this.crosshair.style('display', 'none');
return;
}
this.crosshair.style('display', null);
}
onMouseOut(): void {
if(this.options.eventsCallbacks !== undefined && this.options.eventsCallbacks.mouseOut !== undefined) {
this.options.eventsCallbacks.mouseOut();
}
this.crosshair.style('display', 'none');
}
}
// it is used with Vue.component, e.g.: Vue.component('chartwerk-scatter-pod', VueChartwerkScatterPodObject)
export const VueChartwerkScatterPodObject = {
// alternative to `template: '<div class="chartwerk-scatter-pod" :id="id" />'`
render(createElement) {
return createElement(
'div',
{
class: { 'chartwerk-scatter-pod': true },
attrs: { id: this.id }
}
);
},
mixins: [VueChartwerkPodMixin],
methods: {
render() {
if(this.pod === undefined) {
this.pod = new ChartwerkScatterPod(document.getElementById(this.id), this.series, this.options);
this.pod.render();
} else {
this.pod.updateData(this.series, this.options);
}
},
}
};
export { ScatterData, ScatterOptions, TickOrientation, TimeFormat, PointType, LineType };

30
src/types.ts

@ -0,0 +1,30 @@
import { TimeSerie, Options } from '@chartwerk/core';
type ScatterDataParams = {
pointType: PointType;
lineType: LineType;
pointSize: number;
colorFormatter?: ColorFormatter
}
type ScatterOptionsParams = {
voronoiRadius: number;
circleView: boolean;
renderGrid: boolean;
}
export type ScatterData = TimeSerie & Partial<ScatterDataParams>;
export type ScatterOptions = Options & Partial<ScatterOptionsParams>;
export enum PointType {
NONE = 'none',
CIRCLE = 'circle',
RECTANGLE = 'rectangle'
}
export enum LineType {
NONE = 'none',
SOLID = 'solid',
DASHED = 'dashed'
}
export type ColorFormatter = (datapoint: number[]) => string;

22
tsconfig.json

@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "es5",
"rootDir": "./src",
"module": "esnext",
"moduleResolution": "node",
"declaration": true,
"declarationDir": "dist",
"allowSyntheticDefaultImports": true,
"inlineSourceMap": false,
"sourceMap": true,
"noEmitOnError": false,
"emitDecoratorMetadata": false,
"experimentalDecorators": true,
"noImplicitReturns": true,
"noImplicitThis": false,
"noImplicitUseStrict": false,
"noImplicitAny": false,
"noUnusedLocals": false,
"baseUrl": "./src"
}
}
Loading…
Cancel
Save