Prevented Escape from closing the image editor (#18505)

no issue

`Escape` can often be hit when attempting to cancel or undo an action
through muscle memory despite it not doing anything in Pintura except
closing the modal and losing all changes

- added `willClose` option to the Pintura modal settings which only
returns `true` when we've detected a click on the close button
This commit is contained in:
Kevin Ansfield 2023-10-05 17:30:56 +01:00 committed by GitHub
parent 1775591472
commit d812781888
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 73 additions and 3 deletions

View File

@ -3,7 +3,7 @@ import {Config} from '../api/config';
import {Setting} from '../api/settings';
import {getGhostPaths} from '../utils/helpers';
import {getSettingValues} from '../api/settings';
import {useCallback, useEffect, useState} from 'react';
import {useCallback, useEffect, useRef, useState} from 'react';
import {useGlobalData} from '../components/providers/GlobalDataProvider';
interface PinturaEditorConfig {
@ -42,6 +42,7 @@ declare global {
labelButtonExport: string;
};
previewPad: boolean;
willClose: () => boolean;
}) => {
on: (event: string, callback: (result: { dest: File }) => void) => void;
};
@ -58,6 +59,8 @@ export default function usePinturaEditor({
const [pintura] = getSettingValues<boolean>(settings, ['pintura']);
const [scriptLoaded, setScriptLoaded] = useState<boolean>(false);
const [cssLoaded, setCssLoaded] = useState<boolean>(false);
const [isOpen, setIsOpen] = useState<boolean>(false);
const allowClose = useRef<boolean>(false);
let isEnabled = pintura && scriptLoaded && cssLoaded || false;
const pinturaConfig = globalConfig?.pintura as { js?: string; css?: string };
@ -205,7 +208,16 @@ export default function usePinturaEditor({
locale: {
labelButtonExport: 'Save and close'
},
previewPad: true
previewPad: true,
// Skip default Escape to close behaviour, only allow when the close button is clicked
willClose: () => {
if (allowClose.current) {
setIsOpen(false);
return true;
}
return false;
}
});
editor.on('loaderror', () => {
@ -215,11 +227,32 @@ export default function usePinturaEditor({
editor.on('process', (result) => {
handleSave(result.dest);
});
setIsOpen(true);
}
},
[isEnabled]
);
// Only allow closing the modal if the close button was clicked
useEffect(() => {
if (!isOpen) {
return;
}
const handleCloseClick = (event: MouseEvent) => {
if (event.target instanceof Element && event.target.closest('.PinturaModal button[title="Close"]')) {
allowClose.current = true;
}
};
window.addEventListener('click', handleCloseClick, {capture: true});
return () => {
window.removeEventListener('click', handleCloseClick, {capture: true});
};
}, [isOpen]);
return {
isEnabled,
openEditor

View File

@ -10,8 +10,10 @@ export default class KoenigImageEditor extends Component {
@service feature;
@service settings;
@service ghostPaths;
@tracked scriptLoaded = false;
@tracked cssLoaded = false;
@tracked allowClose = false;
@inject config;
@ -128,6 +130,11 @@ export default class KoenigImageEditor extends Component {
this.loadImageEditorCSS();
}
willDestroy() {
super.willDestroy(...arguments);
this.removeCloseHandler();
}
@action
async onUploadComplete(urlList) {
if (this.args.saveUrl) {
@ -136,9 +143,23 @@ export default class KoenigImageEditor extends Component {
}
}
@action
willClose() {
if (this.allowClose) {
this.allowClose = false;
this.removeCloseHandler();
return true;
}
return false;
}
@action
async handleClick(uploader) {
if (this.isEditorEnabled && this.args.imageSrc) {
this.allowClose = false;
this.addCloseHandler();
// add a timestamp to the image src to bypass cache
// avoids cors issues with cached images
const imageUrl = new URL(this.args.imageSrc);
@ -199,7 +220,8 @@ export default class KoenigImageEditor extends Component {
],
locale: {
labelButtonExport: 'Save and close'
}
},
willClose: this.willClose
});
editor.on('loaderror', () => {
@ -222,4 +244,19 @@ export default class KoenigImageEditor extends Component {
});
}
}
@action
handleCloseClick(event) {
if (event.target.closest('.PinturaModal button[title="Close"]')) {
this.allowClose = true;
}
}
addCloseHandler() {
window.addEventListener('click', this.handleCloseClick, {capture: true});
}
removeCloseHandler() {
window.removeEventListener('click', this.handleCloseClick, {capture: true});
}
}