fix: Capture drop events in clickable padding below editor (#3376)
* fix: Capture drop events in clickable padding below editor * fix: Inconsistency in drop handling
This commit is contained in:
@@ -66,7 +66,7 @@ function CollectionDescription({ collection }: Props) {
|
||||
throw err;
|
||||
}
|
||||
}, 1000),
|
||||
[]
|
||||
[collection, showToast, t]
|
||||
);
|
||||
|
||||
const handleChange = React.useCallback(
|
||||
@@ -111,7 +111,6 @@ function CollectionDescription({ collection }: Props) {
|
||||
maxLength={1000}
|
||||
embedsDisabled
|
||||
readOnlyWriteCheckboxes
|
||||
grow
|
||||
/>
|
||||
</React.Suspense>
|
||||
) : (
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import { deburr, sortBy } from "lodash";
|
||||
import { TextSelection } from "prosemirror-state";
|
||||
import * as React from "react";
|
||||
import { Optional } from "utility-types";
|
||||
import insertFiles from "@shared/editor/commands/insertFiles";
|
||||
import embeds from "@shared/editor/embeds";
|
||||
import { supportedImageMimeTypes } from "@shared/utils/files";
|
||||
import getDataTransferFiles from "@shared/utils/getDataTransferFiles";
|
||||
import parseDocumentSlug from "@shared/utils/parseDocumentSlug";
|
||||
import { isInternalUrl } from "@shared/utils/urls";
|
||||
import Document from "~/models/Document";
|
||||
import ClickablePadding from "~/components/ClickablePadding";
|
||||
import ErrorBoundary from "~/components/ErrorBoundary";
|
||||
import HoverPreview from "~/components/HoverPreview";
|
||||
import type { Props as EditorProps, Editor as SharedEditor } from "~/editor";
|
||||
@@ -44,7 +49,7 @@ export type Props = Optional<
|
||||
onPublish?: (event: React.MouseEvent) => any;
|
||||
};
|
||||
|
||||
function Editor(props: Props, ref: React.Ref<SharedEditor>) {
|
||||
function Editor(props: Props, ref: React.RefObject<SharedEditor>) {
|
||||
const { id, shareId } = props;
|
||||
const { documents } = useStores();
|
||||
const { showToast } = useToasts();
|
||||
@@ -159,6 +164,58 @@ function Editor(props: Props, ref: React.Ref<SharedEditor>) {
|
||||
[shareId]
|
||||
);
|
||||
|
||||
const focusAtEnd = React.useCallback(() => {
|
||||
ref.current?.focusAtEnd();
|
||||
}, [ref]);
|
||||
|
||||
const handleDrop = React.useCallback(
|
||||
(event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const files = getDataTransferFiles(event);
|
||||
const view = ref.current?.view;
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert all files as attachments if any of the files are not images.
|
||||
const isAttachment = files.some(
|
||||
(file) => !supportedImageMimeTypes.includes(file.type)
|
||||
);
|
||||
|
||||
// Find a valid position at the end of the document
|
||||
const pos = TextSelection.near(
|
||||
view.state.doc.resolve(view.state.doc.nodeSize - 2)
|
||||
).from;
|
||||
|
||||
insertFiles(view, event, pos, files, {
|
||||
uploadFile: onUploadFile,
|
||||
onFileUploadStart: props.onFileUploadStart,
|
||||
onFileUploadStop: props.onFileUploadStop,
|
||||
onShowToast: showToast,
|
||||
dictionary,
|
||||
isAttachment,
|
||||
});
|
||||
},
|
||||
[
|
||||
ref,
|
||||
props.onFileUploadStart,
|
||||
props.onFileUploadStop,
|
||||
dictionary,
|
||||
onUploadFile,
|
||||
showToast,
|
||||
]
|
||||
);
|
||||
|
||||
// see: https://stackoverflow.com/a/50233827/192065
|
||||
const handleDragOver = React.useCallback(
|
||||
(event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<ErrorBoundary reloadOnChunkMissing>
|
||||
<>
|
||||
@@ -175,6 +232,14 @@ function Editor(props: Props, ref: React.Ref<SharedEditor>) {
|
||||
placeholder={props.placeholder || ""}
|
||||
defaultValue={props.defaultValue || ""}
|
||||
/>
|
||||
{props.grow && !props.readOnly && (
|
||||
<ClickablePadding
|
||||
onClick={focusAtEnd}
|
||||
onDrop={handleDrop}
|
||||
onDragOver={handleDragOver}
|
||||
grow
|
||||
/>
|
||||
)}
|
||||
{activeLinkEvent && !shareId && (
|
||||
<HoverPreview
|
||||
node={activeLinkEvent.target as HTMLAnchorElement}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useTranslation } from "react-i18next";
|
||||
import { useRouteMatch } from "react-router-dom";
|
||||
import fullPackage from "@shared/editor/packages/full";
|
||||
import Document from "~/models/Document";
|
||||
import ClickablePadding from "~/components/ClickablePadding";
|
||||
import { RefHandle } from "~/components/ContentEditable";
|
||||
import DocumentMetaWithViews from "~/components/DocumentMetaWithViews";
|
||||
import Editor, { Props as EditorProps } from "~/components/Editor";
|
||||
@@ -40,6 +39,17 @@ function DocumentEditor(props: Props, ref: React.RefObject<any>) {
|
||||
const titleRef = React.useRef<RefHandle>(null);
|
||||
const { t } = useTranslation();
|
||||
const match = useRouteMatch();
|
||||
const {
|
||||
document,
|
||||
title,
|
||||
onChangeTitle,
|
||||
isDraft,
|
||||
shareId,
|
||||
readOnly,
|
||||
children,
|
||||
multiplayer,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const focusAtStart = React.useCallback(() => {
|
||||
if (ref.current) {
|
||||
@@ -47,12 +57,6 @@ function DocumentEditor(props: Props, ref: React.RefObject<any>) {
|
||||
}
|
||||
}, [ref]);
|
||||
|
||||
const focusAtEnd = React.useCallback(() => {
|
||||
if (ref.current) {
|
||||
ref.current.focusAtEnd();
|
||||
}
|
||||
}, [ref]);
|
||||
|
||||
const handleBlur = React.useCallback(() => {
|
||||
props.onSave({ autosave: true });
|
||||
}, [props]);
|
||||
@@ -70,17 +74,6 @@ function DocumentEditor(props: Props, ref: React.RefObject<any>) {
|
||||
[focusAtStart, ref]
|
||||
);
|
||||
|
||||
const {
|
||||
document,
|
||||
title,
|
||||
onChangeTitle,
|
||||
isDraft,
|
||||
shareId,
|
||||
readOnly,
|
||||
children,
|
||||
multiplayer,
|
||||
...rest
|
||||
} = props;
|
||||
const EditorComponent = multiplayer ? MultiplayerEditor : Editor;
|
||||
|
||||
return (
|
||||
@@ -121,7 +114,6 @@ function DocumentEditor(props: Props, ref: React.RefObject<any>) {
|
||||
grow
|
||||
{...rest}
|
||||
/>
|
||||
{!readOnly && <ClickablePadding onClick={focusAtEnd} grow />}
|
||||
{children}
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -21,7 +21,10 @@ export type Options = {
|
||||
|
||||
const insertFiles = function (
|
||||
view: EditorView,
|
||||
event: Event | React.ChangeEvent<HTMLInputElement>,
|
||||
event:
|
||||
| Event
|
||||
| React.ChangeEvent<HTMLInputElement>
|
||||
| React.DragEvent<HTMLDivElement>,
|
||||
pos: number,
|
||||
files: File[],
|
||||
options: Options
|
||||
|
||||
Reference in New Issue
Block a user