UNPKG

20.8 kBJavaScriptView Raw
1'use strict';
2
3exports.__esModule = true;
4exports.default = undefined;
5
6var _extends2 = require('babel-runtime/helpers/extends');
7
8var _extends3 = _interopRequireDefault(_extends2);
9
10var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
11
12var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
13
14var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
15
16var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
17
18var _inherits2 = require('babel-runtime/helpers/inherits');
19
20var _inherits3 = _interopRequireDefault(_inherits2);
21
22var _class, _temp;
23
24var _react = require('react');
25
26var _react2 = _interopRequireDefault(_react);
27
28var _reactDom = require('react-dom');
29
30var _propTypes = require('prop-types');
31
32var _propTypes2 = _interopRequireDefault(_propTypes);
33
34var _classnames = require('classnames');
35
36var _classnames2 = _interopRequireDefault(_classnames);
37
38var _util = require('../util');
39
40var _menu = require('../menu');
41
42var _menu2 = _interopRequireDefault(_menu);
43
44var _overlay = require('../overlay');
45
46var _overlay2 = _interopRequireDefault(_overlay);
47
48var _zhCn = require('../locale/zh-cn');
49
50var _zhCn2 = _interopRequireDefault(_zhCn);
51
52var _dataStore = require('./data-store');
53
54var _dataStore2 = _interopRequireDefault(_dataStore);
55
56var _virtualList = require('../virtual-list');
57
58var _virtualList2 = _interopRequireDefault(_virtualList);
59
60var _util2 = require('./util');
61
62function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
63
64var Popup = _overlay2.default.Popup;
65var MenuItem = _menu2.default.Item,
66 MenuGroup = _menu2.default.Group;
67var noop = _util.func.noop,
68 bindCtx = _util.func.bindCtx,
69 makeChain = _util.func.makeChain;
70
71
72function preventDefault(e) {
73 e.preventDefault();
74}
75
76var Base = (_temp = _class = function (_React$Component) {
77 (0, _inherits3.default)(Base, _React$Component);
78
79 function Base(props) {
80 (0, _classCallCheck3.default)(this, Base);
81
82 var _this = (0, _possibleConstructorReturn3.default)(this, _React$Component.call(this, props));
83
84 _this.saveSelectRef = function (ref) {
85 _this.selectDOM = (0, _reactDom.findDOMNode)(ref);
86 };
87
88 _this.saveInputRef = function (ref) {
89 if (ref && ref.getInstance()) {
90 _this.inputRef = ref.getInstance();
91 }
92 };
93
94 _this.savePopupRef = function (ref) {
95 _this.popupRef = ref;
96 if (_this.props.popupProps && typeof _this.props.popupProps.ref === 'function') {
97 _this.props.popupProps.ref(ref);
98 }
99 };
100
101 _this.dataStore = new _dataStore2.default({
102 filter: props.filter,
103 filterLocal: props.filterLocal
104 });
105
106 _this.state = {
107 value: 'value' in props ? props.value : props.defaultValue,
108 visible: 'visible' in props ? props.visible : props.defaultVisible,
109 dataSource: [],
110 width: 100,
111 // current highlight key
112 highlightKey: null,
113 srReader: ''
114 };
115
116 bindCtx(_this, ['handleMenuBodyClick', 'handleVisibleChange', 'focusInput', 'beforeOpen', 'beforeClose', 'afterClose', 'handleResize']);
117 return _this;
118 }
119
120 Base.prototype.componentWillMount = function componentWillMount() {
121 this.setState({
122 dataSource: this.setDataSource(this.props)
123 });
124 };
125
126 Base.prototype.componentDidMount = function componentDidMount() {
127 var _this2 = this;
128
129 // overlay 还没有完成 mount,所以需要滞后同步宽度
130 setTimeout(function () {
131 return _this2.syncWidth();
132 }, 0);
133
134 _util.events.on(window, 'resize', this.handleResize);
135 };
136
137 Base.prototype.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {
138 if (prevProps.label !== this.props.label || prevState.value !== this.state.value) {
139 this.syncWidth();
140 }
141 };
142
143 Base.prototype.componentWillUnmount = function componentWillUnmount() {
144 _util.events.off(window, 'resize', this.handleResize);
145 clearTimeout(this.resizeTimeout);
146 };
147
148 /**
149 * Calculate and set width of popup menu
150 * @protected
151 */
152
153
154 Base.prototype.syncWidth = function syncWidth() {
155 var _this3 = this;
156
157 var width = _util.dom.getStyle(this.selectDOM, 'width');
158
159 if (width && this.width !== width) {
160 this.width = width;
161
162 if (this.popupRef && this.props.autoWidth) {
163 // overy 的 node 节点可能没有挂载完成,所以这里需要异步
164 setTimeout(function () {
165 if (_this3.popupRef && _this3.popupRef.getInstance().overlay) {
166 _util.dom.setStyle(_this3.popupRef.getInstance().overlay.getInstance().getContentNode(), 'width', _this3.width);
167 }
168 }, 0);
169 }
170 }
171 };
172
173 Base.prototype.handleResize = function handleResize() {
174 var _this4 = this;
175
176 clearTimeout(this.resizeTimeout);
177 if (this.state.visible) {
178 this.resizeTimeout = setTimeout(function () {
179 _this4.syncWidth();
180 }, 200);
181 }
182 };
183
184 /**
185 * Get structured dataSource, for cache
186 * @protected
187 * @param {Object} [props=this.props]
188 * @return {Array}
189 */
190
191
192 Base.prototype.setDataSource = function setDataSource(props) {
193 var dataSource = props.dataSource,
194 children = props.children;
195
196 // children is higher priority then dataSource
197
198 if (_react.Children.count(children)) {
199 return this.dataStore.updateByDS(children, true);
200 } else if (Array.isArray(dataSource)) {
201 return this.dataStore.updateByDS(dataSource, false);
202 }
203 return [];
204 };
205
206 /**
207 * Set popup visible
208 * @protected
209 * @param {boolean} visible
210 * @param {string} type trigger type
211 */
212
213
214 Base.prototype.setVisible = function setVisible(visible, type) {
215 if (this.props.disabled || this.state.visible === visible) {
216 return;
217 }
218
219 if (!('visible' in this.props)) {
220 this.setState({
221 visible: visible
222 });
223 }
224
225 this.props.onVisibleChange(visible, type);
226 };
227
228 Base.prototype.setFirstHightLightKeyForMenu = function setFirstHightLightKeyForMenu() {
229 // 设置高亮 item key
230 if (this.dataStore.getMenuDS().length && this.dataStore.getEnableDS().length) {
231 this.setState({
232 highlightKey: '' + this.dataStore.getEnableDS()[0].value
233 });
234 }
235 };
236
237 Base.prototype.handleChange = function handleChange(value) {
238 var _props2;
239
240 // 非受控模式清空内部数据
241 if (!('value' in this.props)) {
242 this.setState({
243 value: value
244 });
245 }
246
247 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
248 args[_key - 1] = arguments[_key];
249 }
250
251 (_props2 = this.props).onChange.apply(_props2, [value].concat(args));
252 };
253
254 /**
255 * Handle Menu body click
256 * @param {Event} e click event
257 */
258
259
260 Base.prototype.handleMenuBodyClick = function handleMenuBodyClick(e) {
261 this.focusInput(e);
262 };
263
264 /**
265 * Toggle highlight MenuItem
266 * @private
267 * @param {number} dir -1: up, 1: down
268 */
269
270
271 Base.prototype.toggleHighlightItem = function toggleHighlightItem(dir) {
272 if (!this.state.visible) {
273 this.setVisible(true, 'enter');
274 return;
275 }
276
277 var maxCount = this.dataStore.getEnableDS().length;
278 // When there is no enabled item
279 if (!maxCount) {
280 return false;
281 }
282
283 var highlightKey = this.state.highlightKey;
284
285 var highlightIndex = -1;
286
287 // find previous highlight index
288 highlightKey !== null && this.dataStore.getEnableDS().some(function (item, index) {
289 if ('' + item.value === highlightKey) {
290 highlightIndex = index;
291 }
292 return highlightIndex > -1;
293 });
294
295 // toggle highlight index
296 highlightIndex += dir;
297 if (highlightIndex < 0) {
298 highlightIndex = maxCount - 1;
299 }
300 if (highlightIndex >= maxCount) {
301 highlightIndex = 0;
302 }
303
304 // get highlight key
305 var highlightItem = this.dataStore.getEnableDS()[highlightIndex];
306 highlightKey = highlightItem ? '' + highlightItem.value : null;
307
308 this.setState({ highlightKey: highlightKey, srReader: highlightItem.label });
309
310 this.scrollMenuIntoView();
311
312 return highlightItem;
313 };
314
315 // scroll into focus item
316
317
318 Base.prototype.scrollMenuIntoView = function scrollMenuIntoView() {
319 var _this5 = this;
320
321 var prefix = this.props.prefix;
322
323
324 clearTimeout(this.highlightTimer);
325 this.highlightTimer = setTimeout(function () {
326 try {
327 var menuNode = (0, _reactDom.findDOMNode)(_this5.menuRef);
328 var itemNode = menuNode.querySelector('.' + prefix + 'select-menu-item.' + prefix + 'focused');
329 itemNode && itemNode.scrollIntoViewIfNeeded();
330 } catch (ex) {
331 // I don't care...
332 }
333 });
334 };
335
336 /**
337 * render popup menu header
338 * @abstract
339 */
340
341
342 Base.prototype.renderMenuHeader = function renderMenuHeader() {
343 return null;
344 };
345
346 Base.prototype.handleSelect = function handleSelect() {};
347
348 /**
349 * render popup children
350 * @protected
351 * @param {object} props
352 */
353
354
355 Base.prototype.renderMenu = function renderMenu() {
356 var _classNames,
357 _this6 = this;
358
359 var _props3 = this.props,
360 prefix = _props3.prefix,
361 mode = _props3.mode,
362 autoWidth = _props3.autoWidth,
363 locale = _props3.locale,
364 notFoundContent = _props3.notFoundContent,
365 useVirtual = _props3.useVirtual;
366 var _state = this.state,
367 dataSource = _state.dataSource,
368 highlightKey = _state.highlightKey;
369
370 var value = this.state.value;
371 var selectedKeys = void 0;
372
373 if ((0, _util2.isNull)(value) || value.length === 0) {
374 selectedKeys = [];
375 } else if ((0, _util2.isSingle)(mode)) {
376 selectedKeys = [(0, _util2.valueToSelectKey)(value)];
377 } else {
378 selectedKeys = [].concat(value).map(function (n) {
379 return (0, _util2.valueToSelectKey)(n);
380 });
381 }
382
383 var children = this.renderMenuItem(dataSource);
384
385 var menuClassName = (0, _classnames2.default)((_classNames = {}, _classNames[prefix + 'select-menu'] = true, _classNames[prefix + 'select-menu-empty'] = !children || !children.length, _classNames));
386
387 if (!children || !children.length) {
388 children = _react2.default.createElement(
389 'span',
390 { className: prefix + 'select-menu-empty-content' },
391 notFoundContent || locale.notFoundContent
392 );
393 }
394
395 var menuProps = {
396 children: children,
397 role: 'listbox',
398 style: autoWidth ? { width: this.width } : { minWidth: this.width },
399 selectedKeys: selectedKeys,
400 focusedKey: highlightKey,
401 focusable: false,
402 selectMode: (0, _util2.isSingle)(mode) ? 'single' : 'multiple',
403 onSelect: this.handleMenuSelect,
404 onItemClick: this.handleItemClick,
405 header: this.renderMenuHeader(),
406 onClick: this.handleMenuBodyClick,
407 onMouseDown: preventDefault,
408 className: menuClassName
409 };
410
411 return useVirtual && children.length ? _react2.default.createElement(
412 'div',
413 {
414 className: prefix + 'select-menu-wrapper',
415 style: { position: 'relative' }
416 },
417 _react2.default.createElement(
418 _virtualList2.default,
419 {
420 itemsRenderer: function itemsRenderer(items, _ref) {
421 return _react2.default.createElement(
422 _menu2.default,
423 (0, _extends3.default)({
424 ref: function ref(c) {
425 _ref(c);
426 _this6.menuRef = c;
427 }
428 }, menuProps),
429 items
430 );
431 }
432 },
433 children
434 )
435 ) : _react2.default.createElement(_menu2.default, menuProps);
436 };
437
438 /**
439 * render menu item
440 * @protected
441 * @param {Array} dataSource
442 */
443
444
445 Base.prototype.renderMenuItem = function renderMenuItem(dataSource) {
446 var _this7 = this;
447
448 var _props4 = this.props,
449 prefix = _props4.prefix,
450 itemRender = _props4.itemRender;
451 // If it has.
452
453 var searchKey = void 0;
454 if (this.isAutoComplete) {
455 // In AutoComplete, value is the searchKey
456 searchKey = this.state.value;
457 } else {
458 searchKey = this.state.searchValue;
459 }
460
461 return dataSource.map(function (item, index) {
462 if (!item) {
463 return null;
464 }
465 if (Array.isArray(item.children)) {
466 return _react2.default.createElement(
467 MenuGroup,
468 { key: index, label: item.label },
469 _this7.renderMenuItem(item.children)
470 );
471 } else {
472 var itemProps = {
473 role: 'option',
474 key: item.value,
475 className: prefix + 'select-menu-item',
476 disabled: item.disabled
477 };
478 if (item.title) {
479 itemProps.title = item.title;
480 }
481
482 return _react2.default.createElement(
483 MenuItem,
484 itemProps,
485 itemRender(item, searchKey)
486 );
487 }
488 });
489 };
490
491 /**
492 * 点击 arrow 或 label 的时候焦点切到 input 中
493 * @override
494 */
495 Base.prototype.focusInput = function focusInput() {
496 this.inputRef.focus();
497 };
498
499 Base.prototype.beforeOpen = function beforeOpen() {
500 var _state2 = this.state,
501 value = _state2.value,
502 highlightKey = _state2.highlightKey;
503
504 if (this.props.mode === 'single' && !value && !highlightKey) {
505 this.setFirstHightLightKeyForMenu();
506 }
507 this.syncWidth();
508 };
509
510 Base.prototype.beforeClose = function beforeClose() {};
511
512 Base.prototype.afterClose = function afterClose() {};
513
514 Base.prototype.render = function render(props) {
515 var _classNames2;
516
517 var prefix = props.prefix,
518 mode = props.mode,
519 popupProps = props.popupProps,
520 popupContainer = props.popupContainer,
521 popupClassName = props.popupClassName,
522 popupStyle = props.popupStyle,
523 popupContent = props.popupContent,
524 autoWidth = props.autoWidth,
525 canCloseByTrigger = props.canCloseByTrigger,
526 followTrigger = props.followTrigger,
527 cache = props.cache;
528
529
530 var cls = (0, _classnames2.default)((_classNames2 = {}, _classNames2[prefix + 'select-auto-complete-menu'] = !popupContent && this.isAutoComplete, _classNames2[prefix + 'select-' + mode + '-menu'] = !popupContent && !!mode, _classNames2), popupClassName || popupProps.className);
531
532 var _props = (0, _extends3.default)({
533 triggerType: 'click',
534 autoFocus: false,
535 cache: cache
536 }, popupProps, {
537 //beforeOpen node not mount, afterOpen too slow.
538 // from display:none to block, we may need to recompute width
539 beforeOpen: makeChain(this.beforeOpen, popupProps.beforeOpen),
540 beforeClose: makeChain(this.beforeClose, popupProps.beforeClose),
541 afterClose: makeChain(this.afterClose, popupProps.afterClose),
542 canCloseByTrigger: canCloseByTrigger,
543 followTrigger: followTrigger,
544 visible: this.state.visible,
545 onVisibleChange: this.handleVisibleChange,
546 shouldUpdatePosition: true,
547 container: popupContainer || popupProps.container,
548 className: cls,
549 style: popupStyle || popupProps.style
550 });
551
552 return _react2.default.createElement(
553 Popup,
554 (0, _extends3.default)({}, _props, {
555 trigger: this.renderSelect(),
556 ref: this.savePopupRef
557 }),
558 popupContent ? _react2.default.createElement(
559 'div',
560 {
561 className: prefix + 'select-popup-wrap',
562 style: autoWidth ? { width: this.width } : {}
563 },
564 popupContent
565 ) : this.renderMenu()
566 );
567 };
568
569 return Base;
570}(_react2.default.Component), _class.propTypes = {
571 prefix: _propTypes2.default.string,
572 /**
573 * 选择器尺寸
574 */
575 size: _propTypes2.default.oneOf(['small', 'medium', 'large']),
576 // 当前值,用于受控模式
577 value: _propTypes2.default.any, // to be override
578 // 初始化的默认值
579 defaultValue: _propTypes2.default.any, // to be override
580 /**
581 * 没有值的时候的占位符
582 */
583 placeholder: _propTypes2.default.string,
584 /**
585 * 下拉菜单是否与选择器对齐
586 */
587 autoWidth: _propTypes2.default.bool,
588 /**
589 * 自定义内联 label
590 */
591 label: _propTypes2.default.node,
592 /**
593 * 是否有清除按钮(单选模式有效)
594 */
595 hasClear: _propTypes2.default.bool,
596 /**
597 * 校验状态
598 */
599 state: _propTypes2.default.oneOf(['error', 'loading']),
600 /**
601 * 是否只读,只读模式下可以展开弹层但不能选
602 */
603 readOnly: _propTypes2.default.bool,
604 /**
605 * 是否禁用选择器
606 */
607 disabled: _propTypes2.default.bool,
608 /**
609 * 当前弹层是否显示
610 */
611 visible: _propTypes2.default.bool,
612 /**
613 * 弹层初始化是否显示
614 */
615 defaultVisible: _propTypes2.default.bool,
616 /**
617 * 弹层显示或隐藏时触发的回调
618 * @param {Boolean} visible 弹层是否显示
619 */
620 onVisibleChange: _propTypes2.default.func,
621 /**
622 * 弹层挂载的容器节点
623 */
624 popupContainer: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.func]),
625 /**
626 * 弹层的 className
627 */
628 popupClassName: _propTypes2.default.any,
629 /**
630 * 弹层的内联样式
631 */
632 popupStyle: _propTypes2.default.object,
633 /**
634 * 添加到弹层上的属性
635 */
636 popupProps: _propTypes2.default.object,
637 /**
638 * 自定义弹层的内容
639 */
640 popupContent: _propTypes2.default.node,
641 /**
642 * 是否使用本地过滤,在数据源为远程的时候需要关闭此项
643 */
644 filterLocal: _propTypes2.default.bool,
645 /**
646 * 本地过滤方法,返回一个 Boolean 值确定是否保留
647 */
648 filter: _propTypes2.default.func,
649 /**
650 * 键盘上下键切换菜单高亮选项的回调
651 */
652 onToggleHighlightItem: _propTypes2.default.func,
653 /**
654 * 是否开启虚拟滚动模式
655 */
656 useVirtual: _propTypes2.default.bool,
657 // 自定义类名
658 className: _propTypes2.default.any,
659 children: _propTypes2.default.any,
660 dataSource: _propTypes2.default.array,
661 itemRender: _propTypes2.default.func,
662 mode: _propTypes2.default.string,
663 notFoundContent: _propTypes2.default.node,
664 locale: _propTypes2.default.object,
665 rtl: _propTypes2.default.bool
666}, _class.defaultProps = {
667 prefix: 'next-',
668 size: 'medium',
669 autoWidth: true,
670 onChange: noop,
671 onVisibleChange: noop,
672 onToggleHighlightItem: noop,
673 popupProps: {},
674 filterLocal: true,
675 filter: _util2.filter,
676 itemRender: function itemRender(item) {
677 return item.label || item.value;
678 },
679 locale: _zhCn2.default.Select
680}, _temp);
681Base.displayName = 'Base';
682exports.default = Base;
683module.exports = exports['default'];
\No newline at end of file