@bianic-ui/modal
Version:
An accessible dialog (modal) component for React & Bianic UI
350 lines (294 loc) • 11.7 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.ModalCloseButton = exports.ModalFooter = exports.ModalBody = exports.ModalHeader = exports.ModalOverlay = exports.ModalContent = exports.Modal = exports.useModalContext = exports.ModalContextProvider = void 0;
var _closeButton = require("@bianic-ui/close-button");
var _focusLock = require("@bianic-ui/focus-lock");
var _portal = require("@bianic-ui/portal");
var _system = require("@bianic-ui/system");
var _utils = require("@bianic-ui/utils");
var React = _interopRequireWildcard(require("react"));
var _reactRemoveScroll = require("react-remove-scroll");
var _useModal = require("./use-modal");
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
var _createContext = (0, _utils.createContext)({
strict: true,
name: "ModalContext",
errorMessage: "useModalContext: `context` is undefined. Seems you forgot to wrap modal components in `<Modal />`"
}),
ModalContextProvider = _createContext[0],
useModalContext = _createContext[1];
exports.useModalContext = useModalContext;
exports.ModalContextProvider = ModalContextProvider;
/**
* Modal
*
* React component that provides context, theming, and accessbility properties
* to all other modal components.
*
* It doesn't render any DOM node.
*/
var Modal = function Modal(props) {
var getContainer = props.getContainer,
children = props.children,
autoFocus = props.autoFocus,
trapFocus = props.trapFocus,
initialFocusRef = props.initialFocusRef,
finalFocusRef = props.finalFocusRef,
returnFocusOnClose = props.returnFocusOnClose,
blockScrollOnMount = props.blockScrollOnMount,
allowPinchZoom = props.allowPinchZoom,
preserveScrollBarGap = props.preserveScrollBarGap;
var styles = (0, _system.useMultiStyleConfig)("Modal", props);
var modal = (0, _useModal.useModal)(props);
var context = _extends({}, modal, {
autoFocus: autoFocus,
trapFocus: trapFocus,
initialFocusRef: initialFocusRef,
finalFocusRef: finalFocusRef,
returnFocusOnClose: returnFocusOnClose,
blockScrollOnMount: blockScrollOnMount,
allowPinchZoom: allowPinchZoom,
preserveScrollBarGap: preserveScrollBarGap
});
if (!context.isOpen) return null;
return /*#__PURE__*/React.createElement(ModalContextProvider, {
value: context
}, /*#__PURE__*/React.createElement(_portal.Portal, {
getContainer: getContainer
}, /*#__PURE__*/React.createElement(_system.StylesProvider, {
value: styles
}, children)));
};
exports.Modal = Modal;
Modal.defaultProps = {
returnFocusOnClose: true,
scrollBehavior: "outside",
trapFocus: true,
autoFocus: true,
blockScrollOnMount: true,
allowPinchZoom: false
};
if (_utils.__DEV__) {
Modal.displayName = "Modal";
}
/**
* ModalContent
*
* React component used to group modal's content. It has all the
* necessary `aria-*` properties to indicate that it's a modal modal
*/
var ModalContent = /*#__PURE__*/(0, _system.forwardRef)(function ModalContent(props, ref) {
var className = props.className,
children = props.children,
otherProps = _objectWithoutPropertiesLoose(props, ["className", "children"]);
var _useModalContext = useModalContext(),
getContentProps = _useModalContext.getContentProps;
var content = getContentProps(otherProps, ref);
var _className = (0, _utils.cx)("bianic-modal__content", className);
var styles = (0, _system.useStyles)();
return /*#__PURE__*/React.createElement(_system.bianic.section, _extends({
className: _className
}, content, {
__css: _extends({
display: "flex",
flexDirection: "column",
position: "relative",
width: "100%",
outline: 0
}, styles.content)
}), children);
});
exports.ModalContent = ModalContent;
if (_utils.__DEV__) {
ModalContent.displayName = "ModalContent";
}
/**
* ModalOverlay
*
* React component that renders a backdrop behind the modal. It's
* also used as a wrapper for the modal content for better positioning.
*
* @see Docs https://bianic-ui.com/components/modal
*/
var ModalOverlay = /*#__PURE__*/(0, _system.forwardRef)(function ModalOverlay(props, ref) {
var className = props.className,
children = props.children,
rest = _objectWithoutPropertiesLoose(props, ["className", "children"]);
var _useModalContext2 = useModalContext(),
getOverlayProps = _useModalContext2.getOverlayProps,
autoFocus = _useModalContext2.autoFocus,
trapFocus = _useModalContext2.trapFocus,
dialogRef = _useModalContext2.dialogRef,
initialFocusRef = _useModalContext2.initialFocusRef,
blockScrollOnMount = _useModalContext2.blockScrollOnMount,
allowPinchZoom = _useModalContext2.allowPinchZoom,
finalFocusRef = _useModalContext2.finalFocusRef,
returnFocusOnClose = _useModalContext2.returnFocusOnClose,
preserveScrollBarGap = _useModalContext2.preserveScrollBarGap;
var overlay = getOverlayProps(rest, ref);
var _className = (0, _utils.cx)("bianic-modal__overlay", className);
var styles = (0, _system.useStyles)();
return /*#__PURE__*/React.createElement(_focusLock.FocusLock, {
autoFocus: autoFocus,
isDisabled: !trapFocus,
initialFocusRef: initialFocusRef,
finalFocusRef: finalFocusRef,
restoreFocus: returnFocusOnClose,
contentRef: dialogRef
}, /*#__PURE__*/React.createElement(_reactRemoveScroll.RemoveScroll, {
removeScrollBar: !preserveScrollBarGap,
allowPinchZoom: allowPinchZoom,
enabled: blockScrollOnMount
}, /*#__PURE__*/React.createElement(_system.bianic.div, _extends({}, overlay, {
className: _className,
__css: _extends({
width: "100vw",
height: "100vh",
position: "fixed",
left: 0,
top: 0
}, styles.overlay)
}), children)));
});
exports.ModalOverlay = ModalOverlay;
if (_utils.__DEV__) {
ModalOverlay.displayName = "ModalOverlay";
}
/**
* ModalHeader
*
* React component that houses the title of the modal.
*
* @see Docs https://bianic-ui.com/components/modal
*/
var ModalHeader = /*#__PURE__*/(0, _system.forwardRef)(function ModalHeader(props, ref) {
var className = props.className,
rest = _objectWithoutPropertiesLoose(props, ["className"]);
var _useModalContext3 = useModalContext(),
headerId = _useModalContext3.headerId,
setHeaderMounted = _useModalContext3.setHeaderMounted;
/**
* Notify us if this component was rendered or used
* so we can append `aria-labelledby` automatically
*/
React.useEffect(function () {
setHeaderMounted(true);
return function () {
return setHeaderMounted(false);
};
}, [setHeaderMounted]);
var _className = (0, _utils.cx)("bianic-modal__header", className);
var styles = (0, _system.useStyles)();
return /*#__PURE__*/React.createElement(_system.bianic.header, _extends({
ref: ref,
className: _className,
id: headerId
}, rest, {
__css: _extends({
flex: 0
}, styles.header)
}));
});
exports.ModalHeader = ModalHeader;
if (_utils.__DEV__) {
ModalHeader.displayName = "ModalHeader";
}
/**
* ModalBody
*
* React component that houses the main content of the modal.
*
* @see Docs https://bianic-ui.com/components/modal
*/
var ModalBody = /*#__PURE__*/(0, _system.forwardRef)(function ModalBody(props, ref) {
var className = props.className,
rest = _objectWithoutPropertiesLoose(props, ["className"]);
var _useModalContext4 = useModalContext(),
bodyId = _useModalContext4.bodyId,
setBodyMounted = _useModalContext4.setBodyMounted;
/**
* Notify us if this component was rendered or used
* so we can append `aria-describedby` automatically
*/
React.useEffect(function () {
setBodyMounted(true);
return function () {
return setBodyMounted(false);
};
}, [setBodyMounted]);
var _className = (0, _utils.cx)("bianic-modal__body", className);
var styles = (0, _system.useStyles)();
return /*#__PURE__*/React.createElement(_system.bianic.div, _extends({
ref: ref,
className: _className,
id: bodyId
}, rest, {
__css: styles.body
}));
});
exports.ModalBody = ModalBody;
if (_utils.__DEV__) {
ModalBody.displayName = "ModalBody";
}
/**
* ModalFooter
*
* React component that houses the action buttons of the modal.
*
* @see Docs https://bianic-ui.com/components/modal
*/
var ModalFooter = /*#__PURE__*/(0, _system.forwardRef)(function ModalFooter(props, ref) {
var className = props.className,
rest = _objectWithoutPropertiesLoose(props, ["className"]);
var _className = (0, _utils.cx)("bianic-modal__footer", className);
var styles = (0, _system.useStyles)();
return /*#__PURE__*/React.createElement(_system.bianic.footer, _extends({
ref: ref
}, rest, {
__css: _extends({
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
flex: 0
}, styles.footer),
className: _className
}));
});
exports.ModalFooter = ModalFooter;
if (_utils.__DEV__) {
ModalFooter.displayName = "ModalFooter";
}
/**
* ModalCloseButton
*
* React component used closes the modal. You don't need
* to pass the `onClick` to it, it's reads the `onClose` action from the
* modal context.
*/
var ModalCloseButton = /*#__PURE__*/(0, _system.forwardRef)(function ModalCloseButton(props, ref) {
var onClick = props.onClick,
className = props.className,
rest = _objectWithoutPropertiesLoose(props, ["onClick", "className"]);
var _useModalContext5 = useModalContext(),
onClose = _useModalContext5.onClose;
var _className = (0, _utils.cx)("bianic-modal__close-btn", className);
return /*#__PURE__*/React.createElement(_closeButton.CloseButton, _extends({
ref: ref,
position: "absolute",
top: "8px",
right: "12px",
className: _className,
onClick: (0, _utils.callAllHandlers)(onClick, function (event) {
event.stopPropagation();
onClose();
})
}, rest));
});
exports.ModalCloseButton = ModalCloseButton;
if (_utils.__DEV__) {
ModalCloseButton.displayName = "ModalCloseButton";
}
//# sourceMappingURL=modal.js.map