소스 검색

wip update filebrowser interfaces

Steven Silvester 8 년 전
부모
커밋
62b668fbb4
7개의 변경된 파일350개의 추가작업 그리고 133개의 파일을 삭제
  1. 10 2
      examples/filebrowser/src/index.ts
  2. 37 8
      src/filebrowser/browser.ts
  3. 33 4
      src/filebrowser/buttons.ts
  4. 21 2
      src/filebrowser/crumbs.ts
  5. 207 111
      src/filebrowser/listing.ts
  6. 32 4
      src/filebrowser/model.ts
  7. 10 2
      src/filebrowser/plugin.ts

+ 10 - 2
examples/filebrowser/src/index.ts

@@ -98,8 +98,16 @@ function createApp(sessionManager: SessionManager, kernelspecs: IKernel.ISpecMod
     canStartKernel: true
   });
 
-  let fbModel = new FileBrowserModel(contentsManager, sessionManager, kernelspecs);
-  let fbWidget = new FileBrowserWidget(fbModel, docManager, opener);
+  let fbModel = new FileBrowserModel({
+    contentsManager,
+    sessionManager,
+    kernelspecs
+  });
+  let fbWidget = new FileBrowserWidget({
+    model: fbModel,
+    manager: docManager,
+    opener
+  });
 
   let panel = new SplitPanel();
   panel.id = 'main';

+ 37 - 8
src/filebrowser/browser.ts

@@ -82,16 +82,17 @@ class FileBrowserWidget extends Widget {
    *
    * @param model - The file browser view model.
    */
-  constructor(model: FileBrowserModel, manager: DocumentManager, opener: IWidgetOpener) {
+  constructor(options: FileBrowserWidget.IOptions) {
     super();
     this.addClass(FILE_BROWSER_CLASS);
-    this._model = model;
-    this._model.refreshed.connect(this._handleRefresh, this);
-    this._crumbs = new BreadCrumbs(model);
-    this._buttons = new FileButtons(model, manager, opener);
-    this._listing = new DirListing(model, manager, opener);
-    this._manager = manager;
-    this._opener = opener;
+    let model = this._model = options.model;
+    let manager = this._manager = options.manager;
+    let opener = this._opener = options.opener;
+
+    model.refreshed.connect(this._handleRefresh, this);
+    this._crumbs = new BreadCrumbs({ model });
+    this._buttons = new FileButtons({ model, manager, opener });
+    this._listing = new DirListing({ model, manager, opener });
 
     model.fileChanged.connect((fbModel, args) => {
       if (args.newValue) {
@@ -311,3 +312,31 @@ class FileBrowserWidget extends Widget {
   private _manager: DocumentManager = null;
   private _opener: IWidgetOpener = null;
 }
+
+
+/**
+ * The namespace for the `FileBrowserWidget` class statics.
+ */
+export
+namespace FileBrowserWidget {
+  /**
+   * An options object for initializing a file browser widget.
+   */
+  export
+  interface IOptions {
+    /**
+     * A file browser model instance.
+     */
+    model: FileBrowserModel;
+
+    /**
+     * A document manager instance.
+     */
+    manager: DocumentManager;
+
+    /**
+     * A widget opener function.
+     */
+    opener: IWidgetOpener;
+  }
+}

+ 33 - 4
src/filebrowser/buttons.ts

@@ -93,10 +93,10 @@ class FileButtons extends Widget {
    *
    * @param model - The file browser view model.
    */
-  constructor(model: FileBrowserModel, manager: DocumentManager, opener: IWidgetOpener) {
+  constructor(options: FileButtons.IOptions) {
     super();
     this.addClass(FILE_BUTTONS_CLASS);
-    this._model = model;
+    this._model = options.model;
 
     this._buttons.create.onmousedown = this._onCreateButtonPressed;
     this._buttons.upload.onclick = this._onUploadButtonClicked;
@@ -108,8 +108,8 @@ class FileButtons extends Widget {
     node.appendChild(this._buttons.upload);
     node.appendChild(this._buttons.refresh);
 
-    this._manager = manager;
-    this._opener = opener;
+    this._manager = options.manager;
+    this._opener = options.opener;
   }
 
   /**
@@ -243,6 +243,35 @@ class FileButtons extends Widget {
 }
 
 
+
+/**
+ * The namespace for the `FileButtons` class statics.
+ */
+export
+namespace FileButtons {
+  /**
+   * An options object for initializing a file buttons widget.
+   */
+  export
+  interface IOptions {
+    /**
+     * A file browser model instance.
+     */
+    model: FileBrowserModel;
+
+    /**
+     * A document manager instance.
+     */
+    manager: DocumentManager;
+
+    /**
+     * A widget opener function.
+     */
+    opener: IWidgetOpener;
+  }
+}
+
+
 /**
  * The namespace for the `FileButtons` private data.
  */

+ 21 - 2
src/filebrowser/crumbs.ts

@@ -52,9 +52,9 @@ class BreadCrumbs extends Widget {
    *
    * @param model - The file browser view model.
    */
-  constructor(model: FileBrowserModel) {
+  constructor(options: BreadCrumbs.IOptions) {
     super();
-    this._model = model;
+    this._model = options.model;
     this.addClass(BREADCRUMB_CLASS);
     this._crumbs = Private.createCrumbs();
     this._crumbSeps = Private.createCrumbSeparators();
@@ -263,6 +263,25 @@ class BreadCrumbs extends Widget {
 }
 
 
+
+/**
+ * The namespace for the `BreadCrumbs` class statics.
+ */
+export
+namespace BreadCrumbs {
+  /**
+   * An options object for initializing a bread crumb widget.
+   */
+  export
+  interface IOptions {
+    /**
+     * A file browser model instance.
+     */
+    model: FileBrowserModel;
+  }
+}
+
+
 /**
  * The namespace for the crumbs private data.
  */

+ 207 - 111
src/filebrowser/listing.ts

@@ -178,133 +178,34 @@ class DirListing extends Widget {
    */
   static createNode(): HTMLElement {
     let node = document.createElement('div');
+    let header = document.createElement('div');
     let content = document.createElement('ul');
-    let header = this.createHeaderNode();
     content.className = CONTENT_CLASS;
+    header.className = HEADER_CLASS;
     node.appendChild(header);
     node.appendChild(content);
     node.tabIndex = 1;
     return node;
   }
 
-  /**
-   * Create the header node for a dir listing.
-   *
-   * @returns A new DOM node to use as the dir listing header.
-   *
-   * #### Notes
-   * This method may be reimplemented to create custom headers.
-   */
-  static createHeaderNode(): HTMLElement {
-    let node = document.createElement('div');
-    let name = createItemNode('Name');
-    let modified = createItemNode('Last Modified');
-    node.className = HEADER_CLASS;
-    name.classList.add(NAME_ID_CLASS);
-    name.classList.add(SELECTED_CLASS);
-    modified.classList.add(MODIFIED_ID_CLASS);
-    node.appendChild(name);
-    node.appendChild(modified);
-    return node;
-
-    function createItemNode(label: string): HTMLElement {
-      let node = document.createElement('div');
-      let text = document.createElement('span');
-      let icon = document.createElement('span');
-      node.className = HEADER_ITEM_CLASS;
-      text.className = HEADER_ITEM_TEXT_CLASS;
-      icon.className = HEADER_ITEM_ICON_CLASS;
-      text.textContent = label;
-      node.appendChild(text);
-      node.appendChild(icon);
-      return node;
-    }
-  }
-
-  /**
-   * Create a new item node for a dir listing.
-   *
-   * @returns A new DOM node to use as a content item.
-   *
-   * #### Notes
-   * This method may be reimplemented to create custom items.
-   */
-  static createItemNode(): HTMLElement {
-    let node = document.createElement('li');
-    let icon = document.createElement('span');
-    let text = document.createElement('span');
-    let modified = document.createElement('span');
-    node.className = ITEM_CLASS;
-    icon.className = ITEM_ICON_CLASS;
-    text.className = ITEM_TEXT_CLASS;
-    modified.className = ITEM_MODIFIED_CLASS;
-    node.appendChild(icon);
-    node.appendChild(text);
-    node.appendChild(modified);
-    return node;
-  }
-
-  /**
-   * Update an item node to reflect the current state of a model.
-   *
-   * @param node - A node created by a call to [[createItemNode]].
-   *
-   * @param model - The model object to use for the item state.
-   *
-   * #### Notes
-   * This is called automatically when the item should be updated.
-   *
-   * If the [[createItemNode]] method is reimplemented, this method
-   * should also be reimplemented so that the item state is properly
-   * updated.
-   */
-  static updateItemNode(node: HTMLElement, model: IContentsModel) {
-    let icon = node.firstChild as HTMLElement;
-    let text = icon.nextSibling as HTMLElement;
-    let modified = text.nextSibling as HTMLElement;
-
-    let type: string;
-    switch (model.type) {
-    case 'directory':
-      type = FOLDER_TYPE_CLASS;
-      break;
-    case 'notebook':
-      type = NOTEBOOK_TYPE_CLASS;
-      break;
-    default:
-      type = FILE_TYPE_CLASS;
-      break;
-    }
-
-    let modText = '';
-    let modTitle = '';
-    if (model.last_modified) {
-      let time = moment(model.last_modified).fromNow();
-      modText = time === 'a few seconds ago' ? 'seconds ago' : time;
-      modTitle = moment(model.last_modified).format('YYYY-MM-DD HH:mm');
-    }
-
-    node.className = `${ITEM_CLASS} ${type}`;
-    text.textContent = model.name;
-    modified.textContent = modText;
-    modified.title = modTitle;
-  }
-
   /**
    * Construct a new file browser directory listing widget.
    *
    * @param model - The file browser view model.
    */
-  constructor(model: FileBrowserModel, manager: DocumentManager, opener: IWidgetOpener) {
+  constructor(options: DirListing.IOptions) {
     super();
     this.addClass(DIR_LISTING_CLASS);
-    this._model = model;
+    this._model = options.model;
     this._model.refreshed.connect(this._onModelRefreshed, this);
     this._model.pathChanged.connect(this._onPathChanged, this);
     this._editNode = document.createElement('input');
     this._editNode.className = EDITOR_CLASS;
-    this._manager = manager;
-    this._opener = opener;
+    this._manager = options.manager;
+    this._opener = options.opener;
+    this._renderer = options.renderer || DirListing.defaultRenderer;
+    let headerNode = utils.findElement(this.node, HEADER_CLASS);
+    this._renderer.populateHeaderNode(headerNode);
   }
 
   /**
@@ -649,7 +550,7 @@ class DirListing extends Widget {
     let items = this._model.sortedItems;
     let nodes = this._items;
     let content = utils.findElement(this.node, CONTENT_CLASS);
-    let subtype = this.constructor as typeof DirListing;
+    let renderer = this._renderer;
 
     this.removeClass(MULTI_SELECTED_CLASS);
     this.removeClass(SELECTED_CLASS);
@@ -662,14 +563,14 @@ class DirListing extends Widget {
 
     // Add any missing item nodes.
     while (nodes.length < items.length) {
-      let node = subtype.createItemNode();
+      let node = renderer.createItemNode();
       nodes.push(node);
       content.appendChild(node);
     }
 
     // Update the node states to match the model contents.
     for (let i = 0, n = items.length; i < n; ++i) {
-      subtype.updateItemNode(nodes[i], items[i]);
+      renderer.updateItemNode(nodes[i], items[i]);
       if (this._selection[items[i].name]) {
         nodes[i].classList.add(SELECTED_CLASS);
         if (this._isCut && this._model.path === this._prevPath) {
@@ -1304,9 +1205,204 @@ class DirListing extends Widget {
   private _manager: DocumentManager = null;
   private _opener: IWidgetOpener = null;
   private _selection: { [key: string]: boolean; } = Object.create(null);
+  private _renderer: DirListing.IRenderer = null;
+}
+
+
+/**
+ * The namespace for the `DirListing` class statics.
+ */
+export
+namespace DirListing {
+  /**
+   * An options object for initializing a file browser directory listing.
+   */
+  export
+  interface IOptions {
+    /**
+     * A file browser model instance.
+     */
+    model: FileBrowserModel;
+
+    /**
+     * A document manager instance.
+     */
+    manager: DocumentManager;
+
+    /**
+     * A widget opener function.
+     */
+    opener: IWidgetOpener;
+
+    /**
+     * A renderer for file items.
+     *
+     * The default is a shared `Renderer` instance.
+     */
+    renderer?: IRenderer;
+  }
+
+
+  /**
+   * The render interface for file browser listing options.
+   */
+  export
+  interface IRenderer {
+    /**
+     * Populate and empty header node for a dir listing.
+     *
+     * @returns A new DOM node to use as the dir listing header.
+     *
+     * #### Notes
+     * This method may be reimplemented to create custom headers.
+     */
+    populateHeaderNode(node: HTMLElement): void;
+
+    /**
+     * Create a new item node for a dir listing.
+     *
+     * @returns A new DOM node to use as a content item.
+     *
+     * #### Notes
+     * This method may be reimplemented to create custom items.
+     */
+    createItemNode(): HTMLElement;
+
+    /**
+     * Update an item node to reflect the current state of a model.
+     *
+     * @param node - A node created by a call to [[createItemNode]].
+     *
+     * @param model - The model object to use for the item state.
+     *
+     * #### Notes
+     * This is called automatically when the item should be updated.
+     *
+     * If the [[createItemNode]] method is reimplemented, this method
+     * should also be reimplemented so that the item state is properly
+     * updated.
+     */
+    updateItemNode(node: HTMLElement, model: IContentsModel): void;
+  }
+
+  /**
+   * The default implementation of an `IRenderer`.
+   */
+  export
+  class Renderer implements IRenderer {
+    /**
+     * Populate and empty header node for a dir listing.
+     *
+     * @returns A new DOM node to use as the dir listing header.
+     *
+     * #### Notes
+     * This method may be reimplemented to create custom headers.
+     */
+    populateHeaderNode(node: HTMLElement): void {
+      let name = this._createHeaderItemNode('Name');
+      let modified = this._createHeaderItemNode('Last Modified');
+      name.classList.add(NAME_ID_CLASS);
+      name.classList.add(SELECTED_CLASS);
+      modified.classList.add(MODIFIED_ID_CLASS);
+      node.appendChild(name);
+      node.appendChild(modified);
+    }
+
+    /**
+     * Create a new item node for a dir listing.
+     *
+     * @returns A new DOM node to use as a content item.
+     *
+     * #### Notes
+     * This method may be reimplemented to create custom items.
+     */
+    createItemNode(): HTMLElement {
+      let node = document.createElement('li');
+      let icon = document.createElement('span');
+      let text = document.createElement('span');
+      let modified = document.createElement('span');
+      node.className = ITEM_CLASS;
+      icon.className = ITEM_ICON_CLASS;
+      text.className = ITEM_TEXT_CLASS;
+      modified.className = ITEM_MODIFIED_CLASS;
+      node.appendChild(icon);
+      node.appendChild(text);
+      node.appendChild(modified);
+      return node;
+    }
+
+    /**
+     * Update an item node to reflect the current state of a model.
+     *
+     * @param node - A node created by a call to [[createItemNode]].
+     *
+     * @param model - The model object to use for the item state.
+     *
+     * #### Notes
+     * This is called automatically when the item should be updated.
+     *
+     * If the [[createItemNode]] method is reimplemented, this method
+     * should also be reimplemented so that the item state is properly
+     * updated.
+     */
+    updateItemNode(node: HTMLElement, model: IContentsModel): void {
+      let icon = node.firstChild as HTMLElement;
+      let text = icon.nextSibling as HTMLElement;
+      let modified = text.nextSibling as HTMLElement;
+
+      let type: string;
+      switch (model.type) {
+      case 'directory':
+        type = FOLDER_TYPE_CLASS;
+        break;
+      case 'notebook':
+        type = NOTEBOOK_TYPE_CLASS;
+        break;
+      default:
+        type = FILE_TYPE_CLASS;
+        break;
+      }
+
+      let modText = '';
+      let modTitle = '';
+      if (model.last_modified) {
+        let time = moment(model.last_modified).fromNow();
+        modText = time === 'a few seconds ago' ? 'seconds ago' : time;
+        modTitle = moment(model.last_modified).format('YYYY-MM-DD HH:mm');
+      }
+
+      node.className = `${ITEM_CLASS} ${type}`;
+      text.textContent = model.name;
+      modified.textContent = modText;
+      modified.title = modTitle;
+    }
+
+    /**
+     * Create a node for a header item.
+     */
+    private _createHeaderItemNode(label: string): HTMLElement {
+      let node = document.createElement('div');
+      let text = document.createElement('span');
+      let icon = document.createElement('span');
+      node.className = HEADER_ITEM_CLASS;
+      text.className = HEADER_ITEM_TEXT_CLASS;
+      icon.className = HEADER_ITEM_ICON_CLASS;
+      text.textContent = label;
+      node.appendChild(text);
+      node.appendChild(icon);
+      return node;
+    }
+  }
+
+  /**
+   * The default `IRenderer` instance.
+   */
+  export
+  const defaultRenderer = new Renderer();
 }
 
 
+
 /**
  * The namespace for the listing private data.
  */

+ 32 - 4
src/filebrowser/model.ts

@@ -30,10 +30,10 @@ class FileBrowserModel implements IDisposable {
   /**
    * Construct a new file browser view model.
    */
-  constructor(contentsManager: IContentsManager, sessionManager: ISession.IManager, specs: IKernel.ISpecModels) {
-    this._contentsManager = contentsManager;
-    this._sessionManager = sessionManager;
-    this._specs = specs;
+  constructor(options: FileBrowserModel.IOptions) {
+    this._contentsManager = options.contentsManager;
+    this._sessionManager = options.sessionManager;
+    this._specs = options.kernelspecs;
     this._model = { path: '', name: '/', type: 'directory', content: [] };
     this.cd();
   }
@@ -471,6 +471,34 @@ class FileBrowserModel implements IDisposable {
 }
 
 
+/**
+ * The namespace for the `FileBrowserModel` class statics.
+ */
+export
+namespace FileBrowserModel {
+  /**
+   * An options object for initializing a file browser.
+   */
+  export
+  interface IOptions {
+    /**
+     * A contents manager instance.
+     */
+    contentsManager: IContentsManager;
+
+    /**
+     * A session manager instance.
+     */
+    sessionManager: ISession.IManager;
+
+    /**
+     * The kernelspec models.
+     */
+    kernelspecs: IKernel.ISpecModels;
+  }
+}
+
+
 /**
  * The namespace for the file browser model private data.
  */

+ 10 - 2
src/filebrowser/plugin.ts

@@ -102,8 +102,16 @@ function activateFileBrowser(app: Application, provider: JupyterServices, regist
     kernelspecs: provider.kernelspecs,
     opener
   });
-  let model = new FileBrowserModel(contents, sessions, provider.kernelspecs);
-  let widget = new FileBrowserWidget(model, docManager, opener);
+  let model = new FileBrowserModel({
+    contentsManager: contents,
+    sessionManager: sessions,
+    kernelspecs: provider.kernelspecs
+  });
+  let widget = new FileBrowserWidget({
+    model,
+    manager: docManager,
+    opener
+  });
   let menu = createMenu(widget);
 
   // Add a context menu to the dir listing.