Browse Source

fix

pull/9/head
tangjinzhou 7 years ago
parent
commit
8341fe44fc
  1. 19
      components/_util/Clone.vue
  2. 43
      components/_util/vnode.js
  3. 158
      components/menu/demo/antd.vue
  4. 16
      components/menu/src/Menu.vue
  5. 10
      components/menu/src/MenuItem.vue
  6. 5
      components/menu/src/MenuMixin.js
  7. 64
      components/menu/src/SubMenu.vue
  8. 11
      components/menu/src/commonPropsType.js
  9. 2
      components/menu/style/index.js
  10. 17
      components/menu/style/index.less
  11. 2
      components/style.js
  12. 27
      components/trigger/Popup.vue
  13. 4
      components/trigger/index.vue
  14. 2
      webpack.config.js

19
components/_util/Clone.vue

@ -0,0 +1,19 @@
<script>
import { cloneElement } from './vnode'
import PropTypes from './vue-types'
export default {
props: {
childProps: PropTypes.object.def({}),
},
render () {
const { $attrs, $listeners, childProps } = this
let children = this.$slots.default[0]
children = cloneElement(children, {
attr: $attrs,
on: $listeners,
props: childProps,
})
return children
},
}
</script>

43
components/_util/vnode.js

@ -1,5 +1,31 @@
import clonedeep from 'lodash.clonedeep'
// export function cloneVNode (vnode, deep) {
// const cloned = new vnode.constructor(
// vnode.tag,
// clonedeep(vnode.data),
// vnode.children,
// vnode.text,
// vnode.elm,
// vnode.context,
// clonedeep(vnode.componentOptions),
// vnode.asyncFactory
// )
// cloned.ns = vnode.ns
// cloned.isStatic = vnode.isStatic
// cloned.key = vnode.key
// cloned.isComment = vnode.isComment
// cloned.isCloned = true
// if (deep && vnode.children) {
// cloned.children = cloneVNodes(vnode.children, deep)
// }
// return cloned
// }
export function cloneVNode (vnode, deep) {
const componentOptions = vnode.componentOptions
// if (componentOptions) {
// componentOptions.propsData = componentOptions.propsData ? clonedeep(componentOptions.propsData) : componentOptions.propsData
// }
const cloned = new vnode.constructor(
vnode.tag,
clonedeep(vnode.data),
@ -7,16 +33,24 @@ export function cloneVNode (vnode, deep) {
vnode.text,
vnode.elm,
vnode.context,
clonedeep(vnode.componentOptions),
componentOptions,
vnode.asyncFactory
)
cloned.ns = vnode.ns
cloned.isStatic = vnode.isStatic
cloned.key = vnode.key
cloned.isComment = vnode.isComment
cloned.fnContext = vnode.fnContext
cloned.fnOptions = vnode.fnOptions
cloned.fnScopeId = vnode.fnScopeId
cloned.isCloned = true
if (deep && vnode.children) {
cloned.children = cloneVNodes(vnode.children)
if (deep) {
if (vnode.children) {
cloned.children = cloneVNodes(vnode.children, true)
}
if (componentOptions && componentOptions.children) {
componentOptions.children = cloneVNodes(componentOptions.children, true)
}
}
return cloned
}
@ -30,7 +64,8 @@ export function cloneVNodes (vnodes, deep) {
return res
}
export function cloneElement (node, nodeProps) {
export function cloneElement (n, nodeProps, clone) {
const node = clone ? cloneVNode(n, true) : n
const { props = {}, key, on = {}} = nodeProps
if (node.componentOptions) {
node.componentOptions.propsData = node.componentOptions.propsData || {}

158
components/menu/demo/antd.vue

@ -1,8 +1,8 @@
<script>
import { cloneElement } from '../../_util/vnode'
import Clone from '../../_util/Clone'
import Menu, { SubMenu, Item as MenuItem, Divider } from '../src/index'
import { Icon } from 'antd'
import '../assets/index.less'
import animate from 'css-animation'
function handleSelect (info) {
@ -11,40 +11,40 @@ function handleSelect (info) {
}
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()
},
})
on: {
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()
},
})
},
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()
},
})
},
},
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()
},
})
props: {
appear: false,
},
}
export default {
@ -52,7 +52,7 @@ export default {
},
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>
<Divider/>
<SubMenu
@ -78,58 +78,62 @@ export default {
function onOpenChange (value) {
console.log('onOpenChange', value)
}
const commonMenu = (<Menu class='test' onSelect={handleSelect} onOpenChange={onOpenChange}>
<SubMenu key='1' title={<span>sub menu</span>}>
<MenuItem key='1-1'>
const commonMenu = () => (
<Menu onSelect={handleSelect} onOpenChange={onOpenChange}>
<SubMenu key='1' title={<span>sub menu</span>}>
<MenuItem key='1-1'>
0-1
</MenuItem>
<MenuItem key='1-2'>0-2</MenuItem>
</SubMenu>
{nestSubMenu}
<MenuItem key='2'>1</MenuItem>
<MenuItem key='3'>outer</MenuItem>
<MenuItem disabled>disabled</MenuItem>
<MenuItem key='5'>outer3</MenuItem>
</Menu>)
const horizontalMenu = cloneElement(commonMenu, { props: {
mode: 'horizontal',
// use openTransition for antd
openAnimation: 'rc-menu-open-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,
// }})
</MenuItem>
<MenuItem key='1-2'>0-2</MenuItem>
</SubMenu>
{nestSubMenu()}
<MenuItem key='2'>1</MenuItem>
<MenuItem key='3'>outer</MenuItem>
<MenuItem disabled>disabled</MenuItem>
<MenuItem key='5'>outer3</MenuItem>
</Menu>
)
return (
<div style={{ margin: '20px' }}>
<h2>antd menu</h2>
<div>
<h3>horizontal</h3>
<div style={{ margin: '20px', width: '800px' }}>{horizontalMenu}</div>
<div style={{ margin: '20px', width: '800px' }}>
<Clone childProps={{
mode: 'horizontal',
openAnimation: 'rc-menu-open-slide-up',
}} >
{commonMenu()}
</Clone>
</div>
<h3>horizontal and click</h3>
{/*
<div style={{ margin: '20px', width: '800px' }}>{horizontalMenu2}</div>
<div style={{ margin: '20px', width: '800px' }}>
<Clone childProps={{
mode: 'horizontal',
openAnimation: 'rc-menu-open-slide-up',
triggerSubMenuAction: 'click',
defaultOpenKeys: ['1'],
}} >
{commonMenu()}
</Clone>
</div>
<h3>vertical</h3>
<div style={{ margin: '20px', width: '200px' }}>
<Clone childProps={{
mode: 'vertical',
openAnimation: 'rc-menu-open-zoom',
}} >
{commonMenu()}
</Clone></div>
<div style={{ margin: '20px', width: '200px' }}>{verticalMenu}</div>
<h3>inline</h3>
<div style={{ margin: '20px', width: '400px' }}>{inlineMenu}</div>
*/}
<div style={{ margin: '20px', width: '400px' }}><Clone childProps={{
mode: 'inline',
defaultOpenKeys: ['1'],
openAnimation: animation,
}} >
{commonMenu()}
</Clone></div>
</div>
</div>
)

16
components/menu/src/Menu.vue

@ -10,10 +10,7 @@ const Menu = {
props: {
getPopupContainer: PropTypes.func,
openTransitionName: PropTypes.string,
subMenuOpenDelay: PropTypes.number.def(0),
subMenuCloseDelay: PropTypes.number.def(0.1),
forceSubMenuRender: PropTypes.bool,
level: PropTypes.number.def(1),
selectable: PropTypes.bool.def(true),
...commonPropsType,
},
@ -23,8 +20,13 @@ const Menu = {
const props = this.$props
let sSelectedKeys = props.defaultSelectedKeys
let sOpenKeys = props.defaultOpenKeys
sSelectedKeys = props.selectedKeys || []
sOpenKeys = props.openKeys || []
if (hasProp(this, 'selectedKeys')) {
sSelectedKeys = props.selectedKeys || []
}
if (hasProp(this, 'openKeys')) {
sOpenKeys = props.openKeys || []
}
this.isRootMenu = true
return {
sSelectedKeys,
@ -35,10 +37,10 @@ const Menu = {
'$props': {
handler: function (nextProps) {
const props = {}
if (nextProps.selectedKeys === undefined) {
if (hasProp(this, 'selectedKeys')) {
props.sSelectedKeys = nextProps.selectedKeys || []
}
if (nextProps.openKeys === undefined) {
if (hasProp(this, 'selectedKeys')) {
props.sOpenKeys = nextProps.openKeys || []
}
this.setState(props)

10
components/menu/src/MenuItem.vue

@ -27,6 +27,9 @@ const MenuItem = {
// onMouseLeave: PropTypes.func,
clearSubMenuTimers: PropTypes.func.def(noop),
},
inject: {
parentMenuContext: { default: undefined },
},
mixins: [StateMixin],
isMenuItem: true,
beforeDestroy () {
@ -55,8 +58,10 @@ const MenuItem = {
},
onMouseEnter (e) {
const { eventKey } = this.$props
this.clearSubMenuTimers()
const { eventKey, parentMenuContext } = this
if (parentMenuContext && parentMenuContext.subMenuInstance) {
parentMenuContext.subMenuInstance.clearSubMenuTimers()
}
this.$emit('itemHover', {
key: eventKey,
hover: true,
@ -133,6 +138,7 @@ const MenuItem = {
mouseenter: this.onMouseEnter,
}
}
const style = {}
if (props.mode === 'inline') {
style.paddingLeft = `${props.inlineIndent * props.level}px`

5
components/menu/src/MenuMixin.js

@ -23,6 +23,11 @@ export default {
sActiveKey: this.getActiveKey(props.activeKey),
}
},
provide () {
return {
parentMenuContext: this,
}
},
watch: {
'$props': {
handler: function (nextProps) {

64
components/menu/src/SubMenu.vue

@ -40,7 +40,10 @@ export default {
forceSubMenuRender: PropTypes.bool,
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
disabled: PropTypes.bool,
subMenuCloseDelay: PropTypes.number,
subMenuOpenDelay: PropTypes.number.def(0),
subMenuCloseDelay: PropTypes.number.def(0.1),
level: PropTypes.number.def(1),
inlineIndent: PropTypes.number.def(24),
// onDeselect: PropTypes.func,
// onDestroy: PropTypes.func,
// onMouseEnter: PropTypes.func,
@ -49,6 +52,9 @@ export default {
// onTitleMouseLeave: PropTypes.func,
// onTitleClick: PropTypes.func,
},
inject: {
parentMenuContext: { default: undefined },
},
mixins: [StateMixin],
isSubMenu: true,
data () {
@ -65,9 +71,11 @@ export default {
},
beforeDestroy () {
const { eventKey } = this.$props
const { eventKey, parentMenuContext } = this
this.$emit('destroy', eventKey)
this.clearSubMenuTimers()
if (parentMenuContext.subMenuInstance === this) {
this.clearSubMenuTimers()
}
},
methods: {
handleUpdated () {
@ -156,8 +164,10 @@ export default {
onMouseLeave (e) {
const {
eventKey,
} = this.$props
this.subMenuLeaveFn = () => {
parentMenuContext,
} = this
parentMenuContext.subMenuInstance = this
parentMenuContext.subMenuLeaveFn = () => {
// trigger mouseleave
this.$emit('mouseleave', {
key: eventKey,
@ -165,7 +175,7 @@ export default {
})
}
// prevent popup menu and submenu gap
this.subMenuLeaveTimer = setTimeout(this.subMenuLeaveFn, 100)
parentMenuContext.subMenuLeaveTimer = setTimeout(parentMenuContext.subMenuLeaveFn, 100)
},
onTitleMouseEnter (domEvent) {
@ -182,8 +192,9 @@ export default {
},
onTitleMouseLeave (e) {
const { eventKey } = this.$props
this.subMenuTitleLeaveFn = () => {
const { eventKey, parentMenuContext } = this
parentMenuContext.subMenuInstance = this
parentMenuContext.subMenuTitleLeaveFn = () => {
this.$emit('itemHover', {
key: eventKey,
hover: false,
@ -193,7 +204,7 @@ export default {
domEvent: e,
})
}
this.subMenuTitleLeaveTimer = setTimeout(this.subMenuTitleLeaveFn, 100)
parentMenuContext.subMenuTitleLeaveTimer = setTimeout(parentMenuContext.subMenuTitleLeaveFn, 100)
},
onTitleClick (e) {
@ -268,18 +279,20 @@ export default {
},
clearSubMenuTitleLeaveTimer () {
if (this.subMenuTitleLeaveTimer) {
clearTimeout(this.subMenuTitleLeaveTimer)
this.subMenuTitleLeaveTimer = null
this.subMenuTitleLeaveFn = null
const parentMenuContext = this.parentMenuContext
if (parentMenuContext.subMenuTitleLeaveTimer) {
clearTimeout(parentMenuContext.subMenuTitleLeaveTimer)
parentMenuContext.subMenuTitleLeaveTimer = null
parentMenuContext.subMenuTitleLeaveFn = null
}
},
clearSubMenuLeaveTimer () {
if (this.subMenuLeaveTimer) {
clearTimeout(this.subMenuLeaveTimer)
this.subMenuLeaveTimer = null
this.subMenuLeaveFn = null
const parentMenuContext = this.parentMenuContext
if (parentMenuContext.subMenuLeaveTimer) {
clearTimeout(parentMenuContext.subMenuLeaveTimer)
parentMenuContext.subMenuLeaveTimer = null
parentMenuContext.subMenuLeaveFn = null
}
},
@ -292,12 +305,13 @@ export default {
return this.$props.openKeys.indexOf(this.$props.eventKey) !== -1
},
renderChildren (children) {
renderChildren (children, vShow) {
const props = this.$props
const isOpen = this.isOpen()
const subPopupMenuProps = {
props: {
mode: props.mode === 'horizontal' ? 'vertical' : props.mode,
visible: this.isOpen(),
visible: isOpen,
level: props.level + 1,
inlineIndent: props.inlineIndent,
focusable: false,
@ -325,7 +339,9 @@ export default {
id: this._menuId,
ref: 'menuInstance',
}
return <SubPopupMenu {...subPopupMenuProps}>{children}</SubPopupMenu>
return vShow
? <SubPopupMenu v-show={isOpen} {...subPopupMenuProps}>{children}</SubPopupMenu>
: <SubPopupMenu {...subPopupMenuProps}>{children}</SubPopupMenu>
},
},
@ -398,7 +414,7 @@ export default {
<i class={`${prefixCls}-arrow`} />
</div>
)
const children = this.renderChildren(this.$slots.default)
// const children = this.renderChildren(this.$slots.default)
const getPopupContainer = this.isRootMenu
? this.getPopupContainer : triggerNode => triggerNode.parentNode
@ -421,7 +437,7 @@ export default {
if (openTransitionName) {
animProps.name = openTransitionName
} else if (typeof openAnimation === 'object') {
animProps = { ...animProps, ...openAnimation }
animProps = { ...animProps, ...openAnimation.props || {}}
if (!transitionAppear) {
animProps.appear = false
}
@ -431,6 +447,10 @@ export default {
const transitionProps = {
props: animProps,
}
if (typeof openAnimation === 'object' && openAnimation.on) {
transitionProps.on = { ...openAnimation.on }
}
const children = isInlineMode ? this.renderChildren(this.$slots.default, true) : this.renderChildren(this.$slots.default)
return (
<li {...liProps}>
{isInlineMode && title}

11
components/menu/src/commonPropsType.js

@ -1,18 +1,21 @@
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),
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string).def([]),
defaultOpenKeys: PropTypes.arrayOf(PropTypes.string).def([]),
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'),
triggerSubMenuAction: PropTypes.string.def('hover'),
openTransitionName: PropTypes.string,
subMenuOpenDelay: PropTypes.number.def(0),
subMenuCloseDelay: PropTypes.number.def(0.1),
level: PropTypes.number.def(1),
inlineIndent: PropTypes.number.def(24),
}

2
components/menu/style/index.js

@ -0,0 +1,2 @@
import '../../style/index.less'
import './index.less'

17
components/menu/assets/index.less → components/menu/style/index.less

@ -25,6 +25,7 @@
&-collapse {
overflow: hidden;
&-active {
overflow: hidden;
transition: height .3s ease-out;
}
}
@ -136,7 +137,9 @@
clear: both;
}
}
&-inline {
overflow: hidden;
}
&-vertical,
&-vertical-left,
&-vertical-right,
@ -166,7 +169,7 @@
transform: rotate(90deg);
transition: transform .3s;
}
& .@{menuPrefixCls}-submenu-open .@{menuPrefixCls}-submenu-title {
& .@{menuPrefixCls}-submenu-open > .@{menuPrefixCls}-submenu-title {
.@{menuPrefixCls}-submenu-arrow {
transform: rotate(-90deg);
}
@ -193,7 +196,7 @@
}
.effect() {
animation-duration: .5s;
animation-duration: 3s;
animation-fill-mode: both;
transform-origin: 0 0;
}
@ -265,12 +268,14 @@
animation-play-state: paused;
}
&-zoom-enter&-zoom-enter-active, &-zoom-appear&-zoom-appear-active {
&-zoom-enter-active, &-zoom-appear-active {
.effect();
animation-name: rcMenuOpenZoomIn;
animation-play-state: running;
}
&-zoom-leave&-zoom-leave-active {
&-zoom-leave-active {
.effect();
animation-name: rcMenuOpenZoomOut;
animation-play-state: running;
}
@ -285,6 +290,7 @@
transform: scale(1, 1);
}
}
@keyframes rcMenuOpenZoomOut {
0% {
transform: scale(1, 1);
@ -295,6 +301,5 @@
}
}
}
}

2
components/style.js

@ -10,3 +10,5 @@ import './avatar/style'
import './badge/style'
import './tabs/style'
import './input/style'
import './menu/style'

27
components/trigger/Popup.vue

@ -24,6 +24,7 @@ export default {
data () {
return {
destroyPopup: false,
initAlign: false, // mountedalign,this.$el
}
},
mounted () {
@ -31,15 +32,25 @@ export default {
this._container = this.getContainer()
this._container.appendChild(this.$el)
this.$nextTick(() => {
this.$refs.alignInstance.forceAlign()
this.initAlign = true
})
},
beforeDestroy () {
this.$el.remove()
},
watch: {
visible (val) {
if (val) {
this.destroyPopup = false
}
},
initAlign (val) {
if (val) {
this.$nextTick(() => {
this.$refs.alignInstance.forceAlign()
})
}
},
},
methods: {
onAlign (popupDomNode, align) {
@ -92,6 +103,9 @@ export default {
},
beforeEnter (el) {
this.$refs.alignInstance && this.$refs.alignInstance.forceAlign()
// this.$nextTick(() => {
// this.$refs.alignInstance && this.$refs.alignInstance.forceAlign()
// })
},
afterLeave (el) {
if (this.destroyPopupOnHide) {
@ -189,12 +203,15 @@ export default {
},
render () {
const { destroyPopup, getMaskElement, getPopupElement } = this
const { destroyPopup, getMaskElement, getPopupElement, initAlign } = this
return (
<div style='position: absolute; top: 0px; left: 0px; width: 100%;'>
{getMaskElement()}
{ destroyPopup
? null : getPopupElement()}
{initAlign ? (
getMaskElement(),
destroyPopup
? null : getPopupElement()
) : null }
</div>
)
},

4
components/trigger/index.vue

@ -258,7 +258,8 @@ export default {
},
getRootDomNode () {
return this.$el.children[0] || this.$el
console.log('this.$el.children', this.$el.children)
return this.$el.children ? this.$el.children[0] : this.$el
},
getPopupClassFromAlign (align) {
@ -509,6 +510,7 @@ export default {
} else {
newChildProps.on.mouseleave = this.createTwoChains('mouseleave')
}
if (this.isFocusToShow() || this.isBlurToHide()) {
newChildProps.on.focus = this.onFocus
newChildProps.on.blur = this.onBlur

2
webpack.config.js

@ -63,7 +63,7 @@ module.exports = {
performance: {
hints: false,
},
devtool: '#eval-source-map',
devtool: '#source-map',
}
if (process.env.NODE_ENV === 'production') {

Loading…
Cancel
Save