burger modal

This commit is contained in:
Max S 2024-07-25 12:30:02 +02:00
parent 5efd8b3713
commit f59f7ad9db
17 changed files with 355 additions and 531 deletions

View File

@ -1,18 +0,0 @@
@import "@Themes/constants.scss";
.root {
.content {
display: flex;
gap: 8px;
align-items: center;
justify-content: center;
}
.sub-menu {
padding: 24px;
text-align: center;
gap: 24px;
display: flex;
flex-direction: column;
}
}

View File

@ -1,58 +0,0 @@
import classNames from "classnames";
import { useRouter } from "next/router";
import React, { useEffect, useState } from "react";
import classes from "./classes.module.scss";
import { IAppRule } from "@Front/Api/Entities/rule";
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
import { IHeaderLinkProps } from "../../../ButtonHeader";
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
import HeaderSubmenuLink from "../../../HeaderSubmenu/HeaderSubmenuLink";
import useToggle from "@Front/Hooks/useToggle";
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/outline";
type IProps = {
text: string | JSX.Element;
links: (IHeaderLinkProps & {
rules?: IAppRule[];
})[];
};
export default function HeaderSubmenu(props: IProps) {
const router = useRouter();
const { pathname } = router;
const [isActive, setIsActive] = useState(true);
const { active: isSubmenuOpened, toggle } = useToggle();
useEffect(() => {
setIsActive(false);
if (props.links.some((link) => link.path === pathname)) setIsActive(true);
if (props.links.some((link) => link.routesActive?.some((routeActive) => pathname.includes(routeActive)))) setIsActive(true);
}, [isActive, pathname, props.links]);
return (
<Rules mode={RulesMode.OPTIONAL} rules={props.links.flatMap((link) => link.rules ?? [])}>
<div className={classes["container"]}>
<div className={classNames(classes["root"], (isActive || isSubmenuOpened) && classes["active"])}>
<div className={classes["content"]} onClick={toggle}>
<Typography
typo={isActive || isSubmenuOpened ? ETypo.TEXT_LG_SEMIBOLD : ETypo.TEXT_LG_REGULAR}
color={isActive || isSubmenuOpened ? ETypoColor.COLOR_NEUTRAL_950 : ETypoColor.COLOR_NEUTRAL_500}>
{props.text}
</Typography>
{isSubmenuOpened ? <ChevronUpIcon height="20" width="20" /> : <ChevronDownIcon height="20" width="20" />}
</div>
<div className={classes["underline"]} data-active={(isActive || isSubmenuOpened).toString()} />
{isSubmenuOpened && (
<div className={classes["sub-menu"]}>
{props.links.map((link) => (
<Rules mode={RulesMode.NECESSARY} rules={link.rules ?? []} key={link.path}>
<HeaderSubmenuLink {...link} />
</Rules>
))}
</div>
)}
</div>
</div>
</Rules>
);
}

View File

@ -1,21 +1,22 @@
@import "@Themes/constants.scss";
.root {
position: absolute;
top: var(--header-height);
left: 0;
width: 100%;
max-height: calc(100vh - var(--header-height));
padding: var(--spacing-05, 4px) var(--spacing-2, 16px);
display: flex;
flex-direction: column;
background-color: var(--color-generic-white);
box-shadow: $shadow-nav;
padding: 24px;
position: absolute;
top: 83px;
width: 100%;
left: 0;
text-align: center;
max-height: calc(100vh - var(--header-height));
overflow: auto;
> *:not(:last-child) {
margin-bottom: 24px;
}
border-radius: var(--menu-radius, 0px);
background: var(--color-generic-white, #FFF);
box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.10);
.separator {
width: 100%;

View File

@ -1,26 +1,24 @@
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
import LogOutButton from "@Front/Components/DesignSystem/LogOutButton";
import MenuItem from "@Front/Components/DesignSystem/Menu/MenuItem";
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
import Module from "@Front/Config/Module";
import React from "react";
import NavigationLink from "../../NavigationLink";
import classes from "./classes.module.scss";
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
import BurgerModalSubmenu from "./BurgerModalSubmenu";
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
type IProps = {
isOpen: boolean;
closeModal: () => void;
};
type IState = {};
export default class BurgerModal extends React.Component<IProps, IState> {
// TODO isEnabled depending on role given by DB
public override render(): JSX.Element | null {
if (!this.props.isOpen) return null;
export default function BurgerModal(props: IProps) {
const { isOpen, closeModal } = props;
if (!isOpen) return null;
return (
<>
<div className={classes["background"]} onClick={this.props.closeModal} />
<div className={classes["background"]} onClick={closeModal} />
<div className={classes["root"]}>
<Rules
mode={RulesMode.OPTIONAL}
@ -31,29 +29,36 @@ export default class BurgerModal extends React.Component<IProps, IState> {
},
]}>
<>
<NavigationLink
path={Module.getInstance().get().modules.pages.Folder.props.path}
text="Dossiers en cours"
routesActive={[
<MenuItem
item={{
text: "Dossiers en cours",
routesActive: [
Module.getInstance().get().modules.pages.Folder.pages.FolderInformation.props.path,
Module.getInstance().get().modules.pages.Folder.pages.CreateFolder.props.path,
]}
],
link: Module.getInstance().get().modules.pages.Folder.props.path,
}}
/>
<NavigationLink
path={Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path}
text="Dossiers archivés"
routesActive={[Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path]}
<MenuItem
item={{
text: "Dossiers archivés",
routesActive: [Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path],
link: Module.getInstance().get().modules.pages.Folder.pages.FolderArchived.props.path,
hasSeparator: true,
}}
/>
<div className={classes["separator"]} />
</>
</Rules>
<BurgerModalSubmenu
text={"Espace super admin"}
links={[
<MenuItem
item={{
text: "Espace super admin",
dropdown: {
items: [
{
text: "Gestion des utilisateurs",
path: Module.getInstance().get().modules.pages.Users.props.path,
link: Module.getInstance().get().modules.pages.Users.props.path,
routesActive: [
Module.getInstance().get().modules.pages.Users.pages.UsersInformations.props.path,
Module.getInstance().get().modules.pages.Users.props.path,
@ -65,28 +70,20 @@ export default class BurgerModal extends React.Component<IProps, IState> {
},
],
},
{
text: "Gestion des offices",
path: Module.getInstance().get().modules.pages.Offices.props.path,
routesActive: [
Module.getInstance().get().modules.pages.Offices.pages.OfficesInformations.props.path,
Module.getInstance().get().modules.pages.Offices.props.path,
],
rules: [
{
action: AppRuleActions.update,
name: AppRuleNames.offices,
},
],
},
]}
}}
/>
<BurgerModalSubmenu
text="Espace office"
links={[
<MenuItem
item={{
text: "Espace office",
hasSeparator: true,
dropdown: {
items: [
{
text: "Collaborateurs",
path: Module.getInstance().get().modules.pages.Collaborators.props.path,
link: Module.getInstance().get().modules.pages.Collaborators.props.path,
routesActive: [
Module.getInstance().get().modules.pages.Collaborators.pages.CollaboratorInformations.props.path,
Module.getInstance().get().modules.pages.Collaborators.props.path,
@ -100,7 +97,7 @@ export default class BurgerModal extends React.Component<IProps, IState> {
},
{
text: "Gestion des rôles",
path: Module.getInstance().get().modules.pages.Roles.props.path,
link: Module.getInstance().get().modules.pages.Roles.props.path,
routesActive: [
Module.getInstance().get().modules.pages.Roles.pages.Create.props.path,
Module.getInstance().get().modules.pages.Roles.pages.RolesInformations.props.path,
@ -115,7 +112,7 @@ export default class BurgerModal extends React.Component<IProps, IState> {
},
{
text: "Paramétrage des listes de pièces",
path: Module.getInstance().get().modules.pages.DeedTypes.props.path,
link: Module.getInstance().get().modules.pages.DeedTypes.props.path,
routesActive: [
Module.getInstance().get().modules.pages.DeedTypes.pages.Create.props.path,
Module.getInstance().get().modules.pages.DeedTypes.pages.Edit.props.path,
@ -134,7 +131,7 @@ export default class BurgerModal extends React.Component<IProps, IState> {
},
{
text: "RIB Office",
path: Module.getInstance().get().modules.pages.OfficesRib.props.path,
link: Module.getInstance().get().modules.pages.OfficesRib.props.path,
rules: [
{
action: AppRuleActions.update,
@ -144,7 +141,7 @@ export default class BurgerModal extends React.Component<IProps, IState> {
},
{
text: "Abonnement",
path: Module.getInstance().get().modules.pages.Subscription.pages.Manage.props.path,
link: Module.getInstance().get().modules.pages.Subscription.pages.Manage.props.path,
routesActive: [
Module.getInstance().get().modules.pages.Subscription.pages.Error.props.path,
Module.getInstance().get().modules.pages.Subscription.pages.Success.props.path,
@ -155,16 +152,45 @@ export default class BurgerModal extends React.Component<IProps, IState> {
Module.getInstance().get().modules.pages.Subscription.pages.Subscribe.props.path,
],
},
]}
],
},
}}
/>
<div className={classes["separator"]} />
<NavigationLink path={Module.getInstance().get().modules.pages.MyAccount.props.path} text="Mon compte" />
<NavigationLink target="blank" path="https://ressources.lecoffre.io/" text="Guide de Prise en Main" />
<NavigationLink target="blank" path="https://tally.so/r/mBGaNY" text="Support" />
<NavigationLink target="blank" path="/CGU_LeCoffre_io.pdf" text="CGU" />
<MenuItem
item={{
text: "Mon compte",
link: Module.getInstance().get().modules.pages.MyAccount.props.path,
hasSeparator: true,
}}
/>
<MenuItem
item={{
text: "Guide de Prise en Main",
link: "https://ressources.lecoffre.io/",
target: "_blank",
}}
/>
<MenuItem
item={{
text: "Support",
link: "https://tally.so/r/mBGaNY",
target: "_blank",
}}
/>
<MenuItem
item={{
text: "CGU",
link: "/CGU_LeCoffre_io.pdf",
hasSeparator: true,
}}
/>
<LogOutButton />
</div>
</>
);
}
}

View File

@ -1,39 +0,0 @@
import Link from "next/link";
import { useRouter } from "next/router";
import React, { useEffect } from "react";
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
import useHoverable from "@Front/Hooks/useHoverable";
type IHeaderLinkProps = {
text: string | JSX.Element;
path: string;
routesActive?: string[];
};
export default function HeaderSubmenuLink(props: IHeaderLinkProps) {
const router = useRouter();
const { pathname } = router;
const [isActive, setIsActive] = React.useState(props.path === pathname);
const { handleMouseLeave, handleMouseEnter, isHovered } = useHoverable();
useEffect(() => {
if (props.path === pathname) setIsActive(true);
if (props.routesActive) {
for (const routeActive of props.routesActive) {
if (isActive) break;
if (pathname.includes(routeActive)) setIsActive(true);
}
}
}, [isActive, pathname, props.path, props.routesActive]);
return (
<Link href={props.path} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
<Typography
typo={isActive || isHovered ? ETypo.TEXT_LG_SEMIBOLD : ETypo.TEXT_LG_REGULAR}
color={isActive || isHovered ? ETypoColor.COLOR_NEUTRAL_950 : ETypoColor.COLOR_NEUTRAL_500}>
{props.text}
</Typography>
</Link>
);
}

View File

@ -1,44 +0,0 @@
@import "@Themes/constants.scss";
.root {
display: flex;
position: relative;
width: fit-content;
margin: auto;
height: 83px;
padding: 10px 16px;
.content {
margin: auto;
}
.underline {
width: 100%;
height: 3px;
background-color: var(--color-generic-white);
position: absolute;
bottom: 0;
left: 0;
&[data-active="true"] {
background-color: var(--color-generic-black);
}
}
&.desactivated {
cursor: not-allowed;
}
.sub-menu {
box-shadow: 0px 8px 10px 0px #00000012;
padding: 24px;
text-align: center;
gap: 24px;
left: 0;
transform: translateX(-25%);
width: 300px;
top: 84px;
display: flex;
flex-direction: column;
background: white;
position: absolute;
}
}

View File

@ -1,57 +0,0 @@
import classNames from "classnames";
import { useRouter } from "next/router";
import React, { useEffect, useState } from "react";
import { IHeaderLinkProps } from "../ButtonHeader";
import Typography, { ETypo, ETypoColor } from "../../Typography";
import classes from "./classes.module.scss";
import useHoverable from "@Front/Hooks/useHoverable";
import HeaderSubmenuLink from "./HeaderSubmenuLink";
import { IAppRule } from "@Front/Api/Entities/rule";
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
type IProps = {
text: string | JSX.Element;
links: (IHeaderLinkProps & {
rules?: IAppRule[];
})[];
};
export default function HeaderSubmenu(props: IProps) {
const router = useRouter();
const { pathname } = router;
const [isActive, setIsActive] = useState(false);
const { handleMouseLeave, handleMouseEnter, isHovered } = useHoverable(100);
useEffect(() => {
setIsActive(false);
if (props.links.some((link) => link.path === pathname)) setIsActive(true);
if (props.links.some((link) => link.routesActive?.some((routeActive) => pathname.includes(routeActive)))) setIsActive(true);
}, [isActive, pathname, props.links]);
return (
<Rules mode={RulesMode.OPTIONAL} rules={props.links.flatMap((link) => link.rules ?? [])}>
<div className={classes["container"]} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
<div className={classNames(classes["root"], (isActive || isHovered) && classes["active"])}>
<div className={classes["content"]}>
<Typography
typo={isActive || isHovered ? ETypo.TEXT_LG_SEMIBOLD : ETypo.TEXT_LG_REGULAR}
color={isActive || isHovered ? ETypoColor.COLOR_NEUTRAL_950 : ETypoColor.COLOR_NEUTRAL_500}>
{props.text}
</Typography>
</div>
<div className={classes["underline"]} data-active={(isActive || isHovered).toString()} />
{isHovered && (
<div className={classes["sub-menu"]}>
{props.links.map((link) => (
<Rules mode={RulesMode.NECESSARY} rules={link.rules ?? []} key={link.path}>
<HeaderSubmenuLink {...link} />
</Rules>
))}
</div>
)}
</div>
</div>
</Rules>
);
}

View File

@ -8,7 +8,8 @@ import { AdjustmentsVerticalIcon, BanknotesIcon, Square3Stack3DIcon, TagIcon, Us
import { usePathname } from "next/navigation";
import React, { useCallback, useEffect } from "react";
import Menu, { IItem } from "../../Menu";
import Menu from "../../Menu";
import { IItem } from "../../Menu/MenuItem";
import ButtonHeader from "../ButtonHeader";
import classes from "./classes.module.scss";

View File

@ -1,12 +0,0 @@
@import "@Themes/constants.scss";
.root {
display: flex;
position: relative;
width: fit-content;
margin: auto;
.content {
align-content: center;
}
}

View File

@ -1,54 +0,0 @@
import React from "react";
import classes from "./classes.module.scss";
import Link from "next/link";
import classNames from "classnames";
import { useRouter } from "next/router";
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
type IProps = {
text: string | JSX.Element;
path?: string;
onClick?: () => void;
isEnabled?: boolean;
isActive?: boolean;
routesActive?: string[];
target?: "blank" | "self" | "_blank";
};
type IPropsClass = IProps;
type IStateClass = {};
class NavigationLinkClass extends React.Component<IPropsClass, IStateClass> {
static defaultProps = { isEnabled: true };
public override render(): JSX.Element | null {
if (!this.props.isEnabled) return null;
return (
<Link
href={this.props.path ?? ""}
className={classNames(classes["root"], this.props.isActive && [classes["active"]])}
onClick={this.props.onClick}
target={this.props.target}>
<div className={classes["content"]}>
<Typography
typo={this.props.isActive ? ETypo.TEXT_LG_SEMIBOLD : ETypo.TEXT_LG_REGULAR}
color={this.props.isActive ? ETypoColor.COLOR_NEUTRAL_950 : ETypoColor.COLOR_NEUTRAL_500}>
{this.props.text}
</Typography>
</div>
</Link>
);
}
}
export default function NavigationLink(props: IProps) {
const router = useRouter();
const { pathname } = router;
let isActive = props.path === pathname;
if (props.routesActive) {
for (const routeActive of props.routesActive) {
if (isActive) break;
isActive = pathname.includes(routeActive);
}
}
return <NavigationLinkClass {...props} isActive={isActive} />;
}

View File

@ -1,14 +1,26 @@
@import "@Themes/constants.scss";
.root {
display: flex;
flex-direction: column;
background-color: var(--color-generic-white);
box-shadow: $shadow-nav;
padding: 24px;
position: absolute;
top: 107px;
right: 66px;
top: 48px;
display: inline-flex;
flex-direction: column;
align-items: flex-start;
padding: var(--spacing-05, 4px) var(--spacing-2, 16px);
border-radius: var(--menu-radius, 0);
border: 1px solid var(--menu-border, #d7dce0);
background: var(--color-generic-white, #fff);
text-wrap: nowrap;
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.1);
z-index: 3;
top: calc(var(--header-height) + 10px);
right: 32px;
text-align: center;
animation: smooth-appear 0.2s ease forwards;
@ -20,15 +32,6 @@
opacity: 1;
}
}
> *:not(:last-child) {
margin-bottom: 24px;
}
.separator {
width: 100%;
border: 1px solid var(--color-neutral-200);
}
}
.background {

View File

@ -2,8 +2,8 @@ import LogOutButton from "@Front/Components/DesignSystem/LogOutButton";
import Module from "@Front/Config/Module";
import React from "react";
import NavigationLink from "../../NavigationLink";
import classes from "./classes.module.scss";
import MenuItem from "@Front/Components/DesignSystem/Menu/MenuItem";
type IProps = {
isOpen: boolean;
@ -19,10 +19,28 @@ export default class ProfileModal extends React.Component<IProps, IState> {
<>
<div className={classes["background"]} onClick={this.props.closeModal} />
<div className={classes["root"]}>
<NavigationLink path={Module.getInstance().get().modules.pages.MyAccount.props.path} text="Mon compte" />
<NavigationLink target="_blank" path="https://ressources.lecoffre.io/" text="Guide de Prise en Main" />
<NavigationLink target="_blank" path="/CGU_LeCoffre_io.pdf" text="CGU" />
<div className={classes["separator"]} />
<MenuItem
item={{
text: "Mon compte",
link: Module.getInstance().get().modules.pages.MyAccount.props.path,
}}
/>
<MenuItem
item={{
text: "Guide de Prise en Main",
link: "https://ressources.lecoffre.io/",
target: "_blank",
}}
/>
<MenuItem
item={{
text: "CGU",
link: "/CGU_LeCoffre_io.pdf",
hasSeparator: true,
}}
/>
<LogOutButton />
</div>
</>

View File

@ -1,27 +1,20 @@
import React from "react";
import Image from "next/image";
import DisconnectIcon from "@Assets/Icons/disconnect.svg";
import classes from "./classes.module.scss";
import Typography, { ETypo, ETypoColor } from "../Typography";
import { useRouter } from "next/router";
import UserStore from "@Front/Stores/UserStore";
import { FrontendVariables } from "@Front/Config/VariablesFront";
import UserStore from "@Front/Stores/UserStore";
import { PowerIcon } from "@heroicons/react/24/outline";
import { useRouter } from "next/router";
import React, { useCallback } from "react";
import MenuItem from "../Menu/MenuItem";
export default function LogOut() {
const router = useRouter();
const variables = FrontendVariables.getInstance();
const disconnect = async () => {
await UserStore.instance.disconnect();
router.push(`https://qual-connexion.idnot.fr/user/auth/logout?sourceURL=${variables.FRONT_APP_HOST}`);
};
const disconnect = useCallback(() => {
UserStore.instance
.disconnect()
.then(() => router.push(`https://qual-connexion.idnot.fr/user/auth/logout?sourceURL=${variables.FRONT_APP_HOST}`));
}, [router, variables.FRONT_APP_HOST]);
return (
<div className={classes["root"]} onClick={disconnect}>
<Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_NEUTRAL_500}>
Déconnexion
</Typography>
<Image src={DisconnectIcon} className={classes["disconnect-icon"]} alt="disconnect" />
</div>
);
return <MenuItem item={{ text: "Déconnexion", icon: <PowerIcon />, onClick: disconnect }} />;
}

View File

@ -1,5 +1,6 @@
.root {
width: 100%;
.menu-item {
display: flex;
padding: var(--spacing-md, 16px);
@ -7,12 +8,16 @@
align-items: center;
gap: var(--spacing-lg, 24px);
cursor: pointer;
}
> svg {
svg {
width: 24px;
height: 24px;
transition: all ease-in-out 0.1s;
transition: transform 0.3s ease-in-out;
}
.chevron.open {
transform: rotate(180deg);
}
.separator {
@ -20,4 +25,14 @@
height: 1px;
background-color: var(--separator-stroke-light, #d7dce0);
}
.dropdown {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-in-out;
}
.dropdown.open {
max-height: 500px;
}
}

View File

@ -3,27 +3,71 @@ import classes from "./classes.module.scss";
import { useRouter } from "next/router";
import React, { useCallback, useEffect } from "react";
import useHoverable from "@Front/Hooks/useHoverable";
import { IItem } from "..";
import classNames from "classnames";
import { IAppRule } from "@Front/Api/Entities/rule";
import { ChevronDownIcon } from "@heroicons/react/24/outline";
import useOpenable from "@Front/Hooks/useOpenable";
type IProps = {
item: IItem;
closeMenuCb: () => void;
};
type IItemBase = {
text: string;
icon?: JSX.Element;
hasSeparator?: boolean;
color?: ETypoColor;
onClose?: () => void;
};
type IItemWithLink = IItemBase & {
link: string;
rules?: IAppRule[];
routesActive?: string[];
onClick?: never;
dropdown?: never;
target?: "_blank";
};
type IItemWithOnClick = IItemBase & {
onClick: () => void;
link?: never;
rules?: never;
routesActive?: never;
dropdown?: never;
target?: never;
};
type IItemWithDropdown = IItemBase & {
dropdown: {
items: IItem[];
};
routesActive?: never;
link?: never;
rules?: never;
onClick?: never;
target?: never;
};
export type IItem = IItemWithLink | IItemWithOnClick | IItemWithDropdown;
export default function MenuItem(props: IProps) {
const { item, closeMenuCb } = props;
const { item } = props;
const router = useRouter();
const { pathname } = router;
const [isActive, setIsActive] = React.useState(item.link === pathname);
const { isOpen, toggle, open } = useOpenable();
const handleClickElement = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
closeMenuCb();
item.onClose?.();
const link = e.currentTarget.getAttribute("data-link");
if (item.target === "_blank") window.open(item.link, "_blank");
if (link) router.push(link);
if (item.onClick) item.onClick();
},
[closeMenuCb, item, router],
[item, router],
);
const { handleMouseEnter, handleMouseLeave, isHovered } = useHoverable();
@ -44,7 +88,25 @@ export default function MenuItem(props: IProps) {
if (pathname.includes(routeActive)) setIsActive(true);
}
}
}, [isActive, item.link, item.routesActive, pathname]);
if (item.dropdown) {
for (const subItem of item.dropdown.items) {
if (isActive) break;
if (subItem.link === pathname) {
!isOpen && open();
setIsActive(true);
}
if (subItem.routesActive) {
for (const routeActive of subItem.routesActive) {
if (isActive) break;
if (pathname.includes(routeActive)) {
!isOpen && open();
setIsActive(true);
}
}
}
}
}
}, [isActive, isOpen, item.dropdown, item.link, item.routesActive, open, pathname]);
return (
<div
@ -53,12 +115,23 @@ export default function MenuItem(props: IProps) {
data-link={item.link}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}>
<div className={classes["menu-item"]}>
{React.cloneElement(item.icon, { color: `var(${getColor()})` })}
<div className={classes["menu-item"]} onClick={item.dropdown && toggle}>
{item.icon && React.cloneElement(item.icon, { color: `var(${getColor()})` })}
<Typography typo={ETypo.TEXT_LG_REGULAR} color={getColor()}>
{item.text}
</Typography>
{item.dropdown &&
React.cloneElement(<ChevronDownIcon className={classNames(classes["chevron"], isOpen && [classes["open"]])} />, {
color: `var(${getColor()})`,
})}
</div>
{item.dropdown && (
<div className={classNames(classes["dropdown"], isOpen && [classes["open"]])}>
{item.dropdown.items.map((subItem, index) => (
<MenuItem key={index} item={subItem} />
))}
</div>
)}
{item.hasSeparator && <div className={classes["separator"]} />}
</div>
);

View File

@ -1,35 +1,10 @@
import { IAppRule } from "@Front/Api/Entities/rule";
import { ETypoColor } from "@Front/Components/DesignSystem/Typography";
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
import useHoverable from "@Front/Hooks/useHoverable";
import useOpenable from "@Front/Hooks/useOpenable";
import React, { useEffect, useRef } from "react";
import classes from "./classes.module.scss";
import MenuItem from "./MenuItem";
type IItemBase = {
icon: JSX.Element;
text: string;
hasSeparator?: boolean;
color?: ETypoColor;
};
type IItemWithLink = IItemBase & {
link: string;
rules?: IAppRule[];
routesActive?: string[];
onClick?: never;
};
type IItemWithOnClick = IItemBase & {
onClick: () => void;
link?: never;
rules?: never;
routesActive?: never;
};
export type IItem = IItemWithLink | IItemWithOnClick;
import MenuItem, { IItem } from "./MenuItem";
type IProps = {
children: React.ReactNode;
@ -79,7 +54,7 @@ export default function Menu(props: IProps) {
{items.map((item, index) => {
return (
<Rules mode={RulesMode.NECESSARY} rules={item.rules ?? []} key={item.link}>
<MenuItem item={item} key={index} closeMenuCb={close} />
<MenuItem item={item} key={index} />
</Rules>
);
})}

View File

@ -1,6 +1,5 @@
import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
import IconButton, { EIconButtonVariant } from "@Front/Components/DesignSystem/IconButton";
import Menu, { IItem } from "@Front/Components/DesignSystem/Menu";
import Tag, { ETagColor } from "@Front/Components/DesignSystem/Tag";
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
import Module from "@Front/Config/Module";
@ -10,6 +9,8 @@ import { useCallback } from "react";
import { AnchorStatus } from "..";
import classes from "./classes.module.scss";
import { IItem } from "@Front/Components/DesignSystem/Menu/MenuItem";
import Menu from "@Front/Components/DesignSystem/Menu";
type IProps = {
folder: OfficeFolder | null;