@jupyterlab/filebrowser 4.6.0-alpha.2 → 4.6.0-alpha.3

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/src/listing.ts CHANGED
@@ -8,19 +8,13 @@ import {
8
8
  showErrorMessage
9
9
  } from '@jupyterlab/apputils';
10
10
  import { PageConfig, PathExt, Time } from '@jupyterlab/coreutils';
11
- import {
12
- IDocumentManager,
13
- isValidFileName,
14
- renameFile
15
- } from '@jupyterlab/docmanager';
11
+ import type { IDocumentManager } from '@jupyterlab/docmanager';
12
+ import { isValidFileName, renameFile } from '@jupyterlab/docmanager';
16
13
  import { DocumentRegistry } from '@jupyterlab/docregistry';
17
- import { Contents } from '@jupyterlab/services';
18
- import { IStateDB } from '@jupyterlab/statedb';
19
- import {
20
- ITranslator,
21
- nullTranslator,
22
- TranslationBundle
23
- } from '@jupyterlab/translation';
14
+ import type { Contents } from '@jupyterlab/services';
15
+ import type { IStateDB } from '@jupyterlab/statedb';
16
+ import type { ITranslator, TranslationBundle } from '@jupyterlab/translation';
17
+ import { nullTranslator } from '@jupyterlab/translation';
24
18
  import {
25
19
  caretDownIcon,
26
20
  caretUpIcon,
@@ -28,19 +22,19 @@ import {
28
22
  LabIcon
29
23
  } from '@jupyterlab/ui-components';
30
24
  import { ArrayExt, filter, StringExt } from '@lumino/algorithm';
31
- import {
32
- MimeData,
33
- PromiseDelegate,
34
- ReadonlyJSONObject
35
- } from '@lumino/coreutils';
25
+ import type { ReadonlyJSONObject } from '@lumino/coreutils';
26
+ import { MimeData, PromiseDelegate } from '@lumino/coreutils';
36
27
  import { ElementExt } from '@lumino/domutils';
37
- import { DisposableDelegate, IDisposable } from '@lumino/disposable';
28
+ import type { IDisposable } from '@lumino/disposable';
29
+ import { DisposableDelegate } from '@lumino/disposable';
38
30
  import { Drag } from '@lumino/dragdrop';
39
- import { Message, MessageLoop } from '@lumino/messaging';
40
- import { ISignal, Signal } from '@lumino/signaling';
31
+ import type { Message } from '@lumino/messaging';
32
+ import { MessageLoop } from '@lumino/messaging';
33
+ import type { ISignal } from '@lumino/signaling';
34
+ import { Signal } from '@lumino/signaling';
41
35
  import { h, VirtualDOM } from '@lumino/virtualdom';
42
36
  import { Widget } from '@lumino/widgets';
43
- import { FilterFileBrowserModel } from './model';
37
+ import type { FilterFileBrowserModel } from './model';
44
38
 
45
39
  /**
46
40
  * The class name added to DirListing widget.
@@ -356,6 +350,7 @@ export class DirListing extends Widget {
356
350
  this.model.items(),
357
351
  state,
358
352
  this._sortNotebooksFirst,
353
+ this._sortFileNamesNaturally,
359
354
  this.translator
360
355
  );
361
356
  this._sortState = state;
@@ -962,8 +957,8 @@ export class DirListing extends Widget {
962
957
  this._modifiedWidth < 100
963
958
  ? 'narrow'
964
959
  : this._modifiedWidth > 120
965
- ? 'long'
966
- : 'short';
960
+ ? 'long'
961
+ : 'short';
967
962
  }
968
963
 
969
964
  /**
@@ -1361,6 +1356,19 @@ export class DirListing extends Widget {
1361
1356
  }
1362
1357
  }
1363
1358
 
1359
+ /**
1360
+ * Update the setting to sort file names naturally
1361
+ * vs lexicographically. Default is true (natural).
1362
+ * This sorts the items again if the internal value is modified.
1363
+ */
1364
+ setSortFileNamesNaturally(natural: boolean): void {
1365
+ const previousValue = this._sortFileNamesNaturally;
1366
+ this._sortFileNamesNaturally = natural;
1367
+ if (this._sortFileNamesNaturally !== previousValue) {
1368
+ this.sort(this._sortState);
1369
+ }
1370
+ }
1371
+
1364
1372
  /**
1365
1373
  * Update the setting to allow single click navigation.
1366
1374
  * This enables opening files/directories with a single click.
@@ -2121,7 +2129,7 @@ export class DirListing extends Widget {
2121
2129
  } as DirListing.IContentsThunk);
2122
2130
  }
2123
2131
 
2124
- if (item && item.type !== 'directory') {
2132
+ if (item.type !== 'directory') {
2125
2133
  const otherPaths = selectedPaths.slice(1).reverse();
2126
2134
  this._drag.mimeData.setData(FACTORY_MIME, () => {
2127
2135
  if (!item) {
@@ -2541,6 +2549,9 @@ export class DirListing extends Widget {
2541
2549
  * Handle a `pathChanged` signal from the model.
2542
2550
  */
2543
2551
  private _onPathChanged(): void {
2552
+ // Check if the directory listing (or any of its children) has focus.
2553
+ const hasFocus = this.node.contains(document.activeElement);
2554
+
2544
2555
  // Reset the selection.
2545
2556
  this.clearSelectedItems();
2546
2557
  // Update the sorted items.
@@ -2548,7 +2559,16 @@ export class DirListing extends Widget {
2548
2559
  // Reset focus. But wait until the DOM has been updated (hence
2549
2560
  // `requestAnimationFrame`).
2550
2561
  requestAnimationFrame(() => {
2551
- this._focusItem(0);
2562
+ if (this.isDisposed) {
2563
+ return;
2564
+ }
2565
+ // Only focus the first item if the listing (or a child) previously had focus.
2566
+ if (hasFocus) {
2567
+ this._focusItem(0);
2568
+ } else {
2569
+ // Otherwise, just reset the internal focus index without moving DOM focus.
2570
+ this._focusIndex = 0;
2571
+ }
2552
2572
  });
2553
2573
  }
2554
2574
 
@@ -2645,6 +2665,7 @@ export class DirListing extends Widget {
2645
2665
  last_modified: null
2646
2666
  };
2647
2667
  private _sortNotebooksFirst = false;
2668
+ private _sortFileNamesNaturally = true;
2648
2669
  private _allowSingleClick = false;
2649
2670
  private _allowDragDropUpload = true;
2650
2671
  // _focusIndex should never be set outside the range [0, this._items.length - 1]
@@ -3679,6 +3700,7 @@ namespace Private {
3679
3700
  items: Iterable<Contents.IModel>,
3680
3701
  state: DirListing.ISortState,
3681
3702
  sortNotebooksFirst: boolean = false,
3703
+ sortFileNamesNaturally: boolean = true,
3682
3704
  translator: ITranslator
3683
3705
  ): Contents.IModel[] {
3684
3706
  const copy = Array.from(items);
@@ -3710,6 +3732,7 @@ namespace Private {
3710
3732
 
3711
3733
  /**
3712
3734
  * Compare two items by their name using `translator.languageCode`, with fallback to `navigator.language`.
3735
+ * When sortFileNamesNaturally is true, uses natural order.
3713
3736
  */
3714
3737
  function compareByName(a: Contents.IModel, b: Contents.IModel) {
3715
3738
  // Wokaround for Chromium invalid language code on CI, see
@@ -3718,19 +3741,17 @@ namespace Private {
3718
3741
  const languageCode = (
3719
3742
  translator.languageCode ?? navigatorLanguage
3720
3743
  ).replace('_', '-');
3744
+ const localeOptions: Intl.CollatorOptions = {
3745
+ numeric: sortFileNamesNaturally,
3746
+ sensitivity: 'base'
3747
+ };
3721
3748
  try {
3722
- return a.name.localeCompare(b.name, languageCode, {
3723
- numeric: true,
3724
- sensitivity: 'base'
3725
- });
3749
+ return a.name.localeCompare(b.name, languageCode, localeOptions);
3726
3750
  } catch (e) {
3727
3751
  console.warn(
3728
3752
  `localeCompare failed to compare ${a.name} and ${b.name} under languageCode: ${languageCode}`
3729
3753
  );
3730
- return a.name.localeCompare(b.name, navigatorLanguage, {
3731
- numeric: true,
3732
- sensitivity: 'base'
3733
- });
3754
+ return a.name.localeCompare(b.name, navigatorLanguage, localeOptions);
3734
3755
  }
3735
3756
  }
3736
3757
 
package/src/model.ts CHANGED
@@ -2,21 +2,22 @@
2
2
  // Distributed under the terms of the Modified BSD License.
3
3
 
4
4
  import { Dialog, showDialog } from '@jupyterlab/apputils';
5
- import { IChangedArgs, PageConfig, PathExt } from '@jupyterlab/coreutils';
6
- import { IDocumentManager, shouldOverwrite } from '@jupyterlab/docmanager';
7
- import { Contents, KernelSpec, Session } from '@jupyterlab/services';
8
- import { IStateDB } from '@jupyterlab/statedb';
9
- import {
10
- ITranslator,
11
- nullTranslator,
12
- TranslationBundle
13
- } from '@jupyterlab/translation';
14
- import { IFilterBoxProps, IScore } from '@jupyterlab/ui-components';
5
+ import type { IChangedArgs } from '@jupyterlab/coreutils';
6
+ import { PageConfig, PathExt } from '@jupyterlab/coreutils';
7
+ import type { IDocumentManager } from '@jupyterlab/docmanager';
8
+ import { shouldOverwrite } from '@jupyterlab/docmanager';
9
+ import type { Contents, KernelSpec, Session } from '@jupyterlab/services';
10
+ import type { IStateDB } from '@jupyterlab/statedb';
11
+ import type { ITranslator, TranslationBundle } from '@jupyterlab/translation';
12
+ import { nullTranslator } from '@jupyterlab/translation';
13
+ import type { IFilterBoxProps, IScore } from '@jupyterlab/ui-components';
15
14
  import { ArrayExt, filter } from '@lumino/algorithm';
16
- import { PromiseDelegate, ReadonlyJSONObject } from '@lumino/coreutils';
17
- import { IDisposable } from '@lumino/disposable';
15
+ import type { ReadonlyJSONObject } from '@lumino/coreutils';
16
+ import { PromiseDelegate } from '@lumino/coreutils';
17
+ import type { IDisposable } from '@lumino/disposable';
18
18
  import { Poll } from '@lumino/polling';
19
- import { ISignal, Signal } from '@lumino/signaling';
19
+ import type { ISignal } from '@lumino/signaling';
20
+ import { Signal } from '@lumino/signaling';
20
21
 
21
22
  /**
22
23
  * The default duration of the auto-refresh in ms
@@ -633,10 +634,10 @@ export class FileBrowserModel implements IDisposable {
633
634
  prefix + PathExt.dirname(oldValue.path) === path
634
635
  ? oldValue
635
636
  : newValue &&
636
- newValue.path &&
637
- prefix + PathExt.dirname(newValue.path) === path
638
- ? newValue
639
- : undefined;
637
+ newValue.path &&
638
+ prefix + PathExt.dirname(newValue.path) === path
639
+ ? newValue
640
+ : undefined;
640
641
 
641
642
  // If either the old value or the new value is in the current path, update.
642
643
  if (value) {
package/src/opendialog.ts CHANGED
@@ -3,14 +3,16 @@
3
3
 
4
4
  import { Dialog, setToolbar, ToolbarButton } from '@jupyterlab/apputils';
5
5
  import { PathExt } from '@jupyterlab/coreutils';
6
- import { IDocumentManager } from '@jupyterlab/docmanager';
7
- import { Contents } from '@jupyterlab/services';
8
- import { ITranslator, nullTranslator } from '@jupyterlab/translation';
9
- import { IScore, newFolderIcon, refreshIcon } from '@jupyterlab/ui-components';
6
+ import type { IDocumentManager } from '@jupyterlab/docmanager';
7
+ import type { Contents } from '@jupyterlab/services';
8
+ import type { ITranslator } from '@jupyterlab/translation';
9
+ import { nullTranslator } from '@jupyterlab/translation';
10
+ import type { IScore } from '@jupyterlab/ui-components';
11
+ import { newFolderIcon, refreshIcon } from '@jupyterlab/ui-components';
10
12
  import { PanelLayout, Widget } from '@lumino/widgets';
11
13
  import { FileBrowser } from './browser';
12
14
  import { FilterFileBrowserModel } from './model';
13
- import { IFileBrowserFactory } from './tokens';
15
+ import type { IFileBrowserFactory } from './tokens';
14
16
  import { PromiseDelegate } from '@lumino/coreutils';
15
17
 
16
18
  /**
@@ -30,16 +32,15 @@ export namespace FileDialog {
30
32
  /**
31
33
  * Options for the open directory dialog
32
34
  */
33
- export interface IDirectoryOptions
34
- extends Partial<
35
- Pick<
36
- Dialog.IOptions<Promise<Contents.IModel[]>>,
37
- Exclude<
38
- keyof Dialog.IOptions<Promise<Contents.IModel[]>>,
39
- 'body' | 'buttons' | 'defaultButton'
40
- >
35
+ export interface IDirectoryOptions extends Partial<
36
+ Pick<
37
+ Dialog.IOptions<Promise<Contents.IModel[]>>,
38
+ Exclude<
39
+ keyof Dialog.IOptions<Promise<Contents.IModel[]>>,
40
+ 'body' | 'buttons' | 'defaultButton'
41
41
  >
42
- > {
42
+ >
43
+ > {
43
44
  /**
44
45
  * Document manager
45
46
  */
package/src/upload.ts CHANGED
@@ -2,15 +2,13 @@
2
2
  // Distributed under the terms of the Modified BSD License.
3
3
 
4
4
  import { showErrorMessage } from '@jupyterlab/apputils';
5
- import {
6
- ITranslator,
7
- nullTranslator,
8
- TranslationBundle
9
- } from '@jupyterlab/translation';
5
+ import type { ITranslator, TranslationBundle } from '@jupyterlab/translation';
6
+ import { nullTranslator } from '@jupyterlab/translation';
10
7
  import { fileUploadIcon, ToolbarButton } from '@jupyterlab/ui-components';
11
- import { FileBrowserModel } from './model';
12
- import { Contents } from '@jupyterlab/services';
13
- import { ISignal, Signal } from '@lumino/signaling';
8
+ import type { FileBrowserModel } from './model';
9
+ import type { Contents } from '@jupyterlab/services';
10
+ import type { ISignal } from '@lumino/signaling';
11
+ import { Signal } from '@lumino/signaling';
14
12
 
15
13
  /**
16
14
  * A widget which provides an upload button.
@@ -2,18 +2,15 @@
2
2
  // Distributed under the terms of the Modified BSD License.
3
3
  //
4
4
 
5
- import { WidgetTracker } from '@jupyterlab/apputils';
6
- import { IChangedArgs } from '@jupyterlab/coreutils';
5
+ import type { WidgetTracker } from '@jupyterlab/apputils';
6
+ import type { IChangedArgs } from '@jupyterlab/coreutils';
7
7
  import { GroupItem, ProgressBar, TextItem } from '@jupyterlab/statusbar';
8
- import {
9
- ITranslator,
10
- nullTranslator,
11
- TranslationBundle
12
- } from '@jupyterlab/translation';
8
+ import type { ITranslator, TranslationBundle } from '@jupyterlab/translation';
9
+ import { nullTranslator } from '@jupyterlab/translation';
13
10
  import { VDomModel, VDomRenderer } from '@jupyterlab/ui-components';
14
11
  import { ArrayExt } from '@lumino/algorithm';
15
12
  import React from 'react';
16
- import { FileBrowser, FileBrowserModel, IUploadModel } from '.';
13
+ import type { FileBrowser, FileBrowserModel, IUploadModel } from '.';
17
14
 
18
15
  /**
19
16
  * Half-spacing between items in the overall status item.