1 | import React from 'react';
|
2 | import ReactDOM from 'react-dom';
|
3 | import PropTypes from 'prop-types';
|
4 | import { Provider, create } from 'mini-store';
|
5 | import { default as SubPopupMenu, getActiveKey } from './SubPopupMenu';
|
6 | import { noop,fireKeyEvent } from './util';
|
7 |
|
8 | class Menu extends React.Component {
|
9 | static propTypes = {
|
10 | defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
|
11 | defaultActiveFirst: PropTypes.bool,
|
12 | selectedKeys: PropTypes.arrayOf(PropTypes.string),
|
13 | defaultOpenKeys: PropTypes.arrayOf(PropTypes.string),
|
14 | openKeys: PropTypes.arrayOf(PropTypes.string),
|
15 | mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']),
|
16 | getPopupContainer: PropTypes.func,
|
17 | onClick: PropTypes.func,
|
18 | onSelect: PropTypes.func,
|
19 | onDeselect: PropTypes.func,
|
20 | onDestroy: PropTypes.func,
|
21 | openTransitionName: PropTypes.string,
|
22 | openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
23 | subMenuOpenDelay: PropTypes.number,
|
24 | subMenuCloseDelay: PropTypes.number,
|
25 | forceSubMenuRender: PropTypes.bool,
|
26 | triggerSubMenuAction: PropTypes.string,
|
27 | level: PropTypes.number,
|
28 | selectable: PropTypes.bool,
|
29 | multiple: PropTypes.bool,
|
30 | children: PropTypes.any,
|
31 | className: PropTypes.string,
|
32 | style: PropTypes.object,
|
33 | activeKey: PropTypes.string,
|
34 | prefixCls: PropTypes.string,
|
35 | builtinPlacements: PropTypes.object,
|
36 | itemIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
|
37 | expandIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
|
38 | overflowedIndicator: PropTypes.node,
|
39 | keyboard: PropTypes.bool,
|
40 | };
|
41 |
|
42 | static defaultProps = {
|
43 | selectable: true,
|
44 | onClick: noop,
|
45 | onSelect: noop,
|
46 | onOpenChange: noop,
|
47 | onDeselect: noop,
|
48 | defaultSelectedKeys: [],
|
49 | defaultOpenKeys: [],
|
50 | subMenuOpenDelay: 0.1,
|
51 | subMenuCloseDelay: 0.1,
|
52 | triggerSubMenuAction: 'hover',
|
53 | prefixCls: 'rc-menu',
|
54 | className: '',
|
55 | mode: 'vertical',
|
56 | style: {},
|
57 | builtinPlacements: {},
|
58 | overflowedIndicator: <span>···</span>,
|
59 | keyboard:false
|
60 | };
|
61 |
|
62 | constructor(props) {
|
63 | super(props);
|
64 |
|
65 | this.isRootMenu = true;
|
66 |
|
67 | let selectedKeys = props.defaultSelectedKeys;
|
68 | let openKeys = props.defaultOpenKeys;
|
69 | if ('selectedKeys' in props) {
|
70 | selectedKeys = props.selectedKeys || [];
|
71 | }
|
72 | if ('openKeys' in props) {
|
73 | openKeys = props.openKeys || [];
|
74 | }
|
75 |
|
76 | this.store = create({
|
77 | selectedKeys,
|
78 | openKeys,
|
79 | activeKey: { '0-menu-': getActiveKey(props, props.activeKey) },
|
80 | });
|
81 | }
|
82 |
|
83 | componentDidMount() {
|
84 | this.updateMiniStore();
|
85 | }
|
86 |
|
87 | componentDidUpdate() {
|
88 | this.updateMiniStore();
|
89 | }
|
90 |
|
91 | onSelect = (selectInfo) => {
|
92 | const props = this.props;
|
93 | if (props.selectable) {
|
94 |
|
95 | let selectedKeys = this.store.getState().selectedKeys;
|
96 | const selectedKey = selectInfo.key;
|
97 | if (props.multiple) {
|
98 | selectedKeys = selectedKeys.concat([selectedKey]);
|
99 | } else {
|
100 | selectedKeys = [selectedKey];
|
101 | }
|
102 | if (!('selectedKeys' in props)) {
|
103 | this.store.setState({
|
104 | selectedKeys,
|
105 | });
|
106 | }
|
107 | props.onSelect({
|
108 | ...selectInfo,
|
109 | selectedKeys,
|
110 | });
|
111 | }
|
112 | }
|
113 |
|
114 | onClick = (e) => {
|
115 | this.props.onClick(e);
|
116 | }
|
117 |
|
118 |
|
119 |
|
120 |
|
121 | onKeyDown = (e, callback) => {
|
122 | this.innerMenu.getWrappedInstance().onKeyDown(e, callback);
|
123 | }
|
124 |
|
125 | onOpenChange = (event) => {
|
126 | const props = this.props;
|
127 | const openKeys = this.store.getState().openKeys.concat();
|
128 | let changed = false;
|
129 | const processSingle = (e) => {
|
130 | let oneChanged = false;
|
131 | if (e.open) {
|
132 | oneChanged = openKeys.indexOf(e.key) === -1;
|
133 | if (oneChanged) {
|
134 | openKeys.push(e.key);
|
135 | }
|
136 | } else {
|
137 | const index = openKeys.indexOf(e.key);
|
138 | oneChanged = index !== -1;
|
139 | if (oneChanged) {
|
140 | openKeys.splice(index, 1);
|
141 | }
|
142 | }
|
143 | changed = changed || oneChanged;
|
144 | };
|
145 | if (Array.isArray(event)) {
|
146 |
|
147 | event.forEach(processSingle);
|
148 | } else {
|
149 | processSingle(event);
|
150 | }
|
151 | if (changed) {
|
152 | if (!('openKeys' in this.props)) {
|
153 | this.store.setState({ openKeys });
|
154 | }
|
155 | props.onOpenChange(openKeys);
|
156 | }
|
157 | }
|
158 |
|
159 | onDeselect = (selectInfo) => {
|
160 | const props = this.props;
|
161 | if (props.selectable) {
|
162 | const selectedKeys = this.store.getState().selectedKeys.concat();
|
163 | const selectedKey = selectInfo.key;
|
164 | const index = selectedKeys.indexOf(selectedKey);
|
165 | if (index !== -1) {
|
166 | selectedKeys.splice(index, 1);
|
167 | }
|
168 | if (!('selectedKeys' in props)) {
|
169 | this.store.setState({
|
170 | selectedKeys,
|
171 | });
|
172 | }
|
173 | props.onDeselect({
|
174 | ...selectInfo,
|
175 | selectedKeys,
|
176 | });
|
177 | }
|
178 | }
|
179 |
|
180 | getOpenTransitionName = () => {
|
181 | const props = this.props;
|
182 | let transitionName = props.openTransitionName;
|
183 | const animationName = props.openAnimation;
|
184 | if (!transitionName && typeof animationName === 'string') {
|
185 | transitionName = `${props.prefixCls}-open-${animationName}`;
|
186 | }
|
187 | return transitionName;
|
188 | }
|
189 |
|
190 | updateMiniStore() {
|
191 | if ('selectedKeys' in this.props) {
|
192 | this.store.setState({
|
193 | selectedKeys: this.props.selectedKeys || [],
|
194 | keyboard:this.props.keyboard||false
|
195 | });
|
196 | }
|
197 | if ('openKeys' in this.props) {
|
198 | this.store.setState({
|
199 | openKeys: this.props.openKeys || [],
|
200 | keyboard:this.props.keyboard||false
|
201 | });
|
202 | }
|
203 | }
|
204 | focus=()=>{
|
205 | fireKeyEvent(ReactDOM.findDOMNode(this.innerMenu),'keydown',40);
|
206 | this.props.onFocus&&this.props.onFocus();
|
207 | }
|
208 |
|
209 | render() {
|
210 | let { ...props } = this.props;
|
211 | props.className += ` ${props.prefixCls}-root`;
|
212 | props = {
|
213 | ...props,
|
214 | onClick: this.onClick,
|
215 | onOpenChange: this.onOpenChange,
|
216 | onDeselect: this.onDeselect,
|
217 | onSelect: this.onSelect,
|
218 | openTransitionName: this.getOpenTransitionName(),
|
219 | parentMenu: this,
|
220 | };
|
221 | return (
|
222 | <Provider store={this.store}>
|
223 | <SubPopupMenu {...props} tabIndex='0' onFocus={this.focus} ref={c => this.innerMenu = c}>{this.props.children}</SubPopupMenu>
|
224 | </Provider>
|
225 | );
|
226 | }
|
227 | }
|
228 |
|
229 | export default Menu;
|