mirror of
https://github.com/suitenumerique/drive.git
synced 2026-04-25 17:15:19 +02:00
✨(frontend) close file preview on backdrop click
Users can now dismiss the preview by clicking the blurry area around the content. Each viewer marks its backdrop zones with a data attribute so the handler in FilesPreview can distinguish backdrop from content clicks. The image viewer also guards against accidental close during pan-drag gestures.
This commit is contained in:
@@ -129,6 +129,16 @@ export const FilePreview = ({
|
||||
window.open(currentFile.url_preview, "_blank");
|
||||
};
|
||||
|
||||
const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (!(e.target instanceof HTMLElement)) return;
|
||||
if (
|
||||
e.target === e.currentTarget ||
|
||||
e.target.dataset.previewBackdrop === "true"
|
||||
) {
|
||||
onClose?.();
|
||||
}
|
||||
};
|
||||
|
||||
// Render the appropriate viewer based on file category
|
||||
const renderViewer = () => {
|
||||
if (!currentFile) {
|
||||
@@ -165,7 +175,10 @@ export const FilePreview = ({
|
||||
);
|
||||
case MimeCategory.VIDEO:
|
||||
return (
|
||||
<div className="video-preview-viewer-container">
|
||||
<div
|
||||
className="video-preview-viewer-container"
|
||||
data-preview-backdrop="true"
|
||||
>
|
||||
<div className="video-preview-viewer">
|
||||
<VideoPlayer
|
||||
src={currentFile.url_preview}
|
||||
@@ -177,7 +190,10 @@ export const FilePreview = ({
|
||||
);
|
||||
case MimeCategory.AUDIO:
|
||||
return (
|
||||
<div className="video-preview-viewer-container">
|
||||
<div
|
||||
className="video-preview-viewer-container"
|
||||
data-preview-backdrop="true"
|
||||
>
|
||||
<div className="video-preview-viewer">
|
||||
<AudioPlayer
|
||||
src={currentFile.url_preview}
|
||||
@@ -302,6 +318,7 @@ export const FilePreview = ({
|
||||
>
|
||||
<div data-testid="file-preview">
|
||||
<div
|
||||
onClick={handleBackdropClick}
|
||||
className={clsx(
|
||||
"file-preview__container",
|
||||
isSidebarOpen && "file-preview__container--sidebar-open",
|
||||
|
||||
@@ -55,6 +55,7 @@ export const ImageViewer: React.FC<ImageViewerProps> = ({
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const imageRef = useRef<HTMLImageElement>(null);
|
||||
const mouseDownPosRef = useRef<{ x: number; y: number } | null>(null);
|
||||
|
||||
// Calculate distance between two touch points
|
||||
const getTouchDistance = useCallback(
|
||||
@@ -193,6 +194,7 @@ export const ImageViewer: React.FC<ImageViewerProps> = ({
|
||||
// Handle mouse down for dragging
|
||||
const handleMouseDown = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
mouseDownPosRef.current = { x: e.clientX, y: e.clientY };
|
||||
if (isImageExceedingBounds()) {
|
||||
setIsDragging(true);
|
||||
setDragStart({
|
||||
@@ -204,6 +206,20 @@ export const ImageViewer: React.FC<ImageViewerProps> = ({
|
||||
[isImageExceedingBounds, position],
|
||||
);
|
||||
|
||||
// Swallow the click that follows a pan-drag so it never bubbles to the
|
||||
// FilePreview backdrop handler. Without this, releasing a drag on the
|
||||
// empty area of the container would close the preview.
|
||||
const handleClickCapture = useCallback((e: React.MouseEvent) => {
|
||||
const start = mouseDownPosRef.current;
|
||||
mouseDownPosRef.current = null;
|
||||
if (!start) return;
|
||||
const dx = e.clientX - start.x;
|
||||
const dy = e.clientY - start.y;
|
||||
if (dx * dx + dy * dy > 16) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Handle mouse move for dragging
|
||||
const handleMouseMove = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
@@ -359,11 +375,13 @@ export const ImageViewer: React.FC<ImageViewerProps> = ({
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="image-viewer__container"
|
||||
data-preview-backdrop="true"
|
||||
tabIndex={0}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseLeave={handleMouseUp}
|
||||
onClickCapture={handleClickCapture}
|
||||
onWheel={handleWheel}
|
||||
onTouchStart={handleTouchStart}
|
||||
onTouchMove={handleTouchMove}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const NotSupportedPreview = ({
|
||||
}, [onDownload, file]);
|
||||
|
||||
return (
|
||||
<div className="file-preview-unsupported">
|
||||
<div className="file-preview-unsupported" data-preview-backdrop="true">
|
||||
<div className="file-preview-unsupported__icon">
|
||||
<FileIcon file={file} size="xlarge" />
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ export const OutdatedBrowserPreview = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="file-preview-unsupported">
|
||||
<div className="file-preview-unsupported" data-preview-backdrop="true">
|
||||
<div className="file-preview-unsupported__icon">
|
||||
<Icon name="security" type={IconType.OUTLINED} size={48} />
|
||||
</div>
|
||||
|
||||
@@ -137,6 +137,7 @@ export function PdfPageViewer({
|
||||
const rowRenderer: ListRowRenderer = ({ index, key, style }) => (
|
||||
<div
|
||||
key={key}
|
||||
data-preview-backdrop="true"
|
||||
style={{
|
||||
...style,
|
||||
display: "flex",
|
||||
|
||||
@@ -114,7 +114,7 @@ export function PdfPreview({
|
||||
|
||||
if (error?.message || documentError === "generic") {
|
||||
return (
|
||||
<div className="file-preview-unsupported">
|
||||
<div className="file-preview-unsupported" data-preview-backdrop="true">
|
||||
<div className="file-preview-unsupported__icon">
|
||||
<Icon name="error" type={IconType.OUTLINED} size={48} />
|
||||
</div>
|
||||
@@ -140,7 +140,7 @@ export function PdfPreview({
|
||||
|
||||
return (
|
||||
<div className="pdf-preview">
|
||||
<div className="pdf-preview__body">
|
||||
<div className="pdf-preview__body" data-preview-backdrop="true">
|
||||
{file ? (
|
||||
<Document
|
||||
file={file}
|
||||
|
||||
@@ -14,7 +14,7 @@ export const SuspiciousPreview = ({
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="file-preview-suspicious">
|
||||
<div className="file-preview-suspicious" data-preview-backdrop="true">
|
||||
<div className="file-preview-suspicious__icon">
|
||||
<img src={mimeSuspicious.src} alt="" className="item-icon xlarge" />
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@ export const WopiOpenInEditor = ({ file }: WopiOpenInEditorProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="file-preview-unsupported">
|
||||
<div className="file-preview-unsupported" data-preview-backdrop="true">
|
||||
<div className="file-preview-unsupported__icon">
|
||||
<FileIcon file={file} size="xlarge" />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user