@alifd/next
Version:
A configurable component library for web built on React.
534 lines (466 loc) • 19 kB
JavaScript
'use strict';
exports.__esModule = true;
exports.default = undefined;
var _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');
var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2);
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _class, _temp;
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _overlay = require('../overlay');
var _overlay2 = _interopRequireDefault(_overlay);
var _zhCn = require('../locale/zh-cn');
var _zhCn2 = _interopRequireDefault(_zhCn);
var _util = require('../util');
var _inner = require('./inner');
var _inner2 = _interopRequireDefault(_inner);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var noop = function noop() {};
var limitTabRange = _util.focus.limitTabRange;
var bindCtx = _util.func.bindCtx;
var pickOthers = _util.obj.pickOthers;
var getStyle = _util.dom.getStyle,
setStyle = _util.dom.setStyle;
// [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 (_util.env.ieVersion && ['width', 'height'].indexOf(name) !== -1 && boxSizing === 'border-box') {
return parseFloat(dom.getBoundingClientRect()[name].toFixed(1));
} else {
return getStyle(dom, name);
}
}
/**
* Dialog
*/
var Dialog = (_temp = _class = function (_Component) {
(0, _inherits3.default)(Dialog, _Component);
function Dialog(props, context) {
(0, _classCallCheck3.default)(this, Dialog);
var _this = (0, _possibleConstructorReturn3.default)(this, _Component.call(this, props, context));
bindCtx(_this, ['onKeyDown', 'beforePosition', 'adjustPosition', 'getOverlayRef']);
return _this;
}
Dialog.prototype.componentDidMount = function componentDidMount() {
_util.events.on(document, 'keydown', this.onKeyDown);
if (!this.useCSSToPosition()) {
this.adjustPosition();
}
};
Dialog.prototype.componentWillUnmount = function componentWillUnmount() {
_util.events.off(document, 'keydown', this.onKeyDown);
};
Dialog.prototype.useCSSToPosition = function useCSSToPosition() {
var _props = this.props,
align = _props.align,
isFullScreen = _props.isFullScreen;
return align === 'cc cc' && isFullScreen;
};
Dialog.prototype.onKeyDown = function onKeyDown(e) {
var node = this.getInnerNode();
if (node) {
limitTabRange(node, e);
}
};
Dialog.prototype.beforePosition = function beforePosition() {
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 adjustPosition() {
if (this.props.visible && this.overlay) {
var inner = this.getInner();
if (inner) {
var node = this.getInnerNode();
var top = getStyle(node, 'top');
var minMargin = this.props.minMargin;
if (top < minMargin) {
top = minMargin;
setStyle(node, 'top', minMargin + 'px');
}
var height = _getSize(node, 'height');
var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
if (viewportHeight < height + top * 2 - 1 || // 分辨率和精确度的原因 高度计算的时候 可能会有1px内的偏差
this.props.height) {
this.adjustSize(inner, node, Math.min(height, viewportHeight - top * 2));
} else {
this.revertSize(inner.bodyNode);
}
this._lastDialogHeight = height;
}
}
};
Dialog.prototype.adjustSize = function adjustSize(inner, node, expectHeight) {
var headerNode = inner.headerNode,
bodyNode = inner.bodyNode,
footerNode = inner.footerNode;
var _map = [headerNode, footerNode].map(function (node) {
return node ? _getSize(node, 'height') : 0;
}),
headerHeight = _map[0],
footerHeight = _map[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': maxBodyHeight + 'px',
'overflow-y': 'auto'
});
}
};
Dialog.prototype.revertSize = function revertSize(bodyNode) {
setStyle(bodyNode, {
'max-height': this.dialogBodyStyleMaxHeight,
'overflow-y': this.dialogBodyStyleOverflowY
});
};
Dialog.prototype.mapcloseableToConfig = function mapcloseableToConfig(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' + key] = value;
} else {
ret['canCloseBy' + key + 'Click'] = value;
}
return ret;
}, {});
};
Dialog.prototype.getOverlayRef = function getOverlayRef(ref) {
this.overlay = ref;
};
Dialog.prototype.getInner = function getInner() {
return this.overlay.getInstance().getContent();
};
Dialog.prototype.getInnerNode = function getInnerNode() {
return this.overlay.getInstance().getContentNode();
};
Dialog.prototype.renderInner = function renderInner(closeable) {
var _props2 = this.props,
prefix = _props2.prefix,
className = _props2.className,
title = _props2.title,
children = _props2.children,
footer = _props2.footer,
footerAlign = _props2.footerAlign,
footerActions = _props2.footerActions,
onOk = _props2.onOk,
onCancel = _props2.onCancel,
okProps = _props2.okProps,
cancelProps = _props2.cancelProps,
onClose = _props2.onClose,
locale = _props2.locale,
visible = _props2.visible,
rtl = _props2.rtl,
height = _props2.height;
var others = pickOthers(Object.keys(Dialog.propTypes), this.props);
return _react2.default.createElement(
_inner2.default,
(0, _extends3.default)({
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
}, others),
children
);
};
Dialog.prototype.render = function render() {
var _props3 = this.props,
prefix = _props3.prefix,
visible = _props3.visible,
hasMask = _props3.hasMask,
animation = _props3.animation,
autoFocus = _props3.autoFocus,
closeable = _props3.closeable,
closeMode = _props3.closeMode,
onClose = _props3.onClose,
afterClose = _props3.afterClose,
shouldUpdatePosition = _props3.shouldUpdatePosition,
align = _props3.align,
popupContainer = _props3.popupContainer,
cache = _props3.cache,
overlayProps = _props3.overlayProps,
rtl = _props3.rtl;
var useCSS = this.useCSSToPosition();
var newCloseable = 'closeMode' in this.props ? Array.isArray(closeMode) ? closeMode.join(',') : closeMode : closeable;
var _mapcloseableToConfig = this.mapcloseableToConfig(newCloseable),
canCloseByCloseClick = _mapcloseableToConfig.canCloseByCloseClick,
closeConfig = (0, _objectWithoutProperties3.default)(_mapcloseableToConfig, ['canCloseByCloseClick']);
var newOverlayProps = (0, _extends3.default)({
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 ? 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 _react2.default.createElement(
_overlay2.default,
newOverlayProps,
useCSS && !hasMask ? _react2.default.createElement(
'div',
{ className: prefix + 'dialog-container', dir: rtl ? 'rtl' : undefined },
inner
) : inner
);
};
return Dialog;
}(_react.Component), _class.propTypes = {
prefix: _propTypes2.default.string,
pure: _propTypes2.default.bool,
rtl: _propTypes2.default.bool,
className: _propTypes2.default.string,
/**
* 是否显示
*/
visible: _propTypes2.default.bool,
/**
* 标题
*/
title: _propTypes2.default.node,
/**
* 内容
*/
children: _propTypes2.default.node,
/**
* 底部内容,设置为 false,则不进行显示
* @default [<Button type="primary">确定</Button>, <Button>取消</Button>]
*/
footer: _propTypes2.default.oneOfType([_propTypes2.default.bool, _propTypes2.default.node]),
/**
* 底部按钮的对齐方式
*/
footerAlign: _propTypes2.default.oneOf(['left', 'center', 'right']),
/**
* 指定确定按钮和取消按钮是否存在以及如何排列,<br><br>**可选值**:
* ['ok', 'cancel'](确认取消按钮同时存在,确认按钮在左)
* ['cancel', 'ok'](确认取消按钮同时存在,确认按钮在右)
* ['ok'](只存在确认按钮)
* ['cancel'](只存在取消按钮)
*/
footerActions: _propTypes2.default.array,
/**
* 在点击确定按钮时触发的回调函数
* @param {Object} event 点击事件对象
*/
onOk: _propTypes2.default.func,
/**
* 在点击取消/关闭按钮时触发的回调函数
* @param {Object} event 点击事件对象, event.triggerType=esc|closeIcon 可区分点击来源
*/
onCancel: _propTypes2.default.func,
/**
* 应用于确定按钮的属性对象
*/
okProps: _propTypes2.default.object,
/**
* 应用于取消按钮的属性对象
*/
cancelProps: _propTypes2.default.object,
/**
* [推荐]1.21.x 支持控制对话框关闭的方式,值可以为字符串或者数组,其中字符串、数组均为以下值的枚举:
* **close** 表示点击关闭按钮可以关闭对话框
* **mask** 表示点击遮罩区域可以关闭对话框
* **esc** 表示按下 esc 键可以关闭对话框
* 如 'close' 或 ['close','esc','mask'], []
* @version 1.21
*/
closeMode: _propTypes2.default.oneOfType([_propTypes2.default.arrayOf(_propTypes2.default.oneOf(['close', 'mask', 'esc'])), _propTypes2.default.oneOf(['close', 'mask', 'esc'])]),
/**
* 隐藏时是否保留子节点,不销毁 (低版本通过 overlayProps 实现)
* @version 1.23
*/
cache: _propTypes2.default.bool,
/**
* 对话框关闭后触发的回调函数, 如果有动画,则在动画结束后触发
*/
afterClose: _propTypes2.default.func,
/**
* 是否显示遮罩
*/
hasMask: _propTypes2.default.bool,
/**
* 显示隐藏时动画的播放方式,支持 { in: 'enter-class', out: 'leave-class' } 的对象参数,如果设置为 false,则不播放动画。 请参考 Animate 组件的文档获取可用的动画名
* @default { in: 'expandInDown', out: 'expandOutUp' }
*/
animation: _propTypes2.default.oneOfType([_propTypes2.default.object, _propTypes2.default.bool]),
/**
* 对话框弹出时是否自动获得焦点
*/
autoFocus: _propTypes2.default.bool,
/**
* [v2废弃] 透传到弹层组件的属性对象
*/
overlayProps: _propTypes2.default.object,
/**
* 自定义国际化文案对象
* @property {String} ok 确认按钮文案
* @property {String} cancel 取消按钮文案
*/
locale: _propTypes2.default.object,
// Do not remove this, it's for <ConfigProvider popupContainer={} />
// see https://github.com/alibaba-fusion/next/issues/1508
/**
* 自定义弹窗挂载位置
*/
popupContainer: _propTypes2.default.any,
/**
* 对话框的高度样式属性
*/
height: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.number]),
/**
* 开启 v2 版本弹窗
*/
v2: _propTypes2.default.bool,
/**
* [v2] 弹窗宽度
* @version 1.25
*/
width: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.number]),
/**
* [v2] 弹窗上边距。默认 100,设置 centered=true 后默认 40
* @version 1.25
*/
top: _propTypes2.default.number,
/**
* [v2] 弹窗下边距
* @version 1.25
*/
bottom: _propTypes2.default.number,
/**
* [v2] 定制关闭按钮 icon
* @version 1.25
*/
closeIcon: _propTypes2.default.node,
/**
* [v2] 弹窗居中对齐
* @version 1.25
*/
centered: _propTypes2.default.bool,
/**
* [v2] 对话框高度超过浏览器视口高度时,对话框是否展示滚动条。关闭此功后对话框会随高度撑开页面
* @version 1.25
*/
overflowScroll: _propTypes2.default.bool,
/**
* [废弃]同closeMode, 控制对话框关闭的方式,值可以为字符串或者布尔值,其中字符串是由以下值组成:
* **close** 表示点击关闭按钮可以关闭对话框
* **mask** 表示点击遮罩区域可以关闭对话框
* **esc** 表示按下 esc 键可以关闭对话框
* 如 'close' 或 'close,esc,mask'
* 如果设置为 true,则以上关闭方式全部生效
* 如果设置为 false,则以上关闭方式全部失效
*/
closeable: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.bool]),
/**
* 点击对话框关闭按钮时触发的回调函数
* @param {String} trigger 关闭触发行为的描述字符串
* @param {Object} event 关闭时事件对象
*/
onClose: _propTypes2.default.func,
/**
* [v2废弃] 对话框对齐方式, 具体见Overlay文档
*/
align: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.bool]),
/**
* [v2废弃] 是否撑开页面。 v2 改用 overflowScroll
*/
isFullScreen: _propTypes2.default.bool,
/**
* [v2废弃] 是否在对话框重新渲染时及时更新对话框位置,一般用于对话框高度变化后依然能保证原来的对齐方式
*/
shouldUpdatePosition: _propTypes2.default.bool,
/**
* [v2废弃] 对话框距离浏览器顶部和底部的最小间距,align 被设置为 'cc cc' 并且 isFullScreen 被设置为 true 时不生效
*/
minMargin: _propTypes2.default.number
}, _class.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: _zhCn2.default.Dialog
}, _temp);
Dialog.displayName = 'Dialog';
exports.default = Dialog;
module.exports = exports['default'];