From 62e5a1abf7fa70d02cc8c2e0ba99f7401a44265a Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Sat, 14 Aug 2021 15:09:36 +0800 Subject: [PATCH] refactor: trigger --- components/vc-trigger/LazyRenderBox.jsx | 27 - components/vc-trigger/Popup/PopupInner.tsx | 12 +- components/vc-trigger/Popup/index.tsx | 10 +- components/vc-trigger/Popup1.jsx | 317 --------- components/vc-trigger/PopupInner.jsx | 21 - components/vc-trigger/Trigger.jsx | 2 +- components/vc-trigger/Trigger2.jsx | 659 ------------------- components/vc-trigger/Trigger2.tsx | 20 +- components/vc-trigger/index.md | 160 ----- components/vc-trigger/{index.js => index.ts} | 2 +- 10 files changed, 32 insertions(+), 1198 deletions(-) delete mode 100644 components/vc-trigger/LazyRenderBox.jsx delete mode 100644 components/vc-trigger/Popup1.jsx delete mode 100644 components/vc-trigger/PopupInner.jsx delete mode 100644 components/vc-trigger/Trigger2.jsx delete mode 100644 components/vc-trigger/index.md rename components/vc-trigger/{index.js => index.ts} (65%) diff --git a/components/vc-trigger/LazyRenderBox.jsx b/components/vc-trigger/LazyRenderBox.jsx deleted file mode 100644 index 5cefd4447..000000000 --- a/components/vc-trigger/LazyRenderBox.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Text } from 'vue'; -import PropTypes from '../_util/vue-types'; -import { getSlot } from '../_util/props-util'; - -export default { - name: 'LazyRenderBox', - props: { - visible: PropTypes.looseBool, - hiddenClassName: PropTypes.string, - }, - render() { - const { hiddenClassName } = this.$props; - const child = getSlot(this); - if ( - hiddenClassName || - (child && child.length > 1) || - (child && child[0] && child[0].type === Text) - ) { - // const cls = ''; - // if (!visible && hiddenClassName) { - // // cls += ` ${hiddenClassName}` - // } - return
{child}
; - } - return child && child[0]; - }, -}; diff --git a/components/vc-trigger/Popup/PopupInner.tsx b/components/vc-trigger/Popup/PopupInner.tsx index 7e22f690f..1d9a1a2ed 100644 --- a/components/vc-trigger/Popup/PopupInner.tsx +++ b/components/vc-trigger/Popup/PopupInner.tsx @@ -154,7 +154,7 @@ export default defineComponent({ onBeforeAppear={onShowPrepare} onBeforeEnter={onShowPrepare} > - {!destroyPopupOnHide ? ( + {!destroyPopupOnHide || visible ? ( {childNode} diff --git a/components/vc-trigger/Popup/index.tsx b/components/vc-trigger/Popup/index.tsx index 41671e546..71b42f906 100644 --- a/components/vc-trigger/Popup/index.tsx +++ b/components/vc-trigger/Popup/index.tsx @@ -8,7 +8,7 @@ export default defineComponent({ props: popupProps, inheritAttrs: false, name: 'Popup', - setup(props, { attrs, slots }) { + setup(props, { attrs, slots, expose }) { const innerVisible = ref(false); const inMobile = ref(false); const popupRef = ref(); @@ -22,6 +22,14 @@ export default defineComponent({ }, { immediate: true, flush: 'post' }, ); + expose({ + forceAlign: () => { + popupRef.value?.forceAlign(); + }, + getElement: () => { + popupRef.value?.getElement(); + }, + }); return () => { const cloneProps = { ...props, ...attrs, visible: innerVisible.value }; const popupNode = inMobile.value ? ( diff --git a/components/vc-trigger/Popup1.jsx b/components/vc-trigger/Popup1.jsx deleted file mode 100644 index 7c49fa836..000000000 --- a/components/vc-trigger/Popup1.jsx +++ /dev/null @@ -1,317 +0,0 @@ -import PropTypes from '../_util/vue-types'; -import Align from '../vc-align'; -import PopupInner from './PopupInner'; -import LazyRenderBox from './LazyRenderBox'; -import BaseMixin from '../_util/BaseMixin'; -import { saveRef } from './utils'; -import { splitAttrs, findDOMNode } from '../_util/props-util'; -import { getTransitionProps, Transition } from '../_util/transition'; - -export default { - name: 'VCTriggerPopup', - mixins: [BaseMixin], - inheritAttrs: false, - props: { - visible: PropTypes.looseBool, - getClassNameFromAlign: PropTypes.func, - getRootDomNode: PropTypes.func, - align: PropTypes.any, - destroyPopupOnHide: PropTypes.looseBool, - prefixCls: PropTypes.string, - getContainer: PropTypes.func, - transitionName: PropTypes.string, - animation: PropTypes.any, - maskAnimation: PropTypes.string, - maskTransitionName: PropTypes.string, - mask: PropTypes.looseBool, - zIndex: PropTypes.number, - popupClassName: PropTypes.any, - popupStyle: PropTypes.object.def(() => ({})), - stretch: PropTypes.string, - point: PropTypes.shape({ - pageX: PropTypes.number, - pageY: PropTypes.number, - }).loose, - }, - data() { - this.domEl = null; - this.currentAlignClassName = undefined; - this.transitionProps = {}; - this.savePopupRef = saveRef.bind(this, 'popupInstance'); - this.saveAlignRef = saveRef.bind(this, 'alignInstance'); - return { - // Used for stretch - stretchChecked: false, - targetWidth: undefined, - targetHeight: undefined, - }; - }, - mounted() { - this.$nextTick(() => { - this.rootNode = this.getPopupDomNode(); - this.setStretchSize(); - }); - }, - // 如添加会导致动画失效,如放开会导致快速输入时闪动 https://github.com/vueComponent/ant-design-vue/issues/1327, - // 目前方案是保留动画,闪动问题(动画多次执行)进一步定位 - // beforeUpdate() { - // if (this.domEl && this.domEl.rcEndListener) { - // this.domEl.rcEndListener(); - // this.domEl = null; - // } - // }, - updated() { - this.$nextTick(() => { - this.setStretchSize(); - }); - }, - methods: { - onAlign(popupDomNode, align) { - const props = this.$props; - const currentAlignClassName = props.getClassNameFromAlign(align); - // FIX: https://github.com/react-component/trigger/issues/56 - // FIX: https://github.com/react-component/tooltip/issues/79 - if (this.currentAlignClassName !== currentAlignClassName) { - this.currentAlignClassName = currentAlignClassName; - popupDomNode.className = this.getClassName(currentAlignClassName, popupDomNode.className); - } - const { onaAlign } = this.$attrs; - onaAlign && onaAlign(popupDomNode, align); - }, - - // Record size if stretch needed - setStretchSize() { - const { stretch, getRootDomNode, visible } = this.$props; - const { stretchChecked, targetHeight, targetWidth } = this.$data; - - if (!stretch || !visible) { - if (stretchChecked) { - this.setState({ stretchChecked: false }); - } - return; - } - - const $ele = getRootDomNode(); - if (!$ele) return; - - const height = $ele.offsetHeight; - const width = $ele.offsetWidth; - - if (targetHeight !== height || targetWidth !== width || !stretchChecked) { - this.setState({ - stretchChecked: true, - targetHeight: height, - targetWidth: width, - }); - } - }, - - getPopupDomNode() { - return findDOMNode(this.popupInstance); - }, - - getTargetElement() { - return this.$props.getRootDomNode(); - }, - - // `target` on `rc-align` can accept as a function to get the bind element or a point. - // ref: https://www.npmjs.com/package/rc-align - getAlignTarget() { - const { point } = this.$props; - if (point) { - return point; - } - return this.getTargetElement; - }, - - getMaskTransitionName() { - const props = this.$props; - let transitionName = props.maskTransitionName; - const animation = props.maskAnimation; - if (!transitionName && animation) { - transitionName = `${props.prefixCls}-${animation}`; - } - return transitionName; - }, - - getTransitionName() { - const props = this.$props; - let transitionName = props.transitionName; - const animation = props.animation; - if (!transitionName) { - if (typeof animation === 'string') { - transitionName = `${animation}`; - } else if (animation && animation.props && animation.props.name) { - transitionName = animation.props.name; - } - } - return transitionName; - }, - - getClassName(currentAlignClassName, originClassName = '') { - // 保留动画 class - const enterActiveClass = []; - if (this.transitionProps) { - Object.keys(this.transitionProps).forEach(k => { - if (typeof this.transitionProps[k] === 'string') { - enterActiveClass.push(...this.transitionProps[k].split(' ')); - } - }); - } - const classNames = originClassName - .split(' ') - .filter(c => enterActiveClass.indexOf(c) !== -1) - .join(' '); - return `${this.$props.prefixCls} ${this.$attrs.class || ''} ${ - this.$props.popupClassName - } ${currentAlignClassName} ${classNames}`; - }, - getPopupElement() { - const { savePopupRef } = this; - const { $props: props, $attrs, $slots, getTransitionName } = this; - const { stretchChecked, targetHeight, targetWidth } = this.$data; - const { style = {} } = $attrs; - const onEvents = splitAttrs($attrs).onEvents; - const { - align, - visible, - prefixCls, - animation, - popupStyle, - getClassNameFromAlign, - destroyPopupOnHide, - stretch, - } = props; - const className = this.getClassName( - this.currentAlignClassName || getClassNameFromAlign(align), - ); - // const hiddenClassName = `${prefixCls}-hidden` - if (!visible) { - this.currentAlignClassName = null; - } - const sizeStyle = {}; - if (stretch) { - // Stretch with target - if (stretch.indexOf('height') !== -1) { - sizeStyle.height = typeof targetHeight === 'number' ? `${targetHeight}px` : targetHeight; - } else if (stretch.indexOf('minHeight') !== -1) { - sizeStyle.minHeight = - typeof targetHeight === 'number' ? `${targetHeight}px` : targetHeight; - } - if (stretch.indexOf('width') !== -1) { - sizeStyle.width = typeof targetWidth === 'number' ? `${targetWidth}px` : targetWidth; - } else if (stretch.indexOf('minWidth') !== -1) { - sizeStyle.minWidth = typeof targetWidth === 'number' ? `${targetWidth}px` : targetWidth; - } - // Delay force align to makes ui smooth - if (!stretchChecked) { - // sizeStyle.visibility = 'hidden' - setTimeout(() => { - if (this.alignInstance) { - this.alignInstance.forceAlign(); - } - }, 0); - } - } - const popupInnerProps = { - prefixCls, - visible, - // hiddenClassName, - class: className, - ...onEvents, - ref: savePopupRef, - style: { ...sizeStyle, ...popupStyle, ...style, ...this.getZIndexStyle() }, - }; - - const transitionName = getTransitionName(); - let useTransition = !!transitionName; - let transitionProps = getTransitionProps(transitionName); - if (typeof animation === 'object') { - useTransition = true; - transitionProps = { ...transitionProps, ...animation }; - } - if (!useTransition) { - transitionProps = {}; - } - this.transitionProps = transitionProps; - if (destroyPopupOnHide) { - return ( - - {visible ? ( - - {$slots.default?.()} - - ) : null} - - ); - } - return ( - - - {$slots.default?.()} - - - ); - }, - - getZIndexStyle() { - const style = {}; - const props = this.$props; - if (props.zIndex !== undefined) { - style.zIndex = props.zIndex; - } - return style; - }, - - getMaskElement() { - const props = this.$props; - let maskElement = null; - if (props.mask) { - const maskTransition = this.getMaskTransitionName(); - maskElement = ( - - ); - if (maskTransition) { - maskElement = ( - - {maskElement} - - ); - } - } - return maskElement; - }, - }, - - render() { - const { getMaskElement, getPopupElement } = this; - return ( -
- {getMaskElement()} - {getPopupElement()} -
- ); - }, -}; diff --git a/components/vc-trigger/PopupInner.jsx b/components/vc-trigger/PopupInner.jsx deleted file mode 100644 index c44d1a189..000000000 --- a/components/vc-trigger/PopupInner.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import PropTypes from '../_util/vue-types'; -import LazyRenderBox from './LazyRenderBox'; - -export default { - props: { - hiddenClassName: PropTypes.string.def(''), - prefixCls: PropTypes.string, - visible: PropTypes.looseBool, - }, - render() { - const { prefixCls, visible, hiddenClassName } = this.$props; - - return ( -
- - {this.$slots.default?.()} - -
- ); - }, -}; diff --git a/components/vc-trigger/Trigger.jsx b/components/vc-trigger/Trigger.jsx index a02cf0bdd..ffde6054f 100644 --- a/components/vc-trigger/Trigger.jsx +++ b/components/vc-trigger/Trigger.jsx @@ -231,7 +231,7 @@ export default defineComponent({ e && e.relatedTarget && !e.relatedTarget.setTimeout && - contains(this.popupRef?.getPopupDomNode(), e.relatedTarget) + contains(this.popupRef?.getElement(), e.relatedTarget) ) { return; } diff --git a/components/vc-trigger/Trigger2.jsx b/components/vc-trigger/Trigger2.jsx deleted file mode 100644 index cbc2f14e4..000000000 --- a/components/vc-trigger/Trigger2.jsx +++ /dev/null @@ -1,659 +0,0 @@ -import { defineComponent, inject, provide } from 'vue'; -import PropTypes from '../_util/vue-types'; -import contains from '../vc-util/Dom/contains'; -import { - hasProp, - getComponent, - getEvents, - filterEmpty, - getSlot, - findDOMNode, -} from '../_util/props-util'; -import { requestAnimationTimeout, cancelAnimationTimeout } from '../_util/requestAnimationTimeout'; -import addEventListener from '../vc-util/Dom/addEventListener'; -import warning from '../_util/warning'; -import Popup from './Popup'; -import { getAlignFromPlacement, getAlignPopupClassName, noop } from './utils'; -import BaseMixin from '../_util/BaseMixin'; -import Portal from '../_util/Portal'; -import classNames from '../_util/classNames'; -import { cloneElement } from '../_util/vnode'; -import supportsPassive from '../_util/supportsPassive'; - -function returnEmptyString() { - return ''; -} - -function returnDocument() { - return window.document; -} -const ALL_HANDLERS = [ - 'onClick', - 'onMousedown', - 'onTouchstart', - 'onMouseenter', - 'onMouseleave', - 'onFocus', - 'onBlur', - 'onContextmenu', -]; - -export default defineComponent({ - name: 'Trigger', - mixins: [BaseMixin], - inheritAttrs: false, - props: { - action: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]).def([]), - showAction: PropTypes.any.def([]), - hideAction: PropTypes.any.def([]), - getPopupClassNameFromAlign: PropTypes.any.def(returnEmptyString), - onPopupVisibleChange: PropTypes.func.def(noop), - afterPopupVisibleChange: PropTypes.func.def(noop), - popup: PropTypes.any, - popupStyle: PropTypes.object.def(() => ({})), - prefixCls: PropTypes.string.def('rc-trigger-popup'), - popupClassName: PropTypes.string.def(''), - popupPlacement: PropTypes.string, - builtinPlacements: PropTypes.object, - popupTransitionName: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), - popupAnimation: PropTypes.any, - mouseEnterDelay: PropTypes.number.def(0), - mouseLeaveDelay: PropTypes.number.def(0.1), - zIndex: PropTypes.number, - focusDelay: PropTypes.number.def(0), - blurDelay: PropTypes.number.def(0.15), - getPopupContainer: PropTypes.func, - getDocument: PropTypes.func.def(returnDocument), - forceRender: PropTypes.looseBool, - destroyPopupOnHide: PropTypes.looseBool.def(false), - mask: PropTypes.looseBool.def(false), - maskClosable: PropTypes.looseBool.def(true), - // onPopupAlign: PropTypes.func.def(noop), - popupAlign: PropTypes.object.def(() => ({})), - popupVisible: PropTypes.looseBool, - defaultPopupVisible: PropTypes.looseBool.def(false), - maskTransitionName: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), - maskAnimation: PropTypes.string, - stretch: PropTypes.string, - alignPoint: PropTypes.looseBool, // Maybe we can support user pass position in the future - }, - setup() { - return { - vcTriggerContext: inject('vcTriggerContext', {}), - savePopupRef: inject('savePopupRef', noop), - dialogContext: inject('dialogContext', null), - }; - }, - data() { - const props = this.$props; - let popupVisible; - if (hasProp(this, 'popupVisible')) { - popupVisible = !!props.popupVisible; - } else { - popupVisible = !!props.defaultPopupVisible; - } - ALL_HANDLERS.forEach(h => { - this[`fire${h}`] = e => { - this.fireEvents(h, e); - }; - }); - this._component = null; - this.focusTime = null; - this.clickOutsideHandler = null; - this.contextmenuOutsideHandler1 = null; - this.contextmenuOutsideHandler2 = null; - this.touchOutsideHandler = null; - return { - prevPopupVisible: popupVisible, - sPopupVisible: popupVisible, - point: null, - }; - }, - watch: { - popupVisible(val) { - if (val !== undefined) { - this.prevPopupVisible = this.sPopupVisible; - this.sPopupVisible = val; - } - }, - }, - created() { - provide('vcTriggerContext', this); - }, - deactivated() { - this.setPopupVisible(false); - }, - mounted() { - this.$nextTick(() => { - this.updatedCal(); - }); - }, - - updated() { - this.$nextTick(() => { - this.updatedCal(); - }); - }, - - beforeUnmount() { - this.clearDelayTimer(); - this.clearOutsideHandler(); - clearTimeout(this.mouseDownTimeout); - }, - methods: { - updatedCal() { - const props = this.$props; - const state = this.$data; - - // We must listen to `mousedown` or `touchstart`, edge case: - // https://github.com/ant-design/ant-design/issues/5804 - // https://github.com/react-component/calendar/issues/250 - // https://github.com/react-component/trigger/issues/50 - if (state.sPopupVisible) { - let currentDocument; - if (!this.clickOutsideHandler && (this.isClickToHide() || this.isContextmenuToShow())) { - currentDocument = props.getDocument(); - this.clickOutsideHandler = addEventListener( - currentDocument, - 'mousedown', - this.onDocumentClick, - ); - } - // always hide on mobile - if (!this.touchOutsideHandler) { - currentDocument = currentDocument || props.getDocument(); - this.touchOutsideHandler = addEventListener( - currentDocument, - 'touchstart', - this.onDocumentClick, - supportsPassive ? { passive: false } : false, - ); - } - // close popup when trigger type contains 'onContextmenu' and document is scrolling. - if (!this.contextmenuOutsideHandler1 && this.isContextmenuToShow()) { - currentDocument = currentDocument || props.getDocument(); - this.contextmenuOutsideHandler1 = addEventListener( - currentDocument, - 'scroll', - this.onContextmenuClose, - ); - } - // close popup when trigger type contains 'onContextmenu' and window is blur. - if (!this.contextmenuOutsideHandler2 && this.isContextmenuToShow()) { - this.contextmenuOutsideHandler2 = addEventListener( - window, - 'blur', - this.onContextmenuClose, - ); - } - } else { - this.clearOutsideHandler(); - } - }, - onMouseenter(e) { - const { mouseEnterDelay } = this.$props; - this.fireEvents('onMouseenter', e); - this.delaySetPopupVisible(true, mouseEnterDelay, mouseEnterDelay ? null : e); - }, - - onMouseMove(e) { - this.fireEvents('onMousemove', e); - this.setPoint(e); - }, - - onMouseleave(e) { - this.fireEvents('onMouseleave', e); - this.delaySetPopupVisible(false, this.$props.mouseLeaveDelay); - }, - - onPopupMouseenter() { - this.clearDelayTimer(); - }, - - onPopupMouseleave(e) { - if ( - e && - e.relatedTarget && - !e.relatedTarget.setTimeout && - this._component && - this._component.getPopupDomNode && - contains(this._component.getPopupDomNode(), e.relatedTarget) - ) { - return; - } - this.delaySetPopupVisible(false, this.$props.mouseLeaveDelay); - }, - - onFocus(e) { - this.fireEvents('onFocus', e); - // incase focusin and focusout - this.clearDelayTimer(); - if (this.isFocusToShow()) { - this.focusTime = Date.now(); - this.delaySetPopupVisible(true, this.$props.focusDelay); - } - }, - - onMousedown(e) { - this.fireEvents('onMousedown', e); - this.preClickTime = Date.now(); - }, - - onTouchstart(e) { - this.fireEvents('onTouchstart', e); - this.preTouchTime = Date.now(); - }, - - onBlur(e) { - if (!contains(e.target, e.relatedTarget || document.activeElement)) { - this.fireEvents('onBlur', e); - this.clearDelayTimer(); - if (this.isBlurToHide()) { - this.delaySetPopupVisible(false, this.$props.blurDelay); - } - } - }, - - onContextmenu(e) { - e.preventDefault(); - this.fireEvents('onContextmenu', e); - this.setPopupVisible(true, e); - }, - - onContextmenuClose() { - if (this.isContextmenuToShow()) { - this.close(); - } - }, - - onClick(event) { - this.fireEvents('onClick', event); - // focus will trigger click - if (this.focusTime) { - let preTime; - if (this.preClickTime && this.preTouchTime) { - preTime = Math.min(this.preClickTime, this.preTouchTime); - } else if (this.preClickTime) { - preTime = this.preClickTime; - } else if (this.preTouchTime) { - preTime = this.preTouchTime; - } - if (Math.abs(preTime - this.focusTime) < 20) { - return; - } - this.focusTime = 0; - } - this.preClickTime = 0; - this.preTouchTime = 0; - // Only prevent default when all the action is click. - // https://github.com/ant-design/ant-design/issues/17043 - // https://github.com/ant-design/ant-design/issues/17291 - if ( - this.isClickToShow() && - (this.isClickToHide() || this.isBlurToHide()) && - event && - event.preventDefault - ) { - event.preventDefault(); - } - if (event && event.domEvent) { - event.domEvent.preventDefault(); - } - const nextVisible = !this.$data.sPopupVisible; - if ((this.isClickToHide() && !nextVisible) || (nextVisible && this.isClickToShow())) { - this.setPopupVisible(!this.$data.sPopupVisible, event); - } - }, - onPopupMouseDown(...args) { - const { vcTriggerContext = {} } = this; - this.hasPopupMouseDown = true; - - clearTimeout(this.mouseDownTimeout); - this.mouseDownTimeout = setTimeout(() => { - this.hasPopupMouseDown = false; - }, 0); - - if (vcTriggerContext.onPopupMouseDown) { - vcTriggerContext.onPopupMouseDown(...args); - } - }, - - onDocumentClick(event) { - if (this.$props.mask && !this.$props.maskClosable) { - return; - } - const target = event.target; - const root = findDOMNode(this); - if (!contains(root, target) && !this.hasPopupMouseDown) { - this.close(); - } - }, - getPopupDomNode() { - if (this._component && this._component.getPopupDomNode) { - return this._component.getPopupDomNode(); - } - return null; - }, - - getRootDomNode() { - return findDOMNode(this); - }, - - handleGetPopupClassFromAlign(align) { - const className = []; - const props = this.$props; - const { - popupPlacement, - builtinPlacements, - prefixCls, - alignPoint, - getPopupClassNameFromAlign, - } = props; - if (popupPlacement && builtinPlacements) { - className.push(getAlignPopupClassName(builtinPlacements, prefixCls, align, alignPoint)); - } - if (getPopupClassNameFromAlign) { - className.push(getPopupClassNameFromAlign(align)); - } - return className.join(' '); - }, - - getPopupAlign() { - const props = this.$props; - const { popupPlacement, popupAlign, builtinPlacements } = props; - if (popupPlacement && builtinPlacements) { - return getAlignFromPlacement(builtinPlacements, popupPlacement, popupAlign); - } - return popupAlign; - }, - savePopup(node) { - this._component = node; - this.savePopupRef(node); - }, - getComponent() { - const self = this; - const mouseProps = {}; - if (this.isMouseEnterToShow()) { - mouseProps.onMouseenter = self.onPopupMouseenter; - } - if (this.isMouseLeaveToHide()) { - mouseProps.onMouseleave = self.onPopupMouseleave; - } - mouseProps.onMousedown = this.onPopupMouseDown; - mouseProps[supportsPassive ? 'onTouchstartPassive' : 'onTouchstart'] = this.onPopupMouseDown; - const { handleGetPopupClassFromAlign, getRootDomNode, getContainer, $attrs } = self; - const { - prefixCls, - destroyPopupOnHide, - popupClassName, - action, - popupAnimation, - popupTransitionName, - popupStyle, - mask, - maskAnimation, - maskTransitionName, - zIndex, - stretch, - alignPoint, - } = self.$props; - const { sPopupVisible, point } = this.$data; - const align = this.getPopupAlign(); - const popupProps = { - prefixCls, - destroyPopupOnHide, - visible: sPopupVisible, - point: alignPoint ? point : null, - action, - align, - animation: popupAnimation, - getClassNameFromAlign: handleGetPopupClassFromAlign, - stretch, - getRootDomNode, - mask, - zIndex, - transitionName: popupTransitionName, - maskAnimation, - maskTransitionName, - getContainer, - popupClassName, - popupStyle, - onAlign: $attrs.onPopupAlign || noop, - ...mouseProps, - ref: this.savePopup, - }; - return {getComponent(self, 'popup')}; - }, - - getContainer() { - const { $props: props, dialogContext } = this; - const popupContainer = document.createElement('div'); - // Make sure default popup container will never cause scrollbar appearing - // https://github.com/react-component/trigger/issues/41 - popupContainer.style.position = 'absolute'; - popupContainer.style.top = '0'; - popupContainer.style.left = '0'; - popupContainer.style.width = '100%'; - const mountNode = props.getPopupContainer - ? props.getPopupContainer(findDOMNode(this), dialogContext) - : props.getDocument().body; - mountNode.appendChild(popupContainer); - this.popupContainer = popupContainer; - return popupContainer; - }, - - setPopupVisible(sPopupVisible, event) { - const { alignPoint, sPopupVisible: prevPopupVisible, onPopupVisibleChange } = this; - this.clearDelayTimer(); - if (prevPopupVisible !== sPopupVisible) { - if (!hasProp(this, 'popupVisible')) { - this.setState({ - sPopupVisible, - prevPopupVisible, - }); - } - onPopupVisibleChange && onPopupVisibleChange(sPopupVisible); - } - // Always record the point position since mouseEnterDelay will delay the show - if (alignPoint && event) { - this.setPoint(event); - } - }, - - setPoint(point) { - const { alignPoint } = this.$props; - if (!alignPoint || !point) return; - - this.setState({ - point: { - pageX: point.pageX, - pageY: point.pageY, - }, - }); - }, - handlePortalUpdate() { - if (this.prevPopupVisible !== this.sPopupVisible) { - this.afterPopupVisibleChange(this.sPopupVisible); - } - }, - delaySetPopupVisible(visible, delayS, event) { - const delay = delayS * 1000; - this.clearDelayTimer(); - if (delay) { - const point = event ? { pageX: event.pageX, pageY: event.pageY } : null; - this.delayTimer = requestAnimationTimeout(() => { - this.setPopupVisible(visible, point); - this.clearDelayTimer(); - }, delay); - } else { - this.setPopupVisible(visible, event); - } - }, - - clearDelayTimer() { - if (this.delayTimer) { - cancelAnimationTimeout(this.delayTimer); - this.delayTimer = null; - } - }, - - clearOutsideHandler() { - if (this.clickOutsideHandler) { - this.clickOutsideHandler.remove(); - this.clickOutsideHandler = null; - } - - if (this.contextmenuOutsideHandler1) { - this.contextmenuOutsideHandler1.remove(); - this.contextmenuOutsideHandler1 = null; - } - - if (this.contextmenuOutsideHandler2) { - this.contextmenuOutsideHandler2.remove(); - this.contextmenuOutsideHandler2 = null; - } - - if (this.touchOutsideHandler) { - this.touchOutsideHandler.remove(); - this.touchOutsideHandler = null; - } - }, - - createTwoChains(event) { - let fn = () => {}; - const events = getEvents(this); - if (this.childOriginEvents[event] && events[event]) { - return this[`fire${event}`]; - } - fn = this.childOriginEvents[event] || events[event] || fn; - return fn; - }, - - isClickToShow() { - const { action, showAction } = this.$props; - return action.indexOf('click') !== -1 || showAction.indexOf('click') !== -1; - }, - - isContextmenuToShow() { - const { action, showAction } = this.$props; - return action.indexOf('contextmenu') !== -1 || showAction.indexOf('contextmenu') !== -1; - }, - - isClickToHide() { - const { action, hideAction } = this.$props; - return action.indexOf('click') !== -1 || hideAction.indexOf('click') !== -1; - }, - - isMouseEnterToShow() { - const { action, showAction } = this.$props; - return action.indexOf('hover') !== -1 || showAction.indexOf('mouseenter') !== -1; - }, - - isMouseLeaveToHide() { - const { action, hideAction } = this.$props; - return action.indexOf('hover') !== -1 || hideAction.indexOf('mouseleave') !== -1; - }, - - isFocusToShow() { - const { action, showAction } = this.$props; - return action.indexOf('focus') !== -1 || showAction.indexOf('focus') !== -1; - }, - - isBlurToHide() { - const { action, hideAction } = this.$props; - return action.indexOf('focus') !== -1 || hideAction.indexOf('blur') !== -1; - }, - forcePopupAlign() { - if (this.$data.sPopupVisible && this._component && this._component.alignInstance) { - this._component.alignInstance.forceAlign(); - } - }, - fireEvents(type, e) { - if (this.childOriginEvents[type]) { - this.childOriginEvents[type](e); - } - const event = this.$props[type] || this.$attrs[type]; - if (event) { - event(e); - } - }, - - close() { - this.setPopupVisible(false); - }, - }, - render() { - const { sPopupVisible, $attrs } = this; - const children = filterEmpty(getSlot(this)); - const { forceRender, alignPoint } = this.$props; - - if (children.length > 1) { - warning(false, 'Trigger children just support only one default', true); - } - const child = children[0]; - this.childOriginEvents = getEvents(child); - const newChildProps = { - key: 'trigger', - }; - - if (this.isContextmenuToShow()) { - newChildProps.onContextmenu = this.onContextmenu; - } else { - newChildProps.onContextmenu = this.createTwoChains('onContextmenu'); - } - - if (this.isClickToHide() || this.isClickToShow()) { - newChildProps.onClick = this.onClick; - newChildProps.onMousedown = this.onMousedown; - newChildProps[supportsPassive ? 'onTouchstartPassive' : 'onTouchstart'] = this.onTouchstart; - } else { - newChildProps.onClick = this.createTwoChains('onClick'); - newChildProps.onMousedown = this.createTwoChains('onMousedown'); - newChildProps[supportsPassive ? 'onTouchstartPassive' : 'onTouchstart'] = - this.createTwoChains('onTouchstart'); - } - if (this.isMouseEnterToShow()) { - newChildProps.onMouseenter = this.onMouseenter; - if (alignPoint) { - newChildProps.onMousemove = this.onMouseMove; - } - } else { - newChildProps.onMouseenter = this.createTwoChains('onMouseenter'); - } - if (this.isMouseLeaveToHide()) { - newChildProps.onMouseleave = this.onMouseleave; - } else { - newChildProps.onMouseleave = this.createTwoChains('onMouseleave'); - } - - if (this.isFocusToShow() || this.isBlurToHide()) { - newChildProps.onFocus = this.onFocus; - newChildProps.onBlur = this.onBlur; - } else { - newChildProps.onFocus = this.createTwoChains('onFocus'); - newChildProps.onBlur = e => { - if (e && (!e.relatedTarget || !contains(e.target, e.relatedTarget))) { - this.createTwoChains('onBlur')(e); - } - }; - } - const childrenClassName = classNames(child && child.props && child.props.class, $attrs.class); - if (childrenClassName) { - newChildProps.class = childrenClassName; - } - const trigger = cloneElement(child, newChildProps); - let portal; - // prevent unmounting after it's rendered - if (sPopupVisible || this._component || forceRender) { - portal = ( - - ); - } - return [portal, trigger]; - }, -}); diff --git a/components/vc-trigger/Trigger2.tsx b/components/vc-trigger/Trigger2.tsx index f17f0e23f..ffde6054f 100644 --- a/components/vc-trigger/Trigger2.tsx +++ b/components/vc-trigger/Trigger2.tsx @@ -1,4 +1,4 @@ -import { defineComponent, inject, provide, ref } from 'vue'; +import { computed, defineComponent, inject, provide, ref } from 'vue'; import PropTypes from '../_util/vue-types'; import contains from '../vc-util/Dom/contains'; import raf from '../_util/raf'; @@ -81,13 +81,22 @@ export default defineComponent({ stretch: PropTypes.string, alignPoint: PropTypes.looseBool, // Maybe we can support user pass position in the future autoDestroy: PropTypes.looseBool.def(false), + mobile: Object, }, - setup() { + setup(props) { + const align = computed(() => { + const { popupPlacement, popupAlign, builtinPlacements } = props; + if (popupPlacement && builtinPlacements) { + return getAlignFromPlacement(builtinPlacements, popupPlacement, popupAlign); + } + return popupAlign; + }); return { vcTriggerContext: inject('vcTriggerContext', {}), dialogContext: inject('dialogContext', null), popupRef: ref(null), triggerRef: ref(null), + align, }; }, data() { @@ -222,7 +231,7 @@ export default defineComponent({ e && e.relatedTarget && !e.relatedTarget.setTimeout && - contains(this.popupRef?.getPopupDomNode(), e.relatedTarget) + contains(this.popupRef?.getElement(), e.relatedTarget) ) { return; } @@ -418,13 +427,12 @@ export default defineComponent({ forceRender, } = self.$props; const { sPopupVisible, point } = this.$data; - const align = this.getPopupAlign(); const popupProps = { prefixCls, destroyPopupOnHide, visible: sPopupVisible, point: alignPoint ? point : null, - align, + align: this.align, animation: popupAnimation, getClassNameFromAlign: handleGetPopupClassFromAlign, stretch, @@ -436,7 +444,7 @@ export default defineComponent({ maskTransitionName, getContainer, popupClassName, - popupStyle, + style: popupStyle, onAlign: $attrs.onPopupAlign || noop, ...mouseProps, ref: 'popupRef', diff --git a/components/vc-trigger/index.md b/components/vc-trigger/index.md deleted file mode 100644 index bae1a0f2a..000000000 --- a/components/vc-trigger/index.md +++ /dev/null @@ -1,160 +0,0 @@ -## API - -### props - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nametypedefaultdescription
popupClassNamestringadditional className added to popup
forceRenderbooleanfalsewhether render popup before first show
destroyPopupOnHidebooleanfalsewhether destroy popup when hide
getPopupClassNameFromAligngetPopupClassNameFromAlign(align: Object):Stringadditional className added to popup according to align
actionstring[]['hover']which actions cause popup shown. enum of 'hover','click','focus','contextmenu'
mouseEnterDelaynumber0delay time to show when mouse enter. unit: s.
mouseLeaveDelaynumber0.1delay time to hide when mouse leave. unit: s.
popupStyleObjectadditional style of popup
prefixClsStringrc-trigger-popupprefix class name
popupTransitionNameString|Objecthttps://github.com/react-component/animate
maskTransitionNameString|Objecthttps://github.com/react-component/animate
maskbooleanfalsewhether to support mask
maskClosablebooleantruewhether to support click mask to hide
popupVisiblebooleanwhether popup is visible
zIndexnumberpopup's zIndex
defaultPopupVisiblebooleanwhether popup is visible initially
popupAlignObject: alignConfig of [dom-align](https://github.com/yiminghe/dom-align)popup 's align config
getPopupContainergetPopupContainer(): HTMLElementfunction returning html node which will act as popup container
getDocumentgetDocument(): HTMLElementfunction returning document node which will be attached click event to close trigger
popupPlacementstringuse preset popup align config from builtinPlacements, can be merged by popupAlign prop
builtinPlacementsobjectbuiltin placement align map. used by placement prop
popupVisibleChange$emitcall when popup visible is changed
popupAlign$emitcallback when popup node is aligned
popupslot='popup'popup content
diff --git a/components/vc-trigger/index.js b/components/vc-trigger/index.ts similarity index 65% rename from components/vc-trigger/index.js rename to components/vc-trigger/index.ts index 8ccbba4d7..dcf37bdc3 100644 --- a/components/vc-trigger/index.js +++ b/components/vc-trigger/index.ts @@ -1,3 +1,3 @@ -// based on rc-trigger 2.6.5 +// based on rc-trigger 5.2.10 import Trigger from './Trigger'; export default Trigger;