react-native-marked 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/commonjs/lib/Parser.js +74 -53
- package/dist/commonjs/lib/Parser.js.map +1 -1
- package/dist/commonjs/lib/Renderer.js +17 -19
- package/dist/commonjs/lib/Renderer.js.map +1 -1
- package/dist/commonjs/theme/styles.js.map +1 -1
- package/dist/commonjs/utils/handlers.js +19 -0
- package/dist/commonjs/utils/handlers.js.map +1 -0
- package/dist/module/lib/Parser.js +74 -53
- package/dist/module/lib/Parser.js.map +1 -1
- package/dist/module/lib/Renderer.js +9 -8
- package/dist/module/lib/Renderer.js.map +1 -1
- package/dist/module/theme/styles.js.map +1 -1
- package/dist/module/utils/handlers.js +9 -0
- package/dist/module/utils/handlers.js.map +1 -0
- package/dist/typescript/lib/Markdown.d.ts +1 -1
- package/dist/typescript/lib/Parser.d.ts +4 -3
- package/dist/typescript/lib/Renderer.d.ts +6 -7
- package/dist/typescript/utils/handlers.d.ts +1 -0
- package/package.json +8 -1
- package/src/lib/Parser.tsx +96 -56
- package/src/lib/Renderer.tsx +20 -15
- package/src/theme/styles.ts +3 -1
- package/src/utils/handlers.ts +9 -0
package/src/lib/Parser.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
1
2
|
import type { marked } from 'marked';
|
|
2
3
|
import Renderer from './Renderer';
|
|
3
4
|
import type { MarkedStyles } from '../theme/types';
|
|
@@ -21,35 +22,17 @@ class Parser {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
parse(tokens: marked.Token[]) {
|
|
24
|
-
const elements:
|
|
25
|
+
const elements: ReactNode[] = tokens.map((token) => {
|
|
25
26
|
switch (token.type) {
|
|
26
27
|
case 'space': {
|
|
27
28
|
return null;
|
|
28
29
|
}
|
|
29
30
|
case 'paragraph': {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (t.type === 'image') {
|
|
35
|
-
paragraphChildren.push(
|
|
36
|
-
this.renderer.getTextNode(
|
|
37
|
-
this.parseInline(tempTokens),
|
|
38
|
-
this.styles.text
|
|
39
|
-
)
|
|
40
|
-
);
|
|
41
|
-
paragraphChildren.push(this.parseInline([t]));
|
|
42
|
-
tempTokens = [];
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
tempTokens = [...tempTokens, t];
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
if (tempTokens.length > 0) {
|
|
49
|
-
paragraphChildren.push(
|
|
50
|
-
this.renderer.getTextNode(this.parseInline(tempTokens), {})
|
|
31
|
+
const paragraphChildren =
|
|
32
|
+
this.getNormalizedSiblingNodesForBlockAndInlineTokens(
|
|
33
|
+
token.tokens,
|
|
34
|
+
this.styles.text
|
|
51
35
|
);
|
|
52
|
-
}
|
|
53
36
|
|
|
54
37
|
return this.renderer.getViewNode(
|
|
55
38
|
paragraphChildren,
|
|
@@ -64,7 +47,10 @@ class Parser {
|
|
|
64
47
|
}
|
|
65
48
|
case 'heading': {
|
|
66
49
|
const styles = this.headingStylesMap[token.depth] ?? this.styles.text;
|
|
67
|
-
return this.renderer.getTextNode(
|
|
50
|
+
return this.renderer.getTextNode(
|
|
51
|
+
this.parseInline(token.tokens, styles),
|
|
52
|
+
styles
|
|
53
|
+
);
|
|
68
54
|
}
|
|
69
55
|
case 'code': {
|
|
70
56
|
return this.renderer.getCodeBlockNode(
|
|
@@ -78,19 +64,22 @@ class Parser {
|
|
|
78
64
|
}
|
|
79
65
|
case 'list': {
|
|
80
66
|
const li = token.items.map((item) => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
this.styles.li
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
67
|
+
const children = item.tokens.map((cItem) => {
|
|
68
|
+
if (cItem.type === 'text') {
|
|
69
|
+
/* getViewNode since tokens could contain a block like elements (i.e. img) */
|
|
70
|
+
const listChildren =
|
|
71
|
+
this.getNormalizedSiblingNodesForBlockAndInlineTokens(
|
|
72
|
+
// @ts-ignore
|
|
73
|
+
cItem.tokens,
|
|
74
|
+
this.styles.li
|
|
75
|
+
);
|
|
76
|
+
return this.renderer.getViewNode(listChildren, this.styles.li);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Parse the nested token */
|
|
80
|
+
return this.parse([cItem]);
|
|
81
|
+
});
|
|
82
|
+
return this.renderer.getViewNode(children, this.styles.li);
|
|
94
83
|
});
|
|
95
84
|
return this.renderer.getListNode(
|
|
96
85
|
token.ordered,
|
|
@@ -99,51 +88,63 @@ class Parser {
|
|
|
99
88
|
this.styles.li
|
|
100
89
|
);
|
|
101
90
|
}
|
|
102
|
-
case 'html': {
|
|
103
|
-
return this.renderer.getTextNode(token.text.trim(), this.styles.text);
|
|
104
|
-
}
|
|
105
91
|
default:
|
|
106
|
-
|
|
107
|
-
return null;
|
|
92
|
+
return this.parseInline([token]);
|
|
108
93
|
}
|
|
109
94
|
});
|
|
110
|
-
return elements;
|
|
95
|
+
return elements.filter((element) => element !== null);
|
|
111
96
|
}
|
|
112
97
|
|
|
113
98
|
parseInline(tokens: marked.Token[], styles?: CustomStyleProp) {
|
|
114
|
-
const elements:
|
|
99
|
+
const elements: ReactNode[] = tokens.map((token) => {
|
|
115
100
|
if (!token) return null;
|
|
116
101
|
|
|
117
102
|
switch (token.type) {
|
|
118
103
|
case 'escape': {
|
|
119
104
|
return this.renderer.getTextNode(token.text, {
|
|
120
|
-
...styles,
|
|
121
105
|
...this.styles.text,
|
|
106
|
+
...styles,
|
|
122
107
|
});
|
|
123
108
|
}
|
|
124
109
|
case 'link': {
|
|
125
|
-
|
|
110
|
+
const linkStyle = {
|
|
126
111
|
...styles,
|
|
127
|
-
...this.styles.link,
|
|
128
|
-
}
|
|
112
|
+
...this.styles.link, // To override color property
|
|
113
|
+
};
|
|
114
|
+
return this.renderer.getLinkNode(
|
|
115
|
+
this.parseInline(token.tokens, linkStyle),
|
|
116
|
+
token.href,
|
|
117
|
+
linkStyle
|
|
118
|
+
);
|
|
129
119
|
}
|
|
130
120
|
case 'image': {
|
|
131
121
|
return this.renderer.getImageNode(token.href);
|
|
132
122
|
}
|
|
133
123
|
case 'strong': {
|
|
124
|
+
const boldStyle = {
|
|
125
|
+
...this.styles.strong,
|
|
126
|
+
...styles,
|
|
127
|
+
};
|
|
134
128
|
return this.renderer.getTextNode(
|
|
135
|
-
this.parseInline(token.tokens,
|
|
136
|
-
|
|
129
|
+
this.parseInline(token.tokens, boldStyle),
|
|
130
|
+
boldStyle
|
|
137
131
|
);
|
|
138
132
|
}
|
|
139
133
|
case 'em': {
|
|
134
|
+
const italicStyle = {
|
|
135
|
+
...this.styles.em,
|
|
136
|
+
...styles,
|
|
137
|
+
};
|
|
140
138
|
return this.renderer.getTextNode(
|
|
141
|
-
this.parseInline(token.tokens,
|
|
142
|
-
|
|
139
|
+
this.parseInline(token.tokens, italicStyle),
|
|
140
|
+
italicStyle
|
|
143
141
|
);
|
|
144
142
|
}
|
|
145
143
|
case 'codespan': {
|
|
146
|
-
return this.renderer.getTextNode(token.text,
|
|
144
|
+
return this.renderer.getTextNode(token.text, {
|
|
145
|
+
...this.styles.codespan,
|
|
146
|
+
...styles,
|
|
147
|
+
});
|
|
147
148
|
}
|
|
148
149
|
case 'br': {
|
|
149
150
|
return this.renderer.getTextNode('\n', {});
|
|
@@ -151,10 +152,15 @@ class Parser {
|
|
|
151
152
|
case 'del': {
|
|
152
153
|
return null;
|
|
153
154
|
}
|
|
154
|
-
case 'text':
|
|
155
|
+
case 'text':
|
|
156
|
+
case 'html': {
|
|
157
|
+
if (token.raw.trim().length < 1) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
155
161
|
return this.renderer.getTextNode(token.raw, {
|
|
156
|
-
...styles,
|
|
157
162
|
...this.styles.text,
|
|
163
|
+
...styles,
|
|
158
164
|
});
|
|
159
165
|
}
|
|
160
166
|
default: {
|
|
@@ -163,8 +169,42 @@ class Parser {
|
|
|
163
169
|
}
|
|
164
170
|
}
|
|
165
171
|
});
|
|
166
|
-
return elements;
|
|
172
|
+
return elements.filter((element) => element !== null);
|
|
167
173
|
}
|
|
174
|
+
|
|
175
|
+
private getNormalizedSiblingNodesForBlockAndInlineTokens = (
|
|
176
|
+
tokens: marked.Token[],
|
|
177
|
+
textStyle: TextStyleProp
|
|
178
|
+
): ReactNode[] => {
|
|
179
|
+
let tempTokens: marked.Token[] = [];
|
|
180
|
+
const siblingNodes: ReactNode[] = [];
|
|
181
|
+
tokens.forEach((t) => {
|
|
182
|
+
/**
|
|
183
|
+
* To avoid inlining images
|
|
184
|
+
* Note: to be extend for other token types
|
|
185
|
+
*/
|
|
186
|
+
if (t.type === 'image') {
|
|
187
|
+
const parsed = this.parseInline(tempTokens);
|
|
188
|
+
if (parsed.length > 0) {
|
|
189
|
+
siblingNodes.push(
|
|
190
|
+
this.renderer.getTextNode(this.parseInline(tempTokens), textStyle)
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
siblingNodes.push(this.parseInline([t]));
|
|
194
|
+
tempTokens = [];
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
tempTokens = [...tempTokens, t];
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
/* Remaining temp tokens if any */
|
|
201
|
+
if (tempTokens.length > 0) {
|
|
202
|
+
siblingNodes.push(
|
|
203
|
+
this.renderer.getTextNode(this.parseInline(tempTokens), {})
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
return siblingNodes;
|
|
207
|
+
};
|
|
168
208
|
}
|
|
169
209
|
|
|
170
210
|
export default Parser;
|
package/src/lib/Renderer.tsx
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { ScrollView, View, Text } from 'react-native';
|
|
3
3
|
import MarkedList from '@jsamr/react-native-li';
|
|
4
4
|
import Disc from '@jsamr/counter-style/presets/disc';
|
|
5
5
|
import Decimal from '@jsamr/counter-style/presets/decimal';
|
|
6
6
|
import MDImage from './../components/MDImage';
|
|
7
7
|
import { generateRandomString } from '../utils/string';
|
|
8
8
|
import type { TextStyleProp, ViewStyleProp } from './types';
|
|
9
|
+
import { onLinkPress } from '../utils/handlers';
|
|
9
10
|
|
|
10
11
|
class Renderer {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
getTextNode = (
|
|
14
|
-
children: string | React.ReactNode[],
|
|
15
|
-
styles: TextStyleProp
|
|
16
|
-
) => {
|
|
12
|
+
getTextNode = (children: string | ReactNode[], styles: TextStyleProp) => {
|
|
17
13
|
return (
|
|
18
14
|
<Text key={generateRandomString()} style={styles}>
|
|
19
15
|
{children}
|
|
@@ -21,19 +17,23 @@ class Renderer {
|
|
|
21
17
|
);
|
|
22
18
|
};
|
|
23
19
|
|
|
24
|
-
getLinkNode = (
|
|
20
|
+
getLinkNode = (
|
|
21
|
+
children: string | ReactNode[],
|
|
22
|
+
href: string,
|
|
23
|
+
styles: TextStyleProp
|
|
24
|
+
) => {
|
|
25
25
|
return (
|
|
26
26
|
<Text
|
|
27
27
|
key={generateRandomString()}
|
|
28
|
-
onPress={
|
|
28
|
+
onPress={onLinkPress(href)}
|
|
29
29
|
style={styles}
|
|
30
30
|
>
|
|
31
|
-
{
|
|
31
|
+
{children}
|
|
32
32
|
</Text>
|
|
33
33
|
);
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
getViewNode(children:
|
|
36
|
+
getViewNode(children: ReactNode[] | null, styles: ViewStyleProp) {
|
|
37
37
|
return (
|
|
38
38
|
<View key={generateRandomString()} style={styles}>
|
|
39
39
|
{children}
|
|
@@ -47,13 +47,17 @@ class Renderer {
|
|
|
47
47
|
textStyle: TextStyleProp
|
|
48
48
|
) {
|
|
49
49
|
return (
|
|
50
|
-
<ScrollView
|
|
50
|
+
<ScrollView
|
|
51
|
+
key={generateRandomString()}
|
|
52
|
+
contentContainerStyle={containerStyle}
|
|
53
|
+
horizontal
|
|
54
|
+
>
|
|
51
55
|
<Text style={textStyle}>{text}</Text>
|
|
52
56
|
</ScrollView>
|
|
53
57
|
);
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
getBlockquoteNode(children:
|
|
60
|
+
getBlockquoteNode(children: ReactNode[], styles: ViewStyleProp) {
|
|
57
61
|
return (
|
|
58
62
|
<View key={generateRandomString()} style={styles}>
|
|
59
63
|
{children}
|
|
@@ -67,7 +71,7 @@ class Renderer {
|
|
|
67
71
|
|
|
68
72
|
getListNode(
|
|
69
73
|
ordered: boolean,
|
|
70
|
-
li:
|
|
74
|
+
li: ReactNode[],
|
|
71
75
|
listStyle: ViewStyleProp,
|
|
72
76
|
textStyle: TextStyleProp
|
|
73
77
|
) {
|
|
@@ -76,6 +80,7 @@ class Renderer {
|
|
|
76
80
|
counterRenderer={ordered ? Decimal : Disc}
|
|
77
81
|
markerTextStyle={textStyle}
|
|
78
82
|
markerBoxStyle={listStyle}
|
|
83
|
+
key={generateRandomString()}
|
|
79
84
|
>
|
|
80
85
|
{li.map((node) => node)}
|
|
81
86
|
</MarkedList>
|
package/src/theme/styles.ts
CHANGED