@shopify/polaris
Version:
Shopify’s product component library
115 lines (99 loc) • 4.44 kB
JavaScript
import { objectWithoutProperties as _objectWithoutProperties } from '../../_virtual/_rollupPluginBabelHelpers.js';
import React$1, { useState, useRef, useCallback, useEffect, Children } from 'react';
import { useUniqueId } from '../../utilities/unique-id/hooks.js';
import { portal } from '../shared.js';
import { findFirstFocusableNodeIncludingDisabled, focusNextFocusableNode } from '../../utilities/focus.js';
import { Portal as Portal$1 } from '../Portal/Portal.js';
import { Section as Section$1 } from './components/Section/Section.js';
import { Pane as Pane$1 } from './components/Pane/Pane.js';
import { PopoverOverlay as PopoverOverlay$1, PopoverCloseSource } from './components/PopoverOverlay/PopoverOverlay.js';
export { PopoverCloseSource } from './components/PopoverOverlay/PopoverOverlay.js';
import { setActivatorAttributes as setActivatorAttributes$1 } from './set-activator-attributes.js';
// TypeScript can't generate types that correctly infer the typing of
// subcomponents so explicitly state the subcomponents in the type definition.
// Letting this be implicit works in this project but fails in projects that use
// generated *.d.ts files.
const Popover = function Popover(_ref) {
let {
activatorWrapper = 'div',
children,
onClose,
activator,
preventFocusOnClose,
active,
fixed,
ariaHaspopup,
preferInputActivator = true,
colorScheme
} = _ref,
rest = _objectWithoutProperties(_ref, ["activatorWrapper", "children", "onClose", "activator", "preventFocusOnClose", "active", "fixed", "ariaHaspopup", "preferInputActivator", "colorScheme"]);
const [activatorNode, setActivatorNode] = useState();
const activatorContainer = useRef(null);
const WrapperComponent = activatorWrapper;
const id = useUniqueId('popover');
const setAccessibilityAttributes = useCallback(() => {
if (activatorContainer.current == null) {
return;
}
const firstFocusable = findFirstFocusableNodeIncludingDisabled(activatorContainer.current);
const focusableActivator = firstFocusable || activatorContainer.current;
const activatorDisabled = 'disabled' in focusableActivator && Boolean(focusableActivator.disabled);
setActivatorAttributes$1(focusableActivator, {
id,
active,
ariaHaspopup,
activatorDisabled
});
}, [id, active, ariaHaspopup]);
const handleClose = source => {
onClose(source);
if (activatorContainer.current == null || preventFocusOnClose) {
return;
}
if ((source === PopoverCloseSource.FocusOut || source === PopoverCloseSource.EscapeKeypress) && activatorNode) {
const focusableActivator = findFirstFocusableNodeIncludingDisabled(activatorNode) || findFirstFocusableNodeIncludingDisabled(activatorContainer.current) || activatorContainer.current;
if (!focusNextFocusableNode(focusableActivator, isInPortal)) {
focusableActivator.focus();
}
}
};
useEffect(() => {
if (!activatorNode && activatorContainer.current) {
setActivatorNode(activatorContainer.current.firstElementChild);
} else if (activatorNode && activatorContainer.current && !activatorContainer.current.contains(activatorNode)) {
setActivatorNode(activatorContainer.current.firstElementChild);
}
setAccessibilityAttributes();
}, [activatorNode, setAccessibilityAttributes]);
useEffect(() => {
if (activatorNode && activatorContainer.current) {
setActivatorNode(activatorContainer.current.firstElementChild);
}
setAccessibilityAttributes();
}, [activatorNode, setAccessibilityAttributes]);
const portal = activatorNode ? /*#__PURE__*/React$1.createElement(Portal$1, {
idPrefix: "popover"
}, /*#__PURE__*/React$1.createElement(PopoverOverlay$1, Object.assign({
id: id,
activator: activatorNode,
preferInputActivator: preferInputActivator,
onClose: handleClose,
active: active,
fixed: fixed,
colorScheme: colorScheme
}, rest), children)) : null;
return /*#__PURE__*/React$1.createElement(WrapperComponent, {
ref: activatorContainer
}, Children.only(activator), portal);
};
function isInPortal(element) {
let parentElement = element.parentElement;
while (parentElement) {
if (parentElement.matches(portal.selector)) return false;
parentElement = parentElement.parentElement;
}
return true;
}
Popover.Pane = Pane$1;
Popover.Section = Section$1;
export { Popover };