1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Desktop: Resolves #9468: Add support for changing text colors in rich text editor (#9578)

This commit is contained in:
Henry Heino 2024-01-04 05:51:26 -08:00 committed by GitHub
parent 4e09b6f2a4
commit 7e8c87e908
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 56 additions and 4 deletions

View File

@ -40,6 +40,10 @@ describe('HtmlToMd', () => {
htmlToMdOptions.preserveNestedTables = true;
}
if (htmlFilename.indexOf('text_color') === 0) {
htmlToMdOptions.preserveColorStyles = true;
}
const html = await readFile(htmlPath, 'utf8');
let expectedMd = await readFile(mdPath, 'utf8');

View File

@ -0,0 +1 @@
<p>Text <span style="color: #fff000;">Text</span></p>

View File

@ -0,0 +1 @@
Text <span style="color: #fff000;">Text</span>

View File

@ -377,6 +377,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
.tox .tox-dialog,
.tox textarea,
.tox input,
.tox .tox-menu,
.tox .tox-dialog__footer {
background-color: ${theme.backgroundColor} !important;
}
@ -385,6 +386,12 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
color: ${theme.color};
}
.tox .tox-menu {
/* Ensures that popover menus (the color swatch menu) has a visible border
even in dark mode. */
border: 1px solid rgba(140, 140, 140, 0.3);
}
/*
When creating dialogs, TinyMCE doesn't seem to offer a way to style the components or to assign classes to them.
We want the code dialog box text area to be monospace, and since we can't target this precisely, we apply the style
@ -406,6 +413,8 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
.tox .tox-tbtn,
.tox .tox-tbtn svg,
.tox .tox-menu button > svg,
.tox .tox-split-button,
.tox .tox-dialog__header,
.tox .tox-button--icon .tox-icon svg,
.tox .tox-button.tox-button--icon .tox-icon svg,
@ -427,7 +436,9 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
}
.tox .tox-tbtn--enabled,
.tox .tox-tbtn--enabled:hover {
.tox .tox-tbtn--enabled:hover,
.tox .tox-menu button:hover,
.tox .tox-split-button {
background-color: ${theme.selectedColor};
}
@ -435,11 +446,13 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
background-color: ${theme.backgroundColor} !important;
}
.tox .tox-tbtn:focus {
.tox .tox-tbtn:focus,
.tox .tox-split-button:focus {
background-color: ${theme.backgroundColor3}
}
.tox .tox-tbtn:hover {
.tox .tox-tbtn:hover,
.tox .tox-menu button:hover > svg {
color: ${theme.colorHover3} !important;
fill: ${theme.colorHover3} !important;
background-color: ${theme.backgroundColorHover3}
@ -470,6 +483,10 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
background-color: ${theme.backgroundColor3} !important;
}
.tox .tox-split-button:hover {
box-shadow: none;
}
.tox-tinymce,
.tox .tox-toolbar__group,
.tox.tox-tinymce-aux .tox-toolbar__overflow,
@ -613,6 +630,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
joplinSub: { inline: 'sub', remove: 'all' },
joplinSup: { inline: 'sup', remove: 'all' },
code: { inline: 'code', remove: 'all', attributes: { spellcheck: false } },
forecolor: { inline: 'span', styles: { color: '%value' } },
},
setup: (editor: Editor) => {
editor.addCommand('joplinAttach', () => {
@ -1036,7 +1054,15 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
//
// Any maybe others, so to catch them all we only check the prefix
const changeCommands = ['mceBlockQuote', 'ToggleJoplinChecklistItem', 'Bold', 'Italic', 'Underline', 'Paragraph'];
const changeCommands = [
'mceBlockQuote',
'ToggleJoplinChecklistItem',
'Bold',
'Italic',
'Underline',
'Paragraph',
'mceApplyTextcolor',
];
if (
changeCommands.includes(c) ||

View File

@ -60,6 +60,9 @@ export default function(editor: any) {
const items: string[] = definitions.filter(d => !!d.grouped).map(d => d.name);
// Additional built-in buttons to show in the formatting sub-menu:
items.push('forecolor');
editor.ui.registry.addGroupToolbarButton('formattingExtras', {
icon: 'image-options',
items: items.join(' '),

View File

@ -12,6 +12,7 @@ export async function htmlToMarkdown(markupLanguage: number, html: string, origi
newBody = htmlToMd.parse(html, {
preserveImageTagsWithSize: true,
preserveNestedTables: true,
preserveColorStyles: true,
});
newBody = await Note.replaceResourceExternalToInternalLinks(newBody, { useAbsolutePaths: true });
} else {

View File

@ -8,6 +8,7 @@ export interface ParseOptions {
anchorNames?: string[];
preserveImageTagsWithSize?: boolean;
preserveNestedTables?: boolean;
preserveColorStyles?: boolean;
baseUrl?: string;
disableEscapeContent?: boolean;
convertEmbeddedPdfsToLinks?: boolean;
@ -22,6 +23,7 @@ export default class HtmlToMd {
codeBlockStyle: 'fenced',
preserveImageTagsWithSize: !!options.preserveImageTagsWithSize,
preserveNestedTables: !!options.preserveNestedTables,
preserveColorStyles: !!options.preserveColorStyles,
bulletListMarker: '-',
emDelimiter: '*',
strongDelimiter: '**',

View File

@ -127,6 +127,19 @@ rules.subscript = {
}
}
// Handles foreground color changes as created by the rich text editor.
// We intentionally don't handle the general style="color: colorhere" case as
// this may leave unwanted formatting when saving websites as markdown.
rules.foregroundColor = {
filter: function (node, options) {
return options.preserveColorStyles && node.nodeName === 'SPAN' && getStyleProp(node, 'color');
},
replacement: function (content, node, options) {
return `<span style="color: ${htmlentities(getStyleProp(node, 'color'))};">${content}</span>`;
},
}
// ==============================
// END Joplin format support
// ==============================

View File

@ -40,6 +40,7 @@ export default function TurndownService (options) {
disableEscapeContent: false,
preformattedCode: false,
preserveNestedTables: false,
preserveColorStyles: false,
blankReplacement: function (content, node) {
return node.isBlock ? '\n\n' : ''
},