Просмотр исходного кода

Merge branch 'master' into support/share-link-for-outside-for-merge

itizawa 5 лет назад
Родитель
Сommit
9c951d1d0e

+ 2 - 1
config/logger/config.dev.js

@@ -34,5 +34,6 @@ module.exports = {
   'growi:cli:app': 'debug',
   'growi:services:*': 'debug',
   // 'growi:StaffCredit': 'debug',
-  // 'growi:TableOfContents': 'debug',
+  // 'growi:cli:StickyStretchableScroller': 'debug',
+
 };

+ 2 - 3
src/client/js/base.jsx

@@ -4,8 +4,8 @@ import loggerFactory from '@alias/logger';
 import Xss from '@commons/service/xss';
 
 import SearchTop from './components/Navbar/SearchTop';
+import GrowiNavbar from './components/Navbar/GrowiNavbar';
 import NavbarToggler from './components/Navbar/NavbarToggler';
-import PersonalDropdown from './components/Navbar/PersonalDropdown';
 import Sidebar from './components/Sidebar';
 import ShareLinkAlert from './components/Page/ShareLinkAlert';
 import StaffCredit from './components/StaffCredit/StaffCredit';
@@ -40,12 +40,11 @@ logger.info('AppContainer has been initialized');
  *  value: React Element
  */
 const componentMappings = {
+  'grw-navbar': <GrowiNavbar />,
   'grw-navbar-toggler': <NavbarToggler />,
 
   'grw-search-top': <SearchTop />,
-  'personal-dropdown': <PersonalDropdown />,
 
-  'create-page-button': <PageCreateButton />,
   'create-page-button-icon': <PageCreateButton isIcon />,
   'page-create-modal': <PageCreateModal />,
 

+ 104 - 0
src/client/js/components/Navbar/GrowiNavbar.jsx

@@ -0,0 +1,104 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { withTranslation } from 'react-i18next';
+
+import { withUnstatedContainers } from '../UnstatedUtils';
+import NavigationContainer from '../../services/NavigationContainer';
+import AppContainer from '../../services/AppContainer';
+
+import PageCreateButton from './PageCreateButton';
+import PersonalDropdown from './PersonalDropdown';
+import GrowiLogo from '../GrowiLogo';
+
+class GrowiNavbar extends React.Component {
+
+  renderNavbarRight() {
+    const { appContainer } = this.props;
+    const isReachable = appContainer.config.isSearchServiceReachable;
+
+    return (
+      <>
+        <li className="nav-item d-none d-md-block">
+          <PageCreateButton />
+        </li>
+
+        {isReachable
+         && (
+         <li className="nav-item d-md-none">
+           <a type="button" className="nav-link px-4" data-target="#grw-search-top-collapse" data-toggle="collapse">
+             <i className="icon-magnifier mr-2"></i>
+           </a>
+         </li>
+         )}
+
+        <li className="grw-personal-dropdown nav-item dropdown dropdown-toggle dropdown-toggle-no-caret">
+          <PersonalDropdown />
+        </li>
+      </>
+    );
+  }
+
+  renderConfidential() {
+    const { appContainer } = this.props;
+    const { crowi } = appContainer.config;
+
+    return (
+      <li className="nav-item confidential text-light">
+        <i className="icon-info d-md-none" data-toggle="tooltip" title={crowi.confidential} />
+        <span className="d-none d-md-inline">
+          {crowi.confidential}
+        </span>
+      </li>
+    );
+  }
+
+  render() {
+    const { appContainer } = this.props;
+    const { crowi } = appContainer.config;
+    const { currentUser } = appContainer;
+
+    return (
+      <>
+
+        {/* Brand Logo  */}
+        <div className="navbar-brand mr-0">
+          <a className="grw-logo d-block" href="/">
+            <GrowiLogo />
+          </a>
+        </div>
+
+        <ul className="navbar-nav d-md-none">
+          <li id="grw-navbar-toggler" className="nav-item"></li>
+        </ul>
+        <div className="grw-app-title d-none d-md-block">
+          {crowi.title}
+        </div>
+
+
+        {/* Navbar Right  */}
+        <ul className="navbar-nav ml-auto">
+          {currentUser != null ? this.renderNavbarRight() : <li id="login-user" className="nav-item"><a className="nav-link" href="/login">Login</a></li>}
+        </ul>
+
+        {crowi.confidential != null && this.renderConfidential()}
+      </>
+    );
+  }
+
+}
+
+/**
+ * Wrapper component for using unstated
+ */
+const GrowiNavbarWrapper = withUnstatedContainers(GrowiNavbar, [AppContainer, NavigationContainer]);
+
+
+GrowiNavbar.propTypes = {
+  t: PropTypes.func.isRequired, //  i18next
+
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
+};
+
+export default withTranslation()(GrowiNavbarWrapper);

+ 1 - 1
src/client/js/components/Navbar/SearchTop.jsx

@@ -61,7 +61,7 @@ class SearchTop extends React.Component {
         </div>
       )
       : (
-        <div className="grw-search-top-fixed position-fixed">
+        <div className="grw-search-top-absolute position-absolute">
           {children}
         </div>
       );

+ 26 - 21
src/client/js/components/Sidebar.jsx

@@ -13,9 +13,8 @@ import AppContainer from '../services/AppContainer';
 import NavigationContainer from '../services/NavigationContainer';
 
 import SidebarNav from './Sidebar/SidebarNav';
-import RecentChanges from './Sidebar/RecentChanges';
-import CustomSidebar from './Sidebar/CustomSidebar';
-
+import SidebarContents from './Sidebar/SidebarContents';
+import StickyStretchableScroller from './StickyStretchableScroller';
 
 const sidebarDefaultWidth = 240;
 
@@ -28,10 +27,6 @@ class Sidebar extends React.Component {
     isDrawerModeOnInit: PropTypes.bool,
   };
 
-  state = {
-    currentContentsId: 'recent',
-  };
-
   componentWillMount() {
     this.hackUIController();
   }
@@ -127,35 +122,45 @@ class Sidebar extends React.Component {
   }
 
   itemSelectedHandler = (contentsId) => {
-    const { navigationUIController } = this.props;
-    const { currentContentsId } = this.state;
+    const { navigationContainer, navigationUIController } = this.props;
+    const { sidebarContentsId } = navigationContainer.state;
 
     // already selected
-    if (currentContentsId === contentsId) {
+    if (sidebarContentsId === contentsId) {
       navigationUIController.toggleCollapse();
     }
     // switch and expand
     else {
-      this.setState({ currentContentsId: contentsId });
       navigationUIController.expand();
     }
   }
 
+  calcViewHeight() {
+    const containerElem = document.querySelector('#grw-sidebar-content-container');
+    return window.innerHeight - containerElem.getBoundingClientRect().top;
+  }
+
   renderGlobalNavigation = () => (
-    <SidebarNav currentContentsId={this.state.currentContentsId} onItemSelected={this.itemSelectedHandler} />
+    <SidebarNav onItemSelected={this.itemSelectedHandler} />
   );
 
   renderSidebarContents = () => {
-    let contents = <CustomSidebar />;
-
-    switch (this.state.currentContentsId) {
-      case 'recent':
-        contents = <RecentChanges />;
-        break;
-    }
+    const scrollTargetSelector = 'div[data-testid="ContextualNavigation"] div[role="group"]';
 
-    return <div className="grw-sidebar-content-container">{contents}</div>;
-  }
+    return (
+      <>
+        <StickyStretchableScroller
+          scrollTargetSelector={scrollTargetSelector}
+          contentsElemSelector="#grw-sidebar-content-container"
+          stickyElemSelector=".grw-sidebar"
+          calcViewHeightFunc={this.calcViewHeight}
+        />
+        <div id="grw-sidebar-content-container" className="grw-sidebar-content-container">
+          <SidebarContents />
+        </div>
+      </>
+    );
+  };
 
   render() {
     const { isDrawerOpened } = this.props.navigationContainer.state;

+ 40 - 0
src/client/js/components/Sidebar/SidebarContents.jsx

@@ -0,0 +1,40 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { withTranslation } from 'react-i18next';
+
+import { withUnstatedContainers } from '../UnstatedUtils';
+import NavigationContainer from '../../services/NavigationContainer';
+
+import RecentChanges from './RecentChanges';
+import CustomSidebar from './CustomSidebar';
+
+const SidebarContents = (props) => {
+
+  const { navigationContainer } = props;
+
+  let Contents;
+  switch (navigationContainer.state.sidebarContentsId) {
+    case 'recent':
+      Contents = RecentChanges;
+      break;
+    default:
+      Contents = CustomSidebar;
+  }
+
+  return (
+    <Contents />
+  );
+
+};
+
+SidebarContents.propTypes = {
+  navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+const SidebarContentsWrapper = withUnstatedContainers(SidebarContents, [NavigationContainer]);
+
+export default withTranslation()(SidebarContentsWrapper);

+ 9 - 5
src/client/js/components/Sidebar/SidebarNav.jsx

@@ -5,12 +5,12 @@ import { withTranslation } from 'react-i18next';
 
 import { withUnstatedContainers } from '../UnstatedUtils';
 import AppContainer from '../../services/AppContainer';
+import NavigationContainer from '../../services/NavigationContainer';
 
 
 class SidebarNav extends React.Component {
 
   static propTypes = {
-    currentContentsId: PropTypes.string,
     onItemSelected: PropTypes.func,
   };
 
@@ -18,14 +18,17 @@ class SidebarNav extends React.Component {
   };
 
   itemSelectedHandler = (contentsId) => {
-    const { onItemSelected } = this.props;
+    const { navigationContainer, onItemSelected } = this.props;
     if (onItemSelected != null) {
       onItemSelected(contentsId);
     }
+
+    navigationContainer.setState({ sidebarContentsId: contentsId });
   }
 
   PrimaryItem = ({ id, label, iconName }) => {
-    const isSelected = this.props.currentContentsId === id;
+    const { sidebarContentsId } = this.props.navigationContainer.state;
+    const isSelected = sidebarContentsId === id;
 
     return (
       <button
@@ -59,7 +62,7 @@ class SidebarNav extends React.Component {
     const { PrimaryItem, SecondaryItem } = this;
 
     return (
-      <div className="grw-sidebar-nav d-flex flex-column justify-content-between pb-4">
+      <div className="grw-sidebar-nav">
         <div className="grw-sidebar-nav-primary-container">
           {!isSharedUser && <PrimaryItem id="custom" label="Custom Sidebar" iconName="code" />}
           {!isSharedUser && <PrimaryItem id="recent" label="Recent Changes" iconName="update" />}
@@ -80,11 +83,12 @@ class SidebarNav extends React.Component {
 
 SidebarNav.propTypes = {
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
 };
 
 /**
  * Wrapper component for using unstated
  */
-const SidebarNavWrapper = withUnstatedContainers(SidebarNav, [AppContainer]);
+const SidebarNavWrapper = withUnstatedContainers(SidebarNav, [AppContainer, NavigationContainer]);
 
 export default withTranslation()(SidebarNavWrapper);

+ 168 - 0
src/client/js/components/StickyStretchableScroller.jsx

@@ -0,0 +1,168 @@
+import React, { useEffect, useCallback } from 'react';
+import PropTypes from 'prop-types';
+import loggerFactory from '@alias/logger';
+
+import { debounce } from 'throttle-debounce';
+import StickyEvents from 'sticky-events';
+
+import NavigationContainer from '../services/NavigationContainer';
+import { withUnstatedContainers } from './UnstatedUtils';
+
+const logger = loggerFactory('growi:cli:StickyStretchableScroller');
+
+
+/**
+ * USAGE:
+ *
+  const calcViewHeight = useCallback(() => {
+    const containerElem = document.querySelector('#sticky-elem');
+    const containerTop = containerElem.getBoundingClientRect().top;
+
+    // stretch to the bottom of window
+    return window.innerHeight - containerTop;
+  });
+
+  return (
+    <StickyStretchableScroller
+      contentsElemSelector="#long-contents-elem"
+      stickyElemSelector="#sticky-elem"
+      calcViewHeightFunc={calcViewHeight}
+    >
+      <div id="scroll-elem">
+        ...
+      </div>
+    </StickyStretchableScroller>
+  );
+
+  or
+
+  return (
+    <StickyStretchableScroller
+      scrollTargetId="scroll-elem"
+      contentsElemSelector="#long-contents-elem"
+      stickyElemSelector="#sticky-elem"
+      calcViewHeightFunc={calcViewHeight}
+    />
+  );
+ */
+const StickyStretchableScroller = (props) => {
+
+  let { scrollTargetSelector } = props;
+  const {
+    navigationContainer,
+    children, contentsElemSelector, stickyElemSelector,
+    calcViewHeightFunc, calcContentsHeightFunc,
+  } = props;
+
+  if (scrollTargetSelector == null && children == null) {
+    throw new Error('Either of scrollTargetSelector or children is required');
+  }
+
+  if (scrollTargetSelector == null) {
+    scrollTargetSelector = `#${children.props.id}`;
+  }
+
+  /**
+   * Reset scrollbar
+   */
+  const resetScrollbar = useCallback(() => {
+    const contentsElem = document.querySelector(contentsElemSelector);
+    if (contentsElem == null) {
+      return;
+    }
+
+    const viewHeight = calcViewHeightFunc != null
+      ? calcViewHeightFunc()
+      : 'auto';
+    const contentsHeight = calcContentsHeightFunc != null
+      ? calcContentsHeightFunc(contentsElem)
+      : contentsElem.getBoundingClientRect().height;
+
+    logger.debug(`[${scrollTargetSelector}] viewHeight`, viewHeight);
+    logger.debug(`[${scrollTargetSelector}] contentsHeight`, contentsHeight);
+
+    $(scrollTargetSelector).slimScroll({
+      color: '#666',
+      railColor: '#999',
+      railVisible: true,
+      position: 'right',
+      height: viewHeight,
+    });
+    if (contentsHeight < viewHeight) {
+      $(scrollTargetSelector).slimScroll({ destroy: true });
+    }
+  }, [contentsElemSelector, calcViewHeightFunc, calcContentsHeightFunc]);
+
+  const resetScrollbarDebounced = debounce(100, resetScrollbar);
+
+
+  const stickyChangeHandler = useCallback((event) => {
+    logger.debug('StickyEvents.CHANGE detected');
+    resetScrollbar();
+  });
+
+  // setup effect by sticky event
+  useEffect(() => {
+    if (stickyElemSelector == null) {
+      return;
+    }
+
+    // sticky
+    // See: https://github.com/ryanwalters/sticky-events
+    const stickyEvents = new StickyEvents({ stickySelector: stickyElemSelector });
+    const { stickySelector } = stickyEvents;
+    const elem = document.querySelector(stickySelector);
+    elem.addEventListener(StickyEvents.CHANGE, stickyChangeHandler);
+
+    // return clean up handler
+    return () => {
+      elem.removeEventListener(StickyEvents.CHANGE, stickyChangeHandler);
+    };
+  }, []);
+
+  // setup effect by resizing event
+  useEffect(() => {
+    const resizeHandler = (event) => {
+      resetScrollbarDebounced();
+    };
+
+    window.addEventListener('resize', resizeHandler);
+
+    // return clean up handler
+    return () => {
+      window.removeEventListener('resize', resizeHandler);
+    };
+  }, []);
+
+  // setup effect by isScrollTop
+  useEffect(() => {
+    if (navigationContainer.state.isScrollTop) {
+      resetScrollbar();
+    }
+  }, [navigationContainer.state.isScrollTop]);
+
+  // setup effect by update props
+  useEffect(() => {
+    resetScrollbarDebounced();
+  });
+
+  return (
+    <>
+      { children }
+    </>
+  );
+};
+
+StickyStretchableScroller.propTypes = {
+  navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
+  contentsElemSelector: PropTypes.string.isRequired,
+
+  children: PropTypes.node,
+  scrollTargetSelector: PropTypes.string,
+  stickyElemSelector: PropTypes.string,
+
+  calcViewHeightFunc: PropTypes.func,
+  calcContentsHeightFunc: PropTypes.func,
+};
+
+export default withUnstatedContainers(StickyStretchableScroller, [NavigationContainer]);

+ 22 - 109
src/client/js/components/TableOfContents.jsx

@@ -1,128 +1,42 @@
-import React from 'react';
+import React, { useCallback } from 'react';
 import PropTypes from 'prop-types';
 import loggerFactory from '@alias/logger';
 
 import { withTranslation } from 'react-i18next';
 
-import { debounce } from 'throttle-debounce';
-import StickyEvents from 'sticky-events';
-
-import AppContainer from '../services/AppContainer';
 import PageContainer from '../services/PageContainer';
 
-import { isUserPage } from '../../../lib/util/path-utils';
 import { withUnstatedContainers } from './UnstatedUtils';
+import StickyStretchableScroller from './StickyStretchableScroller';
 
+// eslint-disable-next-line no-unused-vars
 const logger = loggerFactory('growi:TableOfContents');
 
-// get these value with
-//   document.querySelector('.revision-toc').getBoundingClientRect().top
-const DEFAULT_REVISION_TOC_TOP_FOR_GROWI_LAYOUT = 190;
-const DEFAULT_REVISION_TOC_TOP_FOR_GROWI_LAYOUT_USER_PAGE = 230;
-const DEFAULT_REVISION_TOC_TOP_FOR_KIBELA_LAYOUT = 105;
-
 /**
  * @author Yuki Takei <yuki@weseek.co.jp>
  *
- * @export
- * @class TableOfContents
- * @extends {React.Component}
  */
-class TableOfContents extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.init = this.init.bind(this);
-    this.resetScrollbarDebounced = debounce(100, this.resetScrollbar);
-
-    const { layoutType } = this.props.appContainer.config;
-    const { path } = this.props.pageContainer.state;
-
-    this.defaultRevisionTocTop = DEFAULT_REVISION_TOC_TOP_FOR_GROWI_LAYOUT;
-
-    if (isUserPage(path)) {
-      this.defaultRevisionTocTop = DEFAULT_REVISION_TOC_TOP_FOR_GROWI_LAYOUT_USER_PAGE;
-    }
-
-    if (layoutType === 'kibela') {
-      this.defaultRevisionTocTop = DEFAULT_REVISION_TOC_TOP_FOR_KIBELA_LAYOUT;
-    }
-  }
-
-  componentDidMount() {
-    this.init();
-    this.resetScrollbar();
-  }
+const TableOfContents = (props) => {
 
-  componentDidUpdate() {
-    this.resetScrollbar();
-  }
+  const { pageContainer } = props;
 
-  init() {
-    /*
-     * set event listener
-     */
-    // resize
-    window.addEventListener('resize', (event) => {
-      this.resetScrollbarDebounced(this.defaultRevisionTocTop);
-    });
-
-    // sticky
-    // See: https://github.com/ryanwalters/sticky-events
-    const stickyEvents = new StickyEvents({
-      stickySelector: '#revision-toc',
-    });
-    const { stickySelector } = stickyEvents;
-    const elem = document.querySelector(stickySelector);
-    elem.addEventListener(StickyEvents.STUCK, (event) => {
-      logger.debug('StickyEvents.STUCK detected');
-      this.resetScrollbar();
-    });
-    elem.addEventListener(StickyEvents.UNSTUCK, (event) => {
-      logger.debug('StickyEvents.UNSTUCK detected');
-      this.resetScrollbar(this.defaultRevisionTocTop);
-    });
-  }
-
-  getCurrentRevisionTocTop() {
+  const calcViewHeight = useCallback(() => {
     // calculate absolute top of '#revision-toc' element
-    const revisionTocElem = document.querySelector('.revision-toc');
-    return revisionTocElem.getBoundingClientRect().top;
-  }
-
-  resetScrollbar(defaultRevisionTocTop) {
-    const tocContentElem = document.querySelector('.revision-toc .markdownIt-TOC');
-
-    if (tocContentElem == null) {
-      return;
-    }
+    const containerElem = document.querySelector('#revision-toc');
+    const containerTop = containerElem.getBoundingClientRect().top;
 
-    const revisionTocTop = defaultRevisionTocTop || this.getCurrentRevisionTocTop();
-    // window height - revisionTocTop - .system-version height
-    const viewHeight = window.innerHeight - revisionTocTop - 20;
+    // window height - revisionToc top - .system-version height
+    return window.innerHeight - containerTop - 20;
+  });
 
-    const tocContentHeight = tocContentElem.getBoundingClientRect().height + 15; // add margin
+  const { tocHtml } = pageContainer.state;
 
-    logger.debug('viewHeight', viewHeight);
-    logger.debug('tocContentHeight', tocContentHeight);
-
-    if (viewHeight < tocContentHeight) {
-      $('#revision-toc-content').slimScroll({
-        railVisible: true,
-        position: 'right',
-        height: viewHeight,
-      });
-    }
-    else {
-      $('#revision-toc-content').slimScroll({ destroy: true });
-    }
-  }
-
-  render() {
-    const { tocHtml } = this.props.pageContainer.state;
-
-    return (
+  return (
+    <StickyStretchableScroller
+      contentsElemSelector=".revision-toc .markdownIt-TOC"
+      stickyElemSelector="#revision-toc"
+      calcViewHeightFunc={calcViewHeight}
+    >
       <div
         id="revision-toc-content"
         className="revision-toc-content"
@@ -131,18 +45,17 @@ class TableOfContents extends React.Component {
           __html: tocHtml,
         }}
       />
-    );
-  }
+    </StickyStretchableScroller>
+  );
 
-}
+};
 
 /**
  * Wrapper component for using unstated
  */
-const TableOfContentsWrapper = withUnstatedContainers(TableOfContents, [AppContainer, PageContainer]);
+const TableOfContentsWrapper = withUnstatedContainers(TableOfContents, [PageContainer]);
 
 TableOfContents.propTypes = {
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
 };
 

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

@@ -4,6 +4,9 @@ import { Container } from 'unstated';
  * Service container related to options for Application
  * @extends {Container} unstated Container
  */
+
+const scrollThresForThrottling = 100;
+
 export default class NavigationContainer extends Container {
 
   constructor(appContainer) {
@@ -24,6 +27,10 @@ export default class NavigationContainer extends Container {
       isDrawerMode: null,
       isDrawerOpened: false,
 
+      sidebarContentsId: 'recent',
+
+      isScrollTop: true,
+
       isPageCreateModalShown: false,
     };
 
@@ -32,6 +39,7 @@ export default class NavigationContainer extends Container {
 
     this.initHotkeys();
     this.initDeviceSize();
+    this.initScrollEvent();
   }
 
   /**
@@ -80,6 +88,21 @@ export default class NavigationContainer extends Container {
     this.appContainer.addBreakpointListener('md', mdOrAvobeHandler, true);
   }
 
+  initScrollEvent() {
+    window.addEventListener('scroll', () => {
+      const currentYOffset = window.pageYOffset;
+
+      // original throttling
+      if (scrollThresForThrottling < currentYOffset) {
+        return;
+      }
+
+      this.setState({
+        isScrollTop: currentYOffset === 0,
+      });
+    });
+  }
+
   setEditorMode(editorMode) {
     this.setState({ editorMode });
     this.updateDrawerMode({ ...this.state, editorMode }); // generate newest state object

+ 0 - 20
src/client/js/services/PageContainer.js

@@ -16,9 +16,6 @@ import {
 } from '../util/interceptor/drawio-interceptor';
 
 const logger = loggerFactory('growi:services:PageContainer');
-const scrollThresForSticky = 0;
-const scrollThresForCompact = 30;
-const scrollThresForThrottling = 100;
 
 /**
  * Service container related to Page
@@ -74,9 +71,6 @@ export default class PageContainer extends Container {
       pageIdOnHackmd: mainContent.getAttribute('data-page-id-on-hackmd') || null,
       hasDraftOnHackmd: !!mainContent.getAttribute('data-page-has-draft-on-hackmd'),
       isHackmdDraftUpdatingInRealtime: false,
-
-      isHeaderSticky: false,
-      isSubnavCompact: false,
     };
 
     const { interceptorManager } = this.appContainer;
@@ -93,20 +87,6 @@ export default class PageContainer extends Container {
     this.addWebSocketEventHandlers = this.addWebSocketEventHandlers.bind(this);
     this.addWebSocketEventHandlers();
 
-    window.addEventListener('scroll', () => {
-      const currentYOffset = window.pageYOffset;
-
-      // original throttling
-      if (this.state.isSubnavCompact && scrollThresForThrottling < currentYOffset) {
-        return;
-      }
-
-      this.setState({
-        isHeaderSticky: scrollThresForSticky < currentYOffset,
-        isSubnavCompact: scrollThresForCompact < currentYOffset,
-      });
-    });
-
     const unlinkPageButton = document.getElementById('unlink-page-button');
     if (unlinkPageButton != null) {
       unlinkPageButton.addEventListener('click', async() => {

+ 0 - 4
src/client/styles/scss/_layout.scss

@@ -14,10 +14,6 @@ body {
   font-weight: bold;
 }
 
-#page-wrapper {
-  margin-top: $grw-navbar-height + $grw-navbar-border-width;
-}
-
 .grw-modal-head {
   font-size: 1em;
   border-bottom: 1px solid $gray-500;

+ 2 - 0
src/client/styles/scss/_navbar.scss

@@ -1,4 +1,6 @@
 .grw-navbar {
+  top: -$grw-navbar-height !important;
+
   max-height: $grw-navbar-height + $grw-navbar-border-width;
   border-top: 0;
   border-right: 0;

+ 1 - 1
src/client/styles/scss/_search.scss

@@ -116,7 +116,7 @@
 
 // layout
 .search-top {
-  .grw-search-top-fixed {
+  .grw-search-top-absolute {
     // centering on navbar
     top: $grw-navbar-height / 2;
     left: 50vw;

+ 27 - 3
src/client/styles/scss/_sidebar.scss

@@ -18,7 +18,14 @@
     }
   }
 
+  // sticky
+  position: sticky;
+  top: $grw-navbar-border-width;
+  z-index: $zindex-sticky;
+
   .ak-navigation-resize-button {
+    position: fixed;
+
     // locate to the center of screen
     top: calc(50vh - 20px);
 
@@ -51,19 +58,33 @@
   // override @atlaskit/navigation-next styles
   $navbar-total-height: $grw-navbar-height + $grw-navbar-border-width;
   div[data-layout-container='true'] {
-    height: calc(100vh - #{$navbar-total-height});
+    height: 100vh;
+
+    // css-teprsg
+    > div:nth-of-type(2) {
+      padding-left: unset !important;
+      margin-left: unset !important;
+    }
   }
   div[data-testid='Navigation'] {
+    position: unset;
+
     top: $navbar-total-height;
 
     // Adjust to be on top of the growi subnavigation
-    z-index: $zindex-sticky + 5;
+    // z-index: $zindex-sticky + 5;
 
     transition: left 300ms cubic-bezier(0.25, 1, 0.5, 1);
 
+    // css-xxx-ContainerNavigationMask
+    > div:nth-of-type(1) {
+    }
     // css-xxx-Outer
     > div:nth-of-type(2) {
+      z-index: 100; // greater than the value of slimScrollBar
+
       width: 0;
+      transform: unset; // unset for 'position: fixed' of .ak-navigation-resize-button
 
       // css-xxx-Shadow
       > div:first-child {
@@ -73,7 +94,7 @@
   }
 
   .grw-sidebar-nav {
-    height: calc(100vh - #{$navbar-total-height});
+    height: 100vh;
 
     .btn {
       width: $grw-sidebar-nav-width;
@@ -108,6 +129,9 @@
     }
 
     .grw-sidebar-nav-secondary-container {
+      position: fixed;
+      bottom: 1.5rem;
+
       .btn {
         padding: 0.9em;
         i {

+ 0 - 3
src/client/styles/scss/theme/_apply-colors-dark.scss

@@ -59,9 +59,6 @@ textarea.form-control {
  */
 .dropdown-menu {
   background-color: $bgcolor-global;
-  > li > a {
-    color: $color-global;
-  }
 }
 
 /*

+ 1 - 3
src/client/styles/scss/theme/_apply-colors.scss

@@ -76,9 +76,7 @@ pre:not(.hljs):not(.CodeMirror-line) {
 
 // Dropdown
 .dropdown-menu {
-  form {
-    color: $color-global;
-  }
+  color: $color-global;
 }
 
 .dropdown-item {

+ 1 - 0
src/server/models/config.js

@@ -182,6 +182,7 @@ module.exports = function(crowi) {
       crowi: {
         title: crowi.appService.getAppTitle(),
         url: crowi.appService.getSiteUrl(),
+        confidential: crowi.appService.getAppConfidential(),
       },
       upload: {
         image: crowi.fileUploadService.getIsUploadable(),

+ 4 - 0
src/server/service/app.js

@@ -39,6 +39,10 @@ class AppService {
     return -(this.configManager.getConfig('crowi', 'app:timezone') || 9) * 60;
   }
 
+  getAppConfidential() {
+    return this.configManager.getConfig('crowi', 'app:confidential');
+  }
+
   /**
    * Execute only once for installing application
    */

+ 0 - 6
src/server/views/layout-growi/page.html

@@ -17,14 +17,8 @@
 
       {% include '../widget/page_content.html' %}
 
-      {# force remove #revision-toc from #content_main of parent #}
-      <script>
-        $('#revision-toc').remove();
-      </script>
-
     </div>
 
-    {# relocate #revision-toc #}
     <div class="col-xl-2 col-lg-3 d-none d-lg-block revision-toc-container">
       {% include './widget/liker-and-seenusers.html' %}
       <div id="revision-toc" class="revision-toc mt-3 sps sps--abv" data-sps-offset="123">

+ 0 - 6
src/server/views/layout-growi/page_list.html

@@ -17,14 +17,8 @@
 
       {% include '../widget/page_content.html' %}
 
-      {# force remove #revision-toc from #content_main of parent #}
-      <script>
-        $('#revision-toc').remove();
-      </script>
-
     </div>
 
-    {# relocate #revision-toc #}
     <div class="col-xl-2 col-lg-3 d-none d-lg-block revision-toc-container">
       {% include './widget/liker-and-seenusers.html' %}
       <div id="revision-toc" class="revision-toc mt-3 sps sps--abv" data-sps-offset="123">

+ 0 - 5
src/server/views/layout-kibela/page.html

@@ -16,14 +16,9 @@
   <div class="col-12 col-xl-9 col-lg-8 bg-white round-corner">
 
     {% include '../widget/page_content.html' %}
-    {# force remove #revision-toc from #content_main of parent #}
-    <script>
-      $('#revision-toc').remove();
-    </script>
 
   </div>
 
-  {# relocate #revision-toc #}
   <div class="col-xl-3 col-lg-4 d-none d-lg-block revision-toc-container">
     <div id="revision-toc" class="revision-toc sps sps--abv" data-sps-offset="80">
       <div id="revision-toc-content" class="revision-toc-content"></div>

+ 0 - 6
src/server/views/layout-kibela/page_list.html

@@ -18,12 +18,6 @@
 
   </div>
 
-  {# force remove #revision-toc from #content_main of parent #}
-  <script>
-    $('#revision-toc').remove();
-  </script>
-
-  {# relocate #revision-toc #}
   <div class="col-xl-3 col-lg-4 d-none d-lg-block revision-toc-container">
     <div id="revision-toc" class="revision-toc sps sps--abv" data-sps-offset="80">
       <div id="revision-toc-content" class="revision-toc-content"></div>

+ 0 - 6
src/server/views/layout-kibela/user_page.html

@@ -35,14 +35,8 @@
 
       {% include '../widget/page_content.html' %}
 
-      {# force remove #revision-toc from #content_main of parent #}
-      <script>
-        $('#revision-toc').remove();
-      </script>
-
     </div>
 
-    {# relocate #revision-toc #}
     <div class="col-xl-3 col-lg-4 d-none d-lg-block revision-toc-container">
       <div id="revision-toc" class="revision-toc sps sps--abv" data-sps-offset="75">
         <div id="revision-toc-content" class="revision-toc-content"></div>

+ 1 - 42
src/server/views/layout/layout.html

@@ -74,48 +74,7 @@
 <div id="wrapper">
 
   {% block layout_head_nav %}
-  <nav class="navbar grw-navbar navbar-expand navbar-dark fixed-top mb-0 px-0">
-    {# Brand Logo #}
-    <div class="navbar-brand mr-0">
-      <a class="grw-logo d-block" href="/">
-        {% include '../widget/logo.html' %}
-      </a>
-    </div>
-    <ul class="navbar-nav d-md-none">
-      <li id="grw-navbar-toggler" class="nav-item"></li>
-    </ul>
-    <div class="grw-app-title d-none d-md-block">
-      {{ appService.getAppTitle() | preventXss }}
-    </div>
-
-    {# Navbar Right #}
-    <ul class="navbar-nav ml-auto">
-      {% if user %}
-        <li id="create-page-button" class="nav-item d-none d-md-block"></li>
-        {% if isSearchServiceConfigured() %}
-          <li class="nav-item d-md-none">
-            <a type="button" class="nav-link px-4" data-target="#grw-search-top-collapse" data-toggle="collapse">
-              <i class="icon-magnifier mr-2"></i>
-            </a>
-          </li>
-        {% endif %}
-        <li id="personal-dropdown" class="grw-personal-dropdown nav-item dropdown dropdown-toggle dropdown-toggle-no-caret"></li>
-      {% else %}
-        <li id="login-user" class="nav-item"><a class="nav-link" href="/login">Login</a></li>
-      {% endif %}
-
-      {% if getConfig('crowi', 'app:confidential') %}
-        <li class="nav-item confidential text-light">
-          <i class="icon-info d-md-none" data-toggle="tooltip" title="{{ getConfig('crowi', 'app:confidential') }}"></i>
-          <span class="d-none d-md-inline">
-            {{ getConfig('crowi', 'app:confidential') }}
-          </span>
-        </li>
-      {% endif %}
-    </ul>
-
-  </nav>
-
+    <nav id="grw-navbar" class="navbar grw-navbar navbar-expand navbar-dark sticky-top mb-0 px-0"></nav>
   {% endblock  %} {# layout_head_nav #}
 
   {% block head_warn_breaking_changes %}{% include '../widget/alert_breaking_changes.html' %}{% endblock %}

+ 0 - 4
src/server/views/widget/page_content.html

@@ -46,10 +46,6 @@
 
       {# formatted text #}
       <div class="tab-pane active" id="revision-body">
-        <div class="revision-toc d-print-none" id="revision-toc">
-          <a data-toggle="collapse" data-parent="#revision-toc" href="#revision-toc-content" class="revision-toc-head">{{ t('Table of Contents') }}</a>
-          <div id="revision-toc-content" class="revision-toc-content collapse in"></div>
-        </div>
         <div id="page" class="mt-4"></div>
       </div>
     {% endif %}

+ 3 - 1
src/server/views/widget/user_page_content.html

@@ -12,13 +12,13 @@
         <span class="d-none d-sm-inline">Recently Created</span>
       </a>
     </li>
+    {% if user._id.toString() == pageUser._id.toString() %}
     <li class="nav-item">
       <a class="nav-link" href="#user-draft-list" role="tab" data-toggle="tab">
         <i class="icon-docs"></i>
         <span class="d-none d-sm-inline">My Drafts</span>
       </a>
     </li>
-    {% if user._id.toString() == pageUser._id.toString() %}
     <li class="nav-item">
       <a class="nav-link" href="/me" role="tab">
         <i class="icon-wrench"></i>
@@ -45,10 +45,12 @@
       </div>
     </div>
 
+    {% if user._id.toString() == pageUser._id.toString() %}
     <div class="tab-pane user-draft-list page-list" id="user-draft-list">
       <div class="page-list-container">
       </div>
     </div>
+    {% endif %}
   </div>
 </div>