Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Picker #46

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions packages/react-native-dom/Libraries/Components/Picker/Picker.dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule Picker
* @flow
*/

"use strict";

const ColorPropType = require("ColorPropType");
const PickerDOM = require("PickerDOM");
const Platform = require("Platform");
const React = require("React");
const PropTypes = require("prop-types");
const StyleSheetPropType = require("StyleSheetPropType");
const TextStylePropTypes = require("TextStylePropTypes");
const UnimplementedView = require("UnimplementedView");
const ViewPropTypes = require("ViewPropTypes");
const ViewStylePropTypes = require("ViewStylePropTypes");

const itemStylePropType = StyleSheetPropType(TextStylePropTypes);

const pickerStyleType = StyleSheetPropType({
...ViewStylePropTypes,
color: ColorPropType
});

const MODE_DIALOG = "dialog";
const MODE_DROPDOWN = "dropdown";

/**
* Individual selectable item in a Picker.
*/
class PickerItem extends React.Component<{
label: string,
value?: any,
color?: ColorPropType,
testID?: string
}> {
static propTypes = {
/**
* Text to display for this item.
*/
label: PropTypes.string.isRequired,
/**
* The value to be passed to picker's `onValueChange` callback when
* this item is selected. Can be a string or an integer.
*/
value: PropTypes.any,
/**
* Color of this item's text.
* @platform android
*/
color: ColorPropType,
/**
* Used to locate the item in end-to-end tests.
*/
testID: PropTypes.string
};

render() {
// The items are not rendered directly
throw null;
}
}

/**
* Renders the native picker component on iOS and Android. Example:
*
* <Picker
* selectedValue={this.state.language}
* onValueChange={(itemValue, itemIndex) => this.setState({language: itemValue})}>
* <Picker.Item label="Java" value="java" />
* <Picker.Item label="JavaScript" value="js" />
* </Picker>
*/
class Picker extends React.Component<{
style?: $FlowFixMe,
selectedValue?: any,
onValueChange?: Function,
enabled?: boolean,
mode?: "dialog" | "dropdown",
itemStyle?: $FlowFixMe,
prompt?: string,
testID?: string
}> {
/**
* On Android, display the options in a dialog.
*/
static MODE_DIALOG = MODE_DIALOG;

/**
* On Android, display the options in a dropdown (this is the default).
*/
static MODE_DROPDOWN = MODE_DROPDOWN;

static Item = PickerItem;

static defaultProps = {
mode: MODE_DIALOG
};

// $FlowFixMe(>=0.41.0)
static propTypes = {
...ViewPropTypes,
style: pickerStyleType,
/**
* Value matching value of one of the items. Can be a string or an integer.
*/
selectedValue: PropTypes.any,
/**
* 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
*/
onValueChange: PropTypes.func,
/**
* If set to false, the picker will be disabled, i.e. the user will not be able to make a
* selection.
* @platform android
*/
enabled: PropTypes.bool,
/**
* On Android, specifies how to display the selection items when the user taps on the picker:
*
* - 'dialog': Show a modal dialog. This is the default.
* - 'dropdown': Shows a dropdown anchored to the picker view
*
* @platform android
*/
mode: PropTypes.oneOf(["dialog", "dropdown"]),
/**
* Style to apply to each of the item labels.
* @platform ios
*/
itemStyle: itemStylePropType,
/**
* Prompt string for this picker, used on Android in dialog mode as the title of the dialog.
* @platform android
*/
prompt: PropTypes.string,
/**
* Used to locate this view in end-to-end tests.
*/
testID: PropTypes.string
};

render() {
return <PickerDOM {...this.props}>{this.props.children}</PickerDOM>;
}
}

module.exports = Picker;
152 changes: 152 additions & 0 deletions packages/react-native-dom/Libraries/Components/Picker/PickerDOM.dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule PickerDOM
*
* This is a controlled component version of RCTPickerDOM
*/
"use strict";

const ColorPropType = require("ColorPropType");
const NativeMethodsMixin = require("NativeMethodsMixin");
const React = require("React");
const PropTypes = require("prop-types");
const StyleSheet = require("StyleSheet");
const StyleSheetPropType = require("StyleSheetPropType");
const TextStylePropTypes = require("TextStylePropTypes");
const View = require("View");
const ViewPropTypes = require("ViewPropTypes");
const ViewStylePropTypes = require("ViewStylePropTypes");
const processColor = require("processColor");

const createReactClass = require("create-react-class");
const requireNativeComponent = require("requireNativeComponent");

const pickerStyleType = StyleSheetPropType({
...ViewStylePropTypes,
color: ColorPropType
});

const PickerDOM = createReactClass({
displayName: "PickerDOM",
mixins: [NativeMethodsMixin],

propTypes: {
...ViewPropTypes,
style: pickerStyleType,
selectedValue: PropTypes.any,
enabled: PropTypes.bool,
onValueChange: PropTypes.func,
selectedValue: PropTypes.any // string or integer basically
},

getInitialState: function() {
return this._stateFromProps(this.props);
},

UNSAFE_componentWillReceiveProps: function(nextProps) {
this.setState(this._stateFromProps(nextProps));
},

// Translate PickerDOM prop and children into stuff that RCTPickerDOM understands.
_stateFromProps: function(props) {
let selectedIndex = 0;
const items = [];
React.Children.toArray(props.children).forEach(function(child, index) {
if (child.props.value === props.selectedValue) {
selectedIndex = index;
}
items.push({
value: child.props.value,
label: child.props.label,
textColor: processColor(child.props.color)
});
});
return { selectedIndex, items };
},

render: function() {
return (
<RCTPickerDOM
ref={(picker) => (this._picker = picker)}
style={[styles.pickerDOM, this.props.style]}
items={this.state.items}
selectedIndex={this.state.selectedIndex}
onChange={this._onChange}
enabled={this.props.enabled}
/>
);
},

_onChange: function(event) {
if (this.props.onChange) {
this.props.onChange(event);
}
if (this.props.onValueChange) {
this.props.onValueChange(
event.nativeEvent.newValue,
event.nativeEvent.newIndex
);
}

// The picker is a controlled component. This means we expect the
// on*Change handlers to be in charge of updating our
// `selectedValue` prop. That way they can also
// disallow/undo/mutate the selection of certain values. In other
// words, the embedder of this component should be the source of
// truth, not the native component.
if (
this._picker &&
this.state.selectedIndex !== event.nativeEvent.newIndex
) {
this._picker.setNativeProps({
selectedIndex: this.state.selectedIndex
});
}
}
});

PickerDOM.Item = class extends React.Component {
static propTypes = {
value: PropTypes.any, // string or integer basically
label: PropTypes.string,
color: PropTypes.string
};

render() {
// These items don't get rendered directly.
return null;
}
};

const styles = StyleSheet.create({
pickerDOM: {
// The picker will conform to whatever width is given, but we do
// have to set the component's height explicitly on the
// surrounding view to ensure it gets rendered.
height: 38
}
});

const RCTPickerDOM = requireNativeComponent(
"RCTPicker",
{
propTypes: {
...ViewPropTypes,
style: pickerStyleType
}
},
{
nativeOnly: {
items: true,
onChange: true,
selectedIndex: true,
enabled: true
}
}
);

module.exports = PickerDOM;
3 changes: 2 additions & 1 deletion packages/react-native-dom/ReactDom/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ const builtInNativeModules: any[] = [
import("RCTRedBox"),
import("RCTWebViewManager"),
import("RCTNetworkingNative"),
import("RCTBlobManager")
import("RCTBlobManager"),
import("RCTPickerManager")
];

// Development Specific Native Modules
Expand Down
Loading