@jupyterlab/filebrowser
Version:
JupyterLab - FileBrowser Widget
502 lines • 15.7 kB
JavaScript
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
import { showErrorMessage } from '@jupyterlab/apputils';
import { PathExt } from '@jupyterlab/coreutils';
import { ServerConnection } from '@jupyterlab/services';
import { nullTranslator } from '@jupyterlab/translation';
import { FilenameSearcher, SidePanel, Toolbar } from '@jupyterlab/ui-components';
import { Signal } from '@lumino/signaling';
import { Panel } from '@lumino/widgets';
import { createRef } from 'react';
import { BreadCrumbs } from './crumbs';
import { DirListing } from './listing';
/**
* The class name added to file browsers.
*/
const FILE_BROWSER_CLASS = 'jp-FileBrowser';
/**
* The class name added to file browser panel (gather filter, breadcrumbs and listing).
*/
const FILE_BROWSER_PANEL_CLASS = 'jp-FileBrowser-Panel';
/**
* The class name added to the filebrowser crumbs node.
*/
const CRUMBS_CLASS = 'jp-FileBrowser-crumbs';
/**
* The class name added to the filebrowser toolbar node.
*/
const TOOLBAR_CLASS = 'jp-FileBrowser-toolbar';
/**
* The class name added to the filebrowser filter toolbar node.
*/
const FILTER_TOOLBAR_CLASS = 'jp-FileBrowser-filterToolbar';
/**
* The class name added to the filebrowser listing node.
*/
const LISTING_CLASS = 'jp-FileBrowser-listing';
/**
* The class name added to the filebrowser filterbox node.
*/
const FILTERBOX_CLASS = 'jp-FileBrowser-filterBox';
/**
* A widget which hosts a file browser.
*
* The widget uses the Jupyter Contents API to retrieve contents,
* and presents itself as a flat list of files and directories with
* breadcrumbs.
*/
export class FileBrowser extends SidePanel {
/**
* Construct a new file browser.
*
* @param options - The file browser options.
*/
constructor(options) {
var _a;
super({ content: new Panel(), translator: options.translator });
this._directoryPending = null;
this._filePending = null;
this._fileFilterRef = createRef();
this._allowSingleClick = false;
this._showFileCheckboxes = false;
this._showFileFilter = false;
this._showFileSizeColumn = false;
this._showHiddenFiles = false;
this._showLastModifiedColumn = true;
this._sortNotebooksFirst = false;
this._allowFileUploads = true;
this._selectionChanged = new Signal(this);
this.addClass(FILE_BROWSER_CLASS);
this.toolbar.addClass(TOOLBAR_CLASS);
this.id = options.id;
const translator = (this.translator = (_a = options.translator) !== null && _a !== void 0 ? _a : nullTranslator);
const model = (this.model = options.model);
const renderer = options.renderer;
model.connectionFailure.connect(this._onConnectionFailure, this);
this._manager = model.manager;
this.toolbar.node.setAttribute('aria-label', this._trans.__('file browser'));
// File browser widgets container
this.mainPanel = new Panel();
this.mainPanel.addClass(FILE_BROWSER_PANEL_CLASS);
this.mainPanel.title.label = this._trans.__('File Browser');
this.crumbs = new BreadCrumbs({ model, translator });
this.crumbs.addClass(CRUMBS_CLASS);
// The filter toolbar appears immediately below the breadcrumbs and above the directory listing.
const searcher = FilenameSearcher({
updateFilter: (filterFn, query) => {
this.model.setFilter(value => {
return filterFn(value.name.toLowerCase());
});
},
useFuzzyFilter: this.model.useFuzzyFilter,
placeholder: this._trans.__('Filter files by name'),
forceRefresh: false,
showIcon: false,
inputRef: this._fileFilterRef,
filterSettingsChanged: this.model.filterSettingsChanged
});
searcher.addClass(FILTERBOX_CLASS);
this.filterToolbar = new Toolbar();
this.filterToolbar.addClass(FILTER_TOOLBAR_CLASS);
this.filterToolbar.node.setAttribute('aria-label', this._trans.__('File browser toolbar'));
this.filterToolbar.addItem('fileNameSearcher', searcher);
this.filterToolbar.setHidden(!this.showFileFilter);
this.listing = this.createDirListing({
model,
renderer,
translator,
state: options.state,
handleOpenFile: options.handleOpenFile
});
this.listing.addClass(LISTING_CLASS);
this.listing.selectionChanged.connect(() => {
this._selectionChanged.emit();
});
this.mainPanel.addWidget(this.crumbs);
this.mainPanel.addWidget(this.filterToolbar);
this.mainPanel.addWidget(this.listing);
this.addWidget(this.mainPanel);
if (options.restore !== false) {
void model.restore(this.id);
}
// restore listing regardless of the restore option
void this.listing.restore(this.id);
}
/**
* Whether to show active file in file browser
*/
get navigateToCurrentDirectory() {
return this._navigateToCurrentDirectory;
}
set navigateToCurrentDirectory(value) {
this._navigateToCurrentDirectory = value;
}
/**
* Whether to show the last modified column
*/
get showLastModifiedColumn() {
return this._showLastModifiedColumn;
}
set showLastModifiedColumn(value) {
if (this.listing.setColumnVisibility) {
this.listing.setColumnVisibility('last_modified', value);
this._showLastModifiedColumn = value;
}
else {
console.warn('Listing does not support toggling column visibility');
}
}
/**
* Number of directory items to show on the left side of the ellipsis in breadcrumbs.
*/
get minimumBreadcrumbsLeftItems() {
return this.crumbs.minimumLeftItems;
}
set minimumBreadcrumbsLeftItems(value) {
this.crumbs.minimumLeftItems = value;
}
/**
* Number of directory items to show on the right side of the ellipsis in breadcrumbs.
*/
get minimumBreadcrumbsRightItems() {
return this.crumbs.minimumRightItems;
}
set minimumBreadcrumbsRightItems(value) {
this.crumbs.minimumRightItems = value;
}
/**
* Whether to show the full path in the breadcrumbs
*/
get showFullPath() {
return this.crumbs.fullPath;
}
set showFullPath(value) {
this.crumbs.fullPath = value;
}
/**
* Whether to show the file size column
*/
get showFileSizeColumn() {
return this._showFileSizeColumn;
}
set showFileSizeColumn(value) {
if (this.listing.setColumnVisibility) {
this.listing.setColumnVisibility('file_size', value);
this._showFileSizeColumn = value;
}
else {
console.warn('Listing does not support toggling column visibility');
}
}
/**
* Whether to show hidden files
*/
get showHiddenFiles() {
return this._showHiddenFiles;
}
set showHiddenFiles(value) {
this.model.showHiddenFiles(value);
this._showHiddenFiles = value;
}
/**
* Whether to show checkboxes next to files and folders
*/
get showFileCheckboxes() {
return this._showFileCheckboxes;
}
set showFileCheckboxes(value) {
if (this.listing.setColumnVisibility) {
this.listing.setColumnVisibility('is_selected', value);
this._showFileCheckboxes = value;
}
else {
console.warn('Listing does not support toggling column visibility');
}
}
/**
* Whether to show a text box to filter files by name.
*/
get showFileFilter() {
return this._showFileFilter;
}
set showFileFilter(value) {
var _a;
// If the old value was true and the new value is false, clear the filter
const oldValue = this.showFileFilter;
if (oldValue && !value) {
// Clear the search box input
if (this._fileFilterRef.current) {
this._fileFilterRef.current.value = '';
}
// Set a filter that doesn't exclude anything.
this.model.setFilter(value => {
return {};
});
this.model.refresh().catch(console.warn);
}
this._showFileFilter = value;
// Update widget visibility
this.filterToolbar.setHidden(!this.showFileFilter);
if (this.showFileFilter) {
(_a = this._fileFilterRef.current) === null || _a === void 0 ? void 0 : _a.focus();
}
}
/**
* Whether to sort notebooks above other files
*/
get sortNotebooksFirst() {
return this._sortNotebooksFirst;
}
set sortNotebooksFirst(value) {
if (this.listing.setNotebooksFirstSorting) {
this.listing.setNotebooksFirstSorting(value);
this._sortNotebooksFirst = value;
}
else {
console.warn('Listing does not support sorting notebooks first');
}
}
/**
* Whether to allow single click files and directories
*/
get singleClickNavigation() {
return this._allowSingleClick;
}
set singleClickNavigation(value) {
if (this.listing.setAllowSingleClickNavigation) {
this.listing.setAllowSingleClickNavigation(value);
this._allowSingleClick = value;
}
else {
console.warn('Listing does not support single click navigation');
}
}
/**
* Whether to allow upload of files.
*/
get allowFileUploads() {
return this._allowFileUploads;
}
set allowFileUploads(value) {
this.model.allowFileUploads = value;
if (this.listing.setAllowDragDropUpload) {
this.listing.setAllowDragDropUpload(value);
this._allowFileUploads = value;
}
else {
console.warn('Listing does not support setting upload');
}
}
/**
* Create an iterator over the listing's selected items.
*
* @returns A new iterator over the listing's selected items.
*/
selectedItems() {
return this.listing.selectedItems();
}
/**
* A signal emitted when the selection changes in the file browser.
*/
get selectionChanged() {
return this._selectionChanged;
}
/**
* Select an item by name.
*
* @param name - The name of the item to select.
*/
async selectItemByName(name) {
await this.listing.selectItemByName(name);
}
clearSelectedItems() {
this.listing.clearSelectedItems();
}
/**
* Rename the first currently selected item.
*
* @returns A promise that resolves with the new name of the item.
*/
rename() {
return this.listing.rename();
}
/**
* Cut the selected items.
*/
cut() {
this.listing.cut();
}
/**
* Copy the selected items.
*/
copy() {
this.listing.copy();
}
/**
* Paste the items from the clipboard.
*
* @returns A promise that resolves when the operation is complete.
*/
paste() {
return this.listing.paste();
}
async _createNew(options) {
// normalize the path if the file is created from a custom drive
if (options.path) {
const localPath = this._manager.services.contents.localPath(options.path);
options.path = this._toDrivePath(this.model.driveName, localPath);
}
try {
const model = await this._manager.newUntitled(options);
await this.listing.selectItemByName(model.name, true);
await this.rename();
return model;
}
catch (err) {
void showErrorMessage(this._trans.__('Error'), err);
throw err;
}
}
/**
* Create a new directory
*/
async createNewDirectory() {
if (this._directoryPending) {
return this._directoryPending;
}
this._directoryPending = this._createNew({
path: this.model.path,
type: 'directory'
});
try {
return await this._directoryPending;
}
finally {
this._directoryPending = null;
}
}
/**
* Create a new file
*/
async createNewFile(options) {
if (this._filePending) {
return this._filePending;
}
this._filePending = this._createNew({
path: this.model.path,
type: 'file',
ext: options.ext
});
try {
return await this._filePending;
}
finally {
this._filePending = null;
}
}
/**
* Delete the currently selected item(s).
*
* @returns A promise that resolves when the operation is complete.
*/
delete() {
return this.listing.delete();
}
/**
* Duplicate the currently selected item(s).
*
* @returns A promise that resolves when the operation is complete.
*/
duplicate() {
return this.listing.duplicate();
}
/**
* Select all listing items.
*/
selectAll() {
return this.listing.selectAll();
}
/**
* Download the currently selected item(s).
*/
download() {
return this.listing.download();
}
/**
* cd ..
*
* Go up one level in the directory tree.
*/
async goUp() {
return this.listing.goUp();
}
/**
* Shut down kernels on the applicable currently selected items.
*
* @returns A promise that resolves when the operation is complete.
*/
shutdownKernels() {
return this.listing.shutdownKernels();
}
/**
* Select next item.
*/
selectNext() {
this.listing.selectNext();
}
/**
* Select previous item.
*/
selectPrevious() {
this.listing.selectPrevious();
}
/**
* Find a model given a click.
*
* @param event - The mouse event.
*
* @returns The model for the selected file.
*/
modelForClick(event) {
return this.listing.modelForClick(event);
}
/**
* Create the underlying DirListing instance.
*
* @param options - The DirListing constructor options.
*
* @returns The created DirListing instance.
*/
createDirListing(options) {
return new DirListing(options);
}
/**
* Handle a connection lost signal from the model.
*/
_onConnectionFailure(sender, args) {
if (args instanceof ServerConnection.ResponseError &&
args.response.status === 404) {
const title = this._trans.__('Directory not found');
args.message = this._trans.__('Directory not found: "%1"', this.model.path);
void showErrorMessage(title, args);
}
}
/**
* Given a drive name and a local path, return the full
* drive path which includes the drive name and the local path.
*
* @param driveName the name of the drive
* @param localPath the local path on the drive.
*
* @returns the full drive path
*/
_toDrivePath(driveName, localPath) {
if (driveName === '') {
return localPath;
}
else {
return `${driveName}:${PathExt.removeSlash(localPath)}`;
}
}
}
//# sourceMappingURL=browser.js.map