LeftMenuSubMenu is not a function compontnet rather than a class

This commit is contained in:
Colin Dawson 2026-01-31 15:08:23 +00:00
parent 9fad05fd6d
commit 92eb37a6c5
2 changed files with 68 additions and 61 deletions

View File

@ -8,14 +8,16 @@ import {
faPrint, faPrint,
} from "@fortawesome/pro-thin-svg-icons"; } from "@fortawesome/pro-thin-svg-icons";
import LeftMenuItem from "./LeftMenuItem"; import LeftMenuItem from "./LeftMenuItem";
import LeftMenuSubMenu, { LOCLeftMenuSubMenu } from "./LeftMenuSubMenu"; import LeftMenuSubMenu from "./LeftMenuSubMenu";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Namespaces } from "../../../i18n/i18n"; import { Namespaces } from "../../../i18n/i18n";
const LeftMenu: React.FC = () => { const LeftMenu: React.FC = () => {
const { t } = useTranslation<typeof Namespaces.Common>(); const { t } = useTranslation<typeof Namespaces.Common>();
const [openMenuItem, setOpenMenuItem] = useState<LOCLeftMenuSubMenu>(); const [openMenuItem, setOpenMenuItem] = useState<
{ id: string; children: React.ReactNode } | undefined
>();
// Close menus when clicking outside // Close menus when clicking outside
useEffect(() => { useEffect(() => {
@ -24,9 +26,14 @@ const LeftMenu: React.FC = () => {
return () => document.body.removeEventListener("click", handleClick, true); return () => document.body.removeEventListener("click", handleClick, true);
}, []); }, []);
const handleClick = useCallback((menuItem: LOCLeftMenuSubMenu) => { const handleClick = useCallback(
setOpenMenuItem((current) => (current === menuItem ? undefined : menuItem)); (menuId: string, children: React.ReactNode) => {
}, []); setOpenMenuItem((current) =>
current?.id === menuId ? undefined : { id: menuId, children },
);
},
[],
);
// Access checks // Access checks
const viewOrganisation = authentication.hasAccess("ViewOrganisation"); const viewOrganisation = authentication.hasAccess("ViewOrganisation");
@ -69,10 +76,11 @@ const LeftMenu: React.FC = () => {
{viewAdmin && ( {viewAdmin && (
<LeftMenuSubMenu <LeftMenuSubMenu
openMenu={openMenuItem} menuId="admin"
openMenuId={openMenuItem?.id}
icon={faCog} icon={faCog}
label={t("Admin")} label={t("Admin")}
onClick={handleClick} onToggle={handleClick}
> >
{viewUser && <LeftMenuItem to="/users" label={t("Users")} />} {viewUser && <LeftMenuItem to="/users" label={t("Users")} />}
{viewDomain && ( {viewDomain && (
@ -98,10 +106,11 @@ const LeftMenu: React.FC = () => {
{viewSupport && ( {viewSupport && (
<LeftMenuSubMenu <LeftMenuSubMenu
openMenu={openMenuItem} menuId="support"
openMenuId={openMenuItem?.id}
icon={faCogs} icon={faCogs}
label={t("Support")} label={t("Support")}
onClick={handleClick} onToggle={handleClick}
> >
{viewAuditLog && <LeftMenuItem to="/audit" label={t("AuditLog")} />} {viewAuditLog && <LeftMenuItem to="/audit" label={t("AuditLog")} />}
{viewBlockedIPAddresses && ( {viewBlockedIPAddresses && (
@ -114,9 +123,7 @@ const LeftMenu: React.FC = () => {
)} )}
</div> </div>
{openMenuItem && ( {openMenuItem && <div className="subbar">{openMenuItem.children}</div>}
<div className="subbar">{openMenuItem.props.children}</div>
)}
</> </>
); );
}; };

View File

@ -1,76 +1,76 @@
import { IconDefinition } from "@fortawesome/fontawesome-common-types"; import { IconDefinition } from "@fortawesome/fontawesome-common-types";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React from "react"; import React, { useCallback, useMemo } from "react";
import withRouter, { RouterProps } from "../../../utils/withRouter"; import { useLocation } from "react-router-dom";
interface LeftMenuSubMenuProps extends RouterProps { interface LeftMenuSubMenuProps {
icon: IconDefinition; icon: IconDefinition;
label: string; label: string;
openMenu?: LOCLeftMenuSubMenu; menuId: string;
openMenuId?: string;
children: (false | JSX.Element)[]; children: (false | JSX.Element)[];
onClick?: (menuItem: LOCLeftMenuSubMenu) => void; onToggle?: (menuId: string, children: React.ReactNode) => void;
} }
interface LeftMenuSubMenuState {} const LeftMenuSubMenu: React.FC<LeftMenuSubMenuProps> = ({
icon,
label,
menuId,
openMenuId,
children,
onToggle,
}) => {
const { pathname } = useLocation();
class LOCLeftMenuSubMenu extends React.Component< const handleClick = useCallback((): void => {
LeftMenuSubMenuProps, if (onToggle !== undefined) onToggle(menuId, children);
LeftMenuSubMenuState }, [children, menuId, onToggle]);
> {
state = {};
handleClick = (): void => { const isChildSelected = useCallback(
const { onClick } = this.props; (child: JSX.Element): boolean => {
const { to } = child.props;
let isSelected: boolean = false;
if (
to === "/" ? pathname === to : pathname.toLowerCase().startsWith(to)
) {
isSelected = true;
}
if (onClick !== undefined) onClick(this); return isSelected;
}; },
[pathname],
isChildSelected = (child: JSX.Element): boolean => { );
const { to } = child.props;
const { pathname } = this.props.router.location;
let isSelected: boolean = false;
if (to === "/" ? pathname === to : pathname.toLowerCase().startsWith(to)) {
isSelected = true;
}
return isSelected;
};
isAnyChildSelected = (): boolean => {
const { children } = this.props;
const isAnyChildSelected = useCallback((): boolean => {
let childIsSelected = false; let childIsSelected = false;
children.forEach((child) => { children.forEach((child) => {
if (child === false) { if (child === false) {
return; return;
} }
if (this.isChildSelected(child)) childIsSelected = true; if (isChildSelected(child)) childIsSelected = true;
}); });
return childIsSelected; return childIsSelected;
}; }, [children, isChildSelected]);
render() { const selected = useMemo(
const { icon, label, openMenu } = this.props; () => openMenuId === menuId || isAnyChildSelected(),
[isAnyChildSelected, menuId, openMenuId],
);
const selected = this === openMenu || this.isAnyChildSelected(); let className = "LeftMenuItem leftMenuSubMenu";
let className = "LeftMenuItem leftMenuSubMenu"; if (selected) {
className += " leftMenuSubMenuOpen";
if (selected) {
className += " leftMenuSubMenuOpen";
}
return (
<div className={className} onClick={this.handleClick}>
<FontAwesomeIcon className="leftMenuItemIcon" icon={icon} />
<div className="leftMenuItemLabel">{label}</div>
</div>
);
} }
}
const LeftMenuSubMenu = withRouter(LOCLeftMenuSubMenu); return (
<div className={className} onClick={handleClick}>
<FontAwesomeIcon className="leftMenuItemIcon" icon={icon} />
<div className="leftMenuItemLabel">{label}</div>
</div>
);
};
export default LeftMenuSubMenu; export default LeftMenuSubMenu;
export { LOCLeftMenuSubMenu };