|
|
@@ -1,103 +1,100 @@
|
|
|
-import React, { Component } from 'react';
|
|
|
-import PropTypes from 'prop-types';
|
|
|
-import { withTranslation, Trans } from '@/components/Common/Translate';
|
|
|
-import Link from 'next/link';
|
|
|
-import Router, { withRouter } from 'next/router';
|
|
|
-import { Collapse, Badge } from 'reactstrap';
|
|
|
+import React, { Component } from "react";
|
|
|
+import PropTypes from "prop-types";
|
|
|
+import { withTranslation, Trans } from "@/components/Common/Translate";
|
|
|
+import Link from "next/link";
|
|
|
+import Router, { withRouter } from "next/router";
|
|
|
+import { Collapse, Badge } from "reactstrap";
|
|
|
|
|
|
-import { connect } from 'react-redux';
|
|
|
-import { bindActionCreators } from 'redux';
|
|
|
-import * as actions from '../../store/actions/actions';
|
|
|
+import { connect } from "react-redux";
|
|
|
+import { bindActionCreators } from "redux";
|
|
|
+import * as actions from "../../store/actions/actions";
|
|
|
|
|
|
-import SidebarUserBlock from './SidebarUserBlock';
|
|
|
+import SidebarUserBlock from "./SidebarUserBlock";
|
|
|
|
|
|
-import Menu from './Menu.js';
|
|
|
+import Menu from "./Menu.js";
|
|
|
+import MenuPT from "./MenuPT.js";
|
|
|
+// localStorage.getItem("user");
|
|
|
+// import Menu from './MenuPT.js';
|
|
|
|
|
|
// Helper to check for parrent of an given elements
|
|
|
const parents = (element, selector) => {
|
|
|
- if (typeof selector !== 'string') {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- const parents = [];
|
|
|
- let ancestor = element.parentNode;
|
|
|
-
|
|
|
- while (
|
|
|
- ancestor &&
|
|
|
- ancestor.nodeType === Node.ELEMENT_NODE &&
|
|
|
- ancestor.nodeType !== 3 /*NODE_TEXT*/
|
|
|
- ) {
|
|
|
- if (ancestor.matches(selector)) {
|
|
|
- parents.push(ancestor);
|
|
|
- }
|
|
|
-
|
|
|
- ancestor = ancestor.parentNode;
|
|
|
- }
|
|
|
- return parents;
|
|
|
+ if (typeof selector !== "string") {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ const parents = [];
|
|
|
+ let ancestor = element.parentNode;
|
|
|
+
|
|
|
+ while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== 3 /*NODE_TEXT*/) {
|
|
|
+ if (ancestor.matches(selector)) {
|
|
|
+ parents.push(ancestor);
|
|
|
+ }
|
|
|
+
|
|
|
+ ancestor = ancestor.parentNode;
|
|
|
+ }
|
|
|
+ return parents;
|
|
|
};
|
|
|
// Helper to get outerHeight of a dom element
|
|
|
const outerHeight = (elem, includeMargin) => {
|
|
|
- const style = getComputedStyle(elem);
|
|
|
- const margins = includeMargin
|
|
|
- ? parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10)
|
|
|
- : 0;
|
|
|
- return elem.offsetHeight + margins;
|
|
|
+ const style = getComputedStyle(elem);
|
|
|
+ const margins = includeMargin ? parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10) : 0;
|
|
|
+ return elem.offsetHeight + margins;
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
Component to display headings on sidebar
|
|
|
*/
|
|
|
const SidebarItemHeader = ({ item }) => (
|
|
|
- <li className="nav-heading">
|
|
|
- <span>
|
|
|
- <Trans i18nKey={item.translate}>{item.heading}</Trans>
|
|
|
- </span>
|
|
|
- </li>
|
|
|
+ <li className="nav-heading">
|
|
|
+ <span>
|
|
|
+ <Trans i18nKey={item.translate}>{item.heading}</Trans>
|
|
|
+ </span>
|
|
|
+ </li>
|
|
|
);
|
|
|
|
|
|
/**
|
|
|
Normal items for the sidebar
|
|
|
*/
|
|
|
const SidebarItem = ({ item, isActive, className, onMouseEnter }) => (
|
|
|
- <li className={isActive ? 'active' : ''} onMouseEnter={onMouseEnter}>
|
|
|
- <Link href={item.path} as={item.as}>
|
|
|
- <a title={item.name}>
|
|
|
- {item.label && (
|
|
|
- <Badge tag="div" className="float-right" color={item.label.color}>
|
|
|
- {item.label.value}
|
|
|
- </Badge>
|
|
|
- )}
|
|
|
- {item.icon && <em className={item.icon} />}
|
|
|
- <span>
|
|
|
- <Trans i18nKey={item.translate}>{item.name}</Trans>
|
|
|
- </span>
|
|
|
- </a>
|
|
|
- </Link>
|
|
|
- </li>
|
|
|
+ <li className={isActive ? "active" : ""} onMouseEnter={onMouseEnter}>
|
|
|
+ <Link href={item.path} as={item.as}>
|
|
|
+ <a title={item.name}>
|
|
|
+ {item.label && (
|
|
|
+ <Badge tag="div" className="float-right" color={item.label.color}>
|
|
|
+ {item.label.value}
|
|
|
+ </Badge>
|
|
|
+ )}
|
|
|
+ {item.icon && <em className={item.icon} />}
|
|
|
+ <span>
|
|
|
+ <Trans i18nKey={item.translate}>{item.name}</Trans>
|
|
|
+ </span>
|
|
|
+ </a>
|
|
|
+ </Link>
|
|
|
+ </li>
|
|
|
);
|
|
|
|
|
|
/**
|
|
|
Build a sub menu with items inside and attach collapse behavior
|
|
|
*/
|
|
|
const SidebarSubItem = ({ item, isActive, handler, children, isOpen, onMouseEnter }) => (
|
|
|
- <li className={isActive ? 'active' : ''}>
|
|
|
- <div className="nav-item" onClick={handler} onMouseEnter={onMouseEnter}>
|
|
|
- {item.label && (
|
|
|
- <Badge tag="div" className="float-right" color={item.label.color}>
|
|
|
- {item.label.value}
|
|
|
- </Badge>
|
|
|
- )}
|
|
|
- {item.icon && <em className={item.icon} />}
|
|
|
- <span>
|
|
|
- <Trans i18nKey={item.translate}>{item.name}</Trans>
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- <Collapse isOpen={isOpen}>
|
|
|
- <ul id={item.path} className="sidebar-nav sidebar-subnav">
|
|
|
- {children}
|
|
|
- </ul>
|
|
|
- </Collapse>
|
|
|
- </li>
|
|
|
+ <li className={isActive ? "active" : ""}>
|
|
|
+ <div className="nav-item" onClick={handler} onMouseEnter={onMouseEnter}>
|
|
|
+ {item.label && (
|
|
|
+ <Badge tag="div" className="float-right" color={item.label.color}>
|
|
|
+ {item.label.value}
|
|
|
+ </Badge>
|
|
|
+ )}
|
|
|
+ {item.icon && <em className={item.icon} />}
|
|
|
+ <span>
|
|
|
+ <Trans i18nKey={item.translate}>{item.name}</Trans>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <Collapse isOpen={isOpen}>
|
|
|
+ <ul id={item.path} className="sidebar-nav sidebar-subnav">
|
|
|
+ {children}
|
|
|
+ </ul>
|
|
|
+ </Collapse>
|
|
|
+ </li>
|
|
|
);
|
|
|
|
|
|
/**
|
|
|
@@ -105,263 +102,221 @@ const SidebarSubItem = ({ item, isActive, handler, children, isOpen, onMouseEnte
|
|
|
*/
|
|
|
const SidebarSubHeader = ({ item }) => <li className="sidebar-subnav-header">{item.name}</li>;
|
|
|
|
|
|
-const SidebarBackdrop = ({ closeFloatingNav }) => (
|
|
|
- <div className="sidebar-backdrop" onClick={closeFloatingNav} />
|
|
|
-);
|
|
|
+const SidebarBackdrop = ({ closeFloatingNav }) => <div className="sidebar-backdrop" onClick={closeFloatingNav} />;
|
|
|
|
|
|
const FloatingNav = ({ item, target, routeActive, isFixed, closeFloatingNav }) => {
|
|
|
- let asideContainer = document.querySelector('.aside-container');
|
|
|
- let asideInner = asideContainer.firstElementChild; /*('.aside-inner')*/
|
|
|
- let sidebar = asideInner.firstElementChild; /*('.sidebar')*/
|
|
|
-
|
|
|
- let mar =
|
|
|
- parseInt(getComputedStyle(asideInner)['padding-top'], 0) +
|
|
|
- parseInt(getComputedStyle(asideContainer)['padding-top'], 0);
|
|
|
- let itemTop = target.parentElement.offsetTop + mar - sidebar.scrollTop;
|
|
|
- let vwHeight = document.body.clientHeight;
|
|
|
-
|
|
|
- const setPositionStyle = el => {
|
|
|
- if (!el) return;
|
|
|
- el.style.position = isFixed ? 'fixed' : 'absolute';
|
|
|
- el.style.top = itemTop + 'px';
|
|
|
- el.style.bottom = outerHeight(el, true) + itemTop > vwHeight ? 0 : 'auto';
|
|
|
- };
|
|
|
-
|
|
|
- return (
|
|
|
- <ul
|
|
|
- id={item.path}
|
|
|
- ref={setPositionStyle}
|
|
|
- className="sidebar-nav sidebar-subnav nav-floating"
|
|
|
- onMouseLeave={closeFloatingNav}
|
|
|
- >
|
|
|
- <SidebarSubHeader item={item} />
|
|
|
- {item.submenu.map((subitem, i) => (
|
|
|
- <SidebarItem key={i} item={subitem} isActive={routeActive(subitem.path)} />
|
|
|
- ))}
|
|
|
- </ul>
|
|
|
- );
|
|
|
+ let asideContainer = document.querySelector(".aside-container");
|
|
|
+ let asideInner = asideContainer.firstElementChild; /*('.aside-inner')*/
|
|
|
+ let sidebar = asideInner.firstElementChild; /*('.sidebar')*/
|
|
|
+
|
|
|
+ let mar = parseInt(getComputedStyle(asideInner)["padding-top"], 0) + parseInt(getComputedStyle(asideContainer)["padding-top"], 0);
|
|
|
+ let itemTop = target.parentElement.offsetTop + mar - sidebar.scrollTop;
|
|
|
+ let vwHeight = document.body.clientHeight;
|
|
|
+
|
|
|
+ const setPositionStyle = (el) => {
|
|
|
+ if (!el) return;
|
|
|
+ el.style.position = isFixed ? "fixed" : "absolute";
|
|
|
+ el.style.top = itemTop + "px";
|
|
|
+ el.style.bottom = outerHeight(el, true) + itemTop > vwHeight ? 0 : "auto";
|
|
|
+ };
|
|
|
+
|
|
|
+ return (
|
|
|
+ <ul id={item.path} ref={setPositionStyle} className="sidebar-nav sidebar-subnav nav-floating" onMouseLeave={closeFloatingNav}>
|
|
|
+ <SidebarSubHeader item={item} />
|
|
|
+ {item.submenu.map((subitem, i) => (
|
|
|
+ <SidebarItem key={i} item={subitem} isActive={routeActive(subitem.path)} />
|
|
|
+ ))}
|
|
|
+ </ul>
|
|
|
+ );
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
The main sidebar component
|
|
|
*/
|
|
|
class Sidebar extends Component {
|
|
|
- state = {
|
|
|
- collapse: {},
|
|
|
- showSidebarBackdrop: false,
|
|
|
- currentFloatingItem: null,
|
|
|
- currentFloatingItemTarget: null,
|
|
|
- pathname: this.props.router.pathname
|
|
|
- };
|
|
|
-
|
|
|
- componentDidMount() {
|
|
|
- // prepare the flags to handle menu collapsed states
|
|
|
- this.buildCollapseList();
|
|
|
-
|
|
|
- // Listen for routes changes in order to hide the sidebar on mobile
|
|
|
- Router.events.on('routeChangeStart', this.handleRouteChange);
|
|
|
- Router.events.on('routeChangeComplete', this.handleRouteComplete);
|
|
|
-
|
|
|
- // Attach event listener to automatically close sidebar when click outside
|
|
|
- document.addEventListener('click', this.closeSidebarOnExternalClicks);
|
|
|
- }
|
|
|
-
|
|
|
- handleRouteComplete = (pathname) => {
|
|
|
- this.setState({
|
|
|
- pathname
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- handleRouteChange = () => {
|
|
|
- this.closeFloatingNav();
|
|
|
- this.closeSidebar();
|
|
|
- };
|
|
|
-
|
|
|
- componentWillUnmount() {
|
|
|
- document.removeEventListener('click', this.closeSidebarOnExternalClicks);
|
|
|
- Router.events.off('routeChangeStart', this.handleRouteChange);
|
|
|
- Router.events.off('routeChangeComplete', this.handleRouteComplete);
|
|
|
- }
|
|
|
-
|
|
|
- closeSidebar = () => {
|
|
|
- this.props.actions.toggleSetting('asideToggled');
|
|
|
- };
|
|
|
-
|
|
|
- closeSidebarOnExternalClicks = e => {
|
|
|
- // don't check if sidebar not visible
|
|
|
- if (!this.props.settings.asideToggled) return;
|
|
|
-
|
|
|
- if (
|
|
|
- !parents(e.target, '.aside-container').length && // if not child of sidebar
|
|
|
- !parents(e.target, '.topnavbar-wrapper').length && // if not child of header
|
|
|
- !e.target.matches('#user-block-toggle') && // user block toggle anchor
|
|
|
- !e.target.parentElement.matches('#user-block-toggle') // user block toggle icon
|
|
|
- ) {
|
|
|
- this.closeSidebar();
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- /** prepare initial state of collapse menus.*/
|
|
|
- buildCollapseList = () => {
|
|
|
- let collapse = {};
|
|
|
- Menu.filter(({ heading }) => !heading).forEach(({ name, path, submenu }) => {
|
|
|
- collapse[name] = this.routeActive(submenu ? submenu.map(({ path }) => path) : path);
|
|
|
- });
|
|
|
- this.setState({ collapse });
|
|
|
- };
|
|
|
-
|
|
|
- routeActive = paths => {
|
|
|
- const currpath = this.state.pathname;
|
|
|
- paths = Array.isArray(paths) ? paths : [paths];
|
|
|
- return paths.some(p => (p === '/' ? currpath === p : currpath.indexOf(p) > -1));
|
|
|
- };
|
|
|
-
|
|
|
- toggleItemCollapse = stateName => () => {
|
|
|
- for (let c in this.state.collapse) {
|
|
|
- if (this.state.collapse[c] === true && c !== stateName)
|
|
|
- this.setState({
|
|
|
- collapse: {
|
|
|
- [c]: false
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- this.setState({
|
|
|
- collapse: {
|
|
|
- [stateName]: !this.state.collapse[stateName]
|
|
|
- }
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- getSubRoutes = item => item.submenu.map(({ path }) => path);
|
|
|
-
|
|
|
- /** map menu config to string to determine which element to render */
|
|
|
- itemType = item => {
|
|
|
- if (item.heading) return 'heading';
|
|
|
- if (!item.submenu) return 'menu';
|
|
|
- if (item.submenu) return 'submenu';
|
|
|
- };
|
|
|
-
|
|
|
- shouldUseFloatingNav = () => {
|
|
|
- return (
|
|
|
- this.props.settings.isCollapsed ||
|
|
|
- this.props.settings.isCollapsedText ||
|
|
|
- this.props.settings.asideHover
|
|
|
- );
|
|
|
- };
|
|
|
-
|
|
|
- showFloatingNav = item => e => {
|
|
|
- if (this.shouldUseFloatingNav())
|
|
|
- this.setState({
|
|
|
- currentFloatingItem: item,
|
|
|
- currentFloatingItemTarget: e.currentTarget,
|
|
|
- showSidebarBackdrop: true
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- closeFloatingNav = () => {
|
|
|
- this.setState({
|
|
|
- currentFloatingItem: null,
|
|
|
- currentFloatingItemTarget: null,
|
|
|
- showSidebarBackdrop: false
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- render() {
|
|
|
- return (
|
|
|
- <>
|
|
|
- <aside className="aside-container">
|
|
|
- {/* START Sidebar (left) */}
|
|
|
- <div className="aside-inner">
|
|
|
- <nav
|
|
|
- className={
|
|
|
- 'sidebar ' +
|
|
|
- (this.props.settings.asideScrollbar ? 'show-scrollbar' : '')
|
|
|
- }
|
|
|
- >
|
|
|
- {/* START sidebar nav */}
|
|
|
- <ul className="sidebar-nav">
|
|
|
- {/* START user info */}
|
|
|
- <li className="has-user-block">
|
|
|
- <SidebarUserBlock />
|
|
|
- </li>
|
|
|
- {/* END user info */}
|
|
|
-
|
|
|
- {/* Iterates over all sidebar items */}
|
|
|
- {Menu.map((item, i) => {
|
|
|
- // heading
|
|
|
- if (this.itemType(item) === 'heading')
|
|
|
- return <SidebarItemHeader item={item} key={i} />;
|
|
|
- else {
|
|
|
- if (this.itemType(item) === 'menu')
|
|
|
- return (
|
|
|
- <SidebarItem
|
|
|
- isActive={this.routeActive(item.path)}
|
|
|
- item={item}
|
|
|
- key={i}
|
|
|
- onMouseEnter={this.closeFloatingNav}
|
|
|
- />
|
|
|
- );
|
|
|
- if (this.itemType(item) === 'submenu')
|
|
|
- return [
|
|
|
- <SidebarSubItem
|
|
|
- item={item}
|
|
|
- isOpen={this.state.collapse[item.name]}
|
|
|
- handler={this.toggleItemCollapse(item.name)}
|
|
|
- isActive={this.routeActive(
|
|
|
- this.getSubRoutes(item)
|
|
|
- )}
|
|
|
- key={i}
|
|
|
- onMouseEnter={this.showFloatingNav(item)}
|
|
|
- >
|
|
|
- <SidebarSubHeader item={item} key={i} />
|
|
|
- {item.submenu.map((subitem, i) => (
|
|
|
- <SidebarItem
|
|
|
- key={i}
|
|
|
- item={subitem}
|
|
|
- isActive={this.routeActive(
|
|
|
- subitem.path
|
|
|
- )}
|
|
|
- />
|
|
|
- ))}
|
|
|
- </SidebarSubItem>
|
|
|
- ];
|
|
|
- }
|
|
|
- return null; // unrecognized item
|
|
|
- })}
|
|
|
- </ul>
|
|
|
- {/* END sidebar nav */}
|
|
|
- </nav>
|
|
|
- </div>
|
|
|
- {/* END Sidebar (left) */}
|
|
|
- {this.state.currentFloatingItem && this.state.currentFloatingItem.submenu && (
|
|
|
- <FloatingNav
|
|
|
- item={this.state.currentFloatingItem}
|
|
|
- target={this.state.currentFloatingItemTarget}
|
|
|
- routeActive={this.routeActive}
|
|
|
- isFixed={this.props.settings.isFixed}
|
|
|
- closeFloatingNav={this.closeFloatingNav}
|
|
|
- />
|
|
|
- )}
|
|
|
- </aside>
|
|
|
- {this.state.showSidebarBackdrop && (
|
|
|
- <SidebarBackdrop closeFloatingNav={this.closeFloatingNav} />
|
|
|
- )}
|
|
|
- </>
|
|
|
- );
|
|
|
- }
|
|
|
+ menu = [];
|
|
|
+ state = {
|
|
|
+ collapse: {},
|
|
|
+ showSidebarBackdrop: false,
|
|
|
+ currentFloatingItem: null,
|
|
|
+ currentFloatingItemTarget: null,
|
|
|
+ pathname: this.props.router.pathname,
|
|
|
+ };
|
|
|
+
|
|
|
+ componentDidMount() {
|
|
|
+ this.menu = JSON.parse(sessionStorage.getItem("user")).peran[0].peran.id === 2 ? MenuPT : Menu;
|
|
|
+ // prepare the flags to handle menu collapsed states
|
|
|
+ this.buildCollapseList();
|
|
|
+
|
|
|
+ // Listen for routes changes in order to hide the sidebar on mobile
|
|
|
+ Router.events.on("routeChangeStart", this.handleRouteChange);
|
|
|
+ Router.events.on("routeChangeComplete", this.handleRouteComplete);
|
|
|
+
|
|
|
+ // Attach event listener to automatically close sidebar when click outside
|
|
|
+ document.addEventListener("click", this.closeSidebarOnExternalClicks);
|
|
|
+ }
|
|
|
+
|
|
|
+ handleRouteComplete = (pathname) => {
|
|
|
+ this.setState({
|
|
|
+ pathname,
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ handleRouteChange = () => {
|
|
|
+ this.closeFloatingNav();
|
|
|
+ this.closeSidebar();
|
|
|
+ };
|
|
|
+
|
|
|
+ componentWillUnmount() {
|
|
|
+ document.removeEventListener("click", this.closeSidebarOnExternalClicks);
|
|
|
+ Router.events.off("routeChangeStart", this.handleRouteChange);
|
|
|
+ Router.events.off("routeChangeComplete", this.handleRouteComplete);
|
|
|
+ }
|
|
|
+
|
|
|
+ closeSidebar = () => {
|
|
|
+ this.props.actions.toggleSetting("asideToggled");
|
|
|
+ };
|
|
|
+
|
|
|
+ closeSidebarOnExternalClicks = (e) => {
|
|
|
+ // don't check if sidebar not visible
|
|
|
+ if (!this.props.settings.asideToggled) return;
|
|
|
+
|
|
|
+ if (
|
|
|
+ !parents(e.target, ".aside-container").length && // if not child of sidebar
|
|
|
+ !parents(e.target, ".topnavbar-wrapper").length && // if not child of header
|
|
|
+ !e.target.matches("#user-block-toggle") && // user block toggle anchor
|
|
|
+ !e.target.parentElement.matches("#user-block-toggle") // user block toggle icon
|
|
|
+ ) {
|
|
|
+ this.closeSidebar();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ /** prepare initial state of collapse menus.*/
|
|
|
+ buildCollapseList = () => {
|
|
|
+ let collapse = {};
|
|
|
+ this.menu
|
|
|
+ .filter(({ heading }) => !heading)
|
|
|
+ .forEach(({ name, path, submenu }) => {
|
|
|
+ collapse[name] = this.routeActive(submenu ? submenu.map(({ path }) => path) : path);
|
|
|
+ });
|
|
|
+ this.setState({ collapse });
|
|
|
+ };
|
|
|
+
|
|
|
+ routeActive = (paths) => {
|
|
|
+ const currpath = this.state.pathname;
|
|
|
+ paths = Array.isArray(paths) ? paths : [paths];
|
|
|
+ return paths.some((p) => (p === "/" ? currpath === p : currpath.indexOf(p) > -1));
|
|
|
+ };
|
|
|
+
|
|
|
+ toggleItemCollapse = (stateName) => () => {
|
|
|
+ for (let c in this.state.collapse) {
|
|
|
+ if (this.state.collapse[c] === true && c !== stateName)
|
|
|
+ this.setState({
|
|
|
+ collapse: {
|
|
|
+ [c]: false,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ }
|
|
|
+ this.setState({
|
|
|
+ collapse: {
|
|
|
+ [stateName]: !this.state.collapse[stateName],
|
|
|
+ },
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ getSubRoutes = (item) => item.submenu.map(({ path }) => path);
|
|
|
+
|
|
|
+ /** map menu config to string to determine which element to render */
|
|
|
+ itemType = (item) => {
|
|
|
+ if (item.heading) return "heading";
|
|
|
+ if (!item.submenu) return "menu";
|
|
|
+ if (item.submenu) return "submenu";
|
|
|
+ };
|
|
|
+
|
|
|
+ shouldUseFloatingNav = () => {
|
|
|
+ return this.props.settings.isCollapsed || this.props.settings.isCollapsedText || this.props.settings.asideHover;
|
|
|
+ };
|
|
|
+
|
|
|
+ showFloatingNav = (item) => (e) => {
|
|
|
+ if (this.shouldUseFloatingNav())
|
|
|
+ this.setState({
|
|
|
+ currentFloatingItem: item,
|
|
|
+ currentFloatingItemTarget: e.currentTarget,
|
|
|
+ showSidebarBackdrop: true,
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ closeFloatingNav = () => {
|
|
|
+ this.setState({
|
|
|
+ currentFloatingItem: null,
|
|
|
+ currentFloatingItemTarget: null,
|
|
|
+ showSidebarBackdrop: false,
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ render() {
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <aside className="aside-container">
|
|
|
+ {/* START Sidebar (left) */}
|
|
|
+ <div className="aside-inner">
|
|
|
+ <nav className={"sidebar " + (this.props.settings.asideScrollbar ? "show-scrollbar" : "")}>
|
|
|
+ {/* START sidebar nav */}
|
|
|
+ <ul className="sidebar-nav">
|
|
|
+ {/* START user info */}
|
|
|
+ <li className="has-user-block">
|
|
|
+ <SidebarUserBlock />
|
|
|
+ </li>
|
|
|
+ {/* END user info */}
|
|
|
+
|
|
|
+ {/* Iterates over all sidebar items */}
|
|
|
+ {this.menu.map((item, i) => {
|
|
|
+ // heading
|
|
|
+ if (this.itemType(item) === "heading") return <SidebarItemHeader item={item} key={i} />;
|
|
|
+ else {
|
|
|
+ if (this.itemType(item) === "menu") return <SidebarItem isActive={this.routeActive(item.path)} item={item} key={i} onMouseEnter={this.closeFloatingNav} />;
|
|
|
+ if (this.itemType(item) === "submenu")
|
|
|
+ return [
|
|
|
+ <SidebarSubItem
|
|
|
+ item={item}
|
|
|
+ isOpen={this.state.collapse[item.name]}
|
|
|
+ handler={this.toggleItemCollapse(item.name)}
|
|
|
+ isActive={this.routeActive(this.getSubRoutes(item))}
|
|
|
+ key={i}
|
|
|
+ onMouseEnter={this.showFloatingNav(item)}
|
|
|
+ >
|
|
|
+ <SidebarSubHeader item={item} key={i} />
|
|
|
+ {item.submenu.map((subitem, i) => (
|
|
|
+ <SidebarItem key={i} item={subitem} isActive={this.routeActive(subitem.path)} />
|
|
|
+ ))}
|
|
|
+ </SidebarSubItem>,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ return null; // unrecognized item
|
|
|
+ })}
|
|
|
+ </ul>
|
|
|
+ {/* END sidebar nav */}
|
|
|
+ </nav>
|
|
|
+ </div>
|
|
|
+ {/* END Sidebar (left) */}
|
|
|
+ {this.state.currentFloatingItem && this.state.currentFloatingItem.submenu && (
|
|
|
+ <FloatingNav item={this.state.currentFloatingItem} target={this.state.currentFloatingItemTarget} routeActive={this.routeActive} isFixed={this.props.settings.isFixed} closeFloatingNav={this.closeFloatingNav} />
|
|
|
+ )}
|
|
|
+ </aside>
|
|
|
+ {this.state.showSidebarBackdrop && <SidebarBackdrop closeFloatingNav={this.closeFloatingNav} />}
|
|
|
+ </>
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
Sidebar.propTypes = {
|
|
|
- actions: PropTypes.object,
|
|
|
- settings: PropTypes.object
|
|
|
+ actions: PropTypes.object,
|
|
|
+ settings: PropTypes.object,
|
|
|
};
|
|
|
|
|
|
-const mapStateToProps = state => ({ settings: state.settings });
|
|
|
-const mapDispatchToProps = dispatch => ({
|
|
|
- actions: bindActionCreators(actions, dispatch)
|
|
|
+const mapStateToProps = (state) => ({ settings: state.settings });
|
|
|
+const mapDispatchToProps = (dispatch) => ({
|
|
|
+ actions: bindActionCreators(actions, dispatch),
|
|
|
});
|
|
|
|
|
|
-export default connect(
|
|
|
- mapStateToProps,
|
|
|
- mapDispatchToProps
|
|
|
-)(withRouter(withTranslation(Sidebar)));
|
|
|
+export default connect(mapStateToProps, mapDispatchToProps)(withRouter(withTranslation(Sidebar)));
|