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); + } ?>