UNPKG

@alifd/next

Version:

A configurable component library for web built on React.

249 lines (248 loc) 12 kB
import { __assign, __extends, __read, __rest } from "tslib"; import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Overlay from '../overlay'; import zhCN from '../locale/zh-cn'; import { focus, obj, func, events, dom, env } from '../util'; import Inner from './inner'; var noop = function () { }; var limitTabRange = focus.limitTabRange; var bindCtx = func.bindCtx; var pickOthers = obj.pickOthers; var getStyle = dom.getStyle, setStyle = dom.setStyle; function isWidthOrHeight(name) { return ['width', 'height'].indexOf(name) !== -1; } // [fix issue #1609](https://github.com/alibaba-fusion/next/issues/1609) // https://stackoverflow.com/questions/19717907/getcomputedstyle-reporting-different-heights-between-chrome-safari-firefox-and-i function _getSize(dom, name) { var boxSizing = getStyle(dom, 'boxSizing'); if (env.ieVersion && isWidthOrHeight(name) && boxSizing === 'border-box') { return parseFloat(dom.getBoundingClientRect()[name].toFixed(1)); } else { return getStyle(dom, name); } } /** * Dialog */ var Dialog = /** @class */ (function (_super) { __extends(Dialog, _super); function Dialog(props) { var _this = _super.call(this, props) || this; bindCtx(_this, ['onKeyDown', 'beforePosition', 'adjustPosition', 'getOverlayRef']); return _this; } Dialog.prototype.componentDidMount = function () { events.on(document, 'keydown', this.onKeyDown); if (!this.useCSSToPosition()) { this.adjustPosition(); } }; Dialog.prototype.componentWillUnmount = function () { events.off(document, 'keydown', this.onKeyDown); }; Dialog.prototype.useCSSToPosition = function () { var _a = this.props, align = _a.align, isFullScreen = _a.isFullScreen; return align === 'cc cc' && isFullScreen; }; Dialog.prototype.onKeyDown = function (e) { if (this.overlay) { var node = this.getInnerNode(); if (node) { limitTabRange(node, e); } } }; Dialog.prototype.beforePosition = function () { if (this.props.visible && this.overlay) { var inner = this.getInner(); if (inner) { var node = this.getInnerNode(); if (this._lastDialogHeight !== _getSize(node, 'height')) { this.revertSize(inner.bodyNode); } } } }; Dialog.prototype.adjustPosition = function () { if (this.props.visible && this.overlay) { var inner = this.getInner(); if (inner) { var node = this.getInnerNode(); var top_1 = getStyle(node, 'top'); var minMargin = this.props.minMargin; if (top_1 < minMargin) { top_1 = minMargin; setStyle(node, 'top', "".concat(minMargin, "px")); } var height = _getSize(node, 'height'); var viewportHeight = window.innerHeight || document.documentElement.clientHeight; if (viewportHeight < height + top_1 * 2 - 1 || // 分辨率和精确度的原因 高度计算的时候 可能会有1px内的偏差 this.props.height) { this.adjustSize(inner, node, Math.min(height, viewportHeight - top_1 * 2)); } else { this.revertSize(inner.bodyNode); } this._lastDialogHeight = height; } } }; Dialog.prototype.adjustSize = function (inner, node, expectHeight) { var headerNode = inner.headerNode, bodyNode = inner.bodyNode, footerNode = inner.footerNode; var _a = __read([headerNode, footerNode].map(function (node) { return node ? _getSize(node, 'height') : 0; }), 2), headerHeight = _a[0], footerHeight = _a[1]; var padding = ['padding-top', 'padding-bottom'].reduce(function (sum, attr) { return sum + getStyle(node, attr); }, 0); var maxBodyHeight = expectHeight - headerHeight - footerHeight - padding; if (maxBodyHeight < 0) { maxBodyHeight = 1; } if (bodyNode) { this.dialogBodyStyleMaxHeight = bodyNode.style.maxHeight; this.dialogBodyStyleOverflowY = bodyNode.style.overflowY; setStyle(bodyNode, { 'max-height': "".concat(maxBodyHeight, "px"), 'overflow-y': 'auto', }); } }; Dialog.prototype.revertSize = function (bodyNode) { setStyle(bodyNode, { 'max-height': this.dialogBodyStyleMaxHeight, 'overflow-y': this.dialogBodyStyleOverflowY, }); }; Dialog.prototype.mapcloseableToConfig = function (closeable) { return ['esc', 'close', 'mask'].reduce(function (ret, option) { var key = option.charAt(0).toUpperCase() + option.substr(1); var value = typeof closeable === 'boolean' ? closeable : closeable.split(',').indexOf(option) > -1; if (option === 'esc' || option === 'mask') { ret["canCloseBy".concat(key)] = value; } else { ret["canCloseBy".concat(key, "Click")] = value; } return ret; }, {}); }; Dialog.prototype.getOverlayRef = function (ref) { this.overlay = ref; }; Dialog.prototype.getInner = function () { return this.overlay.getInstance().getContent(); }; Dialog.prototype.getInnerNode = function () { return this.overlay.getInstance().getContentNode(); }; Dialog.prototype.renderInner = function (closeable) { var _a = this.props, prefix = _a.prefix, className = _a.className, title = _a.title, children = _a.children, footer = _a.footer, footerAlign = _a.footerAlign, footerActions = _a.footerActions, onOk = _a.onOk, onCancel = _a.onCancel, okProps = _a.okProps, cancelProps = _a.cancelProps, onClose = _a.onClose, locale = _a.locale, visible = _a.visible, rtl = _a.rtl, height = _a.height, noPadding = _a.noPadding; var others = pickOthers(Object.keys(Dialog.propTypes), this.props); return (React.createElement(Inner, __assign({ prefix: prefix, className: className, title: title, footer: footer, footerAlign: footerAlign, footerActions: footerActions, onOk: visible ? onOk : noop, onCancel: visible ? onCancel : noop, okProps: okProps, cancelProps: cancelProps, locale: locale, closeable: closeable, rtl: rtl, onClose: onClose.bind(this, 'closeClick'), height: height, noPadding: noPadding }, others), children)); }; Dialog.prototype.render = function () { var _a = this.props, prefix = _a.prefix, visible = _a.visible, hasMask = _a.hasMask, animation = _a.animation, autoFocus = _a.autoFocus, closeable = _a.closeable, closeMode = _a.closeMode, onClose = _a.onClose, afterClose = _a.afterClose, shouldUpdatePosition = _a.shouldUpdatePosition, align = _a.align, popupContainer = _a.popupContainer, cache = _a.cache, overlayProps = _a.overlayProps, rtl = _a.rtl; var useCSS = this.useCSSToPosition(); var newCloseable = 'closeMode' in this.props ? Array.isArray(closeMode) ? closeMode.join(',') : closeMode : closeable; var _b = this.mapcloseableToConfig(newCloseable), canCloseByCloseClick = _b.canCloseByCloseClick, closeConfig = __rest(_b, ["canCloseByCloseClick"]); var newOverlayProps = __assign(__assign(__assign(__assign({ disableScroll: true, container: popupContainer, cache: cache }, overlayProps), { prefix: prefix, visible: visible, animation: animation, hasMask: hasMask, autoFocus: autoFocus, afterClose: afterClose }), closeConfig), { canCloseByOutSideClick: false, align: (useCSS ? false : align), onRequestClose: onClose, needAdjust: false, ref: this.getOverlayRef, rtl: rtl, maskClass: useCSS ? "".concat(prefix, "dialog-container") : '', isChildrenInMask: useCSS && hasMask }); if (!useCSS) { newOverlayProps.beforePosition = this.beforePosition; newOverlayProps.onPosition = this.adjustPosition; newOverlayProps.shouldUpdatePosition = shouldUpdatePosition; } var inner = this.renderInner(canCloseByCloseClick); // useCSS && hasMask : isFullScreen 并且 有 mask 的模式下,为了解决 next-overlay-backdrop 覆盖 mask,使得点击 mask 关闭页面的功能不生效的问题,需要开启 Overlay 的 isChildrenInMask 功能,并且把 next-dialog-container 放到 next-overlay-backdrop 上 // useCSS && !hasMask : isFullScreen 并且 没有 mask 的情况下,需要关闭 isChildrenInMask 功能,以防止 children 不渲染 // 其他模式下维持 mask 与 children 同级的关系 return (React.createElement(Overlay, __assign({}, newOverlayProps), useCSS && !hasMask ? (React.createElement("div", { className: "".concat(prefix, "dialog-container"), dir: rtl ? 'rtl' : undefined }, inner)) : (inner))); }; Dialog.propTypes = { prefix: PropTypes.string, pure: PropTypes.bool, rtl: PropTypes.bool, className: PropTypes.string, visible: PropTypes.bool, title: PropTypes.node, children: PropTypes.node, footer: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]), footerAlign: PropTypes.oneOf(['left', 'center', 'right']), footerActions: PropTypes.array, onOk: PropTypes.func, onCancel: PropTypes.func, okProps: PropTypes.object, cancelProps: PropTypes.object, closeMode: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.oneOf(['close', 'mask', 'esc'])), PropTypes.oneOf(['close', 'mask', 'esc']), ]), cache: PropTypes.bool, afterClose: PropTypes.func, hasMask: PropTypes.bool, animation: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), autoFocus: PropTypes.bool, overlayProps: PropTypes.object, locale: PropTypes.object, // Do not remove this, it's for <ConfigProvider popupContainer={} /> // see https://github.com/alibaba-fusion/next/issues/1508 popupContainer: PropTypes.any, height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), v2: PropTypes.bool, width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), top: PropTypes.number, bottom: PropTypes.number, closeIcon: PropTypes.node, centered: PropTypes.bool, overflowScroll: PropTypes.bool, wrapperClassName: PropTypes.string, closeable: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), onClose: PropTypes.func, align: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), isFullScreen: PropTypes.bool, shouldUpdatePosition: PropTypes.bool, minMargin: PropTypes.number, noPadding: PropTypes.bool, }; Dialog.defaultProps = { prefix: 'next-', pure: false, visible: false, footerAlign: 'right', footerActions: ['ok', 'cancel'], onOk: noop, onCancel: noop, cache: false, okProps: {}, cancelProps: {}, closeable: 'esc,close', onClose: noop, afterClose: noop, centered: false, hasMask: true, animation: { in: 'fadeInUp', out: 'fadeOutUp', }, autoFocus: false, align: 'cc cc', isFullScreen: false, overflowScroll: true, shouldUpdatePosition: false, minMargin: 40, bottom: 40, overlayProps: {}, locale: zhCN.Dialog, noPadding: false, }; Dialog.displayName = 'Dialog'; return Dialog; }(Component)); export default Dialog;