UNPKG

@bianic-ui/modal

Version:

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

332 lines (294 loc) 8.69 kB
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); } import { CloseButton } from "@bianic-ui/close-button"; import { FocusLock } from "@bianic-ui/focus-lock"; import { Portal } from "@bianic-ui/portal"; import { bianic, forwardRef, useMultiStyleConfig, StylesProvider, useStyles } from "@bianic-ui/system"; import { callAllHandlers, cx, __DEV__, createContext } from "@bianic-ui/utils"; import * as React from "react"; import { RemoveScroll } from "react-remove-scroll"; import { useModal } from "./use-modal"; var [ModalContextProvider, useModalContext] = createContext({ strict: true, name: "ModalContext", errorMessage: "useModalContext: `context` is undefined. Seems you forgot to wrap modal components in `<Modal />`" }); export { ModalContextProvider, useModalContext }; /** * Modal * * React component that provides context, theming, and accessbility properties * to all other modal components. * * It doesn't render any DOM node. */ export var Modal = props => { var { getContainer, children, autoFocus, trapFocus, initialFocusRef, finalFocusRef, returnFocusOnClose, blockScrollOnMount, allowPinchZoom, preserveScrollBarGap } = props; var styles = useMultiStyleConfig("Modal", props); var modal = useModal(props); var context = _extends({}, modal, { autoFocus, trapFocus, initialFocusRef, finalFocusRef, returnFocusOnClose, blockScrollOnMount, allowPinchZoom, preserveScrollBarGap }); if (!context.isOpen) return null; return /*#__PURE__*/React.createElement(ModalContextProvider, { value: context }, /*#__PURE__*/React.createElement(Portal, { getContainer: getContainer }, /*#__PURE__*/React.createElement(StylesProvider, { value: styles }, children))); }; Modal.defaultProps = { returnFocusOnClose: true, scrollBehavior: "outside", trapFocus: true, autoFocus: true, blockScrollOnMount: true, allowPinchZoom: false }; if (__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 */ export var ModalContent = /*#__PURE__*/forwardRef(function ModalContent(props, ref) { var { className, children } = props, otherProps = _objectWithoutPropertiesLoose(props, ["className", "children"]); var { getContentProps } = useModalContext(); var content = getContentProps(otherProps, ref); var _className = cx("bianic-modal__content", className); var styles = useStyles(); return /*#__PURE__*/React.createElement(bianic.section, _extends({ className: _className }, content, { __css: _extends({ display: "flex", flexDirection: "column", position: "relative", width: "100%", outline: 0 }, styles.content) }), children); }); if (__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 */ export var ModalOverlay = /*#__PURE__*/forwardRef(function ModalOverlay(props, ref) { var { className, children } = props, rest = _objectWithoutPropertiesLoose(props, ["className", "children"]); var { getOverlayProps, autoFocus, trapFocus, dialogRef, initialFocusRef, blockScrollOnMount, allowPinchZoom, finalFocusRef, returnFocusOnClose, preserveScrollBarGap } = useModalContext(); var overlay = getOverlayProps(rest, ref); var _className = cx("bianic-modal__overlay", className); var styles = useStyles(); return /*#__PURE__*/React.createElement(FocusLock, { autoFocus: autoFocus, isDisabled: !trapFocus, initialFocusRef: initialFocusRef, finalFocusRef: finalFocusRef, restoreFocus: returnFocusOnClose, contentRef: dialogRef }, /*#__PURE__*/React.createElement(RemoveScroll, { removeScrollBar: !preserveScrollBarGap, allowPinchZoom: allowPinchZoom, enabled: blockScrollOnMount }, /*#__PURE__*/React.createElement(bianic.div, _extends({}, overlay, { className: _className, __css: _extends({ width: "100vw", height: "100vh", position: "fixed", left: 0, top: 0 }, styles.overlay) }), children))); }); if (__DEV__) { ModalOverlay.displayName = "ModalOverlay"; } /** * ModalHeader * * React component that houses the title of the modal. * * @see Docs https://bianic-ui.com/components/modal */ export var ModalHeader = /*#__PURE__*/forwardRef(function ModalHeader(props, ref) { var { className } = props, rest = _objectWithoutPropertiesLoose(props, ["className"]); var { headerId, setHeaderMounted } = useModalContext(); /** * Notify us if this component was rendered or used * so we can append `aria-labelledby` automatically */ React.useEffect(() => { setHeaderMounted(true); return () => setHeaderMounted(false); }, [setHeaderMounted]); var _className = cx("bianic-modal__header", className); var styles = useStyles(); return /*#__PURE__*/React.createElement(bianic.header, _extends({ ref: ref, className: _className, id: headerId }, rest, { __css: _extends({ flex: 0 }, styles.header) })); }); if (__DEV__) { ModalHeader.displayName = "ModalHeader"; } /** * ModalBody * * React component that houses the main content of the modal. * * @see Docs https://bianic-ui.com/components/modal */ export var ModalBody = /*#__PURE__*/forwardRef(function ModalBody(props, ref) { var { className } = props, rest = _objectWithoutPropertiesLoose(props, ["className"]); var { bodyId, setBodyMounted } = useModalContext(); /** * Notify us if this component was rendered or used * so we can append `aria-describedby` automatically */ React.useEffect(() => { setBodyMounted(true); return () => setBodyMounted(false); }, [setBodyMounted]); var _className = cx("bianic-modal__body", className); var styles = useStyles(); return /*#__PURE__*/React.createElement(bianic.div, _extends({ ref: ref, className: _className, id: bodyId }, rest, { __css: styles.body })); }); if (__DEV__) { ModalBody.displayName = "ModalBody"; } /** * ModalFooter * * React component that houses the action buttons of the modal. * * @see Docs https://bianic-ui.com/components/modal */ export var ModalFooter = /*#__PURE__*/forwardRef(function ModalFooter(props, ref) { var { className } = props, rest = _objectWithoutPropertiesLoose(props, ["className"]); var _className = cx("bianic-modal__footer", className); var styles = useStyles(); return /*#__PURE__*/React.createElement(bianic.footer, _extends({ ref: ref }, rest, { __css: _extends({ display: "flex", alignItems: "center", justifyContent: "flex-end", flex: 0 }, styles.footer), className: _className })); }); if (__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. */ export var ModalCloseButton = /*#__PURE__*/forwardRef(function ModalCloseButton(props, ref) { var { onClick, className } = props, rest = _objectWithoutPropertiesLoose(props, ["onClick", "className"]); var { onClose } = useModalContext(); var _className = cx("bianic-modal__close-btn", className); return /*#__PURE__*/React.createElement(CloseButton, _extends({ ref: ref, position: "absolute", top: "8px", right: "12px", className: _className, onClick: callAllHandlers(onClick, event => { event.stopPropagation(); onClose(); }) }, rest)); }); if (__DEV__) { ModalCloseButton.displayName = "ModalCloseButton"; } //# sourceMappingURL=modal.js.map