import * as _ from 'underscore' ; import * as d3 from 'd3' ; import { LinearScale , OrdinalScale } from 'bqscales' ; import { BaseXSelector } from './Selector' ; import * as sel_utils from './selector_utils' ; import { applyAttrs } from './utils' ; export class FastIntervalSelector extends BaseXSelector { render ( ) { super . render ( ) ; this . freeze_but_move = true ; this . freeze_dont_move = false ; this . active = false ; this . dirty = false ; this . size = this . model . get ( 'size' ) ; this . width = this . parent . width - this . parent . margin . left - this . parent . margin . right ; this . height = this . parent . height - this . parent . margin . top - this . parent . margin . bottom ; const that = this ; const scale_creation_promise = this . create_scales ( ) ; Promise . all ( [ this . mark_views_promise , scale_creation_promise ] ) . then ( ( ) => { that . background = that . d3el . append ( 'rect' ) . attr ( 'x' , 0 ) . attr ( 'y' , 0 ) . attr ( 'width' , that . width ) . attr ( 'height' , that . height ) . attr ( 'class' , 'selector selectormouse' ) . attr ( 'pointer-events' , 'all' ) . attr ( 'visibility' , 'hidden' ) ; that . background . on ( 'mousemove' , _ . bind ( that . mousemove , that ) ) . on ( 'click' , _ . bind ( that . click , that ) ) . on ( 'dblclick' , _ . bind ( that . dblclick , that ) ) ; that . rect = that . d3el . append ( 'rect' ) . attr ( 'class' , 'selector intsel' ) . attr ( 'x' , 0 ) . attr ( 'y' , 0 ) . attr ( 'width' , that . size ) . attr ( 'height' , that . height ) . attr ( 'pointer-events' , 'none' ) . attr ( 'display' , 'none' ) ; that . color_change ( ) ; that . selected_changed ( ) ; that . create_listeners ( ) ; } ) ; } create_listeners ( ) { super . create_listeners ( ) ; this . listenTo ( this . model , 'change:color' , this . color_change ) ; } color_change ( ) { if ( this . model . get ( 'color' ) !== null ) { this . rect . style ( 'fill' , this . model . get ( 'color' ) ) ; } } click ( ) { this . active = true ; this . rect . style ( 'display' , 'inline' ) ; this . freeze_but_move = this . model . get ( 'size' ) ? true : ! this . freeze_but_move ; } dblclick ( ) { this . freeze_dont_move = ! this . freeze_dont_move ; } mousemove ( ) { if ( this . freeze_dont_move || ! this . active ) { return ; } this . dirty = true ; const mouse_pos = d3 . mouse ( this . background . node ( ) ) ; const int_len = this . size > 0 ? this . size : parseInt ( this . rect . attr ( 'width' ) ) ; const vert_factor = ( this . height - mouse_pos [ 1 ] ) / this . height ; const interval_size = this . freeze_but_move ? int_len : Math . round ( vert_factor * this . width ) ; let start ; if ( mouse_pos [ 0 ] - interval_size / 2 < 0 ) { start = 0 ; } else if ( mouse_pos [ 0 ] + interval_size / 2 > this . width ) { start = this . width - interval_size ; } else { start = mouse_pos [ 0 ] - interval_size / 2 ; } this . rect . attr ( 'x' , start ) ; this . rect . attr ( 'width' , interval_size ) ; const pixel_extent = [ start , start + interval_size ] ; this . set_selected ( 'selected' , this . scale . invertRange ( pixel_extent ) ) ; this . update_mark_selected ( pixel_extent , undefined ) ; this . touch ( ) ; this . dirty = false ; } update_mark_selected ( extent_x , extent_y ) { if ( extent_x === undefined || extent_x . length === 0 ) { _ . each ( this . mark_views , ( mark_view ) => { return mark_view . selector_changed ( ) ; } ) ; return ; } let x , y ; if ( extent_y === undefined ) { const orient = this . model . get ( 'orientation' ) ; ( x = orient == 'vertical' ? [ ] : extent_x ) , ( y = orient == 'vertical' ? extent_x : [ ] ) ; } else { ( x = extent_x ) , ( y = extent_y ) ; } const point_selector = function ( p ) { return sel_utils . point_in_rectangle ( p , x , y ) ; } ; const rect_selector = function ( xy ) { return sel_utils . rect_inter_rect ( xy [ 0 ] , xy [ 1 ] , x , y ) ; } ; _ . each ( this . mark_views , ( mark_view ) => { mark_view . selector_changed ( point_selector , rect_selector ) ; } , this ) ; } relayout ( ) { super . relayout ( ) ; this . adjust_rectangle ( ) ; this . background . attr ( 'width' , this . width ) . attr ( 'height' , this . height ) ; this . set_range ( [ this . scale ] ) ; } reset ( ) { this . rect . attr ( 'x' , 0 ) . attr ( 'width' , 0 ) ; this . model . set ( 'selected' , null ) ; this . update_mark_selected ( undefined , undefined ) ; this . touch ( ) ; } update_scale_domain ( ignore_gui_update ) { super . update_scale_domain ( ) ; if ( ignore_gui_update !== true ) { this . selected_changed ( ) ; } } selected_changed ( ) { if ( this . dirty ) { return ; } const selected = this . model . get ( 'selected' ) || [ ] ; if ( selected . length === 0 ) { this . reset ( ) ; } else if ( selected . length != 2 ) { return ; } else { let pixels = selected . map ( this . scale . scale ) ; pixels = pixels . sort ( ( a , b ) => { return a - b ; } ) ; applyAttrs ( this . rect , { x : pixels [ 0 ] , width : pixels [ 1 ] - pixels [ 0 ] , } ) . style ( 'display' , 'inline' ) ; this . active = true ; this . update_mark_selected ( pixels , undefined ) ; } } adjust_rectangle ( ) { if ( this . model . get ( 'orientation' ) == 'vertical' ) { this . d3el . selectAll ( 'rect' ) . attr ( 'x' , 0 ) . attr ( 'width' , this . width ) ; } else { this . d3el . selectAll ( 'rect' ) . attr ( 'y' , 0 ) . attr ( 'height' , this . height ) ; } } freeze_but_move : boolean ; freeze_dont_move : boolean ; active : boolean ; dirty : boolean ; size : number ; background : d3 . Selection < SVGRectElement , any , any , any > ; rect : d3 . Selection < SVGRectElement , any , any , any > ; scale : LinearScale | OrdinalScale ; }