From 253ba77a00302e59b5a6093d7d635eff52690440 Mon Sep 17 00:00:00 2001 From: Duc Trung Le Date: Mon, 4 Sep 2023 11:24:31 +0200 Subject: [PATCH] Refactor Voila plugins so that it's easily reusable in Voici (#1387) * Fix missing theme name * Refactor FileBrowser widget * Refactor VoilaApp --- packages/voila/src/app.ts | 6 ++--- packages/voila/src/index.ts | 3 +++ packages/voila/src/plugins/tree/browser.ts | 18 +++++++++++++++ packages/voila/src/plugins/tree/index.ts | 18 +++++++++------ packages/voila/src/plugins/tree/listing.ts | 23 ++++++++++++++----- packages/voila/src/plugins/widget.ts | 1 - packages/voila/src/tree.ts | 7 +++--- packages/voila/style/index.js | 1 + .../templates/base/spinner.macro.html.j2 | 4 +++- .../jupyter/voila/templates/lab/index.html.j2 | 6 +++-- 10 files changed, 63 insertions(+), 24 deletions(-) diff --git a/packages/voila/src/app.ts b/packages/voila/src/app.ts index 3e1c7ea67..f06399f2e 100644 --- a/packages/voila/src/app.ts +++ b/packages/voila/src/app.ts @@ -40,7 +40,7 @@ export class VoilaApp extends JupyterFrontEnd { /** * The name of the application. */ - readonly name = 'Voila'; + readonly name: string = 'Voila'; /** * A namespace/prefix plugins may use to denote their provenance. @@ -136,8 +136,8 @@ export class VoilaApp extends JupyterFrontEnd { return this._widgetManager; } - private _widgetManager: KernelWidgetManager | null = null; - private _widgetManagerPromise = new PromiseDelegate(); + protected _widgetManager: KernelWidgetManager | null = null; + protected _widgetManagerPromise = new PromiseDelegate(); } /** diff --git a/packages/voila/src/index.ts b/packages/voila/src/index.ts index c786492a7..a185926a7 100644 --- a/packages/voila/src/index.ts +++ b/packages/voila/src/index.ts @@ -11,3 +11,6 @@ export * from './app'; export * from './shell'; export * from './voilaplugins'; export * from './tools'; +export * from './plugins/tree/browser'; +export * from './plugins/tree/listing'; +export * from './plugins/themes/thememanager'; diff --git a/packages/voila/src/plugins/tree/browser.ts b/packages/voila/src/plugins/tree/browser.ts index e40c7dd9c..e20ce2301 100644 --- a/packages/voila/src/plugins/tree/browser.ts +++ b/packages/voila/src/plugins/tree/browser.ts @@ -1,7 +1,18 @@ import { FileBrowser, DirListing } from '@jupyterlab/filebrowser'; import { VoilaDirListing } from './listing'; +import { Widget } from '@lumino/widgets'; export class VoilaFileBrowser extends FileBrowser { + constructor(options: VoilaFileBrowser.IOptions) { + const { urlFactory, title, ...rest } = options; + super(rest); + (this.listing as VoilaDirListing).urlFactory = urlFactory; + this.addClass('voila-FileBrowser'); + + const titleWidget = new Widget(); + titleWidget.node.innerText = title; + this.toolbar.addItem('title', titleWidget); + } /** * Create the underlying DirListing instance. * @@ -13,3 +24,10 @@ export class VoilaFileBrowser extends FileBrowser { return new VoilaDirListing(options); } } + +export namespace VoilaFileBrowser { + export interface IOptions extends FileBrowser.IOptions { + urlFactory: (path: string) => string; + title: string; + } +} diff --git a/packages/voila/src/plugins/tree/index.ts b/packages/voila/src/plugins/tree/index.ts index 6a879d52e..6f2972274 100644 --- a/packages/voila/src/plugins/tree/index.ts +++ b/packages/voila/src/plugins/tree/index.ts @@ -10,12 +10,13 @@ import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; +import { PageConfig, URLExt } from '@jupyterlab/coreutils'; import { DocumentManager } from '@jupyterlab/docmanager'; import { DocumentRegistry } from '@jupyterlab/docregistry'; import { FilterFileBrowserModel } from '@jupyterlab/filebrowser'; +import { Widget } from '@lumino/widgets'; import { VoilaFileBrowser } from './browser'; -import { Widget } from '@lumino/widgets'; /** * The voila file browser provider. @@ -34,19 +35,22 @@ export const treeWidgetPlugin: JupyterFrontEndPlugin = { manager: docManager, refreshInterval: 2147483646 }); + const urlFactory = (path: string) => { + const baseUrl = PageConfig.getBaseUrl(); + const frontend = PageConfig.getOption('frontend'); + const query = PageConfig.getOption('query'); + return URLExt.join(baseUrl, frontend, 'render', path) + `?${query}`; + }; const fb = new VoilaFileBrowser({ id: 'filebrowser', - model: fbModel + model: fbModel, + urlFactory, + title: 'Select items to open with VoilĂ .' }); - fb.addClass('voila-FileBrowser'); fb.showFileCheckboxes = false; fb.showLastModifiedColumn = false; - const title = new Widget(); - title.node.innerText = 'Select items to open with VoilĂ .'; - fb.toolbar.addItem('title', title); - const spacerTop = new Widget(); spacerTop.addClass('spacer-top-widget'); app.shell.add(spacerTop, 'main'); diff --git a/packages/voila/src/plugins/tree/listing.ts b/packages/voila/src/plugins/tree/listing.ts index bef21edbe..284bf1962 100644 --- a/packages/voila/src/plugins/tree/listing.ts +++ b/packages/voila/src/plugins/tree/listing.ts @@ -1,9 +1,15 @@ import { DirListing } from '@jupyterlab/filebrowser'; import { Contents } from '@jupyterlab/services'; import { showErrorMessage } from '@jupyterlab/apputils'; -import { PageConfig, URLExt } from '@jupyterlab/coreutils'; export class VoilaDirListing extends DirListing { + get urlFactory(): VoilaDirListing.IUrlFactory | undefined { + return this._urlFactory; + } + set urlFactory(f: VoilaDirListing.IUrlFactory | undefined) { + this._urlFactory = f; + } + /** * Handle the opening of an item. */ @@ -17,11 +23,11 @@ export class VoilaDirListing extends DirListing { .catch((error) => showErrorMessage('Open directory', error)); } else { const path = item.path; - const baseUrl = PageConfig.getBaseUrl(); - const frontend = PageConfig.getOption('frontend'); - const query = PageConfig.getOption('query'); - const url = URLExt.join(baseUrl, frontend, 'render', path) + `?${query}`; - window.open(url, '_blank'); + if (this.urlFactory) { + window.open(this.urlFactory(path), '_blank'); + } else { + showErrorMessage('Open file', 'URL Factory is not defined'); + } } } @@ -30,4 +36,9 @@ export class VoilaDirListing extends DirListing { this.evtDblClick(event as MouseEvent); } } + private _urlFactory: VoilaDirListing.IUrlFactory | undefined; +} + +export namespace VoilaDirListing { + export type IUrlFactory = (path: string) => string; } diff --git a/packages/voila/src/plugins/widget.ts b/packages/voila/src/plugins/widget.ts index bddab5804..59d579318 100644 --- a/packages/voila/src/plugins/widget.ts +++ b/packages/voila/src/plugins/widget.ts @@ -117,7 +117,6 @@ export const renderOutputsPlugin: JupyterFrontEndPlugin = { const cellOutputs = document.body.querySelectorAll( 'script[type="application/vnd.voila.cell-output+json"]' ); - cellOutputs.forEach(async (cellOutput) => { const model = JSON.parse(cellOutput.innerHTML); diff --git a/packages/voila/src/tree.ts b/packages/voila/src/tree.ts index 0f601e54e..53cbc4cf1 100644 --- a/packages/voila/src/tree.ts +++ b/packages/voila/src/tree.ts @@ -9,7 +9,6 @@ * Distributed under the terms of the Modified BSD License. * ****************************************************************************/ import '../style/index.js'; -import '@jupyterlab/filebrowser/style/index.js'; import { PageConfig, URLExt } from '@jupyterlab/coreutils'; import { ContentsManager, Drive } from '@jupyterlab/services'; @@ -27,7 +26,7 @@ import { widgetManager } from './voilaplugins'; -const disabled = [ +export const TREE_DISABLED_EXTENSIONS = [ '@jupyter-widgets/jupyterlab-manager:plugin', '@jupyter-widgets/jupyterlab-manager:saveWidgetState', '@jupyter-widgets/jupyterlab-manager:base-2.0.0', @@ -101,7 +100,7 @@ async function main() { ); federatedExtensions.forEach((p) => { if (p.status === 'fulfilled') { - for (const plugin of activePlugins(p.value, disabled)) { + for (const plugin of activePlugins(p.value, TREE_DISABLED_EXTENSIONS)) { mods.push(plugin); } } else { @@ -115,7 +114,7 @@ async function main() { ); federatedMimeExtensions.forEach((p) => { if (p.status === 'fulfilled') { - for (const plugin of activePlugins(p.value, disabled)) { + for (const plugin of activePlugins(p.value, TREE_DISABLED_EXTENSIONS)) { mimeExtensions.push(plugin); } } else { diff --git a/packages/voila/style/index.js b/packages/voila/style/index.js index 267061468..9def4e93b 100644 --- a/packages/voila/style/index.js +++ b/packages/voila/style/index.js @@ -4,4 +4,5 @@ import '@jupyterlab/apputils/style/index.js'; import '@jupyterlab/rendermime/style/index.js'; import '@jupyterlab/docregistry/style/index.js'; import '@jupyterlab/markedparser-extension/style/index.js'; +import '@jupyterlab/filebrowser/style/index.js'; import './base.css'; diff --git a/share/jupyter/voila/templates/base/spinner.macro.html.j2 b/share/jupyter/voila/templates/base/spinner.macro.html.j2 index ff6325b2b..627c8ee14 100644 --- a/share/jupyter/voila/templates/base/spinner.macro.html.j2 +++ b/share/jupyter/voila/templates/base/spinner.macro.html.j2 @@ -9,6 +9,8 @@ height: 75vh; color: var(--jp-content-font-color1); font-family: sans-serif; + z-index: 100; + position: relative; } .spinner { @@ -36,7 +38,7 @@ {% endmacro %} {% macro html() %} -