|
@@ -1,178 +1,497 @@
|
|
|
-import type {
|
|
|
|
|
- FC
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ useEffect,
|
|
|
|
|
+ useState,
|
|
|
|
|
+ type FC
|
|
|
} from "react";
|
|
} from "react";
|
|
|
import {
|
|
import {
|
|
|
Text as NativeText,
|
|
Text as NativeText,
|
|
|
|
|
+ TouchableOpacity,
|
|
|
|
|
+ Linking,
|
|
|
Image,
|
|
Image,
|
|
|
View
|
|
View
|
|
|
} from "react-native";
|
|
} from "react-native";
|
|
|
import type IMarkdownViewerProps from "./type";
|
|
import type IMarkdownViewerProps from "./type";
|
|
|
-import stylesheet from "./stylesheet";
|
|
|
|
|
|
|
+import type {
|
|
|
|
|
+ MarkdownObject,
|
|
|
|
|
+ TextMarkdownTypes
|
|
|
|
|
+} from "./type";
|
|
|
|
|
+import stylesheet, {
|
|
|
|
|
+ useStyles
|
|
|
|
|
+} from "./stylesheet";
|
|
|
|
|
+import {
|
|
|
|
|
+ parseMarkdown
|
|
|
|
|
+} from "./util";
|
|
|
|
|
+import {
|
|
|
|
|
+ NCoreUIKitTheme
|
|
|
|
|
+} from "../../core/hooks";
|
|
|
|
|
+import type ITextProps from "../text/type";
|
|
|
|
|
+import {
|
|
|
|
|
+ Quote as QuoteIcon
|
|
|
|
|
+} from "lucide-react-native";
|
|
|
import Text from "../text";
|
|
import Text from "../text";
|
|
|
|
|
|
|
|
-type MarkdownKeys = "# " |
|
|
|
|
|
- "## " |
|
|
|
|
|
- "### " |
|
|
|
|
|
- "#### " |
|
|
|
|
|
- "##### " |
|
|
|
|
|
- "###### " |
|
|
|
|
|
- "* " |
|
|
|
|
|
- "- " |
|
|
|
|
|
- "p";
|
|
|
|
|
-
|
|
|
|
|
-const DEFAULT_VARIANTS: Record<MarkdownKeys, keyof NCoreUIKit.Typography> = {
|
|
|
|
|
- "# ": "displayLargeSize",
|
|
|
|
|
- "## ": "displayMediumSize",
|
|
|
|
|
- "### ": "displaySmallSize",
|
|
|
|
|
- "#### ": "headlineLargeSize",
|
|
|
|
|
- "##### ": "headlineMediumSize",
|
|
|
|
|
- "###### ": "headlineSmallSize",
|
|
|
|
|
- "* ": "bodyLargeSize",
|
|
|
|
|
- "- ": "bodyLargeSize",
|
|
|
|
|
- "p": "bodyMediumSize"
|
|
|
|
|
-};
|
|
|
|
|
|
|
+const MarkdownViewer: FC<IMarkdownViewerProps> = ({
|
|
|
|
|
+ blockquoteIconSize = "titleLargeSize",
|
|
|
|
|
+ codeTextColor = "emphasized",
|
|
|
|
|
+ blockquoteIconColor = "mid",
|
|
|
|
|
+ blockquoteTextColor = "mid",
|
|
|
|
|
+ blockquoteContainerProps,
|
|
|
|
|
+ centerContainerProps,
|
|
|
|
|
+ blockquoteTextStyle,
|
|
|
|
|
+ rightContainerProps,
|
|
|
|
|
+ blockquoteIconStyle,
|
|
|
|
|
+ centerMarkdownProps,
|
|
|
|
|
+ leftContainerProps,
|
|
|
|
|
+ rightMarkdownProps,
|
|
|
|
|
+ listContainerProps,
|
|
|
|
|
+ linkContainerProps,
|
|
|
|
|
+ codeContainerProps,
|
|
|
|
|
+ leftMarkdownProps,
|
|
|
|
|
+ linkMarkdownProps,
|
|
|
|
|
+ customTextStyles,
|
|
|
|
|
+ customTextProps,
|
|
|
|
|
+ italicTextProps,
|
|
|
|
|
+ enterTextProps,
|
|
|
|
|
+ boldTextProps,
|
|
|
|
|
+ listIconProps,
|
|
|
|
|
+ codeTextProps,
|
|
|
|
|
+ listTextProps,
|
|
|
|
|
+ italicStyle,
|
|
|
|
|
+ imageProps,
|
|
|
|
|
+ boldStyle,
|
|
|
|
|
+ content,
|
|
|
|
|
+ style,
|
|
|
|
|
+ ...props
|
|
|
|
|
+}) => {
|
|
|
|
|
+ const {
|
|
|
|
|
+ typography,
|
|
|
|
|
+ radiuses,
|
|
|
|
|
+ colors,
|
|
|
|
|
+ spaces
|
|
|
|
|
+ } = NCoreUIKitTheme.useContext();
|
|
|
|
|
|
|
|
-const parseMarkdown = (rawText: string): Array<{
|
|
|
|
|
- variant?: keyof NCoreUIKit.Typography;
|
|
|
|
|
- nativeType: MarkdownKeys | "image";
|
|
|
|
|
- type: "text" | "bullet" | "image";
|
|
|
|
|
- content: string;
|
|
|
|
|
- url?: string;
|
|
|
|
|
- key: number;
|
|
|
|
|
-}> => {
|
|
|
|
|
- const lines = rawText.split("\n");
|
|
|
|
|
-
|
|
|
|
|
- return lines.map((line, index) => {
|
|
|
|
|
- if (line.startsWith("# ")) return {
|
|
|
|
|
- content: line.replace("# ", ""),
|
|
|
|
|
- variant: DEFAULT_VARIANTS["# "],
|
|
|
|
|
- nativeType: "# ",
|
|
|
|
|
- type: "text",
|
|
|
|
|
- key: index
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ const [
|
|
|
|
|
+ containerWidth,
|
|
|
|
|
+ setContainerWidth
|
|
|
|
|
+ ] = useState<null | number>(null);
|
|
|
|
|
|
|
|
- if (line.startsWith("## ")) return {
|
|
|
|
|
- content: line.replace("## ", ""),
|
|
|
|
|
- variant: DEFAULT_VARIANTS["## "],
|
|
|
|
|
- nativeType: "## ",
|
|
|
|
|
- type: "text",
|
|
|
|
|
- key: index
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ const {
|
|
|
|
|
+ blockquoteContainer: blockquoteContainerDynamicStyle,
|
|
|
|
|
+ blockquoteIcon: blockquoteIconDynamicStyle,
|
|
|
|
|
+ codeContainer: codeContainerDynamicStyle
|
|
|
|
|
+ } = useStyles({
|
|
|
|
|
+ radiuses,
|
|
|
|
|
+ colors,
|
|
|
|
|
+ spaces
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const nodes = parseMarkdown(content);
|
|
|
|
|
|
|
|
- const isStartWithImage = line.startsWith("[");
|
|
|
|
|
|
|
+ const renderBold = ({
|
|
|
|
|
+ part,
|
|
|
|
|
+ i
|
|
|
|
|
+ }: {
|
|
|
|
|
+ part: string;
|
|
|
|
|
+ i: number;
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ return <NativeText
|
|
|
|
|
+ {...boldTextProps}
|
|
|
|
|
+ key={`bold-${i}`}
|
|
|
|
|
+ style={{
|
|
|
|
|
+ ...stylesheet.bold,
|
|
|
|
|
+ ...boldStyle
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ {part.replace(/\*\*/g, "")}
|
|
|
|
|
+ </NativeText>;
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- if(isStartWithImage || line.startsWith("[")) {
|
|
|
|
|
- const contentMatch = line.match(/\[(.*?)\]/);
|
|
|
|
|
|
|
+ const renderItalic = ({
|
|
|
|
|
+ part,
|
|
|
|
|
+ i
|
|
|
|
|
+ }: {
|
|
|
|
|
+ part: string;
|
|
|
|
|
+ i: number;
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ return <NativeText
|
|
|
|
|
+ {...italicTextProps}
|
|
|
|
|
+ key={`italic-${i}`}
|
|
|
|
|
+ style={{
|
|
|
|
|
+ ...stylesheet.italic,
|
|
|
|
|
+ ...italicStyle
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ {part.replace(/__/g, "")}
|
|
|
|
|
+ </NativeText>;
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- const imageContent = (contentMatch && contentMatch[1]) ? contentMatch[1] : "";
|
|
|
|
|
|
|
+ const renderInlineStyles = (_content: string) => {
|
|
|
|
|
+ const parts = _content.split(/(\*\*.*?\*\*|__.*?__)/g);
|
|
|
|
|
|
|
|
- const tmpLine = line.replace(/\[.*?\]/g, "");
|
|
|
|
|
|
|
+ return parts.map((part, i) => {
|
|
|
|
|
+ if (part.startsWith("**") && part.endsWith("**")) return renderBold({
|
|
|
|
|
+ part,
|
|
|
|
|
+ i
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (part.startsWith("__") && part.endsWith("__")) return renderItalic({
|
|
|
|
|
+ part,
|
|
|
|
|
+ i
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return part;
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- const urlMatch = tmpLine.match(/\((.*?)\)/);
|
|
|
|
|
|
|
+ const renderText = ({
|
|
|
|
|
+ node
|
|
|
|
|
+ }: {
|
|
|
|
|
+ node: MarkdownObject
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ let textProps: ITextProps = {
|
|
|
|
|
+ variant: node.variant
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- const imageURL = (urlMatch && urlMatch[1]) ? urlMatch[1] : "";
|
|
|
|
|
|
|
+ if(customTextStyles && customTextStyles[node.nativeType as TextMarkdownTypes]) {
|
|
|
|
|
+ const currentTextStyle = customTextStyles[node.nativeType as TextMarkdownTypes];
|
|
|
|
|
|
|
|
- return {
|
|
|
|
|
- content: imageContent,
|
|
|
|
|
- nativeType: "image",
|
|
|
|
|
- type: "image",
|
|
|
|
|
- url: imageURL,
|
|
|
|
|
- key: index
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ textProps.style = currentTextStyle;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const isStarList = line.startsWith("* ");
|
|
|
|
|
|
|
+ if(customTextProps && customTextProps[node.nativeType as TextMarkdownTypes]) {
|
|
|
|
|
+ const currentTextProps = customTextProps[node.nativeType as TextMarkdownTypes];
|
|
|
|
|
|
|
|
- if (isStarList || line.startsWith("- ")) {
|
|
|
|
|
- return {
|
|
|
|
|
- variant: DEFAULT_VARIANTS[isStarList ? "* " : "- "],
|
|
|
|
|
- nativeType: isStarList ? "* " : "- ",
|
|
|
|
|
- content: line.substring(2),
|
|
|
|
|
- type: "bullet",
|
|
|
|
|
- key: index
|
|
|
|
|
|
|
+ textProps = {
|
|
|
|
|
+ ...textProps,
|
|
|
|
|
+ ...currentTextProps
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return {
|
|
|
|
|
- variant: DEFAULT_VARIANTS["p"],
|
|
|
|
|
- nativeType: "p",
|
|
|
|
|
- content: line,
|
|
|
|
|
- type: "text",
|
|
|
|
|
- key: index
|
|
|
|
|
- };
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
|
|
+ return <Text
|
|
|
|
|
+ {...textProps}
|
|
|
|
|
+ key={node.key}
|
|
|
|
|
+ >{renderInlineStyles(node.content)}</Text>;
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
-const MarkdownViewer: FC<IMarkdownViewerProps> = ({
|
|
|
|
|
- content
|
|
|
|
|
-}) => {
|
|
|
|
|
- const nodes = parseMarkdown(content);
|
|
|
|
|
|
|
+ const renderEnter = ({
|
|
|
|
|
+ node
|
|
|
|
|
+ }: {
|
|
|
|
|
+ node: MarkdownObject
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ return <View
|
|
|
|
|
+ {...enterTextProps}
|
|
|
|
|
+ key={node.key}
|
|
|
|
|
+ style={[
|
|
|
|
|
+ stylesheet.enter,
|
|
|
|
|
+ {
|
|
|
|
|
+ height: typography[node.variant as keyof NCoreUIKit.Typography].fontSize
|
|
|
|
|
+ },
|
|
|
|
|
+ enterTextProps?.style
|
|
|
|
|
+ ]}
|
|
|
|
|
+ />;
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- const renderInlineStyles = (_content: string) => {
|
|
|
|
|
- const parts = _content.split(/(\*\*.*?\*\*)/g);
|
|
|
|
|
|
|
+ const renderBlockquote = ({
|
|
|
|
|
+ node
|
|
|
|
|
+ }: {
|
|
|
|
|
+ node: MarkdownObject
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ return <View
|
|
|
|
|
+ {...blockquoteContainerProps}
|
|
|
|
|
+ key={node.key}
|
|
|
|
|
+ style={[
|
|
|
|
|
+ blockquoteContainerDynamicStyle,
|
|
|
|
|
+ blockquoteContainerProps?.style
|
|
|
|
|
+ ]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <QuoteIcon
|
|
|
|
|
+ color={colors.content.icon[blockquoteIconColor]}
|
|
|
|
|
+ size={typography[blockquoteIconSize].fontSize}
|
|
|
|
|
+ style={{
|
|
|
|
|
+ ...blockquoteIconDynamicStyle,
|
|
|
|
|
+ ...blockquoteIconStyle
|
|
|
|
|
+ }}
|
|
|
|
|
+ />
|
|
|
|
|
+ <Text
|
|
|
|
|
+ color={blockquoteTextColor}
|
|
|
|
|
+ variant={node.variant}
|
|
|
|
|
+ style={{
|
|
|
|
|
+ ...stylesheet.blockquoteText,
|
|
|
|
|
+ ...blockquoteTextStyle
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ {renderInlineStyles(node.content)}
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>;
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- return parts.map((part, i) => {
|
|
|
|
|
- if (part.startsWith("**") && part.endsWith("**")) {
|
|
|
|
|
- return (
|
|
|
|
|
- <NativeText
|
|
|
|
|
- key={i}
|
|
|
|
|
- style={{
|
|
|
|
|
- fontWeight: "bold"
|
|
|
|
|
- }}
|
|
|
|
|
- >
|
|
|
|
|
- {part.replace(/\*\*/g, "")}
|
|
|
|
|
- </NativeText>
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ const renderImage = ({
|
|
|
|
|
+ node
|
|
|
|
|
+ }: {
|
|
|
|
|
+ node: MarkdownObject
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ const [
|
|
|
|
|
+ imgSize,
|
|
|
|
|
+ setImgSize
|
|
|
|
|
+ ] = useState<{
|
|
|
|
|
+ height: number | string;
|
|
|
|
|
+ width: number | string;
|
|
|
|
|
+ isSetted: boolean;
|
|
|
|
|
+ }>({
|
|
|
|
|
+ isSetted: false,
|
|
|
|
|
+ width: "100%",
|
|
|
|
|
+ height: 0
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if(!imgSize.isSetted && node.size) {
|
|
|
|
|
+ setImgSize({
|
|
|
|
|
+ height: node.size[1] as number | string,
|
|
|
|
|
+ width: node.size[0] as number | string,
|
|
|
|
|
+ isSetted: true
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
+ }, [imgSize]);
|
|
|
|
|
|
|
|
- return part;
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ const currentWidth = typeof imgSize.width === "number" || imgSize.width.indexOf("%") === -1 || !containerWidth
|
|
|
|
|
+ ? imgSize.width
|
|
|
|
|
+ : (containerWidth * Number(imgSize.width.split("%")[0])) / 100;
|
|
|
|
|
+
|
|
|
|
|
+ const currentHeight = typeof imgSize.height === "number" || imgSize.height.indexOf("%") === -1 || !containerWidth
|
|
|
|
|
+ ? imgSize.height
|
|
|
|
|
+ : (containerWidth * Number(imgSize.height.split("%")[0])) / 100;
|
|
|
|
|
+
|
|
|
|
|
+ return <Image
|
|
|
|
|
+ {...imageProps}
|
|
|
|
|
+ alt={node.content}
|
|
|
|
|
+ key={node.key}
|
|
|
|
|
+ source={{
|
|
|
|
|
+ uri: node.url
|
|
|
|
|
+ }}
|
|
|
|
|
+ resizeMode={node.resizeMode ? node.resizeMode : "contain"}
|
|
|
|
|
+ onLoad={(event) => {
|
|
|
|
|
+ if(!node.size) {
|
|
|
|
|
+ const {
|
|
|
|
|
+ height,
|
|
|
|
|
+ width
|
|
|
|
|
+ } = event.nativeEvent.source;
|
|
|
|
|
+
|
|
|
|
|
+ setImgSize({
|
|
|
|
|
+ isSetted: true,
|
|
|
|
|
+ height,
|
|
|
|
|
+ width
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }}
|
|
|
|
|
+ style={{
|
|
|
|
|
+ ...stylesheet.image,
|
|
|
|
|
+ height: currentHeight,
|
|
|
|
|
+ width: currentWidth,
|
|
|
|
|
+ ...imageProps?.style
|
|
|
|
|
+ }}
|
|
|
|
|
+ />;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const renderList = ({
|
|
|
|
|
+ node
|
|
|
|
|
+ }: {
|
|
|
|
|
+ node: MarkdownObject
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ return <View
|
|
|
|
|
+ {...listContainerProps}
|
|
|
|
|
+ key={node.key}
|
|
|
|
|
+ style={[
|
|
|
|
|
+ stylesheet.listContainer,
|
|
|
|
|
+ listContainerProps?.style
|
|
|
|
|
+ ]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Text
|
|
|
|
|
+ {...listIconProps}
|
|
|
|
|
+ variant={node.variant}
|
|
|
|
|
+ >
|
|
|
|
|
+ •{" "}
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ <Text
|
|
|
|
|
+ {...listTextProps}
|
|
|
|
|
+ variant={node.variant}
|
|
|
|
|
+ >
|
|
|
|
|
+ {renderInlineStyles(node.content)}
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const renderCode = ({
|
|
|
|
|
+ node
|
|
|
|
|
+ }: {
|
|
|
|
|
+ node: MarkdownObject
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ return <View
|
|
|
|
|
+ {...codeContainerProps}
|
|
|
|
|
+ key={node.key}
|
|
|
|
|
+ style={[
|
|
|
|
|
+ codeContainerDynamicStyle,
|
|
|
|
|
+ codeContainerProps?.style
|
|
|
|
|
+ ]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Text
|
|
|
|
|
+ {...codeTextProps}
|
|
|
|
|
+ variant={node.variant}
|
|
|
|
|
+ color={codeTextColor}
|
|
|
|
|
+ >
|
|
|
|
|
+ {node.content}
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const renderLink = ({
|
|
|
|
|
+ node
|
|
|
|
|
+ }: {
|
|
|
|
|
+ node: MarkdownObject
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ return <TouchableOpacity
|
|
|
|
|
+ {...linkContainerProps}
|
|
|
|
|
+ key={node.key}
|
|
|
|
|
+ onPress={() => {
|
|
|
|
|
+ Linking.canOpenURL(node.url as string);
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <MarkdownViewer
|
|
|
|
|
+ {...linkMarkdownProps}
|
|
|
|
|
+ content={node.content}
|
|
|
|
|
+ />
|
|
|
|
|
+ </TouchableOpacity>;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const renderCenter = ({
|
|
|
|
|
+ node
|
|
|
|
|
+ }: {
|
|
|
|
|
+ node: MarkdownObject
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ return <View
|
|
|
|
|
+ {...centerContainerProps}
|
|
|
|
|
+ key={node.key}
|
|
|
|
|
+ style={[
|
|
|
|
|
+ stylesheet.centerContainer,
|
|
|
|
|
+ centerContainerProps?.style
|
|
|
|
|
+ ]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <MarkdownViewer
|
|
|
|
|
+ {...centerMarkdownProps}
|
|
|
|
|
+ content={node.content}
|
|
|
|
|
+ />
|
|
|
|
|
+ </View>;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const renderLeft = ({
|
|
|
|
|
+ node
|
|
|
|
|
+ }: {
|
|
|
|
|
+ node: MarkdownObject
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ return <View
|
|
|
|
|
+ {...leftContainerProps}
|
|
|
|
|
+ key={node.key}
|
|
|
|
|
+ style={[
|
|
|
|
|
+ stylesheet.leftContainer,
|
|
|
|
|
+ leftContainerProps?.style
|
|
|
|
|
+ ]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <MarkdownViewer
|
|
|
|
|
+ {...leftMarkdownProps}
|
|
|
|
|
+ content={node.content}
|
|
|
|
|
+ />
|
|
|
|
|
+ </View>;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const renderRight = ({
|
|
|
|
|
+ node
|
|
|
|
|
+ }: {
|
|
|
|
|
+ node: MarkdownObject
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ return <View
|
|
|
|
|
+ {...rightContainerProps}
|
|
|
|
|
+ key={node.key}
|
|
|
|
|
+ style={[
|
|
|
|
|
+ stylesheet.rightContainer,
|
|
|
|
|
+ rightContainerProps?.style
|
|
|
|
|
+ ]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <MarkdownViewer
|
|
|
|
|
+ {...rightMarkdownProps}
|
|
|
|
|
+ content={node.content}
|
|
|
|
|
+ />
|
|
|
|
|
+ </View>;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
return <View
|
|
return <View
|
|
|
|
|
+ {...props}
|
|
|
|
|
+ onLayout={({
|
|
|
|
|
+ nativeEvent
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ setContainerWidth(nativeEvent.layout.width);
|
|
|
|
|
+ }}
|
|
|
style={[
|
|
style={[
|
|
|
- stylesheet.container
|
|
|
|
|
|
|
+ stylesheet.container,
|
|
|
|
|
+ style
|
|
|
]}
|
|
]}
|
|
|
>
|
|
>
|
|
|
{nodes.map((node) => {
|
|
{nodes.map((node) => {
|
|
|
- if (!node.content.trim() && node.nativeType === "p") return null;
|
|
|
|
|
|
|
+ if (!node.content.trim() && node.nativeType === "<p>") return null;
|
|
|
|
|
|
|
|
switch (node.type) {
|
|
switch (node.type) {
|
|
|
- case "text":
|
|
|
|
|
- return <Text
|
|
|
|
|
- variant={node.variant}
|
|
|
|
|
- key={node.key}
|
|
|
|
|
- >{renderInlineStyles(node.content)}</Text>;
|
|
|
|
|
- case "image":
|
|
|
|
|
- return <Image
|
|
|
|
|
- alt={node.content}
|
|
|
|
|
- key={node.key}
|
|
|
|
|
- source={{
|
|
|
|
|
- uri: node.url
|
|
|
|
|
- }}
|
|
|
|
|
- resizeMode="contain"
|
|
|
|
|
- style={{
|
|
|
|
|
- width: 300,
|
|
|
|
|
- height: 300
|
|
|
|
|
- }}
|
|
|
|
|
- />;
|
|
|
|
|
- case "bullet":
|
|
|
|
|
- return (
|
|
|
|
|
- <View
|
|
|
|
|
- key={node.key}
|
|
|
|
|
- style={{
|
|
|
|
|
- flexDirection: "row",
|
|
|
|
|
- alignItems: "center"
|
|
|
|
|
- }}
|
|
|
|
|
- >
|
|
|
|
|
- <Text variant={node.variant}>• </Text><Text variant={node.variant}>{renderInlineStyles(node.content)}</Text>
|
|
|
|
|
- </View>
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ case undefined:
|
|
|
|
|
+ return null;
|
|
|
|
|
+ case "text": {
|
|
|
|
|
+ return renderText({
|
|
|
|
|
+ node
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ case "space":
|
|
|
|
|
+ return renderEnter({
|
|
|
|
|
+ node
|
|
|
|
|
+ });
|
|
|
|
|
+ case "blockquote": {
|
|
|
|
|
+ return renderBlockquote({
|
|
|
|
|
+ node
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ case "right": {
|
|
|
|
|
+ return renderRight({
|
|
|
|
|
+ node
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ case "left": {
|
|
|
|
|
+ return renderLeft({
|
|
|
|
|
+ node
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ case "center": {
|
|
|
|
|
+ return renderCenter({
|
|
|
|
|
+ node
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ case "link": {
|
|
|
|
|
+ return renderLink({
|
|
|
|
|
+ node
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ case "code": {
|
|
|
|
|
+ return renderCode({
|
|
|
|
|
+ node
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ case "image": {
|
|
|
|
|
+ return renderImage({
|
|
|
|
|
+ node
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ case "list":
|
|
|
|
|
+ return renderList({
|
|
|
|
|
+ node
|
|
|
|
|
+ });
|
|
|
default:
|
|
default:
|
|
|
- return <Text
|
|
|
|
|
- variant={node.variant}
|
|
|
|
|
- key={node.key}
|
|
|
|
|
- >{renderInlineStyles(node.content)}</Text>;
|
|
|
|
|
|
|
+ return renderText({
|
|
|
|
|
+ node
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
})}
|
|
})}
|
|
|
</View>;
|
|
</View>;
|