<script>
|
import ElScrollbar from 'element-ui/packages/scrollbar';
|
import CascaderNode from './cascader-node.vue';
|
import Locale from 'element-ui/src/mixins/locale';
|
import { generateId } from 'element-ui/src/utils/util';
|
|
export default {
|
name: 'ElCascaderMenu',
|
|
mixins: [Locale],
|
|
inject: ['panel'],
|
|
components: {
|
ElScrollbar,
|
CascaderNode
|
},
|
|
props: {
|
nodes: {
|
type: Array,
|
required: true
|
},
|
index: Number
|
},
|
|
data() {
|
return {
|
activeNode: null,
|
hoverTimer: null,
|
id: generateId()
|
};
|
},
|
|
computed: {
|
isEmpty() {
|
return !this.nodes.length;
|
},
|
menuId() {
|
return `cascader-menu-${this.id}-${this.index}`;
|
}
|
},
|
|
methods: {
|
handleExpand(e) {
|
this.activeNode = e.target;
|
},
|
handleMouseMove(e) {
|
const { activeNode, hoverTimer } = this;
|
const { hoverZone } = this.$refs;
|
|
if (!activeNode || !hoverZone) return;
|
|
if (activeNode.contains(e.target)) {
|
clearTimeout(hoverTimer);
|
|
const { left } = this.$el.getBoundingClientRect();
|
const startX = e.clientX - left;
|
const { offsetWidth, offsetHeight } = this.$el;
|
const top = activeNode.offsetTop;
|
const bottom = top + activeNode.offsetHeight;
|
|
hoverZone.innerHTML = `
|
<path style="pointer-events: auto;" fill="transparent" d="M${startX} ${top} L${offsetWidth} 0 V${top} Z" />
|
<path style="pointer-events: auto;" fill="transparent" d="M${startX} ${bottom} L${offsetWidth} ${offsetHeight} V${bottom} Z" />
|
`;
|
} else if (!hoverTimer) {
|
this.hoverTimer = setTimeout(this.clearHoverZone, this.panel.config.hoverThreshold);
|
}
|
},
|
clearHoverZone() {
|
const { hoverZone } = this.$refs;
|
if (!hoverZone) return;
|
hoverZone.innerHTML = '';
|
},
|
|
renderEmptyText(h) {
|
return (
|
<div class="el-cascader-menu__empty-text">{ this.t('el.cascader.noData') }</div>
|
);
|
},
|
renderNodeList(h) {
|
const { menuId } = this;
|
const { isHoverMenu } = this.panel;
|
const events = { on: {} };
|
|
if (isHoverMenu) {
|
events.on.expand = this.handleExpand;
|
}
|
|
const nodes = this.nodes.map((node, index) => {
|
const { hasChildren } = node;
|
return (
|
<cascader-node
|
key={ node.uid }
|
node={ node }
|
node-id={ `${menuId}-${index}` }
|
aria-haspopup={ hasChildren }
|
aria-owns = { hasChildren ? menuId : null }
|
{ ...events }></cascader-node>
|
);
|
});
|
|
return [
|
...nodes,
|
isHoverMenu ? <svg ref='hoverZone' class='el-cascader-menu__hover-zone'></svg> : null
|
];
|
}
|
},
|
|
render(h) {
|
const { isEmpty, menuId } = this;
|
const events = { nativeOn: {} };
|
|
// optimize hover to expand experience (#8010)
|
if (this.panel.isHoverMenu) {
|
events.nativeOn.mousemove = this.handleMouseMove;
|
// events.nativeOn.mouseleave = this.clearHoverZone;
|
}
|
|
return (
|
<el-scrollbar
|
tag="ul"
|
role="menu"
|
id={ menuId }
|
class="el-cascader-menu"
|
wrap-class="el-cascader-menu__wrap"
|
view-class={{
|
'el-cascader-menu__list': true,
|
'is-empty': isEmpty
|
}}
|
{ ...events }>
|
{ isEmpty ? this.renderEmptyText(h) : this.renderNodeList(h) }
|
</el-scrollbar>
|
);
|
}
|
};
|
</script>
|