Skip to content

Commit a8bf455

Browse files
authored
Merge pull request iamacup#45 from iamacup/5.0.0
5.0.0
2 parents a04a0e5 + 9dc4f11 commit a8bf455

13 files changed

+300
-205
lines changed

README.md

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ a web-view markdown renderer but a renderer that uses native components for all
55

66
### Compatibility with react-native-markdown-renderer
77

8-
This is intended to be a drop-in replacement for react-native-markdown-renderer, with a variety of bug fixes and enhancements.
8+
This is intended to be a drop-in 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)
99

1010
### Install
1111

@@ -42,17 +42,78 @@ export default class Page extends PureComponent {
4242
}
4343
```
4444

45+
### How to style stuff
46+
47+
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.
48+
49+
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.
50+
51+
The gotcha is if you try to use the text style override to change all text styles, this only changes things that are rendered using the ‘text’ rule. Instead you should change root, and then modify child styles (like code blocks etc) as needed.
52+
53+
54+
<details><summary>Example</summary>
55+
<p>
56+
57+
<img src="https://github.com/iamacup/react-native-markdown-display/raw/master/doc/images/style-example.png"/>
58+
59+
```jsx
60+
const copy = `
61+
This is some text which is red because of the root style, which is also really small!
62+
63+
\`\`\`
64+
//This is a code block woooo
65+
66+
const cool = () => {
67+
console.log('????');
68+
};
69+
\`\`\`
70+
71+
and some more small text
72+
73+
# This is a h1
74+
## this is a h2
75+
### this is a h3
76+
`;
77+
78+
const App: () => React$Node = () => {
79+
return (
80+
<>
81+
<SafeAreaView>
82+
<View style={{marginHorizontal: 20}}>
83+
<Markdown
84+
mergeStyle={true}
85+
style={{
86+
root: {color: 'red', fontSize: 10},
87+
heading1: {color: 'purple'},
88+
codeBlock: {color: 'black', fontSize: 14}
89+
}}
90+
>
91+
{copy}
92+
</Markdown>
93+
</View>
94+
</SafeAreaView>
95+
</>
96+
);
97+
};
98+
```
99+
100+
</p>
101+
</details>
102+
103+
45104
### Props and Functions
46105

47106
The `<Markdown>` object takes the following common props:
48107

49108
| Property | Required | Description
50109
| --- | --- | ---
51110
| `children` | `true` | The markdown string to render
52-
| `rules` | `false` | An object of rules that specify how to render each markdown item, [see rules section below](#rules) for full list
53111
| `style` | `false` | An object to override the styling for the various rules, [see style section below](#style) for full list
112+
| `mergeStyle` | `false` | if true, when a style is supplied, the individual items are merged with the default styles instead of overwriting them
113+
| `rules` | `false` | An object of rules that specify how to render each markdown item, [see rules section below](#rules) for full list
54114
| `onLinkPress` | `false` | A handler function to change click behaviour, [see handling links section below](#handling-links) for more info
55115

116+
56117
And some additional, less used options:
57118

58119
| Property | Required | Description
@@ -495,7 +556,7 @@ Rules are used to specify how you want certain elements to be displayed. The exi
495556

496557
The list of rules that can be overwritten is:
497558

498-
```["unknown", "textgroup", "inline", "text", "span", "strong", "s", "link", "blocklink", "em", "heading1", "heading2", "heading3", "heading4", "heading5", "heading6", "paragraph", "hardbreak", "blockquote", "code_inline", "code_block", "fence", "pre", "bullet_list", "ordered_list", "list_item", "table", "thead", "tbody", "th", "tr", "td", "hr", "softbreak", "image"]```
559+
```["root" "unknown", "textgroup", "inline", "text", "span", "strong", "s", "link", "blocklink", "em", "heading1", "heading2", "heading3", "heading4", "heading5", "heading6", "paragraph", "hardbreak", "blockquote", "code_inline", "code_block", "fence", "pre", "bullet_list", "ordered_list", "list_item", "table", "thead", "tbody", "th", "tr", "td", "hr", "softbreak", "image"]```
499560

500561
<details><summary>Example Implementation</summary>
501562
<p>
@@ -551,9 +612,9 @@ Styles are used to override how certain rules are styled. The existing implement
551612

552613
The list of styles that can be overwritten is:
553614

554-
```["root", "view", "codeBlock", "codeInline", "del", "em", "headingContainer", "heading", "heading1", "heading2", "heading3", "heading4", "heading5", "heading6", "hr", "blockquote", "inlineCode", "list", "listItem", "listUnordered", "listUnorderedItem", "listUnorderedItemIcon", "listOrdered", "listOrderedItem", "listOrderedItemIcon", "paragraph", "hardbreak", "strong", "table", "tableHeader", "tableHeaderCell", "tableRow", "tableRowCell", "text", "strikethrough", "link", "blocklink", "u", "image"]```
615+
```["root", "codeBlock", "codeInline", "em", "headingContainer", "heading", "heading1", "heading2", "heading3", "heading4", "heading5", "heading6", "hr", "blockquote", "list", "listItem", "listUnordered", "listUnorderedItem", "listUnorderedItemIcon", "listOrdered", "listOrderedItem", "listOrderedItemIcon", "paragraph", "hardbreak", "strong", "table", "tableHeader", "tableHeaderCell", "tableRow", "tableRowCell", "text", "textGroup", "strikethrough", "link", "blocklink", "image"]```
555616

556-
**NOTE:** There is no merge of the style properties, if you specify a style property, it will completely overwrite existing styles for that property.
617+
**NOTE:** by default there is no of the style properties, if you specify a style property, it will completely overwrite existing styles for that property **UNLESS** you specify `mergeStyle` as true.
557618

558619
<details><summary>Example Implementation</summary>
559620
<p>

doc/images/style-example.png

82.1 KB
Loading

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-markdown-display",
3-
"version": "4.1.0",
3+
"version": "5.0.0",
44
"description": "Markdown renderer for react-native, with CommonMark spec support + adds syntax extensions & sugar (URL autolinking, typographer), originally created by Mient-jan Stelling as react-native-markdown-renderer",
55
"main": "src/index.js",
66
"types": "src/index.d.ts",

src/index.js

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* Base Markdown component
3-
* @author Mient-jan Stelling
3+
* @author Mient-jan Stelling + contributors
44
*/
55

66
import React, {useMemo} from 'react';
@@ -16,11 +16,10 @@ import AstRenderer from './lib/AstRenderer';
1616
import MarkdownIt from 'markdown-it';
1717
import PluginContainer from './lib/plugin/PluginContainer';
1818
import blockPlugin from './lib/plugin/blockPlugin';
19+
import removeTextStyleProps from './lib/util/removeTextStyleProps';
1920
import {styles} from './lib/styles';
2021
import {stringToTokens} from './lib/util/stringToTokens';
21-
/**
22-
*
23-
*/
22+
2423
export {
2524
getUniqueID,
2625
openUrl,
@@ -34,12 +33,14 @@ export {
3433
PluginContainer,
3534
blockPlugin,
3635
styles,
36+
removeTextStyleProps,
3737
};
3838

3939
const getRenderer = (
4040
renderer,
4141
rules,
4242
style,
43+
mergeStyle,
4344
onLinkPress,
4445
maxTopLevelChildren,
4546
topLevelMaxExceededItem,
@@ -68,15 +69,28 @@ const getRenderer = (
6869
);
6970
}
7071
} else {
72+
let useStyles = {};
73+
74+
if (mergeStyle === true) {
75+
Object.keys(styles).forEach(value => {
76+
useStyles[value] = {
77+
...styles[value],
78+
...style[value],
79+
};
80+
});
81+
} else {
82+
useStyles = {
83+
...styles,
84+
...style,
85+
};
86+
}
87+
7188
return new AstRenderer(
7289
{
7390
...renderRules,
7491
...(rules || {}),
7592
},
76-
{
77-
...styles,
78-
...style,
79-
},
93+
useStyles,
8094
onLinkPress,
8195
maxTopLevelChildren,
8296
topLevelMaxExceededItem,
@@ -97,21 +111,19 @@ const getMarkdownParser = (markdownit, plugins) => {
97111
return md;
98112
};
99113

100-
/**
101-
* react-native-markdown-display
102-
*/
103114
const Markdown = ({
104115
children,
105116
renderer = null,
106117
rules = null,
107118
plugins = [],
108119
style = null,
120+
mergeStyle = false,
109121
markdownit = MarkdownIt({
110122
typographer: true,
111123
}),
112124
onLinkPress,
113125
maxTopLevelChildren = null,
114-
topLevelMaxExceededItem = <Text>...</Text>,
126+
topLevelMaxExceededItem = <Text key="dotdotdot">...</Text>,
115127
allowedImageHandlers = [
116128
'data:image/png;base64',
117129
'data:image/gif;base64',
@@ -127,6 +139,7 @@ const Markdown = ({
127139
renderer,
128140
rules,
129141
style,
142+
mergeStyle,
130143
onLinkPress,
131144
maxTopLevelChildren,
132145
topLevelMaxExceededItem,
@@ -139,6 +152,7 @@ const Markdown = ({
139152
renderer,
140153
rules,
141154
style,
155+
mergeStyle,
142156
topLevelMaxExceededItem,
143157
allowedImageHandlers,
144158
defaultImageHandler,
@@ -152,9 +166,6 @@ const Markdown = ({
152166
return parser(children, momoizedRenderer.render, markdownParser);
153167
};
154168

155-
/**
156-
* Definition of the prop types
157-
*/
158169
Markdown.propTypes = {
159170
children: PropTypes.node.isRequired,
160171
renderer: PropTypes.oneOfType([
@@ -192,6 +203,7 @@ Markdown.propTypes = {
192203
markdownit: PropTypes.instanceOf(MarkdownIt),
193204
plugins: PropTypes.arrayOf(PropTypes.instanceOf(PluginContainer)),
194205
style: PropTypes.any,
206+
mergeStyle: PropTypes.bool,
195207
allowedImageHandlers: PropTypes.arrayOf(PropTypes.string),
196208
defaultImageHandler: PropTypes.string,
197209
};

src/lib/AstRenderer.js

Lines changed: 40 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import getUniqueID from './util/getUniqueID';
22
import convertAdditionalStyles from './util/convertAdditionalStyles';
3-
/**
4-
*
5-
*/
3+
4+
import textStyleProps from './data/textStyleProps';
5+
66
export default class AstRenderer {
77
/**
88
*
@@ -25,29 +25,6 @@ export default class AstRenderer {
2525
this._topLevelMaxExceededItem = topLevelMaxExceededItem;
2626
this._allowedImageHandlers = allowedImageHandlers;
2727
this._defaultImageHandler = defaultImageHandler;
28-
29-
// this is all the style props that are unique to <Text> components as of 07/Nov/2019
30-
this._textStyleProps = [
31-
'textShadowOffset',
32-
'color',
33-
'fontSize',
34-
'fontStyle',
35-
'fontWeight',
36-
'lineHeight',
37-
'textAlign',
38-
'textDecorationLine',
39-
'textShadowColor',
40-
'fontFamily',
41-
'textShadowRadius',
42-
'includeFontPadding',
43-
'textAlignVertical',
44-
'fontVariant',
45-
'letterSpacing',
46-
'textDecorationColor',
47-
'textDecorationStyle',
48-
'textTransform',
49-
'writingDirection',
50-
];
5128
}
5229

5330
/**
@@ -63,6 +40,7 @@ export default class AstRenderer {
6340
`${type} renderRule not defined example: <Markdown rules={renderRules}>`,
6441
);
6542
}
43+
6644
return renderFunction;
6745
};
6846

@@ -72,19 +50,33 @@ export default class AstRenderer {
7250
* @param parentNodes
7351
* @return {*}
7452
*/
75-
renderNode = (node, parentNodes) => {
53+
renderNode = (node, parentNodes, isRoot = false) => {
7654
const renderFunction = this.getRenderFunction(node.type);
77-
7855
const parents = [...parentNodes];
56+
7957
parents.unshift(node);
8058

81-
if (node.type === 'text') {
59+
// calculate the children first
60+
61+
let children = node.children.map(value => {
62+
return this.renderNode(value, parents);
63+
});
64+
65+
// text node type is a special case because it is always the 'last' element in a chain
66+
67+
if (
68+
node.type === 'text' ||
69+
node.type === 'list_item' ||
70+
node.type === 'code_inline' ||
71+
node.type === 'code_block' ||
72+
node.type === 'fence'
73+
) {
8274
// we build up a style object for text types, this effectively grabs the styles from parents and
8375
// applies them in order of priority parent (most) to child (least) priority
8476
// so that if we overwride the text style, it does not overwrite a header1 style, for instance.
8577
const styleObj = {};
8678

87-
for (let a = 0; a < parentNodes.length; a++) {
79+
for (let a = parentNodes.length - 1; a > -1; a--) {
8880
// grab and additional attributes specified by markdown-it
8981
let refStyle = {};
9082

@@ -105,18 +97,16 @@ export default class AstRenderer {
10597
const arr = Object.keys(refStyle);
10698

10799
for (let b = 0; b < arr.length; b++) {
108-
if (this._textStyleProps.includes(arr[b])) {
100+
if (textStyleProps.includes(arr[b])) {
109101
styleObj[arr[b]] = refStyle[arr[b]];
110102
}
111103
}
112104
}
113105

114-
return renderFunction(node, [], parentNodes, this._style, styleObj);
106+
return renderFunction(node, children, parentNodes, this._style, styleObj);
115107
}
116108

117-
const children = node.children.map(value => {
118-
return this.renderNode(value, parents);
119-
});
109+
// render any special types of nodes that have different renderRule function signatures
120110

121111
if (node.type === 'link' || node.type === 'blocklink') {
122112
return renderFunction(
@@ -139,26 +129,29 @@ export default class AstRenderer {
139129
);
140130
}
141131

142-
return renderFunction(node, children, parentNodes, this._style);
143-
};
144-
145-
/**
146-
*
147-
* @param nodes
148-
* @return {*}
149-
*/
150-
render = nodes => {
151-
let children = nodes.map(value => this.renderNode(value, []));
152-
const root = {type: 'root', key: getUniqueID()};
132+
// cull top level children
153133

154134
if (
135+
isRoot === true &&
155136
this._maxTopLevelChildren &&
156137
children.length > this._maxTopLevelChildren
157138
) {
158139
children = children.slice(0, this._maxTopLevelChildren);
159140
children.push(this._topLevelMaxExceededItem);
160141
}
161142

162-
return this.getRenderFunction(root.type)(root, children, null, this._style);
143+
// render anythign else that has a normal signature
144+
145+
return renderFunction(node, children, parentNodes, this._style);
146+
};
147+
148+
/**
149+
*
150+
* @param nodes
151+
* @return {*}
152+
*/
153+
render = nodes => {
154+
const root = {type: 'root', key: getUniqueID(), children: nodes};
155+
return this.renderNode(root, [], true);
163156
};
164157
}

0 commit comments

Comments
 (0)