@wordpress/components
Version:
UI components for WordPress.
128 lines (114 loc) • 2.74 kB
JavaScript
/**
* External dependencies
*/
import { noop } from 'lodash';
// eslint-disable-next-line no-restricted-imports
import { PopoverDisclosure, usePopoverState, Portal } from 'reakit';
/**
* WordPress dependencies
*/
import { useCallback, useMemo, cloneElement } from '@wordpress/element';
/**
* Internal dependencies
*/
import { contextConnect, useContextSystem } from '../context';
import { PopoverContext } from './context';
import { usePopoverResizeUpdater } from './utils';
import PopoverContent from './content';
import { useUpdateEffect } from '../../utils/hooks';
/**
*
* @param {import('../context').ViewOwnProps<import('./types').Props, 'div'>} props
* @param {import('react').Ref<any>} forwardedRef
*/
function Popover( props, forwardedRef ) {
const {
animated = true,
animationDuration = 160,
baseId,
children,
elevation = 5,
id,
label,
maxWidth = 360,
onVisibleChange = noop,
placement,
state,
trigger,
visible,
...otherProps
} = useContextSystem( props, 'Popover' );
const _popover = usePopoverState( {
animated: animated ? animationDuration : undefined,
baseId: baseId || id,
placement,
visible,
...otherProps,
} );
const popover = state || _popover;
const resizeListener = usePopoverResizeUpdater( {
onResize: popover.unstable_update,
} );
const uniqueId = `popover-${ popover.baseId }`;
const labelId = label || uniqueId;
const contextProps = useMemo(
() => ( {
label: labelId,
popover,
} ),
[ labelId, popover ]
);
const triggerContent = useCallback(
( triggerProps ) => {
return cloneElement( trigger, triggerProps );
},
[ trigger ]
);
useUpdateEffect( () => {
onVisibleChange( popover.visible );
}, [ popover.visible ] );
return (
<PopoverContext.Provider value={ contextProps }>
{ trigger && (
<PopoverDisclosure
{ ...popover }
ref={ trigger.ref }
{ ...trigger.props }
>
{ triggerContent }
</PopoverDisclosure>
) }
<Portal>
<PopoverContent
ref={ forwardedRef }
{ ...otherProps }
elevation={ elevation }
maxWidth={ maxWidth }
>
{ resizeListener }
{ children }
</PopoverContent>
</Portal>
</PopoverContext.Provider>
);
}
/**
* `Popover` is a component to render a floating content modal.
* It is similar in purpose to a tooltip, but renders content of any sort,
* not only simple text.
*
* @example
* ```jsx
* import { Button, Popover, View, Text } from `@wordpress/components/ui`;
* function Example() {
* return (
* <Popover trigger={ <Button>Popover</Button> }>
* <View>
* <Text>Code is Poetry</Text>
* </View>
* </Popover>
* );
* }
* ```
*/
export default contextConnect( Popover, 'Popover' );