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.
263 lines
7.2 KiB
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'].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
|
|
}
|
|
}
|
|
},
|
|
},
|
|
}
|