pull/9/head
tangjinzhou 2018-01-02 19:05:02 +08:00
parent baec906d5c
commit f8be36593d
11 changed files with 919 additions and 455 deletions

View File

@ -0,0 +1,298 @@
@menuPrefixCls: rc-menu;
@font-face {
font-family: 'FontAwesome';
src: url('https://cdn.bootcss.com/font-awesome/4.2.0/fonts/fontawesome-webfont.eot?v=4.2.0');
src: url('https://cdn.bootcss.com/font-awesome/4.2.0/fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'), url('https://cdn.bootcss.com/font-awesome/4.2.0/fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'), url('https://cdn.bootcss.com/font-awesome/4.2.0/fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'), url('https://cdn.bootcss.com/font-awesome/4.2.0/fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
.@{menuPrefixCls} {
outline: none;
margin-bottom: 0;
padding-left: 0; // Override default ul/ol
list-style: none;
border: 1px solid #d9d9d9;
box-shadow: 0 0 4px #d9d9d9;
border-radius: 3px;
color: #666;
&-hidden {
display: none;
}
&-collapse {
overflow: hidden;
&-active {
transition: height .3s ease-out;
}
}
&-item-group-list {
margin: 0;
padding: 0;
}
&-item-group-title {
color: #999;
line-height: 1.5;
padding: 8px 10px;
border-bottom: 1px solid #dedede;
}
&-item-active,
&-submenu-active > &-submenu-title {
background-color: #eaf8fe;
}
&-item-selected {
background-color: #eaf8fe;
// fix chrome render bug
transform: translateZ(0);
}
&-submenu-selected {
background-color: #eaf8fe;
}
& > li&-submenu {
padding: 0;
}
&-horizontal&-sub,
&-vertical&-sub,
&-vertical-left&-sub,
&-vertical-right&-sub {
min-width: 160px;
margin-top: 0;
}
&-item, &-submenu-title {
margin: 0;
position: relative;
display: block;
padding: 7px 7px 7px 16px;
white-space: nowrap;
// Disabled state sets text to gray and nukes hover/tab effects
&.@{menuPrefixCls}-item-disabled, &.@{menuPrefixCls}-submenu-disabled {
color: #777 !important;
}
}
& > &-item-divider {
height: 1px;
margin: 1px 0;
overflow: hidden;
padding: 0;
line-height: 0;
background-color: #e5e5e5;
}
&-submenu {
&-popup {
position: absolute;
}
> .@{menuPrefixCls} {
background-color: #fff;
}
}
.@{menuPrefixCls}-submenu-title, .@{menuPrefixCls}-item {
.anticon {
width: 14px;
height: 14px;
margin-right: 8px;
top: -1px;
}
}
&-horizontal {
background-color: #F3F5F7;
border: none;
border-bottom: 1px solid transparent;
border-bottom: 1px solid #d9d9d9;
box-shadow: none;
& > .@{menuPrefixCls}-item, & > .@{menuPrefixCls}-submenu .@{menuPrefixCls}-submenu-title {
padding: 15px 20px;
}
& > .@{menuPrefixCls}-submenu, & > .@{menuPrefixCls}-item {
float: left;
border-bottom: 2px solid transparent;
&-active {
border-bottom: 2px solid #2db7f5;
background-color: #F3F5F7;
color: #2baee9;
}
}
&:after {
content: "\20";
display: block;
height: 0;
clear: both;
}
}
&-vertical,
&-vertical-left,
&-vertical-right,
&-inline {
padding: 12px 0;
& > .@{menuPrefixCls}-item, & > .@{menuPrefixCls}-submenu .@{menuPrefixCls}-submenu-title {
padding: 12px 8px 12px 24px;
}
.@{menuPrefixCls}-submenu-arrow {
display: inline-block;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
vertical-align: baseline;
text-align: center;
text-transform: none;
text-rendering: auto;
position: absolute;
right: 16px;
line-height: 1.5em;
&:before {
content: "\f0da";
}
}
}
&-inline {
.@{menuPrefixCls}-submenu-arrow {
transform: rotate(90deg);
transition: transform .3s;
}
& .@{menuPrefixCls}-submenu-open .@{menuPrefixCls}-submenu-title {
.@{menuPrefixCls}-submenu-arrow {
transform: rotate(-90deg);
}
}
}
&-vertical&-sub,
&-vertical-left&-sub,
&-vertical-right&-sub {
padding: 0;
}
&-sub&-inline {
padding: 0;
border: none;
border-radius: 0;
box-shadow: none;
& > .@{menuPrefixCls}-item, & > .@{menuPrefixCls}-submenu .@{menuPrefixCls}-submenu-title {
padding-top: 8px;
padding-bottom: 8px;
padding-right: 0;
}
}
.effect() {
animation-duration: .3s;
animation-fill-mode: both;
transform-origin: 0 0;
}
&-open {
&-slide-up-enter, &-slide-up-appear {
.effect();
opacity: 0;
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
animation-play-state: paused;
}
&-slide-up-leave {
.effect();
opacity: 1;
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
animation-play-state: paused;
}
&-slide-up-enter&-slide-up-enter-active, &-slide-up-appear&-slide-up-appear-active {
animation-name: rcMenuOpenSlideUpIn;
animation-play-state: running;
}
&-slide-up-leave&-slide-up-leave-active {
animation-name: rcMenuOpenSlideUpOut;
animation-play-state: running;
}
@keyframes rcMenuOpenSlideUpIn {
0% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0);
}
100% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
}
@keyframes rcMenuOpenSlideUpOut {
0% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
100% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0);
}
}
&-zoom-enter, &-zoom-appear {
opacity: 0;
.effect();
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
animation-play-state: paused;
}
&-zoom-leave {
.effect();
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
animation-play-state: paused;
}
&-zoom-enter&-zoom-enter-active, &-zoom-appear&-zoom-appear-active {
animation-name: rcMenuOpenZoomIn;
animation-play-state: running;
}
&-zoom-leave&-zoom-leave-active {
animation-name: rcMenuOpenZoomOut;
animation-play-state: running;
}
@keyframes rcMenuOpenZoomIn {
0% {
opacity: 0;
transform: scale(0, 0);
}
100% {
opacity: 1;
transform: scale(1, 1);
}
}
@keyframes rcMenuOpenZoomOut {
0% {
transform: scale(1, 1);
}
100% {
opacity: 0;
transform: scale(0, 0);
}
}
}
}

View File

@ -0,0 +1,132 @@
<script>
import { cloneElement } from '../../_util/vnode'
import Menu, { SubMenu, Item as MenuItem, Divider } from '../src/index'
import '../assets/index.less'
import animate from 'css-animation'
function handleSelect (info) {
console.log(info)
console.log(`selected ${info.key}`)
}
const animation = {
enter (node, done) {
let height
return animate(node, 'rc-menu-collapse', {
start () {
height = node.offsetHeight
node.style.height = 0
},
active () {
node.style.height = `${height}px`
},
end () {
node.style.height = ''
done()
},
})
},
appear () {
return this.enter.apply(this, arguments)
},
leave (node, done) {
return animate(node, 'rc-menu-collapse', {
start () {
node.style.height = `${node.offsetHeight}px`
},
active () {
node.style.height = 0
},
end () {
node.style.height = ''
done()
},
})
},
}
export default {
methods: {
},
render () {
// const nestSubMenu = (<SubMenu title={<span>sub menu 2</span>} key='4'>
// <MenuItem key='4-1'>inner inner</MenuItem>
// <Divider/>
// <SubMenu
// key='4-2'
// title={<span>sub menu 3</span>}
// >
// <SubMenu title='sub 4-2-0' key='4-2-0'>
// <MenuItem key='4-2-0-1'>inner inner</MenuItem>
// <MenuItem key='4-2-0-2'>inner inner2</MenuItem>
// </SubMenu>
// <MenuItem key='4-2-1'>inn</MenuItem>
// <SubMenu title={<span>sub menu 4</span>} key='4-2-2'>
// <MenuItem key='4-2-2-1'>inner inner</MenuItem>
// <MenuItem key='4-2-2-2'>inner inner2</MenuItem>
// </SubMenu>
// <SubMenu title='sub 4-2-3' key='4-2-3'>
// <MenuItem key='4-2-3-1'>inner inner</MenuItem>
// <MenuItem key='4-2-3-2'>inner inner2</MenuItem>
// </SubMenu>
// </SubMenu>
// </SubMenu>)
function onOpenChange (value) {
console.log('onOpenChange', value)
}
const commonMenu = (<Menu class='test' onSelect={handleSelect} onOpenChange={onOpenChange}>
<SubMenu key='1'>
<template slot='title'><span>sub menu</span></template>
<MenuItem key='1-1'>0-1</MenuItem>
<MenuItem key='1-2'>0-2</MenuItem>
</SubMenu>
</Menu>)
const horizontalMenu = cloneElement(commonMenu, { props: {
mode: 'horizontal',
// use openTransition for antd
openAnimation: 'slide-up',
}})
// const horizontalMenu2 = cloneElement(commonMenu, { props: {
// mode: 'horizontal',
// openAnimation: 'slide-up',
// triggerSubMenuAction: 'click',
// }})
// const verticalMenu = cloneElement(commonMenu, { props: {
// mode: 'vertical',
// openAnimation: 'zoom',
// }})
// const inlineMenu = cloneElement(commonMenu, { props: {
// mode: 'inline',
// defaultOpenKeys: ['1'],
// openAnimation: animation,
// }})
return (
<div style={{ margin: '20px' }}>
<h2>antd menu</h2>
<div>
<h3>horizontal</h3>
<div style={{ margin: '20px', width: '800px' }}>{horizontalMenu}</div>
<h3>horizontal and click</h3>
{/*
<div style={{ margin: '20px', width: '800px' }}>{horizontalMenu2}</div>
<h3>vertical</h3>
<div style={{ margin: '20px', width: '200px' }}>{verticalMenu}</div>
<h3>inline</h3>
<div style={{ margin: '20px', width: '400px' }}>{inlineMenu}</div>
*/}
</div>
</div>
)
},
}
</script>

View File

@ -0,0 +1 @@
module.exports = require('./src/')

View File

@ -11,7 +11,10 @@ export default {
type: String, type: String,
default: 'div', default: 'div',
}, },
hiddenClassName: String, hiddenClassName: {
type: String,
default: '',
},
}, },
computed: { computed: {
class () { class () {
@ -31,7 +34,7 @@ export default {
const tagProps = { const tagProps = {
attr: { ...otherProps, ...this.$attrs }, attr: { ...otherProps, ...this.$attrs },
} }
return <Tag {...tagProps} class={this.class} /> return <Tag {...tagProps} class={this.class}>{this.$slots.default}</Tag>
}, },
} }
</script> </script>

View File

@ -2,6 +2,7 @@
import PropTypes from '../../_util/vue-types' import PropTypes from '../../_util/vue-types'
import MenuMixin from './MenuMixin' import MenuMixin from './MenuMixin'
import StateMixin from '../../_util/StateMixin' import StateMixin from '../../_util/StateMixin'
import hasProp from '../../_util/hasProp'
const Menu = { const Menu = {
name: 'Menu', name: 'Menu',
@ -27,14 +28,14 @@ const Menu = {
data () { data () {
const props = this.$props const props = this.$props
let selectedKeys = props.defaultSelectedKeys let sSelectedKeys = props.defaultSelectedKeys
let openKeys = props.defaultOpenKeys let sOpenKeys = props.defaultOpenKeys
selectedKeys = props.selectedKeys || [] sSelectedKeys = props.selectedKeys || []
openKeys = props.openKeys || [] sOpenKeys = props.openKeys || []
this.isRootMenu = true this.isRootMenu = true
return { return {
selectedKeys, sSelectedKeys,
openKeys, sOpenKeys,
} }
}, },
watch: { watch: {
@ -42,10 +43,10 @@ const Menu = {
handler: function (nextProps) { handler: function (nextProps) {
const props = {} const props = {}
if (nextProps.selectedKeys === undefined) { if (nextProps.selectedKeys === undefined) {
props.selectedKeys = nextProps.selectedKeys || [] props.sSelectedKeys = nextProps.selectedKeys || []
} }
if (nextProps.openKeys === undefined) { if (nextProps.openKeys === undefined) {
props.openKeys = nextProps.openKeys || [] props.sOpenKeys = nextProps.openKeys || []
} }
this.setState(props) this.setState(props)
}, },
@ -55,16 +56,15 @@ const Menu = {
methods: { methods: {
onDestroy (key) { onDestroy (key) {
const state = this.$data const state = this.$data
const props = this.$props const sSelectedKeys = state.sSelectedKeys
const selectedKeys = state.selectedKeys const sOpenKeys = state.sOpenKeys
const openKeys = state.openKeys let index = sSelectedKeys.indexOf(key)
let index = selectedKeys.indexOf(key) if (!hasProp(this, 'selectedKeys') && index !== -1) {
if (!('selectedKeys' in props) && index !== -1) { sSelectedKeys.splice(index, 1)
selectedKeys.splice(index, 1)
} }
index = openKeys.indexOf(key) index = sOpenKeys.indexOf(key)
if (!('openKeys' in props) && index !== -1) { if (!hasProp(this, 'openKeys') && index !== -1) {
openKeys.splice(index, 1) sOpenKeys.splice(index, 1)
} }
}, },
@ -72,21 +72,21 @@ const Menu = {
const props = this.$props const props = this.$props
if (props.selectable) { if (props.selectable) {
// root menu // root menu
let selectedKeys = this.$data.selectedKeys let sSelectedKeys = this.$data.sSelectedKeys
const selectedKey = selectInfo.key const selectedKey = selectInfo.key
if (props.multiple) { if (props.multiple) {
selectedKeys = selectedKeys.concat([selectedKey]) sSelectedKeys = sSelectedKeys.concat([selectedKey])
} else { } else {
selectedKeys = [selectedKey] sSelectedKeys = [selectedKey]
} }
if (!('selectedKeys' in props)) { if (!hasProp(this, 'selectedKeys')) {
this.setState({ this.setState({
selectedKeys, sSelectedKeys,
}) })
} }
this.$emit('select', { this.$emit('select', {
...selectInfo, ...selectInfo,
selectedKeys, sSelectedKeys,
}) })
} }
}, },
@ -96,20 +96,20 @@ const Menu = {
}, },
onOpenChange (e_) { onOpenChange (e_) {
const openKeys = this.$data.openKeys.concat() const sOpenKeys = this.$data.sOpenKeys.concat()
let changed = false let changed = false
const processSingle = (e) => { const processSingle = (e) => {
let oneChanged = false let oneChanged = false
if (e.open) { if (e.open) {
oneChanged = openKeys.indexOf(e.key) === -1 oneChanged = sOpenKeys.indexOf(e.key) === -1
if (oneChanged) { if (oneChanged) {
openKeys.push(e.key) sOpenKeys.push(e.key)
} }
} else { } else {
const index = openKeys.indexOf(e.key) const index = sOpenKeys.indexOf(e.key)
oneChanged = index !== -1 oneChanged = index !== -1
if (oneChanged) { if (oneChanged) {
openKeys.splice(index, 1) sOpenKeys.splice(index, 1)
} }
} }
changed = changed || oneChanged changed = changed || oneChanged
@ -121,30 +121,30 @@ const Menu = {
processSingle(e_) processSingle(e_)
} }
if (changed) { if (changed) {
if (this.$props.openKeys === undefined) { if (!hasProp(this, 'openKeys')) {
this.setState({ openKeys }) this.setState({ sOpenKeys })
} }
this.$emit('openChange', openKeys) this.$emit('openChange', sOpenKeys)
} }
}, },
onDeselect (selectInfo) { onDeselect (selectInfo) {
const props = this.$props const props = this.$props
if (props.selectable) { if (props.selectable) {
const selectedKeys = this.$data.selectedKeys.concat() const sSelectedKeys = this.$data.sSelectedKeys.concat()
const selectedKey = selectInfo.key const selectedKey = selectInfo.key
const index = selectedKeys.indexOf(selectedKey) const index = sSelectedKeys.indexOf(selectedKey)
if (index !== -1) { if (index !== -1) {
selectedKeys.splice(index, 1) sSelectedKeys.splice(index, 1)
} }
if (!('selectedKeys' in props)) { if (!hasProp(this, 'selectedKeys')) {
this.setState({ this.setState({
selectedKeys, sSelectedKeys,
}) })
} }
this.$emit('deselect', { this.$emit('deselect', {
...selectInfo, ...selectInfo,
selectedKeys, sSelectedKeys,
}) })
} }
}, },
@ -165,10 +165,10 @@ const Menu = {
lastOpenSubMenu () { lastOpenSubMenu () {
let lastOpen = [] let lastOpen = []
const { openKeys } = this.$data const { sOpenKeys } = this.$data
if (openKeys.length) { if (sOpenKeys.length) {
lastOpen = this.getFlatInstanceArray().filter((c) => { lastOpen = this.getFlatInstanceArray().filter((c) => {
return c && openKeys.indexOf(c.props.eventKey) !== -1 return c && sOpenKeys.indexOf(c.props.eventKey) !== -1
}) })
} }
return lastOpen[0] return lastOpen[0]
@ -180,8 +180,8 @@ const Menu = {
} }
const state = this.$data const state = this.$data
const extraProps = { const extraProps = {
openKeys: state.openKeys, openKeys: state.sOpenKeys,
selectedKeys: state.selectedKeys, selectedKeys: state.sSelectedKeys,
triggerSubMenuAction: this.$props.triggerSubMenuAction, triggerSubMenuAction: this.$props.triggerSubMenuAction,
} }
return this.renderCommonMenuItem(c, i, subIndex, extraProps) return this.renderCommonMenuItem(c, i, subIndex, extraProps)
@ -190,7 +190,7 @@ const Menu = {
render () { render () {
const props = { ...this.$props } const props = { ...this.$props }
props.className += ` ${props.prefixCls}-root` props.class = ` ${props.prefixCls}-root`
return this.renderRoot(props) return this.renderRoot(props)
}, },
} }

View File

@ -10,6 +10,7 @@ const MenuItemGroup = {
className: PropTypes.string, className: PropTypes.string,
rootPrefixCls: PropTypes.string, rootPrefixCls: PropTypes.string,
disabled: PropTypes.bool.def(true), disabled: PropTypes.bool.def(true),
title: PropTypes.any.def(''),
}, },
methods: { methods: {

View File

@ -1,14 +1,21 @@
import PropTypes from 'vue-types' import PropTypes from '../../_util/vue-types'
import hasProp from '../../_util/hasProp'
import KeyCode from '../../_util/KeyCode' import KeyCode from '../../_util/KeyCode'
import scrollIntoView from 'dom-scroll-into-view' import scrollIntoView from 'dom-scroll-into-view'
import { getKeyFromChildrenIndex, loopMenuItem } from './util' import { getKeyFromChildrenIndex, loopMenuItem } from './util'
import StateMixin from '../../_util/StateMixin'
import { cloneElement, cloneVNode } from '../../_util/vnode'
import DOMWrap from './DOMWrap' import DOMWrap from './DOMWrap'
function allDisabled (arr) { function allDisabled (arr) {
if (!arr.length) { if (!arr.length) {
return true return true
} }
return arr.every(c => !!c.props.disabled)
return arr.every(c => {
const propsData = c.componentOptions.propsData || {}
return !!propsData.disabled
})
} }
function getActiveKey (props, originalActiveKey) { function getActiveKey (props, originalActiveKey) {
@ -17,7 +24,8 @@ function getActiveKey (props, originalActiveKey) {
if (activeKey) { if (activeKey) {
let found let found
loopMenuItem(children, (c, i) => { loopMenuItem(children, (c, i) => {
if (c && !c.props.disabled && activeKey === getKeyFromChildrenIndex(c, eventKey, i)) { const propsData = c.componentOptions.propsData || {}
if (c && !propsData.disabled && activeKey === getKeyFromChildrenIndex(c, eventKey, i)) {
found = true found = true
} }
}) })
@ -28,7 +36,8 @@ function getActiveKey (props, originalActiveKey) {
activeKey = null activeKey = null
if (props.defaultActiveFirst) { if (props.defaultActiveFirst) {
loopMenuItem(children, (c, i) => { loopMenuItem(children, (c, i) => {
if (!activeKey && c && !c.props.disabled) { const propsData = c.componentOptions.propsData || {}
if (!activeKey && c && !propsData.disabled) {
activeKey = getKeyFromChildrenIndex(c, eventKey, i) activeKey = getKeyFromChildrenIndex(c, eventKey, i)
} }
}) })
@ -37,24 +46,12 @@ function getActiveKey (props, originalActiveKey) {
return activeKey return activeKey
} }
function saveRef (index, subIndex, c) {
if (c) {
if (subIndex !== undefined) {
this.instanceArray[index] = this.instanceArray[index] || []
this.instanceArray[index][subIndex] = c
} else {
this.instanceArray[index] = c
}
}
}
const MenuMixin = { const MenuMixin = {
props: { props: {
prefixCls: PropTypes.string.def('ant-menu'), prefixCls: PropTypes.string.def('rc-menu'),
inlineIndent: PropTypes.number.def(24), inlineIndent: PropTypes.number.def(24),
focusable: PropTypes.bool.def(true), focusable: PropTypes.bool.def(true),
multiple: PropTypes.bool, multiple: PropTypes.bool,
style: PropTypes.object,
defaultActiveFirst: PropTypes.bool, defaultActiveFirst: PropTypes.bool,
visible: PropTypes.bool.def(true), visible: PropTypes.bool.def(true),
activeKey: PropTypes.string, activeKey: PropTypes.string,
@ -63,45 +60,60 @@ const MenuMixin = {
defaultOpenKeys: PropTypes.arrayOf(PropTypes.string), defaultOpenKeys: PropTypes.arrayOf(PropTypes.string),
openKeys: PropTypes.arrayOf(PropTypes.string), openKeys: PropTypes.arrayOf(PropTypes.string),
}, },
mixin: [StateMixin],
data () { data () {
const props = this.$props const props = this.$props
return { return {
activeKey: getActiveKey(props, props.activeKey), sActiveKey: getActiveKey(props, props.activeKey),
} }
}, },
watch: {
componentWillReceiveProps (nextProps) { '$props': {
let props handler: function (nextProps) {
if ('activeKey' in nextProps) { let props
props = { if (hasProp(this, 'activeKey')) {
activeKey: getActiveKey(nextProps, nextProps.activeKey), props = {
} sActiveKey: getActiveKey(nextProps, nextProps.activeKey),
} else { }
const originalActiveKey = this.state.activeKey } else {
const activeKey = getActiveKey(nextProps, originalActiveKey) const originalActiveKey = this.$data.sActiveKey
// fix: this.setState(), parent.render(), const sActiveKey = getActiveKey(nextProps, originalActiveKey)
if (activeKey !== originalActiveKey) { // fix: this.setState(), parent.render(),
props = { if (sActiveKey !== originalActiveKey) {
activeKey, props = {
sActiveKey,
}
}
} }
} if (props) {
} this.setState(props)
if (props) { }
this.setState(props) },
} deep: true,
},
}, },
created () { created () {
this.instanceArray = [] this.instanceArray = []
}, },
methods: { methods: {
saveRef (index, subIndex, c) {
if (c) {
if (subIndex !== undefined) {
this.instanceArray[index] = this.instanceArray[index] || []
this.instanceArray[index][subIndex] = c
} else {
this.instanceArray[index] = c
}
}
},
// all keyboard events callbacks run from here at first // all keyboard events callbacks run from here at first
onKeyDown (e, callback) { onKeyDown (e, callback) {
const keyCode = e.keyCode const keyCode = e.keyCode
let handled let handled
this.getFlatInstanceArray().forEach((obj) => { this.getFlatInstanceArray().forEach((obj) => {
if (obj && obj.props.active && obj.onKeyDown) { if (obj && obj.$props.active) {
handled = obj.onKeyDown(e) handled = this.$emit('keydown', e)
} }
}) })
if (handled) { if (handled) {
@ -114,7 +126,7 @@ const MenuMixin = {
if (activeItem) { if (activeItem) {
e.preventDefault() e.preventDefault()
this.setState({ this.setState({
activeKey: activeItem.$props.eventKey, sActiveKey: activeItem.$props.eventKey,
}, () => { }, () => {
scrollIntoView(activeItem.$el, this.$el, { scrollIntoView(activeItem.$el, this.$el, {
onlyScrollIfNeeded: true, onlyScrollIfNeeded: true,
@ -128,7 +140,7 @@ const MenuMixin = {
} else if (activeItem === undefined) { } else if (activeItem === undefined) {
e.preventDefault() e.preventDefault()
this.setState({ this.setState({
activeKey: null, sActiveKey: null,
}) })
return 1 return 1
} }
@ -137,7 +149,7 @@ const MenuMixin = {
onItemHover (e) { onItemHover (e) {
const { key, hover } = e const { key, hover } = e
this.setState({ this.setState({
activeKey: hover ? key : null, sActiveKey: hover ? key : null,
}) })
}, },
@ -161,72 +173,83 @@ const MenuMixin = {
}, },
renderCommonMenuItem (child, i, subIndex, extraProps) { renderCommonMenuItem (child, i, subIndex, extraProps) {
const state = this.state const state = this.$data
const props = this.props const props = this.$props
const key = getKeyFromChildrenIndex(child, props.eventKey, i) const key = getKeyFromChildrenIndex(child, props.eventKey, i)
const childProps = child.props const childProps = child.componentOptions.propsData || {}
const isActive = key === state.activeKey const isActive = key === state.sActiveKey
const newChildProps = { const newChildProps = {
mode: props.mode, props: {
level: props.level, mode: props.mode,
inlineIndent: props.inlineIndent, level: props.level,
renderMenuItem: this.renderMenuItem, inlineIndent: props.inlineIndent,
rootPrefixCls: props.prefixCls, renderMenuItem: this.renderMenuItem,
index: i, rootPrefixCls: props.prefixCls,
parentMenu: this, index: i,
ref: child.ref, parentMenu: this,
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: () => { console.log('openChange') },
deselect: this.onDeselect,
destroy: this.onDestroy,
select: this.onSelect,
},
ref: childProps.disabled ? undefined : child.ref,
// ref: childProps.disabled ? undefined // ref: childProps.disabled ? undefined
// : createChainedFunction(child.ref, saveRef.bind(this, i, subIndex)), // : createChainedFunction(child.ref, saveRef.bind(this, i, subIndex)),
eventKey: key,
active: !childProps.disabled && isActive,
multiple: props.multiple,
onClick: this.onClick,
onItemHover: this.onItemHover,
openTransitionName: this.getOpenTransitionName(),
openAnimation: props.openAnimation,
subMenuOpenDelay: props.subMenuOpenDelay,
subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender,
onOpenChange: this.onOpenChange,
onDeselect: this.onDeselect,
onDestroy: this.onDestroy,
onSelect: this.onSelect,
...extraProps,
} }
!childProps.disabled && this.saveRef(i, subIndex, child.ref)
if (props.mode === 'inline') { if (props.mode === 'inline') {
newChildProps.triggerSubMenuAction = 'click' newChildProps.props.triggerSubMenuAction = 'click'
} }
return child return cloneElement(child, newChildProps)
// return React.cloneElement(child, newChildProps)
}, },
renderRoot (props) { renderRoot (props) {
this.instanceArray = [] this.instanceArray = []
const className = { const className = {
[props.prefixCls]: true, [props.prefixCls]: true,
[props.className]: true, [props.class]: true,
[`${props.prefixCls}-${props.mode}`]: true, [`${props.prefixCls}-${props.mode}`]: true,
} }
const domProps = { const domProps = {
className, attrs: {
role: 'menu', role: 'menu',
'aria-activedescendant': '', 'aria-activedescendant': '',
},
props: {
tag: 'ul',
hiddenClassName: `${props.prefixCls}-hidden`,
visible: props.visible,
},
class: className,
on: {},
// style:props.style,
} }
if (props.id) { if (props.id) {
domProps.id = props.id domProps.id = props.id
} }
if (props.focusable) { if (props.focusable) {
domProps.tabIndex = '0' domProps.attrs.tabIndex = '0'
domProps.onKeyDown = this.onKeyDown domProps.on.keydown = this.onKeyDown
} }
return ( return (
// ESLint is not smart enough to know that the type of `children` was checked. // ESLint is not smart enough to know that the type of `children` was checked.
/* eslint-disable */ /* eslint-disable */
<DOMWrap <DOMWrap
style={props.style}
tag="ul"
hiddenClassName={`${props.prefixCls}-hidden`}
visible={props.visible}
{...domProps} {...domProps}
> >
{this.$slots.default.map(this.renderMenuItem)} {this.$slots.default.map(this.renderMenuItem)}
@ -237,7 +260,7 @@ const MenuMixin = {
step (direction) { step (direction) {
let children = this.getFlatInstanceArray() let children = this.getFlatInstanceArray()
const activeKey = this.state.activeKey const sActiveKey = this.$data.sActiveKey
const len = children.length const len = children.length
if (!len) { if (!len) {
return null return null
@ -248,13 +271,14 @@ const MenuMixin = {
// find current activeIndex // find current activeIndex
let activeIndex = -1 let activeIndex = -1
children.every((c, ci) => { children.every((c, ci) => {
if (c && c.props.eventKey === activeKey) { const propsData = c.componentOptions.propsData || {}
if (c && propsData.eventKey === sActiveKey) {
activeIndex = ci activeIndex = ci
return false return false
} }
return true return true
}) })
if (!this.props.defaultActiveFirst && activeIndex !== -1) { if (!this.$props.defaultActiveFirst && activeIndex !== -1) {
if (allDisabled(children.slice(activeIndex, len - 1))) { if (allDisabled(children.slice(activeIndex, len - 1))) {
return undefined return undefined
} }
@ -263,7 +287,8 @@ const MenuMixin = {
let i = start let i = start
for (; ;) { for (; ;) {
const child = children[i] const child = children[i]
if (!child || child.props.disabled) { const propsData = child.componentOptions.propsData || {}
if (!child || propsData.disabled) {
i = (i + 1 + len) % len i = (i + 1 + len) % len
// complete a loop // complete a loop
if (i === start) { if (i === start) {

View File

@ -4,7 +4,8 @@ import Trigger from '../../trigger'
import KeyCode from '../../_util/KeyCode' import KeyCode from '../../_util/KeyCode'
import SubPopupMenu from './SubPopupMenu' import SubPopupMenu from './SubPopupMenu'
import placements from './placements' import placements from './placements'
import { noop, loopMenuItemRecusively } from './util' import { loopMenuItemRecusively } from './util'
import StateMixin from '../../_util/StateMixin'
let guid = 0 let guid = 0
@ -20,335 +21,326 @@ const SubMenu = {
props: { props: {
parentMenu: PropTypes.object, parentMenu: PropTypes.object,
title: PropTypes.node, mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']).def('vertical'),
title: PropTypes.any.def(''),
children: PropTypes.any, children: PropTypes.any,
selectedKeys: PropTypes.array, selectedKeys: PropTypes.array,
openKeys: PropTypes.array, openKeys: PropTypes.array,
onClick: PropTypes.func, // onClick: PropTypes.func,
onOpenChange: PropTypes.func, openChange: PropTypes.func,
rootPrefixCls: PropTypes.string, rootPrefixCls: PropTypes.string,
eventKey: PropTypes.string, eventKey: PropTypes.string,
multiple: PropTypes.bool, multiple: PropTypes.bool,
active: PropTypes.bool, // TODO: remove active: PropTypes.bool, // TODO: remove
onItemHover: PropTypes.func, // onItemHover: PropTypes.func,
onSelect: PropTypes.func, // onSelect: PropTypes.func,
triggerSubMenuAction: PropTypes.string, triggerSubMenuAction: PropTypes.string,
onDeselect: PropTypes.func, popupClassName: PropTypes.string,
onDestroy: PropTypes.func, // onDeselect: PropTypes.func,
onMouseEnter: PropTypes.func, // onDestroy: PropTypes.func,
onMouseLeave: PropTypes.func, // onMouseEnter: PropTypes.func,
onTitleMouseEnter: PropTypes.func, // onMouseLeave: PropTypes.func,
onTitleMouseLeave: PropTypes.func, // onTitleMouseEnter: PropTypes.func,
onTitleClick: PropTypes.func, // onTitleMouseLeave: PropTypes.func,
// onTitleClick: PropTypes.func,
}, },
mixins: [StateMixin],
isRootMenu: false, data () {
getDefaultProps () {
return {
onMouseEnter: noop,
onMouseLeave: noop,
onTitleMouseEnter: noop,
onTitleMouseLeave: noop,
onTitleClick: noop,
title: '',
}
},
getInitialState () {
this.isSubMenu = 1 this.isSubMenu = 1
this.isRootMenu = false
return { return {
defaultActiveFirst: false, defaultActiveFirst: false,
} }
}, },
componentDidMount () { mounted () {
this.componentDidUpdate() this.handleUpdated()
}, },
componentDidUpdate () { updated () {
const { mode, parentMenu } = this.props this.handleUpdated()
if (mode !== 'horizontal' || !parentMenu.isRootMenu || !this.isOpen()) {
return
}
setTimeout(() => {
if (!this.subMenuTitle || !this.menuInstance) {
return
}
const popupMenu = this.$refs.menuInstance.$el
if (popupMenu.offsetWidth >= this.subMenuTitle.offsetWidth) {
return
}
popupMenu.style.minWidth = `${this.subMenuTitle.offsetWidth}px`
}, 0)
}, },
componentWillUnmount () { beforeDestroy () {
const { onDestroy, eventKey, parentMenu } = this.props const { eventKey, parentMenu } = this.$props
if (onDestroy) { this.$emit('destroy', eventKey)
onDestroy(eventKey)
}
if (parentMenu.subMenuInstance === this) { if (parentMenu.subMenuInstance === this) {
this.clearSubMenuTimers() this.clearSubMenuTimers()
} }
}, },
methods: {
handleUpdated () {
const { mode, parentMenu } = this.$props
if (mode !== 'horizontal' || !parentMenu.isRootMenu || !this.isOpen()) {
return
}
setTimeout(() => {
if (!this.subMenuTitle || !this.menuInstance) {
return
}
const popupMenu = this.$refs.menuInstance.$el
if (popupMenu.offsetWidth >= this.subMenuTitle.offsetWidth) {
return
}
popupMenu.style.minWidth = `${this.subMenuTitle.offsetWidth}px`
}, 0)
},
onDestroy (key) {
this.$emit('destroy', key)
},
onDestroy (key) { onKeyDown (e) {
this.props.onDestroy(key) const keyCode = e.keyCode
}, const menu = this.menuInstance
const isOpen = this.isOpen()
onKeyDown (e) { if (keyCode === KeyCode.ENTER) {
const keyCode = e.keyCode this.onTitleClick(e)
const menu = this.menuInstance
const isOpen = this.isOpen()
if (keyCode === KeyCode.ENTER) {
this.onTitleClick(e)
this.setState({
defaultActiveFirst: true,
})
return true
}
if (keyCode === KeyCode.RIGHT) {
if (isOpen) {
menu.onKeyDown(e)
} else {
this.triggerOpenChange(true)
this.setState({ this.setState({
defaultActiveFirst: true, defaultActiveFirst: true,
}) })
return true
} }
return true
} if (keyCode === KeyCode.RIGHT) {
if (keyCode === KeyCode.LEFT) { if (isOpen) {
let handled menu.onKeyDown(e)
if (isOpen) { } else {
handled = menu.onKeyDown(e) this.triggerOpenChange(true)
} else { this.setState({
return undefined defaultActiveFirst: true,
})
}
return true
} }
if (!handled) { if (keyCode === KeyCode.LEFT) {
this.triggerOpenChange(false) let handled
handled = true if (isOpen) {
handled = menu.onKeyDown(e)
} else {
return undefined
}
if (!handled) {
this.triggerOpenChange(false)
handled = true
}
return handled
} }
return handled
}
if (isOpen && (keyCode === KeyCode.UP || keyCode === KeyCode.DOWN)) { if (isOpen && (keyCode === KeyCode.UP || keyCode === KeyCode.DOWN)) {
return menu.onKeyDown(e) return menu.onKeyDown(e)
} }
}, },
onOpenChange (e) { onOpenChange (e) {
this.props.onOpenChange(e) this.$emit('openChange', e)
}, this.$props.openChange(e)
},
onPopupVisibleChange (visible) { onPopupVisibleChange (visible) {
this.triggerOpenChange(visible, visible ? 'mouseenter' : 'mouseleave') this.triggerOpenChange(visible, visible ? 'mouseenter' : 'mouseleave')
}, },
onMouseEnter (e) { onMouseEnter (e) {
const { eventKey: key, onMouseEnter } = this.props const { eventKey: key } = this.$props
this.clearSubMenuLeaveTimer() this.clearSubMenuLeaveTimer()
this.setState({ this.setState({
defaultActiveFirst: false, defaultActiveFirst: false,
}) })
onMouseEnter({ this.$emit('mouseenter', {
key, key,
domEvent: e, domEvent: e,
}) })
}, },
onMouseLeave (e) { onMouseLeave (e) {
const { const {
parentMenu, parentMenu,
eventKey, eventKey,
onMouseLeave, } = this.$props
} = this.props parentMenu.subMenuInstance = this
parentMenu.subMenuInstance = this parentMenu.subMenuLeaveFn = () => {
parentMenu.subMenuLeaveFn = () => {
// trigger mouseleave // trigger mouseleave
onMouseLeave({ this.$emit('mouseleave', {
key: eventKey,
domEvent: e,
})
}
// prevent popup menu and submenu gap
parentMenu.subMenuLeaveTimer = setTimeout(parentMenu.subMenuLeaveFn, 100)
},
onTitleMouseEnter (domEvent) {
const { eventKey: key } = this.$props
this.clearSubMenuTitleLeaveTimer()
this.$emit('itemHover', {
key,
hover: true,
})
this.$emit('titleMouseenter', {
key,
domEvent,
})
},
onTitleMouseLeave (e) {
const { parentMenu, eventKey } = this.$props
parentMenu.subMenuInstance = this
parentMenu.subMenuTitleLeaveFn = () => {
this.$emit('itemHover', {
key: eventKey,
hover: false,
})
this.$emit('titleMouseleave', {
key: eventKey,
domEvent: e,
})
}
parentMenu.subMenuTitleLeaveTimer = setTimeout(parentMenu.subMenuTitleLeaveFn, 100)
},
onTitleClick (e) {
const { triggerSubMenuAction, eventKey } = this.$props
this.$emit('itemClick', {
key: eventKey, key: eventKey,
domEvent: e, domEvent: e,
test: 111,
}) })
} if (triggerSubMenuAction === 'hover') {
// prevent popup menu and submenu gap return
parentMenu.subMenuLeaveTimer = setTimeout(parentMenu.subMenuLeaveFn, 100) }
}, this.triggerOpenChange(!this.isOpen(), 'click')
this.setState({
onTitleMouseEnter (domEvent) { defaultActiveFirst: false,
const { eventKey: key, onItemHover, onTitleMouseEnter } = this.props
this.clearSubMenuTitleLeaveTimer()
onItemHover({
key,
hover: true,
})
onTitleMouseEnter({
key,
domEvent,
})
},
onTitleMouseLeave (e) {
const { parentMenu, eventKey, onItemHover, onTitleMouseLeave } = this.props
parentMenu.subMenuInstance = this
parentMenu.subMenuTitleLeaveFn = () => {
onItemHover({
key: eventKey,
hover: false,
}) })
onTitleMouseLeave({ },
key: eventKey,
domEvent: e, onSubMenuClick (info) {
this.$emit('click', this.addKeyPath(info))
},
onSelect (info) {
this.$emit('select', info)
},
onDeselect (info) {
this.$emit('deselect', info)
},
getPrefixCls () {
return `${this.$props.rootPrefixCls}-submenu`
},
getActiveClassName () {
return `${this.getPrefixCls()}-active`
},
getDisabledClassName () {
return `${this.getPrefixCls()}-disabled`
},
getSelectedClassName () {
return `${this.getPrefixCls()}-selected`
},
getOpenClassName () {
return `${this.$props.rootPrefixCls}-submenu-open`
},
addKeyPath (info) {
return {
...info,
keyPath: (info.keyPath || []).concat(this.$props.eventKey),
}
},
triggerOpenChange (open, type) {
const key = this.$props.eventKey
this.onOpenChange({
key,
item: this,
trigger: type,
open,
}) })
} },
parentMenu.subMenuTitleLeaveTimer = setTimeout(parentMenu.subMenuTitleLeaveFn, 100)
clearSubMenuTimers () {
this.clearSubMenuLeaveTimer()
this.clearSubMenuTitleLeaveTimer()
},
clearSubMenuTitleLeaveTimer () {
const parentMenu = this.$props.parentMenu
if (parentMenu.subMenuTitleLeaveTimer) {
clearTimeout(parentMenu.subMenuTitleLeaveTimer)
parentMenu.subMenuTitleLeaveTimer = null
parentMenu.subMenuTitleLeaveFn = null
}
},
clearSubMenuLeaveTimer () {
const parentMenu = this.$props.parentMenu
if (parentMenu.subMenuLeaveTimer) {
clearTimeout(parentMenu.subMenuLeaveTimer)
parentMenu.subMenuLeaveTimer = null
parentMenu.subMenuLeaveFn = null
}
},
isChildrenSelected () {
const ret = { find: false }
loopMenuItemRecusively(this.$slots.default, this.$props.selectedKeys, ret)
return ret.find
},
isOpen () {
return this.$props.openKeys.indexOf(this.$props.eventKey) !== -1
},
renderChildren (children) {
const props = this.$props
const subPopupMenuProps = {
props: {
mode: props.mode === 'horizontal' ? 'vertical' : props.mode,
visible: this.isOpen(),
level: props.level + 1,
inlineIndent: props.inlineIndent,
focusable: false,
selectedKeys: props.selectedKeys,
eventKey: `${props.eventKey}-menu-`,
openKeys: props.openKeys,
openTransitionName: props.openTransitionName,
openAnimation: props.openAnimation,
subMenuOpenDelay: props.subMenuOpenDelay,
subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender,
triggerSubMenuAction: props.triggerSubMenuAction,
defaultActiveFirst: this.$data.defaultActiveFirst,
multiple: props.multiple,
prefixCls: props.rootPrefixCls,
},
on: {
click: this.onSubMenuClick,
select: this.onSelect,
deselect: this.onDeselect,
destroy: this.onDestroy,
openChange: this.onOpenChange,
},
id: this._menuId,
ref: 'menuInstance',
}
return <SubPopupMenu {...subPopupMenuProps}>{children}</SubPopupMenu>
},
}, },
onTitleClick (e) { render (h) {
const { props } = this
props.onTitleClick({
key: props.eventKey,
domEvent: e,
})
if (props.triggerSubMenuAction === 'hover') {
return
}
this.triggerOpenChange(!this.isOpen(), 'click')
this.setState({
defaultActiveFirst: false,
})
},
onSubMenuClick (info) {
this.props.onClick(this.addKeyPath(info))
},
onSelect (info) {
this.props.onSelect(info)
},
onDeselect (info) {
this.props.onDeselect(info)
},
getPrefixCls () {
return `${this.props.rootPrefixCls}-submenu`
},
getActiveClassName () {
return `${this.getPrefixCls()}-active`
},
getDisabledClassName () {
return `${this.getPrefixCls()}-disabled`
},
getSelectedClassName () {
return `${this.getPrefixCls()}-selected`
},
getOpenClassName () {
return `${this.props.rootPrefixCls}-submenu-open`
},
saveMenuInstance (c) {
this.menuInstance = c
},
addKeyPath (info) {
return {
...info,
keyPath: (info.keyPath || []).concat(this.props.eventKey),
}
},
triggerOpenChange (open, type) {
const key = this.props.eventKey
this.onOpenChange({
key,
item: this,
trigger: type,
open,
})
},
clearSubMenuTimers () {
this.clearSubMenuLeaveTimer()
this.clearSubMenuTitleLeaveTimer()
},
clearSubMenuTitleLeaveTimer () {
const parentMenu = this.props.parentMenu
if (parentMenu.subMenuTitleLeaveTimer) {
clearTimeout(parentMenu.subMenuTitleLeaveTimer)
parentMenu.subMenuTitleLeaveTimer = null
parentMenu.subMenuTitleLeaveFn = null
}
},
clearSubMenuLeaveTimer () {
const parentMenu = this.props.parentMenu
if (parentMenu.subMenuLeaveTimer) {
clearTimeout(parentMenu.subMenuLeaveTimer)
parentMenu.subMenuLeaveTimer = null
parentMenu.subMenuLeaveFn = null
}
},
isChildrenSelected () {
const ret = { find: false }
loopMenuItemRecusively(this.props.children, this.props.selectedKeys, ret)
return ret.find
},
isOpen () {
return this.props.openKeys.indexOf(this.props.eventKey) !== -1
},
renderChildren (children) {
const props = this.$props
const baseProps = {
mode: props.mode === 'horizontal' ? 'vertical' : props.mode,
visible: this.isOpen(),
level: props.level + 1,
inlineIndent: props.inlineIndent,
focusable: false,
onClick: this.onSubMenuClick,
onSelect: this.onSelect,
onDeselect: this.onDeselect,
onDestroy: this.onDestroy,
selectedKeys: props.selectedKeys,
eventKey: `${props.eventKey}-menu-`,
openKeys: props.openKeys,
openTransitionName: props.openTransitionName,
openAnimation: props.openAnimation,
onOpenChange: this.onOpenChange,
subMenuOpenDelay: props.subMenuOpenDelay,
subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender,
triggerSubMenuAction: props.triggerSubMenuAction,
defaultActiveFirst: this.state.defaultActiveFirst,
multiple: props.multiple,
prefixCls: props.rootPrefixCls,
id: this._menuId,
ref: this.saveMenuInstance,
}
return <SubPopupMenu {...baseProps}>{children}</SubPopupMenu>
},
saveSubMenuTitle (subMenuTitle) {
this.subMenuTitle = subMenuTitle
},
render () {
const props = this.$props const props = this.$props
const isOpen = this.isOpen() const isOpen = this.isOpen()
const prefixCls = this.getPrefixCls() const prefixCls = this.getPrefixCls()
const isInlineMode = props.mode === 'inline' const isInlineMode = props.mode === 'inline'
const className = { const className = {
prefixCls: true, [prefixCls]: true,
[`${prefixCls}-${props.mode}`]: true, [`${prefixCls}-${props.mode}`]: true,
[this.getOpenClassName()]: isOpen, [this.getOpenClassName()]: isOpen,
[this.getActiveClassName()]: props.active || (isOpen && !isInlineMode), [this.getActiveClassName()]: props.active || (isOpen && !isInlineMode),
@ -369,65 +361,79 @@ const SubMenu = {
let titleMouseEvents = {} let titleMouseEvents = {}
if (!props.disabled) { if (!props.disabled) {
mouseEvents = { mouseEvents = {
onMouseLeave: this.onMouseLeave, mouseleave: this.onMouseLeave,
onMouseEnter: this.onMouseEnter, mouseenter: this.onMouseEnter,
} }
// only works in title, not outer li // only works in title, not outer li
titleClickEvents = { titleClickEvents = {
onClick: this.onTitleClick, click: this.onTitleClick,
} }
titleMouseEvents = { titleMouseEvents = {
onMouseEnter: this.onTitleMouseEnter, mouseenter: this.onTitleMouseEnter,
onMouseLeave: this.onTitleMouseLeave, mouseleave: this.onTitleMouseLeave,
} }
} }
const style = {} const style = {}
if (isInlineMode) { if (isInlineMode) {
style.paddingLeft = props.inlineIndent * props.level style.paddingLeft = `${props.inlineIndent * props.level}px`
}
const titleProps = {
attrs: {
'aria-expanded': isOpen,
'aria-owns': this._menuId,
'aria-haspopup': 'true',
title: typeof props.title === 'string' ? props.title : undefined,
},
on: {
...titleMouseEvents,
...titleClickEvents,
},
style,
class: `${prefixCls}-title`,
ref: 'subMenuTitle',
} }
const title = ( const title = (
<div <div
ref='subMenuTitle' {...titleProps}
style={style}
class={`${prefixCls}-title`}
{...titleMouseEvents}
{...titleClickEvents}
aria-expanded={isOpen}
aria-owns={this._menuId}
aria-haspopup='true'
title={typeof props.title === 'string' ? props.title : undefined}
> >
{props.title} {this.$slots.title}
<i class={`${prefixCls}-arrow`} /> <i class={`${prefixCls}-arrow`} />
</div> </div>
) )
const children = this.renderChildren(props.children) const children = this.renderChildren(this.$slots.default)
const getPopupContainer = props.parentMenu.isRootMenu const getPopupContainer = props.parentMenu.isRootMenu
? props.parentMenu.props.getPopupContainer : triggerNode => triggerNode.parentNode ? props.parentMenu.getPopupContainer : triggerNode => triggerNode.parentNode
const popupPlacement = popupPlacementMap[props.mode] const popupPlacement = popupPlacementMap[props.mode]
const popupClassName = props.mode === 'inline' ? '' : props.popupClassName const popupClassName = props.mode === 'inline' ? '' : props.popupClassName
const liProps = {
on: { ...mouseEvents },
class: className,
}
console.log(isOpen)
return ( return (
<li {...mouseEvents} class={className}> <li {...liProps}>
{isInlineMode && title} {isInlineMode && title}
{isInlineMode && children} {isInlineMode && children}
{!isInlineMode && ( {!isInlineMode && (
<Trigger <Trigger
prefixCls={prefixCls} prefixCls={prefixCls}
popupClassName={`${prefixCls}-popup ${popupClassName}`} popupClassName={`${prefixCls}-popup ${popupClassName}`}
getPopupContainer={getPopupContainer} // getPopupContainer={getPopupContainer}
builtinPlacements={placements} builtinPlacements={placements}
popupPlacement={popupPlacement} popupPlacement={popupPlacement}
popupVisible={isOpen} popupVisible={isOpen}
popup={children}
action={props.disabled ? [] : [props.triggerSubMenuAction]} action={props.disabled ? [] : [props.triggerSubMenuAction]}
mouseEnterDelay={props.subMenuOpenDelay} mouseEnterDelay={props.subMenuOpenDelay}
mouseLeaveDelay={props.subMenuCloseDelay} mouseLeaveDelay={props.subMenuCloseDelay}
onPopupVisibleChange={this.onPopupVisibleChange} onPopupVisibleChange={this.onPopupVisibleChange}
forceRender={props.forceSubMenuRender} forceRender={props.forceSubMenuRender}
> >
<template slot='popup'>
{children}
</template>
{title} {title}
</Trigger> </Trigger>
)} )}

View File

@ -79,16 +79,13 @@ export default {
delete animProps.animation.appear delete animProps.animation.appear
} }
} }
return ( return (
<Animate <transition
{...animProps} appear
showProp='visible' name={animProps.transitionName}
component=''
transitionAppear={transitionAppear}
> >
{this.renderRoot(props)} {this.renderRoot(props)}
</Animate> </transition>
) )
}, },
} }

View File

@ -7,25 +7,25 @@ export function getKeyFromChildrenIndex (child, menuEventKey, index) {
} }
export function loopMenuItem (children, cb) { export function loopMenuItem (children, cb) {
// let index = -1 let index = -1
// React.Children.forEach(children, (c) => { children.forEach((c) => {
// index++ index++
// if (c && c.type && c.type.isMenuItemGroup) { if (c && c.type && c.type.isMenuItemGroup) {
// React.Children.forEach(c.props.children, (c2) => { c.$slots.default.forEach((c2) => {
// index++ index++
// cb(c2, index) cb(c2, index)
// }) })
// } else { } else {
// cb(c, index) cb(c, index)
// } }
// }) })
} }
export function loopMenuItemRecusively (children, keys, ret) { export function loopMenuItemRecusively (children, keys, ret) {
if (!children || ret.find) { if (!children || ret.find) {
return return
} }
React.Children.forEach(children, (c) => { children.forEach((c) => {
if (ret.find) { if (ret.find) {
return return
} }
@ -36,8 +36,8 @@ export function loopMenuItemRecusively (children, keys, ret) {
} }
if (keys.indexOf(c.key) !== -1) { if (keys.indexOf(c.key) !== -1) {
ret.find = true ret.find = true
} else if (c.props.children) { } else if (c.$slots.default) {
loopMenuItemRecusively(c.props.children, keys, ret) loopMenuItemRecusively(c.$slots.default, keys, ret)
} }
} }
}) })

View File

@ -77,6 +77,7 @@
"dependencies": { "dependencies": {
"add-dom-event-listener": "^1.0.2", "add-dom-event-listener": "^1.0.2",
"css-animation": "^1.4.1", "css-animation": "^1.4.1",
"dom-scroll-into-view": "^1.2.1",
"eslint-plugin-vue": "^3.13.0", "eslint-plugin-vue": "^3.13.0",
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",