فهرست منبع

scrool into highlighted word

yohei0125 4 سال پیش
والد
کامیت
58e4f26e10

+ 2 - 2
packages/app/src/client/util/smooth-scroll.ts

@@ -1,6 +1,6 @@
 const WIKI_HEADER_LINK = 120;
 const WIKI_HEADER_LINK = 120;
 
 
-export const smoothScrollIntoView = (element: HTMLElement, offsetTop = 0): void => {
+export const smoothScrollIntoView = (element: HTMLElement, offsetTop = 0, startFrom: HTMLElement | Window = window): void => {
   const targetElement = element || window.document.body;
   const targetElement = element || window.document.body;
 
 
   // get the distance to the target element top
   // get the distance to the target element top
@@ -8,7 +8,7 @@ export const smoothScrollIntoView = (element: HTMLElement, offsetTop = 0): void
 
 
   const top = window.pageYOffset + rectTop - offsetTop;
   const top = window.pageYOffset + rectTop - offsetTop;
 
 
-  window.scrollTo({
+  startFrom.scrollTo({
     top,
     top,
     behavior: 'smooth',
     behavior: 'smooth',
   });
   });

+ 4 - 0
packages/app/src/components/Page/RevisionBody.jsx

@@ -24,6 +24,9 @@ export default class RevisionBody extends React.PureComponent {
     if (MathJax != null && this.props.isMathJaxEnabled && this.props.renderMathJaxInRealtime) {
     if (MathJax != null && this.props.isMathJaxEnabled && this.props.renderMathJaxInRealtime) {
       this.renderMathJaxWithDebounce();
       this.renderMathJaxWithDebounce();
     }
     }
+    if (this.props.onRevisionBodyRendered) {
+      this.props.onRevisionBodyRendered(true);
+    }
   }
   }
 
 
   componentWillReceiveProps(nextProps) {
   componentWillReceiveProps(nextProps) {
@@ -80,4 +83,5 @@ RevisionBody.propTypes = {
   renderMathJaxOnInit: PropTypes.bool,
   renderMathJaxOnInit: PropTypes.bool,
   renderMathJaxInRealtime: PropTypes.bool,
   renderMathJaxInRealtime: PropTypes.bool,
   additionalClassName: PropTypes.string,
   additionalClassName: PropTypes.string,
+  onRevisionBodyRendered: PropTypes.func,
 };
 };

+ 2 - 0
packages/app/src/components/Page/RevisionLoader.jsx

@@ -107,6 +107,7 @@ class LegacyRevisionLoader extends React.Component {
         growiRenderer={this.props.growiRenderer}
         growiRenderer={this.props.growiRenderer}
         markdown={markdown}
         markdown={markdown}
         highlightKeywords={this.props.highlightKeywords}
         highlightKeywords={this.props.highlightKeywords}
+        onRevisionBodyRendered={this.props.onRevisionBodyRendered}
       />
       />
     );
     );
   }
   }
@@ -126,6 +127,7 @@ LegacyRevisionLoader.propTypes = {
   revisionId: PropTypes.string.isRequired,
   revisionId: PropTypes.string.isRequired,
   lazy: PropTypes.bool,
   lazy: PropTypes.bool,
   onRevisionLoaded: PropTypes.func,
   onRevisionLoaded: PropTypes.func,
+  onRevisionBodyRendered: PropTypes.func,
   highlightKeywords: PropTypes.string,
   highlightKeywords: PropTypes.string,
 };
 };
 
 

+ 3 - 0
packages/app/src/components/Page/RevisionRenderer.jsx

@@ -157,6 +157,7 @@ class LegacyRevisionRenderer extends React.PureComponent {
         isMathJaxEnabled={isMathJaxEnabled}
         isMathJaxEnabled={isMathJaxEnabled}
         additionalClassName={this.props.additionalClassName}
         additionalClassName={this.props.additionalClassName}
         renderMathJaxOnInit
         renderMathJaxOnInit
+        onRevisionBodyRendered={this.props.onRevisionBodyRendered}
       />
       />
     );
     );
   }
   }
@@ -169,6 +170,7 @@ LegacyRevisionRenderer.propTypes = {
   markdown: PropTypes.string.isRequired,
   markdown: PropTypes.string.isRequired,
   highlightKeywords: PropTypes.string,
   highlightKeywords: PropTypes.string,
   additionalClassName: PropTypes.string,
   additionalClassName: PropTypes.string,
+  onRevisionBodyRendered: PropTypes.func,
 };
 };
 
 
 /**
 /**
@@ -187,6 +189,7 @@ RevisionRenderer.propTypes = {
   markdown: PropTypes.string.isRequired,
   markdown: PropTypes.string.isRequired,
   highlightKeywords: PropTypes.string,
   highlightKeywords: PropTypes.string,
   additionalClassName: PropTypes.string,
   additionalClassName: PropTypes.string,
+  onRevisionBodyRendered: PropTypes.func,
 };
 };
 
 
 export default RevisionRenderer;
 export default RevisionRenderer;

+ 22 - 3
packages/app/src/components/SearchPage/SearchResultContent.tsx

@@ -1,11 +1,15 @@
-import React, { FC } from 'react';
-
+import React, {
+  FC, useRef, useState, useEffect,
+} from 'react';
 import { IPageSearchResultData } from '../../interfaces/search';
 import { IPageSearchResultData } from '../../interfaces/search';
 
 
 import RevisionLoader from '../Page/RevisionLoader';
 import RevisionLoader from '../Page/RevisionLoader';
 import AppContainer from '../../client/services/AppContainer';
 import AppContainer from '../../client/services/AppContainer';
+import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
 import SearchResultContentSubNavigation from './SearchResultContentSubNavigation';
 import SearchResultContentSubNavigation from './SearchResultContentSubNavigation';
 
 
+const SCROLL_OFFSET_TOP = 150;
+
 // TODO : set focusedPage type to ?IPageSearchResultData once #80214 is merged
 // TODO : set focusedPage type to ?IPageSearchResultData once #80214 is merged
 // PR: https://github.com/weseek/growi/pull/4649
 // PR: https://github.com/weseek/growi/pull/4649
 
 
@@ -17,6 +21,20 @@ type Props ={
 
 
 
 
 const SearchResultContent: FC<Props> = (props: Props) => {
 const SearchResultContent: FC<Props> = (props: Props) => {
+  const [isRevisionBodyRendered, setIsRevisionBodyRendered] = useState(false);
+  const contentRef = useRef(null);
+  useEffect(() => {
+    // reset state
+    setIsRevisionBodyRendered(false);
+    if (isRevisionBodyRendered) {
+      const searchResultPageContent = contentRef.current as HTMLElement| null;
+      if (searchResultPageContent == null) return;
+      const highlightedWord = searchResultPageContent?.querySelector('.highlighted-keyword') as HTMLElement | null;
+      if (highlightedWord == null) return;
+      smoothScrollIntoView(highlightedWord, SCROLL_OFFSET_TOP, searchResultPageContent);
+    }
+  }, [isRevisionBodyRendered]);
+
   const page = props.focusedSearchResultData?.pageData;
   const page = props.focusedSearchResultData?.pageData;
   // return if page is null
   // return if page is null
   if (page == null) return <></>;
   if (page == null) return <></>;
@@ -29,13 +47,14 @@ const SearchResultContent: FC<Props> = (props: Props) => {
         path={page.path}
         path={page.path}
       >
       >
       </SearchResultContentSubNavigation>
       </SearchResultContentSubNavigation>
-      <div className="search-result-page-content">
+      <div className="search-result-page-content" ref={contentRef}>
         <RevisionLoader
         <RevisionLoader
           growiRenderer={growiRenderer}
           growiRenderer={growiRenderer}
           pageId={page._id}
           pageId={page._id}
           pagePath={page.path}
           pagePath={page.path}
           revisionId={page.revision}
           revisionId={page.revision}
           highlightKeywords={props.searchingKeyword}
           highlightKeywords={props.searchingKeyword}
+          onRevisionBodyRendered={setIsRevisionBodyRendered}
         />
         />
       </div>
       </div>
     </div>
     </div>