Przeglądaj źródła

Merge pull request #2564 from weseek/feat/smoothScroll-for-master-merge

Feat/smooth scroll for master merge
itizawa 5 lat temu
rodzic
commit
cf192e0967

+ 8 - 3
src/client/js/components/Page/RevisionRenderer.jsx

@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
 import { withUnstatedContainers } from '../UnstatedUtils';
 import { withUnstatedContainers } from '../UnstatedUtils';
 import AppContainer from '../../services/AppContainer';
 import AppContainer from '../../services/AppContainer';
 import PageContainer from '../../services/PageContainer';
 import PageContainer from '../../services/PageContainer';
+import NavigationContainer from '../../services/NavigationContainer';
 import GrowiRenderer from '../../util/GrowiRenderer';
 import GrowiRenderer from '../../util/GrowiRenderer';
 
 
 import RevisionBody from './RevisionBody';
 import RevisionBody from './RevisionBody';
@@ -35,7 +36,7 @@ class RevisionRenderer extends React.PureComponent {
 
 
   componentDidUpdate(prevProps) {
   componentDidUpdate(prevProps) {
     const { markdown: prevMarkdown, highlightKeywords: prevHighlightKeywords } = prevProps;
     const { markdown: prevMarkdown, highlightKeywords: prevHighlightKeywords } = prevProps;
-    const { markdown, highlightKeywords } = this.props;
+    const { markdown, highlightKeywords, navigationContainer } = this.props;
 
 
     // render only when props.markdown is updated
     // render only when props.markdown is updated
     if (markdown !== prevMarkdown || highlightKeywords !== prevHighlightKeywords) {
     if (markdown !== prevMarkdown || highlightKeywords !== prevHighlightKeywords) {
@@ -44,6 +45,10 @@ class RevisionRenderer extends React.PureComponent {
       return;
       return;
     }
     }
 
 
+    const HeaderLink = document.getElementsByClassName('revision-head-link');
+    const HeaderLinkArray = Array.from(HeaderLink);
+    navigationContainer.addSmoothScrollEvent(HeaderLinkArray);
+
     const { interceptorManager } = this.props.appContainer;
     const { interceptorManager } = this.props.appContainer;
 
 
     interceptorManager.process('postRenderHtml', this.currentRenderingContext);
     interceptorManager.process('postRenderHtml', this.currentRenderingContext);
@@ -115,12 +120,12 @@ class RevisionRenderer extends React.PureComponent {
 /**
 /**
  * Wrapper component for using unstated
  * Wrapper component for using unstated
  */
  */
-const RevisionRendererWrapper = withUnstatedContainers(RevisionRenderer, [AppContainer, PageContainer]);
+const RevisionRendererWrapper = withUnstatedContainers(RevisionRenderer, [AppContainer, PageContainer, NavigationContainer]);
 
 
 RevisionRenderer.propTypes = {
 RevisionRenderer.propTypes = {
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
-
+  navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
   growiRenderer: PropTypes.instanceOf(GrowiRenderer).isRequired,
   growiRenderer: PropTypes.instanceOf(GrowiRenderer).isRequired,
   markdown: PropTypes.string.isRequired,
   markdown: PropTypes.string.isRequired,
   highlightKeywords: PropTypes.string,
   highlightKeywords: PropTypes.string,

+ 12 - 3
src/client/js/components/TableOfContents.jsx

@@ -1,10 +1,11 @@
-import React, { useCallback } from 'react';
+import React, { useCallback, useEffect } from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import loggerFactory from '@alias/logger';
 import loggerFactory from '@alias/logger';
 
 
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
 
 
 import PageContainer from '../services/PageContainer';
 import PageContainer from '../services/PageContainer';
+import NavigationContainer from '../services/NavigationContainer';
 
 
 import { withUnstatedContainers } from './UnstatedUtils';
 import { withUnstatedContainers } from './UnstatedUtils';
 import StickyStretchableScroller from './StickyStretchableScroller';
 import StickyStretchableScroller from './StickyStretchableScroller';
@@ -18,7 +19,7 @@ const logger = loggerFactory('growi:TableOfContents');
  */
  */
 const TableOfContents = (props) => {
 const TableOfContents = (props) => {
 
 
-  const { pageContainer } = props;
+  const { pageContainer, navigationContainer } = props;
 
 
   const calcViewHeight = useCallback(() => {
   const calcViewHeight = useCallback(() => {
     // calculate absolute top of '#revision-toc' element
     // calculate absolute top of '#revision-toc' element
@@ -31,6 +32,13 @@ const TableOfContents = (props) => {
 
 
   const { tocHtml } = pageContainer.state;
   const { tocHtml } = pageContainer.state;
 
 
+  // execute after generation toc html
+  useEffect(() => {
+    const tocDom = document.getElementById('revision-toc-content');
+    const anchorsInToc = Array.from(tocDom.getElementsByTagName('a'));
+    navigationContainer.addSmoothScrollEvent(anchorsInToc);
+  }, [tocHtml, navigationContainer]);
+
   return (
   return (
     <>
     <>
       {/* TODO GW-3253 add four contents */}
       {/* TODO GW-3253 add four contents */}
@@ -56,10 +64,11 @@ const TableOfContents = (props) => {
 /**
 /**
  * Wrapper component for using unstated
  * Wrapper component for using unstated
  */
  */
-const TableOfContentsWrapper = withUnstatedContainers(TableOfContents, [PageContainer]);
+const TableOfContentsWrapper = withUnstatedContainers(TableOfContents, [PageContainer, NavigationContainer]);
 
 
 TableOfContents.propTypes = {
 TableOfContents.propTypes = {
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
+  navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
 };
 };
 
 
 export default withTranslation()(TableOfContentsWrapper);
 export default withTranslation()(TableOfContentsWrapper);

+ 16 - 0
src/client/js/services/NavigationContainer.js

@@ -6,6 +6,7 @@ import { Container } from 'unstated';
  */
  */
 
 
 const SCROLL_THRES_SKIP = 200;
 const SCROLL_THRES_SKIP = 200;
+const WIKI_HEADER_LINK = 120;
 
 
 export default class NavigationContainer extends Container {
 export default class NavigationContainer extends Container {
 
 
@@ -171,6 +172,21 @@ export default class NavigationContainer extends Container {
     this.setState({ isPageCreateModalShown: false });
     this.setState({ isPageCreateModalShown: false });
   }
   }
 
 
+  /**
+   * Function that implements the click event for realizing smooth scroll
+   * @param {array} elements
+   */
+  addSmoothScrollEvent(elements = {}) {
+    elements.forEach(link => link.addEventListener('click', (e) => {
+      e.preventDefault();
+
+      const href = link.getAttribute('href').replace('#', '');
+      window.location.hash = href;
+      const targetDom = document.getElementById(href);
+      this.smoothScrollIntoView(targetDom, WIKI_HEADER_LINK);
+    }));
+  }
+
   smoothScrollIntoView(element = null, offsetTop = 0) {
   smoothScrollIntoView(element = null, offsetTop = 0) {
     const targetElement = element || window.document.body;
     const targetElement = element || window.document.body;