diff --git a/src/components/editor/editor.tsx b/src/components/editor/editor.tsx index 4b6d915ee..53d4b0faa 100644 --- a/src/components/editor/editor.tsx +++ b/src/components/editor/editor.tsx @@ -12,7 +12,22 @@ import { TaskBar } from './task-bar/task-bar' const Editor: React.FC = () => { const editorMode: EditorMode = useSelector((state: ApplicationState) => state.editorConfig.editorMode) - const [markdownContent, setMarkdownContent] = useState('# Embedding demo\n\n## Slideshare\n{%slideshare mazlan1/internet-of-things-the-tip-of-an-iceberg %}\n\n## Gist\nhttps://gist.github.com/schacon/1\n\n## YouTube\nhttps://www.youtube.com/watch?v=KgMpKsp23yY\n\n## Vimeo\nhttps://vimeo.com/23237102\n\n## PDF\n{%pdf https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf %}') + const [markdownContent, setMarkdownContent] = useState(` +# Embedding demo +## Slideshare +{%slideshare mazlan1/internet-of-things-the-tip-of-an-iceberg %} + +## Gist +https://gist.github.com/schacon/1 + +## YouTube +https://www.youtube.com/watch?v=KgMpKsp23yY + +## Vimeo +https://vimeo.com/23237102 + +## PDF +{%pdf https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf %}`) const isWide = useMedia({ minWidth: 576 }) const [firstDraw, setFirstDraw] = useState(true) diff --git a/src/components/editor/markdown-preview/markdown-preview.tsx b/src/components/editor/markdown-preview/markdown-preview.tsx index 4425f8486..1cc1874b7 100644 --- a/src/components/editor/markdown-preview/markdown-preview.tsx +++ b/src/components/editor/markdown-preview/markdown-preview.tsx @@ -1,15 +1,16 @@ +import { DomElement } from 'domhandler' import MarkdownIt from 'markdown-it' import abbreviation from 'markdown-it-abbr' import markdownItContainer from 'markdown-it-container' import definitionList from 'markdown-it-deflist' import emoji from 'markdown-it-emoji' +import footnote from 'markdown-it-footnote' import inserted from 'markdown-it-ins' import marked from 'markdown-it-mark' import markdownItRegex from 'markdown-it-regex' import subscript from 'markdown-it-sub' import superscript from 'markdown-it-sup' import taskList from 'markdown-it-task-lists' -import footnote from 'markdown-it-footnote' import React, { ReactElement, useMemo } from 'react' import ReactHtmlParser, { convertNodeToElement, Transform } from 'react-html-parser' import { createRenderContainer, validAlertLevels } from './container-plugins/alert' @@ -33,13 +34,25 @@ export interface MarkdownPreviewProps { content: string } +export type ComponentReplacer = (node: DomElement, counterMap: Map) => (ReactElement | undefined); +const allComponentReplacers: ComponentReplacer[] = [getYouTubeReplacement, getVimeoReplacement, getGistReplacement, getPDFReplacement] +type ComponentReplacer2Identifier2CounterMap = Map> + +const tryToReplaceNode = (node: DomElement, componentReplacer2Identifier2CounterMap: ComponentReplacer2Identifier2CounterMap) => { + return allComponentReplacers + .map((componentReplacer) => { + const identifier2CounterMap = componentReplacer2Identifier2CounterMap.get(componentReplacer) || new Map() + return componentReplacer(node, identifier2CounterMap) + }) + .find((replacement) => !!replacement) +} + const MarkdownPreview: React.FC = ({ content }) => { const markdownIt = useMemo(() => { const md = new MarkdownIt('default', { html: true, breaks: true, langPrefix: '', - linkify: false, typographer: true }) md.use(taskList) @@ -70,36 +83,12 @@ const MarkdownPreview: React.FC = ({ content }) => { }, []) const result: ReactElement[] = useMemo(() => { - const youtubeIdCounterMap = new Map() - const vimeoIdCounterMap = new Map() - const gistIdCounterMap = new Map() - + const componentReplacer2Identifier2CounterMap = new Map>() const html: string = markdownIt.render(content) const transform: Transform = (node, index) => { - const resultYT = getYouTubeReplacement(node, youtubeIdCounterMap) - if (resultYT) { - return resultYT - } - - const resultVimeo = getVimeoReplacement(node, vimeoIdCounterMap) - if (resultVimeo) { - return resultVimeo - } - - const resultGist = getGistReplacement(node, gistIdCounterMap) - if (resultGist) { - return resultGist - } - - const resultPdf = getPDFReplacement(node, gistIdCounterMap) - if (resultPdf) { - return resultPdf - } - - return convertNodeToElement(node, index, transform) + return tryToReplaceNode(node, componentReplacer2Identifier2CounterMap) || convertNodeToElement(node, index, transform) } - const ret: ReactElement[] = ReactHtmlParser(html, { transform: transform }) - return ret + return ReactHtmlParser(html, { transform: transform }) }, [content, markdownIt]) return ( diff --git a/src/components/editor/markdown-preview/replace-components/gist/gist-frame.tsx b/src/components/editor/markdown-preview/replace-components/gist/gist-frame.tsx index e6d7a6897..b6184ca9b 100644 --- a/src/components/editor/markdown-preview/replace-components/gist/gist-frame.tsx +++ b/src/components/editor/markdown-preview/replace-components/gist/gist-frame.tsx @@ -1,5 +1,5 @@ -import { DomElement } from 'domhandler' -import React, { ReactElement, useCallback, useEffect, useMemo, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' +import { ComponentReplacer } from '../../markdown-preview' import { OneClickEmbedding } from '../one-click-frame/one-click-embedding' import { getIdFromCodiMdTag } from '../video-util' import './gist-frame.scss' @@ -14,7 +14,7 @@ interface resizeEvent { id: string } -const getElementReplacement = (node: DomElement, counterMap: Map): (ReactElement | undefined) => { +const getElementReplacement:ComponentReplacer = (node, counterMap) => { const gistId = getIdFromCodiMdTag(node, 'gist') if (gistId) { const count = (counterMap.get(gistId) || 0) + 1 diff --git a/src/components/editor/markdown-preview/replace-components/vimeo/vimeo-frame.tsx b/src/components/editor/markdown-preview/replace-components/vimeo/vimeo-frame.tsx index e03de16b0..2d78897cf 100644 --- a/src/components/editor/markdown-preview/replace-components/vimeo/vimeo-frame.tsx +++ b/src/components/editor/markdown-preview/replace-components/vimeo/vimeo-frame.tsx @@ -1,9 +1,9 @@ -import { DomElement } from 'domhandler' -import React, { ReactElement, useCallback } from 'react' +import React, { useCallback } from 'react' +import { ComponentReplacer } from '../../markdown-preview' import { OneClickEmbedding } from '../one-click-frame/one-click-embedding' import { getIdFromCodiMdTag, VideoFrameProps } from '../video-util' -const getElementReplacement = (node: DomElement, counterMap: Map): (ReactElement | undefined) => { +const getElementReplacement:ComponentReplacer = (node, counterMap) => { const videoId = getIdFromCodiMdTag(node, 'vimeo') if (videoId) { const count = (counterMap.get(videoId) || 0) + 1 diff --git a/src/components/editor/markdown-preview/replace-components/youtube/youtube-frame.tsx b/src/components/editor/markdown-preview/replace-components/youtube/youtube-frame.tsx index 9ceca4f43..03d9c9a9f 100644 --- a/src/components/editor/markdown-preview/replace-components/youtube/youtube-frame.tsx +++ b/src/components/editor/markdown-preview/replace-components/youtube/youtube-frame.tsx @@ -1,9 +1,9 @@ -import { DomElement } from 'domhandler' -import React, { ReactElement } from 'react' +import React from 'react' +import { ComponentReplacer } from '../../markdown-preview' import { OneClickEmbedding } from '../one-click-frame/one-click-embedding' import { getIdFromCodiMdTag, VideoFrameProps } from '../video-util' -const getElementReplacement = (node: DomElement, counterMap: Map): (ReactElement | undefined) => { +const getElementReplacement: ComponentReplacer = (node, counterMap) => { const videoId = getIdFromCodiMdTag(node, 'youtube') if (videoId) { const count = (counterMap.get(videoId) || 0) + 1 @@ -14,7 +14,9 @@ const getElementReplacement = (node: DomElement, counterMap: Map export const YouTubeFrame: React.FC = ({ id }) => { return ( - +