Lexical: Use custom transformers

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2023-04-18 23:31:19 +02:00
parent 27975e3ca5
commit 7dda1155d4
3 changed files with 97 additions and 9 deletions

View File

@ -6,7 +6,7 @@ Copyright (c) Meta Platforms, Inc. and affiliates.
This source code is licensed under the MIT license found in the
LICENSE file in the /app/soapbox/features/compose/editor directory.
*/
import { $convertFromMarkdownString, $convertToMarkdownString, TRANSFORMERS } from '@lexical/markdown';
import { $convertFromMarkdownString, $convertToMarkdownString } from '@lexical/markdown';
import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';
import { LexicalComposer, InitialConfigType } from '@lexical/react/LexicalComposer';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
@ -32,6 +32,7 @@ import DraggableBlockPlugin from './plugins/draggable-block-plugin';
import FloatingLinkEditorPlugin from './plugins/floating-link-editor-plugin';
import FloatingTextFormatToolbarPlugin from './plugins/floating-text-format-toolbar-plugin';
import { MentionPlugin } from './plugins/mention-plugin';
import { TO_WYSIWYG_TRANSFORMERS } from './transformers';
const StatePlugin = ({ composeId }: { composeId: string }) => {
const dispatch = useAppDispatch();
@ -108,7 +109,7 @@ const ComposeEditor = React.forwardRef<string, IComposeEditor>(({
return function() {
if (compose.content_type === 'text/markdown') {
$convertFromMarkdownString(compose.text, TRANSFORMERS);
$convertFromMarkdownString(compose.text, TO_WYSIWYG_TRANSFORMERS);
} else {
const paragraph = $createParagraphNode();
const textNode = $createTextNode(compose.text);
@ -175,7 +176,7 @@ const ComposeEditor = React.forwardRef<string, IComposeEditor>(({
{autoFocus && <AutoFocusPlugin />}
<OnChangePlugin onChange={(_, editor) => {
editor.update(() => {
if (editorStateRef) (editorStateRef as any).current = $convertToMarkdownString(TRANSFORMERS);
if (editorStateRef) (editorStateRef as any).current = $convertToMarkdownString(TO_WYSIWYG_TRANSFORMERS);
});
}}
/>

View File

@ -0,0 +1,92 @@
// Adapted from: https://github.com/facebook/lexical/issues/2715#issuecomment-1209090485
import {
BOLD_ITALIC_UNDERSCORE,
BOLD_ITALIC_STAR,
BOLD_STAR,
BOLD_UNDERSCORE,
STRIKETHROUGH,
INLINE_CODE,
HEADING,
QUOTE,
ORDERED_LIST,
UNORDERED_LIST,
LINK,
TextMatchTransformer,
} from '@lexical/markdown';
const replaceEscapedChars = (text: string): string => {
// convert "\*" to "*", "\_" to "_", "\~" to "~", ...
return text
.replaceAll('\\*', '*')
.replaceAll('\\_', '_')
.replaceAll('\\-', '-')
.replaceAll('\\#', '#')
.replaceAll('\\>', '>')
.replaceAll('\\+', '+')
.replaceAll('\\~', '~');
};
const replaceUnescapedChars = (text: string, regexes: RegExp[]): string => {
// convert "*" to "", "_" to "", "~" to "" (all chars, which are not escaped - means with "\" in front)
for (const regex of regexes) {
text = text.replaceAll(regex, '');
}
return text;
};
const UNESCAPE_ITALIC_UNDERSCORE_IMPORT_REGEX =
/([\_])(?<!(?:\1|\w).)(?![_*\s])(.*?[^_*\s])(?=\1)([\_])(?!\w|\3)/;
const UNESCAPE_ITALIC_UNDERSCORE_REGEX =
/([\_])(?<!(?:\1|\w).)(?![_*\s])(.*?[^_*\s])(?=\1)([\_])(?!\w|\3)/;
export const UNESCAPE_ITALIC_UNDERSCORE: TextMatchTransformer = {
dependencies: [],
export: () => null,
importRegExp: UNESCAPE_ITALIC_UNDERSCORE_IMPORT_REGEX,
regExp: UNESCAPE_ITALIC_UNDERSCORE_REGEX,
replace: (textNode, _) => {
const notEscapedUnderscoreRegex = /(?<![\\]{1})[\_]{1}/g;
const textContent = replaceUnescapedChars(textNode.getTextContent(), [
notEscapedUnderscoreRegex,
]);
textNode.setTextContent(replaceEscapedChars(textContent));
textNode.setFormat('italic');
},
trigger: '_',
type: 'text-match',
};
const UNESCAPE_BACKSLASH_IMPORT_REGEX = /(\\(?:\\\\)?).*?\1*[\~\*\_\{\}\[\]\(\)\#\+\-\.\!]/;
const UNESCAPE_BACKSLASH_REGEX = /(\\(?:\\\\)?).*?\1*[\~\*\_\{\}\[\]\(\)\#\+\-\.\!]$/;
export const UNESCAPE_BACKSLASH: TextMatchTransformer = {
dependencies: [],
export: () => null,
importRegExp: UNESCAPE_BACKSLASH_IMPORT_REGEX,
regExp: UNESCAPE_BACKSLASH_REGEX,
replace: (textNode, _) => {
if (textNode) {
textNode.setTextContent(replaceEscapedChars(textNode.getTextContent()));
}
},
trigger: '\\',
type: 'text-match',
};
export const TO_WYSIWYG_TRANSFORMERS = [
UNESCAPE_BACKSLASH,
BOLD_ITALIC_UNDERSCORE,
BOLD_ITALIC_STAR,
BOLD_STAR,
BOLD_UNDERSCORE,
STRIKETHROUGH,
UNESCAPE_ITALIC_UNDERSCORE,
// UNESCAPE_ITALIC_STAR,
INLINE_CODE,
HEADING,
QUOTE,
ORDERED_LIST,
UNORDERED_LIST,
LINK,
];

View File

@ -1,11 +1,6 @@
import data, { EmojiData } from './data';
const stripLeadingZeros = /^0+/;
function replaceAll(str: string, find: string, replace: string) {
return str.replace(new RegExp(find, 'g'), replace);
}
interface UnicodeMap {
[s: string]: {
unified: string
@ -80,7 +75,7 @@ const stripcodes = (unified: string, native: string) => {
if (unified.includes('200d') && !(unified in blacklist)) {
return stripped;
} else {
return replaceAll(stripped, '-fe0f', '');
return stripped.replaceAll('-fe0f', '');
}
};