UNPKG

@atlaskit/inline-dialog

Version:

An inline dialog is a pop-up container for small amounts of information. It can also contain controls.

190 lines (182 loc) 7.02 kB
import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { bind } from 'bind-event-listener'; import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next'; import noop from '@atlaskit/ds-lib/noop'; import { Layering, useCloseOnEscapePress, useLayering } from '@atlaskit/layering'; import { Manager, Popper, Reference } from '@atlaskit/popper'; import { Container } from './styled/container'; var _checkIsChildOfPortal = function checkIsChildOfPortal(node) { if (!node) { return false; } return node.classList && node.classList.contains('atlaskit-portal-container') || _checkIsChildOfPortal(node.parentElement); }; // Close manager for layering var CloseManager = function CloseManager(_ref) { var handleEscapeClose = _ref.handleEscapeClose, handleClick = _ref.handleClick; useCloseOnEscapePress({ onClose: handleEscapeClose }); var _useLayering = useLayering(), isLayerDisabled = _useLayering.isLayerDisabled; useEffect(function () { return bind(window, { type: 'click', listener: function listener(e) { if (isLayerDisabled()) { return; } handleClick(e); }, options: { capture: true } }); }, [handleClick, isLayerDisabled]); // only create a dummy component for using ths hook in class component return /*#__PURE__*/React.createElement("span", null); }; var InlineDialog = /*#__PURE__*/memo(function InlineDialog(_ref2) { var _ref2$isOpen = _ref2.isOpen, isOpen = _ref2$isOpen === void 0 ? false : _ref2$isOpen, _ref2$onContentBlur = _ref2.onContentBlur, onContentBlur = _ref2$onContentBlur === void 0 ? noop : _ref2$onContentBlur, _ref2$onContentClick = _ref2.onContentClick, onContentClick = _ref2$onContentClick === void 0 ? noop : _ref2$onContentClick, _ref2$onContentFocus = _ref2.onContentFocus, onContentFocus = _ref2$onContentFocus === void 0 ? noop : _ref2$onContentFocus, _ref2$onClose = _ref2.onClose, providedOnClose = _ref2$onClose === void 0 ? noop : _ref2$onClose, _ref2$placement = _ref2.placement, placement = _ref2$placement === void 0 ? 'bottom-start' : _ref2$placement, _ref2$strategy = _ref2.strategy, strategy = _ref2$strategy === void 0 ? 'fixed' : _ref2$strategy, testId = _ref2.testId, content = _ref2.content, children = _ref2.children, fallbackPlacements = _ref2.fallbackPlacements; var containerRef = useRef(null); var triggerRef = useRef(null); var onClose = usePlatformLeafEventHandler({ fn: function fn(event) { return providedOnClose(event); }, action: 'closed', componentName: 'inlineDialog', packageName: "@atlaskit/inline-dialog", packageVersion: "0.0.0-development" }); // we put this into a ref to avoid handleCloseRequest having this as a dependency var onCloseRef = useRef(onClose); useEffect(function () { onCloseRef.current = onClose; }); var handleCloseRequest = useCallback(function (event) { var target = event.target; // checks for when target is not HTMLElement if (!(target instanceof HTMLElement)) { return; } // TODO: This is to handle the case where the target is no longer in the DOM. // This happens with react-select in datetime picker. There might be other // edge cases for this. if (!document.body.contains(target)) { return; } if (isOpen) { var _onCloseRef$current; (_onCloseRef$current = onCloseRef.current) === null || _onCloseRef$current === void 0 || _onCloseRef$current.call(onCloseRef, { isOpen: false, event: event }); return; } // handles the case where inline dialog opens portalled elements such as modal if (_checkIsChildOfPortal(target)) { return; } // call onClose if the click originated from outside the dialog if (containerRef.current && !containerRef.current.contains(target)) { var _onCloseRef$current2; (_onCloseRef$current2 = onCloseRef.current) === null || _onCloseRef$current2 === void 0 || _onCloseRef$current2.call(onCloseRef, { isOpen: false, event: event }); } }, [isOpen]); var handleClick = useCallback(function (event) { var _containerRef$current; // exit if we click outside but on the trigger — it can handle the clicks itself if (triggerRef.current && triggerRef.current.contains(event.target)) { return; } if ((_containerRef$current = containerRef.current) !== null && _containerRef$current !== void 0 && _containerRef$current.contains(event.target)) { return; } handleCloseRequest(event); }, [handleCloseRequest, containerRef, triggerRef]); /** * Auto-flip is enabled by default in `@atlaskit/popper` and * the `InlineDialog` API does not allow disabling it. * * We only need to override it if there are `fallbackPlacements` specified. */ var modifiers = useMemo(function () { return fallbackPlacements ? [{ name: 'flip', options: { fallbackPlacements: fallbackPlacements } }] : []; }, [fallbackPlacements]); var popper = isOpen ? /*#__PURE__*/React.createElement(Popper, { placement: placement, strategy: strategy, modifiers: modifiers }, function (_ref3) { var _ref4 = _ref3.ref, style = _ref3.style; return /*#__PURE__*/React.createElement(Container, { onBlur: onContentBlur, onFocus: onContentFocus, onClick: onContentClick, ref: function ref(node) { if (node) { containerRef.current = node; if (typeof _ref4 === 'function') { _ref4(node); } else { _ref4.current = node; } } } // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766 , style: style, testId: testId }, typeof content === 'function' ? content() : content); }) : null; return /*#__PURE__*/React.createElement(Manager, null, /*#__PURE__*/React.createElement(Reference, null, function (_ref5) { var _ref6 = _ref5.ref; return /*#__PURE__*/React.createElement("div", { ref: function ref(node) { // Resolve to the first element child of the div in `children` var firstElementChild = (node === null || node === void 0 ? void 0 : node.firstElementChild) || null; triggerRef.current = firstElementChild; if (typeof _ref6 === 'function') { _ref6(firstElementChild); } else { _ref6.current = firstElementChild; } } }, /*#__PURE__*/React.createElement(React.Fragment, null, children)); }), isOpen ? /*#__PURE__*/React.createElement(Layering, { isDisabled: false }, popper, /*#__PURE__*/React.createElement(CloseManager, { handleEscapeClose: handleCloseRequest, handleClick: handleClick })) : popper); }); // eslint-disable-next-line @repo/internal/react/require-jsdoc export default InlineDialog;