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