Add web interface and tests
This commit is contained in:
100
packages/web/src/components/ItemFieldContextMenu.tsx
Normal file
100
packages/web/src/components/ItemFieldContextMenu.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import styled from "@emotion/styled"
|
||||
|
||||
const Container = styled.menu`
|
||||
background-color: #fff;
|
||||
border-radius: 3px;
|
||||
box-shadow: #0004 0px 1px 4px;
|
||||
left: 99%;
|
||||
margin-block-start: 0;
|
||||
min-width: 120px;
|
||||
padding-inline-start: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
user-select: none;
|
||||
z-index: 2;
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: #3c3c3c;
|
||||
box-shadow: rgb(0 0 0) 0px 2px 4px;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
& & {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
|
||||
const Separator = styled.div`
|
||||
border-bottom: 1px solid #777;
|
||||
margin-top: 0.4em;
|
||||
margin-bottom: 0.4em;
|
||||
margin-left: 0.6em;
|
||||
margin-right: 0.6em;
|
||||
`
|
||||
|
||||
const Item = styled.div`
|
||||
cursor: default;
|
||||
font-size: 13px;
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
height: 2.5em;
|
||||
align-items: center;
|
||||
padding-left: 1em;
|
||||
position: relative;
|
||||
&:hover {
|
||||
background-color: #ddd;
|
||||
border-radius: 3px;
|
||||
.item-field-context-menu {
|
||||
display: block;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: #094771;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
function useContextMenu() {
|
||||
const [show, setShow] = useState(false)
|
||||
const [pos, setPos] = useState({ x: 0, y: 0 })
|
||||
const onRightClick = useCallback((e: React.MouseEvent) => {
|
||||
setShow(true)
|
||||
e.preventDefault()
|
||||
setPos({ x: e.pageX, y: e.pageY })
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const fn = () => setShow(false)
|
||||
document.addEventListener("click", fn)
|
||||
return () => document.removeEventListener("click", fn)
|
||||
}, [])
|
||||
|
||||
return {
|
||||
show,
|
||||
position: {
|
||||
top: pos.y,
|
||||
left: pos.x,
|
||||
},
|
||||
onRightClick,
|
||||
}
|
||||
}
|
||||
|
||||
export function useItemFieldContextMenu() {
|
||||
const { onRightClick, position, show } = useContextMenu()
|
||||
|
||||
const ContextMenuContainer: React.FC = useCallback(
|
||||
({ children }) => {
|
||||
if (!show) return null
|
||||
return (
|
||||
<Container style={position} className="item-field-context-menu">
|
||||
{children}
|
||||
</Container>
|
||||
)
|
||||
},
|
||||
[show, position]
|
||||
)
|
||||
|
||||
return {
|
||||
onRightClick,
|
||||
Item,
|
||||
ContextMenuContainer,
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user