Browse Source

Initial work on document loading spinner.

Ian Rose 7 years ago
parent
commit
f894fb7748

+ 1 - 0
packages/apputils/src/index.ts

@@ -14,6 +14,7 @@ export * from './iframe';
 export * from './instancetracker';
 export * from './mainmenu';
 export * from './sanitizer';
+export * from './spinner';
 export * from './splash';
 export * from './styling';
 export * from './thememanager';

+ 28 - 0
packages/apputils/src/spinner.ts

@@ -0,0 +1,28 @@
+/*-----------------------------------------------------------------------------
+| Copyright (c) Jupyter Development Team.
+| Distributed under the terms of the Modified BSD License.
+|----------------------------------------------------------------------------*/
+
+
+import {
+  Widget 
+} from '@phosphor/widgets';
+
+
+/**
+ * The spinner class.
+ */
+export
+class Spinner extends Widget {
+  /**
+   * Construct a spinner widget.
+   */
+  constructor () {
+    super();
+    this.addClass('jp-Spinner');
+    let content = document.createElement('div');
+    content.className = 'jp-SpinnerContent';
+    content.textContent = 'Loading, yo';
+    this.node.appendChild(content);
+  }
+}

+ 1 - 0
packages/apputils/style/index.css

@@ -9,5 +9,6 @@
 @import './dialog.css';
 @import './hoverbox.css';
 @import './iframe.css';
+@import './spinner.css';
 @import './styling.css';
 @import './toolbar.css';

+ 67 - 0
packages/apputils/style/spinner.css

@@ -0,0 +1,67 @@
+/*-----------------------------------------------------------------------------
+| Copyright (c) 2017, Jupyter Development Team.
+|
+| Distributed under the terms of the Modified BSD License.
+|----------------------------------------------------------------------------*/
+
+
+.jp-Spinner {
+  position: absolute;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 10;
+  width: 100%;
+  height: 100%;
+  background: var(--jp-layout-color0);
+
+}
+
+
+.jp-SpinnerContent {
+  font-size: 10px;
+  margin: 50px auto;
+  text-indent: -9999em;
+  width: 3em;
+  height: 3em;
+  border-radius: 50%;
+  background: var(--jp-brand-color3);
+  background: linear-gradient(to right, var(--jp-brand-color3) 10%, rgba(255, 255, 255, 0) 42%);
+  position: relative;
+  animation: load3 1.4s infinite linear;
+  transform: translateZ(0);
+}
+
+.jp-SpinnerContent:before {
+  width: 50%;
+  height: 50%;
+  background: var(--jp-brand-color3);
+  border-radius: 100% 0 0 0;
+  position: absolute;
+  top: 0;
+  left: 0;
+  content: '';
+}
+
+.jp-SpinnerContent:after {
+  background: var(--jp-layout-color0);
+  width: 75%;
+  height: 75%;
+  border-radius: 50%;
+  content: '';
+  margin: auto;
+  position: absolute;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+}
+
+@keyframes load3 {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}

+ 6 - 1
packages/docmanager-extension/src/index.ts

@@ -6,7 +6,7 @@ import {
 } from '@jupyterlab/application';
 
 import {
-  showDialog, showErrorMessage, Dialog, ICommandPalette, IMainMenu
+  showDialog, showErrorMessage, Spinner, Dialog, ICommandPalette, IMainMenu
 } from '@jupyterlab/apputils';
 
 import {
@@ -90,6 +90,11 @@ const plugin: JupyterLabPlugin<IDocumentManager> = {
         };
         if (!widget.isAttached) {
           app.shell.addToMainArea(widget);
+          let spinner = new Spinner();
+          widget.node.appendChild(spinner.node);
+          setTimeout(() => {
+            widget.node.removeChild(spinner.node);
+          }, 1000);
         }
         app.shell.activateById(widget.id);
 

+ 7 - 7
packages/docmanager/src/manager.ts

@@ -145,7 +145,7 @@ class DocumentManager implements IDisposable {
    *  Uses the same widget factory and context as the source, or returns
    *  `undefined` if the source widget is not managed by this manager.
    */
-  cloneWidget(widget: Widget): Widget | undefined {
+  cloneWidget(widget: Widget): DocumentRegistry.IReadyWidget | undefined {
     return this._widgetManager.cloneWidget(widget);
   }
 
@@ -253,7 +253,7 @@ class DocumentManager implements IDisposable {
    * This can be used to use an existing widget instead of opening
    * a new widget.
    */
-  findWidget(path: string, widgetName='default'): Widget | undefined {
+  findWidget(path: string, widgetName='default'): DocumentRegistry.IReadyWidget | undefined {
     if (widgetName === 'default') {
       let factory = this.registry.defaultWidgetFactory(path);
       if (!factory) {
@@ -295,7 +295,7 @@ class DocumentManager implements IDisposable {
    * This function will return `undefined` if a valid widget factory
    * cannot be found.
    */
-  open(path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): Widget | undefined {
+  open(path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): DocumentRegistry.IReadyWidget | undefined {
     return this._createOrOpenDocument('open', path, widgetName, kernel);
   }
 
@@ -315,7 +315,7 @@ class DocumentManager implements IDisposable {
    * This function will return `undefined` if a valid widget factory
    * cannot be found.
    */
-  openOrReveal(path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): Widget | undefined {
+  openOrReveal(path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): DocumentRegistry.IReadyWidget | undefined {
     let widget = this.findWidget(path, widgetName);
     if (widget) {
       this._opener.open(widget);
@@ -378,7 +378,7 @@ class DocumentManager implements IDisposable {
    * Create a context from a path and a model factory.
    */
   private _createContext(path: string, factory: DocumentRegistry.ModelFactory, kernelPreference: IClientSession.IKernelPreference): Private.IContext {
-    let adopter = (widget: Widget) => {
+    let adopter = (widget: DocumentRegistry.IReadyWidget) => {
       this._widgetManager.adoptWidget(context, widget);
       this._opener.open(widget);
     };
@@ -434,7 +434,7 @@ class DocumentManager implements IDisposable {
    * The two cases differ in how the document context is handled, but the creation
    * of the widget and launching of the kernel are identical.
    */
-  private _createOrOpenDocument(which: 'open'|'create', path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): Widget | undefined {
+  private _createOrOpenDocument(which: 'open'|'create', path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): DocumentRegistry.IReadyWidget | undefined {
     let widgetFactory = this._widgetFactoryFor(path, widgetName);
     if (!widgetFactory) {
       return undefined;
@@ -528,7 +528,7 @@ namespace DocumentManager {
     /**
      * Open the given widget.
      */
-    open(widget: Widget): void;
+    open(widget: DocumentRegistry.IReadyWidget): void;
   }
 }
 

+ 4 - 4
packages/docmanager/src/widgetmanager.ts

@@ -125,7 +125,7 @@ class DocumentWidgetManager implements IDisposable {
    *
    * @param widget - The widget to adopt.
    */
-  adoptWidget(context: DocumentRegistry.Context, widget: Widget): void {
+  adoptWidget(context: DocumentRegistry.Context, widget: DocumentRegistry.IReadyWidget): void {
     let widgets = Private.widgetsProperty.get(context);
     widgets.push(widget);
     MessageLoop.installMessageHook(widget, this);
@@ -146,7 +146,7 @@ class DocumentWidgetManager implements IDisposable {
    * This can be used to use an existing widget instead of opening
    * a new widget.
    */
-  findWidget(context: DocumentRegistry.Context, widgetName: string): Widget | undefined {
+  findWidget(context: DocumentRegistry.Context, widgetName: string): DocumentRegistry.IReadyWidget | undefined {
     let widgets = Private.widgetsProperty.get(context);
     if (!widgets) {
       return undefined;
@@ -182,7 +182,7 @@ class DocumentWidgetManager implements IDisposable {
    *  Uses the same widget factory and context as the source, or throws
    *  if the source widget is not managed by this manager.
    */
-  cloneWidget(widget: Widget): Widget | undefined {
+  cloneWidget(widget: Widget): DocumentRegistry.IReadyWidget | undefined {
     let context = Private.contextProperty.get(widget);
     if (!context) {
       return undefined;
@@ -448,7 +448,7 @@ namespace Private {
    * A private attached property for the widgets associated with a context.
    */
   export
-  const widgetsProperty = new AttachedProperty<DocumentRegistry.Context, Widget[]>({
+  const widgetsProperty = new AttachedProperty<DocumentRegistry.Context, DocumentRegistry.IReadyWidget[]>({
     name: 'widgets',
     create: () => []
   });