Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions apps/app/components/core/views/board-view/single-issue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ export const SingleBoardIssue: React.FC<Props> = ({
}) => {
// context menu
const [contextMenu, setContextMenu] = useState(false);
const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 });
const [contextMenuPosition, setContextMenuPosition] = useState<React.MouseEvent | null>(null);

const [isMenuActive, setIsMenuActive] = useState(false);
const [isDropdownActive, setIsDropdownActive] = useState(false);

Expand Down Expand Up @@ -201,7 +202,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
return (
<>
<ContextMenu
position={contextMenuPosition}
clickEvent={contextMenuPosition}
title="Quick actions"
isOpen={contextMenu}
setIsOpen={setContextMenu}
Expand Down Expand Up @@ -243,7 +244,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
onContextMenu={(e) => {
e.preventDefault();
setContextMenu(true);
setContextMenuPosition({ x: e.pageX, y: e.pageY });
setContextMenuPosition(e);
}}
>
<div className="flex flex-col justify-between gap-1.5 group/card relative select-none px-3.5 py-3 h-[118px]">
Expand Down
8 changes: 4 additions & 4 deletions apps/app/components/core/views/list-view/single-issue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
} from "@heroicons/react/24/outline";
import { LayerDiagonalIcon } from "components/icons";
// helpers
import { copyTextToClipboard, truncateText } from "helpers/string.helper";
import { copyTextToClipboard } from "helpers/string.helper";
import { handleIssuesMutation } from "constants/issue";
// types
import { ICurrentUserResponse, IIssue, IIssueViewProps, ISubIssueResponse, UserAuth } from "types";
Expand Down Expand Up @@ -71,7 +71,7 @@ export const SingleListIssue: React.FC<Props> = ({
}) => {
// context menu
const [contextMenu, setContextMenu] = useState(false);
const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 });
const [contextMenuPosition, setContextMenuPosition] = useState<React.MouseEvent | null>(null);

const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
Expand Down Expand Up @@ -167,7 +167,7 @@ export const SingleListIssue: React.FC<Props> = ({
return (
<>
<ContextMenu
position={contextMenuPosition}
clickEvent={contextMenuPosition}
title="Quick actions"
isOpen={contextMenu}
setIsOpen={setContextMenu}
Expand Down Expand Up @@ -199,7 +199,7 @@ export const SingleListIssue: React.FC<Props> = ({
onContextMenu={(e) => {
e.preventDefault();
setContextMenu(true);
setContextMenuPosition({ x: e.pageX, y: e.pageY });
setContextMenuPosition(e);
}}
>
<div className="flex-grow cursor-pointer min-w-[200px] whitespace-nowrap overflow-hidden overflow-ellipsis">
Expand Down
61 changes: 45 additions & 16 deletions apps/app/components/ui/dropdowns/context-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,76 @@
import React, { useEffect } from "react";
import React, { useEffect, useRef } from "react";

import Link from "next/link";

// hooks
import useOutsideClickDetector from "hooks/use-outside-click-detector";

type Props = {
position: {
x: number;
y: number;
};
clickEvent: React.MouseEvent | null;
children: React.ReactNode;
title?: string | JSX.Element;
isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
};

const ContextMenu = ({ position, children, title, isOpen, setIsOpen }: Props) => {
const ContextMenu = ({ clickEvent, children, title, isOpen, setIsOpen }: Props) => {
const contextMenuRef = useRef<HTMLDivElement>(null);

// Close the context menu when clicked outside
useOutsideClickDetector(contextMenuRef, () => {
if (isOpen) setIsOpen(false);
});

useEffect(() => {
const hideContextMenu = () => {
if (isOpen) setIsOpen(false);
};

window.addEventListener("click", hideContextMenu);
window.addEventListener("keydown", (e: KeyboardEvent) => {
const escapeKeyEvent = (e: KeyboardEvent) => {
if (e.key === "Escape") hideContextMenu();
});
};

window.addEventListener("click", hideContextMenu);
window.addEventListener("keydown", escapeKeyEvent);

return () => {
window.removeEventListener("click", hideContextMenu);
window.removeEventListener("keydown", hideContextMenu);
window.removeEventListener("keydown", escapeKeyEvent);
};
}, [isOpen, setIsOpen]);

useEffect(() => {
const contextMenu = contextMenuRef.current;

if (contextMenu && isOpen) {
const contextMenuWidth = contextMenu.clientWidth;
const contextMenuHeight = contextMenu.clientHeight;

const clickX = clickEvent?.pageX || 0;
const clickY = clickEvent?.pageY || 0;

let top = clickY;
// check if there's enough space at the bottom, otherwise show at the top
if (clickY + contextMenuHeight > window.innerHeight) top = clickY - contextMenuHeight;

// check if there's enough space on the right, otherwise show on the left
let left = clickX;
if (clickX + contextMenuWidth > window.innerWidth) left = clickX - contextMenuWidth;

contextMenu.style.top = `${top}px`;
contextMenu.style.left = `${left}px`;
}
}, [clickEvent, isOpen]);

return (
<div
className={`fixed z-20 h-full w-full ${
className={`fixed z-50 top-0 left-0 h-full w-full ${
isOpen ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
}`}
>
<div
className={`fixed z-20 flex min-w-[8rem] flex-col items-stretch gap-1 rounded-md border border-custom-border-200 bg-custom-background-90 p-2 text-xs shadow-lg`}
style={{
left: `${position.x}px`,
top: `${position.y}px`,
}}
ref={contextMenuRef}
className={`fixed z-50 flex min-w-[8rem] flex-col items-stretch gap-1 rounded-md border border-custom-border-200 bg-custom-background-90 p-2 text-xs shadow-lg`}
>
{title && (
<h4 className="border-b border-custom-border-200 px-1 py-1 pb-2 text-[0.8rem] font-medium">
Expand Down
4 changes: 2 additions & 2 deletions apps/app/hooks/use-outside-click-detector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ const useOutsideClickDetector = (ref: React.RefObject<HTMLElement>, callback: ()
};

useEffect(() => {
document.addEventListener("click", handleClick);
document.addEventListener("mousedown", handleClick);

return () => {
document.removeEventListener("click", handleClick);
document.removeEventListener("mousedown", handleClick);
};
});
};
Expand Down