Updates from Mon 30 Mar
- [ReactNative] Clean up no longer needed reference to NavigationBarClass | Philipp von Weitershausen - [TextInput] returnKeyType, enablesReturnKeyAutomatically, secureTextEntry, more keyboardTypes | Tadeu Zagallo - [ReactNative] PropTypes for NavigationBars | Philipp von Weitershausen - Changed LayoutAnimation to use ms instead of seconds for consistency | Nick Lockwood - Better date support | Nick Lockwood - Renamed throttleScrollCallbackMS to scrollEventThrottle | Nick Lockwood - Fixed threading issues in RCTImageDownloader | Nick Lockwood - [iOS][Assets]: Cleaning up more 1x png from Libraries | Radu Marin - [ReactNative][docs] LinkingIOS | Tadeu Zagallo - Fixing TouchableOpacity and TouchableHighlight documentation | Ben Alpert - [react-native] Add React.addons.createFragment | Ben Alpert
This commit is contained in:
parent
02298b59e2
commit
9a4ee17adb
|
@ -31,8 +31,16 @@ var MovieScreen = require('./MovieScreen');
|
|||
|
||||
var fetch = require('fetch');
|
||||
|
||||
/**
|
||||
* This is for demo purposes only, and rate limited.
|
||||
* In case you want to use the Rotten Tomatoes' API on a real app you should
|
||||
* create an account at http://developer.rottentomatoes.com/
|
||||
*/
|
||||
var API_URL = 'http://api.rottentomatoes.com/api/public/v1.0/';
|
||||
var API_KEYS = ['7waqfqbprs7pajbz28mqf6vz', 'y4vwv8m33hed9ety83jmv52f'];
|
||||
var API_KEYS = [
|
||||
'7waqfqbprs7pajbz28mqf6vz',
|
||||
// 'y4vwv8m33hed9ety83jmv52f', Fallback api_key
|
||||
];
|
||||
|
||||
// Results should be cached keyed by the query
|
||||
// with values of null meaning "being fetched"
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00481BE61AC0C7FA00671115 /* libRCTWebSocketDebugger.a */; };
|
||||
00481BEA1AC0C89D00671115 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 00481BE91AC0C89D00671115 /* libicucore.dylib */; };
|
||||
008F07F31AC5B25A0029DE68 /* main.jsbundle in Resources */ = {isa = PBXBuildFile; fileRef = 008F07F21AC5B25A0029DE68 /* main.jsbundle */; };
|
||||
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
|
||||
00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302B41ABCB8E700DB3ED1 /* libRCTAdSupport.a */; };
|
||||
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
|
||||
|
@ -92,6 +93,7 @@
|
|||
/* Begin PBXFileReference section */
|
||||
00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocketDebugger.xcodeproj; path = ../../Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj; sourceTree = "<group>"; };
|
||||
00481BE91AC0C89D00671115 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = main.jsbundle; path = iOS/main.jsbundle; sourceTree = "<group>"; };
|
||||
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
|
||||
|
@ -189,6 +191,7 @@
|
|||
13B07FAE1A68108700A75B9A /* SampleApp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.m */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
|
@ -405,6 +408,7 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
008F07F31AC5B25A0029DE68 /* main.jsbundle in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
|
||||
);
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
// OPTION 2
|
||||
// Load from pre-bundled file on disk. To re-generate the static bundle, run
|
||||
//
|
||||
// $ curl http://localhost:8081/Examples/SampleApp/index.ios.bundle -o main.jsbundle
|
||||
// $ curl 'http://localhost:8081/Examples/SampleApp/index.ios.bundle?dev=false&minify=true' -o iOS/main.jsbundle
|
||||
//
|
||||
// and uncomment the next following line
|
||||
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
// Offline JS
|
||||
// To re-generate the offline bundle, run this from root of your project
|
||||
// $ curl 'http://localhost:8081/Examples/SampleApp/index.ios.bundle?dev=false&minify=true' -o iOS/main.jsbundle
|
||||
|
||||
throw new Error('Offline JS file is empty. See iOS/main.jsbundle for instructions');
|
|
@ -226,9 +226,9 @@ var styles = StyleSheet.create({
|
|||
var animations = {
|
||||
layout: {
|
||||
spring: {
|
||||
duration: 0.75,
|
||||
duration: 750,
|
||||
create: {
|
||||
duration: 0.3,
|
||||
duration: 300,
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
property: LayoutAnimation.Properties.opacity,
|
||||
},
|
||||
|
@ -238,13 +238,13 @@ var animations = {
|
|||
},
|
||||
},
|
||||
easeInEaseOut: {
|
||||
duration: 0.3,
|
||||
duration: 300,
|
||||
create: {
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
property: LayoutAnimation.Properties.scaleXY,
|
||||
},
|
||||
update: {
|
||||
delay: 0.1,
|
||||
delay: 100,
|
||||
type: LayoutAnimation.Types.easeInEaseOut,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -33,7 +33,7 @@ exports.examples = [
|
|||
return (
|
||||
<ScrollView
|
||||
onScroll={() => { console.log('onScroll!'); }}
|
||||
throttleScrollCallbackMS={200}
|
||||
scrollEventThrottle={200}
|
||||
contentInset={{top: -50}}
|
||||
style={styles.scrollView}>
|
||||
{THUMBS.map(createThumbRow)}
|
||||
|
|
|
@ -76,6 +76,15 @@ exports.examples = [
|
|||
</Text>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Padding',
|
||||
render: function() {
|
||||
return (
|
||||
<Text style={{padding: 10}}>
|
||||
This text is indented by 10px padding on all sides.
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Font Family',
|
||||
render: function() {
|
||||
|
|
|
@ -174,6 +174,89 @@ exports.examples = [
|
|||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Keyboard types',
|
||||
render: function() {
|
||||
var keyboardTypes = [
|
||||
'default',
|
||||
'ascii-capable',
|
||||
'numbers-and-punctuation',
|
||||
'url',
|
||||
'number-pad',
|
||||
'phone-pad',
|
||||
'name-phone-pad',
|
||||
'email-address',
|
||||
'decimal-pad',
|
||||
'twitter',
|
||||
'web-search',
|
||||
'numeric',
|
||||
];
|
||||
var examples = keyboardTypes.map((type) => {
|
||||
return (
|
||||
<WithLabel key={type} label={type}>
|
||||
<TextInput
|
||||
keyboardType={type}
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
);
|
||||
});
|
||||
return <View>{examples}</View>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Return key types',
|
||||
render: function() {
|
||||
var returnKeyTypes = [
|
||||
'default',
|
||||
'go',
|
||||
'google',
|
||||
'join',
|
||||
'next',
|
||||
'route',
|
||||
'search',
|
||||
'send',
|
||||
'yahoo',
|
||||
'done',
|
||||
'emergency-call',
|
||||
];
|
||||
var examples = returnKeyTypes.map((type) => {
|
||||
return (
|
||||
<WithLabel key={type} label={type}>
|
||||
<TextInput
|
||||
returnKeyType={type}
|
||||
style={styles.default}
|
||||
/>
|
||||
</WithLabel>
|
||||
);
|
||||
});
|
||||
return <View>{examples}</View>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Enable return key automatically',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="true">
|
||||
<TextInput enablesReturnKeyAutomatically={true} style={styles.default} />
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Secure text entry',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="true">
|
||||
<TextInput secureTextEntry={true} style={styles.default} value="abc" />
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Event handling',
|
||||
render: function(): ReactElement { return <TextEventsExample /> },
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
diff a/Libraries/FBReactKit/js/react-native-github/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Libraries/FBReactKit/js/react-native-github/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj (rejected hunks)
|
||||
@@ -19,6 +19,7 @@
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 147CED4B1AB34F8C00DA3E4C /* libRCTActionSheet.a */; };
|
||||
+ D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -78,6 +79,13 @@
|
||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RCTActionSheet;
|
||||
};
|
||||
+ D85B829B1AB6D5CE003F4FE2 /* PBXContainerItemProxy */ = {
|
||||
+ isa = PBXContainerItemProxy;
|
||||
+ containerPortal = D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */;
|
||||
+ proxyType = 2;
|
||||
+ remoteGlobalIDString = 832C81801AAF6DEF007FA2F7;
|
||||
+ remoteInfo = RCTVibration;
|
||||
+ };
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -98,6 +106,7 @@
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
|
||||
+ D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -112,6 +121,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
+ D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */,
|
||||
147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */,
|
||||
134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */,
|
||||
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */,
|
||||
@@ -145,6 +155,7 @@
|
||||
1316A21D1AA397F400C0188E /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
+ D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */,
|
||||
14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */,
|
||||
13417FFA1AA91531003F314A /* ReactKit.xcodeproj */,
|
||||
134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */,
|
||||
@@ -334,6 +353,10 @@
|
||||
ProjectRef = 13417FEA1AA914B8003F314A /* RCTText.xcodeproj */;
|
||||
},
|
||||
{
|
||||
+ ProductGroup = D85B82921AB6D5CE003F4FE2 /* Products */;
|
||||
+ ProjectRef = D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */;
|
||||
+ },
|
||||
+ {
|
||||
ProductGroup = 13417FFB1AA91531003F314A /* Products */;
|
||||
ProjectRef = 13417FFA1AA91531003F314A /* ReactKit.xcodeproj */;
|
||||
},
|
||||
@@ -396,6 +419,13 @@
|
||||
remoteRef = 147CED4A1AB34F8C00DA3E4C /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
+ D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */ = {
|
||||
+ isa = PBXReferenceProxy;
|
||||
+ fileType = archive.ar;
|
||||
+ path = libRCTVibration.a;
|
||||
+ remoteRef = D85B829B1AB6D5CE003F4FE2 /* PBXContainerItemProxy */;
|
||||
+ sourceTree = BUILT_PRODUCTS_DIR;
|
||||
+ };
|
||||
/* End PBXReferenceProxy section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
7
Examples/UIExplorer/UIExplorer.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
Examples/UIExplorer/UIExplorer.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:UIExplorer.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -1,10 +1,5 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "story-background.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 336 B |
Binary file not shown.
Before Width: | Height: | Size: 253 KiB After Width: | Height: | Size: 260 KiB |
|
@ -211,21 +211,27 @@ var ticksPerSecond = 60;
|
|||
var lastUsedTag = 0;
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Returns a new unique animation tag.
|
||||
*/
|
||||
allocateTag: function(): number {
|
||||
return ++lastUsedTag;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the values of the easing function at discrete ticks (60 per second).
|
||||
*/
|
||||
evaluateEasingFunction: function(duration: number, easing: string | EasingFunction): Array<number> {
|
||||
if (typeof easing === 'string') {
|
||||
easing = defaults[easing] || defaults.easeOutQuad;
|
||||
}
|
||||
|
||||
var tickCount = Math.round(duration * ticksPerSecond / 1000);
|
||||
var sample = [];
|
||||
var samples = [];
|
||||
for (var i = 0; i <= tickCount; i++) {
|
||||
sample.push(easing(i / tickCount));
|
||||
samples.push(easing.call(defaults, i / tickCount));
|
||||
}
|
||||
|
||||
return sample;
|
||||
return samples;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -92,13 +92,13 @@ var LayoutAnimation = {
|
|||
configChecker: configChecker,
|
||||
Presets: {
|
||||
easeInEaseOut: create(
|
||||
0.3, Types.easeInEaseOut, Properties.opacity
|
||||
300, Types.easeInEaseOut, Properties.opacity
|
||||
),
|
||||
linear: create(
|
||||
0.5, Types.linear, Properties.opacity
|
||||
500, Types.linear, Properties.opacity
|
||||
),
|
||||
spring: {
|
||||
duration: 0.7,
|
||||
duration: 700,
|
||||
create: {
|
||||
type: Types.linear,
|
||||
property: Properties.opacity,
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var NativeModules = require('NativeModules');
|
||||
var NativeModules = require('NativeModules');
|
||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
var Subscribable = require('Subscribable');
|
||||
|
|
|
@ -64,7 +64,7 @@ var ScrollView = React.createClass({
|
|||
showsHorizontalScrollIndicator: PropTypes.bool,
|
||||
showsVerticalScrollIndicator: PropTypes.bool,
|
||||
style: StyleSheetPropType(ViewStylePropTypes),
|
||||
throttleScrollCallbackMS: PropTypes.number, // null
|
||||
scrollEventThrottle: PropTypes.number, // null
|
||||
|
||||
/**
|
||||
* When true, the scroll view bounces horizontally when it reaches the end
|
||||
|
@ -211,12 +211,12 @@ var ScrollView = React.createClass({
|
|||
);
|
||||
}
|
||||
if (__DEV__) {
|
||||
if (this.props.onScroll && !this.props.throttleScrollCallbackMS) {
|
||||
if (this.props.onScroll && !this.props.scrollEventThrottle) {
|
||||
var onScroll = this.props.onScroll;
|
||||
this.props.onScroll = function() {
|
||||
console.log(
|
||||
'You specified `onScroll` on a <ScrollView> but not ' +
|
||||
'`throttleScrollCallbackMS`. You will only receive one event. ' +
|
||||
'`scrollEventThrottle`. You will only receive one event. ' +
|
||||
'Using `16` you get all the events but be aware that it may ' +
|
||||
'cause frame drops, use a bigger number if you don\'t need as ' +
|
||||
'much precision.'
|
||||
|
@ -325,7 +325,7 @@ var validAttributes = {
|
|||
showsHorizontalScrollIndicator: true,
|
||||
showsVerticalScrollIndicator: true,
|
||||
stickyHeaderIndices: {diff: deepDiffer},
|
||||
throttleScrollCallbackMS: true,
|
||||
scrollEventThrottle: true,
|
||||
zoomScale: true,
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ var DocumentSelectionState = require('DocumentSelectionState');
|
|||
var EventEmitter = require('EventEmitter');
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var RCTUIManager = require('NativeModules').UIManager;
|
||||
var Platform = require('Platform');
|
||||
var PropTypes = require('ReactPropTypes');
|
||||
var React = require('React');
|
||||
var ReactChildren = require('ReactChildren');
|
||||
|
@ -27,12 +28,12 @@ var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
|
|||
|
||||
var createReactIOSNativeComponentClass = require('createReactIOSNativeComponentClass');
|
||||
var emptyFunction = require('emptyFunction');
|
||||
var getObjectValues = require('getObjectValues');
|
||||
var invariant = require('invariant');
|
||||
var merge = require('merge');
|
||||
|
||||
var autoCapitalizeConsts = RCTUIManager.UIText.AutocapitalizationType;
|
||||
var clearButtonModeConsts = RCTUIManager.UITextField.clearButtonMode;
|
||||
var keyboardTypeConsts = RCTUIManager.UIKeyboardType;
|
||||
var returnKeyTypeConsts = RCTUIManager.UIReturnKeyType;
|
||||
|
||||
var RCTTextViewAttributes = merge(ReactIOSViewAttributes.UIView, {
|
||||
autoCorrect: true,
|
||||
|
@ -44,6 +45,9 @@ var RCTTextViewAttributes = merge(ReactIOSViewAttributes.UIView, {
|
|||
fontStyle: true,
|
||||
fontWeight: true,
|
||||
keyboardType: true,
|
||||
returnKeyType: true,
|
||||
enablesReturnKeyAutomatically: true,
|
||||
secureTextEntry: true,
|
||||
mostRecentEventCounter: true,
|
||||
placeholder: true,
|
||||
placeholderTextColor: true,
|
||||
|
@ -66,6 +70,27 @@ var notMultiline = {
|
|||
onSubmitEditing: true,
|
||||
};
|
||||
|
||||
var TextInputAndroidAttributes = {
|
||||
autoCapitalize: true,
|
||||
autoCorrect: true,
|
||||
autoFocus: true,
|
||||
keyboardType: true,
|
||||
multiline: true,
|
||||
password: true,
|
||||
placeholder: true,
|
||||
value: true,
|
||||
testID: true,
|
||||
};
|
||||
|
||||
var AndroidTextInput = createReactIOSNativeComponentClass({
|
||||
validAttributes: TextInputAndroidAttributes,
|
||||
uiViewClassName: 'AndroidTextInput',
|
||||
});
|
||||
|
||||
var crossPlatformKeyboardTypeMap = {
|
||||
'numeric': 'decimal-pad',
|
||||
};
|
||||
|
||||
type DefaultProps = {
|
||||
bufferDelay: number;
|
||||
};
|
||||
|
@ -138,8 +163,47 @@ var TextInput = React.createClass({
|
|||
*/
|
||||
keyboardType: PropTypes.oneOf([
|
||||
'default',
|
||||
// iOS
|
||||
'ascii-capable',
|
||||
'numbers-and-punctuation',
|
||||
'url',
|
||||
'number-pad',
|
||||
'phone-pad',
|
||||
'name-phone-pad',
|
||||
'email-address',
|
||||
'decimal-pad',
|
||||
'twitter',
|
||||
'web-search',
|
||||
// Cross-platform
|
||||
'numeric',
|
||||
]),
|
||||
/**
|
||||
* Determines how the return key should look.
|
||||
*/
|
||||
returnKeyType: PropTypes.oneOf([
|
||||
'default',
|
||||
'go',
|
||||
'google',
|
||||
'join',
|
||||
'next',
|
||||
'route',
|
||||
'search',
|
||||
'send',
|
||||
'yahoo',
|
||||
'done',
|
||||
'emergency-call',
|
||||
]),
|
||||
/**
|
||||
* If true, the keyboard disables the return key when there is no text and
|
||||
* automatically enables it when there is text. Default value is false.
|
||||
*/
|
||||
enablesReturnKeyAutomatically: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* If true, the text input obscures the text entered so that sensitive text
|
||||
* like passwords stay secure. Default value is false.
|
||||
*/
|
||||
secureTextEntry: PropTypes.bool,
|
||||
/**
|
||||
* If true, the text input can be multiple lines. Default value is false.
|
||||
*/
|
||||
|
@ -157,10 +221,15 @@ var TextInput = React.createClass({
|
|||
*
|
||||
* Callback that is called when the text input's text changes.
|
||||
*/
|
||||
onChange: PropTypes.func,
|
||||
onChangeText: PropTypes.func,
|
||||
|
||||
onEndEditing: PropTypes.func,
|
||||
onSubmitEditing: PropTypes.func,
|
||||
/**
|
||||
* If true, the TextInput will be a password field. Default value is false.
|
||||
*/
|
||||
password: PropTypes.bool,
|
||||
/**
|
||||
* The string that will be rendered before text input has been entered
|
||||
*/
|
||||
|
@ -202,6 +271,10 @@ var TextInput = React.createClass({
|
|||
]),
|
||||
|
||||
style: Text.propTypes.style,
|
||||
/**
|
||||
* Used to locate this view in end-to-end tests.
|
||||
*/
|
||||
testID: PropTypes.string,
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -313,10 +386,24 @@ var TextInput = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
if (Platform.OS === 'ios') {
|
||||
return this._renderIOs();
|
||||
} else if (Platform.OS === 'android') {
|
||||
return this._renderAndroid();
|
||||
}
|
||||
},
|
||||
|
||||
_renderIOs: function() {
|
||||
var textContainer;
|
||||
|
||||
var autoCapitalize = autoCapitalizeConsts[this.props.autoCapitalize];
|
||||
var clearButtonMode = clearButtonModeConsts[this.props.clearButtonMode];
|
||||
var clearButtonMode = RCTUIManager.UITextField.clearButtonMode[this.props.clearButtonMode];
|
||||
|
||||
var keyboardType = keyboardTypeConsts[
|
||||
crossPlatformKeyboardTypeMap[this.props.keyboardType] ||
|
||||
this.props.keyboardType
|
||||
];
|
||||
var returnKeyType = returnKeyTypeConsts[this.props.returnKeyType];
|
||||
|
||||
if (!this.props.multiline) {
|
||||
for (var propKey in onlyMultiline) {
|
||||
|
@ -331,7 +418,10 @@ var TextInput = React.createClass({
|
|||
ref="input"
|
||||
style={[styles.input, this.props.style]}
|
||||
enabled={this.props.editable}
|
||||
keyboardType={this.props.keyboardType}
|
||||
keyboardType={keyboardType}
|
||||
returnKeyType={returnKeyType}
|
||||
enablesReturnKeyAutomatically={this.props.enablesReturnKeyAutomatically}
|
||||
secureTextEntry={this.props.secureTextEntry}
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
|
@ -373,6 +463,10 @@ var TextInput = React.createClass({
|
|||
children={children}
|
||||
mostRecentEventCounter={this.state.mostRecentEventCounter}
|
||||
editable={this.props.editable}
|
||||
keyboardType={keyboardType}
|
||||
returnKeyType={returnKeyType}
|
||||
enablesReturnKeyAutomatically={this.props.enablesReturnKeyAutomatically}
|
||||
secureTextEntry={this.props.secureTextEntry}
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
|
@ -398,6 +492,27 @@ var TextInput = React.createClass({
|
|||
);
|
||||
},
|
||||
|
||||
_renderAndroid: function() {
|
||||
var autoCapitalize = autoCapitalizeConsts[this.props.autoCapitalize];
|
||||
return (
|
||||
<AndroidTextInput
|
||||
ref="input"
|
||||
style={[this.props.style]}
|
||||
autoCapitalize={autoCapitalize}
|
||||
autoCorrect={this.props.autoCorrect}
|
||||
keyboardType={this.props.keyboardType}
|
||||
multiline={this.props.multiline}
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
password={this.props.password}
|
||||
placeholder={this.props.placeholder}
|
||||
value={this.props.value}
|
||||
testID={this.props.testID}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
_onFocus: function(event: Event) {
|
||||
if (this.props.onFocus) {
|
||||
this.props.onFocus(event);
|
|
@ -50,7 +50,7 @@ var DEFAULT_PROPS = {
|
|||
* style={styles.button}
|
||||
* source={require('image!myButton')}
|
||||
* />
|
||||
* </View>
|
||||
* </TouchableHighlight>
|
||||
* );
|
||||
* },
|
||||
* ```
|
||||
|
|
|
@ -39,7 +39,7 @@ var onlyChild = require('onlyChild');
|
|||
* style={styles.button}
|
||||
* source={require('image!myButton')}
|
||||
* />
|
||||
* </View>
|
||||
* </TouchableOpacity>
|
||||
* );
|
||||
* },
|
||||
* ```
|
||||
|
|
|
@ -325,8 +325,8 @@ var ListView = React.createClass({
|
|||
stickyHeaderIndices: sectionHeaderIndices,
|
||||
}
|
||||
);
|
||||
if (!props.throttleScrollCallbackMS) {
|
||||
props.throttleScrollCallbackMS = DEFAULT_SCROLL_CALLBACK_THROTTLE;
|
||||
if (!props.scrollEventThrottle) {
|
||||
props.scrollEventThrottle = DEFAULT_SCROLL_CALLBACK_THROTTLE;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -985,7 +985,6 @@ var Navigator = React.createClass({
|
|||
},
|
||||
|
||||
renderNavigationStackBar: function() {
|
||||
var NavigationBarClass = this.props.NavigationBarClass;
|
||||
if (!this.props.navigationBar) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -46,15 +46,12 @@ var TITLE_PROPS = Interpolators.map(() => {return {style: {}};});
|
|||
var RIGHT_BUTTON_PROPS = Interpolators.map(() => {return {style: {}};});
|
||||
|
||||
|
||||
/**
|
||||
* TODO: Rename `observedTopOfStack` to `presentedIndex` in `NavigationStack`.
|
||||
*/
|
||||
var navStatePresentedIndex = function(navState) {
|
||||
if (navState.presentedIndex !== undefined) {
|
||||
return navState.presentedIndex;
|
||||
} else {
|
||||
return navState.observedTopOfStack;
|
||||
}
|
||||
// TODO: rename `observedTopOfStack` to `presentedIndex` in `NavigatorIOS`
|
||||
return navState.observedTopOfStack;
|
||||
};
|
||||
|
||||
|
||||
|
@ -85,7 +82,12 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({
|
|||
titleContentForRoute: PropTypes.func,
|
||||
iconForRoute: PropTypes.func,
|
||||
}),
|
||||
navigationBarStyles: PropTypes.number,
|
||||
navState: React.PropTypes.shape({
|
||||
routeStack: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||
idStack: React.PropTypes.arrayOf(React.PropTypes.number),
|
||||
presentedIndex: React.PropTypes.number,
|
||||
}),
|
||||
navigationBarStyles: View.propTypes.style,
|
||||
},
|
||||
|
||||
statics: {
|
||||
|
|
|
@ -34,19 +34,31 @@ var View = require('View');
|
|||
|
||||
var COMPONENT_NAMES = ['Title', 'LeftButton', 'RightButton'];
|
||||
|
||||
/**
|
||||
* TODO (janzer): Rename `observedTopOfStack` to `presentedIndex` in `NavigationStack`.
|
||||
*/
|
||||
var navStatePresentedIndex = function(navState) {
|
||||
if (navState.presentedIndex !== undefined) {
|
||||
return navState.presentedIndex;
|
||||
} else {
|
||||
return navState.observedTopOfStack;
|
||||
}
|
||||
// TODO: rename `observedTopOfStack` to `presentedIndex` in `NavigatorIOS`
|
||||
return navState.observedTopOfStack;
|
||||
};
|
||||
|
||||
var NavigatorNavigationBar = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
navigator: React.PropTypes.object,
|
||||
navigationBarRouteMapper: React.PropTypes.shape({
|
||||
Title: React.PropTypes.func.isRequired,
|
||||
LeftButton: React.PropTypes.func.isRequired,
|
||||
RightButton: React.PropTypes.func.isRequired,
|
||||
}),
|
||||
navState: React.PropTypes.shape({
|
||||
routeStack: React.PropTypes.arrayOf(React.PropTypes.object),
|
||||
idStack: React.PropTypes.arrayOf(React.PropTypes.number),
|
||||
presentedIndex: React.PropTypes.number,
|
||||
}),
|
||||
navigationBarStyles: View.propTypes.style,
|
||||
},
|
||||
|
||||
statics: {
|
||||
Styles: NavigatorNavigationBarStyles,
|
||||
},
|
||||
|
|
|
@ -16,14 +16,30 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
|
|||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/**
|
||||
* Downloads a block of raw data and returns it. Note that the callback block
|
||||
* will not be executed on the same thread you called the method from, nor on
|
||||
* the main thread. Returns a token that can be used to cancel the download.
|
||||
*/
|
||||
- (id)downloadDataForURL:(NSURL *)url
|
||||
block:(RCTDataDownloadBlock)block;
|
||||
|
||||
/**
|
||||
* Downloads an image and decompresses it a the size specified. The compressed
|
||||
* image will be cached in memory and to disk. Note that the callback block
|
||||
* will not be executed on the same thread you called the method from, nor on
|
||||
* the main thread. Returns a token that can be used to cancel the download.
|
||||
*/
|
||||
- (id)downloadImageForURL:(NSURL *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
block:(RCTImageDownloadBlock)block;
|
||||
|
||||
/**
|
||||
* Cancel an in-flight download. If multiple requets have been made for the
|
||||
* same image, only the request that relates to the token passed will be
|
||||
* cancelled.
|
||||
*/
|
||||
- (void)cancelDownload:(id)downloadToken;
|
||||
|
||||
@end
|
||||
|
|
|
@ -17,12 +17,12 @@ typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *e
|
|||
@implementation RCTImageDownloader
|
||||
{
|
||||
RCTCache *_cache;
|
||||
dispatch_queue_t _processingQueue;
|
||||
NSMutableDictionary *_pendingBlocks;
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
static RCTImageDownloader *sharedInstance;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
@ -35,27 +35,32 @@ typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *e
|
|||
{
|
||||
if ((self = [super init])) {
|
||||
_cache = [[RCTCache alloc] initWithName:@"RCTImageDownloader"];
|
||||
_pendingBlocks = [NSMutableDictionary dictionary];
|
||||
_processingQueue = dispatch_queue_create("com.facebook.React.DownloadProcessingQueue", DISPATCH_QUEUE_SERIAL);
|
||||
_pendingBlocks = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)cacheKeyForURL:(NSURL *)url
|
||||
static NSString *RCTCacheKeyForURL(NSURL *url)
|
||||
{
|
||||
return url.absoluteString;
|
||||
}
|
||||
|
||||
- (id)_downloadDataForURL:(NSURL *)url block:(RCTCachedDataDownloadBlock)block
|
||||
{
|
||||
NSString *cacheKey = [self cacheKeyForURL:url];
|
||||
NSString *cacheKey = RCTCacheKeyForURL(url);
|
||||
|
||||
__block BOOL cancelled = NO;
|
||||
__block NSURLSessionDataTask *task = nil;
|
||||
|
||||
dispatch_block_t cancel = ^{
|
||||
|
||||
cancelled = YES;
|
||||
|
||||
NSMutableArray *pendingBlocks = _pendingBlocks[cacheKey];
|
||||
[pendingBlocks removeObject:block];
|
||||
dispatch_async(_processingQueue, ^{
|
||||
NSMutableArray *pendingBlocks = self->_pendingBlocks[cacheKey];
|
||||
[pendingBlocks removeObject:block];
|
||||
});
|
||||
|
||||
if (task) {
|
||||
[task cancel];
|
||||
|
@ -63,56 +68,60 @@ typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *e
|
|||
}
|
||||
};
|
||||
|
||||
NSMutableArray *pendingBlocks = _pendingBlocks[cacheKey];
|
||||
if (pendingBlocks) {
|
||||
[pendingBlocks addObject:block];
|
||||
} else {
|
||||
_pendingBlocks[cacheKey] = [NSMutableArray arrayWithObject:block];
|
||||
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
RCTCachedDataDownloadBlock runBlocks = ^(BOOL cached, NSData *data, NSError *error) {
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
NSArray *blocks = strongSelf->_pendingBlocks[cacheKey];
|
||||
[strongSelf->_pendingBlocks removeObjectForKey:cacheKey];
|
||||
|
||||
for (RCTCachedDataDownloadBlock block in blocks) {
|
||||
block(cached, data, error);
|
||||
}
|
||||
};
|
||||
|
||||
if ([_cache hasDataForKey:cacheKey]) {
|
||||
[_cache fetchDataForKey:cacheKey completionHandler:^(NSData *data) {
|
||||
if (!cancelled) runBlocks(YES, data, nil);
|
||||
}];
|
||||
dispatch_async(_processingQueue, ^{
|
||||
NSMutableArray *pendingBlocks = _pendingBlocks[cacheKey];
|
||||
if (pendingBlocks) {
|
||||
[pendingBlocks addObject:block];
|
||||
} else {
|
||||
task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (!cancelled) runBlocks(NO, data, error);
|
||||
}];
|
||||
_pendingBlocks[cacheKey] = [NSMutableArray arrayWithObject:block];
|
||||
|
||||
[task resume];
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
RCTCachedDataDownloadBlock runBlocks = ^(BOOL cached, NSData *data, NSError *error) {
|
||||
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
NSArray *blocks = strongSelf->_pendingBlocks[cacheKey];
|
||||
[strongSelf->_pendingBlocks removeObjectForKey:cacheKey];
|
||||
for (RCTCachedDataDownloadBlock block in blocks) {
|
||||
block(cached, data, error);
|
||||
}
|
||||
};
|
||||
|
||||
if ([_cache hasDataForKey:cacheKey]) {
|
||||
[_cache fetchDataForKey:cacheKey completionHandler:^(NSData *data) {
|
||||
if (!cancelled) {
|
||||
runBlocks(YES, data, nil);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (!cancelled) {
|
||||
runBlocks(NO, data, error);
|
||||
}
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return [cancel copy];
|
||||
}
|
||||
|
||||
- (id)downloadDataForURL:(NSURL *)url block:(RCTDataDownloadBlock)block
|
||||
{
|
||||
NSString *cacheKey = [self cacheKeyForURL:url];
|
||||
NSString *cacheKey = RCTCacheKeyForURL(url);
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
return [self _downloadDataForURL:url block:^(BOOL cached, NSData *data, NSError *error) {
|
||||
if (!cached) {
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
[strongSelf->_cache setData:data forKey:cacheKey];
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block(data, error);
|
||||
});
|
||||
block(data, error);
|
||||
}];
|
||||
}
|
||||
|
||||
- (id)downloadImageForURL:(NSURL *)url size:(CGSize)size scale:(CGFloat)scale block:(RCTImageDownloadBlock)block
|
||||
- (id)downloadImageForURL:(NSURL *)url size:(CGSize)size
|
||||
scale:(CGFloat)scale block:(RCTImageDownloadBlock)block
|
||||
{
|
||||
return [self downloadDataForURL:url block:^(NSData *data, NSError *error) {
|
||||
|
||||
|
@ -142,20 +151,14 @@ typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *e
|
|||
image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
||||
// TODO: should we cache the decompressed image?
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block(image, nil);
|
||||
});
|
||||
block(image, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)cancelDownload:(id)downloadToken
|
||||
{
|
||||
if (downloadToken) {
|
||||
dispatch_block_t block = (id)downloadToken;
|
||||
block();
|
||||
((dispatch_block_t)downloadToken)();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
#import "RCTImageDownloader.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
NSError *errorWithMessage(NSString *message) {
|
||||
NSError *errorWithMessage(NSString *message)
|
||||
{
|
||||
NSDictionary *errorInfo = @{NSLocalizedDescriptionKey: message};
|
||||
NSError *error = [[NSError alloc] initWithDomain:RCTErrorDomain code:0 userInfo:errorInfo];
|
||||
return error;
|
||||
|
|
|
@ -61,23 +61,24 @@
|
|||
if ([imageURL.pathExtension caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
|
||||
_downloadToken = [_imageDownloader downloadDataForURL:imageURL block:^(NSData *data, NSError *error) {
|
||||
if (data) {
|
||||
CAKeyframeAnimation *animation = RCTGIFImageWithData(data);
|
||||
CGImageRef firstFrame = (__bridge CGImageRef)animation.values.firstObject;
|
||||
self.layer.bounds = CGRectMake(0, 0, CGImageGetWidth(firstFrame), CGImageGetHeight(firstFrame));
|
||||
self.layer.contentsScale = 1.0;
|
||||
self.layer.contentsGravity = kCAGravityResizeAspect;
|
||||
self.layer.minificationFilter = kCAFilterLinear;
|
||||
self.layer.magnificationFilter = kCAFilterLinear;
|
||||
[self.layer addAnimation:animation forKey:@"contents"];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
CAKeyframeAnimation *animation = RCTGIFImageWithData(data);
|
||||
self.layer.contentsScale = 1.0;
|
||||
self.layer.minificationFilter = kCAFilterLinear;
|
||||
self.layer.magnificationFilter = kCAFilterLinear;
|
||||
[self.layer addAnimation:animation forKey:@"contents"];
|
||||
});
|
||||
}
|
||||
// TODO: handle errors
|
||||
}];
|
||||
} else {
|
||||
_downloadToken = [_imageDownloader downloadImageForURL:imageURL size:self.bounds.size scale:RCTScreenScale() block:^(UIImage *image, NSError *error) {
|
||||
if (image) {
|
||||
[self.layer removeAnimationForKey:@"contents"];
|
||||
self.layer.contentsScale = image.scale;
|
||||
self.layer.contents = (__bridge id)image.CGImage;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.layer removeAnimationForKey:@"contents"];
|
||||
self.layer.contentsScale = image.scale;
|
||||
self.layer.contents = (__bridge id)image.CGImage;
|
||||
});
|
||||
}
|
||||
// TODO: handle errors
|
||||
}];
|
||||
|
|
|
@ -21,21 +21,86 @@ var _initialURL = RCTLinkingManager &&
|
|||
|
||||
var DEVICE_NOTIF_EVENT = 'openURL';
|
||||
|
||||
/**
|
||||
* `LinkingIOS` gives you a general interface to interact with both, incoming
|
||||
* and outgoing app links.
|
||||
*
|
||||
* ### Basic Usage
|
||||
*
|
||||
* #### Handling deep links
|
||||
*
|
||||
* If your app was launched from a external url registered to your app you can
|
||||
* access and handle it from any component you want with
|
||||
*
|
||||
* ```
|
||||
* componentDidMount() {
|
||||
* var url = LinkingIOS.popInitialURL();
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In case you also want to listen to incoming app links during your app's
|
||||
* execution you'll need to add the following lines to you `*AppDelegate.m`:
|
||||
*
|
||||
* ```
|
||||
* - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
|
||||
* return [RCTLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* And then on your React component you'll be able to listen to the events on
|
||||
* `LinkingIOS` as follows
|
||||
*
|
||||
* ```
|
||||
* componentDidMount() {
|
||||
* LinkingIOS.addEventListener('url', this._handleOpenURL);
|
||||
* },
|
||||
* componentWillUnmount() {
|
||||
* LinkingIOS.removeEventListener('url', this._handleOpenURL);
|
||||
* },
|
||||
* _handleOpenURL(event) {
|
||||
* console.log(event.url);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* #### Triggering App links
|
||||
*
|
||||
* To trigger an app link (browser, email or custom schemas) you call
|
||||
*
|
||||
* ```
|
||||
* LinkingIOS.openURL(url)
|
||||
* ```
|
||||
*
|
||||
* If you want to check if any installed app can handle a given url beforehand you can call
|
||||
* ```
|
||||
* LinkingIOS.canOpenURL(url, (supported) => {
|
||||
* if (!supported) {
|
||||
* AlertIOS.alert('Can\'t handle url: ' + url);
|
||||
* } else {
|
||||
* LinkingIOS.openURL(url);
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
class LinkingIOS {
|
||||
static addEventListener(type, handler) {
|
||||
/**
|
||||
* Add a handler to LinkingIOS changes by listening to the `url` event type
|
||||
* and providing the handler
|
||||
*/
|
||||
static addEventListener(type: string, handler: Function) {
|
||||
invariant(
|
||||
type === 'url',
|
||||
'LinkingIOS only supports `url` events'
|
||||
);
|
||||
_notifHandlers[handler] = RCTDeviceEventEmitter.addListener(
|
||||
DEVICE_NOTIF_EVENT,
|
||||
(notifData) => {
|
||||
handler(new LinkingIOS(notifData));
|
||||
}
|
||||
handler
|
||||
);
|
||||
}
|
||||
|
||||
static removeEventListener(type, handler) {
|
||||
/**
|
||||
* Remove a handler by passing the `url` event type and the handler
|
||||
*/
|
||||
static removeEventListener(type: string, handler: Function ) {
|
||||
invariant(
|
||||
type === 'url',
|
||||
'LinkingIOS only supports `url` events'
|
||||
|
@ -47,7 +112,10 @@ class LinkingIOS {
|
|||
_notifHandlers[handler] = null;
|
||||
}
|
||||
|
||||
static openURL(url) {
|
||||
/**
|
||||
* Try to open the given `url` with any of the installed apps.
|
||||
*/
|
||||
static openURL(url: string) {
|
||||
invariant(
|
||||
typeof url === 'string',
|
||||
'Invalid url: should be a string'
|
||||
|
@ -55,7 +123,11 @@ class LinkingIOS {
|
|||
RCTLinkingManager.openURL(url);
|
||||
}
|
||||
|
||||
static canOpenURL(url, callback) {
|
||||
/**
|
||||
* Determine wether or not the an installed app can handle a given `url`
|
||||
* The callback function will be called with `bool supported` as the only argument
|
||||
*/
|
||||
static canOpenURL(url: string, callback: Function) {
|
||||
invariant(
|
||||
typeof url === 'string',
|
||||
'Invalid url: should be a string'
|
||||
|
@ -67,7 +139,11 @@ class LinkingIOS {
|
|||
RCTLinkingManager.canOpenURL(url, callback);
|
||||
}
|
||||
|
||||
static popInitialURL() {
|
||||
/**
|
||||
* If the app launch was triggered by an app link, it will pop the link url,
|
||||
* otherwise it will return `null`
|
||||
*/
|
||||
static popInitialURL(): ?string {
|
||||
var initialURL = _initialURL;
|
||||
_initialURL = null;
|
||||
return initialURL;
|
||||
|
|
|
@ -34,7 +34,7 @@ type ReachabilityStateIOS = $Enum<{
|
|||
*
|
||||
* ### reachabilityIOS
|
||||
*
|
||||
* Asynchronously determine if the device is online and on a cellular network.
|
||||
* Asyncronously determine if the device is online and on a cellular network.
|
||||
*
|
||||
* - `none` - device is offline
|
||||
* - `wifi` - device is online and connected via wifi, or is the iOS simulator
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule Portal
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var View = require('View');
|
||||
|
||||
var _portalRef: any;
|
||||
|
||||
/*
|
||||
* A container that renders all the modals on top of everything else in the application.
|
||||
*
|
||||
* Portal makes it possible for application code to pass modal views all the way up to
|
||||
* the root element created in `renderApplication`.
|
||||
*
|
||||
* Never use `<Portal>` in your code. There is only one Portal instance rendered
|
||||
* by the top-level `renderApplication`.
|
||||
*/
|
||||
var Portal = React.createClass({
|
||||
statics: {
|
||||
showModal: function(component) {
|
||||
if (!_portalRef) {
|
||||
console.error('Calling showModal but no Portal has been rendered');
|
||||
return;
|
||||
}
|
||||
_portalRef.setState({modal: component});
|
||||
},
|
||||
|
||||
closeModal: function() {
|
||||
if (!_portalRef) {
|
||||
console.error('Calling closeModal but no Portal has been rendered');
|
||||
return;
|
||||
}
|
||||
_portalRef.setState({modal: null});
|
||||
},
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {modal: (null: any)};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
_portalRef = this;
|
||||
if (!this.state.modal) {
|
||||
return <View />;
|
||||
}
|
||||
return (
|
||||
<View style={styles.modalsContainer}>
|
||||
{this.state.modal}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
modalsContainer: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Portal;
|
|
@ -11,7 +11,10 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var Portal = require('Portal');
|
||||
var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var View = require('View');
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
|
@ -24,12 +27,27 @@ function renderApplication<D, P, S>(
|
|||
rootTag,
|
||||
'Expect to have a valid rootTag, instead got ', rootTag
|
||||
);
|
||||
React.render(
|
||||
<RootComponent
|
||||
{...initialProps}
|
||||
/>,
|
||||
React.render(
|
||||
<View style={styles.appContainer}>
|
||||
<RootComponent
|
||||
{...initialProps}
|
||||
/>
|
||||
<Portal />
|
||||
</View>,
|
||||
rootTag
|
||||
);
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
// This is needed so the application covers the whole screen
|
||||
// and therefore the contents of the Portal are not clipped.
|
||||
appContainer: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = renderApplication;
|
||||
|
|
|
@ -14,5 +14,6 @@
|
|||
@property (nonatomic, copy) NSAttributedString *attributedText;
|
||||
@property (nonatomic, assign) NSLineBreakMode lineBreakMode;
|
||||
@property (nonatomic, assign) NSUInteger numberOfLines;
|
||||
@property (nonatomic, assign) UIEdgeInsets contentInset;
|
||||
|
||||
@end
|
||||
|
|
|
@ -72,20 +72,26 @@
|
|||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (CGRect)textFrame
|
||||
{
|
||||
return UIEdgeInsetsInsetRect(self.bounds, _contentInset);
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
// The header comment for `size` says that a height of 0.0 should be enough,
|
||||
// but it isn't.
|
||||
_textContainer.size = CGSizeMake(self.bounds.size.width, CGFLOAT_MAX);
|
||||
_textContainer.size = CGSizeMake([self textFrame].size.width, CGFLOAT_MAX);
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
CGPoint origin = [self textFrame].origin;
|
||||
NSRange glyphRange = [_layoutManager glyphRangeForTextContainer:_textContainer];
|
||||
[_layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:CGPointZero];
|
||||
[_layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:CGPointZero];
|
||||
[_layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:origin];
|
||||
[_layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:origin];
|
||||
}
|
||||
|
||||
- (NSNumber *)reactTagAtPoint:(CGPoint)point
|
||||
|
|
|
@ -124,4 +124,13 @@ RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, NSInteger, RCTShadowText)
|
|||
};
|
||||
}
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView
|
||||
{
|
||||
NSNumber *reactTag = shadowView.reactTag;
|
||||
UIEdgeInsets padding = shadowView.paddingAsInsets;
|
||||
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
((RCTText *)viewRegistry[reactTag]).contentInset = padding;
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -69,6 +69,7 @@ var ReactNative = Object.assign(Object.create(require('React')), {
|
|||
TestUtils: undefined,
|
||||
batchedUpdates: require('ReactUpdates').batchedUpdates,
|
||||
cloneWithProps: require('cloneWithProps'),
|
||||
createFragment: require('ReactFragment').create,
|
||||
update: require('update'),
|
||||
},
|
||||
});
|
||||
|
|
105
React.podspec
105
React.podspec
|
@ -1,93 +1,78 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = "React"
|
||||
s.version = "0.3.1"
|
||||
s.summary = "Build high quality mobile apps using React."
|
||||
s.description = <<-DESC
|
||||
React Native apps are built using the React JS
|
||||
framework, and render directly to native UIKit
|
||||
elements using a fully asynchronous architecture.
|
||||
There is no browser and no HTML. We have picked what
|
||||
we think is the best set of features from these and
|
||||
other technologies to build what we hope to become
|
||||
the best product development framework available,
|
||||
with an emphasis on iteration speed, developer
|
||||
delight, continuity of technology, and absolutely
|
||||
beautiful and fast products with no compromises in
|
||||
quality or capability.
|
||||
DESC
|
||||
s.homepage = "http://facebook.github.io/react-native/"
|
||||
s.license = "BSD"
|
||||
s.author = "Facebook"
|
||||
s.source = { :git => "https://github.com/facebook/react-native.git", :tag => "v#{s.version}" }
|
||||
s.default_subspec = 'Core'
|
||||
s.requires_arc = true
|
||||
s.platform = :ios, "7.0"
|
||||
s.prepare_command = 'npm install'
|
||||
s.preserve_paths = "cli.js", "Libraries/**/*.js", "lint", "linter.js", "node_modules", "package.json", "packager", "PATENTS", "react-native-cli"
|
||||
|
||||
s.subspec 'Core' do |ss|
|
||||
ss.source_files = "React/**/*.{c,h,m}"
|
||||
ss.exclude_files = "**/__tests__/*", "IntegrationTests/*"
|
||||
ss.frameworks = "JavaScriptCore"
|
||||
end
|
||||
s.name = "React"
|
||||
s.version = "0.1.0"
|
||||
s.summary = "Build high quality mobile apps using React."
|
||||
s.description= <<-DESC
|
||||
React Native apps are built using the React JS framework,
|
||||
and render directly to native UIKit elements using a fully
|
||||
asynchronous architecture. There is no browser and no HTML.
|
||||
We have picked what we think is the best set of features from
|
||||
these and other technologies to build what we hope to become
|
||||
the best product development framework available, with an
|
||||
emphasis on iteration speed, developer delight, continuity
|
||||
of technology, and absolutely beautiful and fast products
|
||||
with no compromises in quality or capability.
|
||||
DESC
|
||||
s.homepage = "http://facebook.github.io/react-native/"
|
||||
s.license = "BSD"
|
||||
s.author = "Facebook"
|
||||
s.platform = :ios, "7.0"
|
||||
s.source = { :git => "https://github.com/facebook/react-native.git", :tag => "v#{s.version}" }
|
||||
s.source_files = "React/**/*.{c,h,m}"
|
||||
s.resources = "Resources/*.png"
|
||||
s.preserve_paths = "cli.js", "Libraries/**/*.js", "lint", "linter.js", "node_modules", "package.json", "packager", "PATENTS", "react-native-cli"
|
||||
s.exclude_files = "**/__tests__/*", "IntegrationTests/*"
|
||||
s.frameworks = "JavaScriptCore"
|
||||
s.requires_arc = true
|
||||
s.prepare_command = 'npm install'
|
||||
s.libraries = 'icucore'
|
||||
|
||||
s.subspec 'RCTActionSheet' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/ActionSheetIOS/*.{h,m}"
|
||||
ss.source_files = "Libraries/ActionSheetIOS/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/ActionSheetIOS/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTAdSupport' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/AdSupport/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/AdSupport/*.js"
|
||||
ss.source_files = "Libraries/RCTAdSupport/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/RCTAdSupport/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTAnimation' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Animation/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Animation/*.js"
|
||||
ss.source_files = "Libraries/Animation/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Animation/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTGeolocation' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Geolocation/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Geolocation/*.js"
|
||||
ss.source_files = "Libraries/Geolocation/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Geolocation/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTImage' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Image/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Image/*.js"
|
||||
ss.source_files = "Libraries/Image/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Image/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTNetwork' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Network/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Network/*.js"
|
||||
ss.source_files = "Libraries/Network/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Network/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTPushNotification' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/PushNotificationIOS/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/PushNotificationIOS/*.js"
|
||||
ss.source_files = "Libraries/PushNotificationIOS/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/PushNotificationIOS/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTWebSocketDebugger' do |ss|
|
||||
ss.libraries = 'icucore'
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/RCTWebSocketDebugger/*.{h,m}"
|
||||
ss.source_files = "Libraries/RCTWebSocketDebugger/*.{h,m}"
|
||||
end
|
||||
|
||||
s.subspec 'RCTText' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Text/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Text/*.js"
|
||||
ss.source_files = "Libraries/Text/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Text/*.js"
|
||||
end
|
||||
|
||||
s.subspec 'RCTVibration' do |ss|
|
||||
ss.dependency 'React/Core'
|
||||
ss.source_files = "Libraries/Vibration/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Vibration/*.js"
|
||||
ss.source_files = "Libraries/Vibration/*.{h,m}"
|
||||
ss.preserve_paths = "Libraries/Vibration/*.js"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
+ (UITextFieldViewMode)UITextFieldViewMode:(id)json;
|
||||
+ (UIScrollViewKeyboardDismissMode)UIScrollViewKeyboardDismissMode:(id)json;
|
||||
+ (UIKeyboardType)UIKeyboardType:(id)json;
|
||||
+ (UIReturnKeyType)UIReturnKeyType:(id)json;
|
||||
|
||||
+ (UIViewContentMode)UIViewContentMode:(id)json;
|
||||
+ (UIBarStyle)UIBarStyle:(id)json;
|
||||
|
@ -123,10 +124,17 @@ BOOL RCTCopyProperty(id target, id source, NSString *keyPath);
|
|||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This macro is used for creating simple converter functions that just call
|
||||
* the specified getter method on the json value.
|
||||
*/
|
||||
#define RCT_CONVERTER(type, name, getter) \
|
||||
RCT_CUSTOM_CONVERTER(type, name, [json getter])
|
||||
|
||||
/**
|
||||
* This macro is used for creating converter functions with arbitrary logic.
|
||||
*/
|
||||
#define RCT_CONVERTER_CUSTOM(type, name, code) \
|
||||
#define RCT_CUSTOM_CONVERTER(type, name, code) \
|
||||
+ (type)name:(id)json \
|
||||
{ \
|
||||
if (json == [NSNull null]) { \
|
||||
|
@ -143,20 +151,13 @@ BOOL RCTCopyProperty(id target, id source, NSString *keyPath);
|
|||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* This macro is used for creating simple converter functions that just call
|
||||
* the specified getter method on the json value.
|
||||
*/
|
||||
#define RCT_CONVERTER(type, name, getter) \
|
||||
RCT_CONVERTER_CUSTOM(type, name, [json getter])
|
||||
|
||||
/**
|
||||
* This macro is similar to RCT_CONVERTER, but specifically geared towards
|
||||
* numeric types. It will handle string input correctly, and provides more
|
||||
* detailed error reporting if a wrong value is passed in.
|
||||
*/
|
||||
#define RCT_NUMBER_CONVERTER(type, getter) \
|
||||
RCT_CONVERTER_CUSTOM(type, type, [[self NSNumber:json] getter])
|
||||
RCT_CUSTOM_CONVERTER(type, type, [[self NSNumber:json] getter])
|
||||
|
||||
/**
|
||||
* This macro is used for creating converters for enum types.
|
||||
|
@ -189,57 +190,6 @@ RCT_CONVERTER_CUSTOM(type, type, [[self NSNumber:json] getter])
|
|||
return value ? [value getter] : default; \
|
||||
}
|
||||
|
||||
/**
|
||||
* This macro is used for creating converter functions for structs that consist
|
||||
* of a number of CGFloat properties, such as CGPoint, CGRect, etc.
|
||||
*/
|
||||
#define RCT_CGSTRUCT_CONVERTER(type, values, _aliases) \
|
||||
+ (type)type:(id)json \
|
||||
{ \
|
||||
@try { \
|
||||
static NSArray *fields; \
|
||||
static NSUInteger count; \
|
||||
static dispatch_once_t onceToken; \
|
||||
dispatch_once(&onceToken, ^{ \
|
||||
fields = values; \
|
||||
count = [fields count]; \
|
||||
}); \
|
||||
type result; \
|
||||
if ([json isKindOfClass:[NSArray class]]) { \
|
||||
if ([json count] != count) { \
|
||||
RCTLogError(@"Expected array with count %zd, but count is %zd: %@", count, [json count], json); \
|
||||
} else { \
|
||||
for (NSUInteger i = 0; i < count; i++) { \
|
||||
((CGFloat *)&result)[i] = [self CGFloat:json[i]]; \
|
||||
} \
|
||||
} \
|
||||
} else if ([json isKindOfClass:[NSDictionary class]]) { \
|
||||
NSDictionary *aliases = _aliases; \
|
||||
if (aliases.count) { \
|
||||
json = [json mutableCopy]; \
|
||||
for (NSString *alias in aliases) { \
|
||||
NSString *key = aliases[alias]; \
|
||||
NSNumber *number = json[key]; \
|
||||
if (number) { \
|
||||
((NSMutableDictionary *)json)[key] = number; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
for (NSUInteger i = 0; i < count; i++) { \
|
||||
((CGFloat *)&result)[i] = [self CGFloat:json[fields[i]]]; \
|
||||
} \
|
||||
} else if (json && json != [NSNull null]) { \
|
||||
RCTLogError(@"Expected NSArray or NSDictionary for %s, received %@: %@", #type, [json class], json); \
|
||||
} \
|
||||
return result; \
|
||||
} \
|
||||
@catch (__unused NSException *e) { \
|
||||
RCTLogError(@"JSON value '%@' cannot be converted to '%s'", json, #type); \
|
||||
type result; \
|
||||
return result; \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* This macro is used for creating converter functions for typed arrays.
|
||||
*/
|
||||
|
|
|
@ -24,8 +24,8 @@ RCT_NUMBER_CONVERTER(uint64_t, unsignedLongLongValue);
|
|||
RCT_NUMBER_CONVERTER(NSInteger, integerValue)
|
||||
RCT_NUMBER_CONVERTER(NSUInteger, unsignedIntegerValue)
|
||||
|
||||
RCT_CONVERTER_CUSTOM(NSArray *, NSArray, [NSArray arrayWithArray:json])
|
||||
RCT_CONVERTER_CUSTOM(NSDictionary *, NSDictionary, [NSDictionary dictionaryWithDictionary:json])
|
||||
RCT_CUSTOM_CONVERTER(NSArray *, NSArray, [NSArray arrayWithArray:json])
|
||||
RCT_CUSTOM_CONVERTER(NSDictionary *, NSDictionary, [NSDictionary dictionaryWithDictionary:json])
|
||||
RCT_CONVERTER(NSString *, NSString, description)
|
||||
|
||||
+ (NSNumber *)NSNumber:(id)json
|
||||
|
@ -33,7 +33,12 @@ RCT_CONVERTER(NSString *, NSString, description)
|
|||
if ([json isKindOfClass:[NSNumber class]]) {
|
||||
return json;
|
||||
} else if ([json isKindOfClass:[NSString class]]) {
|
||||
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
|
||||
static NSNumberFormatter *formatter;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
formatter = [[NSNumberFormatter alloc] init];
|
||||
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
|
||||
});
|
||||
NSNumber *number = [formatter numberFromString:json];
|
||||
if (!number) {
|
||||
RCTLogError(@"JSON String '%@' could not be interpreted as a number", json);
|
||||
|
@ -74,12 +79,35 @@ RCT_CONVERTER(NSString *, NSString, description)
|
|||
return [NSURLRequest requestWithURL:[self NSURL:json]];
|
||||
}
|
||||
|
||||
+ (NSDate *)NSDate:(id)json
|
||||
{
|
||||
if ([json isKindOfClass:[NSNumber class]]) {
|
||||
return [NSDate dateWithTimeIntervalSince1970:[self NSTimeInterval:json]];
|
||||
} else if ([json isKindOfClass:[NSString class]]) {
|
||||
static NSDateFormatter *formatter;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
formatter = [[NSDateFormatter alloc] init];
|
||||
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";
|
||||
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
|
||||
formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
|
||||
});
|
||||
NSDate *date = [formatter dateFromString:json];
|
||||
if (!date) {
|
||||
RCTLogError(@"JSON String '%@' could not be interpreted as a date. Expected format: YYYY-MM-DD'T'HH:mm:ss.sssZ", json);
|
||||
}
|
||||
return date;
|
||||
} else if (json && json != [NSNull null]) {
|
||||
RCTLogError(@"JSON value '%@' of class %@ could not be interpreted as a date", json, [json class]);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// JS Standard for time is milliseconds
|
||||
RCT_CONVERTER_CUSTOM(NSDate *, NSDate, [NSDate dateWithTimeIntervalSince1970:[self double:json] / 1000.0])
|
||||
RCT_CONVERTER_CUSTOM(NSTimeInterval, NSTimeInterval, [self double:json] / 1000.0)
|
||||
RCT_CUSTOM_CONVERTER(NSTimeInterval, NSTimeInterval, [self double:json] / 1000.0)
|
||||
|
||||
// JS standard for time zones is minutes.
|
||||
RCT_CONVERTER_CUSTOM(NSTimeZone *, NSTimeZone, [NSTimeZone timeZoneForSecondsFromGMT:[self double:json] * 60.0])
|
||||
RCT_CUSTOM_CONVERTER(NSTimeZone *, NSTimeZone, [NSTimeZone timeZoneForSecondsFromGMT:[self double:json] * 60.0])
|
||||
|
||||
RCT_ENUM_CONVERTER(NSTextAlignment, (@{
|
||||
@"auto": @(NSTextAlignmentNatural),
|
||||
|
@ -116,10 +144,33 @@ RCT_ENUM_CONVERTER(UIScrollViewKeyboardDismissMode, (@{
|
|||
}), UIScrollViewKeyboardDismissModeNone, integerValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(UIKeyboardType, (@{
|
||||
@"numeric": @(UIKeyboardTypeDecimalPad),
|
||||
@"default": @(UIKeyboardTypeDefault),
|
||||
@"ascii-capable": @(UIKeyboardTypeASCIICapable),
|
||||
@"numbers-and-punctuation": @(UIKeyboardTypeNumbersAndPunctuation),
|
||||
@"url": @(UIKeyboardTypeURL),
|
||||
@"number-pad": @(UIKeyboardTypeNumberPad),
|
||||
@"phone-pad": @(UIKeyboardTypePhonePad),
|
||||
@"name-phone-pad": @(UIKeyboardTypeNamePhonePad),
|
||||
@"email-address": @(UIKeyboardTypeEmailAddress),
|
||||
@"decimal-pad": @(UIKeyboardTypeDecimalPad),
|
||||
@"twitter": @(UIKeyboardTypeTwitter),
|
||||
@"web-search": @(UIKeyboardTypeWebSearch),
|
||||
}), UIKeyboardTypeDefault, integerValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(UIReturnKeyType, (@{
|
||||
@"default": @(UIReturnKeyDefault),
|
||||
@"go": @(UIReturnKeyGo),
|
||||
@"google": @(UIReturnKeyGoogle),
|
||||
@"join": @(UIReturnKeyJoin),
|
||||
@"next": @(UIReturnKeyNext),
|
||||
@"route": @(UIReturnKeyRoute),
|
||||
@"search": @(UIReturnKeySearch),
|
||||
@"send": @(UIReturnKeySend),
|
||||
@"yahoo": @(UIReturnKeyYahoo),
|
||||
@"done": @(UIReturnKeyDone),
|
||||
@"emergency-call": @(UIReturnKeyEmergencyCall),
|
||||
}), UIReturnKeyDefault, integerValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(UIViewContentMode, (@{
|
||||
@"scale-to-fill": @(UIViewContentModeScaleToFill),
|
||||
@"scale-aspect-fit": @(UIViewContentModeScaleAspectFit),
|
||||
|
@ -142,7 +193,58 @@ RCT_ENUM_CONVERTER(UIBarStyle, (@{
|
|||
}), UIBarStyleDefault, integerValue)
|
||||
|
||||
// TODO: normalise the use of w/width so we can do away with the alias values (#6566645)
|
||||
RCT_CONVERTER_CUSTOM(CGFloat, CGFloat, [self double:json])
|
||||
/**
|
||||
* This macro is used for creating converter functions for structs that consist
|
||||
* of a number of CGFloat properties, such as CGPoint, CGRect, etc.
|
||||
*/
|
||||
#define RCT_CGSTRUCT_CONVERTER(type, values, _aliases) \
|
||||
+ (type)type:(id)json \
|
||||
{ \
|
||||
@try { \
|
||||
static NSArray *fields; \
|
||||
static NSUInteger count; \
|
||||
static dispatch_once_t onceToken; \
|
||||
dispatch_once(&onceToken, ^{ \
|
||||
fields = values; \
|
||||
count = [fields count]; \
|
||||
}); \
|
||||
type result; \
|
||||
if ([json isKindOfClass:[NSArray class]]) { \
|
||||
if ([json count] != count) { \
|
||||
RCTLogError(@"Expected array with count %zd, but count is %zd: %@", count, [json count], json); \
|
||||
} else { \
|
||||
for (NSUInteger i = 0; i < count; i++) { \
|
||||
((CGFloat *)&result)[i] = [self CGFloat:json[i]]; \
|
||||
} \
|
||||
} \
|
||||
} else if ([json isKindOfClass:[NSDictionary class]]) { \
|
||||
NSDictionary *aliases = _aliases; \
|
||||
if (aliases.count) { \
|
||||
json = [json mutableCopy]; \
|
||||
for (NSString *alias in aliases) { \
|
||||
NSString *key = aliases[alias]; \
|
||||
NSNumber *number = json[key]; \
|
||||
if (number) { \
|
||||
((NSMutableDictionary *)json)[key] = number; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
for (NSUInteger i = 0; i < count; i++) { \
|
||||
((CGFloat *)&result)[i] = [self CGFloat:json[fields[i]]]; \
|
||||
} \
|
||||
} else if (json && json != [NSNull null]) { \
|
||||
RCTLogError(@"Expected NSArray or NSDictionary for %s, received %@: %@", #type, [json class], json); \
|
||||
} \
|
||||
return result; \
|
||||
} \
|
||||
@catch (__unused NSException *e) { \
|
||||
RCTLogError(@"JSON value '%@' cannot be converted to '%s'", json, #type); \
|
||||
type result; \
|
||||
return result; \
|
||||
} \
|
||||
}
|
||||
|
||||
RCT_CUSTOM_CONVERTER(CGFloat, CGFloat, [self double:json])
|
||||
RCT_CGSTRUCT_CONVERTER(CGPoint, (@[@"x", @"y"]), nil)
|
||||
RCT_CGSTRUCT_CONVERTER(CGSize, (@[@"width", @"height"]), (@{@"w": @"width", @"h": @"height"}))
|
||||
RCT_CGSTRUCT_CONVERTER(CGRect, (@[@"x", @"y", @"width", @"height"]), (@{@"w": @"width", @"h": @"height"}))
|
||||
|
@ -701,25 +803,32 @@ BOOL RCTSetProperty(id target, NSString *keyPath, SEL type, id json)
|
|||
return NO;
|
||||
}
|
||||
|
||||
// Get converted value
|
||||
NSMethodSignature *signature = [RCTConvert methodSignatureForSelector:type];
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&type atIndex:1];
|
||||
[invocation setArgument:&json atIndex:2];
|
||||
[invocation invokeWithTarget:[RCTConvert class]];
|
||||
NSUInteger length = [signature methodReturnLength];
|
||||
void *value = malloc(length);
|
||||
[invocation getReturnValue:value];
|
||||
@try {
|
||||
// Get converted value
|
||||
NSMethodSignature *signature = [RCTConvert methodSignatureForSelector:type];
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&type atIndex:1];
|
||||
[invocation setArgument:&json atIndex:2];
|
||||
[invocation invokeWithTarget:[RCTConvert class]];
|
||||
NSUInteger length = [signature methodReturnLength];
|
||||
void *value = malloc(length);
|
||||
[invocation getReturnValue:value];
|
||||
|
||||
// Set converted value
|
||||
signature = [target methodSignatureForSelector:setter];
|
||||
invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&setter atIndex:1];
|
||||
[invocation setArgument:value atIndex:2];
|
||||
[invocation invokeWithTarget:target];
|
||||
free(value);
|
||||
// Set converted value
|
||||
signature = [target methodSignatureForSelector:setter];
|
||||
invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&setter atIndex:1];
|
||||
[invocation setArgument:value atIndex:2];
|
||||
[invocation invokeWithTarget:target];
|
||||
free(value);
|
||||
|
||||
return YES;
|
||||
return YES;
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
RCTLogError(@"Exception thrown while attempting to set property '%@' of \
|
||||
'%@' with value '%@': %@", key, [target class], json, exception);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL RCTCopyProperty(id target, id source, NSString *keyPath)
|
||||
|
@ -748,7 +857,7 @@ BOOL RCTCopyProperty(id target, id source, NSString *keyPath)
|
|||
return NO;
|
||||
}
|
||||
|
||||
// Get converted value
|
||||
// Get value
|
||||
NSMethodSignature *signature = [source methodSignatureForSelector:getter];
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&getter atIndex:1];
|
||||
|
@ -757,7 +866,7 @@ BOOL RCTCopyProperty(id target, id source, NSString *keyPath)
|
|||
void *value = malloc(length);
|
||||
[invocation getReturnValue:value];
|
||||
|
||||
// Set converted value
|
||||
// Set value
|
||||
signature = [target methodSignatureForSelector:setter];
|
||||
invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
[invocation setArgument:&setter atIndex:1];
|
||||
|
|
|
@ -80,10 +80,18 @@ static UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimatio
|
|||
if ((self = [super init])) {
|
||||
_property = [RCTConvert NSString:config[@"property"]];
|
||||
|
||||
// TODO: this should be provided in ms, not seconds
|
||||
// (this will require changing all call sites to ms as well)
|
||||
_duration = [RCTConvert NSTimeInterval:config[@"duration"]] * 1000.0 ?: duration;
|
||||
_delay = [RCTConvert NSTimeInterval:config[@"delay"]] * 1000.0;
|
||||
_duration = [RCTConvert NSTimeInterval:config[@"duration"]] ?: duration;
|
||||
if (_duration > 0.0 && _duration < 0.01) {
|
||||
RCTLogError(@"RCTLayoutAnimation expects timings to be in ms, not seconds.");
|
||||
_duration = _duration * 1000.0;
|
||||
}
|
||||
|
||||
_delay = [RCTConvert NSTimeInterval:config[@"delay"]];
|
||||
if (_delay > 0.0 && _delay < 0.01) {
|
||||
RCTLogError(@"RCTLayoutAnimation expects timings to be in ms, not seconds.");
|
||||
_delay = _delay * 1000.0;
|
||||
}
|
||||
|
||||
_animationType = [RCTConvert RCTAnimationType:config[@"type"]];
|
||||
if (_animationType == RCTAnimationTypeSpring) {
|
||||
_springDamping = [RCTConvert CGFloat:config[@"springDamping"]];
|
||||
|
@ -142,9 +150,11 @@ static UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimatio
|
|||
|
||||
if ((self = [super init])) {
|
||||
|
||||
// TODO: this should be provided in ms, not seconds
|
||||
// (this will require changing all call sites to ms as well)
|
||||
NSTimeInterval duration = [RCTConvert NSTimeInterval:config[@"duration"]] * 1000.0;
|
||||
NSTimeInterval duration = [RCTConvert NSTimeInterval:config[@"duration"]];
|
||||
if (duration > 0.0 && duration < 0.01) {
|
||||
RCTLogError(@"RCTLayoutAnimation expects timings to be in ms, not seconds.");
|
||||
duration = duration * 1000.0;
|
||||
}
|
||||
|
||||
_createAnimation = [[RCTAnimation alloc] initWithDuration:duration dictionary:config[@"create"]];
|
||||
_updateAnimation = [[RCTAnimation alloc] initWithDuration:duration dictionary:config[@"update"]];
|
||||
|
@ -1291,6 +1301,32 @@ static void RCTMeasureLayout(RCTShadowView *view,
|
|||
@"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),
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
@property (nonatomic, assign) UIEdgeInsets contentInset;
|
||||
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
|
||||
@property (nonatomic, assign) NSUInteger throttleScrollCallbackMS;
|
||||
@property (nonatomic, assign) NSTimeInterval scrollEventThrottle;
|
||||
@property (nonatomic, assign) BOOL centerContent;
|
||||
@property (nonatomic, copy) NSArray *stickyHeaderIndices;
|
||||
|
||||
|
|
|
@ -274,7 +274,7 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
|
|||
_contentInset = UIEdgeInsetsZero;
|
||||
_contentSize = CGSizeZero;
|
||||
|
||||
_throttleScrollCallbackMS = 0;
|
||||
_scrollEventThrottle = 0.0;
|
||||
_lastScrollDispatchTime = CACurrentMediaTime();
|
||||
_cachedChildFrames = [[NSMutableArray alloc] init];
|
||||
|
||||
|
@ -393,16 +393,15 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, RCTScrollEventTypeMove)
|
|||
[self updateClippedSubviews];
|
||||
|
||||
NSTimeInterval now = CACurrentMediaTime();
|
||||
NSTimeInterval throttleScrollCallbackSeconds = _throttleScrollCallbackMS / 1000.0;
|
||||
|
||||
/**
|
||||
* TODO: this logic looks wrong, and it may be because it is. Currently, if _throttleScrollCallbackMS
|
||||
* TODO: this logic looks wrong, and it may be because it is. Currently, if _scrollEventThrottle
|
||||
* is set to zero (the default), the "didScroll" event is only sent once per scroll, instead of repeatedly
|
||||
* while scrolling as expected. However, if you "fix" that bug, ScrollView will generate repeated
|
||||
* warnings, and behave strangely (ListView works fine however), so don't fix it unless you fix that too!
|
||||
*/
|
||||
if (_allowNextScrollNoMatterWhat ||
|
||||
(_throttleScrollCallbackMS != 0 && throttleScrollCallbackSeconds < (now - _lastScrollDispatchTime))) {
|
||||
(_scrollEventThrottle > 0 && _scrollEventThrottle < (now - _lastScrollDispatchTime))) {
|
||||
|
||||
// Calculate changed frames
|
||||
NSMutableArray *updatedChildFrames = [[NSMutableArray alloc] init];
|
||||
|
|
|
@ -39,12 +39,14 @@ RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL)
|
|||
RCT_EXPORT_VIEW_PROPERTY(scrollsToTop, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(stickyHeaderIndices, NSNumberArray);
|
||||
RCT_EXPORT_VIEW_PROPERTY(throttleScrollCallbackMS, double);
|
||||
RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat);
|
||||
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets);
|
||||
RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets);
|
||||
RCT_REMAP_VIEW_PROPERTY(contentOffset, scrollView.contentOffset, CGPoint);
|
||||
RCT_EXPORT_VIEW_PROPERTY(stickyHeaderIndices, NSNumberArray)
|
||||
RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval)
|
||||
RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
|
||||
RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets)
|
||||
RCT_REMAP_VIEW_PROPERTY(contentOffset, scrollView.contentOffset, CGPoint)
|
||||
|
||||
RCT_DEPRECATED_VIEW_PROPERTY(throttleScrollCallbackMS, scrollEventThrottle)
|
||||
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
@property (nonatomic, assign) BOOL caretHidden;
|
||||
@property (nonatomic, assign) BOOL autoCorrect;
|
||||
@property (nonatomic, assign) UIEdgeInsets paddingEdgeInsets; // TODO: contentInset
|
||||
@property (nonatomic, assign) UIEdgeInsets contentInset;
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
[self addTarget:self action:@selector(_textFieldEndEditing) forControlEvents:UIControlEventEditingDidEnd];
|
||||
[self addTarget:self action:@selector(_textFieldSubmitEditing) forControlEvents:UIControlEventEditingDidEndOnExit];
|
||||
_reactSubviews = [[NSMutableArray alloc] init];
|
||||
self.returnKeyType = UIReturnKeyDone;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -71,7 +70,7 @@
|
|||
- (CGRect)textRectForBounds:(CGRect)bounds
|
||||
{
|
||||
CGRect rect = [super textRectForBounds:bounds];
|
||||
return UIEdgeInsetsInsetRect(rect, _paddingEdgeInsets);
|
||||
return UIEdgeInsetsInsetRect(rect, _contentInset);
|
||||
}
|
||||
|
||||
- (CGRect)editingRectForBounds:(CGRect)bounds
|
||||
|
|
|
@ -29,6 +29,9 @@ RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString)
|
|||
RCT_EXPORT_VIEW_PROPERTY(text, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(clearButtonMode, UITextFieldViewMode)
|
||||
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(color, textColor, UIColor)
|
||||
RCT_REMAP_VIEW_PROPERTY(autoCapitalize, autocapitalizationType, UITextAutocapitalizationType)
|
||||
RCT_CUSTOM_VIEW_PROPERTY(fontSize, CGFloat, RCTTextField)
|
||||
|
@ -53,7 +56,7 @@ RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, RCTTextField)
|
|||
NSNumber *reactTag = shadowView.reactTag;
|
||||
UIEdgeInsets padding = shadowView.paddingAsInsets;
|
||||
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
((RCTTextField *)viewRegistry[reactTag]).paddingEdgeInsets = padding;
|
||||
((RCTTextField *)viewRegistry[reactTag]).contentInset = padding;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -153,4 +153,19 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v
|
|||
#define RCT_IGNORE_SHADOW_PROPERTY(name) \
|
||||
- (void)set_##name:(id)value forShadowView:(id)view withDefaultView:(id)defaultView {}
|
||||
|
||||
/**
|
||||
* Used for when view property names change. Will log an error when used.
|
||||
*/
|
||||
#define RCT_DEPRECATED_VIEW_PROPERTY(oldName, newName) \
|
||||
- (void)set_##oldName:(id)json forView:(id)view withDefaultView:(id)defaultView { \
|
||||
RCTLogError(@"Property '%s' has been replaced by '%s'.", #oldName, #newName); \
|
||||
[self set_##newName:json forView:view withDefaultView:defaultView]; \
|
||||
}
|
||||
|
||||
#define RCT_DEPRECATED_SHADOW_PROPERTY(oldName, newName) \
|
||||
- (void)set_##oldName:(id)json forShadowView:(id)view withDefaultView:(id)defaultView { \
|
||||
RCTLogError(@"Property '%s' has been replaced by '%s'.", #oldName, #newName); \
|
||||
[self set_##newName:json forView:view withDefaultView:defaultView]; \
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-native",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.0",
|
||||
"description": "A framework for building native apps using React",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -713,6 +713,6 @@ function NotFoundError() {
|
|||
this.status = 404;
|
||||
}
|
||||
|
||||
NotFoundError.__proto__ = Error.prototype;
|
||||
util.inherits(NotFoundError, Error);
|
||||
|
||||
module.exports = DependecyGraph;
|
||||
|
|
Loading…
Reference in New Issue