feat: update vc-menu to 7.4.19

pull/309/head
tangjinzhou 2018-11-05 21:03:25 +08:00
parent 2bf65a4828
commit 2cce6a5f55
11 changed files with 438 additions and 139 deletions

View File

@ -88,19 +88,19 @@ const getOptionProps = (instance) => {
return filterProps($props, $options.propsData) return filterProps($props, $options.propsData)
} }
const getComponentFromProp = (instance, prop) => { const getComponentFromProp = (instance, prop, options) => {
if (instance.$createElement) { if (instance.$createElement) {
const h = instance.$createElement const h = instance.$createElement
const temp = instance[prop] const temp = instance[prop]
if (temp !== undefined) { if (temp !== undefined) {
return typeof temp === 'function' ? temp(h) : temp return typeof temp === 'function' ? temp(h, options) : temp
} }
return instance.$slots[prop] return instance.$slots[prop] || (instance.$scopedSlots[prop] && instance.$scopedSlots[prop](options)) || undefined
} else { } else {
const h = instance.context.$createElement const h = instance.context.$createElement
const temp = getPropsData(instance)[prop] const temp = getPropsData(instance)[prop]
if (temp !== undefined) { if (temp !== undefined) {
return typeof temp === 'function' ? temp(h) : temp return typeof temp === 'function' ? temp(h, options) : temp
} }
const slotsProp = [] const slotsProp = []
const componentOptions = instance.componentOptions || {}; const componentOptions = instance.componentOptions || {};

View File

@ -1,41 +1,304 @@
import PropTypes from '../_util/vue-types'
import ResizeObserver from 'resize-observer-polyfill'
import SubMenu from './SubMenu'
import BaseMixin from '../_util/BaseMixin'
import { getWidth, setStyle, menuAllProps } from './util'
import { cloneElement } from '../_util/vnode'
import { getClass, getPropsData, filterEmpty } from '../_util/props-util'
import omit from 'omit.js' const canUseDOM = !!(
export default { typeof window !== 'undefined' &&
window.document &&
window.document.createElement
)
const MENUITEM_OVERFLOWED_CLASSNAME = 'menuitem-overflowed'
// Fix ssr
if (canUseDOM) {
require('mutationobserver-shim')
}
const DOMWrap = {
name: 'DOMWrap', name: 'DOMWrap',
props: { mixins: [BaseMixin],
visible: { data () {
type: Boolean, this.resizeObserver = null
default: false, this.mutationObserver = null
},
tag: { // original scroll size of the list
type: String, this.originalTotalWidth = 0
default: 'div',
}, // copy of overflowed items
hiddenClassName: { this.overflowedItems = []
type: String,
default: '', // cache item of the original items (so we can track the size and order)
}, this.menuItemSizes = []
return {
lastVisibleIndex: undefined,
}
}, },
computed: {
class () { mounted () {
const { visible, hiddenClassName } = this.$props this.$nextTick(() => {
return { this.setChildrenWidthAndResize()
// [hiddenClassName]: !visible, if (this.level === 1 && this.mode === 'horizontal') {
const menuUl = this.$el
if (!menuUl) {
return
}
this.resizeObserver = new ResizeObserver(entries => {
entries.forEach(this.setChildrenWidthAndResize)
});
[].slice.call(menuUl.children).concat(menuUl).forEach(el => {
this.resizeObserver.observe(el)
})
if (typeof MutationObserver !== 'undefined') {
this.mutationObserver = new MutationObserver(() => {
this.resizeObserver.disconnect();
[].slice.call(menuUl.children).concat(menuUl).forEach(el => {
this.resizeObserver.observe(el)
})
this.setChildrenWidthAndResize()
})
this.mutationObserver.observe(
menuUl,
{ attributes: false, childList: true, subTree: false }
)
}
} }
})
},
beforeDestroy () {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
}
if (this.mutationObserver) {
this.resizeObserver.disconnect()
}
},
methods: {
// get all valid menuItem nodes
getMenuItemNodes () {
const { prefixCls } = this.$props
const ul = this.$el
if (!ul) {
return []
}
// filter out all overflowed indicator placeholder
return [].slice.call(ul.children)
.filter(node => {
return node.className.split(' ').indexOf(`${prefixCls}-overflowed-submenu`) < 0
})
},
getOverflowedSubMenuItem (keyPrefix, overflowedItems, renderPlaceholder) {
const { overflowedIndicator, level, mode, prefixCls, theme } = this.$props
if (level !== 1 || mode !== 'horizontal') {
return null
}
// put all the overflowed item inside a submenu
// with a title of overflow indicator ('...')
const copy = this.$slots.default[0]
const { title, eventKey, ...rest } = getPropsData(copy)
let style = {}
let key = `${keyPrefix}-overflowed-indicator`
if (overflowedItems.length === 0 && renderPlaceholder !== true) {
style = {
display: 'none',
}
} else if (renderPlaceholder) {
style = {
visibility: 'hidden',
// prevent from taking normal dom space
position: 'absolute',
}
key = `${key}-placeholder`
}
const popupClassName = theme ? `${prefixCls}-${theme}` : ''
const subMenuProps = {
props: {
title,
overflowedIndicator,
popupClassName,
eventKey: `${keyPrefix}-overflowed-indicator`,
disabled: false,
},
class: `${prefixCls}-overflowed-submenu`,
key,
style,
on: copy.$listeners,
}
menuAllProps.props.forEach(k => {
if (rest[k] !== undefined) {
subMenuProps.props[k] = rest[k]
}
})
return (
<SubMenu
{...subMenuProps}
>
{overflowedItems}
</SubMenu>
)
},
// memorize rendered menuSize
setChildrenWidthAndResize () {
if (this.mode !== 'horizontal') {
return
}
const ul = this.$el
if (!ul) {
return
}
const ulChildrenNodes = ul.children
if (!ulChildrenNodes || ulChildrenNodes.length === 0) {
return
}
const lastOverflowedIndicatorPlaceholder = ul.children[ulChildrenNodes.length - 1]
// need last overflowed indicator for calculating length;
setStyle(lastOverflowedIndicatorPlaceholder, 'display', 'inline-block')
const menuItemNodes = this.getMenuItemNodes()
// reset display attribute for all hidden elements caused by overflow to calculate updated width
// and then reset to original state after width calculation
const overflowedItems = menuItemNodes
.filter(c => c.className.split(' ').indexOf(MENUITEM_OVERFLOWED_CLASSNAME) >= 0)
overflowedItems.forEach(c => {
setStyle(c, 'display', 'inline-block')
})
this.menuItemSizes = menuItemNodes.map(c => getWidth(c))
overflowedItems.forEach(c => {
setStyle(c, 'display', 'none')
})
this.overflowedIndicatorWidth = getWidth(ul.children[ul.children.length - 1])
this.originalTotalWidth = this.menuItemSizes.reduce((acc, cur) => acc + cur, 0)
this.handleResize()
// prevent the overflowed indicator from taking space;
setStyle(lastOverflowedIndicatorPlaceholder, 'display', 'none')
},
handleResize () {
if (this.mode !== 'horizontal') {
return
}
const ul = this.$el
if (!ul) {
return
}
const width = getWidth(ul)
this.overflowedItems = []
let currentSumWidth = 0
// index for last visible child in horizontal mode
let lastVisibleIndex
if (this.originalTotalWidth > width) {
lastVisibleIndex = -1
this.menuItemSizes.forEach(liWidth => {
currentSumWidth += liWidth
if (currentSumWidth + this.overflowedIndicatorWidth <= width) {
lastVisibleIndex++
}
})
}
this.setState({ lastVisibleIndex })
},
renderChildren (children) {
// need to take care of overflowed items in horizontal mode
const { lastVisibleIndex } = this.$data
const className = getClass(this)
return (children || []).reduce((acc, childNode, index) => {
let item = childNode
const eventKey = getPropsData(childNode).eventKey
if (this.mode === 'horizontal') {
let overflowed = this.getOverflowedSubMenuItem(eventKey, [])
if (lastVisibleIndex !== undefined &&
className[`${this.prefixCls}-root`]
) {
if (index > lastVisibleIndex) {
item = cloneElement(
childNode,
// eventKey openkeys
{
style: { display: 'none' },
props: { eventKey: `${eventKey}-hidden` },
class: { ...getClass(childNode), [MENUITEM_OVERFLOWED_CLASSNAME]: true },
},
)
}
if (index === lastVisibleIndex + 1) {
this.overflowedItems = children.slice(lastVisibleIndex + 1).map(c => {
return cloneElement(
c,
// children[index].key will become '.$key' in clone by default,
// we have to overwrite with the correct key explicitly
{ key: getPropsData(c).eventKey, props: { mode: 'vertical-left' }},
)
})
overflowed = this.getOverflowedSubMenuItem(
eventKey,
this.overflowedItems,
)
}
}
const ret = [...acc, overflowed, item]
if (index === children.length - 1) {
// need a placeholder for calculating overflowed indicator width
ret.push(this.getOverflowedSubMenuItem(eventKey, [], true))
}
return ret
}
return [...acc, item]
}, [])
}, },
}, },
render () { render () {
const otherProps = omit(this.$props, [
'tag',
'hiddenClassName',
'visible',
])
const Tag = this.$props.tag const Tag = this.$props.tag
const tagProps = { const tagProps = {
attr: { ...otherProps, ...this.$attrs },
on: this.$listeners, on: this.$listeners,
} }
return <Tag {...tagProps} class={this.class}>{this.$slots.default}</Tag> return <Tag {...tagProps}>{this.renderChildren(filterEmpty(this.$slots.default))}</Tag>
}, },
} }
DOMWrap.props = {
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']),
prefixCls: PropTypes.string,
level: PropTypes.number,
theme: PropTypes.string,
overflowedIndicator: PropTypes.node,
visible: PropTypes.bool,
hiddenClassName: PropTypes.string,
tag: PropTypes.string.def('div'),
}
export default DOMWrap

View File

@ -2,7 +2,7 @@ import PropTypes from '../_util/vue-types'
import { Provider, create } from '../_util/store' import { Provider, create } from '../_util/store'
import { default as SubPopupMenu, getActiveKey } from './SubPopupMenu' import { default as SubPopupMenu, getActiveKey } from './SubPopupMenu'
import BaseMixin from '../_util/BaseMixin' import BaseMixin from '../_util/BaseMixin'
import hasProp, { getOptionProps } from '../_util/props-util' import hasProp, { getOptionProps, getComponentFromProp } from '../_util/props-util'
import commonPropsType from './commonPropsType' import commonPropsType from './commonPropsType'
const Menu = { const Menu = {
@ -33,47 +33,13 @@ const Menu = {
// this.isRootMenu = true // props // this.isRootMenu = true // props
return {} return {}
}, },
watch: { mounted () {
selectedKeys (val) { this.updateMiniStore()
this.store.setState({ },
selectedKeys: val || [], updated () {
}) this.updateMiniStore()
},
openKeys (val) {
this.store.setState({
openKeys: val || [],
})
},
// '$props': {
// handler: function (nextProps) {
// if (hasProp(this, 'selectedKeys')) {
// this.setState({
// sSelectedKeys: nextProps.selectedKeys || [],
// })
// }
// if (hasProp(this, 'openKeys')) {
// this.setState({
// sOpenKeys: nextProps.openKeys || [],
// })
// }
// },
// deep: true,
// },
}, },
methods: { methods: {
// onDestroy (key) {
// const state = this.$data
// const sSelectedKeys = state.sSelectedKeys
// const sOpenKeys = state.sOpenKeys
// let index = sSelectedKeys.indexOf(key)
// if (!hasProp(this, 'selectedKeys') && index !== -1) {
// sSelectedKeys.splice(index, 1)
// }
// index = sOpenKeys.indexOf(key)
// if (!hasProp(this, 'openKeys') && index !== -1) {
// sOpenKeys.splice(index, 1)
// }
// },
onSelect (selectInfo) { onSelect (selectInfo) {
const props = this.$props const props = this.$props
@ -170,35 +136,19 @@ const Menu = {
} }
return transitionName return transitionName
}, },
updateMiniStore () {
// isInlineMode () { const props = getOptionProps(this)
// return this.$props.mode === 'inline' if ('selectedKeys' in props) {
// }, this.store.setState({
selectedKeys: props.selectedKeys || [],
// lastOpenSubMenu () { })
// let lastOpen = [] }
// const { sOpenKeys } = this.$data if ('openKeys' in props) {
// if (sOpenKeys.length) { this.store.setState({
// lastOpen = this.getFlatInstanceArray().filter((c) => { openKeys: props.openKeys || [],
// return c && sOpenKeys.indexOf(c.eventKey) !== -1 })
// }) }
// } },
// return lastOpen[0]
// },
// renderMenuItem (c, i, subIndex) {
// if (!c) {
// return null
// }
// const state = this.$data
// const extraProps = {
// openKeys: state.sOpenKeys,
// selectedKeys: state.sSelectedKeys,
// triggerSubMenuAction: this.$props.triggerSubMenuAction,
// isRootMenu: this.isRootMenu,
// }
// return this.renderCommonMenuItem(c, i, subIndex, extraProps)
// },
}, },
render () { render () {
@ -206,6 +156,9 @@ const Menu = {
const subPopupMenuProps = { const subPopupMenuProps = {
props: { props: {
...props, ...props,
itemIcon: getComponentFromProp(this, 'itemIcon', props),
expandIcon: getComponentFromProp(this, 'expandIcon', props),
overflowedIndicator: getComponentFromProp(this, 'overflowedIndicator', props),
openTransitionName: this.getOpenTransitionName(), openTransitionName: this.getOpenTransitionName(),
parentMenu: this, parentMenu: this,
children: this.$slots.default || [], children: this.$slots.default || [],

View File

@ -5,6 +5,7 @@ import BaseMixin from '../_util/BaseMixin'
import scrollIntoView from 'dom-scroll-into-view' import scrollIntoView from 'dom-scroll-into-view'
import { connect } from '../_util/store' import { connect } from '../_util/store'
import { noop, menuAllProps } from './util' import { noop, menuAllProps } from './util'
import { getComponentFromProp } from '../_util/props-util'
const props = { const props = {
attribute: PropTypes.object, attribute: PropTypes.object,
@ -25,6 +26,7 @@ const props = {
manualRef: PropTypes.func.def(noop), manualRef: PropTypes.func.def(noop),
role: PropTypes.any, role: PropTypes.any,
subMenuKey: PropTypes.string, subMenuKey: PropTypes.string,
itemIcon: PropTypes.any,
// clearSubMenuTimers: PropTypes.func.def(noop), // clearSubMenuTimers: PropTypes.func.def(noop),
} }
const MenuItem = { const MenuItem = {
@ -138,7 +140,7 @@ const MenuItem = {
let attrs = { let attrs = {
...props.attribute, ...props.attribute,
title: props.title, title: props.title,
role: 'menuitem', role: props.role || 'menuitem',
'aria-disabled': props.disabled, 'aria-disabled': props.disabled,
} }
if (props.role === 'option') { if (props.role === 'option') {
@ -148,10 +150,13 @@ const MenuItem = {
role: 'option', role: 'option',
'aria-selected': props.isSelected, 'aria-selected': props.isSelected,
} }
} else if (props.role === null) { } else if (props.role === null || props.role === 'none') {
// sometimes we want to specify role inside <li/> element // sometimes we want to specify role inside <li/> element
// <li><a role='menuitem'>Link</a></li> would be a good example // <li><a role='menuitem'>Link</a></li> would be a good example
delete attrs.role // in this case the role on <li/> should be "none" to
// remove the implied listitem role.
// https://www.w3.org/TR/wai-aria-practices-1.1/examples/menubar/menubar-1/menubar-1.html
attrs.role = 'none'
} }
// In case that onClick/onMouseLeave/onMouseEnter is passed down from owner // In case that onClick/onMouseLeave/onMouseEnter is passed down from owner
const mouseEvent = { const mouseEvent = {
@ -184,6 +189,7 @@ const MenuItem = {
class={className} class={className}
> >
{this.$slots.default} {this.$slots.default}
{getComponentFromProp(this, 'itemIcon', props)}
</li> </li>
) )
}, },

View File

@ -66,6 +66,9 @@ const SubMenu = {
store: PropTypes.object, store: PropTypes.object,
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']).def('vertical'), mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']).def('vertical'),
manualRef: PropTypes.func.def(noop), manualRef: PropTypes.func.def(noop),
builtinPlacements: PropTypes.object.def({}),
itemIcon: PropTypes.any,
expandIcon: PropTypes.any,
}, },
mixins: [BaseMixin], mixins: [BaseMixin],
isSubMenu: true, isSubMenu: true,
@ -350,11 +353,14 @@ const SubMenu = {
subMenuCloseDelay: props.subMenuCloseDelay, subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender, forceSubMenuRender: props.forceSubMenuRender,
triggerSubMenuAction: props.triggerSubMenuAction, triggerSubMenuAction: props.triggerSubMenuAction,
builtinPlacements: props.builtinPlacements,
defaultActiveFirst: props.store.getState() defaultActiveFirst: props.store.getState()
.defaultActiveFirst[getMenuIdFromSubMenuEventKey(props.eventKey)], .defaultActiveFirst[getMenuIdFromSubMenuEventKey(props.eventKey)],
multiple: props.multiple, multiple: props.multiple,
prefixCls: props.rootPrefixCls, prefixCls: props.rootPrefixCls,
manualRef: this.saveMenuInstance, manualRef: this.saveMenuInstance,
itemIcon: getComponentFromProp(this, 'itemIcon'),
expandIcon: getComponentFromProp(this, 'expandIcon'),
children, children,
__propsSymbol__: Symbol(), __propsSymbol__: Symbol(),
}, },
@ -475,10 +481,15 @@ const SubMenu = {
class: `${prefixCls}-title`, class: `${prefixCls}-title`,
ref: 'subMenuTitle', ref: 'subMenuTitle',
} }
// expand custom icon should NOT be displayed in menu with horizontal mode.
let icon = null
if (props.mode !== 'horizontal') {
icon = getComponentFromProp(this, 'expandIcon', props)
}
const title = ( const title = (
<div {...titleProps}> <div {...titleProps}>
{getComponentFromProp(this, 'title')} {getComponentFromProp(this, 'title')}
<i class={`${prefixCls}-arrow`} /> {icon || <i class={`${prefixCls}-arrow`} />}
</div> </div>
) )
const children = this.renderChildren(this.$slots.default) const children = this.renderChildren(this.$slots.default)
@ -503,6 +514,7 @@ const SubMenu = {
popupClassName={`${prefixCls}-popup ${rootPrefixCls}-${parentMenu.theme} ${popupClassName || ''}`} popupClassName={`${prefixCls}-popup ${rootPrefixCls}-${parentMenu.theme} ${popupClassName || ''}`}
getPopupContainer={getPopupContainer} getPopupContainer={getPopupContainer}
builtinPlacements={placements} builtinPlacements={placements}
builtinPlacements={Object.assign({}, placements, props.builtinPlacements)}
popupPlacement={popupPlacement} popupPlacement={popupPlacement}
popupVisible={isOpen} popupVisible={isOpen}
popupAlign={popupAlign} popupAlign={popupAlign}

View File

@ -7,7 +7,7 @@ import classNames from 'classnames'
import { getKeyFromChildrenIndex, loopMenuItem, noop } from './util' import { getKeyFromChildrenIndex, loopMenuItem, noop } from './util'
import DOMWrap from './DOMWrap' import DOMWrap from './DOMWrap'
import { cloneElement } from '../_util/vnode' import { cloneElement } from '../_util/vnode'
import { initDefaultProps, getOptionProps, getEvents } from '../_util/props-util' import { initDefaultProps, getOptionProps, getPropsData, getEvents, getComponentFromProp } from '../_util/props-util'
function allDisabled (arr) { function allDisabled (arr) {
if (!arr.length) { if (!arr.length) {
@ -28,6 +28,11 @@ function updateActiveKey (store, menuId, activeKey) {
}) })
} }
function getEventKey (props) {
// when eventKey not available ,it's menu and return menu id '0-menu-'
return props.eventKey || '0-menu-'
}
export function saveRef (key, c) { export function saveRef (key, c) {
if (c) { if (c) {
const index = this.instanceArrayKeyIndexMap[key] const index = this.instanceArrayKeyIndexMap[key]
@ -100,6 +105,8 @@ const SubPopupMenu = {
triggerSubMenuAction: PropTypes.oneOf(['click', 'hover']), triggerSubMenuAction: PropTypes.oneOf(['click', 'hover']),
inlineIndent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), inlineIndent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
manualRef: PropTypes.func, manualRef: PropTypes.func,
itemIcon: PropTypes.any,
expandIcon: PropTypes.any,
children: PropTypes.any.def([]), children: PropTypes.any.def([]),
__propsSymbol__: PropTypes.any, // mock componentWillReceiveProps __propsSymbol__: PropTypes.any, // mock componentWillReceiveProps
}, { }, {
@ -129,18 +136,27 @@ const SubPopupMenu = {
this.manualRef(this) this.manualRef(this)
} }
}, },
watch: { updated () {
__propsSymbol__ () { const props = getOptionProps(this)
const props = getOptionProps(this) const originalActiveKey = 'activeKey' in props ? props.activeKey
const storeActiveKey = this.getStore().getState().activeKey[this.getEventKey()] : props.store.getState().activeKey[getEventKey(props)]
const originalActiveKey = 'activeKey' in props ? props.activeKey const activeKey = getActiveKey(props, originalActiveKey)
: storeActiveKey if (activeKey !== originalActiveKey) {
const activeKey = getActiveKey(props, originalActiveKey) updateActiveKey(props.store, getEventKey(props), activeKey)
if (activeKey !== originalActiveKey || storeActiveKey !== activeKey) { }
updateActiveKey(this.getStore(), this.getEventKey(), activeKey)
}
},
}, },
// watch: {
// __propsSymbol__ () {
// const props = getOptionProps(this)
// const storeActiveKey = this.getStore().getState().activeKey[this.getEventKey()]
// const originalActiveKey = 'activeKey' in props ? props.activeKey
// : storeActiveKey
// const activeKey = getActiveKey(props, originalActiveKey)
// if (activeKey !== originalActiveKey || storeActiveKey !== activeKey) {
// updateActiveKey(this.getStore(), this.getEventKey(), activeKey)
// }
// },
// },
methods: { methods: {
// all keyboard events callbacks run from here at first // all keyboard events callbacks run from here at first
onKeyDown (e, callback) { onKeyDown (e, callback) {
@ -160,7 +176,7 @@ const SubPopupMenu = {
} }
if (activeItem) { if (activeItem) {
e.preventDefault() e.preventDefault()
updateActiveKey(this.getStore(), this.getEventKey(), activeItem.eventKey) updateActiveKey(this.$props.store, getEventKey(this.$props), activeItem.eventKey)
if (typeof callback === 'function') { if (typeof callback === 'function') {
callback(activeItem) callback(activeItem)
@ -172,7 +188,7 @@ const SubPopupMenu = {
onItemHover (e) { onItemHover (e) {
const { key, hover } = e const { key, hover } = e
updateActiveKey(this.getStore(), this.getEventKey(), hover ? key : null) updateActiveKey(this.$props.store, getEventKey(this.$props), hover ? key : null)
}, },
onDeselect (selectInfo) { onDeselect (selectInfo) {
@ -199,22 +215,13 @@ const SubPopupMenu = {
return this.instanceArray return this.instanceArray
}, },
getStore () {
return this.store
},
getEventKey () {
// when eventKey not available ,it's menu and return menu id '0-menu-'
return this.eventKey !== undefined ? this.eventKey : '0-menu-'
},
getOpenTransitionName () { getOpenTransitionName () {
return this.$props.openTransitionName return this.$props.openTransitionName
}, },
step (direction) { step (direction) {
let children = this.getFlatInstanceArray() let children = this.getFlatInstanceArray()
const activeKey = this.getStore().getState().activeKey[this.getEventKey()] const activeKey = this.$props.store.getState().activeKey[this.getEventKey(this.$props)]
const len = children.length const len = children.length
if (!len) { if (!len) {
return null return null
@ -251,14 +258,39 @@ const SubPopupMenu = {
return null return null
}, },
getIcon (instance, name) {
if (instance.$createElement) {
const temp = instance[name]
if (temp !== undefined) {
return temp
}
return instance.$slots[name] || instance.$scopedSlots[name]
} else {
const temp = getPropsData(instance)[name]
if (temp !== undefined) {
return temp
}
const slotsProp = []
const componentOptions = instance.componentOptions || {};
(componentOptions.children || []).forEach((child) => {
if (child.data && child.data.slot === name) {
if (child.tag === 'template') {
slotsProp.push(child.children)
} else {
slotsProp.push(child)
}
}
})
return slotsProp.length ? slotsProp : undefined
}
},
renderCommonMenuItem (child, i, extraProps) { renderCommonMenuItem (child, i, extraProps) {
if (child.tag === undefined) { return child } if (child.tag === undefined) { return child }
const state = this.getStore().getState() const state = this.$props.store.getState()
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.componentOptions.propsData || {} const childProps = child.componentOptions.propsData || {}
const isActive = key === state.activeKey[this.getEventKey()] const isActive = key === state.activeKey
if (!childProps.disabled) { if (!childProps.disabled) {
// manualRef使keyrefthis.instanceArray // manualRef使keyrefthis.instanceArray
this.instanceArrayKeyIndexMap[key] = Object.keys(this.instanceArrayKeyIndexMap).length this.instanceArrayKeyIndexMap[key] = Object.keys(this.instanceArrayKeyIndexMap).length
@ -266,7 +298,7 @@ const SubPopupMenu = {
const childListeners = getEvents(child) const childListeners = getEvents(child)
const newChildProps = { const newChildProps = {
props: { props: {
mode: props.mode, mode: childProps.mode || props.mode,
level: props.level, level: props.level,
inlineIndent: props.inlineIndent, inlineIndent: props.inlineIndent,
renderMenuItem: this.renderMenuItem, renderMenuItem: this.renderMenuItem,
@ -283,6 +315,9 @@ const SubPopupMenu = {
subMenuOpenDelay: props.subMenuOpenDelay, subMenuOpenDelay: props.subMenuOpenDelay,
subMenuCloseDelay: props.subMenuCloseDelay, subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender, forceSubMenuRender: props.forceSubMenuRender,
builtinPlacements: props.builtinPlacements,
itemIcon: this.getIcon(child, 'itemIcon') || this.getIcon(this, 'itemIcon'),
expandIcon: this.getIcon(child, 'expandIcon') || this.getIcon(this, 'expandIcon'),
...extraProps, ...extraProps,
}, },
on: { on: {
@ -307,7 +342,7 @@ const SubPopupMenu = {
if (!c) { if (!c) {
return null return null
} }
const state = this.getStore().getState() const state = this.$props.store.getState()
const extraProps = { const extraProps = {
openKeys: state.openKeys, openKeys: state.openKeys,
selectedKeys: state.selectedKeys, selectedKeys: state.selectedKeys,
@ -320,7 +355,7 @@ const SubPopupMenu = {
}, },
render () { render () {
const { ...props } = this.$props const { ...props } = this.$props
const { eventKey, visible } = props const { eventKey, visible, level, mode, theme } = props
this.instanceArray = [] this.instanceArray = []
this.instanceArrayKeyIndexMap = {} this.instanceArrayKeyIndexMap = {}
const className = classNames( const className = classNames(
@ -332,6 +367,8 @@ const SubPopupMenu = {
tag: 'ul', tag: 'ul',
// hiddenClassName: `${prefixCls}-hidden`, // hiddenClassName: `${prefixCls}-hidden`,
visible, visible,
level, mode, theme,
overflowedIndicator: getComponentFromProp(this, 'overflowedIndicator'),
}, },
attrs: { attrs: {
role: props.role || 'menu', role: props.role || 'menu',

View File

@ -2,8 +2,8 @@
@font-face { @font-face {
font-family: 'FontAwesome'; 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://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/fonts/fontawesome-webfont.eot');
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'); src: url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/fonts/fontawesome-webfont.eot?#iefix') format('embedded-opentype'), url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/fonts/fontawesome-webfont.woff') format('woff'), url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/fonts/fontawesome-webfont.ttf') format('truetype'), url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/fonts/fontawesome-webfont.svg?#fontawesomeregular') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@ -92,6 +92,10 @@
&-submenu { &-submenu {
&-popup { &-popup {
position: absolute; position: absolute;
.submenu-title-wrapper {
padding-right: 20px;
}
} }
> .@{menuPrefixCls} { > .@{menuPrefixCls} {
background-color: #fff; background-color: #fff;
@ -110,17 +114,19 @@
&-horizontal { &-horizontal {
background-color: #F3F5F7; background-color: #F3F5F7;
border: none; border: none;
border-bottom: 1px solid transparent;
border-bottom: 1px solid #d9d9d9; border-bottom: 1px solid #d9d9d9;
box-shadow: none; box-shadow: none;
white-space: nowrap;
overflow: hidden;
& > .@{menuPrefixCls}-item, & > .@{menuPrefixCls}-submenu > .@{menuPrefixCls}-submenu-title { & > .@{menuPrefixCls}-item, & > .@{menuPrefixCls}-submenu > .@{menuPrefixCls}-submenu-title {
padding: 15px 20px; padding: 15px 20px;
} }
& > .@{menuPrefixCls}-submenu, & > .@{menuPrefixCls}-item { & > .@{menuPrefixCls}-submenu, & > .@{menuPrefixCls}-item {
float: left;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
display: inline-block;
vertical-align: bottom;
&-active { &-active {
border-bottom: 2px solid #2db7f5; border-bottom: 2px solid #2db7f5;

View File

@ -23,4 +23,8 @@ export default {
forceSubMenuRender: PropTypes.bool, forceSubMenuRender: PropTypes.bool,
selectable: PropTypes.bool, selectable: PropTypes.bool,
isRootMenu: PropTypes.bool.def(true), isRootMenu: PropTypes.bool.def(true),
builtinPlacements: PropTypes.object.def({}),
itemIcon: PropTypes.any,
expandIcon: PropTypes.any,
overflowedIndicator: PropTypes.any,
} }

View File

@ -1,4 +1,4 @@
// based on rc-menu 7.0.5 // based on rc-menu 7.4.19
import Menu from './Menu' import Menu from './Menu'
import SubMenu from './SubMenu' import SubMenu from './SubMenu'
import MenuItem, { menuItemProps } from './MenuItem' import MenuItem, { menuItemProps } from './MenuItem'

View File

@ -77,6 +77,7 @@ export const menuAllProps = {
'rootPrefixCls', 'rootPrefixCls',
'eventKey', 'eventKey',
'active', 'active',
'popupAlign',
'popupOffset', 'popupOffset',
'isOpen', 'isOpen',
'renderMenuItem', 'renderMenuItem',
@ -87,6 +88,8 @@ export const menuAllProps = {
'isSelected', 'isSelected',
'store', 'store',
'activeKey', 'activeKey',
'builtinPlacements',
'overflowedIndicator',
// the following keys found need to be removed from test regression // the following keys found need to be removed from test regression
'attribute', 'attribute',
@ -95,6 +98,8 @@ export const menuAllProps = {
'inlineCollapsed', 'inlineCollapsed',
'menu', 'menu',
'theme', 'theme',
'itemIcon',
'expandIcon',
], ],
on: [ on: [
'select', 'select',
@ -107,3 +112,15 @@ export const menuAllProps = {
'titleClick', 'titleClick',
], ],
} }
export const getWidth = (elem) => (
elem &&
typeof elem.getBoundingClientRect === 'function' &&
elem.getBoundingClientRect().width
) || 0
export const setStyle = (elem, styleProperty, value) => {
if (elem && typeof elem.style === 'object') {
elem.style[styleProperty] = value
}
}

View File

@ -172,6 +172,7 @@
"json2mq": "^0.2.0", "json2mq": "^0.2.0",
"lodash": "^4.17.5", "lodash": "^4.17.5",
"moment": "^2.21.0", "moment": "^2.21.0",
"mutationobserver-shim": "^0.3.2",
"omit.js": "^1.0.0", "omit.js": "^1.0.0",
"raf": "^3.4.0", "raf": "^3.4.0",
"resize-observer-polyfill": "^1.5.0", "resize-observer-polyfill": "^1.5.0",