diff --git a/packages/basic-modules/src/modules/blockquote/index.ts b/packages/basic-modules/src/modules/blockquote/index.ts index 54c9634cd..e01fd986e 100644 --- a/packages/basic-modules/src/modules/blockquote/index.ts +++ b/packages/basic-modules/src/modules/blockquote/index.ts @@ -6,12 +6,14 @@ import { IModuleConf } from '@wangeditor/core' import { renderBlockQuoteConf } from './render-elem' import { quoteToHtmlConf } from './elem-to-html' +import { parseHtmlConf } from './parse-elem-html' import { blockquoteMenuConf } from './menu/index' import withBlockquote from './plugin' const blockquote: Partial = { renderElems: [renderBlockQuoteConf], elemsToHtml: [quoteToHtmlConf], + parseElemsHtml: [parseHtmlConf], menus: [blockquoteMenuConf], editorPlugin: withBlockquote, } diff --git a/packages/basic-modules/src/modules/blockquote/parse-elem-html.ts b/packages/basic-modules/src/modules/blockquote/parse-elem-html.ts new file mode 100644 index 000000000..1c68f882d --- /dev/null +++ b/packages/basic-modules/src/modules/blockquote/parse-elem-html.ts @@ -0,0 +1,31 @@ +/** + * @description parse html + * @author wangfupeng + */ + +import { Descendant } from 'slate' +import { Dom7Array } from 'dom7' +import { IDomEditor } from '@wangeditor/core' +import { BlockQuoteElement } from './custom-types' + +function parseHtml( + $elem: Dom7Array, + children: Descendant[], + editor: IDomEditor +): BlockQuoteElement { + // 无 children ,则用纯文本 + if (children.length === 0) { + children = [{ text: $elem.text().replace(/\s+/gm, ' ') }] + } + + return { + type: 'blockquote', + // @ts-ignore + children, + } +} + +export const parseHtmlConf = { + selector: 'blockquote', + parseElemHtml: parseHtml, +} diff --git a/packages/basic-modules/src/modules/code-block/index.ts b/packages/basic-modules/src/modules/code-block/index.ts index 115c669f6..d9745aefe 100644 --- a/packages/basic-modules/src/modules/code-block/index.ts +++ b/packages/basic-modules/src/modules/code-block/index.ts @@ -7,6 +7,8 @@ import { IModuleConf } from '@wangeditor/core' import { codeBlockMenuConf } from './menu/index' import withCodeBlock from './plugin' import { renderPreConf, renderCodeConf } from './render-elem' +import { preParseHtmlConf } from './pre-parse-html' +import { parseCodeHtmlConf, parsePreHtmlConf } from './parse-elem-html' import { codeToHtmlConf, preToHtmlConf } from './elem-to-html' const codeBlockModule: Partial = { @@ -14,6 +16,8 @@ const codeBlockModule: Partial = { editorPlugin: withCodeBlock, renderElems: [renderPreConf, renderCodeConf], elemsToHtml: [codeToHtmlConf, preToHtmlConf], + preParseHtml: [preParseHtmlConf], + parseElemsHtml: [parseCodeHtmlConf, parsePreHtmlConf], } export default codeBlockModule diff --git a/packages/basic-modules/src/modules/code-block/parse-elem-html.ts b/packages/basic-modules/src/modules/code-block/parse-elem-html.ts new file mode 100644 index 000000000..a394fd96d --- /dev/null +++ b/packages/basic-modules/src/modules/code-block/parse-elem-html.ts @@ -0,0 +1,35 @@ +/** + * @description parse html + * @author wangfupeng + */ + +import { Descendant } from 'slate' +import { Dom7Array } from 'dom7' +import { IDomEditor, DomEditor } from '@wangeditor/core' +import { PreElement, CodeElement } from './custom-types' + +function parseCodeHtml($elem: Dom7Array, children: Descendant[], editor: IDomEditor): CodeElement { + return { + type: 'code', + language: '', // language 在 code-highlight 中实现 + children: [{ text: $elem[0].textContent || '' }], + } +} + +export const parseCodeHtmlConf = { + selector: 'code', + parseElemHtml: parseCodeHtml, +} + +function parsePreHtml($elem: Dom7Array, children: Descendant[], editor: IDomEditor): PreElement { + return { + type: 'pre', + // @ts-ignore + children: children.filter(child => DomEditor.getNodeType(child) === 'code'), + } +} + +export const parsePreHtmlConf = { + selector: 'pre', + parseElemHtml: parsePreHtml, +} diff --git a/packages/basic-modules/src/modules/code-block/pre-parse-html.ts b/packages/basic-modules/src/modules/code-block/pre-parse-html.ts new file mode 100644 index 000000000..48d091ffe --- /dev/null +++ b/packages/basic-modules/src/modules/code-block/pre-parse-html.ts @@ -0,0 +1,30 @@ +/** + * @description pre parse html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { getTagName } from '../../utils/dom' + +/** + * pre-prase ,去掉其中的 (兼容 V4) + * @param $code $code + */ +function preParse($code: Dom7Array) { + const tagName = getTagName($code) + if (tagName !== 'code') return $code + + const $xmp = $code.find('xmp') + if ($xmp.length === 0) return $code // 不是 V4 格式 + + const codeText = $xmp.text() + $xmp.remove() + $code.text(codeText) + + return $code +} + +export const preParseHtmlConf = { + selector: 'pre>code', // 匹配 <pre> 下的 <code> + preParseHtml: preParse, +} diff --git a/packages/basic-modules/src/modules/color/index.ts b/packages/basic-modules/src/modules/color/index.ts index d122b4e66..7b39a8854 100644 --- a/packages/basic-modules/src/modules/color/index.ts +++ b/packages/basic-modules/src/modules/color/index.ts @@ -6,12 +6,16 @@ import { IModuleConf } from '@wangeditor/core' import { renderStyle } from './render-style' import { styleToHtml } from './style-to-html' +import { preParseHtmlConf } from './pre-parse-html' +import { parseStyleHtml } from './parse-style-html' import { colorMenuConf, bgColorMenuConf } from './menu/index' import withColor from './plugin' const color: Partial<IModuleConf> = { renderStyle, styleToHtml, + preParseHtml: [preParseHtmlConf], + parseStyleHtml, menus: [colorMenuConf, bgColorMenuConf], editorPlugin: withColor, } diff --git a/packages/basic-modules/src/modules/color/menu/config.ts b/packages/basic-modules/src/modules/color/menu/config.ts index 29f10dc12..e350e1c83 100644 --- a/packages/basic-modules/src/modules/color/menu/config.ts +++ b/packages/basic-modules/src/modules/color/menu/config.ts @@ -4,36 +4,36 @@ */ const COLORS = [ - '#000000', - '#262626', - '#595959', - '#8c8c8c', - '#bfbfbf', - '#d9d9d9', - '#e9e9e9e', - '#f5f5f5', - '#fafafa', - '#ffffff', // 10 - '#e13c39', - '#e75f33', - '#eb903a', - '#f5db4d', - '#72c040', - '#59bfc0', - '#4290f7', - '#3658e2', - '#6a39c9', - '#d84493', // 10 - '#fbe9e6', - '#fcede1', - '#fcefd4', - '#fcfbcf', - '#e7f6d5', - '#daf4f0', - '#d9edfa', - '#e0e8fa', - '#ede1f8', - '#f6e2ea', // 10 + 'rgb(0, 0, 0)', + 'rgb(38, 38, 38)', + 'rgb(89, 89, 89)', + 'rgb(140, 140, 140)', + 'rgb(191, 191, 191)', + 'rgb(217, 217, 217)', + 'rgb(233, 233, 233)', + 'rgb(245, 245, 245)', + 'rgb(250, 250, 250)', + 'rgb(255, 255, 255)', // 10 + 'rgb(225, 60, 57)', + 'rgb(231, 95, 51)', + 'rgb(235, 144, 58)', + 'rgb(245, 219, 77)', + 'rgb(114, 192, 64)', + 'rgb(89, 191, 192)', + 'rgb(66, 144, 247)', + 'rgb(54, 88, 226)', + 'rgb(106, 57, 201)', + 'rgb(216, 68, 147)', // 10 + 'rgb(251, 233, 230)', + 'rgb(252, 237, 225)', + 'rgb(252, 239, 212)', + 'rgb(252, 251, 207)', + 'rgb(231, 246, 213)', + 'rgb(218, 244, 240)', + 'rgb(217, 237, 250)', + 'rgb(224, 232, 250)', + 'rgb(237, 225, 248)', + 'rgb(246, 226, 234)', // 10 'rgb(255, 163, 158)', 'rgb(255, 187, 150)', 'rgb(255, 213, 145)', diff --git a/packages/basic-modules/src/modules/color/parse-style-html.ts b/packages/basic-modules/src/modules/color/parse-style-html.ts new file mode 100644 index 000000000..46b45b106 --- /dev/null +++ b/packages/basic-modules/src/modules/color/parse-style-html.ts @@ -0,0 +1,27 @@ +/** + * @description parse style html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { Descendant, Text } from 'slate' +import { ColorText } from './custom-types' +import { getStyleValue } from '../../utils/dom' + +export function parseStyleHtml($text: Dom7Array, node: Descendant): Descendant { + if (!Text.isText(node)) return node + + const textNode = node as ColorText + + const color = getStyleValue($text, 'color') + if (color) { + textNode.color = color + } + + const bgColor = getStyleValue($text, 'background-color') + if (bgColor) { + textNode.bgColor = bgColor + } + + return textNode +} diff --git a/packages/basic-modules/src/modules/color/pre-parse-html.ts b/packages/basic-modules/src/modules/color/pre-parse-html.ts new file mode 100644 index 000000000..33101d529 --- /dev/null +++ b/packages/basic-modules/src/modules/color/pre-parse-html.ts @@ -0,0 +1,30 @@ +/** + * @description pre-parse html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { getTagName } from '../../utils/dom' + +/** + * pre-prase font ,兼容 V4 + * @param $font $font + */ +function preParse($font: Dom7Array) { + const tagName = getTagName($font) + if (tagName !== 'font') return $font + + // 处理 color (V4 使用 <font color="#ccc">xx</font> 格式) + const color = $font.attr('color') || '' + if (color) { + $font.removeAttr('color') + $font.css('color', color) + } + + return $font +} + +export const preParseHtmlConf = { + selector: 'font', + preParseHtml: preParse, +} diff --git a/packages/basic-modules/src/modules/divider/index.ts b/packages/basic-modules/src/modules/divider/index.ts index 43d6cdd80..fa7fce99b 100644 --- a/packages/basic-modules/src/modules/divider/index.ts +++ b/packages/basic-modules/src/modules/divider/index.ts @@ -7,11 +7,13 @@ import { IModuleConf } from '@wangeditor/core' import withDivider from './plugin' import { renderDividerConf } from './render-elem' import { dividerToHtmlConf } from './elem-to-html' +import { parseHtmlConf } from './parse-elem-html' import { insertDividerMenuConf, deleteDividerMenuConf } from './menu/index' const image: Partial<IModuleConf> = { renderElems: [renderDividerConf], elemsToHtml: [dividerToHtmlConf], + parseElemsHtml: [parseHtmlConf], menus: [insertDividerMenuConf, deleteDividerMenuConf], editorPlugin: withDivider, } diff --git a/packages/basic-modules/src/modules/divider/parse-elem-html.ts b/packages/basic-modules/src/modules/divider/parse-elem-html.ts new file mode 100644 index 000000000..50fda7e1f --- /dev/null +++ b/packages/basic-modules/src/modules/divider/parse-elem-html.ts @@ -0,0 +1,21 @@ +/** + * @description parse html + * @author wangfupeng + */ + +import { Descendant } from 'slate' +import { Dom7Array } from 'dom7' +import { IDomEditor } from '@wangeditor/core' +import { DividerElement } from './custom-types' + +function parseHtml($elem: Dom7Array, children: Descendant[], editor: IDomEditor): DividerElement { + return { + type: 'divider', + children: [{ text: '' }], // void node 有一个空白 text + } +} + +export const parseHtmlConf = { + selector: 'hr', + parseElemHtml: parseHtml, +} diff --git a/packages/basic-modules/src/modules/font-size-family/index.ts b/packages/basic-modules/src/modules/font-size-family/index.ts index ee44ebae0..03307c584 100644 --- a/packages/basic-modules/src/modules/font-size-family/index.ts +++ b/packages/basic-modules/src/modules/font-size-family/index.ts @@ -6,11 +6,15 @@ import { IModuleConf } from '@wangeditor/core' import { renderStyle } from './render-style' import { styleToHtml } from './style-to-html' +import { preParseHtmlConf } from './pre-parse-html' +import { parseStyleHtml } from './parse-style-html' import { fontSizeMenuConf, fontFamilyMenuConf } from './menu/index' const fontSizeAndFamily: Partial<IModuleConf> = { renderStyle, styleToHtml, + preParseHtml: [preParseHtmlConf], + parseStyleHtml, menus: [fontSizeMenuConf, fontFamilyMenuConf], } diff --git a/packages/basic-modules/src/modules/font-size-family/parse-style-html.ts b/packages/basic-modules/src/modules/font-size-family/parse-style-html.ts new file mode 100644 index 000000000..26e6cb55f --- /dev/null +++ b/packages/basic-modules/src/modules/font-size-family/parse-style-html.ts @@ -0,0 +1,27 @@ +/** + * @description parse style html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { Descendant, Text } from 'slate' +import { FontSizeAndFamilyText } from './custom-types' +import { getStyleValue } from '../../utils/dom' + +export function parseStyleHtml($text: Dom7Array, node: Descendant): Descendant { + if (!Text.isText(node)) return node + + const textNode = node as FontSizeAndFamilyText + + const fontSize = getStyleValue($text, 'font-size') + if (fontSize) { + textNode.fontSize = fontSize + } + + const fontFamily = getStyleValue($text, 'font-family') + if (fontFamily) { + textNode.fontFamily = fontFamily + } + + return textNode +} diff --git a/packages/basic-modules/src/modules/font-size-family/pre-parse-html.ts b/packages/basic-modules/src/modules/font-size-family/pre-parse-html.ts new file mode 100644 index 000000000..711007b39 --- /dev/null +++ b/packages/basic-modules/src/modules/font-size-family/pre-parse-html.ts @@ -0,0 +1,48 @@ +/** + * @description pre-parse html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { getTagName } from '../../utils/dom' + +// V4 font-size 对应关系(V4 使用 <font size="1">xxx</font> 格式) +const FONT_SIZE_MAP_FOR_V4 = { + '1': '12px', + '2': '14px', + '3': '16px', + '4': '19px', + '5': '24px', + '6': '32px', + '7': '48px', +} + +/** + * pre-prase font ,兼容 V4 + * @param $font $font + */ +function preParse($font: Dom7Array) { + const tagName = getTagName($font) + if (tagName !== 'font') return $font + + // 处理 size (V4 使用 <font size="1">xxx</font> 格式) + const size = $font.attr('size') || '' + if (size) { + $font.removeAttr('size') + $font.css('font-size', FONT_SIZE_MAP_FOR_V4[size]) + } + + // 处理 face (V4 使用 <font face="黑体">xx</font> 格式) + const face = $font.attr('face') || '' + if (face) { + $font.removeAttr('face') + $font.css('font-family', face) + } + + return $font +} + +export const preParseHtmlConf = { + selector: 'font', + preParseHtml: preParse, +} diff --git a/packages/basic-modules/src/modules/header/index.ts b/packages/basic-modules/src/modules/header/index.ts index 24b569e01..46e875ec9 100644 --- a/packages/basic-modules/src/modules/header/index.ts +++ b/packages/basic-modules/src/modules/header/index.ts @@ -26,6 +26,13 @@ import { header4ToHtmlConf, header5ToHtmlConf, } from './elem-to-html' +import { + parseHeader1HtmlConf, + parseHeader2HtmlConf, + parseHeader3HtmlConf, + parseHeader4HtmlConf, + parseHeader5HtmlConf, +} from './parse-elem-html' import withHeader from './plugin' const header: Partial<IModuleConf> = { @@ -43,6 +50,13 @@ const header: Partial<IModuleConf> = { header4ToHtmlConf, header5ToHtmlConf, ], + parseElemsHtml: [ + parseHeader1HtmlConf, + parseHeader2HtmlConf, + parseHeader3HtmlConf, + parseHeader4HtmlConf, + parseHeader5HtmlConf, + ], menus: [ HeaderSelectMenuConf, Header1ButtonMenuConf, diff --git a/packages/basic-modules/src/modules/header/parse-elem-html.ts b/packages/basic-modules/src/modules/header/parse-elem-html.ts new file mode 100644 index 000000000..183fb2a99 --- /dev/null +++ b/packages/basic-modules/src/modules/header/parse-elem-html.ts @@ -0,0 +1,57 @@ +/** + * @description parse html + * @author wangfupeng + */ + +import { Descendant } from 'slate' +import { Dom7Array } from 'dom7' +import { IDomEditor } from '@wangeditor/core' +import { + Header1Element, + Header2Element, + Header3Element, + Header4Element, + Header5Element, +} from './custom-types' + +function genParser<T>(level: number) { + function parseHtml($elem: Dom7Array, children: Descendant[], editor: IDomEditor): T { + // 无 children ,则用纯文本 + if (children.length === 0) { + children = [{ text: $elem.text().replace(/\s+/gm, ' ') }] + } + + const elem = { + type: `header${level}`, + children, + } as unknown as T + + return elem + } + return parseHtml +} + +export const parseHeader1HtmlConf = { + selector: 'h1', + parseElemHtml: genParser<Header1Element>(1), +} + +export const parseHeader2HtmlConf = { + selector: 'h2', + parseElemHtml: genParser<Header2Element>(2), +} + +export const parseHeader3HtmlConf = { + selector: 'h3', + parseElemHtml: genParser<Header3Element>(3), +} + +export const parseHeader4HtmlConf = { + selector: 'h4', + parseElemHtml: genParser<Header4Element>(4), +} + +export const parseHeader5HtmlConf = { + selector: 'h5', + parseElemHtml: genParser<Header5Element>(5), +} diff --git a/packages/basic-modules/src/modules/image/index.ts b/packages/basic-modules/src/modules/image/index.ts index d173df879..f22b1bfd8 100644 --- a/packages/basic-modules/src/modules/image/index.ts +++ b/packages/basic-modules/src/modules/image/index.ts @@ -7,6 +7,7 @@ import { IModuleConf } from '@wangeditor/core' import withImage from './plugin' import { renderImageConf } from './render-elem' import { imageToHtmlConf } from './elem-to-html' +import { parseHtmlConf } from './parse-elem-html' import { insertImageMenuConf, deleteImageMenuConf, @@ -20,6 +21,7 @@ import { const image: Partial<IModuleConf> = { renderElems: [renderImageConf], elemsToHtml: [imageToHtmlConf], + parseElemsHtml: [parseHtmlConf], menus: [ insertImageMenuConf, deleteImageMenuConf, diff --git a/packages/basic-modules/src/modules/image/parse-elem-html.ts b/packages/basic-modules/src/modules/image/parse-elem-html.ts new file mode 100644 index 000000000..e5fa71453 --- /dev/null +++ b/packages/basic-modules/src/modules/image/parse-elem-html.ts @@ -0,0 +1,32 @@ +/** + * @description parse html + * @author wangfupeng + */ + +import { Descendant } from 'slate' +import { Dom7Array } from 'dom7' +import { IDomEditor } from '@wangeditor/core' +import { ImageElement } from './custom-types' +import { getStyleValue } from '../../utils/dom' + +function parseHtml($elem: Dom7Array, children: Descendant[], editor: IDomEditor): ImageElement { + let href = $elem.attr('data-href') || '' + href = decodeURIComponent(href) // 兼容 V4 + + return { + type: 'image', + src: $elem.attr('src') || '', + alt: $elem.attr('alt') || '', + href, + style: { + width: getStyleValue($elem, 'width'), + height: getStyleValue($elem, 'height'), + }, + children: [{ text: '' }], // void node 有一个空白 text + } +} + +export const parseHtmlConf = { + selector: 'img', + parseElemHtml: parseHtml, +} diff --git a/packages/basic-modules/src/modules/indent/index.ts b/packages/basic-modules/src/modules/indent/index.ts index 39f66d755..245f26ef2 100644 --- a/packages/basic-modules/src/modules/indent/index.ts +++ b/packages/basic-modules/src/modules/indent/index.ts @@ -6,11 +6,13 @@ import { IModuleConf } from '@wangeditor/core' import { renderStyle } from './render-style' import { styleToHtml } from './style-to-html' +import { parseStyleHtml } from './parse-style-html' import { indentMenuConf, delIndentMenuConf } from './menu/index' const indent: Partial<IModuleConf> = { renderStyle, styleToHtml, + parseStyleHtml, menus: [indentMenuConf, delIndentMenuConf], } diff --git a/packages/basic-modules/src/modules/indent/parse-style-html.ts b/packages/basic-modules/src/modules/indent/parse-style-html.ts new file mode 100644 index 000000000..b6328b4e0 --- /dev/null +++ b/packages/basic-modules/src/modules/indent/parse-style-html.ts @@ -0,0 +1,22 @@ +/** + * @description parse style html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { Descendant, Element } from 'slate' +import { IndentElement } from './custom-types' +import { getStyleValue } from '../../utils/dom' + +export function parseStyleHtml($elem: Dom7Array, node: Descendant): Descendant { + if (!Element.isElement(node)) return node + + const elemNode = node as IndentElement + + const indent = getStyleValue($elem, 'padding-left') + if (indent) { + elemNode.indent = indent + } + + return elemNode +} diff --git a/packages/basic-modules/src/modules/justify/index.ts b/packages/basic-modules/src/modules/justify/index.ts index 433714d16..46fe593ae 100644 --- a/packages/basic-modules/src/modules/justify/index.ts +++ b/packages/basic-modules/src/modules/justify/index.ts @@ -6,6 +6,7 @@ import { IModuleConf } from '@wangeditor/core' import { renderStyle } from './render-style' import { styleToHtml } from './style-to-html' +import { parseStyleHtml } from './parse-style-html' import { justifyLeftMenuConf, justifyRightMenuConf, @@ -16,6 +17,7 @@ import { const justify: Partial<IModuleConf> = { renderStyle, styleToHtml, + parseStyleHtml, menus: [justifyLeftMenuConf, justifyRightMenuConf, justifyCenterMenuConf, justifyJustifyMenuConf], } diff --git a/packages/basic-modules/src/modules/justify/parse-style-html.ts b/packages/basic-modules/src/modules/justify/parse-style-html.ts new file mode 100644 index 000000000..738ee95f6 --- /dev/null +++ b/packages/basic-modules/src/modules/justify/parse-style-html.ts @@ -0,0 +1,22 @@ +/** + * @description parse style html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { Descendant, Element } from 'slate' +import { JustifyElement } from './custom-types' +import { getStyleValue } from '../../utils/dom' + +export function parseStyleHtml($elem: Dom7Array, node: Descendant): Descendant { + if (!Element.isElement(node)) return node + + const elemNode = node as JustifyElement + + const textAlign = getStyleValue($elem, 'text-align') + if (textAlign) { + elemNode.textAlign = textAlign + } + + return elemNode +} diff --git a/packages/basic-modules/src/modules/line-height/index.ts b/packages/basic-modules/src/modules/line-height/index.ts index 3e90fa7fa..8861458d6 100644 --- a/packages/basic-modules/src/modules/line-height/index.ts +++ b/packages/basic-modules/src/modules/line-height/index.ts @@ -7,10 +7,12 @@ import { IModuleConf } from '@wangeditor/core' import { renderStyle } from './render-style' import { styleToHtml } from './style-to-html' import { lineHeightMenuConf } from './menu/index' +import { parseStyleHtml } from './parse-style-html' const lineHeight: Partial<IModuleConf> = { renderStyle, styleToHtml, + parseStyleHtml, menus: [lineHeightMenuConf], } diff --git a/packages/basic-modules/src/modules/line-height/parse-style-html.ts b/packages/basic-modules/src/modules/line-height/parse-style-html.ts new file mode 100644 index 000000000..42c2989a7 --- /dev/null +++ b/packages/basic-modules/src/modules/line-height/parse-style-html.ts @@ -0,0 +1,22 @@ +/** + * @description parse style html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { Descendant, Element } from 'slate' +import { LineHeightElement } from './custom-types' +import { getStyleValue } from '../../utils/dom' + +export function parseStyleHtml($elem: Dom7Array, node: Descendant): Descendant { + if (!Element.isElement(node)) return node + + const elemNode = node as LineHeightElement + + const lineHeight = getStyleValue($elem, 'line-height') + if (lineHeight) { + elemNode.lineHeight = lineHeight + } + + return elemNode +} diff --git a/packages/basic-modules/src/modules/link/index.ts b/packages/basic-modules/src/modules/link/index.ts index 83d869268..eb4aef170 100644 --- a/packages/basic-modules/src/modules/link/index.ts +++ b/packages/basic-modules/src/modules/link/index.ts @@ -7,6 +7,7 @@ import { IModuleConf } from '@wangeditor/core' import withLink from './plugin' import { renderLinkConf } from './render-elem' import { linkToHtmlConf } from './elem-to-html' +import { parseHtmlConf } from './parse-elem-html' import { insertLinkMenuConf, editLinkMenuConf, @@ -17,6 +18,7 @@ import { const link: Partial<IModuleConf> = { renderElems: [renderLinkConf], elemsToHtml: [linkToHtmlConf], + parseElemsHtml: [parseHtmlConf], menus: [insertLinkMenuConf, editLinkMenuConf, unLinkMenuConf, viewLinkMenuConf], editorPlugin: withLink, } diff --git a/packages/basic-modules/src/modules/link/parse-elem-html.ts b/packages/basic-modules/src/modules/link/parse-elem-html.ts new file mode 100644 index 000000000..046f61e4f --- /dev/null +++ b/packages/basic-modules/src/modules/link/parse-elem-html.ts @@ -0,0 +1,29 @@ +/** + * @description parse html + * @author wangfupeng + */ + +import { Descendant } from 'slate' +import { Dom7Array } from 'dom7' +import { IDomEditor } from '@wangeditor/core' +import { LinkElement } from './custom-types' + +function parseHtml($elem: Dom7Array, children: Descendant[], editor: IDomEditor): LinkElement { + // 无 children ,则用纯文本 + if (children.length === 0) { + children = [{ text: $elem.text().replace(/\s+/gm, ' ') }] + } + + return { + type: 'link', + url: $elem.attr('href') || '', + target: $elem.attr('target') || '', + // @ts-ignore + children, + } +} + +export const parseHtmlConf = { + selector: 'a', + parseElemHtml: parseHtml, +} diff --git a/packages/basic-modules/src/modules/paragraph/index.ts b/packages/basic-modules/src/modules/paragraph/index.ts index a127bff97..80c1e64d1 100644 --- a/packages/basic-modules/src/modules/paragraph/index.ts +++ b/packages/basic-modules/src/modules/paragraph/index.ts @@ -6,11 +6,13 @@ import { IModuleConf } from '@wangeditor/core' import { renderParagraphConf } from './render-elem' import { pToHtmlConf } from './elem-to-html' +import { parseParagraphHtmlConf } from './parse-elem-html' import withParagraph from './plugin' const p: Partial<IModuleConf> = { renderElems: [renderParagraphConf], elemsToHtml: [pToHtmlConf], + parseElemsHtml: [parseParagraphHtmlConf], editorPlugin: withParagraph, } diff --git a/packages/basic-modules/src/modules/paragraph/parse-elem-html.ts b/packages/basic-modules/src/modules/paragraph/parse-elem-html.ts new file mode 100644 index 000000000..5f85b018b --- /dev/null +++ b/packages/basic-modules/src/modules/paragraph/parse-elem-html.ts @@ -0,0 +1,31 @@ +/** + * @description parse html + * @author wangfupeng + */ + +import { Descendant, Text } from 'slate' +import { Dom7Array } from 'dom7' +import { IDomEditor } from '@wangeditor/core' +import { ParagraphElement } from './custom-types' + +function parseParagraphHtml( + $elem: Dom7Array, + children: Descendant[], + editor: IDomEditor +): ParagraphElement { + // 无 children ,则用纯文本 + if (children.length === 0) { + children = [{ text: $elem.text().replace(/\s+/gm, ' ') }] + } + + return { + type: 'paragraph', + // @ts-ignore + children, + } +} + +export const parseParagraphHtmlConf = { + selector: 'p', + parseElemHtml: parseParagraphHtml, +} diff --git a/packages/basic-modules/src/modules/text-style/index.ts b/packages/basic-modules/src/modules/text-style/index.ts index f43c73414..c5b518206 100644 --- a/packages/basic-modules/src/modules/text-style/index.ts +++ b/packages/basic-modules/src/modules/text-style/index.ts @@ -6,6 +6,7 @@ import { IModuleConf } from '@wangeditor/core' import { renderStyle } from './render-style' import { styleToHtml } from './style-to-html' +import { parseStyleHtml } from './parse-style-html' import { boldMenuConf, underlineMenuConf, @@ -31,6 +32,7 @@ const textStyle: Partial<IModuleConf> = { clearStyleMenuConf, ], styleToHtml, + parseStyleHtml, editorPlugin: withTextStyle, } diff --git a/packages/basic-modules/src/modules/text-style/parse-style-html.ts b/packages/basic-modules/src/modules/text-style/parse-style-html.ts new file mode 100644 index 000000000..cf2b1b7b0 --- /dev/null +++ b/packages/basic-modules/src/modules/text-style/parse-style-html.ts @@ -0,0 +1,73 @@ +/** + * @description parse style html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { Descendant, Text } from 'slate' +import { StyledText } from './custom-types' +import { getTagName } from '../../utils/dom' + +/** + * $text 是否匹配 tags + * @param $text $text + * @param tags tags,如 ['b', 'strong'] + */ +function isMatch($text: Dom7Array, tags: string[] | string): boolean { + const tagName = getTagName($text) + if (typeof tags === 'string') tags = [tags] + + const length = tags.length + for (let i = 0; i < length; i++) { + const tag = tags[i] + if (tagName === tag) return true + break + } + + if ($text.find(tags.join(',')).length > 0) return true + + return false +} + +export function parseStyleHtml($text: Dom7Array, node: Descendant): Descendant { + if (!Text.isText(node)) return node + + const textNode = node as StyledText + + // bold + if (isMatch($text, ['b', 'strong'])) { + textNode.bold = true + } + + // italic + if (isMatch($text, ['i', 'em'])) { + textNode.italic = true + } + + // underline + if (isMatch($text, 'u')) { + textNode.underline = true + } + + // through + if (isMatch($text, ['s', 'strike'])) { + textNode.through = true + } + + // sub + if (isMatch($text, 'sub')) { + textNode.sub = true + } + + // sup + if (isMatch($text, 'sup')) { + textNode.sup = true + } + + // code + if (isMatch($text, 'code')) { + textNode.code = true + } + + return textNode +} diff --git a/packages/basic-modules/src/utils/dom.ts b/packages/basic-modules/src/utils/dom.ts index 9df9f30a3..0f246a9c8 100644 --- a/packages/basic-modules/src/utils/dom.ts +++ b/packages/basic-modules/src/utils/dom.ts @@ -67,3 +67,36 @@ export function getOuterHTML($elem: Dom7Array) { $div.append($elem) return $div.html() } + +/** + * 获取 tagName lower-case + * @param $elem $elem + */ +export function getTagName($elem: Dom7Array): string { + if ($elem.length) return $elem[0].tagName.toLowerCase() + return '' +} + +/** + * 获取 $elem 某一个 style 值 + * @param $elem $elem + * @param styleKey style key + */ +export function getStyleValue($elem: Dom7Array, styleKey: string): string { + let res = '' + + const styleStr = $elem.attr('style') || '' // 如 'line-height: 2.5; color: red;' + const styleArr = styleStr.split(';') // 如 ['line-height: 2.5', ' color: red', ''] + const length = styleArr.length + for (let i = 0; i < length; i++) { + const styleItemStr = styleArr[i] // 如 'line-height: 2.5' + if (styleItemStr) { + const arr = styleItemStr.split(':') // ['line-height', ' 2.5'] + if (arr[0].trim() === styleKey) { + res = arr[1].trim() + } + } + } + + return res +} diff --git a/packages/code-highlight/src/module/index.ts b/packages/code-highlight/src/module/index.ts index 627c4e690..0e314b56f 100644 --- a/packages/code-highlight/src/module/index.ts +++ b/packages/code-highlight/src/module/index.ts @@ -5,11 +5,13 @@ import { IModuleConf } from '@wangeditor/core' import { renderStyle } from './render-style' +import { parseCodeStyleHtml } from './parse-style-html' import { selectLangMenuConf } from './menu/index' import { codeToHtmlConf } from './elem-to-html' const codeHighlightModule: Partial<IModuleConf> = { renderStyle, + parseStyleHtml: parseCodeStyleHtml, menus: [selectLangMenuConf], elemsToHtml: [codeToHtmlConf], } diff --git a/packages/code-highlight/src/module/parse-style-html.ts b/packages/code-highlight/src/module/parse-style-html.ts new file mode 100644 index 000000000..d326555fd --- /dev/null +++ b/packages/code-highlight/src/module/parse-style-html.ts @@ -0,0 +1,27 @@ +/** + * @description parse style html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { Descendant, Element } from 'slate' +import { DomEditor } from '@wangeditor/core' +import { CodeElement } from '../custom-types' + +export function parseCodeStyleHtml($elem: Dom7Array, node: Descendant): Descendant { + if (!Element.isElement(node)) return node + if (DomEditor.getNodeType(node) !== 'code') return node // 只针对 pre/code 元素 + + const elemNode = node as CodeElement + + const langAttr = $elem.attr('class') || '' + if (langAttr.indexOf('language-') === 0) { + // V5 版本,格式如 class="language-javascript" + elemNode.language = langAttr.split('-')[1] || '' // 获取 'javascript' + } else { + // 兼容 V4 版本,格式如 class="Javascript" + elemNode.language = langAttr.toLowerCase() + } + + return elemNode +} diff --git a/packages/code-highlight/src/utils/dom.ts b/packages/code-highlight/src/utils/dom.ts new file mode 100644 index 000000000..95d1ad758 --- /dev/null +++ b/packages/code-highlight/src/utils/dom.ts @@ -0,0 +1,10 @@ +/** + * @description DOM 操作 + * @author wangfupeng + */ + +import { $, attr } from 'dom7' + +$.fn.attr = attr + +export default $ diff --git a/packages/core/src/create/create-editor.ts b/packages/core/src/create/create-editor.ts index d8582c7a5..5dec19224 100644 --- a/packages/core/src/create/create-editor.ts +++ b/packages/core/src/create/create-editor.ts @@ -29,13 +29,16 @@ import { EDITOR_TO_HOVER_BAR, } from '../utils/weak-maps' import bindNodeRelation from './bind-node-relation' +import $ from '../utils/dom' +import parseElemHtml from '../parse-html/parse-elem-html' type PluginFnType = <T extends IDomEditor>(editor: T) => T interface ICreateOption { selector: string | DOMElement config: Partial<IEditorConfig> - content: Descendant[] + content?: Descendant[] + html?: string plugins: PluginFnType[] } @@ -43,7 +46,7 @@ interface ICreateOption { * 创建编辑器 */ export default function (option: Partial<ICreateOption>) { - const { selector = '', config = {}, content, plugins = [] } = option + const { selector = '', config = {}, content, html, plugins = [] } = option // 创建实例 - 使用插件 let editor = withHistory( @@ -69,10 +72,19 @@ export default function (option: Partial<ICreateOption>) { }) // 初始化内容(要在 config 和 plugins 后面) - if (content && content.length) { - editor.children = content - } else { - editor.children = genDefaultContent() + if (content) { + editor.children = content // 传入 JSON content + } + if (html) { + // 传入 html ,转换为 JSON content + const $content = $(`<div>${html}</div>`) + editor.children = Array.from($content.children()).map(child => { + const $child = $(child) + return parseElemHtml($child, editor) + }) + } + if (editor.children.length === 0) { + editor.children = genDefaultContent() // 默认内容 } DomEditor.normalizeContent(editor) // 格式化,用户输入的 content 可能不规范(如两个相连的 text 没有合并) diff --git a/packages/core/src/editor/plugins/with-content.ts b/packages/core/src/editor/plugins/with-content.ts index 1677a13b8..5e6adcca4 100644 --- a/packages/core/src/editor/plugins/with-content.ts +++ b/packages/core/src/editor/plugins/with-content.ts @@ -7,7 +7,7 @@ import { Editor, Node, Text, Path, Operation, Range, Transforms, Element } from import { DomEditor } from '../dom-editor' import { IDomEditor } from '../..' import { EDITOR_TO_SELECTION, NODE_TO_KEY } from '../../utils/weak-maps' -import { node2html } from '../../to-html/node2html' +import node2html from '../../to-html/node2html' import { genElemId } from '../../render/helper' import { Key } from '../../utils/key' import { DOMElement, getPlainText } from '../../utils/dom' diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 5f223fb06..87785e6bd 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -7,6 +7,7 @@ import './assets/index.less' import { RenderStyleFnType, IRenderElemConf } from './render/index' import { styleToHtmlFnType, IElemToHtmlConf } from './to-html/index' +import { IPreParseHtmlConf, ParseStyleHtmlFnType, IParseElemHtmlConf } from './parse-html/index' import { IRegisterMenuConf } from './menus/index' import { IDomEditor } from './editor/interface' @@ -26,6 +27,9 @@ export * from './render/index' // 注册 toHtml export * from './to-html/index' +// 注册 parseHtml +export * from './parse-html/index' + // menu 的接口、注册、方法等 export * from './menus/index' @@ -47,6 +51,11 @@ export interface IModuleConf { styleToHtml: styleToHtmlFnType elemsToHtml: Array<IElemToHtmlConf> + // parse html + preParseHtml: Array<IPreParseHtmlConf> + parseStyleHtml: ParseStyleHtmlFnType + parseElemsHtml: Array<IParseElemHtmlConf> + // 注册插件 editorPlugin: <T extends IDomEditor>(editor: T) => T } diff --git a/packages/core/src/parse-html/README.md b/packages/core/src/parse-html/README.md new file mode 100644 index 000000000..7ec999845 --- /dev/null +++ b/packages/core/src/parse-html/README.md @@ -0,0 +1,3 @@ +# parse html + +把 html 转换为 JSON content diff --git a/packages/core/src/parse-html/index.ts b/packages/core/src/parse-html/index.ts new file mode 100644 index 000000000..a153765be --- /dev/null +++ b/packages/core/src/parse-html/index.ts @@ -0,0 +1,62 @@ +/** + * @description parse html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { Element as SlateElement, Descendant } from 'slate' +import { IDomEditor } from '../editor/interface' + +// ------------------------------------ pre-parse html ------------------------------------ +export type PreParseHtmlFnType = ($node: Dom7Array) => Dom7Array + +export interface IPreParseHtmlConf { + selector: string // css 选择器,如 `p` `div[data-type="xxx"]` + preParseHtml: PreParseHtmlFnType +} + +export const PRE_PARSE_HTML_CONF_LIST: IPreParseHtmlConf[] = [] + +/** + * 注册 pre-parse html 配置 + * @param conf pre-parse html conf + */ +export function registerPreParseHtmlConf(conf: IPreParseHtmlConf) { + PRE_PARSE_HTML_CONF_LIST.push(conf) +} + +// ------------------------------------ parse style html ------------------------------------ + +export type ParseStyleHtmlFnType = ($node: Dom7Array, node: Descendant) => Descendant + +export const PARSE_STYLE_HTML_FN_LIST: ParseStyleHtmlFnType[] = [] + +/** + * 注册 parseStyleHtml 函数 + * @param fn parse style html 的函数 + */ +export function registerParseStyleHtmlHandler(fn: ParseStyleHtmlFnType) { + PARSE_STYLE_HTML_FN_LIST.push(fn) +} + +// ------------------------------------ parse elem html ------------------------------------ + +export type ParseElemHtmlFnType = ( + $elem: Dom7Array, + children: Descendant[], + editor: IDomEditor +) => SlateElement + +export const PARSE_ELEM_HTML_CONF: { + [key: string]: ParseElemHtmlFnType // key 是 css 选择器,如 `p` `div[data-type="xxx"]` +} = {} + +export interface IParseElemHtmlConf { + selector: string + parseElemHtml: ParseElemHtmlFnType +} + +export function registerParseElemHtmlConf(conf: IParseElemHtmlConf) { + const { selector, parseElemHtml } = conf + PARSE_ELEM_HTML_CONF[selector] = parseElemHtml +} diff --git a/packages/core/src/parse-html/parse-common-elem-html.ts b/packages/core/src/parse-html/parse-common-elem-html.ts new file mode 100644 index 000000000..e029123ca --- /dev/null +++ b/packages/core/src/parse-html/parse-common-elem-html.ts @@ -0,0 +1,118 @@ +/** + * @description parse elem html + * @author wangfupeng + */ + +import $, { Dom7Array } from 'dom7' +import { Editor, Element, Descendant } from 'slate' +import { IDomEditor } from '../editor/interface' +import parseElemHtml from './parse-elem-html' +import { PARSE_ELEM_HTML_CONF, ParseElemHtmlFnType, PARSE_STYLE_HTML_FN_LIST } from './index' +import { NodeType } from '../utils/dom' + +/** + * 生成 slate node children + * @param $elem $elem + * @param editor editor + */ +function genChildren($elem: Dom7Array, editor: IDomEditor): Descendant[] { + const children: Descendant[] = [] + + // void node( html 中编辑的,如 video 的 html 中会有 data-w-e-isVoid 属性 ),不需要生成 children + const isVoid = $elem.attr('data-w-e-isVoid') != null + if (isVoid) { + return children + } + + // 处理空行(只有一个 child ,是 <br>) + if ($elem[0].children.length === 1) { + if ($elem[0].children[0].nodeName === 'BR') { + children.push({ text: '' }) + return children // 直接返回 + } + } + + // 遍历 DOM 子节点,生成 slate elem node children + const childNodes = $elem[0].childNodes + childNodes.forEach(child => { + if (child.nodeType === NodeType.ELEMENT_NODE) { + // elem + const $child = $(child) + children.push(parseElemHtml($child, editor)) + return + } + if (child.nodeType === NodeType.TEXT_NODE) { + // text + let text = child.textContent || '' + if (text.trim() === '' && text.indexOf('\n') >= 0) { + // 有换行,但无实际内容 + return + } + + // text + text = text.replace(/\s+/gm, ' ') + if (text) { + children.push({ text }) + } + return + } + }) + return children +} + +/** + * 默认的 parseElemHtml ,直接转换为 paragraph + * @param $elem $elem + * @param children children + */ +function defaultParser($elem: Dom7Array, children: Descendant[], editor: IDomEditor): Element { + console.log('defaultParser...', $elem[0].outerHTML) + return { + type: 'paragraph', + children: [{ text: $elem.text().replace(/\s+/gm, ' ') }], + } +} + +/** + * 获取当前 html 元素的 parseElemHtml 函数 + * @param $elem $elem + */ +function getParser($elem: Dom7Array): ParseElemHtmlFnType { + for (let selector in PARSE_ELEM_HTML_CONF) { + if ($elem[0].matches(selector)) { + return PARSE_ELEM_HTML_CONF[selector] + } + } + return defaultParser +} + +/** + * 处理普通 DOM elem html ,非 span font 等文本 elem + * @param $elem $elem + * @param editor editor + * @returns slate element + */ +function parseCommonElemHtml($elem: Dom7Array, editor: IDomEditor): Element { + const children = genChildren($elem, editor) + + // parse + const parser = getParser($elem) + let elem = parser($elem, children, editor) + + const isVoid = Editor.isVoid(editor, elem) + if (!isVoid) { + // 非 void ,如果没有 children ,则取纯文本 + if (children.length === 0) { + elem.children = [{ text: $elem.text().replace(/\s+/gm, ' ') }] + } + + // 处理 style + PARSE_STYLE_HTML_FN_LIST.forEach(fn => { + elem = fn($elem, elem) as Element + }) + } + + return elem +} + +export default parseCommonElemHtml diff --git a/packages/core/src/parse-html/parse-elem-html.ts b/packages/core/src/parse-html/parse-elem-html.ts new file mode 100644 index 000000000..b12e3d607 --- /dev/null +++ b/packages/core/src/parse-html/parse-elem-html.ts @@ -0,0 +1,56 @@ +/** + * @description parse node html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { Descendant } from 'slate' +import { IDomEditor } from '../editor/interface' +import parseCommonElemHtml from './parse-common-elem-html' +import parseTextElemHtml from './parse-text-elem-html' +import { getTagName } from '../utils/dom' +import { PRE_PARSE_HTML_CONF_LIST } from '../index' + +// 常见的 text tag (不用太全面,因为 wangEditor 产出的 text 全部包裹了 span ,所以只再兼容其他常见的即可) +const TEXT_TAGS = ['span', 'b', 'strong', 'i', 'em', 's', 'strike', 'u', 'font'] + +/** + * 处理 DOM Elem html + * @param $elem $elem + * @param editor editor + * @returns slate Descendant + */ +function parseElemHtml($elem: Dom7Array, editor: IDomEditor): Descendant { + // pre-parse + PRE_PARSE_HTML_CONF_LIST.forEach(conf => { + const { selector, preParseHtml } = conf + if ($elem[0].matches(selector)) { + $elem = preParseHtml($elem) + } + }) + + const tagName = getTagName($elem) + + // <code> 特殊处理 + if (tagName === 'code') { + const parentTagName = getTagName($elem.parent()) + if (parentTagName === 'pre') { + // <code> 在 <pre> 内,则是 elem + return parseCommonElemHtml($elem, editor) + } else { + // <code> 不在 <pre> 内,则是 text + return parseTextElemHtml($elem, editor) + } + } + + // 非 <code> ,正常处理 + if (TEXT_TAGS.includes(tagName)) { + // text node + return parseTextElemHtml($elem, editor) + } else { + // elem node + return parseCommonElemHtml($elem, editor) + } +} + +export default parseElemHtml diff --git a/packages/core/src/parse-html/parse-text-elem-html.ts b/packages/core/src/parse-html/parse-text-elem-html.ts new file mode 100644 index 000000000..109adba11 --- /dev/null +++ b/packages/core/src/parse-html/parse-text-elem-html.ts @@ -0,0 +1,37 @@ +/** + * @description parse text html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { Text } from 'slate' +import { IDomEditor } from '../editor/interface' +import { PARSE_STYLE_HTML_FN_LIST } from './index' +import { deReplaceHtmlSpecialSymbols } from '../utils/util' + +/** + * 处理 text elem ,如 <span> <strong> <em> 等(并不是 DOM Text Node) + * @param $text $text + * @param editor editor + * @returns slate text + */ +function parseTextElemHtml($text: Dom7Array, editor: IDomEditor): Text { + // 用 textContent ,不能用 .text() 。后者无法识别 text 开头和末尾的 &nbsp; + let text = $text[0].textContent || '' + text = text.replace(/\s+/gm, ' ') + + //【翻转】替换 html 特殊字符,如 &lt; 替换为 < + text = deReplaceHtmlSpecialSymbols(text) + + // 生成 text node + let textNode = { text } + + // 处理 style + PARSE_STYLE_HTML_FN_LIST.forEach(fn => { + textNode = fn($text, textNode) as Text + }) + + return textNode +} + +export default parseTextElemHtml diff --git a/packages/core/src/render/README.md b/packages/core/src/render/README.md new file mode 100644 index 000000000..5d56c6e73 --- /dev/null +++ b/packages/core/src/render/README.md @@ -0,0 +1,3 @@ +# render + +把 JSON content 转换为 vdom diff --git a/packages/core/src/to-html/README.md b/packages/core/src/to-html/README.md new file mode 100644 index 000000000..a86f48407 --- /dev/null +++ b/packages/core/src/to-html/README.md @@ -0,0 +1,3 @@ +# to html + +把 content 为 html diff --git a/packages/core/src/to-html/elem2html.ts b/packages/core/src/to-html/elem2html.ts index 27e1d105a..6a811fc55 100644 --- a/packages/core/src/to-html/elem2html.ts +++ b/packages/core/src/to-html/elem2html.ts @@ -5,7 +5,7 @@ import { Editor, Element } from 'slate' import { IDomEditor } from '../editor/interface' -import { node2html } from './node2html' +import node2html from './node2html' import { ElemToHtmlFnType, ELEM_TO_HTML_CONF, STYLE_TO_HTML_FN_LIST } from './index' /** diff --git a/packages/core/src/to-html/node2html.ts b/packages/core/src/to-html/node2html.ts index a2dc0c79f..54ecdcd3a 100644 --- a/packages/core/src/to-html/node2html.ts +++ b/packages/core/src/to-html/node2html.ts @@ -8,7 +8,7 @@ import { IDomEditor } from '../editor/interface' import elemToHtml from './elem2html' import textToHtml from './text2html' -export function node2html(node: Descendant, editor: IDomEditor): string { +function node2html(node: Descendant, editor: IDomEditor): string { if (Element.isElement(node)) { // elem node return elemToHtml(node, editor) @@ -17,3 +17,5 @@ export function node2html(node: Descendant, editor: IDomEditor): string { return textToHtml(node, editor) } } + +export default node2html diff --git a/packages/core/src/to-html/text2html.ts b/packages/core/src/to-html/text2html.ts index 5b6002f89..6fe79fd28 100644 --- a/packages/core/src/to-html/text2html.ts +++ b/packages/core/src/to-html/text2html.ts @@ -7,23 +7,15 @@ import { Text } from 'slate' import { IDomEditor } from '../editor/interface' import { DomEditor } from '../editor/dom-editor' import { STYLE_TO_HTML_FN_LIST } from './index' - -function replaceSymbols(str: string) { - return str - .replace(/</g, '&lt;') - .replace(/>/g, '&gt;') - .replace(/®/g, '&reg;') - .replace(/©/g, '&copy;') - .replace(/™/g, '&trade;') -} +import { replaceHtmlSpecialSymbols } from '../utils/util' function textToHtml(textNode: Text, editor: IDomEditor): string { const { text } = textNode if (text == null) throw new Error(`Current node is not slate Text ${JSON.stringify(textNode)}`) let textHtml = text - // 替换特殊字符 - textHtml = replaceSymbols(textHtml) + // 替换 html 特殊字符 + textHtml = replaceHtmlSpecialSymbols(textHtml) // 替换 \n 为 <br> (一定要在替换特殊字符之后) const parents = DomEditor.getParentsNodes(editor, textNode) @@ -33,6 +25,11 @@ function textToHtml(textNode: Text, editor: IDomEditor): string { textHtml = textHtml.replace(/\n|\r/g, '<br>') } + // 在 <pre> 内部,&nbsp; 替换为空格 + if (hasPre) { + textHtml = textHtml.replace(/&nbsp;/g, ' ') + } + // 空标签 if (!textHtml) textHtml = '<br>' diff --git a/packages/core/src/utils/dom.ts b/packages/core/src/utils/dom.ts index f87de9bdd..db75390e8 100644 --- a/packages/core/src/utils/dom.ts +++ b/packages/core/src/utils/dom.ts @@ -33,6 +33,7 @@ import { remove, find, each, + Dom7Array, } from 'dom7' export { Dom7Array } from 'dom7' @@ -366,3 +367,12 @@ export enum NodeType { DOCUMENT_TYPE_NODE = 10, DOCUMENT_FRAGMENT_NODE = 11, } + +/** + * 获取 tagName lower-case + * @param $elem $elem + */ +export function getTagName($elem: Dom7Array): string { + if ($elem.length) return $elem[0].tagName.toLowerCase() + return '' +} diff --git a/packages/core/src/utils/util.ts b/packages/core/src/utils/util.ts index bb2367e67..dccc65a5c 100644 --- a/packages/core/src/utils/util.ts +++ b/packages/core/src/utils/util.ts @@ -52,3 +52,31 @@ export function addQueryToUrl(url: string, data: object): string { return urlWithoutHash } } + +/** + * 替换 html 特殊字符,如 > 替换为 &gt; + * @param str html str + */ +export function replaceHtmlSpecialSymbols(str: string) { + return str + .replace(/ /g, '&nbsp;') + .replace(/</g, '&lt;') + .replace(/>/g, '&gt;') + .replace(/®/g, '&reg;') + .replace(/©/g, '&copy;') + .replace(/™/g, '&trade;') +} + +/** + *【反转】替换 html 特殊字符,如 &gt; 替换为 > + * @param str html str + */ +export function deReplaceHtmlSpecialSymbols(str: string) { + return str + .replace(/&nbsp;/g, ' ') + .replace(/&lt;/g, '<') + .replace(/&gt;/g, '>') + .replace(/&reg;/g, '®') + .replace(/&copy;/g, '©') + .replace(/&trade;/g, '™') +} diff --git a/packages/editor/examples/dom7-demo.html b/packages/editor/examples/dom7-demo.html new file mode 100644 index 000000000..478cbd089 --- /dev/null +++ b/packages/editor/examples/dom7-demo.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>dom7 demo</title> +</head> +<body> + <p>dom7 demo</p> + + <script src="https://cdn.jsdelivr.net/npm/dom7@4.0.2/dom7.min.js"></script> + <script> + const $text = Dom7('<span data-a><b>hello</b></span>') + const $p = Dom7('<p style="line-height: 2.5; color: red;"><span>行高文字 line-height</span></p>') + // Dom7('body').append(p) + + const tableStr = ` + <table border="0" width="100%" cellpadding="0" cellspacing="0"><tbody><tr><th></th><th></th><th></th><th></th><th></th></tr><tr><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td><td></td></tr></tbody></table> + ` + const $table = Dom7(tableStr) + const $tbody = $table.find('tbody') + const $tr = $table.find('tr') + $table.append($tr) + $tbody.remove() + </script> +</body> +</html> \ No newline at end of file diff --git a/packages/editor/examples/index.html b/packages/editor/examples/index.html index 6864dbb71..4ba031a92 100644 --- a/packages/editor/examples/index.html +++ b/packages/editor/examples/index.html @@ -23,6 +23,7 @@ <h1>wangEditor examples</h1> <ul> <li><a href="./default-mode.html">Default mode 默认模式</a></li> <li><a href="./simple-mode.html">Simple mode 简洁模式</a></li> + <li><a href="./parse-html.html">Parse html 回显使用 html</a></li> <li><a href="./menu.html">Menu config 菜单配置</a></li> <li><a href="./like-yuque.html">Like QQ Doc 模仿腾讯文档编辑器</a></li> <li><a href="./simple-mode.html">Sync to textarea 同步到 textarea</a></li> diff --git a/packages/editor/examples/parse-html.html b/packages/editor/examples/parse-html.html new file mode 100644 index 000000000..55c4a7815 --- /dev/null +++ b/packages/editor/examples/parse-html.html @@ -0,0 +1,159 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>回显 html</title> + <link href="https://cdn.bootcdn.net/ajax/libs/normalize/8.0.1/normalize.min.css" rel="stylesheet"> + <link href="./css/editor.css" rel="stylesheet"> + <link href="../dist/css/style.css" rel="stylesheet"> +</head> +<body> + <p style="background-color: #ccc; padding: 5px 0;"><b>回显 html</b> - 使用 html 初始化编辑器内容</p> + + <div style="margin: 10px 0;"> + <button id="btn-v4-html">填入 V4 html 示例</button> + <button id="btn-v5-html">填入 V5 html 示例</button> + <span style="color: red;"><b>【注意】只识别从 wangEditor 生成的 html</b> ,不可以随意自定义 html 代码(html 格式太灵活了,不会全部兼容)</span> + </div> + <textarea id="text-html" style="width: 100%; height: 300px; font-size: 12px;" placeholder="输入 html 然后创建编辑器">&lt;p&gt;hello world&lt;/p&gt;</textarea> + + <div style="margin: 10px 0;"> + <button id="btn-create">创建编辑器</button> + (如有 JS 报错,再次创建时要刷新页面) + </div> + <div> + <div id="editor-toolbar" class="editor-toolbar"></div> + <div id="editor-text-area" class="editor-text-area"></div> + </div> + + <script src="../dist/index.js"></script> + <script> + const E = window.wangEditor + const textarea = document.getElementById('text-html') + let editor + + // 创建编辑器 + document.getElementById('btn-create').addEventListener('click', () => { + if (editor) editor.destroy() + + const html = textarea.value + editor = E.createEditor({ + selector: '#editor-text-area', + html, + }) + E.createToolbar({ + editor, + selector: '#editor-toolbar', + }) + }) + + // 填入 v4 html + document.getElementById('btn-v4-html').addEventListener('click', () => { + textarea.value = `<p>你好&nbsp;<font size="6">世界</font> <font face="黑体">黑体文字</font>!</p> +<p>欢迎<font color="#eeece0">使用</font> <b>wangEditor</b> <span style="background-color: rgb(139, 170, 74);">富文本</span>编辑器</p> +<h1 id="94fco">标题</h1> +<blockquote><p>欢迎使用 <b>wangEditor</b> 富文本编辑器</p></blockquote> +<p>欢迎<i>使用</i> <b>wangEditor</b><u>富文本</u><strike>编辑器</strike>~</p> +<p style="padding-left:2em;">缩进&nbsp;<a href="http://106.12.198.214:8881/dev/examples/" target="_blank">链接</a></p> +<p data-we-empty-p="" style="line-height:3;">行高&nbsp;</p><p> + <img src="http://www.wangeditor.com/imgs/logo.jpeg"/></p> +<p style="text-align:center;">欢迎使用 <b>wangEditor</b> 富文本编辑器</p> +<ol> + <li>abc</li> + <li>def</li> +</ol> +<ul><li>123</li><li>123</li></ul> +<p> + <img src="http://www.wangeditor.com/imgs/logo.jpeg" alt="111" data-href="http%3A%2F%2F106.12.198.214%3A8881%2Fdev%2Fexamples%2F" style="max-width:100%;" contenteditable="false"/> +</p> +<table border="0" width="100%" cellpadding="0" cellspacing="0"> + <tbody> + <tr><th>是的</th><th>你好</th><th>世界</th></tr><tr><td>aaa</td><td></td><td></td></tr> + <tr><td>bb</td><td>33</td><td></td></tr><tr><td></td><td></td><td>55</td></tr> + <tr><td></td><td></td><td></td></tr> + </tbody> +</table> +<p>123123 🤣😎</p> +<pre><code class="JavaScript"><xmp>const a = 100; +function fn() { +return a; +} +


+


` + }) + + // 填入 v5 html + document.getElementById('btn-v5-html').addEventListener('click', () => { + textarea.value = `

标题1

+

标题3

+
引用文字123
+

+ hello  + word + ~😊😬 +

+
+

+ 百度 +   + var +

+

行高文字 line-height

+

对齐方式 text-align

+

增加缩进 indent

+

+ google +

+

+ 设置字号 +   + 设置字体 +

+

+ 颜色 +   + 颜色 +   + 颜色 +

+
const a = 10;
+function fn() {
+// 代码
+}
+
    +
  • 项目A
  • +
  • 项目B
  • +
  • 项目C word
  • +
+
    +
  1. 项目1 百度
  2. +
  3. 项目2
  4. +
  5. 项目3
  6. +
+
+ +
+
+ +
+

hello

+ + + + + +
abc
123
+
+ 其他标签 +
` + }) + + + + \ No newline at end of file diff --git a/packages/editor/src/Boot.ts b/packages/editor/src/Boot.ts index 4451585c6..010b551c4 100644 --- a/packages/editor/src/Boot.ts +++ b/packages/editor/src/Boot.ts @@ -25,6 +25,15 @@ import { styleToHtmlFnType, registerStyleToHtmlHandler, registerElemToHtmlConf, + + // parseHtml + PreParseHtmlFnType, + IPreParseHtmlConf, + registerPreParseHtmlConf, + ParseStyleHtmlFnType, + IParseElemHtmlConf, + registerParseElemHtmlConf, + registerParseStyleHtmlHandler, } from '@wangeditor/core' type PluginType = (editor: T) => T @@ -97,6 +106,21 @@ class Boot { static registerStyleToHtml(fn: styleToHtmlFnType) { registerStyleToHtmlHandler(fn) } + + // 注册 preParseHtml + static registerPreParseHtml(preParseHtmlConf: IPreParseHtmlConf) { + registerPreParseHtmlConf(preParseHtmlConf) + } + + // 注册 parseElemHtml + static registerParseElemHtml(parseElemHtmlConf: IParseElemHtmlConf) { + registerParseElemHtmlConf(parseElemHtmlConf) + } + + // 注册 parseStyleHtml + static registerParseStyleHtml(fn: ParseStyleHtmlFnType) { + registerParseStyleHtmlHandler(fn) + } } export default Boot diff --git a/packages/editor/src/create.ts b/packages/editor/src/create.ts index 890f872c4..7e79414c1 100644 --- a/packages/editor/src/create.ts +++ b/packages/editor/src/create.ts @@ -18,7 +18,8 @@ import { export interface ICreateEditorOption { selector: string | DOMElement config: Partial - content: Descendant[] + content?: Descendant[] + html?: string mode: string } @@ -33,7 +34,7 @@ export interface ICreateToolbarOption { * 创建 editor 实例 */ export function createEditor(option: Partial = {}): IDomEditor { - const { selector = '', content = [], config = {}, mode = 'default' } = option + const { selector = '', content = [], html, config = {}, mode = 'default' } = option let globalConfig = mode === 'simple' ? Boot.simpleEditorConfig : Boot.editorConfig @@ -51,6 +52,7 @@ export function createEditor(option: Partial = {}): IDomEdi hoverbarKeys: newHoverbarKeys, }, content, + html, plugins: Boot.plugins, }) diff --git a/packages/editor/src/register-builtin-modules/register.ts b/packages/editor/src/register-builtin-modules/register.ts index e8ee745a5..59773c396 100644 --- a/packages/editor/src/register-builtin-modules/register.ts +++ b/packages/editor/src/register-builtin-modules/register.ts @@ -7,7 +7,17 @@ import Boot from '../Boot' import { IModuleConf } from '@wangeditor/core' function registerModule(module: Partial) { - const { menus, renderElems, renderStyle, elemsToHtml, styleToHtml, editorPlugin } = module + const { + menus, + renderElems, + renderStyle, + elemsToHtml, + styleToHtml, + preParseHtml, + parseElemsHtml, + parseStyleHtml, + editorPlugin, + } = module if (menus) { menus.forEach(menu => Boot.registerMenu(menu)) @@ -24,6 +34,15 @@ function registerModule(module: Partial) { if (styleToHtml) { Boot.registerStyleToHtml(styleToHtml) } + if (preParseHtml) { + preParseHtml.forEach(conf => Boot.registerPreParseHtml(conf)) + } + if (parseElemsHtml) { + parseElemsHtml.forEach(parseElemHtmlConf => Boot.registerParseElemHtml(parseElemHtmlConf)) + } + if (parseStyleHtml) { + Boot.registerParseStyleHtml(parseStyleHtml) + } if (editorPlugin) { Boot.registerPlugin(editorPlugin) } diff --git a/packages/list-module/src/module/index.ts b/packages/list-module/src/module/index.ts index 0f1974db6..90d40056f 100644 --- a/packages/list-module/src/module/index.ts +++ b/packages/list-module/src/module/index.ts @@ -6,12 +6,18 @@ import { IModuleConf } from '@wangeditor/core' import { renderBulletedListConf, renderNumberedListConf, renderListItemConf } from './render-elem' import { bulletedToHtmlConf, numberedToHtmlConf, listItemToHtmlConf } from './elem-to-html' +import { + parseItemHtmlConf, + parseNumberedListHtmlConf, + parseBulletedListHtmlConf, +} from './parse-elem-html' import { bulletedListMenuConf, numberedListMenuConf } from './menu/index' import withList from './plugin' const bold: Partial = { renderElems: [renderBulletedListConf, renderNumberedListConf, renderListItemConf], elemsToHtml: [bulletedToHtmlConf, numberedToHtmlConf, listItemToHtmlConf], + parseElemsHtml: [parseItemHtmlConf, parseNumberedListHtmlConf, parseBulletedListHtmlConf], menus: [bulletedListMenuConf, numberedListMenuConf], editorPlugin: withList, } diff --git a/packages/list-module/src/module/parse-elem-html.ts b/packages/list-module/src/module/parse-elem-html.ts new file mode 100644 index 000000000..6955ff73e --- /dev/null +++ b/packages/list-module/src/module/parse-elem-html.ts @@ -0,0 +1,65 @@ +/** + * @description parse html + * @author wangfupeng + */ + +import { Descendant } from 'slate' +import { Dom7Array } from 'dom7' +import { IDomEditor, DomEditor } from '@wangeditor/core' +import { ListItemElement, BulletedListElement, NumberedListElement } from './custom-types' + +function parseItemHtml( + $elem: Dom7Array, + children: Descendant[], + editor: IDomEditor +): ListItemElement { + // 无 children ,则用纯文本 + if (children.length === 0) { + children = [{ text: $elem.text().replace(/\s+/gm, ' ') }] + } + + return { + type: 'list-item', + // @ts-ignore + children, + } +} + +export const parseItemHtmlConf = { + selector: 'li', + parseElemHtml: parseItemHtml, +} + +function parseBulletedListHtml( + $elem: Dom7Array, + children: Descendant[], + editor: IDomEditor +): BulletedListElement { + return { + type: 'bulleted-list', + // @ts-ignore + children: children.filter(child => DomEditor.getNodeType(child) === 'list-item'), + } +} + +export const parseBulletedListHtmlConf = { + selector: 'ul', + parseElemHtml: parseBulletedListHtml, +} + +function parseNumberedListHtml( + $elem: Dom7Array, + children: Descendant[], + editor: IDomEditor +): NumberedListElement { + return { + type: 'numbered-list', + // @ts-ignore + children: children.filter(child => DomEditor.getNodeType(child) === 'list-item'), + } +} + +export const parseNumberedListHtmlConf = { + selector: 'ol', + parseElemHtml: parseNumberedListHtml, +} diff --git a/packages/table-module/src/module/index.ts b/packages/table-module/src/module/index.ts index 55a1051ba..983365d9e 100644 --- a/packages/table-module/src/module/index.ts +++ b/packages/table-module/src/module/index.ts @@ -7,6 +7,8 @@ import { IModuleConf } from '@wangeditor/core' import withTable from './plugin' import { renderTableConf, renderTableRowConf, renderTableCellConf } from './render-elem' import { tableToHtmlConf, tableRowToHtmlConf, tableCellToHtmlConf } from './elem-to-html' +import { preParseTableHtmlConf } from './pre-parse-html' +import { parseCellHtmlConf, parseRowHtmlConf, parseTableHtmlConf } from './parse-elem-html' import { insertTableMenuConf, deleteTableMenuConf, @@ -21,6 +23,8 @@ import { const table: Partial = { renderElems: [renderTableConf, renderTableRowConf, renderTableCellConf], elemsToHtml: [tableToHtmlConf, tableRowToHtmlConf, tableCellToHtmlConf], + preParseHtml: [preParseTableHtmlConf], + parseElemsHtml: [parseCellHtmlConf, parseRowHtmlConf, parseTableHtmlConf], menus: [ insertTableMenuConf, deleteTableMenuConf, diff --git a/packages/table-module/src/module/parse-elem-html.ts b/packages/table-module/src/module/parse-elem-html.ts new file mode 100644 index 000000000..e0e5b7818 --- /dev/null +++ b/packages/table-module/src/module/parse-elem-html.ts @@ -0,0 +1,78 @@ +/** + * @description parse html + * @author wangfupeng + */ + +import { Descendant } from 'slate' +import { Dom7Array } from 'dom7' +import { IDomEditor, DomEditor } from '@wangeditor/core' +import { TableCellElement, TableRowElement, TableElement } from './custom-types' +import { getTagName, getStyleValue } from '../utils/dom' + +function parseCellHtml( + $elem: Dom7Array, + children: Descendant[], + editor: IDomEditor +): TableCellElement { + // 无 children ,则用纯文本 + if (children.length === 0) { + children = [{ text: $elem.text().replace(/\s+/gm, ' ') }] + } + + const colSpan = parseInt($elem.attr('colSpan') || '1') + const rowSpan = parseInt($elem.attr('rowSpan') || '1') + + return { + type: 'table-cell', + isHeader: getTagName($elem) === 'th', + colSpan, + rowSpan, + // @ts-ignore + children, + } +} + +export const parseCellHtmlConf = { + selector: 'td,th', + parseElemHtml: parseCellHtml, +} + +function parseRowHtml( + $elem: Dom7Array, + children: Descendant[], + editor: IDomEditor +): TableRowElement { + return { + type: 'table-row', + // @ts-ignore + children: children.filter(child => DomEditor.getNodeType(child) === 'table-cell'), + } +} + +export const parseRowHtmlConf = { + selector: 'tr', + parseElemHtml: parseRowHtml, +} + +function parseTableHtml( + $elem: Dom7Array, + children: Descendant[], + editor: IDomEditor +): TableElement { + // 判断 fullWidth + let fullWidth = false + fullWidth = getStyleValue($elem, 'width') === '100%' + fullWidth = $elem.attr('width') === '100%' // 兼容 V4 + + return { + type: 'table', + fullWidth, + // @ts-ignore + children: children.filter(child => DomEditor.getNodeType(child) === 'table-row'), + } +} + +export const parseTableHtmlConf = { + selector: 'table', + parseElemHtml: parseTableHtml, +} diff --git a/packages/table-module/src/module/pre-parse-html.ts b/packages/table-module/src/module/pre-parse-html.ts new file mode 100644 index 000000000..5c13a3933 --- /dev/null +++ b/packages/table-module/src/module/pre-parse-html.ts @@ -0,0 +1,32 @@ +/** + * @description pre parse html + * @author wangfupeng + */ + +import { Dom7Array } from 'dom7' +import { getTagName } from '../utils/dom' + +/** + * pre-prase table ,去掉 + * @param $table $table + */ +function preParse($table: Dom7Array) { + const tagName = getTagName($table) + if (tagName !== 'table') return $table + + // 没有 则直接返回 + const $tbody = $table.find('tbody') + if ($tbody.length === 0) return $table + + // 去掉 ,把 移动到 下面 + const $tr = $table.find('tr') + $table.append($tr) + $tbody.remove() + + return $table +} + +export const preParseTableHtmlConf = { + selector: 'table', + preParseHtml: preParse, +} diff --git a/packages/table-module/src/utils/dom.ts b/packages/table-module/src/utils/dom.ts index f454ef962..ce0eb2e0e 100644 --- a/packages/table-module/src/utils/dom.ts +++ b/packages/table-module/src/utils/dom.ts @@ -16,6 +16,8 @@ import { removeClass, children, each, + find, + Dom7Array, } from 'dom7' export { Dom7Array } from 'dom7' @@ -30,5 +32,39 @@ $.fn.addClass = addClass $.fn.removeClass = removeClass $.fn.children = children $.fn.each = each +$.fn.find = find export default $ + +/** + * 获取 tagName lower-case + * @param $elem $elem + */ +export function getTagName($elem: Dom7Array): string { + if ($elem.length) return $elem[0].tagName.toLowerCase() + return '' +} + +/** + * 获取 $elem 某一个 style 值 + * @param $elem $elem + * @param styleKey style key + */ +export function getStyleValue($elem: Dom7Array, styleKey: string): string { + let res = '' + + const styleStr = $elem.attr('style') || '' // 如 'line-height: 2.5; color: red;' + const styleArr = styleStr.split(';') // 如 ['line-height: 2.5', ' color: red', ''] + const length = styleArr.length + for (let i = 0; i < length; i++) { + const styleItemStr = styleArr[i] // 如 'line-height: 2.5' + if (styleItemStr) { + const arr = styleItemStr.split(':') // ['line-height', ' 2.5'] + if (arr[0].trim() === styleKey) { + res = arr[1].trim() + } + } + } + + return res +} diff --git a/packages/video-module/src/module/elem-to-html.ts b/packages/video-module/src/module/elem-to-html.ts index 31418a113..28d49e9c0 100644 --- a/packages/video-module/src/module/elem-to-html.ts +++ b/packages/video-module/src/module/elem-to-html.ts @@ -8,7 +8,7 @@ import { VideoElement } from './custom-types' function videoToHtml(elemNode: Element, childrenHtml?: string): string { const { src = '' } = elemNode as VideoElement - let res = '
\n' + let res = '
\n' if (src.trim().indexOf(' = { renderElems: [renderVideoConf], elemsToHtml: [videoToHtmlConf], + preParseHtml: [preParseHtmlConf], + parseElemsHtml: [parseHtmlConf], menus: [insertVideoMenuConf, deleteVideoMenuConf], editorPlugin: withVideo, } diff --git a/packages/video-module/src/module/parse-elem-html.ts b/packages/video-module/src/module/parse-elem-html.ts new file mode 100644 index 000000000..35808fcf2 --- /dev/null +++ b/packages/video-module/src/module/parse-elem-html.ts @@ -0,0 +1,37 @@ +/** + * @description parse html + * @author wangfupeng + */ + +import { Descendant } from 'slate' +import { Dom7Array } from 'dom7' +import { IDomEditor } from '@wangeditor/core' +import { VideoElement } from './custom-types' + +function parseHtml($elem: Dom7Array, children: Descendant[], editor: IDomEditor): VideoElement { + let src = '' + + // \n
' + '
\n\n
' ) }) })