diff --git a/app/components/commander/index.hbs b/app/components/commander/index.hbs
index 455f2d219b08dafcae4eafbcfa5ab9645dfcd216..8330202c93645ee6b3c699e77b8b8f46156e09dc 100644
--- a/app/components/commander/index.hbs
+++ b/app/components/commander/index.hbs
@@ -1,7 +1,9 @@
 <div class="panel commander col m-2 p-2 user-select-none"
   {{droppable
     onDrop=this.onDrop
-    onDragOver=this.onDragOver }}
+    onDragOver=this.onDragOver
+    onDragEnter=this.onDragEnter
+    onDragLeave=this.onDragLeave }}
 
   {{uiSelect view_mode=this.view_mode enabled_on='grid'}}
   {{contextMenu}}>
diff --git a/app/components/commander/index.js b/app/components/commander/index.js
index 789840e8d8a461ac70ca0d081b536e0e8e418f22..bd35b4ff2857e3a2124028dc92d1a0ea840ae81a 100644
--- a/app/components/commander/index.js
+++ b/app/components/commander/index.js
@@ -170,7 +170,64 @@ export default class CommanderComponent extends Component {
   }
 
   @action
-  async onDrop(data) {
+  onDrop({event, element}) {
+    let data, files_list;
+    const isNodeDrop = event.dataTransfer.types.includes("application/x.node");
+
+    event.preventDefault();
+    element.classList.remove('droparea');
+
+    if (isNodeDrop) {
+      // drop incoming from another panel
+      data = event.dataTransfer.getData('application/x.node');
+      if (data) {
+        this.drop_callback({
+          'application/x.node': JSON.parse(data)
+        });
+      }
+    } else if (this._is_desktop_drop(event)) {
+      files_list = this._get_desktop_files(event);
+      this.drop_callback({
+        'application/x.desktop': files_list
+      });
+    }
+  }
+
+  _is_desktop_drop(event) {
+    // https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop
+    let items = event.dataTransfer.items;
+    let files = event.dataTransfer.files;
+
+    if (items && items.length > 0) {
+      return true;
+    }
+
+    return files && files.length > 0;
+  }
+
+  _get_desktop_files(event) {
+    // https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop
+    let result = [], i;
+
+    if (event.dataTransfer.items) {
+      // Use DataTransferItemList interface to access the file(s)
+      for (i = 0; i < event.dataTransfer.items.length; i++) {
+        // If dropped items aren't files, reject them
+        if (event.dataTransfer.items[i].kind === 'file') {
+          result.push(event.dataTransfer.items[i].getAsFile());
+        }
+      }
+    } else {
+      // Use DataTransfer interface to access the file(s)
+      for (i = 0; i < event.dataTransfer.files.length; i++) {
+        result.push(event.dataTransfer.files[i]);
+      }
+    }
+
+    return result;
+  }
+
+  drop_callback(data) {
     /**
      * data is a dictionary of following format:
      * {
@@ -239,6 +296,18 @@ export default class CommanderComponent extends Component {
 
   }
 
+  @action
+  onDragEnter({event, element}) {
+    event.preventDefault();
+    element.classList.add('droparea');
+  }
+
+  @action
+  onDragLeave({event, element}) {
+    event.preventDefault();
+    element.classList.remove('droparea');
+  }
+
   @action
   onDragendCancel(model) {
     /**
@@ -252,7 +321,7 @@ export default class CommanderComponent extends Component {
   }
 
   @action
-  onDragendSuccess(model, sel_nodes) {
+  onDragendSuccess() {
     /**
       Action invoked when drag operation for one or multiple nodes
       succeeded. It is invoked on the SOURCE panel.
diff --git a/app/components/document/index.hbs b/app/components/document/index.hbs
index 67b77408e3ec68404414b35884eaf81b14ef96c2..86085aa336158a17f1e18eb9be238222f9320fdf 100644
--- a/app/components/document/index.hbs
+++ b/app/components/document/index.hbs
@@ -1,7 +1,7 @@
 <div class="node document {{if this.is_selected "checked"}}"
   {{draggable @model
-    selectedNodes=@selectedNodes
-    sourceParent=@sourceParen
+    selectedItems=@selectedNodes
+    onDragStart=this.onDragStart
     onDragendSuccess=@onDragendSuccess
     onDragendCancel=@onDragendCancel}}>
   <Input
@@ -19,4 +19,4 @@
       {{yield}}
     </div>
   </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/app/components/folder/index.hbs b/app/components/folder/index.hbs
index 102ae6faeb03b99654b225dc94ad0a21cefdc75f..34d8a2d2edc50c1112096ceb8b3bcc1b45986742 100644
--- a/app/components/folder/index.hbs
+++ b/app/components/folder/index.hbs
@@ -1,7 +1,8 @@
 <div class="node folder
   {{if this.is_selected "checked"}}"
   {{draggable @model
-    selectedNodes=@selectedNodes
+    selectedItems=@selectedNodes
+    onDragStart=this.onDragStart
     onDragendSuccess=@onDragendSuccess
     onDragendCancel=@onDragendCancel}}>
   <Input
@@ -12,4 +13,4 @@
   <div class="title">
     {{yield}}
   </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/app/components/node.js b/app/components/node.js
index 24ef3a952b973033337f131729a584aa2c963a22..d75fb053bf5c2a6e0b25dd854c4dec4183bc02ff 100644
--- a/app/components/node.js
+++ b/app/components/node.js
@@ -2,6 +2,7 @@ import Component from '@glimmer/component';
 import { action } from '@ember/object';
 
 
+
 export default class NodeComponent extends Component {
   /**
    * Receives arguments:
@@ -37,4 +38,23 @@ export default class NodeComponent extends Component {
       is_selected: is_checked
     });
   }
+
+  @action
+  onDragStart({event, model, items, canvas}) {
+    let data;
+
+    data = {
+      nodes: items,
+      source_parent: {
+        id: model.parent.get('id')
+      }
+    }
+
+    event.dataTransfer.setData(
+      "application/x.node",
+      JSON.stringify(data)
+    );
+
+    event.dataTransfer.setDragImage(canvas, 0, -15);
+  }
 }
diff --git a/app/components/viewer/document/index.hbs b/app/components/viewer/document/index.hbs
index 9a7a17b572b5aa44cbb1af598771ae122b6bb697..3b3a77eb07885f199e0aca7297c54ee63e7fc558 100644
--- a/app/components/viewer/document/index.hbs
+++ b/app/components/viewer/document/index.hbs
@@ -16,4 +16,4 @@
     @onZoomOut={{this.onZoomOut}}
     @onZoomFit={{this.onZoomFit}}
     @zoom_factor={{this.zoom_factor}} />
-</div>
\ No newline at end of file
+</div>
diff --git a/app/components/viewer/thumbnail/index.hbs b/app/components/viewer/thumbnail/index.hbs
index 30de6f9a3c9d1450d06036b1191c22324e242595..1a85c20a686406e9756fb6b88ad3d8fb87abe0d9 100644
--- a/app/components/viewer/thumbnail/index.hbs
+++ b/app/components/viewer/thumbnail/index.hbs
@@ -1,5 +1,10 @@
 <div
   class="thumbnail d-flex flex-column align-items-center px-2 {{if this.is_selected 'checked'}}"
+  {{draggable @page
+    selectedItems=@selectedPages
+    onDragStart=this.onDragStart
+    onDragendSuccess=@onDragendSuccess
+    onDragendCancel=@onDragendCancel}}
   {{on "dblclick" this.onDblClick}} >
   <Input
     class="align-self-start m-1"
@@ -14,4 +19,4 @@
   <div class="number fs-3 m-3">
     {{@page.number}}
   </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/app/components/viewer/thumbnail/index.js b/app/components/viewer/thumbnail/index.js
index c4d9bb0e872a2ea430c280d566291c8c01465bee..6bea713783ab368b432224c095b4ad48225b2a23 100644
--- a/app/components/viewer/thumbnail/index.js
+++ b/app/components/viewer/thumbnail/index.js
@@ -19,6 +19,24 @@ export default class ViewerThumbnailComponent extends Component {
     });
   }
 
+  @action
+  onDragStart({event, model, items, canvas, element}) {
+    let data;
+
+    data = {
+      pages: items,
+      page: model
+    };
+
+    event.dataTransfer.setData(
+      'application/x.page',
+      JSON.stringify(data)
+    );
+
+    event.dataTransfer.setDragImage(canvas, 0, -15);
+    console.log(`Thumbnails onDragStart elment=${element}`);
+  }
+
   get is_selected() {
     let page = this.args.page,
       selected_page_ids;
@@ -41,4 +59,4 @@ export default class ViewerThumbnailComponent extends Component {
   set is_selected(value) {
 
   }
-}
\ No newline at end of file
+}
diff --git a/app/components/viewer/thumbnails.hbs b/app/components/viewer/thumbnails/index.hbs
similarity index 55%
rename from app/components/viewer/thumbnails.hbs
rename to app/components/viewer/thumbnails/index.hbs
index b8fc91cb31bafdccf412793ca569e56c8efba017..292336e6079762ed73a7a5e7c0b1bf768c0aed22 100644
--- a/app/components/viewer/thumbnails.hbs
+++ b/app/components/viewer/thumbnails/index.hbs
@@ -1,11 +1,19 @@
 <div
   class="d-flex flex-column thumbnails"
+  {{droppable
+    onDrop=this.onDrop
+    onDragOver=this.onDragOver
+    onDragEnter=this.onDragEnter
+    onDragLeave=this.onDragLeave }}
+
   {{adjust_element_height}}>
   {{#each @pages as |page|}}
     <Viewer::Thumbnail
       @page={{page}}
       @selectedPages={{@selectedPages}}
       @onDblClick={{@onDblClick}}
+      @onDragendSuccess={{this.onDragendSuccess}}
+      @onDragendCancel={{this.onDragendCancel}}
       @onCheckboxChange={{@onCheckboxChange}} />
   {{/each}}
 </div>
diff --git a/app/components/viewer/thumbnails/index.js b/app/components/viewer/thumbnails/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..326814818e1bc9165ca55de37e973451866a0a7d
--- /dev/null
+++ b/app/components/viewer/thumbnails/index.js
@@ -0,0 +1,36 @@
+import Component from '@glimmer/component';
+import { action } from '@ember/object';
+
+
+export default class ViewerThumbnailsComponent extends Component {
+  @action
+  onDragendCancel() {
+  }
+
+  @action
+  onDragendSuccess() {
+  }
+
+  @action
+  onDrop({event, element}) {
+    let data, json_data, page_ids;
+
+    event.preventDefault();
+    data = event.dataTransfer.getData('application/x.page');
+    json_data = JSON.parse(data);
+    page_ids = json_data['pages'].map(page => page.id);
+    console.log(`Thumbnails received: dropped page_ids=${page_ids}`);
+  }
+
+  @action
+  onDragOver() {
+  }
+
+  @action
+  onDragEnter() {
+  }
+
+  @action
+  onDragLeave() {
+  }
+}
diff --git a/app/modifiers/draggable.js b/app/modifiers/draggable.js
index 733367b4851cf07d7d5ecf69aad3ecf15e5f0bd8..b35828a23a135a12f8e094ca493b17e891c5052a 100644
--- a/app/modifiers/draggable.js
+++ b/app/modifiers/draggable.js
@@ -1,6 +1,6 @@
 import { action } from '@ember/object';
 import Modifier from 'ember-modifier';
-import { merge_nodes } from 'papermerge/utils/array';
+import { merge_items } from 'papermerge/utils/array';
 
 
 export default class DraggableModifier extends Modifier {
@@ -15,6 +15,7 @@ export default class DraggableModifier extends Modifier {
     Usage:
 
     {{draggable @model
+      onDragStart=this.onDragStart
       onDragendSuccess=@onDragendSuccess
       onDragendCancel=@onDragendCancel}}
 
@@ -59,32 +60,33 @@ export default class DraggableModifier extends Modifier {
 
   @action
   onDragStart(event) {
-    let data, nodes, canvas;
+    let model,
+      selected_items,
+      _onDragStart,
+      canvas,
+      items,
+      element;
 
-    this.model = this.args.positional[0];
-    this.selected_nodes = this.args.named['selectedNodes'];
+    model = this.model = this.args.positional[0];
+    selected_items = this.selected_items = this.args.named['selectedItems'];
+    _onDragStart = this.args.named['onDragStart'];
 
     // Merge model from which user started dragging
-    // with rest of selected nodes (in case there are some)
-    // into one single list of {id: <node_id>} objects.
+    // with rest of selected items (in case there are some)
+    // into one single list of {id: <item_id>} objects.
     // Resulted list won't have any duplicates.
-    nodes = merge_nodes(this.model.id, this.selected_nodes);
-
-    data = {
-      nodes: nodes,
-      source_parent: {
-        id: this.model.parent.get('id')
-      }
-    }
-
-    canvas = this.get_drag_canvas(nodes.length);
-
-    event.dataTransfer.setData(
-      "application/x.node",
-      JSON.stringify(data)
-    );
-
-    event.dataTransfer.setDragImage(canvas, 0, -15);
+    items = merge_items(model.id, selected_items);
+
+    element = this.element;
+    canvas = this.get_drag_canvas(items.length);
+
+    _onDragStart({
+      event,
+      element,
+      model,
+      items,
+      canvas
+    });
   }
 
   get_drag_canvas(count) {
@@ -110,9 +112,9 @@ export default class DraggableModifier extends Modifier {
     const ondragend_cancel = this.args.named['onDragendCancel'];
 
     if (event.dataTransfer.dropEffect === "move") {
-      ondragend_success(this.model, this.selected_nodes);
+      ondragend_success(this.model, this.selected_items);
     } else {
-      ondragend_cancel(this.model, this.selected_nodes);
+      ondragend_cancel(this.model, this.selected_items);
     }
   }
 }
diff --git a/app/modifiers/droppable.js b/app/modifiers/droppable.js
index 02f25601c049f0e48b286979a1e63028623ba1c5..32577290db2a8629905ccc29484334badf75c5c6 100644
--- a/app/modifiers/droppable.js
+++ b/app/modifiers/droppable.js
@@ -30,28 +30,10 @@ export default class DrappableModifier extends Modifier {
 
   @action
   onDrop(event) {
-    let data, files_list;
-    const isNodeDrop = event.dataTransfer.types.includes("application/x.node");
-    const callback = this.args.named['onDrop'];
-
-    event.preventDefault();
-    this.element.classList.remove('droparea');
-
-    if (isNodeDrop && callback) {
-      // drop incoming from another panel
-      data = event.dataTransfer.getData('application/x.node');
-      if (data) {
-        callback({
-          'application/x.node': JSON.parse(data)
-        });
-      }
-    } else if (this._is_desktop_drop(event)) {
-      files_list = this._get_desktop_files(event);
-      callback({
-        'application/x.desktop': files_list
-      });
-    }
+    let _onDrop = this.args.named['onDrop'], element;
 
+    element = this.element;
+    _onDrop({event, element});
   }
 
   @action
@@ -66,47 +48,19 @@ export default class DrappableModifier extends Modifier {
 
   @action
   onDragEnter(event) {
-    event.preventDefault();
-    this.element.classList.add('droparea');
+    let _onDragEnter = this.args.named['onDragEnter'],
+      element;
+
+    element = this.element;
+    _onDragEnter({event, element});
   }
 
   @action
   onDragLeave(event) {
-    event.preventDefault();
-    this.element.classList.remove('droparea');
-  }
-
-  _is_desktop_drop(event) {
-    // https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop
-    let items = event.dataTransfer.items;
-    let files = event.dataTransfer.files;
-
-    if (items && items.length > 0) {
-      return true;
-    }
-
-    return files && files.length > 0;
-  }
-
-  _get_desktop_files(event) {
-    // https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop
-    let result = [], i;
-
-    if (event.dataTransfer.items) {
-      // Use DataTransferItemList interface to access the file(s)
-      for (i = 0; i < event.dataTransfer.items.length; i++) {
-        // If dropped items aren't files, reject them
-        if (event.dataTransfer.items[i].kind === 'file') {
-          result.push(event.dataTransfer.items[i].getAsFile());
-        }
-      }
-    } else {
-      // Use DataTransfer interface to access the file(s)
-      for (i = 0; i < event.dataTransfer.files.length; i++) {
-        result.push(event.dataTransfer.files[i]);
-      }
-    }
+    let _onDragLeave = this.args.named['onDragLeave'],
+      element;
 
-    return result;
+    element = this.element;
+    _onDragLeave({event, element});
   }
 }
diff --git a/app/utils/array.js b/app/utils/array.js
index d7f1e1770150f103a06bb15f1b4b42d7b5630667..8744ab5d3d38e7ec60044ea2b8540b28ac24bc42 100644
--- a/app/utils/array.js
+++ b/app/utils/array.js
@@ -1,32 +1,41 @@
-function merge_nodes(node_id, nodes) {
+
+function get_id(item) {
+  if (item.id) {
+    return item.id;
+  }
+
+  return item.get('id');
+}
+
+function merge_items(item_id, items) {
   /*
-  Returns a list of {id: <node.id>} objects with no duplicates.
-  List contains as node_id given as first parameter as well as all nodes
+  Returns a list of {id: <item.id>} objects with no duplicates.
+  List contains as item_id given as first parameter as well as all items
   given as second parameter.
   */
-  let source_nodes;
+  let result_items;
 
-  if (!nodes) {
-    return [{id: node_id}];
+  if (!items) {
+    return [{id: item_id}];
   }
 
-  if (!nodes.length) {
-    return [{id: node_id}];
+  if (!items.length) {
+    return [{id: item_id}];
   }
 
-  source_nodes = nodes.map(item => {
-    return {'id': item.get('id')};
+  result_items = items.map(item => {
+    return {'id': get_id(item)};
   });
 
-  // if by concatinating nodes with node_id there
+  // if by concatinating items with item_id there
   // will be no duplicates:
-  if (!source_nodes.find(item => item.id == node_id)) {
-    return [{id: node_id}].concat(source_nodes)
+  if (!result_items.find(item => item.id == item_id)) {
+    return [{id: item_id}].concat(result_items)
   }
 
-  return source_nodes;
+  return result_items;
 }
 
 export {
-  merge_nodes
+  merge_items
 }
\ No newline at end of file