From b7fabfda68a13c10d771094649435abac2552775 Mon Sep 17 00:00:00 2001 From: John BOTELLA <68917336+thersane-john@users.noreply.github.com> Date: Sat, 6 Dec 2025 14:21:15 +0100 Subject: [PATCH] add grab to scroll behavior --- htdocs/public/webportal/css/mixin.css | 14 +++++ htdocs/public/webportal/js/theme.js | 59 ++++++++++++++++++++++ htdocs/public/webportal/tpl/footer.tpl.php | 5 ++ 3 files changed, 78 insertions(+) diff --git a/htdocs/public/webportal/css/mixin.css b/htdocs/public/webportal/css/mixin.css index 01c3717456e..ca1c9c7970e 100644 --- a/htdocs/public/webportal/css/mixin.css +++ b/htdocs/public/webportal/css/mixin.css @@ -14,3 +14,17 @@ .width150 { width: 150px; } + +/* special style for drag to scroll behavior */ +.--drag-to-scroll{ + cursor: grab; +} +.--drag-to-scroll-is-active { + user-select: none; + cursor: grabbing; +} +.--drag-to-scroll-is-active object, +.--drag-to-scroll-is-active iframe, +.--drag-to-scroll-is-active embed { + pointer-events: none; +} diff --git a/htdocs/public/webportal/js/theme.js b/htdocs/public/webportal/js/theme.js index 10d7a2231a4..068916e1036 100644 --- a/htdocs/public/webportal/js/theme.js +++ b/htdocs/public/webportal/js/theme.js @@ -19,3 +19,62 @@ document.addEventListener("DOMContentLoaded", function() { }); }); + +/** + * Enables drag-to-scroll behavior on a given element. + * Supports mouse, touch, pen, X and Y scrolling. + * + * @param {string | Element | jQuery} slider - A CSS selector, DOM element, or jQuery object. + * @param {string|false} classForEnable - Optional class required to enable dragging. + */ +function dragToScroll(slider, classForEnable = false) { + + const classActive = '--drag-to-scroll-is-active'; + + // Normalize input + if (typeof slider === 'string') { + slider = document.querySelector(slider); + } else if (window.jQuery && slider instanceof jQuery) { + slider = slider[0]; + } else if (!(slider instanceof Element)) { + console.warn('dragToScroll: invalid element', slider); + return; + } + + let isDown = false; + let startX; + let startY; + let scrollLeft; + let scrollTop; + + slider.addEventListener('pointerdown', (e) => { + if (classForEnable && !slider.classList.contains(classForEnable)) return; + + isDown = true; + slider.setPointerCapture(e.pointerId); + slider.classList.add(classActive); + + startX = e.clientX; + startY = e.clientY; + + scrollLeft = slider.scrollLeft; + scrollTop = slider.scrollTop; + }); + + slider.addEventListener('pointermove', (e) => { + if (!isDown) return; + + e.preventDefault(); + + const walkX = e.clientX - startX; + const walkY = e.clientY - startY; + + slider.scrollLeft = scrollLeft - walkX; + slider.scrollTop = scrollTop - walkY; + }); + + slider.addEventListener('pointerup', () => { + isDown = false; + slider.classList.remove(classActive); + }); +} diff --git a/htdocs/public/webportal/tpl/footer.tpl.php b/htdocs/public/webportal/tpl/footer.tpl.php index 902a8c5cedf..87a363188ab 100644 --- a/htdocs/public/webportal/tpl/footer.tpl.php +++ b/htdocs/public/webportal/tpl/footer.tpl.php @@ -258,6 +258,10 @@ if (empty($conf->browser->layout) || $conf->browser->layout != 'phone') { ?> $('#modalforpopup article').html(newElem); + if (mode == 'image' && showOriginalSizeButton) { + dragToScroll($('#modalforpopup .dialog-body'), '--drag-to-scroll'); + } + let modal = document.getElementById('modalforpopup'); // Add close button handler @@ -278,6 +282,7 @@ if (empty($conf->browser->layout) || $conf->browser->layout != 'phone') { ?> function document_preview_original_size() { console.log("document_preview_original_size A click on original size"); jQuery("#modalforpopup object.object-preview").toggleClass('--show-original-size'); + jQuery("#modalforpopup .dialog-body").toggleClass('--drag-to-scroll'); }