@awsui/components-react
Version:
On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en
77 lines (76 loc) • 4.05 kB
JavaScript
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useLayoutEffect, useRef } from 'react';
import clsx from 'clsx';
import { nodeContains } from '@awsui/component-toolkit/dom';
import { useResizeObserver } from '@awsui/component-toolkit/internal';
import { useVisualRefresh } from '../internal/hooks/use-visual-mode';
import usePopoverPosition from './use-popover-position.js';
import styles from './styles.css.js';
export default function PopoverContainer({ position, trackRef, trackKey, arrow, children, zIndex, renderWithPortal, size, fixedWidth, variant, keepPosition, allowScrollToFit, allowVerticalOverflow, hideOnOverscroll, className, }) {
const bodyRef = useRef(null);
const contentRef = useRef(null);
const popoverRef = useRef(null);
const arrowRef = useRef(null);
const isRefresh = useVisualRefresh();
// Updates the position handler.
const { updatePositionHandler, popoverStyle, internalPosition, positionHandlerRef, isOverscrolling } = usePopoverPosition({
popoverRef,
bodyRef,
arrowRef,
trackRef,
contentRef,
allowScrollToFit,
allowVerticalOverflow,
preferredPosition: position,
renderWithPortal,
keepPosition,
hideOnOverscroll,
});
// Recalculate position when properties change.
useLayoutEffect(() => {
updatePositionHandler();
}, [updatePositionHandler, trackKey]);
// Recalculate position when content size changes.
useResizeObserver(contentRef, () => {
updatePositionHandler(true);
});
// Recalculate position on DOM events.
useLayoutEffect(() => {
/*
This is a heuristic. Some layout changes are caused by user clicks (e.g. toggling the tools panel, submitting a form),
and by tracking the click event we can adapt the popover's position to the new layout.
TODO: extend this to Enter and Spacebar?
*/
const onClick = (event) => {
if (
// Do not update position if keepPosition is true.
keepPosition ||
// If the click was on the trigger, this will make the popover appear or disappear,
// so no need to update its position either in this case.
nodeContains(trackRef.current, event.target)) {
return;
}
requestAnimationFrame(() => {
updatePositionHandler();
});
};
const updatePositionOnResize = () => requestAnimationFrame(() => updatePositionHandler());
const refreshPosition = () => requestAnimationFrame(() => positionHandlerRef.current());
const controller = new AbortController();
window.addEventListener('click', onClick, { signal: controller.signal });
window.addEventListener('resize', updatePositionOnResize, { signal: controller.signal });
window.addEventListener('scroll', refreshPosition, { capture: true, signal: controller.signal });
return () => {
controller.abort();
};
}, [hideOnOverscroll, keepPosition, positionHandlerRef, trackRef, updatePositionHandler]);
return isOverscrolling ? null : (React.createElement("div", { ref: popoverRef, style: Object.assign(Object.assign({}, popoverStyle), { zIndex }), className: clsx(styles.container, isRefresh && styles.refresh, className) },
React.createElement("div", { ref: arrowRef, className: clsx(styles[`container-arrow`], styles[`container-arrow-position-${internalPosition}`]), "aria-hidden": true }, arrow(internalPosition)),
React.createElement("div", { ref: bodyRef, className: clsx(styles['container-body'], styles[`container-body-size-${size}`], {
[styles['fixed-width']]: fixedWidth,
[styles[`container-body-variant-${variant}`]]: variant,
}) },
React.createElement("div", { ref: contentRef }, children))));
}
//# sourceMappingURL=container.js.map