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.

69 lines (67 loc) 2.65 kB
'use client'; import * as React from 'react'; import { useAnimationFrame } from '@base-ui-components/utils/useAnimationFrame'; import { EMPTY_OBJECT } from "../../utils/constants.js"; import { isMouseLikePointerType } from "../utils.js"; /** * Opens or closes the floating element when clicking the reference element. * @see https://floating-ui.com/docs/useClick */ export function useClick(context, props = {}) { const { open, onOpenChange, dataRef } = context; const { enabled = true, event: eventOption = 'click', toggle = true, ignoreMouse = false, stickIfOpen = true } = props; const pointerTypeRef = React.useRef(undefined); const frame = useAnimationFrame(); const reference = React.useMemo(() => ({ onPointerDown(event) { pointerTypeRef.current = event.pointerType; }, onMouseDown(event) { const pointerType = pointerTypeRef.current; const nativeEvent = event.nativeEvent; // Ignore all buttons except for the "main" button. // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button if (event.button !== 0 || eventOption === 'click' || isMouseLikePointerType(pointerType, true) && ignoreMouse) { return; } const openEvent = dataRef.current.openEvent; const openEventType = openEvent?.type; const nextOpen = !(open && toggle && (openEvent && stickIfOpen ? openEventType === 'click' || openEventType === 'mousedown' : true)); // Wait until focus is set on the element. This is an alternative to // `event.preventDefault()` to avoid :focus-visible from appearing when using a pointer. frame.request(() => { onOpenChange(nextOpen, nativeEvent, 'click'); }); }, onClick(event) { const pointerType = pointerTypeRef.current; if (eventOption === 'mousedown' && pointerType) { pointerTypeRef.current = undefined; return; } if (isMouseLikePointerType(pointerType, true) && ignoreMouse) { return; } const openEvent = dataRef.current.openEvent; const openEventType = openEvent?.type; const nextOpen = !(open && toggle && (openEvent && stickIfOpen ? openEventType === 'click' || openEventType === 'mousedown' || openEventType === 'keydown' || openEventType === 'keyup' : true)); onOpenChange(nextOpen, event.nativeEvent, 'click'); }, onKeyDown() { pointerTypeRef.current = undefined; } }), [dataRef, eventOption, ignoreMouse, onOpenChange, open, stickIfOpen, toggle, frame]); return React.useMemo(() => enabled ? { reference } : EMPTY_OBJECT, [enabled, reference]); }