feat: HoverPopover done

This commit is contained in:
butterfly
2024-04-28 14:10:58 +08:00
parent 9569888b0e
commit 9f4813326c
6 changed files with 183 additions and 63 deletions

View File

@@ -0,0 +1,37 @@
import * as HoverCard from "@radix-ui/react-hover-card";
import { ComponentProps } from "react";
export interface PopoverProps {
content?: JSX.Element | string;
children?: JSX.Element;
arrowClassName?: string;
popoverClassName?: string;
noArrow?: boolean;
align?: ComponentProps<typeof HoverCard.Content>["align"];
}
export default function HoverPopover(props: PopoverProps) {
const {
content,
children,
arrowClassName,
popoverClassName,
noArrow = false,
align,
} = props;
return (
<HoverCard.Root>
<HoverCard.Trigger asChild>{children}</HoverCard.Trigger>
<HoverCard.Portal>
<HoverCard.Content
className={`${popoverClassName}`}
sideOffset={5}
align={align}
>
{content}
{!noArrow && <HoverCard.Arrow className={`${arrowClassName}`} />}
</HoverCard.Content>
</HoverCard.Portal>
</HoverCard.Root>
);
}

View File

@@ -54,6 +54,7 @@ export interface PopoverProps {
placement?: "t" | "lt" | "rt" | "lb" | "rb" | "b" | "l" | "r";
noArrow?: boolean;
delayClose?: number;
useGlobalRoot?: boolean;
}
export default function Popover(props: PopoverProps) {
@@ -68,6 +69,7 @@ export default function Popover(props: PopoverProps) {
placement = "t",
noArrow = false,
delayClose = 0,
useGlobalRoot,
} = props;
const [internalShow, setShow] = useState(false);
@@ -244,62 +246,66 @@ export default function Popover(props: PopoverProps) {
);
}
// return (
// <div
// className={`relative ${className}`}
// onPointerEnter={(e) => {
// e.preventDefault();
// clearTimeout(closeTimer.current);
// onShow?.(true);
// setShow(true);
// getRelativePosition(e.currentTarget, "");
// window.document.documentElement.style.overflow = "hidden";
// }}
// onPointerLeave={(e) => {
// e.preventDefault();
// if (delayClose) {
// closeTimer.current = window.setTimeout(() => {
// onShow?.(false);
// setShow(false);
// }, delayClose);
// } else {
// onShow?.(false);
// setShow(false);
// }
// window.document.documentElement.style.overflow = "auto";
// }}
// >
// {children}
// {mergedShow && (
// <>
// <div
// className={`${
// noArrow ? "opacity-0" : ""
// } bg-inherit ${arrowClassName}`}
// style={{ zIndex: baseZIndex + 1 }}
// >
// <ArrowIcon sibling={popoverRef} />
// </div>
// {createPortal(
// <div
// className={` whitespace-nowrap ${popoverCommonClass} ${popoverClassName} cursor-pointer`}
// style={{ zIndex: baseZIndex + 1, ...placementStyle }}
// ref={popoverRef}
// >
// {content}
// </div>,
// popoverRoot,
// )}
// </>
// )}
// </div>
// );
if (useGlobalRoot) {
return (
<div
className={`relative ${className}`}
onPointerEnter={(e) => {
e.preventDefault();
clearTimeout(closeTimer.current);
onShow?.(true);
setShow(true);
getRelativePosition(e.currentTarget, "");
window.document.documentElement.style.overflow = "hidden";
}}
onPointerLeave={(e) => {
e.preventDefault();
if (delayClose) {
closeTimer.current = window.setTimeout(() => {
onShow?.(false);
setShow(false);
}, delayClose);
} else {
onShow?.(false);
setShow(false);
}
window.document.documentElement.style.overflow = "auto";
}}
>
{children}
{mergedShow && (
<>
<div
className={`${
noArrow ? "opacity-0" : ""
} bg-inherit ${arrowClassName}`}
style={{ zIndex: baseZIndex + 1 }}
>
<ArrowIcon sibling={popoverRef} />
</div>
{createPortal(
<div
className={` whitespace-nowrap ${popoverCommonClass} ${popoverClassName} cursor-pointer`}
style={{ zIndex: baseZIndex + 1, ...placementStyle }}
ref={popoverRef}
>
{content}
</div>,
popoverRoot,
)}
</>
)}
</div>
);
}
return (
<div
className={`group relative ${className}`}
onPointerEnter={(e) => {
getRelativePosition(e.currentTarget, "");
e.preventDefault();
e.stopPropagation();
}}
>
{children}

View File

@@ -10,9 +10,6 @@ import { showToast } from "@/app/components/ui-lib";
import Locale from "@/app/locales";
import { Path } from "@/app/constant";
import LightIcon from "@/app/icons/light.svg";
import DarkIcon from "@/app/icons/dark.svg";
import AutoIcon from "@/app/icons/auto.svg";
import BottomIcon from "@/app/icons/bottom.svg";
import StopIcon from "@/app/icons/pause.svg";
import RobotIcon from "@/app/icons/robot.svg";

View File

@@ -23,8 +23,8 @@ import LogIcon from "@/app/icons/logIcon.svg";
import MenuLayout from "@/app/components/MenuLayout";
import Panel from "./ChatPanel";
import Popover, { PopoverProps } from "@/app/components/Popover";
import Confirm from "@/app/components/Confirm";
import HoverPopover from "@/app/components/HoverPopover";
export function SessionItem(props: {
onClick?: () => void;
@@ -96,7 +96,7 @@ export function SessionItem(props: {
{Locale.ChatItem.ChatItemCount(props.count)}
</div>
</div>
<Popover
<HoverPopover
content={
<div
className={`flex items-center gap-3 p-3 rounded-action-btn leading-6 cursor-pointer`}
@@ -113,20 +113,17 @@ export function SessionItem(props: {
</div>
}
popoverClassName={`
px-2 py-1 border-delete-chat-popover bg-delete-chat-popover-panel rounded-md shadow-delete-chat-popover-shadow
px-2 py-1 border-delete-chat-popover bg-delete-chat-popover-panel rounded-md shadow-delete-chat-popover-shadow
`}
noArrow
placement={props.isMobileScreen ? "r" : "l"}
className="!absolute top-[50%] translate-y-[-50%] right-3"
trigger={props.isMobileScreen ? "click" : "hover"}
delayClose={100}
align={props.isMobileScreen ? "end" : "start"}
>
<div
className={` pointer-events-none opacity-0 group-hover/chat-menu-list:pointer-events-auto group-hover/chat-menu-list:opacity-100`}
>
<DeleteIcon />
</div>
</Popover>
</HoverPopover>
</div>
)}
</Draggable>
@@ -175,7 +172,6 @@ export default MenuLayout(function SessionList(props) {
<div
className={`
h-[100%] flex flex-col
max-md:pb-chat-panel-mobile
md:px-0
`}
>
@@ -213,7 +209,7 @@ export default MenuLayout(function SessionList(props) {
</div>
<div
className={`flex-1 overflow-y-auto`}
className={`flex-1 overflow-y-auto max-md:pb-chat-panel-mobile `}
// onClick={(e) => {
// if (e.target === e.currentTarget) {
// navigate(Path.Home);