This commit is contained in:
Eric Vicenti 2015-06-05 15:11:57 -07:00
Родитель 8b93b99d4a bb95400f24
Коммит 0293def7a9
67 изменённых файлов: 1761 добавлений и 658 удалений

Просмотреть файл

@ -281,7 +281,7 @@ var SearchScreen = React.createClass({
renderRow={this.renderRow}
onEndReached={this.onEndReached}
automaticallyAdjustContentInsets={false}
keyboardDismissMode="onDrag"
keyboardDismissMode="on-drag"
keyboardShouldPersistTaps={true}
showsVerticalScrollIndicator={false}
/>;

Просмотреть файл

@ -0,0 +1,83 @@
/**
* 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';
var React = require('react-native');
var {
ProgressViewIOS,
StyleSheet,
View,
} = React;
var TimerMixin = require('react-timer-mixin');
var ProgressViewExample = React.createClass({
mixins: [TimerMixin],
getInitialState() {
return {
progress: 0,
};
},
componentDidMount() {
this.updateProgress();
},
updateProgress() {
var progress = this.state.progress + 0.01;
this.setState({ progress });
this.requestAnimationFrame(() => this.updateProgress());
},
getProgress(offset) {
var progress = this.state.progress + offset;
return Math.sin(progress % Math.PI) % 1;
},
render() {
return (
<View style={styles.container}>
<ProgressViewIOS style={styles.progressView} progress={this.getProgress(0)}/>
<ProgressViewIOS style={styles.progressView} progressTintColor="purple" progress={this.getProgress(0.2)}/>
<ProgressViewIOS style={styles.progressView} progressTintColor="red" progress={this.getProgress(0.4)}/>
<ProgressViewIOS style={styles.progressView} progressTintColor="orange" progress={this.getProgress(0.6)}/>
<ProgressViewIOS style={styles.progressView} progressTintColor="yellow" progress={this.getProgress(0.8)}/>
</View>
);
},
});
exports.framework = 'React';
exports.title = 'ProgressViewIOS';
exports.description = 'ProgressViewIOS';
exports.examples = [{
title: 'ProgressViewIOS',
render() {
return (
<ProgressViewExample/>
);
}
}];
var styles = StyleSheet.create({
container: {
marginTop: -20,
backgroundColor: 'transparent',
},
progressView: {
marginTop: 20,
}
});

Просмотреть файл

@ -32,11 +32,11 @@ exports.examples = [{
render() {
return (
<View>
{Object.keys(StatusBarIOS.Style).map((key) =>
{['default', 'light-content'].map((style) =>
<TouchableHighlight style={styles.wrapper}
onPress={() => StatusBarIOS.setStyle(StatusBarIOS.Style[key])}>
onPress={() => StatusBarIOS.setStyle(style)}>
<View style={styles.button}>
<Text>setStyle(StatusBarIOS.Style.{key})</Text>
<Text>setStyle('{style}')</Text>
</View>
</TouchableHighlight>
)}
@ -48,11 +48,11 @@ exports.examples = [{
render() {
return (
<View>
{Object.keys(StatusBarIOS.Style).map((key) =>
{['default', 'light-content'].map((style) =>
<TouchableHighlight style={styles.wrapper}
onPress={() => StatusBarIOS.setStyle(StatusBarIOS.Style[key], true)}>
onPress={() => StatusBarIOS.setStyle(style, true)}>
<View style={styles.button}>
<Text>setStyle(StatusBarIOS.Style.{key}, true)</Text>
<Text>setStyle('{style}', true)</Text>
</View>
</TouchableHighlight>
)}
@ -64,18 +64,18 @@ exports.examples = [{
render() {
return (
<View>
{Object.keys(StatusBarIOS.Animation).map((key) =>
{['none', 'fade', 'slide'].map((animation) =>
<View>
<TouchableHighlight style={styles.wrapper}
onPress={() => StatusBarIOS.setHidden(true, StatusBarIOS.Animation[key])}>
onPress={() => StatusBarIOS.setHidden(true, animation)}>
<View style={styles.button}>
<Text>setHidden(true, StatusBarIOS.Animation.{key})</Text>
<Text>setHidden(true, '{animation}')</Text>
</View>
</TouchableHighlight>
<TouchableHighlight style={styles.wrapper}
onPress={() => StatusBarIOS.setHidden(false, StatusBarIOS.Animation[key])}>
onPress={() => StatusBarIOS.setHidden(false, animation)}>
<View style={styles.button}>
<Text>setHidden(false, StatusBarIOS.Animation.{key})</Text>
<Text>setHidden(false, '{animation}')</Text>
</View>
</TouchableHighlight>
</View>

Просмотреть файл

@ -75,6 +75,14 @@ exports.examples = [
render: function(): ReactElement {
return <TouchableFeedbackEvents />;
},
}, {
title: 'Touchable delay for events',
description: '<Touchable*> components also accept delayPressIn, ' +
'delayPressOut, and delayLongPress as props. These props impact the ' +
'timing of feedback events.',
render: function(): ReactElement {
return <TouchableDelayEvents />;
},
}];
var TextOnPressBox = React.createClass({
@ -148,6 +156,44 @@ var TouchableFeedbackEvents = React.createClass({
},
});
var TouchableDelayEvents = React.createClass({
getInitialState: function() {
return {
eventLog: [],
};
},
render: function() {
return (
<View>
<View style={[styles.row, {justifyContent: 'center'}]}>
<TouchableOpacity
style={styles.wrapper}
onPress={() => this._appendEvent('press')}
delayPressIn={400}
onPressIn={() => this._appendEvent('pressIn - 400ms delay')}
delayPressOut={1000}
onPressOut={() => this._appendEvent('pressOut - 1000ms delay')}
delayLongPress={800}
onLongPress={() => this._appendEvent('longPress - 800ms delay')}>
<Text style={styles.button}>
Press Me
</Text>
</TouchableOpacity>
</View>
<View style={styles.eventLogBox}>
{this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)}
</View>
</View>
);
},
_appendEvent: function(eventName) {
var limit = 6;
var eventLog = this.state.eventLog.slice(0, limit - 1);
eventLog.unshift(eventName);
this.setState({eventLog});
},
});
var heartImage = {uri: 'https://pbs.twimg.com/media/BlXBfT3CQAA6cVZ.png:small'};
var styles = StyleSheet.create({

Просмотреть файл

@ -50,6 +50,10 @@
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#if RUNNING_ON_CI
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"UIExplorerApp"
launchOptions:launchOptions];

Просмотреть файл

@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.facebook.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>com.facebook.internal.uiexplorer.local</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@ -22,6 +22,8 @@
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSLocationWhenInUseUsageDescription</key>
<string>You need to add NSLocationWhenInUseUsageDescription key in Info.plist to enable geolocation, otherwise it is going to *fail silently*!</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
@ -34,8 +36,6 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSLocationWhenInUseUsageDescription</key>
<string>You need to add NSLocationWhenInUseUsageDescription key in Info.plist to enable geolocation, otherwise it is going to *fail silently*!</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>

Просмотреть файл

@ -45,6 +45,7 @@ var COMPONENTS = [
require('./NavigatorIOSColorsExample'),
require('./NavigatorIOSExample'),
require('./PickerIOSExample'),
require('./ProgressViewIOSExample'),
require('./ScrollViewExample'),
require('./SegmentedControlIOSExample'),
require('./SliderIOSExample'),
@ -156,7 +157,7 @@ class UIExplorerList extends React.Component {
renderSectionHeader={this._renderSectionHeader}
keyboardShouldPersistTaps={true}
automaticallyAdjustContentInsets={false}
keyboardDismissMode="onDrag"
keyboardDismissMode="on-drag"
/>
</View>
);

Просмотреть файл

@ -16,12 +16,19 @@ var {
View,
} = React;
var deepDiffer = require('deepDiffer');
var DEBUG = false;
var KEY_1 = 'key_1';
var VAL_1 = 'val_1';
var KEY_2 = 'key_2';
var VAL_2 = 'val_2';
var KEY_MERGE = 'key_merge';
var VAL_MERGE_1 = {'foo': 1, 'bar': {'hoo': 1, 'boo': 1}, 'moo': {'a': 3}};
var VAL_MERGE_2 = {'bar': {'hoo': 2}, 'baz': 2, 'moo': {'a': 3}};
var VAL_MERGE_EXPECT =
{'foo': 1, 'bar': {'hoo': 2, 'boo': 1}, 'baz': 2, 'moo': {'a': 3}};
// setup in componentDidMount
var done;
@ -40,8 +47,9 @@ function expectTrue(condition, message) {
function expectEqual(lhs, rhs, testname) {
expectTrue(
lhs === rhs,
'Error in test ' + testname + ': expected ' + rhs + ', got ' + lhs
!deepDiffer(lhs, rhs),
'Error in test ' + testname + ': expected\n' + JSON.stringify(rhs) +
'\ngot\n' + JSON.stringify(lhs)
);
}
@ -93,25 +101,25 @@ function testRemoveItem() {
'Missing KEY_1 or KEY_2 in ' + '(' + result + ')'
);
updateMessage('testRemoveItem - add two items');
AsyncStorage.removeItem(KEY_1, (err) => {
expectAsyncNoError(err);
AsyncStorage.removeItem(KEY_1, (err2) => {
expectAsyncNoError(err2);
updateMessage('delete successful ');
AsyncStorage.getItem(KEY_1, (err, result) => {
expectAsyncNoError(err);
AsyncStorage.getItem(KEY_1, (err3, result2) => {
expectAsyncNoError(err3);
expectEqual(
result,
result2,
null,
'testRemoveItem: key_1 present after delete'
);
updateMessage('key properly removed ');
AsyncStorage.getAllKeys((err, result2) => {
expectAsyncNoError(err);
AsyncStorage.getAllKeys((err4, result3) => {
expectAsyncNoError(err4);
expectTrue(
result2.indexOf(KEY_1) === -1,
'Unexpected: KEY_1 present in ' + result2
result3.indexOf(KEY_1) === -1,
'Unexpected: KEY_1 present in ' + result3
);
updateMessage('proper length returned.\nDone!');
done();
updateMessage('proper length returned.');
runTestCase('should merge values', testMerge);
});
});
});
@ -120,6 +128,21 @@ function testRemoveItem() {
});
}
function testMerge() {
AsyncStorage.setItem(KEY_MERGE, JSON.stringify(VAL_MERGE_1), (err1) => {
expectAsyncNoError(err1);
AsyncStorage.mergeItem(KEY_MERGE, JSON.stringify(VAL_MERGE_2), (err2) => {
expectAsyncNoError(err2);
AsyncStorage.getItem(KEY_MERGE, (err3, result) => {
expectAsyncNoError(err3);
expectEqual(JSON.parse(result), VAL_MERGE_EXPECT, 'testMerge');
updateMessage('objects deeply merged\nDone!');
done();
});
});
});
}
var AsyncStorageTest = React.createClass({
getInitialState() {
return {

Просмотреть файл

@ -7,7 +7,6 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule POPAnimation
* @flow
*/
'use strict';
@ -17,7 +16,7 @@ if (!RCTPOPAnimationManager) {
// workaround to enable its availability to be determined at runtime.
// For Flow let's pretend like we always export POPAnimation
// so all our users don't need to do null checks
module.exports = ((null: any): typeof POPAnimation);
module.exports = null;
} else {
var ReactPropTypes = require('ReactPropTypes');

Просмотреть файл

@ -120,7 +120,7 @@ var DatePickerIOS = React.createClass({
<View style={props.style}>
<RCTDatePickerIOS
ref={DATEPICKER}
style={styles.rkDatePickerIOS}
style={styles.datePickerIOS}
date={props.date.getTime()}
maximumDate={
props.maximumDate ? props.maximumDate.getTime() : undefined
@ -128,7 +128,7 @@ var DatePickerIOS = React.createClass({
minimumDate={
props.minimumDate ? props.minimumDate.getTime() : undefined
}
mode={RCTDatePickerIOSConsts.DatePickerModes[props.mode]}
mode={props.mode}
minuteInterval={props.minuteInterval}
timeZoneOffsetInMinutes={props.timeZoneOffsetInMinutes}
onChange={this._onChange}
@ -139,7 +139,7 @@ var DatePickerIOS = React.createClass({
});
var styles = StyleSheet.create({
rkDatePickerIOS: {
datePickerIOS: {
height: RCTDatePickerIOSConsts.ComponentHeight,
width: RCTDatePickerIOSConsts.ComponentWidth,
},

Просмотреть файл

@ -0,0 +1,49 @@
/**
* 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 ProgressViewIOS
*/
'use strict';
var React = require('React');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var View = require('View');
var DummyProgressViewIOS = React.createClass({
render: function() {
return (
<View style={[styles.dummy, this.props.style]}>
<Text style={styles.text}>
ProgressViewIOS is not supported on this platform!
</Text>
</View>
);
},
});
var styles = StyleSheet.create({
dummy: {
width: 120,
height: 20,
backgroundColor: '#ffbcbc',
borderWidth: 1,
borderColor: 'red',
alignItems: 'center',
justifyContent: 'center',
},
text: {
color: '#333333',
margin: 5,
fontSize: 10,
}
});
module.exports = DummyProgressViewIOS;

Просмотреть файл

@ -0,0 +1,83 @@
/**
* 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 ProgressViewIOS
* @flow
*/
'use strict';
var Image = require('Image');
var NativeMethodsMixin = require('NativeMethodsMixin');
var NativeModules = require('NativeModules');
var PropTypes = require('ReactPropTypes');
var React = require('React');
var StyleSheet = require('StyleSheet');
var requireNativeComponent = require('requireNativeComponent');
var verifyPropTypes = require('verifyPropTypes');
/**
* Use `ProgressViewIOS` to render a UIProgressView on iOS.
*/
var ProgressViewIOS = React.createClass({
mixins: [NativeMethodsMixin],
propTypes: {
/**
* The progress bar style.
*/
progressViewStyle: PropTypes.oneOf(['default', 'bar']),
/**
* The progress value (between 0 and 1).
*/
progress: PropTypes.number,
/**
* The tint color of the progress bar itself.
*/
progressTintColor: PropTypes.string,
/**
* The tint color of the progress bar track.
*/
trackTintColor: PropTypes.string,
/**
* A stretchable image to display as the progress bar.
*/
progressImage: Image.propTypes.source,
/**
* A stretchable image to display behind the progress bar.
*/
trackImage: Image.propTypes.source,
},
render: function() {
return (
<RCTProgressView
{...this.props}
style={[styles.progressView, this.props.style]}
/>
);
}
});
var styles = StyleSheet.create({
progressView: {
height: NativeModules.ProgressViewManager.ComponentHeight
},
});
var RCTProgressView = requireNativeComponent(
'RCTProgressView',
ProgressViewIOS
);
module.exports = ProgressViewIOS;

Просмотреть файл

@ -38,12 +38,6 @@ var PropTypes = React.PropTypes;
var SCROLLVIEW = 'ScrollView';
var INNERVIEW = 'InnerScrollView';
var keyboardDismissModeConstants = {
'none': RCTScrollViewConsts.KeyboardDismissMode.None, // default
'interactive': RCTScrollViewConsts.KeyboardDismissMode.Interactive,
'onDrag': RCTScrollViewConsts.KeyboardDismissMode.OnDrag,
};
/**
* Component that wraps platform ScrollView while providing
* integration with touch locking "responder" system.
@ -147,7 +141,7 @@ var ScrollView = React.createClass({
keyboardDismissMode: PropTypes.oneOf([
'none', // default
'interactive',
'onDrag',
'on-drag',
]),
/**
* When false, tapping outside of the focused text input when the keyboard
@ -287,9 +281,6 @@ var ScrollView = React.createClass({
...this.props,
alwaysBounceHorizontal,
alwaysBounceVertical,
keyboardDismissMode: this.props.keyboardDismissMode ?
keyboardDismissModeConstants[this.props.keyboardDismissMode] :
undefined,
style: ([styles.base, this.props.style]: ?Array<any>),
onTouchStart: this.scrollResponderHandleTouchStart,
onTouchMove: this.scrollResponderHandleTouchMove,
@ -308,7 +299,7 @@ var ScrollView = React.createClass({
onResponderRelease: this.scrollResponderHandleResponderRelease,
onResponderReject: this.scrollResponderHandleResponderReject,
};
var ScrollViewClass;
if (Platform.OS === 'ios') {
ScrollViewClass = RCTScrollView;
@ -318,6 +309,13 @@ var ScrollView = React.createClass({
} else {
ScrollViewClass = AndroidScrollView;
}
var keyboardDismissModeConstants = {
'none': RCTScrollViewConsts.KeyboardDismissMode.None, // default
'interactive': RCTScrollViewConsts.KeyboardDismissMode.Interactive,
'on-drag': RCTScrollViewConsts.KeyboardDismissMode.OnDrag,
};
props.keyboardDismissMode = props.keyboardDismissMode ?
keyboardDismissModeConstants[props.keyboardDismissMode] : undefined;
}
invariant(
ScrollViewClass !== undefined,

Просмотреть файл

@ -17,7 +17,7 @@ var StyleSheet = require('StyleSheet');
var Text = require('Text');
var View = require('View');
var Dummy = React.createClass({
var DummySegmentedControlIOS = React.createClass({
render: function() {
return (
<View style={[styles.dummy, this.props.style]}>
@ -46,4 +46,4 @@ var styles = StyleSheet.create({
}
});
module.exports = Dummy;
module.exports = DummySegmentedControlIOS;

Просмотреть файл

@ -108,13 +108,7 @@ var styles = StyleSheet.create({
var RCTSegmentedControl = requireNativeComponent(
'RCTSegmentedControl',
null
SegmentedControlIOS
);
if (__DEV__) {
verifyPropTypes(
RCTSegmentedControl,
RCTSegmentedControl.viewConfig
);
}
module.exports = SegmentedControlIOS;

Просмотреть файл

@ -13,26 +13,26 @@
var RCTStatusBarManager = require('NativeModules').StatusBarManager;
type StatusBarStyle = $Enum<{
'default': string,
'light-content': string,
}>;
type StatusBarAnimation = $Enum<{
'none': string,
'fade': string,
'slide': string,
}>;
var StatusBarIOS = {
Style: {
default: RCTStatusBarManager.Style.default,
lightContent: RCTStatusBarManager.Style.lightContent
},
Animation: {
none: RCTStatusBarManager.Animation.none,
fade: RCTStatusBarManager.Animation.fade,
slide: RCTStatusBarManager.Animation.slide,
},
setStyle(style: number, animated?: boolean) {
setStyle(style: StatusBarStyle, animated?: boolean) {
animated = animated || false;
RCTStatusBarManager.setStyle(style, animated);
},
setHidden(hidden: boolean, animation: number) {
animation = animation || StatusBarIOS.Animation.none;
setHidden(hidden: boolean, animation?: StatusBarAnimation) {
animation = animation || 'none';
RCTStatusBarManager.setHidden(hidden, animation);
},
};

Просмотреть файл

@ -31,10 +31,6 @@ var emptyFunction = require('emptyFunction');
var invariant = require('invariant');
var merge = require('merge');
var autoCapitalizeConsts = RCTUIManager.UIText.AutocapitalizationType;
var keyboardTypeConsts = RCTUIManager.UIKeyboardType;
var returnKeyTypeConsts = RCTUIManager.UIReturnKeyType;
var RCTTextViewAttributes = merge(ReactNativeViewAttributes.UIView, {
autoCorrect: true,
autoCapitalize: true,
@ -96,10 +92,6 @@ var viewConfigAndroid = {
validAttributes: AndroidTextInputAttributes,
};
var crossPlatformKeyboardTypeMap = {
'numeric': 'decimal-pad',
};
type DefaultProps = {
bufferDelay: number;
};
@ -171,8 +163,11 @@ var TextInput = React.createClass({
* Determines which keyboard to open, e.g.`numeric`.
*/
keyboardType: PropTypes.oneOf([
'default',
// iOS
// Cross-platform
'default',
'numeric',
'email-address',
// iOS-only
'ascii-capable',
'numbers-and-punctuation',
'url',
@ -182,9 +177,6 @@ var TextInput = React.createClass({
'decimal-pad',
'twitter',
'web-search',
// Cross-platform
'numeric',
'email-address',
]),
/**
* Determines how the return key should look.
@ -426,18 +418,12 @@ var TextInput = React.createClass({
_renderIOS: function() {
var textContainer;
var autoCapitalize = autoCapitalizeConsts[this.props.autoCapitalize];
var clearButtonMode = RCTUIManager.UITextField.clearButtonMode[this.props.clearButtonMode];
var props = this.props;
props.style = [styles.input, this.props.style];
var keyboardType = keyboardTypeConsts[
crossPlatformKeyboardTypeMap[this.props.keyboardType] ||
this.props.keyboardType
];
var returnKeyType = returnKeyTypeConsts[this.props.returnKeyType];
if (!this.props.multiline) {
if (!props.multiline) {
for (var propKey in onlyMultiline) {
if (this.props[propKey]) {
if (props[propKey]) {
throw new Error(
'TextInput prop `' + propKey + '` is only supported with multiline.'
);
@ -446,77 +432,48 @@ var TextInput = React.createClass({
textContainer =
<RCTTextField
ref="input"
style={[styles.input, this.props.style]}
enabled={this.props.editable}
keyboardType={keyboardType}
returnKeyType={returnKeyType}
enablesReturnKeyAutomatically={this.props.enablesReturnKeyAutomatically}
secureTextEntry={this.props.password || this.props.secureTextEntry}
{...props}
onFocus={this._onFocus}
onBlur={this._onBlur}
onChange={this._onChange}
onEndEditing={this.props.onEndEditing}
onSubmitEditing={this.props.onSubmitEditing}
onSelectionChangeShouldSetResponder={() => true}
onLayout={this.props.onLayout}
placeholder={this.props.placeholder}
placeholderTextColor={this.props.placeholderTextColor}
text={this.state.bufferedValue}
autoCapitalize={autoCapitalize}
autoCorrect={this.props.autoCorrect}
clearButtonMode={clearButtonMode}
clearTextOnFocus={this.props.clearTextOnFocus}
selectTextOnFocus={this.props.selectTextOnFocus}
/>;
} else {
for (var propKey in notMultiline) {
if (this.props[propKey]) {
if (props[propKey]) {
throw new Error(
'TextInput prop `' + propKey + '` cannot be used with multiline.'
);
}
}
var children = this.props.children;
var children = props.children;
var childCount = 0;
ReactChildren.forEach(children, () => ++childCount);
invariant(
!(this.props.value && childCount),
!(props.value && childCount),
'Cannot specify both value and children.'
);
if (childCount > 1) {
children = <Text>{children}</Text>;
}
if (this.props.inputView) {
children = [children, this.props.inputView];
if (props.inputView) {
children = [children, props.inputView];
}
textContainer =
<RCTTextView
ref="input"
style={[styles.input, this.props.style]}
{...props}
children={children}
mostRecentEventCounter={this.state.mostRecentEventCounter}
editable={this.props.editable}
keyboardType={keyboardType}
returnKeyType={returnKeyType}
enablesReturnKeyAutomatically={this.props.enablesReturnKeyAutomatically}
secureTextEntry={this.props.password || this.props.secureTextEntry}
onFocus={this._onFocus}
onBlur={this._onBlur}
onChange={this._onChange}
onEndEditing={this.props.onEndEditing}
onSelectionChange={this._onSelectionChange}
onTextInput={this._onTextInput}
onSelectionChangeShouldSetResponder={emptyFunction.thatReturnsTrue}
onLayout={this.props.onLayout}
placeholder={this.props.placeholder}
placeholderTextColor={this.props.placeholderTextColor}
text={this.state.bufferedValue}
autoCapitalize={autoCapitalize}
autoCorrect={this.props.autoCorrect}
clearButtonMode={clearButtonMode}
selectTextOnFocus={this.props.selectTextOnFocus}
clearTextOnFocus={this.props.clearTextOnFocus}
/>;
}
@ -524,14 +481,14 @@ var TextInput = React.createClass({
<TouchableWithoutFeedback
onPress={this._onPress}
rejectResponderTermination={true}
testID={this.props.testID}>
testID={props.testID}>
{textContainer}
</TouchableWithoutFeedback>
);
},
_renderAndroid: function() {
var autoCapitalize = autoCapitalizeConsts[this.props.autoCapitalize];
var autoCapitalize = RCTUIManager.UIText.AutocapitalizationType[this.props.autoCapitalize];
var children = this.props.children;
var childCount = 0;
ReactChildren.forEach(children, () => ++childCount);

Просмотреть файл

@ -23,6 +23,7 @@ var View = require('View');
var cloneWithProps = require('cloneWithProps');
var ensureComponentIsNative = require('ensureComponentIsNative');
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
var keyOf = require('keyOf');
var merge = require('merge');
var onlyChild = require('onlyChild');
@ -111,6 +112,7 @@ var TouchableHighlight = React.createClass({
},
componentDidMount: function() {
ensurePositiveDelayProps(this.props);
ensureComponentIsNative(this.refs[CHILD_REF]);
},
@ -119,6 +121,7 @@ var TouchableHighlight = React.createClass({
},
componentWillReceiveProps: function(nextProps) {
ensurePositiveDelayProps(nextProps);
if (nextProps.activeOpacity !== this.props.activeOpacity ||
nextProps.underlayColor !== this.props.underlayColor ||
nextProps.style !== this.props.style) {
@ -152,7 +155,8 @@ var TouchableHighlight = React.createClass({
touchableHandlePress: function() {
this.clearTimeout(this._hideTimeout);
this._showUnderlay();
this._hideTimeout = this.setTimeout(this._hideUnderlay, 100);
this._hideTimeout = this.setTimeout(this._hideUnderlay,
this.props.delayPressOut || 100);
this.props.onPress && this.props.onPress();
},
@ -164,6 +168,18 @@ var TouchableHighlight = React.createClass({
return PRESS_RECT_OFFSET; // Always make sure to predeclare a constant!
},
touchableGetHighlightDelayMS: function() {
return this.props.delayPressIn;
},
touchableGetLongPressDelayMS: function() {
return this.props.delayLongPress;
},
touchableGetPressOutDelayMS: function() {
return this.props.delayPressOut;
},
_showUnderlay: function() {
this.refs[UNDERLAY_REF].setNativeProps(this.state.activeUnderlayProps);
this.refs[CHILD_REF].setNativeProps(this.state.activeProps);

Просмотреть файл

@ -15,11 +15,13 @@
var NativeMethodsMixin = require('NativeMethodsMixin');
var POPAnimationMixin = require('POPAnimationMixin');
var React = require('React');
var TimerMixin = require('react-timer-mixin');
var Touchable = require('Touchable');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
var cloneWithProps = require('cloneWithProps');
var ensureComponentIsNative = require('ensureComponentIsNative');
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
var flattenStyle = require('flattenStyle');
var keyOf = require('keyOf');
var onlyChild = require('onlyChild');
@ -50,7 +52,7 @@ var onlyChild = require('onlyChild');
*/
var TouchableOpacity = React.createClass({
mixins: [Touchable.Mixin, NativeMethodsMixin, POPAnimationMixin],
mixins: [TimerMixin, Touchable.Mixin, NativeMethodsMixin, POPAnimationMixin],
propTypes: {
...TouchableWithoutFeedback.propTypes,
@ -72,6 +74,7 @@ var TouchableOpacity = React.createClass({
},
componentDidMount: function() {
ensurePositiveDelayProps(this.props);
ensureComponentIsNative(this.refs[CHILD_REF]);
},
@ -79,6 +82,10 @@ var TouchableOpacity = React.createClass({
ensureComponentIsNative(this.refs[CHILD_REF]);
},
componentWillReceiveProps: function(nextProps) {
ensurePositiveDelayProps(nextProps);
},
setOpacityTo: function(value) {
if (POPAnimationMixin) {
// Reset with animation if POP is available
@ -86,6 +93,7 @@ var TouchableOpacity = React.createClass({
var anim = {
type: this.AnimationTypes.linear,
property: this.AnimationProperties.opacity,
duration: 0.15,
toValue: value,
};
this.startAnimation(CHILD_REF, anim);
@ -102,20 +110,26 @@ var TouchableOpacity = React.createClass({
* defined on your component.
*/
touchableHandleActivePressIn: function() {
this.refs[CHILD_REF].setNativeProps({
opacity: this.props.activeOpacity
});
this.clearTimeout(this._hideTimeout);
this._hideTimeout = null;
this._opacityActive();
this.props.onPressIn && this.props.onPressIn();
},
touchableHandleActivePressOut: function() {
var child = onlyChild(this.props.children);
var childStyle = flattenStyle(child.props.style) || {};
this.setOpacityTo(childStyle.opacity === undefined ? 1 : childStyle.opacity);
if (!this._hideTimeout) {
this._opacityInactive();
}
this.props.onPressOut && this.props.onPressOut();
},
touchableHandlePress: function() {
this.clearTimeout(this._hideTimeout);
this._opacityActive();
this._hideTimeout = this.setTimeout(
this._opacityInactive,
this.props.delayPressOut || 100
);
this.props.onPress && this.props.onPress();
},
@ -128,7 +142,30 @@ var TouchableOpacity = React.createClass({
},
touchableGetHighlightDelayMS: function() {
return 0;
return this.props.delayPressIn || 0;
},
touchableGetLongPressDelayMS: function() {
return this.props.delayLongPress === 0 ? 0 :
this.props.delayLongPress || 500;
},
touchableGetPressOutDelayMS: function() {
return this.props.delayPressOut;
},
_opacityActive: function() {
this.setOpacityTo(this.props.activeOpacity);
},
_opacityInactive: function() {
this.clearTimeout(this._hideTimeout);
this._hideTimeout = null;
var child = onlyChild(this.props.children);
var childStyle = flattenStyle(child.props.style) || {};
this.setOpacityTo(
childStyle.opacity === undefined ? 1 : childStyle.opacity
);
},
render: function() {

Просмотреть файл

@ -12,7 +12,9 @@
'use strict';
var React = require('React');
var TimerMixin = require('react-timer-mixin');
var Touchable = require('Touchable');
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
var onlyChild = require('onlyChild');
/**
@ -31,23 +33,44 @@ type Event = Object;
* one of the primary reason a "web" app doesn't feel "native".
*/
var TouchableWithoutFeedback = React.createClass({
mixins: [Touchable.Mixin],
mixins: [TimerMixin, Touchable.Mixin],
propTypes: {
/**
* Called when the touch is released, but not if cancelled (e.g. by a scroll
* that steals the responder lock).
*/
accessible: React.PropTypes.bool,
onPress: React.PropTypes.func,
onPressIn: React.PropTypes.func,
onPressOut: React.PropTypes.func,
onLongPress: React.PropTypes.func,
/**
* Delay in ms, from the start of the touch, before onPressIn is called.
*/
delayPressIn: React.PropTypes.number,
/**
* Delay in ms, from the release of the touch, before onPressOut is called.
*/
delayPressOut: React.PropTypes.number,
/**
* Delay in ms, from onPressIn, before onLongPress is called.
*/
delayLongPress: React.PropTypes.number,
},
getInitialState: function() {
return this.touchableGetInitialState();
},
componentDidMount: function() {
ensurePositiveDelayProps(this.props);
},
componentWillReceiveProps: function(nextProps: Object) {
ensurePositiveDelayProps(nextProps);
},
/**
* `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
* defined on your component.
@ -73,13 +96,22 @@ var TouchableWithoutFeedback = React.createClass({
},
touchableGetHighlightDelayMS: function(): number {
return 0;
return this.props.delayPressIn || 0;
},
touchableGetLongPressDelayMS: function(): number {
return this.props.delayLongPress === 0 ? 0 :
this.props.delayLongPress || 500;
},
touchableGetPressOutDelayMS: function(): number {
return this.props.delayPressOut || 0;
},
render: function(): ReactElement {
// Note(avik): remove dynamic typecast once Flow has been upgraded
return (React: any).cloneElement(onlyChild(this.props.children), {
accessible: true,
accessible: this.props.accessible !== false,
testID: this.props.testID,
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest,

Просмотреть файл

@ -0,0 +1,24 @@
/**
* 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 ensurePositiveDelayProps
* @flow
*/
'use strict';
var invariant = require('invariant');
var ensurePositiveDelayProps = function(props: any) {
invariant(
!(props.delayPressIn < 0 || props.delayPressOut < 0 ||
props.delayLongPress < 0),
'Touchable components cannot have negative delay properties'
);
};
module.exports = ensurePositiveDelayProps;

Просмотреть файл

@ -146,20 +146,11 @@ var Image = React.createClass({
if (this.props.style && this.props.style.tintColor) {
warning(RawImage === RCTStaticImage, 'tintColor style only supported on static images.');
}
var resizeMode = this.props.resizeMode || style.resizeMode;
var contentModes = NativeModules.UIManager.UIView.ContentMode;
var contentMode;
if (resizeMode === ImageResizeMode.stretch) {
contentMode = contentModes.ScaleToFill;
} else if (resizeMode === ImageResizeMode.contain) {
contentMode = contentModes.ScaleAspectFit;
} else { // ImageResizeMode.cover or undefined
contentMode = contentModes.ScaleAspectFill;
}
var resizeMode = this.props.resizeMode || style.resizeMode || 'cover';
var nativeProps = merge(this.props, {
style,
contentMode,
resizeMode,
tintColor: style.tintColor,
});
if (isStored) {
@ -187,7 +178,7 @@ var nativeOnlyProps = {
src: true,
defaultImageSrc: true,
imageTag: true,
contentMode: true,
resizeMode: true,
};
if (__DEV__) {
verifyPropTypes(Image, RCTStaticImage.viewConfig, nativeOnlyProps);

Просмотреть файл

@ -82,8 +82,8 @@ static NSString *RCTCacheKeyForURL(NSURL *url)
RCTImageDownloader *strongSelf = weakSelf;
NSArray *blocks = strongSelf->_pendingBlocks[cacheKey];
[strongSelf->_pendingBlocks removeObjectForKey:cacheKey];
for (RCTCachedDataDownloadBlock block in blocks) {
block(cached, data, error);
for (RCTCachedDataDownloadBlock cacheDownloadBlock in blocks) {
cacheDownloadBlock(cached, data, error);
}
});
};

Просмотреть файл

@ -29,6 +29,6 @@ RCT_EXPORT_MODULE()
RCT_REMAP_VIEW_PROPERTY(defaultImageSrc, defaultImage, UIImage)
RCT_REMAP_VIEW_PROPERTY(src, imageURL, NSURL)
RCT_EXPORT_VIEW_PROPERTY(contentMode, UIViewContentMode)
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode)
@end

Просмотреть файл

@ -26,7 +26,7 @@ RCT_EXPORT_MODULE()
}
RCT_EXPORT_VIEW_PROPERTY(capInsets, UIEdgeInsets)
RCT_EXPORT_VIEW_PROPERTY(contentMode, UIViewContentMode)
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode)
RCT_CUSTOM_VIEW_PROPERTY(src, NSURL, RCTStaticImage)
{
if (json) {

Просмотреть файл

@ -50,6 +50,9 @@ function findInstanceByNativeTag(rootTag, nativeTag) {
var containerID = ReactNativeTagHandles.tagToRootNodeID[rootTag];
var rootInstance = ReactNativeMount._instancesByContainerID[containerID];
var targetID = ReactNativeTagHandles.tagToRootNodeID[nativeTag];
if (!targetID) {
return undefined;
}
return findInstance(rootInstance, targetID);
}

Просмотреть файл

@ -20,8 +20,7 @@ var RCTPickerIOSConsts = require('NativeModules').UIManager.RCTPicker.Constants;
var StyleSheet = require('StyleSheet');
var View = require('View');
var createReactNativeComponentClass =
require('createReactNativeComponentClass');
var requireNativeComponent = require('requireNativeComponent');
var merge = require('merge');
var PICKER = 'picker';
@ -60,7 +59,7 @@ var PickerIOS = React.createClass({
<View style={this.props.style}>
<RCTPickerIOS
ref={PICKER}
style={styles.rkPickerIOS}
style={styles.pickerIOS}
items={this.state.items}
selectedIndex={this.state.selectedIndex}
onChange={this._onChange}
@ -104,7 +103,7 @@ PickerIOS.Item = React.createClass({
});
var styles = StyleSheet.create({
rkPickerIOS: {
pickerIOS: {
// 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.
@ -112,14 +111,6 @@ var styles = StyleSheet.create({
},
});
var rkPickerIOSAttributes = merge(ReactNativeViewAttributes.UIView, {
items: true,
selectedIndex: true,
});
var RCTPickerIOS = createReactNativeComponentClass({
validAttributes: rkPickerIOSAttributes,
uiViewClassName: 'RCTPicker',
});
var RCTPickerIOS = requireNativeComponent('RCTPicker', null);
module.exports = PickerIOS;

Просмотреть файл

@ -20,6 +20,7 @@ var _initialNotification = RCTPushNotificationManager &&
RCTPushNotificationManager.initialNotification;
var DEVICE_NOTIF_EVENT = 'remoteNotificationReceived';
var NOTIF_REGISTER_EVENT = 'remoteNotificationsRegistered';
/**
* Handle push notifications for your app, including permission handling and
@ -49,30 +50,72 @@ class PushNotificationIOS {
}
/**
* Attaches a listener to remote notifications while the app is running in the
* foreground or the background.
* Attaches a listener to remote notification events while the app is running
* in the foreground or the background.
*
* The handler will get be invoked with an instance of `PushNotificationIOS`
* Valid events are:
*
* - `notification` : Fired when a remote notification is received. The
* handler will be invoked with an instance of `PushNotificationIOS`.
* - `register`: Fired when the user registers for remote notifications. The
* handler will be invoked with a hex string representing the deviceToken.
*/
static addEventListener(type: string, handler: Function) {
invariant(
type === 'notification',
'PushNotificationIOS only supports `notification` events'
);
_notifHandlers[handler] = RCTDeviceEventEmitter.addListener(
DEVICE_NOTIF_EVENT,
(notifData) => {
handler(new PushNotificationIOS(notifData));
}
type === 'notification' || type === 'register',
'PushNotificationIOS only supports `notification` and `register` events'
);
if (type === 'notification') {
_notifHandlers[handler] = RCTDeviceEventEmitter.addListener(
DEVICE_NOTIF_EVENT,
(notifData) => {
handler(new PushNotificationIOS(notifData));
}
);
} else if (type === 'register') {
_notifHandlers[handler] = RCTDeviceEventEmitter.addListener(
NOTIF_REGISTER_EVENT,
(registrationInfo) => {
handler(registrationInfo.deviceToken);
}
);
}
}
/**
* Requests all notification permissions from iOS, prompting the user's
* dialog box.
* Requests notification permissions from iOS, prompting the user's
* dialog box. By default, it will request all notification permissions, but
* a subset of these can be requested by passing a map of requested
* permissions.
* The following permissions are supported:
*
* - `alert`
* - `badge`
* - `sound`
*
* If a map is provided to the method, only the permissions with truthy values
* will be requested.
*/
static requestPermissions() {
RCTPushNotificationManager.requestPermissions();
static requestPermissions(permissions?: {
alert?: boolean,
badge?: boolean,
sound?: boolean
}) {
var requestedPermissions = {};
if (permissions) {
requestedPermissions = {
alert: !!permissions.alert,
badge: !!permissions.badge,
sound: !!permissions.sound
};
} else {
requestedPermissions = {
alert: true,
badge: true,
sound: true
};
}
RCTPushNotificationManager.requestPermissions(requestedPermissions);
}
/**
@ -97,8 +140,8 @@ class PushNotificationIOS {
*/
static removeEventListener(type: string, handler: Function) {
invariant(
type === 'notification',
'PushNotificationIOS only supports `notification` events'
type === 'notification' || type === 'register',
'PushNotificationIOS only supports `notification` and `register` events'
);
if (!_notifHandlers[handler]) {
return;

Просмотреть файл

@ -14,6 +14,7 @@
@interface RCTPushNotificationManager : NSObject <RCTBridgeModule>
+ (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings;
+ (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
+ (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification;
@end

Просмотреть файл

@ -12,7 +12,18 @@
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
#define UIUserNotificationTypeAlert UIRemoteNotificationTypeAlert
#define UIUserNotificationTypeBadge UIRemoteNotificationTypeBadge
#define UIUserNotificationTypeSound UIRemoteNotificationTypeSound
#define UIUserNotificationTypeNone UIRemoteNotificationTypeNone
#define UIUserNotificationType UIRemoteNotificationType
#endif
NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived";
NSString *const RCTRemoteNotificationsRegistered = @"RemoteNotificationsRegistered";
@implementation RCTPushNotificationManager
{
@ -30,6 +41,10 @@ RCT_EXPORT_MODULE()
selector:@selector(handleRemoteNotificationReceived:)
name:RCTRemoteNotificationReceived
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleRemoteNotificationsRegistered:)
name:RCTRemoteNotificationsRegistered
object:nil];
}
return self;
}
@ -52,6 +67,21 @@ RCT_EXPORT_MODULE()
}
}
+ (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSMutableString *hexString = [NSMutableString string];
const unsigned char *bytes = [deviceToken bytes];
for (int i = 0; i < [deviceToken length]; i++) {
[hexString appendFormat:@"%02x", bytes[i]];
}
NSDictionary *userInfo = @{
@"deviceToken" : [hexString copy]
};
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationsRegistered
object:self
userInfo:userInfo];
}
+ (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationReceived
@ -65,6 +95,12 @@ RCT_EXPORT_MODULE()
body:[notification userInfo]];
}
- (void)handleRemoteNotificationsRegistered:(NSNotification *)notification
{
[_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistered"
body:[notification userInfo]];
}
/**
* Update the application icon badge number on the home screen
*/
@ -83,36 +119,35 @@ RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback
]);
}
RCT_EXPORT_METHOD(requestPermissions)
RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions)
{
Class _UIUserNotificationSettings;
if ((_UIUserNotificationSettings = NSClassFromString(@"UIUserNotificationSettings"))) {
UIUserNotificationType types = UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert;
UIUserNotificationSettings *notificationSettings = [_UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
UIUserNotificationType types = UIRemoteNotificationTypeNone;
if (permissions) {
if ([permissions[@"alert"] boolValue]) {
types |= UIUserNotificationTypeAlert;
}
if ([permissions[@"badge"] boolValue]) {
types |= UIUserNotificationTypeBadge;
}
if ([permissions[@"sound"] boolValue]) {
types |= UIUserNotificationTypeSound;
}
} else {
types = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound;
}
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert];
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0
id notificationSettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
#else
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:types];
#endif
}
}
RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback)
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
#define UIUserNotificationTypeAlert UIRemoteNotificationTypeAlert
#define UIUserNotificationTypeBadge UIRemoteNotificationTypeBadge
#define UIUserNotificationTypeSound UIRemoteNotificationTypeSound
#endif
NSUInteger types = 0;
if ([UIApplication instancesRespondToSelector:@selector(currentUserNotificationSettings)]) {
types = [[[UIApplication sharedApplication] currentUserNotificationSettings] types];

Просмотреть файл

@ -35,7 +35,11 @@
sanitizedAppName = [sanitizedAppName stringByReplacingOccurrencesOfString:@"\\" withString:@"-"];
_testController = [[FBSnapshotTestController alloc] initWithTestName:sanitizedAppName];
_testController.referenceImagesDirectory = referenceDir;
#if RUNNING_ON_CI
_scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"];
#else
_scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.includeRequire.runModule.bundle?dev=true", app]];
#endif
}
return self;
}

Просмотреть файл

@ -0,0 +1,38 @@
/**
* 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 BorderBox
* @flow
*/
'use strict';
var React = require('React');
var View = require('View');
class BorderBox extends React.Component {
render() {
var box = this.props.box;
if (!box) {
return this.props.children;
}
var style = {
borderTopWidth: box.top,
borderBottomWidth: box.bottom,
borderLeftWidth: box.left,
borderRightWidth: box.right,
};
return (
<View style={[style, this.props.style]}>
{this.props.children}
</View>
);
}
}
module.exports = BorderBox;

Просмотреть файл

@ -0,0 +1,113 @@
/**
* 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 BoxInspector
* @flow
*/
'use strict';
var React = require('React');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var View = require('View');
var resolveBoxStyle = require('resolveBoxStyle');
var blank = {
top: 0,
left: 0,
right: 0,
bottom: 0,
};
class BoxInspector extends React.Component {
render() {
var frame = this.props.frame;
var style = this.props.style;
var margin = style && resolveBoxStyle('margin', style) || blank;
var padding = style && resolveBoxStyle('padding', style) || blank;
return (
<BoxContainer title="margin" titleStyle={styles.marginLabel} box={margin}>
<BoxContainer title="padding" box={padding}>
<View>
<Text style={styles.innerText}>
({frame.left}, {frame.top})
</Text>
<Text style={styles.innerText}>
{frame.width} &times; {frame.height}
</Text>
</View>
</BoxContainer>
</BoxContainer>
);
}
}
class BoxContainer extends React.Component {
render() {
var box = this.props.box;
return (
<View style={styles.box}>
<View style={styles.row}>
<Text style={[this.props.titleStyle, styles.label]}>{this.props.title}</Text>
<Text style={styles.boxText}>{box.top}</Text>
</View>
<View style={styles.row}>
<Text style={styles.boxText}>{box.left}</Text>
{this.props.children}
<Text style={styles.boxText}>{box.right}</Text>
</View>
<Text style={styles.boxText}>{box.bottom}</Text>
</View>
);
}
}
var styles = StyleSheet.create({
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
},
marginLabel: {
width: 60,
},
label: {
fontSize: 10,
color: 'rgb(255,100,0)',
marginLeft: 5,
flex: 1,
textAlign: 'left',
top: -3,
},
buffer: {
fontSize: 10,
color: 'yellow',
flex: 1,
textAlign: 'center',
},
innerText: {
color: 'yellow',
fontSize: 12,
textAlign: 'center',
width: 70,
},
box: {
borderWidth: 1,
borderColor: 'grey',
},
boxText: {
color: 'white',
fontSize: 12,
marginHorizontal: 3,
marginVertical: 2,
textAlign: 'center',
},
});
module.exports = BoxInspector;

Просмотреть файл

@ -0,0 +1,74 @@
/**
* 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 ElementBox
* @flow
*/
'use strict';
var React = require('React');
var View = require('View');
var StyleSheet = require('StyleSheet');
var BorderBox = require('BorderBox');
var resolveBoxStyle = require('resolveBoxStyle');
var flattenStyle = require('flattenStyle');
class ElementBox extends React.Component {
render() {
var style = flattenStyle(this.props.style) || {};
var margin = resolveBoxStyle('margin', style);
var padding = resolveBoxStyle('padding', style);
var frameStyle = this.props.frame;
if (margin) {
frameStyle = {
top: frameStyle.top - margin.top,
left: frameStyle.left - margin.left,
height: frameStyle.height + margin.top + margin.bottom,
width: frameStyle.width + margin.left + margin.right,
};
}
var contentStyle = {
width: this.props.frame.width,
height: this.props.frame.height,
};
if (padding) {
contentStyle = {
width: contentStyle.width - padding.left - padding.right,
height: contentStyle.height - padding.top - padding.bottom,
};
}
return (
<View style={[styles.frame, frameStyle]} pointerEvents="none">
<BorderBox box={margin} style={styles.margin}>
<BorderBox box={padding} style={styles.padding}>
<View style={[styles.content, contentStyle]} />
</BorderBox>
</BorderBox>
</View>
);
}
}
var styles = StyleSheet.create({
frame: {
position: 'absolute',
},
content: {
backgroundColor: 'rgba(200, 230, 255, 0.8)',
},
padding: {
borderColor: 'rgba(77, 255, 0, 0.3)',
},
margin: {
borderColor: 'rgba(255, 132, 0, 0.3)',
},
});
module.exports = ElementBox;

Просмотреть файл

@ -0,0 +1,105 @@
/**
* 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 ElementProperties
* @flow
*/
'use strict';
var React = require('React');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var View = require('View');
var PropTypes = require('ReactPropTypes');
var BoxInspector = require('BoxInspector');
var StyleInspector = require('StyleInspector');
var TouchableHighlight = require('TouchableHighlight');
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
var flattenStyle = require('flattenStyle');
var mapWithSeparator = require('mapWithSeparator');
var ElementProperties = React.createClass({
propTypes: {
hierarchy: PropTypes.array.isRequired,
style: PropTypes.array.isRequired,
},
render: function() {
var style = flattenStyle(this.props.style);
var selection = this.props.selection;
// Without the `TouchableWithoutFeedback`, taps on this inspector pane
// would change the inspected element to whatever is under the inspector
return (
<TouchableWithoutFeedback>
<View style={styles.info}>
<View style={styles.breadcrumb}>
{mapWithSeparator(
this.props.hierarchy,
(item, i) => (
<TouchableHighlight
style={[styles.breadItem, i === selection && styles.selected]}
onPress={() => this.props.setSelection(i)}>
<Text style={styles.breadItemText}>
{item.getName ? item.getName() : 'Unknown'}
</Text>
</TouchableHighlight>
),
() => <Text style={styles.breadSep}>&#9656;</Text>
)}
</View>
<View style={styles.row}>
<StyleInspector style={style} />
<BoxInspector style={style} frame={this.props.frame} />
</View>
</View>
</TouchableWithoutFeedback>
);
}
});
var styles = StyleSheet.create({
breadSep: {
fontSize: 8,
color: 'white',
},
breadcrumb: {
flexDirection: 'row',
flexWrap: 'wrap',
marginBottom: 5,
},
selected: {
borderColor: 'white',
borderRadius: 5,
},
breadItem: {
borderWidth: 1,
borderColor: 'transparent',
marginHorizontal: 2,
},
breadItemText: {
fontSize: 10,
color: 'white',
marginHorizontal: 5,
},
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
info: {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: 10,
},
path: {
color: 'white',
fontSize: 9,
},
});
module.exports = ElementProperties;

Просмотреть файл

@ -17,12 +17,16 @@ var StyleSheet = require('StyleSheet');
var Text = require('Text');
var UIManager = require('NativeModules').UIManager;
var View = require('View');
var ElementBox = require('ElementBox');
var ElementProperties = require('ElementProperties');
var InspectorOverlay = React.createClass({
getInitialState: function() {
return {
frame: null,
pointerY: 0,
hierarchy: [],
selection: -1,
};
},
@ -33,15 +37,34 @@ var InspectorOverlay = React.createClass({
[locationX, locationY],
(nativeViewTag, left, top, width, height) => {
var instance = Inspector.findInstanceByNativeTag(this.props.rootTag, nativeViewTag);
if (!instance) {
return;
}
var hierarchy = Inspector.getOwnerHierarchy(instance);
var publicInstance = instance.getPublicInstance();
this.setState({
hierarchy,
frame: {left, top, width, height}
pointerY: locationY,
selection: hierarchy.length - 1,
frame: {left, top, width, height},
style: publicInstance.props ? publicInstance.props.style : {},
});
}
);
},
setSelection(i) {
var instance = this.state.hierarchy[i];
var publicInstance = instance.getPublicInstance();
UIManager.measure(React.findNodeHandle(instance), (x, y, width, height, left, top) => {
this.setState({
frame: {left, top, width, height},
style: publicInstance.props ? publicInstance.props.style : {},
selection: i,
});
});
},
shouldSetResponser: function(e) {
this.findViewForTouchEvent(e);
return true;
@ -49,18 +72,32 @@ var InspectorOverlay = React.createClass({
render: function() {
var content = [];
var justifyContent = 'flex-end';
if (this.state.frame) {
var distanceToTop = this.state.frame.top;
var distanceToBottom = Dimensions.get('window').height -
(this.state.frame.top + this.state.frame.height);
var distanceToTop = this.state.pointerY;
var distanceToBottom = Dimensions.get('window').height - distanceToTop;
var justifyContent = distanceToTop > distanceToBottom
justifyContent = distanceToTop > distanceToBottom
? 'flex-start'
: 'flex-end';
content.push(<View pointerEvents="none" style={[styles.frame, this.state.frame]} />);
content.push(<ElementProperties hierarchy={this.state.hierarchy} />);
content.push(<ElementBox frame={this.state.frame} style={this.state.style} />);
content.push(
<ElementProperties
style={this.state.style}
frame={this.state.frame}
hierarchy={this.state.hierarchy}
selection={this.state.selection}
setSelection={this.setSelection}
/>
);
} else {
content.push(
<View style={styles.welcomeMessage}>
<Text style={styles.welcomeText}>Welcome to the inspector! Tap something to inspect it.</Text>
</View>
);
}
return (
<View
@ -73,42 +110,23 @@ var InspectorOverlay = React.createClass({
}
});
var ElementProperties = React.createClass({
render: function() {
var path = this.props.hierarchy.map((instance) => {
return instance.getName ? instance.getName() : 'Unknown';
}).join(' > ');
return (
<View style={styles.info}>
<Text style={styles.path}>
{path}
</Text>
</View>
);
}
});
var styles = StyleSheet.create({
welcomeMessage: {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: 10,
paddingVertical: 50,
},
welcomeText: {
color: 'white',
},
inspector: {
backgroundColor: 'rgba(255,255,255,0.8)',
backgroundColor: 'rgba(255,255,255,0.0)',
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
},
frame: {
position: 'absolute',
backgroundColor: 'rgba(155,155,255,0.3)',
},
info: {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: 10,
},
path: {
color: 'white',
fontSize: 9,
}
});
module.exports = InspectorOverlay;

Просмотреть файл

@ -0,0 +1,63 @@
/**
* 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 StyleInspector
* @flow
*/
'use strict';
var React = require('React');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var View = require('View');
class StyleInspector extends React.Component {
render() {
if (!this.props.style) {
return <Text style={styles.noStyle}>No style</Text>;
}
var names = Object.keys(this.props.style);
return (
<View style={styles.container}>
<View>
{names.map(name => <Text style={styles.attr}>{name}:</Text>)}
</View>
<View>
{names.map(name => <Text style={styles.value}>{this.props.style[name]}</Text>)}
</View>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flexDirection: 'row',
},
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
},
attr: {
fontSize: 10,
color: '#ccc',
},
value: {
fontSize: 10,
color: 'white',
marginLeft: 10,
},
noStyle: {
color: 'white',
fontSize: 10,
},
});
module.exports = StyleInspector;

Просмотреть файл

@ -0,0 +1,59 @@
/**
* 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 resolveBoxStyle
* @flow
*/
'use strict';
/**
* Resolve a style property into it's component parts, e.g.
*
* resolveProperties('margin', {margin: 5, marginBottom: 10})
* ->
* {top: 5, left: 5, right: 5, bottom: 10}
*
* If none are set, returns false.
*/
function resolveBoxStyle(prefix: String, style: Object): ?Object {
var res = {};
var subs = ['top', 'left', 'bottom', 'right'];
var set = false;
subs.forEach(sub => {
res[sub] = style[prefix] || 0;
});
if (style[prefix]) {
set = true;
}
if (style[prefix + 'Vertical']) {
res.top = res.bottom = style[prefix + 'Vertical'];
set = true;
}
if (style[prefix + 'Horizontal']) {
res.left = res.right = style[prefix + 'Horizontal'];
set = true;
}
subs.forEach(sub => {
var val = style[prefix + capFirst(sub)];
if (val) {
res[sub] = val;
set = true;
}
});
if (!set) {
return;
}
return res;
}
function capFirst(text) {
return text[0].toUpperCase() + text.slice(1);
}
module.exports = resolveBoxStyle;

Просмотреть файл

@ -0,0 +1,19 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule mapWithSeparator
*/
'use strict';
function mapWithSeparator(array, valueFunction, separatorFunction) {
var results = [];
for (var i = 0; i < array.length; i++) {
results.push(valueFunction(array[i], i, array));
if (i !== array.length - 1) {
results.push(separatorFunction(i));
}
}
return results;
}
module.exports = mapWithSeparator;

9
Libraries/react-native/react-native.js поставляемый
Просмотреть файл

@ -24,11 +24,12 @@ var ReactNative = Object.assign(Object.create(require('React')), {
Image: require('Image'),
ListView: require('ListView'),
MapView: require('MapView'),
Navigator: require('Navigator'),
NavigatorIOS: require('NavigatorIOS'),
PickerIOS: require('PickerIOS'),
Navigator: require('Navigator'),
SegmentedControlIOS: require('SegmentedControlIOS'),
ProgressViewIOS: require('ProgressViewIOS'),
ScrollView: require('ScrollView'),
SegmentedControlIOS: require('SegmentedControlIOS'),
SliderIOS: require('SliderIOS'),
SwitchIOS: require('SwitchIOS'),
TabBarIOS: require('TabBarIOS'),
@ -47,12 +48,12 @@ var ReactNative = Object.assign(Object.create(require('React')), {
AsyncStorage: require('AsyncStorage'),
CameraRoll: require('CameraRoll'),
InteractionManager: require('InteractionManager'),
LinkingIOS: require('LinkingIOS'),
LayoutAnimation: require('LayoutAnimation'),
LinkingIOS: require('LinkingIOS'),
NetInfo: require('NetInfo'),
PanResponder: require('PanResponder'),
PixelRatio: require('PixelRatio'),
PushNotificationIOS: require('PushNotificationIOS'),
PanResponder: require('PanResponder'),
StatusBarIOS: require('StatusBarIOS'),
StyleSheet: require('StyleSheet'),
VibrationIOS: require('VibrationIOS'),

Просмотреть файл

@ -232,6 +232,8 @@ var PRESS_EXPAND_PX = 20;
var LONG_PRESS_THRESHOLD = 500;
var LONG_PRESS_DELAY_MS = LONG_PRESS_THRESHOLD - HIGHLIGHT_DELAY_MS;
var LONG_PRESS_ALLOWED_MOVEMENT = 10;
// Default amount "active" region protrudes beyond box
@ -276,7 +278,7 @@ var LONG_PRESS_ALLOWED_MOVEMENT = 10;
* +
* | RESPONDER_GRANT (HitRect)
* v
* +---------------------------+ DELAY +-------------------------+ T - DELAY +------------------------------+
* +---------------------------+ DELAY +-------------------------+ T + DELAY +------------------------------+
* |RESPONDER_INACTIVE_PRESS_IN|+-------->|RESPONDER_ACTIVE_PRESS_IN| +------------> |RESPONDER_ACTIVE_LONG_PRESS_IN|
* +---------------------------+ +-------------------------+ +------------------------------+
* + ^ + ^ + ^
@ -288,7 +290,7 @@ var LONG_PRESS_ALLOWED_MOVEMENT = 10;
* |RESPONDER_INACTIVE_PRESS_OUT|+------->|RESPONDER_ACTIVE_PRESS_OUT| |RESPONDER_ACTIVE_LONG_PRESS_OUT|
* +----------------------------+ +--------------------------+ +-------------------------------+
*
* T - DELAY => LONG_PRESS_THRESHOLD - DELAY
* T + DELAY => LONG_PRESS_DELAY_MS + DELAY
*
* Not drawn are the side effects of each transition. The most important side
* effect is the `touchableHandlePress` abstract method invocation that occurs
@ -348,12 +350,16 @@ var TouchableMixin = {
// event to make sure it doesn't get reused in the event object pool.
e.persist();
this.pressOutDelayTimeout && clearTimeout(this.pressOutDelayTimeout);
this.pressOutDelayTimeout = null;
this.state.touchable.touchState = States.NOT_RESPONDER;
this.state.touchable.responderID = dispatchID;
this._receiveSignal(Signals.RESPONDER_GRANT, e);
var delayMS =
this.touchableGetHighlightDelayMS !== undefined ?
this.touchableGetHighlightDelayMS() : HIGHLIGHT_DELAY_MS;
Math.max(this.touchableGetHighlightDelayMS(), 0) : HIGHLIGHT_DELAY_MS;
delayMS = isNaN(delayMS) ? HIGHLIGHT_DELAY_MS : delayMS;
if (delayMS !== 0) {
this.touchableDelayTimeout = setTimeout(
this._handleDelay.bind(this, e),
@ -363,9 +369,13 @@ var TouchableMixin = {
this._handleDelay(e);
}
var longDelayMS =
this.touchableGetLongPressDelayMS !== undefined ?
Math.max(this.touchableGetLongPressDelayMS(), 10) : LONG_PRESS_DELAY_MS;
longDelayMS = isNaN(longDelayMS) ? LONG_PRESS_DELAY_MS : longDelayMS;
this.longPressDelayTimeout = setTimeout(
this._handleLongDelay.bind(this, e),
LONG_PRESS_THRESHOLD - delayMS
longDelayMS + delayMS
);
},
@ -632,8 +642,14 @@ var TouchableMixin = {
if (newIsHighlight && !curIsHighlight) {
this._savePressInLocation(e);
this.touchableHandleActivePressIn && this.touchableHandleActivePressIn();
} else if (!newIsHighlight && curIsHighlight) {
this.touchableHandleActivePressOut && this.touchableHandleActivePressOut();
} else if (!newIsHighlight && curIsHighlight && this.touchableHandleActivePressOut) {
if (this.touchableGetPressOutDelayMS && this.touchableGetPressOutDelayMS()) {
this.pressOutDelayTimeout = this.setTimeout(function() {
this.touchableHandleActivePressOut();
}, this.touchableGetPressOutDelayMS());
} else {
this.touchableHandleActivePressOut();
}
}
if (IsPressingIn[curState] && signal === Signals.RESPONDER_RELEASE) {

Просмотреть файл

@ -244,30 +244,19 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
@implementation RCTModuleMethod
{
BOOL _isClassMethod;
Class _moduleClass;
SEL _selector;
NSMethodSignature *_methodSignature;
NSArray *_argumentBlocks;
NSString *_methodName;
dispatch_block_t _methodQueue;
}
static NSString *RCTStringUpToFirstArgument(NSString *methodName)
{
NSRange colonRange = [methodName rangeOfString:@":"];
if (colonRange.length) {
methodName = [methodName substringToIndex:colonRange.location];
}
return methodName;
}
- (instancetype)initWithReactMethodName:(NSString *)reactMethodName
objCMethodName:(NSString *)objCMethodName
JSMethodName:(NSString *)JSMethodName
{
if ((self = [super init])) {
_methodName = reactMethodName;
NSArray *parts = [[reactMethodName substringWithRange:(NSRange){2, reactMethodName.length - 3}] componentsSeparatedByString:@" "];
// Parse class and method
@ -277,36 +266,33 @@ static NSString *RCTStringUpToFirstArgument(NSString *methodName)
_moduleClassName = [_moduleClassName substringToIndex:categoryRange.location];
}
NSArray *argumentNames = nil;
if ([parts[1] hasPrefix:@"__rct_export__"]) {
// New format
NSString *selectorString = [parts[1] substringFromIndex:14];
_selector = NSSelectorFromString(selectorString);
_JSMethodName = JSMethodName ?: RCTStringUpToFirstArgument(selectorString);
static NSRegularExpression *regExp;
if (!regExp) {
NSString *unusedPattern = @"(?:(?:__unused|__attribute__\\(\\(unused\\)\\)))";
NSString *constPattern = @"(?:const)";
NSString *constUnusedPattern = [NSString stringWithFormat:@"(?:(?:%@|%@)\\s*)", unusedPattern, constPattern];
NSString *pattern = [NSString stringWithFormat:@"\\(%1$@?(\\w+?)(?:\\s*\\*)?%1$@?\\)", constUnusedPattern];
regExp = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:NULL];
NSString *selectorString = [parts[1] substringFromIndex:14];
_selector = NSSelectorFromString(selectorString);
_JSMethodName = JSMethodName ?: ({
NSString *methodName = selectorString;
NSRange colonRange = [methodName rangeOfString:@":"];
if (colonRange.length) {
methodName = [methodName substringToIndex:colonRange.location];
}
methodName;
});
argumentNames = [NSMutableArray array];
[regExp enumerateMatchesInString:objCMethodName options:0 range:NSMakeRange(0, objCMethodName.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSString *argumentName = [objCMethodName substringWithRange:[result rangeAtIndex:1]];
[(NSMutableArray *)argumentNames addObject:argumentName];
}];
} else {
// Old format
NSString *selectorString = parts[1];
_selector = NSSelectorFromString(selectorString);
_JSMethodName = JSMethodName ?: RCTStringUpToFirstArgument(selectorString);
static NSRegularExpression *regExp;
if (!regExp) {
NSString *unusedPattern = @"(?:(?:__unused|__attribute__\\(\\(unused\\)\\)))";
NSString *constPattern = @"(?:const)";
NSString *constUnusedPattern = [NSString stringWithFormat:@"(?:(?:%@|%@)\\s*)", unusedPattern, constPattern];
NSString *pattern = [NSString stringWithFormat:@"\\(%1$@?(\\w+?)(?:\\s*\\*)?%1$@?\\)", constUnusedPattern];
regExp = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:NULL];
}
NSMutableArray *argumentNames = [NSMutableArray array];
[regExp enumerateMatchesInString:objCMethodName options:0 range:NSMakeRange(0, objCMethodName.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSString *argumentName = [objCMethodName substringWithRange:[result rangeAtIndex:1]];
[argumentNames addObject:argumentName];
}];
// Extract class and method details
_isClassMethod = [reactMethodName characterAtIndex:0] == '+';
_moduleClass = NSClassFromString(_moduleClassName);
if (RCT_DEBUG) {
@ -318,9 +304,7 @@ static NSString *RCTStringUpToFirstArgument(NSString *methodName)
}
// Get method signature
_methodSignature = _isClassMethod ?
[_moduleClass methodSignatureForSelector:_selector] :
[_moduleClass instanceMethodSignatureForSelector:_selector];
_methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector];
// Process arguments
NSUInteger numberOfArguments = _methodSignature.numberOfArguments;
@ -363,119 +347,64 @@ static NSString *RCTStringUpToFirstArgument(NSString *methodName)
for (NSUInteger i = 2; i < numberOfArguments; i++) {
const char *argumentType = [_methodSignature getArgumentTypeAtIndex:i];
BOOL useFallback = YES;
if (argumentNames) {
NSString *argumentName = argumentNames[i - 2];
SEL selector = NSSelectorFromString([argumentName stringByAppendingString:@":"]);
if ([RCTConvert respondsToSelector:selector]) {
useFallback = NO;
switch (argumentType[0]) {
#define RCT_CONVERT_CASE(_value, _type) \
case _value: { \
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
break; \
}
RCT_CONVERT_CASE(':', SEL)
RCT_CONVERT_CASE('*', const char *)
RCT_CONVERT_CASE('c', char)
RCT_CONVERT_CASE('C', unsigned char)
RCT_CONVERT_CASE('s', short)
RCT_CONVERT_CASE('S', unsigned short)
RCT_CONVERT_CASE('i', int)
RCT_CONVERT_CASE('I', unsigned int)
RCT_CONVERT_CASE('l', long)
RCT_CONVERT_CASE('L', unsigned long)
RCT_CONVERT_CASE('q', long long)
RCT_CONVERT_CASE('Q', unsigned long long)
RCT_CONVERT_CASE('f', float)
RCT_CONVERT_CASE('d', double)
RCT_CONVERT_CASE('B', BOOL)
RCT_CONVERT_CASE('@', id)
RCT_CONVERT_CASE('^', void *)
case '{': {
[argumentBlocks addObject:^(RCTBridge *bridge, NSNumber *context, NSInvocation *invocation, NSUInteger index, id json) {
NSMethodSignature *methodSignature = [RCTConvert methodSignatureForSelector:selector];
void *returnValue = malloc(methodSignature.methodReturnLength);
NSInvocation *_invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[_invocation setTarget:[RCTConvert class]];
[_invocation setSelector:selector];
[_invocation setArgument:&json atIndex:2];
[_invocation invoke];
[_invocation getReturnValue:returnValue];
[invocation setArgument:returnValue atIndex:index];
free(returnValue);
}];
break;
}
default:
defaultCase(argumentType);
}
} else if ([argumentName isEqualToString:@"RCTResponseSenderBlock"]) {
addBlockArgument();
useFallback = NO;
}
}
if (useFallback) {
NSString *argumentName = argumentNames[i - 2];
SEL selector = NSSelectorFromString([argumentName stringByAppendingString:@":"]);
if ([RCTConvert respondsToSelector:selector]) {
switch (argumentType[0]) {
#define RCT_CASE(_value, _class, _logic) \
case _value: { \
RCT_ARG_BLOCK( \
if (RCT_DEBUG && json && ![json isKindOfClass:[_class class]]) { \
RCTLogError(@"Argument %tu (%@) of %@.%@ should be of type %@", index, \
json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, [_class class]); \
return; \
} \
_logic \
) \
break; \
}
#define RCT_CONVERT_CASE(_value, _type) \
case _value: { \
_type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \
RCT_ARG_BLOCK( _type value = convert([RCTConvert class], selector, json); ) \
break; \
}
RCT_CASE(':', NSString, SEL value = NSSelectorFromString(json); )
RCT_CASE('*', NSString, const char *value = [json UTF8String]; )
RCT_CONVERT_CASE(':', SEL)
RCT_CONVERT_CASE('*', const char *)
RCT_CONVERT_CASE('c', char)
RCT_CONVERT_CASE('C', unsigned char)
RCT_CONVERT_CASE('s', short)
RCT_CONVERT_CASE('S', unsigned short)
RCT_CONVERT_CASE('i', int)
RCT_CONVERT_CASE('I', unsigned int)
RCT_CONVERT_CASE('l', long)
RCT_CONVERT_CASE('L', unsigned long)
RCT_CONVERT_CASE('q', long long)
RCT_CONVERT_CASE('Q', unsigned long long)
RCT_CONVERT_CASE('f', float)
RCT_CONVERT_CASE('d', double)
RCT_CONVERT_CASE('B', BOOL)
RCT_CONVERT_CASE('@', id)
RCT_CONVERT_CASE('^', void *)
#define RCT_SIMPLE_CASE(_value, _type, _selector) \
case _value: { \
RCT_ARG_BLOCK( \
if (RCT_DEBUG && json && ![json respondsToSelector:@selector(_selector)]) { \
RCTLogError(@"Argument %tu (%@) of %@.%@ does not respond to selector: %@", \
index, json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, @#_selector); \
return; \
} \
_type value = [json _selector]; \
) \
break; \
}
case '{': {
[argumentBlocks addObject:^(RCTBridge *bridge, NSNumber *context, NSInvocation *invocation, NSUInteger index, id json) {
NSMethodSignature *methodSignature = [RCTConvert methodSignatureForSelector:selector];
void *returnValue = malloc(methodSignature.methodReturnLength);
NSInvocation *_invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[_invocation setTarget:[RCTConvert class]];
[_invocation setSelector:selector];
[_invocation setArgument:&json atIndex:2];
[_invocation invoke];
[_invocation getReturnValue:returnValue];
RCT_SIMPLE_CASE('c', char, charValue)
RCT_SIMPLE_CASE('C', unsigned char, unsignedCharValue)
RCT_SIMPLE_CASE('s', short, shortValue)
RCT_SIMPLE_CASE('S', unsigned short, unsignedShortValue)
RCT_SIMPLE_CASE('i', int, intValue)
RCT_SIMPLE_CASE('I', unsigned int, unsignedIntValue)
RCT_SIMPLE_CASE('l', long, longValue)
RCT_SIMPLE_CASE('L', unsigned long, unsignedLongValue)
RCT_SIMPLE_CASE('q', long long, longLongValue)
RCT_SIMPLE_CASE('Q', unsigned long long, unsignedLongLongValue)
RCT_SIMPLE_CASE('f', float, floatValue)
RCT_SIMPLE_CASE('d', double, doubleValue)
RCT_SIMPLE_CASE('B', BOOL, boolValue)
[invocation setArgument:returnValue atIndex:index];
case '{':
RCTLogMustFix(@"Cannot convert JSON to struct %s", argumentType);
break;
free(returnValue);
}];
break;
}
default:
defaultCase(argumentType);
}
} else if ([argumentName isEqualToString:@"RCTResponseSenderBlock"]) {
addBlockArgument();
} else {
// Unknown argument type
RCTLogError(@"Unknown argument type '%@' in method %@. Extend RCTConvert"
" to support this type.", argumentName, [self methodName]);
}
}
@ -494,7 +423,7 @@ static NSString *RCTStringUpToFirstArgument(NSString *methodName)
// Sanity check
RCTAssert([module class] == _moduleClass, @"Attempted to invoke method \
%@ on a module of class %@", _methodName, [module class]);
%@ on a module of class %@", [self methodName], [module class]);
// Safety check
if (arguments.count != _argumentBlocks.count) {
@ -520,12 +449,19 @@ static NSString *RCTStringUpToFirstArgument(NSString *methodName)
}
// Invoke method
[invocation invokeWithTarget:_isClassMethod ? [module class] : module];
[invocation invokeWithTarget:module];
}
- (NSString *)methodName
{
return [NSString stringWithFormat:@"-[%@ %@]", _moduleClass,
NSStringFromSelector(_selector)];
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p; exports %@ as %@;>", NSStringFromClass(self.class), self, _methodName, _JSMethodName];
return [NSString stringWithFormat:@"<%@: %p; exports %@ as %@();>",
[self class], self, [self methodName], _JSMethodName];
}
@end
@ -562,19 +498,10 @@ static RCTSparseArray *RCTExportedMethodsByModuleID(void)
const char **entries = (const char **)(mach_header + addr);
// Create method
RCTModuleMethod *moduleMethod;
if (entries[2] == NULL) {
// Legacy support for RCT_EXPORT()
moduleMethod = [[RCTModuleMethod alloc] initWithReactMethodName:@(entries[0])
objCMethodName:@(entries[0])
JSMethodName:strlen(entries[1]) ? @(entries[1]) : nil];
} else {
moduleMethod = [[RCTModuleMethod alloc] initWithReactMethodName:@(entries[0])
objCMethodName:strlen(entries[1]) ? @(entries[1]) : nil
JSMethodName:strlen(entries[2]) ? @(entries[2]) : nil];
}
RCTModuleMethod *moduleMethod =
[[RCTModuleMethod alloc] initWithReactMethodName:@(entries[0])
objCMethodName:@(entries[1])
JSMethodName:strlen(entries[2]) ? @(entries[2]) : nil];
// Cache method
NSArray *methods = methodsByModuleClassName[moduleMethod.moduleClassName];
methodsByModuleClassName[moduleMethod.moduleClassName] =
@ -726,7 +653,7 @@ static NSDictionary *RCTLocalModulesConfig()
@"methodID": @(methods.count),
@"type": @"local"
};
[RCTLocalMethodNames addObject:methodName];
[RCTLocalMethodNames addObject:methodName];
}
// Add module and method lookup
@ -1313,7 +1240,12 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
#pragma mark - Payload Generation
- (void)dispatchBlock:(dispatch_block_t)block forModule:(NSNumber *)moduleID
- (void)dispatchBlock:(dispatch_block_t)block forModule:(id<RCTBridgeModule>)module
{
[self dispatchBlock:block forModuleID:RCTModuleIDsByName[RCTBridgeModuleNameForClass([module class])]];
}
- (void)dispatchBlock:(dispatch_block_t)block forModuleID:(NSNumber *)moduleID
{
RCTAssertJSThread();
@ -1458,7 +1390,7 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
if ([module respondsToSelector:@selector(batchDidComplete)]) {
[self dispatchBlock:^{
[module batchDidComplete];
} forModule:moduleID];
} forModuleID:moduleID];
}
}];
}
@ -1526,7 +1458,7 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
@"selector": NSStringFromSelector(method.selector),
@"args": RCTJSONStringify(params ?: [NSNull null], NULL),
});
} forModule:@(moduleID)];
} forModuleID:@(moduleID)];
return YES;
}
@ -1546,7 +1478,7 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
RCTProfileBeginEvent();
[observer didUpdateFrame:frameUpdate];
RCTProfileEndEvent(name, @"objc_call,fps", nil);
} forModule:RCTModuleIDsByName[RCTBridgeModuleNameForClass([observer class])]];
} forModule:(id<RCTBridgeModule>)observer];
}
}
@ -1591,35 +1523,39 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
- (void)startProfiling
{
RCTAssertMainThread();
RCTAssertMainThread();
if (![_parentBridge.bundleURL.scheme isEqualToString:@"http"]) {
RCTLogError(@"To run the profiler you must be running from the dev server");
return;
}
RCTProfileInit();
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
RCTProfileInit(self);
}];
}
- (void)stopProfiling
{
RCTAssertMainThread();
RCTAssertMainThread();
NSString *log = RCTProfileEnd();
NSURL *bundleURL = _parentBridge.bundleURL;
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", bundleURL.scheme, bundleURL.host, bundleURL.port];
NSURL *URL = [NSURL URLWithString:URLString];
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
URLRequest.HTTPMethod = @"POST";
[URLRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSURLSessionTask *task = [[NSURLSession sharedSession] uploadTaskWithRequest:URLRequest
fromData:[log dataUsingEncoding:NSUTF8StringEncoding]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
RCTLogError(@"%@", error.localizedDescription);
}
}];
[task resume];
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
NSString *log = RCTProfileEnd(self);
NSURL *bundleURL = _parentBridge.bundleURL;
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", bundleURL.scheme, bundleURL.host, bundleURL.port];
NSURL *URL = [NSURL URLWithString:URLString];
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
URLRequest.HTTPMethod = @"POST";
[URLRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSURLSessionTask *task = [[NSURLSession sharedSession] uploadTaskWithRequest:URLRequest
fromData:[log dataUsingEncoding:NSUTF8StringEncoding]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
RCTLogError(@"%@", error.localizedDescription);
}
}];
[task resume];
}];
}
@end

Просмотреть файл

@ -145,15 +145,6 @@ extern const dispatch_queue_t RCTJSThread;
static const char *__rct_export_entry__[] = { __func__, #method, #js_name }; \
}
/**
* Deprecated, do not use.
*/
#define RCT_EXPORT(js_name) \
_Pragma("message(\"RCT_EXPORT is deprecated. Use RCT_EXPORT_METHOD instead.\")") \
__attribute__((used, section("__DATA,RCTExport"))) \
__attribute__((__aligned__(1))) \
static const char *__rct_export_entry__[] = { __func__, #js_name, NULL }
/**
* The queue that will be used to call all exported methods. If omitted, this
* will call on the default background queue, which is avoids blocking the main

Просмотреть файл

@ -23,6 +23,8 @@
*/
@interface RCTConvert : NSObject
+ (id)id:(id)json;
+ (BOOL)BOOL:(id)json;
+ (double)double:(id)json;
+ (float)float:(id)json;
@ -52,7 +54,6 @@
+ (NSWritingDirection)NSWritingDirection:(id)json;
+ (UITextAutocapitalizationType)UITextAutocapitalizationType:(id)json;
+ (UITextFieldViewMode)UITextFieldViewMode:(id)json;
+ (UIScrollViewKeyboardDismissMode)UIScrollViewKeyboardDismissMode:(id)json;
+ (UIKeyboardType)UIKeyboardType:(id)json;
+ (UIReturnKeyType)UIReturnKeyType:(id)json;

Просмотреть файл

@ -21,6 +21,8 @@ void RCTLogConvertError(id json, const char *type)
json, [json classForCoder], type);
}
RCT_CONVERTER(id, id, self)
RCT_CONVERTER(BOOL, BOOL, boolValue)
RCT_NUMBER_CONVERTER(double, doubleValue)
RCT_NUMBER_CONVERTER(float, floatValue)
@ -219,12 +221,6 @@ RCT_ENUM_CONVERTER(UITextFieldViewMode, (@{
@"always": @(UITextFieldViewModeAlways),
}), UITextFieldViewModeNever, integerValue)
RCT_ENUM_CONVERTER(UIScrollViewKeyboardDismissMode, (@{
@"none": @(UIScrollViewKeyboardDismissModeNone),
@"on-drag": @(UIScrollViewKeyboardDismissModeOnDrag),
@"interactive": @(UIScrollViewKeyboardDismissModeInteractive),
}), UIScrollViewKeyboardDismissModeNone, integerValue)
RCT_ENUM_CONVERTER(UIKeyboardType, (@{
@"default": @(UIKeyboardTypeDefault),
@"ascii-capable": @(UIKeyboardTypeASCIICapable),
@ -237,6 +233,8 @@ RCT_ENUM_CONVERTER(UIKeyboardType, (@{
@"decimal-pad": @(UIKeyboardTypeDecimalPad),
@"twitter": @(UIKeyboardTypeTwitter),
@"web-search": @(UIKeyboardTypeWebSearch),
// Added for Android compatibility
@"numeric": @(UIKeyboardTypeDecimalPad),
}), UIKeyboardTypeDefault, integerValue)
RCT_ENUM_CONVERTER(UIReturnKeyType, (@{
@ -267,7 +265,11 @@ RCT_ENUM_CONVERTER(UIViewContentMode, (@{
@"top-right": @(UIViewContentModeTopRight),
@"bottom-left": @(UIViewContentModeBottomLeft),
@"bottom-right": @(UIViewContentModeBottomRight),
}), UIViewContentModeScaleToFill, integerValue)
// Cross-platform values
@"cover": @(UIViewContentModeScaleAspectFill),
@"contain": @(UIViewContentModeScaleAspectFit),
@"stretch": @(UIViewContentModeScaleToFill),
}), UIViewContentModeScaleAspectFill, integerValue)
RCT_ENUM_CONVERTER(UIBarStyle, (@{
@"default": @(UIBarStyleDefault),

Просмотреть файл

@ -51,6 +51,12 @@
*/
- (void)reload;
/**
* Add custom item to the development menu. The handler will be called
* when user selects the item.
*/
- (void)addItem:(NSString *)title handler:(dispatch_block_t)handler;
@end
/**

Просмотреть файл

@ -43,6 +43,28 @@ static NSString *const RCTDevMenuSettingsKey = @"RCTDevMenu";
@end
@interface RCTDevMenuItem : NSObject
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) dispatch_block_t handler;
- (instancetype)initWithTitle:(NSString *)title handler:(dispatch_block_t)handler;
@end
@implementation RCTDevMenuItem
- (instancetype)initWithTitle:(NSString *)title handler:(dispatch_block_t)handler
{
if (self = [super init]) {
self.title = title;
self.handler = handler;
}
return self;
}
@end
@interface RCTDevMenu () <RCTBridgeModule, UIActionSheetDelegate>
@property (nonatomic, strong) Class executorClass;
@ -57,6 +79,8 @@ static NSString *const RCTDevMenuSettingsKey = @"RCTDevMenu";
NSURLSessionDataTask *_updateTask;
NSURL *_liveReloadURL;
BOOL _jsLoaded;
NSArray *_presentedItems;
NSMutableArray *_extraMenuItems;
}
@synthesize bridge = _bridge;
@ -94,6 +118,7 @@ RCT_EXPORT_MODULE()
_defaults = [NSUserDefaults standardUserDefaults];
_settings = [[NSMutableDictionary alloc] init];
_extraMenuItems = [NSMutableArray array];
// Delay setup until after Bridge init
[self settingsDidChange];
@ -110,6 +135,13 @@ RCT_EXPORT_MODULE()
[weakSelf toggle];
}];
// Toggle element inspector
[commands registerKeyCommandWithInput:@"i"
modifierFlags:UIKeyModifierCommand
action:^(UIKeyCommand *command) {
[_bridge.eventDispatcher sendDeviceEventWithName:@"toggleElementInspector" body:nil];
}];
// Reload in normal mode
[commands registerKeyCommandWithInput:@"n"
modifierFlags:UIKeyModifierCommand
@ -225,32 +257,82 @@ RCT_EXPORT_MODULE()
}
}
- (void)addItem:(NSString *)title handler:(dispatch_block_t)handler
{
[_extraMenuItems addObject:[[RCTDevMenuItem alloc] initWithTitle:title handler:handler]];
}
- (NSArray *)menuItems
{
NSMutableArray *items = [NSMutableArray array];
[items addObject:[[RCTDevMenuItem alloc] initWithTitle:@"Reload" handler:^{
[self reload];
}]];
Class chromeExecutorClass = NSClassFromString(@"RCTWebSocketExecutor");
if (!chromeExecutorClass) {
[items addObject:[[RCTDevMenuItem alloc] initWithTitle:@"Chrome Debugger Unavailable" handler:^{
[[[UIAlertView alloc] initWithTitle:@"Chrome Debugger Unavailable"
message:@"You need to include the RCTWebSocket library to enable Chrome debugging"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}]];
} else {
BOOL isDebuggingInChrome = _executorClass && _executorClass == chromeExecutorClass;
NSString *debugTitleChrome = isDebuggingInChrome ? @"Disable Chrome Debugging" : @"Debug in Chrome";
[items addObject:[[RCTDevMenuItem alloc] initWithTitle:debugTitleChrome handler:^{
self.executorClass = isDebuggingInChrome ? Nil : chromeExecutorClass;
}]];
}
Class safariExecutorClass = NSClassFromString(@"RCTWebViewExecutor");
BOOL isDebuggingInSafari = _executorClass && _executorClass == safariExecutorClass;
NSString *debugTitleSafari = isDebuggingInSafari ? @"Disable Safari Debugging" : @"Debug in Safari";
[items addObject:[[RCTDevMenuItem alloc] initWithTitle:debugTitleSafari handler:^{
self.executorClass = isDebuggingInSafari ? Nil : safariExecutorClass;
}]];
NSString *fpsMonitor = _showFPS ? @"Hide FPS Monitor" : @"Show FPS Monitor";
[items addObject:[[RCTDevMenuItem alloc] initWithTitle:fpsMonitor handler:^{
self.showFPS = !_showFPS;
}]];
[items addObject:[[RCTDevMenuItem alloc] initWithTitle:@"Inspect Element" handler:^{
[_bridge.eventDispatcher sendDeviceEventWithName:@"toggleElementInspector" body:nil];
}]];
if (_liveReloadURL) {
NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload";
[items addObject:[[RCTDevMenuItem alloc] initWithTitle:liveReloadTitle handler:^{
self.liveReloadEnabled = !_liveReloadEnabled;
}]];
NSString *profilingTitle = RCTProfileIsProfiling() ? @"Stop Profiling" : @"Start Profiling";
[items addObject:[[RCTDevMenuItem alloc] initWithTitle:profilingTitle handler:^{
self.profilingEnabled = !_profilingEnabled;
}]];
}
[items addObjectsFromArray:_extraMenuItems];
return items;
}
RCT_EXPORT_METHOD(show)
{
if (_actionSheet || !_bridge) {
return;
}
NSString *debugTitleChrome = _executorClass && _executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Debug in Chrome";
NSString *debugTitleSafari = _executorClass && _executorClass == NSClassFromString(@"RCTWebViewExecutor") ? @"Disable Safari Debugging" : @"Debug in Safari";
NSString *fpsMonitor = _showFPS ? @"Hide FPS Monitor" : @"Show FPS Monitor";
UIActionSheet *actionSheet = [[UIActionSheet alloc] init];
actionSheet.title = @"React Native: Development";
actionSheet.delegate = self;
UIActionSheet *actionSheet =
[[UIActionSheet alloc] initWithTitle:@"React Native: Development"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, fpsMonitor, nil];
[actionSheet addButtonWithTitle:@"Inspect Element"];
if (_liveReloadURL) {
NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload";
NSString *profilingTitle = RCTProfileIsProfiling() ? @"Stop Profiling" : @"Start Profiling";
[actionSheet addButtonWithTitle:liveReloadTitle];
[actionSheet addButtonWithTitle:profilingTitle];
NSArray *items = [self menuItems];
for (RCTDevMenuItem *item in items) {
[actionSheet addButtonWithTitle:item.title];
}
[actionSheet addButtonWithTitle:@"Cancel"];
@ -259,13 +341,7 @@ RCT_EXPORT_METHOD(show)
actionSheet.actionSheetStyle = UIBarStyleBlack;
[actionSheet showInView:[UIApplication sharedApplication].keyWindow.rootViewController.view];
_actionSheet = actionSheet;
}
RCT_EXPORT_METHOD(reload)
{
_jsLoaded = NO;
_liveReloadURL = nil;
[_bridge reload];
_presentedItems = items;
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
@ -275,48 +351,16 @@ RCT_EXPORT_METHOD(reload)
return;
}
switch (buttonIndex) {
case 0: {
[self reload];
break;
}
case 1: {
Class cls = NSClassFromString(@"RCTWebSocketExecutor");
if (!cls) {
[[[UIAlertView alloc] initWithTitle:@"Chrome Debugger Unavailable"
message:@"You need to include the RCTWebSocket library to enable Chrome debugging"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
return;
}
self.executorClass = (_executorClass == cls) ? Nil : cls;
break;
}
case 2: {
Class cls = NSClassFromString(@"RCTWebViewExecutor");
self.executorClass = (_executorClass == cls) ? Nil : cls;
break;
}
case 3: {
self.showFPS = !_showFPS;
break;
}
case 4: {
[_bridge.eventDispatcher sendDeviceEventWithName:@"toggleElementInspector" body:nil];
break;
}
case 5: {
self.liveReloadEnabled = !_liveReloadEnabled;
break;
}
case 6: {
self.profilingEnabled = !_profilingEnabled;
break;
}
default:
break;
}
RCTDevMenuItem *item = _presentedItems[buttonIndex];
item.handler();
return;
}
RCT_EXPORT_METHOD(reload)
{
_jsLoaded = NO;
_liveReloadURL = nil;
[_bridge reload];
}
- (void)setShakeToShow:(BOOL)shakeToShow
@ -438,6 +482,7 @@ RCT_EXPORT_METHOD(reload)
- (void)show {}
- (void)reload {}
- (void)addItem:(NSString *)title handler:(dispatch_block_t)handler {}
@end

Просмотреть файл

@ -25,6 +25,8 @@ NSString *const RCTProfileDidEndProfiling;
#if RCT_DEV
@class RCTBridge;
#define RCTProfileBeginFlowEvent() \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
@ -45,14 +47,14 @@ RCT_EXTERN BOOL RCTProfileIsProfiling(void);
/**
* Start collecting profiling information
*/
RCT_EXTERN void RCTProfileInit(void);
RCT_EXTERN void RCTProfileInit(RCTBridge *);
/**
* Stop profiling and return a JSON string of the collected data - The data
* returned is compliant with google's trace event format - the format used
* as input to trace-viewer
*/
RCT_EXTERN NSString *RCTProfileEnd(void);
RCT_EXTERN NSString *RCTProfileEnd(RCTBridge *);
/**
* Collects the initial event information for the event and returns a reference ID

Просмотреть файл

@ -10,10 +10,13 @@
#import "RCTProfile.h"
#import <mach/mach.h>
#import <objc/message.h>
#import <objc/runtime.h>
#import <UIKit/UIKit.h>
#import "RCTAssert.h"
#import "RCTBridge.h"
#import "RCTDefines.h"
#import "RCTUtils.h"
@ -32,6 +35,7 @@ NSDictionary *RCTProfileGetMemoryUsage(void);
NSString const *RCTProfileTraceEvents = @"traceEvents";
NSString const *RCTProfileSamples = @"samples";
NSString *const RCTProfilePrefix = @"rct_profile_";
#pragma mark - Variables
@ -92,6 +96,111 @@ NSDictionary *RCTProfileGetMemoryUsage(void)
}
}
#pragma mark - Module hooks
@interface RCTBridge (Private)
- (void)dispatchBlock:(dispatch_block_t)block forModule:(id<RCTBridgeModule>)module;
@end
static const char *RCTProfileProxyClassName(Class);
static const char *RCTProfileProxyClassName(Class class)
{
return [RCTProfilePrefix stringByAppendingString:NSStringFromClass(class)].UTF8String;
}
static SEL RCTProfileProxySelector(SEL);
static SEL RCTProfileProxySelector(SEL selector)
{
NSString *selectorName = NSStringFromSelector(selector);
return NSSelectorFromString([RCTProfilePrefix stringByAppendingString:selectorName]);
}
static void RCTProfileForwardInvocation(NSObject *, SEL, NSInvocation *);
static void RCTProfileForwardInvocation(NSObject *self, SEL cmd, NSInvocation *invocation)
{
NSString *name = [NSString stringWithFormat:@"-[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(invocation.selector)];
SEL newSel = RCTProfileProxySelector(invocation.selector);
if ([object_getClass(self) instancesRespondToSelector:newSel]) {
invocation.selector = newSel;
RCTProfileBeginEvent();
[invocation invoke];
RCTProfileEndEvent(name, @"objc_call,modules,auto", nil);
} else {
// Use original selector to don't change error message
[self doesNotRecognizeSelector:invocation.selector];
}
}
static IMP RCTProfileMsgForward(NSObject *, SEL);
static IMP RCTProfileMsgForward(NSObject *self, SEL selector)
{
IMP imp = _objc_msgForward;
#if !defined(__arm64__)
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
if (signature.methodReturnType[0] == _C_STRUCT_B && signature.methodReturnLength > 8) {
imp = _objc_msgForward_stret;
}
#endif
return imp;
}
static void RCTProfileHookModules(RCTBridge *);
static void RCTProfileHookModules(RCTBridge *bridge)
{
[bridge.modules enumerateKeysAndObjectsUsingBlock:^(NSString *className, id<RCTBridgeModule> module, BOOL *stop) {
[bridge dispatchBlock:^{
Class moduleClass = object_getClass(module);
Class proxyClass = objc_allocateClassPair(moduleClass, RCTProfileProxyClassName(moduleClass), 0);
unsigned int methodCount;
Method *methods = class_copyMethodList(moduleClass, &methodCount);
for (NSUInteger i = 0; i < methodCount; i++) {
Method method = methods[i];
SEL selector = method_getName(method);
if ([NSStringFromSelector(selector) hasPrefix:@"rct"] || [NSObject instancesRespondToSelector:selector]) {
continue;
}
IMP originalIMP = method_getImplementation(method);
const char *returnType = method_getTypeEncoding(method);
class_addMethod(proxyClass, selector, RCTProfileMsgForward(module, selector), returnType);
class_addMethod(proxyClass, RCTProfileProxySelector(selector), originalIMP, returnType);
}
free(methods);
for (Class cls in @[proxyClass, object_getClass(proxyClass)]) {
Method oldImp = class_getInstanceMethod(cls, @selector(class));
class_replaceMethod(cls, @selector(class), imp_implementationWithBlock(^{ return moduleClass; }), method_getTypeEncoding(oldImp));
}
IMP originalFwd = class_replaceMethod(moduleClass, @selector(forwardInvocation:), (IMP)RCTProfileForwardInvocation, "v@:@");
if (originalFwd != NULL) {
class_addMethod(proxyClass, RCTProfileProxySelector(@selector(forwardInvocation:)), originalFwd, "v@:@");
}
objc_registerClassPair(proxyClass);
object_setClass(module, proxyClass);
} forModule:module];
}];
}
void RCTProfileUnhookModules(RCTBridge *);
void RCTProfileUnhookModules(RCTBridge *bridge)
{
[bridge.modules enumerateKeysAndObjectsUsingBlock:^(NSString *className, id<RCTBridgeModule> module, BOOL *stop) {
[bridge dispatchBlock:^{
Class proxyClass = object_getClass(module);
if (module.class != proxyClass) {
object_setClass(module, module.class);
objc_disposeClassPair(proxyClass);
}
} forModule:module];
}];
}
#pragma mark - Public Functions
BOOL RCTProfileIsProfiling(void)
@ -102,8 +211,10 @@ BOOL RCTProfileIsProfiling(void)
return profiling;
}
void RCTProfileInit(void)
void RCTProfileInit(RCTBridge *bridge)
{
RCTProfileHookModules(bridge);
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_RCTProfileLock = [[NSLock alloc] init];
@ -121,7 +232,7 @@ void RCTProfileInit(void)
object:nil];
}
NSString *RCTProfileEnd(void)
NSString *RCTProfileEnd(RCTBridge *bridge)
{
[[NSNotificationCenter defaultCenter] postNotificationName:RCTProfileDidEndProfiling
object:nil];
@ -132,6 +243,9 @@ NSString *RCTProfileEnd(void)
RCTProfileInfo = nil;
RCTProfileOngoingEvents = nil;
);
RCTProfileUnhookModules(bridge);
return log;
}

Просмотреть файл

@ -85,11 +85,6 @@
selector:@selector(dismiss)
name:RCTReloadNotification
object:nil];
[notificationCenter addObserver:self
selector:@selector(dismiss)
name:RCTJavaScriptDidLoadNotification
object:nil];
}
return self;
}

Просмотреть файл

@ -36,8 +36,6 @@
BOOL _recordingInteractionTiming;
CFTimeInterval _mostRecentEnqueueJS;
NSMutableArray *_pendingTouches;
NSMutableArray *_bridgeInteractionTiming;
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
@ -52,9 +50,6 @@
_reactTouches = [[NSMutableArray alloc] init];
_touchViews = [[NSMutableArray alloc] init];
_pendingTouches = [[NSMutableArray alloc] init];
_bridgeInteractionTiming = [[NSMutableArray alloc] init];
// `cancelsTouchesInView` is needed in order to be used as a top level
// event delegated recognizer. Otherwise, lower-level components not built
// using RCT, will fail to recognize gestures.
@ -94,11 +89,11 @@ typedef NS_ENUM(NSInteger, RCTTouchEventType) {
return;
}
// Get new, unique touch id
// Get new, unique touch identifier for the react touch
const NSUInteger RCTMaxTouches = 11; // This is the maximum supported by iDevices
NSInteger touchID = ([_reactTouches.lastObject[@"target"] integerValue] + 1) % RCTMaxTouches;
NSInteger touchID = ([_reactTouches.lastObject[@"identifier"] integerValue] + 1) % RCTMaxTouches;
for (NSDictionary *reactTouch in _reactTouches) {
NSInteger usedID = [reactTouch[@"target"] integerValue];
NSInteger usedID = [reactTouch[@"identifier"] integerValue];
if (usedID == touchID) {
// ID has already been used, try next value
touchID ++;

Просмотреть файл

@ -18,6 +18,8 @@
// Utility functions for JSON object <-> string serialization/deserialization
RCT_EXTERN NSString *RCTJSONStringify(id jsonObject, NSError **error);
RCT_EXTERN id RCTJSONParse(NSString *jsonString, NSError **error);
RCT_EXTERN id RCTJSONParseMutable(NSString *jsonString, NSError **error);
RCT_EXTERN id RCTJSONParseWithOptions(NSString *jsonString, NSError **error, NSJSONReadingOptions options);
// Strip non JSON-safe values from an object graph
RCT_EXTERN id RCTJSONClean(id object);

Просмотреть файл

@ -24,7 +24,7 @@ NSString *RCTJSONStringify(id jsonObject, NSError **error)
return jsonData ? [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] : nil;
}
id RCTJSONParse(NSString *jsonString, NSError **error)
id RCTJSONParseWithOptions(NSString *jsonString, NSError **error, NSJSONReadingOptions options)
{
if (!jsonString) {
return nil;
@ -39,7 +39,15 @@ id RCTJSONParse(NSString *jsonString, NSError **error)
return nil;
}
}
return [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:error];
return [NSJSONSerialization JSONObjectWithData:jsonData options:options error:error];
}
id RCTJSONParse(NSString *jsonString, NSError **error) {
return RCTJSONParseWithOptions(jsonString, error, NSJSONReadingAllowFragments);
}
id RCTJSONParseMutable(NSString *jsonString, NSError **error) {
return RCTJSONParseWithOptions(jsonString, error, NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves);
}
id RCTJSONClean(id object)

Просмотреть файл

@ -61,6 +61,34 @@ static id RCTReadFile(NSString *filePath, NSString *key, NSDictionary **errorOut
return nil;
}
// Only merges objects - all other types are just clobbered (including arrays)
static void RCTMergeRecursive(NSMutableDictionary *destination, NSDictionary *source)
{
for (NSString *key in source) {
id sourceValue = source[key];
if ([sourceValue isKindOfClass:[NSDictionary class]]) {
id destinationValue = destination[key];
NSMutableDictionary *nestedDestination;
if ([destinationValue classForCoder] == [NSMutableDictionary class]) {
nestedDestination = destinationValue;
} else {
if ([destinationValue isKindOfClass:[NSDictionary class]]) {
// Ideally we wouldn't eagerly copy here...
nestedDestination = [destinationValue mutableCopy];
} else {
destination[key] = [sourceValue copy];
}
}
if (nestedDestination) {
RCTMergeRecursive(nestedDestination, sourceValue);
destination[key] = nestedDestination;
}
} else {
destination[key] = sourceValue;
}
}
}
#pragma mark - RCTAsyncLocalStorage
@implementation RCTAsyncLocalStorage
@ -135,13 +163,19 @@ RCT_EXPORT_MODULE()
if (errorOut) {
return errorOut;
}
id value = [self _getValueForKey:key errorOut:&errorOut];
[result addObject:@[key, value ?: [NSNull null]]]; // Insert null if missing or failure.
return errorOut;
}
- (NSString *)_getValueForKey:(NSString *)key errorOut:(NSDictionary **)errorOut
{
id value = _manifest[key]; // nil means missing, null means there is a data file, anything else is an inline value.
if (value == [NSNull null]) {
NSString *filePath = [self _filePathForKey:key];
value = RCTReadFile(filePath, key, &errorOut);
value = RCTReadFile(filePath, key, errorOut);
}
[result addObject:@[key, value ?: [NSNull null]]]; // Insert null if missing or failure.
return errorOut;
return value;
}
- (id)_writeEntry:(NSArray *)entry
@ -198,7 +232,6 @@ RCT_EXPORT_METHOD(multiGet:(NSArray *)keys
id keyError = [self _appendItemForKey:key toArray:result];
RCTAppendError(keyError, &errors);
}
[self _writeManifest:&errors];
callback(@[errors ?: [NSNull null], result]);
}
@ -221,6 +254,38 @@ RCT_EXPORT_METHOD(multiSet:(NSArray *)kvPairs
}
}
RCT_EXPORT_METHOD(multiMerge:(NSArray *)kvPairs
callback:(RCTResponseSenderBlock)callback)
{
id errorOut = [self _ensureSetup];
if (errorOut) {
callback(@[@[errorOut]]);
return;
}
NSMutableArray *errors;
for (__strong NSArray *entry in kvPairs) {
id keyError;
NSString *value = [self _getValueForKey:entry[0] errorOut:&keyError];
if (keyError) {
RCTAppendError(keyError, &errors);
} else {
if (value) {
NSMutableDictionary *mergedVal = [RCTJSONParseMutable(value, &keyError) mutableCopy];
RCTMergeRecursive(mergedVal, RCTJSONParse(entry[1], &keyError));
entry = @[entry[0], RCTJSONStringify(mergedVal, &keyError)];
}
if (!keyError) {
keyError = [self _writeEntry:entry];
}
RCTAppendError(keyError, &errors);
}
}
[self _writeManifest:&errors];
if (callback) {
callback(@[errors ?: [NSNull null]]);
}
}
RCT_EXPORT_METHOD(multiRemove:(NSArray *)keys
callback:(RCTResponseSenderBlock)callback)
{

Просмотреть файл

@ -10,6 +10,14 @@
#import <UIKit/UIKit.h>
#import "RCTBridgeModule.h"
#import "RCTConvert.h"
@interface RCTConvert (UIStatusBar)
+ (UIStatusBarStyle)UIStatusBarStyle:(id)json;
+ (UIStatusBarAnimation)UIStatusBarAnimation:(id)json;
@end
@interface RCTStatusBarManager : NSObject <RCTBridgeModule>

Просмотреть файл

@ -11,6 +11,21 @@
#import "RCTLog.h"
@implementation RCTConvert (UIStatusBar)
RCT_ENUM_CONVERTER(UIStatusBarStyle, (@{
@"default": @(UIStatusBarStyleDefault),
@"light-content": @(UIStatusBarStyleLightContent),
}), UIStatusBarStyleDefault, integerValue);
RCT_ENUM_CONVERTER(UIStatusBarAnimation, (@{
@"none": @(UIStatusBarAnimationNone),
@"fade": @(UIStatusBarAnimationFade),
@"slide": @(UIStatusBarAnimationSlide),
}), UIStatusBarAnimationNone, integerValue);
@end
@implementation RCTStatusBarManager
static BOOL RCTViewControllerBasedStatusBarAppearance()
@ -18,7 +33,8 @@ static BOOL RCTViewControllerBasedStatusBarAppearance()
static BOOL value;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
value = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"] ?: @YES boolValue];
value = [[[NSBundle mainBundle] objectForInfoDictionaryKey:
@"UIViewControllerBasedStatusBarAppearance"] ?: @YES boolValue];
});
return value;
@ -55,19 +71,4 @@ RCT_EXPORT_METHOD(setHidden:(BOOL)hidden
}
}
- (NSDictionary *)constantsToExport
{
return @{
@"Style": @{
@"default": @(UIStatusBarStyleDefault),
@"lightContent": @(UIStatusBarStyleLightContent),
},
@"Animation": @{
@"none": @(UIStatusBarAnimationNone),
@"fade": @(UIStatusBarAnimationFade),
@"slide": @(UIStatusBarAnimationSlide),
},
};
}
@end

Просмотреть файл

@ -1397,11 +1397,6 @@ RCT_EXPORT_METHOD(clearJSResponder)
NSMutableDictionary *allJSConstants = [@{
@"customBubblingEventTypes": [self customBubblingEventTypes],
@"customDirectEventTypes": [self customDirectEventTypes],
@"NSTextAlignment": @{
@"Left": @(NSTextAlignmentLeft),
@"Center": @(NSTextAlignmentCenter),
@"Right": @(NSTextAlignmentRight),
},
@"Dimensions": @{
@"window": @{
@"width": @(RCTScreenSize().width),
@ -1413,73 +1408,6 @@ RCT_EXPORT_METHOD(clearJSResponder)
@"height": @(RCTScreenSize().height),
},
},
@"StyleConstants": @{
@"PointerEventsValues": @{
@"none": @(RCTPointerEventsNone),
@"box-none": @(RCTPointerEventsBoxNone),
@"box-only": @(RCTPointerEventsBoxOnly),
@"auto": @(RCTPointerEventsUnspecified),
},
},
@"UIText": @{
@"AutocapitalizationType": @{
@"characters": @(UITextAutocapitalizationTypeAllCharacters),
@"sentences": @(UITextAutocapitalizationTypeSentences),
@"words": @(UITextAutocapitalizationTypeWords),
@"none": @(UITextAutocapitalizationTypeNone),
},
},
@"UITextField": @{
@"clearButtonMode": @{
@"never": @(UITextFieldViewModeNever),
@"while-editing": @(UITextFieldViewModeWhileEditing),
@"unless-editing": @(UITextFieldViewModeUnlessEditing),
@"always": @(UITextFieldViewModeAlways),
},
},
@"UIKeyboardType": @{
@"default": @(UIKeyboardTypeDefault),
@"ascii-capable": @(UIKeyboardTypeASCIICapable),
@"numbers-and-punctuation": @(UIKeyboardTypeNumbersAndPunctuation),
@"url": @(UIKeyboardTypeURL),
@"number-pad": @(UIKeyboardTypeNumberPad),
@"phone-pad": @(UIKeyboardTypePhonePad),
@"name-phone-pad": @(UIKeyboardTypeNamePhonePad),
@"decimal-pad": @(UIKeyboardTypeDecimalPad),
@"email-address": @(UIKeyboardTypeEmailAddress),
@"twitter": @(UIKeyboardTypeTwitter),
@"web-search": @(UIKeyboardTypeWebSearch),
},
@"UIReturnKeyType": @{
@"default": @(UIReturnKeyDefault),
@"go": @(UIReturnKeyGo),
@"google": @(UIReturnKeyGoogle),
@"join": @(UIReturnKeyJoin),
@"next": @(UIReturnKeyNext),
@"route": @(UIReturnKeyRoute),
@"search": @(UIReturnKeySearch),
@"send": @(UIReturnKeySend),
@"yahoo": @(UIReturnKeyYahoo),
@"done": @(UIReturnKeyDone),
@"emergency-call": @(UIReturnKeyEmergencyCall),
},
@"UIView": @{
@"ContentMode": @{
@"ScaleToFill": @(UIViewContentModeScaleToFill),
@"ScaleAspectFit": @(UIViewContentModeScaleAspectFit),
@"ScaleAspectFill": @(UIViewContentModeScaleAspectFill),
@"Redraw": @(UIViewContentModeRedraw),
@"Center": @(UIViewContentModeCenter),
@"Top": @(UIViewContentModeTop),
@"Bottom": @(UIViewContentModeBottom),
@"Left": @(UIViewContentModeLeft),
@"Right": @(UIViewContentModeRight),
@"TopLeft": @(UIViewContentModeTopLeft),
@"TopRight": @(UIViewContentModeTopRight),
@"BottomLeft": @(UIViewContentModeBottomLeft),
@"BottomRight": @(UIViewContentModeBottomRight),
},
},
} mutableCopy];
[_viewManagers enumerateKeysAndObjectsUsingBlock:^(NSString *name, RCTViewManager *manager, BOOL *stop) {

Просмотреть файл

@ -16,6 +16,7 @@
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; };
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; };
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */; };
13513F3C1B1F43F400FCE529 /* RCTProgressViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13513F3B1B1F43F400FCE529 /* RCTProgressViewManager.m */; };
13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */; };
1372B70A1AB030C200659ED6 /* RCTAppState.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7091AB030C200659ED6 /* RCTAppState.m */; };
137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 137327E01AA5CF210034F82E /* RCTTabBar.m */; };
@ -104,6 +105,8 @@
134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTContextExecutor.m; sourceTree = "<group>"; };
134FCB3B1A6E7F0800051CC8 /* RCTWebViewExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebViewExecutor.h; sourceTree = "<group>"; };
134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebViewExecutor.m; sourceTree = "<group>"; };
13513F3A1B1F43F400FCE529 /* RCTProgressViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTProgressViewManager.h; sourceTree = "<group>"; };
13513F3B1B1F43F400FCE529 /* RCTProgressViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTProgressViewManager.m; sourceTree = "<group>"; };
13723B4E1A82FD3C00F88898 /* RCTStatusBarManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStatusBarManager.h; sourceTree = "<group>"; };
13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStatusBarManager.m; sourceTree = "<group>"; };
1372B7081AB030C200659ED6 /* RCTAppState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAppState.h; sourceTree = "<group>"; };
@ -307,6 +310,8 @@
58114A141AAE854800E7D092 /* RCTPickerManager.h */,
58114A151AAE854800E7D092 /* RCTPickerManager.m */,
13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */,
13513F3A1B1F43F400FCE529 /* RCTProgressViewManager.h */,
13513F3B1B1F43F400FCE529 /* RCTProgressViewManager.m */,
131B6AF01AF1093D00FFC3E0 /* RCTSegmentedControl.h */,
131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */,
131B6AF21AF1093D00FFC3E0 /* RCTSegmentedControlManager.h */,
@ -524,6 +529,7 @@
58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */,
832348161A77A5AA00B55238 /* Layout.c in Sources */,
14F4D38B1AE1B7E40049C042 /* RCTProfile.m in Sources */,
13513F3C1B1F43F400FCE529 /* RCTProgressViewManager.m in Sources */,
14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */,
14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */,
13B080201A69489C00A75B9A /* RCTActivityIndicatorViewManager.m in Sources */,

Просмотреть файл

@ -8,6 +8,13 @@
*/
#import "RCTViewManager.h"
#import "RCTConvert.h"
@interface RCTConvert(UIDatePicker)
+ (UIDatePickerMode)UIDatePickerMode:(id)json;
@end
@interface RCTDatePickerManager : RCTViewManager

Просмотреть файл

@ -10,7 +10,6 @@
#import "RCTDatePickerManager.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "UIView+React.h"
@ -20,7 +19,7 @@ RCT_ENUM_CONVERTER(UIDatePickerMode, (@{
@"time": @(UIDatePickerModeTime),
@"date": @(UIDatePickerModeDate),
@"datetime": @(UIDatePickerModeDateAndTime),
//@"countdown": @(UIDatePickerModeCountDownTimer) // not supported yet
@"countdown": @(UIDatePickerModeCountDownTimer), // not supported yet
}), UIDatePickerModeTime, integerValue)
@end
@ -31,9 +30,12 @@ RCT_EXPORT_MODULE()
- (UIView *)view
{
// TODO: we crash here if the RCTDatePickerManager is released
// while the UIDatePicker is still sending onChange events. To
// fix this we should maybe subclass UIDatePicker and make it
// be its own event target.
UIDatePicker *picker = [[UIDatePicker alloc] init];
[picker addTarget:self
action:@selector(onChange:)
[picker addTarget:self action:@selector(onChange:)
forControlEvents:UIControlEventValueChanged];
return picker;
}
@ -56,17 +58,10 @@ RCT_REMAP_VIEW_PROPERTY(timeZoneOffsetInMinutes, timeZone, NSTimeZone)
- (NSDictionary *)constantsToExport
{
UIDatePicker *dp = [[UIDatePicker alloc] init];
[dp layoutIfNeeded];
UIDatePicker *view = [[UIDatePicker alloc] init];
return @{
@"ComponentHeight": @(CGRectGetHeight(dp.frame)),
@"ComponentWidth": @(CGRectGetWidth(dp.frame)),
@"DatePickerModes": @{
@"time": @(UIDatePickerModeTime),
@"date": @(UIDatePickerModeDate),
@"datetime": @(UIDatePickerModeDateAndTime),
}
@"ComponentHeight": @(view.intrinsicContentSize.height),
@"ComponentWidth": @(view.intrinsicContentSize.width),
};
}

Просмотреть файл

@ -27,10 +27,10 @@ RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSInteger)
- (NSDictionary *)constantsToExport
{
RCTPicker *pv = [[RCTPicker alloc] init];
RCTPicker *view = [[RCTPicker alloc] init];
return @{
@"ComponentHeight": @(CGRectGetHeight(pv.frame)),
@"ComponentWidth": @(CGRectGetWidth(pv.frame))
@"ComponentHeight": @(view.intrinsicContentSize.height),
@"ComponentWidth": @(view.intrinsicContentSize.width)
};
}

Просмотреть файл

@ -0,0 +1,14 @@
/**
* 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.
*/
#import "RCTViewManager.h"
@interface RCTProgressViewManager : RCTViewManager
@end

Просмотреть файл

@ -0,0 +1,47 @@
/**
* 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.
*/
#import "RCTProgressViewManager.h"
#import "RCTConvert.h"
@implementation RCTConvert (RCTProgressViewManager)
RCT_ENUM_CONVERTER(UIProgressViewStyle, (@{
@"default": @(UIProgressViewStyleDefault),
@"bar": @(UIProgressViewStyleBar),
}), UIProgressViewStyleDefault, integerValue)
@end
@implementation RCTProgressViewManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [[UIProgressView alloc] init];
}
RCT_EXPORT_VIEW_PROPERTY(progressViewStyle, UIProgressViewStyle)
RCT_EXPORT_VIEW_PROPERTY(progress, float)
RCT_EXPORT_VIEW_PROPERTY(progressTintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(trackTintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(progressImage, UIImage)
RCT_EXPORT_VIEW_PROPERTY(trackImage, UIImage)
- (NSDictionary *)constantsToExport
{
UIProgressView *view = [[UIProgressView alloc] init];
return @{
@"ComponentHeight": @(view.intrinsicContentSize.height),
};
}
@end

Просмотреть файл

@ -8,6 +8,13 @@
*/
#import "RCTViewManager.h"
#import "RCTConvert.h"
@interface RCTConvert (UIScrollView)
+ (UIScrollViewKeyboardDismissMode)UIScrollViewKeyboardDismissMode:(id)json;
@end
@interface RCTScrollViewManager : RCTViewManager

Просмотреть файл

@ -10,11 +10,22 @@
#import "RCTScrollViewManager.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTScrollView.h"
#import "RCTSparseArray.h"
#import "RCTUIManager.h"
@implementation RCTConvert (UIScrollView)
RCT_ENUM_CONVERTER(UIScrollViewKeyboardDismissMode, (@{
@"none": @(UIScrollViewKeyboardDismissModeNone),
@"on-drag": @(UIScrollViewKeyboardDismissModeOnDrag),
@"interactive": @(UIScrollViewKeyboardDismissModeInteractive),
// Backwards compatibility
@"onDrag": @(UIScrollViewKeyboardDismissModeOnDrag),
}), UIScrollViewKeyboardDismissModeNone, integerValue)
@end
@implementation RCTScrollViewManager
RCT_EXPORT_MODULE()
@ -53,14 +64,10 @@ RCT_DEPRECATED_VIEW_PROPERTY(throttleScrollCallbackMS, scrollEventThrottle)
- (NSDictionary *)constantsToExport
{
return @{
// TODO: unused - remove these?
@"DecelerationRate": @{
@"Normal": @(UIScrollViewDecelerationRateNormal),
@"Fast": @(UIScrollViewDecelerationRateFast),
},
@"KeyboardDismissMode": @{
@"None": @(UIScrollViewKeyboardDismissModeNone),
@"Interactive": @(UIScrollViewKeyboardDismissModeInteractive),
@"OnDrag": @(UIScrollViewKeyboardDismissModeOnDrag),
@"normal": @(UIScrollViewDecelerationRateNormal),
@"fast": @(UIScrollViewDecelerationRateFast),
},
};
}

Просмотреть файл

@ -25,7 +25,7 @@ RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(caretHidden, BOOL)
RCT_EXPORT_VIEW_PROPERTY(autoCorrect, BOOL)
RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL)
RCT_REMAP_VIEW_PROPERTY(editable, enabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString)
RCT_EXPORT_VIEW_PROPERTY(placeholderTextColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(text, NSString)
@ -36,6 +36,7 @@ RCT_EXPORT_VIEW_PROPERTY(keyboardType, UIKeyboardType)
RCT_EXPORT_VIEW_PROPERTY(returnKeyType, UIReturnKeyType)
RCT_EXPORT_VIEW_PROPERTY(enablesReturnKeyAutomatically, BOOL)
RCT_EXPORT_VIEW_PROPERTY(secureTextEntry, BOOL)
RCT_REMAP_VIEW_PROPERTY(password, secureTextEntry, BOOL) // backwards compatibility
RCT_REMAP_VIEW_PROPERTY(color, textColor, UIColor)
RCT_REMAP_VIEW_PROPERTY(autoCapitalize, autocapitalizationType, UITextAutocapitalizationType)
RCT_CUSTOM_VIEW_PROPERTY(fontSize, CGFloat, RCTTextField)

Просмотреть файл

@ -62,7 +62,7 @@ if the option is boolean `1/0` or `true/false` is accepted.
Here are the current options the packager accepts:
* `dev` boolean, defaults to true: sets a global `__DEV__` variable
which will effect how the React Nativeg core libraries behave.
which will effect how the React Native core libraries behave.
* `minify` boolean, defaults to false: whether to minify the bundle.
* `runModule` boolean, defaults to true: whether to require your entry
point module. So if you requested `moduleName`, this option will add