Commit 2b42f5da authored by David Anderson's avatar David Anderson
Browse files

getDataAtEvent now groups the charts and datapoints into one callback

parent f0a534da
...@@ -73,9 +73,13 @@ export class AreaChartDemoComponent { ...@@ -73,9 +73,13 @@ export class AreaChartDemoComponent {
'click', 'click',
{ {
chart: activeChart, chart: activeChart,
onEvent: ( dpl: DataPoint[] ) => { onEvent: ( reports ) => {
let first = dpl[ 0 ]; let dpl: DataPoint[] = [];
if ( !first ) { reports.forEach( r => {
dpl = dpl.concat( r.dpl );
});
if (!dpl.length ) {
console.info( 'no data ' ); console.info( 'no data ' );
return; return;
} }
......
...@@ -36,9 +36,12 @@ export class BarChartDemoComponent { ...@@ -36,9 +36,12 @@ export class BarChartDemoComponent {
'click', 'click',
{ {
chart: barChart, chart: barChart,
onEvent: ( dpl: DataPoint[] ) => { onEvent: ( reports ) => {
let first = dpl[ 0 ]; let dpl: DataPoint[] = [];
if ( !first ) { reports.forEach( r => dpl.push( ...r.dpl ) );
if (!dpl.length ) {
console.info( 'no data ' );
return; return;
} }
console.info( dpl ); console.info( dpl );
......
...@@ -65,12 +65,10 @@ export class BoxplotDemoComponent { ...@@ -65,12 +65,10 @@ export class BoxplotDemoComponent {
'click', 'click',
{ {
chart: boxPlot, chart: boxPlot,
onEvent: ( dpl: DataPoint[], chart: BoxPlot ) => { onEvent: ( reports ) => {
let first = dpl[ 0 ]; reports
if ( !first ) { .filter( r => r.dpl.length > 0 )
return; .forEach( r => console.info( `You are hovering over ${ r.dpl.length } points in Chart ${ r.chart.id }.` ) );
}
console.info( `You are hovering over ${ dpl.length } points in Chart ${ chart.id }.` );
} }
} as any } as any
) ); ) );
......
...@@ -56,12 +56,15 @@ export class HeatmapChartDemoComponent{ ...@@ -56,12 +56,15 @@ export class HeatmapChartDemoComponent{
'click', 'click',
{ {
chart: barChart, chart: barChart,
onEvent: ( dpl: DataPoint[] ) => { onEvent: ( reports ) => {
let first = dpl[ 0 ]; let dpl: DataPoint[] = [];
if ( !first ) { reports.forEach( r => dpl.push( ...r.dpl ) );
if (!dpl.length ) {
console.info( 'no data ' );
return; return;
} }
console.info( first.getExtent(barChart) ); console.info( dpl );
} }
}, },
) ); ) );
......
...@@ -89,9 +89,15 @@ export class LineChartDemoComponent { ...@@ -89,9 +89,15 @@ export class LineChartDemoComponent {
'mousemove', 'mousemove',
{ {
chart: activeChart, chart: activeChart,
onEvent: ( dpl: DataPoint[], chart: LineChart ) => { onEvent: ( reports ) => {
let first = dpl[ 0 ]; let dpl: DataPoint[] = [];
console.log( first ); reports.forEach( r => dpl.push( ...r.dpl ) );
if (!dpl.length ) {
console.info( 'no data ' );
return;
}
console.info( dpl );
} }
} as any } as any
) ); ) );
......
...@@ -72,13 +72,15 @@ export class RadialChartDemoComponent { ...@@ -72,13 +72,15 @@ export class RadialChartDemoComponent {
'click', 'click',
{ {
chart: activeChart, chart: activeChart,
onEvent: ( dpl: DataPoint[] ) => { onEvent: ( reports ) => {
let first = dpl[ 0 ]; let dpl: DataPoint[] = [];
if ( !first ) { reports.forEach( r => dpl.push( ...r.dpl ) );
// console.info( 'no data ' );
if (!dpl.length ) {
console.info( 'no data ' );
return; return;
} }
console.info( dpl.map( dp => dp) ); console.info( dpl );
} }
}, },
) ); ) );
......
...@@ -11,11 +11,11 @@ import { ...@@ -11,11 +11,11 @@ import {
import { Component, ViewChild, ElementRef } from '@angular/core'; import { Component, ViewChild, ElementRef } from '@angular/core';
/* cSpell: words divs scdp */ /* cSpell: words divs scdp */
@Component({ @Component( {
selector: 'app-scatterchart-demo', selector: 'app-scatterchart-demo',
templateUrl: './scatterchart-demo.component.html', templateUrl: './scatterchart-demo.component.html',
styleUrls: ['./scatterchart-demo.component.css'] styleUrls: [ './scatterchart-demo.component.css' ]
}) } )
export class ScatterChartDemoComponent { export class ScatterChartDemoComponent {
@ViewChild( 'drawspace' ) @ViewChild( 'drawspace' )
drawspace: ElementRef; drawspace: ElementRef;
...@@ -51,7 +51,7 @@ export class ScatterChartDemoComponent { ...@@ -51,7 +51,7 @@ export class ScatterChartDemoComponent {
let leftAxis: ContinuousAxis = new ContinuousAxis( 'left' ); let leftAxis: ContinuousAxis = new ContinuousAxis( 'left' );
let bottomAxis: ContinuousAxis = new ContinuousAxis( 'bottom' ); let bottomAxis: ContinuousAxis = new ContinuousAxis( 'bottom' );
let rightAxis: ContinuousAxis = new ContinuousAxis( 'right' ); let rightAxis: ContinuousAxis = new ContinuousAxis( 'right' );
let topAxis: ContinuousAxis = new ContinuousAxis('top'); let topAxis: ContinuousAxis = new ContinuousAxis( 'top' );
let axis = [ let axis = [
// topAxis, // topAxis,
bottomAxis, bottomAxis,
...@@ -61,12 +61,12 @@ export class ScatterChartDemoComponent { ...@@ -61,12 +61,12 @@ export class ScatterChartDemoComponent {
me.chart = new ChartCanvas( me.drawspace.nativeElement ); me.chart = new ChartCanvas( me.drawspace.nativeElement );
// me.chart.background = 'rgb(255,255,255)'; // me.chart.background = 'rgb(255,255,255)';
axis.forEach(a => { axis.forEach( a => {
a.registerChart(charts); a.registerChart( charts );
a.registerChart( selection ); a.registerChart( selection );
a.explicitBounds = [0, pointCount ] a.explicitBounds = [ 0, pointCount ]
// a.fill = 'white'; // a.fill = 'white';
}); } );
topAxis.getValue = bottomAxis.getValue = ( dp ) => { return dp.data.x; }; topAxis.getValue = bottomAxis.getValue = ( dp ) => { return dp.data.x; };
rightAxis.getValue = leftAxis.getValue = ( dp ) => { return dp.data.y; }; rightAxis.getValue = leftAxis.getValue = ( dp ) => { return dp.data.y; };
...@@ -80,7 +80,6 @@ export class ScatterChartDemoComponent { ...@@ -80,7 +80,6 @@ export class ScatterChartDemoComponent {
selection.isActive = false; selection.isActive = false;
} }
console.log( selectedData ); console.log( selectedData );
// me.chart.beginRender();
} }
...@@ -92,35 +91,34 @@ export class ScatterChartDemoComponent { ...@@ -92,35 +91,34 @@ export class ScatterChartDemoComponent {
leftAxis.shouldExtendTicksAcrossChart = true; leftAxis.shouldExtendTicksAcrossChart = true;
bottomAxis.shouldExtendTicksAcrossChart = true; bottomAxis.shouldExtendTicksAcrossChart = true;
me.chart.addChart(charts); me.chart.addChart( charts );
me.chart.addChart(selection); me.chart.addChart( selection );
me.chart.addAxis( axis); me.chart.addAxis( axis );
let getData = new GetDataAtMouseEvent( let getData = new GetDataAtMouseEvent(
'click', 'click',
{ {
chart: charts, chart: charts,
onEvent: ( dpl: DataPoint[] ) => { onEvent: ( reports ) => {
let first = dpl[ 0 ]; let pointsAtEvent: DataPoint[] = [];
if ( !first ) { reports.forEach( r => pointsAtEvent.push( ...r.dpl ) );
if ( !pointsAtEvent.length ) {
selectedData = []; selectedData = [];
selectionUpdate();
return;
} }
// first.data.selected = true; selectedData.push( ...pointsAtEvent );
selectedData.push(first);
selectionUpdate(); selectionUpdate();
} }
}, },
); );
let zoom = new ZoomBehavior(); let zoom = new ZoomBehavior();
// zoom.limitZoom( axis, 0.5, 1.5 ); zoom.limitZoom( axis, 0.5, 1.5 );
let pan = new PanBehavior(); let pan = new PanBehavior('wheel'); // use wheel because we want to click on the data
// pan.limitPan(bottomAxis, pointCount * -0.25, pointCount * 1.25 ); pan.limitPan( bottomAxis, pointCount * -0.25, pointCount * 1.25 );
me.chart.addBehavior( [getData, zoom, pan] ); me.chart.addBehavior( [ getData, zoom, pan ] );
charts[0].alpha = 1; charts[ 0 ].alpha = 1;
let dataList = randomData( pointCount ); let dataList = randomData( pointCount );
charts[ 0 ].datalist( dataList ); charts[ 0 ].datalist( dataList );
me.chart.beginRender(); me.chart.beginRender();
...@@ -142,17 +140,17 @@ function randomData( pointCount: number = 10000 ): Array<DataPoint> { ...@@ -142,17 +140,17 @@ function randomData( pointCount: number = 10000 ): Array<DataPoint> {
// scdp.length = pointCount; // scdp.length = pointCount;
let tmpArr = [ 'blue', 'green', 'grey', 'black', 'brown', 'yellow', 'orange', 'purple', 'pink', 'red', 'blue', 'green', 'grey', 'black', 'brown', 'yellow', 'orange', 'purple', 'pink', 'red' ]; let tmpArr = [ 'blue', 'green', 'grey', 'black', 'brown', 'yellow', 'orange', 'purple', 'pink', 'red', 'blue', 'green', 'grey', 'black', 'brown', 'yellow', 'orange', 'purple', 'pink', 'red' ];
for ( let i = 0; i < pointCount; i++ ) { for ( let i = 0; i < pointCount; i++ ) {
let fill = tmpArr[ randomInt( 0, 9 )]; let fill = tmpArr[ randomInt( 0, 9 ) ];
let dp = new DataPoint( { let dp = new DataPoint( {
x: randomInt(0, pointCount), x: randomInt( 0, pointCount ),
y: randomInt( 0, pointCount ), y: randomInt( 0, pointCount ),
radius: 3, radius: 3,
fill: fill, fill: fill,
stroke: undefined stroke: undefined
}); } );
// let stroke = ( randomColors ) ? fill : tmpArr[ Math.floor(( i + xStart + div / 2 ) / div ) ]; // let stroke = ( randomColors ) ? fill : tmpArr[ Math.floor(( i + xStart + div / 2 ) / div ) ];
scdp.push(dp ); scdp.push( dp );
} }
// scdp.sort(); // scdp.sort();
return scdp; return scdp;
...@@ -167,6 +165,6 @@ class ScatterData { ...@@ -167,6 +165,6 @@ class ScatterData {
x: number = 0; x: number = 0;
y: number = 0; y: number = 0;
radius: number = 0; radius: number = 0;
fill: string =''; fill: string = '';
stroke: string =''; stroke: string = '';
} }
import { EventHandler } from '../classes/eventHandler.class'; import { BaseChart } from '../';
import { EventHandler } from '../types/eventHandler.interface';
import { makeList } from '../utility/makeList.method'; import { makeList } from '../utility/makeList.method';
import { GistBehavior } from './gistBehavior.class'; import { GistBehavior } from './gistBehavior.class';
import { LimiterType } from "../types/limiterType.type"; import { LimiterType } from "../types/limiterType.type";
import { DataPoint } from '../classes/datapoint.class';
export type supportedEventType = 'dblclick' | 'click' | 'mousedown' | 'mousemove' | 'mouseout' | 'mouseover' | 'mouseup'; export type supportedEventType = 'dblclick' | 'click' | 'mousedown' | 'mousemove' | 'mouseout' | 'mouseover' | 'mouseup';
...@@ -12,24 +14,37 @@ export type supportedEventType = 'dblclick' | 'click' | 'mousedown' | 'mousemove ...@@ -12,24 +14,37 @@ export type supportedEventType = 'dblclick' | 'click' | 'mousedown' | 'mousemove
* @class GetDataAtMouseEvent * @class GetDataAtMouseEvent
*/ */
export class GetDataAtMouseEvent extends GistBehavior { export class GetDataAtMouseEvent extends GistBehavior {
/**
*Creates an instance of GetDataAtMouseEvent.
* @param {(supportedEventType | supportedEventType[])} 'dblclick' | 'click' | 'mousedown' | 'mousemove' | 'mouseout' | 'mouseover' | 'mouseup'
* @param {(EventHandler | EventHandler[])} eventHandlers Define what to do when the event happens
* @param {number} [throttleTime=250] How often this event can happen
* @param {LimiterType} [limiter='throttle'] 'throttle' will ensure the event will wait throttleTime, before firing again. 'debounce' will wait throttletime without a repeat event to fire.
* @memberof GetDataAtMouseEvent
*/
constructor( constructor(
eventNames: supportedEventType | supportedEventType[], eventNames: supportedEventType | supportedEventType[],
eventHandlers: EventHandler | EventHandler[], eventHandlers: EventHandler | EventHandler[],
waitTime: number = 250, throttleTime: number = 250,
limiter: LimiterType = 'throttle' ) { limiter: LimiterType = 'throttle' ) {
super( eventNames, eventHandlers, waitTime, limiter ); super( eventNames, eventHandlers, throttleTime, limiter );
} }
_activate(): void { _activate(): void {
const me = this; const me = this;
let reportList: Array<{ dpl: DataPoint[], chart: BaseChart }> ;
me.eventHandlers.forEach( ( eh ) => { me.eventHandlers.forEach( ( eh ) => {
makeList( eh.chart ).forEach( chart => { reportList = [];
if ( chart.isActive ) { makeList( eh.chart )
eh.onEvent( chart._implementation.getDataAtCoord( me.currentX, me.currentY ), chart, me.currentEvent ); .filter( chart => chart !== undefined && chart.isActive )
} .forEach( chart => {
reportList.push( {
dpl: chart._implementation.getDataAtCoord( me.currentX, me.currentY ),
chart: chart
} ); } );
} );
eh.onEvent( reportList, me.currentEvent );
} ); } );
} }
} }
import { EventHandler } from '../classes/eventHandler.class'; import { EventHandler } from '../types/eventHandler.interface';
import { ChartCanvas } from '../classes/chartCanvas.class'; import { ChartCanvas } from '../classes/chartCanvas.class';
import { makeList } from '../utility/makeList.method'; import { makeList } from '../utility/makeList.method';
import { uID } from '../utility/uID.method'; import { uID } from '../utility/uID.method';
...@@ -63,7 +63,7 @@ export abstract class GistBehavior { ...@@ -63,7 +63,7 @@ export abstract class GistBehavior {
* @type {number} * @type {number}
* @memberof GistBehavior * @memberof GistBehavior
*/ */
public readonly waitTime: number; public readonly throttleTime: number;
/** /**
...@@ -137,11 +137,11 @@ export abstract class GistBehavior { ...@@ -137,11 +137,11 @@ export abstract class GistBehavior {
constructor( constructor(
eventNames: string | string[], eventNames: string | string[],
eventHandlers: EventHandler | EventHandler[], eventHandlers: EventHandler | EventHandler[],
waitTime: number = 250, throttleTime: number = 250,
limiter: LimiterType ) { limiter: LimiterType ) {
this.eventNames = makeList( eventNames ); this.eventNames = makeList( eventNames );
this.eventHandlers = makeList( eventHandlers ); this.eventHandlers = makeList( eventHandlers );
this.waitTime = waitTime; this.throttleTime = throttleTime;
this.limiter = limiter; this.limiter = limiter;
} }
...@@ -155,7 +155,7 @@ export abstract class GistBehavior { ...@@ -155,7 +155,7 @@ export abstract class GistBehavior {
protected _activate(): void { protected _activate(): void {
const me = this; const me = this;
me.eventHandlers.forEach(( eh ) => { me.eventHandlers.forEach(( eh ) => {
eh.onEvent( [], undefined, me.currentEvent ); eh.onEvent( [], me.currentEvent );
} ); } );
} }
...@@ -220,7 +220,7 @@ export abstract class GistBehavior { ...@@ -220,7 +220,7 @@ export abstract class GistBehavior {
me.reset(); me.reset();
} }
}, me.waitTime ); }, me.throttleTime );
} }
} }
...@@ -232,7 +232,7 @@ export abstract class GistBehavior { ...@@ -232,7 +232,7 @@ export abstract class GistBehavior {
me.currentEvent = event; me.currentEvent = event;
me.timer = window.setTimeout(() => { me.timer = window.setTimeout(() => {
me.callActivate(); me.callActivate();
}, me.waitTime ); }, me.throttleTime );
} }
private callActivate(): void { private callActivate(): void {
......
import { BaseChart } from '../configs/charts/baseChart.class'; import { EventHandler } from '../types/eventHandler.interface';
import { GistBehavior } from './gistBehavior.class'; import { GistBehavior } from './gistBehavior.class';
import { DataPoint } from '../classes/datapoint.class';
import { EventHandler } from '../classes/eventHandler.class';
import { SupportedPanEvent } from "../types/supportedPanEvent.type"; import { SupportedPanEvent } from "../types/supportedPanEvent.type";
import { makeList } from '../utility/makeList.method'; import { makeList } from '../utility/makeList.method';
import { BaseAxis } from '../configs/axis/baseAxis.class'; import { BaseAxis } from '../configs/axis/baseAxis.class';
...@@ -16,18 +14,21 @@ import { BaseAxis } from '../configs/axis/baseAxis.class'; ...@@ -16,18 +14,21 @@ import { BaseAxis } from '../configs/axis/baseAxis.class';
*/ */
export class PanBehavior extends GistBehavior { export class PanBehavior extends GistBehavior {
// private zoomConstant: number = 1.15;
// private zoomMethod: zoomMethod;
private mousedown: boolean; private mousedown: boolean;
private lastPosition!: { x: number, y: number }; private lastPosition!: { x: number, y: number };
private maskingElement!: HTMLElement; private maskingElement!: HTMLElement;
private userSelectValue!: string | null; private userSelectValue!: string | null;
private hasWarned: boolean | undefined; private hasWarned: boolean | undefined;
/**
*Creates an instance of PanBehavior.
* @param {(SupportedPanEvent | SupportedPanEvent[])} [eventNames='mousedrag'] // mouse drag works by holding the mouse down and dragging, will stop when the mouse is released. Wheel works on scroll ( magic mouse compatible )
* @param {number} [throttleTime=15] // the delay on pans
* @memberof PanBehavior
*/
constructor( constructor(
eventNames: SupportedPanEvent | SupportedPanEvent[] = 'mousedrag' , eventNames: SupportedPanEvent | SupportedPanEvent[] = 'mousedrag' ,
waitTime: number = 15, throttleTime: number = 15,
) { ) {
let givenEvents = makeList( eventNames ); let givenEvents = makeList( eventNames );
let dragIndex = givenEvents.indexOf( 'mousedrag' ); let dragIndex = givenEvents.indexOf( 'mousedrag' );
...@@ -37,15 +38,26 @@ export class PanBehavior extends GistBehavior { ...@@ -37,15 +38,26 @@ export class PanBehavior extends GistBehavior {
super( super(
givenEvents, givenEvents,
{ {
onEvent: ( dpl: DataPoint[], chart: BaseChart, mouseEvent: MouseEvent ) => { this._togglepan( dpl, chart, mouseEvent ); } onEvent: ( [], mouseEvent: MouseEvent ) => {
this._togglepan( mouseEvent );
}
}as EventHandler }as EventHandler
, waitTime, , throttleTime,
'throttle' 'throttle'
); );
this.mousedown = false; this.mousedown = false;
} }
/**
* BETA feature! still buggy
* Set the min and max pan value for the given axis. Values inside the pan limits will always be visible.
*
* @param {(BaseAxis | Array<BaseAxis>)} targets
* @param {number} minZoomLevel
* @param {number} maxZoomLevel
* @memberof ZoomBehavior
*/
public limitPan( axis: BaseAxis, minValue: any, maxValue: any ) { public limitPan( axis: BaseAxis, minValue: any, maxValue: any ) {
if ( !this.hasWarned ) { if ( !this.hasWarned ) {
window.console.warn( 'limitZoom is still in beta. There are known bugs, but the api should not change' ); window.console.warn( 'limitZoom is still in beta. There are known bugs, but the api should not change' );
...@@ -54,7 +66,7 @@ export class PanBehavior extends GistBehavior { ...@@ -54,7 +66,7 @@ export class PanBehavior extends GistBehavior {
axis._implementation.panExtent = [minValue, maxValue]; axis._implementation.panExtent = [minValue, maxValue];
} }
_togglepan( dpl: DataPoint[], chart: BaseChart | undefined, mouseEvent: MouseEvent ): void { _togglepan( mouseEvent: MouseEvent ): void {