feat: update vc-menu

pull/2502/head
tanjinzhou 2020-06-28 17:50:33 +08:00
parent 93bde3fb67
commit 19e4e27814
3 changed files with 95 additions and 149 deletions

View File

@ -2,14 +2,15 @@ import raf from 'raf';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import Menu from '../vc-menu'; import Menu from '../vc-menu';
import scrollIntoView from 'dom-scroll-into-view'; import scrollIntoView from 'dom-scroll-into-view';
import { getSelectKeys, preventDefaultEvent } from './util'; import { getSelectKeys, preventDefaultEvent, saveRef } from './util';
import { cloneElement } from '../_util/vnode'; import { cloneElement } from '../_util/vnode';
import BaseMixin from '../_util/BaseMixin'; import BaseMixin from '../_util/BaseMixin';
import { getSlotOptions, getComponentFromProp, getListeners } from '../_util/props-util'; import { getSlotOptions, findDOMNode } from '../_util/props-util';
export default { export default {
name: 'DropdownMenu', name: 'DropdownMenu',
mixins: [BaseMixin], mixins: [BaseMixin],
inheritAttrs: false,
props: { props: {
ariaId: PropTypes.string, ariaId: PropTypes.string,
defaultActiveFirstOption: PropTypes.bool, defaultActiveFirstOption: PropTypes.bool,
@ -42,6 +43,7 @@ export default {
created() { created() {
this.rafInstance = null; this.rafInstance = null;
this.saveMenuRef = saveRef(this, 'menuRef');
this.lastInputValue = this.$props.inputValue; this.lastInputValue = this.$props.inputValue;
this.lastVisible = false; this.lastVisible = false;
}, },
@ -86,51 +88,36 @@ export default {
// Delay to scroll since current frame item position is not ready when pre view is by filter // Delay to scroll since current frame item position is not ready when pre view is by filter
// https://github.com/ant-design/ant-design/issues/11268#issuecomment-406634462 // https://github.com/ant-design/ant-design/issues/11268#issuecomment-406634462
this.rafInstance = raf(() => { this.rafInstance = raf(() => {
scrollIntoView(itemComponent, this.$refs.menuRef.$el, scrollIntoViewOpts); scrollIntoView(itemComponent, findDOMNode(this.menuRef), scrollIntoViewOpts);
}); });
}, },
renderMenu() { renderMenu() {
const props = this.$props; const props = { ...this.$props, ...this.$attrs };
const { const {
menuItems, menuItems,
menuItemSelectedIcon,
defaultActiveFirstOption, defaultActiveFirstOption,
value,
prefixCls, prefixCls,
multiple, multiple,
onMenuSelect,
inputValue, inputValue,
firstActiveValue,
dropdownMenuStyle,
backfillValue, backfillValue,
onMenuDeselect,
visible, visible,
} = props; } = props;
const menuItemSelectedIcon = getComponentFromProp(this, 'menuItemSelectedIcon'); const firstActiveValue = this.firstActiveValue;
const { menuDeselect, menuSelect, popupScroll } = getListeners(this);
if (menuItems && menuItems.length) { if (menuItems && menuItems.length) {
const selectedKeys = getSelectKeys(menuItems, value); const menuProps = {};
const menuProps = {
props: {
multiple,
itemIcon: multiple ? menuItemSelectedIcon : null,
selectedKeys,
prefixCls: `${prefixCls}-menu`,
},
on: {},
style: dropdownMenuStyle,
ref: 'menuRef',
attrs: {
role: 'listbox',
},
};
if (popupScroll) {
menuProps.on.scroll = popupScroll;
}
if (multiple) { if (multiple) {
menuProps.on.deselect = menuDeselect; menuProps.onDeselect = onMenuDeselect;
menuProps.on.select = menuSelect; menuProps.onSelect = onMenuSelect;
} else { } else {
menuProps.on.click = menuSelect; menuProps.onClick = onMenuSelect;
} }
const value = this.value;
const selectedKeys = getSelectKeys(menuItems, value);
const activeKeyProps = {}; const activeKeyProps = {};
let defaultActiveFirst = defaultActiveFirstOption; let defaultActiveFirst = defaultActiveFirstOption;
@ -155,20 +142,16 @@ export default {
) { ) {
foundFirst = true; foundFirst = true;
return cloneElement(item, { return cloneElement(item, {
directives: [ ref: ref => {
{ this.firstActiveItem = ref;
name: 'ant-ref', },
value: ref => {
this.firstActiveItem = ref;
},
},
],
}); });
} }
return item; return item;
}; };
clonedMenuItems = menuItems.map(item => { clonedMenuItems = menuItems.map(item => {
debugger;
if (getSlotOptions(item).isMenuItemGroup) { if (getSlotOptions(item).isMenuItemGroup) {
const children = item.componentOptions.children.map(clone); const children = item.componentOptions.children.map(clone);
return cloneElement(item, { children }); return cloneElement(item, { children });
@ -187,15 +170,29 @@ export default {
if (inputValue !== this.lastInputValue && (!lastValue || lastValue !== backfillValue)) { if (inputValue !== this.lastInputValue && (!lastValue || lastValue !== backfillValue)) {
activeKeyProps.activeKey = ''; activeKeyProps.activeKey = '';
} }
menuProps.props = { ...activeKeyProps, ...menuProps.props, defaultActiveFirst }; return (
return <Menu {...menuProps}>{clonedMenuItems}</Menu>; <Menu
ref={this.saveMenuRef}
style={this.dropdownMenuStyle}
defaultActiveFirst={defaultActiveFirst}
role="listbox"
itemIcon={multiple ? menuItemSelectedIcon : null}
{...activeKeyProps}
multiple={multiple}
{...menuProps}
selectedKeys={selectedKeys}
prefixCls={`${prefixCls}-menu`}
>
{clonedMenuItems}
</Menu>
);
} }
return null; return null;
}, },
}, },
render() { render() {
const renderMenu = this.renderMenu(); const renderMenu = this.renderMenu();
const { popupFocus, popupScroll } = getListeners(this); const { onPopupFocus, onPopupScroll } = this.$attrs;
return renderMenu ? ( return renderMenu ? (
<div <div
style={{ style={{
@ -204,10 +201,9 @@ export default {
}} }}
id={this.$props.ariaId} id={this.$props.ariaId}
tabIndex="-1" tabIndex="-1"
onFocus={popupFocus} onFocus={onPopupFocus}
onMousedown={preventDefaultEvent} onMousedown={preventDefaultEvent}
onScroll={popupScroll} onScroll={onPopupScroll}
ref="menuContainer"
> >
{renderMenu} {renderMenu}
</div> </div>

View File

@ -81,6 +81,7 @@ const Select = {
OptGroup, OptGroup,
name: 'Select', name: 'Select',
mixins: [BaseMixin], mixins: [BaseMixin],
inheritAttrs: false,
props: { props: {
...SelectPropTypes, ...SelectPropTypes,
prefixCls: SelectPropTypes.prefixCls.def('rc-select'), prefixCls: SelectPropTypes.prefixCls.def('rc-select'),
@ -112,10 +113,10 @@ const Select = {
// onDeselect: noop, // onDeselect: noop,
// onInputKeydown: noop, // onInputKeydown: noop,
}, },
model: { // model: {
prop: 'value', // prop: 'value',
event: 'change', // event: 'change',
}, // },
created() { created() {
this.saveInputRef = saveRef(this, 'inputRef'); this.saveInputRef = saveRef(this, 'inputRef');
this.saveInputMirrorRef = saveRef(this, 'inputMirrorRef'); this.saveInputMirrorRef = saveRef(this, 'inputMirrorRef');
@ -1667,14 +1668,7 @@ const Select = {
onMouseleave={mouseleave} onMouseleave={mouseleave}
showAction={props.showAction} showAction={props.showAction}
menuItemSelectedIcon={getComponentFromProp(this, 'menuItemSelectedIcon')} menuItemSelectedIcon={getComponentFromProp(this, 'menuItemSelectedIcon')}
{...{ ref={this.saveSelectTriggerRef}
directives: [
{
name: 'ant-ref',
value: this.saveSelectTriggerRef,
},
],
}}
dropdownRender={props.dropdownRender} dropdownRender={props.dropdownRender}
ariaId={this.$data._ariaId} ariaId={this.$data._ariaId}
> >

View File

@ -5,7 +5,7 @@ import PropTypes from '../_util/vue-types';
import DropdownMenu from './DropdownMenu'; import DropdownMenu from './DropdownMenu';
import { isSingleMode, saveRef } from './util'; import { isSingleMode, saveRef } from './util';
import BaseMixin from '../_util/BaseMixin'; import BaseMixin from '../_util/BaseMixin';
import { getListeners } from '../_util/props-util'; import { findDOMNode, getSlot } from '../_util/props-util';
const BUILT_IN_PLACEMENTS = { const BUILT_IN_PLACEMENTS = {
bottomLeft: { bottomLeft: {
@ -29,6 +29,7 @@ const BUILT_IN_PLACEMENTS = {
export default { export default {
name: 'SelectTrigger', name: 'SelectTrigger',
mixins: [BaseMixin], mixins: [BaseMixin],
inheritAttrs: false,
props: { props: {
// onPopupFocus: PropTypes.func, // onPopupFocus: PropTypes.func,
// onPopupScroll: PropTypes.func, // onPopupScroll: PropTypes.func,
@ -89,7 +90,7 @@ export default {
setDropdownWidth() { setDropdownWidth() {
this.cancelRafInstance(); this.cancelRafInstance();
this.rafInstance = raf(() => { this.rafInstance = raf(() => {
const width = this.$el.offsetWidth; const width = findDOMNode(this).offsetWidth;
if (width !== this.dropdownWidth) { if (width !== this.dropdownWidth) {
this.setState({ dropdownWidth: width }); this.setState({ dropdownWidth: width });
} }
@ -101,7 +102,7 @@ export default {
} }
}, },
getInnerMenu() { getInnerMenu() {
return this.dropdownMenuRef && this.dropdownMenuRef.$refs.menuRef; return this.dropdownMenuRef && this.dropdownMenuRef.menuRef;
}, },
getPopupDOMNode() { getPopupDOMNode() {
@ -109,45 +110,25 @@ export default {
}, },
getDropdownElement(newProps) { getDropdownElement(newProps) {
const { const props = { ...this.$props, ...this.$attrs };
value,
firstActiveValue,
defaultActiveFirstOption,
dropdownMenuStyle,
getDropdownPrefixCls,
backfillValue,
menuItemSelectedIcon,
} = this;
const { menuSelect, menuDeselect, popupScroll } = getListeners(this);
const props = this.$props;
const { dropdownRender, ariaId } = props; const { dropdownRender, ariaId } = props;
const dropdownMenuProps = { const menuNode = (
props: { <DropdownMenu
...newProps.props, ref={this.saveDropdownMenuRef}
ariaId, {...newProps}
prefixCls: getDropdownPrefixCls(), ariaId={ariaId}
value, prefixCls={this.getDropdownPrefixCls()}
firstActiveValue, onMenuSelect={props.onMenuSelect}
defaultActiveFirstOption, onMenuDeselect={props.onMenuDeselect}
dropdownMenuStyle, onPopupScroll={props.onPopupScroll}
backfillValue, value={props.value}
menuItemSelectedIcon, backfillValue={props.backfillValue}
}, firstActiveValue={props.firstActiveValue}
on: { defaultActiveFirstOption={props.defaultActiveFirstOption}
...newProps.on, dropdownMenuStyle={props.dropdownMenuStyle}
menuSelect, menuItemSelectedIcon={props.menuItemSelectedIcon}
menuDeselect, />
popupScroll, );
},
directives: [
{
name: 'ant-ref',
value: this.saveDropdownMenuRef,
},
],
};
const menuNode = <DropdownMenu {...dropdownMenuProps} />;
if (dropdownRender) { if (dropdownRender) {
return dropdownRender(menuNode, props); return dropdownRender(menuNode, props);
@ -170,7 +151,7 @@ export default {
}, },
render() { render() {
const { $props, $slots } = this; const { onPopupFocus, empty, ...props } = { ...this.$props, ...this.$attrs };
const { const {
multiple, multiple,
visible, visible,
@ -181,12 +162,8 @@ export default {
dropdownClassName, dropdownClassName,
dropdownStyle, dropdownStyle,
dropdownMatchSelectWidth, dropdownMatchSelectWidth,
options, } = props;
getPopupContainer, //const { mouseenter, mouseleave, popupFocus, dropdownVisibleChange } = getListeners(this);
showAction,
empty,
} = $props;
const { mouseenter, mouseleave, popupFocus, dropdownVisibleChange } = getListeners(this);
const dropdownPrefixCls = this.getDropdownPrefixCls(); const dropdownPrefixCls = this.getDropdownPrefixCls();
const popupClassName = { const popupClassName = {
[dropdownClassName]: !!dropdownClassName, [dropdownClassName]: !!dropdownClassName,
@ -194,20 +171,16 @@ export default {
[`${dropdownPrefixCls}--empty`]: empty, [`${dropdownPrefixCls}--empty`]: empty,
}; };
const popupElement = this.getDropdownElement({ const popupElement = this.getDropdownElement({
props: { menuItems: props.options,
menuItems: options, multiple,
multiple, inputValue,
inputValue, visible,
visible, onPopupFocus,
},
on: {
popupFocus,
},
}); });
let hideAction; let hideAction;
if (disabled) { if (disabled) {
hideAction = []; hideAction = [];
} else if (isSingleMode($props) && !showSearch) { } else if (isSingleMode(props) && !showSearch) {
hideAction = ['click']; hideAction = ['click'];
} else { } else {
hideAction = ['blur']; hideAction = ['blur'];
@ -217,42 +190,25 @@ export default {
if (this.dropdownWidth) { if (this.dropdownWidth) {
popupStyle[widthProp] = `${this.dropdownWidth}px`; popupStyle[widthProp] = `${this.dropdownWidth}px`;
} }
const triggerProps = {
props: {
...$props,
showAction: disabled ? [] : showAction,
hideAction,
ref: 'triggerRef',
popupPlacement: 'bottomLeft',
builtinPlacements: BUILT_IN_PLACEMENTS,
prefixCls: dropdownPrefixCls,
popupTransitionName: this.getDropdownTransitionName(),
popupAlign: dropdownAlign,
popupVisible: visible,
getPopupContainer,
popupClassName: classnames(popupClassName),
popupStyle,
},
on: {
popupVisibleChange: dropdownVisibleChange,
},
directives: [
{
name: 'ant-ref',
value: this.saveTriggerRef,
},
],
};
if (mouseenter) {
triggerProps.on.mouseenter = mouseenter;
}
if (mouseleave) {
triggerProps.on.mouseleave = mouseleave;
}
return ( return (
<Trigger {...triggerProps}> <Trigger
{$slots.default} {...props}
<template slot="popup">{popupElement}</template> showAction={disabled ? [] : this.$props.showAction}
hideAction={hideAction}
ref={this.saveTriggerRef}
popupPlacement="bottomLeft"
builtinPlacements={BUILT_IN_PLACEMENTS}
prefixCls={dropdownPrefixCls}
popupTransitionName={this.getDropdownTransitionName()}
onPopupVisibleChange={props.onDropdownVisibleChange}
popup={popupElement}
popupAlign={dropdownAlign}
popupVisible={visible}
getPopupContainer={props.getPopupContainer}
popupClassName={classnames(popupClassName)}
popupStyle={popupStyle}
>
{getSlot(this)[0]}
</Trigger> </Trigger>
); );
}, },