react-native-marked 1.0.0-alpha.3 → 1.0.2

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.
Files changed (55) hide show
  1. package/README.md +7 -5
  2. package/dist/commonjs/components/MDImage.js +5 -22
  3. package/dist/commonjs/components/MDImage.js.map +1 -1
  4. package/dist/commonjs/lib/Markdown.js +19 -11
  5. package/dist/commonjs/lib/Markdown.js.map +1 -1
  6. package/dist/commonjs/lib/Parser.js +49 -130
  7. package/dist/commonjs/lib/Parser.js.map +1 -1
  8. package/dist/commonjs/lib/Renderer.js +19 -13
  9. package/dist/commonjs/lib/Renderer.js.map +1 -1
  10. package/dist/commonjs/theme/colors.js +25 -0
  11. package/dist/commonjs/theme/colors.js.map +1 -0
  12. package/dist/commonjs/theme/styles.js +113 -0
  13. package/dist/commonjs/theme/styles.js.map +1 -0
  14. package/dist/commonjs/theme/theme.js +19 -0
  15. package/dist/commonjs/theme/theme.js.map +1 -0
  16. package/dist/commonjs/{types → theme}/types.js +0 -0
  17. package/dist/commonjs/theme/types.js.map +1 -0
  18. package/dist/module/components/MDImage.js +6 -22
  19. package/dist/module/components/MDImage.js.map +1 -1
  20. package/dist/module/lib/Markdown.js +18 -12
  21. package/dist/module/lib/Markdown.js.map +1 -1
  22. package/dist/module/lib/Parser.js +50 -129
  23. package/dist/module/lib/Parser.js.map +1 -1
  24. package/dist/module/lib/Renderer.js +16 -13
  25. package/dist/module/lib/Renderer.js.map +1 -1
  26. package/dist/module/theme/colors.js +18 -0
  27. package/dist/module/theme/colors.js.map +1 -0
  28. package/dist/module/theme/styles.js +102 -0
  29. package/dist/module/theme/styles.js.map +1 -0
  30. package/dist/module/theme/theme.js +7 -0
  31. package/dist/module/theme/theme.js.map +1 -0
  32. package/dist/module/{types → theme}/types.js +0 -0
  33. package/dist/module/theme/types.js.map +1 -0
  34. package/dist/typescript/components/MDImage.d.ts +1 -2
  35. package/dist/typescript/lib/Markdown.d.ts +6 -5
  36. package/dist/typescript/lib/Parser.d.ts +7 -5
  37. package/dist/typescript/lib/Renderer.d.ts +6 -6
  38. package/dist/typescript/theme/colors.d.ts +3 -0
  39. package/dist/typescript/theme/styles.d.ts +4 -0
  40. package/dist/typescript/theme/theme.d.ts +3 -0
  41. package/dist/typescript/theme/types.d.ts +25 -0
  42. package/package.json +15 -9
  43. package/src/components/MDImage.tsx +5 -28
  44. package/src/lib/Markdown.tsx +28 -10
  45. package/src/lib/Parser.tsx +177 -0
  46. package/src/lib/Renderer.tsx +28 -13
  47. package/src/theme/colors.ts +20 -0
  48. package/src/theme/styles.ts +117 -0
  49. package/src/theme/theme.ts +9 -0
  50. package/src/theme/types.ts +28 -0
  51. package/dist/commonjs/types/types.js.map +0 -1
  52. package/dist/module/types/types.js.map +0 -1
  53. package/dist/typescript/types/types.d.ts +0 -23
  54. package/src/lib/Parser.ts +0 -256
  55. package/src/types/types.ts +0 -25
@@ -0,0 +1,177 @@
1
+ import type { marked } from 'marked';
2
+ import type { ImageStyle, StyleProp, TextStyle, ViewStyle } from 'react-native';
3
+ import Renderer from './Renderer';
4
+ import type { MarkedStyles } from '../theme/types';
5
+
6
+ interface ParserOptions {
7
+ styles?: MarkedStyles;
8
+ }
9
+
10
+ class Parser {
11
+ private renderer;
12
+ private styles: MarkedStyles;
13
+ private headingStylesMap: Record<number, TextStyle | undefined>;
14
+ constructor(options: ParserOptions) {
15
+ this.styles = { ...options.styles };
16
+ this.renderer = new Renderer();
17
+ this.headingStylesMap = {
18
+ 1: this.styles.h1,
19
+ 2: this.styles.h2,
20
+ 3: this.styles.h3,
21
+ 4: this.styles.h4,
22
+ 5: this.styles.h5,
23
+ 6: this.styles.h6,
24
+ };
25
+ }
26
+
27
+ parse(tokens: marked.Token[]) {
28
+ const elements: React.ReactNode[] = tokens.map((token) => {
29
+ switch (token.type) {
30
+ case 'space': {
31
+ return null;
32
+ }
33
+ case 'paragraph': {
34
+ let tempTokens: marked.Token[] = [];
35
+ const paragraphChildren: React.ReactNode[] = [];
36
+ token.tokens.forEach((t) => {
37
+ if (t.type === 'image') {
38
+ paragraphChildren.push(
39
+ this.renderer.getTextNode(
40
+ this.parseInline(tempTokens),
41
+ this.styles.text
42
+ )
43
+ );
44
+ paragraphChildren.push(this.parseInline([t]));
45
+ tempTokens = [];
46
+ return;
47
+ }
48
+ tempTokens = [...tempTokens, t];
49
+ });
50
+
51
+ if (tempTokens.length > 0) {
52
+ paragraphChildren.push(
53
+ this.renderer.getTextNode(this.parseInline(tempTokens), {})
54
+ );
55
+ }
56
+
57
+ return this.renderer.getViewNode(
58
+ paragraphChildren,
59
+ this.styles.paragraph
60
+ );
61
+ }
62
+ case 'blockquote': {
63
+ return this.renderer.getBlockquoteNode(
64
+ this.parse(token.tokens),
65
+ this.styles.blockquote
66
+ );
67
+ }
68
+ case 'heading': {
69
+ const styles = this.headingStylesMap[token.depth] ?? this.styles.text;
70
+ return this.renderer.getTextNode(token.text, styles);
71
+ }
72
+ case 'code': {
73
+ return this.renderer.getCodeBlockNode(
74
+ token.text,
75
+ this.styles.code,
76
+ this.styles.em
77
+ );
78
+ }
79
+ case 'hr': {
80
+ return this.renderer.getViewNode(null, this.styles.hr);
81
+ }
82
+ case 'list': {
83
+ const li = token.items.map((item) => {
84
+ if (
85
+ item.tokens.length > 0 &&
86
+ item.tokens[0]?.type === 'text' &&
87
+ // @ts-ignore
88
+ Array.isArray(item.tokens[0]?.tokens)
89
+ ) {
90
+ return this.renderer.getTextNode(
91
+ // @ts-ignore
92
+ this.parseInline(item.tokens[0]?.tokens),
93
+ this.styles.li
94
+ );
95
+ }
96
+ return this.renderer.getTextNode(item.text, this.styles.li);
97
+ });
98
+ return this.renderer.getListNode(
99
+ token.ordered,
100
+ li,
101
+ this.styles.list,
102
+ this.styles.li
103
+ );
104
+ }
105
+ default:
106
+ console.warn(`Token with '${token.type}' type was not found.`);
107
+ return null;
108
+ }
109
+ });
110
+ return elements;
111
+ }
112
+
113
+ parseInline(
114
+ tokens: marked.Token[],
115
+ styles: StyleProp<ViewStyle | TextStyle | ImageStyle> = {}
116
+ ) {
117
+ const elements: React.ReactNode[] = tokens.map((token) => {
118
+ if (!token) return null;
119
+
120
+ switch (token.type) {
121
+ case 'escape': {
122
+ return this.renderer.getTextNode(token.text, [
123
+ styles,
124
+ this.styles.text,
125
+ ]);
126
+ }
127
+ case 'html': {
128
+ console.log('html', token);
129
+ return null;
130
+ }
131
+ case 'link': {
132
+ return this.renderer.getLinkNode(token.text, token.href, [
133
+ styles,
134
+ this.styles.link,
135
+ ]);
136
+ }
137
+ case 'image': {
138
+ return this.renderer.getImageNode(token.href);
139
+ }
140
+ case 'strong': {
141
+ return this.renderer.getTextNode(
142
+ this.parseInline(token.tokens, this.styles.strong),
143
+ [styles, this.styles.strong]
144
+ );
145
+ }
146
+ case 'em': {
147
+ return this.renderer.getTextNode(
148
+ this.parseInline(token.tokens, this.styles.em),
149
+ [styles, this.styles.em]
150
+ );
151
+ }
152
+ case 'codespan': {
153
+ return this.renderer.getTextNode(token.text, this.styles.codespan);
154
+ }
155
+ case 'br': {
156
+ return this.renderer.getTextNode('\n', {});
157
+ }
158
+ case 'del': {
159
+ return null;
160
+ }
161
+ case 'text': {
162
+ return this.renderer.getTextNode(token.raw, [
163
+ styles,
164
+ this.styles.text,
165
+ ]);
166
+ }
167
+ default: {
168
+ console.warn(`Token with '${token.type}' type was not found.`);
169
+ return null;
170
+ }
171
+ }
172
+ });
173
+ return elements;
174
+ }
175
+ }
176
+
177
+ export default Parser;
@@ -1,8 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import { Linking, ScrollView, View, ViewStyle } from 'react-native';
3
- import type { StyleProp, TextStyle } from 'react-native';
4
- import { Text } from 'react-native';
3
+ import { Text, StyleProp, TextStyle } from 'react-native';
4
+ import MarkedList from '@jsamr/react-native-li';
5
+ import Disc from '@jsamr/counter-style/presets/disc';
6
+ import Decimal from '@jsamr/counter-style/presets/decimal';
5
7
  import MDImage from './../components/MDImage';
8
+
6
9
  import { generateRandomString } from '../utils/string';
7
10
 
8
11
  class Renderer {
@@ -31,7 +34,10 @@ class Renderer {
31
34
  );
32
35
  };
33
36
 
34
- getParagraph(children: React.ReactNode[], styles: StyleProp<ViewStyle>) {
37
+ getViewNode(
38
+ children: React.ReactNode[] | null,
39
+ styles: StyleProp<ViewStyle>
40
+ ) {
35
41
  return (
36
42
  <View key={generateRandomString()} style={styles}>
37
43
  {children}
@@ -39,7 +45,7 @@ class Renderer {
39
45
  );
40
46
  }
41
47
 
42
- getCodeBlock(
48
+ getCodeBlockNode(
43
49
  text: string,
44
50
  containerStyle: StyleProp<ViewStyle>,
45
51
  textStyle: StyleProp<TextStyle>
@@ -51,7 +57,7 @@ class Renderer {
51
57
  );
52
58
  }
53
59
 
54
- getBlockquote(children: React.ReactNode[], styles: StyleProp<ViewStyle>) {
60
+ getBlockquoteNode(children: React.ReactNode[], styles: StyleProp<ViewStyle>) {
55
61
  return (
56
62
  <View key={generateRandomString()} style={styles}>
57
63
  {children}
@@ -59,16 +65,25 @@ class Renderer {
59
65
  );
60
66
  }
61
67
 
62
- getHeading(text: string, styles: StyleProp<TextStyle>) {
63
- return (
64
- <Text key={generateRandomString()} style={styles}>
65
- {text}
66
- </Text>
67
- );
68
+ getImageNode(uri: string) {
69
+ return <MDImage key={generateRandomString()} uri={uri} />;
68
70
  }
69
71
 
70
- getImage(uri: string, width: number) {
71
- return <MDImage key={generateRandomString()} uri={uri} width={width} />;
72
+ getListNode(
73
+ ordered: boolean,
74
+ li: React.ReactNode[],
75
+ listStyle: ViewStyle | undefined,
76
+ textStyle: TextStyle | undefined
77
+ ) {
78
+ return (
79
+ <MarkedList
80
+ counterRenderer={ordered ? Decimal : Disc}
81
+ markerTextStyle={textStyle}
82
+ markerBoxStyle={listStyle}
83
+ >
84
+ {li.map((node) => node)}
85
+ </MarkedList>
86
+ );
72
87
  }
73
88
  }
74
89
 
@@ -0,0 +1,20 @@
1
+ import type { ColorKeys } from './types';
2
+
3
+ const colors: Record<'light' | 'dark', Record<ColorKeys, string>> = {
4
+ light: {
5
+ background: '#ffffff',
6
+ code: '#f6f8fa',
7
+ link: '#58a6ff',
8
+ text: '#333333',
9
+ border: '#d0d7de',
10
+ },
11
+ dark: {
12
+ background: '#000000',
13
+ code: '#161b22',
14
+ link: '#58a6ff',
15
+ text: '#ffffff',
16
+ border: '#30363d',
17
+ },
18
+ };
19
+
20
+ export default colors;
@@ -0,0 +1,117 @@
1
+ import { ColorSchemeName, StyleSheet } from 'react-native';
2
+ import { getTheme } from './theme';
3
+ import type { MarkedStyles, Theme } from './types';
4
+
5
+ const getFontStyles = (theme: Theme) => {
6
+ return StyleSheet.create({
7
+ regular: {
8
+ fontSize: 16,
9
+ lineHeight: 24,
10
+ color: theme.colors.text,
11
+ },
12
+ heading: {
13
+ fontWeight: '500',
14
+ color: theme.colors.text,
15
+ },
16
+ });
17
+ };
18
+
19
+ const getStyles = (
20
+ userStyles?: MarkedStyles,
21
+ colorScheme?: ColorSchemeName
22
+ ): MarkedStyles => {
23
+ const theme = getTheme(colorScheme || 'light');
24
+ const fontStyle = getFontStyles(theme);
25
+ const defaultStyles = StyleSheet.create<MarkedStyles>({
26
+ container: {
27
+ backgroundColor: theme.colors.background,
28
+ },
29
+ em: {
30
+ ...fontStyle.regular,
31
+ fontStyle: 'italic',
32
+ },
33
+ strong: {
34
+ ...fontStyle.regular,
35
+ fontWeight: 'bold',
36
+ },
37
+ text: { ...fontStyle.regular },
38
+ paragraph: {
39
+ ...fontStyle.regular,
40
+ paddingVertical: 8,
41
+ },
42
+ link: {
43
+ ...fontStyle.regular,
44
+ fontStyle: 'italic',
45
+ color: theme.colors.link,
46
+ },
47
+ blockquote: {
48
+ borderLeftColor: theme.colors.border,
49
+ paddingLeft: 16,
50
+ borderLeftWidth: 5,
51
+ opacity: 0.8,
52
+ },
53
+ h1: {
54
+ fontSize: 32,
55
+ lineHeight: 40,
56
+ fontWeight: 'bold',
57
+ marginVertical: 8,
58
+ letterSpacing: 0,
59
+ },
60
+ h2: {
61
+ ...fontStyle.heading,
62
+ fontSize: 28,
63
+ lineHeight: 36,
64
+ marginVertical: 8,
65
+ },
66
+ h3: {
67
+ ...fontStyle.heading,
68
+ fontSize: 24,
69
+ lineHeight: 32,
70
+ marginVertical: 4,
71
+ },
72
+ h4: {
73
+ ...fontStyle.heading,
74
+ fontSize: 22,
75
+ lineHeight: 28,
76
+ marginVertical: 4,
77
+ },
78
+ h5: {
79
+ ...fontStyle.regular,
80
+ ...fontStyle.heading,
81
+ marginVertical: 2,
82
+ },
83
+ h6: {
84
+ ...fontStyle.heading,
85
+ fontSize: 14,
86
+ lineHeight: 20,
87
+ marginVertical: 2,
88
+ },
89
+ codespan: {
90
+ ...fontStyle.regular,
91
+ fontStyle: 'italic',
92
+ backgroundColor: theme.colors.code,
93
+ fontWeight: '300',
94
+ },
95
+ code: {
96
+ padding: 16,
97
+ backgroundColor: theme.colors.code,
98
+ minWidth: '100%',
99
+ },
100
+ hr: {
101
+ borderBottomWidth: 1,
102
+ borderBottomColor: theme.colors.border,
103
+ marginVertical: 4,
104
+ },
105
+ li: {
106
+ ...fontStyle.regular,
107
+ flexShrink: 1,
108
+ },
109
+ });
110
+
111
+ return {
112
+ ...defaultStyles,
113
+ ...userStyles,
114
+ };
115
+ };
116
+
117
+ export default getStyles;
@@ -0,0 +1,9 @@
1
+ import type { ColorSchemeName } from 'react-native';
2
+ import colors from './colors';
3
+ import type { Theme } from './types';
4
+
5
+ export const getTheme = (theme: ColorSchemeName): Theme => {
6
+ return {
7
+ colors: colors[theme || 'light'],
8
+ };
9
+ };
@@ -0,0 +1,28 @@
1
+ import type { TextStyle, ViewStyle } from 'react-native';
2
+
3
+ export interface MarkedStyles {
4
+ container?: ViewStyle;
5
+ em?: TextStyle;
6
+ strong?: TextStyle;
7
+ text?: TextStyle;
8
+ paragraph?: ViewStyle;
9
+ link?: TextStyle;
10
+ blockquote?: ViewStyle;
11
+ h1?: TextStyle;
12
+ h2?: TextStyle;
13
+ h3?: TextStyle;
14
+ h4?: TextStyle;
15
+ h5?: TextStyle;
16
+ h6?: TextStyle;
17
+ codespan?: TextStyle;
18
+ code?: ViewStyle;
19
+ hr?: ViewStyle;
20
+ list?: ViewStyle;
21
+ li?: TextStyle;
22
+ }
23
+
24
+ export type ColorKeys = 'background' | 'code' | 'link' | 'text' | 'border';
25
+
26
+ export interface Theme {
27
+ colors: Record<ColorKeys, string>;
28
+ }
@@ -1 +0,0 @@
1
- {"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import type { ImageStyle, TextStyle, ViewStyle } from 'react-native';\n\nexport interface MarkedStyles {\n em?: TextStyle;\n strong?: TextStyle;\n text?: TextStyle;\n paragraph?: ViewStyle;\n link?: TextStyle;\n blockquoteText?: TextStyle;\n blockquoteContainer?: ViewStyle;\n h1?: TextStyle;\n h2?: TextStyle;\n h3?: TextStyle;\n h4?: TextStyle;\n h5?: TextStyle;\n h6?: TextStyle;\n image?: ImageStyle;\n codespan?: TextStyle;\n code?: ViewStyle;\n}\n\nexport interface ParserOptions {\n contentWidth: number;\n styles?: MarkedStyles;\n}\n"],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import type { ImageStyle, TextStyle, ViewStyle } from 'react-native';\n\nexport interface MarkedStyles {\n em?: TextStyle;\n strong?: TextStyle;\n text?: TextStyle;\n paragraph?: ViewStyle;\n link?: TextStyle;\n blockquoteText?: TextStyle;\n blockquoteContainer?: ViewStyle;\n h1?: TextStyle;\n h2?: TextStyle;\n h3?: TextStyle;\n h4?: TextStyle;\n h5?: TextStyle;\n h6?: TextStyle;\n image?: ImageStyle;\n codespan?: TextStyle;\n code?: ViewStyle;\n}\n\nexport interface ParserOptions {\n contentWidth: number;\n styles?: MarkedStyles;\n}\n"],"mappings":""}
@@ -1,23 +0,0 @@
1
- import type { ImageStyle, TextStyle, ViewStyle } from 'react-native';
2
- export interface MarkedStyles {
3
- em?: TextStyle;
4
- strong?: TextStyle;
5
- text?: TextStyle;
6
- paragraph?: ViewStyle;
7
- link?: TextStyle;
8
- blockquoteText?: TextStyle;
9
- blockquoteContainer?: ViewStyle;
10
- h1?: TextStyle;
11
- h2?: TextStyle;
12
- h3?: TextStyle;
13
- h4?: TextStyle;
14
- h5?: TextStyle;
15
- h6?: TextStyle;
16
- image?: ImageStyle;
17
- codespan?: TextStyle;
18
- code?: ViewStyle;
19
- }
20
- export interface ParserOptions {
21
- contentWidth: number;
22
- styles?: MarkedStyles;
23
- }