react-native-nitro-markdown 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -10
- package/android/CMakeLists.txt +1 -1
- package/android/src/main/java/com/margelo/nitro/com/nitromarkdown/HybridMarkdownSession.kt +9 -3
- package/cpp/bindings/HybridMarkdownParser.cpp +4 -2
- package/cpp/core/MD4CParser.cpp +20 -2
- package/cpp/core/MarkdownTypes.hpp +1 -1
- package/lib/commonjs/headless.js +2 -2
- package/lib/commonjs/markdown.js +16 -12
- package/lib/commonjs/markdown.js.map +1 -1
- package/lib/commonjs/renderers/blockquote.js +15 -13
- package/lib/commonjs/renderers/blockquote.js.map +1 -1
- package/lib/commonjs/renderers/code.js +57 -53
- package/lib/commonjs/renderers/code.js.map +1 -1
- package/lib/commonjs/renderers/heading.js +48 -46
- package/lib/commonjs/renderers/heading.js.map +1 -1
- package/lib/commonjs/renderers/horizontal-rule.js +10 -8
- package/lib/commonjs/renderers/horizontal-rule.js.map +1 -1
- package/lib/commonjs/renderers/image.js +12 -3
- package/lib/commonjs/renderers/image.js.map +1 -1
- package/lib/commonjs/renderers/list.js +75 -70
- package/lib/commonjs/renderers/list.js.map +1 -1
- package/lib/commonjs/renderers/math.js +4 -3
- package/lib/commonjs/renderers/math.js.map +1 -1
- package/lib/commonjs/renderers/paragraph.js +15 -13
- package/lib/commonjs/renderers/paragraph.js.map +1 -1
- package/lib/commonjs/renderers/style-cache.js +14 -0
- package/lib/commonjs/renderers/style-cache.js.map +1 -0
- package/lib/commonjs/renderers/table/index.js +7 -4
- package/lib/commonjs/renderers/table/index.js.map +1 -1
- package/lib/module/headless.js +2 -2
- package/lib/module/markdown.js +16 -12
- package/lib/module/markdown.js.map +1 -1
- package/lib/module/renderers/blockquote.js +15 -13
- package/lib/module/renderers/blockquote.js.map +1 -1
- package/lib/module/renderers/code.js +57 -53
- package/lib/module/renderers/code.js.map +1 -1
- package/lib/module/renderers/heading.js +48 -46
- package/lib/module/renderers/heading.js.map +1 -1
- package/lib/module/renderers/horizontal-rule.js +10 -8
- package/lib/module/renderers/horizontal-rule.js.map +1 -1
- package/lib/module/renderers/image.js +13 -4
- package/lib/module/renderers/image.js.map +1 -1
- package/lib/module/renderers/list.js +75 -70
- package/lib/module/renderers/list.js.map +1 -1
- package/lib/module/renderers/math.js +4 -3
- package/lib/module/renderers/math.js.map +1 -1
- package/lib/module/renderers/paragraph.js +15 -13
- package/lib/module/renderers/paragraph.js.map +1 -1
- package/lib/module/renderers/style-cache.js +10 -0
- package/lib/module/renderers/style-cache.js.map +1 -0
- package/lib/module/renderers/table/index.js +7 -4
- package/lib/module/renderers/table/index.js.map +1 -1
- package/lib/typescript/commonjs/Markdown.nitro.d.ts +1 -0
- package/lib/typescript/commonjs/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/headless.d.ts +2 -2
- package/lib/typescript/commonjs/markdown.d.ts +1 -1
- package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/blockquote.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/blockquote.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/heading.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/image.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/list.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/math.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/paragraph.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/style-cache.d.ts +3 -0
- package/lib/typescript/commonjs/renderers/style-cache.d.ts.map +1 -0
- package/lib/typescript/commonjs/renderers/table/index.d.ts.map +1 -1
- package/lib/typescript/module/Markdown.nitro.d.ts +1 -0
- package/lib/typescript/module/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/module/headless.d.ts +2 -2
- package/lib/typescript/module/markdown.d.ts +1 -1
- package/lib/typescript/module/markdown.d.ts.map +1 -1
- package/lib/typescript/module/renderers/blockquote.d.ts +1 -1
- package/lib/typescript/module/renderers/blockquote.d.ts.map +1 -1
- package/lib/typescript/module/renderers/code.d.ts.map +1 -1
- package/lib/typescript/module/renderers/heading.d.ts +1 -1
- package/lib/typescript/module/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/module/renderers/horizontal-rule.d.ts +1 -1
- package/lib/typescript/module/renderers/horizontal-rule.d.ts.map +1 -1
- package/lib/typescript/module/renderers/image.d.ts.map +1 -1
- package/lib/typescript/module/renderers/list.d.ts +1 -1
- package/lib/typescript/module/renderers/list.d.ts.map +1 -1
- package/lib/typescript/module/renderers/math.d.ts +1 -1
- package/lib/typescript/module/renderers/math.d.ts.map +1 -1
- package/lib/typescript/module/renderers/paragraph.d.ts +1 -1
- package/lib/typescript/module/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/module/renderers/style-cache.d.ts +3 -0
- package/lib/typescript/module/renderers/style-cache.d.ts.map +1 -0
- package/lib/typescript/module/renderers/table/index.d.ts.map +1 -1
- package/nitro.json +12 -3
- package/nitrogen/generated/android/NitroMarkdownOnLoad.cpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +2 -2
- package/nitrogen/generated/android/c++/JHybridMarkdownSessionSpec.hpp +2 -2
- package/nitrogen/generated/ios/NitroMarkdown+autolinking.rb +2 -0
- package/nitrogen/generated/shared/c++/ParserOptions.hpp +6 -2
- package/package.json +5 -5
- package/react-native-nitro-markdown.podspec +3 -0
- package/src/Markdown.nitro.ts +1 -0
- package/src/headless.ts +2 -2
- package/src/markdown.tsx +30 -12
- package/src/renderers/blockquote.tsx +22 -17
- package/src/renderers/code.tsx +75 -63
- package/src/renderers/heading.tsx +60 -54
- package/src/renderers/horizontal-rule.tsx +17 -12
- package/src/renderers/image.tsx +15 -4
- package/src/renderers/list.tsx +93 -76
- package/src/renderers/math.tsx +8 -3
- package/src/renderers/paragraph.tsx +22 -17
- package/src/renderers/style-cache.ts +14 -0
- package/src/renderers/table/index.tsx +15 -10
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FC, ReactNode } from "react";
|
|
2
2
|
import { Text, StyleSheet, Platform, type TextStyle } from "react-native";
|
|
3
|
+
import { getCachedStyles } from "./style-cache";
|
|
3
4
|
import { useMarkdownContext } from "../MarkdownContext";
|
|
5
|
+
import type { MarkdownTheme } from "../theme";
|
|
4
6
|
|
|
5
7
|
type HeadingProps = {
|
|
6
8
|
level: number;
|
|
@@ -20,59 +22,7 @@ const ANDROID_SYSTEM_FONTS = new Set([
|
|
|
20
22
|
|
|
21
23
|
export const Heading: FC<HeadingProps> = ({ level, children, style }) => {
|
|
22
24
|
const { theme } = useMarkdownContext();
|
|
23
|
-
const
|
|
24
|
-
theme.headingWeight ??
|
|
25
|
-
(Platform.OS === "android" &&
|
|
26
|
-
theme.fontFamilies.heading &&
|
|
27
|
-
!ANDROID_SYSTEM_FONTS.has(theme.fontFamilies.heading)
|
|
28
|
-
? "normal"
|
|
29
|
-
: "700");
|
|
30
|
-
const styles = useMemo(
|
|
31
|
-
() =>
|
|
32
|
-
StyleSheet.create({
|
|
33
|
-
heading: {
|
|
34
|
-
color: theme.colors.heading,
|
|
35
|
-
fontWeight: headingWeight,
|
|
36
|
-
marginTop: theme.spacing.xl,
|
|
37
|
-
marginBottom: theme.spacing.m,
|
|
38
|
-
fontFamily: theme.fontFamilies.heading,
|
|
39
|
-
letterSpacing: -0.2,
|
|
40
|
-
...(Platform.OS === "android" && { includeFontPadding: false }),
|
|
41
|
-
},
|
|
42
|
-
h1: {
|
|
43
|
-
fontSize: theme.fontSizes.h1,
|
|
44
|
-
lineHeight: theme.fontSizes.h1 * 1.3,
|
|
45
|
-
borderBottomWidth: 1,
|
|
46
|
-
borderBottomColor: theme.colors.border,
|
|
47
|
-
paddingBottom: theme.spacing.s,
|
|
48
|
-
letterSpacing: -0.6,
|
|
49
|
-
},
|
|
50
|
-
h2: {
|
|
51
|
-
fontSize: theme.fontSizes.h2,
|
|
52
|
-
lineHeight: theme.fontSizes.h2 * 1.3,
|
|
53
|
-
letterSpacing: -0.4,
|
|
54
|
-
},
|
|
55
|
-
h3: {
|
|
56
|
-
fontSize: theme.fontSizes.h3,
|
|
57
|
-
lineHeight: theme.fontSizes.h3 * 1.3,
|
|
58
|
-
letterSpacing: -0.2,
|
|
59
|
-
},
|
|
60
|
-
h4: {
|
|
61
|
-
fontSize: theme.fontSizes.h4,
|
|
62
|
-
lineHeight: theme.fontSizes.h4 * 1.3,
|
|
63
|
-
},
|
|
64
|
-
h5: {
|
|
65
|
-
fontSize: theme.fontSizes.h5,
|
|
66
|
-
lineHeight: theme.fontSizes.h5 * 1.3,
|
|
67
|
-
},
|
|
68
|
-
h6: {
|
|
69
|
-
fontSize: theme.fontSizes.h6,
|
|
70
|
-
lineHeight: theme.fontSizes.h6 * 1.3,
|
|
71
|
-
color: theme.colors.textMuted,
|
|
72
|
-
},
|
|
73
|
-
}),
|
|
74
|
-
[headingWeight, theme],
|
|
75
|
-
);
|
|
25
|
+
const styles = getCachedStyles(stylesCache, theme, createStyles);
|
|
76
26
|
|
|
77
27
|
const headingStyles = [
|
|
78
28
|
styles.heading,
|
|
@@ -86,3 +36,59 @@ export const Heading: FC<HeadingProps> = ({ level, children, style }) => {
|
|
|
86
36
|
|
|
87
37
|
return <Text style={[...headingStyles, style]}>{children}</Text>;
|
|
88
38
|
};
|
|
39
|
+
|
|
40
|
+
type HeadingStyles = ReturnType<typeof createStyles>;
|
|
41
|
+
|
|
42
|
+
const stylesCache = new WeakMap<MarkdownTheme, HeadingStyles>();
|
|
43
|
+
|
|
44
|
+
const getHeadingWeight = (theme: MarkdownTheme) =>
|
|
45
|
+
theme.headingWeight ??
|
|
46
|
+
(Platform.OS === "android" &&
|
|
47
|
+
theme.fontFamilies.heading &&
|
|
48
|
+
!ANDROID_SYSTEM_FONTS.has(theme.fontFamilies.heading)
|
|
49
|
+
? "normal"
|
|
50
|
+
: "700");
|
|
51
|
+
|
|
52
|
+
const createStyles = (theme: MarkdownTheme) =>
|
|
53
|
+
StyleSheet.create({
|
|
54
|
+
heading: {
|
|
55
|
+
color: theme.colors.heading,
|
|
56
|
+
fontWeight: getHeadingWeight(theme),
|
|
57
|
+
marginTop: theme.spacing.xl,
|
|
58
|
+
marginBottom: theme.spacing.m,
|
|
59
|
+
fontFamily: theme.fontFamilies.heading,
|
|
60
|
+
letterSpacing: -0.2,
|
|
61
|
+
...(Platform.OS === "android" && { includeFontPadding: false }),
|
|
62
|
+
},
|
|
63
|
+
h1: {
|
|
64
|
+
fontSize: theme.fontSizes.h1,
|
|
65
|
+
lineHeight: theme.fontSizes.h1 * 1.3,
|
|
66
|
+
borderBottomWidth: 1,
|
|
67
|
+
borderBottomColor: theme.colors.border,
|
|
68
|
+
paddingBottom: theme.spacing.s,
|
|
69
|
+
letterSpacing: -0.6,
|
|
70
|
+
},
|
|
71
|
+
h2: {
|
|
72
|
+
fontSize: theme.fontSizes.h2,
|
|
73
|
+
lineHeight: theme.fontSizes.h2 * 1.3,
|
|
74
|
+
letterSpacing: -0.4,
|
|
75
|
+
},
|
|
76
|
+
h3: {
|
|
77
|
+
fontSize: theme.fontSizes.h3,
|
|
78
|
+
lineHeight: theme.fontSizes.h3 * 1.3,
|
|
79
|
+
letterSpacing: -0.2,
|
|
80
|
+
},
|
|
81
|
+
h4: {
|
|
82
|
+
fontSize: theme.fontSizes.h4,
|
|
83
|
+
lineHeight: theme.fontSizes.h4 * 1.3,
|
|
84
|
+
},
|
|
85
|
+
h5: {
|
|
86
|
+
fontSize: theme.fontSizes.h5,
|
|
87
|
+
lineHeight: theme.fontSizes.h5 * 1.3,
|
|
88
|
+
},
|
|
89
|
+
h6: {
|
|
90
|
+
fontSize: theme.fontSizes.h6,
|
|
91
|
+
lineHeight: theme.fontSizes.h6 * 1.3,
|
|
92
|
+
color: theme.colors.textMuted,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FC } from "react";
|
|
2
2
|
import { View, StyleSheet, type ViewStyle } from "react-native";
|
|
3
|
+
import { getCachedStyles } from "./style-cache";
|
|
3
4
|
import { useMarkdownContext } from "../MarkdownContext";
|
|
5
|
+
import type { MarkdownTheme } from "../theme";
|
|
4
6
|
|
|
5
7
|
type HorizontalRuleProps = {
|
|
6
8
|
style?: ViewStyle;
|
|
@@ -8,16 +10,19 @@ type HorizontalRuleProps = {
|
|
|
8
10
|
|
|
9
11
|
export const HorizontalRule: FC<HorizontalRuleProps> = ({ style }) => {
|
|
10
12
|
const { theme } = useMarkdownContext();
|
|
11
|
-
const styles =
|
|
12
|
-
() =>
|
|
13
|
-
StyleSheet.create({
|
|
14
|
-
horizontalRule: {
|
|
15
|
-
height: 1,
|
|
16
|
-
backgroundColor: theme.colors.border,
|
|
17
|
-
marginVertical: theme.spacing.xl,
|
|
18
|
-
},
|
|
19
|
-
}),
|
|
20
|
-
[theme],
|
|
21
|
-
);
|
|
13
|
+
const styles = getCachedStyles(stylesCache, theme, createStyles);
|
|
22
14
|
return <View style={[styles.horizontalRule, style]} />;
|
|
23
15
|
};
|
|
16
|
+
|
|
17
|
+
type HorizontalRuleStyles = ReturnType<typeof createStyles>;
|
|
18
|
+
|
|
19
|
+
const stylesCache = new WeakMap<MarkdownTheme, HorizontalRuleStyles>();
|
|
20
|
+
|
|
21
|
+
const createStyles = (theme: MarkdownTheme) =>
|
|
22
|
+
StyleSheet.create({
|
|
23
|
+
horizontalRule: {
|
|
24
|
+
height: 1,
|
|
25
|
+
backgroundColor: theme.colors.border,
|
|
26
|
+
marginVertical: theme.spacing.xl,
|
|
27
|
+
},
|
|
28
|
+
});
|
package/src/renderers/image.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useState,
|
|
3
|
-
|
|
3
|
+
useEffect,
|
|
4
4
|
useMemo,
|
|
5
5
|
type ReactNode,
|
|
6
6
|
type FC,
|
|
@@ -110,21 +110,28 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer, style }) => {
|
|
|
110
110
|
[theme, aspectRatio],
|
|
111
111
|
);
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
let isMounted = true;
|
|
115
|
+
setLoading(true);
|
|
116
|
+
setError(false);
|
|
117
|
+
setAspectRatio(undefined);
|
|
118
|
+
|
|
115
119
|
const picsumMatch = url.match(/picsum\.photos\/.*\/(\d+)\/(\d+)/);
|
|
116
120
|
if (picsumMatch) {
|
|
117
121
|
const w = parseInt(picsumMatch[1], 10);
|
|
118
122
|
const h = parseInt(picsumMatch[2], 10);
|
|
119
123
|
if (!isNaN(w) && !isNaN(h) && h !== 0) {
|
|
120
124
|
setAspectRatio(w / h);
|
|
125
|
+
return () => {
|
|
126
|
+
isMounted = false;
|
|
127
|
+
};
|
|
121
128
|
}
|
|
122
129
|
}
|
|
123
130
|
|
|
124
131
|
RNImage.getSize(
|
|
125
132
|
url,
|
|
126
133
|
(width, height) => {
|
|
127
|
-
if (width > 0 && height > 0) {
|
|
134
|
+
if (isMounted && width > 0 && height > 0) {
|
|
128
135
|
setAspectRatio(width / height);
|
|
129
136
|
}
|
|
130
137
|
},
|
|
@@ -138,6 +145,10 @@ export const Image: FC<ImageProps> = ({ url, title, alt, Renderer, style }) => {
|
|
|
138
145
|
}
|
|
139
146
|
},
|
|
140
147
|
);
|
|
148
|
+
|
|
149
|
+
return () => {
|
|
150
|
+
isMounted = false;
|
|
151
|
+
};
|
|
141
152
|
}, [url]);
|
|
142
153
|
|
|
143
154
|
const altContent = useMemo(() => {
|
package/src/renderers/list.tsx
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FC, ReactNode } from "react";
|
|
2
2
|
import { View, Text, StyleSheet, Platform, type ViewStyle } from "react-native";
|
|
3
|
+
import { getCachedStyles } from "./style-cache";
|
|
3
4
|
import { useMarkdownContext } from "../MarkdownContext";
|
|
5
|
+
import type { MarkdownTheme } from "../theme";
|
|
4
6
|
|
|
5
7
|
type ListProps = {
|
|
6
8
|
ordered: boolean;
|
|
@@ -10,23 +12,10 @@ type ListProps = {
|
|
|
10
12
|
style?: ViewStyle;
|
|
11
13
|
};
|
|
12
14
|
|
|
13
|
-
//
|
|
14
|
-
// handled at the ListItem level — the List container is style-only.
|
|
15
|
+
// ordered/start stay on List for renderer API parity; markers render in ListItem.
|
|
15
16
|
export const List: FC<ListProps> = ({ depth, children, style }) => {
|
|
16
17
|
const { theme } = useMarkdownContext();
|
|
17
|
-
const styles =
|
|
18
|
-
() =>
|
|
19
|
-
StyleSheet.create({
|
|
20
|
-
list: {
|
|
21
|
-
marginBottom: theme.spacing.m,
|
|
22
|
-
},
|
|
23
|
-
listNested: {
|
|
24
|
-
marginLeft: theme.spacing.s,
|
|
25
|
-
marginBottom: 0,
|
|
26
|
-
},
|
|
27
|
-
}),
|
|
28
|
-
[theme],
|
|
29
|
-
);
|
|
18
|
+
const styles = getCachedStyles(listStylesCache, theme, createListStyles);
|
|
30
19
|
return (
|
|
31
20
|
<View style={[styles.list, depth > 0 && styles.listNested, style]}>
|
|
32
21
|
{children}
|
|
@@ -50,30 +39,10 @@ export const ListItem: FC<ListItemProps> = ({
|
|
|
50
39
|
style,
|
|
51
40
|
}) => {
|
|
52
41
|
const { theme } = useMarkdownContext();
|
|
53
|
-
const styles =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
flexDirection: "row",
|
|
58
|
-
marginBottom: theme.spacing.s,
|
|
59
|
-
alignItems: "flex-start",
|
|
60
|
-
},
|
|
61
|
-
listBullet: {
|
|
62
|
-
color: theme.colors.accent,
|
|
63
|
-
fontSize: theme.fontSizes.m,
|
|
64
|
-
lineHeight: theme.fontSizes.m * 1.6,
|
|
65
|
-
marginRight: theme.spacing.s,
|
|
66
|
-
minWidth: 22,
|
|
67
|
-
textAlign: "center",
|
|
68
|
-
fontFamily: theme.fontFamilies.regular,
|
|
69
|
-
...(Platform.OS === "android" && { includeFontPadding: false }),
|
|
70
|
-
},
|
|
71
|
-
listItemContent: {
|
|
72
|
-
flex: 1,
|
|
73
|
-
minWidth: 0,
|
|
74
|
-
},
|
|
75
|
-
}),
|
|
76
|
-
[theme],
|
|
42
|
+
const styles = getCachedStyles(
|
|
43
|
+
listItemStylesCache,
|
|
44
|
+
theme,
|
|
45
|
+
createListItemStyles,
|
|
77
46
|
);
|
|
78
47
|
const bullet = ordered ? `${start + index}.` : "•";
|
|
79
48
|
return (
|
|
@@ -96,42 +65,10 @@ export const TaskListItem: FC<TaskListItemProps> = ({
|
|
|
96
65
|
style,
|
|
97
66
|
}) => {
|
|
98
67
|
const { theme } = useMarkdownContext();
|
|
99
|
-
const styles =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
flexDirection: "row",
|
|
104
|
-
alignItems: "flex-start",
|
|
105
|
-
marginBottom: theme.spacing.s,
|
|
106
|
-
},
|
|
107
|
-
taskCheckbox: {
|
|
108
|
-
width: 18,
|
|
109
|
-
height: 18,
|
|
110
|
-
borderRadius: 4,
|
|
111
|
-
borderWidth: 2,
|
|
112
|
-
borderColor: theme.colors.accent,
|
|
113
|
-
alignItems: "center",
|
|
114
|
-
justifyContent: "center",
|
|
115
|
-
marginRight: theme.spacing.s,
|
|
116
|
-
marginTop: 2,
|
|
117
|
-
},
|
|
118
|
-
taskCheckboxChecked: {
|
|
119
|
-
backgroundColor: theme.colors.accent,
|
|
120
|
-
},
|
|
121
|
-
taskCheckboxText: {
|
|
122
|
-
color: theme.colors.surface,
|
|
123
|
-
fontSize: 12,
|
|
124
|
-
lineHeight: 12,
|
|
125
|
-
fontWeight: "700",
|
|
126
|
-
fontFamily: theme.fontFamilies.regular,
|
|
127
|
-
...(Platform.OS === "android" && { includeFontPadding: false }),
|
|
128
|
-
},
|
|
129
|
-
taskContent: {
|
|
130
|
-
flex: 1,
|
|
131
|
-
minWidth: 0,
|
|
132
|
-
},
|
|
133
|
-
}),
|
|
134
|
-
[theme],
|
|
68
|
+
const styles = getCachedStyles(
|
|
69
|
+
taskListItemStylesCache,
|
|
70
|
+
theme,
|
|
71
|
+
createTaskListItemStyles,
|
|
135
72
|
);
|
|
136
73
|
return (
|
|
137
74
|
<View style={[styles.taskListItem, style]}>
|
|
@@ -144,3 +81,83 @@ export const TaskListItem: FC<TaskListItemProps> = ({
|
|
|
144
81
|
</View>
|
|
145
82
|
);
|
|
146
83
|
};
|
|
84
|
+
|
|
85
|
+
type ListStyles = ReturnType<typeof createListStyles>;
|
|
86
|
+
type ListItemStyles = ReturnType<typeof createListItemStyles>;
|
|
87
|
+
type TaskListItemStyles = ReturnType<typeof createTaskListItemStyles>;
|
|
88
|
+
|
|
89
|
+
const listStylesCache = new WeakMap<MarkdownTheme, ListStyles>();
|
|
90
|
+
const listItemStylesCache = new WeakMap<MarkdownTheme, ListItemStyles>();
|
|
91
|
+
const taskListItemStylesCache = new WeakMap<
|
|
92
|
+
MarkdownTheme,
|
|
93
|
+
TaskListItemStyles
|
|
94
|
+
>();
|
|
95
|
+
|
|
96
|
+
const createListStyles = (theme: MarkdownTheme) =>
|
|
97
|
+
StyleSheet.create({
|
|
98
|
+
list: {
|
|
99
|
+
marginBottom: theme.spacing.m,
|
|
100
|
+
},
|
|
101
|
+
listNested: {
|
|
102
|
+
marginLeft: theme.spacing.s,
|
|
103
|
+
marginBottom: 0,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const createListItemStyles = (theme: MarkdownTheme) =>
|
|
108
|
+
StyleSheet.create({
|
|
109
|
+
listItem: {
|
|
110
|
+
flexDirection: "row",
|
|
111
|
+
marginBottom: theme.spacing.s,
|
|
112
|
+
alignItems: "flex-start",
|
|
113
|
+
},
|
|
114
|
+
listBullet: {
|
|
115
|
+
color: theme.colors.accent,
|
|
116
|
+
fontSize: theme.fontSizes.m,
|
|
117
|
+
lineHeight: theme.fontSizes.m * 1.6,
|
|
118
|
+
marginRight: theme.spacing.s,
|
|
119
|
+
minWidth: 22,
|
|
120
|
+
textAlign: "center",
|
|
121
|
+
fontFamily: theme.fontFamilies.regular,
|
|
122
|
+
...(Platform.OS === "android" && { includeFontPadding: false }),
|
|
123
|
+
},
|
|
124
|
+
listItemContent: {
|
|
125
|
+
flex: 1,
|
|
126
|
+
minWidth: 0,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const createTaskListItemStyles = (theme: MarkdownTheme) =>
|
|
131
|
+
StyleSheet.create({
|
|
132
|
+
taskListItem: {
|
|
133
|
+
flexDirection: "row",
|
|
134
|
+
alignItems: "flex-start",
|
|
135
|
+
marginBottom: theme.spacing.s,
|
|
136
|
+
},
|
|
137
|
+
taskCheckbox: {
|
|
138
|
+
width: 18,
|
|
139
|
+
height: 18,
|
|
140
|
+
borderRadius: 4,
|
|
141
|
+
borderWidth: 2,
|
|
142
|
+
borderColor: theme.colors.accent,
|
|
143
|
+
alignItems: "center",
|
|
144
|
+
justifyContent: "center",
|
|
145
|
+
marginRight: theme.spacing.s,
|
|
146
|
+
marginTop: 2,
|
|
147
|
+
},
|
|
148
|
+
taskCheckboxChecked: {
|
|
149
|
+
backgroundColor: theme.colors.accent,
|
|
150
|
+
},
|
|
151
|
+
taskCheckboxText: {
|
|
152
|
+
color: theme.colors.surface,
|
|
153
|
+
fontSize: 12,
|
|
154
|
+
lineHeight: 12,
|
|
155
|
+
fontWeight: "700",
|
|
156
|
+
fontFamily: theme.fontFamilies.regular,
|
|
157
|
+
...(Platform.OS === "android" && { includeFontPadding: false }),
|
|
158
|
+
},
|
|
159
|
+
taskContent: {
|
|
160
|
+
flex: 1,
|
|
161
|
+
minWidth: 0,
|
|
162
|
+
},
|
|
163
|
+
});
|
package/src/renderers/math.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FC, ComponentType } from "react";
|
|
2
2
|
import {
|
|
3
3
|
View,
|
|
4
4
|
Text,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type StyleProp,
|
|
8
8
|
type ViewStyle,
|
|
9
9
|
} from "react-native";
|
|
10
|
+
import { getCachedStyles } from "./style-cache";
|
|
10
11
|
import { useMarkdownContext } from "../MarkdownContext";
|
|
11
12
|
import type { MarkdownTheme } from "../theme";
|
|
12
13
|
|
|
@@ -36,6 +37,10 @@ type MathInlineProps = {
|
|
|
36
37
|
style?: ViewStyle;
|
|
37
38
|
};
|
|
38
39
|
|
|
40
|
+
type MathStyles = ReturnType<typeof createMathStyles>;
|
|
41
|
+
|
|
42
|
+
const mathStylesCache = new WeakMap<MarkdownTheme, MathStyles>();
|
|
43
|
+
|
|
39
44
|
const createMathStyles = (theme: MarkdownTheme) =>
|
|
40
45
|
StyleSheet.create({
|
|
41
46
|
mathInlineContainer: {
|
|
@@ -92,7 +97,7 @@ const createMathStyles = (theme: MarkdownTheme) =>
|
|
|
92
97
|
|
|
93
98
|
export const MathInline: FC<MathInlineProps> = ({ content, style }) => {
|
|
94
99
|
const { theme } = useMarkdownContext();
|
|
95
|
-
const styles =
|
|
100
|
+
const styles = getCachedStyles(mathStylesCache, theme, createMathStyles);
|
|
96
101
|
|
|
97
102
|
if (!content) return null;
|
|
98
103
|
|
|
@@ -126,7 +131,7 @@ type MathBlockProps = {
|
|
|
126
131
|
|
|
127
132
|
export const MathBlock: FC<MathBlockProps> = ({ content, style }) => {
|
|
128
133
|
const { theme } = useMarkdownContext();
|
|
129
|
-
const styles =
|
|
134
|
+
const styles = getCachedStyles(mathStylesCache, theme, createMathStyles);
|
|
130
135
|
|
|
131
136
|
if (!content) return null;
|
|
132
137
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { FC, ReactNode } from "react";
|
|
2
2
|
import { View, StyleSheet, type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import { getCachedStyles } from "./style-cache";
|
|
3
4
|
import { useMarkdownContext } from "../MarkdownContext";
|
|
5
|
+
import type { MarkdownTheme } from "../theme";
|
|
4
6
|
|
|
5
7
|
type ParagraphProps = {
|
|
6
8
|
children: ReactNode;
|
|
@@ -14,22 +16,7 @@ export const Paragraph: FC<ParagraphProps> = ({
|
|
|
14
16
|
style,
|
|
15
17
|
}) => {
|
|
16
18
|
const { theme } = useMarkdownContext();
|
|
17
|
-
const styles =
|
|
18
|
-
() =>
|
|
19
|
-
StyleSheet.create({
|
|
20
|
-
paragraph: {
|
|
21
|
-
flexDirection: "row",
|
|
22
|
-
flexWrap: "wrap",
|
|
23
|
-
marginBottom: theme.spacing.l,
|
|
24
|
-
gap: 0,
|
|
25
|
-
},
|
|
26
|
-
paragraphInListItem: {
|
|
27
|
-
marginBottom: 0,
|
|
28
|
-
marginTop: 0,
|
|
29
|
-
},
|
|
30
|
-
}),
|
|
31
|
-
[theme],
|
|
32
|
-
);
|
|
19
|
+
const styles = getCachedStyles(stylesCache, theme, createStyles);
|
|
33
20
|
|
|
34
21
|
return (
|
|
35
22
|
<View
|
|
@@ -43,3 +30,21 @@ export const Paragraph: FC<ParagraphProps> = ({
|
|
|
43
30
|
</View>
|
|
44
31
|
);
|
|
45
32
|
};
|
|
33
|
+
|
|
34
|
+
type ParagraphStyles = ReturnType<typeof createStyles>;
|
|
35
|
+
|
|
36
|
+
const stylesCache = new WeakMap<MarkdownTheme, ParagraphStyles>();
|
|
37
|
+
|
|
38
|
+
const createStyles = (theme: MarkdownTheme) =>
|
|
39
|
+
StyleSheet.create({
|
|
40
|
+
paragraph: {
|
|
41
|
+
flexDirection: "row",
|
|
42
|
+
flexWrap: "wrap",
|
|
43
|
+
marginBottom: theme.spacing.l,
|
|
44
|
+
gap: 0,
|
|
45
|
+
},
|
|
46
|
+
paragraphInListItem: {
|
|
47
|
+
marginBottom: 0,
|
|
48
|
+
marginTop: 0,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MarkdownTheme } from "../theme";
|
|
2
|
+
|
|
3
|
+
export function getCachedStyles<T>(
|
|
4
|
+
cache: WeakMap<MarkdownTheme, T>,
|
|
5
|
+
theme: MarkdownTheme,
|
|
6
|
+
createStyles: (theme: MarkdownTheme) => T,
|
|
7
|
+
): T {
|
|
8
|
+
const cached = cache.get(theme);
|
|
9
|
+
if (cached) return cached;
|
|
10
|
+
|
|
11
|
+
const styles = createStyles(theme);
|
|
12
|
+
cache.set(theme, styles);
|
|
13
|
+
return styles;
|
|
14
|
+
}
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
useMarkdownContext,
|
|
27
27
|
type NodeRendererProps,
|
|
28
28
|
} from "../../MarkdownContext";
|
|
29
|
+
import { getCachedStyles } from "../style-cache";
|
|
29
30
|
import type { MarkdownTheme } from "../../theme";
|
|
30
31
|
|
|
31
32
|
type TableRendererProps = {
|
|
@@ -58,7 +59,7 @@ export const TableRenderer: FC<TableRendererProps> = ({
|
|
|
58
59
|
);
|
|
59
60
|
|
|
60
61
|
const columnCount = headers.length;
|
|
61
|
-
const styles =
|
|
62
|
+
const styles = getCachedStyles(tableStylesCache, theme, createTableStyles);
|
|
62
63
|
const estimatedColumnWidths = useMemo(
|
|
63
64
|
() => estimateColumnWidths(headers, rows, columnCount, minColumnWidth),
|
|
64
65
|
[headers, rows, columnCount, minColumnWidth],
|
|
@@ -213,6 +214,14 @@ export const TableRenderer: FC<TableRendererProps> = ({
|
|
|
213
214
|
return "flex-start";
|
|
214
215
|
};
|
|
215
216
|
|
|
217
|
+
const tableBackgroundStyle = useMemo(
|
|
218
|
+
() => ({
|
|
219
|
+
backgroundColor:
|
|
220
|
+
style?.backgroundColor ?? theme.colors.surface ?? "#111827",
|
|
221
|
+
}),
|
|
222
|
+
[style, theme.colors.surface],
|
|
223
|
+
);
|
|
224
|
+
|
|
216
225
|
if (columnCount === 0) return null;
|
|
217
226
|
|
|
218
227
|
const hasWidths = columnWidths.length === columnCount;
|
|
@@ -269,15 +278,7 @@ export const TableRenderer: FC<TableRendererProps> = ({
|
|
|
269
278
|
style={styles.tableScroll}
|
|
270
279
|
bounces={false}
|
|
271
280
|
>
|
|
272
|
-
<View
|
|
273
|
-
style={[
|
|
274
|
-
styles.table,
|
|
275
|
-
{
|
|
276
|
-
backgroundColor:
|
|
277
|
-
style?.backgroundColor ?? theme.colors.surface ?? "#111827",
|
|
278
|
-
},
|
|
279
|
-
]}
|
|
280
|
-
>
|
|
281
|
+
<View style={[styles.table, tableBackgroundStyle]}>
|
|
281
282
|
<View style={styles.headerRow}>
|
|
282
283
|
{headers.map((cell, colIndex) => (
|
|
283
284
|
<View
|
|
@@ -338,6 +339,10 @@ export const TableRenderer: FC<TableRendererProps> = ({
|
|
|
338
339
|
);
|
|
339
340
|
};
|
|
340
341
|
|
|
342
|
+
type TableStyles = ReturnType<typeof createTableStyles>;
|
|
343
|
+
|
|
344
|
+
const tableStylesCache = new WeakMap<MarkdownTheme, TableStyles>();
|
|
345
|
+
|
|
341
346
|
const createTableStyles = (theme: MarkdownTheme) => {
|
|
342
347
|
const colors = theme?.colors || {};
|
|
343
348
|
const borderRadius = theme?.borderRadius || { m: 8 };
|