From 3663d3eaaa4883294b766dc0248d09da9c500abe Mon Sep 17 00:00:00 2001 From: rozetko Date: Thu, 11 Jul 2024 15:39:33 +0300 Subject: [PATCH 01/20] upd chartwerk core to 0.6.25 --- .gitignore | 2 +- package.json | 2 +- yarn.lock | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 5cd6bd8..1fa1112 100755 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ node_modules dist # yarn -.yarn/* +.yarn !.yarn/patches !.yarn/plugins !.yarn/releases diff --git a/package.json b/package.json index 26b0e52..bbfb368 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "author": "CorpGlory", "license": "ISC", "dependencies": { - "@chartwerk/core": "^0.6.23" + "@chartwerk/core": "^0.6.25" }, "devDependencies": { "copy-webpack-plugin": "^11.0.0", diff --git a/yarn.lock b/yarn.lock index fd4c3b3..0da71de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,13 +5,13 @@ __metadata: version: 6 cacheKey: 8 -"@chartwerk/core@npm:^0.6.23": - version: 0.6.23 - resolution: "@chartwerk/core@npm:0.6.23" +"@chartwerk/core@npm:^0.6.25": + version: 0.6.25 + resolution: "@chartwerk/core@npm:0.6.25" dependencies: d3: ^5.16.0 lodash: ^4.17.21 - checksum: 629b0438e8cea02914e12956069d318caa98e6b3e2dd2514aab267474fa87e0aa92c190c4ca0fe95ca8091f83be1e1897801f5632c3f11d9cb3be39fa89cca84 + checksum: 08a638c26c99bf5f0e91d8acb136406aa5968ea720a590e0c9fdbe25cec326806e25f2b2457d0feee9bec6f8933f8ae1e1def5226a83c3effa18352de9c127af languageName: node linkType: hard @@ -19,7 +19,7 @@ __metadata: version: 0.0.0-use.local resolution: "@chartwerk/line-pod@workspace:." dependencies: - "@chartwerk/core": ^0.6.23 + "@chartwerk/core": ^0.6.25 copy-webpack-plugin: ^11.0.0 css-loader: ^6.8.1 style-loader: ^3.3.3 From 7e721ab880357d265e48a7d661f58f3db21a205b Mon Sep 17 00:00:00 2001 From: rozetko Date: Thu, 11 Jul 2024 15:39:48 +0300 Subject: [PATCH 02/20] 0.6.20 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bbfb368..89016d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chartwerk/line-pod", - "version": "0.6.19", + "version": "0.6.20", "description": "Chartwerk line chart", "main": "dist/index.js", "files": [ From 121f7a0305f69f41bd789107c88c7e2cc1d67668 Mon Sep 17 00:00:00 2001 From: rozetko Date: Thu, 11 Jul 2024 18:28:47 +0300 Subject: [PATCH 03/20] fix renderDots not working for right y-axis --- package.json | 2 +- src/index.ts | 4 ++-- yarn.lock | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 89016d6..63d8bc8 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "author": "CorpGlory", "license": "ISC", "dependencies": { - "@chartwerk/core": "^0.6.25" + "@chartwerk/core": "^0.6.26" }, "devDependencies": { "copy-webpack-plugin": "^11.0.0", diff --git a/src/index.ts b/src/index.ts index 4a2f291..36cad59 100644 --- a/src/index.ts +++ b/src/index.ts @@ -88,7 +88,7 @@ class LinePod extends ChartwerkPod { if(renderArea) { return yOrientation === yAxisOrientation.LEFT ? this.areaGenerator : this.areaGeneratorY1; } - return yOrientation === yAxisOrientation.LEFT ? this.lineGenerator : this.areaGeneratorY1; + return yOrientation === yAxisOrientation.LEFT ? this.lineGenerator : this.lineGeneratorY1; } _renderDots(serie: LineTimeSerie): void { @@ -101,7 +101,7 @@ class LinePod extends ChartwerkPod { .attr('r', METRIC_CIRCLE_RADIUS) .style('pointer-events', 'none') .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 { diff --git a/yarn.lock b/yarn.lock index 0da71de..284f259 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,13 +5,13 @@ __metadata: version: 6 cacheKey: 8 -"@chartwerk/core@npm:^0.6.25": - version: 0.6.25 - resolution: "@chartwerk/core@npm:0.6.25" +"@chartwerk/core@npm:^0.6.26": + version: 0.6.26 + resolution: "@chartwerk/core@npm:0.6.26" dependencies: d3: ^5.16.0 lodash: ^4.17.21 - checksum: 08a638c26c99bf5f0e91d8acb136406aa5968ea720a590e0c9fdbe25cec326806e25f2b2457d0feee9bec6f8933f8ae1e1def5226a83c3effa18352de9c127af + checksum: d77ef83701dc13cf2b7fb36dc96448060b6301928bcc0730a7150930f83c51f295e176bcda4e1b8cb8f56d15fef5696edfe6f4e1033adbb5ef5d3487a02c3390 languageName: node linkType: hard @@ -19,7 +19,7 @@ __metadata: version: 0.0.0-use.local resolution: "@chartwerk/line-pod@workspace:." dependencies: - "@chartwerk/core": ^0.6.25 + "@chartwerk/core": ^0.6.26 copy-webpack-plugin: ^11.0.0 css-loader: ^6.8.1 style-loader: ^3.3.3 From 529f439ac991b64f3163bfd295df9092ec612bcd Mon Sep 17 00:00:00 2001 From: rozetko Date: Thu, 11 Jul 2024 18:28:54 +0300 Subject: [PATCH 04/20] 0.6.21 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 63d8bc8..f084a38 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chartwerk/line-pod", - "version": "0.6.20", + "version": "0.6.21", "description": "Chartwerk line chart", "main": "dist/index.js", "files": [ From f4354d7a77e64611d551362f8db94451bdfeab6d Mon Sep 17 00:00:00 2001 From: rozetko Date: Fri, 19 Jul 2024 20:26:50 +0300 Subject: [PATCH 05/20] better tooltips for markers --- examples/markers.html | 19 ++-- examples/markers_select.html | 12 +-- src/components/markers.ts | 168 +++++++++++++++++++++++++---------- src/index.ts | 5 +- src/models/marker.ts | 12 ++- 5 files changed, 154 insertions(+), 62 deletions(-) diff --git a/examples/markers.html b/examples/markers.html index 5f8d5f9..a08430d 100644 --- a/examples/markers.html +++ b/examples/markers.html @@ -13,9 +13,18 @@ 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]); - // TODO: make this one-dimensinal data when implemented - const markersData1 = [3, 6, 9].map(el => [startTime + el * 1000]); - const markersData2 = [4, 11].map(el => [startTime + el * 1000]); + const markersData1 = [3, 6, 9].map(el => ({ + x: 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 = { renderLegend: false, axis: { @@ -31,8 +40,8 @@ options, { series: [ - { data: markersData1, color: 'red' }, - { data: markersData2, color: 'blue' }, + { data: markersData1 }, + { data: markersData2 }, ] } ); diff --git a/examples/markers_select.html b/examples/markers_select.html index 48a1623..f801e3b 100644 --- a/examples/markers_select.html +++ b/examples/markers_select.html @@ -13,11 +13,11 @@ 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]); - // TODO: make this one-dimensinal data when implemented - const markersData = [3, 6, 9].map(el => [ - startTime + el * 1000, - { el } - ]); + const markersData = [3, 6, 9].map(el => ({ + x: startTime + el * 1000, + payload: el, + color: 'red', + })); let options = { renderLegend: false, axis: { @@ -33,7 +33,7 @@ options, { series: [ - { data: markersData, color: 'red' }, + { data: markersData }, ], events: { onMouseMove: (el) => { console.log(el); }, diff --git a/src/components/markers.ts b/src/components/markers.ts index a481059..9d9c290 100644 --- a/src/components/markers.ts +++ b/src/components/markers.ts @@ -1,59 +1,137 @@ -import { MarkersConf, MarkerSerie } from "../models/marker"; -import { PodState } from "@chartwerk/core"; -import { LineTimeSerie, LineOptions } from "../types"; +import { MarkerElem, MarkersConf, MarkerSerie } from '../models/marker'; +import { LineTimeSerie, LineOptions } from '../types'; -import d3 from "d3"; +import { PodState } from '@chartwerk/core'; + +import d3 from 'd3'; export class Markers { - // TODO: more semantic name - private _d3Holder = null; + private _layerContainer = null; + private _chartHeight = 0; - constructor(private _markerConf: MarkersConf, private _state: PodState) { + constructor( + private _chartContainer: d3.Selection, + private _markerConf: MarkersConf, + private _state: PodState + ) { } + clear() { + if(this._layerContainer !== null) { + this._layerContainer.remove(); + } + this._chartContainer.selectAll('.marker-content').remove(); } - render(metricContainer: d3.Selection) { - if(this._d3Holder !== null) { - this._d3Holder.remove(); + render(metricContainer: d3.Selection, chartHeight: number) { + this._chartHeight = chartHeight; + 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); + } + + private _renderCircle(marker: MarkerElem) { + const linePosition = this._getLinePosition(marker); + + 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('stroke', marker.color) + .attr('stroke-width', '1px') + .attr('stroke-opacity', '0.3') + .attr('stroke-dasharray', '4') + .attr('x1', linePosition) + .attr('x2', linePosition) + .attr('y1', 0) + // @ts-ignore // TODO: remove ignore but boxParams are protected + .attr('y2', this._state.boxParams.height) + .attr('pointer-events', 'none'); + } + + private _renderTooltip(marker: MarkerElem) { + if(marker.html === undefined) { + return; } + + const linePosition = this._getLinePosition(marker); + + this._chartContainer + .append('div') + .attr('class', 'marker-content') + .attr('class', `marker-content-${marker.x}`) + // @ts-ignore // TODO: remove ignore but boxParams are protected + .style('top', `${this._state.boxParams.height - this._chartHeight}px`) + .style('left', `${linePosition + 50}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); } protected renderSerie(serie: MarkerSerie) { - serie.data.forEach((d) => { - let linePosition = this._state.xScale(d[0]) as number; - this._d3Holder.append('line') - .attr('class', 'gap-line') - .attr('stroke', serie.color) - .attr('stroke-width', '1px') - .attr('stroke-opacity', '0.3') - .attr('stroke-dasharray', '4') - .attr('x1', linePosition) - .attr('x2', linePosition) - .attr('y1', 0) - // @ts-ignore // TODO: remove ignore but boxParams are protected - .attr('y2', this._state.boxParams.height) - .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) { - circle - .attr('pointer-events', 'all') - .style('cursor', 'pointer') - .on('mousemove', () => this._markerConf.events.onMouseMove(d)) - .on('mouseout', () => this._markerConf.events.onMouseOut()) - } - + serie.data.forEach((marker: MarkerElem) => { + this._renderLine(marker); + this._renderCircle(marker); + this._renderTooltip(marker); }); - } -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index 36cad59..ce5fc5d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -51,8 +51,8 @@ class LinePod extends ChartwerkPod { this._renderMetric(serie); } if(this._markersConf !== undefined) { - this._markersLayer = new Markers(this._markersConf, this.state); - this._markersLayer.render(this.metricContainer); + this._markersLayer = new Markers(this.d3Node, this._markersConf, this.state); + this._markersLayer.render(this.metricContainer, this.height); } this._segmentsLayer = new Segments(this._segmentSeries, this.state); @@ -62,6 +62,7 @@ class LinePod extends ChartwerkPod { clearAllMetrics(): void { // TODO: temporary hack before it will be implemented in core. this.chartContainer.selectAll('.metric-el').remove(); + this._markersLayer?.clear(); } initLineGenerator(): void { diff --git a/src/models/marker.ts b/src/models/marker.ts index 7b75deb..f0ae211 100644 --- a/src/models/marker.ts +++ b/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 = { - color: string; - // TODO: make one-dimensional array with only x - data: MarkerElem[] // [x, payload] payload is any data for tooltip + data: MarkerElem[]; } export type MarkersConf = { From aac8f6f5acafb5a723e514abc021cdae74b485a5 Mon Sep 17 00:00:00 2001 From: rozetko Date: Fri, 19 Jul 2024 20:32:05 +0300 Subject: [PATCH 06/20] bump version to `0.7.0` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f084a38..16f7620 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chartwerk/line-pod", - "version": "0.6.21", + "version": "0.7.0", "description": "Chartwerk line chart", "main": "dist/index.js", "files": [ From 6549415febae7ed623077f86502a88d6a5a775fd Mon Sep 17 00:00:00 2001 From: rozetko Date: Mon, 22 Jul 2024 16:40:57 +0300 Subject: [PATCH 07/20] markers hotfix --- src/components/markers.ts | 10 +++++----- src/index.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/markers.ts b/src/components/markers.ts index 9d9c290..2f50627 100644 --- a/src/components/markers.ts +++ b/src/components/markers.ts @@ -1,7 +1,7 @@ import { MarkerElem, MarkersConf, MarkerSerie } from '../models/marker'; import { LineTimeSerie, LineOptions } from '../types'; -import { PodState } from '@chartwerk/core'; +import { Margin, PodState } from '@chartwerk/core'; import d3 from 'd3'; @@ -12,7 +12,8 @@ export class Markers { constructor( private _chartContainer: d3.Selection, private _markerConf: MarkersConf, - private _state: PodState + private _state: PodState, + private _margin: Margin, ) { } clear() { @@ -106,11 +107,10 @@ export class Markers { this._chartContainer .append('div') - .attr('class', 'marker-content') - .attr('class', `marker-content-${marker.x}`) + .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('left', `${linePosition + 50}px`) + .style('left', `${linePosition + this._margin.left + 10}px`) .style('visibility', marker.alwaysDisplay ? 'visible' : 'hidden') .style('position', 'absolute') .style('border', '1px solid black') diff --git a/src/index.ts b/src/index.ts index ce5fc5d..27e0c59 100644 --- a/src/index.ts +++ b/src/index.ts @@ -51,7 +51,7 @@ class LinePod extends ChartwerkPod { this._renderMetric(serie); } if(this._markersConf !== undefined) { - this._markersLayer = new Markers(this.d3Node, this._markersConf, this.state); + this._markersLayer = new Markers(this.d3Node, this._markersConf, this.state, this.margin); this._markersLayer.render(this.metricContainer, this.height); } From f38829787c1a71f36bc38f03e8a9053709c28a35 Mon Sep 17 00:00:00 2001 From: rozetko Date: Mon, 22 Jul 2024 16:41:06 +0300 Subject: [PATCH 08/20] 0.7.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 16f7620..d6a8dd6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chartwerk/line-pod", - "version": "0.7.0", + "version": "0.7.1", "description": "Chartwerk line chart", "main": "dist/index.js", "files": [ From ebb49adf8248f56d190821daba6589498cd4f65c Mon Sep 17 00:00:00 2001 From: rozetko Date: Mon, 22 Jul 2024 19:05:40 +0300 Subject: [PATCH 09/20] markers: render tooltip in center --- src/components/markers.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/markers.ts b/src/components/markers.ts index 2f50627..0a79ace 100644 --- a/src/components/markers.ts +++ b/src/components/markers.ts @@ -105,12 +105,11 @@ export class Markers { const linePosition = this._getLinePosition(marker); - this._chartContainer + 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('left', `${linePosition + this._margin.left + 10}px`) .style('visibility', marker.alwaysDisplay ? 'visible' : 'hidden') .style('position', 'absolute') .style('border', '1px solid black') @@ -125,6 +124,10 @@ export class Markers { .style('pointer-events', 'none') .style('z-index', 1) .html(marker.html); + + // align tooltip: center (we need it to be rendered first) + // PS: 8px is the empiric constant to center the div ideally + div.style('left', `${linePosition + this._margin.left - div.node().getBoundingClientRect().width / 2 + 8}px`) } protected renderSerie(serie: MarkerSerie) { From 701feaaf2ff3038feb4254fc696657d5201b6741 Mon Sep 17 00:00:00 2001 From: rozetko Date: Mon, 22 Jul 2024 19:06:23 +0300 Subject: [PATCH 10/20] 0.7.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6a8dd6..c0b49d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chartwerk/line-pod", - "version": "0.7.1", + "version": "0.7.2", "description": "Chartwerk line chart", "main": "dist/index.js", "files": [ From 85cd952b9f61375a9b995b61a07c627483e54023 Mon Sep 17 00:00:00 2001 From: rozetko Date: Mon, 22 Jul 2024 19:14:13 +0300 Subject: [PATCH 11/20] hotfix --- src/components/markers.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/markers.ts b/src/components/markers.ts index 0a79ace..2dfa2d1 100644 --- a/src/components/markers.ts +++ b/src/components/markers.ts @@ -126,8 +126,7 @@ export class Markers { .html(marker.html); // align tooltip: center (we need it to be rendered first) - // PS: 8px is the empiric constant to center the div ideally - div.style('left', `${linePosition + this._margin.left - div.node().getBoundingClientRect().width / 2 + 8}px`) + div.style('left', `${linePosition + this._margin.left - div.node().getBoundingClientRect().width / 2}px`) } protected renderSerie(serie: MarkerSerie) { From 0c3b4041550843cae80926dd815095111f460040 Mon Sep 17 00:00:00 2001 From: rozetko Date: Mon, 22 Jul 2024 19:14:18 +0300 Subject: [PATCH 12/20] 0.7.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0b49d5..fe9bd96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chartwerk/line-pod", - "version": "0.7.2", + "version": "0.7.3", "description": "Chartwerk line chart", "main": "dist/index.js", "files": [ From bb46fe79beb1bd78195c9dc2c37fee29302345eb Mon Sep 17 00:00:00 2001 From: vargburz Date: Wed, 14 Aug 2024 17:25:58 +0300 Subject: [PATCH 13/20] update area render type --- examples/area.html | 39 +++++++++++++++++++++ src/index.ts | 72 +++++++++++++++++---------------------- src/models/line_series.ts | 4 +-- src/types.ts | 14 +++++--- 4 files changed, 83 insertions(+), 46 deletions(-) create mode 100644 examples/area.html diff --git a/examples/area.html b/examples/area.html new file mode 100644 index 0000000..67082dd --- /dev/null +++ b/examples/area.html @@ -0,0 +1,39 @@ + + + + + + + + + +
+ + + + diff --git a/src/index.ts b/src/index.ts index 27e0c59..b67c343 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ 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 { Segments } from './components/segments'; @@ -14,14 +14,9 @@ const METRIC_CIRCLE_RADIUS = 1.5; const CROSSHAIR_CIRCLE_RADIUS = 3; const CROSSHAIR_BACKGROUND_RAIDUS = 9; const CROSSHAIR_BACKGROUND_OPACITY = 0.3; - +type Generator = d3.Line<[number, number]> | d3.Area<[number, number]>; class LinePod extends ChartwerkPod { - lineGenerator = null; - areaGenerator = null; - lineGeneratorY1 = null; - areaGeneratorY1 = null; - private _markersLayer: Markers = null; private _segmentsLayer: Segments = null; @@ -37,18 +32,18 @@ class LinePod extends ChartwerkPod { } override renderMetrics(): void { + console.log('renderMetrics1'); this.clearAllMetrics(); this.updateCrosshair(); - this.initLineGenerator(); - this.initAreaGenerator(); if(!this.series.isSeriesAvailable) { this.renderNoDataPointsMessage(); return; } 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) { this._markersLayer = new Markers(this.d3Node, this._markersConf, this.state, this.margin); @@ -65,31 +60,28 @@ class LinePod extends ChartwerkPod { this._markersLayer?.clear(); } - initLineGenerator(): void { - this.lineGenerator = d3.line() - .x(d => this.state.xScale(d[0])) - .y(d => this.state.yScale(d[1])); - this.lineGeneratorY1 = d3.line() - .x(d => this.state.xScale(d[0])) - .y(d => this.state.y1Scale(d[1])); - } - - initAreaGenerator(): void { - this.areaGenerator = d3.area() - .x(d => this.state.xScale(d[0])) - .y1(d => this.state.yScale(d[1])) - .y0(d => this.height); - this.areaGeneratorY1 = d3.area() - .x(d => this.state.xScale(d[0])) - .y1(d => this.state.y1Scale(d[1])) - .y0(d => this.height); - } - getRenderGenerator(renderArea: boolean, yOrientation: yAxisOrientation): any { - if(renderArea) { - return yOrientation === yAxisOrientation.LEFT ? this.areaGenerator : this.areaGeneratorY1; + getRenderGenerator(renderArea: AreaType, yOrientation: yAxisOrientation): Generator { + const yScale = yOrientation === yAxisOrientation.LEFT ? this.state.yScale : this.state.y1Scale; + switch(renderArea) { + case AreaType.NONE: + // return line generator + return d3.line() + .x(d => this.state.xScale(d[0])) + .y(d => yScale(d[1])); + case AreaType.ABOVE: + return d3.area() + .x(d => this.state.xScale(d[0])) + .y0(this.height) + .y1(d => yScale(d[1])); + case AreaType.BELOW: + return d3.area() + .x(d => this.state.xScale(d[0])) + .y0(d => yScale(d[1])) + .y1(0); + default: + throw new Error(`Unknown type of renderArea: ${renderArea}`); } - return yOrientation === yAxisOrientation.LEFT ? this.lineGenerator : this.lineGeneratorY1; } _renderDots(serie: LineTimeSerie): void { @@ -105,9 +97,9 @@ class LinePod extends ChartwerkPod { .attr('cy', d => this.state.getYScaleByOrientation(serie.yOrientation)(d[1])); } - _renderLines(serie: LineTimeSerie): void { - const fillColor = serie.renderArea ? serie.color : 'none'; - const fillOpacity = serie.renderArea ? 0.5 : 'none'; + _renderLines(serie: LineTimeSerie, generator: Generator): void { + const fillColor = serie.renderArea !== AreaType.NONE ? serie.color : 'none'; + const fillOpacity = serie.renderArea !== AreaType.NONE ? 0.5 : 'none'; this.metricContainer .append('path') @@ -120,12 +112,12 @@ class LinePod extends ChartwerkPod { .attr('stroke-opacity', 0.7) .attr('pointer-events', 'none') .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) { - this._renderLines(serie); + this._renderLines(serie, generator); } if(serie.renderDots === true) { @@ -490,4 +482,4 @@ export const VueChartwerkLinePod = { } }; -export { LineTimeSerie, LineOptions, TimeFormat, LinePod }; +export { LineTimeSerie, LineOptions, TimeFormat, LinePod, AreaType }; diff --git a/src/models/line_series.ts b/src/models/line_series.ts index d75cf07..55737ef 100644 --- a/src/models/line_series.ts +++ b/src/models/line_series.ts @@ -1,5 +1,5 @@ import { CoreSeries, yAxisOrientation } from '@chartwerk/core'; -import { LineTimeSerie } from '../types'; +import { LineTimeSerie, AreaType } from '../types'; import * as _ from 'lodash'; @@ -10,7 +10,7 @@ const LINE_SERIE_DEFAULTS = { renderLines: true, dashArray: '0', class: '', - renderArea: false, + renderArea: AreaType.NONE, yOrientation: yAxisOrientation.LEFT, }; diff --git a/src/types.ts b/src/types.ts index 5e172ea..a67e1d4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,12 +2,18 @@ import { Serie, Options } from '@chartwerk/core'; import { AxisRange } from '@chartwerk/core/dist/types'; type LineTimeSerieParams = { - maxLength: number, - renderDots: boolean, - renderLines: boolean, // TODO: refactor same as scatter-pod + maxLength: number; + renderDots: boolean; + renderLines: boolean; // TODO: refactor same as scatter-pod dashArray: string; // dasharray attr, only for lines 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; From 3f9342b5e10cfb50372dd8674df147655e8fe92f Mon Sep 17 00:00:00 2001 From: vargburz Date: Wed, 14 Aug 2024 17:43:36 +0300 Subject: [PATCH 14/20] remove log --- src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index b67c343..84f67f3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,7 +32,6 @@ class LinePod extends ChartwerkPod { } override renderMetrics(): void { - console.log('renderMetrics1'); this.clearAllMetrics(); this.updateCrosshair(); From ebcb3185cd23d65db4ec50188a68e8a4d18b3ea4 Mon Sep 17 00:00:00 2001 From: vargburz Date: Wed, 14 Aug 2024 18:25:29 +0300 Subject: [PATCH 15/20] 0.7.4 version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fe9bd96..0126f1d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chartwerk/line-pod", - "version": "0.7.3", + "version": "0.7.4", "description": "Chartwerk line chart", "main": "dist/index.js", "files": [ From 53c233b96975f961998af854e21e235917bc347f Mon Sep 17 00:00:00 2001 From: rozetko Date: Wed, 14 Aug 2024 20:11:04 +0300 Subject: [PATCH 16/20] area generator: support `invert` option for y/y1 axes --- examples/area.html | 11 +++++++---- src/index.ts | 11 ++++++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/examples/area.html b/examples/area.html index 67082dd..290b64a 100644 --- a/examples/area.html +++ b/examples/area.html @@ -13,8 +13,9 @@ let options = { renderLegend: false, usePanning: false, axis: { - y: { invert: false, range: [0, 100] }, 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' } }, @@ -24,16 +25,18 @@ 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' }, + { target: 'test1', datapoints: data1, color: 'green', renderArea: 'Above' }, { target: 'test2', datapoints: data2, color: 'blue', renderArea: 'Below' }, - { target: 'test3', datapoints: data3, color: 'orange', renderArea: 'Above' }, + { target: 'test3', datapoints: data3, color: 'orange', renderArea: 'Below', yOrientation: 'right' }, + { target: 'test4', datapoints: data4, color: 'purple', renderArea: 'Above', yOrientation: 'right' }, ], options ); - pod.render(); + pod.render(); diff --git a/src/index.ts b/src/index.ts index 84f67f3..13fe2ab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -62,6 +62,11 @@ class LinePod extends ChartwerkPod { getRenderGenerator(renderArea: AreaType, yOrientation: yAxisOrientation): Generator { 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 @@ -71,13 +76,13 @@ class LinePod extends ChartwerkPod { case AreaType.ABOVE: return d3.area() .x(d => this.state.xScale(d[0])) - .y0(this.height) + .y0(topChartBorder) .y1(d => yScale(d[1])); case AreaType.BELOW: return d3.area() .x(d => this.state.xScale(d[0])) .y0(d => yScale(d[1])) - .y1(0); + .y1(bottomChartBorder); default: throw new Error(`Unknown type of renderArea: ${renderArea}`); } @@ -438,7 +443,7 @@ class LinePod extends ChartwerkPod { // TODO: refactor core not to take _options explicitly if( - this.options._options.events !== undefined && + this.options._options.events !== undefined && this.options._options.events.zoomOut !== undefined ) { this.options._options.events.zoomOut( From f67ce313dd783f835815c599a2d2c9ebc40cec1b Mon Sep 17 00:00:00 2001 From: rozetko Date: Wed, 14 Aug 2024 20:12:37 +0300 Subject: [PATCH 17/20] 0.7.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0126f1d..ebaab58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chartwerk/line-pod", - "version": "0.7.4", + "version": "0.7.5", "description": "Chartwerk line chart", "main": "dist/index.js", "files": [ From 6e6fa7dc8ec9bfd7950101c3b17b4805f3dbe14b Mon Sep 17 00:00:00 2001 From: vargburz Date: Wed, 21 Aug 2024 18:06:15 +0300 Subject: [PATCH 18/20] 0.7.6 && update vue with markers and conf --- package.json | 2 +- src/index.ts | 39 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index ebaab58..b029f67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chartwerk/line-pod", - "version": "0.7.5", + "version": "0.7.6", "description": "Chartwerk line chart", "main": "dist/index.js", "files": [ diff --git a/src/index.ts b/src/index.ts index 13fe2ab..f6d187f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -452,12 +452,45 @@ class LinePod extends ChartwerkPod { ); } } + + // 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); + } } // TODO: it should be moved to VUE folder // it is used with Vue.component, e.g.: Vue.component('chartwerk-line-pod', VueChartwerkLinePod) export const VueChartwerkLinePod = { // alternative to `template: '
'` + 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) { return createElement( 'div', @@ -471,10 +504,10 @@ export const VueChartwerkLinePod = { methods: { render() { 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(); } else { - this.pod.updateData(this.series, this.options); + this.pod.updateLineData(this.series, this.options, this.markersConf, this.segments); } }, renderSharedCrosshair(values) { @@ -486,4 +519,4 @@ export const VueChartwerkLinePod = { } }; -export { LineTimeSerie, LineOptions, TimeFormat, LinePod, AreaType }; +export { LineTimeSerie, LineOptions, TimeFormat, LinePod, AreaType, MarkersConf, SegmentSerie }; From a8408a748c44bde66cc5f55f293740f5926b41b8 Mon Sep 17 00:00:00 2001 From: vargburz Date: Thu, 22 Aug 2024 18:13:37 +0300 Subject: [PATCH 19/20] 0.7.7 && handle context menu --- examples/right_click.html | 33 +++++++++++++++++++++++++++++++++ package.json | 2 +- src/index.ts | 24 +++++++++++++++++++++++- src/types.ts | 4 ++++ 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 examples/right_click.html diff --git a/examples/right_click.html b/examples/right_click.html new file mode 100644 index 0000000..692d69a --- /dev/null +++ b/examples/right_click.html @@ -0,0 +1,33 @@ + + + + + + + + +
+ + + diff --git a/package.json b/package.json index b029f67..e993309 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chartwerk/line-pod", - "version": "0.7.6", + "version": "0.7.7", "description": "Chartwerk line chart", "main": "dist/index.js", "files": [ diff --git a/src/index.ts b/src/index.ts index f6d187f..5b209d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,6 +35,7 @@ class LinePod extends ChartwerkPod { this.clearAllMetrics(); this.updateCrosshair(); + this.updateEvents(); if(!this.series.isSeriesAvailable) { this.renderNoDataPointsMessage(); @@ -59,6 +60,16 @@ class LinePod extends ChartwerkPod { this._markersLayer?.clear(); } + protected updateEvents(): void { + // overlay - core component that is used to handle mouse events + if(!this.overlay) { + return; + } + if(this.options._options.events?.contextMenu === undefined) { + return; + } + this.overlay.on('contextmenu', this.onContextMenu.bind(this)); + } getRenderGenerator(renderArea: AreaType, yOrientation: yAxisOrientation): Generator { const yScale = yOrientation === yAxisOrientation.LEFT ? this.state.yScale : this.state.y1Scale; @@ -453,8 +464,19 @@ class LinePod extends ChartwerkPod { } } + 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( + protected updateLineData( series?: LineTimeSerie[], options?: LineOptions, markersConf?: MarkersConf, diff --git a/src/types.ts b/src/types.ts index a67e1d4..d5493a3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,6 +23,10 @@ export type LineOptions = Options & { x: number; y: number; }, range: AxisRange[]) => void; + contextMenu?: (position: { + x: number; + y: number; + }) => void; } } From b3e7f943ad54d0f602cfc67fc0871c36a1082945 Mon Sep 17 00:00:00 2001 From: vargburz Date: Wed, 11 Sep 2024 13:11:24 +0300 Subject: [PATCH 20/20] 0.7.8 && render segments if exists --- package.json | 2 +- src/index.ts | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index e993309..babb7ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chartwerk/line-pod", - "version": "0.7.7", + "version": "0.7.8", "description": "Chartwerk line chart", "main": "dist/index.js", "files": [ diff --git a/src/index.ts b/src/index.ts index 5b209d2..d66ff42 100644 --- a/src/index.ts +++ b/src/index.ts @@ -45,13 +45,15 @@ class LinePod extends ChartwerkPod { 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.d3Node, this._markersConf, this.state, this.margin); this._markersLayer.render(this.metricContainer, this.height); } - this._segmentsLayer = new Segments(this._segmentSeries, this.state); - this._segmentsLayer.render(this.metricContainer, this.chartContainer); + if(!_.isEmpty(this._segmentSeries)) { + this._segmentsLayer = new Segments(this._segmentSeries, this.state); + this._segmentsLayer.render(this.metricContainer, this.chartContainer); + } } clearAllMetrics(): void {