import React, {
useEffect, useState, useRef, useMemo, useCallback,
} from 'react';
import {
Nav, NavItem, NavLink,
} from 'reactstrap';
import { ICustomNavTabMappings } from '~/interfaces/ui';
import styles from './CustomNav.module.scss';
function getBreakpointOneLevelLarger(breakpoint) {
switch (breakpoint) {
case 'sm':
return 'md';
case 'md':
return 'lg';
case 'lg':
return 'xl';
case 'xl':
default:
return 'xxl';
}
}
type CustomNavDropdownProps = {
navTabMapping: ICustomNavTabMappings,
activeTab: string,
onNavSelected: (activeTabKey: string) => void,
};
export const CustomNavDropdown = (props: CustomNavDropdownProps): JSX.Element => {
const {
activeTab, navTabMapping, onNavSelected,
} = props;
const { Icon, i18n } = navTabMapping[activeTab];
const menuItemClickHandler = useCallback((key) => {
if (onNavSelected != null) {
onNavSelected(key);
}
}, [onNavSelected]);
return (
{Object.entries(navTabMapping).map(([key, value]) => {
const isActive = activeTab === key;
const _isLinkEnabled = value.isLinkEnabled ?? true;
const isLinkEnabled = typeof _isLinkEnabled === 'boolean' ? _isLinkEnabled : _isLinkEnabled(value);
const { Icon, i18n } = value;
return (
);
})}
);
};
type CustomNavTabProps = {
activeTab: string,
navTabMapping: ICustomNavTabMappings,
onNavSelected?: (selectedTabKey: string) => void,
hideBorderBottom?: boolean,
breakpointToHideInactiveTabsDown?: 'xs' | 'sm' | 'md' | 'lg' | 'xl',
navRightElement?: JSX.Element,
};
export const CustomNavTab = (props: CustomNavTabProps): JSX.Element => {
const [sliderWidth, setSliderWidth] = useState(0);
const [sliderMarginLeft, setSliderMarginLeft] = useState(0);
const {
activeTab, navTabMapping, onNavSelected,
hideBorderBottom,
breakpointToHideInactiveTabsDown, navRightElement,
} = props;
const navContainerRef = useRef(null);
const navTabRefs: { [key: string]: HTMLAnchorElement } = useMemo(() => {
const obj = {};
Object.keys(navTabMapping).forEach((key) => {
obj[key] = React.createRef();
});
return obj;
}, [navTabMapping]);
const navLinkClickHandler = useCallback((key) => {
if (onNavSelected != null) {
onNavSelected(key);
}
}, [onNavSelected]);
function registerNavLink(key: string, anchorElem: HTMLAnchorElement) {
if (anchorElem != null) {
navTabRefs[key] = anchorElem;
}
}
// Might make this dynamic for px, %, pt, em
function getPercentage(min, max) {
return min / max * 100;
}
useEffect(() => {
if (activeTab == null || activeTab === '') {
return;
}
if (navContainerRef.current == null) {
return;
}
const navContainer = navContainerRef.current;
let marginLeft = 0;
for (const [key, anchorElem] of Object.entries(navTabRefs)) {
const width = getPercentage(anchorElem.offsetWidth, navContainer.offsetWidth);
if (key === activeTab) {
setSliderWidth(width);
setSliderMarginLeft(marginLeft);
break;
}
marginLeft += width;
}
}, [activeTab, navTabRefs, navTabMapping]);
// determine inactive classes to hide NavItem
const inactiveClassnames = [];
if (breakpointToHideInactiveTabsDown != null) {
const breakpointOneLevelLarger = getBreakpointOneLevelLarger(breakpointToHideInactiveTabsDown);
inactiveClassnames.push('d-none');
inactiveClassnames.push(`d-${breakpointOneLevelLarger}-block`);
}
return (
{navRightElement}
{ !hideBorderBottom &&
}
);
};
const CustomNav = (props) => {
const tabClassnames = ['d-none'];
const dropdownClassnames = ['d-block'];
// determine classes to show/hide
const breakpointOneLevelLarger = getBreakpointOneLevelLarger(props.breakpointToSwitchDropdownDown);
tabClassnames.push(`d-${breakpointOneLevelLarger}-block`);
dropdownClassnames.push(`d-${breakpointOneLevelLarger}-none`);
return (
);
};
CustomNav.propTypes = {
activeTab: PropTypes.string.isRequired,
navTabMapping: PropTypes.object.isRequired,
onNavSelected: PropTypes.func,
hideBorderBottom: PropTypes.bool,
breakpointToHideInactiveTabsDown: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
breakpointToSwitchDropdownDown: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
};
CustomNav.defaultProps = {
hideBorderBottom: false,
breakpointToSwitchDropdownDown: 'sm',
};
export default CustomNav;