Skip to content

Instantly share code, notes, and snippets.

@strdr4605
Created June 12, 2024 14:59
Show Gist options
  • Save strdr4605/fada2a3bd0d2a05690a475a23f18c404 to your computer and use it in GitHub Desktop.
Save strdr4605/fada2a3bd0d2a05690a475a23f18c404 to your computer and use it in GitHub Desktop.
react-native-controlled-mentions to lexical

hey @strdr4605 can you share how you converted react-native-controlled-mentions value to lexical state?

@davevilela, we have a fork that also passes mention data to onChange

And we had a function that will transform text and mentions from

export type LocalEditorData = Array<Mendex>;

export type Mendex =
  | {
      type: 'text';
      text: string;
    }
  | {
      type: 'mention';
      text: string;
    };


// to handle @[hello](1) mentions
export const MarkdownMentionRegex = /@\[[^\]]+\]\([\d\w]+\)/g;

export const convertMarkdownToEditorData = (text: string, mentions: ApiMention[]): LocalEditorData => {
  // get all markdown alike mentions
  const matches = [...text.matchAll(MarkdownMentionRegex)];

  if (!matches.length || !mentions.length) return [{ type: 'text', text }];

  const finalArr: LocalEditorData = [{ type: 'text', text: '' }];

  text.split(MarkdownMentionRegex).forEach((line, index) => {
    finalArr.push({ type: 'text', text: line });
    // sync back mentions that were splitted
    // text line tag index is always before the mention tag index
    if (matches[index]) {
      const match = matches[index];

      const foundMention = mentions.find(mention => match[0].includes(getMentionId(mention, type)));

      if (foundMention) {
        const totalLength = finalArr.reduce((total, current) => total + current.text.length, 0);
        finalArr.push({
          type: 'mention',
          text: foundMention.name,
        });
      }
    }
  });

  return finalArr;
};

and after having the LocalEditorData array we would convert to lexical state using a headless editor.

export const transformAllNodes = (editor: LexicalEditor) =>
  mergeRegister(
    editor.registerNodeTransform(ParagraphNode, transformParagraphDirection),
    editor.registerNodeTransform(TextNode, findAndTransformHashtagNode),
    editor.registerNodeTransform(TextNode, findAndTransformAutolinkNode),
    editor.registerNodeTransform(TextNode, node => findAndTransformEmojiNode(node, $createCoreEmojiNode))
  );

export const convertEditorDataToLexical = (editorData: LocalEditorData): LexicalNode[] =>
  editorData.map(element => {
    if (element.type === 'mention') {
      const mention = element.data as Mention;
      const { offset, ...remainingMention } = mention;
      return $createCoreMentionNode(remainingMention);
    }
    if (element.type === 'text' && element.text === '\n') {
      return $createLineBreakNode();
    }
    return $createTextNode(element.text);
  });

const postEditor = createPostHeadlessEditor();

export const convertToLexical = (editorData: LocalEditorData): Promise<LexicalOutputType> =>
  new Promise(resolve => {
    postEditor.update(() => {
      const defaultNodes = convertEditorDataToLexical(editorData);
      const rootEditor = $getRoot().setDirection('ltr');

      rootEditor.clear();

      rootEditor.append($createParagraphNode().append(...defaultNodes));
    });

    const unsubscribeListeners = mergeRegister(
      postEditor.registerNodeTransform(ParagraphNode, transformParagraphDirection),
      postEditor.registerNodeTransform(TextNode, findAndTransformHashtagNode),
      postEditor.registerNodeTransform(TextNode, findAndTransformAutolinkNode)
    );

    const removeUpdateListener = postEditor.registerUpdateListener(({ editorState }) => {
      const serializedEditorState = editorState.toJSON();
      const stringifiedEditorState = JSON.stringify(serializedEditorState);
      const parsedEditorState = postEditor.parseEditorState(stringifiedEditorState);
      const plainText = parsedEditorState.read(() => $getRoot().getTextContent());

      resolve({ editorState: serializedEditorState, plainText });
      removeUpdateListener();
      unsubscribeListeners();
    });
  });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment