first
This commit is contained in:
138
packages/cascader-panel/src/cascader-menu.vue
Normal file
138
packages/cascader-panel/src/cascader-menu.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<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>
|
Reference in New Issue
Block a user