1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-02 12:47:41 +02:00

Desktop: Fixes #11274: Fix content dropped into the Markdown editor is missing a cursor preview or dropped at the wrong location (#11289)

This commit is contained in:
Henry Heino 2024-10-30 14:09:59 -07:00 committed by GitHub
parent 2974465882
commit 612d72d765
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 57 additions and 16 deletions

View File

@ -1,10 +1,10 @@
import { RefObject, useMemo } from 'react'; import { RefObject, useMemo } from 'react';
import { CommandValue } from '../../../utils/types'; import { CommandValue, DropCommandValue } from '../../../utils/types';
import { commandAttachFileToBody } from '../../../utils/resourceHandling'; import { commandAttachFileToBody } from '../../../utils/resourceHandling';
import { _ } from '@joplin/lib/locale'; import { _ } from '@joplin/lib/locale';
import dialogs from '../../../../dialogs'; import dialogs from '../../../../dialogs';
import { EditorCommandType } from '@joplin/editor/types'; import { EditorCommandType, UserEventSource } from '@joplin/editor/types';
import Logger from '@joplin/utils/Logger'; import Logger from '@joplin/utils/Logger';
import CodeMirrorControl from '@joplin/editor/CodeMirror/CodeMirrorControl'; import CodeMirrorControl from '@joplin/editor/CodeMirror/CodeMirrorControl';
import { MarkupLanguage } from '@joplin/renderer'; import { MarkupLanguage } from '@joplin/renderer';
@ -38,13 +38,22 @@ const useEditorCommands = (props: Props) => {
}; };
return { return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied dropItems: async (cmd: DropCommandValue) => {
dropItems: async (cmd: any) => { let pos = cmd.pos && editorRef.current.editor.posAtCoords({ x: cmd.pos.clientX, y: cmd.pos.clientY });
if (cmd.type === 'notes') { if (cmd.type === 'notes') {
editorRef.current.insertText(cmd.markdownTags.join('\n')); const text = cmd.markdownTags.join('\n');
if ((pos ?? null) !== null) {
editorRef.current.select(pos, pos);
}
editorRef.current.insertText(text, UserEventSource.Drop);
} else if (cmd.type === 'files') { } else if (cmd.type === 'files') {
const pos = props.selectionRange.from; pos ??= props.selectionRange.from;
const newBody = await commandAttachFileToBody(props.editorContent, cmd.paths, { createFileURL: !!cmd.createFileURL, position: pos, markupLanguage: props.contentMarkupLanguage }); const newBody = await commandAttachFileToBody(props.editorContent, cmd.paths, {
createFileURL: !!cmd.createFileURL,
position: pos,
markupLanguage: props.contentMarkupLanguage,
});
editorRef.current.updateBody(newBody); editorRef.current.updateBody(newBody);
} else { } else {
logger.warn('CodeMirror: unsupported drop item: ', cmd); logger.warn('CodeMirror: unsupported drop item: ', cmd);

View File

@ -252,3 +252,19 @@ export interface CommandValue {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
value?: any; // For TinyMCE only value?: any; // For TinyMCE only
} }
type DropCommandBase = {
pos: {
clientX: number;
clientY: number;
}|undefined;
};
export type DropCommandValue = ({
type: 'notes';
markdownTags: string[];
}|{
type: 'files';
paths: string[];
createFileURL: boolean;
}) & DropCommandBase;

View File

@ -1,6 +1,7 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import Note from '@joplin/lib/models/Note'; import Note from '@joplin/lib/models/Note';
import { DragEvent as ReactDragEvent } from 'react'; import { DragEvent as ReactDragEvent } from 'react';
import { DropCommandValue } from './types';
interface HookDependencies { interface HookDependencies {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
@ -19,6 +20,11 @@ export default function useDropHandler(dependencies: HookDependencies): DropHand
const dt = event.dataTransfer; const dt = event.dataTransfer;
const createFileURL = event.altKey; const createFileURL = event.altKey;
const eventPosition = {
clientX: event.clientX,
clientY: event.clientY,
};
if (dt.types.indexOf('text/x-jop-note-ids') >= 0) { if (dt.types.indexOf('text/x-jop-note-ids') >= 0) {
const noteIds = JSON.parse(dt.getData('text/x-jop-note-ids')); const noteIds = JSON.parse(dt.getData('text/x-jop-note-ids'));
@ -29,12 +35,15 @@ export default function useDropHandler(dependencies: HookDependencies): DropHand
noteMarkdownTags.push(Note.markdownTag(note)); noteMarkdownTags.push(Note.markdownTag(note));
} }
const props: DropCommandValue = {
type: 'notes',
pos: eventPosition,
markdownTags: noteMarkdownTags,
};
editorRef.current.execCommand({ editorRef.current.execCommand({
name: 'dropItems', name: 'dropItems',
value: { value: props,
type: 'notes',
markdownTags: noteMarkdownTags,
},
}); });
}; };
void dropNotes(); void dropNotes();
@ -51,13 +60,16 @@ export default function useDropHandler(dependencies: HookDependencies): DropHand
paths.push(file.path); paths.push(file.path);
} }
const props: DropCommandValue = {
type: 'files',
pos: eventPosition,
paths: paths,
createFileURL: createFileURL,
};
editorRef.current.execCommand({ editorRef.current.execCommand({
name: 'dropItems', name: 'dropItems',
value: { value: props,
type: 'files',
paths: paths,
createFileURL: createFileURL,
},
}); });
return true; return true;
} }

View File

@ -6,6 +6,7 @@ import { classHighlighter } from '@lezer/highlight';
import { import {
EditorView, drawSelection, highlightSpecialChars, ViewUpdate, Command, rectangularSelection, EditorView, drawSelection, highlightSpecialChars, ViewUpdate, Command, rectangularSelection,
dropCursor,
} from '@codemirror/view'; } from '@codemirror/view';
import { history, undoDepth, redoDepth, standardKeymap } from '@codemirror/commands'; import { history, undoDepth, redoDepth, standardKeymap } from '@codemirror/commands';
@ -253,6 +254,8 @@ const createEditor = (
// Apply styles to entire lines (block-display decorations) // Apply styles to entire lines (block-display decorations)
decoratorExtension, decoratorExtension,
dropCursor(),
biDirectionalTextExtension, biDirectionalTextExtension,
// Adds additional CSS classes to tokens (the default CSS classes are // Adds additional CSS classes to tokens (the default CSS classes are

View File

@ -90,6 +90,7 @@ export interface ContentScriptData {
// Intended to correspond with https://codemirror.net/docs/ref/#state.Transaction%5EuserEvent // Intended to correspond with https://codemirror.net/docs/ref/#state.Transaction%5EuserEvent
export enum UserEventSource { export enum UserEventSource {
Paste = 'input.paste', Paste = 'input.paste',
Drop = 'input.drop',
} }
export interface EditorControl { export interface EditorControl {