@jupyterlab/filebrowser 4.5.0-alpha.0 → 4.5.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/upload.js CHANGED
@@ -3,6 +3,7 @@
3
3
  import { showErrorMessage } from '@jupyterlab/apputils';
4
4
  import { nullTranslator } from '@jupyterlab/translation';
5
5
  import { fileUploadIcon, ToolbarButton } from '@jupyterlab/ui-components';
6
+ import { Signal } from '@lumino/signaling';
6
7
  /**
7
8
  * A widget which provides an upload button.
8
9
  */
@@ -17,7 +18,8 @@ export class Uploader extends ToolbarButton {
17
18
  onClick: () => {
18
19
  this._input.click();
19
20
  },
20
- tooltip: Private.translateToolTip(options.translator)
21
+ tooltip: Private.translateToolTip(options.translator),
22
+ enabled: options.model.allowFileUploads
21
23
  });
22
24
  /**
23
25
  * The 'change' handler for the input field.
@@ -25,7 +27,12 @@ export class Uploader extends ToolbarButton {
25
27
  this._onInputChanged = () => {
26
28
  const files = Array.prototype.slice.call(this._input.files);
27
29
  const pending = files.map(file => this.fileBrowserModel.upload(file));
28
- void Promise.all(pending).catch(error => {
30
+ void Promise.all(pending)
31
+ .then(models => {
32
+ // emit the batch
33
+ this._filesUploaded.emit(models);
34
+ })
35
+ .catch(error => {
29
36
  void showErrorMessage(this._trans._p('showErrorMessage', 'Upload Error'), error);
30
37
  });
31
38
  };
@@ -38,6 +45,7 @@ export class Uploader extends ToolbarButton {
38
45
  this._input.value = '';
39
46
  };
40
47
  this._input = Private.createUploadInput();
48
+ this._filesUploaded = new Signal(this);
41
49
  this.fileBrowserModel = options.model;
42
50
  this.translator = options.translator || nullTranslator;
43
51
  this._trans = this.translator.load('jupyterlab');
@@ -45,6 +53,12 @@ export class Uploader extends ToolbarButton {
45
53
  this._input.onchange = this._onInputChanged;
46
54
  this.addClass('jp-id-upload');
47
55
  }
56
+ /**
57
+ * A signal emitted with file info when a batch of upload completes.
58
+ */
59
+ get filesUploaded() {
60
+ return this._filesUploaded;
61
+ }
48
62
  }
49
63
  /**
50
64
  * The namespace for module private data.
package/lib/upload.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"upload.js","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,2DAA2D;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAEL,cAAc,EAEf,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG1E;;GAEG;AACH,MAAM,OAAO,QAAS,SAAQ,aAAa;IACzC;;OAEG;IACH,YAAY,OAA0B;QACpC,KAAK,CAAC;YACJ,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,OAAO,EAAE,GAAG,EAAE;gBACZ,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACtB,CAAC;YACD,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAC;SACtD,CAAC,CAAC;QAgBL;;WAEG;QACK,oBAAe,GAAG,GAAG,EAAE;YAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAW,CAAC;YACtE,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACtE,KAAK,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;gBACtC,KAAK,gBAAgB,CACnB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,kBAAkB,EAAE,cAAc,CAAC,EAClD,KAAK,CACN,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEF;;WAEG;QACK,oBAAe,GAAG,GAAG,EAAE;YAC7B,gFAAgF;YAChF,8DAA8D;YAC9D,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;QACzB,CAAC,CAAC;QAIM,WAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;QAxC3C,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC;QACtC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,cAAc,CAAC;QACvD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC;QAC5C,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IAChC,CAAC;CAmCF;AA2BD;;GAEG;AACH,IAAU,OAAO,CAmBhB;AAnBD,WAAU,OAAO;IACf;;OAEG;IACH,SAAgB,iBAAiB;QAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC9C,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC;QACpB,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IALe,yBAAiB,oBAKhC,CAAA;IAED;;OAEG;IACH,SAAgB,gBAAgB,CAAC,UAAwB;QACvD,UAAU,GAAG,UAAU,IAAI,cAAc,CAAC;QAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC5C,OAAO,KAAK,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IAJe,wBAAgB,mBAI/B,CAAA;AACH,CAAC,EAnBS,OAAO,KAAP,OAAO,QAmBhB"}
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,2DAA2D;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAEL,cAAc,EAEf,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG1E,OAAO,EAAW,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;GAEG;AACH,MAAM,OAAO,QAAS,SAAQ,aAAa;IACzC;;OAEG;IACH,YAAY,OAA0B;QACpC,KAAK,CAAC;YACJ,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,OAAO,EAAE,GAAG,EAAE;gBACZ,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACtB,CAAC;YACD,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAC;YACrD,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,gBAAgB;SACxC,CAAC,CAAC;QAuBL;;WAEG;QACK,oBAAe,GAAG,GAAG,EAAE;YAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAW,CAAC;YACtE,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACtE,KAAK,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;iBACtB,IAAI,CAAC,MAAM,CAAC,EAAE;gBACb,iBAAiB;gBACjB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnC,CAAC,CAAC;iBACD,KAAK,CAAC,KAAK,CAAC,EAAE;gBACb,KAAK,gBAAgB,CACnB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,kBAAkB,EAAE,cAAc,CAAC,EAClD,KAAK,CACN,CAAC;YACJ,CAAC,CAAC,CAAC;QACP,CAAC,CAAC;QAEF;;WAEG;QACK,oBAAe,GAAG,GAAG,EAAE;YAC7B,gFAAgF;YAChF,8DAA8D;YAC9D,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;QACzB,CAAC,CAAC;QAIM,WAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;QACrC,mBAAc,GAAG,IAAI,MAAM,CAA0B,IAAI,CAAC,CAAC;QArDjE,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC;QACtC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,cAAc,CAAC;QACvD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC;QAC5C,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;CAyCF;AA2BD;;GAEG;AACH,IAAU,OAAO,CAmBhB;AAnBD,WAAU,OAAO;IACf;;OAEG;IACH,SAAgB,iBAAiB;QAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC9C,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC;QACpB,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IALe,yBAAiB,oBAKhC,CAAA;IAED;;OAEG;IACH,SAAgB,gBAAgB,CAAC,UAAwB;QACvD,UAAU,GAAG,UAAU,IAAI,cAAc,CAAC;QAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC5C,OAAO,KAAK,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IAJe,wBAAgB,mBAI/B,CAAA;AACH,CAAC,EAnBS,OAAO,KAAP,OAAO,QAmBhB"}
@@ -116,6 +116,8 @@ export class FileUploadStatus extends VDomRenderer {
116
116
  else if (uploads.name === 'failure') {
117
117
  ArrayExt.removeFirstWhere(this._items, val => val.path === uploads.newValue.path);
118
118
  }
119
+ else
120
+ return;
119
121
  this.stateChanged.emit(void 0);
120
122
  };
121
123
  this._items = [];
@@ -1 +1 @@
1
- {"version":3,"file":"uploadstatus.js","sourceRoot":"","sources":["../src/uploadstatus.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"uploadstatus.js","sourceRoot":"","sources":["../src/uploadstatus.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupyterlab/filebrowser",
3
- "version": "4.5.0-alpha.0",
3
+ "version": "4.5.0-alpha.2",
4
4
  "description": "JupyterLab - FileBrowser Widget",
5
5
  "homepage": "https://github.com/jupyterlab/jupyterlab",
6
6
  "bugs": {
@@ -41,15 +41,15 @@
41
41
  "watch": "tsc -b --watch"
42
42
  },
43
43
  "dependencies": {
44
- "@jupyterlab/apputils": "^4.6.0-alpha.0",
45
- "@jupyterlab/coreutils": "^6.5.0-alpha.0",
46
- "@jupyterlab/docmanager": "^4.5.0-alpha.0",
47
- "@jupyterlab/docregistry": "^4.5.0-alpha.0",
48
- "@jupyterlab/services": "^7.5.0-alpha.0",
49
- "@jupyterlab/statedb": "^4.5.0-alpha.0",
50
- "@jupyterlab/statusbar": "^4.5.0-alpha.0",
51
- "@jupyterlab/translation": "^4.5.0-alpha.0",
52
- "@jupyterlab/ui-components": "^4.5.0-alpha.0",
44
+ "@jupyterlab/apputils": "^4.6.0-alpha.2",
45
+ "@jupyterlab/coreutils": "^6.5.0-alpha.2",
46
+ "@jupyterlab/docmanager": "^4.5.0-alpha.2",
47
+ "@jupyterlab/docregistry": "^4.5.0-alpha.2",
48
+ "@jupyterlab/services": "^7.5.0-alpha.2",
49
+ "@jupyterlab/statedb": "^4.5.0-alpha.2",
50
+ "@jupyterlab/statusbar": "^4.5.0-alpha.2",
51
+ "@jupyterlab/translation": "^4.5.0-alpha.2",
52
+ "@jupyterlab/ui-components": "^4.5.0-alpha.2",
53
53
  "@lumino/algorithm": "^2.0.3",
54
54
  "@lumino/coreutils": "^2.2.1",
55
55
  "@lumino/disposable": "^2.1.4",
@@ -60,10 +60,11 @@
60
60
  "@lumino/signaling": "^2.1.4",
61
61
  "@lumino/virtualdom": "^2.0.3",
62
62
  "@lumino/widgets": "^2.7.1",
63
+ "jest-environment-jsdom": "^29.3.0",
63
64
  "react": "^18.2.0"
64
65
  },
65
66
  "devDependencies": {
66
- "@jupyterlab/testing": "^4.5.0-alpha.0",
67
+ "@jupyterlab/testing": "^4.5.0-alpha.2",
67
68
  "@types/jest": "^29.2.0",
68
69
  "jest": "^29.2.0",
69
70
  "rimraf": "~5.0.5",
package/src/browser.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  SidePanel,
14
14
  Toolbar
15
15
  } from '@jupyterlab/ui-components';
16
+ import { ISignal, Signal } from '@lumino/signaling';
16
17
  import { Panel } from '@lumino/widgets';
17
18
  import { createRef } from 'react';
18
19
  import { BreadCrumbs } from './crumbs';
@@ -128,6 +129,9 @@ export class FileBrowser extends SidePanel {
128
129
  state: options.state
129
130
  });
130
131
  this.listing.addClass(LISTING_CLASS);
132
+ this.listing.selectionChanged.connect(() => {
133
+ this._selectionChanged.emit();
134
+ });
131
135
 
132
136
  this.mainPanel.addWidget(this.crumbs);
133
137
  this.mainPanel.addWidget(this.filterToolbar);
@@ -292,6 +296,23 @@ export class FileBrowser extends SidePanel {
292
296
  }
293
297
  }
294
298
 
299
+ /**
300
+ * Whether to allow upload of files.
301
+ */
302
+ get allowFileUploads(): boolean {
303
+ return this._allowFileUploads;
304
+ }
305
+
306
+ set allowFileUploads(value: boolean) {
307
+ this.model.allowFileUploads = value;
308
+ if (this.listing.setAllowDragDropUpload) {
309
+ this.listing.setAllowDragDropUpload(value);
310
+ this._allowFileUploads = value;
311
+ } else {
312
+ console.warn('Listing does not support setting upload');
313
+ }
314
+ }
315
+
295
316
  /**
296
317
  * Create an iterator over the listing's selected items.
297
318
  *
@@ -301,6 +322,13 @@ export class FileBrowser extends SidePanel {
301
322
  return this.listing.selectedItems();
302
323
  }
303
324
 
325
+ /**
326
+ * A signal emitted when the selection changes in the file browser.
327
+ */
328
+ get selectionChanged(): ISignal<this, void> {
329
+ return this._selectionChanged;
330
+ }
331
+
304
332
  /**
305
333
  * Select an item by name.
306
334
  *
@@ -422,6 +450,13 @@ export class FileBrowser extends SidePanel {
422
450
  return this.listing.duplicate();
423
451
  }
424
452
 
453
+ /**
454
+ * Select all listing items.
455
+ */
456
+ selectAll(): Promise<void> {
457
+ return this.listing.selectAll();
458
+ }
459
+
425
460
  /**
426
461
  * Download the currently selected item(s).
427
462
  */
@@ -539,6 +574,8 @@ export class FileBrowser extends SidePanel {
539
574
  private _showHiddenFiles: boolean = false;
540
575
  private _showLastModifiedColumn: boolean = true;
541
576
  private _sortNotebooksFirst: boolean = false;
577
+ private _allowFileUploads: boolean = true;
578
+ private _selectionChanged = new Signal<this, void>(this);
542
579
  }
543
580
 
544
581
  /**
@@ -0,0 +1,21 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ // From: https://github.com/jsdom/jsdom/issues/1724#issuecomment-1446858041
7
+ // Mostly the same as in @jupyterlab/testing, but using jsdom version of `File`
8
+ // so that it is compatible with `FileReader` from jsdom.
9
+
10
+ import JSDOMEnvironment from 'jest-environment-jsdom';
11
+
12
+ export default class FixJSDOMEnvironment extends JSDOMEnvironment {
13
+ constructor(...args: ConstructorParameters<typeof JSDOMEnvironment>) {
14
+ super(...args);
15
+
16
+ this.global.fetch = fetch;
17
+ this.global.Headers = Headers;
18
+ this.global.Request = Request;
19
+ this.global.Response = Response;
20
+ }
21
+ }
package/src/listing.ts CHANGED
@@ -238,6 +238,7 @@ export class DirListing extends Widget {
238
238
  this._manager = this._model.manager;
239
239
  this._renderer = options.renderer || DirListing.defaultRenderer;
240
240
  this._state = options.state || null;
241
+ this._allowDragDropUpload = options.allowDragDropUpload ?? true;
241
242
 
242
243
  // Get the width of the "modified" column
243
244
  this._updateModifiedSize(this.node);
@@ -316,6 +317,13 @@ export class DirListing extends Widget {
316
317
  return this._onItemOpened;
317
318
  }
318
319
 
320
+ /**
321
+ * A signal fired when the selection changes.
322
+ */
323
+ get selectionChanged(): ISignal<DirListing, void> {
324
+ return this._selectionChanged;
325
+ }
326
+
319
327
  /**
320
328
  * Create an iterator over the listing's selected items.
321
329
  *
@@ -514,6 +522,22 @@ export class DirListing extends Widget {
514
522
  });
515
523
  }
516
524
 
525
+ /**
526
+ * Select all listing items in the current directory.
527
+ */
528
+ async selectAll(): Promise<void> {
529
+ const items = this._model.items();
530
+ const newSelection: { [key: string]: boolean } = {};
531
+
532
+ for (const item of items) {
533
+ newSelection[item.path] = true;
534
+ }
535
+
536
+ this.selection = newSelection;
537
+ this._selectionChanged.emit();
538
+ this.update();
539
+ }
540
+
517
541
  /**
518
542
  * Download the currently selected item(s).
519
543
  */
@@ -708,8 +732,11 @@ export class DirListing extends Widget {
708
732
  /**
709
733
  * Clear the selected items.
710
734
  */
711
- clearSelectedItems(): void {
735
+ clearSelectedItems(emit = true): void {
712
736
  this.selection = Object.create(null);
737
+ if (emit) {
738
+ this._selectionChanged.emit();
739
+ }
713
740
  }
714
741
 
715
742
  /**
@@ -790,7 +817,10 @@ export class DirListing extends Widget {
790
817
  break;
791
818
  case 'dragenter':
792
819
  case 'dragover':
793
- this.addClass('jp-mod-native-drop');
820
+ // Only show visual feedback if drag and drop upload is enabled
821
+ if (this._allowDragDropUpload) {
822
+ this.addClass('jp-mod-native-drop');
823
+ }
794
824
  event.preventDefault();
795
825
  break;
796
826
  case 'dragleave':
@@ -844,6 +874,7 @@ export class DirListing extends Widget {
844
874
  content.addEventListener('lm-dragleave', this);
845
875
  content.addEventListener('lm-dragover', this);
846
876
  content.addEventListener('lm-drop', this);
877
+ this._updateColumnSizes();
847
878
  }
848
879
 
849
880
  /**
@@ -1057,6 +1088,8 @@ export class DirListing extends Widget {
1057
1088
  // Fetch common variables.
1058
1089
  const items = this._sortedItems;
1059
1090
  const nodes = this._items;
1091
+ // If we didn't get any item yet, we'll need to resize once items are added
1092
+ const needsResize = nodes.length === 0;
1060
1093
  const content = DOMUtils.findElement(this.node, CONTENT_CLASS);
1061
1094
  const renderer = this._renderer;
1062
1095
 
@@ -1091,12 +1124,18 @@ export class DirListing extends Widget {
1091
1124
  checkbox.checked = false;
1092
1125
  }
1093
1126
 
1094
- // Handle `tabIndex`
1127
+ // Handle `tabIndex` & `role`
1095
1128
  const nameNode = renderer.getNameNode(node);
1096
1129
  if (nameNode) {
1097
1130
  // Must check if the name node is there because it gets replaced by the
1098
1131
  // edit node when editing the name of the file or directory.
1099
- nameNode.tabIndex = i === this._focusIndex ? 0 : -1;
1132
+ if (i === this._focusIndex) {
1133
+ nameNode.setAttribute('tabIndex', '0');
1134
+ nameNode.setAttribute('role', 'button');
1135
+ } else {
1136
+ nameNode.setAttribute('tabIndex', '-1');
1137
+ nameNode.removeAttribute('role');
1138
+ }
1100
1139
  }
1101
1140
  });
1102
1141
 
@@ -1126,6 +1165,11 @@ export class DirListing extends Widget {
1126
1165
 
1127
1166
  this.updateNodes(items, nodes);
1128
1167
 
1168
+ if (needsResize) {
1169
+ this._width = this._computeContentWidth();
1170
+ this._updateColumnSizes();
1171
+ }
1172
+
1129
1173
  this._prevPath = this._model.path;
1130
1174
  }
1131
1175
 
@@ -1320,6 +1364,14 @@ export class DirListing extends Widget {
1320
1364
  this._allowSingleClick = isEnabled;
1321
1365
  }
1322
1366
 
1367
+ /**
1368
+ * Update the setting to allow drag and drop upload.
1369
+ * This enables uploading files via drag and drop.
1370
+ */
1371
+ setAllowDragDropUpload(isEnabled: boolean) {
1372
+ this._allowDragDropUpload = isEnabled;
1373
+ }
1374
+
1323
1375
  /**
1324
1376
  * Would this click (or other event type) hit the checkbox by default?
1325
1377
  */
@@ -1357,6 +1409,7 @@ export class DirListing extends Widget {
1357
1409
  this._sortedItems.forEach(
1358
1410
  (item: Contents.IModel) => (this.selection[item.path] = true)
1359
1411
  );
1412
+ this._selectionChanged.emit();
1360
1413
  } else {
1361
1414
  // Unselect all items
1362
1415
  this.clearSelectedItems();
@@ -1487,6 +1540,7 @@ export class DirListing extends Widget {
1487
1540
  if (!altered && event.button === 0) {
1488
1541
  this.clearSelectedItems();
1489
1542
  this.selection[this._softSelection] = true;
1543
+ this._selectionChanged.emit();
1490
1544
  this.update();
1491
1545
  }
1492
1546
  this._softSelection = '';
@@ -1737,6 +1791,7 @@ export class DirListing extends Widget {
1737
1791
  } else {
1738
1792
  this.selection[path] = true;
1739
1793
  }
1794
+ this._selectionChanged.emit();
1740
1795
 
1741
1796
  this.update();
1742
1797
  // Key was handled, so return.
@@ -1823,6 +1878,11 @@ export class DirListing extends Widget {
1823
1878
  // Prevent navigation
1824
1879
  event.preventDefault();
1825
1880
 
1881
+ // Check if drag and drop upload is disabled
1882
+ if (!this._allowDragDropUpload) {
1883
+ return;
1884
+ }
1885
+
1826
1886
  const items = event.dataTransfer?.items;
1827
1887
  if (!items) {
1828
1888
  // Fallback to simple upload of files (if any)
@@ -2138,6 +2198,7 @@ export class DirListing extends Widget {
2138
2198
  } else {
2139
2199
  this.selection[path] = true;
2140
2200
  }
2201
+ this._selectionChanged.emit();
2141
2202
  this._focusItem(index);
2142
2203
  // Handle multiple select.
2143
2204
  } else if (event.shiftKey) {
@@ -2217,7 +2278,10 @@ export class DirListing extends Widget {
2217
2278
  // the focussed item to gain but not lose selected status on shift-click.
2218
2279
  // (MacOS is irrelevant here because MacOS Finder has no notion of a
2219
2280
  // focused-but-not-selected state.)
2220
- this.selection[target.path] = true;
2281
+ if (!this.selection[target.path]) {
2282
+ this.selection[target.path] = true;
2283
+ this._selectionChanged.emit();
2284
+ }
2221
2285
  return;
2222
2286
  }
2223
2287
 
@@ -2247,6 +2311,7 @@ export class DirListing extends Widget {
2247
2311
  (!anteAnchor || !this.selection[anteAnchor.path])
2248
2312
  ) {
2249
2313
  delete this.selection[anchor.path];
2314
+ this._selectionChanged.emit();
2250
2315
  }
2251
2316
  } else if (this._allSelectedBetween(fromIndex, index)) {
2252
2317
  shouldAdd = false;
@@ -2256,21 +2321,33 @@ export class DirListing extends Widget {
2256
2321
  // Select (or unselect) the rows between chosen index (target) and the last
2257
2322
  // focussed.
2258
2323
  const step = fromIndex < index ? 1 : -1;
2324
+ let selectionModified = false;
2325
+
2259
2326
  for (let i = fromIndex; i !== index + step; i += step) {
2260
2327
  if (shouldAdd) {
2261
2328
  if (i === fromIndex) {
2262
2329
  // Do not change the selection state of the starting (fromIndex) item.
2263
2330
  continue;
2264
2331
  }
2265
- this.selection[items[i].path] = true;
2332
+ if (!this.selection[items[i].path]) {
2333
+ this.selection[items[i].path] = true;
2334
+ selectionModified = true;
2335
+ }
2266
2336
  } else {
2267
2337
  if (i === index) {
2268
2338
  // Do not unselect the target item.
2269
2339
  continue;
2270
2340
  }
2271
- delete this.selection[items[i].path];
2341
+ if (this.selection[items[i].path]) {
2342
+ delete this.selection[items[i].path];
2343
+ selectionModified = true;
2344
+ }
2272
2345
  }
2273
2346
  }
2347
+
2348
+ if (selectionModified) {
2349
+ this._selectionChanged.emit();
2350
+ }
2274
2351
  }
2275
2352
 
2276
2353
  /**
@@ -2425,10 +2502,12 @@ export class DirListing extends Widget {
2425
2502
  }
2426
2503
  const path = items[index].path;
2427
2504
  this.selection[path] = true;
2505
+ this._selectionChanged.emit();
2428
2506
 
2429
2507
  if (focus) {
2430
2508
  this._focusItem(index);
2431
2509
  }
2510
+
2432
2511
  this.update();
2433
2512
  }
2434
2513
 
@@ -2438,7 +2517,7 @@ export class DirListing extends Widget {
2438
2517
  private _onModelRefreshed(): void {
2439
2518
  // Update the selection.
2440
2519
  const existing = Object.keys(this.selection);
2441
- this.clearSelectedItems();
2520
+ this.clearSelectedItems(false);
2442
2521
  for (const item of this._model.items()) {
2443
2522
  const path = item.path;
2444
2523
  if (existing.indexOf(path) !== -1) {
@@ -2515,6 +2594,7 @@ export class DirListing extends Widget {
2515
2594
  key: 'name'
2516
2595
  };
2517
2596
  private _onItemOpened = new Signal<DirListing, Contents.IModel>(this);
2597
+ private _selectionChanged = new Signal<DirListing, void>(this);
2518
2598
  private _drag: Drag | null = null;
2519
2599
  private _dragData: {
2520
2600
  pressX: number;
@@ -2560,6 +2640,7 @@ export class DirListing extends Widget {
2560
2640
  };
2561
2641
  private _sortNotebooksFirst = false;
2562
2642
  private _allowSingleClick = false;
2643
+ private _allowDragDropUpload = true;
2563
2644
  // _focusIndex should never be set outside the range [0, this._items.length - 1]
2564
2645
  private _focusIndex = 0;
2565
2646
  // Width of the "last modified" column for an individual file
@@ -2607,6 +2688,12 @@ export namespace DirListing {
2607
2688
  * the columns sizes
2608
2689
  */
2609
2690
  state?: IStateDB;
2691
+
2692
+ /**
2693
+ * Whether to allow drag and drop upload of files.
2694
+ * The default is `true`.
2695
+ */
2696
+ allowDragDropUpload?: boolean;
2610
2697
  }
2611
2698
 
2612
2699
  /**
@@ -3256,13 +3343,13 @@ export namespace DirListing {
3256
3343
 
3257
3344
  // add file size to pop up if its available
3258
3345
  if (model.size !== null && model.size !== undefined) {
3259
- const fileSizeText = Private.formatFileSize(model.size, 1, 1024);
3346
+ const fileSizeText = formatFileSize(model.size, 1, 1024);
3260
3347
  if (fileSize) {
3261
3348
  fileSize.textContent = fileSizeText;
3262
3349
  }
3263
3350
  hoverText += trans.__(
3264
3351
  '\nSize: %1',
3265
- Private.formatFileSize(model.size, 1, 1024)
3352
+ formatFileSize(model.size, 1, 1024)
3266
3353
  );
3267
3354
  } else if (fileSize) {
3268
3355
  fileSize.textContent = '';
@@ -3716,28 +3803,6 @@ namespace Private {
3716
3803
  );
3717
3804
  }
3718
3805
 
3719
- /**
3720
- * Format bytes to human readable string.
3721
- */
3722
- export function formatFileSize(
3723
- bytes: number,
3724
- decimalPoint: number,
3725
- k: number
3726
- ): string {
3727
- // https://www.codexworld.com/how-to/convert-file-size-bytes-kb-mb-gb-javascript/
3728
- if (bytes === 0) {
3729
- return '0 B';
3730
- }
3731
- const dm = decimalPoint || 2;
3732
- const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
3733
- const i = Math.floor(Math.log(bytes) / Math.log(k));
3734
- if (i >= 0 && i < sizes.length) {
3735
- return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
3736
- } else {
3737
- return String(bytes);
3738
- }
3739
- }
3740
-
3741
3806
  /**
3742
3807
  * Update an inline svg caret icon in a node.
3743
3808
  */
@@ -3834,3 +3899,25 @@ namespace Private {
3834
3899
  return allEntries;
3835
3900
  }
3836
3901
  }
3902
+
3903
+ /**
3904
+ * Format bytes to human readable string.
3905
+ */
3906
+ export function formatFileSize(
3907
+ bytes: number,
3908
+ decimalPoint: number,
3909
+ k: number
3910
+ ): string {
3911
+ // https://www.codexworld.com/how-to/convert-file-size-bytes-kb-mb-gb-javascript/
3912
+ if (bytes === 0) {
3913
+ return '0 B';
3914
+ }
3915
+ const dm = decimalPoint || 2;
3916
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
3917
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
3918
+ if (i >= 0 && i < sizes.length) {
3919
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
3920
+ } else {
3921
+ return String(bytes);
3922
+ }
3923
+ }
package/src/model.ts CHANGED
@@ -60,6 +60,7 @@ export class FileBrowserModel implements IDisposable {
60
60
  this.translator = options.translator || nullTranslator;
61
61
  this._trans = this.translator.load('jupyterlab');
62
62
  this._driveName = options.driveName || '';
63
+ this._allowFileUploads = options.allowFileUploads ?? true;
63
64
  this._model = {
64
65
  path: this.rootPath,
65
66
  name: PathExt.basename(this.rootPath),
@@ -379,6 +380,17 @@ export class FileBrowserModel implements IDisposable {
379
380
  this._restored.resolve(undefined);
380
381
  }
381
382
 
383
+ /**
384
+ * Whether uploads are allowed.
385
+ */
386
+ get allowFileUploads(): boolean {
387
+ return this._allowFileUploads;
388
+ }
389
+
390
+ set allowFileUploads(value: boolean) {
391
+ this._allowFileUploads = value;
392
+ }
393
+
382
394
  /**
383
395
  * Upload a `File` object.
384
396
  *
@@ -394,6 +406,11 @@ export class FileBrowserModel implements IDisposable {
394
406
  * chunks.
395
407
  */
396
408
  async upload(file: File, path?: string): Promise<Contents.IModel> {
409
+ // Check if upload is allowed
410
+ if (!this._allowFileUploads) {
411
+ throw new Error(this._trans.__('File uploads are disabled'));
412
+ }
413
+
397
414
  // We do not support Jupyter Notebook version less than 4, and Jupyter
398
415
  // Server advertises itself as version 1 and supports chunked
399
416
  // uploading. We assume any version less than 4.0.0 to be Jupyter Server
@@ -657,6 +674,7 @@ export class FileBrowserModel implements IDisposable {
657
674
  private _sessions: Session.IModel[] = [];
658
675
  private _state: IStateDB | null = null;
659
676
  private _driveName: string;
677
+ private _allowFileUploads: boolean;
660
678
  private _isDisposed = false;
661
679
  private _restored = new PromiseDelegate<void>();
662
680
  private _uploads: IUploadModel[] = [];
@@ -713,6 +731,11 @@ export namespace FileBrowserModel {
713
731
  * The application language translator.
714
732
  */
715
733
  translator?: ITranslator;
734
+
735
+ /**
736
+ * Whether to allow file uploads. Defaults to `true`.
737
+ */
738
+ allowFileUploads?: boolean;
716
739
  }
717
740
  }
718
741
 
package/src/tokens.ts CHANGED
@@ -115,6 +115,11 @@ export namespace IFileBrowserFactory {
115
115
  * database will be automatically passed in and used for state restoration.
116
116
  */
117
117
  state?: IStateDB | null;
118
+
119
+ /**
120
+ * Whether to allow file uploads. Defaults to `true`.
121
+ */
122
+ allowFileUploads?: boolean;
118
123
  }
119
124
  }
120
125