pull/9/head
tangjinzhou 2017-12-12 13:49:02 +08:00
parent fb4a9970ef
commit e918a45e03
9 changed files with 1094 additions and 629 deletions

521
components/_util/KeyCode.js Normal file
View File

@ -0,0 +1,521 @@
/**
* @ignore
* some key-codes definition and utils from closure-library
* @author yiminghe@gmail.com
*/
const KeyCode = {
/**
* MAC_ENTER
*/
MAC_ENTER: 3,
/**
* BACKSPACE
*/
BACKSPACE: 8,
/**
* TAB
*/
TAB: 9,
/**
* NUMLOCK on FF/Safari Mac
*/
NUM_CENTER: 12, // NUMLOCK on FF/Safari Mac
/**
* ENTER
*/
ENTER: 13,
/**
* SHIFT
*/
SHIFT: 16,
/**
* CTRL
*/
CTRL: 17,
/**
* ALT
*/
ALT: 18,
/**
* PAUSE
*/
PAUSE: 19,
/**
* CAPS_LOCK
*/
CAPS_LOCK: 20,
/**
* ESC
*/
ESC: 27,
/**
* SPACE
*/
SPACE: 32,
/**
* PAGE_UP
*/
PAGE_UP: 33, // also NUM_NORTH_EAST
/**
* PAGE_DOWN
*/
PAGE_DOWN: 34, // also NUM_SOUTH_EAST
/**
* END
*/
END: 35, // also NUM_SOUTH_WEST
/**
* HOME
*/
HOME: 36, // also NUM_NORTH_WEST
/**
* LEFT
*/
LEFT: 37, // also NUM_WEST
/**
* UP
*/
UP: 38, // also NUM_NORTH
/**
* RIGHT
*/
RIGHT: 39, // also NUM_EAST
/**
* DOWN
*/
DOWN: 40, // also NUM_SOUTH
/**
* PRINT_SCREEN
*/
PRINT_SCREEN: 44,
/**
* INSERT
*/
INSERT: 45, // also NUM_INSERT
/**
* DELETE
*/
DELETE: 46, // also NUM_DELETE
/**
* ZERO
*/
ZERO: 48,
/**
* ONE
*/
ONE: 49,
/**
* TWO
*/
TWO: 50,
/**
* THREE
*/
THREE: 51,
/**
* FOUR
*/
FOUR: 52,
/**
* FIVE
*/
FIVE: 53,
/**
* SIX
*/
SIX: 54,
/**
* SEVEN
*/
SEVEN: 55,
/**
* EIGHT
*/
EIGHT: 56,
/**
* NINE
*/
NINE: 57,
/**
* QUESTION_MARK
*/
QUESTION_MARK: 63, // needs localization
/**
* A
*/
A: 65,
/**
* B
*/
B: 66,
/**
* C
*/
C: 67,
/**
* D
*/
D: 68,
/**
* E
*/
E: 69,
/**
* F
*/
F: 70,
/**
* G
*/
G: 71,
/**
* H
*/
H: 72,
/**
* I
*/
I: 73,
/**
* J
*/
J: 74,
/**
* K
*/
K: 75,
/**
* L
*/
L: 76,
/**
* M
*/
M: 77,
/**
* N
*/
N: 78,
/**
* O
*/
O: 79,
/**
* P
*/
P: 80,
/**
* Q
*/
Q: 81,
/**
* R
*/
R: 82,
/**
* S
*/
S: 83,
/**
* T
*/
T: 84,
/**
* U
*/
U: 85,
/**
* V
*/
V: 86,
/**
* W
*/
W: 87,
/**
* X
*/
X: 88,
/**
* Y
*/
Y: 89,
/**
* Z
*/
Z: 90,
/**
* META
*/
META: 91, // WIN_KEY_LEFT
/**
* WIN_KEY_RIGHT
*/
WIN_KEY_RIGHT: 92,
/**
* CONTEXT_MENU
*/
CONTEXT_MENU: 93,
/**
* NUM_ZERO
*/
NUM_ZERO: 96,
/**
* NUM_ONE
*/
NUM_ONE: 97,
/**
* NUM_TWO
*/
NUM_TWO: 98,
/**
* NUM_THREE
*/
NUM_THREE: 99,
/**
* NUM_FOUR
*/
NUM_FOUR: 100,
/**
* NUM_FIVE
*/
NUM_FIVE: 101,
/**
* NUM_SIX
*/
NUM_SIX: 102,
/**
* NUM_SEVEN
*/
NUM_SEVEN: 103,
/**
* NUM_EIGHT
*/
NUM_EIGHT: 104,
/**
* NUM_NINE
*/
NUM_NINE: 105,
/**
* NUM_MULTIPLY
*/
NUM_MULTIPLY: 106,
/**
* NUM_PLUS
*/
NUM_PLUS: 107,
/**
* NUM_MINUS
*/
NUM_MINUS: 109,
/**
* NUM_PERIOD
*/
NUM_PERIOD: 110,
/**
* NUM_DIVISION
*/
NUM_DIVISION: 111,
/**
* F1
*/
F1: 112,
/**
* F2
*/
F2: 113,
/**
* F3
*/
F3: 114,
/**
* F4
*/
F4: 115,
/**
* F5
*/
F5: 116,
/**
* F6
*/
F6: 117,
/**
* F7
*/
F7: 118,
/**
* F8
*/
F8: 119,
/**
* F9
*/
F9: 120,
/**
* F10
*/
F10: 121,
/**
* F11
*/
F11: 122,
/**
* F12
*/
F12: 123,
/**
* NUMLOCK
*/
NUMLOCK: 144,
/**
* SEMICOLON
*/
SEMICOLON: 186, // needs localization
/**
* DASH
*/
DASH: 189, // needs localization
/**
* EQUALS
*/
EQUALS: 187, // needs localization
/**
* COMMA
*/
COMMA: 188, // needs localization
/**
* PERIOD
*/
PERIOD: 190, // needs localization
/**
* SLASH
*/
SLASH: 191, // needs localization
/**
* APOSTROPHE
*/
APOSTROPHE: 192, // needs localization
/**
* SINGLE_QUOTE
*/
SINGLE_QUOTE: 222, // needs localization
/**
* OPEN_SQUARE_BRACKET
*/
OPEN_SQUARE_BRACKET: 219, // needs localization
/**
* BACKSLASH
*/
BACKSLASH: 220, // needs localization
/**
* CLOSE_SQUARE_BRACKET
*/
CLOSE_SQUARE_BRACKET: 221, // needs localization
/**
* WIN_KEY
*/
WIN_KEY: 224,
/**
* MAC_FF_META
*/
MAC_FF_META: 224, // Firefox (Gecko) fires this for the meta key instead of 91
/**
* WIN_IME
*/
WIN_IME: 229,
}
/*
whether text and modified key is entered at the same time.
*/
KeyCode.isTextModifyingKeyEvent = function isTextModifyingKeyEvent (e) {
const keyCode = e.keyCode
if (e.altKey && !e.ctrlKey || e.metaKey ||
// Function keys don't generate text
keyCode >= KeyCode.F1 && keyCode <= KeyCode.F12) {
return false
}
// The following keys are quite harmless, even in combination with
// CTRL, ALT or SHIFT.
switch (keyCode) {
case KeyCode.ALT:
case KeyCode.CAPS_LOCK:
case KeyCode.CONTEXT_MENU:
case KeyCode.CTRL:
case KeyCode.DOWN:
case KeyCode.END:
case KeyCode.ESC:
case KeyCode.HOME:
case KeyCode.INSERT:
case KeyCode.LEFT:
case KeyCode.MAC_FF_META:
case KeyCode.META:
case KeyCode.NUMLOCK:
case KeyCode.NUM_CENTER:
case KeyCode.PAGE_DOWN:
case KeyCode.PAGE_UP:
case KeyCode.PAUSE:
case KeyCode.PRINT_SCREEN:
case KeyCode.RIGHT:
case KeyCode.SHIFT:
case KeyCode.UP:
case KeyCode.WIN_KEY:
case KeyCode.WIN_KEY_RIGHT:
return false
default:
return true
}
}
/*
whether character is entered.
*/
KeyCode.isCharacterKey = function isCharacterKey (keyCode) {
if (keyCode >= KeyCode.ZERO &&
keyCode <= KeyCode.NINE) {
return true
}
if (keyCode >= KeyCode.NUM_ZERO &&
keyCode <= KeyCode.NUM_MULTIPLY) {
return true
}
if (keyCode >= KeyCode.A &&
keyCode <= KeyCode.Z) {
return true
}
// Safari sends zero key code for non-latin characters.
if (window.navigation.userAgent.indexOf('WebKit') !== -1 && keyCode === 0) {
return true
}
switch (keyCode) {
case KeyCode.SPACE:
case KeyCode.QUESTION_MARK:
case KeyCode.NUM_PLUS:
case KeyCode.NUM_MINUS:
case KeyCode.NUM_PERIOD:
case KeyCode.NUM_DIVISION:
case KeyCode.SEMICOLON:
case KeyCode.DASH:
case KeyCode.EQUALS:
case KeyCode.COMMA:
case KeyCode.PERIOD:
case KeyCode.SLASH:
case KeyCode.APOSTROPHE:
case KeyCode.SINGLE_QUOTE:
case KeyCode.OPEN_SQUARE_BRACKET:
case KeyCode.BACKSLASH:
case KeyCode.CLOSE_SQUARE_BRACKET:
return true
default:
return false
}
}
export default KeyCode

View File

@ -1,221 +0,0 @@
// import React from 'react';
import PropTypes from 'prop-types'
import createReactClass from 'create-react-class'
import MenuMixin from './MenuMixin'
import { noop } from './util'
const Menu = createReactClass({
displayName: 'Menu',
propTypes: {
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
selectedKeys: PropTypes.arrayOf(PropTypes.string),
defaultOpenKeys: PropTypes.arrayOf(PropTypes.string),
openKeys: PropTypes.arrayOf(PropTypes.string),
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']),
getPopupContainer: PropTypes.func,
onClick: PropTypes.func,
onSelect: PropTypes.func,
onDeselect: PropTypes.func,
onDestroy: PropTypes.func,
openTransitionName: PropTypes.string,
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
subMenuOpenDelay: PropTypes.number,
subMenuCloseDelay: PropTypes.number,
forceSubMenuRender: PropTypes.bool,
triggerSubMenuAction: PropTypes.string,
level: PropTypes.number,
selectable: PropTypes.bool,
multiple: PropTypes.bool,
children: PropTypes.any,
},
mixins: [MenuMixin],
isRootMenu: true,
getDefaultProps () {
return {
selectable: true,
onClick: noop,
onSelect: noop,
onOpenChange: noop,
onDeselect: noop,
defaultSelectedKeys: [],
defaultOpenKeys: [],
subMenuOpenDelay: 0,
subMenuCloseDelay: 0.1,
triggerSubMenuAction: 'hover',
}
},
getInitialState () {
const props = this.props
let selectedKeys = props.defaultSelectedKeys
let openKeys = props.defaultOpenKeys
if ('selectedKeys' in props) {
selectedKeys = props.selectedKeys || []
}
if ('openKeys' in props) {
openKeys = props.openKeys || []
}
return {
selectedKeys,
openKeys,
}
},
componentWillReceiveProps (nextProps) {
const props = {}
if ('selectedKeys' in nextProps) {
props.selectedKeys = nextProps.selectedKeys || []
}
if ('openKeys' in nextProps) {
props.openKeys = nextProps.openKeys || []
}
this.setState(props)
},
onDestroy (key) {
const state = this.state
const props = this.props
const selectedKeys = state.selectedKeys
const openKeys = state.openKeys
let index = selectedKeys.indexOf(key)
if (!('selectedKeys' in props) && index !== -1) {
selectedKeys.splice(index, 1)
}
index = openKeys.indexOf(key)
if (!('openKeys' in props) && index !== -1) {
openKeys.splice(index, 1)
}
},
onSelect (selectInfo) {
const props = this.props
if (props.selectable) {
// root menu
let selectedKeys = this.state.selectedKeys
const selectedKey = selectInfo.key
if (props.multiple) {
selectedKeys = selectedKeys.concat([selectedKey])
} else {
selectedKeys = [selectedKey]
}
if (!('selectedKeys' in props)) {
this.setState({
selectedKeys,
})
}
props.onSelect({
...selectInfo,
selectedKeys,
})
}
},
onClick (e) {
this.props.onClick(e)
},
onOpenChange (e_) {
const props = this.props
const openKeys = this.state.openKeys.concat()
let changed = false
const processSingle = (e) => {
let oneChanged = false
if (e.open) {
oneChanged = openKeys.indexOf(e.key) === -1
if (oneChanged) {
openKeys.push(e.key)
}
} else {
const index = openKeys.indexOf(e.key)
oneChanged = index !== -1
if (oneChanged) {
openKeys.splice(index, 1)
}
}
changed = changed || oneChanged
}
if (Array.isArray(e_)) {
// batch change call
e_.forEach(processSingle)
} else {
processSingle(e_)
}
if (changed) {
if (!('openKeys' in this.props)) {
this.setState({ openKeys })
}
props.onOpenChange(openKeys)
}
},
onDeselect (selectInfo) {
const props = this.props
if (props.selectable) {
const selectedKeys = this.state.selectedKeys.concat()
const selectedKey = selectInfo.key
const index = selectedKeys.indexOf(selectedKey)
if (index !== -1) {
selectedKeys.splice(index, 1)
}
if (!('selectedKeys' in props)) {
this.setState({
selectedKeys,
})
}
props.onDeselect({
...selectInfo,
selectedKeys,
})
}
},
getOpenTransitionName () {
const props = this.props
let transitionName = props.openTransitionName
const animationName = props.openAnimation
if (!transitionName && typeof animationName === 'string') {
transitionName = `${props.prefixCls}-open-${animationName}`
}
return transitionName
},
isInlineMode () {
return this.props.mode === 'inline'
},
lastOpenSubMenu () {
let lastOpen = []
const { openKeys } = this.state
if (openKeys.length) {
lastOpen = this.getFlatInstanceArray().filter((c) => {
return c && openKeys.indexOf(c.props.eventKey) !== -1
})
}
return lastOpen[0]
},
renderMenuItem (c, i, subIndex) {
if (!c) {
return null
}
const state = this.state
const extraProps = {
openKeys: state.openKeys,
selectedKeys: state.selectedKeys,
triggerSubMenuAction: this.props.triggerSubMenuAction,
}
return this.renderCommonMenuItem(c, i, subIndex, extraProps)
},
render () {
const props = { ...this.props }
props.className += ` ${props.prefixCls}-root`
return this.renderRoot(props)
},
})
export default Menu

View File

@ -0,0 +1,195 @@
<script>
import PropTypes from 'vue-types'
import MenuMixin from './MenuMixin'
import StateMixin from '../../_util/StateMixin'
const Menu = {
name: 'Menu',
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,
openTransitionName: PropTypes.string,
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
subMenuOpenDelay: PropTypes.number.def(0),
subMenuCloseDelay: PropTypes.number.def(0.1),
forceSubMenuRender: PropTypes.bool,
triggerSubMenuAction: PropTypes.string.def('hover'),
level: PropTypes.number.def(1),
selectable: PropTypes.bool.def(true),
multiple: PropTypes.bool,
children: PropTypes.any,
},
mixins: [StateMixin, MenuMixin],
data () {
const props = this.$props
let selectedKeys = props.defaultSelectedKeys
let openKeys = props.defaultOpenKeys
selectedKeys = props.selectedKeys || []
openKeys = props.openKeys || []
return {
selectedKeys,
openKeys,
isRootMenu: true,
}
},
componentWillReceiveProps (nextProps) {
const props = {}
if ('selectedKeys' in nextProps) {
props.selectedKeys = nextProps.selectedKeys || []
}
if ('openKeys' in nextProps) {
props.openKeys = nextProps.openKeys || []
}
this.setState(props)
},
methods: {
onDestroy (key) {
const state = this.$data
const props = this.$props
const selectedKeys = state.selectedKeys
const openKeys = state.openKeys
let index = selectedKeys.indexOf(key)
if (!('selectedKeys' in props) && index !== -1) {
selectedKeys.splice(index, 1)
}
index = openKeys.indexOf(key)
if (!('openKeys' in props) && index !== -1) {
openKeys.splice(index, 1)
}
},
onSelect (selectInfo) {
const props = this.$props
if (props.selectable) {
// root menu
let selectedKeys = this.$data.selectedKeys
const selectedKey = selectInfo.key
if (props.multiple) {
selectedKeys = selectedKeys.concat([selectedKey])
} else {
selectedKeys = [selectedKey]
}
if (!('selectedKeys' in props)) {
this.setState({
selectedKeys,
})
}
this.$emit('select', {
...selectInfo,
selectedKeys,
})
}
},
onClick (e) {
this.$emit('click', e)
},
onOpenChange (e_) {
const openKeys = this.$data.openKeys.concat()
let changed = false
const processSingle = (e) => {
let oneChanged = false
if (e.open) {
oneChanged = openKeys.indexOf(e.key) === -1
if (oneChanged) {
openKeys.push(e.key)
}
} else {
const index = openKeys.indexOf(e.key)
oneChanged = index !== -1
if (oneChanged) {
openKeys.splice(index, 1)
}
}
changed = changed || oneChanged
}
if (Array.isArray(e_)) {
// batch change call
e_.forEach(processSingle)
} else {
processSingle(e_)
}
if (changed) {
if (this.$props.openKeys === undefined) {
this.setState({ openKeys })
}
this.$emit('openChange', openKeys)
}
},
onDeselect (selectInfo) {
const props = this.$props
if (props.selectable) {
const selectedKeys = this.$data.selectedKeys.concat()
const selectedKey = selectInfo.key
const index = selectedKeys.indexOf(selectedKey)
if (index !== -1) {
selectedKeys.splice(index, 1)
}
if (!('selectedKeys' in props)) {
this.setState({
selectedKeys,
})
}
this.$emit('deselect', {
...selectInfo,
selectedKeys,
})
}
},
getOpenTransitionName () {
const props = this.$props
let transitionName = props.openTransitionName
const animationName = props.openAnimation
if (!transitionName && typeof animationName === 'string') {
transitionName = `${props.prefixCls}-open-${animationName}`
}
return transitionName
},
isInlineMode () {
return this.$props.mode === 'inline'
},
lastOpenSubMenu () {
let lastOpen = []
const { openKeys } = this.$data
if (openKeys.length) {
lastOpen = this.getFlatInstanceArray().filter((c) => {
return c && openKeys.indexOf(c.props.eventKey) !== -1
})
}
return lastOpen[0]
},
renderMenuItem (c, i, subIndex) {
if (!c) {
return null
}
const state = this.$data
const extraProps = {
openKeys: state.openKeys,
selectedKeys: state.selectedKeys,
triggerSubMenuAction: this.$props.triggerSubMenuAction,
}
return this.renderCommonMenuItem(c, i, subIndex, extraProps)
},
},
render () {
const props = { ...this.$props }
props.className += ` ${props.prefixCls}-root`
return this.renderRoot(props)
},
}
export default Menu
</script>

View File

@ -1,165 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import createReactClass from 'create-react-class'
import KeyCode from 'rc-util/lib/KeyCode'
import classNames from 'classnames'
import { noop } from './util'
/* eslint react/no-is-mounted:0 */
const MenuItem = createReactClass({
displayName: 'MenuItem',
propTypes: {
rootPrefixCls: PropTypes.string,
eventKey: PropTypes.string,
active: PropTypes.bool,
children: PropTypes.any,
selectedKeys: PropTypes.array,
disabled: PropTypes.bool,
title: PropTypes.string,
onItemHover: PropTypes.func,
onSelect: PropTypes.func,
onClick: PropTypes.func,
onDeselect: PropTypes.func,
parentMenu: PropTypes.object,
onDestroy: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
},
getDefaultProps () {
return {
onSelect: noop,
onMouseEnter: noop,
onMouseLeave: noop,
}
},
componentWillUnmount () {
const props = this.props
if (props.onDestroy) {
props.onDestroy(props.eventKey)
}
},
onKeyDown (e) {
const keyCode = e.keyCode
if (keyCode === KeyCode.ENTER) {
this.onClick(e)
return true
}
},
onMouseLeave (e) {
const { eventKey, onItemHover, onMouseLeave } = this.props
onItemHover({
key: eventKey,
hover: false,
})
onMouseLeave({
key: eventKey,
domEvent: e,
})
},
onMouseEnter (e) {
const { eventKey, parentMenu, onItemHover, onMouseEnter } = this.props
if (parentMenu.subMenuInstance) {
parentMenu.subMenuInstance.clearSubMenuTimers()
}
onItemHover({
key: eventKey,
hover: true,
})
onMouseEnter({
key: eventKey,
domEvent: e,
})
},
onClick (e) {
const { eventKey, multiple, onClick, onSelect, onDeselect } = this.props
const selected = this.isSelected()
const info = {
key: eventKey,
keyPath: [eventKey],
item: this,
domEvent: e,
}
onClick(info)
if (multiple) {
if (selected) {
onDeselect(info)
} else {
onSelect(info)
}
} else if (!selected) {
onSelect(info)
}
},
getPrefixCls () {
return `${this.props.rootPrefixCls}-item`
},
getActiveClassName () {
return `${this.getPrefixCls()}-active`
},
getSelectedClassName () {
return `${this.getPrefixCls()}-selected`
},
getDisabledClassName () {
return `${this.getPrefixCls()}-disabled`
},
isSelected () {
return this.props.selectedKeys.indexOf(this.props.eventKey) !== -1
},
render () {
const props = this.props
const selected = this.isSelected()
const className = classNames(this.getPrefixCls(), props.className, {
[this.getActiveClassName()]: !props.disabled && props.active,
[this.getSelectedClassName()]: selected,
[this.getDisabledClassName()]: props.disabled,
})
const attrs = {
...props.attribute,
title: props.title,
className,
role: 'menuitem',
'aria-selected': selected,
'aria-disabled': props.disabled,
}
let mouseEvent = {}
if (!props.disabled) {
mouseEvent = {
onClick: this.onClick,
onMouseLeave: this.onMouseLeave,
onMouseEnter: this.onMouseEnter,
}
}
const style = {
...props.style,
}
if (props.mode === 'inline') {
style.paddingLeft = props.inlineIndent * props.level
}
return (
<li
{...attrs}
{...mouseEvent}
style={style}
>
{props.children}
</li>
)
},
})
MenuItem.isMenuItem = 1
export default MenuItem

View File

@ -0,0 +1,157 @@
<script>
import PropTypes from 'vue-types'
import KeyCode from '../../_util/KeyCode'
const MenuItem = {
name: 'MenuItem',
props: {
rootPrefixCls: PropTypes.string,
eventKey: PropTypes.string,
active: PropTypes.bool,
children: PropTypes.any,
selectedKeys: PropTypes.array,
disabled: PropTypes.bool,
title: PropTypes.string,
// onItemHover: PropTypes.func,
// onSelect: PropTypes.func,
// onClick: PropTypes.func,
// onDeselect: PropTypes.func,
parentMenu: PropTypes.object,
// onDestroy: PropTypes.func,
// onMouseEnter: PropTypes.func,
// onMouseLeave: PropTypes.func,
},
beforeDestroy () {
const props = this.$props
this.$emit('destroy', props.eventKey)
},
methods: {
onKeyDown (e) {
const keyCode = e.keyCode
if (keyCode === KeyCode.ENTER) {
this.$emit('click', e)
return true
}
},
onMouseLeave (e) {
const { eventKey } = this.$props
this.$emit('itemHover', {
key: eventKey,
hover: false,
})
this.$emit('mouseLeave', {
key: eventKey,
domEvent: e,
})
},
onMouseEnter (e) {
const { eventKey, parentMenu } = this.$props
if (parentMenu.subMenuInstance) {
parentMenu.subMenuInstance.clearSubMenuTimers()
}
this.$emit('itemHover', {
key: eventKey,
hover: true,
})
this.$emit('mouseEnter', {
key: eventKey,
domEvent: e,
})
},
onClick (e) {
const { eventKey, multiple } = this.$props
const selected = this.isSelected()
const info = {
key: eventKey,
keyPath: [eventKey],
item: this,
domEvent: e,
}
this.$emit('click', info)
if (multiple) {
if (selected) {
this.$emit('deselect', info)
} else {
this.$emit('select', info)
}
} else if (!selected) {
this.$emit('select', info)
}
},
getPrefixCls () {
return `${this.$props.rootPrefixCls}-item`
},
getActiveClassName () {
return `${this.getPrefixCls()}-active`
},
getSelectedClassName () {
return `${this.getPrefixCls()}-selected`
},
getDisabledClassName () {
return `${this.getPrefixCls()}-disabled`
},
isSelected () {
return this.$props.selectedKeys.indexOf(this.$props.eventKey) !== -1
},
},
render () {
const props = this.$props
const selected = this.isSelected()
const className = {
[this.getPrefixCls()]: true,
[this.getActiveClassName()]: !props.disabled && props.active,
[this.getSelectedClassName()]: selected,
[this.getDisabledClassName()]: props.disabled,
}
const attrs = {
...props.attribute,
title: props.title,
role: 'menuitem',
'aria-selected': selected,
'aria-disabled': props.disabled,
}
let mouseEvent = {}
if (!props.disabled) {
mouseEvent = {
click: this.onClick,
mouseleave: this.onMouseLeave,
mouseenter: this.onMouseEnter,
}
}
const style = {}
if (props.mode === 'inline') {
style.paddingLeft = `${props.inlineIndent * props.level}px`
}
const liProps = {
attrs,
on: {
mouseEvent,
},
}
return (
<li
{...liProps}
style={style}
class={className}
>
{this.$slots.default}
</li>
)
},
}
MenuItem.isMenuItem = 1
export default MenuItem
</script>

View File

@ -1,48 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import createReactClass from 'create-react-class'
const MenuItemGroup = createReactClass({
displayName: 'MenuItemGroup',
propTypes: {
renderMenuItem: PropTypes.func,
index: PropTypes.number,
className: PropTypes.string,
rootPrefixCls: PropTypes.string,
},
getDefaultProps () {
// To fix keyboard UX.
return { disabled: true }
},
renderInnerMenuItem (item, subIndex) {
const { renderMenuItem, index } = this.props
return renderMenuItem(item, index, subIndex)
},
render () {
const props = this.props
const { className = '', rootPrefixCls } = props
const titleClassName = `${rootPrefixCls}-item-group-title`
const listClassName = `${rootPrefixCls}-item-group-list`
return (
<li className={`${className} ${rootPrefixCls}-item-group`}>
<div
className={titleClassName}
title={typeof props.title === 'string' ? props.title : undefined}
>
{props.title}
</div>
<ul className={listClassName}>
{React.Children.map(props.children, this.renderInnerMenuItem)}
</ul>
</li>
)
},
})
MenuItemGroup.isMenuItemGroup = true
export default MenuItemGroup

View File

@ -0,0 +1,45 @@
<script>
import PropTypes from 'vue-types'
const MenuItemGroup = {
name: 'MenuItemGroup',
props: {
renderMenuItem: PropTypes.func,
index: PropTypes.number,
className: PropTypes.string,
rootPrefixCls: PropTypes.string,
disabled: PropTypes.bool.def(true),
},
methods: {
renderInnerMenuItem (item, subIndex) {
const { renderMenuItem, index } = this.$props
return renderMenuItem(item, index, subIndex)
},
},
render () {
const props = this.$props
const { rootPrefixCls } = props
const titleClassName = `${rootPrefixCls}-item-group-title`
const listClassName = `${rootPrefixCls}-item-group-list`
return (
<li class={`${rootPrefixCls}-item-group`}>
<div
class={titleClassName}
title={typeof props.title === 'string' ? props.title : undefined}
>
{props.title}
</div>
<ul class={listClassName}>
{this.$slots.default.map(this.renderInnerMenuItem)}
</ul>
</li>
)
},
}
MenuItemGroup.isMenuItemGroup = true
export default MenuItemGroup
</script>

View File

@ -1,9 +1,5 @@
import React from 'react'
import PropTypes from 'prop-types'
import ReactDOM from 'react-dom'
import KeyCode from 'rc-util/lib/KeyCode'
import createChainedFunction from 'rc-util/lib/createChainedFunction'
import classNames from 'classnames'
import PropTypes from 'vue-types'
import KeyCode from '../../_util/KeyCode'
import scrollIntoView from 'dom-scroll-into-view'
import { getKeyFromChildrenIndex, loopMenuItem } from './util'
import DOMWrap from './DOMWrap'
@ -53,35 +49,22 @@ function saveRef (index, subIndex, c) {
}
const MenuMixin = {
propTypes: {
focusable: PropTypes.bool,
props: {
prefixCls: PropTypes.string.def('ant-menu'),
inlineIndent: PropTypes.number.def(24),
focusable: PropTypes.bool.def(true),
multiple: PropTypes.bool,
style: PropTypes.object,
defaultActiveFirst: PropTypes.bool,
visible: 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),
children: PropTypes.any,
},
getDefaultProps () {
return {
prefixCls: 'rc-menu',
className: '',
mode: 'vertical',
level: 1,
inlineIndent: 24,
visible: true,
focusable: true,
style: {},
}
},
getInitialState () {
const props = this.props
data () {
const props = this.$props
return {
activeKey: getActiveKey(props, props.activeKey),
}
@ -108,190 +91,189 @@ const MenuMixin = {
}
},
shouldComponentUpdate (nextProps) {
return this.props.visible || nextProps.visible
},
componentWillMount () {
created () {
this.instanceArray = []
},
// all keyboard events callbacks run from here at first
onKeyDown (e, callback) {
const keyCode = e.keyCode
let handled
this.getFlatInstanceArray().forEach((obj) => {
if (obj && obj.props.active && obj.onKeyDown) {
handled = obj.onKeyDown(e)
methods: {
// all keyboard events callbacks run from here at first
onKeyDown (e, callback) {
const keyCode = e.keyCode
let handled
this.getFlatInstanceArray().forEach((obj) => {
if (obj && obj.props.active && obj.onKeyDown) {
handled = obj.onKeyDown(e)
}
})
if (handled) {
return 1
}
})
if (handled) {
return 1
}
let activeItem = null
if (keyCode === KeyCode.UP || keyCode === KeyCode.DOWN) {
activeItem = this.step(keyCode === KeyCode.UP ? -1 : 1)
}
if (activeItem) {
e.preventDefault()
this.setState({
activeKey: activeItem.props.eventKey,
}, () => {
scrollIntoView(ReactDOM.findDOMNode(activeItem), ReactDOM.findDOMNode(this), {
onlyScrollIfNeeded: true,
let activeItem = null
if (keyCode === KeyCode.UP || keyCode === KeyCode.DOWN) {
activeItem = this.step(keyCode === KeyCode.UP ? -1 : 1)
}
if (activeItem) {
e.preventDefault()
this.setState({
activeKey: activeItem.$props.eventKey,
}, () => {
scrollIntoView(activeItem.$el, this.$el, {
onlyScrollIfNeeded: true,
})
// https://github.com/react-component/menu/commit/9899a9672f6f028ec3cdf773f1ecea5badd2d33e
if (typeof callback === 'function') {
callback(activeItem)
}
})
// https://github.com/react-component/menu/commit/9899a9672f6f028ec3cdf773f1ecea5badd2d33e
if (typeof callback === 'function') {
callback(activeItem)
}
})
return 1
} else if (activeItem === undefined) {
e.preventDefault()
return 1
} else if (activeItem === undefined) {
e.preventDefault()
this.setState({
activeKey: null,
})
return 1
}
},
onItemHover (e) {
const { key, hover } = e
this.setState({
activeKey: null,
activeKey: hover ? key : null,
})
return 1
}
},
},
onItemHover (e) {
const { key, hover } = e
this.setState({
activeKey: hover ? key : null,
})
},
getFlatInstanceArray () {
let instanceArray = this.instanceArray
const hasInnerArray = instanceArray.some((a) => {
return Array.isArray(a)
})
if (hasInnerArray) {
instanceArray = []
this.instanceArray.forEach((a) => {
if (Array.isArray(a)) {
instanceArray.push.apply(instanceArray, a)
} else {
instanceArray.push(a)
}
getFlatInstanceArray () {
let instanceArray = this.instanceArray
const hasInnerArray = instanceArray.some((a) => {
return Array.isArray(a)
})
this.instanceArray = instanceArray
}
return instanceArray
},
if (hasInnerArray) {
instanceArray = []
this.instanceArray.forEach((a) => {
if (Array.isArray(a)) {
instanceArray.push.apply(instanceArray, a)
} else {
instanceArray.push(a)
}
})
this.instanceArray = instanceArray
}
return instanceArray
},
renderCommonMenuItem (child, i, subIndex, extraProps) {
const state = this.state
const props = this.props
const key = getKeyFromChildrenIndex(child, props.eventKey, i)
const childProps = child.props
const isActive = key === state.activeKey
const newChildProps = {
mode: props.mode,
level: props.level,
inlineIndent: props.inlineIndent,
renderMenuItem: this.renderMenuItem,
rootPrefixCls: props.prefixCls,
index: i,
parentMenu: this,
ref: childProps.disabled ? undefined
: createChainedFunction(child.ref, saveRef.bind(this, i, subIndex)),
eventKey: key,
active: !childProps.disabled && isActive,
multiple: props.multiple,
onClick: this.onClick,
onItemHover: this.onItemHover,
openTransitionName: this.getOpenTransitionName(),
openAnimation: props.openAnimation,
subMenuOpenDelay: props.subMenuOpenDelay,
subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender,
onOpenChange: this.onOpenChange,
onDeselect: this.onDeselect,
onDestroy: this.onDestroy,
onSelect: this.onSelect,
...extraProps,
}
if (props.mode === 'inline') {
newChildProps.triggerSubMenuAction = 'click'
}
return React.cloneElement(child, newChildProps)
},
renderCommonMenuItem (child, i, subIndex, extraProps) {
const state = this.state
const props = this.props
const key = getKeyFromChildrenIndex(child, props.eventKey, i)
const childProps = child.props
const isActive = key === state.activeKey
const newChildProps = {
mode: props.mode,
level: props.level,
inlineIndent: props.inlineIndent,
renderMenuItem: this.renderMenuItem,
rootPrefixCls: props.prefixCls,
index: i,
parentMenu: this,
ref: child.ref,
// ref: childProps.disabled ? undefined
// : createChainedFunction(child.ref, saveRef.bind(this, i, subIndex)),
eventKey: key,
active: !childProps.disabled && isActive,
multiple: props.multiple,
onClick: this.onClick,
onItemHover: this.onItemHover,
openTransitionName: this.getOpenTransitionName(),
openAnimation: props.openAnimation,
subMenuOpenDelay: props.subMenuOpenDelay,
subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender,
onOpenChange: this.onOpenChange,
onDeselect: this.onDeselect,
onDestroy: this.onDestroy,
onSelect: this.onSelect,
...extraProps,
}
if (props.mode === 'inline') {
newChildProps.triggerSubMenuAction = 'click'
}
return child
// return React.cloneElement(child, newChildProps)
},
renderRoot (props) {
this.instanceArray = []
const className = classNames(
props.prefixCls,
props.className,
`${props.prefixCls}-${props.mode}`,
)
const domProps = {
className,
role: 'menu',
'aria-activedescendant': '',
}
if (props.id) {
domProps.id = props.id
}
if (props.focusable) {
domProps.tabIndex = '0'
domProps.onKeyDown = this.onKeyDown
}
return (
renderRoot (props) {
this.instanceArray = []
const className = {
[props.prefixCls]: true,
[props.className]: true,
[`${props.prefixCls}-${props.mode}`]: true,
}
const domProps = {
className,
role: 'menu',
'aria-activedescendant': '',
}
if (props.id) {
domProps.id = props.id
}
if (props.focusable) {
domProps.tabIndex = '0'
domProps.onKeyDown = this.onKeyDown
}
return (
// ESLint is not smart enough to know that the type of `children` was checked.
/* eslint-disable */
<DOMWrap
style={props.style}
tag="ul"
hiddenClassName={`${props.prefixCls}-hidden`}
visible={props.visible}
{...domProps}
>
{React.Children.map(props.children, this.renderMenuItem)}
</DOMWrap>
/*eslint -enable */
)
},
<DOMWrap
style={props.style}
tag="ul"
hiddenClassName={`${props.prefixCls}-hidden`}
visible={props.visible}
{...domProps}
>
{this.$slots.default.map(this.renderMenuItem)}
</DOMWrap>
/*eslint -enable */
)
},
step (direction) {
let children = this.getFlatInstanceArray()
const activeKey = this.state.activeKey
const len = children.length
if (!len) {
return null
}
if (direction < 0) {
children = children.concat().reverse()
}
// find current activeIndex
let activeIndex = -1
children.every((c, ci) => {
if (c && c.props.eventKey === activeKey) {
activeIndex = ci
return false
step (direction) {
let children = this.getFlatInstanceArray()
const activeKey = this.state.activeKey
const len = children.length
if (!len) {
return null
}
return true
})
if (!this.props.defaultActiveFirst && activeIndex !== -1) {
if (allDisabled(children.slice(activeIndex, len - 1))) {
return undefined
if (direction < 0) {
children = children.concat().reverse()
}
}
const start = (activeIndex + 1) % len
let i = start
for (; ;) {
const child = children[i]
if (!child || child.props.disabled) {
i = (i + 1 + len) % len
// complete a loop
if (i === start) {
return null
// find current activeIndex
let activeIndex = -1
children.every((c, ci) => {
if (c && c.props.eventKey === activeKey) {
activeIndex = ci
return false
}
return true
})
if (!this.props.defaultActiveFirst && activeIndex !== -1) {
if (allDisabled(children.slice(activeIndex, len - 1))) {
return undefined
}
} else {
return child
}
}
const start = (activeIndex + 1) % len
let i = start
for (; ;) {
const child = children[i]
if (!child || child.props.disabled) {
i = (i + 1 + len) % len
// complete a loop
if (i === start) {
return null
}
} else {
return child
}
}
},
},
}

View File

@ -1,5 +1,4 @@
import React from 'react'
import PropTypes from 'prop-types'
import PropTypes from 'vue-types'
import createReactClass from 'create-react-class'
import Animate from 'rc-animate'
import MenuMixin from './MenuMixin'