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.

95 lines (92 loc) 3.39 kB
"use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.useInteractions = useInteractions; var React = _interopRequireWildcard(require("react")); var _constants = require("../utils/constants"); /** * Merges an array of interaction hooks' props into prop getters, allowing * event handler functions to be composed together without overwriting one * another. * @see https://floating-ui.com/docs/useInteractions */ function useInteractions(propsList = []) { const referenceDeps = propsList.map(key => key?.reference); const floatingDeps = propsList.map(key => key?.floating); const itemDeps = propsList.map(key => key?.item); const triggerDeps = propsList.map(key => key?.trigger); const getReferenceProps = React.useCallback(userProps => mergeProps(userProps, propsList, 'reference'), // eslint-disable-next-line react-hooks/exhaustive-deps referenceDeps); const getFloatingProps = React.useCallback(userProps => mergeProps(userProps, propsList, 'floating'), // eslint-disable-next-line react-hooks/exhaustive-deps floatingDeps); const getItemProps = React.useCallback(userProps => mergeProps(userProps, propsList, 'item'), // eslint-disable-next-line react-hooks/exhaustive-deps itemDeps); const getTriggerProps = React.useCallback(userProps => mergeProps(userProps, propsList, 'trigger'), // eslint-disable-next-line react-hooks/exhaustive-deps triggerDeps); return React.useMemo(() => ({ getReferenceProps, getFloatingProps, getItemProps, getTriggerProps }), [getReferenceProps, getFloatingProps, getItemProps, getTriggerProps]); } /* eslint-disable guard-for-in */ function mergeProps(userProps, propsList, elementKey) { const eventHandlers = new Map(); const isItem = elementKey === 'item'; const outputProps = {}; if (elementKey === 'floating') { outputProps.tabIndex = -1; outputProps[_constants.FOCUSABLE_ATTRIBUTE] = ''; } for (const key in userProps) { if (isItem && userProps) { if (key === _constants.ACTIVE_KEY || key === _constants.SELECTED_KEY) { continue; } } outputProps[key] = userProps[key]; } for (let i = 0; i < propsList.length; i += 1) { let props; const propsOrGetProps = propsList[i]?.[elementKey]; if (typeof propsOrGetProps === 'function') { props = userProps ? propsOrGetProps(userProps) : null; } else { props = propsOrGetProps; } if (!props) { continue; } mutablyMergeProps(outputProps, props, isItem, eventHandlers); } mutablyMergeProps(outputProps, userProps, isItem, eventHandlers); return outputProps; } function mutablyMergeProps(outputProps, props, isItem, eventHandlers) { for (const key in props) { const value = props[key]; if (isItem && (key === _constants.ACTIVE_KEY || key === _constants.SELECTED_KEY)) { continue; } if (!key.startsWith('on')) { outputProps[key] = value; } else { if (!eventHandlers.has(key)) { eventHandlers.set(key, []); } if (typeof value === 'function') { eventHandlers.get(key)?.push(value); outputProps[key] = (...args) => { return eventHandlers.get(key)?.map(fn => fn(...args)).find(val => val !== undefined); }; } } } }