import useClickOutside from "../../hooks/useClickOutside" import type Anchor from "../../models/Anchor" import { useEffect, useRef, useState } from "preact/hooks" import type { JSX } from "preact" import { createPortal } from "preact/compat" export type SelectionOptions = { name: string func: (e: MouseEvent) => void disabled?: boolean } export type PopoverMenuProps = { close: (e: MouseEvent) => void anchor: Anchor trigger?: HTMLElement | null } & JSX.IntrinsicElements["div"] export type PopoverMenuItem = SelectionOptions | "divider" | "none" export type PopoverMenuItemsObj = { opts: PopoverMenuItem[] } export function makeOpt( name: string, closeFunc: (e: MouseEvent) => void, f?: () => void, hide = false, disabled = false, ): PopoverMenuItem { return hide ? "none" : { name, disabled, func: (e: MouseEvent) => { if (f) f() closeFunc(e) }, } } export function PopoverMenu({ close, anchor, opts, trigger, ...props }: PopoverMenuItemsObj & PopoverMenuProps) { const divRef = useRef(null) const [adjAnchor, setAnchor] = useState(anchor) const { left, top } = adjAnchor useEffect(() => { if (!divRef.current) return const { offsetHeight, offsetWidth } = divRef.current const { clientHeight, clientWidth } = document.documentElement const docBottom = clientHeight const menuBottom = top + offsetHeight const docRight = clientWidth const menuRight = left + offsetWidth setAnchor({ top: top - (menuBottom > docBottom ? menuBottom - docBottom : 0), left: left - (menuRight > docRight ? menuRight - docRight : 0), }) }, [anchor, left, top]) useClickOutside({ handler: close, events: ["click", "contextmenu"], ref: divRef, exclude: [trigger ?? null], }) const [initialMenuScale, setInitialMenuScale] = useState(false) useEffect(() => { setInitialMenuScale(true) }, []) return createPortal(
{ event.preventDefault() close(event) }} onClick={(event) => event.stopPropagation()} {...props} > {opts.map((e, index) => { if (e === "divider") { return ( ) } else if (e !== "none") { const { name, func } = e return ( {name} ) } return null })}
, document.body, ) }