Spaces:
Running
Running
File size: 1,928 Bytes
f23825d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
import styled from "@emotion/styled"
import * as Portal from "@radix-ui/react-portal"
import { FC, ReactNode, useEffect } from "react"
import { IPoint } from "../common/geometry"
export const ContextMenuHotKey = styled.div`
font-size: 0.9em;
flex-grow: 1;
text-align: right;
color: ${({ theme }) => theme.secondaryTextColor};
margin-left: 2em;
`
const Wrapper = styled.div`
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
`
const Content = styled.div`
position: absolute;
background: ${({ theme }) => theme.secondaryBackgroundColor};
border-radius: 0.5rem;
box-shadow: 0 1rem 3rem ${({ theme }) => theme.shadowColor};
border: 1px solid ${({ theme }) => theme.backgroundColor};
padding: 0.5rem 0;
`
const List = styled.ul`
list-style: none;
padding: 0;
margin: 0;
`
export interface ContextMenuProps {
isOpen: boolean
position: IPoint
handleClose: () => void
children?: ReactNode
}
const estimatedWidth = 200
export const ContextMenu: FC<ContextMenuProps> = ({
isOpen,
handleClose,
position,
children,
}) => {
// Menu cannot handle keydown while disabling focus, so we deal with global keydown event
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if (isOpen && e.key === "Escape") {
handleClose()
}
}
document.addEventListener("keydown", onKeyDown)
return () => {
document.removeEventListener("keydown", onKeyDown)
}
}, [isOpen])
if (!isOpen) {
return <></>
}
// fix position to avoid placing menu outside of the screen
const fixedX = Math.min(position.x, window.innerWidth - estimatedWidth)
return (
<Portal.Root>
<Wrapper onClick={handleClose}>
<Content
style={{ left: fixedX, top: position.y }}
onClick={(e) => e.stopPropagation()}
>
<List>{children}</List>
</Content>
</Wrapper>
</Portal.Root>
)
}
|