pull/9/head
tangjinzhou 2018-01-04 19:06:54 +08:00
parent 066c727e49
commit b985efc438
13 changed files with 157 additions and 162 deletions

View File

@ -44,7 +44,7 @@ export function cloneElement (node, nodeProps) {
class: cls = data.class, class: cls = data.class,
attrs = data.attrs, attrs = data.attrs,
} = nodeProps } = nodeProps
node.data = Object.assign(data, { style, attrs, class: cls }) node.data = Object.assign(data, { style, attrs, class: cls, on: { ...(data.on || {}), ...on }})
if (key !== undefined) { if (key !== undefined) {
node.key = key node.key = key
node.data.key = key node.data.key = key

View File

@ -42,7 +42,7 @@
} }
&-item-active, &-item-active,
&-submenu-active > &-submenu-title { &-submenu-active > span > &-submenu-title {
background-color: #eaf8fe; background-color: #eaf8fe;
} }
@ -193,7 +193,7 @@
} }
.effect() { .effect() {
animation-duration: .3s; animation-duration: .5s;
animation-fill-mode: both; animation-fill-mode: both;
transform-origin: 0 0; transform-origin: 0 0;
} }
@ -214,12 +214,14 @@
animation-play-state: paused; animation-play-state: paused;
} }
&-slide-up-enter&-slide-up-enter-active, &-slide-up-appear&-slide-up-appear-active { &-slide-up-enter-active, &-slide-up-appear-active {
.effect();
animation-name: rcMenuOpenSlideUpIn; animation-name: rcMenuOpenSlideUpIn;
animation-play-state: running; animation-play-state: running;
} }
&-slide-up-leave&-slide-up-leave-active { &-slide-up-leave-active {
.effect();
animation-name: rcMenuOpenSlideUpOut; animation-name: rcMenuOpenSlideUpOut;
animation-play-state: running; animation-play-state: running;
} }
@ -236,6 +238,7 @@
transform: scaleY(1); transform: scaleY(1);
} }
} }
@keyframes rcMenuOpenSlideUpOut { @keyframes rcMenuOpenSlideUpOut {
0% { 0% {
opacity: 1; opacity: 1;
@ -284,7 +287,6 @@
} }
@keyframes rcMenuOpenZoomOut { @keyframes rcMenuOpenZoomOut {
0% { 0% {
transform: scale(1, 1); transform: scale(1, 1);
} }
100% { 100% {

View File

@ -52,46 +52,49 @@ export default {
}, },
render () { render () {
// const nestSubMenu = (<SubMenu title={<span>sub menu 2</span>} key='4'> const nestSubMenu = (<SubMenu title={<span>sub menu 2</span>} key='4'>
// <MenuItem key='4-1'>inner inner</MenuItem> <MenuItem key='4-1'>inner inner</MenuItem>
// <Divider/> <Divider/>
// <SubMenu <SubMenu
// key='4-2' key='4-2'
// title={<span>sub menu 3</span>} title={<span>sub menu 3</span>}
// > >
// <SubMenu title='sub 4-2-0' key='4-2-0'> <SubMenu title='sub 4-2-0' key='4-2-0'>
// <MenuItem key='4-2-0-1'>inner inner</MenuItem> <MenuItem key='4-2-0-1'>inner inner</MenuItem>
// <MenuItem key='4-2-0-2'>inner inner2</MenuItem> <MenuItem key='4-2-0-2'>inner inner2</MenuItem>
// </SubMenu> </SubMenu>
// <MenuItem key='4-2-1'>inn</MenuItem> <MenuItem key='4-2-1'>inn</MenuItem>
// <SubMenu title={<span>sub menu 4</span>} key='4-2-2'> <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-1'>inner inner</MenuItem>
// <MenuItem key='4-2-2-2'>inner inner2</MenuItem> <MenuItem key='4-2-2-2'>inner inner2</MenuItem>
// </SubMenu> </SubMenu>
// <SubMenu title='sub 4-2-3' key='4-2-3'> <SubMenu title='sub 4-2-3' key='4-2-3'>
// <MenuItem key='4-2-3-1'>inner inner</MenuItem> <MenuItem key='4-2-3-1'>inner inner</MenuItem>
// <MenuItem key='4-2-3-2'>inner inner2</MenuItem> <MenuItem key='4-2-3-2'>inner inner2</MenuItem>
// </SubMenu> </SubMenu>
// </SubMenu> </SubMenu>
// </SubMenu>) </SubMenu>)
function onOpenChange (value) { function onOpenChange (value) {
console.log('onOpenChange', value) console.log('onOpenChange', value)
} }
const commonMenu = (<Menu class='test' onSelect={handleSelect} onOpenChange={onOpenChange}> const commonMenu = (<Menu class='test' onSelect={handleSelect} onOpenChange={onOpenChange}>
<SubMenu key='1'> <SubMenu key='1' title={<span>sub menu</span>}>
<template slot='title'><span>sub menu</span></template>
<MenuItem key='1-1'> <MenuItem key='1-1'>
0-1 0-1
<Icon type='search'/>
</MenuItem> </MenuItem>
<MenuItem key='1-2'>0-2</MenuItem> <MenuItem key='1-2'>0-2</MenuItem>
</SubMenu> </SubMenu>
{nestSubMenu}
<MenuItem key='2'>1</MenuItem>
<MenuItem key='3'>outer</MenuItem>
<MenuItem disabled>disabled</MenuItem>
<MenuItem key='5'>outer3</MenuItem>
</Menu>) </Menu>)
const horizontalMenu = cloneElement(commonMenu, { props: { const horizontalMenu = cloneElement(commonMenu, { props: {
mode: 'horizontal', mode: 'horizontal',
// use openTransition for antd // use openTransition for antd
openAnimation: 'slide-up', openAnimation: 'rc-menu-open-slide-up',
}}) }})
// const horizontalMenu2 = cloneElement(commonMenu, { props: { // const horizontalMenu2 = cloneElement(commonMenu, { props: {

View File

@ -3,26 +3,19 @@ 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' import hasProp from '../../_util/hasProp'
import commonPropsType from './commonPropsType'
const Menu = { const Menu = {
name: 'Menu', name: 'Menu',
props: { props: {
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string).def([]),
selectedKeys: PropTypes.arrayOf(PropTypes.string),
defaultOpenKeys: PropTypes.arrayOf(PropTypes.string).def([]),
openKeys: PropTypes.arrayOf(PropTypes.string),
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']).def('vertical'),
getPopupContainer: PropTypes.func, getPopupContainer: PropTypes.func,
openTransitionName: PropTypes.string, openTransitionName: PropTypes.string,
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
subMenuOpenDelay: PropTypes.number.def(0), subMenuOpenDelay: PropTypes.number.def(0),
subMenuCloseDelay: PropTypes.number.def(0.1), subMenuCloseDelay: PropTypes.number.def(0.1),
forceSubMenuRender: PropTypes.bool, forceSubMenuRender: PropTypes.bool,
triggerSubMenuAction: PropTypes.string.def('click'),
level: PropTypes.number.def(1), level: PropTypes.number.def(1),
selectable: PropTypes.bool.def(true), selectable: PropTypes.bool.def(true),
multiple: PropTypes.bool, ...commonPropsType,
children: PropTypes.any,
}, },
mixins: [StateMixin, MenuMixin], mixins: [StateMixin, MenuMixin],

View File

@ -28,15 +28,11 @@ const MenuItem = {
clearSubMenuTimers: PropTypes.func.def(noop), clearSubMenuTimers: PropTypes.func.def(noop),
}, },
mixins: [StateMixin], mixins: [StateMixin],
isMenuItem: true,
beforeDestroy () { beforeDestroy () {
const props = this.$props const props = this.$props
this.$emit('destroy', props.eventKey) this.$emit('destroy', props.eventKey)
}, },
data () {
return {
isMenuItem: 1,
}
},
methods: { methods: {
onKeyDown (e) { onKeyDown (e) {
const keyCode = e.keyCode const keyCode = e.keyCode

View File

@ -12,11 +12,7 @@ const MenuItemGroup = {
disabled: PropTypes.bool.def(true), disabled: PropTypes.bool.def(true),
title: PropTypes.any.def(''), title: PropTypes.any.def(''),
}, },
data () { isMenuItemGroup: true,
return {
isMenuItemGroup: true,
}
},
methods: { methods: {
renderInnerMenuItem (item, subIndex) { renderInnerMenuItem (item, subIndex) {
const { renderMenuItem, index } = this.$props const { renderMenuItem, index } = this.$props

View File

@ -1,9 +1,8 @@
import PropTypes from '../../_util/vue-types'
import hasProp from '../../_util/hasProp' 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 { cloneElement, cloneVNode } from '../../_util/vnode' import { cloneElement } from '../../_util/vnode'
import DOMWrap from './DOMWrap' import DOMWrap from './DOMWrap'
function allDisabled (arr) { function allDisabled (arr) {
@ -17,54 +16,11 @@ function allDisabled (arr) {
}) })
} }
function getActiveKey (props, originalActiveKey) {
let activeKey = originalActiveKey
const { children, eventKey } = props
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 (props.defaultActiveFirst) {
loopMenuItem(children, (c, i) => {
const propsData = c.componentOptions.propsData || {}
if (!activeKey && c && !propsData.disabled) {
activeKey = getKeyFromChildrenIndex(c, eventKey, i)
}
})
return activeKey
}
return activeKey
}
export default { export default {
props: {
test: PropTypes.any,
prefixCls: PropTypes.string.def('rc-menu'),
inlineIndent: PropTypes.number.def(24),
focusable: PropTypes.bool.def(true),
multiple: PropTypes.bool,
defaultActiveFirst: PropTypes.bool,
visible: PropTypes.bool.def(true),
activeKey: PropTypes.string,
selectedKeys: PropTypes.arrayOf(PropTypes.string),
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
defaultOpenKeys: PropTypes.arrayOf(PropTypes.string),
openKeys: PropTypes.arrayOf(PropTypes.string),
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']).def('vertical'),
},
data () { data () {
const props = this.$props const props = this.$props
return { return {
sActiveKey: getActiveKey(props, props.activeKey), sActiveKey: this.getActiveKey(props.activeKey),
} }
}, },
watch: { watch: {
@ -73,11 +29,11 @@ export default {
let props let props
if (hasProp(this, 'activeKey')) { if (hasProp(this, 'activeKey')) {
props = { props = {
sActiveKey: getActiveKey(nextProps, nextProps.activeKey), sActiveKey: this.getActiveKey(nextProps.activeKey),
} }
} else { } else {
const originalActiveKey = this.$data.sActiveKey const originalActiveKey = this.$data.sActiveKey
const sActiveKey = getActiveKey(nextProps, originalActiveKey) const sActiveKey = this.getActiveKey(originalActiveKey)
// fix: this.setState(), parent.render(), // fix: this.setState(), parent.render(),
if (sActiveKey !== originalActiveKey) { if (sActiveKey !== originalActiveKey) {
props = { props = {
@ -97,6 +53,34 @@ export default {
this.instanceArray = [] this.instanceArray = []
}, },
methods: { 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
},
saveRef (index, subIndex, c) { saveRef (index, subIndex, c) {
if (c) { if (c) {
if (subIndex !== undefined) { if (subIndex !== undefined) {
@ -234,8 +218,8 @@ export default {
}, },
props: { props: {
tag: 'ul', tag: 'ul',
hiddenClassName: `${props.prefixCls}-hidden`, // hiddenClassName: `${props.prefixCls}-hidden`,
visible: props.visible, // visible: props.visible,
}, },
class: className, class: className,
on: {}, on: {},
@ -249,11 +233,11 @@ export default {
} }
const newChildren = children.map(this.renderMenuItem) const newChildren = children.map(this.renderMenuItem)
return ( return (
<ul <DOMWrap
{...domProps} {...domProps}
> >
{newChildren} {newChildren}
</ul> </DOMWrap>
) )
}, },

View File

@ -37,6 +37,10 @@ export default {
popupClassName: PropTypes.string, popupClassName: PropTypes.string,
getPopupContainer: PropTypes.func, getPopupContainer: PropTypes.func,
test: PropTypes.any, test: PropTypes.any,
forceSubMenuRender: PropTypes.bool,
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
disabled: PropTypes.bool,
subMenuCloseDelay: PropTypes.number,
// onDeselect: PropTypes.func, // onDeselect: PropTypes.func,
// onDestroy: PropTypes.func, // onDestroy: PropTypes.func,
// onMouseEnter: PropTypes.func, // onMouseEnter: PropTypes.func,
@ -46,10 +50,10 @@ export default {
// onTitleClick: PropTypes.func, // onTitleClick: PropTypes.func,
}, },
mixins: [StateMixin], mixins: [StateMixin],
isSubMenu: true,
data () { data () {
return { return {
defaultActiveFirst: false, defaultActiveFirst: false,
isSubMenu: 1,
} }
}, },
mounted () { mounted () {
@ -160,7 +164,6 @@ export default {
domEvent: e, domEvent: e,
}) })
} }
// prevent popup menu and submenu gap // prevent popup menu and submenu gap
this.subMenuLeaveTimer = setTimeout(this.subMenuLeaveFn, 100) this.subMenuLeaveTimer = setTimeout(this.subMenuLeaveFn, 100)
}, },
@ -390,6 +393,7 @@ export default {
<div <div
{...titleProps} {...titleProps}
> >
{typeof props.title === 'function' ? props.title(h) : props.title}
{this.$slots.title} {this.$slots.title}
<i class={`${prefixCls}-arrow`} /> <i class={`${prefixCls}-arrow`} />
</div> </div>
@ -404,12 +408,38 @@ export default {
on: { ...mouseEvents }, on: { ...mouseEvents },
class: className, class: className,
} }
const { forceSubMenuRender, mode, openTransitionName, openAnimation } = this.$props
const haveRendered = this.haveRendered
this.haveRendered = true
this.haveOpened = this.haveOpened || isOpen || forceSubMenuRender
const transitionAppear = !(!haveRendered && isOpen && mode === 'inline')
let animProps = { appear: true }
if (openTransitionName) {
animProps.name = openTransitionName
} else if (typeof openAnimation === 'object') {
animProps = { ...animProps, ...openAnimation }
if (!transitionAppear) {
animProps.appear = false
}
} else if (typeof openAnimation === 'string') {
animProps.name = openAnimation
}
const transitionProps = {
props: animProps,
}
return ( return (
<li {...liProps}> <li {...liProps}>
{isInlineMode && title} {isInlineMode && title}
{isInlineMode && children} {isInlineMode && (
<transition {...transitionProps}>
{children}
</transition>
)}
{!isInlineMode && ( {!isInlineMode && (
<Trigger <Trigger
prefixCls={prefixCls} prefixCls={prefixCls}
popupClassName={`${prefixCls}-popup ${popupClassName || ''}`} popupClassName={`${prefixCls}-popup ${popupClassName || ''}`}
@ -422,9 +452,11 @@ export default {
mouseLeaveDelay={props.subMenuCloseDelay} mouseLeaveDelay={props.subMenuCloseDelay}
onPopupVisibleChange={this.onPopupVisibleChange} onPopupVisibleChange={this.onPopupVisibleChange}
forceRender={props.forceSubMenuRender} forceRender={props.forceSubMenuRender}
// popupTransitionName='rc-menu-open-slide-up'
popupAnimation={animProps}
> >
<template slot='popup'> <template slot='popup'>
{children} {this.haveOpened ? children : null}
</template> </template>
{title} {title}
</Trigger> </Trigger>

View File

@ -2,19 +2,12 @@
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 commonPropsType from './commonPropsType'
import { noop } from './util'
export default { export default {
name: 'SubPopupMenu', name: 'SubPopupMenu',
props: { props: { ...commonPropsType,
// onSelect: PropTypes.func, clearSubMenuTimers: PropTypes.func.def(noop),
// onClick: PropTypes.func,
// onDeselect: PropTypes.func,
// onOpenChange: PropTypes.func,
// onDestroy: PropTypes.func,
openTransitionName: PropTypes.string,
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
openKeys: PropTypes.arrayOf(PropTypes.string),
visible: PropTypes.bool,
}, },
mixins: [MenuMixin, StateMixin], mixins: [MenuMixin, StateMixin],
@ -36,7 +29,7 @@ export default {
}, },
onDestroy (key) { onDestroy (key) {
this.$$emit('destroy', key) this.$emit('destroy', key)
}, },
getOpenTransitionName () { getOpenTransitionName () {
@ -58,36 +51,8 @@ export default {
}, },
}, },
render () { render () {
const props = { ...this.$props } const { prefixCls } = this.$props
return this.renderRoot({ ...this.$props, class: `${prefixCls}-sub` }, this.$slots.default)
const haveRendered = this.haveRendered
this.haveRendered = true
this.haveOpened = this.haveOpened || props.visible || props.forceSubMenuRender
if (!this.haveOpened) {
return null
}
const transitionAppear = !(!haveRendered && props.visible && props.mode === 'inline')
props.class = `${props.prefixCls}-sub`
const animProps = {}
if (props.openTransitionName) {
animProps.transitionName = props.openTransitionName
} else if (typeof props.openAnimation === 'object') {
animProps.animation = { ...props.openAnimation }
if (!transitionAppear) {
delete animProps.animation.appear
}
}
return (
<transition
appear
name={animProps.transitionName}
>
{this.renderRoot(props, this.$slots.default)}
</transition>
)
}, },
} }
</script> </script>

View File

@ -0,0 +1,18 @@
import PropTypes from '../../_util/vue-types'
export default {
prefixCls: PropTypes.string.def('rc-menu'),
inlineIndent: PropTypes.number.def(24),
focusable: PropTypes.bool.def(true),
multiple: PropTypes.bool,
defaultActiveFirst: PropTypes.bool,
visible: PropTypes.bool.def(true),
activeKey: PropTypes.string,
selectedKeys: PropTypes.arrayOf(PropTypes.string),
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
defaultOpenKeys: PropTypes.arrayOf(PropTypes.string),
openKeys: PropTypes.arrayOf(PropTypes.string),
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']).def('vertical'),
triggerSubMenuAction: PropTypes.string.def('click'),
openTransitionName: PropTypes.string,
}

View File

@ -29,16 +29,18 @@ export function loopMenuItemRecusively (children, keys, ret) {
if (ret.find) { if (ret.find) {
return return
} }
if (c) { if (c.data && c.data.slot && c.data.slot !== 'default') {
console.log(c) return
const construt = c.type }
if (!construt || !(construt.isSubMenu || construt.isMenuItem || construt.isMenuItemGroup)) { if (c && c.componentOptions) {
const options = c.componentOptions.Ctor.options
if (!options || !(options.isSubMenu || options.isMenuItem || options.isMenuItemGroup)) {
return return
} }
if (keys.indexOf(c.key) !== -1) { if (keys.indexOf(c.key) !== -1) {
ret.find = true ret.find = true
} else if (c.$slots.default) { } else if (c.componentOptions.children) {
loopMenuItemRecusively(c.$slots.default, keys, ret) loopMenuItemRecusively(c.componentOptions.children, keys, ret)
} }
} }
}) })

View File

@ -75,8 +75,8 @@ export default {
getTransitionName () { getTransitionName () {
const props = this.$props const props = this.$props
let transitionName = props.transitionName let transitionName = props.transitionName
if (!transitionName && props.animation) { if (!transitionName && typeof props.animation === 'string') {
transitionName = `${props.prefixCls}-${props.animation}` transitionName = `${props.animation}`
} }
return transitionName return transitionName
}, },
@ -100,7 +100,7 @@ export default {
}, },
getPopupElement () { getPopupElement () {
const { $props: props, onMouseEnter, onMouseLeave, $slots } = this const { $props: props, onMouseEnter, onMouseLeave, $slots } = this
const { align, visible, prefixCls } = props const { align, visible, prefixCls, animation } = props
const className = this.getClassName(this.currentAlignClassName || const className = this.getClassName(this.currentAlignClassName ||
props.getClassNameFromAlign(align)) props.getClassNameFromAlign(align))
// const hiddenClassName = `${prefixCls}-hidden` // const hiddenClassName = `${prefixCls}-hidden`
@ -121,9 +121,13 @@ export default {
ref: 'popupInstance', ref: 'popupInstance',
style: { ...this.getZIndexStyle() }, style: { ...this.getZIndexStyle() },
} }
const transitionProps = {
props: Object.assign({
appear: true,
}, typeof animation === 'object' ? animation : { name: this.getTransitionName() }),
}
return (<transition return (<transition
appear {...transitionProps}
name={this.getTransitionName()}
onBeforeEnter={this.beforeEnter} onBeforeEnter={this.beforeEnter}
onAfterLeave={this.afterLeave} onAfterLeave={this.afterLeave}
> >

View File

@ -265,7 +265,7 @@ export default {
// mouseLeaveDelay={0.1} // mouseLeaveDelay={0.1}
action={Object.keys(state.trigger)} action={Object.keys(state.trigger)}
builtinPlacements={builtinPlacements} builtinPlacements={builtinPlacements}
popupTransitionName={state.transitionName} popupAnimation={state.transitionName}
> >
<div slot='popup' style={{ border: '1px solid red', padding: '10px', background: 'white' }}> <div slot='popup' style={{ border: '1px solid red', padding: '10px', background: 'white' }}>
i am a popup i am a popup