1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Desktop: Add 'Paste as text' to the Context menu of the Rich Text Editor (#7769)

This commit is contained in:
pedr 2023-02-15 10:59:32 -03:00 committed by GitHub
parent 7d7b7ed6f3
commit dd86940c6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 63 additions and 29 deletions

View File

@ -221,6 +221,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
packages/app-desktop/gui/NoteEditor/NoteEditor.js packages/app-desktop/gui/NoteEditor/NoteEditor.js

1
.gitignore vendored
View File

@ -209,6 +209,7 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/openEditDialog.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/types.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
packages/app-desktop/gui/NoteEditor/NoteEditor.js packages/app-desktop/gui/NoteEditor/NoteEditor.js

View File

@ -24,6 +24,7 @@ import { MarkupToHtmlOptions } from '../../utils/useMarkupToHtml';
import { themeStyle } from '@joplin/lib/theme'; import { themeStyle } from '@joplin/lib/theme';
import { loadScript } from '../../../utils/loadScript'; import { loadScript } from '../../../utils/loadScript';
import bridge from '../../../../services/bridge'; import bridge from '../../../../services/bridge';
import { TinyMceEditorEvents } from './utils/types';
const { clipboard } = require('electron'); const { clipboard } = require('electron');
const supportedLocales = require('./supportedLocales'); const supportedLocales = require('./supportedLocales');
@ -169,7 +170,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
const nodeName = event.target ? event.target.nodeName : ''; const nodeName = event.target ? event.target.nodeName : '';
if (nodeName === 'INPUT' && event.target.getAttribute('type') === 'checkbox') { if (nodeName === 'INPUT' && event.target.getAttribute('type') === 'checkbox') {
editor.fire('joplinChange'); editor.fire(TinyMceEditorEvents.JoplinChange);
dispatchDidUpdate(editor); dispatchDidUpdate(editor);
} }
@ -261,7 +262,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
}, },
replaceSelection: (value: any) => { replaceSelection: (value: any) => {
editor.selection.setContent(value); editor.selection.setContent(value);
editor.fire('joplinChange'); editor.fire(TinyMceEditorEvents.JoplinChange);
dispatchDidUpdate(editor); dispatchDidUpdate(editor);
// It doesn't make sense but it seems calling setContent // It doesn't make sense but it seems calling setContent
@ -270,7 +271,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
// https://github.com/tinymce/tinymce/issues/3745 // https://github.com/tinymce/tinymce/issues/3745
window.requestAnimationFrame(() => editor.undoManager.add()); window.requestAnimationFrame(() => editor.undoManager.add());
}, },
pasteAsText: () => editor.fire('pasteAsText'), pasteAsText: () => editor.fire(TinyMceEditorEvents.PasteAsText),
}; };
if (additionalCommands[cmd.name]) { if (additionalCommands[cmd.name]) {
@ -674,7 +675,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
editor.on('ObjectResized', function(event: any) { editor.on('ObjectResized', function(event: any) {
if (event.target.nodeName === 'IMG') { if (event.target.nodeName === 'IMG') {
editor.fire('joplinChange'); editor.fire(TinyMceEditorEvents.JoplinChange);
dispatchDidUpdate(editor); dispatchDidUpdate(editor);
} }
}); });
@ -1085,35 +1086,35 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
await onPaste(createSyntheticClipboardEventWithoutHTML()); await onPaste(createSyntheticClipboardEventWithoutHTML());
} }
editor.on('keyup', onKeyUp); editor.on(TinyMceEditorEvents.KeyUp, onKeyUp);
editor.on('keydown', onKeyDown); editor.on(TinyMceEditorEvents.KeyDown, onKeyDown);
editor.on('keypress', onKeypress); editor.on(TinyMceEditorEvents.KeyPress, onKeypress);
editor.on('paste', onPaste); editor.on(TinyMceEditorEvents.Paste, onPaste);
editor.on('pasteAsText', onPasteAsText); editor.on(TinyMceEditorEvents.PasteAsText, onPasteAsText);
editor.on('copy', onCopy); editor.on(TinyMceEditorEvents.Copy, onCopy);
// `compositionend` means that a user has finished entering a Chinese // `compositionend` means that a user has finished entering a Chinese
// (or other languages that require IME) character. // (or other languages that require IME) character.
editor.on('compositionend', onChangeHandler); editor.on(TinyMceEditorEvents.CompositionEnd, onChangeHandler);
editor.on('cut', onCut); editor.on(TinyMceEditorEvents.Cut, onCut);
editor.on('joplinChange', onChangeHandler); editor.on(TinyMceEditorEvents.JoplinChange, onChangeHandler);
editor.on('Undo', onChangeHandler); editor.on(TinyMceEditorEvents.Undo, onChangeHandler);
editor.on('Redo', onChangeHandler); editor.on(TinyMceEditorEvents.Redo, onChangeHandler);
editor.on('ExecCommand', onExecCommand); editor.on(TinyMceEditorEvents.ExecCommand, onExecCommand);
return () => { return () => {
try { try {
editor.off('keyup', onKeyUp); editor.off(TinyMceEditorEvents.KeyUp, onKeyUp);
editor.off('keydown', onKeyDown); editor.off(TinyMceEditorEvents.KeyDown, onKeyDown);
editor.off('keypress', onKeypress); editor.off(TinyMceEditorEvents.KeyPress, onKeypress);
editor.off('paste', onPaste); editor.off(TinyMceEditorEvents.Paste, onPaste);
editor.off('pasteAsText', onPasteAsText); editor.off(TinyMceEditorEvents.PasteAsText, onPasteAsText);
editor.off('copy', onCopy); editor.off(TinyMceEditorEvents.Copy, onCopy);
editor.off('compositionend', onChangeHandler); editor.off(TinyMceEditorEvents.CompositionEnd, onChangeHandler);
editor.off('cut', onCut); editor.off(TinyMceEditorEvents.Cut, onCut);
editor.off('joplinChange', onChangeHandler); editor.off(TinyMceEditorEvents.JoplinChange, onChangeHandler);
editor.off('Undo', onChangeHandler); editor.off(TinyMceEditorEvents.Undo, onChangeHandler);
editor.off('Redo', onChangeHandler); editor.off(TinyMceEditorEvents.Redo, onChangeHandler);
editor.off('ExecCommand', onExecCommand); editor.off(TinyMceEditorEvents.ExecCommand, onExecCommand);
} catch (error) { } catch (error) {
console.warn('Error removing events', error); console.warn('Error removing events', error);
} }

View File

@ -1,5 +1,6 @@
import { _ } from '@joplin/lib/locale'; import { _ } from '@joplin/lib/locale';
import { MarkupToHtml } from '@joplin/renderer'; import { MarkupToHtml } from '@joplin/renderer';
import { TinyMceEditorEvents } from './types';
const taboverride = require('taboverride'); const taboverride = require('taboverride');
interface SourceInfo { interface SourceInfo {
@ -102,7 +103,7 @@ export default function openEditDialog(editor: any, markupToHtml: any, dispatchD
} }
dialogApi.close(); dialogApi.close();
editor.fire('joplinChange'); editor.fire(TinyMceEditorEvents.JoplinChange);
dispatchDidUpdate(editor); dispatchDidUpdate(editor);
}, },
onClose: () => { onClose: () => {

View File

@ -0,0 +1,15 @@
// eslint-disable-next-line import/prefer-default-export
export enum TinyMceEditorEvents {
KeyUp = 'keyup',
KeyDown = 'keydown',
KeyPress = 'keypress',
Paste = 'paste',
PasteAsText = 'pasteAsText',
Copy = 'copy',
CompositionEnd = 'compositionend',
Cut = 'cut',
JoplinChange = 'joplinChange',
Undo = 'Undo',
Redo = 'Redo',
ExecCommand = 'ExecCommand',
}

View File

@ -11,6 +11,7 @@ import convertToScreenCoordinates from '../../../../utils/convertToScreenCoordin
import Setting from '@joplin/lib/models/Setting'; import Setting from '@joplin/lib/models/Setting';
import Resource from '@joplin/lib/models/Resource'; import Resource from '@joplin/lib/models/Resource';
import { TinyMceEditorEvents } from './types';
const menuUtils = new MenuUtils(CommandService.instance()); const menuUtils = new MenuUtils(CommandService.instance());
@ -77,6 +78,9 @@ export default function(editor: any, plugins: PluginStates, dispatch: Function)
editor.insertContent(content); editor.insertContent(content);
}, },
isReadOnly: false, isReadOnly: false,
fireEditorEvent: (event: TinyMceEditorEvents) => {
editor.fire(event);
},
}; };
let template = []; let template = [];

View File

@ -10,6 +10,7 @@ import BaseItem from '@joplin/lib/models/BaseItem';
import BaseModel from '@joplin/lib/BaseModel'; import BaseModel from '@joplin/lib/BaseModel';
import { processPastedHtml } from './resourceHandling'; import { processPastedHtml } from './resourceHandling';
import { NoteEntity, ResourceEntity } from '@joplin/lib/services/database/types'; import { NoteEntity, ResourceEntity } from '@joplin/lib/services/database/types';
import { TinyMceEditorEvents } from '../NoteBody/TinyMCE/utils/types';
const fs = require('fs-extra'); const fs = require('fs-extra');
const { writeFile } = require('fs-extra'); const { writeFile } = require('fs-extra');
const { clipboard } = require('electron'); const { clipboard } = require('electron');
@ -176,6 +177,13 @@ export function menuItems(dispatch: Function): ContextMenuItems {
}, },
isActive: (_itemType: ContextMenuItemType, options: ContextMenuOptions) => !options.isReadOnly && (!!clipboard.readText() || !!clipboard.readHTML()), isActive: (_itemType: ContextMenuItemType, options: ContextMenuOptions) => !options.isReadOnly && (!!clipboard.readText() || !!clipboard.readHTML()),
}, },
pasteAsText: {
label: _('Paste as text'),
onAction: async (options: ContextMenuOptions) => {
options.fireEditorEvent(TinyMceEditorEvents.PasteAsText);
},
isActive: (_itemType: ContextMenuItemType, options: ContextMenuOptions) => !options.isReadOnly && (!!clipboard.readText() || !!clipboard.readHTML()),
},
copyLinkUrl: { copyLinkUrl: {
label: _('Copy Link Address'), label: _('Copy Link Address'),
onAction: async (options: ContextMenuOptions) => { onAction: async (options: ContextMenuOptions) => {

View File

@ -19,6 +19,7 @@ export interface ContextMenuOptions {
htmlToCopy: string; htmlToCopy: string;
insertContent: Function; insertContent: Function;
isReadOnly?: boolean; isReadOnly?: boolean;
fireEditorEvent: Function;
} }
export interface ContextMenuItem { export interface ContextMenuItem {

View File

@ -41,6 +41,7 @@ export default function useMessageHandler(scrollWhenReady: any, setScrollWhenRea
linkToCopy: arg0.linkToCopy || null, linkToCopy: arg0.linkToCopy || null,
htmlToCopy: '', htmlToCopy: '',
insertContent: () => { console.warn('insertContent() not implemented'); }, insertContent: () => { console.warn('insertContent() not implemented'); },
fireEditorEvent: () => { console.warn('fireEditorEvent() not implemented'); },
}, dispatch); }, dispatch);
menu.popup(bridge().window()); menu.popup(bridge().window());

View File

@ -59,6 +59,7 @@ export default function PdfViewer(props: Props) {
linkToCopy: null, linkToCopy: null,
htmlToCopy: '', htmlToCopy: '',
insertContent: () => { console.warn('insertContent() not implemented'); }, insertContent: () => { console.warn('insertContent() not implemented'); },
fireEditorEvent: () => { console.warn('fireEditorEvent() not implemented'); },
} as ContextMenuOptions, props.dispatch); } as ContextMenuOptions, props.dispatch);
menu.popup(bridge().window()); menu.popup(bridge().window());