UNPKG

@bianic-ui/modal

Version:

An accessible dialog (modal) component for React & Bianic UI

350 lines (294 loc) 11.7 kB
"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