mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
fix: Dragging images in editor (#12647)
* fix: Editor image dragging * feedback
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { EditorAwareHTML5Backend } from "~/components/EditorAwareHTML5Backend";
|
||||
import ErrorSuspended from "~/scenes/Errors/ErrorSuspended";
|
||||
import Layout from "~/components/Layout";
|
||||
import RegisterKeyDown from "~/components/RegisterKeyDown";
|
||||
@@ -106,7 +106,7 @@ const AuthenticatedLayout: React.FC = ({ children }: Props) => {
|
||||
<DocumentContextProvider>
|
||||
<RightSidebarProvider>
|
||||
<PortalContext.Provider value={layoutRef.current}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<DndProvider backend={EditorAwareHTML5Backend}>
|
||||
<Layout title={team.name} sidebar={sidebar} ref={layoutRef}>
|
||||
<RegisterKeyDown trigger="n" handler={goToNewDocument} />
|
||||
<RegisterKeyDown trigger="t" handler={goToSearch} />
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import type { BackendFactory } from "dnd-core";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
|
||||
/**
|
||||
* react-dnd's HTML5 backend installs global capture-phase listeners on `window`
|
||||
* that call `preventDefault()` on drops whose dataTransfer resembles a native
|
||||
* item – including a dragged `<img>`, which is how ProseMirror serializes an
|
||||
* image drag.
|
||||
*
|
||||
* These handlers run before ProseMirror's, and they live on `window`, so a
|
||||
* propagation-based guard can't stop react-dnd without also starving the editor
|
||||
* of the event. Instead we wrap the backend and make its top-level capture
|
||||
* handlers no-op for events that occur within the editor surface.
|
||||
*/
|
||||
const captureHandlerNames = [
|
||||
"handleTopDragStartCapture",
|
||||
"handleTopDragEnterCapture",
|
||||
"handleTopDragOverCapture",
|
||||
"handleTopDragLeaveCapture",
|
||||
"handleTopDropCapture",
|
||||
"handleTopDragEndCapture",
|
||||
] as const;
|
||||
|
||||
const isWithinEditor = (target: EventTarget | null): boolean =>
|
||||
target instanceof Element && Boolean(target.closest(".ProseMirror"));
|
||||
|
||||
/**
|
||||
* An HTML5 drag-and-drop backend that ignores drag events originating within the
|
||||
* rich text editor so that ProseMirror can handle them itself.
|
||||
*
|
||||
* @param manager The drag-and-drop manager.
|
||||
* @param context The global context.
|
||||
* @param options Backend options.
|
||||
* @returns The wrapped HTML5 backend instance.
|
||||
*/
|
||||
export const EditorAwareHTML5Backend: BackendFactory = (
|
||||
manager,
|
||||
context,
|
||||
options
|
||||
) => {
|
||||
const backend = HTML5Backend(manager, context, options);
|
||||
|
||||
// The capture handlers are private instance fields on the backend, so reach
|
||||
// for them through an index signature view of the instance.
|
||||
const handlers = backend as unknown as Record<
|
||||
string,
|
||||
(event: DragEvent) => void
|
||||
>;
|
||||
|
||||
for (const name of captureHandlerNames) {
|
||||
const original = handlers[name];
|
||||
if (typeof original === "function") {
|
||||
handlers[name] = (event: DragEvent) => {
|
||||
if (isWithinEditor(event.target)) {
|
||||
return;
|
||||
}
|
||||
original.call(backend, event);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return backend;
|
||||
};
|
||||
Reference in New Issue
Block a user