Skip to content

Commit 1dd5ba7

Browse files
Martin Konicekfacebook-github-bot-3
authored andcommitted
Add a cross-platform Picker
Summary: The basic API is consistent with iOS; there are several platform-specific props. Also fixed the flickering when a value is selected. public Reviewed By: bestander Differential Revision: D2871092 fb-gh-sync-id: f5cdf6858cb7344b28ee46954cb6b0a3b144b646
1 parent c33e852 commit 1dd5ba7

File tree

8 files changed

+373
-280
lines changed

8 files changed

+373
-280
lines changed

Examples/UIExplorer/PickerAndroidExample.js

Lines changed: 64 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,21 @@
1616
'use strict';
1717

1818
const React = require('react-native');
19+
const StyleSheet = require('StyleSheet');
1920
const UIExplorerBlock = require('UIExplorerBlock');
2021
const UIExplorerPage = require('UIExplorerPage');
2122

2223
const {
23-
PickerAndroid,
24+
Picker,
2425
Text,
2526
TouchableWithoutFeedback,
2627
} = React;
27-
const Item = PickerAndroid.Item;
28+
const Item = Picker.Item;
2829

29-
const PickerAndroidExample = React.createClass({
30+
const PickerExample = React.createClass({
3031

3132
statics: {
32-
title: '<PickerAndroid>',
33+
title: '<Picker>',
3334
description: 'Provides multiple options to choose from, using either a dropdown menu or a dialog.',
3435
},
3536

@@ -38,103 +39,102 @@ const PickerAndroidExample = React.createClass({
3839
selected1: 'key1',
3940
selected2: 'key1',
4041
selected3: 'key1',
41-
selected4: 'key1',
4242
color: 'red',
43-
mode: PickerAndroid.MODE_DIALOG,
43+
mode: Picker.MODE_DIALOG,
4444
};
4545
},
4646

4747
displayName: 'Android Picker',
4848

4949
render: function() {
5050
return (
51-
<UIExplorerPage title="<PickerAndroid>">
51+
<UIExplorerPage title="<Picker>">
5252
<UIExplorerBlock title="Basic Picker">
53-
<PickerAndroid
54-
style={{width: 100, height: 56}}
55-
onSelect={this.onSelect.bind(this, 'selected1')}>
56-
<Item text="hello" value="key0" selected={this.state.selected1 === 'key0'} />
57-
<Item text="world" value="key1" selected={this.state.selected1 === 'key1'} />
58-
</PickerAndroid>
53+
<Picker
54+
style={styles.picker}
55+
selectedValue={this.state.selected1}
56+
onValueChange={this.onValueChange.bind(this, 'selected1')}>
57+
<Item label="hello" value="key0" />
58+
<Item label="world" value="key1" />
59+
</Picker>
5960
</UIExplorerBlock>
6061
<UIExplorerBlock title="Disabled picker">
61-
<PickerAndroid style={{width: 100, height: 56}} enabled={false}>
62-
<Item text="hello" value="key0" selected={this.state.selected1 === 'key0'} />
63-
<Item text="world" value="key1" selected={this.state.selected1 === 'key1'} />
64-
</PickerAndroid>
62+
<Picker style={styles.picker} enabled={false} selectedValue={this.state.selected1}>
63+
<Item label="hello" value="key0" />
64+
<Item label="world" value="key1" />
65+
</Picker>
6566
</UIExplorerBlock>
6667
<UIExplorerBlock title="Dropdown Picker">
67-
<PickerAndroid
68-
style={{width: 100, height: 56}}
69-
onSelect={this.onSelect.bind(this, 'selected2')}
68+
<Picker
69+
style={styles.picker}
70+
selectedValue={this.state.selected2}
71+
onValueChange={this.onValueChange.bind(this, 'selected2')}
7072
mode="dropdown">
71-
<Item text="hello" value="key0" selected={this.state.selected2 === 'key0'} />
72-
<Item text="world" value="key1" selected={this.state.selected2 === 'key1'} />
73-
</PickerAndroid>
74-
</UIExplorerBlock>
75-
<UIExplorerBlock title="Alternating Picker">
76-
<PickerAndroid
77-
style={{width: 100, height: 56}}
78-
onSelect={this.onSelect.bind(this, 'selected3')}
79-
mode={this.state.mode}>
80-
<Item text="hello" value="key0" selected={this.state.selected3 === 'key0'} />
81-
<Item text="world" value="key1" selected={this.state.selected3 === 'key1'} />
82-
</PickerAndroid>
83-
<TouchableWithoutFeedback onPress={this.changeMode}>
84-
<Text>Tap here to switch between dialog/dropdown.</Text>
85-
</TouchableWithoutFeedback>
73+
<Item label="hello" value="key0" />
74+
<Item label="world" value="key1" />
75+
</Picker>
8676
</UIExplorerBlock>
8777
<UIExplorerBlock title="Picker with prompt message">
88-
<PickerAndroid
89-
style={{width: 100, height: 56}}
90-
onSelect={this.onSelect.bind(this, 'selected4')}
78+
<Picker
79+
style={styles.picker}
80+
selectedValue={this.state.selected3}
81+
onValueChange={this.onValueChange.bind(this, 'selected3')}
9182
prompt="Pick one, just one">
92-
<Item text="hello" value="key0" selected={this.state.selected4 === 'key0'} />
93-
<Item text="world" value="key1" selected={this.state.selected4 === 'key1'} />
94-
</PickerAndroid>
83+
<Item label="hello" value="key0" />
84+
<Item label="world" value="key1" />
85+
</Picker>
9586
</UIExplorerBlock>
9687
<UIExplorerBlock title="Picker with no listener">
97-
<PickerAndroid style={{width: 100, height: 56}}>
98-
<Item text="hello" value="key0" />
99-
<Item text="world" value="key1" />
100-
</PickerAndroid>
88+
<Picker style={styles.picker}>
89+
<Item label="hello" value="key0" />
90+
<Item label="world" value="key1" />
91+
</Picker>
10192
<Text>
102-
You can not change the value of this picker because it doesn't set a selected prop on
103-
its items.
93+
Cannot change the value of this picker because it doesn't update selectedValue.
10494
</Text>
10595
</UIExplorerBlock>
10696
<UIExplorerBlock title="Colorful pickers">
107-
<PickerAndroid style={{width: 100, height: 56, color: 'black'}}
108-
onSelect={this.onSelect.bind(this, 'color')}
97+
<Picker
98+
style={[styles.picker, {color: 'white', backgroundColor: '#333'}]}
99+
selectedValue={this.state.color}
100+
onValueChange={this.onValueChange.bind(this, 'color')}
109101
mode="dropdown">
110-
<Item text="red" color="red" value="red" selected={this.state.color === 'red'}/>
111-
<Item text="green" color="green" value="green" selected={this.state.color === 'green'}/>
112-
<Item text="blue" color="blue" value="blue" selected={this.state.color === 'blue'}/>
113-
</PickerAndroid>
114-
<PickerAndroid style={{width: 100, height: 56}}
115-
onSelect={this.onSelect.bind(this, 'color')}
102+
<Item label="red" color="red" value="red" />
103+
<Item label="green" color="green" value="green" />
104+
<Item label="blue" color="blue" value="blue" />
105+
</Picker>
106+
<Picker
107+
style={styles.picker}
108+
selectedValue={this.state.color}
109+
onValueChange={this.onValueChange.bind(this, 'color')}
116110
mode="dialog">
117-
<Item text="red" color="red" value="red" selected={this.state.color === 'red'}/>
118-
<Item text="green" color="green" value="green" selected={this.state.color === 'green'}/>
119-
<Item text="blue" color="blue" value="blue" selected={this.state.color === 'blue'} />
120-
</PickerAndroid>
111+
<Item label="red" color="red" value="red" />
112+
<Item label="green" color="green" value="green" />
113+
<Item label="blue" color="blue" value="blue" />
114+
</Picker>
121115
</UIExplorerBlock>
122116
</UIExplorerPage>
123117
);
124118
},
125119

126120
changeMode: function() {
127-
const newMode = this.state.mode === PickerAndroid.MODE_DIALOG
128-
? PickerAndroid.MODE_DROPDOWN
129-
: PickerAndroid.MODE_DIALOG;
121+
const newMode = this.state.mode === Picker.MODE_DIALOG
122+
? Picker.MODE_DROPDOWN
123+
: Picker.MODE_DIALOG;
130124
this.setState({mode: newMode});
131125
},
132126

133-
onSelect: function(key: string, value: string) {
127+
onValueChange: function(key: string, value: string) {
134128
const newState = {};
135129
newState[key] = value;
136130
this.setState(newState);
137131
},
138132
});
139133

140-
module.exports = PickerAndroidExample;
134+
var styles = StyleSheet.create({
135+
picker: {
136+
width: 100,
137+
},
138+
});
139+
140+
module.exports = PickerExample;
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
* @providesModule Picker
10+
* @flow
11+
*/
12+
13+
'use strict';
14+
15+
var ColorPropType = require('ColorPropType');
16+
var PickerIOS = require('PickerIOS');
17+
var PickerAndroid = require('PickerAndroid');
18+
var Platform = require('Platform');
19+
var React = require('React');
20+
var StyleSheet = require('StyleSheet');
21+
var StyleSheetPropType = require('StyleSheetPropType');
22+
var TextStylePropTypes = require('TextStylePropTypes');
23+
var UnimplementedView = require('UnimplementedView');
24+
var View = require('View');
25+
var ViewStylePropTypes = require('ViewStylePropTypes');
26+
27+
var itemStylePropType = StyleSheetPropType(TextStylePropTypes);
28+
29+
var pickerStyleType = StyleSheetPropType({
30+
...ViewStylePropTypes,
31+
color: ColorPropType,
32+
});
33+
34+
var MODE_DIALOG = 'dialog';
35+
var MODE_DROPDOWN = 'dropdown';
36+
37+
/**
38+
* Renders the native picker component on iOS and Android. Example:
39+
*
40+
* <Picker
41+
* selectedValue={this.state.language}
42+
* onValueChange={(lang) => this.setState({language: lang})}>
43+
* <Picker.Item label="Java" value="java" />
44+
* <Picker.Item label="JavaScript" value="js" />
45+
* </Picker>
46+
*/
47+
var Picker = React.createClass({
48+
49+
statics: {
50+
/**
51+
* On Android, display the options in a dialog.
52+
*/
53+
MODE_DIALOG: MODE_DIALOG,
54+
/**
55+
* On Android, display the options in a dropdown (this is the default).
56+
*/
57+
MODE_DROPDOWN: MODE_DROPDOWN,
58+
},
59+
60+
getDefaultProps: function() {
61+
return {
62+
mode: MODE_DIALOG,
63+
};
64+
},
65+
66+
propTypes: {
67+
...View.propTypes,
68+
style: pickerStyleType,
69+
/**
70+
* Value matching value of one of the items. Can be a string or an integer.
71+
*/
72+
selectedValue: React.PropTypes.any,
73+
/**
74+
* Callback for when an item is selected. This is called with the following parameters:
75+
* - `itemValue`: the `value` prop of the item that was selected
76+
* - `itemPosition`: the index of the selected item in this picker
77+
*/
78+
onValueChange: React.PropTypes.func,
79+
/**
80+
* If set to false, the picker will be disabled, i.e. the user will not be able to make a
81+
* selection.
82+
* @platform android
83+
*/
84+
enabled: React.PropTypes.bool,
85+
/**
86+
* On Android, specifies how to display the selection items when the user taps on the picker:
87+
* - 'dialog': Show a modal dialog. This is the default.
88+
* - 'dropdown': Shows a dropdown anchored to the picker view
89+
*
90+
* @platform android
91+
*/
92+
mode: React.PropTypes.oneOf([MODE_DIALOG, MODE_DROPDOWN]),
93+
/**
94+
* Style to apply to each of the item labels.
95+
* @platform ios
96+
*/
97+
itemStyle: itemStylePropType,
98+
/**
99+
* Prompt string for this picker, used on Android in dialog mode as the title of the dialog.
100+
* @platform android
101+
*/
102+
prompt: React.PropTypes.string,
103+
/**
104+
* Used to locate this view in end-to-end tests.
105+
*/
106+
testID: React.PropTypes.string,
107+
},
108+
109+
render: function() {
110+
if (Platform.OS === 'ios') {
111+
return <PickerIOS {...this.props}>{this.props.children}</PickerIOS>;
112+
} else if (Platform.OS === 'android') {
113+
return <PickerAndroid {...this.props}>{this.props.children}</PickerAndroid>;
114+
} else {
115+
return <UnimplementedView />;
116+
}
117+
},
118+
});
119+
120+
/**
121+
* Individual selectable item in a Picker.
122+
*/
123+
Picker.Item = React.createClass({
124+
125+
propTypes: {
126+
/**
127+
* Text to display for this item.
128+
*/
129+
label: React.PropTypes.string.isRequired,
130+
/**
131+
* The value to be passed to picker's `onValueChange` callback when
132+
* this item is selected. Can be a string or an integer.
133+
*/
134+
value: React.PropTypes.any,
135+
/**
136+
* Color of this item's text.
137+
* @platform android
138+
*/
139+
color: ColorPropType,
140+
/**
141+
* Used to locate the item in end-to-end tests.
142+
*/
143+
testID: React.PropTypes.string,
144+
},
145+
146+
render: function() {
147+
// The items are not rendered directly
148+
throw null;
149+
},
150+
});
151+
152+
module.exports = Picker;

0 commit comments

Comments
 (0)