diff --git a/src/main/resources/static/css/dragdrop.css b/src/main/resources/static/css/dragdrop.css new file mode 100644 index 00000000..e05cb32e --- /dev/null +++ b/src/main/resources/static/css/dragdrop.css @@ -0,0 +1,78 @@ +#drag-container { + position: fixed; + display:flex; + inset: 0; + pointer-events: none; + z-index: 10000; + visibility: hidden; +} + +#drag-container:not(:empty) { + visibility: visible; +} + +#drag-container .dragged-img { + position: fixed; + max-width: 200px; + max-height: 200px; + box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.58); + transform-origin: top left; +} + +.drag-manager_dragging { + width: 0px; + visibility: hidden; +} + +.drag-manager_draghover { + width: 375px !important; +} + +.drag-manager_draghover .insert-file-button-container { + display: none !important; +} + +.drag-manager_draghover .button-container { + visibility: hidden !important; +} + +html[lang-direction=ltr] .drag-manager_draghover img { + left: calc(50% + 62.5px) !important; +} + +html[lang-direction=rtl] .drag-manager_draghover img { + left: unset; +} + +.drag-manager_dragging-container .hide-on-drag { + display: none !important; +} + +.drag-manager_endpoint { + width: 80px; + height: 100%; + background-color: #FFFFFF10; + transition: width 0.1s; + animation: end-drop-expand .3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.drag-manager_endpoint svg { + width: 50px; + height: 50px; +} + +.drag-manager_endpoint.drag-manager_draghover { + width: 150px !important; +} + +@keyframes end-drop-expand { + from { + width: 0; + } + to { + width: 80px; + } +} \ No newline at end of file diff --git a/src/main/resources/static/css/imageHighlighter.css b/src/main/resources/static/css/imageHighlighter.css new file mode 100644 index 00000000..231895d6 --- /dev/null +++ b/src/main/resources/static/css/imageHighlighter.css @@ -0,0 +1,40 @@ + +#image-highlighter { + position: fixed; + display:flex; + inset: 0; + z-index: 10000; + background-color: rgba(0, 0, 0, 0); + visibility: hidden; + align-items: center; + justify-content: center; + transition: visbility 0.1s linear, background-color 0.1s linear; +} + +#image-highlighter > * { + max-width: 80vw; + max-height: 80vh; + animation: image-highlight .1s linear; + transition: transform .1s linear, opacity .1s linear; +} + +#image-highlighter > *.remove { + transform: scale(0.8) !important; + opacity: 0 !important; +} + +#image-highlighter:not(:empty) { + background-color: rgba(0, 0, 0, 0.37); + visibility: visible; +} + +@keyframes image-highlight { + from { + transform: scale(0.8); + opacity: 0; + } + to { + transform: scale(1); + opacity: 1; + } +} \ No newline at end of file diff --git a/src/main/resources/static/css/pdfActions.css b/src/main/resources/static/css/pdfActions.css new file mode 100644 index 00000000..152e3ebc --- /dev/null +++ b/src/main/resources/static/css/pdfActions.css @@ -0,0 +1,87 @@ + +.pdf-actions_button-container { + z-index: 2; + display:flex; + opacity: 0; + transition: opacity 0.1s linear; +} + +.pdf-actions_container:hover .pdf-actions_button-container { + opacity: 1; +} + +.pdf-actions_button-container > * { + padding: 0.25rem 0.5rem; + margin: 3px; + display: block; +} + +.pdf-actions_container svg { + width: 16px; + height: 16px; +} +.pdf-actions_container:nth-child(1) .pdf-actions_move-left-button { + display: none; +} +.pdf-actions_container:last-child .pdf-actions_move-right-button { + display: none; +} + +/* "insert pdf" buttons that appear on the right when hover */ +.pdf-actions_insert-file-button-container { + translate: 0 -50%; + width: 80px; + height: 100%; + + z-index: 1; + opacity: 0; + transition: opacity 0.2s; +} + +.pdf-actions_insert-file-button-container.left { + left: -20px; +} + +.pdf-actions_insert-file-button-container.right { + right: -20px; +} + +html[lang-direction=ltr] .pdf-actions_insert-file-button-container.right { + display:none; +} + +html[lang-direction=rtl] .pdf-actions_insert-file-button-container.left { + display:none; +} + +.pdf-actions_insert-file-button-container.left .pdf-actions_insert-file-button { + left: 0; + translate: 0 -50%; +} + +.pdf-actions_insert-file-button-container.right .pdf-actions_insert-file-button { + right: 0; + translate: 0 -50%; +} + +html[lang-direction=ltr] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.right { + display: block; +} + + +html[lang-direction=rtl] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.left { + display: block; +} + +.pdf-actions_insert-file-button-container:hover { + opacity: 1; + transition: opacity 0.05s; +} +.pdf-actions_insert-file-button { + position: absolute; + top: 50%; + right: 50%; + translate: 50% -50%; + aspect-ratio: 1; + border-radius: 100px; +} \ No newline at end of file diff --git a/src/main/resources/static/js/multitool/DragDropManager.js b/src/main/resources/static/js/multitool/DragDropManager.js index 90354551..93fbbdf1 100644 --- a/src/main/resources/static/js/multitool/DragDropManager.js +++ b/src/main/resources/static/js/multitool/DragDropManager.js @@ -2,58 +2,95 @@ class DragDropManager { dragContainer; + wrapper; + pageDirection; movePageTo; pageDragging; draggelEl; draggedImageEl; hoveredEl; + endInsertionElement; - constructor(id) { + constructor(id, wrapperId) { this.dragContainer = document.getElementById(id); + this.pageDirection = document.documentElement.getAttribute("lang-direction"); + this.wrapper = document.getElementById(wrapperId); this.pageDragging = false; this.hoveredEl = undefined; this.draggelEl = undefined this.draggedImageEl = undefined; + var styleElement = document.createElement('link'); + styleElement.rel = 'stylesheet'; + styleElement.href = 'css/dragdrop.css' + + document.head.appendChild(styleElement); + + const div = document.createElement('div'); + div.classList.add('drag-manager_endpoint'); + div.innerHTML = ` + + + ` + this.endInsertionElement = div; + this.startDraggingPage = this.startDraggingPage.bind(this); this.onDragEl = this.onDragEl.bind(this); this.stopDraggingPage = this.stopDraggingPage.bind(this); + + this.adapt(div); } - startDraggingPage(div, imageSrc) { + startDraggingPage(div,) { this.pageDragging = true; this.draggedEl = div; - div.classList.add('dragging'); + const img = div.querySelector('img'); + div.classList.add('drag-manager_dragging'); + const imageSrc = img.src; + const imgEl = document.createElement('img'); imgEl.classList.add('dragged-img'); imgEl.src = imageSrc; this.draggedImageEl = imgEl; - this.draggedImageEl.style.left = screenX; - this.draggedImageEl.style.right = screenY; + imgEl.style.left = screenX; + imgEl.style.right = screenY; + imgEl.style.transform = `rotate(${img.style.rotate === '' ? '0deg' : img.style.rotate}) translate(-50%, -50%)`; this.dragContainer.appendChild(imgEl); + window.addEventListener('mouseup', this.stopDraggingPage) window.addEventListener('mousemove', this.onDragEl) + this.wrapper.classList.add('drag-manager_dragging-container'); + this.wrapper.appendChild(this.endInsertionElement); } onDragEl(mouseEvent) { const { clientX, clientY } = mouseEvent; if(this.draggedImageEl) { this.draggedImageEl.style.left = `${clientX}px`; - this.draggedImageEl.style.top = `${clientY}px`; + this.draggedImageEl.style.top = `${clientY}px`; } } stopDraggingPage() { window.removeEventListener('mousemove', this.onDragEl); + this.wrapper.classList.remove('drag-manager_dragging-container'); + this.wrapper.removeChild(this.endInsertionElement); window.removeEventListener('mouseup', this.stopDraggingPage) this.draggedImageEl = undefined; this.pageDragging = false; - this.draggedEl.classList.remove('dragging'); - this.hoveredEl.classList.remove('draghover'); + this.draggedEl.classList.remove('drag-manager_dragging'); + this.hoveredEl?.classList.remove('drag-manager_draghover'); this.dragContainer.childNodes.forEach((dragChild) => { this.dragContainer.removeChild(dragChild); }) + if(!this.hoveredEl) { + return; + } + if(this.hoveredEl === this.endInsertionElement) { + this.movePageTo(this.draggedEl); + return; + } this.movePageTo(this.draggedEl, this.hoveredEl); } @@ -64,19 +101,19 @@ class DragDropManager { adapt(div) { const onDragStart = () => { - this.startDraggingPage(div, div.querySelector('img').src); + this.startDraggingPage(div); } const onMouseEnter = () => { if (this.pageDragging) { this.hoveredEl = div; - div.classList.add('draghover'); + div.classList.add('drag-manager_draghover'); } } const onMouseLeave = () => { this.hoveredEl = undefined - div.classList.remove('draghover'); + div.classList.remove('drag-manager_draghover'); } div.addEventListener('dragstart', onDragStart); diff --git a/src/main/resources/static/js/multitool/PdfActionsManager.js b/src/main/resources/static/js/multitool/PdfActionsManager.js index b716f1d1..4bff39e3 100644 --- a/src/main/resources/static/js/multitool/PdfActionsManager.js +++ b/src/main/resources/static/js/multitool/PdfActionsManager.js @@ -5,6 +5,12 @@ class PdfActionsManager { constructor(id) { this.pagesContainer = document.getElementById(id); this.pageDirection = document.documentElement.getAttribute("lang-direction"); + + var styleElement = document.createElement('link'); + styleElement.rel = 'stylesheet'; + styleElement.href = 'css/pdfActions.css' + + document.head.appendChild(styleElement); } getPageContainer(element) { @@ -70,20 +76,21 @@ class PdfActionsManager { } adapt(div) { + div.classList.add('pdf-actions_container'); const leftDirection = this.pageDirection === 'rtl' ? 'right' : 'left' const rightDirection = this.pageDirection === 'rtl' ? 'left' : 'right' const buttonContainer = document.createElement('div'); - buttonContainer.classList.add("button-container"); + buttonContainer.classList.add("pdf-actions_button-container", "hide-on-drag"); const moveUp = document.createElement('button'); - moveUp.classList.add("move-left-button","btn", "btn-secondary"); + moveUp.classList.add("pdf-actions_move-left-button","btn", "btn-secondary"); moveUp.innerHTML = ``; moveUp.onclick = this.moveUpButtonCallback; buttonContainer.appendChild(moveUp); const moveDown = document.createElement('button'); - moveDown.classList.add("move-right-button","btn", "btn-secondary"); + moveDown.classList.add("pdf-actions_move-right-button","btn", "btn-secondary"); moveDown.innerHTML = ``; moveDown.onclick = this.moveDownButtonCallback; buttonContainer.appendChild(moveDown); @@ -120,12 +127,12 @@ class PdfActionsManager { const insertFileButtonContainer = document.createElement('div'); insertFileButtonContainer.classList.add( - "insert-file-button-container", + "pdf-actions_insert-file-button-container", leftDirection, `align-center-${leftDirection}`); const insertFileButton = document.createElement('button'); - insertFileButton.classList.add("btn", "btn-primary", "insert-file-button"); + insertFileButton.classList.add("btn", "btn-primary", "pdf-actions_insert-file-button"); insertFileButton.innerHTML = ` @@ -138,12 +145,12 @@ class PdfActionsManager { // add this button to every element, but only show it on the last one :D const insertFileButtonRightContainer = document.createElement('div'); insertFileButtonRightContainer.classList.add( - "insert-file-button-container", + "pdf-actions_insert-file-button-container", rightDirection, `align-center-${rightDirection}`); const insertFileButtonRight = document.createElement('button'); - insertFileButtonRight.classList.add("btn", "btn-primary", "insert-file-button"); + insertFileButtonRight.classList.add("btn", "btn-primary", "pdf-actions_insert-file-button"); insertFileButtonRight.innerHTML = ` diff --git a/src/main/resources/static/js/multitool/imageHighlighter.js b/src/main/resources/static/js/multitool/imageHighlighter.js index d20510d3..6f7cd22e 100644 --- a/src/main/resources/static/js/multitool/imageHighlighter.js +++ b/src/main/resources/static/js/multitool/imageHighlighter.js @@ -3,6 +3,13 @@ class ImageHiglighter { constructor(id) { this.imageHighlighter = document.getElementById(id); this.imageHighlightCallback = this.imageHighlightCallback.bind(this); + + var styleElement = document.createElement('link'); + styleElement.rel = 'stylesheet'; + styleElement.href = 'css/imageHighlighter.css' + + document.head.appendChild(styleElement); + this.imageHighlighter.onclick = () => { this.imageHighlighter.childNodes.forEach((child) => { child.classList.add('remove'); diff --git a/src/main/resources/templates/multi-tool.html b/src/main/resources/templates/multi-tool.html index 6f3ea682..2c6944b0 100644 --- a/src/main/resources/templates/multi-tool.html +++ b/src/main/resources/templates/multi-tool.html @@ -67,7 +67,7 @@ import PdfActionsManager from '/js/multitool/PdfActionsManager.js'; // enables drag and drop - const dragDropManager = new DragDropManager('drag-container'); + const dragDropManager = new DragDropManager('drag-container', 'pages-container'); // enables image highlight on click const imageHighlighter = new ImageHighlighter('image-highlighter'); // enables the default action buttons on each pdf @@ -93,6 +93,7 @@ max-width: 95vw; margin: 0 auto; } + #global-buttons-container { display: flex; gap: 10px; @@ -178,27 +179,6 @@ } - .page-container.dragging { - width: 0px; - visibility: hidden; - } - - .page-container.draghover { - width: 500px !important; - } - - .page-container.draghover .insert-file-button-container { - display: none !important; - } - - .page-container.draghover .button-container { - visibility: hidden !important; - } - - .page-container.draghover img { - left: calc(50% + 125px) !important; - } - .page-container img { /* max-width: calc(100% - 15px); */ max-height: calc(100% - 15px); @@ -213,157 +193,6 @@ transition: rotate 0.3s; } - .page-container .button-container { - z-index: 2; - display:flex; - opacity: 0; - transition: opacity 0.1s linear; - } - - .page-container:hover .button-container { - opacity: 1; - } - - .page-container .button-container > * { - padding: 0.25rem 0.5rem; - margin: 3px; - display: block; - } - .page-container svg { - width: 16px; - height: 16px; - } - .page-container:nth-child(1) .move-left-button { - display: none; - } - .page-container:last-child .move-right-button { - display: none; - } - - .page-image { - cursor:pointer; - } - - /* "insert pdf" buttons that appear on the right when hover */ - .insert-file-button-container { - translate: 0 -50%; - width: 80px; - height: 100%; - - z-index: 1; - opacity: 0; - transition: opacity 0.2s; - } - - .insert-file-button-container.left { - left: -20px; - } - - .insert-file-button-container.right { - right: -20px; - } - - html[lang-direction=ltr] .insert-file-button-container.right { - display:none; - } - - html[lang-direction=rtl] .insert-file-button-container.left { - display:none; - } - - .insert-file-button-container.left .insert-file-button { - left: 0; - translate: 0 -50%; - } - - .insert-file-button-container.right .insert-file-button { - right: 0; - translate: 0 -50%; - } - - html[lang-direction=ltr] .page-container:last-child > .insert-file-button-container.right { - display: block; - } - - - html[lang-direction=rtl] .page-container:last-child > .insert-file-button-container.left { - display: block; - } - - .insert-file-button-container:hover { - opacity: 1; - transition: opacity 0.05s; - } - .insert-file-button { - position: absolute; - top: 50%; - right: 50%; - translate: 50% -50%; - aspect-ratio: 1; - border-radius: 100px; - } - - #image-highlighter { - position: fixed; - display:flex; - inset: 0; - z-index: 10000; - background-color: rgba(0, 0, 0, 0); - visibility: hidden; - align-items: center; - justify-content: center; - transition: visbility 0.1s linear, background-color 0.1s linear; - } - - #image-highlighter > * { - max-width: 80vw; - max-height: 80vh; - animation: image-highlight .1s linear; - transition: transform .1s linear, opacity .1s linear; - } - - #image-highlighter > *.remove { - transform: scale(0.8) !important; - opacity: 0 !important; - } - - #image-highlighter:not(:empty) { - background-color: rgba(0, 0, 0, 0.37); - visibility: visible; - } - - @keyframes image-highlight { - from { - transform: scale(0.8); - opacity: 0; - } - to { - transform: scale(1); - opacity: 1; - } - } - - #drag-container { - position: fixed; - display:flex; - inset: 0; - pointer-events: none; - z-index: 10000; - visibility: hidden; - } - - #drag-container:not(:empty) { - visibility: visible; - } - - #drag-container .dragged-img { - position: fixed; - max-width: 200px; - max-height: 200px; - transform: translate(-50%, -50%); - box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.58); - } - #add-pdf-button { margin: 0 auto; }