@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
1 lines • 4.28 kB
Source Map (JSON)
{"version":3,"file":"ContextMenuHost.mjs","names":[],"sources":["../../src/ContextMenu/ContextMenuHost.tsx"],"sourcesContent":["'use client';\n\nimport { ContextMenu } from '@base-ui/react/context-menu';\nimport { memo, useEffect, useMemo, useSyncExternalStore } from 'react';\n\nimport { LOBE_THEME_APP_ID } from '@/ThemeProvider';\nimport { useIsClient } from '@/hooks/useIsClient';\nimport { usePortalContainer } from '@/hooks/usePortalContainer';\nimport { registerDevSingleton } from '@/utils/devSingleton';\nimport { preventDefaultAndStopPropagation } from '@/utils/dom';\n\nimport { renderContextMenuItems } from './renderItems';\nimport {\n closeContextMenu,\n getServerSnapshot,\n getSnapshot,\n setContextMenuState,\n subscribe,\n updateLastPointer,\n} from './store';\nimport { styles } from './style';\n\nconst CONTEXT_MENU_CONTAINER_ATTR = 'data-lobe-ui-context-menu-container';\n\nexport const ContextMenuHost = memo(() => {\n const isClient = useIsClient();\n const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n\n useEffect(() => {\n const DEV = process.env.NODE_ENV === 'development';\n if (!isClient || !DEV) return;\n // Enforce singleton per portal container (dev-only).\n const themeApp = document.querySelector<HTMLElement>(`#${LOBE_THEME_APP_ID}`);\n const contextMenuContainer = document.querySelector<HTMLElement>(\n `[${CONTEXT_MENU_CONTAINER_ATTR}=\"true\"]`,\n );\n const scope = themeApp ?? contextMenuContainer ?? document.body;\n return registerDevSingleton('ContextMenuHost', scope);\n }, [isClient]);\n\n useEffect(() => {\n const handler = (event: MouseEvent | PointerEvent) => updateLastPointer(event);\n window.addEventListener('pointerdown', handler, true);\n window.addEventListener('contextmenu', handler, true);\n return () => {\n window.removeEventListener('pointerdown', handler, true);\n window.removeEventListener('contextmenu', handler, true);\n };\n }, []);\n\n const menuItems = useMemo(\n () => renderContextMenuItems(state.items, [], { iconSpaceMode: state.iconSpaceMode }),\n [state.items, state.iconSpaceMode],\n );\n const portalContainer = usePortalContainer(CONTEXT_MENU_CONTAINER_ATTR);\n\n if (!isClient) return null;\n if (!state.open && state.items.length === 0) return null;\n if (!portalContainer) return null;\n\n return (\n <ContextMenu.Root\n onOpenChange={(open) => {\n if (open) {\n setContextMenuState({ open });\n return;\n }\n closeContextMenu();\n }}\n open={state.open}\n >\n <ContextMenu.Portal container={portalContainer}>\n <ContextMenu.Positioner\n anchor={state.anchor ?? undefined}\n className={styles.positioner}\n sideOffset={6}\n >\n <ContextMenu.Popup\n className={styles.popup}\n onContextMenu={preventDefaultAndStopPropagation}\n >\n {menuItems}\n </ContextMenu.Popup>\n </ContextMenu.Positioner>\n </ContextMenu.Portal>\n </ContextMenu.Root>\n );\n});\n\nContextMenuHost.displayName = 'ContextMenuHost';\n"],"mappings":";;;;;;;;;;;;;;;AAsBA,MAAM,8BAA8B;AAEpC,MAAa,kBAAkB,WAAW;CACxC,MAAM,WAAW,aAAa;CAC9B,MAAM,QAAQ,qBAAqB,WAAW,aAAa,kBAAkB;AAE7E,iBAAgB;EACd,MAAM,MAAM,QAAQ,IAAI,aAAa;AACrC,MAAI,CAAC,YAAY,CAAC,IAAK;EAEvB,MAAM,WAAW,SAAS,cAA2B,IAAI,oBAAoB;EAC7E,MAAM,uBAAuB,SAAS,cACpC,IAAI,4BAA4B,UACjC;AAED,SAAO,qBAAqB,mBADd,YAAY,wBAAwB,SAAS,KACN;IACpD,CAAC,SAAS,CAAC;AAEd,iBAAgB;EACd,MAAM,WAAW,UAAqC,kBAAkB,MAAM;AAC9E,SAAO,iBAAiB,eAAe,SAAS,KAAK;AACrD,SAAO,iBAAiB,eAAe,SAAS,KAAK;AACrD,eAAa;AACX,UAAO,oBAAoB,eAAe,SAAS,KAAK;AACxD,UAAO,oBAAoB,eAAe,SAAS,KAAK;;IAEzD,EAAE,CAAC;CAEN,MAAM,YAAY,cACV,uBAAuB,MAAM,OAAO,EAAE,EAAE,EAAE,eAAe,MAAM,eAAe,CAAC,EACrF,CAAC,MAAM,OAAO,MAAM,cAAc,CACnC;CACD,MAAM,kBAAkB,mBAAmB,4BAA4B;AAEvE,KAAI,CAAC,SAAU,QAAO;AACtB,KAAI,CAAC,MAAM,QAAQ,MAAM,MAAM,WAAW,EAAG,QAAO;AACpD,KAAI,CAAC,gBAAiB,QAAO;AAE7B,QACE,oBAAC,YAAY;EACX,eAAe,SAAS;AACtB,OAAI,MAAM;AACR,wBAAoB,EAAE,MAAM,CAAC;AAC7B;;AAEF,qBAAkB;;EAEpB,MAAM,MAAM;YAEZ,oBAAC,YAAY;GAAO,WAAW;aAC7B,oBAAC,YAAY;IACX,QAAQ,MAAM,UAAU;IACxB,WAAW,OAAO;IAClB,YAAY;cAEZ,oBAAC,YAAY;KACX,WAAW,OAAO;KAClB,eAAe;eAEd;MACiB;KACG;IACN;GACJ;EAErB;AAEF,gBAAgB,cAAc"}