1 | import PropTypes from 'prop-types';
|
2 | import React, { useCallback, useEffect, useRef, } from 'react';
|
3 | import scrollIntoView from 'scroll-into-view-if-needed';
|
4 | import { useTypeaheadContext } from '../core/Context';
|
5 | import { getDisplayName, getMenuItemId, preventInputBlur } from '../utils';
|
6 | import { optionType } from '../propTypes';
|
7 | const propTypes = {
|
8 | option: optionType.isRequired,
|
9 | position: PropTypes.number,
|
10 | };
|
11 | export function useItem({ label, onClick, option, position, ...props }) {
|
12 | const { activeIndex, id, isOnlyResult, onActiveItemChange, onInitialItemChange, onMenuItemClick, setItem, } = useTypeaheadContext();
|
13 | const itemRef = useRef(null);
|
14 | useEffect(() => {
|
15 | if (position === 0) {
|
16 | onInitialItemChange(option);
|
17 | }
|
18 | });
|
19 | useEffect(() => {
|
20 | if (position === activeIndex) {
|
21 | onActiveItemChange(option);
|
22 | const node = itemRef.current;
|
23 | node &&
|
24 | scrollIntoView(node, {
|
25 | block: 'nearest',
|
26 | boundary: node.parentNode,
|
27 | inline: 'nearest',
|
28 | scrollMode: 'if-needed',
|
29 | });
|
30 | }
|
31 | });
|
32 | const handleClick = useCallback((e) => {
|
33 | onMenuItemClick(option, e);
|
34 | onClick && onClick(e);
|
35 | }, [onClick, onMenuItemClick, option]);
|
36 | const active = isOnlyResult || activeIndex === position;
|
37 | setItem(option, position);
|
38 | return {
|
39 | ...props,
|
40 | active,
|
41 | 'aria-label': label,
|
42 | 'aria-selected': active,
|
43 | id: getMenuItemId(id, position),
|
44 | onClick: handleClick,
|
45 | onMouseDown: preventInputBlur,
|
46 | ref: itemRef,
|
47 | role: 'option',
|
48 | };
|
49 | }
|
50 | export function withItem(Component) {
|
51 | const WrappedMenuItem = (props) => (React.createElement(Component, { ...props, ...useItem(props) }));
|
52 | WrappedMenuItem.displayName = `withItem(${getDisplayName(Component)})`;
|
53 | WrappedMenuItem.propTypes = propTypes;
|
54 | return WrappedMenuItem;
|
55 | }
|