panBehavior.class.ts 5.9 KB
Newer Older
1
import { EventHandler } from '../types/eventHandler.interface';
2
3
import { GistBehavior } from './gistBehavior.class';
import { SupportedPanEvent } from "../types/supportedPanEvent.type";
4
5
import { makeList } from '../utility/makeList.method';
import { BaseAxis } from '../configs/axis/baseAxis.class';
6

7
/* cSpell: words gcbb, togglepan */
8
9
10
11
12
13
14
15
16

/**
 * Adds a mouse event to a gist chart that will fire when the given event name fires and will call a function with data at the point given
 *
 * @export
 * @class GetDataAtMouseEvent
 */
export class PanBehavior extends GistBehavior {

17
    private mousedown: boolean;
18
19
    private lastPosition!: { x: number, y: number };
    private maskingElement!: HTMLElement;
20
21
    private userSelectValue!: string | null;
    private hasWarned: boolean | undefined;
22

23
24
25
26
27
28
    /**
     *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
     */
29
    constructor(
30
        eventNames: SupportedPanEvent | SupportedPanEvent[] =  'mousedrag' ,
31
        throttleTime: number = 15,
32
33
34
35
36
37
    ) {
        let givenEvents = makeList( eventNames );
        let dragIndex = givenEvents.indexOf( 'mousedrag' );
        if ( dragIndex !== -1 ) {
            givenEvents.splice( dragIndex, 1, 'mousedown' , 'mousemove'  , 'mouseup' );
        }
38
        super(
39
            givenEvents,
40
            {
41
42
43
                onEvent: ( [], mouseEvent: MouseEvent ) => {
                    this._togglepan( mouseEvent );
                }
44
            }as EventHandler
45
            , throttleTime,
46
            'throttle'
47
        );
48
49

        this.mousedown = false;
50
51
    }

52
53
54
55
56
57
58
59
60
    /**
     * 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
     */
61
62
63
64
65
66
    public limitPan( axis: BaseAxis, minValue: any, maxValue: any ) {
        if ( !this.hasWarned ) {
            window.console.warn( 'limitZoom is still in beta. There are known bugs, but the api should not change' );
            this.hasWarned = true;
        }
        axis._implementation.panExtent = [minValue, maxValue];
67
68
    }

69
    _togglepan(  mouseEvent: MouseEvent ): void {
70
        if ( mouseEvent.type === 'mousedown' ) {
71
            this.mousedown = true;
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
            this.maskingElement = document.body.appendChild( document.createElement( 'div' ) );
            this.maskingElement.style.position = 'absolute';
            this.maskingElement.style.top = '0';
            this.maskingElement.style.bottom = '0';
            this.maskingElement.style.left = '0';
            this.maskingElement.style.right = '0';
            this.maskingElement.style.zIndex = '16777271';


            this.userSelectValue = document.body.style.userSelect;
            document.body.style.userSelect = 'none';
            document.body.style.msUserSelect = 'none';
            document.body.style.webkitUserSelect = 'none';

            this.lastPosition = { x: mouseEvent.clientX, y: mouseEvent.clientY }

            this.maskingElement.addEventListener( 'mouseup', () => {
89
90
                document.body.removeChild( this.maskingElement );
                this.mousedown = false;
91
92
93
94
95
96
97
                document.body.style.userSelect = this.userSelectValue;
                document.body.style.msUserSelect = this.userSelectValue;
                document.body.style.webkitUserSelect = this.userSelectValue;
            } );

            this.eventNames.filter( name => name !== 'mouseup' ).forEach( name => {
                this.maskingElement.addEventListener( name, ( event ) => {
98
                    this._pan(event as MouseEvent);
99
100
                } )
            } )
101
        } else if ( mouseEvent.type === 'wheel' || mouseEvent.type === 'dblclick' ) {
102
            this._pan( mouseEvent);
103
104
105
        }

    }
106
    _pan(mouseEvent: MouseEvent ): void {
107
        const me = this;
David Anderson's avatar
David Anderson committed
108
        let xy: { x: number, y: number } | undefined;
109
110
111
112
113
114
115
        switch ( mouseEvent.type ) {
            case 'mouseup':
                me.mousedown = false;
                me.preventDefault = false;
                break;
            case 'mousemove':
                if ( me.mousedown ) {
David Anderson's avatar
David Anderson committed
116
                    xy = { x: mouseEvent.clientX - me.lastPosition.x, y: mouseEvent.clientY - me.lastPosition.y };
117
                    me.canvas.pan( xy.x, xy.y );
118
119
120
121
122
123
124
125
126
127
128
                    me.lastPosition = { x: mouseEvent.clientX, y: mouseEvent.clientY };
                }
                me.preventDefault = false;
                break;
            case 'mousedown':
                me.lastPosition = { x: mouseEvent.clientX, y: mouseEvent.clientY };
                me.mousedown = true;
                me.preventDefault = false;
                break;
            case 'wheel':
                let wheel: WheelEvent = mouseEvent as WheelEvent;
David Anderson's avatar
David Anderson committed
129
                xy = { x: -wheel.deltaX, y: -wheel.deltaY };
130
131
                me.preventDefault = true;
                break;
132
133
134
135
136
137
138
139
140
141
142
143

            // dblclick does not ever fire now that we have the drop over element
            // case 'dblclick':
            //     const gcbb = me.canvas._panClickSpace;
            //     if ( !( mouseEvent.clientX < gcbb.x0 || mouseEvent.clientX > gcbb.x1 ||
            //         mouseEvent.clientY < gcbb.y0 || mouseEvent.clientY > gcbb.y1 ) ) {
            //         const xa: number = ( gcbb.x1 + gcbb.x0 ) / 2;
            //         const ya: number = ( gcbb.y1 + gcbb.y0 ) / 2;

            //         xy = { x: xa - mouseEvent.clientX, y: ya - mouseEvent.clientY };
            //     }
            //     break;
144
        }
145

David Anderson's avatar
David Anderson committed
146
        if ( xy ) {
147
            me.canvas.pan( xy.x, xy.y);
David Anderson's avatar
David Anderson committed
148
        }
149
150
    }
}