@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.
- package/LICENSE +22 -0
- package/README.md +1206 -0
- package/dist/cjs/index.js +164 -0
- package/dist/cjs/lib/AstRenderer.js +129 -0
- package/dist/cjs/lib/data/textStyleProps.js +24 -0
- package/dist/cjs/lib/parser.js +22 -0
- package/dist/cjs/lib/renderRules.js +148 -0
- package/dist/cjs/lib/styles.js +183 -0
- package/dist/cjs/lib/util/Token.js +11 -0
- package/dist/cjs/lib/util/cleanupTokens.js +61 -0
- package/dist/cjs/lib/util/convertAdditionalStyles.js +39 -0
- package/dist/cjs/lib/util/flattenInlineTokens.js +17 -0
- package/dist/cjs/lib/util/getTokenTypeByToken.js +21 -0
- package/dist/cjs/lib/util/getUniqueID.js +8 -0
- package/dist/cjs/lib/util/groupTextTokens.js +30 -0
- package/dist/cjs/lib/util/hasParents.js +6 -0
- package/dist/cjs/lib/util/omitListItemParagraph.js +33 -0
- package/dist/cjs/lib/util/openUrl.js +15 -0
- package/dist/cjs/lib/util/removeTextStyleProps.js +15 -0
- package/dist/cjs/lib/util/renderInlineAsText.js +16 -0
- package/dist/cjs/lib/util/splitTextNonTextNodes.js +21 -0
- package/dist/cjs/lib/util/stringToTokens.js +13 -0
- package/dist/cjs/lib/util/tokensToAST.js +63 -0
- package/dist/cjs/types.js +2 -0
- package/dist/esm/index.js +112 -0
- package/dist/esm/lib/AstRenderer.js +123 -0
- package/dist/esm/lib/data/textStyleProps.js +22 -0
- package/dist/esm/lib/parser.js +16 -0
- package/dist/esm/lib/renderRules.js +143 -0
- package/dist/esm/lib/styles.js +180 -0
- package/dist/esm/lib/util/Token.js +8 -0
- package/dist/esm/lib/util/cleanupTokens.js +55 -0
- package/dist/esm/lib/util/convertAdditionalStyles.js +36 -0
- package/dist/esm/lib/util/flattenInlineTokens.js +14 -0
- package/dist/esm/lib/util/getTokenTypeByToken.js +18 -0
- package/dist/esm/lib/util/getUniqueID.js +5 -0
- package/dist/esm/lib/util/groupTextTokens.js +24 -0
- package/dist/esm/lib/util/hasParents.js +3 -0
- package/dist/esm/lib/util/omitListItemParagraph.js +30 -0
- package/dist/esm/lib/util/openUrl.js +12 -0
- package/dist/esm/lib/util/removeTextStyleProps.js +9 -0
- package/dist/esm/lib/util/renderInlineAsText.js +13 -0
- package/dist/esm/lib/util/splitTextNonTextNodes.js +18 -0
- package/dist/esm/lib/util/stringToTokens.js +10 -0
- package/dist/esm/lib/util/tokensToAST.js +57 -0
- package/dist/esm/types.js +1 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/lib/AstRenderer.d.ts +16 -0
- package/dist/types/lib/data/textStyleProps.d.ts +2 -0
- package/dist/types/lib/parser.d.ts +3 -0
- package/dist/types/lib/renderRules.d.ts +3 -0
- package/dist/types/lib/styles.d.ts +141 -0
- package/dist/types/lib/util/Token.d.ts +8 -0
- package/dist/types/lib/util/cleanupTokens.d.ts +2 -0
- package/dist/types/lib/util/convertAdditionalStyles.d.ts +1 -0
- package/dist/types/lib/util/flattenInlineTokens.d.ts +2 -0
- package/dist/types/lib/util/getTokenTypeByToken.d.ts +2 -0
- package/dist/types/lib/util/getUniqueID.d.ts +1 -0
- package/dist/types/lib/util/groupTextTokens.d.ts +2 -0
- package/dist/types/lib/util/hasParents.d.ts +2 -0
- package/dist/types/lib/util/omitListItemParagraph.d.ts +2 -0
- package/dist/types/lib/util/openUrl.d.ts +1 -0
- package/dist/types/lib/util/removeTextStyleProps.d.ts +1 -0
- package/dist/types/lib/util/renderInlineAsText.d.ts +2 -0
- package/dist/types/lib/util/splitTextNonTextNodes.d.ts +7 -0
- package/dist/types/lib/util/stringToTokens.d.ts +2 -0
- package/dist/types/lib/util/tokensToAST.d.ts +2 -0
- package/dist/types/types.d.ts +59 -0
- package/package.json +69 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text, TouchableWithoutFeedback, View, Platform, StyleSheet, } from 'react-native';
|
|
3
|
+
import { Image } from 'expo-image';
|
|
4
|
+
import openUrl from './util/openUrl';
|
|
5
|
+
import hasParents from './util/hasParents';
|
|
6
|
+
import textStyleProps from './data/textStyleProps';
|
|
7
|
+
const baseRule = (fn) => fn;
|
|
8
|
+
const linkRule = (fn) => fn;
|
|
9
|
+
const imageRule = (fn) => fn;
|
|
10
|
+
const renderRules = {
|
|
11
|
+
// when unknown elements are introduced, so it wont break
|
|
12
|
+
unknown: baseRule(() => null),
|
|
13
|
+
// The main container
|
|
14
|
+
body: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_body, children: children }, node.key))),
|
|
15
|
+
// Headings
|
|
16
|
+
heading1: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_heading1, children: children }, node.key))),
|
|
17
|
+
heading2: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_heading2, children: children }, node.key))),
|
|
18
|
+
heading3: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_heading3, children: children }, node.key))),
|
|
19
|
+
heading4: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_heading4, children: children }, node.key))),
|
|
20
|
+
heading5: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_heading5, children: children }, node.key))),
|
|
21
|
+
heading6: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_heading6, children: children }, node.key))),
|
|
22
|
+
// Horizontal Rule
|
|
23
|
+
hr: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_hr }, node.key))),
|
|
24
|
+
// Emphasis
|
|
25
|
+
strong: baseRule((node, children, parent, styles) => (_jsx(Text, { style: styles.strong, children: children }, node.key))),
|
|
26
|
+
em: baseRule((node, children, parent, styles) => (_jsx(Text, { style: styles.em, children: children }, node.key))),
|
|
27
|
+
s: baseRule((node, children, parent, styles) => (_jsx(Text, { style: styles.s, children: children }, node.key))),
|
|
28
|
+
// Blockquotes
|
|
29
|
+
blockquote: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_blockquote, children: children }, node.key))),
|
|
30
|
+
// Lists
|
|
31
|
+
bullet_list: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_bullet_list, children: children }, node.key))),
|
|
32
|
+
ordered_list: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_ordered_list, children: children }, node.key))),
|
|
33
|
+
// this is a unique and quite annoying render rule because it has
|
|
34
|
+
// child items that can be styled (the list icon and the list content)
|
|
35
|
+
// outside of the AST tree so there are some work arounds in the
|
|
36
|
+
// AST renderer specifically to get the styling right here
|
|
37
|
+
list_item: baseRule((node, children, parent, styles, inheritedStyles = {}) => {
|
|
38
|
+
// we need to grab any text specific stuff here that is applied on the list_item style
|
|
39
|
+
// and apply it onto bullet_list_icon. the AST renderer has some workaround code to make
|
|
40
|
+
// the content classes apply correctly to the child AST tree items as well
|
|
41
|
+
// as code that forces the creation of the inheritedStyles object for list_items
|
|
42
|
+
const refStyle = {
|
|
43
|
+
...inheritedStyles,
|
|
44
|
+
...StyleSheet.flatten(styles.list_item),
|
|
45
|
+
};
|
|
46
|
+
const arr = Object.keys(refStyle);
|
|
47
|
+
const modifiedInheritedStylesObj = {};
|
|
48
|
+
for (let b = 0; b < arr.length; b++) {
|
|
49
|
+
if (textStyleProps.includes(arr[b])) {
|
|
50
|
+
modifiedInheritedStylesObj[arr[b]] = refStyle[arr[b]];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (hasParents(parent, 'bullet_list')) {
|
|
54
|
+
return (_jsxs(View, { style: styles._VIEW_SAFE_list_item, children: [_jsx(Text, { style: [modifiedInheritedStylesObj, styles.bullet_list_icon], accessible: false, children: Platform.select({
|
|
55
|
+
android: '\u2022',
|
|
56
|
+
ios: '\u00B7',
|
|
57
|
+
default: '\u2022',
|
|
58
|
+
}) }), _jsx(View, { style: styles._VIEW_SAFE_bullet_list_content, children: children })] }, node.key));
|
|
59
|
+
}
|
|
60
|
+
if (hasParents(parent, 'ordered_list')) {
|
|
61
|
+
const orderedListIndex = parent.findIndex((el) => el.type === 'ordered_list');
|
|
62
|
+
const orderedList = parent[orderedListIndex];
|
|
63
|
+
let listItemNumber;
|
|
64
|
+
if (orderedList.attributes && orderedList.attributes.start) {
|
|
65
|
+
listItemNumber = orderedList.attributes.start + node.index;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
listItemNumber = node.index + 1;
|
|
69
|
+
}
|
|
70
|
+
return (_jsxs(View, { style: styles._VIEW_SAFE_list_item, children: [_jsxs(Text, { style: [modifiedInheritedStylesObj, styles.ordered_list_icon], children: [listItemNumber, node.markup] }), _jsx(View, { style: styles._VIEW_SAFE_ordered_list_content, children: children })] }, node.key));
|
|
71
|
+
}
|
|
72
|
+
// we should not need this, but just in case
|
|
73
|
+
return (_jsx(View, { style: styles._VIEW_SAFE_list_item, children: children }, node.key));
|
|
74
|
+
}),
|
|
75
|
+
// Code
|
|
76
|
+
code_inline: baseRule((node, children, parent, styles, inheritedStyles = {}) => (_jsx(Text, { style: [inheritedStyles, styles.code_inline], children: node.content }, node.key))),
|
|
77
|
+
code_block: baseRule((node, children, parent, styles, inheritedStyles = {}) => {
|
|
78
|
+
// we trim new lines off the end of code blocks because the parser sends an extra one.
|
|
79
|
+
let { content } = node;
|
|
80
|
+
if (typeof node.content === 'string' &&
|
|
81
|
+
node.content.charAt(node.content.length - 1) === '\n') {
|
|
82
|
+
content = node.content.substring(0, node.content.length - 1);
|
|
83
|
+
}
|
|
84
|
+
return (_jsx(Text, { style: [inheritedStyles, styles.code_block], children: content }, node.key));
|
|
85
|
+
}),
|
|
86
|
+
fence: baseRule((node, children, parent, styles, inheritedStyles = {}) => {
|
|
87
|
+
// we trim new lines off the end of code blocks because the parser sends an extra one.
|
|
88
|
+
let { content } = node;
|
|
89
|
+
if (typeof node.content === 'string' &&
|
|
90
|
+
node.content.charAt(node.content.length - 1) === '\n') {
|
|
91
|
+
content = node.content.substring(0, node.content.length - 1);
|
|
92
|
+
}
|
|
93
|
+
return (_jsx(Text, { style: [inheritedStyles, styles.fence], children: content }, node.key));
|
|
94
|
+
}),
|
|
95
|
+
// Tables
|
|
96
|
+
table: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_table, children: children }, node.key))),
|
|
97
|
+
thead: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_thead, children: children }, node.key))),
|
|
98
|
+
tbody: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_tbody, children: children }, node.key))),
|
|
99
|
+
th: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_th, children: children }, node.key))),
|
|
100
|
+
tr: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_tr, children: children }, node.key))),
|
|
101
|
+
td: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_td, children: children }, node.key))),
|
|
102
|
+
// Links
|
|
103
|
+
link: linkRule((node, children, parent, styles, onLinkPress) => (_jsx(Text, { style: styles.link, onPress: () => openUrl(node.attributes.href, onLinkPress), children: children }, node.key))),
|
|
104
|
+
blocklink: linkRule((node, children, parent, styles, onLinkPress) => (_jsx(TouchableWithoutFeedback, { onPress: () => openUrl(node.attributes.href, onLinkPress), style: styles.blocklink, children: _jsx(View, { style: styles.image, children: children }) }, node.key))),
|
|
105
|
+
// Images
|
|
106
|
+
image: imageRule((node, children, parent, styles, allowedImageHandlers, defaultImageHandler) => {
|
|
107
|
+
const { src, alt } = node.attributes;
|
|
108
|
+
if (!src) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
// we check that the source starts with at least one of the elements in allowedImageHandlers
|
|
112
|
+
const show = allowedImageHandlers.filter((value) => {
|
|
113
|
+
return src.toLowerCase().startsWith(value.toLowerCase());
|
|
114
|
+
}).length > 0;
|
|
115
|
+
if (show === false && defaultImageHandler === null) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const imageUri = show === true ? src : `${defaultImageHandler !== null && defaultImageHandler !== void 0 ? defaultImageHandler : ''}${src}`;
|
|
119
|
+
const imageProps = {
|
|
120
|
+
key: node.key,
|
|
121
|
+
style: styles._VIEW_SAFE_image,
|
|
122
|
+
source: {
|
|
123
|
+
uri: imageUri,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
if (alt) {
|
|
127
|
+
imageProps.accessible = true;
|
|
128
|
+
imageProps.accessibilityLabel = alt;
|
|
129
|
+
}
|
|
130
|
+
return _jsx(Image, { ...imageProps });
|
|
131
|
+
}),
|
|
132
|
+
// Text Output
|
|
133
|
+
text: baseRule((node, children, parent, styles, inheritedStyles = {}) => (_jsx(Text, { style: [inheritedStyles, styles.text], children: node.content }, node.key))),
|
|
134
|
+
textgroup: baseRule((node, children, parent, styles) => (_jsx(Text, { style: styles.textgroup, children: children }, node.key))),
|
|
135
|
+
paragraph: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_paragraph, children: children }, node.key))),
|
|
136
|
+
hardbreak: baseRule((node, children, parent, styles) => (_jsx(Text, { style: styles.hardbreak, children: '\n' }, node.key))),
|
|
137
|
+
softbreak: baseRule((node, children, parent, styles) => (_jsx(Text, { style: styles.softbreak, children: '\n' }, node.key))),
|
|
138
|
+
// Believe these are never used but retained for completeness
|
|
139
|
+
pre: baseRule((node, children, parent, styles) => (_jsx(View, { style: styles._VIEW_SAFE_pre, children: children }, node.key))),
|
|
140
|
+
inline: baseRule((node, children, parent, styles) => (_jsx(Text, { style: styles.inline, children: children }, node.key))),
|
|
141
|
+
span: baseRule((node, children, parent, styles) => (_jsx(Text, { style: styles.span, children: children }, node.key))),
|
|
142
|
+
};
|
|
143
|
+
export default renderRules;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
// this is converted to a stylesheet internally at run time with StyleSheet.create(
|
|
3
|
+
export const styles = {
|
|
4
|
+
// The main container
|
|
5
|
+
body: {},
|
|
6
|
+
// Headings
|
|
7
|
+
heading1: {
|
|
8
|
+
flexDirection: 'row',
|
|
9
|
+
fontSize: 32,
|
|
10
|
+
},
|
|
11
|
+
heading2: {
|
|
12
|
+
flexDirection: 'row',
|
|
13
|
+
fontSize: 24,
|
|
14
|
+
},
|
|
15
|
+
heading3: {
|
|
16
|
+
flexDirection: 'row',
|
|
17
|
+
fontSize: 18,
|
|
18
|
+
},
|
|
19
|
+
heading4: {
|
|
20
|
+
flexDirection: 'row',
|
|
21
|
+
fontSize: 16,
|
|
22
|
+
},
|
|
23
|
+
heading5: {
|
|
24
|
+
flexDirection: 'row',
|
|
25
|
+
fontSize: 13,
|
|
26
|
+
},
|
|
27
|
+
heading6: {
|
|
28
|
+
flexDirection: 'row',
|
|
29
|
+
fontSize: 11,
|
|
30
|
+
},
|
|
31
|
+
// Horizontal Rule
|
|
32
|
+
hr: {
|
|
33
|
+
backgroundColor: '#000000',
|
|
34
|
+
height: 1,
|
|
35
|
+
},
|
|
36
|
+
// Emphasis
|
|
37
|
+
strong: {
|
|
38
|
+
fontWeight: 'bold',
|
|
39
|
+
},
|
|
40
|
+
em: {
|
|
41
|
+
fontStyle: 'italic',
|
|
42
|
+
},
|
|
43
|
+
s: {
|
|
44
|
+
textDecorationLine: 'line-through',
|
|
45
|
+
},
|
|
46
|
+
// Blockquotes
|
|
47
|
+
blockquote: {
|
|
48
|
+
backgroundColor: '#F5F5F5',
|
|
49
|
+
borderColor: '#CCC',
|
|
50
|
+
borderLeftWidth: 4,
|
|
51
|
+
marginLeft: 5,
|
|
52
|
+
paddingHorizontal: 5,
|
|
53
|
+
},
|
|
54
|
+
// Lists
|
|
55
|
+
bullet_list: {},
|
|
56
|
+
ordered_list: {},
|
|
57
|
+
list_item: {
|
|
58
|
+
flexDirection: 'row',
|
|
59
|
+
justifyContent: 'flex-start',
|
|
60
|
+
},
|
|
61
|
+
// @pseudo class, does not have a unique render rule
|
|
62
|
+
bullet_list_icon: {
|
|
63
|
+
marginLeft: 10,
|
|
64
|
+
marginRight: 10,
|
|
65
|
+
},
|
|
66
|
+
// @pseudo class, does not have a unique render rule
|
|
67
|
+
bullet_list_content: {
|
|
68
|
+
flex: 1,
|
|
69
|
+
},
|
|
70
|
+
// @pseudo class, does not have a unique render rule
|
|
71
|
+
ordered_list_icon: {
|
|
72
|
+
marginLeft: 10,
|
|
73
|
+
marginRight: 10,
|
|
74
|
+
},
|
|
75
|
+
// @pseudo class, does not have a unique render rule
|
|
76
|
+
ordered_list_content: {
|
|
77
|
+
flex: 1,
|
|
78
|
+
},
|
|
79
|
+
// Code
|
|
80
|
+
code_inline: {
|
|
81
|
+
borderWidth: 1,
|
|
82
|
+
borderColor: '#CCCCCC',
|
|
83
|
+
backgroundColor: '#f5f5f5',
|
|
84
|
+
padding: 10,
|
|
85
|
+
borderRadius: 4,
|
|
86
|
+
...Platform.select({
|
|
87
|
+
['ios']: {
|
|
88
|
+
fontFamily: 'Courier',
|
|
89
|
+
},
|
|
90
|
+
['android']: {
|
|
91
|
+
fontFamily: 'monospace',
|
|
92
|
+
},
|
|
93
|
+
}),
|
|
94
|
+
},
|
|
95
|
+
code_block: {
|
|
96
|
+
borderWidth: 1,
|
|
97
|
+
borderColor: '#CCCCCC',
|
|
98
|
+
backgroundColor: '#f5f5f5',
|
|
99
|
+
padding: 10,
|
|
100
|
+
borderRadius: 4,
|
|
101
|
+
...Platform.select({
|
|
102
|
+
['ios']: {
|
|
103
|
+
fontFamily: 'Courier',
|
|
104
|
+
},
|
|
105
|
+
['android']: {
|
|
106
|
+
fontFamily: 'monospace',
|
|
107
|
+
},
|
|
108
|
+
}),
|
|
109
|
+
},
|
|
110
|
+
fence: {
|
|
111
|
+
borderWidth: 1,
|
|
112
|
+
borderColor: '#CCCCCC',
|
|
113
|
+
backgroundColor: '#f5f5f5',
|
|
114
|
+
padding: 10,
|
|
115
|
+
borderRadius: 4,
|
|
116
|
+
...Platform.select({
|
|
117
|
+
['ios']: {
|
|
118
|
+
fontFamily: 'Courier',
|
|
119
|
+
},
|
|
120
|
+
['android']: {
|
|
121
|
+
fontFamily: 'monospace',
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
// Tables
|
|
126
|
+
table: {
|
|
127
|
+
borderWidth: 1,
|
|
128
|
+
borderColor: '#000000',
|
|
129
|
+
borderRadius: 3,
|
|
130
|
+
},
|
|
131
|
+
thead: {},
|
|
132
|
+
tbody: {},
|
|
133
|
+
th: {
|
|
134
|
+
flex: 1,
|
|
135
|
+
padding: 5,
|
|
136
|
+
},
|
|
137
|
+
tr: {
|
|
138
|
+
borderBottomWidth: 1,
|
|
139
|
+
borderColor: '#000000',
|
|
140
|
+
flexDirection: 'row',
|
|
141
|
+
},
|
|
142
|
+
td: {
|
|
143
|
+
flex: 1,
|
|
144
|
+
padding: 5,
|
|
145
|
+
},
|
|
146
|
+
// Links
|
|
147
|
+
link: {
|
|
148
|
+
textDecorationLine: 'underline',
|
|
149
|
+
},
|
|
150
|
+
blocklink: {
|
|
151
|
+
flex: 1,
|
|
152
|
+
borderColor: '#000000',
|
|
153
|
+
borderBottomWidth: 1,
|
|
154
|
+
},
|
|
155
|
+
// Images
|
|
156
|
+
image: {
|
|
157
|
+
flex: 1,
|
|
158
|
+
},
|
|
159
|
+
// Text Output
|
|
160
|
+
text: {},
|
|
161
|
+
textgroup: {},
|
|
162
|
+
paragraph: {
|
|
163
|
+
marginTop: 10,
|
|
164
|
+
marginBottom: 10,
|
|
165
|
+
flexWrap: 'wrap',
|
|
166
|
+
flexDirection: 'row',
|
|
167
|
+
alignItems: 'flex-start',
|
|
168
|
+
justifyContent: 'flex-start',
|
|
169
|
+
width: '100%',
|
|
170
|
+
},
|
|
171
|
+
hardbreak: {
|
|
172
|
+
width: '100%',
|
|
173
|
+
height: 1,
|
|
174
|
+
},
|
|
175
|
+
softbreak: {},
|
|
176
|
+
// Believe these are never used but retained for completeness
|
|
177
|
+
pre: {},
|
|
178
|
+
inline: {},
|
|
179
|
+
span: {},
|
|
180
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import getTokenTypeByToken from './getTokenTypeByToken';
|
|
2
|
+
import flattenInlineTokens from './flattenInlineTokens';
|
|
3
|
+
import renderInlineAsText from './renderInlineAsText';
|
|
4
|
+
export function cleanupTokens(tokens) {
|
|
5
|
+
tokens = flattenInlineTokens(tokens);
|
|
6
|
+
tokens.forEach((token) => {
|
|
7
|
+
var _a;
|
|
8
|
+
token.type = getTokenTypeByToken(token);
|
|
9
|
+
// set image and hardbreak to block elements
|
|
10
|
+
if (token.type === 'image' || token.type === 'hardbreak') {
|
|
11
|
+
token.block = true;
|
|
12
|
+
}
|
|
13
|
+
// Set img alt text
|
|
14
|
+
if (token.type === 'image' && token.attrs) {
|
|
15
|
+
const altIndex = typeof token.attrIndex === 'function'
|
|
16
|
+
? token.attrIndex('alt')
|
|
17
|
+
: token.attrs.findIndex((attr) => attr[0] === 'alt');
|
|
18
|
+
if (altIndex >= 0) {
|
|
19
|
+
token.attrs[altIndex][1] = renderInlineAsText((_a = token.children) !== null && _a !== void 0 ? _a : []);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* changing a link token to a blocklink to fix issue where link tokens with
|
|
25
|
+
* nested non text tokens breaks component
|
|
26
|
+
*/
|
|
27
|
+
const stack = [];
|
|
28
|
+
tokens = tokens.reduce((acc, token) => {
|
|
29
|
+
if (token.type === 'link' && token.nesting === 1) {
|
|
30
|
+
stack.push(token);
|
|
31
|
+
}
|
|
32
|
+
else if (stack.length > 0 &&
|
|
33
|
+
token.type === 'link' &&
|
|
34
|
+
token.nesting === -1) {
|
|
35
|
+
if (stack.some((stackToken) => stackToken.block)) {
|
|
36
|
+
stack[0].type = 'blocklink';
|
|
37
|
+
stack[0].block = true;
|
|
38
|
+
token.type = 'blocklink';
|
|
39
|
+
token.block = true;
|
|
40
|
+
}
|
|
41
|
+
stack.push(token);
|
|
42
|
+
while (stack.length) {
|
|
43
|
+
acc.push(stack.shift());
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (stack.length > 0) {
|
|
47
|
+
stack.push(token);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
acc.push(token);
|
|
51
|
+
}
|
|
52
|
+
return acc;
|
|
53
|
+
}, []);
|
|
54
|
+
return tokens;
|
|
55
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function toCamelCase(input) {
|
|
2
|
+
return input.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
3
|
+
}
|
|
4
|
+
function parseValue(value) {
|
|
5
|
+
const trimmed = value.trim();
|
|
6
|
+
const pxMatch = trimmed.match(/^(-?\d+(\.\d+)?)px$/);
|
|
7
|
+
if (pxMatch) {
|
|
8
|
+
return Number(pxMatch[1]);
|
|
9
|
+
}
|
|
10
|
+
const numMatch = trimmed.match(/^-?\d+(\.\d+)?$/);
|
|
11
|
+
if (numMatch) {
|
|
12
|
+
return Number(trimmed);
|
|
13
|
+
}
|
|
14
|
+
return trimmed;
|
|
15
|
+
}
|
|
16
|
+
export default function convertAdditionalStyles(style) {
|
|
17
|
+
if (!style || typeof style !== 'string') {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
const rules = style.split(';');
|
|
21
|
+
const tuples = rules
|
|
22
|
+
.map((rule) => {
|
|
23
|
+
const [rawKey, rawValue] = rule.split(':');
|
|
24
|
+
if (rawKey && rawValue) {
|
|
25
|
+
const key = toCamelCase(rawKey.trim());
|
|
26
|
+
const value = parseValue(rawValue);
|
|
27
|
+
return [key, value];
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
})
|
|
31
|
+
.filter((x) => x !== null);
|
|
32
|
+
return tuples.reduce((acc, [key, value]) => {
|
|
33
|
+
acc[key] = value;
|
|
34
|
+
return acc;
|
|
35
|
+
}, {});
|
|
36
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default function flattenTokens(tokens) {
|
|
2
|
+
return tokens.reduce((acc, curr) => {
|
|
3
|
+
if (curr.type === 'inline' && curr.children && curr.children.length > 0) {
|
|
4
|
+
const children = flattenTokens(curr.children);
|
|
5
|
+
while (children.length) {
|
|
6
|
+
acc.push(children.shift());
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
acc.push(curr);
|
|
11
|
+
}
|
|
12
|
+
return acc;
|
|
13
|
+
}, []);
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const regSelectOpenClose = /_open|_close/g;
|
|
2
|
+
export default function getTokenTypeByToken(token) {
|
|
3
|
+
var _a, _b;
|
|
4
|
+
let cleanedType = 'unknown';
|
|
5
|
+
if (token.type) {
|
|
6
|
+
cleanedType = token.type.replace(regSelectOpenClose, '');
|
|
7
|
+
}
|
|
8
|
+
switch (cleanedType) {
|
|
9
|
+
case 'heading': {
|
|
10
|
+
cleanedType = `${cleanedType}${(_b = (_a = token.tag) === null || _a === void 0 ? void 0 : _a.substr(1)) !== null && _b !== void 0 ? _b : ''}`;
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
default: {
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return cleanedType;
|
|
18
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import TokenGroup from './Token';
|
|
2
|
+
export default function groupTextTokens(tokens) {
|
|
3
|
+
const result = [];
|
|
4
|
+
let hasGroup = false;
|
|
5
|
+
tokens.forEach((token) => {
|
|
6
|
+
if (!token.block && !hasGroup) {
|
|
7
|
+
hasGroup = true;
|
|
8
|
+
result.push(new TokenGroup('textgroup', 1));
|
|
9
|
+
result.push(token);
|
|
10
|
+
}
|
|
11
|
+
else if (!token.block && hasGroup) {
|
|
12
|
+
result.push(token);
|
|
13
|
+
}
|
|
14
|
+
else if (token.block && hasGroup) {
|
|
15
|
+
hasGroup = false;
|
|
16
|
+
result.push(new TokenGroup('textgroup', -1));
|
|
17
|
+
result.push(token);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
result.push(token);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export default function omitListItemParagraph(tokens) {
|
|
2
|
+
// used to ensure that we remove the correct ending paragraph token
|
|
3
|
+
let depth = null;
|
|
4
|
+
return tokens.filter((token, index) => {
|
|
5
|
+
// update depth if we've already removed a starting paragraph token
|
|
6
|
+
if (depth !== null) {
|
|
7
|
+
depth = depth + token.nesting;
|
|
8
|
+
}
|
|
9
|
+
// check for a list_item token followed by paragraph token (to remove)
|
|
10
|
+
if (token.type === 'list_item' && token.nesting === 1 && depth === null) {
|
|
11
|
+
const next = index + 1 in tokens ? tokens[index + 1] : null;
|
|
12
|
+
if (next && next.type === 'paragraph' && next.nesting === 1) {
|
|
13
|
+
depth = 0;
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
else if (token.type === 'paragraph') {
|
|
18
|
+
if (token.nesting === 1 && depth === 1) {
|
|
19
|
+
// remove the paragraph token immediately after the list_item token
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
else if (token.nesting === -1 && depth === 0) {
|
|
23
|
+
// remove the ending paragraph token; reset depth
|
|
24
|
+
depth = null;
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Linking } from 'react-native';
|
|
2
|
+
export default function openUrl(url, customCallback) {
|
|
3
|
+
if (customCallback) {
|
|
4
|
+
const result = customCallback(url);
|
|
5
|
+
if (url && result && typeof result === 'boolean') {
|
|
6
|
+
Linking.openURL(url);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
else if (url) {
|
|
10
|
+
Linking.openURL(url);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import textStyleProps from '../data/textStyleProps';
|
|
2
|
+
export default function removeTextStyleProps(style) {
|
|
3
|
+
const intersection = textStyleProps.filter((value) => Object.keys(style).includes(value));
|
|
4
|
+
const obj = { ...style };
|
|
5
|
+
intersection.forEach((value) => {
|
|
6
|
+
delete obj[value];
|
|
7
|
+
});
|
|
8
|
+
return obj;
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default function renderInlineAsText(tokens) {
|
|
2
|
+
var _a;
|
|
3
|
+
let result = '';
|
|
4
|
+
for (let i = 0, len = tokens.length; i < len; i++) {
|
|
5
|
+
if (tokens[i].type === 'text') {
|
|
6
|
+
result += tokens[i].content;
|
|
7
|
+
}
|
|
8
|
+
else if (tokens[i].type === 'image') {
|
|
9
|
+
result += renderInlineAsText((_a = tokens[i].children) !== null && _a !== void 0 ? _a : []);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function isTextElement(node) {
|
|
2
|
+
if (typeof node.type === 'string') {
|
|
3
|
+
return node.type === 'Text';
|
|
4
|
+
}
|
|
5
|
+
const displayName = node.type.displayName;
|
|
6
|
+
return displayName === 'Text';
|
|
7
|
+
}
|
|
8
|
+
export default function splitTextNonTextNodes(children) {
|
|
9
|
+
return children.reduce((acc, curr) => {
|
|
10
|
+
if (isTextElement(curr)) {
|
|
11
|
+
acc.textNodes.push(curr);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
acc.nonTextNodes.push(curr);
|
|
15
|
+
}
|
|
16
|
+
return acc;
|
|
17
|
+
}, { textNodes: [], nonTextNodes: [] });
|
|
18
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import getUniqueID from './getUniqueID';
|
|
2
|
+
import getTokenTypeByToken from './getTokenTypeByToken';
|
|
3
|
+
function createNode(token, tokenIndex) {
|
|
4
|
+
var _a, _b, _c;
|
|
5
|
+
const type = getTokenTypeByToken(token);
|
|
6
|
+
const content = (_a = token.content) !== null && _a !== void 0 ? _a : '';
|
|
7
|
+
let attributes = {};
|
|
8
|
+
if (token.attrs) {
|
|
9
|
+
attributes = token.attrs.reduce((prev, curr) => {
|
|
10
|
+
const [name, value] = curr;
|
|
11
|
+
return { ...prev, [name]: value };
|
|
12
|
+
}, {});
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
type,
|
|
16
|
+
sourceType: token.type,
|
|
17
|
+
sourceInfo: token.info,
|
|
18
|
+
sourceMeta: token.meta,
|
|
19
|
+
block: token.block,
|
|
20
|
+
markup: (_b = token.markup) !== null && _b !== void 0 ? _b : '',
|
|
21
|
+
key: `${getUniqueID()}_${type}`,
|
|
22
|
+
content,
|
|
23
|
+
tokenIndex,
|
|
24
|
+
index: 0,
|
|
25
|
+
attributes,
|
|
26
|
+
children: tokensToAST((_c = token.children) !== null && _c !== void 0 ? _c : []),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export default function tokensToAST(tokens) {
|
|
30
|
+
var _a;
|
|
31
|
+
let stack = [];
|
|
32
|
+
let children = [];
|
|
33
|
+
if (!tokens || tokens.length === 0) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
37
|
+
const token = tokens[i];
|
|
38
|
+
const astNode = createNode(token, i);
|
|
39
|
+
if (!(astNode.type === 'text' &&
|
|
40
|
+
astNode.children.length === 0 &&
|
|
41
|
+
astNode.content === '')) {
|
|
42
|
+
astNode.index = children.length;
|
|
43
|
+
if (token.nesting === 1) {
|
|
44
|
+
children.push(astNode);
|
|
45
|
+
stack.push(children);
|
|
46
|
+
children = astNode.children;
|
|
47
|
+
}
|
|
48
|
+
else if (token.nesting === -1) {
|
|
49
|
+
children = (_a = stack.pop()) !== null && _a !== void 0 ? _a : [];
|
|
50
|
+
}
|
|
51
|
+
else if (token.nesting === 0) {
|
|
52
|
+
children.push(astNode);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return children;
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|