diff --git a/app/components/viewer/document/index.hbs b/app/components/viewer/document/index.hbs index b887ef330f66a0a6e64ddfd235a746d636b35c6c..3f6e66c0134b5ce718eee2b74c687f60aa53b8d7 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 693d352c88fd8c9e992c00f7586886d9620790cd..1925961bb7d809f71d10d11cc373c90fb5b96709 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 4066871d8382112a82c93c3b0c3dd3ab2eed459f..6289865215a07fc85356c3e3944f625f3fd8e473 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 a5d37e3691be78985db3202833a9cc667093367a..75524ad3cc1d787c563f03dd95110387e96ac1f2 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 10729f555557f7e762031990469546d098ac3836..8befac90f1e7a6170787336f291ff6701d1668db 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 32577290db2a8629905ccc29484334badf75c5c6..1822f8b6019aa21ad8619c2ed3cc6b38ec0015fb 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 46b92d115b0f28062144cc5258b20781ff5fac73..67bcecce12294ed515cc4c6aac77b6f5f70bc38a 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) => {