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
|
import React from 'react';
interface PropTypes {
data: string;
}
type RuleName = 'bold' | 'italic' | 'inlineCode' | 'strikeThrough';
interface RuleProperties {
enclosure: string;
style: React.CSSProperties;
pattern?: RegExp;
}
const rules: Record<RuleName, RuleProperties>= { // Order matters - lowest property has highest priority
strikeThrough: {
enclosure: '~~',
style: { textDecoration: 'line-through' },
},
inlineCode: {
enclosure: '`',
style: { background: '#444444', padding: '4px' },
},
italic: {
enclosure: '\\*',
style: { fontStyle: 'italic' },
},
bold: {
enclosure: '\\*\\*',
style: { fontWeight: 'bold' },
},
};
const ruleNames = Object.keys(rules) as RuleName[];
const capture = (enclosure: string): RegExp => {
return new RegExp(enclosure + '([^' + enclosure + ']+)' + enclosure);
}
const captureSplit = (enclosure: string): string => {
return '(' + enclosure + '[^' + enclosure + ']+' + enclosure + ')';
}
const ruleSplitPatterns: string[] = [];
ruleNames.forEach(name => {
rules[name].pattern = capture(rules[name].enclosure);
ruleSplitPatterns.push(captureSplit(rules[name].enclosure));
});
const splitter = new RegExp(ruleSplitPatterns.join('|'));
const SyntaxSpan: React.FC<PropTypes> = ({ data }) => {
if (!data) return null;
let span = <>{data}</>;
ruleNames.forEach(name => {
const rule = rules[name];
const match = data.match(rule.pattern || '');
if (match) span = <span style={rule.style}>{match[1]}</span>;
});
return span;
}
const Paragraph: React.FC<PropTypes> = ({ data }) => {
const result = data.split(splitter).map(span => <SyntaxSpan data={span} />);
return <p> {result} </p>;
}
export default Paragraph;
|