From a0e637da9c3c95fa73fd68ede26f8dc7f8e941f9 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Sun, 21 Jun 2020 22:45:30 +0800 Subject: [PATCH] feat: update dropdown --- antdv-demo | 2 +- breakChange-2.x.md | 4 ++ components/_util/props-util.js | 3 +- components/dropdown/dropdown-button.jsx | 45 +++++------- components/dropdown/dropdown.jsx | 73 ++++++++----------- components/vc-align/Align.jsx | 9 ++- components/vc-dropdown/src/Dropdown.jsx | 96 +++++++++++-------------- components/vc-trigger/LazyRenderBox.jsx | 7 +- examples/index.js | 2 + 9 files changed, 110 insertions(+), 131 deletions(-) diff --git a/antdv-demo b/antdv-demo index 78341d7cd..bf1777054 160000 --- a/antdv-demo +++ b/antdv-demo @@ -1 +1 @@ -Subproject commit 78341d7cd7c34015b7369982cfa7914fbc558232 +Subproject commit bf1777054fbe55a0d3bbdd037573e3a235137fbb diff --git a/breakChange-2.x.md b/breakChange-2.x.md index 83c852871..91ca89edb 100644 --- a/breakChange-2.x.md +++ b/breakChange-2.x.md @@ -43,3 +43,7 @@ v-model -> v-model:value ## menu v-model -> v-model:selectedKeys :openKeys.sync -> v-mdoel:openKeys + +## dropdown + +v-model -> v-model:visible diff --git a/components/_util/props-util.js b/components/_util/props-util.js index ccec0fa7f..5ea7f004c 100644 --- a/components/_util/props-util.js +++ b/components/_util/props-util.js @@ -200,7 +200,6 @@ const getAllProps = ele => { return props; }; -// 使用 getOptionProps 替换 ,待测试 const getPropsData = vnode => { const res = {}; const originProps = vnode.props || {}; @@ -208,7 +207,7 @@ const getPropsData = vnode => { Object.keys(originProps).forEach(key => { props[camelize(key)] = originProps[key]; }); - const options = vnode.type.props; + const options = isPlainObject(vnode.type) ? vnode.type.props : {}; Object.keys(options).forEach(k => { const v = resolvePropValue(options, props, k, props[k]); if (k in props) { diff --git a/components/dropdown/dropdown-button.jsx b/components/dropdown/dropdown-button.jsx index d5b21a13c..c587629e1 100644 --- a/components/dropdown/dropdown-button.jsx +++ b/components/dropdown/dropdown-button.jsx @@ -1,9 +1,10 @@ +import { provide, inject } from 'vue'; import Button from '../button'; import buttonTypes from '../button/buttonTypes'; import { ButtonGroupProps } from '../button/button-group'; import Dropdown from './dropdown'; import PropTypes from '../_util/vue-types'; -import { hasProp, getComponentFromProp } from '../_util/props-util'; +import { hasProp, getComponent, getSlot } from '../_util/props-util'; import getDropdownProps from './getDropdownProps'; import { ConfigConsumerProps } from '../config-provider'; import EllipsisOutlined from '@ant-design/icons-vue/EllipsisOutlined'; @@ -27,18 +28,14 @@ const DropdownButtonProps = { export { DropdownButtonProps }; export default { name: 'ADropdownButton', - model: { - prop: 'visible', - event: 'visibleChange', - }, props: DropdownButtonProps, - provide() { + setup() { return { - savePopupRef: this.savePopupRef, + configProvider: inject('configProvider', ConfigConsumerProps), }; }, - inject: { - configProvider: { default: () => ConfigConsumerProps }, + created() { + provide('savePopupRef', this.savePopupRef); }, methods: { savePopupRef(ref) { @@ -48,6 +45,7 @@ export default { this.$emit('click', e); }, onVisibleChange(val) { + this.$emit('update:visible', val); this.$emit('visibleChange', val); }, }, @@ -66,30 +64,24 @@ export default { title, ...restProps } = this.$props; - const icon = getComponentFromProp(this, 'icon') || ; + const icon = getComponent(this, 'icon') || ; const { getPopupContainer: getContextPopupContainer } = this.configProvider; const getPrefixCls = this.configProvider.getPrefixCls; const prefixCls = getPrefixCls('dropdown-button', customizePrefixCls); const dropdownProps = { - props: { - align, - disabled, - trigger: disabled ? [] : trigger, - placement, - getPopupContainer: getPopupContainer || getContextPopupContainer, - }, - on: { - visibleChange: this.onVisibleChange, - }, + align, + disabled, + trigger: disabled ? [] : trigger, + placement, + getPopupContainer: getPopupContainer || getContextPopupContainer, + onVisibleChange: this.onVisibleChange, }; if (hasProp(this, 'visible')) { - dropdownProps.props.visible = visible; + dropdownProps.visible = visible; } const buttonGroupProps = { - props: { - ...restProps, - }, + ...restProps, class: prefixCls, }; @@ -103,10 +95,9 @@ export default { href={href} title={title} > - {this.$slots.default} + {getSlot(this)} - - + diff --git a/components/dropdown/dropdown.jsx b/components/dropdown/dropdown.jsx index eb6a3bf69..957525692 100644 --- a/components/dropdown/dropdown.jsx +++ b/components/dropdown/dropdown.jsx @@ -1,3 +1,4 @@ +import { provide, inject, cloneVNode } from 'vue'; import RcDropdown from '../vc-dropdown/src/index'; import DropdownButton from './dropdown-button'; import PropTypes from '../_util/vue-types'; @@ -5,8 +6,9 @@ import { cloneElement } from '../_util/vnode'; import { getOptionProps, getPropsData, - getComponentFromProp, - getListeners, + getComponent, + isValidElement, + getSlot, } from '../_util/props-util'; import getDropdownProps from './getDropdownProps'; import { ConfigConsumerProps } from '../config-provider'; @@ -22,17 +24,13 @@ const Dropdown = { mouseLeaveDelay: PropTypes.number.def(0.1), placement: DropdownProps.placement.def('bottomLeft'), }, - model: { - prop: 'visible', - event: 'visibleChange', - }, - provide() { + setup() { return { - savePopupRef: this.savePopupRef, + configProvider: inject('configProvider', ConfigConsumerProps), }; }, - inject: { - configProvider: { default: () => ConfigConsumerProps }, + created() { + provide('savePopupRef', this.savePopupRef); }, methods: { savePopupRef(ref) { @@ -49,7 +47,7 @@ const Dropdown = { return 'slide-up'; }, renderOverlay(prefixCls) { - const overlay = getComponentFromProp(this, 'overlay'); + const overlay = getComponent(this, 'overlay'); const overlayNode = Array.isArray(overlay) ? overlay[0] : overlay; // menu cannot be selectable in dropdown defaultly // menu should be focusable in dropdown defaultly @@ -61,57 +59,48 @@ const Dropdown = { ); - const fixedModeOverlay = - overlayNode && overlayNode.componentOptions - ? cloneElement(overlayNode, { - props: { - mode: 'vertical', - selectable, - focusable, - expandIcon, - }, - }) - : overlay; + const fixedModeOverlay = isValidElement(overlayNode) + ? cloneVNode(overlayNode, { + mode: 'vertical', + selectable, + focusable, + expandIcon, + }) + : overlay; return fixedModeOverlay; }, }, render() { - const { $slots } = this; const props = getOptionProps(this); const { prefixCls: customizePrefixCls, trigger, disabled, getPopupContainer } = props; const { getPopupContainer: getContextPopupContainer } = this.configProvider; const getPrefixCls = this.configProvider.getPrefixCls; const prefixCls = getPrefixCls('dropdown', customizePrefixCls); - const dropdownTrigger = cloneElement($slots.default, { - class: `${prefixCls}-trigger`, - props: { + const dropdownTrigger = cloneElement( + getSlot(this), + { + class: `${prefixCls}-trigger`, disabled, }, - }); + false, + ); const triggerActions = disabled ? [] : trigger; let alignPoint; if (triggerActions && triggerActions.indexOf('contextmenu') !== -1) { alignPoint = true; } const dropdownProps = { - props: { - alignPoint, - ...props, - prefixCls, - getPopupContainer: getPopupContainer || getContextPopupContainer, - transitionName: this.getTransitionName(), - trigger: triggerActions, - }, - on: getListeners(this), + alignPoint, + ...props, + prefixCls, + getPopupContainer: getPopupContainer || getContextPopupContainer, + transitionName: this.getTransitionName(), + trigger: triggerActions, + overlay: this.renderOverlay(prefixCls), }; - return ( - - {dropdownTrigger} - - - ); + return {dropdownTrigger}; }, }; diff --git a/components/vc-align/Align.jsx b/components/vc-align/Align.jsx index 942db23e3..719d0cf81 100644 --- a/components/vc-align/Align.jsx +++ b/components/vc-align/Align.jsx @@ -5,7 +5,7 @@ import addEventListener from '../vc-util/Dom/addEventListener'; import { isWindow, buffer, isSamePoint, isSimilarValue, restoreFocus } from './util'; import { cloneElement } from '../_util/vnode.js'; import clonedeep from 'lodash/cloneDeep'; -import { getSlot, getListeners } from '../_util/props-util'; +import { getSlot, findDOMNode } from '../_util/props-util'; function getElement(func) { if (typeof func !== 'function' || !func) return null; @@ -117,8 +117,7 @@ export default { forceAlign() { const { disabled, target, align } = this.$props; if (!disabled && target) { - const source = this.$el; - const listeners = getListeners(this); + const source = findDOMNode(this); let result; const element = getElement(target); const point = getPoint(target); @@ -134,7 +133,7 @@ export default { } restoreFocus(activeElement, source); this.aligned = true; - listeners.align && listeners.align(source, result); + this.$attrs.onAlign && this.$attrs.onAlign(source, result); } }, }, @@ -143,7 +142,7 @@ export default { const { childrenProps } = this.$props; const child = getSlot(this); if (child && childrenProps) { - return cloneElement(child[0], { props: childrenProps }); + return cloneElement(child[0], childrenProps); } return child && child[0]; }, diff --git a/components/vc-dropdown/src/Dropdown.jsx b/components/vc-dropdown/src/Dropdown.jsx index fe28e6cc3..08effac34 100644 --- a/components/vc-dropdown/src/Dropdown.jsx +++ b/components/vc-dropdown/src/Dropdown.jsx @@ -1,7 +1,14 @@ +import { Text } from 'vue'; import PropTypes from '../../_util/vue-types'; import Trigger from '../../vc-trigger'; import placements from './placements'; -import { hasProp, getEvents, getOptionProps } from '../../_util/props-util'; +import { + hasProp, + getComponent, + getOptionProps, + getSlot, + findDOMNode, +} from '../../_util/props-util'; import BaseMixin from '../../_util/BaseMixin'; import { cloneElement } from '../../_util/vnode'; @@ -48,6 +55,7 @@ export default { }, methods: { onClick(e) { + const overlayProps = this.getOverlayElement().props; // do no call onVisibleChange, if you need click to hide, use onClick and control visible if (!hasProp(this, 'visible')) { this.setState({ @@ -55,8 +63,8 @@ export default { }); } this.$emit('overlayClick', e); - if (this.childOriginEvents.click) { - this.childOriginEvents.click(e); + if (overlayProps.onClick) { + overlayProps.onClick(e); } }, @@ -66,6 +74,7 @@ export default { sVisible: visible, }); } + this.$emit('update:visible', visible); this.__emit('visibleChange', visible); }, @@ -80,37 +89,26 @@ export default { }, getOverlayElement() { - const overlay = this.overlay || this.$slots.overlay || this.$scopedSlots.overlay; - let overlayElement; - if (typeof overlay === 'function') { - overlayElement = overlay(); - } else { - overlayElement = overlay; - } - return overlayElement; + const overlay = getComponent(this, 'overlay'); + return Array.isArray(overlay) ? overlay[0] : overlay; }, getMenuElement() { - const { onClick, prefixCls, $slots } = this; - this.childOriginEvents = getEvents($slots.overlay[0]); + const { onClick, prefixCls } = this; const overlayElement = this.getOverlayElement(); const extraOverlayProps = { - props: { - prefixCls: `${prefixCls}-menu`, - getPopupContainer: () => this.getPopupDomNode(), - }, - on: { - click: onClick, - }, + prefixCls: `${prefixCls}-menu`, + getPopupContainer: () => this.getPopupDomNode(), + onClick, }; - if (typeof overlayElement.type === 'string') { - delete extraOverlayProps.props.prefixCls; + if (overlayElement && overlayElement.type === Text) { + delete extraOverlayProps.prefixCls; } - return cloneElement($slots.overlay[0], extraOverlayProps); + return cloneElement(overlayElement, extraOverlayProps); }, getMenuElementOrLambda() { - const overlay = this.overlay || this.$slots.overlay || this.$scopedSlots.overlay; + const overlay = this.overlay || this.$slots.overlay; if (typeof overlay === 'function') { return this.getMenuElement; } @@ -132,7 +130,7 @@ export default { afterVisibleChange(visible) { if (visible && this.getMinOverlayWidthMatchTrigger()) { const overlayNode = this.getPopupDomNode(); - const rootNode = this.$el; + const rootNode = findDOMNode(this); if (rootNode && overlayNode && rootNode.offsetWidth > overlayNode.offsetWidth) { overlayNode.style.minWidth = `${rootNode.offsetWidth}px`; if ( @@ -147,10 +145,10 @@ export default { }, renderChildren() { - const children = this.$slots.default && this.$slots.default[0]; + const children = getSlot(this); const { sVisible } = this; return sVisible && children - ? cloneElement(children, { class: this.getOpenClassName() }) + ? cloneElement(children[0], { class: this.getOpenClassName() }, false) : children; }, }, @@ -176,33 +174,25 @@ export default { } const triggerProps = { - props: { - ...otherProps, - prefixCls, - popupClassName: overlayClassName, - popupStyle: overlayStyle, - builtinPlacements: placements, - action: trigger, - showAction, - hideAction: triggerHideAction || [], - popupPlacement: placement, - popupAlign: align, - popupTransitionName: transitionName, - popupAnimation: animation, - popupVisible: this.sVisible, - afterPopupVisibleChange: this.afterVisibleChange, - getPopupContainer, - }, - on: { - popupVisibleChange: this.onVisibleChange, - }, + ...otherProps, + prefixCls, + popupClassName: overlayClassName, + popupStyle: overlayStyle, + builtinPlacements: placements, + action: trigger, + showAction, + hideAction: triggerHideAction || [], + popupPlacement: placement, + popupAlign: align, + popupTransitionName: transitionName, + popupAnimation: animation, + popupVisible: this.sVisible, + afterPopupVisibleChange: this.afterVisibleChange, + getPopupContainer, + onPopupVisibleChange: this.onVisibleChange, + popup: this.getMenuElementOrLambda(), ref: 'trigger', }; - return ( - - {this.renderChildren()} - - - ); + return {this.renderChildren()}; }, }; diff --git a/components/vc-trigger/LazyRenderBox.jsx b/components/vc-trigger/LazyRenderBox.jsx index bbc656ca0..83f3b7c3d 100644 --- a/components/vc-trigger/LazyRenderBox.jsx +++ b/components/vc-trigger/LazyRenderBox.jsx @@ -1,3 +1,4 @@ +import { Text } from 'vue'; import PropTypes from '../_util/vue-types'; import { getSlot } from '../_util/props-util'; @@ -10,7 +11,11 @@ export default { render() { const { hiddenClassName } = this.$props; const child = getSlot(this); - if (hiddenClassName || (child && child.length > 1)) { + if ( + hiddenClassName || + (child && child.length > 1) || + (child && child[0] && child[0].type === Text) + ) { // const cls = ''; // if (!visible && hiddenClassName) { // // cls += ` ${hiddenClassName}` diff --git a/examples/index.js b/examples/index.js index dedf6245a..9f0851cfd 100644 --- a/examples/index.js +++ b/examples/index.js @@ -32,6 +32,7 @@ import message from 'ant-design-vue/message'; import Modal from 'ant-design-vue/modal'; import Menu from 'ant-design-vue/menu'; import Mentions from 'ant-design-vue/mentions'; +import Dropdown from 'ant-design-vue/dropdown'; import 'ant-design-vue/style.js'; const basic = { @@ -77,4 +78,5 @@ app .use(Modal) .use(Menu) .use(Mentions) + .use(Dropdown) .mount('#app');