UNPKG

@base-ui-components/react

Version:

Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.

164 lines (162 loc) 6.02 kB
"use strict"; 'use client'; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.ContextMenuTrigger = void 0; var React = _interopRequireWildcard(require("react")); var _owner = require("@base-ui-components/utils/owner"); var _useTimeout = require("@base-ui-components/utils/useTimeout"); var _utils = require("../../floating-ui-react/utils"); var _ContextMenuRootContext = require("../root/ContextMenuRootContext"); var _MenuRootContext = require("../../menu/root/MenuRootContext"); var _useRenderElement = require("../../utils/useRenderElement"); var _createBaseUIEventDetails = require("../../utils/createBaseUIEventDetails"); var _popupStateMapping = require("../../utils/popupStateMapping"); var _reasons = require("../../utils/reasons"); var _findRootOwnerId = require("../../menu/utils/findRootOwnerId"); const LONG_PRESS_DELAY = 500; /** * An area that opens the menu on right click or long press. * Renders a `<div>` element. * * Documentation: [Base UI Context Menu](https://base-ui.com/react/components/context-menu) */ const ContextMenuTrigger = exports.ContextMenuTrigger = /*#__PURE__*/React.forwardRef(function ContextMenuTrigger(componentProps, forwardedRef) { const { render, className, ...elementProps } = componentProps; const { setAnchor, actionsRef, internalBackdropRef, backdropRef, positionerRef, allowMouseUpTriggerRef, initialCursorPointRef, rootId } = (0, _ContextMenuRootContext.useContextMenuRootContext)(false); const { store } = (0, _MenuRootContext.useMenuRootContext)(false); const open = store.useState('open'); const triggerRef = React.useRef(null); const touchPositionRef = React.useRef(null); const longPressTimeout = (0, _useTimeout.useTimeout)(); const allowMouseUpTimeout = (0, _useTimeout.useTimeout)(); const allowMouseUpRef = React.useRef(false); function handleLongPress(x, y, event) { const isTouchEvent = event.type.startsWith('touch'); initialCursorPointRef.current = { x, y }; setAnchor({ getBoundingClientRect() { return DOMRect.fromRect({ width: isTouchEvent ? 10 : 0, height: isTouchEvent ? 10 : 0, x, y }); } }); allowMouseUpRef.current = false; actionsRef.current?.setOpen(true, (0, _createBaseUIEventDetails.createChangeEventDetails)(_reasons.REASONS.triggerPress, event)); allowMouseUpTimeout.start(LONG_PRESS_DELAY, () => { allowMouseUpRef.current = true; }); } function handleContextMenu(event) { allowMouseUpTriggerRef.current = true; (0, _utils.stopEvent)(event); handleLongPress(event.clientX, event.clientY, event.nativeEvent); const doc = (0, _owner.ownerDocument)(triggerRef.current); doc.addEventListener('mouseup', mouseEvent => { allowMouseUpTriggerRef.current = false; if (!allowMouseUpRef.current) { return; } allowMouseUpTimeout.clear(); allowMouseUpRef.current = false; const mouseUpTarget = (0, _utils.getTarget)(mouseEvent); if ((0, _utils.contains)(positionerRef.current, mouseUpTarget)) { return; } if (rootId && mouseUpTarget && (0, _findRootOwnerId.findRootOwnerId)(mouseUpTarget) === rootId) { return; } actionsRef.current?.setOpen(false, (0, _createBaseUIEventDetails.createChangeEventDetails)(_reasons.REASONS.cancelOpen, mouseEvent)); }, { once: true }); } function handleTouchStart(event) { allowMouseUpTriggerRef.current = false; if (event.touches.length === 1) { event.stopPropagation(); const touch = event.touches[0]; touchPositionRef.current = { x: touch.clientX, y: touch.clientY }; longPressTimeout.start(LONG_PRESS_DELAY, () => { if (touchPositionRef.current) { handleLongPress(touchPositionRef.current.x, touchPositionRef.current.y, event.nativeEvent); } }); } } function handleTouchMove(event) { if (longPressTimeout.isStarted() && touchPositionRef.current && event.touches.length === 1) { const touch = event.touches[0]; const moveThreshold = 10; const deltaX = Math.abs(touch.clientX - touchPositionRef.current.x); const deltaY = Math.abs(touch.clientY - touchPositionRef.current.y); if (deltaX > moveThreshold || deltaY > moveThreshold) { longPressTimeout.clear(); } } } function handleTouchEnd() { longPressTimeout.clear(); touchPositionRef.current = null; } React.useEffect(() => { function handleDocumentContextMenu(event) { const target = (0, _utils.getTarget)(event); const targetElement = target; if ((0, _utils.contains)(triggerRef.current, targetElement) || (0, _utils.contains)(internalBackdropRef.current, targetElement) || (0, _utils.contains)(backdropRef.current, targetElement)) { event.preventDefault(); } } const doc = (0, _owner.ownerDocument)(triggerRef.current); doc.addEventListener('contextmenu', handleDocumentContextMenu); return () => { doc.removeEventListener('contextmenu', handleDocumentContextMenu); }; }, [backdropRef, internalBackdropRef]); const state = React.useMemo(() => ({ open }), [open]); const element = (0, _useRenderElement.useRenderElement)('div', componentProps, { state, ref: [triggerRef, forwardedRef], props: [{ onContextMenu: handleContextMenu, onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, onTouchCancel: handleTouchEnd, style: { WebkitTouchCallout: 'none' } }, elementProps], stateAttributesMapping: _popupStateMapping.pressableTriggerOpenStateMapping }); return element; }); if (process.env.NODE_ENV !== "production") ContextMenuTrigger.displayName = "ContextMenuTrigger";