修复卡片右键菜单底部定位
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
HiLink,
|
||||
HiOutlineArrowRightCircle,
|
||||
HiOutlineCalendar,
|
||||
HiOutlineDocumentDuplicate,
|
||||
HiOutlineTag,
|
||||
HiOutlineTrash,
|
||||
HiOutlineUserGroup,
|
||||
HiOutlineArrowRightCircle,
|
||||
} from "react-icons/hi2";
|
||||
|
||||
import { useIsomorphicLayoutEffect } from "~/hooks/useIsomorphicLayoutEffect";
|
||||
|
||||
export type CardContextMenuAction =
|
||||
| "members"
|
||||
| "move"
|
||||
@@ -85,6 +87,55 @@ export function CardContextMenu({
|
||||
canEdit,
|
||||
}: CardContextMenuProps) {
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const [position, setPosition] = useState({ x, y });
|
||||
|
||||
const items = MENU_ITEMS.filter((item) => !item.requiresEdit || canEdit);
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
const menu = menuRef.current;
|
||||
if (!menu) return;
|
||||
|
||||
const gutter = 8;
|
||||
const updatePosition = () => {
|
||||
const { width, height } = menu.getBoundingClientRect();
|
||||
const viewportWidth = window.visualViewport?.width ?? window.innerWidth;
|
||||
const viewportHeight =
|
||||
window.visualViewport?.height ?? window.innerHeight;
|
||||
const maxX = Math.max(gutter, viewportWidth - width - gutter);
|
||||
const maxY = Math.max(gutter, viewportHeight - height - gutter);
|
||||
const nextPosition = {
|
||||
x: Math.min(Math.max(x, gutter), maxX),
|
||||
y: Math.min(Math.max(y, gutter), maxY),
|
||||
};
|
||||
|
||||
setPosition((currentPosition) =>
|
||||
currentPosition.x === nextPosition.x &&
|
||||
currentPosition.y === nextPosition.y
|
||||
? currentPosition
|
||||
: nextPosition,
|
||||
);
|
||||
};
|
||||
|
||||
updatePosition();
|
||||
|
||||
const resizeObserver =
|
||||
typeof ResizeObserver !== "undefined"
|
||||
? new ResizeObserver(updatePosition)
|
||||
: null;
|
||||
resizeObserver?.observe(menu);
|
||||
window.addEventListener("resize", updatePosition);
|
||||
window.addEventListener("scroll", updatePosition, true);
|
||||
window.visualViewport?.addEventListener("resize", updatePosition);
|
||||
window.visualViewport?.addEventListener("scroll", updatePosition);
|
||||
|
||||
return () => {
|
||||
resizeObserver?.disconnect();
|
||||
window.removeEventListener("resize", updatePosition);
|
||||
window.removeEventListener("scroll", updatePosition, true);
|
||||
window.visualViewport?.removeEventListener("resize", updatePosition);
|
||||
window.visualViewport?.removeEventListener("scroll", updatePosition);
|
||||
};
|
||||
}, [x, y, canEdit, items.length]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
@@ -103,13 +154,11 @@ export function CardContextMenu({
|
||||
};
|
||||
}, [onClose]);
|
||||
|
||||
const items = MENU_ITEMS.filter((item) => !item.requiresEdit || canEdit);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={menuRef}
|
||||
className="fixed z-[200] min-w-[200px] rounded-md border border-light-200 bg-white py-1 shadow-lg dark:border-dark-400 dark:bg-dark-200"
|
||||
style={{ left: x, top: y }}
|
||||
className="fixed z-[200] max-h-[calc(100dvh-1rem)] min-w-[min(200px,calc(100vw-1rem))] max-w-[calc(100vw-1rem)] overflow-y-auto overflow-x-hidden rounded-md border border-light-200 bg-white py-1 shadow-lg dark:border-dark-400 dark:bg-dark-200"
|
||||
style={{ left: position.x, top: position.y }}
|
||||
>
|
||||
{items.map(({ action, label, icon }) => (
|
||||
<button
|
||||
@@ -119,7 +168,7 @@ export function CardContextMenu({
|
||||
onAction(action);
|
||||
onClose();
|
||||
}}
|
||||
className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-neutral-900 hover:bg-light-200 dark:text-dark-1000 dark:hover:bg-dark-400"
|
||||
className="flex w-full items-center gap-2 break-words px-3 py-2 text-left text-sm text-neutral-900 hover:bg-light-200 dark:text-dark-1000 dark:hover:bg-dark-400"
|
||||
>
|
||||
{icon}
|
||||
{label}
|
||||
|
||||
Reference in New Issue
Block a user