feat: update dropdown

pull/2379/head
tangjinzhou 4 years ago
parent 96ba93cf9b
commit a0e637da9c

@ -1 +1 @@
Subproject commit 78341d7cd7c34015b7369982cfa7914fbc558232
Subproject commit bf1777054fbe55a0d3bbdd037573e3a235137fbb

@ -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

@ -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) {

@ -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') || <EllipsisOutlined />;
const icon = getComponent(this, 'icon') || <EllipsisOutlined />;
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)}
</Button>
<Dropdown {...dropdownProps}>
<template slot="overlay">{getComponentFromProp(this, 'overlay')}</template>
<Dropdown {...dropdownProps} overlay={getComponent(this, 'overlay')}>
<Button type={type}>{icon}</Button>
</Dropdown>
</ButtonGroup>

@ -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 = {
</span>
);
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 (
<RcDropdown {...dropdownProps}>
{dropdownTrigger}
<template slot="overlay">{this.renderOverlay(prefixCls)}</template>
</RcDropdown>
);
return <RcDropdown {...dropdownProps}>{dropdownTrigger}</RcDropdown>;
},
};

@ -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];
},

@ -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 (
<Trigger {...triggerProps}>
{this.renderChildren()}
<template slot="popup">{this.$slots.overlay && this.getMenuElement()}</template>
</Trigger>
);
return <Trigger {...triggerProps}>{this.renderChildren()}</Trigger>;
},
};

@ -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}`

@ -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');

Loading…
Cancel
Save