| 
1 |  | -import { useLayoutEffect, useRef } from 'react'  | 
 | 1 | +import { useEffect, useRef } from 'react'  | 
2 | 2 | 
 
  | 
3 | 3 | export default function SideBySide({ sideBySideContents }) {  | 
4 | 4 |   const baseTop = useRef(0)  | 
5 |  | -  useLayoutEffect(() => {  | 
 | 5 | +  useEffect(() => {  | 
6 | 6 |     baseTop.current = leftContainer.current.getBoundingClientRect().top  | 
7 | 7 |   }, [])  | 
8 | 8 | 
 
  | 
9 | 9 |   const leftContainer = useRef(null)  | 
10 | 10 |   const rightContainer = useRef(null)  | 
11 |  | - | 
12 |  | -  let timer = null  | 
13 |  | -  let isLeftScroll = false  | 
14 |  | -  let isRightScroll = false  | 
15 |  | -  function handleScroll(type) {  | 
16 |  | -    if (type === 'left') {  | 
17 |  | -      if (isRightScroll) return  | 
18 |  | -      isLeftScroll = true  | 
19 |  | -      clearTimeout(timer)  | 
20 |  | -      timer = setTimeout(() => {  | 
21 |  | -        isLeftScroll = false  | 
22 |  | -      }, 300)  | 
23 |  | -      syncScroll(leftContainer.current, rightContainer.current)  | 
24 |  | -    } else {  | 
25 |  | -      if (isLeftScroll) return  | 
26 |  | -      isRightScroll = true  | 
27 |  | -      clearTimeout(timer)  | 
28 |  | -      timer = setTimeout(() => {  | 
29 |  | -        isRightScroll = false  | 
30 |  | -      }, 300)  | 
31 |  | -      syncScroll(rightContainer.current, leftContainer.current)  | 
32 |  | -    }  | 
33 |  | -  }  | 
34 |  | -  function syncScroll(origin, target) {  | 
35 |  | -    let findSeq = ''  | 
36 |  | -    let leftTop = 0  | 
37 |  | -    for (const el of origin.children) {  | 
38 |  | -      if (el.dataset.seq && el.getBoundingClientRect().top > baseTop.current) {  | 
39 |  | -        findSeq = el.dataset.seq  | 
40 |  | -        leftTop = el.getBoundingClientRect().top  | 
41 |  | -        break  | 
 | 11 | +  useEffect(() => {  | 
 | 12 | +    let timer = null  | 
 | 13 | +    let isLeftScroll = false  | 
 | 14 | +    let isRightScroll = false  | 
 | 15 | +    function handleScroll(type) {  | 
 | 16 | +      if (type === 'left') {  | 
 | 17 | +        if (isRightScroll) return  | 
 | 18 | +        isLeftScroll = true  | 
 | 19 | +        clearTimeout(timer)  | 
 | 20 | +        timer = setTimeout(() => {  | 
 | 21 | +          isLeftScroll = false  | 
 | 22 | +        }, 300)  | 
 | 23 | +        syncScroll(leftContainer.current, rightContainer.current)  | 
 | 24 | +      } else {  | 
 | 25 | +        if (isLeftScroll) return  | 
 | 26 | +        isRightScroll = true  | 
 | 27 | +        clearTimeout(timer)  | 
 | 28 | +        timer = setTimeout(() => {  | 
 | 29 | +          isRightScroll = false  | 
 | 30 | +        }, 300)  | 
 | 31 | +        syncScroll(rightContainer.current, leftContainer.current)  | 
42 | 32 |       }  | 
43 | 33 |     }  | 
44 |  | -    if (!findSeq) return  | 
 | 34 | +    function syncScroll(origin, target) {  | 
 | 35 | +      let findSeq = ''  | 
 | 36 | +      let leftTop = 0  | 
 | 37 | +      for (const el of origin.children) {  | 
 | 38 | +        if (el.dataset.seq && el.getBoundingClientRect().top > baseTop.current) {  | 
 | 39 | +          findSeq = el.dataset.seq  | 
 | 40 | +          leftTop = el.getBoundingClientRect().top  | 
 | 41 | +          break  | 
 | 42 | +        }  | 
 | 43 | +      }  | 
 | 44 | +      if (!findSeq) return  | 
45 | 45 | 
 
  | 
46 |  | -    let syncEl = null  | 
47 |  | -    for (const el of target.children) {  | 
48 |  | -      if (el.dataset.seq === findSeq) {  | 
49 |  | -        syncEl = el  | 
50 |  | -        break  | 
 | 46 | +      let syncEl = null  | 
 | 47 | +      for (const el of target.children) {  | 
 | 48 | +        if (el.dataset.seq === findSeq) {  | 
 | 49 | +          syncEl = el  | 
 | 50 | +          break  | 
 | 51 | +        }  | 
51 | 52 |       }  | 
 | 53 | +      if (!syncEl) return  | 
 | 54 | + | 
 | 55 | +      const rightTop = syncEl.getBoundingClientRect().top  | 
 | 56 | +      const delta = rightTop - leftTop  | 
 | 57 | +      target.scrollTo({ top: target.scrollTop + delta })  | 
52 | 58 |     }  | 
53 |  | -    if (!syncEl) return  | 
54 | 59 | 
 
  | 
55 |  | -    const rightTop = syncEl.getBoundingClientRect().top  | 
56 |  | -    const delta = rightTop - leftTop  | 
57 |  | -    target.scrollTo({ top: target.scrollTop + delta })  | 
58 |  | -  }  | 
 | 60 | +    const handleLeftScroll = () => handleScroll('left')  | 
 | 61 | +    const handleRightScroll = () => handleScroll('right')  | 
 | 62 | +    leftContainer.current.addEventListener('scroll', handleLeftScroll)  | 
 | 63 | +    rightContainer.current.addEventListener('scroll', handleRightScroll)  | 
 | 64 | + | 
 | 65 | +    return () => {  | 
 | 66 | +      clearTimeout(timer)  | 
 | 67 | +      leftContainer.current.removeEventListener('scroll', handleLeftScroll)  | 
 | 68 | +      rightContainer.current.removeEventListener('scroll', handleRightScroll)  | 
 | 69 | +    }  | 
 | 70 | +  }, [])  | 
59 | 71 | 
 
  | 
60 | 72 |   return (  | 
61 | 73 |     <div className="flex h-[calc(100%-32px)]">  | 
62 | 74 |       <div  | 
63 | 75 |         ref={leftContainer}  | 
64 | 76 |         className="markdown-body overflow-auto h-full w-1/2"  | 
65 | 77 |         dangerouslySetInnerHTML={{ __html: sideBySideContents[0] }}  | 
66 |  | -        onScroll={e => handleScroll('left')}  | 
67 | 78 |       />  | 
68 | 79 |       <div className="w-2"></div>  | 
69 | 80 |       <div  | 
70 | 81 |         ref={rightContainer}  | 
71 | 82 |         className="markdown-body overflow-auto h-full w-1/2"  | 
72 | 83 |         dangerouslySetInnerHTML={{ __html: sideBySideContents[1] }}  | 
73 |  | -        onScroll={e => handleScroll('right')}  | 
74 | 84 |       />  | 
75 | 85 |     </div>  | 
76 | 86 |   )  | 
 | 
0 commit comments