adding context menu to entities list

This commit is contained in:
dswbx
2025-01-18 09:05:35 +01:00
parent 7ddcfc89b4
commit 89b29256cf
8 changed files with 339 additions and 156 deletions

View File

@@ -1,5 +1,11 @@
import { useClickOutside } from "@mantine/hooks";
import { Fragment, type ReactElement, cloneElement, useState } from "react";
import {
type ComponentPropsWithoutRef,
Fragment,
type ReactElement,
cloneElement,
useState
} from "react";
import { twMerge } from "tailwind-merge";
import { useEvent } from "../../hooks/use-event";
@@ -14,26 +20,33 @@ export type DropdownItem =
[key: string]: any;
};
export type DropdownClickableChild = ReactElement<{ onClick: () => void }>;
export type DropdownProps = {
className?: string;
openEvent?: "onClick" | "onContextMenu";
defaultOpen?: boolean;
title?: string | ReactElement;
dropdownWrapperProps?: Omit<ComponentPropsWithoutRef<"div">, "style">;
position?: "bottom-start" | "bottom-end" | "top-start" | "top-end";
hideOnEmpty?: boolean;
items: (DropdownItem | undefined | boolean)[];
itemsClassName?: string;
children: ReactElement<{ onClick: () => void }>;
children: DropdownClickableChild;
onClickItem?: (item: DropdownItem) => void;
renderItem?: (
item: DropdownItem,
props: { key: number; onClick: () => void }
) => ReactElement<{ onClick: () => void }>;
) => DropdownClickableChild;
};
export function Dropdown({
children,
defaultOpen = false,
openEvent = "onClick",
position = "bottom-start",
dropdownWrapperProps,
items,
title,
hideOnEmpty = true,
onClickItem,
renderItem,
@@ -48,6 +61,11 @@ export function Dropdown({
setTimeout(() => setOpen((prev) => !prev), typeof delay === "number" ? delay : 0)
);
const openEventHandler = useEvent((e) => {
e.preventDefault();
toggle();
});
const offset = 4;
const dropdownStyle = {
"bottom-start": { top: "100%", left: 0, marginTop: offset },
@@ -94,13 +112,26 @@ export function Dropdown({
));
return (
<div role="dropdown" className={twMerge("relative flex", className)} ref={clickoutsideRef}>
{cloneElement(children as any, { onClick: toggle })}
<div
role="dropdown"
className={twMerge("relative flex", className)}
ref={clickoutsideRef}
onContextMenu={openEvent === "onContextMenu" ? openEventHandler : undefined}
>
{cloneElement(
children as any,
openEvent === "onClick" ? { onClick: openEventHandler } : {}
)}
{open && (
<div
className="absolute z-30 flex flex-col bg-background border border-muted px-1 py-1 rounded-lg shadow-lg min-w-full"
{...dropdownWrapperProps}
className={twMerge(
"absolute z-30 flex flex-col bg-background border border-muted px-1 py-1 rounded-lg shadow-lg min-w-full",
dropdownWrapperProps?.className
)}
style={dropdownStyle}
>
{title && <div className="text-sm font-bold px-3 mb-1 mt-1 opacity-50">{title}</div>}
{menuItems.map((item, i) =>
itemRenderer(item, { key: i, onClick: () => internalOnClickItem(item) })
)}