@@ -35,16 +35,24 @@ import { ITranslator } from '@jupyterlab/translation';
35
35
import {
36
36
NotebookApp ,
37
37
NotebookShell ,
38
- INotebookShell
38
+ INotebookShell ,
39
+ SideBarPanel ,
40
+ SideBarHandler
39
41
} from '@jupyter-notebook/application' ;
40
42
41
43
import { jupyterIcon } from '@jupyter-notebook/ui-components' ;
42
44
43
45
import { PromiseDelegate } from '@lumino/coreutils' ;
44
46
45
- import { DisposableDelegate , DisposableSet } from '@lumino/disposable' ;
47
+ import {
48
+ DisposableDelegate ,
49
+ DisposableSet ,
50
+ IDisposable
51
+ } from '@lumino/disposable' ;
52
+
53
+ import { Menu , Widget } from '@lumino/widgets' ;
46
54
47
- import { Widget } from '@lumino/widgets ' ;
55
+ import { SideBarPalette } from './sidebarpalette ' ;
48
56
49
57
/**
50
58
* A regular expression to match path to notebooks and documents
@@ -65,6 +73,11 @@ namespace CommandIDs {
65
73
*/
66
74
export const toggleTop = 'application:toggle-top' ;
67
75
76
+ /**
77
+ * Toggle sidebar visibility
78
+ */
79
+ export const togglePanel = 'application:toggle-panel' ;
80
+
68
81
/**
69
82
* Toggle the Zen mode
70
83
*/
@@ -173,7 +186,7 @@ const opener: JupyterFrontEndPlugin<void> = {
173
186
const file = decodeURIComponent ( path ) ;
174
187
const urlParams = new URLSearchParams ( parsed . search ) ;
175
188
const factory = urlParams . get ( 'factory' ) ?? 'default' ;
176
- app . restored . then ( async ( ) => {
189
+ app . started . then ( async ( ) => {
177
190
docManager . open ( file , factory , undefined , {
178
191
ref : '_noref'
179
192
} ) ;
@@ -187,8 +200,6 @@ const opener: JupyterFrontEndPlugin<void> = {
187
200
188
201
/**
189
202
* A plugin to customize menus
190
- *
191
- * TODO: use this plugin to customize the menu items and their order
192
203
*/
193
204
const menus : JupyterFrontEndPlugin < void > = {
194
205
id : '@jupyter-notebook/application-extension:menus' ,
@@ -557,6 +568,219 @@ const topVisibility: JupyterFrontEndPlugin<void> = {
557
568
autoStart : true
558
569
} ;
559
570
571
+ /**
572
+ * Plugin to toggle the left or right sidebar's visibility.
573
+ */
574
+ const sidebarVisibility : JupyterFrontEndPlugin < void > = {
575
+ id : '@jupyter-notebook/application-extension:sidebar' ,
576
+ requires : [ INotebookShell , ITranslator ] ,
577
+ optional : [ IMainMenu , ICommandPalette ] ,
578
+ autoStart : true ,
579
+ activate : (
580
+ app : JupyterFrontEnd < JupyterFrontEnd . IShell > ,
581
+ notebookShell : INotebookShell ,
582
+ translator : ITranslator ,
583
+ menu : IMainMenu | null ,
584
+ palette : ICommandPalette | null
585
+ ) => {
586
+ const trans = translator . load ( 'notebook' ) ;
587
+
588
+ /* Arguments for togglePanel command:
589
+ * side, left or right area
590
+ * title, widget title to show in the menu
591
+ * id, widget ID to activate in the sidebar
592
+ */
593
+ app . commands . addCommand ( CommandIDs . togglePanel , {
594
+ label : args => args [ 'title' ] as string ,
595
+ caption : args => {
596
+ // We do not substitute the parameter into the string because the parameter is not
597
+ // localized (e.g., it is always 'left') even though the string is localized.
598
+ if ( args [ 'side' ] === 'left' ) {
599
+ return trans . __ (
600
+ 'Show %1 in the left sidebar' ,
601
+ args [ 'title' ] as string
602
+ ) ;
603
+ } else if ( args [ 'side' ] === 'right' ) {
604
+ return trans . __ (
605
+ 'Show %1 in the right sidebar' ,
606
+ args [ 'title' ] as string
607
+ ) ;
608
+ }
609
+ return trans . __ ( 'Show %1 in the sidebar' , args [ 'title' ] as string ) ;
610
+ } ,
611
+ execute : args => {
612
+ switch ( args [ 'side' ] as string ) {
613
+ case 'left' :
614
+ if ( notebookShell . leftCollapsed ) {
615
+ notebookShell . expandLeft ( args . id as string ) ;
616
+ } else if (
617
+ notebookShell . leftHandler . currentWidget ?. id !== args . id
618
+ ) {
619
+ notebookShell . expandLeft ( args . id as string ) ;
620
+ } else {
621
+ notebookShell . collapseLeft ( ) ;
622
+ if ( notebookShell . currentWidget ) {
623
+ notebookShell . activateById ( notebookShell . currentWidget . id ) ;
624
+ }
625
+ }
626
+ break ;
627
+ case 'right' :
628
+ if ( notebookShell . rightCollapsed ) {
629
+ notebookShell . expandRight ( args . id as string ) ;
630
+ } else if (
631
+ notebookShell . rightHandler . currentWidget ?. id !== args . id
632
+ ) {
633
+ notebookShell . expandRight ( args . id as string ) ;
634
+ } else {
635
+ notebookShell . collapseRight ( ) ;
636
+ if ( notebookShell . currentWidget ) {
637
+ notebookShell . activateById ( notebookShell . currentWidget . id ) ;
638
+ }
639
+ }
640
+ break ;
641
+ }
642
+ } ,
643
+ isToggled : args => {
644
+ switch ( args [ 'side' ] as string ) {
645
+ case 'left' : {
646
+ if ( notebookShell . leftCollapsed ) {
647
+ return false ;
648
+ }
649
+ const currentWidget = notebookShell . leftHandler . currentWidget ;
650
+ if ( ! currentWidget ) {
651
+ return false ;
652
+ }
653
+
654
+ return currentWidget . id === ( args [ 'id' ] as string ) ;
655
+ }
656
+ case 'right' : {
657
+ if ( notebookShell . rightCollapsed ) {
658
+ return false ;
659
+ }
660
+ const currentWidget = notebookShell . rightHandler . currentWidget ;
661
+ if ( ! currentWidget ) {
662
+ return false ;
663
+ }
664
+
665
+ return currentWidget . id === ( args [ 'id' ] as string ) ;
666
+ }
667
+ }
668
+ return false ;
669
+ }
670
+ } ) ;
671
+
672
+ const sideBarMenu : { [ area in SideBarPanel . Area ] : IDisposable | null } = {
673
+ left : null ,
674
+ right : null
675
+ } ;
676
+
677
+ /**
678
+ * The function which adds entries to the View menu for each widget of a sidebar.
679
+ *
680
+ * @param area - 'left' or 'right', the area of the side bar.
681
+ * @param entryLabel - the name of the main entry in the View menu for that sidebar.
682
+ * @returns - The disposable menu added to the View menu or null.
683
+ */
684
+ const updateMenu = ( area : SideBarPanel . Area , entryLabel : string ) => {
685
+ if ( menu === null ) {
686
+ return null ;
687
+ }
688
+
689
+ // Remove the previous menu entry for this sidebar.
690
+ sideBarMenu [ area ] ?. dispose ( ) ;
691
+
692
+ // Creates a new menu entry and populates it with sidebar widgets.
693
+ const newMenu = new Menu ( { commands : app . commands } ) ;
694
+ newMenu . title . label = entryLabel ;
695
+ const widgets = notebookShell . widgets ( area ) ;
696
+ let menuToAdd = false ;
697
+
698
+ for ( let widget of widgets ) {
699
+ newMenu . addItem ( {
700
+ command : CommandIDs . togglePanel ,
701
+ args : {
702
+ side : area ,
703
+ title : `Show ${ widget . title . caption } ` ,
704
+ id : widget . id
705
+ }
706
+ } ) ;
707
+ menuToAdd = true ;
708
+ }
709
+
710
+ // If there are widgets, add the menu to the main menu entry.
711
+ if ( menuToAdd ) {
712
+ sideBarMenu [ area ] = menu . viewMenu . addItem ( {
713
+ type : 'submenu' ,
714
+ submenu : newMenu
715
+ } ) ;
716
+ }
717
+ } ;
718
+
719
+ app . restored . then ( ( ) => {
720
+ // Create menu entries for the left and right panel.
721
+ if ( menu ) {
722
+ const getSideBarLabel = ( area : SideBarPanel . Area ) : string => {
723
+ if ( area === 'left' ) {
724
+ return trans . __ ( `Left Sidebar` ) ;
725
+ } else {
726
+ return trans . __ ( `Right Sidebar` ) ;
727
+ }
728
+ } ;
729
+ const leftArea = notebookShell . leftHandler . area ;
730
+ const leftLabel = getSideBarLabel ( leftArea ) ;
731
+ updateMenu ( leftArea , leftLabel ) ;
732
+
733
+ const rightArea = notebookShell . rightHandler . area ;
734
+ const rightLabel = getSideBarLabel ( rightArea ) ;
735
+ updateMenu ( rightArea , rightLabel ) ;
736
+
737
+ const handleSideBarChange = (
738
+ sidebar : SideBarHandler ,
739
+ widget : Widget
740
+ ) => {
741
+ const label = getSideBarLabel ( sidebar . area ) ;
742
+ updateMenu ( sidebar . area , label ) ;
743
+ } ;
744
+
745
+ notebookShell . leftHandler . widgetAdded . connect ( handleSideBarChange ) ;
746
+ notebookShell . leftHandler . widgetRemoved . connect ( handleSideBarChange ) ;
747
+ notebookShell . rightHandler . widgetAdded . connect ( handleSideBarChange ) ;
748
+ notebookShell . rightHandler . widgetRemoved . connect ( handleSideBarChange ) ;
749
+ }
750
+
751
+ // Add palette entries for side panels.
752
+ if ( palette ) {
753
+ const sideBarPalette = new SideBarPalette ( {
754
+ commandPalette : palette as ICommandPalette ,
755
+ command : CommandIDs . togglePanel
756
+ } ) ;
757
+
758
+ notebookShell . leftHandler . widgets . forEach ( widget => {
759
+ sideBarPalette . addItem ( widget , notebookShell . leftHandler . area ) ;
760
+ } ) ;
761
+
762
+ notebookShell . rightHandler . widgets . forEach ( widget => {
763
+ sideBarPalette . addItem ( widget , notebookShell . rightHandler . area ) ;
764
+ } ) ;
765
+
766
+ // Update menu and palette when widgets are added or removed from sidebars.
767
+ notebookShell . leftHandler . widgetAdded . connect ( ( sidebar , widget ) => {
768
+ sideBarPalette . addItem ( widget , sidebar . area ) ;
769
+ } ) ;
770
+ notebookShell . leftHandler . widgetRemoved . connect ( ( sidebar , widget ) => {
771
+ sideBarPalette . removeItem ( widget , sidebar . area ) ;
772
+ } ) ;
773
+ notebookShell . rightHandler . widgetAdded . connect ( ( sidebar , widget ) => {
774
+ sideBarPalette . addItem ( widget , sidebar . area ) ;
775
+ } ) ;
776
+ notebookShell . rightHandler . widgetRemoved . connect ( ( sidebar , widget ) => {
777
+ sideBarPalette . removeItem ( widget , sidebar . area ) ;
778
+ } ) ;
779
+ }
780
+ } ) ;
781
+ }
782
+ } ;
783
+
560
784
/**
561
785
* The default tree route resolver plugin.
562
786
*/
@@ -711,6 +935,7 @@ const plugins: JupyterFrontEndPlugin<any>[] = [
711
935
router ,
712
936
sessionDialogs ,
713
937
shell ,
938
+ sidebarVisibility ,
714
939
status ,
715
940
tabTitle ,
716
941
title ,
0 commit comments