mirror of
https://github.com/status-im/react-native.git
synced 2025-02-28 09:00:55 +00:00
Updates from Fri 5 Jun
This commit is contained in:
commit
0293def7a9
@ -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}
|
||||
/>;
|
||||
|
83
Examples/UIExplorer/ProgressViewIOSExample.js
Normal file
83
Examples/UIExplorer/ProgressViewIOSExample.js
Normal file
@ -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;
|
83
Libraries/Components/ProgressViewIOS/ProgressViewIOS.ios.js
Normal file
83
Libraries/Components/ProgressViewIOS/ProgressViewIOS.ios.js
Normal file
@ -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,
|
||||
|
24
Libraries/Components/Touchable/ensurePositiveDelayProps.js
Normal file
24
Libraries/Components/Touchable/ensurePositiveDelayProps.js
Normal file
@ -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;
|
||||
}
|
||||
|
38
Libraries/ReactIOS/InspectorOverlay/BorderBox.js
Normal file
38
Libraries/ReactIOS/InspectorOverlay/BorderBox.js
Normal file
@ -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;
|
||||
|
113
Libraries/ReactIOS/InspectorOverlay/BoxInspector.js
Normal file
113
Libraries/ReactIOS/InspectorOverlay/BoxInspector.js
Normal file
@ -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} × {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;
|
||||
|
74
Libraries/ReactIOS/InspectorOverlay/ElementBox.js
Normal file
74
Libraries/ReactIOS/InspectorOverlay/ElementBox.js
Normal file
@ -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;
|
||||
|
105
Libraries/ReactIOS/InspectorOverlay/ElementProperties.js
Normal file
105
Libraries/ReactIOS/InspectorOverlay/ElementProperties.js
Normal file
@ -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}>▸</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;
|
63
Libraries/ReactIOS/InspectorOverlay/StyleInspector.js
Normal file
63
Libraries/ReactIOS/InspectorOverlay/StyleInspector.js
Normal file
@ -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;
|
||||
|
59
Libraries/ReactIOS/InspectorOverlay/resolveBoxStyle.js
Normal file
59
Libraries/ReactIOS/InspectorOverlay/resolveBoxStyle.js
Normal file
@ -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;
|
||||
|
19
Libraries/Utilities/mapWithSeparator.js
Normal file
19
Libraries/Utilities/mapWithSeparator.js
Normal file
@ -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
vendored
9
Libraries/react-native/react-native.js
vendored
@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
14
React/Views/RCTProgressViewManager.h
Normal file
14
React/Views/RCTProgressViewManager.h
Normal file
@ -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
|
47
React/Views/RCTProgressViewManager.m
Normal file
47
React/Views/RCTProgressViewManager.m
Normal file
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user