-
Notifications
You must be signed in to change notification settings - Fork 24.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Open sourced spinner aka picker aka drop down for android
Reviewed By: mkonicek Differential Revision: D2830803 fb-gh-sync-id: e6b6fcdbe33d942180cf2c1041076ad71d0473ce
- Loading branch information
Showing
11 changed files
with
779 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/** | ||
* The examples provided by Facebook are for non-commercial testing and | ||
* evaluation purposes only. | ||
* | ||
* Facebook reserves all rights not expressly granted. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL | ||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | ||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
* | ||
* @flow | ||
*/ | ||
'use strict'; | ||
|
||
const React = require('react-native'); | ||
const UIExplorerBlock = require('UIExplorerBlock'); | ||
const UIExplorerPage = require('UIExplorerPage'); | ||
|
||
const { | ||
PickerAndroid, | ||
Text, | ||
TouchableWithoutFeedback, | ||
} = React; | ||
const Item = PickerAndroid.Item; | ||
|
||
const PickerAndroidExample = React.createClass({ | ||
getInitialState: function() { | ||
return { | ||
selected1: 'key1', | ||
selected2: 'key1', | ||
selected3: 'key1', | ||
selected4: 'key1', | ||
color: 'red', | ||
mode: PickerAndroid.MODE_DIALOG, | ||
}; | ||
}, | ||
|
||
displayName: 'Android Picker', | ||
|
||
render: function() { | ||
return ( | ||
<UIExplorerPage title="<PickerAndroid>"> | ||
<UIExplorerBlock title="Basic Picker"> | ||
<PickerAndroid | ||
style={{width: 100, height: 56}} | ||
onSelect={this.onSelect.bind(this, 'selected1')}> | ||
<Item text="hello" value="key0" selected={this.state.selected1 === 'key0'} /> | ||
<Item text="world" value="key1" selected={this.state.selected1 === 'key1'} /> | ||
</PickerAndroid> | ||
</UIExplorerBlock> | ||
<UIExplorerBlock title="Disabled picker"> | ||
<PickerAndroid style={{width: 100, height: 56}} enabled={false}> | ||
<Item text="hello" value="key0" selected={this.state.selected1 === 'key0'} /> | ||
<Item text="world" value="key1" selected={this.state.selected1 === 'key1'} /> | ||
</PickerAndroid> | ||
</UIExplorerBlock> | ||
<UIExplorerBlock title="Dropdown Picker"> | ||
<PickerAndroid | ||
style={{width: 100, height: 56}} | ||
onSelect={this.onSelect.bind(this, 'selected2')} | ||
mode="dropdown"> | ||
<Item text="hello" value="key0" selected={this.state.selected2 === 'key0'} /> | ||
<Item text="world" value="key1" selected={this.state.selected2 === 'key1'} /> | ||
</PickerAndroid> | ||
</UIExplorerBlock> | ||
<UIExplorerBlock title="Alternating Picker"> | ||
<PickerAndroid | ||
style={{width: 100, height: 56}} | ||
onSelect={this.onSelect.bind(this, 'selected3')} | ||
mode={this.state.mode}> | ||
<Item text="hello" value="key0" selected={this.state.selected3 === 'key0'} /> | ||
<Item text="world" value="key1" selected={this.state.selected3 === 'key1'} /> | ||
</PickerAndroid> | ||
<TouchableWithoutFeedback onPress={this.changeMode}> | ||
<Text>Tap here to switch between dialog/dropdown.</Text> | ||
</TouchableWithoutFeedback> | ||
</UIExplorerBlock> | ||
<UIExplorerBlock title="Picker with prompt message"> | ||
<PickerAndroid | ||
style={{width: 100, height: 56}} | ||
onSelect={this.onSelect.bind(this, 'selected4')} | ||
prompt="Pick one, just one"> | ||
<Item text="hello" value="key0" selected={this.state.selected4 === 'key0'} /> | ||
<Item text="world" value="key1" selected={this.state.selected4 === 'key1'} /> | ||
</PickerAndroid> | ||
</UIExplorerBlock> | ||
<UIExplorerBlock title="Picker with no listener"> | ||
<PickerAndroid style={{width: 100, height: 56}}> | ||
<Item text="hello" value="key0" /> | ||
<Item text="world" value="key1" /> | ||
</PickerAndroid> | ||
<Text> | ||
You can not change the value of this picker because it doesn't set a selected prop on | ||
its items. | ||
</Text> | ||
</UIExplorerBlock> | ||
<UIExplorerBlock title="Colorful pickers"> | ||
<PickerAndroid style={{width: 100, height: 56, color: 'black'}} | ||
onSelect={this.onSelect.bind(this, 'color')} | ||
mode="dropdown"> | ||
<Item text="red" color="red" value="red" selected={this.state.color === 'red'}/> | ||
<Item text="green" color="green" value="green" selected={this.state.color === 'green'}/> | ||
<Item text="blue" color="blue" value="blue" selected={this.state.color === 'blue'}/> | ||
</PickerAndroid> | ||
<PickerAndroid style={{width: 100, height: 56}} | ||
onSelect={this.onSelect.bind(this, 'color')} | ||
mode="dialog"> | ||
<Item text="red" color="red" value="red" selected={this.state.color === 'red'}/> | ||
<Item text="green" color="green" value="green" selected={this.state.color === 'green'}/> | ||
<Item text="blue" color="blue" value="blue" selected={this.state.color === 'blue'} /> | ||
</PickerAndroid> | ||
</UIExplorerBlock> | ||
</UIExplorerPage> | ||
); | ||
}, | ||
|
||
changeMode: function() { | ||
const newMode = this.state.mode === PickerAndroid.MODE_DIALOG | ||
? PickerAndroid.MODE_DROPDOWN | ||
: PickerAndroid.MODE_DIALOG; | ||
this.setState({mode: newMode}); | ||
}, | ||
|
||
onSelect: function(key, value) { | ||
const newState = {}; | ||
newState[key] = value; | ||
this.setState(newState); | ||
}, | ||
}); | ||
|
||
exports.title = '<PickerAndroid>'; | ||
exports.displayName = 'PickerAndroidExample'; | ||
exports.description = 'The Android Picker component provides multiple options to choose from'; | ||
exports.examples = [ | ||
{ | ||
title: 'PickerAndroidExample', | ||
render(): ReactElement { return <PickerAndroidExample />; } | ||
}, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
* | ||
* @providesModule PickerAndroid | ||
* @flow | ||
*/ | ||
|
||
'use strict'; | ||
|
||
var ColorPropType = require('ColorPropType'); | ||
var React = require('React'); | ||
var ReactChildren = require('ReactChildren'); | ||
var ReactPropTypes = require('ReactPropTypes'); | ||
var StyleSheetPropType = require('StyleSheetPropType'); | ||
var View = require('View'); | ||
var ViewStylePropTypes = require('ViewStylePropTypes'); | ||
|
||
var processColor = require('processColor'); | ||
var requireNativeComponent = require('requireNativeComponent'); | ||
|
||
var MODE_DIALOG = 'dialog'; | ||
var MODE_DROPDOWN = 'dropdown'; | ||
var REF_PICKER = 'picker'; | ||
|
||
var pickerStyleType = StyleSheetPropType({ | ||
...ViewStylePropTypes, | ||
color: ColorPropType, | ||
}); | ||
|
||
type Items = { | ||
selected: number; | ||
items: any[]; | ||
}; | ||
|
||
type Event = Object; | ||
|
||
/** | ||
* Individual selectable item in a Picker. | ||
*/ | ||
var Item = React.createClass({ | ||
|
||
propTypes: { | ||
/** | ||
* Color of this item's text. | ||
*/ | ||
color: ColorPropType, | ||
/** | ||
* Text to display for this item. | ||
*/ | ||
text: ReactPropTypes.string.isRequired, | ||
/** | ||
* The value to be passed to picker's `onSelect` callback when this item is selected. | ||
*/ | ||
value: ReactPropTypes.string, | ||
/** | ||
* If `true`, this item is selected and shown in the picker. | ||
* Usually this is set based on state. | ||
*/ | ||
selected: ReactPropTypes.bool, | ||
/** | ||
* Used to locate this view in end-to-end tests. | ||
*/ | ||
testID: ReactPropTypes.string, | ||
}, | ||
|
||
render: function() { | ||
throw new Error('Picker items should never be rendered'); | ||
}, | ||
|
||
}); | ||
|
||
/** | ||
* <PickerAndroid> - A React component that renders the native Picker widget on Android. The items | ||
* that can be selected are specified as children views of type Item. Example usage: | ||
* | ||
* <PickerAndroid> | ||
* <PickerAndroid.Item text="Java" value="js" /> | ||
* <PickerAndroid.Item text="JavaScript" value="java" selected={true} /> | ||
* </PickerAndroid> | ||
*/ | ||
var PickerAndroid = React.createClass({ | ||
|
||
propTypes: { | ||
...View.propTypes, | ||
style: pickerStyleType, | ||
/** | ||
* If set to false, the picker will be disabled, i.e. the user will not be able to make a | ||
* selection. | ||
*/ | ||
enabled: ReactPropTypes.bool, | ||
/** | ||
* Specifies how to display the selection items when the user taps on the picker: | ||
* | ||
* - dialog: Show a modal dialog | ||
* - dropdown: Shows a dropdown anchored to the picker view | ||
*/ | ||
mode: ReactPropTypes.oneOf([MODE_DIALOG, MODE_DROPDOWN]), | ||
/** | ||
* Callback for when an item is selected. This is called with the following parameters: | ||
* | ||
* - `itemValue`: the `value` prop of the item that was selected | ||
* - `itemPosition`: the index of the selected item in this picker | ||
*/ | ||
onSelect: ReactPropTypes.func, | ||
/** | ||
* Prompt string for this picker, currently only used in `dialog` mode as the title of the | ||
* dialog. | ||
*/ | ||
prompt: ReactPropTypes.string, | ||
/** | ||
* Used to locate this view in end-to-end tests. | ||
*/ | ||
testID: ReactPropTypes.string, | ||
}, | ||
|
||
statics: { | ||
Item: Item, | ||
MODE_DIALOG: MODE_DIALOG, | ||
MODE_DROPDOWN: MODE_DROPDOWN, | ||
}, | ||
|
||
getDefaultProps: function() { | ||
return { | ||
mode: MODE_DIALOG, | ||
}; | ||
}, | ||
|
||
render: function() { | ||
var Picker = this.props.mode === MODE_DROPDOWN ? DropdownPicker : DialogPicker; | ||
|
||
var { selected, items } = this._getItems(); | ||
|
||
var nativeProps = { | ||
enabled: this.props.enabled, | ||
items: items, | ||
mode: this.props.mode, | ||
onSelect: this._onSelect, | ||
prompt: this.props.prompt, | ||
selected: selected, | ||
style: this.props.style, | ||
testID: this.props.testID, | ||
}; | ||
|
||
return <Picker ref={REF_PICKER} {...nativeProps} />; | ||
}, | ||
|
||
/** | ||
* Transform this view's children into an array of items to be passed to the native component. | ||
* Since we're traversing the children, also determine the selected position. | ||
* | ||
* @returns an object with two keys: | ||
* | ||
* - `selected` (number) - the index of the selected item | ||
* - `items` (array) - the items of this picker, as an array of strings | ||
*/ | ||
_getItems: function(): Items { | ||
var items = []; | ||
var selected = 0; | ||
ReactChildren.forEach(this.props.children, function(child, index) { | ||
var childProps = Object.assign({}, child.props); | ||
if (childProps.color) { | ||
childProps.color = processColor(childProps.color); | ||
} | ||
items.push(childProps); | ||
if (childProps.selected) { | ||
selected = index; | ||
} | ||
}); | ||
return { | ||
selected: selected, | ||
items: items, | ||
}; | ||
}, | ||
|
||
_onSelect: function(event: Event) { | ||
if (this.props.onSelect) { | ||
var position = event.nativeEvent.position; | ||
if (position >= 0) { | ||
var value = this.props.children[position].props.value; | ||
this.props.onSelect(value, position); | ||
} else { | ||
this.props.onSelect(null, position); | ||
} | ||
} | ||
|
||
// The native Picker has changed, but the props haven't (yet). If | ||
// the handler decides to not accept the new value or do something | ||
// else with it we might end up in a bad state, so we reset the | ||
// selection on the native component. | ||
// tl;dr: PickerAndroid is a controlled component. | ||
var { selected } = this._getItems(); | ||
if (this.refs[REF_PICKER]) { | ||
this.refs[REF_PICKER].setNativeProps({selected: selected}); | ||
} | ||
}, | ||
|
||
}); | ||
|
||
var cfg = { | ||
nativeOnly: { | ||
items: true, | ||
selected: true, | ||
} | ||
} | ||
var DropdownPicker = requireNativeComponent('AndroidDropdownPicker', PickerAndroid, cfg); | ||
var DialogPicker = requireNativeComponent('AndroidDialogPicker', PickerAndroid, cfg); | ||
|
||
module.exports = PickerAndroid; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
1843709
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀
1843709
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh my GOD.
1843709
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So excited about this. Trying it now! It's not possible to style the actual dropdown or dialog, is it? (I'm trying to set the item color to white, but it shows up white on white in the dropdown.)