@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
JavaScript
;
'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";