@material-ui/core
Version:
React components that implement Google's Material Design.
204 lines (163 loc) • 7.31 kB
JavaScript
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var React = _interopRequireWildcard(require("react"));
var ReactDOM = _interopRequireWildcard(require("react-dom"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _ownerDocument = _interopRequireDefault(require("../utils/ownerDocument"));
var _useForkRef = _interopRequireDefault(require("../utils/useForkRef"));
var _useEventCallback = _interopRequireDefault(require("../utils/useEventCallback"));
var _utils = require("@material-ui/utils");
function mapEventPropToEvent(eventProp) {
return eventProp.substring(2).toLowerCase();
}
function clickedRootScrollbar(event) {
return document.documentElement.clientWidth < event.clientX || document.documentElement.clientHeight < event.clientY;
}
/**
* Listen for click events that occur somewhere in the document, outside of the element itself.
* For instance, if you need to hide a menu when people click anywhere else on your page.
*/
function ClickAwayListener(props) {
var children = props.children,
_props$disableReactTr = props.disableReactTree,
disableReactTree = _props$disableReactTr === void 0 ? false : _props$disableReactTr,
_props$mouseEvent = props.mouseEvent,
mouseEvent = _props$mouseEvent === void 0 ? 'onClick' : _props$mouseEvent,
onClickAway = props.onClickAway,
_props$touchEvent = props.touchEvent,
touchEvent = _props$touchEvent === void 0 ? 'onTouchEnd' : _props$touchEvent;
var movedRef = React.useRef(false);
var nodeRef = React.useRef(null);
var activatedRef = React.useRef(false);
var syntheticEventRef = React.useRef(false);
React.useEffect(function () {
// Ensure that this component is not "activated" synchronously.
// https://github.com/facebook/react/issues/20074
setTimeout(function () {
activatedRef.current = true;
}, 0);
return function () {
activatedRef.current = false;
};
}, []); // can be removed once we drop support for non ref forwarding class components
var handleOwnRef = React.useCallback(function (instance) {
// #StrictMode ready
nodeRef.current = ReactDOM.findDOMNode(instance);
}, []);
var handleRef = (0, _useForkRef.default)(children.ref, handleOwnRef); // The handler doesn't take event.defaultPrevented into account:
//
// event.preventDefault() is meant to stop default behaviours like
// clicking a checkbox to check it, hitting a button to submit a form,
// and hitting left arrow to move the cursor in a text input etc.
// Only special HTML elements have these default behaviors.
var handleClickAway = (0, _useEventCallback.default)(function (event) {
// Given developers can stop the propagation of the synthetic event,
// we can only be confident with a positive value.
var insideReactTree = syntheticEventRef.current;
syntheticEventRef.current = false; // 1. IE 11 support, which trigger the handleClickAway even after the unbind
// 2. The child might render null.
// 3. Behave like a blur listener.
if (!activatedRef.current || !nodeRef.current || clickedRootScrollbar(event)) {
return;
} // Do not act if user performed touchmove
if (movedRef.current) {
movedRef.current = false;
return;
}
var insideDOM; // If not enough, can use https://github.com/DieterHolvoet/event-propagation-path/blob/master/propagationPath.js
if (event.composedPath) {
insideDOM = event.composedPath().indexOf(nodeRef.current) > -1;
} else {
// TODO v6 remove dead logic https://caniuse.com/#search=composedPath.
var doc = (0, _ownerDocument.default)(nodeRef.current);
insideDOM = !doc.documentElement.contains(event.target) || nodeRef.current.contains(event.target);
}
if (!insideDOM && (disableReactTree || !insideReactTree)) {
onClickAway(event);
}
}); // Keep track of mouse/touch events that bubbled up through the portal.
var createHandleSynthetic = function createHandleSynthetic(handlerName) {
return function (event) {
syntheticEventRef.current = true;
var childrenPropsHandler = children.props[handlerName];
if (childrenPropsHandler) {
childrenPropsHandler(event);
}
};
};
var childrenProps = {
ref: handleRef
};
if (touchEvent !== false) {
childrenProps[touchEvent] = createHandleSynthetic(touchEvent);
}
React.useEffect(function () {
if (touchEvent !== false) {
var mappedTouchEvent = mapEventPropToEvent(touchEvent);
var doc = (0, _ownerDocument.default)(nodeRef.current);
var handleTouchMove = function handleTouchMove() {
movedRef.current = true;
};
doc.addEventListener(mappedTouchEvent, handleClickAway);
doc.addEventListener('touchmove', handleTouchMove);
return function () {
doc.removeEventListener(mappedTouchEvent, handleClickAway);
doc.removeEventListener('touchmove', handleTouchMove);
};
}
return undefined;
}, [handleClickAway, touchEvent]);
if (mouseEvent !== false) {
childrenProps[mouseEvent] = createHandleSynthetic(mouseEvent);
}
React.useEffect(function () {
if (mouseEvent !== false) {
var mappedMouseEvent = mapEventPropToEvent(mouseEvent);
var doc = (0, _ownerDocument.default)(nodeRef.current);
doc.addEventListener(mappedMouseEvent, handleClickAway);
return function () {
doc.removeEventListener(mappedMouseEvent, handleClickAway);
};
}
return undefined;
}, [handleClickAway, mouseEvent]);
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.cloneElement(children, childrenProps));
}
process.env.NODE_ENV !== "production" ? ClickAwayListener.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the d.ts file and run "yarn proptypes" |
// ----------------------------------------------------------------------
/**
* The wrapped element.
*/
children: _utils.elementAcceptingRef.isRequired,
/**
* If `true`, the React tree is ignored and only the DOM tree is considered.
* This prop changes how portaled elements are handled.
*/
disableReactTree: _propTypes.default.bool,
/**
* The mouse event to listen to. You can disable the listener by providing `false`.
*/
mouseEvent: _propTypes.default.oneOf(['onClick', 'onMouseDown', 'onMouseUp', false]),
/**
* Callback fired when a "click away" event is detected.
*/
onClickAway: _propTypes.default.func.isRequired,
/**
* The touch event to listen to. You can disable the listener by providing `false`.
*/
touchEvent: _propTypes.default.oneOf(['onTouchEnd', 'onTouchStart', false])
} : void 0;
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line
ClickAwayListener['propTypes' + ''] = (0, _utils.exactProp)(ClickAwayListener.propTypes);
}
var _default = ClickAwayListener;
exports.default = _default;
;