material-ui-popup-state
Version:
easiest way to create menus, popovers, and poppers with material-ui
477 lines (464 loc) • 16.9 kB
JavaScript
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.anchorRef = anchorRef;
exports.bindContextMenu = bindContextMenu;
exports.bindDialog = bindDialog;
exports.bindDoubleClick = bindDoubleClick;
exports.bindFocus = bindFocus;
exports.bindHover = bindHover;
exports.bindMenu = bindMenu;
exports.bindPopover = bindPopover;
exports.bindPopper = bindPopper;
exports.bindToggle = bindToggle;
exports.bindTrigger = bindTrigger;
exports.initCoreState = void 0;
exports.usePopupState = usePopupState;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _react2 = _interopRequireWildcard(require("react"));
var React = _react2;
var _useEvent = require("./useEvent.js");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /* eslint-env browser */
var printedWarnings = {};
function warn(key, message) {
if (printedWarnings[key]) return;
printedWarnings[key] = true;
console.error('[material-ui-popup-state] WARNING', message);
}
var initCoreState = exports.initCoreState = {
isOpen: false,
setAnchorElUsed: false,
anchorEl: undefined,
anchorPosition: undefined,
hovered: false,
focused: false,
_openEventType: null,
_childPopupState: null,
_deferNextOpen: false,
_deferNextClose: false
};
// https://github.com/jcoreio/material-ui-popup-state/issues/138
// Webpack prod build doesn't like it if we refer to React.useId conditionally,
// but aliasing to a variable like this works
var _react = React;
var defaultPopupId = 'useId' in _react ? function () {
return _react.useId();
}
// istanbul ignore next
: function () {
return undefined;
};
function usePopupState(_ref) {
var parentPopupState = _ref.parentPopupState,
_ref$popupId = _ref.popupId,
popupId = _ref$popupId === void 0 ? defaultPopupId() : _ref$popupId,
variant = _ref.variant,
disableAutoFocus = _ref.disableAutoFocus;
var isMounted = (0, _react2.useRef)(true);
(0, _react2.useEffect)(function () {
isMounted.current = true;
return function () {
isMounted.current = false;
};
}, []);
var _useState = (0, _react2.useState)(initCoreState),
_useState2 = (0, _slicedToArray2["default"])(_useState, 2),
state = _useState2[0],
_setState = _useState2[1];
var setState = (0, _react2.useCallback)(function (state) {
if (isMounted.current) _setState(state);
}, []);
var setAnchorEl = (0, _react2.useCallback)(function (anchorEl) {
return setState(function (state) {
return _objectSpread(_objectSpread({}, state), {}, {
setAnchorElUsed: true,
anchorEl: anchorEl !== null && anchorEl !== void 0 ? anchorEl : undefined
});
});
}, []);
var toggle = (0, _useEvent.useEvent)(function (eventOrAnchorEl) {
if (state.isOpen) close(eventOrAnchorEl);else open(eventOrAnchorEl);
return state;
});
var open = (0, _useEvent.useEvent)(function (eventOrAnchorEl) {
var event = eventOrAnchorEl instanceof Element ? undefined : eventOrAnchorEl;
var element = eventOrAnchorEl instanceof Element ? eventOrAnchorEl : (eventOrAnchorEl === null || eventOrAnchorEl === void 0 ? void 0 : eventOrAnchorEl.currentTarget) instanceof Element ? eventOrAnchorEl.currentTarget : undefined;
if ((event === null || event === void 0 ? void 0 : event.type) === 'touchstart') {
setState(function (state) {
return _objectSpread(_objectSpread({}, state), {}, {
_deferNextOpen: true
});
});
return;
}
var clientX = event === null || event === void 0 ? void 0 : event.clientX;
var clientY = event === null || event === void 0 ? void 0 : event.clientY;
var anchorPosition = typeof clientX === 'number' && typeof clientY === 'number' ? {
left: clientX,
top: clientY
} : undefined;
var doOpen = function doOpen(state) {
if (!eventOrAnchorEl && !state.setAnchorElUsed && variant !== 'dialog') {
warn('missingEventOrAnchorEl', 'eventOrAnchorEl should be defined if setAnchorEl is not used');
}
if (parentPopupState) {
if (!parentPopupState.isOpen) return state;
setTimeout(function () {
return parentPopupState._setChildPopupState(popupState);
});
}
var newState = _objectSpread(_objectSpread({}, state), {}, {
isOpen: true,
anchorPosition: anchorPosition,
hovered: (event === null || event === void 0 ? void 0 : event.type) === 'mouseover' || state.hovered,
focused: (event === null || event === void 0 ? void 0 : event.type) === 'focus' || state.focused,
_openEventType: event === null || event === void 0 ? void 0 : event.type
});
if (!state.setAnchorElUsed) {
if (event !== null && event !== void 0 && event.currentTarget) {
newState.anchorEl = event.currentTarget;
} else if (element) {
newState.anchorEl = element;
}
}
return newState;
};
setState(function (state) {
if (state._deferNextOpen) {
setTimeout(function () {
return setState(doOpen);
}, 0);
return _objectSpread(_objectSpread({}, state), {}, {
_deferNextOpen: false
});
} else {
return doOpen(state);
}
});
});
var doClose = function doClose(state) {
var _childPopupState = state._childPopupState;
setTimeout(function () {
_childPopupState === null || _childPopupState === void 0 || _childPopupState.close();
parentPopupState === null || parentPopupState === void 0 || parentPopupState._setChildPopupState(null);
});
return _objectSpread(_objectSpread({}, state), {}, {
isOpen: false,
hovered: false,
focused: false
});
};
var close = (0, _useEvent.useEvent)(function (eventOrAnchorEl) {
var event = eventOrAnchorEl instanceof Element ? undefined : eventOrAnchorEl;
if ((event === null || event === void 0 ? void 0 : event.type) === 'touchstart') {
setState(function (state) {
return _objectSpread(_objectSpread({}, state), {}, {
_deferNextClose: true
});
});
return;
}
setState(function (state) {
if (state._deferNextClose) {
setTimeout(function () {
return setState(doClose);
}, 0);
return _objectSpread(_objectSpread({}, state), {}, {
_deferNextClose: false
});
} else {
return doClose(state);
}
});
});
var setOpen = (0, _react2.useCallback)(function (nextOpen, eventOrAnchorEl) {
if (nextOpen) {
open(eventOrAnchorEl);
} else {
close(eventOrAnchorEl);
}
}, []);
var onMouseLeave = (0, _useEvent.useEvent)(function (event) {
var relatedTarget = event.relatedTarget;
setState(function (state) {
if (state.hovered && !(relatedTarget instanceof Element && isElementInPopup(relatedTarget, popupState))) {
if (state.focused) {
return _objectSpread(_objectSpread({}, state), {}, {
hovered: false
});
} else {
return doClose(state);
}
}
return state;
});
});
var onBlur = (0, _useEvent.useEvent)(function (event) {
if (!event) return;
var relatedTarget = event.relatedTarget;
setState(function (state) {
if (state.focused && !(relatedTarget instanceof Element && isElementInPopup(relatedTarget, popupState))) {
if (state.hovered) {
return _objectSpread(_objectSpread({}, state), {}, {
focused: false
});
} else {
return doClose(state);
}
}
return state;
});
});
var _setChildPopupState = (0, _react2.useCallback)(function (_childPopupState) {
return setState(function (state) {
return _objectSpread(_objectSpread({}, state), {}, {
_childPopupState: _childPopupState
});
});
}, []);
var popupState = _objectSpread(_objectSpread({}, state), {}, {
setAnchorEl: setAnchorEl,
popupId: popupId !== null && popupId !== void 0 ? popupId : undefined,
variant: variant,
open: open,
close: close,
toggle: toggle,
setOpen: setOpen,
onBlur: onBlur,
onMouseLeave: onMouseLeave,
disableAutoFocus: disableAutoFocus !== null && disableAutoFocus !== void 0 ? disableAutoFocus : Boolean(state.hovered || state.focused),
_setChildPopupState: _setChildPopupState
});
return popupState;
}
/**
* Creates a ref that sets the anchorEl for the popup.
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
function anchorRef(_ref2) {
var setAnchorEl = _ref2.setAnchorEl;
return setAnchorEl;
}
function controlAriaProps(_ref3) {
var isOpen = _ref3.isOpen,
popupId = _ref3.popupId,
variant = _ref3.variant;
return _objectSpread({}, variant === 'popover' ? {
'aria-haspopup': true,
'aria-controls': isOpen ? popupId : undefined
} : variant === 'popper' ? {
'aria-describedby': isOpen ? popupId : undefined
} : undefined);
}
/**
* Creates props for a component that opens the popup when clicked.
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
function bindTrigger(popupState) {
return _objectSpread(_objectSpread({}, controlAriaProps(popupState)), {}, {
onClick: popupState.open,
onTouchStart: popupState.open
});
}
/**
* Creates props for a component that opens the popup on its contextmenu event (right click).
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
function bindContextMenu(popupState) {
return _objectSpread(_objectSpread({}, controlAriaProps(popupState)), {}, {
onContextMenu: function onContextMenu(e) {
e.preventDefault();
popupState.open(e);
}
});
}
/**
* Creates props for a component that toggles the popup when clicked.
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
function bindToggle(popupState) {
return _objectSpread(_objectSpread({}, controlAriaProps(popupState)), {}, {
onClick: popupState.toggle,
onTouchStart: popupState.toggle
});
}
/**
* Creates props for a component that opens the popup while hovered.
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
function bindHover(popupState) {
var open = popupState.open,
onMouseLeave = popupState.onMouseLeave;
return _objectSpread(_objectSpread({}, controlAriaProps(popupState)), {}, {
onTouchStart: open,
onMouseOver: open,
onMouseLeave: onMouseLeave
});
}
/**
* Creates props for a component that opens the popup while focused.
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
function bindFocus(popupState) {
var open = popupState.open,
onBlur = popupState.onBlur;
return _objectSpread(_objectSpread({}, controlAriaProps(popupState)), {}, {
onFocus: open,
onBlur: onBlur
});
}
/**
* Creates props for a component that opens the popup while double click.
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
function bindDoubleClick(_ref4) {
var isOpen = _ref4.isOpen,
open = _ref4.open,
popupId = _ref4.popupId,
variant = _ref4.variant;
return (0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])({}, variant === 'popover' ? 'aria-controls' : 'aria-describedby', isOpen ? popupId : null), 'aria-haspopup', variant === 'popover' ? true : undefined), "onDoubleClick", open);
}
/**
* Creates props for a `Popover` component.
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
function bindPopover(_ref6) {
var isOpen = _ref6.isOpen,
anchorEl = _ref6.anchorEl,
anchorPosition = _ref6.anchorPosition,
close = _ref6.close,
popupId = _ref6.popupId,
onMouseLeave = _ref6.onMouseLeave,
disableAutoFocus = _ref6.disableAutoFocus,
_openEventType = _ref6._openEventType;
var usePopoverPosition = _openEventType === 'contextmenu';
return _objectSpread({
id: popupId,
anchorEl: anchorEl,
anchorPosition: anchorPosition,
anchorReference: usePopoverPosition ? 'anchorPosition' : 'anchorEl',
open: isOpen,
onClose: close,
onMouseLeave: onMouseLeave
}, disableAutoFocus && {
disableAutoFocus: true,
disableEnforceFocus: true,
disableRestoreFocus: true
});
}
/**
* Creates props for a `Menu` component.
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
/**
* Creates props for a `Popover` component.
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
function bindMenu(_ref7) {
var isOpen = _ref7.isOpen,
anchorEl = _ref7.anchorEl,
anchorPosition = _ref7.anchorPosition,
close = _ref7.close,
popupId = _ref7.popupId,
onMouseLeave = _ref7.onMouseLeave,
disableAutoFocus = _ref7.disableAutoFocus,
_openEventType = _ref7._openEventType;
var usePopoverPosition = _openEventType === 'contextmenu';
return _objectSpread({
id: popupId,
anchorEl: anchorEl,
anchorPosition: anchorPosition,
anchorReference: usePopoverPosition ? 'anchorPosition' : 'anchorEl',
open: isOpen,
onClose: close,
onMouseLeave: onMouseLeave
}, disableAutoFocus && {
autoFocus: false,
disableAutoFocusItem: true,
disableAutoFocus: true,
disableEnforceFocus: true,
disableRestoreFocus: true
});
}
/**
* Creates props for a `Popper` component.
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
function bindPopper(_ref8) {
var isOpen = _ref8.isOpen,
anchorEl = _ref8.anchorEl,
popupId = _ref8.popupId,
onMouseLeave = _ref8.onMouseLeave;
return {
id: popupId,
anchorEl: anchorEl,
open: isOpen,
onMouseLeave: onMouseLeave
};
}
/**
* Creates props for a `Dialog` component.
*
* @param {object} popupState the argument passed to the child function of
* `PopupState`
*/
function bindDialog(_ref9) {
var isOpen = _ref9.isOpen,
close = _ref9.close;
return {
open: isOpen,
onClose: close
};
}
function getPopup(element, _ref10) {
var popupId = _ref10.popupId;
if (!popupId) return null;
var rootNode = typeof element.getRootNode === 'function' ? element.getRootNode() : document;
if (typeof rootNode.getElementById === 'function') {
return rootNode.getElementById(popupId);
}
return null;
}
function isElementInPopup(element, popupState) {
var anchorEl = popupState.anchorEl,
_childPopupState = popupState._childPopupState;
return isAncestor(anchorEl, element) || isAncestor(getPopup(element, popupState), element) || _childPopupState != null && isElementInPopup(element, _childPopupState);
}
function isAncestor(parent, child) {
if (!parent) return false;
while (child) {
if (child === parent) return true;
child = child.parentElement;
}
return false;
}
//# sourceMappingURL=hooks.js.map
;