Compare commits

...

56 Commits

Author SHA1 Message Date
rozetko d89b80a180 Merge pull request 'typ for mousemove' (#66) from types-for-mousemove into main 2 weeks ago
rozetko bebc1bbb86 Merge branch 'main' of code.corpglory.net:chartwerk/line-pod into types-for-mousemove 2 weeks ago
vargburz b3e7f943ad 0.7.8 && render segments if exists 3 months ago
vargburz a8408a748c 0.7.7 && handle context menu 4 months ago
vargburz 6e6fa7dc8e 0.7.6 && update vue with markers and conf 4 months ago
rozetko f67ce313dd 0.7.5 4 months ago
rozetko c1c3b77dac Merge pull request 'area generator: support `invert` option for y/y1 axes' (#70) from area-generator-fix-invert into main 4 months ago
rozetko 53c233b969 area generator: support `invert` option for y/y1 axes 4 months ago
vargburz ebcb3185cd 0.7.4 version 4 months ago
vargburz 6422b49bf0 Merge pull request 'update area render type' (#69) from update-area-type-render into main 4 months ago
vargburz 3f9342b5e1 remove log 4 months ago
vargburz bb46fe79be update area render type 4 months ago
rozetko 0c3b404155 0.7.3 5 months ago
rozetko 85cd952b9f hotfix 5 months ago
rozetko 701feaaf2f 0.7.2 5 months ago
rozetko ebb49adf82 markers: render tooltip in center 5 months ago
rozetko f38829787c 0.7.1 5 months ago
rozetko 6549415feb markers hotfix 5 months ago
rozetko 117c0af469 Merge pull request 'better tooltips for markers' (#68) from better-markers-tooltips into main 5 months ago
rozetko aac8f6f5ac bump version to `0.7.0` 5 months ago
rozetko f4354d7a77 better tooltips for markers 5 months ago
rozetko 529f439ac9 0.6.21 5 months ago
rozetko 121f7a0305 fix renderDots not working for right y-axis 5 months ago
rozetko 7e721ab880 0.6.20 5 months ago
rozetko 3663d3eaaa upd chartwerk core to 0.6.25 5 months ago
glitch4347 85ab6ad0aa typ for mousemove 7 months ago
Coin de Gamma bd598d6a0b Merge pull request '0.6.19' (#64) from 0.6.19 into main 7 months ago
glitch4347 4620ef7aa9 0.6.19 7 months ago
rozetko 44a4e6ce74 Merge pull request 'segment select' (#63) from segment-select into main 7 months ago
glitch4347 04a01b75a1 opacity param 7 months ago
glitch4347 bf3aab2db1 segment select 7 months ago
Coin de Gamma 348a289f1a Merge pull request '0.6.18' (#61) from 0.6.18 into main 9 months ago
glitch4347 17a2fdf94e 0.6.18 9 months ago
Coin de Gamma dc89f6cbb9 Merge pull request 'basic react component implementatino is sep project' (#59) from better-react-component-#58 into main 9 months ago
glitch4347 547c15c0b6 optimise excludes in libs in webpack 9 months ago
glitch4347 32d122ba4d back dev build 9 months ago
glitch4347 9c165f4e1f basic react component implementatino is sep project 9 months ago
Coin de Gamma ec2bd27acd Merge pull request '0.6.17' (#54) from 0.6.17 into main 9 months ago
glitch4347 815144d209 update core 0.6.23 9 months ago
glitch4347 1a80ca2cab 0.6.17 9 months ago
Coin de Gamma ee08db2f54 Merge pull request 'use events instead of eventsCallbacks' (#53) from core-0.6.23 into main 9 months ago
glitch4347 884fed6582 use events instead of eventsCallbacks 9 months ago
glitch4347 8aca72ce04 zoom out with ranges logic 9 months ago
Coin de Gamma c62a1c386f Merge pull request 'rename demo->basic, live and vertical && update readme' (#47) from rename-examples into main 9 months ago
rozetko 7bcd0d298a 0.6.16 11 months ago
rozetko ee2f02be96 upd core 11 months ago
rozetko 456b888fe1 0.6.15 11 months ago
rozetko a34c0574e4 hotfix 11 months ago
rozetko 73dbf0ea67 0.6.14 11 months ago
rozetko ff5973b106 Merge pull request 'updateData: update segments and markers' (#44) from update-segments-and-markers-on-update-data into main 11 months ago
rozetko 878e86d849 updateData: update segments and markers 11 months ago
vargburz 1e8f8d0cb3 0.6.13 11 months ago
rozetko 39412e5f65 Merge pull request 'roken shared crosshair #42' (#43) from broken-shared-crosshair-#42 into main 11 months ago
rozetko 37da147343 Merge pull request '0.6.19 core usdage' (#41) from build-fail-on-core-0.6.18-udpate-#40 into main 11 months ago
glitch4347 0dac1c390e fix 11 months ago
glitch4347 07f454fe51 0.6.19 core usdage 11 months ago
  1. 2
      .gitignore
  2. 3
      build/webpack.base.conf.js
  3. 42
      examples/area.html
  4. 19
      examples/markers.html
  5. 12
      examples/markers_select.html
  6. 33
      examples/right_click.html
  7. 9
      examples/segments.html
  8. 46
      examples/segments_select.html
  9. 37
      examples/zoom_out.html
  10. 11
      package.json
  11. 26
      react/build/webpack.base.conf.js
  12. 8
      react/build/webpack.dev.conf.js
  13. 6
      react/build/webpack.prod.conf.js
  14. 23
      react/package.json
  15. 9
      react/src/index.tsx
  16. 23
      react/tsconfig.json
  17. 1322
      react/yarn.lock
  18. 144
      src/components/markers.ts
  19. 48
      src/components/segments.ts
  20. 214
      src/index.ts
  21. 4
      src/models/line_series.ts
  22. 12
      src/models/marker.ts
  23. 9
      src/models/segment.ts
  24. 29
      src/types.ts
  25. 3
      tsconfig.json
  26. 10
      yarn.lock

2
.gitignore vendored

@ -2,7 +2,7 @@ node_modules
dist dist
# yarn # yarn
.yarn/* .yarn
!.yarn/patches !.yarn/patches
!.yarn/plugins !.yarn/plugins
!.yarn/releases !.yarn/releases

3
build/webpack.base.conf.js

@ -12,7 +12,8 @@ module.exports = {
plugins: [ plugins: [
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
{ from: "../react", to: "react" }, { from: "../react/dist/index.js", to: "react/index.js" },
{ from: "../react/dist/index.d.ts", to: "react/index.d.ts" },
], ],
}) })
], ],

42
examples/area.html

@ -0,0 +1,42 @@
<!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.dev.js" type="text/javascript"></script>
</head>
<body>
<div id="chart" style="width: 50%; height: 500px;"></div>
<script type="text/javascript">
let options = {
renderLegend: false, usePanning: false,
axis: {
x: { format: 'numeric', range: [0, 100] },
y: { invert: true, range: [0, 100] },
y1: { isActive: true, format: 'numeric', range: [0, 1000] },
},
zoomEvents: {
mouse: { zoom: { isActive: false, orientation: 'horizontal' } },
scroll: { zoom: { isActive: false, orientation: 'horizontal' } }
},
}
const data1 = [[0,0], [35, 40], [65, 60], [100, 100]];
const data2 = [[0,0], [35, 50], [65, 65], [80, 100]];
const data3 = [[0,0], [35, 20], [65, 50], [100, 80]];
const data4 = [[0,900], [35, 800], [65, 700], [100, 600]];
var pod = new LinePod(
document.getElementById('chart'),
[
{ target: 'test1', datapoints: data1, color: 'green', renderArea: 'Above' },
{ target: 'test2', datapoints: data2, color: 'blue', renderArea: 'Below' },
{ target: 'test3', datapoints: data3, color: 'orange', renderArea: 'Below', yOrientation: 'right' },
{ target: 'test4', datapoints: data4, color: 'purple', renderArea: 'Above', yOrientation: 'right' },
],
options
);
pod.render();
</script>
</body>
</html>

19
examples/markers.html

@ -13,9 +13,18 @@
const startTime = 1701790172908; const startTime = 1701790172908;
const timeSerieData = [5, 6, 3, 7, 5, 6, 8, 4, 5, 6, 4, 3, 5, 7, 8] const timeSerieData = [5, 6, 3, 7, 5, 6, 8, 4, 5, 6, 4, 3, 5, 7, 8]
.map((el, idx) => [startTime + idx * 1000, el]); .map((el, idx) => [startTime + idx * 1000, el]);
// TODO: make this one-dimensinal data when implemented const markersData1 = [3, 6, 9].map(el => ({
const markersData1 = [3, 6, 9].map(el => [startTime + el * 1000]); x: startTime + el * 1000,
const markersData2 = [4, 11].map(el => [startTime + el * 1000]); color: 'red',
alwaysDisplay: false,
html: new Date(startTime).toISOString(),
}));
const markersData2 = [4, 11].map(el => ({
x: startTime + el * 1000,
color: 'blue',
alwaysDisplay: true,
html: new Date(startTime).toISOString(),
}));
let options = { let options = {
renderLegend: false, renderLegend: false,
axis: { axis: {
@ -31,8 +40,8 @@
options, options,
{ {
series: [ series: [
{ data: markersData1, color: 'red' }, { data: markersData1 },
{ data: markersData2, color: 'blue' }, { data: markersData2 },
] ]
} }
); );

12
examples/markers_select.html

@ -13,11 +13,11 @@
const startTime = 1701790172908; const startTime = 1701790172908;
const timeSerieData = [5, 6, 3, 7, 5, 6, 8, 4, 5, 6, 4, 3, 5, 7, 8] const timeSerieData = [5, 6, 3, 7, 5, 6, 8, 4, 5, 6, 4, 3, 5, 7, 8]
.map((el, idx) => [startTime + idx * 1000, el]); .map((el, idx) => [startTime + idx * 1000, el]);
// TODO: make this one-dimensinal data when implemented const markersData = [3, 6, 9].map(el => ({
const markersData = [3, 6, 9].map(el => [ x: startTime + el * 1000,
startTime + el * 1000, payload: el,
{ el } color: 'red',
]); }));
let options = { let options = {
renderLegend: false, renderLegend: false,
axis: { axis: {
@ -33,7 +33,7 @@
options, options,
{ {
series: [ series: [
{ data: markersData, color: 'red' }, { data: markersData },
], ],
events: { events: {
onMouseMove: (el) => { console.log(el); }, onMouseMove: (el) => { console.log(el); },

33
examples/right_click.html

@ -0,0 +1,33 @@
<!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.dev.js" type="text/javascript"></script>
</head>
<body>
<div id="chart" style="width: 50%; height: 500px;"></div>
<script type="text/javascript">
const startTime = 1590590148;
const data = Array.from(
{ length: 20 },
(el, idx) => [startTime + idx * 10000, Math.floor(Math.random() * 30)]
);
let options = {
renderLegend: false, usePanning: false,
axis: { y: { range: [0, 50] } },
zoomEvents: { mouse: {
zoom: { isActive: false },
pan: { isActive: false },
} },
eventsCallbacks: { contextMenu: (position) => console.log('contextMenu', position) }
}
var pod = new LinePod(
document.getElementById('chart'),
[{ datapoints: data }],
options
);
pod.render();
</script>
</body>
</html>

9
examples/segments.html

@ -13,7 +13,7 @@
const startTime = 1701790172908; const startTime = 1701790172908;
const timeSerieData = [5, 6, 3, 7, 5, 6, 8, 4, 5, 6, 4, 3, 5, 7, 8] const timeSerieData = [5, 6, 3, 7, 5, 6, 8, 4, 5, 6, 4, 3, 5, 7, 8]
.map((el, idx) => [startTime + idx * 1000, el]); .map((el, idx) => [startTime + idx * 1000, el]);
const segmentsData = [3, 6, 9].map(el => [startTime + el * 1000, startTime + (el + 1) * 1000]); const segmentsData = [3, 6, 9].map(el => [startTime + el * 1000, startTime + (el + 1) * 1100]);
let options = { let options = {
renderLegend: false, renderLegend: false,
axis: { axis: {
@ -27,9 +27,12 @@
{ datapoints: timeSerieData, color: 'black' }, { datapoints: timeSerieData, color: 'black' },
], ],
options, options,
[], undefined,
[ [
{ data: segmentsData, color:'#FFE545' } {
data: segmentsData,
color:'#FFE545'
}
] ]
); );
pod.render(); pod.render();

46
examples/segments_select.html

@ -0,0 +1,46 @@
<!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.dev.js" type="text/javascript"></script>
</head>
<body>
<div id="chart" style="width: 100%; height: 500px;"></div>
<script type="text/javascript">
const startTime = 1701790172908;
const timeSerieData = [5, 6, 3, 7, 5, 6, 8, 4, 5, 6, 4, 3, 5, 7, 8]
.map((el, idx) => [startTime + idx * 1000, el]);
const segmentsData = [3, 6, 9].map(el => [startTime + el * 1000, startTime + (el + 1) * 1100]);
let options = {
renderLegend: false,
axis: {
y: { range: [0, 10] },
x: { format: 'time' }
},
}
var pod = new LinePod(
document.getElementById('chart'),
[
{ datapoints: timeSerieData, color: 'black' },
],
options,
undefined,
[
{
data: segmentsData,
color:'#FFE545',
select: true,
opacity: 0.4,
opacitySelect: 0.8,
onSelect: console.log,
onUnselect: console.log
}
]
);
pod.render();
</script>
</body>
</html>

37
examples/zoom_out.html

@ -0,0 +1,37 @@
<!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.dev.js" type="text/javascript"></script>
</head>
<body>
<div id="chart" style="width: 50%; height: 500px;"></div>
<script type="text/javascript">
const startTime = 1590590148;
const data = Array.from(
{ length: 20 },
(el, idx) => [startTime + idx * 10000, Math.floor(Math.random() * 30)]
);
let options = {
renderLegend: false, usePanning: false,
axis: { y: { range: [0, 50] } },
zoomEvents: { mouse: {
zoom: { isActive: true, orientation: "horizontal" },
pan: { isActive: false },
}},
events: {
zoomOut: function(centers, ranges) {
console.log('zoomOut', centers, ranges);
}
}
}
var pod = new LinePod(
document.getElementById('chart'),
[{ datapoints: data }],
options
);
pod.render();
</script>
</body>
</html>

11
package.json

@ -1,13 +1,13 @@
{ {
"name": "@chartwerk/line-pod", "name": "@chartwerk/line-pod",
"version": "0.6.12", "version": "0.7.8",
"description": "Chartwerk line chart", "description": "Chartwerk line chart",
"main": "dist/index.js", "main": "dist/index.js",
"files": [ "files": [
"/dist" "/dist"
], ],
"scripts": { "scripts": {
"build": "webpack --config build/webpack.prod.conf.js && webpack --config build/webpack.dev.conf.js", "build": "rm -rf dist && cd react && yarn build && cd .. && 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" "update-core": "yarn up @chartwerk/core && yarn up @chartwerk/core@latest"
@ -19,7 +19,7 @@
"author": "CorpGlory", "author": "CorpGlory",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@chartwerk/core": "latest" "@chartwerk/core": "^0.6.26"
}, },
"devDependencies": { "devDependencies": {
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^11.0.0",
@ -30,5 +30,8 @@
"webpack": "^5.87.0", "webpack": "^5.87.0",
"webpack-cli": "^5.1.4" "webpack-cli": "^5.1.4"
}, },
"packageManager": "yarn@3.2.1" "packageManager": "yarn@3.2.1",
"workspaces": [
"react/*"
]
} }

26
react/build/webpack.base.conf.js

@ -0,0 +1,26 @@
const path = require('path');
module.exports = {
entry: './src/index.tsx',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: 'index.js',
path: path.resolve(__dirname, '../dist'),
libraryTarget: 'umd',
umdNamedDefine: true,
},
externals: [
'@chartwerk/line-pod', 'react'
]
};

8
react/build/webpack.dev.conf.js

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

6
react/build/webpack.prod.conf.js

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

23
react/package.json

@ -0,0 +1,23 @@
{
"name": "line-pod-react",
"version": "0.0.1",
"description": "React wrapper around line-pod",
"main": "dist/index.js",
"repository": "http://code.corpglory.net/chartwerk/line-pod.git",
"author": "CorpGlory Inc.",
"license": "ISC",
"scripts": {
"build": "webpack --config build/webpack.prod.conf.js",
"dev": "webpack --config build/webpack.dev.conf.js"
},
"dependencies": {
"@chartwerk/line-pod": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"ts-loader": "^9.5.1",
"typescript": "^5.4.3",
"webpack": "^5.87.0"
}
}

9
react/ChartwerkLinePod.tsx → react/src/index.tsx

@ -2,7 +2,7 @@ import { LineTimeSerie, LineOptions, LinePod } from '@chartwerk/line-pod';
import { MarkersConf } from '@chartwerk/line-pod/dist/models/marker'; import { MarkersConf } from '@chartwerk/line-pod/dist/models/marker';
import { SegmentSerie } from '@chartwerk/line-pod/dist/models/segment'; import { SegmentSerie } from '@chartwerk/line-pod/dist/models/segment';
import { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import _ from 'lodash'; import _ from 'lodash';
@ -47,6 +47,12 @@ export function ChartwerkLinePod(props: ChartwerkLinePodProps) {
setPod(newPod); setPod(newPod);
newPod.render(); newPod.render();
} else { } else {
if(props.markersConf) {
pod.updateMarkers(props.markersConf);
}
if(props.segments) {
pod.updateSegments(props.segments);
}
// TODO: actually it's wrong logic with updates // TODO: actually it's wrong logic with updates
// because it creates new pod anyway // because it creates new pod anyway
pod.updateData(props.series, props.options); pod.updateData(props.series, props.options);
@ -60,7 +66,6 @@ export function ChartwerkLinePod(props: ChartwerkLinePodProps) {
} }
}, 1); }, 1);
return ( return (
<div id={props.id} className={props.className} ref={chartRef}></div> <div id={props.id} className={props.className} ref={chartRef}></div>
); );

23
react/tsconfig.json

@ -0,0 +1,23 @@
{
"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",
"jsx": "react"
}
}

1322
react/yarn.lock

File diff suppressed because it is too large Load Diff

144
src/components/markers.ts

@ -1,33 +1,92 @@
import { MarkersConf, MarkerSerie } from "../models/marker"; import { MarkerElem, MarkersConf, MarkerSerie } from '../models/marker';
import { PodState } from "@chartwerk/core"; import { LineTimeSerie, LineOptions } from '../types';
import { LineTimeSerie, LineOptions } from "../types";
import d3 from "d3"; import { Margin, PodState } from '@chartwerk/core';
import d3 from 'd3';
export class Markers { export class Markers {
// TODO: more semantic name private _layerContainer = null;
private _d3Holder = null; private _chartHeight = 0;
constructor(private _markerConf: MarkersConf, private _state: PodState<LineTimeSerie, LineOptions>) { constructor(
private _chartContainer: d3.Selection<HTMLElement, unknown, null, undefined>,
private _markerConf: MarkersConf,
private _state: PodState<LineTimeSerie, LineOptions>,
private _margin: Margin,
) { }
clear() {
if(this._layerContainer !== null) {
this._layerContainer.remove();
}
this._chartContainer.selectAll('.marker-content').remove();
} }
render(metricContainer: d3.Selection<SVGGElement, unknown, null, undefined>) { render(metricContainer: d3.Selection<SVGGElement, unknown, null, undefined>, chartHeight: number) {
if(this._d3Holder !== null) { this._chartHeight = chartHeight;
this._d3Holder.remove(); this._layerContainer = metricContainer
.append('g')
.attr('class', 'markers-layer');
for(const serie of this._markerConf.series) {
this.renderSerie(serie);
} }
this._d3Holder = metricContainer.append('g').attr('class', 'markers-layer');
for (const ms of this._markerConf.series) {
this.renderSerie(ms);
} }
private _getLinePosition(marker: MarkerElem): number {
return this._state.xScale(marker.x);
} }
protected renderSerie(serie: MarkerSerie) { private _renderCircle(marker: MarkerElem) {
serie.data.forEach((d) => { const linePosition = this._getLinePosition(marker);
let linePosition = this._state.xScale(d[0]) as number;
this._d3Holder.append('line') let circle = this._layerContainer.append('circle')
.attr('class', 'gap-circle')
.attr('stroke', marker.color)
.attr('stroke-width', '2px')
.attr('r', 4)
.attr('cx', linePosition)
.attr('cy', 5)
circle
.attr('pointer-events', 'all')
.style('cursor', 'pointer')
.on('mousemove', () => {
const onMouseMove = this._markerConf.events?.onMouseMove;
if(onMouseMove) {
onMouseMove(marker);
return
}
if(marker.alwaysDisplay) {
return;
}
this._chartContainer
.selectAll(`.marker-content-${marker.x}`)
.style('visibility', 'visible')
.style('z-index', 9999);
})
.on('mouseout', () => {
const onMouseOut = this._markerConf.events?.onMouseOut;
if(onMouseOut) {
onMouseOut()
return
}
if(marker.alwaysDisplay) {
return;
}
this._chartContainer
.selectAll(`.marker-content-${marker.x}`)
.style('visibility', 'hidden')
.style('z-index', 1);
});
}
private _renderLine(marker: MarkerElem) {
const linePosition = this._getLinePosition(marker);
this._layerContainer.append('line')
.attr('class', 'gap-line') .attr('class', 'gap-line')
.attr('stroke', serie.color) .attr('stroke', marker.color)
.attr('stroke-width', '1px') .attr('stroke-width', '1px')
.attr('stroke-opacity', '0.3') .attr('stroke-opacity', '0.3')
.attr('stroke-dasharray', '4') .attr('stroke-dasharray', '4')
@ -37,23 +96,44 @@ export class Markers {
// @ts-ignore // TODO: remove ignore but boxParams are protected // @ts-ignore // TODO: remove ignore but boxParams are protected
.attr('y2', this._state.boxParams.height) .attr('y2', this._state.boxParams.height)
.attr('pointer-events', 'none'); .attr('pointer-events', 'none');
let circle = this._d3Holder.append('circle') }
.attr('class', 'gap-circle')
.attr('stroke', serie.color)
.attr('stroke-width', '2px')
.attr('r', 4)
.attr('cx', linePosition)
.attr('cy', 5)
if(this._markerConf !== undefined) { private _renderTooltip(marker: MarkerElem) {
circle if(marker.html === undefined) {
.attr('pointer-events', 'all') return;
.style('cursor', 'pointer')
.on('mousemove', () => this._markerConf.events.onMouseMove(d))
.on('mouseout', () => this._markerConf.events.onMouseOut())
} }
}); const linePosition = this._getLinePosition(marker);
const div = this._chartContainer
.append('div')
.attr('class', `marker-content marker-content-${marker.x}`)
// @ts-ignore // TODO: remove ignore but boxParams are protected
.style('top', `${this._state.boxParams.height - this._chartHeight}px`)
.style('visibility', marker.alwaysDisplay ? 'visible' : 'hidden')
.style('position', 'absolute')
.style('border', '1px solid black')
.style('background-color', 'rgb(33, 37, 41)')
.style('color', 'rgb(255, 255, 255)')
.style('line-height', '1.55')
.style('font-size', '0.875rem')
.style('border-radius', '0.5rem')
.style('padding', 'calc(0.3125rem) 0.625rem')
.style('position', 'absolute')
.style('white-space', 'nowrap')
.style('pointer-events', 'none')
.style('z-index', 1)
.html(marker.html);
// align tooltip: center (we need it to be rendered first)
div.style('left', `${linePosition + this._margin.left - div.node().getBoundingClientRect().width / 2}px`)
}
protected renderSerie(serie: MarkerSerie) {
serie.data.forEach((marker: MarkerElem) => {
this._renderLine(marker);
this._renderCircle(marker);
this._renderTooltip(marker);
});
} }
} }

48
src/components/segments.ts

@ -1,34 +1,44 @@
import { SegmentSerie } from "../models/segment"; import { SegmentSerie, SegmentElement } from "../models/segment";
import { PodState } from "@chartwerk/core"; import { PodState } from "@chartwerk/core";
import { LineTimeSerie, LineOptions } from "../types"; import { LineTimeSerie, LineOptions } from "../types";
import d3 from "d3"; import * as d3 from "d3";
const OPACITY = 0.3;
const OPACITY_SELECT = 0.3;
export class Segments { export class Segments {
// TODO: more semantic name // TODO: more semantic name
private _d3Holder = null; private _d3Holder = null;
private _metricCon = null;
constructor(private _series: SegmentSerie[], private _state: PodState<LineTimeSerie, LineOptions>) { constructor(
private _series: SegmentSerie[],
private _state: PodState<LineTimeSerie, LineOptions>
) {
} }
render(metricContainer: d3.Selection<SVGGElement, unknown, null, undefined>) { render(metricContainer: d3.Selection<SVGGElement, unknown, null, undefined>, chartContainer: d3.Selection<SVGGElement, unknown, null, undefined>) {
if(this._d3Holder !== null) { if(this._d3Holder !== null) {
this._d3Holder.remove(); this._d3Holder.remove();
} }
this._d3Holder = metricContainer.append('g').attr('class', 'markers-layer'); this._d3Holder = metricContainer.append('g').attr('class', 'markers-layer');
for (const s of this._series) { for (const s of this._series) {
this.renderSerie(s); this.renderSerie(chartContainer, s);
} }
} }
protected renderSerie(serie: SegmentSerie) { protected renderSerie(chartContainer: d3.Selection<SVGGElement, unknown, null, undefined>, serie: SegmentSerie) {
// TODO: it's hack with core, need to find a better way
const overlay = chartContainer.select('.overlay');
serie.data.forEach((d) => { serie.data.forEach((d) => {
// @ts-ignore // @ts-ignore
const startPositionX = this._state.xScale(d[0]) as number; const startPositionX = this._state.xScale(d[0]) as number;
// @ts-ignore // @ts-ignore
const endPositionX = this._state.xScale(d[1]) as number; const endPositionX = this._state.xScale(d[1]) as number;
const width = endPositionX - startPositionX // Math.max(endPositionX - startPositionX, MIMIMUM_SEGMENT_WIDTH); const width = endPositionX - startPositionX // Math.max(endPositionX - startPositionX, MIMIMUM_SEGMENT_WIDTH);
const opacity = serie.opacity || OPACITY;
const opacitySelect = serie.opacitySelect || OPACITY_SELECT;
this._d3Holder.append('rect') this._d3Holder.append('rect')
.attr('class', 'segment') .attr('class', 'segment')
.attr('x', startPositionX) .attr('x', startPositionX)
@ -36,10 +46,28 @@ export class Segments {
.attr('width', width) .attr('width', width)
// @ts-ignore // TODO: remove ignore but boxParams are protected // @ts-ignore // TODO: remove ignore but boxParams are protected
.attr('height', this._state.boxParams.height) .attr('height', this._state.boxParams.height)
.attr('opacity', 0.3) .attr('opacity', opacity)
.style('fill', serie.color) .style('fill', serie.color)
.style('pointer-events', 'none'); .on('mouseover', function() {
if(serie.select === true) {
d3.select(this).attr('opacity', opacitySelect);
if(serie.onSelect) {
serie.onSelect(d);
}
}
})
.on('mouseout', function(e) {
if(serie.select === true) {
d3.select(this).attr('opacity', opacity);
if(serie.onUnselect) {
serie.onUnselect(d);
}
}
})
.on('mousemove', function(e) {
var event = new MouseEvent('mousemove', d3.event);
overlay.node().dispatchEvent(event)
})
}); });
} }
} }

214
src/index.ts

@ -1,10 +1,11 @@
import { ChartwerkPod, VueChartwerkPodMixin, TimeFormat, CrosshairOrientation, BrushOrientation, yAxisOrientation } from '@chartwerk/core'; import { ChartwerkPod, VueChartwerkPodMixin, TimeFormat, CrosshairOrientation, BrushOrientation, yAxisOrientation } from '@chartwerk/core';
import { LineTimeSerie, LineOptions, MouseObj } from './types'; import { LineTimeSerie, LineOptions, MouseObj, AreaType } from './types';
import { Markers } from './components/markers'; import { Markers } from './components/markers';
import { Segments } from './components/segments'; import { Segments } from './components/segments';
import { LineSeries } from './models/line_series'; import { LineSeries } from './models/line_series';
import { MarkersConf } from './models/marker'; import { MarkersConf } from './models/marker';
import { SegmentSerie } from './models/segment';
import * as d3 from 'd3'; import * as d3 from 'd3';
import * as _ from 'lodash'; import * as _ from 'lodash';
@ -13,14 +14,9 @@ const METRIC_CIRCLE_RADIUS = 1.5;
const CROSSHAIR_CIRCLE_RADIUS = 3; const CROSSHAIR_CIRCLE_RADIUS = 3;
const CROSSHAIR_BACKGROUND_RAIDUS = 9; const CROSSHAIR_BACKGROUND_RAIDUS = 9;
const CROSSHAIR_BACKGROUND_OPACITY = 0.3; const CROSSHAIR_BACKGROUND_OPACITY = 0.3;
type Generator = d3.Line<[number, number]> | d3.Area<[number, number]>;
class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> { class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
lineGenerator = null;
areaGenerator = null;
lineGeneratorY1 = null;
areaGeneratorY1 = null;
private _markersLayer: Markers = null; private _markersLayer: Markers = null;
private _segmentsLayer: Segments = null; private _segmentsLayer: Segments = null;
@ -29,7 +25,7 @@ class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
_series: LineTimeSerie[] = [], _series: LineTimeSerie[] = [],
_options: LineOptions = {}, _options: LineOptions = {},
private _markersConf?: MarkersConf, private _markersConf?: MarkersConf,
private _segmentSeries = [], private _segmentSeries: SegmentSerie[] = [],
) { ) {
super(_el, _series, _options); super(_el, _series, _options);
this.series = new LineSeries(_series); this.series = new LineSeries(_series);
@ -39,55 +35,70 @@ class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
this.clearAllMetrics(); this.clearAllMetrics();
this.updateCrosshair(); this.updateCrosshair();
this.initLineGenerator(); this.updateEvents();
this.initAreaGenerator();
if(!this.series.isSeriesAvailable) { if(!this.series.isSeriesAvailable) {
this.renderNoDataPointsMessage(); this.renderNoDataPointsMessage();
return; return;
} }
for(const serie of this.series.visibleSeries) { for(const serie of this.series.visibleSeries) {
this._renderMetric(serie); const generator = this.getRenderGenerator(serie.renderArea, serie.yOrientation);
this._renderMetric(serie, generator);
} }
if(this._markersConf !== undefined) { if(!_.isEmpty(this._markersConf)) {
this._markersLayer = new Markers(this._markersConf, this.state); this._markersLayer = new Markers(this.d3Node, this._markersConf, this.state, this.margin);
this._markersLayer.render(this.metricContainer); this._markersLayer.render(this.metricContainer, this.height);
} }
if(!_.isEmpty(this._segmentSeries)) {
this._segmentsLayer = new Segments(this._segmentSeries, this.state); this._segmentsLayer = new Segments(this._segmentSeries, this.state);
this._segmentsLayer.render(this.metricContainer); this._segmentsLayer.render(this.metricContainer, this.chartContainer);
}
} }
clearAllMetrics(): void { clearAllMetrics(): void {
// TODO: temporary hack before it will be implemented in core. // TODO: temporary hack before it will be implemented in core.
this.chartContainer.selectAll('.metric-el').remove(); this.chartContainer.selectAll('.metric-el').remove();
this._markersLayer?.clear();
} }
initLineGenerator(): void { protected updateEvents(): void {
this.lineGenerator = d3.line() // overlay - core component that is used to handle mouse events
.x(d => this.state.xScale(d[0])) if(!this.overlay) {
.y(d => this.state.yScale(d[1])); return;
this.lineGeneratorY1 = d3.line() }
.x(d => this.state.xScale(d[0])) if(this.options._options.events?.contextMenu === undefined) {
.y(d => this.state.y1Scale(d[1])); return;
}
this.overlay.on('contextmenu', this.onContextMenu.bind(this));
} }
initAreaGenerator(): void { getRenderGenerator(renderArea: AreaType, yOrientation: yAxisOrientation): Generator {
this.areaGenerator = d3.area() const yScale = yOrientation === yAxisOrientation.LEFT ? this.state.yScale : this.state.y1Scale;
const yValueRange = yOrientation === yAxisOrientation.LEFT ? this.state.yValueRange : this.state.y1ValueRange;
const yAxisOptions = yOrientation === yAxisOrientation.LEFT ? this.options.axis.y : this.options.axis.y1;
const topChartBorder = !yAxisOptions.invert ? yScale(yValueRange[1]) : yScale(yValueRange[0]);
const bottomChartBorder = !yAxisOptions.invert ? yScale(yValueRange[0]) : yScale(yValueRange[1]);
switch(renderArea) {
case AreaType.NONE:
// return line generator
return d3.line()
.x(d => this.state.xScale(d[0])) .x(d => this.state.xScale(d[0]))
.y1(d => this.state.yScale(d[1])) .y(d => yScale(d[1]));
.y0(d => this.height); case AreaType.ABOVE:
this.areaGeneratorY1 = d3.area() return d3.area()
.x(d => this.state.xScale(d[0])) .x(d => this.state.xScale(d[0]))
.y1(d => this.state.y1Scale(d[1])) .y0(topChartBorder)
.y0(d => this.height); .y1(d => yScale(d[1]));
} case AreaType.BELOW:
return d3.area()
getRenderGenerator(renderArea: boolean, yOrientation: yAxisOrientation): any { .x(d => this.state.xScale(d[0]))
if(renderArea) { .y0(d => yScale(d[1]))
return yOrientation === yAxisOrientation.LEFT ? this.areaGenerator : this.areaGeneratorY1; .y1(bottomChartBorder);
default:
throw new Error(`Unknown type of renderArea: ${renderArea}`);
} }
return yOrientation === yAxisOrientation.LEFT ? this.lineGenerator : this.areaGeneratorY1;
} }
_renderDots(serie: LineTimeSerie): void { _renderDots(serie: LineTimeSerie): void {
@ -100,12 +111,12 @@ class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
.attr('r', METRIC_CIRCLE_RADIUS) .attr('r', METRIC_CIRCLE_RADIUS)
.style('pointer-events', 'none') .style('pointer-events', 'none')
.attr('cx', d => this.state.xScale(d[0])) .attr('cx', d => this.state.xScale(d[0]))
.attr('cy', d => this.state.yScale(d[1])); .attr('cy', d => this.state.getYScaleByOrientation(serie.yOrientation)(d[1]));
} }
_renderLines(serie: LineTimeSerie): void { _renderLines(serie: LineTimeSerie, generator: Generator): void {
const fillColor = serie.renderArea ? serie.color : 'none'; const fillColor = serie.renderArea !== AreaType.NONE ? serie.color : 'none';
const fillOpacity = serie.renderArea ? 0.5 : 'none'; const fillOpacity = serie.renderArea !== AreaType.NONE ? 0.5 : 'none';
this.metricContainer this.metricContainer
.append('path') .append('path')
@ -118,12 +129,12 @@ class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
.attr('stroke-opacity', 0.7) .attr('stroke-opacity', 0.7)
.attr('pointer-events', 'none') .attr('pointer-events', 'none')
.style('stroke-dasharray', serie.dashArray) .style('stroke-dasharray', serie.dashArray)
.attr('d', this.getRenderGenerator(serie.renderArea, serie.yOrientation)); .attr('d', generator);
} }
_renderMetric(serie: LineTimeSerie): void { _renderMetric(serie: LineTimeSerie, generator: Generator): void {
if(serie.renderLines === true) { if(serie.renderLines === true) {
this._renderLines(serie); this._renderLines(serie, generator);
} }
if(serie.renderDots === true) { if(serie.renderDots === true) {
@ -167,23 +178,14 @@ class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
.style('pointer-events', 'none'); .style('pointer-events', 'none');
} }
public renderSharedCrosshair(values: { x?: number, y?: number }): void {
this.onMouseOver(); // TODO: refactor to use it once
const eventX = this.state.xScale(values.x);
const eventY = this.state.yScale(values.y);
this.moveCrosshairLine(eventX, eventY);
const datapoints = this.findAndHighlightDatapoints(values.x, values.y);
this.options.callbackSharedCrosshairMove({
datapoints: datapoints,
eventX, eventY
});
}
public hideSharedCrosshair(): void { public hideSharedCrosshair(): void {
this.crosshair.style('display', 'none'); this.crosshair.style('display', 'none');
} }
// TODO: refactor to make xPosition and yPosition optional
// and trough error if they are provided for wrong orientation
moveCrosshairLine(xPosition: number, yPosition: number): void { moveCrosshairLine(xPosition: number, yPosition: number): void {
this.crosshair.style('display', null); this.crosshair.style('display', null);
switch(this.options.crosshair.orientation) { switch(this.options.crosshair.orientation) {
@ -291,12 +293,10 @@ class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
} }
getMouseObj(): MouseObj { getMouseObj(): MouseObj {
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.state.xScale.invert(eventX); // mouse x position in xScale const xValue = this.state.xScale.invert(eventX); // mouse x position in xScale
const yValue = this.state.yScale.invert(eventY); const yValue = this.state.yScale.invert(eventY);
this.moveCrosshairLine(eventX, eventY);
const datapoints = this.findAndHighlightDatapoints(xValue, yValue); const datapoints = this.findAndHighlightDatapoints(xValue, yValue);
@ -311,19 +311,35 @@ class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
chartX: eventX, chartX: eventX,
chartWidth: this.width chartWidth: this.width
}; };
} }
override onMouseMove(): void { override onMouseMove(): void {
const obj = this.getMouseObj(); const obj = this.getMouseObj();
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];
this.moveCrosshairLine(eventX, eventY); this.moveCrosshairLine(eventX, eventY);
// TODO: is shift key pressed // TODO: is shift key pressed
// TODO: need to refactor this object // TODO: need to refactor this object
this.options.callbackMouseMove(obj); this.options.callbackMouseMove(obj);
} }
public renderSharedCrosshair(values: { x?: number, y?: number }): void {
this.showCrosshair();
this.moveCrosshairLine(
values.x ? this.state.xScale(values.x) : 0,
values.y ? this.state.yScale(values.y) : 0
);
const datapoints = this.findAndHighlightDatapoints(values.x, values.y);
this.options.callbackSharedCrosshairMove({
datapoints: datapoints,
eventX: values.x ? this.state.xScale(values.x) : 0,
eventY: values.y ? this.state.yScale(values.y) : 0
});
}
override onMouseClick(): void { override onMouseClick(): void {
this.options.callbackMouseClick(this.getMouseObj()); this.options.callbackMouseClick(this.getMouseObj());
} }
@ -357,16 +373,26 @@ class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
return points; return points;
} }
override onMouseOver(): void { showCrosshair() {
this.onMouseMove();
this.crosshair.style('display', null); this.crosshair.style('display', null);
this.crosshair.selectAll('.crosshair-circle') this.crosshair.selectAll('.crosshair-circle')
.style('display', null); .style('display', null);
} }
hideCrosshair() {
this.crosshair.style('display', 'none');
this.crosshair.selectAll('.crosshair-circle')
.style('display', 'none');
}
override onMouseOver(): void {
this.showCrosshair();
this.onMouseMove();
}
override onMouseOut(): void { override onMouseOut(): void {
this.hideCrosshair();
this.options.callbackMouseOut(); this.options.callbackMouseOut();
this.crosshair.style('display', 'none');
} }
isDoubleClickActive(): boolean { isDoubleClickActive(): boolean {
@ -378,6 +404,14 @@ class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
this.onMouseOver(); this.onMouseOver();
} }
updateMarkers(markersConf: MarkersConf): void {
this._markersConf = markersConf;
}
updateSegments(segments: SegmentSerie[]): void {
this._segmentSeries = segments;
}
// methods below rewrite s, (move more methods here) // methods below rewrite s, (move more methods here)
protected zoomOut(): void { protected zoomOut(): void {
if(d3.event.type === 'dblclick' && !this.isDoubleClickActive()) { if(d3.event.type === 'dblclick' && !this.isDoubleClickActive()) {
@ -419,7 +453,41 @@ class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
x: xAxisMiddleValue, x: xAxisMiddleValue,
y: yAxisMiddleValue y: yAxisMiddleValue
} }
this.options.callbackZoomOut(centers);
// TODO: refactor core not to take _options explicitly
if(
this.options._options.events !== undefined &&
this.options._options.events.zoomOut !== undefined
) {
this.options._options.events.zoomOut(
centers,
[this.state.xValueRange, this.state.yValueRange]
);
}
}
protected onContextMenu(): void {
d3.event.preventDefault(); // do not open browser's context menu.
const eventX = d3.mouse(this.chartContainer.node())[0];
const eventY = d3.mouse(this.chartContainer.node())[1];
this.options._options.events.contextMenu({
x: this.state.xScale.invert(eventX),
y: this.state.yScale.invert(eventY),
});
}
// override parent updateData method to provide markers and segments
protected updateLineData(
series?: LineTimeSerie[],
options?: LineOptions,
markersConf?: MarkersConf,
segments?: SegmentSerie[],
shouldRerender = true
): void {
this.updateMarkers(markersConf);
this.updateSegments(segments);
this.updateData(series, options, shouldRerender);
} }
} }
@ -427,6 +495,26 @@ class LinePod extends ChartwerkPod<LineTimeSerie, LineOptions> {
// it is used with Vue.component, e.g.: Vue.component('chartwerk-line-pod', VueChartwerkLinePod) // it is used with Vue.component, e.g.: Vue.component('chartwerk-line-pod', VueChartwerkLinePod)
export const VueChartwerkLinePod = { export const VueChartwerkLinePod = {
// alternative to `template: '<div class="chartwerk-line-pod" :id="id" />'` // alternative to `template: '<div class="chartwerk-line-pod" :id="id" />'`
props: {
markersConf: {
type: Object,
required: false,
default: function() { return {}; }
},
segments: {
type: Array,
required: false,
default: function() { return []; }
},
},
watch: {
markersConf() {
this.renderChart();
},
segments() {
this.renderChart();
},
},
render(createElement) { render(createElement) {
return createElement( return createElement(
'div', 'div',
@ -440,10 +528,10 @@ export const VueChartwerkLinePod = {
methods: { methods: {
render() { render() {
if(this.pod === undefined) { if(this.pod === undefined) {
this.pod = new LinePod(document.getElementById(this.id), this.series, this.options); this.pod = new LinePod(document.getElementById(this.id), this.series, this.options, this.markersConf, this.segments);
this.pod.render(); this.pod.render();
} else { } else {
this.pod.updateData(this.series, this.options); this.pod.updateLineData(this.series, this.options, this.markersConf, this.segments);
} }
}, },
renderSharedCrosshair(values) { renderSharedCrosshair(values) {
@ -455,4 +543,4 @@ export const VueChartwerkLinePod = {
} }
}; };
export { LineTimeSerie, LineOptions, TimeFormat, LinePod }; export { LineTimeSerie, LineOptions, TimeFormat, LinePod, AreaType, MarkersConf, SegmentSerie };

4
src/models/line_series.ts

@ -1,5 +1,5 @@
import { CoreSeries, yAxisOrientation } from '@chartwerk/core'; import { CoreSeries, yAxisOrientation } from '@chartwerk/core';
import { LineTimeSerie } from '../types'; import { LineTimeSerie, AreaType } from '../types';
import * as _ from 'lodash'; import * as _ from 'lodash';
@ -10,7 +10,7 @@ const LINE_SERIE_DEFAULTS = {
renderLines: true, renderLines: true,
dashArray: '0', dashArray: '0',
class: '', class: '',
renderArea: false, renderArea: AreaType.NONE,
yOrientation: yAxisOrientation.LEFT, yOrientation: yAxisOrientation.LEFT,
}; };

12
src/models/marker.ts

@ -1,9 +1,13 @@
export type MarkerElem = [number, any?]; export type MarkerElem = {
x: number;
color: string;
html?: string;
alwaysDisplay?: boolean;
payload?: any;
}
export type MarkerSerie = { export type MarkerSerie = {
color: string; data: MarkerElem[];
// TODO: make one-dimensional array with only x
data: MarkerElem[] // [x, payload] payload is any data for tooltip
} }
export type MarkersConf = { export type MarkersConf = {

9
src/models/segment.ts

@ -1,4 +1,11 @@
export type SegmentElement = [number, number, any?];
export type SegmentSerie = { export type SegmentSerie = {
color: string; color: string;
data: [number, number, any?][] // [from, to, payload?] payload is any data for tooltip data: SegmentElement[] // [from, to, payload?] payload is any data for tooltip,
select?: boolean,
opacity?: number,
opacitySelect?: number,
onSelect?: (SegmentElement) => void
onUnselect?: (SegmentElement) => void
} }

29
src/types.ts

@ -1,16 +1,35 @@
import { Serie, Options } from '@chartwerk/core'; import { Serie, Options } from '@chartwerk/core';
import { AxisRange } from '@chartwerk/core/dist/types';
type LineTimeSerieParams = { type LineTimeSerieParams = {
maxLength: number, maxLength: number;
renderDots: boolean, renderDots: boolean;
renderLines: boolean, // TODO: refactor same as scatter-pod renderLines: boolean; // TODO: refactor same as scatter-pod
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: AreaType; // default is none
}
export enum AreaType {
NONE = 'None',
ABOVE = 'Above',
BELOW = 'Below',
} }
export type LineTimeSerie = Serie & Partial<LineTimeSerieParams>; export type LineTimeSerie = Serie & Partial<LineTimeSerieParams>;
export type LineOptions = Options; export type LineOptions = Options & {
events? : {
zoomOut?: (centers: {
x: number;
y: number;
}, range: AxisRange[]) => void;
mouseMove?: (evt: MouseObj) => void;
contextMenu?: (position: {
x: number;
y: number;
}) => void;
}
}
export type MouseObj = { export type MouseObj = {
x: number, x: number,

3
tsconfig.json

@ -19,6 +19,5 @@
"noUnusedLocals": false, "noUnusedLocals": false,
"baseUrl": "./src", "baseUrl": "./src",
}, },
"include": ["src/**/*"], "include": ["src/**/*"]
"exclude": ["*.tsx"]
} }

10
yarn.lock

@ -5,13 +5,13 @@ __metadata:
version: 6 version: 6
cacheKey: 8 cacheKey: 8
"@chartwerk/core@npm:latest": "@chartwerk/core@npm:^0.6.26":
version: 0.6.14 version: 0.6.26
resolution: "@chartwerk/core@npm:0.6.14" resolution: "@chartwerk/core@npm:0.6.26"
dependencies: dependencies:
d3: ^5.16.0 d3: ^5.16.0
lodash: ^4.17.21 lodash: ^4.17.21
checksum: 34888b555d91a96d5a279f43dc4b33f345d055574855e1c90cb22913f7e42b13b6c9abbd0af07e6531b6f872cdf8e04660c22a2c14938189b67b9c494c6a0bfb checksum: d77ef83701dc13cf2b7fb36dc96448060b6301928bcc0730a7150930f83c51f295e176bcda4e1b8cb8f56d15fef5696edfe6f4e1033adbb5ef5d3487a02c3390
languageName: node languageName: node
linkType: hard linkType: hard
@ -19,7 +19,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@chartwerk/line-pod@workspace:." resolution: "@chartwerk/line-pod@workspace:."
dependencies: dependencies:
"@chartwerk/core": latest "@chartwerk/core": ^0.6.26
copy-webpack-plugin: ^11.0.0 copy-webpack-plugin: ^11.0.0
css-loader: ^6.8.1 css-loader: ^6.8.1
style-loader: ^3.3.3 style-loader: ^3.3.3

Loading…
Cancel
Save