Cascader: optimize hover to expand experience (#8010)

* Cascader: optimize hover to expand experience

pass eslint

* Cascader: hoverThreshold configuable
pull/7973/head^2
Joe Wong 2017-11-08 12:24:29 +08:00 committed by 杨奕
parent 651b517fd0
commit 4ea53ab896
2 changed files with 81 additions and 2 deletions

View File

@ -149,6 +149,10 @@ export default {
beforeFilter: { beforeFilter: {
type: Function, type: Function,
default: () => (() => {}) default: () => (() => {})
},
hoverThreshold: {
type: Number,
default: 500
} }
}, },
@ -224,6 +228,7 @@ export default {
this.menu.expandTrigger = this.expandTrigger; this.menu.expandTrigger = this.expandTrigger;
this.menu.changeOnSelect = this.changeOnSelect; this.menu.changeOnSelect = this.changeOnSelect;
this.menu.popperClass = this.popperClass; this.menu.popperClass = this.popperClass;
this.menu.hoverThreshold = this.hoverThreshold;
this.popperElm = this.menu.$el; this.popperElm = this.menu.$el;
this.menu.$on('pick', this.handlePick); this.menu.$on('pick', this.handlePick);
this.menu.$on('activeItemChange', this.handleActiveItemChange); this.menu.$on('activeItemChange', this.handleActiveItemChange);

View File

@ -39,7 +39,8 @@
value: [], value: [],
expandTrigger: 'click', expandTrigger: 'click',
changeOnSelect: false, changeOnSelect: false,
popperClass: '' popperClass: '',
hoverTimer: 0
}; };
}, },
@ -135,9 +136,35 @@
activeOptions, activeOptions,
visible, visible,
expandTrigger, expandTrigger,
popperClass popperClass,
hoverThreshold
} = this; } = this;
let hoverMenuRefs = {};
const hoverMenuHandler = e => {
const offsetX = e.offsetX;
const width = hoverMenuRefs.activeMenu.offsetWidth;
const height = hoverMenuRefs.activeMenu.offsetHeight;
if (e.target === hoverMenuRefs.activeItem) {
clearTimeout(this.hoverTimer);
const {activeItem} = hoverMenuRefs;
const offsetY_top = activeItem.offsetTop;
const offsetY_Bottom = offsetY_top + activeItem.offsetHeight;
hoverMenuRefs.hoverZone.innerHTML = `
<path style="pointer-events: auto;" fill="transparent" d="M${offsetX} ${offsetY_top} L${width} 0 V${offsetY_top} Z" />
<path style="pointer-events: auto;" fill="transparent" d="M${offsetX} ${offsetY_Bottom} L${width} ${height} V${offsetY_Bottom} Z" />
`;
} else {
if (!this.hoverTimer) {
this.hoverTimer = setTimeout(() => {
hoverMenuRefs.hoverZone.innerHTML = '';
}, hoverThreshold);
}
}
};
const menus = this._l(activeOptions, (menu, menuIndex) => { const menus = this._l(activeOptions, (menu, menuIndex) => {
let isFlat = false; let isFlat = false;
const items = this._l(menu, item => { const items = this._l(menu, item => {
@ -177,6 +204,7 @@
'is-active': item.value === activeValue[menuIndex], 'is-active': item.value === activeValue[menuIndex],
'is-disabled': item.disabled 'is-disabled': item.disabled
}} }}
ref={item.value === activeValue[menuIndex] ? 'activeItem' : null}
{...events} {...events}
> >
{item.label} {item.label}
@ -188,19 +216,65 @@
menuStyle.minWidth = this.inputWidth + 'px'; menuStyle.minWidth = this.inputWidth + 'px';
} }
const isHoveredMenu = expandTrigger === 'hover' && activeValue.length - 1 === menuIndex;
const hoverMenuEvent = {
on: {
}
};
if (isHoveredMenu) {
hoverMenuEvent.on.mousemove = hoverMenuHandler;
menuStyle.position = 'relative';
}
return ( return (
<ul <ul
class={{ class={{
'el-cascader-menu': true, 'el-cascader-menu': true,
'el-cascader-menu--flexible': isFlat 'el-cascader-menu--flexible': isFlat
}} }}
{...hoverMenuEvent}
style={menuStyle} style={menuStyle}
refInFor refInFor
ref="menus"> ref="menus">
{items} {items}
{
isHoveredMenu
? (<svg
ref="hoverZone"
style={{
position: 'absolute',
top: 0,
height: '100%',
width: '100%',
left: 0,
pointerEvents: 'none'
}}
></svg>) : null
}
</ul> </ul>
); );
}); });
if (expandTrigger === 'hover') {
this.$nextTick(() => {
const activeItem = this.$refs.activeItem;
if (activeItem) {
const activeMenu = activeItem.parentElement;
const hoverZone = this.$refs.hoverZone;
hoverMenuRefs = {
activeMenu,
activeItem,
hoverZone
};
} else {
hoverMenuRefs = {};
}
});
}
return ( return (
<transition name="el-zoom-in-top" on-before-enter={this.handleMenuEnter} on-after-leave={this.handleMenuLeave}> <transition name="el-zoom-in-top" on-before-enter={this.handleMenuEnter} on-after-leave={this.handleMenuLeave}>
<div <div