refactor(AccordionNav): Optimize the secondary navigation menu

This commit is contained in:
haitao(lj) 2022-12-09 15:39:30 +08:00
parent 1537f502f4
commit a5a95c5f3d
3 changed files with 96 additions and 80 deletions

View File

@ -0,0 +1,7 @@
.collapse-indicator {
transition: all .2s ease;
}
.expanding .collapse-indicator {
transform: rotate(90deg);
}

View File

@ -1,44 +1,63 @@
import React, { FC } from 'react';
import { Accordion, Button, Stack } from 'react-bootstrap';
import React, { FC, useEffect, useState } from 'react';
import { Accordion, Nav } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import { useNavigate, useMatch } from 'react-router-dom';
import { useAccordionButton } from 'react-bootstrap/AccordionButton';
import classNames from 'classnames';
import { Icon } from '@/components';
import './index.css';
function MenuNode({ menu, callback, activeKey, isLeaf = false }) {
function MenuNode({
menu,
callback,
activeKey,
expanding = false,
path = '/',
}) {
const { t } = useTranslation('translation', { keyPrefix: 'nav_menus' });
const accordionClick = useAccordionButton(menu.name);
const menuOnClick = (evt) => {
evt.preventDefault();
evt.stopPropagation();
if (!isLeaf) {
accordionClick(evt);
}
if (typeof callback === 'function') {
callback(menu);
}
};
const isLeaf = !menu.children.length;
const href = isLeaf ? `${path}${menu.name}` : '#';
let menuCls = 'text-start text-dark text-nowrap shadow-none bg-body border-0';
let menuVariant = 'light';
if (activeKey === menu.name) {
menuCls = 'text-start text-white text-nowrap shadow-none';
menuVariant = 'primary';
}
return (
<Button variant={menuVariant} className={menuCls} onClick={menuOnClick}>
<Stack direction="horizontal">
{!isLeaf ? <Icon name="chevron-right" className="me-1" /> : null}
{t(menu.name)}
<Nav.Item key={menu.name}>
<Nav.Link
eventKey={menu.name}
as={isLeaf ? 'a' : 'button'}
onClick={(evt) => {
callback(evt, menu, href, isLeaf);
}}
href={href}
className={classNames(
'text-nowrap d-flex flex-nowrap align-items-center w-100',
{ expanding, 'link-dark': activeKey !== menu.name },
)}>
<span className="me-auto">{t(menu.name)}</span>
{menu.badgeContent ? (
<span className="badge text-bg-dark ms-auto top-0">
{menu.badgeContent}
</span>
<span className="badge text-bg-dark">{menu.badgeContent}</span>
) : null}
</Stack>
</Button>
{!isLeaf && (
<Icon className="collapse-indicator" name="chevron-right" />
)}
</Nav.Link>
{menu.children.length ? (
<Accordion.Collapse eventKey={menu.name} className="ms-3">
<>
{menu.children.map((leaf) => {
return (
<MenuNode
menu={leaf}
callback={callback}
activeKey={activeKey}
path={path}
key={leaf.name}
/>
);
})}
</>
</Accordion.Collapse>
) : null}
</Nav.Item>
);
}
@ -46,12 +65,9 @@ interface AccordionProps {
menus: any[];
path?: string;
}
const AccordionNav: FC<AccordionProps> = ({ menus, path = '/' }) => {
const AccordionNav: FC<AccordionProps> = ({ menus = [], path = '/' }) => {
const navigate = useNavigate();
const pathMatch = useMatch(`${path}*`);
if (!menus.length) {
return null;
}
// auto set menu fields
menus.forEach((m) => {
if (!Array.isArray(m.children)) {
@ -68,57 +84,50 @@ const AccordionNav: FC<AccordionProps> = ({ menus, path = '/' }) => {
if (splat) {
activeKey = splat;
}
const menuClick = (clickedMenu) => {
const menuKey = clickedMenu.name;
if (clickedMenu.children.length) {
return;
}
if (activeKey !== menuKey) {
const routePath = `${path}${menuKey}`;
navigate(routePath);
}
const getOpenKey = () => {
let openKey = '';
menus.forEach((li) => {
if (li.children.length) {
const matchedChild = li.children.find((el) => {
return el.name === activeKey;
});
if (matchedChild) {
openKey = li.name;
}
}
});
return openKey;
};
let defaultOpenKey;
menus.forEach((li) => {
if (li.children.length) {
const matchedChild = li.children.find((el) => {
return el.name === activeKey;
});
if (matchedChild) {
defaultOpenKey = li.name;
}
const [openKey, setOpenKey] = useState(getOpenKey());
const menuClick = (evt, menu, href, isLeaf) => {
evt.preventDefault();
evt.stopPropagation();
if (isLeaf) {
navigate(href);
} else {
setOpenKey(openKey === menu.name ? '' : menu.name);
}
});
};
useEffect(() => {
setOpenKey(getOpenKey());
}, [activeKey]);
return (
<Accordion defaultActiveKey={defaultOpenKey} flush>
<Stack direction="vertical" gap={1}>
<Accordion activeKey={openKey} flush>
<Nav variant="pills" className="flex-column" activeKey={activeKey}>
{menus.map((li) => {
return (
<React.Fragment key={li.name}>
<MenuNode menu={li} callback={menuClick} activeKey={activeKey} />
{li.children.length ? (
<Accordion.Collapse eventKey={li.name} className="ms-4">
<Stack direction="vertical" gap={1}>
{li.children.map((leaf) => {
return (
<MenuNode
menu={leaf}
callback={menuClick}
activeKey={activeKey}
isLeaf
key={leaf.name}
/>
);
})}
</Stack>
</Accordion.Collapse>
) : null}
</React.Fragment>
<MenuNode
menu={li}
path={path}
callback={menuClick}
activeKey={activeKey}
expanding={openKey === li.name}
key={li.name}
/>
);
})}
</Stack>
</Nav>
</Accordion>
);
};

View File

@ -77,9 +77,9 @@ const Index: FC = () => {
useEffect(() => {
getSeoSetting().then((setting) => {
if (setting) {
const formMeta = { ...formData };
formMeta.robots.value = setting.robots;
setFormData(formMeta);
// const formMeta = { ...formData };
// formMeta.robots.value = setting.robots;
// setFormData(formMeta);
}
});
}, []);