Popup.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. import _typeof from 'babel-runtime/helpers/typeof';
  2. import _extends from 'babel-runtime/helpers/extends';
  3. import PropTypes from '../_util/vue-types';
  4. import Align from '../vc-align';
  5. import PopupInner from './PopupInner';
  6. import LazyRenderBox from './LazyRenderBox';
  7. import animate from '../_util/css-animation';
  8. import BaseMixin from '../_util/BaseMixin';
  9. import { getListeners } from '../_util/props-util';
  10. export default {
  11. name: 'VCTriggerPopup',
  12. mixins: [BaseMixin],
  13. props: {
  14. visible: PropTypes.bool,
  15. getClassNameFromAlign: PropTypes.func,
  16. getRootDomNode: PropTypes.func,
  17. align: PropTypes.any,
  18. destroyPopupOnHide: PropTypes.bool,
  19. prefixCls: PropTypes.string,
  20. getContainer: PropTypes.func,
  21. transitionName: PropTypes.string,
  22. animation: PropTypes.any,
  23. maskAnimation: PropTypes.string,
  24. maskTransitionName: PropTypes.string,
  25. mask: PropTypes.bool,
  26. zIndex: PropTypes.number,
  27. popupClassName: PropTypes.any,
  28. popupStyle: PropTypes.object.def(function () {
  29. return {};
  30. }),
  31. stretch: PropTypes.string,
  32. point: PropTypes.shape({
  33. pageX: PropTypes.number,
  34. pageY: PropTypes.number
  35. })
  36. },
  37. data: function data() {
  38. this.domEl = null;
  39. return {
  40. // Used for stretch
  41. stretchChecked: false,
  42. targetWidth: undefined,
  43. targetHeight: undefined
  44. };
  45. },
  46. mounted: function mounted() {
  47. var _this = this;
  48. this.$nextTick(function () {
  49. _this.rootNode = _this.getPopupDomNode();
  50. _this.setStretchSize();
  51. });
  52. },
  53. // 如添加会导致动画失效,如放开会导致快速输入时闪动 https://github.com/vueComponent/ant-design-vue/issues/1327,
  54. // 目前方案是保留动画,闪动问题(动画多次执行)进一步定位
  55. // beforeUpdate() {
  56. // if (this.domEl && this.domEl.rcEndListener) {
  57. // this.domEl.rcEndListener();
  58. // this.domEl = null;
  59. // }
  60. // },
  61. updated: function updated() {
  62. var _this2 = this;
  63. this.$nextTick(function () {
  64. _this2.setStretchSize();
  65. });
  66. },
  67. beforeDestroy: function beforeDestroy() {
  68. if (this.$el.parentNode) {
  69. this.$el.parentNode.removeChild(this.$el);
  70. } else if (this.$el.remove) {
  71. this.$el.remove();
  72. }
  73. },
  74. methods: {
  75. onAlign: function onAlign(popupDomNode, align) {
  76. var props = this.$props;
  77. var currentAlignClassName = props.getClassNameFromAlign(align);
  78. // FIX: https://github.com/react-component/trigger/issues/56
  79. // FIX: https://github.com/react-component/tooltip/issues/79
  80. if (this.currentAlignClassName !== currentAlignClassName) {
  81. this.currentAlignClassName = currentAlignClassName;
  82. popupDomNode.className = this.getClassName(currentAlignClassName);
  83. }
  84. var listeners = getListeners(this);
  85. listeners.align && listeners.align(popupDomNode, align);
  86. },
  87. // Record size if stretch needed
  88. setStretchSize: function setStretchSize() {
  89. var _$props = this.$props,
  90. stretch = _$props.stretch,
  91. getRootDomNode = _$props.getRootDomNode,
  92. visible = _$props.visible;
  93. var _$data = this.$data,
  94. stretchChecked = _$data.stretchChecked,
  95. targetHeight = _$data.targetHeight,
  96. targetWidth = _$data.targetWidth;
  97. if (!stretch || !visible) {
  98. if (stretchChecked) {
  99. this.setState({ stretchChecked: false });
  100. }
  101. return;
  102. }
  103. var $ele = getRootDomNode();
  104. if (!$ele) return;
  105. var height = $ele.offsetHeight;
  106. var width = $ele.offsetWidth;
  107. if (targetHeight !== height || targetWidth !== width || !stretchChecked) {
  108. this.setState({
  109. stretchChecked: true,
  110. targetHeight: height,
  111. targetWidth: width
  112. });
  113. }
  114. },
  115. getPopupDomNode: function getPopupDomNode() {
  116. return this.$refs.popupInstance ? this.$refs.popupInstance.$el : null;
  117. },
  118. getTargetElement: function getTargetElement() {
  119. return this.$props.getRootDomNode();
  120. },
  121. // `target` on `rc-align` can accept as a function to get the bind element or a point.
  122. // ref: https://www.npmjs.com/package/rc-align
  123. getAlignTarget: function getAlignTarget() {
  124. var point = this.$props.point;
  125. if (point) {
  126. return point;
  127. }
  128. return this.getTargetElement;
  129. },
  130. getMaskTransitionName: function getMaskTransitionName() {
  131. var props = this.$props;
  132. var transitionName = props.maskTransitionName;
  133. var animation = props.maskAnimation;
  134. if (!transitionName && animation) {
  135. transitionName = props.prefixCls + '-' + animation;
  136. }
  137. return transitionName;
  138. },
  139. getTransitionName: function getTransitionName() {
  140. var props = this.$props;
  141. var transitionName = props.transitionName;
  142. var animation = props.animation;
  143. if (!transitionName) {
  144. if (typeof animation === 'string') {
  145. transitionName = '' + animation;
  146. } else if (animation && animation.props && animation.props.name) {
  147. transitionName = animation.props.name;
  148. }
  149. }
  150. return transitionName;
  151. },
  152. getClassName: function getClassName(currentAlignClassName) {
  153. return this.$props.prefixCls + ' ' + this.$props.popupClassName + ' ' + currentAlignClassName;
  154. },
  155. getPopupElement: function getPopupElement() {
  156. var _this3 = this;
  157. var h = this.$createElement;
  158. var props = this.$props,
  159. $slots = this.$slots,
  160. getTransitionName = this.getTransitionName;
  161. var _$data2 = this.$data,
  162. stretchChecked = _$data2.stretchChecked,
  163. targetHeight = _$data2.targetHeight,
  164. targetWidth = _$data2.targetWidth;
  165. var align = props.align,
  166. visible = props.visible,
  167. prefixCls = props.prefixCls,
  168. animation = props.animation,
  169. popupStyle = props.popupStyle,
  170. getClassNameFromAlign = props.getClassNameFromAlign,
  171. destroyPopupOnHide = props.destroyPopupOnHide,
  172. stretch = props.stretch;
  173. var className = this.getClassName(this.currentAlignClassName || getClassNameFromAlign(align));
  174. // const hiddenClassName = `${prefixCls}-hidden`
  175. if (!visible) {
  176. this.currentAlignClassName = null;
  177. }
  178. var sizeStyle = {};
  179. if (stretch) {
  180. // Stretch with target
  181. if (stretch.indexOf('height') !== -1) {
  182. sizeStyle.height = typeof targetHeight === 'number' ? targetHeight + 'px' : targetHeight;
  183. } else if (stretch.indexOf('minHeight') !== -1) {
  184. sizeStyle.minHeight = typeof targetHeight === 'number' ? targetHeight + 'px' : targetHeight;
  185. }
  186. if (stretch.indexOf('width') !== -1) {
  187. sizeStyle.width = typeof targetWidth === 'number' ? targetWidth + 'px' : targetWidth;
  188. } else if (stretch.indexOf('minWidth') !== -1) {
  189. sizeStyle.minWidth = typeof targetWidth === 'number' ? targetWidth + 'px' : targetWidth;
  190. }
  191. // Delay force align to makes ui smooth
  192. if (!stretchChecked) {
  193. // sizeStyle.visibility = 'hidden'
  194. setTimeout(function () {
  195. if (_this3.$refs.alignInstance) {
  196. _this3.$refs.alignInstance.forceAlign();
  197. }
  198. }, 0);
  199. }
  200. }
  201. var popupInnerProps = {
  202. props: {
  203. prefixCls: prefixCls,
  204. visible: visible
  205. // hiddenClassName,
  206. },
  207. 'class': className,
  208. on: getListeners(this),
  209. ref: 'popupInstance',
  210. style: _extends({}, sizeStyle, popupStyle, this.getZIndexStyle())
  211. };
  212. var transitionProps = {
  213. props: {
  214. appear: true,
  215. css: false
  216. }
  217. };
  218. var transitionName = getTransitionName();
  219. var useTransition = !!transitionName;
  220. var transitionEvent = {
  221. beforeEnter: function beforeEnter() {
  222. // el.style.display = el.__vOriginalDisplay
  223. // this.$refs.alignInstance.forceAlign();
  224. },
  225. enter: function enter(el, done) {
  226. // render 后 vue 会移除通过animate动态添加的 class导致动画闪动,延迟两帧添加动画class,可以进一步定位或者重写 transition 组件
  227. _this3.$nextTick(function () {
  228. if (_this3.$refs.alignInstance) {
  229. _this3.$refs.alignInstance.$nextTick(function () {
  230. _this3.domEl = el;
  231. animate(el, transitionName + '-enter', done);
  232. });
  233. } else {
  234. done();
  235. }
  236. });
  237. },
  238. beforeLeave: function beforeLeave() {
  239. _this3.domEl = null;
  240. },
  241. leave: function leave(el, done) {
  242. animate(el, transitionName + '-leave', done);
  243. }
  244. };
  245. if ((typeof animation === 'undefined' ? 'undefined' : _typeof(animation)) === 'object') {
  246. useTransition = true;
  247. var _animation$on = animation.on,
  248. on = _animation$on === undefined ? {} : _animation$on,
  249. _animation$props = animation.props,
  250. _props = _animation$props === undefined ? {} : _animation$props;
  251. transitionProps.props = _extends({}, transitionProps.props, _props);
  252. transitionProps.on = _extends({}, transitionEvent, on);
  253. } else {
  254. transitionProps.on = transitionEvent;
  255. }
  256. if (!useTransition) {
  257. transitionProps = {};
  258. }
  259. if (destroyPopupOnHide) {
  260. return h(
  261. 'transition',
  262. transitionProps,
  263. [visible ? h(
  264. Align,
  265. {
  266. attrs: {
  267. target: this.getAlignTarget(),
  268. monitorWindowResize: true,
  269. align: align
  270. },
  271. key: 'popup',
  272. ref: 'alignInstance', on: {
  273. 'align': this.onAlign
  274. }
  275. },
  276. [h(
  277. PopupInner,
  278. popupInnerProps,
  279. [$slots['default']]
  280. )]
  281. ) : null]
  282. );
  283. }
  284. return h(
  285. 'transition',
  286. transitionProps,
  287. [h(
  288. Align,
  289. {
  290. directives: [{
  291. name: 'show',
  292. value: visible
  293. }],
  294. attrs: {
  295. target: this.getAlignTarget(),
  296. monitorWindowResize: true,
  297. disabled: !visible,
  298. align: align
  299. },
  300. key: 'popup',
  301. ref: 'alignInstance', on: {
  302. 'align': this.onAlign
  303. }
  304. },
  305. [h(
  306. PopupInner,
  307. popupInnerProps,
  308. [$slots['default']]
  309. )]
  310. )]
  311. );
  312. },
  313. getZIndexStyle: function getZIndexStyle() {
  314. var style = {};
  315. var props = this.$props;
  316. if (props.zIndex !== undefined) {
  317. style.zIndex = props.zIndex;
  318. }
  319. return style;
  320. },
  321. getMaskElement: function getMaskElement() {
  322. var h = this.$createElement;
  323. var props = this.$props;
  324. var maskElement = null;
  325. if (props.mask) {
  326. var maskTransition = this.getMaskTransitionName();
  327. maskElement = h(LazyRenderBox, {
  328. directives: [{
  329. name: 'show',
  330. value: props.visible
  331. }],
  332. style: this.getZIndexStyle(),
  333. key: 'mask',
  334. 'class': props.prefixCls + '-mask',
  335. attrs: { visible: props.visible
  336. }
  337. });
  338. if (maskTransition) {
  339. maskElement = h(
  340. 'transition',
  341. {
  342. attrs: { appear: true, name: maskTransition }
  343. },
  344. [maskElement]
  345. );
  346. }
  347. }
  348. return maskElement;
  349. }
  350. },
  351. render: function render() {
  352. var h = arguments[0];
  353. var getMaskElement = this.getMaskElement,
  354. getPopupElement = this.getPopupElement;
  355. return h('div', [getMaskElement(), getPopupElement()]);
  356. }
  357. };