@@ -12,7 +12,7 @@ import { DOMUtils, showErrorMessage } from '@jupyterlab/apputils';
1212
1313import { JupyterFrontEnd } from '@jupyterlab/application' ;
1414
15- import { Contents , ContentsManager } from '@jupyterlab/services' ;
15+ import { Contents } from '@jupyterlab/services' ;
1616
1717import { DocumentRegistry } from '@jupyterlab/docregistry' ;
1818
@@ -85,21 +85,6 @@ export namespace DirTreeListing {
8585 }
8686}
8787
88- /**
89- * The namespace for the `FilterFileTreeBrowserModel` class statics.
90- */
91- export namespace FilterFileTreeBrowserModel {
92- /**
93- * An options object for initializing a file tree listing widget.
94- */
95- export interface IOptions extends FilterFileBrowserModel . IOptions {
96- /**
97- * The JupyterFrontEnd app.
98- */
99- app : JupyterFrontEnd ;
100- }
101- }
102-
10388/**
10489 * The namespace for the `FileTreeBrowser` class statics.
10590 */
@@ -161,7 +146,7 @@ export class FileTreeRenderer extends DirListing.Renderer {
161146 ) : void {
162147 super . updateItemNode ( node , model , fileType , translator , hiddenColumns ) ;
163148
164- if ( model . type === 'directory' && this . model . isOpen ( model ) ) {
149+ if ( model . type === 'directory' && this . model . isOpen ( model . path ) ) {
165150 const iconContainer = DOMUtils . findElement (
166151 node ,
167152 'jp-DirListing-itemIcon'
@@ -222,26 +207,6 @@ export class DirTreeListing extends DirListing {
222207 return this . _model ;
223208 }
224209
225- protected async handleFileSelect ( event : MouseEvent ) : Promise < void > {
226- super . handleFileSelect ( event ) ;
227- const entry = this . modelForClick ( event ) ;
228-
229- if ( entry ) {
230- if ( entry . type === 'directory' ) {
231- this . model . path = '/' + entry . path ;
232-
233- if (
234- this . _singleClickToUnfold &&
235- Object . keys ( this . selection ) . length === 1
236- ) {
237- this . model . toggle ( entry . path ) ;
238- }
239- } else {
240- this . model . path = '/' + PathExt . dirname ( entry . path ) ;
241- }
242- }
243- }
244-
245210 private async _eventDblClick ( event : MouseEvent ) : Promise < void > {
246211 const entry = this . modelForClick ( event ) ;
247212
@@ -389,24 +354,56 @@ export class DirTreeListing extends DirListing {
389354 case 'lm-drop' :
390355 this . _eventDrop ( event as IDragEvent ) ;
391356 break ;
357+ case 'mousedown' :
358+ super . handleEvent ( event ) ;
359+ this . _changeModelPath ( event as MouseEvent ) ;
360+ break ;
392361 default :
393362 super . handleEvent ( event ) ;
394363 break ;
395364 }
396365 }
397366
367+ /**
368+ * Change the model path on each 'mousedown' event
369+ *
370+ * Note: This allow to change the path to the root when the user
371+ * is clicking on an empty space.
372+ */
373+ private _changeModelPath ( event : MouseEvent ) : void {
374+ const entry = this . modelForClick ( event ) ;
375+
376+ if ( entry ) {
377+ if ( entry . type === 'directory' ) {
378+ this . model . path = '/' + entry . path ;
379+
380+ if (
381+ this . _singleClickToUnfold &&
382+ ( event . button === 0 || // State toggled on main button
383+ ( event . button === 2 && ! this . model . isOpen ( entry . path ) ) || // State toggled on right click if folder is closed
384+ event . type === 'click' ) // State toggled on click and double click
385+ ) {
386+ this . model . toggle ( entry . path ) ;
387+ }
388+ } else {
389+ this . model . path = '/' + PathExt . dirname ( entry . path ) ;
390+ }
391+ } else {
392+ this . model . path = this . model . rootPath ;
393+ }
394+ }
395+
398396 private _singleClickToUnfold = true ;
399397}
400398
401399/**
402400 * Filetree browser model with optional filter on element.
403401 */
404402export class FilterFileTreeBrowserModel extends FilterFileBrowserModel {
405- constructor ( options : FilterFileTreeBrowserModel . IOptions ) {
403+ constructor ( options : FilterFileBrowserModel . IOptions ) {
406404 super ( options ) ;
407405
408- this . app = options . app ;
409- this . contentManager = this . app . serviceManager . contents ;
406+ this . contentManager = this . manager . services . contents ;
410407 this . basePath = '.' ;
411408
412409 this . _savedState = options . state || null ;
@@ -523,15 +520,15 @@ export class FilterFileTreeBrowserModel extends FilterFileBrowserModel {
523520 }
524521
525522 /**
526- * Check whether a directory entry is open or not.
523+ * Check whether a directory path is opened or not.
527524 *
528- * @param model - The given entry.
525+ * @param path - The given path
529526 *
530- * @returns Whether the directory is open or not.
527+ * @returns Whether the directory is opened or not.
531528 *
532529 */
533- isOpen ( model : Contents . IModel ) : boolean {
534- return ! ! this . openState [ model . path ] ;
530+ isOpen ( path : string ) : boolean {
531+ return ! ! this . openState [ path ] ;
535532 }
536533
537534 private async fetchContent (
@@ -555,7 +552,7 @@ export class FilterFileTreeBrowserModel extends FilterFileBrowserModel {
555552
556553 const isOpen =
557554 ( pathToUpdate && pathToUpdate . startsWith ( '/' + entry . path ) ) ||
558- this . isOpen ( entry ) ;
555+ this . isOpen ( entry . path ) ;
559556
560557 if ( isOpen ) {
561558 const subEntryContent = await this . fetchContent (
@@ -602,8 +599,7 @@ export class FilterFileTreeBrowserModel extends FilterFileBrowserModel {
602599 private _stateKey : string | null = null ;
603600 private _path = '.' ;
604601 private basePath : string ;
605- private contentManager : ContentsManager ;
606- private app : JupyterFrontEnd ;
602+ private contentManager : Contents . IManager ;
607603 private openState : { [ path : string ] : boolean } = { } ;
608604}
609605
0 commit comments