@appbuckets/react-ui-core
Version:
Core utilities built for AppBuckets React UI Framework
458 lines (452 loc) • 13.2 kB
JavaScript
;
var tslib = require('tslib');
var React = require('react');
var index = require('../useAutoControlledValue/index.js');
var index$1 = require('../useDOMElementEvent/index.js');
var Ref = require('../Ref/Ref.js');
var refUtils = require('../utils/refUtils.js');
var doesNodeContainClick = require('../utils/doesNodeContainClick.js');
var PortalInner = require('./PortalInner.js');
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(
n,
k,
d.get
? d
: {
enumerable: true,
get: function () {
return e[k];
},
}
);
}
});
}
n['default'] = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/ _interopNamespace(React);
var Portal = function (props) {
var _a, _b, _c, _d, _e;
var children = props.children,
closeOnDocumentClick = props.closeOnDocumentClick,
closeOnEscape = props.closeOnEscape,
closeOnPortalMouseLeave = props.closeOnPortalMouseLeave,
closeOnTriggerBlur = props.closeOnTriggerBlur,
closeOnTriggerClick = props.closeOnTriggerClick,
closeOnTriggerMouseLeave = props.closeOnTriggerMouseLeave,
userDefinedDefaultOpen = props.defaultOpen,
mountNode = props.mountNode,
mouseEnterDelay = props.mouseEnterDelay,
mouseLeaveDelay = props.mouseLeaveDelay,
userDefinedOpen = props.open,
openOnTriggerClick = props.openOnTriggerClick,
openOnTriggerFocus = props.openOnTriggerFocus,
openOnTriggerMouseEnter = props.openOnTriggerMouseEnter,
trigger = props.trigger,
userDefinedTriggerRef = props.triggerRef,
userDefinedOnCloseHandler = props.onClose,
userDefinedOnMountHandler = props.onMount,
userDefinedOnOpenHandler = props.onOpen,
userDefinedOnUnmountHandler = props.onUnmount;
// ----
// AutoControlled Props
// ----
var _f = tslib.__read(
index(false, {
defaultProp: userDefinedDefaultOpen,
prop: userDefinedOpen,
}),
2
),
open = _f[0],
trySetOpen = _f[1];
// ----
// DOM Ref
// ----
var contentRef = React__namespace.useRef();
var triggerRef = React__namespace.useRef();
// ----
// Internal Variables
// ----
var latestDocumentMouseEvent = React__namespace.useRef(null);
var mouseEnterTimer = React__namespace.useRef(null);
var mouseLeaveTimer = React__namespace.useRef(null);
// ----
// Lifecycle event to cancel timer
// ----
var clearMouseEnterTimer = React__namespace.useCallback(function () {
if (mouseEnterTimer.current) {
clearTimeout(mouseEnterTimer.current);
mouseEnterTimer.current = null;
}
}, []);
var clearMouseLeaveTimer = React__namespace.useCallback(function () {
if (mouseEnterTimer.current) {
clearTimeout(mouseEnterTimer.current);
mouseEnterTimer.current = null;
}
}, []);
React__namespace.useEffect(
function () {
return function () {
clearMouseEnterTimer();
clearMouseLeaveTimer();
};
},
[clearMouseEnterTimer, clearMouseLeaveTimer]
);
// ----
// Portal Control
// ----
var openPortal = React__namespace.useCallback(
function (e) {
/** Avoid event propagation */
e.stopPropagation();
if (typeof userDefinedOnOpenHandler === 'function') {
userDefinedOnOpenHandler(e);
}
trySetOpen(true);
},
[userDefinedOnOpenHandler, trySetOpen]
);
var openPortalWithTimeout = React__namespace.useCallback(
function (e) {
return setTimeout(function () {
return openPortal(e);
}, mouseEnterDelay);
},
[openPortal, mouseEnterDelay]
);
var closePortal = React__namespace.useCallback(
function (e) {
/** Avoid event propagation */
e.stopPropagation();
if (typeof userDefinedOnCloseHandler === 'function') {
userDefinedOnCloseHandler(e);
}
trySetOpen(false);
},
[userDefinedOnCloseHandler, trySetOpen]
);
var closePortalWithTimeout = React__namespace.useCallback(
function (e) {
return setTimeout(
function () {
return closePortal(e);
},
mouseLeaveDelay !== null && mouseLeaveDelay !== void 0
? mouseLeaveDelay
: 0
);
},
[closePortal, mouseLeaveDelay]
);
// ----
// Trigger Handler
// ----
var handleTriggerRef = function (component) {
triggerRef.current = component;
refUtils.handleRef(userDefinedTriggerRef, component);
};
var triggerOnBlurHandler =
(_a = trigger === null || trigger === void 0 ? void 0 : trigger.props) ===
null || _a === void 0
? void 0
: _a.onBlur;
var handleTriggerBlur = React__namespace.useCallback(
function (e) {
var _a;
var rest = [];
for (var _i = 1; _i < arguments.length; _i++) {
rest[_i - 1] = arguments[_i];
}
/** Invoke original trigger event handler */
if (typeof triggerOnBlurHandler === 'function') {
triggerOnBlurHandler.apply(
void 0,
tslib.__spreadArray([e], tslib.__read(rest), false)
);
}
/** Do not close if focus is given to the portal */
var target = e.relatedTarget || document.activeElement;
var didFocusPortal =
(_a = contentRef.current) === null || _a === void 0
? void 0
: _a.contains(target);
if (!closeOnTriggerBlur || didFocusPortal) {
return;
}
closePortal(e);
},
[triggerOnBlurHandler, closeOnTriggerBlur, closePortal]
);
var triggerOnClickHandler =
(_b = trigger === null || trigger === void 0 ? void 0 : trigger.props) ===
null || _b === void 0
? void 0
: _b.onClick;
var handleTriggerClick = React__namespace.useCallback(
function (e) {
var rest = [];
for (var _i = 1; _i < arguments.length; _i++) {
rest[_i - 1] = arguments[_i];
}
/** Invoke original trigger event handler */
if (typeof triggerOnClickHandler === 'function') {
triggerOnClickHandler.apply(
void 0,
tslib.__spreadArray([e], tslib.__read(rest), false)
);
}
/** Toggle the Portal */
if (open && closeOnTriggerClick) {
closePortal(e);
} else if (!open && openOnTriggerClick) {
openPortal(e);
}
},
[
closeOnTriggerClick,
closePortal,
open,
openOnTriggerClick,
openPortal,
triggerOnClickHandler,
]
);
var triggerOnFocusHandler =
(_c = trigger === null || trigger === void 0 ? void 0 : trigger.props) ===
null || _c === void 0
? void 0
: _c.onFocus;
var handleTriggerFocus = React__namespace.useCallback(
function (e) {
var rest = [];
for (var _i = 1; _i < arguments.length; _i++) {
rest[_i - 1] = arguments[_i];
}
/** Invoke original trigger event handler */
if (typeof triggerOnFocusHandler === 'function') {
triggerOnFocusHandler.apply(
void 0,
tslib.__spreadArray([e], tslib.__read(rest), false)
);
}
if (!openOnTriggerFocus) {
return;
}
openPortal(e);
},
[openOnTriggerFocus, openPortal, triggerOnFocusHandler]
);
var triggerOnMouseEnterHandler =
(_d = trigger === null || trigger === void 0 ? void 0 : trigger.props) ===
null || _d === void 0
? void 0
: _d.onMouseEnter;
var handleTriggerMouseEnter = React__namespace.useCallback(
function (e) {
var rest = [];
for (var _i = 1; _i < arguments.length; _i++) {
rest[_i - 1] = arguments[_i];
}
/** Remove mouse leave timer */
clearMouseLeaveTimer();
/** Invoke original trigger event handler */
if (typeof triggerOnMouseEnterHandler === 'function') {
triggerOnMouseEnterHandler.apply(
void 0,
tslib.__spreadArray([e], tslib.__read(rest), false)
);
}
if (!openOnTriggerMouseEnter) {
return;
}
mouseEnterTimer.current = openPortalWithTimeout(e);
},
[
clearMouseLeaveTimer,
openOnTriggerMouseEnter,
openPortalWithTimeout,
triggerOnMouseEnterHandler,
]
);
var triggerOnMouseLeaveHandler =
(_e = trigger === null || trigger === void 0 ? void 0 : trigger.props) ===
null || _e === void 0
? void 0
: _e.onMouseLeave;
var handleTriggerMouseLeave = React__namespace.useCallback(
function (e) {
var rest = [];
for (var _i = 1; _i < arguments.length; _i++) {
rest[_i - 1] = arguments[_i];
}
/** Remove mouse enter timer */
clearMouseEnterTimer();
/** Invoke original trigger event handler */
if (typeof triggerOnMouseLeaveHandler === 'function') {
triggerOnMouseLeaveHandler.apply(
void 0,
tslib.__spreadArray([e], tslib.__read(rest), false)
);
}
if (!closeOnTriggerMouseLeave) {
return;
}
mouseLeaveTimer.current = closePortalWithTimeout(e);
},
[
clearMouseEnterTimer,
closeOnTriggerMouseLeave,
closePortalWithTimeout,
triggerOnMouseLeaveHandler,
]
);
// ----
// Document and DOM Event
// ----
var handleDocumentMouseDown = React__namespace.useCallback(function (e) {
latestDocumentMouseEvent.current = e;
}, []);
index$1({
disabled: !closeOnDocumentClick,
event: 'mousedown',
callback: handleDocumentMouseDown,
});
var handleDocumentClick = React__namespace.useCallback(
function (e) {
var currentMouseDownEvent = latestDocumentMouseEvent.current;
latestDocumentMouseEvent.current = null;
var triggerElement = triggerRef.current;
var contentElement = contentRef.current;
/**
* Ignore the click if there's no Portal
* or the event happened in trigger, or the event
* is originated in Portal but ended outside, or the event
* happened in the portal
*/
if (
!contentElement ||
doesNodeContainClick.doesNodeContainClick(triggerElement, e) ||
(currentMouseDownEvent &&
doesNodeContainClick.doesNodeContainClick(
contentElement,
currentMouseDownEvent
)) ||
doesNodeContainClick.doesNodeContainClick(contentElement, e)
) {
return;
}
if (closeOnDocumentClick) {
closePortal(e);
}
},
[closeOnDocumentClick, closePortal]
);
index$1({
disabled: !closeOnDocumentClick,
event: 'click',
callback: handleDocumentClick,
});
var handleEscapeKey = React__namespace.useCallback(
function (e) {
if (!closeOnEscape) {
return;
}
if (e.key === 'Escape') {
closePortal(e);
}
},
[closeOnEscape, closePortal]
);
index$1({
disabled: !closeOnEscape,
event: 'keydown',
callback: handleEscapeKey,
});
// ----
// Portal Mouse Event Handler
// ----
var handlePortalMouseEnter = React__namespace.useCallback(
function () {
if (!closeOnPortalMouseLeave) {
return;
}
clearMouseLeaveTimer();
},
[closeOnPortalMouseLeave, clearMouseLeaveTimer]
);
index$1({
disabled: !closeOnPortalMouseLeave,
target: contentRef.current,
event: 'mouseenter',
callback: handlePortalMouseEnter,
});
var handlePortalMouseLeave = React__namespace.useCallback(
function (e) {
if (!closeOnPortalMouseLeave) {
return;
}
if (e.target !== contentRef.current) {
return;
}
mouseLeaveTimer.current = closePortalWithTimeout(e);
},
[closeOnPortalMouseLeave, closePortalWithTimeout]
);
index$1({
disabled: !closeOnPortalMouseLeave,
target: contentRef.current,
event: 'mouseleave',
callback: handlePortalMouseLeave,
});
// ----
// Component Render
// ----
return React__namespace.createElement(
React__namespace.Fragment,
null,
open &&
React__namespace.createElement(
PortalInner,
{
innerRef: contentRef,
mountNode: mountNode,
onMount: userDefinedOnMountHandler,
onUnmount: userDefinedOnUnmountHandler,
},
children
),
trigger &&
React__namespace.createElement(
Ref,
{ innerRef: handleTriggerRef },
React__namespace.cloneElement(trigger, {
onBlur: handleTriggerBlur,
onClick:
openOnTriggerClick || closeOnTriggerClick || triggerOnClickHandler
? handleTriggerClick
: undefined,
onFocus: handleTriggerFocus,
onMouseEnter: handleTriggerMouseEnter,
onMouseLeave: handleTriggerMouseLeave,
})
)
);
};
Portal.displayName = 'Portal';
Portal.defaultProps = {
closeOnDocumentClick: true,
closeOnEscape: true,
openOnTriggerClick: true,
};
module.exports = Portal;