Parcourir la source

Merge pull request #2877 from weseek/imprv/gw4065-create-component-for-trash-andNotFound

Imprv/gw4065 create component for trash and not found
Kaori Tokashiki il y a 5 ans
Parent
commit
65aa1f703a

+ 3 - 0
src/client/js/app.jsx

@@ -15,6 +15,7 @@ import PageComments from './components/PageComments';
 import PageTimeline from './components/PageTimeline';
 import CommentEditorLazyRenderer from './components/PageComment/CommentEditorLazyRenderer';
 import PageManagement from './components/Page/PageManagement';
+import TrashPage from './components/TrashPage';
 import TrashPageAlert from './components/Page/TrashPageAlert';
 import NotFoundAlert from './components/Page/NotFoundAlert';
 import PageStatusAlert from './components/PageStatusAlert';
@@ -74,6 +75,8 @@ Object.assign(componentMappings, {
 
   'trash-page-alert': <TrashPageAlert />,
 
+  'trash-page': <TrashPage />,
+
   'not-found-alert': <NotFoundAlert onPageCreateClicked={navigationContainer.setEditorMode} />,
 
   'page-timeline': <PageTimeline />,

+ 83 - 0
src/client/js/components/CustomNavigation.jsx

@@ -0,0 +1,83 @@
+import React, { useEffect, useState } from 'react';
+import PropTypes from 'prop-types';
+import {
+  Nav, NavItem, NavLink, TabContent, TabPane,
+} from 'reactstrap';
+
+
+const CustomNavigation = (props) => {
+  const [activeTab, setActiveTab] = useState('');
+  // [TODO: set default active tab by gw4079]
+  const [sliderWidth, setSliderWidth] = useState(null);
+  const [sliderMarginLeft, setSliderMarginLeft] = useState(null);
+
+  function switchActiveTab(activeTab) {
+    setActiveTab(activeTab);
+  }
+
+  // Might make this dynamic for px, %, pt, em
+  function getPercentage(min, max) {
+    return min / max * 100;
+  }
+
+  useEffect(() => {
+    if (activeTab === '') {
+      return;
+    }
+
+    const navBar = document.getElementById('grw-custom-navbar');
+    const navTabs = document.querySelectorAll('ul.grw-custom-navbar > li.grw-custom-navtab');
+
+    if (navBar == null || navTabs == null) {
+      return;
+    }
+
+    let tempML = 0;
+
+    const styles = [].map.call(navTabs, (el) => {
+      const width = getPercentage(el.offsetWidth, navBar.offsetWidth);
+      const marginLeft = tempML;
+      tempML += width;
+      return { width, marginLeft };
+    });
+    const { width, marginLeft } = styles[props.navTabMapping[activeTab].index];
+
+    setSliderWidth(width);
+    setSliderMarginLeft(marginLeft);
+
+  }, [activeTab]);
+
+
+  return (
+    <React.Fragment>
+      <Nav className="nav-title grw-custom-navbar" id="grw-custom-navbar">
+        {Object.entries(props.navTabMapping).map(([key, value]) => {
+          return (
+            <NavItem key={key} type="button" className={`p-0 grw-custom-navtab ${activeTab === key && 'active'}`}>
+              <NavLink onClick={() => { switchActiveTab(key) }}>
+                {value.icon}
+                {value.i18n}
+              </NavLink>
+            </NavItem>
+          );
+        })}
+      </Nav>
+      <hr className="my-0 grw-nav-slide-hr border-none" style={{ width: `${sliderWidth}%`, marginLeft: `${sliderMarginLeft}%` }} />
+      <TabContent activeTab={activeTab} className="p-5">
+        {Object.entries(props.navTabMapping).map(([key, value]) => {
+          return (
+            <TabPane key={key} tabId={key}>
+              {value.tabContent}
+            </TabPane>
+          );
+        })}
+      </TabContent>
+    </React.Fragment>
+  );
+};
+
+CustomNavigation.propTypes = {
+  navTabMapping: PropTypes.object,
+};
+
+export default CustomNavigation;

+ 32 - 0
src/client/js/components/TrashPage.jsx

@@ -0,0 +1,32 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
+import PageListIcon from './Icons/PageListIcon';
+import CustomNavigation from './CustomNavigation';
+
+
+const TrashPage = (props) => {
+  const { t } = props;
+
+  const navTabMapping = {
+    pagelist: {
+      icon: <PageListIcon />,
+      i18n: t('page_list'),
+      // [TODO: show trash page list by gw4064]
+      tabContent: t('Trash page list'),
+      index: 0,
+    },
+  };
+
+  return (
+    <div className="grw-trash-page mt-5">
+      <CustomNavigation navTabMapping={navTabMapping} />
+    </div>
+  );
+};
+
+TrashPage.propTypes = {
+  t: PropTypes.func.isRequired, //  i18next
+};
+
+export default withTranslation()(TrashPage);

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

@@ -75,3 +75,15 @@
     bottom: -$grw-navbar-bottom-height;
   }
 }
+
+.grw-trash-page {
+  .grw-nav-slide-hr {
+    border-bottom: 2px solid;
+    transition: 0.3s ease-in-out;
+  }
+  .nav-link svg {
+    width: 17px;
+    height: 17px;
+    margin-right: 5px;
+  }
+}

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

@@ -288,6 +288,18 @@ pre:not(.hljs):not(.CodeMirror-line) {
   }
 }
 
+.grw-trash-page {
+  .nav-title {
+    color: $color-link;
+  }
+  .nav-link svg {
+    fill: $color-link;
+  }
+  .grw-nav-slide-hr {
+    border-color: $color-link;
+  }
+}
+
 .grw-page-accessories-modal {
   .nav-title {
     color: $color-link;

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

@@ -26,6 +26,7 @@
 
 
 {% block content_main_after %}
+  <div id="trash-page"></div>
   {% if page %}
     {% include '../widget/page_attachments.html' %}
   {% endif %}