@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
JavaScript
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;