Compare commits

...

40 Commits

Author SHA1 Message Date
Coin de Gamma e841792c34 Merge pull request 'mouse over event type and it's calling' (#44) from onmouseover-event-callback-#42 into main 5 days ago
rozetko c546284e70 Merge pull request 'replace todo with comment' (#40) from styles-use-todo into main 5 days ago
glitch4347 e36148772f mouse over event type and it's calling 5 days ago
glitch4347 90af9810a3 update comment 1 week ago
glitch4347 e24bee024b replace todo with comment 1 week ago
Coin de Gamma bc1e06e515 Merge pull request '0.6.24' (#37) from 0.6.24 into main 3 months ago
glitch4347 eea338db16 0.6.24 3 months ago
Coin de Gamma 63bf4ca931 Merge pull request 'rm abstact keyword and methods' (#36) from bad-abstract-onmouseclick-#35 into main 3 months ago
glitch4347 5fc85d6329 rm abstact keyword and methods 3 months ago
Coin de Gamma b36c226bae Merge pull request '0.6.23' (#34) from 0.6.23 into main 3 months ago
glitch4347 6721f7d127 0.6.23 3 months ago
Coin de Gamma 443d2342f7 Merge pull request 'rm demo from build' (#33) from broken-demo-#32 into main 3 months ago
glitch4347 0a64dd94f6 rm demo from build 3 months ago
Coin de Gamma 1711b2cc1e Merge pull request '0.6.22' (#31) from 0.6.22 into main 3 months ago
glitch4347 2d4794ad0c 0.6.22 3 months ago
Coin de Gamma 476feba10c Merge pull request 'rename-eventCallbacs-to-events-#26-2' (#29) from rename-eventCallbacs-to-events-#26-2 into main 3 months ago
glitch4347 c05d989347 rename EventsCallbacs to Events 3 months ago
glitch4347 88dbb2bdec comment about obolure eventsCallbacks 3 months ago
glitch4347 c83becf2aa events refactoring begin 3 months ago
rozetko 3590eeebcc Merge pull request 'zoomin protected method' (#28) from zoomin-protected into main 3 months ago
glitch4347 c1a2124d13 AxisRange to top 3 months ago
glitch4347 29c73517d6 rm rnages undefined check 3 months ago
glitch4347 5d75ccb39f zoomin protected method 3 months ago
rozetko b8f1dac3bd 0.6.21 4 months ago
rozetko e0ecc385a6 0.6.20 4 months ago
rozetko f137cc727e disable right-click brushing 4 months ago
Coin de Gamma 6254f1386a Merge pull request '0.6.19' (#25) from 0.6.19 into main 5 months ago
glitch4347 8f278a15be 0.6.19 5 months ago
Coin de Gamma 3c8865d2c3 Merge pull request 'fix demo pod' (#23) from demopod-build-error-#22 into main 5 months ago
glitch4347 85c68d279c fix demo pod 5 months ago
vargburz a7bf96f7d4 0.6.18 5 months ago
vargburz f0c21dbf67 Merge pull request 'onclick' (#19) from onclick-event-#15 into main 5 months ago
glitch4347 493d52629c onclick 5 months ago
rozetko ffc73dbd85 Merge pull request 'Demo' (#14) from examples into main 5 months ago
vargburz 43852582b6 rename example to demo 5 months ago
vargburz a4251b69b7 build example 5 months ago
vargburz 0cb408c9b5 add example pod 5 months ago
vargburz ae8904079b Merge pull request 'set git repo in package.json' (#7) from set-git-package into main 6 months ago
glitch4347 c4da056f2d set git repo in package.json 6 months ago
vargburz 1e0702a8b6 Merge pull request 'Remove unused methods and options' (#4) from remove-unused-methods into main 6 months ago
  1. 40
      build/webpack.demo.conf.js
  2. 5
      demo/README.md
  3. 38
      demo/demo.html
  4. 56
      demo/demo_pod.ts
  5. 5
      package.json
  6. 42
      src/VueChartwerkPodMixin.ts
  7. 58
      src/index.ts
  8. 69
      src/models/options.ts
  9. 37
      src/types.ts
  10. 11
      tsconfig.json

40
build/webpack.demo.conf.js

@ -0,0 +1,40 @@
const path = require('path');
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
context: resolve('demo'),
entry: './demo_pod.ts',
plugins: [],
devtool: 'inline-source-map',
watch: true,
mode: 'development',
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{ loader: 'style-loader', options: { injectType: 'lazyStyleTag' } },
'css-loader',
],
exclude: /node_modules/
}
],
},
resolve: {
extensions: ['.ts', '.js'],
},
output: {
filename: 'demo.js',
path: resolve('demo/dist'),
libraryTarget: 'umd',
umdNamedDefine: true
}
};

5
demo/README.md

@ -0,0 +1,5 @@
### HOW TO RUN
run `yarn run dev` and `yarn run demo` in separate terminals simultaneously.
open `demo.html` in your browser.

38
demo/demo.html

@ -0,0 +1,38 @@
<!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/demo.js" type="text/javascript"></script>
</head>
<body>
<div id="chart" style="width: 50%; height: 500px;"></div>
<script type="text/javascript">
const startTime = 1590590148;
const arrayLength = 20;
const data1 = Array.from({ length: arrayLength }, (el, idx) => [startTime + idx * 10000, Math.floor(Math.random() * 40)]);
let options = {
renderLegend: false,
axis: {
y: { range: [0, 350] },
x: { format: 'time' }
},
zoomEvents: {
mouse: { zoom: { isActive: false }, pan: { isActive: false } },
scroll: { zoom: { isActive: false } }
},
}
var pod = new DemoPod(
document.getElementById('chart'),
[
{ target: 'test1', datapoints: data1, color: 'green' },
],
options
);
pod.render();
</script>
</body>
</html>

56
demo/demo_pod.ts

@ -0,0 +1,56 @@
import {
ChartwerkPod,
Serie,
Options
} from '../dist/index';
import * as d3 from 'd3';
import * as _ from 'lodash';
class DemoPod extends ChartwerkPod<Serie, Options> {
lineGenerator = null;
constructor(
_el: HTMLElement,
_series: Serie[] = [],
_options: Options = {},
) {
super(_el, _series, _options);
}
override renderMetrics(): void {
this.clearAllMetrics();
this.initLineGenerator();
for(const serie of this.series.visibleSeries) {
this.renderLine(serie);
}
}
clearAllMetrics(): void {
// TODO: temporary hack before it will be implemented in core.
this.chartContainer.selectAll('.metric-el').remove();
}
initLineGenerator(): void {
this.lineGenerator = d3.line()
.x(d => this.state.xScale(d[0]))
.y(d => this.state.yScale(d[1]));
}
renderLine(serie: Serie): void {
this.metricContainer
.append('path')
.datum(serie.datapoints)
.attr('class', `metric-path-${serie.idx} metric-el ${serie.class}`)
.attr('fill-opacity', 0)
.attr('stroke', serie.color)
.attr('stroke-width', 1)
.attr('stroke-opacity', 0.7)
.attr('pointer-events', 'none')
.attr('d', this.lineGenerator);
}
}
export { DemoPod };

5
package.json

@ -1,6 +1,6 @@
{
"name": "@chartwerk/core",
"version": "0.6.17",
"version": "0.6.24",
"description": "Chartwerk core",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@ -10,11 +10,12 @@
"scripts": {
"build": "webpack --config build/webpack.prod.conf.js",
"dev": "webpack --config build/webpack.dev.conf.js",
"demo": "webpack --config build/webpack.demo.conf.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://gitlab.com/chartwerk/core.git"
"url": "http://code.corpglory.net/chartwerk/core.git"
},
"author": "CorpGlory Inc.",
"license": "ISC",

42
src/VueChartwerkPodMixin.ts

@ -39,9 +39,9 @@ export default {
},
methods: {
// it's "abstract" method. "children" components should override it
render() { },
renderSharedCrosshair(values: { x?: number, y?: number }) { },
hideSharedCrosshair() { },
render() {},
renderSharedCrosshair(values: { x?: number, y?: number }) {},
hideSharedCrosshair() {},
onPanningRescale(event) {
this.pod.rescaleMetricAndAxis(event);
},
@ -50,41 +50,48 @@ export default {
this.render();
},
appendEvents() {
if(this.options.eventsCallbacks === undefined) {
this.options.eventsCallbacks = {}
if(this.options.events === undefined) {
if(this.options.eventsCallbacks !== undefined) {
this.options.events = this.options.eventsCallbacks
} else {
this.options.events = {};
}
}
if(has(this.$listeners, 'zoomIn')) {
this.options.eventsCallbacks.zoomIn = this.zoomIn.bind(this);
this.options.events.zoomIn = this.zoomIn.bind(this);
}
if(has(this.$listeners, 'zoomOut')) {
this.options.eventsCallbacks.zoomOut = this.zoomOut.bind(this);
this.options.events.zoomOut = this.zoomOut.bind(this);
}
if(has(this.$listeners, 'mouseOver')) {
this.options.events.mouseOver = this.mouseOver.bind(this);
}
if(has(this.$listeners, 'mouseMove')) {
this.options.eventsCallbacks.mouseMove = this.mouseMove.bind(this);
this.options.events.mouseMove = this.mouseMove.bind(this);
}
if(has(this.$listeners, 'mouseOut')) {
this.options.eventsCallbacks.mouseOut = this.mouseOut.bind(this);
this.options.events.mouseOut = this.mouseOut.bind(this);
}
if(has(this.$listeners, 'onLegendClick')) {
this.options.eventsCallbacks.onLegendClick = this.onLegendClick.bind(this);
this.options.events.onLegendClick = this.onLegendClick.bind(this);
}
if(has(this.$listeners, 'panningEnd')) {
this.options.eventsCallbacks.panningEnd = this.panningEnd.bind(this);
this.options.events.panningEnd = this.panningEnd.bind(this);
}
if(has(this.$listeners, 'panning')) {
this.options.eventsCallbacks.panning = this.panning.bind(this);
this.options.events.panning = this.panning.bind(this);
}
if(has(this.$listeners, 'contextMenu')) {
this.options.eventsCallbacks.contextMenu = this.contextMenu.bind(this);
this.options.events.contextMenu = this.contextMenu.bind(this);
}
if(has(this.$listeners, 'sharedCrosshairMove')) {
this.options.eventsCallbacks.sharedCrosshairMove = this.sharedCrosshairMove.bind(this);
this.options.events.sharedCrosshairMove = this.sharedCrosshairMove.bind(this);
}
if(has(this.$listeners, 'renderStart')) {
this.options.eventsCallbacks.renderStart = this.renderStart.bind(this);
this.options.events.renderStart = this.renderStart.bind(this);
}
if(has(this.$listeners, 'renderEnd')) {
this.options.eventsCallbacks.renderEnd = this.renderEnd.bind(this);
this.options.events.renderEnd = this.renderEnd.bind(this);
}
},
zoomIn(range) {
@ -93,6 +100,9 @@ export default {
zoomOut(centers) {
this.$emit('zoomOut', centers);
},
mouseOver() {
this.$emit('mouseOver');
},
mouseMove(evt) {
this.$emit('mouseMove', evt);
},

58
src/index.ts

@ -1,3 +1,4 @@
import { AxisRange } from './types';
import VueChartwerkPodMixin from './VueChartwerkPodMixin';
import { Grid } from './components/grid';
@ -30,15 +31,13 @@ import { palette } from './colors';
import * as d3 from 'd3';
import first from 'lodash/first';
import last from 'lodash/last';
import debounce from 'lodash/debounce';
const DEFAULT_TICK_SIZE = 2;
const MILISECONDS_IN_MINUTE = 60 * 1000;
abstract class ChartwerkPod<T extends Serie, O extends Options> {
class ChartwerkPod<T extends Serie, O extends Options> {
protected series: CoreSeries<T>;
protected options: CoreOptions<O>;
@ -80,7 +79,8 @@ abstract class ChartwerkPod<T extends Serie, O extends Options> {
_series: T[] = [],
_options: O
) {
// TODO: test if it's necessary
// need to call explicitly because option lazyStyleTag
// in webpack style-loader
styles.use();
this.options = new CoreOptions(_options);
@ -152,12 +152,13 @@ abstract class ChartwerkPod<T extends Serie, O extends Options> {
this.series.updateSeries(newSeries);
}
protected abstract renderMetrics(): void;
protected abstract onMouseOver(): void;
protected abstract onMouseOut(): void;
protected abstract onMouseMove(): void;
public abstract renderSharedCrosshair(values: { x?: number, y?: number }): void;
public abstract hideSharedCrosshair(): void;
protected renderMetrics(): void {}
protected onMouseOver(): void {}
protected onMouseOut(): void {}
protected onMouseMove(): void {}
protected onMouseClick(): void {}
public renderSharedCrosshair(values: { x?: number, y?: number }): void {}
public hideSharedCrosshair(): void {}
protected initPodState(): void {
const boxPararms = {
@ -327,12 +328,21 @@ abstract class ChartwerkPod<T extends Serie, O extends Options> {
// TODO: refactor for a new mouse/scroll events
const panKeyEvent = this.options.mousePanEvent.keyEvent;
const isPanActive = this.options.mousePanEvent.isActive;
if(isPanActive === true && panKeyEvent === KeyEvent.MAIN) {
this.initPan();
this.initBrush();
const isBrushActive = this.options.mouseZoomEvent.isActive;
if(panKeyEvent === KeyEvent.MAIN) {
if(isPanActive) {
this.initPan();
}
if(isBrushActive) {
this.initBrush();
}
} else {
this.initBrush();
this.initPan();
if(isBrushActive) {
this.initBrush();
}
if(isPanActive) {
this.initPan();
}
}
this.ensureOverlayExisting();
@ -341,6 +351,7 @@ abstract class ChartwerkPod<T extends Serie, O extends Options> {
.on('mouseover', this.onMouseOver.bind(this))
.on('mouseout', this.onMouseOut.bind(this))
.on('mousemove', this.onMouseMove.bind(this))
.on('click', this.onMouseClick.bind(this))
.on('dblclick', () => {
d3.event.stopPropagation();
// TODO: add the same check as we have in line-pod
@ -372,10 +383,6 @@ abstract class ChartwerkPod<T extends Serie, O extends Options> {
}
protected initBrush(): void {
const isBrushActive = this.options.mouseZoomEvent.isActive;
if(isBrushActive === false) {
return;
}
switch(this.options.mouseZoomEvent.orientation) {
case BrushOrientation.VERTICAL:
this.brush = d3.brushY();
@ -409,9 +416,9 @@ abstract class ChartwerkPod<T extends Serie, O extends Options> {
// TODO: refactor
switch(key) {
case KeyEvent.MAIN:
return () => !d3.event.shiftKey;
return () => !d3.event.shiftKey && !d3.event.button;
case KeyEvent.SHIFT:
return () => d3.event.shiftKey;
return () => d3.event.shiftKey && !d3.event.button;
default:
throw new Error(`Unknown type of KeyEvent: ${key}`);
}
@ -793,8 +800,7 @@ abstract class ChartwerkPod<T extends Serie, O extends Options> {
this.state.yValueRange = yRange;
this.brushStartSelection = null;
}
this.options.callbackZoomIn([xRange, yRange]);
this.zoomIn([xRange, yRange]);
}
protected zoomOut(): void {
@ -807,6 +813,10 @@ abstract class ChartwerkPod<T extends Serie, O extends Options> {
this.options.callbackZoomOut(centers);
}
protected zoomIn(ranges: AxisRange[]): void {
this.options.callbackZoomIn(ranges);
}
getAxisTicksFormatter(axisOptions: AxisOption): (d: any, i: number) => any {
// TODO: ticksCount === 0 -> suspicious option
if(axisOptions.ticksCount === 0) {

69
src/models/options.ts

@ -90,7 +90,6 @@ export const CORE_DEFAULT_OPTIONS: Options = {
crosshair: DEFAULT_CROSSHAIR_OPTIONS,
renderLegend: true,
margin: DEFAULT_MARGIN,
eventsCallbacks: {},
}
export class CoreOptions<O extends Options> {
@ -106,6 +105,14 @@ export class CoreOptions<O extends Options> {
protected setOptions(options: O): void {
this._options = lodashDefaultsDeep(lodashCloneDeep(options), this.getDefaults());
if(this._options.eventsCallbacks !== undefined) {
if(this._options.events !== undefined) {
throw new Error('events and eventsCallbacks are mutually exclusive');
}
this._options.events = this._options.eventsCallbacks;
}
// also bakward compatibility for clients who use "eventsCallbacks" instead of "events"
this._options.eventsCallbacks = this._options.events;
}
// this getter can be overrited in Pod
@ -160,74 +167,86 @@ export class CoreOptions<O extends Options> {
// event callbacks
callbackRenderStart(): void {
if(has(this._options.eventsCallbacks, 'renderStart')) {
this._options.eventsCallbacks.renderStart();
if(has(this._options.events, 'renderStart')) {
this._options.events.renderStart();
}
}
callbackRenderEnd(): void {
if(has(this._options.eventsCallbacks, 'renderEnd')) {
this._options.eventsCallbacks.renderEnd();
if(has(this._options.events, 'renderEnd')) {
this._options.events.renderEnd();
}
}
callbackComponentRenderEnd(part: RenderComponent): void {
if(has(this._options.eventsCallbacks, 'componentRenderEnd')) {
this._options.eventsCallbacks.componentRenderEnd(part);
if(has(this._options.events, 'componentRenderEnd')) {
this._options.events.componentRenderEnd(part);
}
}
callbackLegendClick(idx: number): void {
if(has(this._options.eventsCallbacks, 'onLegendClick')) {
this._options.eventsCallbacks.onLegendClick(idx);
if(has(this._options.events, 'onLegendClick')) {
this._options.events.onLegendClick(idx);
}
}
callbackLegendLabelClick(idx: number): void {
if(has(this._options.eventsCallbacks, 'onLegendLabelClick')) {
this._options.eventsCallbacks.onLegendLabelClick(idx);
if(has(this._options.events, 'onLegendLabelClick')) {
this._options.events.onLegendLabelClick(idx);
}
}
callbackPanning(event: { ranges: AxisRange[], d3Event: any }): void {
if(has(this._options.eventsCallbacks, 'panning')) {
this._options.eventsCallbacks.panning(event);
if(has(this._options.events, 'panning')) {
this._options.events.panning(event);
}
}
callbackPanningEnd(ranges: AxisRange[]): void {
if(has(this._options.eventsCallbacks, 'panningEnd')) {
this._options.eventsCallbacks.panningEnd(ranges);
if(has(this._options.events, 'panningEnd')) {
this._options.events.panningEnd(ranges);
}
}
callbackZoomIn(ranges: AxisRange[]): void {
if(has(this._options.eventsCallbacks, 'zoomIn')) {
this._options.eventsCallbacks.zoomIn(ranges);
if(has(this._options.events, 'zoomIn')) {
this._options.events.zoomIn(ranges);
}
}
callbackZoomOut(centers: { x: number, y: number }): void {
if(has(this._options.eventsCallbacks, 'zoomOut')) {
this._options.eventsCallbacks.zoomOut(centers);
if(has(this._options.events, 'zoomOut')) {
this._options.events.zoomOut(centers);
}
}
callbackSharedCrosshairMove(event: { datapoints, eventX, eventY }): void {
if(has(this._options.eventsCallbacks, 'sharedCrosshairMove')) {
this._options.eventsCallbacks.sharedCrosshairMove(event);
if(has(this._options.events, 'sharedCrosshairMove')) {
this._options.events.sharedCrosshairMove(event);
}
}
callbackMouseOver(): void {
if(has(this._options.events, 'mouseOver')) {
this._options.events.mouseOver();
}
}
callbackMouseMove(event): void {
if(has(this._options.eventsCallbacks, 'mouseMove')) {
this._options.eventsCallbacks.mouseMove(event);
if(has(this._options.events, 'mouseMove')) {
this._options.events.mouseMove(event);
}
}
callbackMouseOut(): void {
if(has(this._options.eventsCallbacks, 'mouseOut')) {
this._options.eventsCallbacks.mouseOut();
if(has(this._options.events, 'mouseOut')) {
this._options.events.mouseOut();
}
}
callbackMouseClick(event): void {
if(has(this._options.events, 'mouseClick')) {
this._options.events.mouseClick(event);
}
}
}

37
src/types.ts

@ -14,23 +14,30 @@ export type Serie = {
yOrientation?: yAxisOrientation,
};
// TODO: move some options to line-chart
export type Events = {
zoomIn?: (range: AxisRange[]) => void,
panning?: (event: { ranges: AxisRange[], d3Event: any }) => void,
panningEnd?: (range: AxisRange[]) => void,
zoomOut?: (centers: {x: number, y: number}) => void,
mouseOver?: () => void,
mouseMove?: (evt: any) => void,
mouseClick?: (evt: any) => void,
mouseOut?: () => void,
onLegendClick?: (idx: number) => void,
onLegendLabelClick?: (idx: number) => void,
contextMenu?: (evt: any) => void, // the same name as in d3.events
sharedCrosshairMove?: (event: any) => void,
renderStart?: () => void,
renderEnd?: () => void,
componentRenderEnd?: (part: RenderComponent) => void,
}
export type Options = {
margin?: Margin;
eventsCallbacks?: {
zoomIn?: (range: AxisRange[]) => void,
panning?: (event: { ranges: AxisRange[], d3Event: any }) => void,
panningEnd?: (range: AxisRange[]) => void,
zoomOut?: (centers: {x: number, y: number}) => void,
mouseMove?: (evt: any) => void,
mouseOut?: () => void,
onLegendClick?: (idx: number) => void,
onLegendLabelClick?: (idx: number) => void,
contextMenu?: (evt: any) => void, // the same name as in d3.events
sharedCrosshairMove?: (event: any) => void,
renderStart?: () => void,
renderEnd?: () => void,
componentRenderEnd?: (part: RenderComponent) => void,
};
// obsolete property, use events instead
eventsCallbacks?: Events;
events?: Events;
axis?: AxesOptions;
grid?: GridOptions;
crosshair?: CrosshairOptions;

11
tsconfig.json

@ -1,7 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"rootDir": "./src",
"module": "esnext",
"declaration": true,
"declarationDir": "dist",
@ -16,7 +15,11 @@
"noImplicitUseStrict": false,
"noImplicitAny": false,
"noUnusedLocals": false,
"baseUrl": "./src",
"moduleResolution": "node"
}
"moduleResolution": "node",
},
"exclude": [
"node_modules",
"dist",
"demo"
]
}

Loading…
Cancel
Save