120 lines
3.2 KiB
TypeScript
120 lines
3.2 KiB
TypeScript
/**
|
|
* External dependencies
|
|
*/
|
|
import { Popover } from '@wordpress/components';
|
|
import classnames from 'classnames';
|
|
import {
|
|
createElement,
|
|
useEffect,
|
|
useRef,
|
|
createPortal,
|
|
Children,
|
|
useLayoutEffect,
|
|
} from '@wordpress/element';
|
|
|
|
/**
|
|
* Internal dependencies
|
|
*/
|
|
import { getMenuPropsType } from './types';
|
|
|
|
type MenuProps = {
|
|
children?: JSX.Element | JSX.Element[];
|
|
getMenuProps: getMenuPropsType;
|
|
isOpen: boolean;
|
|
className?: string;
|
|
position?: Popover.Position;
|
|
scrollIntoViewOnOpen?: boolean;
|
|
};
|
|
|
|
export const Menu = ( {
|
|
children,
|
|
getMenuProps,
|
|
isOpen,
|
|
className,
|
|
position = 'bottom right',
|
|
scrollIntoViewOnOpen = false,
|
|
}: MenuProps ) => {
|
|
const selectControlMenuRef = useRef< HTMLDivElement >( null );
|
|
const popoverRef = useRef< HTMLDivElement >( null );
|
|
|
|
useLayoutEffect( () => {
|
|
const comboboxWrapper = selectControlMenuRef.current?.closest(
|
|
'.woocommerce-experimental-select-control__combo-box-wrapper'
|
|
);
|
|
const popoverContent =
|
|
popoverRef.current?.querySelector< HTMLDivElement >(
|
|
'.components-popover__content'
|
|
);
|
|
if ( comboboxWrapper && comboboxWrapper?.clientWidth > 0 ) {
|
|
if ( popoverContent ) {
|
|
popoverContent.style.width = `${
|
|
comboboxWrapper.getBoundingClientRect().width
|
|
}px`;
|
|
}
|
|
}
|
|
}, [
|
|
selectControlMenuRef.current,
|
|
selectControlMenuRef.current?.clientWidth,
|
|
popoverRef.current,
|
|
] );
|
|
|
|
// Scroll the selected item into view when the menu opens.
|
|
useEffect( () => {
|
|
if ( isOpen && scrollIntoViewOnOpen ) {
|
|
selectControlMenuRef.current?.scrollIntoView();
|
|
}
|
|
}, [ isOpen, scrollIntoViewOnOpen ] );
|
|
|
|
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events */
|
|
/* Disabled because of the onmouseup on the ul element below. */
|
|
return (
|
|
<div
|
|
ref={ selectControlMenuRef }
|
|
className="woocommerce-experimental-select-control__menu"
|
|
>
|
|
<div>
|
|
<Popover
|
|
// @ts-expect-error this prop does exist, see: https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/popover/index.tsx#L180.
|
|
__unstableSlotName="woocommerce-select-control-menu"
|
|
focusOnMount={ false }
|
|
className={ classnames(
|
|
'woocommerce-experimental-select-control__popover-menu',
|
|
{
|
|
'is-open': isOpen,
|
|
'has-results': Children.count( children ) > 0,
|
|
}
|
|
) }
|
|
position={ position }
|
|
animate={ false }
|
|
resize={ false }
|
|
ref={ popoverRef }
|
|
>
|
|
<ul
|
|
{ ...getMenuProps() }
|
|
className={ classnames(
|
|
'woocommerce-experimental-select-control__popover-menu-container',
|
|
className
|
|
) }
|
|
onMouseUp={ ( e ) =>
|
|
// Fix to prevent select control dropdown from closing when selecting within the Popover.
|
|
e.stopPropagation()
|
|
}
|
|
>
|
|
{ isOpen && children }
|
|
</ul>
|
|
</Popover>
|
|
</div>
|
|
</div>
|
|
);
|
|
/* eslint-enable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events */
|
|
};
|
|
|
|
export const MenuSlot: React.FC = () =>
|
|
createPortal(
|
|
<div aria-live="off">
|
|
{ /* @ts-expect-error name does exist on PopoverSlot see: https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/popover/index.tsx#L555 */ }
|
|
<Popover.Slot name="woocommerce-select-control-menu" />
|
|
</div>,
|
|
document.body
|
|
);
|