diff options
Diffstat (limited to 'src/Select.jsx')
-rw-r--r-- | src/Select.jsx | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/src/Select.jsx b/src/Select.jsx new file mode 100644 index 0000000..238a776 --- /dev/null +++ b/src/Select.jsx | |||
@@ -0,0 +1,108 @@ | |||
1 | import React, { PropTypes } from 'react'; | ||
2 | import ReactDom from 'react-dom'; | ||
3 | import classnames from 'classnames'; | ||
4 | |||
5 | const scrollTo = (element, to, duration) => { | ||
6 | const requestAnimationFrame = window.requestAnimationFrame || | ||
7 | function requestAnimationFrameTimeout() { | ||
8 | return setTimeout(arguments[0], 10); | ||
9 | }; | ||
10 | // jump to target if duration zero | ||
11 | if (duration <= 0) { | ||
12 | element.scrollTop = to; | ||
13 | return; | ||
14 | } | ||
15 | const difference = to - element.scrollTop; | ||
16 | const perTick = difference / duration * 10; | ||
17 | |||
18 | requestAnimationFrame(() => { | ||
19 | element.scrollTop = element.scrollTop + perTick; | ||
20 | if (element.scrollTop === to) return; | ||
21 | scrollTo(element, to, duration - 10); | ||
22 | }); | ||
23 | }; | ||
24 | |||
25 | const Select = React.createClass({ | ||
26 | propTypes: { | ||
27 | prefixCls: PropTypes.string, | ||
28 | options: PropTypes.array, | ||
29 | selectedIndex: PropTypes.number, | ||
30 | type: PropTypes.string, | ||
31 | onSelect: PropTypes.func, | ||
32 | onMouseEnter: PropTypes.func, | ||
33 | }, | ||
34 | |||
35 | componentDidMount() { | ||
36 | // jump to selected option | ||
37 | this.scrollToSelected(0); | ||
38 | }, | ||
39 | |||
40 | componentDidUpdate(prevProps) { | ||
41 | // smooth scroll to selected option | ||
42 | if (prevProps.selectedIndex !== this.props.selectedIndex) { | ||
43 | this.scrollToSelected(120); | ||
44 | } | ||
45 | }, | ||
46 | |||
47 | onSelect(value) { | ||
48 | const { onSelect, type } = this.props; | ||
49 | onSelect(type, value); | ||
50 | }, | ||
51 | |||
52 | getOptions() { | ||
53 | const { options, selectedIndex, prefixCls } = this.props; | ||
54 | return options.map((item, index) => { | ||
55 | const cls = classnames({ | ||
56 | [`${prefixCls}-select-option-selected`]: selectedIndex === index, | ||
57 | [`${prefixCls}-select-option-disabled`]: item.disabled, | ||
58 | }); | ||
59 | let onclick = null; | ||
60 | if (!item.disabled) { | ||
61 | onclick = this.onSelect.bind(this, +item.value); | ||
62 | } | ||
63 | return (<li | ||
64 | className={cls} | ||
65 | key={index} | ||
66 | onClick={onclick} | ||
67 | disabled={item.disabled} | ||
68 | > | ||
69 | {item.value} | ||
70 | </li>); | ||
71 | }); | ||
72 | }, | ||
73 | |||
74 | scrollToSelected(duration) { | ||
75 | // move to selected item | ||
76 | const select = ReactDom.findDOMNode(this); | ||
77 | const list = ReactDom.findDOMNode(this.refs.list); | ||
78 | if (!list) { | ||
79 | return; | ||
80 | } | ||
81 | let index = this.props.selectedIndex; | ||
82 | if (index < 0) { | ||
83 | index = 0; | ||
84 | } | ||
85 | const topOption = list.children[index]; | ||
86 | const to = topOption.offsetTop; | ||
87 | scrollTo(select, to, duration); | ||
88 | }, | ||
89 | |||
90 | render() { | ||
91 | if (this.props.options.length === 0) { | ||
92 | return null; | ||
93 | } | ||
94 | |||
95 | const { prefixCls } = this.props; | ||
96 | |||
97 | return ( | ||
98 | <div | ||
99 | className={`${prefixCls}-select`} | ||
100 | onMouseEnter={this.props.onMouseEnter} | ||
101 | > | ||
102 | <ul ref="list">{this.getOptions()}</ul> | ||
103 | </div> | ||
104 | ); | ||
105 | }, | ||
106 | }); | ||
107 | |||
108 | export default Select; | ||