Dialog.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. 'use strict';
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. var _babelHelperVueJsxMergeProps = require('babel-helper-vue-jsx-merge-props');
  6. var _babelHelperVueJsxMergeProps2 = _interopRequireDefault(_babelHelperVueJsxMergeProps);
  7. var _defineProperty2 = require('babel-runtime/helpers/defineProperty');
  8. var _defineProperty3 = _interopRequireDefault(_defineProperty2);
  9. var _extends2 = require('babel-runtime/helpers/extends');
  10. var _extends3 = _interopRequireDefault(_extends2);
  11. var _propsUtil = require('../_util/props-util');
  12. var _KeyCode = require('../_util/KeyCode');
  13. var _KeyCode2 = _interopRequireDefault(_KeyCode);
  14. var _contains = require('../vc-util/Dom/contains');
  15. var _contains2 = _interopRequireDefault(_contains);
  16. var _LazyRenderBox = require('./LazyRenderBox');
  17. var _LazyRenderBox2 = _interopRequireDefault(_LazyRenderBox);
  18. var _BaseMixin = require('../_util/BaseMixin');
  19. var _BaseMixin2 = _interopRequireDefault(_BaseMixin);
  20. var _getTransitionProps = require('../_util/getTransitionProps');
  21. var _getTransitionProps2 = _interopRequireDefault(_getTransitionProps);
  22. var _switchScrollingEffect2 = require('../_util/switchScrollingEffect');
  23. var _switchScrollingEffect3 = _interopRequireDefault(_switchScrollingEffect2);
  24. var _IDialogPropTypes = require('./IDialogPropTypes');
  25. var _IDialogPropTypes2 = _interopRequireDefault(_IDialogPropTypes);
  26. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  27. var IDialogPropTypes = (0, _IDialogPropTypes2['default'])();
  28. var uuid = 0;
  29. function noop() {}
  30. function getScroll(w, top) {
  31. var ret = w['page' + (top ? 'Y' : 'X') + 'Offset'];
  32. var method = 'scroll' + (top ? 'Top' : 'Left');
  33. if (typeof ret !== 'number') {
  34. var d = w.document;
  35. ret = d.documentElement[method];
  36. if (typeof ret !== 'number') {
  37. ret = d.body[method];
  38. }
  39. }
  40. return ret;
  41. }
  42. function setTransformOrigin(node, value) {
  43. var style = node.style;
  44. ['Webkit', 'Moz', 'Ms', 'ms'].forEach(function (prefix) {
  45. style[prefix + 'TransformOrigin'] = value;
  46. });
  47. style['transformOrigin'] = value;
  48. }
  49. function offset(el) {
  50. var rect = el.getBoundingClientRect();
  51. var pos = {
  52. left: rect.left,
  53. top: rect.top
  54. };
  55. var doc = el.ownerDocument;
  56. var w = doc.defaultView || doc.parentWindow;
  57. pos.left += getScroll(w);
  58. pos.top += getScroll(w, true);
  59. return pos;
  60. }
  61. var cacheOverflow = {};
  62. exports['default'] = {
  63. mixins: [_BaseMixin2['default']],
  64. props: (0, _propsUtil.initDefaultProps)(IDialogPropTypes, {
  65. mask: true,
  66. visible: false,
  67. keyboard: true,
  68. closable: true,
  69. maskClosable: true,
  70. destroyOnClose: false,
  71. prefixCls: 'rc-dialog',
  72. getOpenCount: function getOpenCount() {
  73. return null;
  74. },
  75. focusTriggerAfterClose: true
  76. }),
  77. data: function data() {
  78. return {
  79. destroyPopup: false
  80. };
  81. },
  82. provide: function provide() {
  83. return {
  84. dialogContext: this
  85. };
  86. },
  87. watch: {
  88. visible: function visible(val) {
  89. var _this = this;
  90. if (val) {
  91. this.destroyPopup = false;
  92. }
  93. this.$nextTick(function () {
  94. _this.updatedCallback(!val);
  95. });
  96. }
  97. },
  98. beforeMount: function beforeMount() {
  99. this.inTransition = false;
  100. this.titleId = 'rcDialogTitle' + uuid++;
  101. },
  102. mounted: function mounted() {
  103. var _this2 = this;
  104. this.$nextTick(function () {
  105. _this2.updatedCallback(false);
  106. // if forceRender is true, set element style display to be none;
  107. if ((_this2.forceRender || _this2.getContainer === false && !_this2.visible) && _this2.$refs.wrap) {
  108. _this2.$refs.wrap.style.display = 'none';
  109. }
  110. });
  111. },
  112. beforeDestroy: function beforeDestroy() {
  113. var visible = this.visible,
  114. getOpenCount = this.getOpenCount;
  115. if ((visible || this.inTransition) && !getOpenCount()) {
  116. this.switchScrollingEffect();
  117. }
  118. clearTimeout(this.timeoutId);
  119. },
  120. methods: {
  121. // 对外暴露的 api 不要更改名称或删除
  122. getDialogWrap: function getDialogWrap() {
  123. return this.$refs.wrap;
  124. },
  125. updatedCallback: function updatedCallback(visible) {
  126. var mousePosition = this.mousePosition;
  127. var mask = this.mask,
  128. focusTriggerAfterClose = this.focusTriggerAfterClose;
  129. if (this.visible) {
  130. // first show
  131. if (!visible) {
  132. this.openTime = Date.now();
  133. // this.lastOutSideFocusNode = document.activeElement
  134. this.switchScrollingEffect();
  135. // this.$refs.wrap.focus()
  136. this.tryFocus();
  137. var dialogNode = this.$refs.dialog.$el;
  138. if (mousePosition) {
  139. var elOffset = offset(dialogNode);
  140. setTransformOrigin(dialogNode, mousePosition.x - elOffset.left + 'px ' + (mousePosition.y - elOffset.top) + 'px');
  141. } else {
  142. setTransformOrigin(dialogNode, '');
  143. }
  144. }
  145. } else if (visible) {
  146. this.inTransition = true;
  147. if (mask && this.lastOutSideFocusNode && focusTriggerAfterClose) {
  148. try {
  149. this.lastOutSideFocusNode.focus();
  150. } catch (e) {
  151. this.lastOutSideFocusNode = null;
  152. }
  153. this.lastOutSideFocusNode = null;
  154. }
  155. }
  156. },
  157. tryFocus: function tryFocus() {
  158. if (!(0, _contains2['default'])(this.$refs.wrap, document.activeElement)) {
  159. this.lastOutSideFocusNode = document.activeElement;
  160. this.$refs.sentinelStart.focus();
  161. }
  162. },
  163. onAnimateLeave: function onAnimateLeave() {
  164. var afterClose = this.afterClose,
  165. destroyOnClose = this.destroyOnClose;
  166. // need demo?
  167. // https://github.com/react-component/dialog/pull/28
  168. if (this.$refs.wrap) {
  169. this.$refs.wrap.style.display = 'none';
  170. }
  171. if (destroyOnClose) {
  172. this.destroyPopup = true;
  173. }
  174. this.inTransition = false;
  175. this.switchScrollingEffect();
  176. if (afterClose) {
  177. afterClose();
  178. }
  179. },
  180. onDialogMouseDown: function onDialogMouseDown() {
  181. this.dialogMouseDown = true;
  182. },
  183. onMaskMouseUp: function onMaskMouseUp() {
  184. var _this3 = this;
  185. if (this.dialogMouseDown) {
  186. this.timeoutId = setTimeout(function () {
  187. _this3.dialogMouseDown = false;
  188. }, 0);
  189. }
  190. },
  191. onMaskClick: function onMaskClick(e) {
  192. // android trigger click on open (fastclick??)
  193. if (Date.now() - this.openTime < 300) {
  194. return;
  195. }
  196. if (e.target === e.currentTarget && !this.dialogMouseDown) {
  197. this.close(e);
  198. }
  199. },
  200. onKeydown: function onKeydown(e) {
  201. var props = this.$props;
  202. if (props.keyboard && e.keyCode === _KeyCode2['default'].ESC) {
  203. e.stopPropagation();
  204. this.close(e);
  205. return;
  206. }
  207. // keep focus inside dialog
  208. if (props.visible) {
  209. if (e.keyCode === _KeyCode2['default'].TAB) {
  210. var activeElement = document.activeElement;
  211. var sentinelStart = this.$refs.sentinelStart;
  212. if (e.shiftKey) {
  213. if (activeElement === sentinelStart) {
  214. this.$refs.sentinelEnd.focus();
  215. }
  216. } else if (activeElement === this.$refs.sentinelEnd) {
  217. sentinelStart.focus();
  218. }
  219. }
  220. }
  221. },
  222. getDialogElement: function getDialogElement() {
  223. var h = this.$createElement;
  224. var closable = this.closable,
  225. prefixCls = this.prefixCls,
  226. width = this.width,
  227. height = this.height,
  228. title = this.title,
  229. tempFooter = this.footer,
  230. bodyStyle = this.bodyStyle,
  231. visible = this.visible,
  232. bodyProps = this.bodyProps,
  233. forceRender = this.forceRender,
  234. dialogStyle = this.dialogStyle,
  235. dialogClass = this.dialogClass;
  236. var dest = (0, _extends3['default'])({}, dialogStyle);
  237. if (width !== undefined) {
  238. dest.width = typeof width === 'number' ? width + 'px' : width;
  239. }
  240. if (height !== undefined) {
  241. dest.height = typeof height === 'number' ? height + 'px' : height;
  242. }
  243. var footer = void 0;
  244. if (tempFooter) {
  245. footer = h(
  246. 'div',
  247. { key: 'footer', 'class': prefixCls + '-footer', ref: 'footer' },
  248. [tempFooter]
  249. );
  250. }
  251. var header = void 0;
  252. if (title) {
  253. header = h(
  254. 'div',
  255. { key: 'header', 'class': prefixCls + '-header', ref: 'header' },
  256. [h(
  257. 'div',
  258. { 'class': prefixCls + '-title', attrs: { id: this.titleId }
  259. },
  260. [title]
  261. )]
  262. );
  263. }
  264. var closer = void 0;
  265. if (closable) {
  266. var closeIcon = (0, _propsUtil.getComponentFromProp)(this, 'closeIcon');
  267. closer = h(
  268. 'button',
  269. {
  270. attrs: {
  271. type: 'button',
  272. 'aria-label': 'Close'
  273. },
  274. key: 'close',
  275. on: {
  276. 'click': this.close || noop
  277. },
  278. 'class': prefixCls + '-close'
  279. },
  280. [closeIcon || h('span', { 'class': prefixCls + '-close-x' })]
  281. );
  282. }
  283. var style = dest;
  284. var sentinelStyle = { width: 0, height: 0, overflow: 'hidden' };
  285. var cls = (0, _defineProperty3['default'])({}, prefixCls, true);
  286. var transitionName = this.getTransitionName();
  287. var dialogElement = h(
  288. _LazyRenderBox2['default'],
  289. {
  290. directives: [{
  291. name: 'show',
  292. value: visible
  293. }],
  294. key: 'dialog-element',
  295. attrs: { role: 'document',
  296. forceRender: forceRender
  297. },
  298. ref: 'dialog',
  299. style: style,
  300. 'class': [cls, dialogClass], on: {
  301. 'mousedown': this.onDialogMouseDown
  302. }
  303. },
  304. [h('div', {
  305. attrs: { tabIndex: 0, 'aria-hidden': 'true' },
  306. ref: 'sentinelStart', style: sentinelStyle }), h(
  307. 'div',
  308. { 'class': prefixCls + '-content' },
  309. [closer, header, h(
  310. 'div',
  311. (0, _babelHelperVueJsxMergeProps2['default'])([{ key: 'body', 'class': prefixCls + '-body', style: bodyStyle, ref: 'body' }, bodyProps]),
  312. [this.$slots['default']]
  313. ), footer]
  314. ), h('div', {
  315. attrs: { tabIndex: 0, 'aria-hidden': 'true' },
  316. ref: 'sentinelEnd', style: sentinelStyle })]
  317. );
  318. var dialogTransitionProps = (0, _getTransitionProps2['default'])(transitionName, {
  319. afterLeave: this.onAnimateLeave
  320. });
  321. return h(
  322. 'transition',
  323. (0, _babelHelperVueJsxMergeProps2['default'])([{ key: 'dialog' }, dialogTransitionProps]),
  324. [visible || !this.destroyPopup ? dialogElement : null]
  325. );
  326. },
  327. getZIndexStyle: function getZIndexStyle() {
  328. var style = {};
  329. var props = this.$props;
  330. if (props.zIndex !== undefined) {
  331. style.zIndex = props.zIndex;
  332. }
  333. return style;
  334. },
  335. getWrapStyle: function getWrapStyle() {
  336. return (0, _extends3['default'])({}, this.getZIndexStyle(), this.wrapStyle);
  337. },
  338. getMaskStyle: function getMaskStyle() {
  339. return (0, _extends3['default'])({}, this.getZIndexStyle(), this.maskStyle);
  340. },
  341. getMaskElement: function getMaskElement() {
  342. var h = this.$createElement;
  343. var props = this.$props;
  344. var maskElement = void 0;
  345. if (props.mask) {
  346. var maskTransition = this.getMaskTransitionName();
  347. maskElement = h(_LazyRenderBox2['default'], (0, _babelHelperVueJsxMergeProps2['default'])([{
  348. directives: [{
  349. name: 'show',
  350. value: props.visible
  351. }],
  352. style: this.getMaskStyle(),
  353. key: 'mask',
  354. 'class': props.prefixCls + '-mask'
  355. }, props.maskProps]));
  356. if (maskTransition) {
  357. var maskTransitionProps = (0, _getTransitionProps2['default'])(maskTransition);
  358. maskElement = h(
  359. 'transition',
  360. (0, _babelHelperVueJsxMergeProps2['default'])([{ key: 'mask' }, maskTransitionProps]),
  361. [maskElement]
  362. );
  363. }
  364. }
  365. return maskElement;
  366. },
  367. getMaskTransitionName: function getMaskTransitionName() {
  368. var props = this.$props;
  369. var transitionName = props.maskTransitionName;
  370. var animation = props.maskAnimation;
  371. if (!transitionName && animation) {
  372. transitionName = props.prefixCls + '-' + animation;
  373. }
  374. return transitionName;
  375. },
  376. getTransitionName: function getTransitionName() {
  377. var props = this.$props;
  378. var transitionName = props.transitionName;
  379. var animation = props.animation;
  380. if (!transitionName && animation) {
  381. transitionName = props.prefixCls + '-' + animation;
  382. }
  383. return transitionName;
  384. },
  385. // setScrollbar() {
  386. // if (this.bodyIsOverflowing && this.scrollbarWidth !== undefined) {
  387. // document.body.style.paddingRight = `${this.scrollbarWidth}px`;
  388. // }
  389. // },
  390. switchScrollingEffect: function switchScrollingEffect() {
  391. var getOpenCount = this.getOpenCount;
  392. var openCount = getOpenCount();
  393. if (openCount === 1) {
  394. if (cacheOverflow.hasOwnProperty('overflowX')) {
  395. return;
  396. }
  397. cacheOverflow = {
  398. overflowX: document.body.style.overflowX,
  399. overflowY: document.body.style.overflowY,
  400. overflow: document.body.style.overflow
  401. };
  402. (0, _switchScrollingEffect3['default'])();
  403. // Must be set after switchScrollingEffect
  404. document.body.style.overflow = 'hidden';
  405. } else if (!openCount) {
  406. // IE browser doesn't merge overflow style, need to set it separately
  407. // https://github.com/ant-design/ant-design/issues/19393
  408. if (cacheOverflow.overflow !== undefined) {
  409. document.body.style.overflow = cacheOverflow.overflow;
  410. }
  411. if (cacheOverflow.overflowX !== undefined) {
  412. document.body.style.overflowX = cacheOverflow.overflowX;
  413. }
  414. if (cacheOverflow.overflowY !== undefined) {
  415. document.body.style.overflowY = cacheOverflow.overflowY;
  416. }
  417. cacheOverflow = {};
  418. (0, _switchScrollingEffect3['default'])(true);
  419. }
  420. },
  421. // removeScrollingEffect() {
  422. // const { getOpenCount } = this;
  423. // const openCount = getOpenCount();
  424. // if (openCount !== 0) {
  425. // return;
  426. // }
  427. // document.body.style.overflow = '';
  428. // switchScrollingEffect(true);
  429. // // this.resetAdjustments();
  430. // },
  431. close: function close(e) {
  432. this.__emit('close', e);
  433. }
  434. },
  435. render: function render() {
  436. var h = arguments[0];
  437. var prefixCls = this.prefixCls,
  438. maskClosable = this.maskClosable,
  439. visible = this.visible,
  440. wrapClassName = this.wrapClassName,
  441. title = this.title,
  442. wrapProps = this.wrapProps;
  443. var style = this.getWrapStyle();
  444. // clear hide display
  445. // and only set display after async anim, not here for hide
  446. if (visible) {
  447. style.display = null;
  448. }
  449. return h(
  450. 'div',
  451. { 'class': prefixCls + '-root' },
  452. [this.getMaskElement(), h(
  453. 'div',
  454. (0, _babelHelperVueJsxMergeProps2['default'])([{
  455. attrs: {
  456. tabIndex: -1,
  457. role: 'dialog',
  458. 'aria-labelledby': title ? this.titleId : null
  459. },
  460. on: {
  461. 'keydown': this.onKeydown,
  462. 'click': maskClosable ? this.onMaskClick : noop,
  463. 'mouseup': maskClosable ? this.onMaskMouseUp : noop
  464. },
  465. 'class': prefixCls + '-wrap ' + (wrapClassName || ''),
  466. ref: 'wrap',
  467. style: style
  468. }, wrapProps]),
  469. [this.getDialogElement()]
  470. )]
  471. );
  472. }
  473. };