/** * check the original code at * https://github.com/facebook/lexical/blob/main/packages/lexical-playground/src/App.tsx */ import type { DOMConversionMap } from "lexical"; import { $isTextNode, TextNode } from "lexical"; const MIN_ALLOWED_FONT_SIZE = 8; const MAX_ALLOWED_FONT_SIZE = 72; const parseAllowedFontSize = (input: string): string => { const match = input.match(/^(\d+(?:\.\d+)?)px$/); if (match) { const n = Number(match[1]); if (n >= MIN_ALLOWED_FONT_SIZE && n <= MAX_ALLOWED_FONT_SIZE) { return input; } } return ""; }; function parseAllowedColor(input: string) { return /^rgb\(\d+, \d+, \d+\)$/.test(input) ? input : ""; } const getExtraStyles = (element: HTMLElement): string => { // Parse styles from pasted input, but only if they match exactly the // sort of styles that would be produced by exportDOM let extraStyles = ""; const fontSize = parseAllowedFontSize(element.style.fontSize); const backgroundColor = parseAllowedColor(element.style.backgroundColor); const color = parseAllowedColor(element.style.color); if (fontSize !== "" && fontSize !== "15px") { extraStyles += `font-size: ${fontSize};`; } if (backgroundColor !== "" && backgroundColor !== "rgb(255, 255, 255)") { extraStyles += `background-color: ${backgroundColor};`; } if (color !== "" && color !== "rgb(0, 0, 0)") { extraStyles += `color: ${color};`; } return extraStyles; }; export const constructImportMap = (): DOMConversionMap => { const importMap: DOMConversionMap = {}; // Wrap all TextNode importers with a function that also imports // the custom styles implemented by the playground for (const [tag, fn] of Object.entries(TextNode.importDOM?.() || {})) { importMap[tag] = (importNode) => { const importer = fn(importNode); if (!importer) { return null; } return { ...importer, conversion: (element) => { const output = importer.conversion(element); if ( output === null || output.forChild === undefined || output.after !== undefined || output.node !== null ) { return output; } const extraStyles = getExtraStyles(element); if (extraStyles) { const { forChild } = output; return { ...output, forChild: (child, parent) => { const textNode = forChild(child, parent); if ($isTextNode(textNode)) { textNode.setStyle(textNode.getStyle() + extraStyles); } return textNode; }, }; } return output; }, }; }; } return importMap; };