✨ submenu on folder
This commit is contained in:
parent
7530435acc
commit
d9183701ff
@ -1,20 +1,26 @@
|
|||||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { ISubElementWithLink, ISubElementWithOnClick } from "..";
|
|
||||||
import React, { useCallback } from "react";
|
import React, { useCallback } from "react";
|
||||||
import useHoverable from "@Front/Hooks/useHoverable";
|
import useHoverable from "@Front/Hooks/useHoverable";
|
||||||
|
import { ISubElement } from "..";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
element: ISubElementWithLink | ISubElementWithOnClick;
|
element: ISubElement;
|
||||||
|
closeMenuCb: () => void;
|
||||||
};
|
};
|
||||||
export default function SubMenuItem(props: IProps) {
|
export default function SubMenuItem(props: IProps) {
|
||||||
const { element } = props;
|
const { element, closeMenuCb } = props;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const handleClickElement = (e: React.MouseEvent<HTMLDivElement>) => {
|
const handleClickElement = useCallback(
|
||||||
const link = e.currentTarget.getAttribute("data-link");
|
(e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (link) router.push(link);
|
closeMenuCb();
|
||||||
};
|
const link = e.currentTarget.getAttribute("data-link");
|
||||||
|
if (link) router.push(link);
|
||||||
|
if (element.onClick) element.onClick();
|
||||||
|
},
|
||||||
|
[closeMenuCb, element, router],
|
||||||
|
);
|
||||||
|
|
||||||
const { handleMouseEnter, handleMouseLeave, isHovered } = useHoverable();
|
const { handleMouseEnter, handleMouseLeave, isHovered } = useHoverable();
|
||||||
|
|
||||||
@ -28,7 +34,7 @@ export default function SubMenuItem(props: IProps) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classes["menu-item-wrapper"]}
|
className={classes["menu-item-wrapper"]}
|
||||||
onClick={element.onClick ?? handleClickElement}
|
onClick={handleClickElement}
|
||||||
data-link={element.link}
|
data-link={element.link}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}>
|
onMouseLeave={handleMouseLeave}>
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
.sub-menu {
|
.sub-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 48px;
|
top: 48px;
|
||||||
left: 0px;
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
padding: var(--spacing-05, 4px) var(--spacing-2, 16px);
|
padding: var(--spacing-05, 4px) var(--spacing-2, 16px);
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -16,5 +15,15 @@
|
|||||||
text-wrap: nowrap;
|
text-wrap: nowrap;
|
||||||
/* shadow/sm */
|
/* shadow/sm */
|
||||||
box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.1);
|
box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 2;
|
||||||
|
&[data-opening-side="left"] {
|
||||||
|
left: auto;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-opening-side="right"] {
|
||||||
|
left: 0px;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,23 +11,28 @@ type ISubElementBase = {
|
|||||||
color?: ETypoColor;
|
color?: ETypoColor;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ISubElementWithLink = ISubElementBase & {
|
type ISubElementWithLink = ISubElementBase & {
|
||||||
link: string;
|
link: string;
|
||||||
onClick?: never;
|
onClick?: never;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ISubElementWithOnClick = ISubElementBase & {
|
type ISubElementWithOnClick = ISubElementBase & {
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
link?: never;
|
link?: never;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ISubElement = ISubElementWithLink | ISubElementWithOnClick;
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
text?: string;
|
text?: string;
|
||||||
subElements: (ISubElementWithLink | ISubElementWithOnClick)[];
|
subElements: ISubElement[];
|
||||||
|
openingSide?: "left" | "right";
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ButtonWithSubMenu(props: IProps) {
|
export default function ButtonWithSubMenu(props: IProps) {
|
||||||
|
const { openingSide = "left" } = props;
|
||||||
|
|
||||||
const [isSubMenuOpened, setIsSubMenuOpened] = useState(false);
|
const [isSubMenuOpened, setIsSubMenuOpened] = useState(false);
|
||||||
|
|
||||||
const subMenuRef = useRef<HTMLDivElement>(null);
|
const subMenuRef = useRef<HTMLDivElement>(null);
|
||||||
@ -37,6 +42,10 @@ export default function ButtonWithSubMenu(props: IProps) {
|
|||||||
setIsSubMenuOpened((prev) => !prev);
|
setIsSubMenuOpened((prev) => !prev);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeMenu = () => {
|
||||||
|
setIsSubMenuOpened(false);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (e: MouseEvent) => {
|
const handleClickOutside = (e: MouseEvent) => {
|
||||||
if (
|
if (
|
||||||
@ -58,9 +67,9 @@ export default function ButtonWithSubMenu(props: IProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isSubMenuOpened && (
|
{isSubMenuOpened && (
|
||||||
<div className={classes["sub-menu"]} ref={subMenuRef}>
|
<div className={classes["sub-menu"]} ref={subMenuRef} data-opening-side={openingSide}>
|
||||||
{props.subElements.map((element, index) => {
|
{props.subElements.map((element, index) => {
|
||||||
return <SubMenuItem element={element} key={index} />;
|
return <SubMenuItem element={element} key={index} closeMenuCb={closeMenu} />;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
|
import CircleProgress from "@Front/Components/DesignSystem/CircleProgress";
|
||||||
import IconButton, { EIconButtonVariant } from "@Front/Components/DesignSystem/IconButton";
|
|
||||||
import Tag, { ETagColor } from "@Front/Components/DesignSystem/Tag";
|
import Tag, { ETagColor } from "@Front/Components/DesignSystem/Tag";
|
||||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||||
import Module from "@Front/Config/Module";
|
import Module from "@Front/Config/Module";
|
||||||
import { ArchiveBoxIcon, PencilSquareIcon, UserGroupIcon } from "@heroicons/react/24/outline";
|
import { ArchiveBoxIcon, EllipsisHorizontalIcon, PencilSquareIcon, UsersIcon } from "@heroicons/react/24/outline";
|
||||||
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
import { OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import { AnchorStatus } from "..";
|
import { AnchorStatus } from "..";
|
||||||
|
import ButtonWithSubMenu, { ISubElement } from "@Front/Components/Elements/ButtonWithSubMenu";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
folder: OfficeFolder | null;
|
folder: OfficeFolder | null;
|
||||||
@ -21,6 +21,49 @@ type IProps = {
|
|||||||
export default function InformationSection(props: IProps) {
|
export default function InformationSection(props: IProps) {
|
||||||
const { folder, progress, onArchive, anchorStatus, isArchived } = props;
|
const { folder, progress, onArchive, anchorStatus, isArchived } = props;
|
||||||
|
|
||||||
|
const getSubMenuElement = useCallback(() => {
|
||||||
|
let elements: ISubElement[] = [];
|
||||||
|
|
||||||
|
// Creating the three elements and adding them conditionnally
|
||||||
|
const modifyCollaboratorsElement = {
|
||||||
|
icon: <UsersIcon />,
|
||||||
|
text: "Modifier les collaborateurs",
|
||||||
|
link: Module.getInstance()
|
||||||
|
.get()
|
||||||
|
.modules.pages.Folder.pages.EditCollaborators.props.path.replace("[folderUid]", folder?.uid ?? ""),
|
||||||
|
hasSeparator: true,
|
||||||
|
};
|
||||||
|
const modifyInformationsElement = {
|
||||||
|
icon: <PencilSquareIcon />,
|
||||||
|
text: "Modifier les informations du dossier",
|
||||||
|
link: Module.getInstance()
|
||||||
|
.get()
|
||||||
|
.modules.pages.Folder.pages.EditInformations.props.path.replace("[folderUid]", folder?.uid ?? ""),
|
||||||
|
hasSeparator: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const archiveElement = {
|
||||||
|
icon: <ArchiveBoxIcon />,
|
||||||
|
text: "Archiver le dossier",
|
||||||
|
onClick: onArchive,
|
||||||
|
color: ETypoColor.ERROR_WEAK_CONTRAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the folder is not anchored, we can modify the collaborators and the informations
|
||||||
|
if (anchorStatus === AnchorStatus.NOT_ANCHORED) {
|
||||||
|
elements.push(modifyCollaboratorsElement);
|
||||||
|
// Remove the separator if it's the last item (if the folder is not archived)
|
||||||
|
if (isArchived) modifyInformationsElement.hasSeparator = false;
|
||||||
|
|
||||||
|
elements.push(modifyInformationsElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the folder is not archived, we can archive it
|
||||||
|
if (!isArchived) {
|
||||||
|
elements.push(archiveElement);
|
||||||
|
}
|
||||||
|
return elements;
|
||||||
|
}, [anchorStatus, folder?.uid, isArchived, onArchive]);
|
||||||
return (
|
return (
|
||||||
<section className={classes["root"]}>
|
<section className={classes["root"]}>
|
||||||
<div className={classes["info-box1"]}>
|
<div className={classes["info-box1"]}>
|
||||||
@ -43,37 +86,7 @@ export default function InformationSection(props: IProps) {
|
|||||||
<div className={classes["progress-container"]}>
|
<div className={classes["progress-container"]}>
|
||||||
<CircleProgress percentage={progress} />
|
<CircleProgress percentage={progress} />
|
||||||
<div className={classes["icon-container"]}>
|
<div className={classes["icon-container"]}>
|
||||||
{anchorStatus === AnchorStatus.NOT_ANCHORED && (
|
<ButtonWithSubMenu icon={<EllipsisHorizontalIcon />} subElements={getSubMenuElement()} />
|
||||||
<>
|
|
||||||
<Link
|
|
||||||
href={Module.getInstance()
|
|
||||||
.get()
|
|
||||||
.modules.pages.Folder.pages.EditCollaborators.props.path.replace("[folderUid]", folder?.uid ?? "")}
|
|
||||||
title="Modifier les collaborateurs">
|
|
||||||
<IconButton
|
|
||||||
icon={<UserGroupIcon title="Modifier les collaborateurs" />}
|
|
||||||
variant={EIconButtonVariant.NEUTRAL}
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href={Module.getInstance()
|
|
||||||
.get()
|
|
||||||
.modules.pages.Folder.pages.EditInformations.props.path.replace("[folderUid]", folder?.uid ?? "")}
|
|
||||||
title="Modifier les informations du dossiers">
|
|
||||||
<IconButton
|
|
||||||
icon={<PencilSquareIcon title="Modifier les informations du dossiers" />}
|
|
||||||
variant={EIconButtonVariant.NEUTRAL}
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!isArchived && (
|
|
||||||
<IconButton
|
|
||||||
onClick={onArchive}
|
|
||||||
icon={<ArchiveBoxIcon title="Archiver le dossier" />}
|
|
||||||
variant={EIconButtonVariant.ERROR}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes["description-container"]}>
|
<div className={classes["description-container"]}>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user