]> git.immae.eu Git - github/fretlink/time-picker.git/blob - src/Picker.jsx
release 0.1.0
[github/fretlink/time-picker.git] / src / Picker.jsx
1 import React, {PropTypes} from 'react';
2 import ReactDOM from 'react-dom';
3 import Trigger from 'rc-trigger';
4 import {createChainedFunction} from 'rc-util';
5 import placements from './util/placements';
6 import CommonMixin from './mixin/CommonMixin';
7
8 function noop() {
9 }
10
11 function refFn(field, component) {
12 this[field] = component;
13 }
14
15 const Picker = React.createClass({
16 propTypes: {
17 prefixCls: PropTypes.string,
18 panel: PropTypes.element,
19 children: PropTypes.func,
20 disabled: PropTypes.bool,
21 value: PropTypes.object,
22 open: PropTypes.bool,
23 align: PropTypes.object,
24 placement: PropTypes.any,
25 transitionName: PropTypes.string,
26 onChange: PropTypes.func,
27 onOpen: PropTypes.func,
28 onClose: PropTypes.func,
29 },
30
31 mixins: [CommonMixin],
32
33 getDefaultProps() {
34 return {
35 open: false,
36 align: {},
37 placement: 'bottomLeft',
38 onChange: noop,
39 onOpen: noop,
40 onClose: noop,
41 };
42 },
43
44 getInitialState() {
45 this.savePanelRef = refFn.bind(this, 'panelInstance');
46 const { open, value } = this.props;
47 return { open, value };
48 },
49
50 componentWillMount() {
51 document.addEventListener('click', this.handleDocumentClick, false);
52 },
53
54 componentWillReceiveProps(nextProps) {
55 const { value, open } = nextProps;
56 if (value !== undefined) {
57 this.setState({value});
58 }
59 if (open !== undefined) {
60 this.setState({open});
61 }
62 },
63
64 componentWillUnmount() {
65 document.removeEventListener('click', this.handleDocumentClick, false);
66 },
67
68 onPanelChange(value) {
69 const props = this.props;
70 this.setState({
71 value: value,
72 });
73 props.onChange(value);
74 },
75
76 onPanelClear() {
77 this.setOpen(false, this.focus);
78 },
79
80 onVisibleChange(open) {
81 this.setOpen(open, () => {
82 if (open) {
83 ReactDOM.findDOMNode(this.panelInstance).focus();
84 }
85 });
86 },
87
88 getPanelElement() {
89 const panel = this.props.panel;
90 const extraProps = {
91 ref: this.savePanelRef,
92 defaultValue: this.state.value || panel.props.defaultValue,
93 onChange: createChainedFunction(panel.props.onChange, this.onPanelChange),
94 onClear: createChainedFunction(panel.props.onClear, this.onPanelClear),
95 };
96
97 return React.cloneElement(panel, extraProps);
98 },
99
100 setOpen(open, callback) {
101 const {onOpen, onClose} = this.props;
102 if (this.state.open !== open) {
103 this.setState({
104 open: open,
105 }, callback);
106 const event = {
107 open: open,
108 };
109 if (open) {
110 onOpen(event);
111 } else {
112 onClose(event);
113 }
114 }
115 },
116
117 handleDocumentClick(event) {
118 // hide popup when click outside
119 if (this.state.open && ReactDOM.findDOMNode(this.panelInstance).contains(event.target)) {
120 return;
121 }
122 this.setState({
123 open: false,
124 });
125 },
126
127 focus() {
128 if (!this.state.open) {
129 ReactDOM.findDOMNode(this).focus();
130 }
131 },
132
133 render() {
134 const state = this.state;
135 const props = this.props;
136 const { prefixCls, placement, align, disabled, transitionName, children } = props;
137 return (
138 <Trigger
139 prefixCls={prefixCls}
140 popup={this.getPanelElement()}
141 popupAlign={align}
142 builtinPlacements={placements}
143 popupPlacement={placement}
144 action={disabled ? [] : ['click']}
145 destroyPopupOnHide
146 popupTransitionName={transitionName}
147 popupVisible={state.open}
148 onPopupVisibleChange={this.onVisibleChange}
149 >
150 <span className={`${prefixCls}-picker`}>
151 {children(state, props)}
152 </span>
153 </Trigger>
154 );
155 },
156 });
157
158 export default Picker;