mirror of https://github.com/laurent22/joplin.git
Desktop: Upgrade to TinyMCE v6 (#11652)
parent
cbf81d1257
commit
5782ee6ba1
|
@ -8,7 +8,6 @@
|
||||||
import { Node } from '@ephox/dom-globals';
|
import { Node } from '@ephox/dom-globals';
|
||||||
import { Arr, Option } from '@ephox/katamari';
|
import { Arr, Option } from '@ephox/katamari';
|
||||||
import { HTMLElement } from '@ephox/sand';
|
import { HTMLElement } from '@ephox/sand';
|
||||||
import DomQuery from 'tinymce/core/api/dom/DomQuery';
|
|
||||||
import Editor from 'tinymce/core/api/Editor';
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
import Tools from 'tinymce/core/api/util/Tools';
|
import Tools from 'tinymce/core/api/util/Tools';
|
||||||
import * as NodeType from './NodeType';
|
import * as NodeType from './NodeType';
|
||||||
|
@ -49,7 +48,7 @@ const findParentListItemsNodes = function (editor, elms) {
|
||||||
return parentLi ? parentLi : elm;
|
return parentLi ? parentLi : elm;
|
||||||
});
|
});
|
||||||
|
|
||||||
return DomQuery.unique(listItemsElms);
|
return [...new Set(listItemsElms)];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSelectedListItems = function (editor) {
|
const getSelectedListItems = function (editor) {
|
||||||
|
@ -89,7 +88,7 @@ const getSelectedListRoots = (editor: Editor): Node[] => {
|
||||||
|
|
||||||
const getUniqueListRoots = (editor: Editor, lists: Node[]): Node[] => {
|
const getUniqueListRoots = (editor: Editor, lists: Node[]): Node[] => {
|
||||||
const listRoots = Arr.map(lists, (list) => findLastParentListNode(editor, list).getOr(list));
|
const listRoots = Arr.map(lists, (list) => findLastParentListNode(editor, list).getOr(list));
|
||||||
return DomQuery.unique(listRoots);
|
return [...new Set(listRoots)];
|
||||||
};
|
};
|
||||||
|
|
||||||
const isList = (editor: Editor): boolean => {
|
const isList = (editor: Editor): boolean => {
|
||||||
|
|
|
@ -48,8 +48,7 @@ const listState = function (editor: Editor, listName, options:any = {}) {
|
||||||
|
|
||||||
const register = function (editor: Editor) {
|
const register = function (editor: Editor) {
|
||||||
const hasPlugin = function (editor, plugin) {
|
const hasPlugin = function (editor, plugin) {
|
||||||
const plugins = editor.settings.plugins ? editor.settings.plugins : '';
|
return editor.hasPlugin(plugin);
|
||||||
return Tools.inArray(plugins.split(/[ ,]/), plugin) !== -1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const _ = Settings.getLocalizationFunction(editor);
|
const _ = Settings.getLocalizationFunction(editor);
|
||||||
|
|
|
@ -24,7 +24,7 @@ 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';
|
import { TinyMceEditorEvents } from './utils/types';
|
||||||
import type { Editor } from 'tinymce';
|
import type { Editor, EditorEvent } from 'tinymce';
|
||||||
import { joplinCommandToTinyMceCommands, TinyMceCommand } from './utils/joplinCommandToTinyMceCommands';
|
import { joplinCommandToTinyMceCommands, TinyMceCommand } from './utils/joplinCommandToTinyMceCommands';
|
||||||
import shouldPasteResources from './utils/shouldPasteResources';
|
import shouldPasteResources from './utils/shouldPasteResources';
|
||||||
import lightTheme from '@joplin/lib/themes/light';
|
import lightTheme from '@joplin/lib/themes/light';
|
||||||
|
@ -412,9 +412,11 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
element.setAttribute('id', 'tinyMceStyle');
|
element.setAttribute('id', 'tinyMceStyle');
|
||||||
editorContainerDom.head.appendChild(element);
|
editorContainerDom.head.appendChild(element);
|
||||||
element.appendChild(editorContainerDom.createTextNode(`
|
element.appendChild(editorContainerDom.createTextNode(`
|
||||||
.joplin-tinymce .tox-editor-header {
|
.joplin-tinymce .tox-editor-header.tox-editor-header {
|
||||||
padding-left: ${styles.leftExtraToolbarContainer.width + styles.leftExtraToolbarContainer.padding * 2}px;
|
margin-left: ${styles.leftExtraToolbarContainer.width + styles.leftExtraToolbarContainer.padding * 2}px;
|
||||||
padding-right: ${styles.rightExtraToolbarContainer.width + styles.rightExtraToolbarContainer.padding * 2}px;
|
margin-right: ${styles.rightExtraToolbarContainer.width + styles.rightExtraToolbarContainer.padding * 2}px;
|
||||||
|
padding: 0;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tox .tox-toolbar,
|
.tox .tox-toolbar,
|
||||||
|
@ -434,7 +436,8 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tox .tox-dialog__body-content,
|
.tox .tox-dialog__body-content,
|
||||||
.tox .tox-collection__item {
|
.tox .tox-collection__item,
|
||||||
|
.tox .tox-insert-table-picker__label {
|
||||||
color: ${theme.color};
|
color: ${theme.color};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,7 +476,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.tox .tox-dialog textarea {
|
.tox .tox-dialog textarea {
|
||||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
font-family: Menlo, Monaco, Consolas, "Courier New", monospace !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tox .tox-dialog-wrap__backdrop {
|
.tox .tox-dialog-wrap__backdrop {
|
||||||
|
@ -498,6 +501,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
.tox .tox-toolbar-label {
|
.tox .tox-toolbar-label {
|
||||||
color: ${theme.color3} !important;
|
color: ${theme.color3} !important;
|
||||||
fill: ${theme.color3} !important;
|
fill: ${theme.color3} !important;
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tox .tox-statusbar a,
|
.tox .tox-statusbar a,
|
||||||
|
@ -524,6 +528,11 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
.tox .tox-split-button:focus {
|
.tox .tox-split-button:focus {
|
||||||
background-color: ${theme.backgroundColor3}
|
background-color: ${theme.backgroundColor3}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tox .tox-tbtn:focus-visible,
|
||||||
|
.tox .tox-split-button:focus-visible {
|
||||||
|
background-color: ${theme.backgroundColorHover3}
|
||||||
|
}
|
||||||
|
|
||||||
.tox .tox-tbtn:hover,
|
.tox .tox-tbtn:hover,
|
||||||
.tox .tox-menu button:hover > svg {
|
.tox .tox-menu button:hover > svg {
|
||||||
|
@ -560,6 +569,12 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
.tox .tox-split-button:hover {
|
.tox .tox-split-button:hover {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Decrease the spacing between groups */
|
||||||
|
.tox .tox-toolbar__group {
|
||||||
|
padding-left: 7px;
|
||||||
|
padding-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
.tox-tinymce,
|
.tox-tinymce,
|
||||||
.tox .tox-toolbar__group,
|
.tox .tox-toolbar__group,
|
||||||
|
@ -628,7 +643,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!editor) return;
|
if (!editor) return;
|
||||||
editor.setMode(props.disabled ? 'readonly' : 'design');
|
editor.mode.set(props.disabled ? 'readonly' : 'design');
|
||||||
}, [editor, props.disabled]);
|
}, [editor, props.disabled]);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------
|
||||||
|
@ -675,14 +690,19 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
const containerWindow = editorContainerDom.defaultView as any;
|
const containerWindow = editorContainerDom.defaultView as any;
|
||||||
const editors = await containerWindow.tinymce.init({
|
const editors = await containerWindow.tinymce.init({
|
||||||
selector: `#${editorContainer.id}`,
|
selector: `#${editorContainer.id}`,
|
||||||
|
|
||||||
|
// Ensures that the "Premium plugins" toolbar option is disabled. See
|
||||||
|
// https://www.tiny.cloud/docs/tinymce/latest/editor-premium-upgrade-promotion/
|
||||||
|
promotion: false,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
body_class: 'jop-tinymce',
|
body_class: 'jop-tinymce',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
resize: false,
|
resize: false,
|
||||||
|
highlight_on_focus: false,
|
||||||
icons: 'Joplin',
|
icons: 'Joplin',
|
||||||
icons_url: 'gui/NoteEditor/NoteBody/TinyMCE/icons.js',
|
icons_url: 'gui/NoteEditor/NoteBody/TinyMCE/icons.js',
|
||||||
plugins: 'noneditable link joplinLists hr searchreplace codesample table',
|
plugins: 'link joplinLists searchreplace codesample table',
|
||||||
noneditable_noneditable_class: 'joplin-editable', // Can be a regex too
|
noneditable_class: 'joplin-editable', // Can be a regex too
|
||||||
iframe_aria_text: _('Rich Text editor. Press Escape then Tab to escape focus.'),
|
iframe_aria_text: _('Rich Text editor. Press Escape then Tab to escape focus.'),
|
||||||
|
|
||||||
// #p: Pad empty paragraphs with to prevent them from being removed.
|
// #p: Pad empty paragraphs with to prevent them from being removed.
|
||||||
|
@ -694,7 +714,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
relative_urls: false,
|
relative_urls: false,
|
||||||
branding: false,
|
branding: false,
|
||||||
statusbar: false,
|
statusbar: false,
|
||||||
target_list: false,
|
link_target_list: false,
|
||||||
// Handle the first table row as table header.
|
// Handle the first table row as table header.
|
||||||
// https://www.tiny.cloud/docs/plugins/table/#table_header_type
|
// https://www.tiny.cloud/docs/plugins/table/#table_header_type
|
||||||
table_header_type: 'sectionCells',
|
table_header_type: 'sectionCells',
|
||||||
|
@ -704,13 +724,6 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
contextmenu: false,
|
contextmenu: false,
|
||||||
browser_spellcheck: true,
|
browser_spellcheck: true,
|
||||||
|
|
||||||
// Work around an issue where images with a base64 SVG data URL would be broken.
|
|
||||||
//
|
|
||||||
// See https://github.com/tinymce/tinymce/issues/3864
|
|
||||||
//
|
|
||||||
// This was fixed in TinyMCE 6.1, so remove it when we upgrade.
|
|
||||||
images_dataimg_filter: (img: HTMLImageElement) => !img.src.startsWith('data:'),
|
|
||||||
|
|
||||||
formats: {
|
formats: {
|
||||||
joplinHighlight: { inline: 'mark', remove: 'all' },
|
joplinHighlight: { inline: 'mark', remove: 'all' },
|
||||||
joplinStrikethrough: { inline: 's', remove: 'all' },
|
joplinStrikethrough: { inline: 's', remove: 'all' },
|
||||||
|
@ -747,14 +760,15 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
tooltip: _('Inline Code'),
|
tooltip: _('Inline Code'),
|
||||||
icon: 'sourcecode',
|
icon: 'sourcecode',
|
||||||
onAction: function() {
|
onAction: function() {
|
||||||
editor.execCommand('mceToggleFormat', false, 'code', { class: 'inline-code' });
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
|
editor.execCommand('mceToggleFormat', false, 'code', { class: 'inline-code' } as any);
|
||||||
},
|
},
|
||||||
onSetup: function(api) {
|
onSetup: function(api) {
|
||||||
api.setActive(editor.formatter.match('code'));
|
api.setActive(editor.formatter.match('code'));
|
||||||
const unbind = editor.formatter.formatChanged('code', api.setActive).unbind;
|
const handle = editor.formatter.formatChanged('code', active => api.setActive(active));
|
||||||
|
|
||||||
return function() {
|
return function() {
|
||||||
if (unbind) unbind();
|
handle?.unbind();
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1206,9 +1220,10 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
const onSetAttrib = (event: any) => {
|
const onSetAttrib = (event: EditorEvent<any>) => {
|
||||||
// Dispatch onChange when a link is edited
|
// Dispatch onChange when a link is edited
|
||||||
if (event.attrElm[0].nodeName === 'A') {
|
const target = Array.isArray(event.attrElm) ? event.attrElm[0] : event.attrElm;
|
||||||
|
if (target.nodeName === 'A') {
|
||||||
if (event.attrName === 'title' || event.attrName === 'href' || event.attrName === 'rel') {
|
if (event.attrName === 'title' || event.attrName === 'href' || event.attrName === 'rel') {
|
||||||
onChangeHandler();
|
onChangeHandler();
|
||||||
}
|
}
|
||||||
|
|
|
@ -886,7 +886,7 @@
|
||||||
var parentLi = editor.dom.getParent(elm, 'li,dd,dt', getClosestListRootElm(editor, elm));
|
var parentLi = editor.dom.getParent(elm, 'li,dd,dt', getClosestListRootElm(editor, elm));
|
||||||
return parentLi ? parentLi : elm;
|
return parentLi ? parentLi : elm;
|
||||||
});
|
});
|
||||||
return DomQuery.unique(listItemsElms);
|
return [...new Set(listItemsElms)];
|
||||||
};
|
};
|
||||||
var getSelectedListItems = function (editor) {
|
var getSelectedListItems = function (editor) {
|
||||||
var selectedBlocks = editor.selection.getSelectedBlocks();
|
var selectedBlocks = editor.selection.getSelectedBlocks();
|
||||||
|
@ -919,7 +919,7 @@
|
||||||
var listRoots = map(lists, function (list) {
|
var listRoots = map(lists, function (list) {
|
||||||
return findLastParentListNode(editor, list).getOr(list);
|
return findLastParentListNode(editor, list).getOr(list);
|
||||||
});
|
});
|
||||||
return DomQuery.unique(listRoots);
|
return [...new Set(listRoots)];
|
||||||
};
|
};
|
||||||
|
|
||||||
var shouldIndentOnTab = function (editor) {
|
var shouldIndentOnTab = function (editor) {
|
||||||
|
@ -2119,8 +2119,7 @@
|
||||||
};
|
};
|
||||||
var register$1 = function (editor) {
|
var register$1 = function (editor) {
|
||||||
var hasPlugin = function (editor, plugin) {
|
var hasPlugin = function (editor, plugin) {
|
||||||
var plugins = editor.settings.plugins ? editor.settings.plugins : '';
|
return editor.hasPlugin(plugin);
|
||||||
return Tools.inArray(plugins.split(/[ ,]/), plugin) !== -1;
|
|
||||||
};
|
};
|
||||||
var _ = getLocalizationFunction(editor);
|
var _ = getLocalizationFunction(editor);
|
||||||
var exec = function (command) {
|
var exec = function (command) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { Page } from '@playwright/test';
|
||||||
const createScanner = (page: Page) => {
|
const createScanner = (page: Page) => {
|
||||||
return new AxeBuilder({ page })
|
return new AxeBuilder({ page })
|
||||||
.disableRules(['page-has-heading-one'])
|
.disableRules(['page-has-heading-one'])
|
||||||
|
// Needed because we're using Electron. See https://github.com/dequelabs/axe-core-npm/issues/1141
|
||||||
.setLegacyMode(true);
|
.setLegacyMode(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,6 +64,12 @@ test.describe('wcag', () => {
|
||||||
await mainScreen.noteEditor.noteTitleInput.hover();
|
await mainScreen.noteEditor.noteTitleInput.hover();
|
||||||
|
|
||||||
await expectNoViolations(mainWindow);
|
await expectNoViolations(mainWindow);
|
||||||
|
|
||||||
|
// Should not find issues with the Rich Text Editor
|
||||||
|
await mainScreen.noteEditor.toggleEditorsButton.click();
|
||||||
|
await mainScreen.noteEditor.richTextEditor.click();
|
||||||
|
|
||||||
|
await expectNoViolations(mainWindow);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -208,6 +208,6 @@
|
||||||
"styled-system": "5.1.5",
|
"styled-system": "5.1.5",
|
||||||
"taboverride": "4.0.3",
|
"taboverride": "4.0.3",
|
||||||
"tesseract.js": "5.1.0",
|
"tesseract.js": "5.1.0",
|
||||||
"tinymce": "5.10.6"
|
"tinymce": "6.8.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,4 +158,4 @@ ldaps
|
||||||
Bluesky
|
Bluesky
|
||||||
Tebi
|
Tebi
|
||||||
unwatcher
|
unwatcher
|
||||||
pedr
|
pedr
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -8340,7 +8340,7 @@ __metadata:
|
||||||
styled-system: 5.1.5
|
styled-system: 5.1.5
|
||||||
taboverride: 4.0.3
|
taboverride: 4.0.3
|
||||||
tesseract.js: 5.1.0
|
tesseract.js: 5.1.0
|
||||||
tinymce: 5.10.6
|
tinymce: 6.8.5
|
||||||
ts-jest: 29.1.5
|
ts-jest: 29.1.5
|
||||||
ts-node: 10.9.2
|
ts-node: 10.9.2
|
||||||
typescript: 5.4.5
|
typescript: 5.4.5
|
||||||
|
@ -45807,10 +45807,10 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"tinymce@npm:5.10.6":
|
"tinymce@npm:6.8.5":
|
||||||
version: 5.10.6
|
version: 6.8.5
|
||||||
resolution: "tinymce@npm:5.10.6"
|
resolution: "tinymce@npm:6.8.5"
|
||||||
checksum: 806cd733fde872f97d84b22ed267d2ee0404aa003d89b6c295b91867ac8b2bafc99542bf04924f253cf093c920a9da4b37a7ef73a00ff57b2b75490fb9705caf
|
checksum: 7f7ad8dd2b117b8a671f97e41fc094935cfe4d4b525c90e97c6fdb480b19514e334f4360d89f34c04915a928e0cf5264fc1a60559554770452d6fce17b884940
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue