import { DOMWidgetView , ViewList , JupyterLuminoWidget , WidgetModel , reject , WidgetView , } from '@jupyter-widgets/base' ; import { BoxModel , BoxView } from './widget_box' ; import { TabBar } from '@lumino/widgets' ; import { TabPanel } from './lumino/tabpanel' ; import { Accordion } from './lumino/accordion' ; import { Widget } from '@lumino/widgets' ; import { each , ArrayExt } from '@lumino/algorithm' ; import { Message , MessageLoop } from '@lumino/messaging' ; import $ from 'jquery' ; export class SelectionContainerModel extends BoxModel { defaults ( ) { return { ... super . defaults ( ) , _model_name : 'SelectionContainerModel' , selected_index : null , titles : [ ] , } ; } } export class AccordionModel extends SelectionContainerModel { defaults ( ) { return { ... super . defaults ( ) , _model_name : 'AccordionModel' , _view_name : 'AccordionView' , } ; } } export class JupyterLuminoAccordionWidget extends Accordion { constructor ( options ) { const view = options . view ; delete ( options as any ) . view ; super ( options ) ; this . _view = view ; } /**
     * Process the Lumino message.
     *
     * Any custom Lumino widget used inside a Jupyter widget should override
     * the processMessage function like this.
     */ processMessage ( msg ) { super . processMessage ( msg ) ; this . _view ?. processLuminoMessage ( msg ) ; } /**
     * Dispose the widget.
     *
     * This causes the view to be destroyed as well with 'remove'
     */ dispose ( ) { if ( this . isDisposed ) { return ; } super . dispose ( ) ; this . _view . remove ( ) ; this . _view = null ! ; } private _view : DOMWidgetView ; } export class AccordionView extends DOMWidgetView { _createElement ( tagName ) { this . luminoWidget = new JupyterLuminoAccordionWidget ( { view : this } ) ; return this . luminoWidget . node ; } _setElement ( el ) { if ( this . el || el !== this . luminoWidget . node ) { throw new Error ( 'Cannot reset the DOM element.' ) ; } this . el = this . luminoWidget . node ; this . $el = $ ( this . luminoWidget . node ) ; } initialize ( parameters ) { super . initialize ( parameters ) ; this . children_views = new ViewList ( this . add_child_view , this . remove_child_view , this ) ; this . listenTo ( this . model , 'change:children' , ( ) => this . updateChildren ( ) ) ; this . listenTo ( this . model , 'change:selected_index' , ( ) => this . update_selected_index ( ) ) ; this . listenTo ( this . model , 'change:titles' , ( ) => this . update_titles ( ) ) ; } /**
     * Called when view is rendered.
     */ render ( ) { super . render ( ) ; const accordion = this . luminoWidget ; accordion . addClass ( 'jupyter-widgets' ) ; accordion . addClass ( 'widget-accordion' ) ; accordion . addClass ( 'widget-container' ) ; accordion . selection . selectionChanged . connect ( ( sender ) => { if ( ! this . updatingChildren ) { this . model . set ( 'selected_index' , accordion . selection . index ) ; this . touch ( ) ; } } ) ; this . children_views ?. update ( this . model . get ( 'children' ) ) ; this . update_titles ( ) ; this . update_selected_index ( ) ; } /**
     * Update children
     */ updateChildren ( ) { this . updatingChildren = true ; this . luminoWidget . selection . index = null ; this . children_views ?. update ( this . model . get ( 'children' ) ) ; this . update_selected_index ( ) ; this . updatingChildren = false ; } /**
     * Set header titles
     */ update_titles ( ) { const collapsed = this . luminoWidget . collapseWidgets ; const titles = this . model . get ( 'titles' ) ; for ( let i = 0 ; i < collapsed . length ; i ++ ) { if ( titles [ i ] !== void 0 ) { collapsed [ i ] . widget . title . label = titles [ i ] ; } } } /**
     * Make the rendering and selected index consistent.
     */ update_selected_index ( ) { this . luminoWidget . selection . index = this . model . get ( 'selected_index' ) ; } /**
     * Called when a child is removed from children list.
     */ remove_child_view ( view ) { this . luminoWidget . removeWidget ( view . luminoWidget ) ; view . remove ( ) ; } /**
     * Called when a child is added to children list.
     */ add_child_view ( model , index ) { const accordion = this . luminoWidget ; const placeholder = new Widget ( ) ; placeholder . title . label = this . model . get ( 'titles' ) [ index ] || '' ; accordion . addWidget ( placeholder ) ; return this . create_child_view ( model ) . then ( ( view ) => { const widget = view . luminoWidget ; widget . title . label = placeholder . title . label ; const collapse = accordion . collapseWidgets [ accordion . indexOf ( placeholder ) ] ; collapse . widget = widget ; placeholder . dispose ( ) ; return view ; } ) . catch ( reject ( 'Could not add child view to box' , true ) ) ; } remove ( ) { this . children_views = null ; super . remove ( ) ; } children_views : ViewList < DOMWidgetView > | null ; luminoWidget : Accordion ; updatingChildren : boolean ; } export class TabModel extends SelectionContainerModel { defaults ( ) { return { ... super . defaults ( ) , _model_name : 'TabModel' , _view_name : 'TabView' , } ; } } export class JupyterLuminoTabPanelWidget extends TabPanel { constructor ( options ) { const view = options . view ; delete ( options as any ) . view ; super ( options ) ; this . _view = view ; MessageLoop . installMessageHook ( this . tabContents , ( handler , msg ) => { this . _view . processLuminoMessage ( msg ) ; return true ; } ) ; } /**
     * Dispose the widget.
     *
     * This causes the view to be destroyed as well with 'remove'
     */ dispose ( ) { if ( this . isDisposed ) { return ; } super . dispose ( ) ; this . _view . remove ( ) ; this . _view = null ! ; } private _view : DOMWidgetView ; } export class TabView extends DOMWidgetView { _createElement ( tagName ) { this . luminoWidget = new JupyterLuminoTabPanelWidget ( { view : this , } ) ; return this . luminoWidget . node ; } _setElement ( el ) { if ( this . el || el !== this . luminoWidget . node ) { throw new Error ( 'Cannot reset the DOM element.' ) ; } this . el = this . luminoWidget . node ; this . $el = $ ( this . luminoWidget . node ) ; } /**
     * Public constructor.
     */ initialize ( parameters ) { super . initialize ( parameters ) ; this . childrenViews = new ViewList ( this . addChildView , ( view ) => { view . remove ( ) ; } , this ) ; this . listenTo ( this . model , 'change:children' , ( ) => this . updateTabs ( ) ) ; this . listenTo ( this . model , 'change:titles' , ( ) => this . updateTitles ( ) ) ; } /**
     * Called when view is rendered.
     */ render ( ) { super . render ( ) ; const tabs = this . luminoWidget ; tabs . addClass ( 'jupyter-widgets' ) ; tabs . addClass ( 'widget-container' ) ; tabs . addClass ( 'widget-tab' ) ; tabs . tabsMovable = true ; tabs . tabBar . insertBehavior = 'none' ; tabs . tabBar . currentChanged . connect ( this . _onTabChanged , this ) ; tabs . tabBar . tabMoved . connect ( this . _onTabMoved , this ) ; tabs . tabBar . addClass ( 'widget-tab-bar' ) ; tabs . tabContents . addClass ( 'widget-tab-contents' ) ; tabs . tabBar . tabsMovable = false ; this . updateTabs ( ) ; this . update ( ) ; } /**
     * Render tab views based on the current model's children.
     */ updateTabs ( ) { this . updatingTabs = true ; this . luminoWidget . currentIndex = null ; this . childrenViews ?. update ( this . model . get ( 'children' ) ) ; this . luminoWidget . currentIndex = this . model . get ( 'selected_index' ) ; this . updatingTabs = false ; } /**
     * Called when a child is added to children list.
     */ addChildView ( model , index ) { const label = this . model . get ( 'titles' ) [ index ] || '' ; const tabs = this . luminoWidget ; const placeholder = new Widget ( ) ; placeholder . title . label = label ; tabs . addWidget ( placeholder ) ; return this . create_child_view ( model ) . then ( ( view ) => { const widget = view . luminoWidget ; widget . title . label = placeholder . title . label ; widget . title . closable = false ; const i = ArrayExt . firstIndexOf ( tabs . widgets , placeholder ) ; tabs . insertWidget ( i + 1 , widget ) ; placeholder . dispose ( ) ; return view ; } ) . catch ( reject ( 'Could not add child view to box' , true ) ) ; } /**
     * Update the contents of this view
     *
     * Called when the model is changed.  The model may have been
     * changed by another view or by a state update from the back-end.
     */ update ( ) { this . updateSelectedIndex ( ) ; return super . update ( ) ; } /**
     * Updates the tab page titles.
     */ updateTitles ( ) { const titles = this . model . get ( 'titles' ) || [ ] ; each ( this . luminoWidget . widgets , ( widget , i ) => { widget . title . label = titles [ i ] || '' ; } ) ; } /**
     * Updates the selected index.
     */ updateSelectedIndex ( ) { this . luminoWidget . currentIndex = this . model . get ( 'selected_index' ) ; } remove ( ) { this . childrenViews = null ; super . remove ( ) ; } _onTabChanged ( sender , args ) { if ( ! this . updatingTabs ) { const i = args . currentIndex ; this . model . set ( 'selected_index' , i === - 1 ? null : i ) ; this . touch ( ) ; } } /**
     * Handle the `tabMoved` signal from the tab bar.
     */ _onTabMoved ( sender , args ) { const children = this . model . get ( 'children' ) . slice ( ) ; ArrayExt . move ( children , args . fromIndex , args . toIndex ) ; this . model . set ( 'children' , children ) ; this . touch ( ) ; } updatingTabs = false ; childrenViews : ViewList < DOMWidgetView > | null ; luminoWidget : JupyterLuminoTabPanelWidget ; } export class StackedModel extends SelectionContainerModel { defaults ( ) { return { ... super . defaults ( ) , _model_name : 'StackedModel' , _view_name : 'StackedView' , } ; } } export class StackedView extends BoxView { initialize ( parameters ) { super . initialize ( parameters ) ; this . listenTo ( this . model , 'change:selected_index' , this . update_children ) ; } update_children ( ) { const selected_child = this . model . get ( 'children' ) [ this . model . get ( 'selected_index' ) ] ; this . children_views ?. update ( [ selected_child ] ) . then ( ( views ) => { views . forEach ( ( view ) => { MessageLoop . postMessage ( view . luminoWidget , Widget . ResizeMessage . UnknownSize ) ; } ) ; } ) ; } }