| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 | 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 SidebarUserBlock from "./SidebarUserBlock";// import { getUser } from "@/actions/auth";import Menu from "./Menu.js";import MenuLLDIKTI from "./MenuLLDIKTI.js";import MenuPT from "./MenuPT.js";// localStorage.getItem("user");// import Menu from './MenuPT.js';// Helper to check for parrent of an given elementsconst 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;};// Helper to get outerHeight of a dom elementconst outerHeight = (elem, includeMargin) => {	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>);/**    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>);/**    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>);/**    Component used to display a header on menu when using collapsed/hover mode*/const SidebarSubHeader = ({ item }) => <li className="sidebar-subnav-header">{item.name}</li>;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>	);};/**    The main sidebar component*/class Sidebar extends Component {	menu = [];	state = {		collapse: {},		showSidebarBackdrop: false,		currentFloatingItem: null,		currentFloatingItemTarget: null,		pathname: this.props.router.pathname,	};	async componentDidMount() {		// const user = await getUser();		const user = this.props.user;		this.menu = user.peran[0].peran.id === 2022 ? MenuPT : user.peran[0].peran.id === 2021 ? MenuLLDIKTI : 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,};const mapStateToProps = (state) => ({ settings: state.settings, user: state.user });const mapDispatchToProps = (dispatch) => ({	actions: bindActionCreators(actions, dispatch),});export default connect(mapStateToProps, mapDispatchToProps)(withRouter(withTranslation(Sidebar)));
 |