修复卡片右键菜单底部定位
This commit is contained in:
@@ -1,15 +1,17 @@
|
|||||||
import { t } from "@lingui/core/macro";
|
import { t } from "@lingui/core/macro";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
HiLink,
|
HiLink,
|
||||||
|
HiOutlineArrowRightCircle,
|
||||||
HiOutlineCalendar,
|
HiOutlineCalendar,
|
||||||
HiOutlineDocumentDuplicate,
|
HiOutlineDocumentDuplicate,
|
||||||
HiOutlineTag,
|
HiOutlineTag,
|
||||||
HiOutlineTrash,
|
HiOutlineTrash,
|
||||||
HiOutlineUserGroup,
|
HiOutlineUserGroup,
|
||||||
HiOutlineArrowRightCircle,
|
|
||||||
} from "react-icons/hi2";
|
} from "react-icons/hi2";
|
||||||
|
|
||||||
|
import { useIsomorphicLayoutEffect } from "~/hooks/useIsomorphicLayoutEffect";
|
||||||
|
|
||||||
export type CardContextMenuAction =
|
export type CardContextMenuAction =
|
||||||
| "members"
|
| "members"
|
||||||
| "move"
|
| "move"
|
||||||
@@ -85,6 +87,55 @@ export function CardContextMenu({
|
|||||||
canEdit,
|
canEdit,
|
||||||
}: CardContextMenuProps) {
|
}: CardContextMenuProps) {
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
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(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (e: MouseEvent) => {
|
const handleClickOutside = (e: MouseEvent) => {
|
||||||
@@ -103,13 +154,11 @@ export function CardContextMenu({
|
|||||||
};
|
};
|
||||||
}, [onClose]);
|
}, [onClose]);
|
||||||
|
|
||||||
const items = MENU_ITEMS.filter((item) => !item.requiresEdit || canEdit);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={menuRef}
|
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"
|
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: x, top: y }}
|
style={{ left: position.x, top: position.y }}
|
||||||
>
|
>
|
||||||
{items.map(({ action, label, icon }) => (
|
{items.map(({ action, label, icon }) => (
|
||||||
<button
|
<button
|
||||||
@@ -119,7 +168,7 @@ export function CardContextMenu({
|
|||||||
onAction(action);
|
onAction(action);
|
||||||
onClose();
|
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}
|
{icon}
|
||||||
{label}
|
{label}
|
||||||
|
|||||||
Reference in New Issue
Block a user