This repository was archived by the owner on May 16, 2025. It is now read-only.
Repository navigation
Expand file tree
/
Copy pathMarkdownView.js
More file actions
91 lines (74 loc) · 3.19 KB
/
Copy pathMarkdownView.js
File metadata and controls
91 lines (74 loc) · 3.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/* @flow */
import React, {memo, useMemo} from 'react'
import SimpleMarkdown from 'simple-markdown'
import DefaultRenders from './renders'
import DefaultStyles from './styles'
import type {ImageNode, Rules, Styles} from './types'
function simpleMarkdownRule(rule, styles) {
const {render, ...properties} = rule
const reactRender = render ? {react: (node, output, state) => render(node, output, state, styles)} : null
return {...properties, ...reactRender}
}
function simpleMarkdownRules(rules, styles) {
const markdownRules = {}
Object.keys(rules).forEach(nodeKey => markdownRules[nodeKey] = simpleMarkdownRule(rules[nodeKey], styles))
return markdownRules
}
function mergeStyles(baseStyles, styles) {
const mergedStyles = {...styles}
Object.keys(baseStyles).forEach(nodeKey => mergedStyles[nodeKey] = styles[nodeKey] ? [baseStyles[nodeKey], styles[nodeKey]] : baseStyles[nodeKey])
return mergedStyles
}
function mergeRules(baseRules, rules) {
const mergedRules = {...rules}
Object.keys(baseRules).forEach(nodeKey => mergedRules[nodeKey] = {...baseRules[nodeKey], ...rules[nodeKey]})
return mergedRules
}
const IMAGE_LINK = "(?:\\[[^\\]]*\\]|[^\\[\\]]|\\](?=[^\\[]*\\]))*";
const IMAGE_HREF_AND_TITLE = "\\s*<?((?:[^\\s\\\\]|\\\\.)*?)>?(?:\\s+['\"]([\\s\\S]*?)['\"])?"
const IMAGE_SIZE = "(?:\\s+=([0-9]+)?x([0-9]+)?)?\\)\\s*"
const inlineRegex = (regex) => ((source, state) => state.inline ? regex.exec(source) : null)
const unescapeUrl = (url) => url.replace(/\\([^0-9A-Za-z\s])/g, '$1')
const DefaultRules : Rules = Object.freeze(mergeRules(
Object.assign(
{},
...Object.entries(DefaultRenders).map(([nodeKey, render]) => ({[nodeKey]: {render: render}}))
),
{
heading: {
match: SimpleMarkdown.blockRegex(/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n *)*\n/),
},
image: {
match: inlineRegex(new RegExp("^!\\[(" + IMAGE_LINK + ")\\]\\(" + IMAGE_HREF_AND_TITLE + IMAGE_SIZE)),
parse: (capture, parse, state): ImageNode => ({
alt: capture[1],
target: unescapeUrl(capture[2]),
title: capture[3],
width: capture[4] ? parseInt(capture[4]) : undefined,
height: capture[5] ? parseInt(capture[5]) : undefined,
})
}
}
))
const emptyObject = {};
const MarkdownView = ({rules = emptyObject, onLinkPress, changeParsingResult, styles = emptyObject, children}: {
rules?: Rules,
onLinkPress?: (string) => void,
changeParsingResult?: (any) => any,
styles?: Styles,
children: string,
}) => {
return useMemo(() => {
const mergedStyles = mergeStyles(DefaultStyles, styles);
const mergedRules = mergeRules(SimpleMarkdown.defaultRules, simpleMarkdownRules(mergeRules(DefaultRules, rules), mergedStyles));
const markdown = (Array.isArray(children) ? children.join('') : children) + '\n\n'
let ast = SimpleMarkdown.parserFor(mergedRules)(markdown, {inline: false})
if(changeParsingResult) {
ast = changeParsingResult(ast);
}
const render = SimpleMarkdown.reactFor(SimpleMarkdown.ruleOutput(mergedRules, 'react'))
const initialRenderState = {onLinkPress: onLinkPress};
return render(ast, initialRenderState);
}, [styles, rules, onLinkPress, children, changeParsingResult]);
}
export default memo(MarkdownView);