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 ( {newChildren} ) }, 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 } } }, }, }