diff --git a/components/_util/props-util.js b/components/_util/props-util.js
index 9fddb4427..ca17fae78 100644
--- a/components/_util/props-util.js
+++ b/components/_util/props-util.js
@@ -88,19 +88,19 @@ const getOptionProps = (instance) => {
return filterProps($props, $options.propsData)
}
-const getComponentFromProp = (instance, prop) => {
+const getComponentFromProp = (instance, prop, options) => {
if (instance.$createElement) {
const h = instance.$createElement
const temp = instance[prop]
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 {
const h = instance.context.$createElement
const temp = getPropsData(instance)[prop]
if (temp !== undefined) {
- return typeof temp === 'function' ? temp(h) : temp
+ return typeof temp === 'function' ? temp(h, options) : temp
}
const slotsProp = []
const componentOptions = instance.componentOptions || {};
diff --git a/components/vc-menu/DOMWrap.jsx b/components/vc-menu/DOMWrap.jsx
index d51569d11..647f4fb1e 100644
--- a/components/vc-menu/DOMWrap.jsx
+++ b/components/vc-menu/DOMWrap.jsx
@@ -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'
-export default {
+const canUseDOM = !!(
+ 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',
- props: {
- visible: {
- type: Boolean,
- default: false,
- },
- tag: {
- type: String,
- default: 'div',
- },
- hiddenClassName: {
- type: String,
- default: '',
- },
+ mixins: [BaseMixin],
+ data () {
+ this.resizeObserver = null
+ this.mutationObserver = null
+
+ // original scroll size of the list
+ this.originalTotalWidth = 0
+
+ // copy of overflowed items
+ this.overflowedItems = []
+
+ // cache item of the original items (so we can track the size and order)
+ this.menuItemSizes = []
+ return {
+ lastVisibleIndex: undefined,
+ }
},
- computed: {
- class () {
- const { visible, hiddenClassName } = this.$props
- return {
- // [hiddenClassName]: !visible,
+
+ mounted () {
+ this.$nextTick(() => {
+ this.setChildrenWidthAndResize()
+ 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 (
+
+ {overflowedItems}
+
+ )
+ },
+
+ // 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 () {
- const otherProps = omit(this.$props, [
- 'tag',
- 'hiddenClassName',
- 'visible',
- ])
const Tag = this.$props.tag
const tagProps = {
- attr: { ...otherProps, ...this.$attrs },
on: this.$listeners,
}
- return {this.$slots.default}
+ return {this.renderChildren(filterEmpty(this.$slots.default))}
},
}
+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
diff --git a/components/vc-menu/Menu.jsx b/components/vc-menu/Menu.jsx
index ab92158b1..2a1920fe3 100644
--- a/components/vc-menu/Menu.jsx
+++ b/components/vc-menu/Menu.jsx
@@ -2,7 +2,7 @@ import PropTypes from '../_util/vue-types'
import { Provider, create } from '../_util/store'
import { default as SubPopupMenu, getActiveKey } from './SubPopupMenu'
import BaseMixin from '../_util/BaseMixin'
-import hasProp, { getOptionProps } from '../_util/props-util'
+import hasProp, { getOptionProps, getComponentFromProp } from '../_util/props-util'
import commonPropsType from './commonPropsType'
const Menu = {
@@ -33,47 +33,13 @@ const Menu = {
// this.isRootMenu = true // 声明在props上
return {}
},
- watch: {
- selectedKeys (val) {
- this.store.setState({
- selectedKeys: val || [],
- })
- },
- 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,
- // },
+ mounted () {
+ this.updateMiniStore()
+ },
+ updated () {
+ this.updateMiniStore()
},
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) {
const props = this.$props
@@ -170,35 +136,19 @@ const Menu = {
}
return transitionName
},
-
- // isInlineMode () {
- // return this.$props.mode === 'inline'
- // },
-
- // lastOpenSubMenu () {
- // let lastOpen = []
- // const { sOpenKeys } = this.$data
- // if (sOpenKeys.length) {
- // lastOpen = this.getFlatInstanceArray().filter((c) => {
- // 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)
- // },
+ updateMiniStore () {
+ const props = getOptionProps(this)
+ if ('selectedKeys' in props) {
+ this.store.setState({
+ selectedKeys: props.selectedKeys || [],
+ })
+ }
+ if ('openKeys' in props) {
+ this.store.setState({
+ openKeys: props.openKeys || [],
+ })
+ }
+ },
},
render () {
@@ -206,6 +156,9 @@ const Menu = {
const subPopupMenuProps = {
props: {
...props,
+ itemIcon: getComponentFromProp(this, 'itemIcon', props),
+ expandIcon: getComponentFromProp(this, 'expandIcon', props),
+ overflowedIndicator: getComponentFromProp(this, 'overflowedIndicator', props),
openTransitionName: this.getOpenTransitionName(),
parentMenu: this,
children: this.$slots.default || [],
diff --git a/components/vc-menu/MenuItem.jsx b/components/vc-menu/MenuItem.jsx
index d197c2fac..2e12d011c 100644
--- a/components/vc-menu/MenuItem.jsx
+++ b/components/vc-menu/MenuItem.jsx
@@ -5,6 +5,7 @@ import BaseMixin from '../_util/BaseMixin'
import scrollIntoView from 'dom-scroll-into-view'
import { connect } from '../_util/store'
import { noop, menuAllProps } from './util'
+import { getComponentFromProp } from '../_util/props-util'
const props = {
attribute: PropTypes.object,
@@ -25,6 +26,7 @@ const props = {
manualRef: PropTypes.func.def(noop),
role: PropTypes.any,
subMenuKey: PropTypes.string,
+ itemIcon: PropTypes.any,
// clearSubMenuTimers: PropTypes.func.def(noop),
}
const MenuItem = {
@@ -138,7 +140,7 @@ const MenuItem = {
let attrs = {
...props.attribute,
title: props.title,
- role: 'menuitem',
+ role: props.role || 'menuitem',
'aria-disabled': props.disabled,
}
if (props.role === 'option') {
@@ -148,10 +150,13 @@ const MenuItem = {
role: 'option',
'aria-selected': props.isSelected,
}
- } else if (props.role === null) {
+ } else if (props.role === null || props.role === 'none') {
// sometimes we want to specify role inside
element
// Link would be a good example
- delete attrs.role
+ // in this case the role on 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
const mouseEvent = {
@@ -184,6 +189,7 @@ const MenuItem = {
class={className}
>
{this.$slots.default}
+ {getComponentFromProp(this, 'itemIcon', props)}
)
},
diff --git a/components/vc-menu/SubMenu.jsx b/components/vc-menu/SubMenu.jsx
index 275a89d02..5a6f670d2 100644
--- a/components/vc-menu/SubMenu.jsx
+++ b/components/vc-menu/SubMenu.jsx
@@ -66,6 +66,9 @@ const SubMenu = {
store: PropTypes.object,
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']).def('vertical'),
manualRef: PropTypes.func.def(noop),
+ builtinPlacements: PropTypes.object.def({}),
+ itemIcon: PropTypes.any,
+ expandIcon: PropTypes.any,
},
mixins: [BaseMixin],
isSubMenu: true,
@@ -350,11 +353,14 @@ const SubMenu = {
subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender,
triggerSubMenuAction: props.triggerSubMenuAction,
+ builtinPlacements: props.builtinPlacements,
defaultActiveFirst: props.store.getState()
.defaultActiveFirst[getMenuIdFromSubMenuEventKey(props.eventKey)],
multiple: props.multiple,
prefixCls: props.rootPrefixCls,
manualRef: this.saveMenuInstance,
+ itemIcon: getComponentFromProp(this, 'itemIcon'),
+ expandIcon: getComponentFromProp(this, 'expandIcon'),
children,
__propsSymbol__: Symbol(),
},
@@ -475,10 +481,15 @@ const SubMenu = {
class: `${prefixCls}-title`,
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 = (
{getComponentFromProp(this, 'title')}
-
+ {icon || }
)
const children = this.renderChildren(this.$slots.default)
@@ -503,6 +514,7 @@ const SubMenu = {
popupClassName={`${prefixCls}-popup ${rootPrefixCls}-${parentMenu.theme} ${popupClassName || ''}`}
getPopupContainer={getPopupContainer}
builtinPlacements={placements}
+ builtinPlacements={Object.assign({}, placements, props.builtinPlacements)}
popupPlacement={popupPlacement}
popupVisible={isOpen}
popupAlign={popupAlign}
diff --git a/components/vc-menu/SubPopupMenu.jsx b/components/vc-menu/SubPopupMenu.jsx
index 0109cf808..e0c54ffeb 100644
--- a/components/vc-menu/SubPopupMenu.jsx
+++ b/components/vc-menu/SubPopupMenu.jsx
@@ -7,7 +7,7 @@ import classNames from 'classnames'
import { getKeyFromChildrenIndex, loopMenuItem, noop } from './util'
import DOMWrap from './DOMWrap'
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) {
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) {
if (c) {
const index = this.instanceArrayKeyIndexMap[key]
@@ -100,6 +105,8 @@ const SubPopupMenu = {
triggerSubMenuAction: PropTypes.oneOf(['click', 'hover']),
inlineIndent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
manualRef: PropTypes.func,
+ itemIcon: PropTypes.any,
+ expandIcon: PropTypes.any,
children: PropTypes.any.def([]),
__propsSymbol__: PropTypes.any, // mock componentWillReceiveProps
}, {
@@ -129,18 +136,27 @@ const SubPopupMenu = {
this.manualRef(this)
}
},
- 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)
- }
- },
+ updated () {
+ const props = getOptionProps(this)
+ const originalActiveKey = 'activeKey' in props ? props.activeKey
+ : props.store.getState().activeKey[getEventKey(props)]
+ const activeKey = getActiveKey(props, originalActiveKey)
+ if (activeKey !== originalActiveKey) {
+ updateActiveKey(props.store, getEventKey(props), 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: {
// all keyboard events callbacks run from here at first
onKeyDown (e, callback) {
@@ -160,7 +176,7 @@ const SubPopupMenu = {
}
if (activeItem) {
e.preventDefault()
- updateActiveKey(this.getStore(), this.getEventKey(), activeItem.eventKey)
+ updateActiveKey(this.$props.store, getEventKey(this.$props), activeItem.eventKey)
if (typeof callback === 'function') {
callback(activeItem)
@@ -172,7 +188,7 @@ const SubPopupMenu = {
onItemHover (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) {
@@ -199,22 +215,13 @@ const SubPopupMenu = {
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 () {
return this.$props.openTransitionName
},
step (direction) {
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
if (!len) {
return null
@@ -251,14 +258,39 @@ const SubPopupMenu = {
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) {
if (child.tag === undefined) { return child }
- const state = this.getStore().getState()
+ const state = this.$props.store.getState()
const props = this.$props
const key = getKeyFromChildrenIndex(child, props.eventKey, i)
const childProps = child.componentOptions.propsData || {}
- const isActive = key === state.activeKey[this.getEventKey()]
+ const isActive = key === state.activeKey
if (!childProps.disabled) {
// manualRef的执行顺序不能保证,使用key映射ref在this.instanceArray中的位置
this.instanceArrayKeyIndexMap[key] = Object.keys(this.instanceArrayKeyIndexMap).length
@@ -266,7 +298,7 @@ const SubPopupMenu = {
const childListeners = getEvents(child)
const newChildProps = {
props: {
- mode: props.mode,
+ mode: childProps.mode || props.mode,
level: props.level,
inlineIndent: props.inlineIndent,
renderMenuItem: this.renderMenuItem,
@@ -283,6 +315,9 @@ const SubPopupMenu = {
subMenuOpenDelay: props.subMenuOpenDelay,
subMenuCloseDelay: props.subMenuCloseDelay,
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,
},
on: {
@@ -307,7 +342,7 @@ const SubPopupMenu = {
if (!c) {
return null
}
- const state = this.getStore().getState()
+ const state = this.$props.store.getState()
const extraProps = {
openKeys: state.openKeys,
selectedKeys: state.selectedKeys,
@@ -320,7 +355,7 @@ const SubPopupMenu = {
},
render () {
const { ...props } = this.$props
- const { eventKey, visible } = props
+ const { eventKey, visible, level, mode, theme } = props
this.instanceArray = []
this.instanceArrayKeyIndexMap = {}
const className = classNames(
@@ -332,6 +367,8 @@ const SubPopupMenu = {
tag: 'ul',
// hiddenClassName: `${prefixCls}-hidden`,
visible,
+ level, mode, theme,
+ overflowedIndicator: getComponentFromProp(this, 'overflowedIndicator'),
},
attrs: {
role: props.role || 'menu',
diff --git a/components/vc-menu/assets/index.less b/components/vc-menu/assets/index.less
index 1da996cf5..fc4a1b839 100644
--- a/components/vc-menu/assets/index.less
+++ b/components/vc-menu/assets/index.less
@@ -2,8 +2,8 @@
@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');
+ src: url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/fonts/fontawesome-webfont.eot');
+ 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-style: normal;
}
@@ -92,6 +92,10 @@
&-submenu {
&-popup {
position: absolute;
+
+ .submenu-title-wrapper {
+ padding-right: 20px;
+ }
}
> .@{menuPrefixCls} {
background-color: #fff;
@@ -110,17 +114,19 @@
&-horizontal {
background-color: #F3F5F7;
border: none;
- border-bottom: 1px solid transparent;
border-bottom: 1px solid #d9d9d9;
box-shadow: none;
+ white-space: nowrap;
+ overflow: hidden;
& > .@{menuPrefixCls}-item, & > .@{menuPrefixCls}-submenu > .@{menuPrefixCls}-submenu-title {
padding: 15px 20px;
}
& > .@{menuPrefixCls}-submenu, & > .@{menuPrefixCls}-item {
- float: left;
border-bottom: 2px solid transparent;
+ display: inline-block;
+ vertical-align: bottom;
&-active {
border-bottom: 2px solid #2db7f5;
diff --git a/components/vc-menu/commonPropsType.js b/components/vc-menu/commonPropsType.js
index f7736e4aa..02c06a026 100644
--- a/components/vc-menu/commonPropsType.js
+++ b/components/vc-menu/commonPropsType.js
@@ -23,4 +23,8 @@ export default {
forceSubMenuRender: PropTypes.bool,
selectable: PropTypes.bool,
isRootMenu: PropTypes.bool.def(true),
+ builtinPlacements: PropTypes.object.def({}),
+ itemIcon: PropTypes.any,
+ expandIcon: PropTypes.any,
+ overflowedIndicator: PropTypes.any,
}
diff --git a/components/vc-menu/index.js b/components/vc-menu/index.js
index ce70ff19d..3e6b27248 100644
--- a/components/vc-menu/index.js
+++ b/components/vc-menu/index.js
@@ -1,4 +1,4 @@
-// based on rc-menu 7.0.5
+// based on rc-menu 7.4.19
import Menu from './Menu'
import SubMenu from './SubMenu'
import MenuItem, { menuItemProps } from './MenuItem'
diff --git a/components/vc-menu/util.js b/components/vc-menu/util.js
index dc41512b8..55228298f 100644
--- a/components/vc-menu/util.js
+++ b/components/vc-menu/util.js
@@ -77,6 +77,7 @@ export const menuAllProps = {
'rootPrefixCls',
'eventKey',
'active',
+ 'popupAlign',
'popupOffset',
'isOpen',
'renderMenuItem',
@@ -87,6 +88,8 @@ export const menuAllProps = {
'isSelected',
'store',
'activeKey',
+ 'builtinPlacements',
+ 'overflowedIndicator',
// the following keys found need to be removed from test regression
'attribute',
@@ -95,6 +98,8 @@ export const menuAllProps = {
'inlineCollapsed',
'menu',
'theme',
+ 'itemIcon',
+ 'expandIcon',
],
on: [
'select',
@@ -107,3 +112,15 @@ export const menuAllProps = {
'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
+ }
+}
diff --git a/package.json b/package.json
index 2f86bbc5a..fe35602bb 100644
--- a/package.json
+++ b/package.json
@@ -172,6 +172,7 @@
"json2mq": "^0.2.0",
"lodash": "^4.17.5",
"moment": "^2.21.0",
+ "mutationobserver-shim": "^0.3.2",
"omit.js": "^1.0.0",
"raf": "^3.4.0",
"resize-observer-polyfill": "^1.5.0",