You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ant-design-vue/components/vc-menu/MenuMixin.js

263 lines
7.2 KiB

import { hasProp, getComponentName } from '../_util/props-util'
import KeyCode from '../_util/KeyCode'
import scrollIntoView from 'dom-scroll-into-view'
import { getKeyFromChildrenIndex, loopMenuItem } from './util'
import { cloneElement } from '../_util/vnode'
import DOMWrap from './DOMWrap'
import warning from '../_util/warning'
function allDisabled (arr) {
if (!arr.length) {
return true
}
return arr.every(c => {
return !!c.disabled
})
}
export default {
data () {
const props = this.$props
return {
sActiveKey: this.getActiveKey(props.activeKey),
}
},
provide () {
return {
parentMenuContext: this,
}
},
watch: {
'$props': {
handler: function (nextProps) {
let props
if (hasProp(this, 'activeKey')) {
props = {
sActiveKey: this.getActiveKey(nextProps.activeKey),
}
} else {
const originalActiveKey = this.$data.sActiveKey
const sActiveKey = this.getActiveKey(originalActiveKey)
// fix: this.setState(), parent.render(),
if (sActiveKey !== originalActiveKey) {
props = {
sActiveKey,
}
}
}
if (props) {
this.setState(props)
}
},
deep: true,
},
},
methods: {
getActiveKey (originalActiveKey) {
let activeKey = originalActiveKey
const { eventKey, defaultActiveFirst } = this.$props
const children = this.$slots.default
if (activeKey) {
let found
loopMenuItem(children, (c, i) => {
const propsData = c.componentOptions.propsData || {}
if (c && !propsData.disabled && activeKey === getKeyFromChildrenIndex(c, eventKey, i)) {
found = true
}
})
if (found) {
return activeKey
}
}
activeKey = null
if (defaultActiveFirst) {
loopMenuItem(children, (c, i) => {
const propsData = c.componentOptions.propsData || {}
if (!activeKey && c && !propsData.disabled) {
activeKey = getKeyFromChildrenIndex(c, eventKey, i)
}
})
return activeKey
}
return activeKey
},
// all keyboard events callbacks run from here at first
onKeyDown (e) {
const keyCode = e.keyCode
let handled
this.getFlatInstanceArray().forEach((obj) => {
if (obj && obj.active) {
handled = obj.onKeyDown(e)
}
})
if (handled) {
return 1
}
let activeItem = null
if (keyCode === KeyCode.UP || keyCode === KeyCode.DOWN) {
activeItem = this.step(keyCode === KeyCode.UP ? -1 : 1)
}
if (activeItem) {
e.preventDefault()
this.setState({
sActiveKey: activeItem.eventKey,
}, () => {
scrollIntoView(activeItem.$el, this.$el, {
onlyScrollIfNeeded: true,
})
})
return 1
} else if (activeItem === undefined) {
e.preventDefault()
this.setState({
sActiveKey: null,
})
return 1
}
},
onItemHover (e) {
const { key, hover } = e
this.setState({
sActiveKey: hover ? key : null,
})
},
getFlatInstanceArray () {
let instance = []
try {
instance = this.$children[0].$children || []
} catch (error) {
}
return instance
},
renderCommonMenuItem (child, i, subIndex, extraProps) {
if (child.tag === undefined) { return child }
warning(['MenuItem', 'MenuItemGroup', 'SubMenu', 'MenuDivider', 'AMenuItem', 'AMenuItemGroup', 'ASubMenu', 'AMenuDivider'].includes(getComponentName(child.componentOptions)),
`Menu children not support ${getComponentName(child.componentOptions)}`,
)
const state = this.$data
const props = this.$props
const key = getKeyFromChildrenIndex(child, props.eventKey, i)
const childProps = child.componentOptions.propsData || {}
const isActive = key === state.sActiveKey
const newChildProps = {
props: {
mode: props.mode,
level: props.level,
inlineIndent: props.inlineIndent,
renderMenuItem: this.renderMenuItem,
rootPrefixCls: props.prefixCls,
index: i,
eventKey: key,
active: !childProps.disabled && isActive,
multiple: props.multiple,
openTransitionName: this.getOpenTransitionName(),
openAnimation: props.openAnimation,
subMenuOpenDelay: props.subMenuOpenDelay,
subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender,
...extraProps,
openChange: this.onOpenChange,
},
on: {
click: this.onClick,
itemHover: this.onItemHover,
openChange: this.onOpenChange,
deselect: this.onDeselect,
// destroy: this.onDestroy,
select: this.onSelect,
},
}
if (props.mode === 'inline') {
newChildProps.props.triggerSubMenuAction = 'click'
}
// if (!extraProps.isRootMenu) {
// newChildProps.props.clearSubMenuTimers = this.clearSubMenuTimers
// }
return cloneElement(child, newChildProps)
},
renderRoot (props, children = []) {
const className = {
[props.prefixCls]: true,
[props.class]: true,
[`${props.prefixCls}-${props.mode}`]: true,
}
const domProps = {
attrs: {
role: 'menu',
'aria-activedescendant': '',
},
props: {
tag: 'ul',
// hiddenClassName: `${props.prefixCls}-hidden`,
// visible: props.visible,
},
class: className,
on: {},
}
if (this.$listeners.popupScroll) {
domProps.on.scroll = this.$listeners.popupScroll
}
if (props.id) {
domProps.id = props.id
}
if (props.focusable) {
domProps.attrs.tabIndex = '0'
domProps.on.keydown = this.onKeyDown
}
const newChildren = children.map((c, i) => this.renderMenuItem(c, i))
return (
<DOMWrap
{...domProps}
>
{newChildren}
</DOMWrap>
)
},
step (direction) {
let children = this.getFlatInstanceArray()
const sActiveKey = this.$data.sActiveKey
const len = children.length
if (!len) {
return null
}
if (direction < 0) {
children = children.concat().reverse()
}
// find current activeIndex
let activeIndex = -1
children.every((c, ci) => {
if (c && c.eventKey === sActiveKey) {
activeIndex = ci
return false
}
return true
})
if (!this.$props.defaultActiveFirst && activeIndex !== -1) {
if (allDisabled(children.slice(activeIndex, len - 1))) {
return undefined
}
}
const start = (activeIndex + 1) % len
let i = start
for (; ;) {
const child = children[i]
if (!child || child.disabled) {
i = (i + 1 + len) % len
// complete a loop
if (i === start) {
return null
}
} else {
return child
}
}
},
},
}