64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
import { HelpCircle, X } from 'lucide-react';
|
|
import React, { ReactNode, useEffect, useRef, useState } from 'react';
|
|
import { createPortal } from 'react-dom';
|
|
import { Box } from '@/ui/Box';
|
|
import { Heading } from '@/ui/Heading';
|
|
import { Icon } from '@/ui/Icon';
|
|
import { IconButton } from '@/ui/IconButton';
|
|
import { Surface } from '@/ui/Surface';
|
|
|
|
export interface InfoFlyoutProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
title: string;
|
|
children: ReactNode;
|
|
anchorRef: React.RefObject<HTMLElement>;
|
|
}
|
|
|
|
export const InfoFlyout = ({ isOpen, onClose, title, children, anchorRef }: InfoFlyoutProps) => {
|
|
const [position, setPosition] = useState({ top: 0, left: 0 });
|
|
const flyoutRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (isOpen && anchorRef.current) {
|
|
const rect = anchorRef.current.getBoundingClientRect();
|
|
const flyoutWidth = 380;
|
|
const padding = 16;
|
|
|
|
let left = rect.right + 12;
|
|
let top = rect.top;
|
|
|
|
if (left + flyoutWidth > window.innerWidth - padding) {
|
|
left = rect.left - flyoutWidth - 12;
|
|
}
|
|
|
|
setPosition({ top, left });
|
|
}
|
|
}, [isOpen, anchorRef]);
|
|
|
|
if (!isOpen) return null;
|
|
|
|
return createPortal(
|
|
<Box
|
|
ref={flyoutRef as any}
|
|
position="fixed"
|
|
zIndex={100}
|
|
style={{ top: position.top, left: position.left, width: '24rem' }}
|
|
>
|
|
<Surface variant="muted" rounded="xl" shadow="xl" style={{ border: '1px solid var(--ui-color-border-default)', overflow: 'hidden' }}>
|
|
<Box display="flex" alignItems="center" justifyContent="between" padding={4} bg="var(--ui-color-bg-surface-muted)" style={{ borderBottom: '1px solid var(--ui-color-border-default)' }}>
|
|
<Box display="flex" alignItems="center" gap={2}>
|
|
<Icon icon={HelpCircle} size={4} intent="primary" />
|
|
<Heading level={6}>{title}</Heading>
|
|
</Box>
|
|
<IconButton icon={X} size="sm" variant="ghost" onClick={onClose} title="Close" />
|
|
</Box>
|
|
<Box padding={4} style={{ maxHeight: '20rem', overflowY: 'auto' }}>
|
|
{children}
|
|
</Box>
|
|
</Surface>
|
|
</Box>,
|
|
document.body
|
|
);
|
|
};
|