diff --git a/src/plugins/driveBrowserPlugin.ts b/src/plugins/driveBrowserPlugin.ts index af6b446..2664dbc 100644 --- a/src/plugins/driveBrowserPlugin.ts +++ b/src/plugins/driveBrowserPlugin.ts @@ -5,6 +5,8 @@ import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; +import { IDocumentWidgetOpener } from '@jupyterlab/docmanager'; +import { IStatusBar } from '@jupyterlab/statusbar'; import { IFileBrowserFactory, FileBrowser, @@ -38,6 +40,67 @@ import { Drive } from '../contents'; import { setListingLimit } from '../requests'; import { CommandIDs } from '../token'; +/** + * Status bar widget for displaying drive information + */ +class DriveStatusWidget extends Widget { + constructor() { + super(); + this.node.classList.add( + 'drive-status-widget', + 'drive-status-loading', + 'lm-mod-hidden' + ); + + this._textSpan = document.createElement('span'); + this._textSpan.textContent = ''; + this.node.appendChild(this._textSpan); + + this._isLoading = false; + } + + updateStatus(text: string) { + this._textSpan.textContent = text; + } + + /** + * Update status when loading a directory or file + */ + setLoading(path: string, type: string) { + this._isLoading = true; + + if (type === 'directory') { + const displayPath = + path === '' ? 'Root' : path.split('/').pop() || 'Directory'; + this.updateStatus(`Opening: ${displayPath}`); + } else { + const fileName = path.split('/').pop() || 'File'; + this.updateStatus(`Opening: ${fileName}`); + } + this.removeClass('lm-mod-hidden'); + } + + /** + * Clear loading state and show current status + */ + setLoaded(path?: string) { + this._isLoading = false; + this.addClass('lm-mod-hidden'); + + this.updateStatus(''); + } + + /** + * Check if currently loading + */ + get isLoading(): boolean { + return this._isLoading; + } + + private _isLoading: boolean; + private _textSpan: HTMLSpanElement; +} + /** * The file browser factory ID. */ @@ -69,13 +132,15 @@ export const driveFileBrowser: JupyterFrontEndPlugin = { IFileBrowserFactory, IToolbarWidgetRegistry, ISettingRegistry, - ITranslator + ITranslator, + IDocumentWidgetOpener ], optional: [ IRouter, JupyterFrontEnd.ITreeResolver, ILabShell, - ILayoutRestorer + ILayoutRestorer, + IStatusBar ], activate: async ( app: JupyterFrontEnd, @@ -83,10 +148,12 @@ export const driveFileBrowser: JupyterFrontEndPlugin = { toolbarRegistry: IToolbarWidgetRegistry, settingsRegistry: ISettingRegistry, translator: ITranslator, + docWidgetOpener: IDocumentWidgetOpener, router: IRouter | null, tree: JupyterFrontEnd.ITreeResolver | null, labShell: ILabShell | null, - restorer: ILayoutRestorer | null + restorer: ILayoutRestorer | null, + statusBar: IStatusBar | null ): Promise => { console.log( 'JupyterLab extension jupyter-drives:drives-file-browser is activated!' @@ -125,6 +192,34 @@ export const driveFileBrowser: JupyterFrontEndPlugin = { restorer.add(driveBrowser, 'drive-file-browser'); } + // Register status bar widget + if (statusBar) { + const driveStatusWidget = new DriveStatusWidget(); + + statusBar.registerStatusItem('driveBrowserStatus', { + item: driveStatusWidget, + align: 'right', + rank: 500 + }); + + // Item/dir being opened + //@ts-expect-error listing is protected + driveBrowser.listing.onItemOpened.connect((_, args) => { + const { path, type } = args; + driveStatusWidget.setLoading(path, type); + }); + + const doneLoading = () => { + driveStatusWidget.setLoaded(); + }; + + // Item done opening + docWidgetOpener.opened.connect(doneLoading); + + // Directory done opening + driveBrowser.model.pathChanged.connect(doneLoading); + } + const uploader = new Uploader({ model: driveBrowser.model, translator }); toolbarRegistry.addFactory(FILE_BROWSER_FACTORY, 'uploader', () => { return uploader; diff --git a/src/requests.ts b/src/requests.ts index d9c2ce0..9c29119 100644 --- a/src/requests.ts +++ b/src/requests.ts @@ -102,6 +102,7 @@ export async function getContents( 'drives/' + driveName + '/' + options.path, 'GET' ); + // checking if we are dealing with a directory or a file const isDir: boolean = response.data.length !== undefined; diff --git a/style/base.css b/style/base.css index 204e0f5..b895cf3 100644 --- a/style/base.css +++ b/style/base.css @@ -162,3 +162,45 @@ li { color: var(--md-red-600); font-size: 0.7rem; } + +/* Status bar widget styling */ +.drive-status-widget { + font-size: 12px; + font-weight: 500; + color: var(--jp-ui-font-color1); + padding: 2px 8px; + border-radius: 3px; + background-color: var(--jp-layout-color2); + border: 1px solid var(--jp-border-color1); + transition: all 0.2s ease; +} + +.drive-status-widget:hover { + background-color: var(--jp-layout-color3); +} + +.drive-status-error { + color: var(--jp-error-color1); + background-color: var(--jp-error-color0); + border-color: var(--jp-error-color1); +} + +.drive-status-loading { + color: var(--jp-ui-font-color1); + background-color: var(--jp-layout-color3); + animation: pulse 1.5s ease-in-out infinite; +} + +@keyframes pulse { + 0% { + opacity: 1; + } + + 50% { + opacity: 0.7; + } + + 100% { + opacity: 1; + } +}