UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

369 lines 12.9 kB
import _noop from "lodash/noop"; var __rest = this && this.__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { cssClasses, strings } from '@douyinfe/semi-foundation/lib/es/modal/constants'; import '@douyinfe/semi-foundation/lib/es/modal/modal.css'; import ModalFoundation from '@douyinfe/semi-foundation/lib/es/modal/modalFoundation'; import cls from 'classnames'; import PropTypes from 'prop-types'; import React from 'react'; import BaseComponent from '../_base/baseComponent'; import CSSAnimation from "../_cssAnimation"; import Portal from '../_portal'; import { getDefaultPropsFromGlobalConfig, getScrollbarWidth } from '../_utils'; import Button from '../button'; import LocaleConsumer from '../locale/localeConsumer'; import ModalContent from './ModalContent'; import confirm, { withConfirm, withError, withInfo, withSuccess, withWarning } from './confirm'; import useModal from './useModal'; export let destroyFns = []; class Modal extends BaseComponent { constructor(props) { super(props); this.bodyOverflow = null; this.handleCancel = e => { this.foundation.handleCancel(e); }; this.handleOk = e => { this.foundation.handleOk(e); }; this.updateState = () => { const { visible } = this.props; this.foundation.toggleDisplayNone(!visible); }; this.renderFooter = () => { const { okText, okType, cancelText, confirmLoading, cancelLoading, hasCancel, footerFill } = this.props; const getCancelButton = locale => { var _a; if (!hasCancel) { return null; } else { return /*#__PURE__*/React.createElement(Button, Object.assign({ "aria-label": "cancel", onClick: this.handleCancel, loading: cancelLoading === undefined ? this.state.onCancelReturnPromiseStatus === "pending" : cancelLoading, type: "tertiary", block: footerFill, autoFocus: true }, this.props.cancelButtonProps, { style: Object.assign(Object.assign({}, footerFill ? { marginLeft: "unset" } : {}), (_a = this.props.cancelButtonProps) === null || _a === void 0 ? void 0 : _a.style), "x-semi-children-alias": "cancelText" }), cancelText || locale.cancel); } }; return /*#__PURE__*/React.createElement(LocaleConsumer, { componentName: "Modal" }, (locale, localeCode) => (/*#__PURE__*/React.createElement("div", { className: cls({ [`${cssClasses.DIALOG}-footerfill`]: footerFill }) }, getCancelButton(locale), /*#__PURE__*/React.createElement(Button, Object.assign({ "aria-label": "confirm", type: okType, theme: "solid", block: footerFill, loading: confirmLoading === undefined ? this.state.onOKReturnPromiseStatus === "pending" : confirmLoading, onClick: this.handleOk }, this.props.okButtonProps, { "x-semi-children-alias": "okText" }), okText || locale.confirm)))); }; // getDialog = () => { // const { // footer, // ...restProps // } = this.props; // const renderFooter = 'footer' in this.props ? footer : this.renderFooter(); // return <ModalContent {...restProps} footer={renderFooter} onClose={this.handleCancel}/>; // }; this.renderDialog = () => { var _a; let _b = this.props, { footer, className, motion, maskStyle: maskStyleFromProps, keepDOM, style: styleFromProps, zIndex, getPopupContainer, visible, modalContentClass } = _b, restProps = __rest(_b, ["footer", "className", "motion", "maskStyle", "keepDOM", "style", "zIndex", "getPopupContainer", "visible", "modalContentClass"]); let style = styleFromProps; const maskStyle = maskStyleFromProps; const renderFooter = 'footer' in this.props ? footer : this.renderFooter(); let wrapperStyle = { zIndex }; if (getPopupContainer && getPopupContainer() !== ((_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.document) === null || _a === void 0 ? void 0 : _a.body)) { wrapperStyle = { zIndex, position: 'static' }; } const classList = cls(className, { [`${cssClasses.DIALOG}-displayNone`]: keepDOM && this.state.displayNone }); const shouldRender = this.props.visible || this.props.keepDOM && (!this.props.lazyRender || this._haveRendered) || this.props.motion && !this.state.displayNone /* When there is animation, we use displayNone to judge whether animation is ended and judge whether to unmount content */; if (shouldRender) { this._haveRendered = true; } return /*#__PURE__*/React.createElement(CSSAnimation, { motion: this.props.motion, animationState: visible ? 'enter' : 'leave', startClassName: visible ? `${cssClasses.DIALOG}-content-animate-show` : `${cssClasses.DIALOG}-content-animate-hide`, onAnimationEnd: () => { this.updateState(); } }, _ref => { let { animationClassName, animationEventsNeedBind } = _ref; return /*#__PURE__*/React.createElement(CSSAnimation, { motion: this.props.motion, animationState: visible ? 'enter' : 'leave', startClassName: visible ? `${cssClasses.DIALOG}-mask-animate-show` : `${cssClasses.DIALOG}-mask-animate-hide`, onAnimationEnd: () => { this.updateState(); } }, _ref2 => { let { animationClassName: maskAnimationClassName, animationEventsNeedBind: maskAnimationEventsNeedBind } = _ref2; return shouldRender ? /*#__PURE__*/React.createElement(Portal, { style: wrapperStyle, getPopupContainer: getPopupContainer }, " ", /*#__PURE__*/React.createElement(ModalContent, Object.assign({}, restProps, { contentExtraProps: animationEventsNeedBind, maskExtraProps: maskAnimationEventsNeedBind, isFullScreen: this.state.isFullScreen, contentClassName: `${animationClassName} ${modalContentClass}`, maskClassName: maskAnimationClassName, className: classList, getPopupContainer: getPopupContainer, maskStyle: maskStyle, style: style, ref: this.modalRef, footer: renderFooter, onClose: this.handleCancel }))) : /*#__PURE__*/React.createElement(React.Fragment, null); }); }); }; this.state = { displayNone: !props.visible, isFullScreen: props.fullScreen }; this.foundation = new ModalFoundation(this.adapter); this.modalRef = /*#__PURE__*/React.createRef(); this.scrollBarWidth = 0; this.originBodyWidth = '100%'; } get adapter() { return Object.assign(Object.assign({}, super.adapter), { getProps: () => this.props, disabledBodyScroll: () => { var _a; const { getPopupContainer } = this.props; this.bodyOverflow = document.body.style.overflow || ''; if ((!getPopupContainer || getPopupContainer() === ((_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.document) === null || _a === void 0 ? void 0 : _a.body)) && this.bodyOverflow !== 'hidden') { document.body.style.overflow = 'hidden'; document.body.style.width = `calc(${this.originBodyWidth || '100%'} - ${this.scrollBarWidth}px)`; } }, enabledBodyScroll: () => { var _a; const { getPopupContainer } = this.props; if ((!getPopupContainer || getPopupContainer() === ((_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.document) === null || _a === void 0 ? void 0 : _a.body)) && this.bodyOverflow !== null && this.bodyOverflow !== 'hidden') { document.body.style.overflow = this.bodyOverflow; document.body.style.width = this.originBodyWidth; } }, notifyCancel: e => { return this.props.onCancel(e); }, notifyOk: e => { return this.props.onOk(e); }, notifyClose: () => { this.props.afterClose(); }, toggleDisplayNone: (displayNone, callback) => { if (displayNone !== this.state.displayNone) { this.setState({ displayNone: displayNone }, callback || _noop); } }, notifyFullScreen: isFullScreen => { if (isFullScreen !== this.state.isFullScreen) { this.setState({ isFullScreen }); } } }); } static getDerivedStateFromProps(props, prevState) { const newState = {}; if (props.fullScreen !== prevState.isFullScreen) { newState.isFullScreen = props.fullScreen; } if (props.visible && prevState.displayNone) { newState.displayNone = false; } // // if (!props.visible && !props.motion && !prevState.displayNone) { // newState.displayNone = true; // } return newState; } componentDidMount() { this.scrollBarWidth = getScrollbarWidth(); this.originBodyWidth = document.body.style.width; if (this.props.visible) { this.foundation.beforeShow(); } } componentDidUpdate(prevProps, prevState, snapshot) { // hide => show if (!prevProps.visible && this.props.visible) { this.foundation.beforeShow(); } if (!prevState.displayNone && this.state.displayNone) { this.foundation.afterHide(); } } componentWillUnmount() { if (this.props.visible) { this.foundation.destroy(); } else { this.foundation.enabledBodyScroll(); } } render() { const { visible, keepDOM, lazyRender } = this.props; return this.renderDialog(); } } Modal.propTypes = { mask: PropTypes.bool, closable: PropTypes.bool, centered: PropTypes.bool, visible: PropTypes.bool, width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), confirmLoading: PropTypes.bool, cancelLoading: PropTypes.bool, okText: PropTypes.string, okType: PropTypes.string, cancelText: PropTypes.string, maskClosable: PropTypes.bool, onCancel: PropTypes.func, onOk: PropTypes.func, modalRender: PropTypes.func, afterClose: PropTypes.func, okButtonProps: PropTypes.object, cancelButtonProps: PropTypes.object, style: PropTypes.object, className: PropTypes.string, maskStyle: PropTypes.object, bodyStyle: PropTypes.object, zIndex: PropTypes.number, title: PropTypes.node, icon: PropTypes.node, header: PropTypes.node, footer: PropTypes.node, hasCancel: PropTypes.bool, motion: PropTypes.bool, children: PropTypes.node, getPopupContainer: PropTypes.func, getContainerContext: PropTypes.func, maskFixed: PropTypes.bool, closeIcon: PropTypes.node, closeOnEsc: PropTypes.bool, size: PropTypes.oneOf(strings.SIZE), keepDOM: PropTypes.bool, lazyRender: PropTypes.bool, direction: PropTypes.oneOf(strings.directions), fullScreen: PropTypes.bool, footerFill: PropTypes.bool }; Modal.__SemiComponentName__ = "Modal"; Modal.defaultProps = getDefaultPropsFromGlobalConfig(Modal.__SemiComponentName__, { zIndex: 1000, motion: true, mask: true, centered: false, closable: true, visible: false, okType: 'primary', maskClosable: true, hasCancel: true, onCancel: _noop, onOk: _noop, afterClose: _noop, maskFixed: false, closeOnEsc: true, size: 'small', keepDOM: false, lazyRender: true, fullScreen: false }); Modal.useModal = useModal; Modal.info = function (props) { return confirm(withInfo(props)); }; Modal.success = function (props) { return confirm(withSuccess(props)); }; Modal.error = function (props) { return confirm(withError(props)); }; Modal.warning = function (props) { return confirm(withWarning(props)); }; Modal.confirm = function (props) { return confirm(withConfirm(props)); }; Modal.destroyAll = function destroyAllFn() { for (let i = 0, len = destroyFns.length; i < len; i++) { const close = destroyFns[i]; if (close) { close(); } } destroyFns = []; }; export default Modal;