From e62ee973aee785ab732509769ba3a8d6e8641525 Mon Sep 17 00:00:00 2001 From: Thomas Skerbis Date: Sat, 30 May 2026 22:17:00 +0200 Subject: [PATCH] fixes issue by norbert --- assets/cropper_ui_fix.css | 89 ++++++++++++++++++--------------- assets/js/rex_cropper.js | 87 +++++++++++++++++++++++++------- fragments/cropper_panel.php | 55 ++++++-------------- install.php | 4 ++ lang/de_de.lang | 2 + lang/en_gb.lang | 2 + lib/Cropper/CropperExecutor.php | 4 +- pages/mediapool.cropper.php | 57 ++++++++++++++++++++- pages/settings.php | 4 ++ update.php | 4 ++ 10 files changed, 206 insertions(+), 102 deletions(-) diff --git a/assets/cropper_ui_fix.css b/assets/cropper_ui_fix.css index 6e9fc4f..a90879f 100644 --- a/assets/cropper_ui_fix.css +++ b/assets/cropper_ui_fix.css @@ -30,6 +30,25 @@ form.cropper-is-saving .cropper-save-overlay { z-index: 1200; } + .cropper-page-options { + align-items: center; + display: inline-flex; + gap: 10px; + } + + .cropper-page-toggles { + align-items: center; + display: inline-flex; + gap: 8px; + } + + .cropper-page-toggles .btn { + border-radius: 8px; + min-height: 32px; + min-width: 32px; + padding: 0; + } + body.rex-theme-dark #cropper-workspace { --cropper-bg: #18212b; --cropper-panel: #202b36; @@ -186,43 +205,12 @@ form.cropper-is-saving .cropper-save-overlay { overflow: hidden; } - #cropper-workspace .cropper-stage-header, #cropper-workspace .docs-buttons, #cropper-workspace .docs-toggles, #cropper-workspace .cropper-sidebar-panel { padding: 18px 20px; } - #cropper-workspace .cropper-stage-header { - align-items: flex-start; - display: flex; - gap: 16px; - justify-content: space-between; - } - - #cropper-workspace .cropper-stage-actions { - align-items: center; - display: inline-flex; - gap: 8px; - } - - #cropper-workspace .cropper-sidebar-toggle { - min-height: 40px; - min-width: 40px; - } - - #cropper-workspace .cropper-toolbar-toggle { - display: none; - min-height: 40px; - min-width: 40px; - } - - #cropper-workspace.is-compact-toolbar .cropper-toolbar-toggle { - align-items: center; - display: inline-flex; - justify-content: center; - } - #cropper-workspace.is-sidebar-collapsed .cropper-hero { grid-template-columns: minmax(0, 1fr); } @@ -231,7 +219,6 @@ form.cropper-is-saving .cropper-save-overlay { display: none; } - #cropper-workspace .cropper-stage-kicker, #cropper-workspace .cropper-toolbar-label { color: var(--cropper-text-soft); display: block; @@ -242,15 +229,8 @@ form.cropper-is-saving .cropper-save-overlay { text-transform: uppercase; } - #cropper-workspace .cropper-stage-title { - font-size: 18px; - font-weight: 600; - line-height: 1.3; - margin: 0; - } - #cropper-workspace .cropper_image_wrapper { - padding: 0 20px 20px; + padding: 20px; } #cropper-workspace .cropper-stage { @@ -267,7 +247,6 @@ form.cropper-is-saving .cropper-save-overlay { #cropper-workspace cropper-canvas { background-color: transparent; - border-radius: 12px; height: var(--cropper-stage-height); min-height: var(--cropper-stage-height); width: 100%; @@ -404,6 +383,34 @@ form.cropper-is-saving .cropper-save-overlay { #cropper-workspace .cropper-settings { margin-left: auto; + position: relative; + } + + #cropper-workspace .cropper-settings .dropdown-menu { + border-radius: 10px; + margin-top: 8px; + min-width: 180px; + padding: 8px; + z-index: 1200; + } + + #cropper-workspace .cropper-settings .dropdown-menu > li { + list-style: none; + } + + #cropper-workspace .cropper-settings .dropdown-menu > li > label { + align-items: center; + cursor: pointer; + display: inline-flex; + gap: 8px; + margin: 0; + padding: 4px 2px; + width: 100%; + } + + #cropper-workspace .cropper-settings .dropdown-menu .form-check-input { + margin: 0; + position: static; } #cropper-workspace .cropper-sidebar { diff --git a/assets/js/rex_cropper.js b/assets/js/rex_cropper.js index 8c6a64a..d7246ea 100644 --- a/assets/js/rex_cropper.js +++ b/assets/js/rex_cropper.js @@ -177,8 +177,8 @@ class BackendCropper { this.selectionOverlay = this.root.querySelector('#cropper-selection-overlay'); this.selectionGrabHandle = this.root.querySelector('#cropper-selection-grab'); this.sidebar = this.root.querySelector('#cropper-sidebar'); - this.sidebarToggle = this.root.querySelector('#cropper_sidebar_toggle'); - this.toolbarToggle = this.root.querySelector('#cropper_toolbar_toggle'); + this.sidebarToggle = this.root.querySelector('#cropper_sidebar_toggle') || document.querySelector('#cropper_sidebar_toggle'); + this.toolbarToggle = this.root.querySelector('#cropper_toolbar_toggle') || document.querySelector('#cropper_toolbar_toggle'); this.toolbarClose = this.root.querySelector('#cropper_toolbar_close'); this.toolbarButtons = this.root.querySelector('#cropper-toolbar-buttons'); this.toolbarToggles = this.root.querySelector('#cropper-toolbar-toggles'); @@ -221,7 +221,7 @@ class BackendCropper { } this.handleCanvasAction = this.syncHiddenFields.bind(this); - this.handleWheel = this.preventWheelZoom.bind(this); + this.handleStageWheel = this.forwardWheelToPageWhenZoomDisabled.bind(this); this.handleWindowResize = this.updateStageHeight.bind(this); this.handleSelectionGripMove = this.moveSelectionWithGrip.bind(this); this.handleSelectionGripEnd = this.stopSelectionGripDrag.bind(this); @@ -245,7 +245,8 @@ class BackendCropper { window.addEventListener('resize', this.handleWindowResize); this.cropperCanvas.addEventListener('action', this.handleCanvasAction); this.cropperCanvas.addEventListener('actionend', this.handleCanvasAction); - this.cropperCanvas.addEventListener('wheel', this.handleWheel, { capture: true }); + this.cropperCanvas.addEventListener('wheel', this.handleStageWheel, { capture: true, passive: false }); + this.stage?.addEventListener('wheel', this.handleStageWheel, { capture: true, passive: false }); this.root.querySelector('.docs-buttons')?.addEventListener('click', (event) => { const button = event.target.closest('[data-method]'); @@ -281,10 +282,17 @@ class BackendCropper { } if (input.type === 'checkbox' && input.name === 'zoomOnWheel') { - this.state.wheelZoomEnabled = input.checked; + this.applyWheelZoomState(input.checked); } }); + const wheelZoomCheckbox = this.root.querySelector('input[name="zoomOnWheel"]'); + if (wheelZoomCheckbox instanceof HTMLInputElement) { + this.applyWheelZoomState(wheelZoomCheckbox.checked); + } else { + this.applyWheelZoomState(false); + } + this.root.querySelector('.cropper-ratio-group')?.addEventListener('click', (event) => { const label = event.target.closest('label.btn'); @@ -407,12 +415,14 @@ class BackendCropper { return; } - let collapsed = false; + const initialOpen = this.root.dataset.sidebarInitialOpen === '1'; + let collapsed = !initialOpen; try { - collapsed = window.localStorage.getItem(this.sidebarStorageKey) === '1'; + const storedValue = window.localStorage.getItem(this.sidebarStorageKey); + collapsed = storedValue === null ? !initialOpen : storedValue === '1'; } catch (error) { - collapsed = false; + collapsed = !initialOpen; } this.setSidebarCollapsed(collapsed, false); @@ -633,7 +643,7 @@ class BackendCropper { this.cropperSelection.movable = true; this.cropperSelection.resizable = true; this.cropperSelection.keyboard = true; - this.cropperSelection.zoomable = true; + this.cropperSelection.zoomable = this.state.wheelZoomEnabled; this.cropperSelection.aspectRatio = this.getSelectedAspectRatio(); this.cropperSelection.initialAspectRatio = this.getSelectedAspectRatio(); this.cropperSelection.hidden = false; @@ -641,6 +651,56 @@ class BackendCropper { this.setDragMode('crop'); } + applyWheelZoomState(enabled) { + const normalized = enabled === true; + this.state.wheelZoomEnabled = normalized; + + if (this.cropperSelection) { + this.cropperSelection.zoomable = normalized; + } + + if (this.cropperCanvas && 'scaleStep' in this.cropperCanvas) { + this.cropperCanvas.scaleStep = normalized ? 0.1 : 0; + } + } + + forwardWheelToPageWhenZoomDisabled(event) { + if (this.state.wheelZoomEnabled) { + return; + } + + const target = event.target; + const inStage = target instanceof Node + && ( + (this.cropperCanvas instanceof HTMLElement && this.cropperCanvas.contains(target)) + || (this.stage instanceof HTMLElement && this.stage.contains(target)) + ); + + if (!inStage) { + return; + } + + event.preventDefault(); + event.stopImmediatePropagation(); + + let deltaX = event.deltaX; + let deltaY = event.deltaY; + + if (event.deltaMode === 1) { + deltaX *= 16; + deltaY *= 16; + } else if (event.deltaMode === 2) { + deltaX *= window.innerWidth; + deltaY *= window.innerHeight; + } + + window.scrollBy({ + left: deltaX, + top: deltaY, + behavior: 'auto', + }); + } + ensureSelection() { if (!this.cropperSelection) { return false; @@ -892,15 +952,6 @@ class BackendCropper { this.selectionOverlay.style.height = `${this.cropperSelection.height}px`; } - preventWheelZoom(event) { - if (this.state.wheelZoomEnabled) { - return; - } - - event.preventDefault(); - event.stopImmediatePropagation(); - } - applyAspectRatio(ratio) { if (!this.ensureSelection()) { return; diff --git a/fragments/cropper_panel.php b/fragments/cropper_panel.php index 4d392b9..28766aa 100644 --- a/fragments/cropper_panel.php +++ b/fragments/cropper_panel.php @@ -39,6 +39,21 @@ } elseif (is_array($compactToolbarConfig)) { $compactToolbarInStage = in_array(1, $compactToolbarConfig, true) || in_array('1', $compactToolbarConfig, true); } + + $showSidebarInitiallyConfig = rex_config::get('cropper', 'show_info_sidebar_initially', 0); + $showSidebarInitially = false; + if (is_bool($showSidebarInitiallyConfig)) { + $showSidebarInitially = $showSidebarInitiallyConfig; + } elseif (is_int($showSidebarInitiallyConfig) || is_float($showSidebarInitiallyConfig)) { + $showSidebarInitially = (int) $showSidebarInitiallyConfig === 1; + } elseif (is_string($showSidebarInitiallyConfig)) { + $trimmedConfig = trim($showSidebarInitiallyConfig); + if ('' !== $trimmedConfig) { + $showSidebarInitially = preg_match('/(^|\|)1(\||$)/', $trimmedConfig) === 1; + } + } elseif (is_array($showSidebarInitiallyConfig)) { + $showSidebarInitially = in_array(1, $showSidebarInitiallyConfig, true) || in_array('1', $showSidebarInitiallyConfig, true); + } ?>
-
-
- -

escape($media->getFileName()) ?>

-
-
- - - - -
-
-
diff --git a/install.php b/install.php index b5928db..21220e1 100644 --- a/install.php +++ b/install.php @@ -12,3 +12,7 @@ if (null === $addon->getConfig('compact_toolbar_in_stage')) { $addon->setConfig('compact_toolbar_in_stage', 0); } + +if (null === $addon->getConfig('show_info_sidebar_initially')) { + $addon->setConfig('show_info_sidebar_initially', 0); +} diff --git a/lang/de_de.lang b/lang/de_de.lang index 666c8b8..99b30f0 100644 --- a/lang/de_de.lang +++ b/lang/de_de.lang @@ -17,6 +17,8 @@ cropper_settings_show_edit_in_list = Zuschneiden-Link in der Medienliste einblen cropper_settings_show = Zuschneiden-Link einblenden cropper_settings_toolbar_mode = Toolbar-Modus cropper_settings_toolbar_compact_hover = Kompakte Werkzeug-Seitenleiste neben der Bühne aktivieren +cropper_settings_sidebar_mode = Info-Sidebar +cropper_settings_sidebar_initial_open = Info-Sidebar beim Öffnen anzeigen cropper_saving_message = Bild wird gespeichert... cropper_workspace_title = Arbeitsbereich cropper_workspace_hint = Mehr Platz, schnelleres Zuschneiden: Ausschnitt ziehen, mit den Presets skalieren und direkt in der Vorschau kontrollieren. diff --git a/lang/en_gb.lang b/lang/en_gb.lang index 5a1d1e9..0898bc4 100644 --- a/lang/en_gb.lang +++ b/lang/en_gb.lang @@ -16,6 +16,8 @@ cropper_settings_show_edit_in_list = Show cropping link in media list cropper_settings_show = Show cropping link cropper_settings_toolbar_mode = Toolbar mode cropper_settings_toolbar_compact_hover = Enable compact tool sidebar next to stage +cropper_settings_sidebar_mode = Info sidebar +cropper_settings_sidebar_initial_open = Show info sidebar on open cropper_saving_message = Saving image... cropper_workspace_title = Workspace cropper_workspace_hint = More room, faster cropping: draw a selection, resize it with presets and verify the result in the live preview. diff --git a/lib/Cropper/CropperExecutor.php b/lib/Cropper/CropperExecutor.php index c142aba..4e64f2f 100644 --- a/lib/Cropper/CropperExecutor.php +++ b/lib/Cropper/CropperExecutor.php @@ -24,8 +24,6 @@ use const PATHINFO_EXTENSION; use const PATHINFO_FILENAME; -class CroppingException extends Exception {} - class CropperExecutor { public const MSG_SUCCESSFUL_CREATED = 'cropper_successful_created'; @@ -197,7 +195,7 @@ public function crop(): array // crop image if ($cropWidth > 0 && $cropHeight > 0) { $imageSize = @getimagesize($this->zebraImage->getTargetPath()); - if (is_array($imageSize) && isset($imageSize[0], $imageSize[1])) { + if (is_array($imageSize)) { $imageWidth = (int) $imageSize[0]; $imageHeight = (int) $imageSize[1]; diff --git a/pages/mediapool.cropper.php b/pages/mediapool.cropper.php index d7db39c..1f5f87b 100644 --- a/pages/mediapool.cropper.php +++ b/pages/mediapool.cropper.php @@ -23,7 +23,62 @@ $title = ''; $class = 'edit'; -$back = '' . rex_i18n::msg('cropper_back_to_media') . ''; +$compactToolbarConfig = rex_config::get('cropper', 'compact_toolbar_in_stage', 0); +$compactToolbarInStage = false; +if (is_bool($compactToolbarConfig)) { + $compactToolbarInStage = $compactToolbarConfig; +} elseif (is_int($compactToolbarConfig) || is_float($compactToolbarConfig)) { + $compactToolbarInStage = (int) $compactToolbarConfig === 1; +} elseif (is_string($compactToolbarConfig)) { + $trimmedConfig = trim($compactToolbarConfig); + if ('' !== $trimmedConfig) { + $compactToolbarInStage = preg_match('/(^|\|)1(\||$)/', $trimmedConfig) === 1; + } +} elseif (is_array($compactToolbarConfig)) { + $compactToolbarInStage = in_array(1, $compactToolbarConfig, true) || in_array('1', $compactToolbarConfig, true); +} + +$backLink = '' . rex_i18n::msg('cropper_back_to_media') . ''; + +$toggleButtons = ' +
+ '; + +if ($compactToolbarInStage) { + $toggleButtons .= ' + '; +} + +$toggleButtons .= ' +
'; + +$back = '
' . $backLink . $toggleButtons . '
'; if (!$user instanceof rex_user || !$user->hasPerm('cropper[]')) { rex_response::sendRedirect(rex_url::backendPage(POOL_MEDIA, $urlParameter, false)); diff --git a/pages/settings.php b/pages/settings.php index 12e2c80..b7e579c 100644 --- a/pages/settings.php +++ b/pages/settings.php @@ -20,6 +20,10 @@ $field->setLabel($addon->i18n('cropper_settings_toolbar_mode')); $field->addOption($addon->i18n('cropper_settings_toolbar_compact_hover'), 1); +$field = $form->addCheckboxField('show_info_sidebar_initially'); +$field->setLabel($addon->i18n('cropper_settings_sidebar_mode')); +$field->addOption($addon->i18n('cropper_settings_sidebar_initial_open'), 1); + // Ausgabe des Formulars $fragment = new rex_fragment(); $fragment->setVar('class', 'edit', false); diff --git a/update.php b/update.php index 58fd0ed..280e3e7 100644 --- a/update.php +++ b/update.php @@ -13,5 +13,9 @@ $addon->setConfig('compact_toolbar_in_stage', 0); } +if (null === $addon->getConfig('show_info_sidebar_initially')) { + $addon->setConfig('show_info_sidebar_initially', 0); +} +