From 1007918690ed6385cec0aa6edc1b858e164dbeb7 Mon Sep 17 00:00:00 2001 From: Eugen Ciur <eugen@papermerge.com> Date: Sat, 9 Apr 2022 08:13:52 +0200 Subject: [PATCH] feature: thumbnails viewer drag and drop feedback --- app/components/viewer/document/index.hbs | 2 + app/components/viewer/index.hbs | 2 + app/components/viewer/index.js | 29 +++++++++++- app/components/viewer/thumbnails/index.hbs | 20 +++++---- app/components/viewer/thumbnails/index.js | 52 +++++++++++++++++++++- app/modifiers/droppable.js | 12 +++-- app/utils/array.js | 2 +- 7 files changed, 105 insertions(+), 14 deletions(-) diff --git a/app/components/viewer/document/index.hbs b/app/components/viewer/document/index.hbs index b887ef3..3f6e66c 100644 --- a/app/components/viewer/document/index.hbs +++ b/app/components/viewer/document/index.hbs @@ -7,6 +7,8 @@ @onDblClick={{this.onThumbnailDblClick}} @onCheckboxChange={{@onCheckboxChange}} @onThumbnailsPositionChanged={{@onThumbnailsPositionChanged}} + @onAddThumbnailPlaceholderAt={{@onAddThumbnailPlaceholderAt}} + @onRemoveThumbnailPlaceholder={{@onRemoveThumbnailPlaceholder}} @onIncomingPages={{@onIncomingPages}} /> {{/if}} <Viewer::ThumbnailsSwitch diff --git a/app/components/viewer/index.hbs b/app/components/viewer/index.hbs index 693d352..1925961 100644 --- a/app/components/viewer/index.hbs +++ b/app/components/viewer/index.hbs @@ -53,5 +53,7 @@ @thumbnails_visible={{this.thumbnails_visible}} @onCheckboxChange={{this.onThumbnailCheckboxChange}} @onThumbnailsPositionChanged={{this.onThumbnailsPositionChanged}} + @onAddThumbnailPlaceholderAt={{this.onAddThumbnailPlaceholderAt}} + @onRemoveThumbnailPlaceholder={{this.onRemoveThumbnailPlaceholder}} @onIncomingPages={{this.onIncomingPages}} /> </div> diff --git a/app/components/viewer/index.js b/app/components/viewer/index.js index 4066871..6289865 100644 --- a/app/components/viewer/index.js +++ b/app/components/viewer/index.js @@ -5,7 +5,6 @@ import { action } from '@ember/object'; import { A } from '@ember/array'; import { reposition_items, - get_id, detect_order_changes } from 'papermerge/utils/array'; @@ -163,6 +162,34 @@ export default class ViewerComponent extends Component { }); } + @action + onAddThumbnailPlaceholderAt(pos) { + let new_pages, + placeholder; + + new_pages = Array.from(this.pages); + placeholder = {'is_placeholder': true}; + if (!new_pages.find(item => item.is_placeholder)) { + // Only one placeholder is allowed + new_pages.splice(pos, 0, placeholder); + this.pages = new_pages; + } + } + + @action + onRemoveThumbnailPlaceholder() { + let new_pages, + placeholder_pos; + + new_pages = Array.from(this.pages); + placeholder_pos = new_pages.findIndex(item => item.is_placeholder); + if (placeholder_pos >= 0) { + console.log(`placeholder found at pos ${placeholder_pos}`) + new_pages.splice(placeholder_pos, 1); + this.pages = new_pages; + } + } + @action async onIncomingPages({page_ids, drop_pos}) { await this.requests.moveToDocument({ diff --git a/app/components/viewer/thumbnails/index.hbs b/app/components/viewer/thumbnails/index.hbs index a5d37e3..75524ad 100644 --- a/app/components/viewer/thumbnails/index.hbs +++ b/app/components/viewer/thumbnails/index.hbs @@ -8,13 +8,17 @@ {{adjust_element_height}}> {{#each @pages as |page|}} - <Viewer::Thumbnail - @doc={{@doc}} - @page={{page}} - @selectedPages={{@selectedPages}} - @onDblClick={{@onDblClick}} - @onDragendSuccess={{this.onDragendSuccess}} - @onDragendCancel={{this.onDragendCancel}} - @onCheckboxChange={{@onCheckboxChange}} /> + {{#if page.is_placeholder}} + Placeholder + {{else}} + <Viewer::Thumbnail + @doc={{@doc}} + @page={{page}} + @selectedPages={{@selectedPages}} + @onDblClick={{@onDblClick}} + @onDragendSuccess={{this.onDragendSuccess}} + @onDragendCancel={{this.onDragendCancel}} + @onCheckboxChange={{@onCheckboxChange}} /> + {{/if}} {{/each}} </div> diff --git a/app/components/viewer/thumbnails/index.js b/app/components/viewer/thumbnails/index.js index 10729f5..8befac9 100644 --- a/app/components/viewer/thumbnails/index.js +++ b/app/components/viewer/thumbnails/index.js @@ -54,7 +54,57 @@ export default class ViewerThumbnailsComponent extends Component { } @action - onDragOver() { + onDragOver({event, element}) { + /* + Creates DOM placeholder suggesting to user that here he/she can drop the page. + + Only one placeholder DOM element is allowed. + */ + let thumbnail_dom_items, + cursor_coord, + pos, + rect, + cursor_before_child = 0, + outside_all_thumbnails = true, + svg_element; + + if (!element) { + return; + } + + cursor_coord = new Point(event.clientX, event.clientY); + thumbnail_dom_items = Array.from(element.children); + + thumbnail_dom_items.forEach(thumbnail_dom_item => { + // page_item is DOM element which may be real thumbnail of the page or + // it may be a paceholder used as suggestion that it is OK to drop page here. + // Real page thumbnail DOM element contains DOM element for image/svg + // and DOM element denoting page number + svg_element = thumbnail_dom_item.querySelector('svg'); + if (svg_element) { // in case of thumbnail placeholder, there won't be SVG element + rect = svg_element.getBoundingClientRect(); + + if (cursor_coord.y <= rect.y) { + cursor_before_child += 1; + } + // Check if cursor position is outside of any thumbnail i.e. + // position to drop will be suggested only in case cursor coordinate + // is BETWEEN thumbnails images/svg + if ((cursor_coord.y < rect.bottom) && (cursor_coord.y > rect.top)) { + outside_all_thumbnails = false; + } + } + }); + + // position where to suggest page drop + pos = thumbnail_dom_items.length - cursor_before_child; + + if (outside_all_thumbnails) { + // suggest position to drop ONLY of cursor is outside of all thumbnails + this.args.onAddThumbnailPlaceholderAt(pos); + } else { + this.args.onRemoveThumbnailPlaceholder(); + } } @action diff --git a/app/modifiers/droppable.js b/app/modifiers/droppable.js index 3257729..1822f8b 100644 --- a/app/modifiers/droppable.js +++ b/app/modifiers/droppable.js @@ -38,11 +38,17 @@ export default class DrappableModifier extends Modifier { @action onDragOver(event) { - const isNode = event.dataTransfer.types.includes("application/x.node"); + //const isNode = event.dataTransfer.types.includes("application/x.node"); - event.preventDefault(); - if (isNode) { + //event.preventDefault(); + //if (isNode) { //console.log(`dragging over a node`); + //} + let _onDragOver = this.args.named['onDragOver'], element; + + element = this.element; + if (_onDragOver) { + _onDragOver({event, element}); } } diff --git a/app/utils/array.js b/app/utils/array.js index 46b92d1..67bcecc 100644 --- a/app/utils/array.js +++ b/app/utils/array.js @@ -124,7 +124,7 @@ function detect_order_changes(arr1, arr2) { } if (arr1.length != arr2.length) { - throw 'Invalid input. Both arrays need to be of same length'; + return false; } arr1.forEach((item, index) => { -- GitLab