import * as d3 from 'd3' ; import { LinearScale , OrdinalScale } from 'bqscales' ; import { Interaction } from './Interaction' ; const convert_dates = require ( './utils' ) . convert_dates ; import { WidgetView } from '@jupyter-widgets/base' ; export abstract class BaseSelector extends Interaction { initialize ( parameters ) { this . setElement ( document . createElementNS ( d3 . namespaces . svg , 'g' ) as HTMLElement ) ; this . d3el = d3 . select ( this . el ) ; WidgetView . prototype . initialize . call ( this , parameters ) ; } render ( ) { this . parent = this . options . parent ; 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 ; this . mark_views_promise = this . populate_mark_views ( ) ; } create_listeners ( ) { this . listenTo ( this . parent , 'margin_updated' , this . relayout ) ; this . listenTo ( this . model , 'change:selected' , this . selected_changed ) ; this . listenTo ( this . model , 'change:marks' , this . marks_changed ) ; this . listenTo ( this . model , 'msg:custom' , this . handle_custom_messages ) ; } relayout ( ) { this . height = this . parent . height - this . parent . margin . top - this . parent . margin . bottom ; this . width = this . parent . width - this . parent . margin . left - this . parent . margin . right ; } async populate_mark_views ( ) { const fig = this . parent ; const mark_ids = this . model . get ( 'marks' ) . map ( ( m ) => m . model_id ) ; const views = await Promise . all ( fig . mark_views . views ) ; const fig_mark_ids = fig . mark_views . _models . map ( ( m ) => m . model_id ) ; const mark_indices = mark_ids . map ( ( mid ) => fig_mark_ids . indexOf ( mid ) ) ; this . mark_views = mark_indices . map ( ( elem ) => views [ elem ] ) ; } marks_changed ( ) { this . populate_mark_views ( ) . then ( ( ) => this . selected_changed ( ) ) ; } handle_custom_messages ( msg ) { if ( msg . type === 'reset' ) { this . reset ( ) ; } } abstract reset ( ) ; abstract selected_changed ( ) ; abstract create_scales ( ) ; set_selected ( name , value ) { this . model . set ( name , convert_dates ( value ) ) ; } width : number ; height : number ; mark_views_promise : Promise < void > ; } export abstract class BaseXSelector extends BaseSelector { create_scales ( ) { if ( this . scale ) { this . scale . remove ( ) ; } if ( this . model . get ( 'scale' ) ) { const that = this ; return this . create_child_view ( this . model . get ( 'scale' ) ) . then ( ( view ) => { that . scale = view as WidgetView as LinearScale ; that . update_scale_domain ( true ) ; that . set_range ( [ that . scale ] ) ; that . scale . on ( 'domain_changed' , that . update_scale_domain , that ) ; return view ; } ) ; } } update_scale_domain ( ignore_gui_update = false ) { const xy = this . model . get ( 'orientation' ) == 'vertical' ? 'y' : 'x' ; const initial_range = this . parent . padded_range ( xy , this . scale . model ) ; const target_range = this . parent . range ( xy ) ; this . scale . expandDomain ( initial_range , target_range ) ; } set_range ( array ) { const xy = this . model . get ( 'orientation' ) == 'vertical' ? 'y' : 'x' ; for ( let iter = 0 ; iter < array . length ; iter ++ ) { array [ iter ] . setRange ( this . parent . range ( xy ) ) ; } } scale : LinearScale | OrdinalScale ; } export abstract class BaseXYSelector extends BaseSelector { create_scales ( ) { const that = this ; if ( this . x_scale ) { this . x_scale . remove ( ) ; } if ( this . y_scale ) { this . y_scale . remove ( ) ; } const scale_promises = [ ] ; if ( this . model . get ( 'x_scale' ) ) { scale_promises . push ( this . create_child_view ( this . model . get ( 'x_scale' ) ) . then ( ( view ) => { that . x_scale = view ; that . update_xscale_domain ( ) ; that . set_x_range ( [ that . x_scale ] ) ; that . x_scale . on ( 'domain_changed' , that . update_xscale_domain , that ) ; return view ; } ) ) ; } if ( this . model . get ( 'y_scale' ) ) { scale_promises . push ( this . create_child_view ( this . model . get ( 'y_scale' ) ) . then ( ( view ) => { that . y_scale = view ; that . update_yscale_domain ( ) ; that . set_y_range ( [ that . y_scale ] ) ; that . y_scale . on ( 'domain_changed' , that . update_yscale_domain , that ) ; return view ; } ) ) ; } return Promise . all ( scale_promises ) ; } set_x_range ( array ) { for ( let iter = 0 ; iter < array . length ; iter ++ ) { array [ iter ] . setRange ( this . parent . range ( 'x' ) ) ; } } set_y_range ( array ) { for ( let iter = 0 ; iter < array . length ; iter ++ ) { array [ iter ] . setRange ( this . parent . range ( 'y' ) ) ; } } update_xscale_domain ( ) { const initial_range = this . parent . padded_range ( 'x' , this . x_scale . model ) ; const target_range = this . parent . range ( 'x' ) ; this . x_scale . expandDomain ( initial_range , target_range ) ; } update_yscale_domain ( ) { const initial_range = this . parent . padded_range ( 'y' , this . y_scale . model ) ; const target_range = this . parent . range ( 'y' ) ; this . y_scale . expandDomain ( initial_range , target_range ) ; } x_scale : LinearScale | OrdinalScale ; y_scale : LinearScale | OrdinalScale ; }