@@ -9,6 +9,7 @@ import { useRef, useState, RefObject, useEffect } from "react";
9
9
import { copyToClipboard } from "../utils" ;
10
10
11
11
import LoadingIcon from "../icons/three-dots.svg" ;
12
+ import React from "react" ;
12
13
13
14
export function PreCode ( props : { children : any } ) {
14
15
const ref = useRef < HTMLPreElement > ( null ) ;
@@ -29,6 +30,32 @@ export function PreCode(props: { children: any }) {
29
30
) ;
30
31
}
31
32
33
+ function _MarkDownContent ( props : { content : string } ) {
34
+ return (
35
+ < ReactMarkdown
36
+ remarkPlugins = { [ RemarkMath , RemarkGfm , RemarkBreaks ] }
37
+ rehypePlugins = { [
38
+ RehypeKatex ,
39
+ [
40
+ RehypeHighlight ,
41
+ {
42
+ detect : false ,
43
+ ignoreMissing : true ,
44
+ } ,
45
+ ] ,
46
+ ] }
47
+ components = { {
48
+ pre : PreCode ,
49
+ } }
50
+ linkTarget = { "_blank" }
51
+ >
52
+ { props . content }
53
+ </ ReactMarkdown >
54
+ ) ;
55
+ }
56
+
57
+ export const MarkdownContent = React . memo ( _MarkDownContent ) ;
58
+
32
59
export function Markdown (
33
60
props : {
34
61
content : string ;
@@ -38,69 +65,49 @@ export function Markdown(
38
65
} & React . DOMAttributes < HTMLDivElement > ,
39
66
) {
40
67
const mdRef = useRef < HTMLDivElement > ( null ) ;
68
+ const renderedHeight = useRef ( 0 ) ;
69
+ const inView = useRef ( false ) ;
41
70
42
71
const parent = props . parentRef . current ;
43
72
const md = mdRef . current ;
44
- const rendered = useRef ( true ) ; // disable lazy loading for bad ux
45
- const [ counter , setCounter ] = useState ( 0 ) ;
46
-
47
- useEffect ( ( ) => {
48
- // to triggr rerender
49
- setCounter ( counter + 1 ) ;
50
- // eslint-disable-next-line react-hooks/exhaustive-deps
51
- } , [ props . loading ] ) ;
52
-
53
- const inView =
54
- rendered . current ||
55
- ( ( ) => {
56
- if ( parent && md ) {
57
- const parentBounds = parent . getBoundingClientRect ( ) ;
58
- const mdBounds = md . getBoundingClientRect ( ) ;
59
- const isInRange = ( x : number ) =>
60
- x <= parentBounds . bottom && x >= parentBounds . top ;
61
- const inView = isInRange ( mdBounds . top ) || isInRange ( mdBounds . bottom ) ;
62
-
63
- if ( inView ) {
64
- rendered . current = true ;
65
- }
66
73
67
- return inView ;
68
- }
69
- } ) ( ) ;
74
+ if ( parent && md ) {
75
+ const parentBounds = parent . getBoundingClientRect ( ) ;
76
+ const twoScreenHeight = Math . max ( 500 , parentBounds . height * 2 ) ;
77
+ const mdBounds = md . getBoundingClientRect ( ) ;
78
+ const isInRange = ( x : number ) =>
79
+ x <= parentBounds . bottom + twoScreenHeight &&
80
+ x >= parentBounds . top - twoScreenHeight ;
81
+ inView . current = isInRange ( mdBounds . top ) || isInRange ( mdBounds . bottom ) ;
82
+ }
70
83
71
- const shouldLoading = props . loading || ! inView ;
84
+ if ( inView . current && md ) {
85
+ renderedHeight . current = Math . max (
86
+ renderedHeight . current ,
87
+ md . getBoundingClientRect ( ) . height ,
88
+ ) ;
89
+ }
72
90
73
91
return (
74
92
< div
75
93
className = "markdown-body"
76
- style = { { fontSize : `${ props . fontSize ?? 14 } px` } }
94
+ style = { {
95
+ fontSize : `${ props . fontSize ?? 14 } px` ,
96
+ height :
97
+ ! inView . current && renderedHeight . current > 0
98
+ ? renderedHeight . current
99
+ : "auto" ,
100
+ } }
77
101
ref = { mdRef }
78
102
onContextMenu = { props . onContextMenu }
79
103
onDoubleClickCapture = { props . onDoubleClickCapture }
80
104
>
81
- { shouldLoading ? (
82
- < LoadingIcon />
83
- ) : (
84
- < ReactMarkdown
85
- remarkPlugins = { [ RemarkMath , RemarkGfm , RemarkBreaks ] }
86
- rehypePlugins = { [
87
- RehypeKatex ,
88
- [
89
- RehypeHighlight ,
90
- {
91
- detect : false ,
92
- ignoreMissing : true ,
93
- } ,
94
- ] ,
95
- ] }
96
- components = { {
97
- pre : PreCode ,
98
- } }
99
- linkTarget = { "_blank" }
100
- >
101
- { props . content }
102
- </ ReactMarkdown >
103
- ) }
105
+ { inView . current &&
106
+ ( props . loading ? (
107
+ < LoadingIcon />
108
+ ) : (
109
+ < MarkdownContent content = { props . content } />
110
+ ) ) }
104
111
</ div >
105
112
) ;
106
113
}
0 commit comments