import { Box, Flex, Menu, MenuDivider, MenuGroup, MenuItem, Popover, Spinner, Stack, Text, type PopoverMargins, } from '@sanity/ui/_visual-editing' import {useCallback, useEffect, useMemo, useState, type FunctionComponent} from 'react' import type {ContextMenuNode, ContextMenuProps} from '../../types' import {useDocuments} from '../../react/useDocuments' import {getNodeIcon} from '../../util/getNodeIcon' import {PopoverPortal} from '../PopoverPortal' import {useSchema} from '../schema/useSchema' import {useTelemetry} from '../telemetry/useTelemetry' import {getContextMenuItems} from './contextMenuItems' const POPOVER_MARGINS: PopoverMargins = [-4, 4, -4, 4] function ContextMenuItem(props: { node: ContextMenuNode onDismiss?: () => void boundaryElement: HTMLDivElement | null }) { const {node, onDismiss, boundaryElement} = props const sendTelemetry = useTelemetry() const onClick = useCallback(() => { if (node.type === 'action') { node.action?.() onDismiss?.() if (node.telemetryEvent) { sendTelemetry(node.telemetryEvent, null) } } }, [node, onDismiss, sendTelemetry]) if (node.type === 'divider') { return } if (node.type === 'action') { return ( ) } if (node.type === 'group') { return ( {node.items.map((item, itemIndex) => ( ))} ) } if (node.type === 'custom') { const {component: Component} = node return } return null } export const ContextMenu: FunctionComponent = (props) => { const { node, onDismiss, position: {x, y}, } = props const [boundaryElement, setBoundaryElement] = useState(null) const {getField} = useSchema() const {getDocument} = useDocuments() const {field, parent} = getField(node) const title = useMemo(() => { return field?.title || field?.name || 'Unknown type' }, [field]) const [items, setItems] = useState(undefined) useEffect(() => { const fetchContextMenuItems = async () => { const doc = getDocument(node.id) if (!doc) return const items = await getContextMenuItems({node, field, parent, doc}) setItems(items) } fetchContextMenuItems() }, [field, node, parent, getDocument]) const contextMenuReferenceElement = useMemo(() => { return { getBoundingClientRect: () => ({ bottom: y, left: x, right: x, top: y, width: 0, height: 0, }), } as HTMLElement }, [x, y]) const icon = useMemo(() => { return getNodeIcon(field) }, [field]) return ( {items ? {icon} : } {items ? title : 'Loading...'} {items && ( <> {items.map((item, i) => ( ))} )} } >
) }