@novastera-oss/react-native-markdown-display 8.0.1

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 (69) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +1206 -0
  3. package/dist/cjs/index.js +164 -0
  4. package/dist/cjs/lib/AstRenderer.js +129 -0
  5. package/dist/cjs/lib/data/textStyleProps.js +24 -0
  6. package/dist/cjs/lib/parser.js +22 -0
  7. package/dist/cjs/lib/renderRules.js +148 -0
  8. package/dist/cjs/lib/styles.js +183 -0
  9. package/dist/cjs/lib/util/Token.js +11 -0
  10. package/dist/cjs/lib/util/cleanupTokens.js +61 -0
  11. package/dist/cjs/lib/util/convertAdditionalStyles.js +39 -0
  12. package/dist/cjs/lib/util/flattenInlineTokens.js +17 -0
  13. package/dist/cjs/lib/util/getTokenTypeByToken.js +21 -0
  14. package/dist/cjs/lib/util/getUniqueID.js +8 -0
  15. package/dist/cjs/lib/util/groupTextTokens.js +30 -0
  16. package/dist/cjs/lib/util/hasParents.js +6 -0
  17. package/dist/cjs/lib/util/omitListItemParagraph.js +33 -0
  18. package/dist/cjs/lib/util/openUrl.js +15 -0
  19. package/dist/cjs/lib/util/removeTextStyleProps.js +15 -0
  20. package/dist/cjs/lib/util/renderInlineAsText.js +16 -0
  21. package/dist/cjs/lib/util/splitTextNonTextNodes.js +21 -0
  22. package/dist/cjs/lib/util/stringToTokens.js +13 -0
  23. package/dist/cjs/lib/util/tokensToAST.js +63 -0
  24. package/dist/cjs/types.js +2 -0
  25. package/dist/esm/index.js +112 -0
  26. package/dist/esm/lib/AstRenderer.js +123 -0
  27. package/dist/esm/lib/data/textStyleProps.js +22 -0
  28. package/dist/esm/lib/parser.js +16 -0
  29. package/dist/esm/lib/renderRules.js +143 -0
  30. package/dist/esm/lib/styles.js +180 -0
  31. package/dist/esm/lib/util/Token.js +8 -0
  32. package/dist/esm/lib/util/cleanupTokens.js +55 -0
  33. package/dist/esm/lib/util/convertAdditionalStyles.js +36 -0
  34. package/dist/esm/lib/util/flattenInlineTokens.js +14 -0
  35. package/dist/esm/lib/util/getTokenTypeByToken.js +18 -0
  36. package/dist/esm/lib/util/getUniqueID.js +5 -0
  37. package/dist/esm/lib/util/groupTextTokens.js +24 -0
  38. package/dist/esm/lib/util/hasParents.js +3 -0
  39. package/dist/esm/lib/util/omitListItemParagraph.js +30 -0
  40. package/dist/esm/lib/util/openUrl.js +12 -0
  41. package/dist/esm/lib/util/removeTextStyleProps.js +9 -0
  42. package/dist/esm/lib/util/renderInlineAsText.js +13 -0
  43. package/dist/esm/lib/util/splitTextNonTextNodes.js +18 -0
  44. package/dist/esm/lib/util/stringToTokens.js +10 -0
  45. package/dist/esm/lib/util/tokensToAST.js +57 -0
  46. package/dist/esm/types.js +1 -0
  47. package/dist/types/index.d.ts +23 -0
  48. package/dist/types/lib/AstRenderer.d.ts +16 -0
  49. package/dist/types/lib/data/textStyleProps.d.ts +2 -0
  50. package/dist/types/lib/parser.d.ts +3 -0
  51. package/dist/types/lib/renderRules.d.ts +3 -0
  52. package/dist/types/lib/styles.d.ts +141 -0
  53. package/dist/types/lib/util/Token.d.ts +8 -0
  54. package/dist/types/lib/util/cleanupTokens.d.ts +2 -0
  55. package/dist/types/lib/util/convertAdditionalStyles.d.ts +1 -0
  56. package/dist/types/lib/util/flattenInlineTokens.d.ts +2 -0
  57. package/dist/types/lib/util/getTokenTypeByToken.d.ts +2 -0
  58. package/dist/types/lib/util/getUniqueID.d.ts +1 -0
  59. package/dist/types/lib/util/groupTextTokens.d.ts +2 -0
  60. package/dist/types/lib/util/hasParents.d.ts +2 -0
  61. package/dist/types/lib/util/omitListItemParagraph.d.ts +2 -0
  62. package/dist/types/lib/util/openUrl.d.ts +1 -0
  63. package/dist/types/lib/util/removeTextStyleProps.d.ts +1 -0
  64. package/dist/types/lib/util/renderInlineAsText.d.ts +2 -0
  65. package/dist/types/lib/util/splitTextNonTextNodes.d.ts +7 -0
  66. package/dist/types/lib/util/stringToTokens.d.ts +2 -0
  67. package/dist/types/lib/util/tokensToAST.d.ts +2 -0
  68. package/dist/types/types.d.ts +59 -0
  69. package/package.json +69 -0
package/README.md ADDED
@@ -0,0 +1,1206 @@
1
+ # React Native Markdown Display
2
+
3
+ It a 100% compatible CommonMark renderer, a react-native markdown renderer done right. This is __not__ a web-view markdown renderer but a renderer that uses native components for all its elements. These components can be overwritten and styled as needed.
4
+
5
+ ### Compatibility with react-native-markdown-renderer
6
+
7
+ This is intended to be a replacement for react-native-markdown-renderer, with a variety of bug fixes and enhancements - **Due to how the new style rules work, there may be some tweaking needed**, [see how to style stuff section below](#How-to-style-stuff)
8
+
9
+ ### Install
10
+
11
+ #### Yarn
12
+ ```npm
13
+ yarn add @novastera-oss/react-native-markdown-display
14
+ ```
15
+
16
+ #### NPM
17
+ ```npm
18
+ npm install -S @novastera-oss/react-native-markdown-display
19
+ ```
20
+
21
+ ### Breaking Changes
22
+
23
+ See `BREAKING_CHANGE.md` for the full migration notes from the legacy version.
24
+
25
+ ### Get Started
26
+
27
+ ```jsx
28
+ import React from 'react';
29
+ import { SafeAreaView, ScrollView, StatusBar } from 'react-native';
30
+
31
+ import Markdown from '@novastera-oss/react-native-markdown-display';
32
+
33
+ const copy = `# h1 Heading 8-)
34
+
35
+ **This is some bold text!**
36
+
37
+ This is normal text
38
+ `;
39
+
40
+ const App: () => React$Node = () => {
41
+ return (
42
+ <>
43
+ <StatusBar barStyle="dark-content" />
44
+ <SafeAreaView>
45
+ <ScrollView
46
+ contentInsetAdjustmentBehavior="automatic"
47
+ style={{height: '100%'}}
48
+ >
49
+ <Markdown>
50
+ {copy}
51
+ </Markdown>
52
+ </ScrollView>
53
+ </SafeAreaView>
54
+ </>
55
+ );
56
+ };
57
+
58
+ export default App;
59
+ ```
60
+
61
+
62
+ ### Props and Functions
63
+
64
+ The `<Markdown>` object takes the following common props:
65
+
66
+ | Property | Default | Required | Description
67
+ | --- | --- | --- | ---
68
+ | `children` | N/A | `true` | The markdown string to render, or the [pre-processed tree](#pre-processing)
69
+ | `style` | [source](https://github.com/iamacup/react-native-markdown-display/blob/master/src/lib/styles.js) | `false` | An object to override the styling for the various rules, [see style section below](#rules-and-styles) for more info
70
+ | `mergeStyle` | `true` | `false` | If true, when a style is supplied, the individual items are merged with the default styles instead of overwriting them
71
+ | `rules` | [source](https://github.com/iamacup/react-native-markdown-display/blob/master/src/lib/renderRules.js) | `false` | An object of rules that specify how to render each markdown item, [see rules section below](#rules) for more info
72
+ | `onLinkPress` | `import { Linking } from 'react-native';` and `Linking.openURL(url);` | `false` | A handler function to change click behaviour, [see handling links section below](#handling-links) for more info
73
+ | `debugPrintTree` | `false` | `false` | Will print the AST tree to the console to help you see what the markdown is being translated to
74
+
75
+
76
+ And some additional, less used options:
77
+
78
+ | Property | Default | Required | Description
79
+ | --- | --- | --- | ---
80
+ | `renderer` | `instanceOf(AstRenderer)` | `false` | Used to specify a custom renderer, you can not use the rules or styles props with a custom renderer.
81
+ | `markdownit` | `instanceOf(MarkdownIt)` | `false` | A custom markdownit instance with your configuration, default is `MarkdownIt({typographer: true})`
82
+ | `maxTopLevelChildren` | `null` | `false` | If defined as a number will only render out first `n` many top level children, then will try to render out `topLevelMaxExceededItem`
83
+ | `topLevelMaxExceededItem` | `<Text key="dotdotdot">...</Text>` | `false` | Will render when `maxTopLevelChildren` is hit. Make sure to give it a key!
84
+ | `allowedImageHandlers` | `['data:image/png;base64', 'data:image/gif;base64', 'data:image/jpeg;base64', 'https://', 'http://']` | `false` | Any image that does not start with one of these will have the `defaultImageHandler` value prepended to it (unless `defaultImageHandler` is null in which case it won't try to render anything)
85
+ | `defaultImageHandler` | `http://` | `false` | Will be prepended to an image url if it does not start with something in the `allowedImageHandlers` array, if this is set to null, it won't try to recover but will just not render anything instead.
86
+
87
+
88
+ # Syntax Support
89
+
90
+ <details><summary>Headings</summary>
91
+ <p>
92
+
93
+ ```
94
+ # h1 Heading 8-)
95
+ ## h2 Heading
96
+ ### h3 Heading
97
+ #### h4 Heading
98
+ ##### h5 Heading
99
+ ###### h6 Heading
100
+ ```
101
+
102
+ | iOS | Android
103
+ | --- | ---
104
+ | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/ios-1.png"/> | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/android-1.png"/>
105
+
106
+ </p>
107
+ </details>
108
+
109
+
110
+ <details><summary>Horizontal Rules</summary>
111
+ <p>
112
+
113
+ ```
114
+ Some text above
115
+ ___
116
+
117
+ Some text in the middle
118
+
119
+ ---
120
+
121
+ Some text below
122
+ ```
123
+
124
+ | iOS | Android
125
+ | --- | ---
126
+ | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/ios-2.png"/> | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/android-2.png"/>
127
+
128
+
129
+ </p>
130
+ </details>
131
+
132
+
133
+
134
+ <details><summary>Emphasis</summary>
135
+ <p>
136
+
137
+ ```
138
+ **This is bold text**
139
+
140
+ __This is bold text__
141
+
142
+ *This is italic text*
143
+
144
+ _This is italic text_
145
+
146
+ ~~Strikethrough~~
147
+ ```
148
+
149
+ | iOS | Android
150
+ | --- | ---
151
+ | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/ios-4.png"/> | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/android-4.png"/>
152
+
153
+ </p>
154
+ </details>
155
+
156
+
157
+ <details><summary>Blockquotes</summary>
158
+ <p>
159
+
160
+ ```
161
+ > Blockquotes can also be nested...
162
+ >> ...by using additional greater-than signs right next to each other...
163
+ > > > ...or with spaces between arrows.
164
+ ```
165
+
166
+ | iOS | Android
167
+ | --- | ---
168
+ | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/ios-5.png"/> | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/android-5.png"/>
169
+
170
+ </p>
171
+ </details>
172
+
173
+
174
+ <details><summary>Lists</summary>
175
+ <p>
176
+
177
+ ```
178
+ Unordered
179
+
180
+ + Create a list by starting a line with `+`, `-`, or `*`
181
+ + Sub-lists are made by indenting 2 spaces:
182
+ - Marker character change forces new list start:
183
+ * Ac tristique libero volutpat at
184
+ + Facilisis in pretium nisl aliquet. This is a very long list item that will surely wrap onto the next line.
185
+ - Nulla volutpat aliquam velit
186
+ + Very easy!
187
+
188
+ Ordered
189
+
190
+ 1. Lorem ipsum dolor sit amet
191
+ 2. Consectetur adipiscing elit. This is a very long list item that will surely wrap onto the next line.
192
+ 3. Integer molestie lorem at massa
193
+
194
+ Start numbering with offset:
195
+
196
+ 57. foo
197
+ 58. bar
198
+ ```
199
+
200
+ | iOS | Android
201
+ | --- | ---
202
+ | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/ios-6.png"/> | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/android-6.png"/>
203
+
204
+ </p>
205
+ </details>
206
+
207
+
208
+ <details><summary>Code</summary>
209
+ <p>
210
+
211
+ ```
212
+ Inline \`code\`
213
+
214
+ Indented code
215
+
216
+ // Some comments
217
+ line 1 of code
218
+ line 2 of code
219
+ line 3 of code
220
+
221
+
222
+ Block code "fences"
223
+
224
+ \`\`\`
225
+ Sample text here...
226
+ \`\`\`
227
+
228
+ Syntax highlighting
229
+
230
+ \`\`\` js
231
+ var foo = function (bar) {
232
+ return bar++;
233
+ };
234
+
235
+ console.log(foo(5));
236
+ \`\`\`
237
+ ```
238
+
239
+ | iOS | Android
240
+ | --- | ---
241
+ | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/ios-7.png"/> | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/android-7.png"/>
242
+
243
+ </p>
244
+ </details>
245
+
246
+
247
+ <details><summary>Tables</summary>
248
+ <p>
249
+
250
+ ```
251
+ | Option | Description |
252
+ | ------ | ----------- |
253
+ | data | path to data files to supply the data that will be passed into templates. |
254
+ | engine | engine to be used for processing templates. Handlebars is the default. |
255
+ | ext | extension to be used for dest files. |
256
+
257
+ Right aligned columns
258
+
259
+ | Option | Description |
260
+ | ------:| -----------:|
261
+ | data | path to data files to supply the data that will be passed into templates. |
262
+ | engine | engine to be used for processing templates. Handlebars is the default. |
263
+ | ext | extension to be used for dest files. |
264
+ ```
265
+
266
+ | iOS | Android
267
+ | --- | ---
268
+ | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/ios-8.png"/> | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/android-8.png"/>
269
+
270
+ </p>
271
+ </details>
272
+
273
+ <details><summary>Links</summary>
274
+ <p>
275
+
276
+ ```
277
+ [link text](https://www.google.com)
278
+
279
+ [link with title](https://www.google.com "title text!")
280
+
281
+ Autoconverted link https://www.google.com (enable linkify to see)
282
+ ```
283
+
284
+ | iOS | Android
285
+ | --- | ---
286
+ | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/ios-9.png"/> | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/android-9.png"/>
287
+
288
+ </p>
289
+ </details>
290
+
291
+ <details><summary>Images</summary>
292
+ <p>
293
+
294
+ ```
295
+ ![Minion](https://octodex.github.com/images/minion.png)
296
+ ![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat")
297
+
298
+ Like links, Images also have a footnote style syntax
299
+
300
+ ![Alt text][id]
301
+
302
+ With a reference later in the document defining the URL location:
303
+
304
+ [id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat"
305
+ ```
306
+
307
+ | iOS | Android
308
+ | --- | ---
309
+ | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/ios-10.png"/> | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/android-10.png"/>
310
+
311
+ </p>
312
+ </details>
313
+
314
+
315
+ <details><summary>Typographic Replacements</summary>
316
+ <p>
317
+
318
+ ```
319
+ Enable typographer option to see result.
320
+
321
+ (c) (C) (r) (R) (tm) (TM) (p) (P) +-
322
+
323
+ test.. test... test..... test?..... test!....
324
+
325
+ !!!!!! ???? ,, -- ---
326
+
327
+ "Smartypants, double quotes" and 'single quotes'
328
+ ```
329
+
330
+ | iOS | Android
331
+ | --- | ---
332
+ | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/ios-3.png"/> | <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/android-3.png"/>
333
+
334
+ </p>
335
+ </details>
336
+
337
+
338
+ <details><summary>Plugins and Extensions</summary>
339
+ <p>
340
+
341
+ Plugins for **extra** syntax support can be added using any markdown-it compatible plugins - [see plugins](https://www.npmjs.com/browse/keyword/markdown-it-plugin) for documentation from markdown-it. An example for integration follows:
342
+
343
+
344
+ #### Step 1
345
+
346
+ Identify the new components and integrate the plugin with a rendered component. We can use the `debugPrintTree` property to see what rules we are rendering:
347
+
348
+
349
+ ```jsx
350
+ import React from 'react';
351
+ import { SafeAreaView, ScrollView, StatusBar } from 'react-native';
352
+
353
+ import Markdown, { MarkdownIt } from '@novastera-oss/react-native-markdown-display';
354
+ import blockEmbedPlugin from 'markdown-it-block-embed';
355
+
356
+ const markdownItInstance =
357
+ MarkdownIt({typographer: true})
358
+ .use(blockEmbedPlugin, {
359
+ containerClassName: "video-embed"
360
+ });
361
+
362
+ const copy = `
363
+ # Some header
364
+
365
+ @[youtube](lJIrF4YjHfQ)
366
+ `;
367
+
368
+ const App: () => React$Node = () => {
369
+ return (
370
+ <>
371
+ <StatusBar barStyle="dark-content" />
372
+ <SafeAreaView>
373
+ <ScrollView
374
+ contentInsetAdjustmentBehavior="automatic"
375
+ style={{height: '100%'}}
376
+ >
377
+ <Markdown
378
+ debugPrintTree
379
+ markdownit={markdownItInstance}
380
+ >
381
+ {copy}
382
+ </Markdown>
383
+ </ScrollView>
384
+ </SafeAreaView>
385
+ </>
386
+ );
387
+ };
388
+
389
+ export default App;
390
+
391
+ ```
392
+
393
+ In the console, we will see the following rendered tree:
394
+
395
+ ```
396
+ body
397
+ -heading1
398
+ --textgroup
399
+ ---text
400
+ -video
401
+ ```
402
+
403
+ With the following error message:
404
+
405
+ ```
406
+ Warning, unknown render rule encountered: video. 'unknown' render rule used (by default, returns null - nothing rendered)
407
+ ```
408
+
409
+
410
+ #### Step 2
411
+
412
+ We need to create the **render rules** and **styles** to handle this new **'video'** component
413
+
414
+
415
+ ```jsx
416
+ import React from 'react';
417
+ import { SafeAreaView, ScrollView, StatusBar } from 'react-native';
418
+
419
+ import Markdown, { MarkdownIt } from '@novastera-oss/react-native-markdown-display';
420
+ import blockEmbedPlugin from 'markdown-it-block-embed';
421
+
422
+ const markdownItInstance =
423
+ MarkdownIt({typographer: true})
424
+ .use(blockEmbedPlugin, {
425
+ containerClassName: "video-embed"
426
+ });
427
+
428
+ const copy = `
429
+ # Some header
430
+
431
+ @[youtube](lJIrF4YjHfQ)
432
+ `;
433
+
434
+ const App: () => React$Node = () => {
435
+ return (
436
+ <>
437
+ <StatusBar barStyle="dark-content" />
438
+ <SafeAreaView>
439
+ <ScrollView
440
+ contentInsetAdjustmentBehavior="automatic"
441
+ style={{height: '100%'}}
442
+ >
443
+ <Markdown
444
+ debugPrintTree
445
+ markdownit={markdownItInstance}
446
+ style={{
447
+ video: {
448
+ color: 'red',
449
+ }
450
+ }}
451
+ rules={{
452
+ video: (node, children, parent, styles) =>{
453
+ // examine the node properties to see what video we need to render
454
+ console.log(node); // expected output of this is in readme.md below this code snip
455
+
456
+ return (<Text key={node.key} style={styles.video}>
457
+ Return a video component instead of this text component!
458
+ </Text>);
459
+ }
460
+
461
+ }}
462
+ >
463
+ {copy}
464
+ </Markdown>
465
+ </ScrollView>
466
+ </SafeAreaView>
467
+ </>
468
+ );
469
+ };
470
+
471
+ export default App;
472
+ ```
473
+
474
+ And all of the video properties needed to render something meaningful are on the node, like this:
475
+
476
+ ```
477
+ {type: "video", sourceType: "video", sourceInfo: {…}, sourceMeta: null, block: true, …}
478
+ attributes: {}
479
+ block: true
480
+ children: []
481
+ content: ""
482
+ index: 1
483
+ key: "rnmr_1720a98f540_video"
484
+ markup: "@[youtube](lJIrF4YjHfQ)"
485
+ sourceInfo:
486
+ service: YouTubeService
487
+ env: PluginEnvironment {md: MarkdownIt, options: {…}, services: {…}}
488
+ name: "youtube"
489
+ options:
490
+ height: 390
491
+ width: 640
492
+ serviceName: "youtube"
493
+ videoID: "lJIrF4YjHfQ"
494
+ videoReference: "lJIrF4YjHfQ"
495
+ sourceMeta: null
496
+ sourceType: "video"
497
+ tokenIndex: 5
498
+ type: "video"
499
+ ```
500
+
501
+ #### Other Debugging
502
+
503
+ You can do some additional debugging of what the markdown instance is spitting out like this:
504
+
505
+ ```jsx
506
+ import Markdown, { MarkdownIt } from '@novastera-oss/react-native-markdown-display';
507
+ import blockEmbedPlugin from 'markdown-it-block-embed';
508
+
509
+ const markdownItInstance =
510
+ MarkdownIt({typographer: true})
511
+ .use(blockEmbedPlugin, {
512
+ containerClassName: "video-embed"
513
+ });
514
+
515
+ const copy = `
516
+ # Some header
517
+
518
+ @[youtube](lJIrF4YjHfQ)
519
+ `;
520
+
521
+ // this shows you the tree that is used by the react-native-markdown-display
522
+ const astTree = markdownItInstance.parse(copy, {});
523
+ console.log(astTree);
524
+
525
+ //this contains the html that would be generated - not used by react-native-markdown-display but useful for reference
526
+ const html = markdownItInstance.render(copy);
527
+ console.log(html);
528
+
529
+ ```
530
+
531
+ The above code will output something like this:
532
+
533
+ ```
534
+ astTree:
535
+
536
+ (4) [Token, Token, Token, Token]
537
+
538
+ 0: Token {type: "heading_open", tag: "h1", attrs: null, map: Array(2), nesting: 1, …}
539
+ 1: Token {type: "inline", tag: "", attrs: null, map: Array(2), nesting: 0, …}
540
+ 2: Token {type: "heading_close", tag: "h1", attrs: null, map: null, nesting: -1, …}
541
+ 3: Token {type: "video", tag: "div", attrs: null, map: Array(2), nesting: 0, …}
542
+
543
+ length: 4
544
+ ```
545
+
546
+ ```
547
+ html:
548
+
549
+
550
+ <h1>Some header</h1>
551
+ <div class="video-embed block-embed-service-youtube"><iframe type="text/html" src="//www.youtube.com/embed/lJIrF4YjHfQ" frameborder="0" width="640" height="390" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></div>
552
+ ```
553
+
554
+
555
+ </p>
556
+ </details>
557
+
558
+
559
+ <details><summary>All Markdown for Testing</summary>
560
+ <p>
561
+
562
+ This is all of the markdown in one place for testing that your applied styles work in all cases
563
+
564
+ ```
565
+ Headings
566
+
567
+ # h1 Heading 8-)
568
+ ## h2 Heading
569
+ ### h3 Heading
570
+ #### h4 Heading
571
+ ##### h5 Heading
572
+ ###### h6 Heading
573
+
574
+
575
+ Horizontal Rules
576
+
577
+ Some text above
578
+ ___
579
+
580
+ Some text in the middle
581
+
582
+ ---
583
+
584
+ Some text below
585
+
586
+
587
+ Emphasis
588
+
589
+ **This is bold text**
590
+
591
+ __This is bold text__
592
+
593
+ *This is italic text*
594
+
595
+ _This is italic text_
596
+
597
+ ~~Strikethrough~~
598
+
599
+
600
+ Blockquotes
601
+
602
+ > Blockquotes can also be nested...
603
+ >> ...by using additional greater-than signs right next to each other...
604
+ > > > ...or with spaces between arrows.
605
+
606
+
607
+ Lists
608
+
609
+ Unordered
610
+
611
+ + Create a list by starting a line with `+`, `-`, or `*`
612
+ + Sub-lists are made by indenting 2 spaces:
613
+ - Marker character change forces new list start:
614
+ * Ac tristique libero volutpat at
615
+ + Facilisis in pretium nisl aliquet. This is a very long list item that will surely wrap onto the next line.
616
+ - Nulla volutpat aliquam velit
617
+ + Very easy!
618
+
619
+ Ordered
620
+
621
+ 1. Lorem ipsum dolor sit amet
622
+ 2. Consectetur adipiscing elit. This is a very long list item that will surely wrap onto the next line.
623
+ 3. Integer molestie lorem at massa
624
+
625
+ Start numbering with offset:
626
+
627
+ 57. foo
628
+ 58. bar
629
+
630
+
631
+ Code
632
+
633
+ Inline \`code\`
634
+
635
+ Indented code
636
+
637
+ // Some comments
638
+ line 1 of code
639
+ line 2 of code
640
+ line 3 of code
641
+
642
+
643
+ Block code "fences"
644
+
645
+ \`\`\`
646
+ Sample text here...
647
+ \`\`\`
648
+
649
+ Syntax highlighting
650
+
651
+ \`\`\` js
652
+ var foo = function (bar) {
653
+ return bar++;
654
+ };
655
+
656
+ console.log(foo(5));
657
+ \`\`\`
658
+
659
+
660
+ Tables
661
+
662
+ | Option | Description |
663
+ | ------ | ----------- |
664
+ | data | path to data files to supply the data that will be passed into templates. |
665
+ | engine | engine to be used for processing templates. Handlebars is the default. |
666
+ | ext | extension to be used for dest files. |
667
+
668
+ Right aligned columns
669
+
670
+ | Option | Description |
671
+ | ------:| -----------:|
672
+ | data | path to data files to supply the data that will be passed into templates. |
673
+ | engine | engine to be used for processing templates. Handlebars is the default. |
674
+ | ext | extension to be used for dest files. |
675
+
676
+
677
+ Links
678
+
679
+ [link text](https://www.google.com)
680
+
681
+ [link with title](https://www.google.com "title text!")
682
+
683
+ Autoconverted link https://www.google.com (enable linkify to see)
684
+
685
+
686
+ Images
687
+
688
+ ![Minion](https://octodex.github.com/images/minion.png)
689
+ ![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat")
690
+
691
+ Like links, Images also have a footnote style syntax
692
+
693
+ ![Alt text][id]
694
+
695
+ With a reference later in the document defining the URL location:
696
+
697
+ [id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat"
698
+
699
+
700
+ Typographic Replacements
701
+
702
+ Enable typographer option to see result.
703
+
704
+ (c) (C) (r) (R) (tm) (TM) (p) (P) +-
705
+
706
+ test.. test... test..... test?..... test!....
707
+
708
+ !!!!!! ???? ,, -- ---
709
+
710
+ "Smartypants, double quotes" and 'single quotes'
711
+
712
+ ```
713
+
714
+ </p>
715
+ </details>
716
+
717
+
718
+ # Rules and Styles
719
+
720
+ ### How to style stuff
721
+
722
+ Text styles are applied in a way that makes it much more convenient to manage changes to global styles while also allowing fine tuning of individual elements.
723
+
724
+ Think of the implementation like applying styles in CSS. changes to the `body` effect everything, but can be overwritten further down the style / component tree.
725
+
726
+ **Be careful when styling 'text':** the text rule is not applied to all rendered text, most notably list bullet points. If you want to, for instance, color all text, change the `body` style.
727
+
728
+
729
+ <details><summary>Example</summary>
730
+ <p>
731
+
732
+ <img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/style-example.png"/>
733
+
734
+ ```jsx
735
+ import React from 'react';
736
+ import { SafeAreaView, ScrollView, StatusBar } from 'react-native';
737
+
738
+ import Markdown from '@novastera-oss/react-native-markdown-display';
739
+
740
+ const copy = `
741
+ This is some text which is red because of the body style, which is also really small!
742
+
743
+ \`\`\`
744
+ //This is a code block woooo
745
+
746
+ const cool = () => {
747
+ console.log('????');
748
+ };
749
+ \`\`\`
750
+
751
+ and some more small text
752
+
753
+ # This is a h1
754
+ ## this is a h2
755
+ ### this is a h3
756
+ `;
757
+
758
+ const App: () => React$Node = () => {
759
+ return (
760
+ <>
761
+ <StatusBar barStyle="dark-content" />
762
+ <SafeAreaView>
763
+ <ScrollView
764
+ contentInsetAdjustmentBehavior="automatic"
765
+ style={{height: '100%'}}
766
+ >
767
+ <Markdown
768
+ style={{
769
+ body: {color: 'red', fontSize: 10},
770
+ heading1: {color: 'purple'},
771
+ code_block: {color: 'black', fontSize: 14}
772
+ }}
773
+ >
774
+ {copy}
775
+ </Markdown>
776
+ </ScrollView>
777
+ </SafeAreaView>
778
+ </>
779
+ );
780
+ };
781
+
782
+ export default App;
783
+ ```
784
+
785
+ </p>
786
+ </details>
787
+
788
+ ### Styles
789
+
790
+ Styles are used to override how certain rules are styled. The existing
791
+ implementation is in `src/lib/styles.ts`.
792
+
793
+ **NOTE:** By default styles are merged with the existing implementation, to
794
+ change this, see the `mergeStyle` prop.
795
+
796
+ #### Inline CSS (HTML style attributes)
797
+
798
+ Inline `style="..."` attributes inside markdown are supported only with a
799
+ minimal parser. It accepts simple `key:value;` pairs and basic numeric/`px`
800
+ values, and is not a full CSS engine. For anything complex, use the `style`
801
+ prop or custom `rules`.
802
+
803
+ <details><summary>Example Implementation</summary>
804
+ <p>
805
+
806
+ ```jsx
807
+ import React from 'react';
808
+ import { SafeAreaView, ScrollView, StatusBar, StyleSheet } from 'react-native';
809
+
810
+ import Markdown from '@novastera-oss/react-native-markdown-display';
811
+
812
+ const styles = StyleSheet.create({
813
+ heading1: {
814
+ fontSize: 32,
815
+ backgroundColor: '#000000',
816
+ color: '#FFFFFF',
817
+ },
818
+ heading2: {
819
+ fontSize: 24,
820
+ },
821
+ heading3: {
822
+ fontSize: 18,
823
+ },
824
+ heading4: {
825
+ fontSize: 16,
826
+ },
827
+ heading5: {
828
+ fontSize: 13,
829
+ },
830
+ heading6: {
831
+ fontSize: 11,
832
+ }
833
+ });
834
+
835
+ const copy = `
836
+ # h1 Heading 8-)
837
+ ## h2 Heading 8-)
838
+ ### h3 Heading 8-)
839
+
840
+ | Option | Description |
841
+ | ------ | ----------- |
842
+ | data | path to data files to supply the data that will be passed into templates. |
843
+ | engine | engine to be used for processing templates. Handlebars is the default. |
844
+ | ext | extension to be used for dest files. |
845
+ `;
846
+
847
+ const App: () => React$Node = () => {
848
+ return (
849
+ <>
850
+ <StatusBar barStyle="dark-content" />
851
+ <SafeAreaView>
852
+ <ScrollView
853
+ contentInsetAdjustmentBehavior="automatic"
854
+ style={{height: '100%'}}
855
+ >
856
+ <Markdown
857
+ style={styles}
858
+ >
859
+ {copy}
860
+ </Markdown>
861
+ </ScrollView>
862
+ </SafeAreaView>
863
+ </>
864
+ );
865
+ };
866
+
867
+ export default App;
868
+ ```
869
+
870
+ </p>
871
+ </details>
872
+
873
+ ### Rules
874
+
875
+ Rules are used to specify how you want certain elements to be displayed. The existing implementation is [here](https://github.com/iamacup/react-native-markdown-display/blob/master/src/lib/renderRules.js)
876
+
877
+ <details><summary>Example Implementation</summary>
878
+ <p>
879
+
880
+ ```jsx
881
+ import React from 'react';
882
+ import { SafeAreaView, ScrollView, StatusBar, Text } from 'react-native';
883
+
884
+ import Markdown from '@novastera-oss/react-native-markdown-display';
885
+
886
+ const rules = {
887
+ heading1: (node, children, parent, styles) =>
888
+ <Text key={node.key} style={[styles.heading, styles.heading1]}>
889
+ >> H1 TEXT HERE >> "{children}"
890
+ </Text>,
891
+ heading2: (node, children, parent, styles) =>
892
+ <Text key={node.key} style={[styles.heading, styles.heading2]}>
893
+ >> H2 TEXT HERE >> "{children}"
894
+ </Text>,
895
+ heading3: (node, children, parent, styles) =>
896
+ <Text key={node.key} style={[styles.heading, styles.heading3]}>
897
+ >> H3 TEXT HERE >> "{children}"
898
+ </Text>,
899
+ };
900
+
901
+ const copy = `
902
+ # h1 Heading 8-)
903
+ ## h2 Heading 8-)
904
+ ### h3 Heading 8-)
905
+
906
+ | Option | Description |
907
+ | ------ | ----------- |
908
+ | data | path to data files to supply the data that will be passed into templates. |
909
+ | engine | engine to be used for processing templates. Handlebars is the default. |
910
+ | ext | extension to be used for dest files. |
911
+ `;
912
+
913
+ const App: () => React$Node = () => {
914
+ return (
915
+ <>
916
+ <StatusBar barStyle="dark-content" />
917
+ <SafeAreaView>
918
+ <ScrollView
919
+ contentInsetAdjustmentBehavior="automatic"
920
+ style={{height: '100%'}}
921
+ >
922
+ <Markdown
923
+ rules={rules}
924
+ >
925
+ {copy}
926
+ </Markdown>
927
+ </ScrollView>
928
+ </SafeAreaView>
929
+ </>
930
+ );
931
+ };
932
+
933
+ export default App;
934
+ ```
935
+
936
+ </p>
937
+ </details>
938
+
939
+
940
+ ### All rules and their associated styles:
941
+
942
+ | Render Rule | Style(s) |
943
+ | ------ | ----------- |
944
+ | `body` | `body` |
945
+ | `heading1` | `heading1` |
946
+ | `heading2` | `heading2` |
947
+ | `heading3` | `heading3` |
948
+ | `heading4` | `heading4` |
949
+ | `heading5` | `heading5` |
950
+ | `heading6` | `heading6` |
951
+ | `hr` | `hr` |
952
+ | `strong` | `strong` |
953
+ | `em` | `em` |
954
+ | `s` | `s` |
955
+ | `blockquote` | `blockquote` |
956
+ | `bullet_list` | `bullet_list` |
957
+ | `ordered_list` | `ordered_list` |
958
+ | `list_item` | `list_item` - This is a special case that contains a set of pseudo classes that don't align to the render rule: `ordered_list_icon`, `ordered_list_content`, `bullet_list_icon`, `bullet_list_content` |
959
+ | `code_inline` | `code_inline` |
960
+ | `code_block` | `code_block` |
961
+ | `fence` | `fence` |
962
+ | `table` | `table` |
963
+ | `thead` | `thead` |
964
+ | `tbody` | `tbody` |
965
+ | `th` | `th` |
966
+ | `tr` | `tr` |
967
+ | `td` | `td` |
968
+ | `link` | `link` |
969
+ | `blocklink` | `blocklink` |
970
+ | `image` | `image` |
971
+ | `text` | `text` |
972
+ | `textgroup` | `textgroup` |
973
+ | `paragraph` | `paragraph` |
974
+ | `hardbreak` | `hardbreak` |
975
+ | `softbreak` | `softbreak` |
976
+ | `pre` | `pre` |
977
+ | `inline` | `inline` |
978
+ | `span` | `span` |
979
+
980
+ # Handling Links
981
+
982
+ Links, by default, will be handled with the `import { Linking } from 'react-native';` import and `Linking.openURL(url);` call.
983
+
984
+ It is possible to overwrite this behaviour in one of two ways:
985
+
986
+ <details><summary>onLinkPress Callback</summary>
987
+ <p>
988
+
989
+ ```jsx
990
+ import React from 'react';
991
+ import { SafeAreaView, ScrollView, StatusBar } from 'react-native';
992
+
993
+ import Markdown from '@novastera-oss/react-native-markdown-display';
994
+
995
+ const copy = `[This is a link!](https://github.com/iamacup/react-native-markdown-display/)`;
996
+
997
+ const onLinkPress = (url) => {
998
+ if (url) {
999
+ // some custom logic
1000
+ return false;
1001
+ }
1002
+
1003
+ // return true to open with `Linking.openURL
1004
+ // return false to handle it yourself
1005
+ return true
1006
+ }
1007
+
1008
+ const App: () => React$Node = () => {
1009
+ return (
1010
+ <>
1011
+ <StatusBar barStyle="dark-content" />
1012
+ <SafeAreaView>
1013
+ <ScrollView
1014
+ contentInsetAdjustmentBehavior="automatic"
1015
+ style={{height: '100%'}}
1016
+ >
1017
+ <Markdown
1018
+ onLinkPress={onLinkPress}
1019
+ >
1020
+ {copy}
1021
+ </Markdown>
1022
+ </ScrollView>
1023
+ </SafeAreaView>
1024
+ </>
1025
+ );
1026
+ };
1027
+
1028
+ export default App;
1029
+ ```
1030
+
1031
+ </p>
1032
+ </details>
1033
+
1034
+ <details><summary>Using a Custom Rule</summary>
1035
+ <p>
1036
+
1037
+ You will need to overwrite one or both of `link` and `blocklink`, the original defenitions can be [found here](https://github.com/iamacup/react-native-markdown-display/blob/master/src/lib/renderRules.js)
1038
+
1039
+ Something like this with `yourCustomHandlerFunctionOrLogicHere`:
1040
+
1041
+ ```jsx
1042
+ import React from 'react';
1043
+ import { SafeAreaView, ScrollView, StatusBar, Text } from 'react-native';
1044
+
1045
+ import Markdown from '@novastera-oss/react-native-markdown-display';
1046
+
1047
+ const copy = `[This is a link!](https://github.com/iamacup/react-native-markdown-display/)`;
1048
+
1049
+ const rules = {
1050
+ link: (node, children, parent, styles) => {
1051
+ return (
1052
+ <Text key={node.key} style={styles.link} onPress={() => yourCustomHandlerFunctionOrLogicHere(node.attributes.href) }>
1053
+ {children}
1054
+ </Text>
1055
+ );
1056
+ },
1057
+ };
1058
+
1059
+ const App: () => React$Node = () => {
1060
+ return (
1061
+ <>
1062
+ <StatusBar barStyle="dark-content" />
1063
+ <SafeAreaView>
1064
+ <ScrollView
1065
+ contentInsetAdjustmentBehavior="automatic"
1066
+ style={{height: '100%'}}
1067
+ >
1068
+ <Markdown
1069
+ rules={rules}
1070
+ >
1071
+ {copy}
1072
+ </Markdown>
1073
+ </ScrollView>
1074
+ </SafeAreaView>
1075
+ </>
1076
+ );
1077
+ };
1078
+
1079
+ export default App;
1080
+ ```
1081
+
1082
+ </p>
1083
+ </details>
1084
+
1085
+
1086
+ # Disabling Specific Types of Markdown
1087
+
1088
+ You can dissable any type of markdown you want, which is very useful in a mobile environment, by passing the markdownit property like below. Note that for convenience we also export the `MarkdownIt` instance so you don't have to include it as a project dependency directly just to remove some types of markdown.
1089
+
1090
+ This example will stop images and links.
1091
+
1092
+ ```jsx
1093
+ import React from 'react';
1094
+ import { SafeAreaView, ScrollView, StatusBar, Text } from 'react-native';
1095
+
1096
+ import Markdown, { MarkdownIt } from '@novastera-oss/react-native-markdown-display';
1097
+
1098
+ const copy = `
1099
+ # This heading will show with formatting
1100
+
1101
+ [but this link will just](be displayed as this text)
1102
+ `;
1103
+
1104
+ const App: () => React$Node = () => {
1105
+ return (
1106
+ <>
1107
+ <StatusBar barStyle="dark-content" />
1108
+ <SafeAreaView>
1109
+ <ScrollView
1110
+ contentInsetAdjustmentBehavior="automatic"
1111
+ style={{height: '100%'}}
1112
+ >
1113
+ <Markdown
1114
+ markdownit={
1115
+ MarkdownIt({typographer: true}).disable([ 'link', 'image' ])
1116
+ }
1117
+ >
1118
+ {copy}
1119
+ </Markdown>
1120
+ </ScrollView>
1121
+ </SafeAreaView>
1122
+ </>
1123
+ );
1124
+ };
1125
+
1126
+ export default App;
1127
+ ```
1128
+
1129
+ A full list of things you can turn off is [here](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/commonmark.js)
1130
+
1131
+
1132
+ ### Pre Processing
1133
+
1134
+ It is possible to need to pre-process the data outside of this library ([related discussion here](https://github.com/iamacup/react-native-markdown-display/issues/79)). As a result, you can pass an AST tree directly as the children like this:
1135
+
1136
+ ```jsx
1137
+ import React from 'react';
1138
+ import { SafeAreaView, ScrollView, StatusBar, Text } from 'react-native';
1139
+
1140
+ import Markdown, { MarkdownIt, tokensToAST, stringToTokens } from '@novastera-oss/react-native-markdown-display';
1141
+
1142
+ const markdownItInstance = MarkdownIt({typographer: true});
1143
+
1144
+ const copy = `
1145
+ # Hello this is a title
1146
+
1147
+ This is some text with **BOLD!**
1148
+ `;
1149
+
1150
+ const ast = tokensToAST(stringToTokens(copy, markdownItInstance))
1151
+
1152
+ const App: () => React$Node = () => {
1153
+ return (
1154
+ <>
1155
+ <StatusBar barStyle="dark-content" />
1156
+ <SafeAreaView>
1157
+ <ScrollView
1158
+ contentInsetAdjustmentBehavior="automatic"
1159
+ style={{height: '100%'}}
1160
+ >
1161
+ <Markdown
1162
+ >
1163
+ {ast}
1164
+ </Markdown>
1165
+ </ScrollView>
1166
+ </SafeAreaView>
1167
+ </>
1168
+ );
1169
+ };
1170
+
1171
+ export default App;
1172
+ ```
1173
+
1174
+
1175
+ ### Other Notes
1176
+
1177
+ This is a fork of [react-native-markdown-renderer](https://github.com/mientjan/react-native-markdown-renderer), a library that unfortunately has not been updated for some time so i took all of the outstanding pull requests from that library and tested + merged as necessary.
1178
+
1179
+ ## About Novastera
1180
+
1181
+ This library is part of the Novastera open-source ecosystem, a modern CRM/ERP
1182
+ platform designed for the next generation of business applications. Novastera
1183
+ combines AI capabilities with comprehensive business management tools, enabling
1184
+ organizations to leverage on-device AI for enhanced productivity and data
1185
+ privacy.
1186
+
1187
+ ### Key Features of Novastera Platform
1188
+
1189
+ - Modern CRM/ERP system with AI-powered insights
1190
+ - On-device AI with a privacy-first approach
1191
+ - Mobile-first native iOS and Android apps built with React Native
1192
+ - Open-source commitment to developer-friendly tooling
1193
+
1194
+ This library is currently used in Novastera’s mobile application, demonstrating
1195
+ its capabilities in production environments. We are committed to enabling
1196
+ privacy-preserving AI and building tools that respect user data.
1197
+
1198
+ Learn more about Novastera: https://novastera.com/resources
1199
+
1200
+ ## Acknowledgments
1201
+
1202
+ Complete rework by Hassan Allybocus.
1203
+
1204
+ Foundation and original authors:
1205
+ - Mient-jan Stelling and Tom Pickard
1206
+ - The react-native-markdown-renderer community