Explorar el Código

Merge pull request #3506 from weseek/feat/custom-sidebar-3

Feat/custom sidebar 3
Yuki Takei hace 5 años
padre
commit
219c79595a

+ 2 - 0
src/client/js/components/Page/RevisionRenderer.jsx

@@ -110,6 +110,7 @@ class RevisionRenderer extends React.PureComponent {
       <RevisionBody
         html={this.state.html}
         isMathJaxEnabled={isMathJaxEnabled}
+        additionalClassName={this.props.additionalClassName}
         renderMathJaxOnInit
       />
     );
@@ -129,6 +130,7 @@ RevisionRenderer.propTypes = {
   growiRenderer: PropTypes.instanceOf(GrowiRenderer).isRequired,
   markdown: PropTypes.string.isRequired,
   highlightKeywords: PropTypes.string,
+  additionalClassName: PropTypes.string,
 };
 
 export default RevisionRendererWrapper;

+ 72 - 27
src/client/js/components/Sidebar/CustomSidebar.jsx

@@ -1,45 +1,90 @@
-import React from 'react';
-// import PropTypes from 'prop-types';
+import React, {
+  useState, useCallback, useEffect,
+} from 'react';
+import PropTypes from 'prop-types';
 
-import { withTranslation } from 'react-i18next';
+import loggerFactory from '@alias/logger';
 
 import { withUnstatedContainers } from '../UnstatedUtils';
 import AppContainer from '../../services/AppContainer';
+import RevisionRenderer from '../Page/RevisionRenderer';
 
-class CustomSidebar extends React.Component {
+const logger = loggerFactory('growi:cli:CustomSidebar');
 
-  static propTypes = {
-  };
 
-  state = {
-  };
+const SidebarNotFound = () => {
+  return (
+    <div className="grw-sidebar-content-header h5 text-center p-3">
+      <a href="/Sidebar#edit">
+        <i className="icon-magic-wand"></i> Create <strong>/Sidebar</strong> page
+      </a>
+    </div>
+  );
+};
 
-  renderHeaderWordmark() {
-    return <h3>Custom Sidebar</h3>;
-  }
+const CustomSidebar = (props) => {
 
-  render() {
-    return (
-      <>
-        <div className="grw-sidebar-content-header p-3 d-flex">
-          <h3 className="mb-0">Custom Sidebar</h3>
-          <button type="button" className="btn btn-sm btn-outline-secondary ml-auto" onClick={this.reloadData}>
-            <i className="icon icon-reload"></i>
-          </button>
-        </div>
-        <div className="grw-sidebar-content-header p-3">
-          (TBD) Under implementation
+  const { appContainer } = props;
+  const { apiGet } = appContainer;
+
+  const [isMounted, setMounted] = useState(false);
+  const [markdown, setMarkdown] = useState();
+
+  const growiRenderer = appContainer.getRenderer('sidebar');
+
+  // TODO: refactor with SWR
+  const fetchDataAndRenderHtml = useCallback(async() => {
+    let page = null;
+    try {
+      const result = await apiGet('/pages.get', { path: '/Sidebar' });
+      page = result.page;
+    }
+    catch (e) {
+      logger.warn(e.message);
+      return;
+    }
+    finally {
+      setMounted(true);
+    }
+
+    setMarkdown(page.revision.body);
+  }, [apiGet]);
+
+  useEffect(() => {
+    fetchDataAndRenderHtml();
+  }, [fetchDataAndRenderHtml]);
+
+  return (
+    <>
+      <div className="grw-sidebar-content-header p-3 d-flex">
+        <h3 className="mb-0">Custom Sidebar</h3>
+        <button type="button" className="btn btn-sm btn-outline-secondary ml-auto" onClick={fetchDataAndRenderHtml}>
+          <i className="icon icon-reload"></i>
+        </button>
+      </div>
+      { isMounted && markdown == null && <SidebarNotFound /> }
+      {/* eslint-disable-next-line react/no-danger */}
+      { markdown != null && (
+        <div className="p-3">
+          <RevisionRenderer
+            growiRenderer={growiRenderer}
+            markdown={markdown}
+            additionalClassName="grw-custom-sidebar-content"
+          />
         </div>
-      </>
-    );
+      ) }
+    </>
+  );
 
-  }
+};
 
-}
+CustomSidebar.propTypes = {
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+};
 
 /**
  * Wrapper component for using unstated
  */
 const CustomSidebarWrapper = withUnstatedContainers(CustomSidebar, [AppContainer]);
 
-export default withTranslation()(CustomSidebarWrapper);
+export default CustomSidebarWrapper;

+ 1 - 1
src/client/js/components/Sidebar/SidebarNav.jsx

@@ -23,7 +23,7 @@ class SidebarNav extends React.Component {
       onItemSelected(contentsId);
     }
 
-    navigationContainer.setState({ sidebarContentsId: contentsId });
+    navigationContainer.selectSidebarContents(contentsId);
   }
 
   PrimaryItem = ({ id, label, iconName }) => {

+ 12 - 2
src/client/js/services/NavigationContainer.js

@@ -31,7 +31,7 @@ export default class NavigationContainer extends Container {
       isDrawerMode: null,
       isDrawerOpened: false,
 
-      sidebarContentsId: 'recent',
+      sidebarContentsId: localStorage.sidebarContentsId || 'recent',
 
       isScrollTop: true,
 
@@ -109,6 +109,7 @@ export default class NavigationContainer extends Container {
       $('body').removeClass('on-edit');
       $('body').removeClass('builtin-editor');
       $('body').removeClass('hackmd');
+      $('body').removeClass('pathname-sidebar');
       window.history.replaceState(null, '', window.location.pathname);
     }
 
@@ -116,6 +117,10 @@ export default class NavigationContainer extends Container {
       $('body').addClass('on-edit');
       $('body').addClass('builtin-editor');
       $('body').removeClass('hackmd');
+      // editing /Sidebar
+      if (window.location.pathname === '/Sidebar') {
+        $('body').addClass('pathname-sidebar');
+      }
       window.location.hash = '#edit';
     }
 
@@ -123,8 +128,8 @@ export default class NavigationContainer extends Container {
       $('body').addClass('on-edit');
       $('body').addClass('hackmd');
       $('body').removeClass('builtin-editor');
+      $('body').removeClass('pathname-sidebar');
       window.location.hash = '#hackmd';
-
     }
 
     this.updateDrawerMode({ ...this.state, editorMode }); // generate newest state object
@@ -185,6 +190,11 @@ export default class NavigationContainer extends Container {
     this.setState({ isDrawerMode, isDrawerOpened });
   }
 
+  selectSidebarContents(contentsId) {
+    window.localStorage.setItem('sidebarContentsId', contentsId);
+    this.setState({ sidebarContentsId: contentsId });
+  }
+
   openPageCreateModal() {
     if (this.appContainer.currentUser == null) {
       logger.warn('Please login or signup to create a new page.');

+ 14 - 0
src/client/styles/scss/_on-edit.scss

@@ -256,6 +256,20 @@ body.on-edit {
 
   // .builtin-editor .tab-pane#edit
 
+  // editing /Sidebar
+  &.pathname-sidebar {
+    .page-editor-preview-body {
+      width: 320px;
+      padding-top: 0;
+      margin-right: auto;
+      margin-left: auto;
+
+      .wiki {
+        @extend %grw-custom-sidebar-content;
+      }
+    }
+  }
+
   &.hackmd {
     .hackmd-preinit,
     #iframe-hackmd-container > iframe {

+ 50 - 0
src/client/styles/scss/_sidebar-wiki.scss

@@ -0,0 +1,50 @@
+%grw-custom-sidebar-content {
+  h1,
+  h2,
+  h3,
+  h4,
+  h5,
+  h6 {
+    margin-top: 1em;
+    margin-bottom: 0.4em;
+
+    &:first-child {
+      margin-top: 0;
+    }
+  }
+
+  h1 {
+    padding-top: 0.2em;
+    font-size: 1.4em;
+    line-height: 1em;
+  }
+  h2 {
+    padding-top: 0.2em;
+    font-size: 1.2em;
+    line-height: 1em;
+  }
+  h3 {
+    font-size: 1.1em;
+  }
+  h4 {
+    font-size: 1.05em;
+  }
+  h5 {
+    font-size: 1.03em;
+  }
+
+  ul,
+  ol {
+    padding-left: 20px;
+    margin: 10px 0;
+  }
+
+  .page-list .page-list-ul {
+    padding-left: 0;
+    margin: 0;
+  }
+}
+
+.grw-custom-sidebar-content.wiki {
+  @extend %grw-custom-sidebar-content;
+}

+ 1 - 0
src/client/styles/scss/style-app.scss

@@ -59,6 +59,7 @@
 @import 'search';
 @import 'shortcuts';
 @import 'sidebar';
+@import 'sidebar-wiki';
 @import 'subnav';
 @import 'tag';
 @import 'toc';

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

@@ -439,6 +439,16 @@ body.on-edit {
   }
 }
 
+/*
+ * Preview for editing /Sidebar
+ */
+body.pathname-sidebar {
+  .page-editor-preview-body {
+    color: $color-sidebar-context;
+    background-color: $bgcolor-sidebar-context;
+  }
+}
+
 /*
  * GROWI Grid Edit Modal
  */