浏览代码

Add drag drop styling and clean up loops

Steven Silvester 8 年之前
父节点
当前提交
1c81a686d6
共有 2 个文件被更改,包括 95 次插入51 次删除
  1. 26 2
      src/notebook/notebook/index.css
  2. 69 49
      src/notebook/notebook/widget.ts

+ 26 - 2
src/notebook/notebook/index.css

@@ -24,7 +24,7 @@
 }
 
 
-.jp-Notebook.jp-mod-commandMode .jp-Notebook-cell.jp-mod-active.jp-mod-selected {
+.jp-Notebook.jp-mod-commandMode .jp-Notebook-cell.jp-mod-active.jp-mod-selected{
   border-color: #ABABAB;
   border-left-width: var(--jp-border-width);
   background: linear-gradient(to right, #42A5F5 -40px, #42A5F5 5px, transparent 5px, transparent 100%);
@@ -55,4 +55,28 @@
 
 .jp-Notebook {
   flex: 1 1 auto;
-}
+}
+
+
+.jp-Notebook-cell.jp-mod-dropSource {
+  opacity: 0.5;
+}
+
+
+.jp-Notebook-cell.jp-mod-dropTarget {
+  border-top: 1px solid #42A5F5;
+}
+
+
+.jp-Notebook-cell .jp-Cell-prompt {
+  cursor: move;
+}
+
+.jp-dragImage {
+  text-align: center;
+  line-height: 50px;
+  min-width: 50px;
+  min-height: 50px;
+  border: 1px solid #ABABAB;
+  background: linear-gradient(to right, #42A5F5 -40px, #42A5F5 5px, white 5px, white 100%);
+}

+ 69 - 49
src/notebook/notebook/widget.ts

@@ -6,11 +6,11 @@ import {
 } from '@jupyterlab/services';
 
 import {
-  each
+  each, enumerate
 } from 'phosphor/lib/algorithm/iteration';
 
 import {
-  find, indexOf
+  find, findIndex, indexOf
 } from 'phosphor/lib/algorithm/searching';
 
 import {
@@ -126,6 +126,16 @@ const UNCONFINED_CLASS = 'jp-mod-unconfined';
  */
 const DROP_TARGET_CLASS = 'jp-mod-dropTarget';
 
+/**
+ * The class name added to a drop source.
+ */
+const DROP_SOURCE_CLASS = 'jp-mod-dropSource';
+
+/**
+ * The class name added to drag images.
+ */
+const DRAG_IMAGE_CLASS = 'jp-dragImage';
+
 /**
  * The mimetype used for Jupyter cell data.
  */
@@ -327,9 +337,9 @@ class StaticNotebook extends Widget {
     }
     this._updateMimetype();
     let cells = newValue.cells;
-    for (let i = 0; i < cells.length; i++) {
-      this._insertCell(i, cells.at(i));
-    }
+    each(enumerate(cells), ([i, cell]) => {
+      this._insertCell(i, cell);
+    });
     cells.changed.connect(this._onCellsChanged, this);
     newValue.contentChanged.connect(this.onModelContentChanged, this);
     newValue.metadataChanged.connect(this.onMetadataChanged, this);
@@ -415,10 +425,9 @@ class StaticNotebook extends Widget {
    * Update the cell widgets.
    */
   private _updateCells(): void {
-    let layout = this.layout as PanelLayout;
-    for (let i = 0; i < layout.widgets.length; i++) {
+    each(enumerate(this.widgets), ([i, widget]) => {
       this._updateCell(i);
-    }
+    });
   }
 
   /**
@@ -618,11 +627,7 @@ class Notebook extends StaticNotebook {
     }
     // Edit mode deselects all cells.
     if (newValue === 'edit') {
-      let layout = this.layout as PanelLayout;
-      for (let i = 0; i < layout.widgets.length; i++) {
-        let widget = layout.widgets.at(i) as BaseCellWidget;
-        this.deselect(widget);
-      }
+      each(this.widgets, widget => { this.deselect(widget); });
       if (activeCell instanceof MarkdownCellWidget) {
         activeCell.rendered = false;
       }
@@ -879,10 +884,8 @@ class Notebook extends StaticNotebook {
     }
 
     let count = 0;
-    let layout = this.layout as PanelLayout;
-    for (let i = 0; i < layout.widgets.length; i++) {
-      let widget = layout.widgets.at(i) as BaseCellWidget;
-      if (i !== this.activeCellIndex) {
+    each(this.widgets, widget => {
+      if (widget !== activeCell) {
         widget.removeClass(ACTIVE_CLASS);
       }
       widget.removeClass(OTHER_SELECTED_CLASS);
@@ -892,7 +895,7 @@ class Notebook extends StaticNotebook {
       } else {
         widget.removeClass(SELECTED_CLASS);
       }
-    }
+    });
     if (count > 1) {
       activeCell.addClass(OTHER_SELECTED_CLASS);
     }
@@ -966,13 +969,11 @@ class Notebook extends StaticNotebook {
   private _findCell(node: HTMLElement): number {
     // Trace up the DOM hierarchy to find the root cell node.
     // Then find the corresponding child and select it.
-    let layout = this.layout as PanelLayout;
     while (node && node !== this.node) {
       if (node.classList.contains(NB_CELL_CLASS)) {
-        for (let i = 0; i < layout.widgets.length; i++) {
-          if (layout.widgets.at(i).node === node) {
-            return i;
-          }
+        let i = findIndex(this.widgets, widget => widget.node === node);
+        if (i) {
+          return i;
         }
         break;
       }
@@ -1058,22 +1059,26 @@ class Notebook extends StaticNotebook {
    * Handle the `'p-dragenter'` event for the widget.
    */
   private _evtDragEnter(event: IDragEvent): void {
-    if (event.mimeData.hasData(JUPYTER_CELL_MIME)) {
-      let index = this._findCell(event.target as HTMLElement);
-      if (index === -1) {
-        return;
-      }
-      let target = (this.layout as PanelLayout).widgets.at(index);
-      target.node.classList.add(DROP_TARGET_CLASS);
-      event.preventDefault();
-      event.stopPropagation();
+    if (!event.mimeData.hasData(JUPYTER_CELL_MIME)) {
+      return;
+    }
+    event.preventDefault();
+    event.stopPropagation();
+    let index = this._findCell(event.target as HTMLElement);
+    if (index === -1) {
+      return;
     }
+    let target = (this.layout as PanelLayout).widgets.at(index);
+    target.node.classList.add(DROP_TARGET_CLASS);
   }
 
   /**
    * Handle the `'p-dragleave'` event for the widget.
    */
   private _evtDragLeave(event: IDragEvent): void {
+    if (!event.mimeData.hasData(JUPYTER_CELL_MIME)) {
+      return;
+    }
     event.preventDefault();
     event.stopPropagation();
     let elements = this.node.getElementsByClassName(DROP_TARGET_CLASS);
@@ -1086,6 +1091,9 @@ class Notebook extends StaticNotebook {
    * Handle the `'p-dragover'` event for the widget.
    */
   private _evtDragOver(event: IDragEvent): void {
+    if (!event.mimeData.hasData(JUPYTER_CELL_MIME)) {
+      return;
+    }
     event.preventDefault();
     event.stopPropagation();
     event.dropAction = event.proposedAction;
@@ -1105,15 +1113,15 @@ class Notebook extends StaticNotebook {
    * Handle the `'p-drop'` event for the widget.
    */
   private _evtDrop(event: IDragEvent): void {
+    if (!event.mimeData.hasData(JUPYTER_CELL_MIME)) {
+      return;
+    }
     event.preventDefault();
     event.stopPropagation();
     if (event.proposedAction === 'none') {
       event.dropAction = 'none';
       return;
     }
-    if (!event.mimeData.hasData(JUPYTER_CELL_MIME)) {
-      return;
-    }
     event.dropAction = event.proposedAction;
 
     let target = event.target as HTMLElement;
@@ -1131,7 +1139,7 @@ class Notebook extends StaticNotebook {
     let values = event.mimeData.getData(JUPYTER_CELL_MIME);
 
     // Insert the copies of the original cells.
-    for (let value of values) {
+    each(values, (value: nbformat.ICell) => {
       let cell: ICellModel;
       switch (value.cell_type) {
       case 'code':
@@ -1145,7 +1153,7 @@ class Notebook extends StaticNotebook {
         break;
       }
       model.cells.insert(index, cell);
-    }
+    });
     // Activate the last cell.
     this.activeCellIndex = index + values.length - 1;
   }
@@ -1156,24 +1164,24 @@ class Notebook extends StaticNotebook {
   private _startDrag(index: number, clientX: number, clientY: number): void {
     let cells = this.model.cells;
     let selected: nbformat.ICell[] = [];
-    let toremove: ICellModel[] = [];
-    let widgets = this.widgets;
-    for (let i = 0; i < cells.length; i++) {
+    let toremove: BaseCellWidget[] = [];
+
+    each(enumerate(this.widgets), ([i, widget]) => {
       let cell = cells.at(i);
-      if (this.isSelected(widgets.at(i))) {
+      if (this.isSelected(widget)) {
+        widget.addClass(DROP_SOURCE_CLASS);
         selected.push(cell.toJSON());
-        toremove.push(cell);
+        toremove.push(widget);
       }
-    }
+    });
 
     // Create the drag image.
-    //let activeIndex = this.model.activeCellIndex;
-    // let activeCell = (this.layout as PanelLayout).childAt(activeIndex);
-    // let dragImage = activeCell.node.cloneNode(true) as HTMLElement;
+    let dragImage = Private.createDragImage(selected.length);
 
     // Set up the drag event.
     this._drag = new Drag({
       mimeData: new MimeData(),
+      dragImage,
       supportedActions: 'move',
       proposedAction: 'move'
     });
@@ -1184,13 +1192,14 @@ class Notebook extends StaticNotebook {
     document.removeEventListener('mouseup', this, true);
     this._drag.start(clientX, clientY).then(action => {
       this._drag = null;
+      each(toremove, widget => { widget.removeClass(DROP_SOURCE_CLASS); });
       if (action === 'none') {
         return;
       }
       let activeCell = cells.at(this.activeCellIndex);
-      for (let cell of toremove) {
-        this.model.cells.remove(cell);
-      }
+      each(toremove, widget => {
+        this.model.cells.remove(widget.model);
+      });
       this.activeCellIndex = indexOf(cells, activeCell);
     });
 
@@ -1340,4 +1349,15 @@ namespace Private {
       // This is a no-op.
     }
   }
+
+  /**
+   * Create a cell drag image.
+   */
+  export
+  function createDragImage(count: number): HTMLElement {
+    let node = document.createElement('span');
+    node.textContent = `(${count})`;
+    node.className = DRAG_IMAGE_CLASS;
+    return node;
+  }
 }