Merge pull request #1432 from tadeuzagallo/Update_Thu_28_May
Updates from Thu 28 May
34
.eslintrc
|
@ -1,8 +1,19 @@
|
|||
{
|
||||
"parser": "babel-eslint",
|
||||
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
|
||||
"env": {
|
||||
"es6": true,
|
||||
"jasmine": true,
|
||||
},
|
||||
|
||||
"plugins": [
|
||||
"react"
|
||||
],
|
||||
|
||||
// Map from global var to bool specifying if it can be redefined
|
||||
"globals": {
|
||||
"__DEV__": true,
|
||||
|
@ -36,10 +47,10 @@
|
|||
},
|
||||
|
||||
"rules": {
|
||||
"comma-dangle": 0, // disallow trailing commas in object literals
|
||||
"no-cond-assign": 1, // disallow assignment in conditional expressions
|
||||
"no-console": 0, // disallow use of console (off by default in the node environment)
|
||||
"no-constant-condition": 0, // disallow use of constant expressions in conditions
|
||||
"no-comma-dangle": 0, // disallow trailing commas in object literals
|
||||
"no-control-regex": 1, // disallow control characters in regular expressions
|
||||
"no-debugger": 1, // disallow use of debugger
|
||||
"no-dupe-keys": 1, // disallow duplicate keys when creating object literals
|
||||
|
@ -107,6 +118,7 @@
|
|||
"no-warning-comments": 0, // disallow usage of configurable warning terms in comments": 1, // e.g. TODO or FIXME (off by default)
|
||||
"no-with": 1, // disallow use of the with statement
|
||||
"radix": 1, // require use of the second argument for parseInt() (off by default)
|
||||
"semi-spacing": 1, // require a space after a semi-colon
|
||||
"vars-on-top": 0, // requires to declare all vars on top of their containing scope (off by default)
|
||||
"wrap-iife": 0, // require immediate function invocation to be wrapped in parentheses (off by default)
|
||||
"yoda": 1, // require or disallow Yoda conditions
|
||||
|
@ -177,7 +189,7 @@
|
|||
"space-in-parens": 0, // require or disallow spaces inside parentheses (off by default)
|
||||
"space-infix-ops": 1, // require spaces around operators
|
||||
"space-return-throw-case": 1, // require a space after return, throw, and case
|
||||
"space-unary-word-ops": 1, // require a space around word operators such as typeof (off by default)
|
||||
"space-unary-ops": [1, { "words": true, "nonwords": false }], // require or disallow spaces before/after unary operators (words on by default, nonwords off by default)
|
||||
"max-nested-callbacks": 0, // specify the maximum depth callbacks can be nested (off by default)
|
||||
"one-var": 0, // allow just one var statement per function (off by default)
|
||||
"wrap-regex": 0, // require regex literals to be wrapped in parentheses (off by default)
|
||||
|
@ -190,6 +202,22 @@
|
|||
"max-params": 0, // limits the number of parameters that can be used in the function declaration. (off by default)
|
||||
"max-statements": 0, // specify the maximum number of statement allowed in a function (off by default)
|
||||
"no-bitwise": 1, // disallow use of bitwise operators (off by default)
|
||||
"no-plusplus": 0 // disallow use of unary operators, ++ and -- (off by default)
|
||||
"no-plusplus": 0, // disallow use of unary operators, ++ and -- (off by default)
|
||||
|
||||
"react/display-name": 0,
|
||||
"react/jsx-boolean-value": 0,
|
||||
"react/jsx-quotes": [1, "double", "avoid-escape"],
|
||||
"react/jsx-no-undef": 1,
|
||||
"react/jsx-sort-props": 0,
|
||||
"react/jsx-uses-react": 0,
|
||||
"react/jsx-uses-vars": 1,
|
||||
"react/no-did-mount-set-state": [1, "allow-in-func"],
|
||||
"react/no-did-update-set-state": [1, "allow-in-func"],
|
||||
"react/no-multi-comp": 0,
|
||||
"react/no-unknown-property": 0,
|
||||
"react/prop-types": 0,
|
||||
"react/react-in-jsx-scope": 0,
|
||||
"react/self-closing-comp": 1,
|
||||
"react/wrap-multilines": 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
.*/node_modules/react-tools/src/event/EventPropagators.js
|
||||
|
||||
# Ignore commoner tests
|
||||
.*/node_modules/react-tools/node_modules/commoner/test/.*
|
||||
.*/node_modules/commoner/test/.*
|
||||
|
||||
# See https://github.com/facebook/flow/issues/442
|
||||
.*/react-tools/node_modules/commoner/lib/reader.js
|
||||
|
||||
# Ignore jest
|
||||
.*/react-native/node_modules/jest-cli/.*
|
||||
.*/node_modules/jest-cli/.*
|
||||
|
||||
# Ignore Website
|
||||
.*/website/.*
|
||||
|
|
|
@ -34,7 +34,10 @@ var MovieCell = React.createClass({
|
|||
var criticsScore = this.props.movie.ratings.critics_score;
|
||||
return (
|
||||
<View>
|
||||
<TouchableHighlight onPress={this.props.onSelect}>
|
||||
<TouchableHighlight
|
||||
onPress={this.props.onSelect}
|
||||
onShowUnderlay={this.props.onHighlight}
|
||||
onHideUnderlay={this.props.onUnhighlight}>
|
||||
<View style={styles.row}>
|
||||
<Image
|
||||
source={getImageSource(this.props.movie, 'det')}
|
||||
|
@ -54,7 +57,6 @@ var MovieCell = React.createClass({
|
|||
</View>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
<View style={styles.cellBorder} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
140D9B661AC36C42004F25EE /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14312D241AC3654D00CDC950 /* libRCTLinking.a */; };
|
||||
14A2D4421AC3E43800CC738A /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14A2D4411AC3E41A00CC738A /* libReact.a */; };
|
||||
58C5726B1AA6239E00CDF9C8 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C5725B1AA6236500CDF9C8 /* libRCTText.a */; };
|
||||
67C95F201B0E64A30040BCE2 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 67C95F1E1B0E647A0040BCE2 /* libRCTWebSocket.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -54,6 +55,13 @@
|
|||
remoteGlobalIDString = 58B5119B1A9E6C1200147676;
|
||||
remoteInfo = RCTText;
|
||||
};
|
||||
67C95F1D1B0E647A0040BCE2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 67C95F151B0E647A0040BCE2 /* RCTWebSocket.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 3C86DF461ADF2C930047B81A;
|
||||
remoteInfo = RCTWebSocket;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
|
@ -69,6 +77,7 @@
|
|||
14312D1E1AC3654D00CDC950 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = "<group>"; };
|
||||
14A2D43C1AC3E41A00CC738A /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../React/React.xcodeproj; sourceTree = "<group>"; };
|
||||
587650F61A9EB120008B8F17 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = SOURCE_ROOT; };
|
||||
67C95F151B0E647A0040BCE2 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -76,6 +85,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
67C95F201B0E64A30040BCE2 /* libRCTWebSocket.a in Frameworks */,
|
||||
14A2D4421AC3E43800CC738A /* libReact.a in Frameworks */,
|
||||
140D9B661AC36C42004F25EE /* libRCTLinking.a in Frameworks */,
|
||||
1341801E1AA91750003F314A /* libRCTNetwork.a in Frameworks */,
|
||||
|
@ -135,6 +145,7 @@
|
|||
58C571FC1AA6124500CDF9C8 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
67C95F151B0E647A0040BCE2 /* RCTWebSocket.xcodeproj */,
|
||||
14A2D43C1AC3E41A00CC738A /* React.xcodeproj */,
|
||||
14312D1E1AC3654D00CDC950 /* RCTLinking.xcodeproj */,
|
||||
134180151AA91740003F314A /* RCTNetwork.xcodeproj */,
|
||||
|
@ -152,6 +163,14 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
67C95F161B0E647A0040BCE2 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
67C95F1E1B0E647A0040BCE2 /* libRCTWebSocket.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83CBB9F61A601CBA00E9B192 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -228,6 +247,10 @@
|
|||
ProductGroup = 58C572571AA6236500CDF9C8 /* Products */;
|
||||
ProjectRef = 587650F61A9EB120008B8F17 /* RCTText.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 67C95F161B0E647A0040BCE2 /* Products */;
|
||||
ProjectRef = 67C95F151B0E647A0040BCE2 /* RCTWebSocket.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 14A2D43D1AC3E41A00CC738A /* Products */;
|
||||
ProjectRef = 14A2D43C1AC3E41A00CC738A /* React.xcodeproj */;
|
||||
|
@ -276,6 +299,13 @@
|
|||
remoteRef = 58C5725A1AA6236500CDF9C8 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
67C95F1E1B0E647A0040BCE2 /* libRCTWebSocket.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTWebSocket.a;
|
||||
remoteRef = 67C95F1D1B0E647A0040BCE2 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
/* End PBXReferenceProxy section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
|
|
|
@ -237,10 +237,31 @@ var SearchScreen = React.createClass({
|
|||
return <ActivityIndicatorIOS style={styles.scrollSpinner} />;
|
||||
},
|
||||
|
||||
renderRow: function(movie: Object) {
|
||||
renderSeparator: function(
|
||||
sectionID: number | string,
|
||||
rowID: number | string,
|
||||
adjacentRowHighlighted: boolean
|
||||
) {
|
||||
var style = styles.rowSeparator;
|
||||
if (adjacentRowHighlighted) {
|
||||
style = [style, styles.rowSeparatorHide];
|
||||
}
|
||||
return (
|
||||
<View key={"SEP_" + sectionID + "_" + rowID} style={style}/>
|
||||
);
|
||||
},
|
||||
|
||||
renderRow: function(
|
||||
movie: Object,
|
||||
sectionID: number | string,
|
||||
rowID: number | string,
|
||||
highlightRowFunc: (sectionID: ?number | string, rowID: ?number | string) => void,
|
||||
) {
|
||||
return (
|
||||
<MovieCell
|
||||
onSelect={() => this.selectMovie(movie)}
|
||||
onHighlight={() => highlightRowFunc(sectionID, rowID)}
|
||||
onUnhighlight={() => highlightRowFunc(null, null)}
|
||||
movie={movie}
|
||||
/>
|
||||
);
|
||||
|
@ -254,6 +275,7 @@ var SearchScreen = React.createClass({
|
|||
/> :
|
||||
<ListView
|
||||
ref="listview"
|
||||
renderSeparator={this.renderSeparator}
|
||||
dataSource={this.state.dataSource}
|
||||
renderFooter={this.renderFooter}
|
||||
renderRow={this.renderRow}
|
||||
|
@ -352,6 +374,14 @@ var styles = StyleSheet.create({
|
|||
scrollSpinner: {
|
||||
marginVertical: 20,
|
||||
},
|
||||
rowSeparator: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
||||
height: 1,
|
||||
marginLeft: 4,
|
||||
},
|
||||
rowSeparatorHide: {
|
||||
opacity: 0.0,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = SearchScreen;
|
||||
|
|
|
@ -50,11 +50,8 @@
|
|||
redboxError = [[RCTRedBox sharedInstance] currentErrorMessage];
|
||||
|
||||
foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
|
||||
if ([view respondsToSelector:@selector(attributedText)]) {
|
||||
NSString *text = [(id)view attributedText].string;
|
||||
if ([text isEqualToString:TEXT_TO_LOOK_FOR]) {
|
||||
return YES;
|
||||
}
|
||||
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}];
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
.*/node_modules/react-tools/src/event/EventPropagators.js
|
||||
|
||||
# Ignore commoner tests
|
||||
.*/node_modules/react-tools/node_modules/commoner/test/.*
|
||||
.*/node_modules/commoner/test/.*
|
||||
|
||||
# See https://github.com/facebook/flow/issues/442
|
||||
.*/react-tools/node_modules/commoner/lib/reader.js
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* 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 {
|
||||
Text,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var AccessibilityIOSExample = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<View
|
||||
onAccessibilityTap={() => alert('onAccessibilityTap success')}
|
||||
accessible={true}>
|
||||
<Text>
|
||||
Accessibility normal tap example
|
||||
</Text>
|
||||
</View>
|
||||
<View onMagicTap={() => alert('onMagicTap success')}
|
||||
accessible={true}>
|
||||
<Text>
|
||||
Accessibility magic tap example
|
||||
</Text>
|
||||
</View>
|
||||
<View accessibilityLabel="Some announcement"
|
||||
accessible={true}>
|
||||
<Text>
|
||||
Accessibility label example
|
||||
</Text>
|
||||
</View>
|
||||
<View accessibilityTraits={["button", "selected"]}
|
||||
accessible={true}>
|
||||
<Text>
|
||||
Accessibility traits example
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = 'AcccessibilityIOS';
|
||||
exports.description = 'Interface to show iOS\' accessibility samples';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Accessibility elements',
|
||||
render(): ReactElement { return <AccessibilityIOSExample />; }
|
||||
},
|
||||
];
|
|
@ -28,13 +28,19 @@ var AppStateSubscription = React.createClass({
|
|||
return {
|
||||
appState: AppStateIOS.currentState,
|
||||
previousAppStates: [],
|
||||
memoryWarnings: 0,
|
||||
};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
AppStateIOS.addEventListener('change', this._handleAppStateChange);
|
||||
AppStateIOS.addEventListener('memoryWarning', this._handleMemoryWarning);
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
AppStateIOS.removeEventListener('change', this._handleAppStateChange);
|
||||
AppStateIOS.removeEventListener('memoryWarning', this._handleMemoryWarning);
|
||||
},
|
||||
_handleMemoryWarning: function() {
|
||||
this.setState({memoryWarnings: this.state.memoryWarnings + 1})
|
||||
},
|
||||
_handleAppStateChange: function(appState) {
|
||||
var previousAppStates = this.state.previousAppStates.slice();
|
||||
|
@ -45,6 +51,13 @@ var AppStateSubscription = React.createClass({
|
|||
});
|
||||
},
|
||||
render() {
|
||||
if (this.props.showMemoryWarnings) {
|
||||
return (
|
||||
<View>
|
||||
<Text>{this.state.memoryWarnings}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
if (this.props.showCurrentOnly) {
|
||||
return (
|
||||
<View>
|
||||
|
@ -77,4 +90,9 @@ exports.examples = [
|
|||
title: 'Previous states:',
|
||||
render(): ReactElement { return <AppStateSubscription showCurrentOnly={false} />; }
|
||||
},
|
||||
{
|
||||
title: 'Memory Warnings',
|
||||
description: "In the simulator, hit Shift+Command+M to simulate a memory warning.",
|
||||
render(): ReactElement { return <AppStateSubscription showMemoryWarnings={true} />; }
|
||||
},
|
||||
];
|
||||
|
|
|
@ -80,6 +80,13 @@ var styles = StyleSheet.create({
|
|||
|
||||
borderTopLeftRadius: 100,
|
||||
},
|
||||
border7: {
|
||||
borderRadius: 20,
|
||||
},
|
||||
border7_inner: {
|
||||
backgroundColor: 'blue',
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = 'Border';
|
||||
|
@ -134,4 +141,15 @@ exports.examples = [
|
|||
return <View style={[styles.box, styles.border6]} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom Borders',
|
||||
description: 'borderRadius & clipping',
|
||||
render() {
|
||||
return (
|
||||
<View style={[styles.box, styles.border7]}>
|
||||
<View style={styles.border7_inner} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
|
|
@ -48,7 +48,9 @@ var TabBarExample = React.createClass({
|
|||
|
||||
render: function() {
|
||||
return (
|
||||
<TabBarIOS>
|
||||
<TabBarIOS
|
||||
tintColor="black"
|
||||
barTintColor="#3abeff">
|
||||
<TabBarIOS.Item
|
||||
title="Blue Tab"
|
||||
selected={this.state.selectedTab === 'blueTab'}
|
||||
|
|
|
@ -58,6 +58,7 @@ var COMPONENTS = [
|
|||
];
|
||||
|
||||
var APIS = [
|
||||
require('./AccessibilityIOSExample'),
|
||||
require('./ActionSheetIOSExample'),
|
||||
require('./AdSupportIOSExample'),
|
||||
require('./AlertIOSExample'),
|
||||
|
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 352 KiB After Width: | Height: | Size: 270 KiB |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 86 KiB |
|
@ -74,11 +74,8 @@
|
|||
|
||||
redboxError = [[RCTRedBox sharedInstance] currentErrorMessage];
|
||||
foundElement = [self findSubviewInView:vc.view matching:^(UIView *view) {
|
||||
if ([view respondsToSelector:@selector(attributedText)]) {
|
||||
NSString *text = [(id)view attributedText].string;
|
||||
if ([text isEqualToString:@"<View>"]) {
|
||||
return YES;
|
||||
}
|
||||
if ([view.accessibilityLabel isEqualToString:@"<View>"]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}];
|
||||
|
|
|
@ -11,15 +11,18 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var Map = require('Map');
|
||||
var NativeModules = require('NativeModules');
|
||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
var RCTAppState = NativeModules.AppState;
|
||||
|
||||
var logError = require('logError');
|
||||
var invariant = require('invariant');
|
||||
|
||||
var DEVICE_APPSTATE_EVENT = 'appStateDidChange';
|
||||
|
||||
var _appStateHandlers = {};
|
||||
var _eventHandlers = {
|
||||
change: new Map(),
|
||||
memoryWarning: new Map(),
|
||||
};
|
||||
|
||||
/**
|
||||
* `AppStateIOS` can tell you if the app is in the foreground or background,
|
||||
|
@ -82,12 +85,23 @@ var AppStateIOS = {
|
|||
type: string,
|
||||
handler: Function
|
||||
) {
|
||||
_appStateHandlers[handler] = RCTDeviceEventEmitter.addListener(
|
||||
DEVICE_APPSTATE_EVENT,
|
||||
(appStateData) => {
|
||||
handler(appStateData.app_state);
|
||||
}
|
||||
invariant(
|
||||
['change', 'memoryWarning'].indexOf(type) !== -1,
|
||||
'Trying to subscribe to unknown event: "%s"', type
|
||||
);
|
||||
if (type === 'change') {
|
||||
_eventHandlers[type].set(handler, RCTDeviceEventEmitter.addListener(
|
||||
'appStateDidChange',
|
||||
(appStateData) => {
|
||||
handler(appStateData.app_state);
|
||||
}
|
||||
));
|
||||
} else if (type === 'memoryWarning') {
|
||||
_eventHandlers[type].set(handler, RCTDeviceEventEmitter.addListener(
|
||||
'memoryWarning',
|
||||
handler
|
||||
));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -97,11 +111,15 @@ var AppStateIOS = {
|
|||
type: string,
|
||||
handler: Function
|
||||
) {
|
||||
if (!_appStateHandlers[handler]) {
|
||||
invariant(
|
||||
['change', 'memoryWarning'].indexOf(type) !== -1,
|
||||
'Trying to remove listener for unknown event: "%s"', type
|
||||
);
|
||||
if (!_eventHandlers[type].has(handler)) {
|
||||
return;
|
||||
}
|
||||
_appStateHandlers[handler].remove();
|
||||
_appStateHandlers[handler] = null;
|
||||
_eventHandlers[type].get(handler).remove();
|
||||
_eventHandlers[type].delete(handler);
|
||||
},
|
||||
|
||||
currentState: (null : ?String),
|
||||
|
@ -109,7 +127,7 @@ var AppStateIOS = {
|
|||
};
|
||||
|
||||
RCTDeviceEventEmitter.addListener(
|
||||
DEVICE_APPSTATE_EVENT,
|
||||
'appStateDidChange',
|
||||
(appStateData) => {
|
||||
AppStateIOS.currentState = appStateData.app_state;
|
||||
}
|
||||
|
|
|
@ -25,11 +25,22 @@ var TabBarIOS = React.createClass({
|
|||
|
||||
propTypes: {
|
||||
style: View.propTypes.style,
|
||||
/**
|
||||
* Color of the currently selected tab icon
|
||||
*/
|
||||
tintColor: React.PropTypes.string,
|
||||
/**
|
||||
* Background color of the tab bar
|
||||
*/
|
||||
barTintColor: React.PropTypes.string
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<RCTTabBar style={[styles.tabGroup, this.props.style]}>
|
||||
<RCTTabBar
|
||||
style={[styles.tabGroup, this.props.style]}
|
||||
tintColor={this.props.tintColor}
|
||||
barTintColor={this.props.barTintColor}>
|
||||
{this.props.children}
|
||||
</RCTTabBar>
|
||||
);
|
||||
|
|
|
@ -70,6 +70,14 @@ var TouchableHighlight = React.createClass({
|
|||
*/
|
||||
underlayColor: React.PropTypes.string,
|
||||
style: View.propTypes.style,
|
||||
/**
|
||||
* Called immediately after the underlay is shown
|
||||
*/
|
||||
onShowUnderlay: React.PropTypes.func,
|
||||
/**
|
||||
* Called immediately after the underlay is hidden
|
||||
*/
|
||||
onHideUnderlay: React.PropTypes.func,
|
||||
},
|
||||
|
||||
mixins: [NativeMethodsMixin, TimerMixin, Touchable.Mixin],
|
||||
|
@ -159,6 +167,7 @@ var TouchableHighlight = React.createClass({
|
|||
_showUnderlay: function() {
|
||||
this.refs[UNDERLAY_REF].setNativeProps(this.state.activeUnderlayProps);
|
||||
this.refs[CHILD_REF].setNativeProps(this.state.activeProps);
|
||||
this.props.onShowUnderlay && this.props.onShowUnderlay();
|
||||
},
|
||||
|
||||
_hideUnderlay: function() {
|
||||
|
@ -170,6 +179,7 @@ var TouchableHighlight = React.createClass({
|
|||
...INACTIVE_UNDERLAY_PROPS,
|
||||
style: this.state.underlayStyle,
|
||||
});
|
||||
this.props.onHideUnderlay && this.props.onHideUnderlay();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ var ListView = React.createClass({
|
|||
* You must provide a renderRow function. If you omit any of the other render
|
||||
* functions, ListView will simply skip rendering them.
|
||||
*
|
||||
* - renderRow(rowData, sectionID, rowID);
|
||||
* - renderRow(rowData, sectionID, rowID, highlightRow);
|
||||
* - renderSectionHeader(sectionData, sectionID);
|
||||
*/
|
||||
propTypes: {
|
||||
|
@ -116,11 +116,22 @@ var ListView = React.createClass({
|
|||
|
||||
dataSource: PropTypes.instanceOf(ListViewDataSource).isRequired,
|
||||
/**
|
||||
* (rowData, sectionID, rowID) => renderable
|
||||
* (sectionID, rowID, adjacentRowHighlighted) => renderable
|
||||
* If provided, a renderable component to be rendered as the separator
|
||||
* below each row but not the last row if there is a section header below.
|
||||
* Take a sectionID and rowID of the row above and whether its adjacent row
|
||||
* is highlighted.
|
||||
*/
|
||||
renderSeparator: PropTypes.func,
|
||||
/**
|
||||
* (rowData, sectionID, rowID, highlightRow) => renderable
|
||||
* Takes a data entry from the data source and its ids and should return
|
||||
* a renderable component to be rendered as the row. By default the data
|
||||
* is exactly what was put into the data source, but it's also possible to
|
||||
* provide custom extractors.
|
||||
* provide custom extractors. ListView can be notified when a row is
|
||||
* being highlighted by calling highlightRow function. The separators above and
|
||||
* below will be hidden when a row is highlighted. The highlighted state of
|
||||
* a row can be reset by calling highlightRow(null).
|
||||
*/
|
||||
renderRow: PropTypes.func.isRequired,
|
||||
/**
|
||||
|
@ -227,6 +238,7 @@ var ListView = React.createClass({
|
|||
return {
|
||||
curRenderedRowsCount: this.props.initialListSize,
|
||||
prevRenderedRowsCount: 0,
|
||||
highlightedRow: {},
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -256,6 +268,10 @@ var ListView = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
onRowHighlighted: function(sectionID, rowID) {
|
||||
this.setState({highlightedRow: {sectionID, rowID}});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var bodyComponents = [];
|
||||
|
||||
|
@ -305,11 +321,28 @@ var ListView = React.createClass({
|
|||
null,
|
||||
dataSource.getRowData(sectionIdx, rowIdx),
|
||||
sectionID,
|
||||
rowID
|
||||
rowID,
|
||||
this.onRowHighlighted
|
||||
)}
|
||||
/>;
|
||||
bodyComponents.push(row);
|
||||
totalIndex++;
|
||||
|
||||
if (this.props.renderSeparator &&
|
||||
(rowIdx !== rowIDs.length - 1 || sectionIdx === allRowIDs.length -1)) {
|
||||
var adjacentRowHighlighted =
|
||||
this.state.highlightedRow.sectionID === sectionID && (
|
||||
this.state.highlightedRow.rowID === rowID ||
|
||||
this.state.highlightedRow.rowID === rowIDs[rowIdx + 1]
|
||||
);
|
||||
var separator = this.props.renderSeparator(
|
||||
sectionID,
|
||||
rowID,
|
||||
adjacentRowHighlighted
|
||||
);
|
||||
bodyComponents.push(separator);
|
||||
totalIndex++;
|
||||
}
|
||||
if (++rowCount === this.state.curRenderedRowsCount) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -332,7 +332,7 @@ var Navigator = React.createClass({
|
|||
this._subRouteFocus = [];
|
||||
this.navigatorContext = {
|
||||
// Actions for child navigators or interceptors:
|
||||
setHandlerForRoute: this.setHandlerForRoute,
|
||||
setHandlerForIndex: this.setHandlerForIndex,
|
||||
request: this.request,
|
||||
|
||||
// Contextual utilities
|
||||
|
@ -340,14 +340,13 @@ var Navigator = React.createClass({
|
|||
getCurrentRoutes: this.getCurrentRoutes,
|
||||
// `route` is injected by NavigatorStaticContextContainer
|
||||
|
||||
// Contextual nav actions
|
||||
// Contextual nav action
|
||||
pop: this.requestPop,
|
||||
popToRoute: this.requestPopTo,
|
||||
|
||||
// Legacy, imperitive nav actions. Will transition these to contextual actions
|
||||
jumpBack: this.jumpBack,
|
||||
jumpForward: this.jumpForward,
|
||||
jumpTo: this.jumpTo,
|
||||
popToRoute: this.popToRoute,
|
||||
push: this.push,
|
||||
replace: this.replace,
|
||||
replaceAtIndex: this.replaceAtIndex,
|
||||
|
@ -410,8 +409,6 @@ var Navigator = React.createClass({
|
|||
switch (action) {
|
||||
case 'pop':
|
||||
return this._handlePop(arg1);
|
||||
case 'popTo':
|
||||
return this._handlePopTo(arg1);
|
||||
case 'push':
|
||||
return this._handlePush(arg1);
|
||||
default:
|
||||
|
@ -440,30 +437,13 @@ var Navigator = React.createClass({
|
|||
return true;
|
||||
},
|
||||
|
||||
_handlePopTo: function(destRoute) {
|
||||
if (destRoute) {
|
||||
var hasRoute = this.state.routeStack.indexOf(destRoute) !== -1;
|
||||
if (hasRoute) {
|
||||
this.popToRoute(destRoute);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this.state.presentedIndex === 0) {
|
||||
return false;
|
||||
}
|
||||
this.pop();
|
||||
return true;
|
||||
},
|
||||
|
||||
_handlePush: function(route) {
|
||||
this.push(route);
|
||||
return true;
|
||||
},
|
||||
|
||||
setHandlerForRoute: function(route, handler) {
|
||||
this._handlers[this.state.routeStack.indexOf(route)] = handler;
|
||||
setHandlerForIndex: function(index, handler) {
|
||||
this._handlers[index] = handler;
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
|
@ -1155,17 +1135,13 @@ var Navigator = React.createClass({
|
|||
this.popToRoute(this.state.routeStack[0]);
|
||||
},
|
||||
|
||||
_getNumToPopForRoute: function(route) {
|
||||
popToRoute: function(route) {
|
||||
var indexOfRoute = this.state.routeStack.indexOf(route);
|
||||
invariant(
|
||||
indexOfRoute !== -1,
|
||||
'Calling pop to route for a route that doesn\'t exist!'
|
||||
'Calling popToRoute for a route that doesn\'t exist!'
|
||||
);
|
||||
return this.state.presentedIndex - indexOfRoute;
|
||||
},
|
||||
|
||||
popToRoute: function(route) {
|
||||
var numToPop = this._getNumToPopForRoute(route);
|
||||
var numToPop = this.state.presentedIndex - indexOfRoute;
|
||||
this._popN(numToPop);
|
||||
},
|
||||
|
||||
|
@ -1262,7 +1238,7 @@ var Navigator = React.createClass({
|
|||
...this.navigatorContext,
|
||||
route,
|
||||
setHandler: (handler) => {
|
||||
this.navigatorContext.setHandlerForRoute(route, handler);
|
||||
this.navigatorContext.setHandlerForIndex(i, handler);
|
||||
},
|
||||
onWillFocus: (childRoute) => {
|
||||
this._subRouteFocus[i] = childRoute;
|
||||
|
|
|
@ -85,7 +85,9 @@ static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /
|
|||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_timeoutTimer invalidate];
|
||||
if (_timeoutTimer.valid) {
|
||||
[_timeoutTimer invalidate];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -273,6 +275,7 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
|
|||
// Fire all queued callbacks
|
||||
for (RCTLocationRequest *request in _pendingRequests) {
|
||||
request.successBlock(@[_lastLocationEvent]);
|
||||
[request.timeoutTimer invalidate];
|
||||
}
|
||||
[_pendingRequests removeAllObjects];
|
||||
|
||||
|
@ -311,6 +314,7 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
|
|||
// Fire all queued error callbacks
|
||||
for (RCTLocationRequest *request in _pendingRequests) {
|
||||
request.errorBlock(@[jsError]);
|
||||
[request.timeoutTimer invalidate];
|
||||
}
|
||||
[_pendingRequests removeAllObjects];
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* 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 Inspector
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var ReactInstanceHandles = require('ReactInstanceHandles');
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactNativeMount = require('ReactNativeMount');
|
||||
var ReactNativeTagHandles = require('ReactNativeTagHandles');
|
||||
|
||||
function traverseOwnerTreeUp(hierarchy, instance) {
|
||||
if (instance) {
|
||||
hierarchy.unshift(instance);
|
||||
traverseOwnerTreeUp(hierarchy, instance._currentElement._owner);
|
||||
}
|
||||
}
|
||||
|
||||
function findInstance(component, targetID) {
|
||||
if (targetID === findRootNodeID(component)) {
|
||||
return component;
|
||||
}
|
||||
if (component._renderedComponent) {
|
||||
return findInstance(component._renderedComponent, targetID);
|
||||
} else {
|
||||
for (var key in component._renderedChildren) {
|
||||
var child = component._renderedChildren[key];
|
||||
if (ReactInstanceHandles.isAncestorIDOf(findRootNodeID(child), targetID)) {
|
||||
var instance = findInstance(child, targetID);
|
||||
if (instance) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findRootNodeID(component) {
|
||||
var internalInstance = ReactInstanceMap.get(component);
|
||||
return internalInstance ? internalInstance._rootNodeID : component._rootNodeID;
|
||||
}
|
||||
|
||||
function findInstanceByNativeTag(rootTag, nativeTag) {
|
||||
var containerID = ReactNativeTagHandles.tagToRootNodeID[rootTag];
|
||||
var rootInstance = ReactNativeMount._instancesByContainerID[containerID];
|
||||
var targetID = ReactNativeTagHandles.tagToRootNodeID[nativeTag];
|
||||
return findInstance(rootInstance, targetID);
|
||||
}
|
||||
|
||||
function getOwnerHierarchy(instance) {
|
||||
var hierarchy = [];
|
||||
traverseOwnerTreeUp(hierarchy, instance);
|
||||
return hierarchy;
|
||||
}
|
||||
|
||||
module.exports = {findInstanceByNativeTag, getOwnerHierarchy};
|
|
@ -23,7 +23,7 @@ class XMLHttpRequestBase {
|
|||
readyState: number;
|
||||
responseHeaders: ?Object;
|
||||
responseText: ?string;
|
||||
status: ?string;
|
||||
status: number;
|
||||
|
||||
_method: ?string;
|
||||
_url: ?string;
|
||||
|
@ -43,7 +43,7 @@ class XMLHttpRequestBase {
|
|||
this.readyState = this.UNSENT;
|
||||
this.responseHeaders = undefined;
|
||||
this.responseText = undefined;
|
||||
this.status = undefined;
|
||||
this.status = 0;
|
||||
|
||||
this._method = null;
|
||||
this._url = null;
|
||||
|
@ -73,7 +73,7 @@ class XMLHttpRequestBase {
|
|||
}
|
||||
|
||||
setRequestHeader(header: string, value: any): void {
|
||||
this._headers[header] = value;
|
||||
this._headers[header.toLowerCase()] = value;
|
||||
}
|
||||
|
||||
open(method: string, url: string, async: ?boolean): void {
|
||||
|
@ -127,7 +127,7 @@ class XMLHttpRequestBase {
|
|||
this._aborted = true;
|
||||
}
|
||||
|
||||
callback(status: string, responseHeaders: ?Object, responseText: string): void {
|
||||
callback(status: number, responseHeaders: ?Object, responseText: string): void {
|
||||
if (this._aborted) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* 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 InspectorOverlay
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Dimensions = require('Dimensions');
|
||||
var Inspector = require('Inspector');
|
||||
var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var Text = require('Text');
|
||||
var UIManager = require('NativeModules').UIManager;
|
||||
var View = require('View');
|
||||
|
||||
var InspectorOverlay = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
frame: null,
|
||||
hierarchy: [],
|
||||
};
|
||||
},
|
||||
|
||||
findViewForTouchEvent: function(e) {
|
||||
var {locationX, locationY} = e.nativeEvent.touches[0];
|
||||
UIManager.findSubviewIn(
|
||||
this.props.inspectedViewTag,
|
||||
[locationX, locationY],
|
||||
(nativeViewTag, left, top, width, height) => {
|
||||
var instance = Inspector.findInstanceByNativeTag(this.props.rootTag, nativeViewTag);
|
||||
var hierarchy = Inspector.getOwnerHierarchy(instance);
|
||||
this.setState({
|
||||
hierarchy,
|
||||
frame: {left, top, width, height}
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
shouldSetResponser: function(e) {
|
||||
this.findViewForTouchEvent(e);
|
||||
return true;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var content = [];
|
||||
|
||||
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 justifyContent = distanceToTop > distanceToBottom
|
||||
? 'flex-start'
|
||||
: 'flex-end';
|
||||
|
||||
content.push(<View style={[styles.frame, this.state.frame]} />);
|
||||
content.push(<ElementProperties hierarchy={this.state.hierarchy} />);
|
||||
}
|
||||
return (
|
||||
<View
|
||||
onStartShouldSetResponder={this.shouldSetResponser}
|
||||
onResponderMove={this.findViewForTouchEvent}
|
||||
style={[styles.inspector, {justifyContent}]}>
|
||||
{content}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ElementProperties = React.createClass({
|
||||
render: function() {
|
||||
var path = this.props.hierarchy.map((instance) => instance.getName()).join(' > ');
|
||||
return (
|
||||
<View style={styles.info}>
|
||||
<Text style={styles.path}>
|
||||
{path}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
inspector: {
|
||||
backgroundColor: 'rgba(255,255,255,0.8)',
|
||||
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;
|
|
@ -7,17 +7,59 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule renderApplication
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var InspectorOverlay = require('InspectorOverlay');
|
||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var Subscribable = require('Subscribable');
|
||||
var View = require('View');
|
||||
var WarningBox = require('WarningBox');
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
var AppContainer = React.createClass({
|
||||
mixins: [Subscribable.Mixin],
|
||||
|
||||
getInitialState: function() {
|
||||
return { inspector: null };
|
||||
},
|
||||
|
||||
toggleElementInspector: function() {
|
||||
var inspector = this.state.inspector
|
||||
? null
|
||||
: <InspectorOverlay
|
||||
rootTag={this.props.rootTag}
|
||||
inspectedViewTag={React.findNodeHandle(this.refs.main)}
|
||||
/>;
|
||||
this.setState({inspector});
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.addListenerOn(
|
||||
RCTDeviceEventEmitter,
|
||||
'toggleElementInspector',
|
||||
this.toggleElementInspector
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var shouldRenderWarningBox = __DEV__ && console.yellowBoxEnabled;
|
||||
var warningBox = shouldRenderWarningBox ? <WarningBox /> : null;
|
||||
return (
|
||||
<View style={styles.appContainer}>
|
||||
<View style={styles.appContainer} ref="main">
|
||||
{this.props.children}
|
||||
</View>
|
||||
{warningBox}
|
||||
{this.state.inspector}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function renderApplication<D, P, S>(
|
||||
RootComponent: ReactClass<D, P, S>,
|
||||
initialProps: P,
|
||||
|
@ -27,15 +69,12 @@ function renderApplication<D, P, S>(
|
|||
rootTag,
|
||||
'Expect to have a valid rootTag, instead got ', rootTag
|
||||
);
|
||||
var shouldRenderWarningBox = __DEV__ && console.yellowBoxEnabled;
|
||||
var warningBox = shouldRenderWarningBox ? <WarningBox /> : null;
|
||||
React.render(
|
||||
<View style={styles.appContainer}>
|
||||
<AppContainer rootTag={rootTag}>
|
||||
<RootComponent
|
||||
{...initialProps}
|
||||
/>
|
||||
{warningBox}
|
||||
</View>,
|
||||
</AppContainer>,
|
||||
rootTag
|
||||
);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ var pointsDiffer = require('pointsDiffer');
|
|||
var matricesDiffer = require('matricesDiffer');
|
||||
var sizesDiffer = require('sizesDiffer');
|
||||
var verifyPropTypes = require('verifyPropTypes');
|
||||
var warning = require('warning');
|
||||
|
||||
/**
|
||||
* Used to create React components that directly wrap native component
|
||||
|
@ -42,12 +43,13 @@ function requireNativeComponent(
|
|||
wrapperComponent: ?Function
|
||||
): Function {
|
||||
var viewConfig = RCTUIManager[viewName];
|
||||
if (!viewConfig || !viewConfig.nativeProps) {
|
||||
if (!viewConfig || !viewConfig.NativeProps) {
|
||||
warning(false, 'Native component for "%s" does not exist', viewName);
|
||||
return UnimplementedView;
|
||||
}
|
||||
var nativeProps = {
|
||||
...RCTUIManager.RCTView.nativeProps,
|
||||
...viewConfig.nativeProps,
|
||||
...RCTUIManager.RCTView.NativeProps,
|
||||
...viewConfig.NativeProps,
|
||||
};
|
||||
viewConfig.uiViewClassName = viewName;
|
||||
viewConfig.validAttributes = {};
|
||||
|
|
|
@ -29,7 +29,7 @@ function verifyPropTypes(
|
|||
);
|
||||
}
|
||||
|
||||
var nativeProps = viewConfig.nativeProps;
|
||||
var nativeProps = viewConfig.NativeProps;
|
||||
for (var prop in nativeProps) {
|
||||
if (!component.propTypes[prop] &&
|
||||
!View.propTypes[prop] &&
|
||||
|
|
|
@ -14,8 +14,6 @@ extern NSString *const RCTReactTagAttributeName;
|
|||
|
||||
@interface RCTShadowText : RCTShadowView
|
||||
|
||||
@property (nonatomic, assign) NSWritingDirection writingDirection;
|
||||
@property (nonatomic, strong) UIColor *textBackgroundColor;
|
||||
@property (nonatomic, strong) UIColor *color;
|
||||
@property (nonatomic, copy) NSString *fontFamily;
|
||||
@property (nonatomic, assign) CGFloat fontSize;
|
||||
|
@ -24,17 +22,12 @@ extern NSString *const RCTReactTagAttributeName;
|
|||
@property (nonatomic, assign) BOOL isHighlighted;
|
||||
@property (nonatomic, assign) CGFloat letterSpacing;
|
||||
@property (nonatomic, assign) CGFloat lineHeight;
|
||||
@property (nonatomic, assign) NSUInteger maximumNumberOfLines;
|
||||
@property (nonatomic, assign) NSUInteger numberOfLines;
|
||||
@property (nonatomic, assign) CGSize shadowOffset;
|
||||
@property (nonatomic, assign) NSTextAlignment textAlign;
|
||||
@property (nonatomic, strong) UIColor *textBackgroundColor;
|
||||
@property (nonatomic, assign) NSWritingDirection writingDirection;
|
||||
|
||||
// Not exposed to JS
|
||||
@property (nonatomic, strong) UIFont *font;
|
||||
@property (nonatomic, assign) NSLineBreakMode truncationMode;
|
||||
@property (nonatomic, assign) CGFloat effectiveLetterSpacing;
|
||||
|
||||
@property (nonatomic, copy, readonly) NSAttributedString *attributedString;
|
||||
@property (nonatomic, strong, readonly) NSLayoutManager *layoutManager;
|
||||
@property (nonatomic, strong, readonly) NSTextContainer *textContainer;
|
||||
- (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width;
|
||||
|
||||
@end
|
||||
|
|
|
@ -17,62 +17,61 @@
|
|||
NSString *const RCTIsHighlightedAttributeName = @"IsHighlightedAttributeName";
|
||||
NSString *const RCTReactTagAttributeName = @"ReactTagAttributeName";
|
||||
|
||||
@implementation RCTShadowText
|
||||
{
|
||||
NSAttributedString *_cachedAttributedString;
|
||||
CGFloat _effectiveLetterSpacing;
|
||||
}
|
||||
|
||||
static css_dim_t RCTMeasure(void *context, float width)
|
||||
{
|
||||
RCTShadowText *shadowText = (__bridge RCTShadowText *)context;
|
||||
|
||||
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:[shadowText attributedString]];
|
||||
NSTextStorage *previousTextStorage = shadowText.layoutManager.textStorage;
|
||||
if (previousTextStorage) {
|
||||
[previousTextStorage removeLayoutManager:shadowText.layoutManager];
|
||||
}
|
||||
[textStorage addLayoutManager:shadowText.layoutManager];
|
||||
|
||||
shadowText.textContainer.size = CGSizeMake(isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX);
|
||||
[shadowText.layoutManager ensureLayoutForTextContainer:shadowText.textContainer];
|
||||
|
||||
CGSize computedSize = [shadowText.layoutManager usedRectForTextContainer:shadowText.textContainer].size;
|
||||
|
||||
[textStorage removeLayoutManager:shadowText.layoutManager];
|
||||
if (previousTextStorage) {
|
||||
[previousTextStorage addLayoutManager:shadowText.layoutManager];
|
||||
}
|
||||
NSTextStorage *textStorage = [shadowText buildTextStorageForWidth:width];
|
||||
NSLayoutManager *layoutManager = [textStorage.layoutManagers firstObject];
|
||||
NSTextContainer *textContainer = [layoutManager.textContainers firstObject];
|
||||
CGSize computedSize = [layoutManager usedRectForTextContainer:textContainer].size;
|
||||
|
||||
css_dim_t result;
|
||||
result.dimensions[CSS_WIDTH] = RCTCeilPixelValue(computedSize.width);
|
||||
if (shadowText.effectiveLetterSpacing < 0) {
|
||||
result.dimensions[CSS_WIDTH] -= shadowText.effectiveLetterSpacing;
|
||||
if (shadowText->_effectiveLetterSpacing < 0) {
|
||||
result.dimensions[CSS_WIDTH] -= shadowText->_effectiveLetterSpacing;
|
||||
}
|
||||
result.dimensions[CSS_HEIGHT] = RCTCeilPixelValue(computedSize.height);
|
||||
return result;
|
||||
}
|
||||
|
||||
@implementation RCTShadowText
|
||||
{
|
||||
NSLayoutManager *_layoutManager;
|
||||
NSTextContainer *_textContainer;
|
||||
NSAttributedString *_cachedAttributedString;
|
||||
UIFont *_font;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_fontSize = NAN;
|
||||
_letterSpacing = NAN;
|
||||
_isHighlighted = NO;
|
||||
|
||||
_textContainer = [[NSTextContainer alloc] init];
|
||||
_textContainer.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
_textContainer.lineFragmentPadding = 0.0;
|
||||
|
||||
_layoutManager = [[NSLayoutManager alloc] init];
|
||||
[_layoutManager addTextContainer:_textContainer];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width
|
||||
{
|
||||
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
|
||||
|
||||
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedString];
|
||||
[textStorage addLayoutManager:layoutManager];
|
||||
|
||||
NSTextContainer *textContainer = [[NSTextContainer alloc] init];
|
||||
textContainer.lineFragmentPadding = 0.0;
|
||||
textContainer.lineBreakMode = _numberOfLines > 0 ? NSLineBreakByTruncatingTail : NSLineBreakByClipping;
|
||||
textContainer.maximumNumberOfLines = _numberOfLines;
|
||||
|
||||
UIEdgeInsets padding = self.paddingAsInsets;
|
||||
width -= (padding.left + padding.right);
|
||||
textContainer.size = (CGSize){isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX};
|
||||
|
||||
[layoutManager addTextContainer:textContainer];
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
|
||||
return textStorage;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedString
|
||||
{
|
||||
return [self _attributedStringWithFontFamily:nil
|
||||
|
@ -135,8 +134,8 @@ static css_dim_t RCTMeasure(void *context, float width)
|
|||
[self _addAttribute:NSBackgroundColorAttributeName withValue:self.textBackgroundColor toAttributedString:attributedString];
|
||||
}
|
||||
|
||||
_font = [RCTConvert UIFont:nil withFamily:fontFamily size:fontSize weight:fontWeight style:fontStyle];
|
||||
[self _addAttribute:NSFontAttributeName withValue:_font toAttributedString:attributedString];
|
||||
UIFont *font = [RCTConvert UIFont:nil withFamily:fontFamily size:fontSize weight:fontWeight style:fontStyle];
|
||||
[self _addAttribute:NSFontAttributeName withValue:font toAttributedString:attributedString];
|
||||
[self _addAttribute:NSKernAttributeName withValue:letterSpacing toAttributedString:attributedString];
|
||||
[self _addAttribute:RCTReactTagAttributeName withValue:self.reactTag toAttributedString:attributedString];
|
||||
[self _setParagraphStyleOnAttributedString:attributedString];
|
||||
|
@ -148,11 +147,6 @@ static css_dim_t RCTMeasure(void *context, float width)
|
|||
return _cachedAttributedString;
|
||||
}
|
||||
|
||||
- (UIFont *)font
|
||||
{
|
||||
return _font ?: [RCTConvert UIFont:nil withFamily:_fontFamily size:@(_fontSize) weight:_fontWeight style:_fontStyle];
|
||||
}
|
||||
|
||||
- (void)_addAttribute:(NSString *)attribute withValue:(id)attributeValue toAttributedString:(NSMutableAttributedString *)attributedString
|
||||
{
|
||||
[attributedString enumerateAttribute:attribute inRange:NSMakeRange(0, [attributedString length]) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
|
||||
|
@ -231,38 +225,18 @@ static css_dim_t RCTMeasure(void *context, float width)
|
|||
[self dirtyText]; \
|
||||
}
|
||||
|
||||
RCT_TEXT_PROPERTY(TextBackgroundColor, _textBackgroundColor, UIColor *);
|
||||
RCT_TEXT_PROPERTY(Color, _color, UIColor *);
|
||||
RCT_TEXT_PROPERTY(FontFamily, _fontFamily, NSString *);
|
||||
RCT_TEXT_PROPERTY(FontSize, _fontSize, CGFloat);
|
||||
RCT_TEXT_PROPERTY(FontWeight, _fontWeight, NSString *);
|
||||
RCT_TEXT_PROPERTY(LetterSpacing, _letterSpacing, CGFloat);
|
||||
RCT_TEXT_PROPERTY(LineHeight, _lineHeight, CGFloat);
|
||||
RCT_TEXT_PROPERTY(ShadowOffset, _shadowOffset, CGSize);
|
||||
RCT_TEXT_PROPERTY(TextAlign, _textAlign, NSTextAlignment);
|
||||
RCT_TEXT_PROPERTY(IsHighlighted, _isHighlighted, BOOL);
|
||||
RCT_TEXT_PROPERTY(Font, _font, UIFont *);
|
||||
|
||||
- (NSLineBreakMode)truncationMode
|
||||
{
|
||||
return _textContainer.lineBreakMode;
|
||||
}
|
||||
|
||||
- (void)setTruncationMode:(NSLineBreakMode)truncationMode
|
||||
{
|
||||
_textContainer.lineBreakMode = truncationMode;
|
||||
[self dirtyText];
|
||||
}
|
||||
|
||||
- (NSUInteger)maximumNumberOfLines
|
||||
{
|
||||
return _textContainer.maximumNumberOfLines;
|
||||
}
|
||||
|
||||
- (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines
|
||||
{
|
||||
_textContainer.maximumNumberOfLines = maximumNumberOfLines;
|
||||
[self dirtyText];
|
||||
}
|
||||
RCT_TEXT_PROPERTY(Color, _color, UIColor *)
|
||||
RCT_TEXT_PROPERTY(FontFamily, _fontFamily, NSString *)
|
||||
RCT_TEXT_PROPERTY(FontSize, _fontSize, CGFloat)
|
||||
RCT_TEXT_PROPERTY(FontWeight, _fontWeight, NSString *)
|
||||
RCT_TEXT_PROPERTY(FontStyle, _fontStyle, NSString *)
|
||||
RCT_TEXT_PROPERTY(IsHighlighted, _isHighlighted, BOOL)
|
||||
RCT_TEXT_PROPERTY(LetterSpacing, _letterSpacing, CGFloat)
|
||||
RCT_TEXT_PROPERTY(LineHeight, _lineHeight, CGFloat)
|
||||
RCT_TEXT_PROPERTY(NumberOfLines, _numberOfLines, NSUInteger)
|
||||
RCT_TEXT_PROPERTY(ShadowOffset, _shadowOffset, CGSize)
|
||||
RCT_TEXT_PROPERTY(TextAlign, _textAlign, NSTextAlignment)
|
||||
RCT_TEXT_PROPERTY(TextBackgroundColor, _textBackgroundColor, UIColor *)
|
||||
RCT_TEXT_PROPERTY(WritingDirection, _writingDirection, NSWritingDirection)
|
||||
|
||||
@end
|
||||
|
|
|
@ -11,9 +11,7 @@
|
|||
|
||||
@interface RCTText : UIView
|
||||
|
||||
@property (nonatomic, strong) NSLayoutManager *layoutManager;
|
||||
@property (nonatomic, strong) NSTextContainer *textContainer;
|
||||
@property (nonatomic, copy) NSAttributedString *attributedText;
|
||||
@property (nonatomic, assign) UIEdgeInsets contentInset;
|
||||
@property (nonatomic, strong) NSTextStorage *textStorage;
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
|
||||
@implementation RCTText
|
||||
{
|
||||
NSLayoutManager *_layoutManager;
|
||||
NSTextStorage *_textStorage;
|
||||
NSTextContainer *_textContainer;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
|
@ -31,103 +29,42 @@
|
|||
self.opaque = NO;
|
||||
self.contentMode = UIViewContentModeRedraw;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedText
|
||||
- (void)setTextStorage:(NSTextStorage *)textStorage
|
||||
{
|
||||
return [_textStorage copy];
|
||||
}
|
||||
|
||||
- (void)setAttributedText:(NSAttributedString *)attributedText
|
||||
{
|
||||
for (NSLayoutManager *existingLayoutManager in _textStorage.layoutManagers) {
|
||||
[_textStorage removeLayoutManager:existingLayoutManager];
|
||||
}
|
||||
|
||||
_textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedText];
|
||||
|
||||
if (_layoutManager) {
|
||||
[_textStorage addLayoutManager:_layoutManager];
|
||||
}
|
||||
|
||||
_textStorage = textStorage;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)setTextContainer:(NSTextContainer *)textContainer
|
||||
{
|
||||
if ([_textContainer isEqual:textContainer]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_textContainer = textContainer;
|
||||
|
||||
for (NSInteger i = _layoutManager.textContainers.count - 1; i >= 0; i--) {
|
||||
[_layoutManager removeTextContainerAtIndex:i];
|
||||
}
|
||||
|
||||
if (_textContainer) {
|
||||
[_layoutManager addTextContainer:_textContainer];
|
||||
}
|
||||
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)setLayoutManager:(NSLayoutManager *)layoutManager
|
||||
{
|
||||
if ([_layoutManager isEqual:layoutManager]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_layoutManager = layoutManager;
|
||||
|
||||
for (NSLayoutManager *existingLayoutManager in _textStorage.layoutManagers) {
|
||||
[_textStorage removeLayoutManager:existingLayoutManager];
|
||||
}
|
||||
|
||||
if (_layoutManager) {
|
||||
[_textStorage addLayoutManager:_layoutManager];
|
||||
}
|
||||
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (CGRect)textFrame
|
||||
{
|
||||
return UIEdgeInsetsInsetRect(self.bounds, _contentInset);
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
CGRect textFrame = [self textFrame];
|
||||
|
||||
// We reset the text container size every time because RCTShadowText's
|
||||
// RCTMeasure overrides it. The header comment for `size` says that a height
|
||||
// of 0.0 should be enough, but it isn't.
|
||||
_textContainer.size = CGSizeMake(textFrame.size.width, CGFLOAT_MAX);
|
||||
|
||||
NSRange glyphRange = [_layoutManager glyphRangeForTextContainer:_textContainer];
|
||||
[_layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin];
|
||||
[_layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin];
|
||||
NSLayoutManager *layoutManager = [_textStorage.layoutManagers firstObject];
|
||||
NSTextContainer *textContainer = [layoutManager.textContainers firstObject];
|
||||
CGRect textFrame = UIEdgeInsetsInsetRect(self.bounds, _contentInset);
|
||||
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
|
||||
[layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin];
|
||||
[layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin];
|
||||
}
|
||||
|
||||
- (NSNumber *)reactTagAtPoint:(CGPoint)point
|
||||
{
|
||||
CGFloat fraction;
|
||||
NSUInteger characterIndex = [_layoutManager characterIndexForPoint:point
|
||||
inTextContainer:_textContainer
|
||||
fractionOfDistanceBetweenInsertionPoints:&fraction];
|
||||
NSNumber *reactTag = self.reactTag;
|
||||
|
||||
NSNumber *reactTag = nil;
|
||||
CGFloat fraction;
|
||||
NSLayoutManager *layoutManager = [_textStorage.layoutManagers firstObject];
|
||||
NSTextContainer *textContainer = [layoutManager.textContainers firstObject];
|
||||
NSUInteger characterIndex = [layoutManager characterIndexForPoint:point
|
||||
inTextContainer:textContainer
|
||||
fractionOfDistanceBetweenInsertionPoints:&fraction];
|
||||
|
||||
// If the point is not before (fraction == 0.0) the first character and not
|
||||
// after (fraction == 1.0) the last character, then the attribute is valid.
|
||||
if (_textStorage.length > 0 && (fraction > 0 || characterIndex > 0) && (fraction < 1 || characterIndex < _textStorage.length - 1)) {
|
||||
reactTag = [_textStorage attribute:RCTReactTagAttributeName atIndex:characterIndex effectiveRange:NULL];
|
||||
}
|
||||
|
||||
return reactTag ?: self.reactTag;
|
||||
return reactTag;
|
||||
}
|
||||
|
||||
#pragma mark - Accessibility
|
||||
|
|
|
@ -47,24 +47,11 @@ RCT_EXPORT_SHADOW_PROPERTY(fontStyle, NSString)
|
|||
RCT_EXPORT_SHADOW_PROPERTY(isHighlighted, BOOL)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(letterSpacing, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(lineHeight, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(maximumNumberOfLines, NSInteger)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(shadowOffset, CGSize)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(textAlign, NSTextAlignment)
|
||||
RCT_REMAP_SHADOW_PROPERTY(backgroundColor, textBackgroundColor, UIColor)
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(containerBackgroundColor, UIColor, RCTShadowText)
|
||||
{
|
||||
view.backgroundColor = json ? [RCTConvert UIColor:json] : defaultView.backgroundColor;
|
||||
view.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
||||
}
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, NSInteger, RCTShadowText)
|
||||
{
|
||||
NSLineBreakMode truncationMode = NSLineBreakByClipping;
|
||||
view.maximumNumberOfLines = json ? [RCTConvert NSInteger:json] : defaultView.maximumNumberOfLines;
|
||||
if (view.maximumNumberOfLines > 0) {
|
||||
truncationMode = NSLineBreakByTruncatingTail;
|
||||
}
|
||||
view.truncationMode = truncationMode;
|
||||
}
|
||||
RCT_REMAP_SHADOW_PROPERTY(containerBackgroundColor, backgroundColor, UIColor)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(numberOfLines, NSUInteger)
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry
|
||||
{
|
||||
|
@ -81,7 +68,7 @@ RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, NSInteger, RCTShadowText)
|
|||
continue;
|
||||
}
|
||||
|
||||
RCTSparseArray *reactTaggedAttributedStrings = [[RCTSparseArray alloc] init];
|
||||
RCTSparseArray *reactTaggedTextStorage = [[RCTSparseArray alloc] init];
|
||||
NSMutableArray *queue = [NSMutableArray arrayWithObject:rootView];
|
||||
for (NSInteger i = 0; i < [queue count]; i++) {
|
||||
RCTShadowView *shadowView = queue[i];
|
||||
|
@ -89,9 +76,11 @@ RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, NSInteger, RCTShadowText)
|
|||
|
||||
if ([shadowView isKindOfClass:[RCTShadowText class]]) {
|
||||
RCTShadowText *shadowText = (RCTShadowText *)shadowView;
|
||||
reactTaggedAttributedStrings[shadowText.reactTag] = [shadowText attributedString];
|
||||
NSTextStorage *textStorage = [shadowText buildTextStorageForWidth:shadowView.frame.size.width];
|
||||
reactTaggedTextStorage[shadowText.reactTag] = textStorage;
|
||||
} else if ([shadowView isKindOfClass:[RCTShadowRawText class]]) {
|
||||
RCTLogError(@"Raw text cannot be used outside of a <Text> tag. Not rendering string: '%@'", [(RCTShadowRawText *)shadowView text]);
|
||||
RCTLogError(@"Raw text cannot be used outside of a <Text> tag. Not rendering string: '%@'",
|
||||
[(RCTShadowRawText *)shadowView text]);
|
||||
} else {
|
||||
for (RCTShadowView *child in [shadowView reactSubviews]) {
|
||||
if ([child isTextDirty]) {
|
||||
|
@ -104,9 +93,9 @@ RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, NSInteger, RCTShadowText)
|
|||
}
|
||||
|
||||
[uiBlocks addObject:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
[reactTaggedAttributedStrings enumerateObjectsUsingBlock:^(NSAttributedString *attributedString, NSNumber *reactTag, BOOL *stop) {
|
||||
[reactTaggedTextStorage enumerateObjectsUsingBlock:^(NSTextStorage *textStorage, NSNumber *reactTag, BOOL *stop) {
|
||||
RCTText *text = viewRegistry[reactTag];
|
||||
text.attributedText = attributedString;
|
||||
text.textStorage = textStorage;
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
@ -126,8 +115,6 @@ RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, NSInteger, RCTShadowText)
|
|||
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
RCTText *text = viewRegistry[reactTag];
|
||||
text.contentInset = padding;
|
||||
text.layoutManager = shadowView.layoutManager;
|
||||
text.textContainer = shadowView.textContainer;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @generated SignedSource<<9bce659a43d6f6115b20a18f6c995d8a>>
|
||||
* @generated SignedSource<<77bdeb858138636c96c405d64b6be55c>>
|
||||
*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !! This file is a check-in of a static_upstream project! !!
|
||||
|
@ -20,57 +20,73 @@
|
|||
/* jslint bitwise: true */
|
||||
|
||||
/**
|
||||
* Copyright (c) 2006 Andrea Ercolino
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* Modified from the original for performance improvements.
|
||||
*
|
||||
* @see http://create.stephan-brumme.com/crc32/
|
||||
* @see http://stackoverflow.com/questions/18638900/
|
||||
* @copyright 2006 Andrea Ercolino
|
||||
* @license MIT
|
||||
*/
|
||||
var table = '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 ' +
|
||||
'9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 ' +
|
||||
'90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 ' +
|
||||
'83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 ' +
|
||||
'8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ' +
|
||||
'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ' +
|
||||
'ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 ' +
|
||||
'B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB ' +
|
||||
'B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 ' +
|
||||
'E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ' +
|
||||
'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 ' +
|
||||
'F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 ' +
|
||||
'FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D ' +
|
||||
'D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F ' +
|
||||
'DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ' +
|
||||
'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B ' +
|
||||
'C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 ' +
|
||||
'73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 ' +
|
||||
'7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 ' +
|
||||
'6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ' +
|
||||
'60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD ' +
|
||||
'48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF ' +
|
||||
'4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 ' +
|
||||
'5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B ' +
|
||||
'5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ' +
|
||||
'05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 ' +
|
||||
'0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 ' +
|
||||
'18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 ' +
|
||||
'166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D ' +
|
||||
'3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ' +
|
||||
'30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 ' +
|
||||
'23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B ' +
|
||||
'2D02EF8D';
|
||||
|
||||
var table = [
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
|
||||
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
|
||||
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
|
||||
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
|
||||
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
|
||||
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
|
||||
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
|
||||
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
|
||||
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
|
||||
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
|
||||
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
|
||||
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
|
||||
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
|
||||
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
|
||||
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
|
||||
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
|
||||
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
|
||||
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
|
||||
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
|
||||
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
|
||||
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
|
||||
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
];
|
||||
|
||||
if (global.Int32Array !== undefined) {
|
||||
table = new Int32Array(table);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Number
|
||||
*/
|
||||
function crc32(str) {
|
||||
var crc = 0;
|
||||
var n = 0;
|
||||
var x = 0;
|
||||
crc = crc ^ (-1);
|
||||
for (var i = 0, iTop = str.length; i < iTop; i++) {
|
||||
n = (crc ^ str.charCodeAt(i)) & 0xFF;
|
||||
x = "0x" + table.substr(n * 9, 8);
|
||||
crc = (crc >>> 8) ^ x;
|
||||
var crc = -1;
|
||||
for (var i = 0, len = str.length; i < len; i++) {
|
||||
crc = (crc >>> 8) ^ table[(crc ^ str.charCodeAt(i)) & 0xFF];
|
||||
}
|
||||
return crc ^ (-1);
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
module.exports = crc32;
|
||||
|
|
|
@ -62,3 +62,31 @@ RCT_EXTERN RCTAssertFunction RCTGetAssertFunction(void);
|
|||
* assert info to an extra service without changing the default behavior.
|
||||
*/
|
||||
RCT_EXTERN void RCTAddAssertFunction(RCTAssertFunction assertFunction);
|
||||
|
||||
/**
|
||||
* Get the current thread's name (or the current queue, if in debug mode)
|
||||
*/
|
||||
RCT_EXTERN NSString *RCTCurrentThreadName(void);
|
||||
|
||||
/**
|
||||
* Convenience macro to assert which thread is currently running (DEBUG mode only)
|
||||
*/
|
||||
#if DEBUG
|
||||
|
||||
#define RCTAssertThread(thread, format...) \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \
|
||||
RCTAssert( \
|
||||
[(id)thread isKindOfClass:[NSString class]] ? \
|
||||
[RCTCurrentThreadName() isEqualToString:(NSString *)thread] : \
|
||||
[(id)thread isKindOfClass:[NSThread class]] ? \
|
||||
[NSThread currentThread] == (NSThread *)thread : \
|
||||
dispatch_get_current_queue() == (dispatch_queue_t)thread, \
|
||||
format); \
|
||||
_Pragma("clang diagnostic pop")
|
||||
|
||||
#else
|
||||
|
||||
#define RCTAssertThread(thread, format...)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -60,3 +60,20 @@ void RCTAddAssertFunction(RCTAssertFunction assertFunction)
|
|||
RCTCurrentAssertFunction = assertFunction;
|
||||
}
|
||||
}
|
||||
|
||||
NSString *RCTCurrentThreadName(void)
|
||||
{
|
||||
NSThread *thread = [NSThread currentThread];
|
||||
NSString *threadName = [thread isMainThread] ? @"main" : thread.name;
|
||||
if (threadName.length == 0) {
|
||||
#if DEBUG // This is DEBUG not RCT_DEBUG because it *really* must not ship in RC
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
threadName = @(dispatch_queue_get_label(dispatch_get_current_queue()));
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
threadName = [NSString stringWithFormat:@"%p", thread];
|
||||
#endif
|
||||
}
|
||||
return threadName;
|
||||
}
|
||||
|
|
|
@ -95,6 +95,11 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method;
|
|||
/**
|
||||
* The event dispatcher is a wrapper around -enqueueJSCall:args: that provides a
|
||||
* higher-level interface for sending UI events such as touches and text input.
|
||||
*
|
||||
* NOTE: RCTEventDispatcher is now a bridge module, this is implemented as a
|
||||
* category but remains declared in the bridge to avoid breaking changes
|
||||
*
|
||||
* To be moved.
|
||||
*/
|
||||
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
#import "RCTContextExecutor.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTJavaScriptLoader.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
#import "RCTLog.h"
|
||||
|
@ -211,7 +210,6 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|||
|
||||
@property (nonatomic, strong) RCTBatchedBridge *batchedBridge;
|
||||
@property (nonatomic, strong) RCTBridgeModuleProviderBlock moduleProvider;
|
||||
@property (nonatomic, strong, readwrite) RCTEventDispatcher *eventDispatcher;
|
||||
|
||||
- (void)_invokeAndProcessModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
|
@ -875,11 +873,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
return _batchedBridge.modules;
|
||||
}
|
||||
|
||||
- (RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
return _eventDispatcher ?: _batchedBridge.eventDispatcher;
|
||||
}
|
||||
|
||||
#define RCT_INNER_BRIDGE_ONLY(...) \
|
||||
- (void)__VA_ARGS__ \
|
||||
{ \
|
||||
|
@ -943,11 +936,6 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
|||
_javaScriptExecutor = RCTCreateExecutor(executorClass);
|
||||
_latestJSExecutor = _javaScriptExecutor;
|
||||
|
||||
/**
|
||||
* Setup event dispatcher before initializing modules to allow init calls
|
||||
*/
|
||||
self.eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
||||
|
||||
/**
|
||||
* Initialize and register bridge modules *before* adding the display link
|
||||
* so we don't have threading issues
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTPerfStats.h"
|
||||
|
@ -241,6 +242,8 @@ RCT_EXPORT_METHOD(show)
|
|||
destructiveButtonTitle:nil
|
||||
otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, fpsMonitor, nil];
|
||||
|
||||
[actionSheet addButtonWithTitle:@"Inspect Element"];
|
||||
|
||||
if (_liveReloadURL) {
|
||||
|
||||
NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload";
|
||||
|
@ -300,10 +303,14 @@ RCT_EXPORT_METHOD(reload)
|
|||
break;
|
||||
}
|
||||
case 4: {
|
||||
self.liveReloadEnabled = !_liveReloadEnabled;
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"toggleElementInspector" body:nil];
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
self.liveReloadEnabled = !_liveReloadEnabled;
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
self.profilingEnabled = !_profilingEnabled;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class RCTBridge;
|
||||
#import "RCTBridge.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTTextEventType) {
|
||||
RCTTextEventTypeFocus,
|
||||
|
@ -28,14 +28,36 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) {
|
|||
RCTScrollEventTypeEndAnimation,
|
||||
};
|
||||
|
||||
@protocol RCTEvent <NSObject>
|
||||
|
||||
@required
|
||||
|
||||
@property (nonatomic, strong, readonly) NSNumber *viewTag;
|
||||
@property (nonatomic, copy, readonly) NSString *eventName;
|
||||
@property (nonatomic, copy, readonly) NSDictionary *body;
|
||||
@property (nonatomic, assign, readonly) uint16_t coalescingKey;
|
||||
|
||||
- (BOOL)canCoalesce;
|
||||
- (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent;
|
||||
|
||||
+ (NSString *)moduleDotMethod;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTBaseEvent : NSObject <RCTEvent>
|
||||
|
||||
- (instancetype)initWithViewTag:(NSNumber *)viewTag
|
||||
eventName:(NSString *)eventName
|
||||
body:(NSDictionary *)body NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This class wraps the -[RCTBridge enqueueJSCall:args:] method, and
|
||||
* provides some convenience methods for generating event calls.
|
||||
*/
|
||||
@interface RCTEventDispatcher : NSObject
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
/**
|
||||
* Send an application-specific event that does not relate to a specific
|
||||
* view, e.g. a navigation or data update notification.
|
||||
|
@ -61,13 +83,6 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) {
|
|||
reactTag:(NSNumber *)reactTag
|
||||
text:(NSString *)text;
|
||||
|
||||
/**
|
||||
* Send a scroll event.
|
||||
* (You can send a fake scroll event by passing nil for scrollView).
|
||||
*/
|
||||
- (void)sendScrollEventWithType:(RCTScrollEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
scrollView:(UIScrollView *)scrollView
|
||||
userData:(NSDictionary *)userData;
|
||||
- (void)sendEvent:(id<RCTEvent>)event;
|
||||
|
||||
@end
|
||||
|
|
|
@ -11,16 +11,76 @@
|
|||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTSparseArray.h"
|
||||
|
||||
static uint64_t RCTGetEventID(id<RCTEvent> event)
|
||||
{
|
||||
return (
|
||||
[event.viewTag intValue] |
|
||||
(((uint64_t)event.eventName.hash & 0xFFFF) << 32) |
|
||||
(((uint64_t)event.coalescingKey) << 48)
|
||||
);
|
||||
}
|
||||
|
||||
@implementation RCTBaseEvent
|
||||
|
||||
@synthesize viewTag = _viewTag;
|
||||
@synthesize eventName = _eventName;
|
||||
@synthesize body = _body;
|
||||
|
||||
- (instancetype)initWithViewTag:(NSNumber *)viewTag
|
||||
eventName:(NSString *)eventName
|
||||
body:(NSDictionary *)body
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_viewTag = viewTag;
|
||||
_eventName = eventName;
|
||||
_body = body;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (uint16_t)coalescingKey
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (BOOL)canCoalesce
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent
|
||||
{
|
||||
return newEvent;
|
||||
}
|
||||
|
||||
+ (NSString *)moduleDotMethod
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTEventDispatcher() <RCTBridgeModule, RCTFrameUpdateObserver>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTEventDispatcher
|
||||
{
|
||||
RCTBridge __weak *_bridge;
|
||||
RCTSparseArray *_eventQueue;
|
||||
NSLock *_eventQueueLock;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_bridge = bridge;
|
||||
_eventQueue = [[RCTSparseArray alloc] init];
|
||||
_eventQueueLock = [[NSLock alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -70,58 +130,71 @@ RCT_IMPORT_METHOD(RCTEventEmitter, receiveEvent);
|
|||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: throttling
|
||||
* NOTE: the old system used a per-scrollview throttling
|
||||
* which would be fairly easy to re-implement if needed,
|
||||
* but this is non-optimal as it leads to degradation in
|
||||
* scroll responsiveness. A better solution would be to
|
||||
* coalesce multiple scroll events into a single batch.
|
||||
*/
|
||||
- (void)sendScrollEventWithType:(RCTScrollEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
scrollView:(UIScrollView *)scrollView
|
||||
userData:(NSDictionary *)userData
|
||||
- (void)sendEvent:(id<RCTEvent>)event
|
||||
{
|
||||
static NSString *events[] = {
|
||||
@"topScrollBeginDrag",
|
||||
@"topScroll",
|
||||
@"topScrollEndDrag",
|
||||
@"topMomentumScrollBegin",
|
||||
@"topMomentumScrollEnd",
|
||||
@"topScrollAnimationEnd",
|
||||
};
|
||||
|
||||
NSDictionary *body = @{
|
||||
@"contentOffset": @{
|
||||
@"x": @(scrollView.contentOffset.x),
|
||||
@"y": @(scrollView.contentOffset.y)
|
||||
},
|
||||
@"contentInset": @{
|
||||
@"top": @(scrollView.contentInset.top),
|
||||
@"left": @(scrollView.contentInset.left),
|
||||
@"bottom": @(scrollView.contentInset.bottom),
|
||||
@"right": @(scrollView.contentInset.right)
|
||||
},
|
||||
@"contentSize": @{
|
||||
@"width": @(scrollView.contentSize.width),
|
||||
@"height": @(scrollView.contentSize.height)
|
||||
},
|
||||
@"layoutMeasurement": @{
|
||||
@"width": @(scrollView.frame.size.width),
|
||||
@"height": @(scrollView.frame.size.height)
|
||||
},
|
||||
@"zoomScale": @(scrollView.zoomScale ?: 1),
|
||||
@"target": reactTag
|
||||
};
|
||||
|
||||
if (userData) {
|
||||
NSMutableDictionary *mutableBody = [body mutableCopy];
|
||||
[mutableBody addEntriesFromDictionary:userData];
|
||||
body = mutableBody;
|
||||
if (!event.canCoalesce) {
|
||||
[self dispatchEvent:event];
|
||||
return;
|
||||
}
|
||||
|
||||
[self sendInputEventWithName:events[type] body:body];
|
||||
[_eventQueueLock lock];
|
||||
|
||||
uint64_t eventID = RCTGetEventID(event);
|
||||
id<RCTEvent> previousEvent = _eventQueue[eventID];
|
||||
|
||||
if (previousEvent) {
|
||||
event = [previousEvent coalesceWithEvent:event];
|
||||
}
|
||||
|
||||
_eventQueue[eventID] = event;
|
||||
|
||||
[_eventQueueLock unlock];
|
||||
}
|
||||
|
||||
- (void)dispatchEvent:(id<RCTEvent>)event
|
||||
{
|
||||
NSMutableArray *arguments = [[NSMutableArray alloc] init];
|
||||
|
||||
if (event.viewTag) {
|
||||
[arguments addObject:event.viewTag];
|
||||
}
|
||||
|
||||
[arguments addObject:event.eventName];
|
||||
|
||||
if (event.body) {
|
||||
[arguments addObject:event.body];
|
||||
}
|
||||
|
||||
[_bridge enqueueJSCall:[[event class] moduleDotMethod]
|
||||
args:arguments];
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)methodQueue
|
||||
{
|
||||
return RCTJSThread;
|
||||
}
|
||||
|
||||
- (void)didUpdateFrame:(RCTFrameUpdate *)update
|
||||
{
|
||||
RCTSparseArray *eventQueue;
|
||||
|
||||
[_eventQueueLock lock];
|
||||
eventQueue = _eventQueue;
|
||||
_eventQueue = [[RCTSparseArray alloc] init];
|
||||
[_eventQueueLock unlock];
|
||||
|
||||
for (id<RCTEvent> event in eventQueue.allObjects) {
|
||||
[self dispatchEvent:event];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTBridge (RCTEventDispatcher)
|
||||
|
||||
- (RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
return self.modules[RCTBridgeModuleNameForClass([RCTEventDispatcher class])];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -42,18 +42,12 @@ typedef void (^RCTLogFunction)(
|
|||
NSString *message
|
||||
);
|
||||
|
||||
/**
|
||||
* Get a given thread's name (or the current queue, if in debug mode)
|
||||
*/
|
||||
RCT_EXTERN NSString *RCTThreadName(NSThread *);
|
||||
|
||||
/**
|
||||
* A method to generate a string from a collection of log data. To omit any
|
||||
* particular data from the log, just pass nil or zero for the argument.
|
||||
*/
|
||||
RCT_EXTERN NSString *RCTFormatLog(
|
||||
NSDate *timestamp,
|
||||
NSThread *thread,
|
||||
RCTLogLevel level,
|
||||
NSString *fileName,
|
||||
NSNumber *lineNumber,
|
||||
|
|
|
@ -53,7 +53,7 @@ RCTLogFunction RCTDefaultLogFunction = ^(
|
|||
)
|
||||
{
|
||||
NSString *log = RCTFormatLog(
|
||||
[NSDate date], [NSThread currentThread], level, fileName, lineNumber, message
|
||||
[NSDate date], level, fileName, lineNumber, message
|
||||
);
|
||||
fprintf(stderr, "%s\n", log.UTF8String);
|
||||
fflush(stderr);
|
||||
|
@ -99,25 +99,8 @@ void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix)
|
|||
[prefixStack removeLastObject];
|
||||
}
|
||||
|
||||
NSString *RCTThreadName(NSThread *thread)
|
||||
{
|
||||
NSString *threadName = [thread isMainThread] ? @"main" : thread.name;
|
||||
if (threadName.length == 0) {
|
||||
#if DEBUG // This is DEBUG not RCT_DEBUG because it *really* must not ship in RC
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
threadName = @(dispatch_queue_get_label(dispatch_get_current_queue()));
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
threadName = [NSString stringWithFormat:@"%p", thread];
|
||||
#endif
|
||||
}
|
||||
return threadName;
|
||||
}
|
||||
|
||||
NSString *RCTFormatLog(
|
||||
NSDate *timestamp,
|
||||
NSThread *thread,
|
||||
RCTLogLevel level,
|
||||
NSString *fileName,
|
||||
NSNumber *lineNumber,
|
||||
|
@ -137,9 +120,9 @@ NSString *RCTFormatLog(
|
|||
if (level) {
|
||||
[log appendFormat:@"[%s]", RCTLogLevels[level - 1]];
|
||||
}
|
||||
if (thread) {
|
||||
[log appendFormat:@"[tid:%@]", RCTThreadName(thread)];
|
||||
}
|
||||
|
||||
[log appendFormat:@"[tid:%@]", RCTCurrentThreadName()];
|
||||
|
||||
if (fileName) {
|
||||
fileName = [fileName lastPathComponent];
|
||||
if (lineNumber) {
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#if RCT_DEV
|
||||
|
@ -43,7 +43,7 @@ NSLock *_RCTProfileLock;
|
|||
#define RCTProfileAddEvent(type, props...) \
|
||||
[RCTProfileInfo[type] addObject:@{ \
|
||||
@"pid": @([[NSProcessInfo processInfo] processIdentifier]), \
|
||||
@"tid": RCTThreadName([NSThread currentThread]), \
|
||||
@"tid": RCTCurrentThreadName(), \
|
||||
props \
|
||||
}];
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
RCTRootContentView *_contentView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
moduleName:(NSString *)moduleName
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
@ -87,6 +87,12 @@
|
|||
return [self initWithBridge:bridge moduleName:moduleName];
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor
|
||||
{
|
||||
super.backgroundColor = backgroundColor;
|
||||
_contentView.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
- (UIViewController *)backingViewController
|
||||
{
|
||||
return _backingViewController ?: [super backingViewController];
|
||||
|
@ -100,7 +106,6 @@
|
|||
RCT_IMPORT_METHOD(AppRegistry, runApplication)
|
||||
RCT_IMPORT_METHOD(ReactNative, unmountComponentAtNodeAndRemoveContainer)
|
||||
|
||||
|
||||
- (void)javaScriptDidLoad:(NSNotification *)notification
|
||||
{
|
||||
RCTBridge *bridge = notification.userInfo[@"bridge"];
|
||||
|
@ -124,6 +129,7 @@ RCT_IMPORT_METHOD(ReactNative, unmountComponentAtNodeAndRemoveContainer)
|
|||
[_contentView removeFromSuperview];
|
||||
_contentView = [[RCTRootContentView alloc] initWithFrame:self.bounds
|
||||
bridge:bridge];
|
||||
_contentView.backgroundColor = self.backgroundColor;
|
||||
[self addSubview:_contentView];
|
||||
|
||||
NSString *moduleName = _moduleName ?: @"";
|
||||
|
@ -172,6 +178,7 @@ RCT_IMPORT_METHOD(ReactNative, unmountComponentAtNodeAndRemoveContainer)
|
|||
{
|
||||
__weak RCTBridge *_bridge;
|
||||
RCTTouchHandler *_touchHandler;
|
||||
UIColor *_backgroundColor;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
|
@ -181,18 +188,32 @@ RCT_IMPORT_METHOD(ReactNative, unmountComponentAtNodeAndRemoveContainer)
|
|||
_bridge = bridge;
|
||||
[self setUp];
|
||||
self.frame = frame;
|
||||
self.layer.backgroundColor = NULL;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
super.frame = frame;
|
||||
if (self.reactTag && _bridge.isValid) {
|
||||
[_bridge.uiManager setFrame:self.bounds forRootView:self];
|
||||
[_bridge.uiManager setFrame:frame forRootView:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor
|
||||
{
|
||||
_backgroundColor = backgroundColor;
|
||||
if (self.reactTag && _bridge.isValid) {
|
||||
[_bridge.uiManager setBackgroundColor:backgroundColor forRootView:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIColor *)backgroundColor
|
||||
{
|
||||
return _backgroundColor;
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
{
|
||||
RCTJavaScriptContext *_context;
|
||||
NSThread *_javaScriptThread;
|
||||
JSValueRef _undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -237,6 +238,9 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||
JSContextGroupRelease(group);
|
||||
}
|
||||
|
||||
// Constant value used for comparison
|
||||
_undefined = JSValueMakeUndefined(ctx);
|
||||
|
||||
strongSelf->_context = [[RCTJavaScriptContext alloc] initWithJSContext:ctx];
|
||||
[strongSelf _addNativeHook:RCTNativeLoggingHook withName:"nativeLoggingHook"];
|
||||
[strongSelf _addNativeHook:RCTNoop withName:"noop"];
|
||||
|
@ -291,21 +295,77 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||
return;
|
||||
}
|
||||
NSError *error;
|
||||
NSString *argsString = RCTJSONStringify(arguments, &error);
|
||||
NSString *argsString = (arguments.count == 1) ? RCTJSONStringify(arguments[0], &error) : RCTJSONStringify(arguments, &error);
|
||||
if (!argsString) {
|
||||
RCTLogError(@"Cannot convert argument to string: %@", error);
|
||||
onComplete(nil, error);
|
||||
return;
|
||||
}
|
||||
NSString *execString = [NSString stringWithFormat:@"require('%@').%@.apply(null, %@);", name, method, argsString];
|
||||
|
||||
JSValueRef jsError = NULL;
|
||||
JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)execString);
|
||||
JSValueRef result = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, NULL, 0, &jsError);
|
||||
JSStringRelease(execJSString);
|
||||
JSValueRef errorJSRef = NULL;
|
||||
JSValueRef resultJSRef = NULL;
|
||||
JSGlobalContextRef contextJSRef = JSContextGetGlobalContext(strongSelf->_context.ctx);
|
||||
JSObjectRef globalObjectJSRef = JSContextGetGlobalObject(strongSelf->_context.ctx);
|
||||
|
||||
if (!result) {
|
||||
onComplete(nil, RCTNSErrorFromJSError(strongSelf->_context.ctx, jsError));
|
||||
// get require
|
||||
JSStringRef requireNameJSStringRef = JSStringCreateWithUTF8CString("require");
|
||||
JSValueRef requireJSRef = JSObjectGetProperty(contextJSRef, globalObjectJSRef, requireNameJSStringRef, &errorJSRef);
|
||||
JSStringRelease(requireNameJSStringRef);
|
||||
|
||||
if (requireJSRef != NULL && requireJSRef != _undefined && errorJSRef == NULL) {
|
||||
|
||||
// get module
|
||||
JSStringRef moduleNameJSStringRef = JSStringCreateWithCFString((__bridge CFStringRef)name);
|
||||
JSValueRef moduleNameJSRef = JSValueMakeString(contextJSRef, moduleNameJSStringRef);
|
||||
JSValueRef moduleJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)requireJSRef, NULL, 1, (const JSValueRef *)&moduleNameJSRef, &errorJSRef);
|
||||
JSStringRelease(moduleNameJSStringRef);
|
||||
|
||||
if (moduleJSRef != NULL && errorJSRef == NULL) {
|
||||
|
||||
// get method
|
||||
JSStringRef methodNameJSStringRef = JSStringCreateWithCFString((__bridge CFStringRef)method);
|
||||
JSValueRef methodJSRef = JSObjectGetProperty(contextJSRef, (JSObjectRef)moduleJSRef, methodNameJSStringRef, &errorJSRef);
|
||||
JSStringRelease(methodNameJSStringRef);
|
||||
|
||||
if (methodJSRef != NULL && errorJSRef == NULL) {
|
||||
|
||||
// direct method invoke with no arguments
|
||||
if (arguments.count == 0) {
|
||||
resultJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)methodJSRef, (JSObjectRef)moduleJSRef, 0, NULL, &errorJSRef);
|
||||
}
|
||||
|
||||
// direct method invoke with 1 argument
|
||||
else if(arguments.count == 1) {
|
||||
JSStringRef argsJSStringRef = JSStringCreateWithCFString((__bridge CFStringRef)argsString);
|
||||
JSValueRef argsJSRef = JSValueMakeFromJSONString(contextJSRef, argsJSStringRef);
|
||||
resultJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)methodJSRef, (JSObjectRef)moduleJSRef, 1, &argsJSRef, &errorJSRef);
|
||||
JSStringRelease(argsJSStringRef);
|
||||
|
||||
} else {
|
||||
// apply invoke with array of arguments
|
||||
JSStringRef applyNameJSStringRef = JSStringCreateWithUTF8CString("apply");
|
||||
JSValueRef applyJSRef = JSObjectGetProperty(contextJSRef, (JSObjectRef)methodJSRef, applyNameJSStringRef, &errorJSRef);
|
||||
JSStringRelease(applyNameJSStringRef);
|
||||
|
||||
if (applyJSRef != NULL && errorJSRef == NULL) {
|
||||
// invoke apply
|
||||
JSStringRef argsJSStringRef = JSStringCreateWithCFString((__bridge CFStringRef)argsString);
|
||||
JSValueRef argsJSRef = JSValueMakeFromJSONString(contextJSRef, argsJSStringRef);
|
||||
|
||||
JSValueRef args[2];
|
||||
args[0] = JSValueMakeNull(contextJSRef);
|
||||
args[1] = argsJSRef;
|
||||
|
||||
resultJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)applyJSRef, (JSObjectRef)methodJSRef, 2, args, &errorJSRef);
|
||||
JSStringRelease(argsJSStringRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!resultJSRef) {
|
||||
onComplete(nil, RCTNSErrorFromJSError(contextJSRef, errorJSRef));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -315,8 +375,8 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||
id objcValue;
|
||||
// We often return `null` from JS when there is nothing for native side. JSONKit takes an extra hundred microseconds
|
||||
// to handle this simple case, so we are adding a shortcut to make executeJSCall method even faster
|
||||
if (!JSValueIsNull(strongSelf->_context.ctx, result)) {
|
||||
JSStringRef jsJSONString = JSValueCreateJSONString(strongSelf->_context.ctx, result, 0, nil);
|
||||
if (!JSValueIsNull(contextJSRef, resultJSRef)) {
|
||||
JSStringRef jsJSONString = JSValueCreateJSONString(contextJSRef, resultJSRef, 0, nil);
|
||||
if (jsJSONString) {
|
||||
NSString *objcJSONString = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsJSONString);
|
||||
JSStringRelease(jsJSONString);
|
||||
|
|
|
@ -53,11 +53,21 @@ RCT_EXPORT_MODULE()
|
|||
selector:@selector(handleAppStateDidChange)
|
||||
name:name
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleMemoryWarning)
|
||||
name:UIApplicationDidReceiveMemoryWarningNotification
|
||||
object:nil];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)handleMemoryWarning
|
||||
{
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"memoryWarning"
|
||||
body:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
|
|
|
@ -36,10 +36,16 @@
|
|||
|
||||
/**
|
||||
* Update the frame of a root view. This might be in response to a screen rotation
|
||||
* or some other layout event outsde of the React-managed view hierarchy.
|
||||
* or some other layout event outside of the React-managed view hierarchy.
|
||||
*/
|
||||
- (void)setFrame:(CGRect)frame forRootView:(UIView *)rootView;
|
||||
|
||||
/**
|
||||
* Update the background color of a root view. This is usually triggered by
|
||||
* manually setting the background color of the root view with native code.
|
||||
*/
|
||||
- (void)setBackgroundColor:(UIColor *)color forRootView:(UIView *)rootView;
|
||||
|
||||
/**
|
||||
* Schedule a block to be executed on the UI thread. Useful if you need to execute
|
||||
* view logic after all currently queued view updates have completed.
|
||||
|
|
|
@ -338,17 +338,14 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||
_viewRegistry[reactTag] = rootView;
|
||||
CGRect frame = rootView.frame;
|
||||
|
||||
// Register manager (TODO: should we do this, or leave it nil?)
|
||||
_viewManagerRegistry[reactTag] = _viewManagers[@"RCTView"];
|
||||
|
||||
// Register shadow view
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
RCTShadowView *shadowView = [[RCTShadowView alloc] init];
|
||||
shadowView.reactTag = reactTag;
|
||||
shadowView.frame = frame;
|
||||
shadowView.backgroundColor = [UIColor whiteColor];
|
||||
shadowView.backgroundColor = rootView.backgroundColor;
|
||||
shadowView.viewName = NSStringFromClass([rootView class]);
|
||||
_shadowViewRegistry[shadowView.reactTag] = shadowView;
|
||||
|
||||
[_rootViewTags addObject:reactTag];
|
||||
});
|
||||
}
|
||||
|
@ -372,6 +369,22 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||
});
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)color forRootView:(UIView *)rootView
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
NSNumber *reactTag = rootView.reactTag;
|
||||
RCTAssert(RCTIsReactRootView(reactTag), @"Specified view %@ is not a root view", reactTag);
|
||||
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
RCTShadowView *rootShadowView = _shadowViewRegistry[reactTag];
|
||||
RCTAssert(rootShadowView != nil, @"Could not locate root view with tag #%@", reactTag);
|
||||
rootShadowView.backgroundColor = color;
|
||||
[self _amendPendingUIBlocksWithStylePropagationUpdateForRootView:rootShadowView];
|
||||
[self flushUIBlocks];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters views from registries
|
||||
*/
|
||||
|
@ -390,6 +403,14 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||
|
||||
- (void)addUIBlock:(RCTViewManagerUIBlock)block
|
||||
{
|
||||
RCTAssertThread(_shadowQueue,
|
||||
@"-[RCTUIManager addUIBlock:] should only be called from the "
|
||||
"UIManager's _shadowQueue (it may be accessed via `bridge.uiManager.methodQueue`)");
|
||||
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.isValid) {
|
||||
return;
|
||||
}
|
||||
|
@ -795,6 +816,11 @@ RCT_EXPORT_METHOD(createView:(NSNumber *)reactTag
|
|||
}
|
||||
_shadowViewRegistry[reactTag] = shadowView;
|
||||
|
||||
// Shadow view is the source of truth for background color this is a little
|
||||
// bit counter-intuitive if people try to set background color when setting up
|
||||
// the view, but it's the only way that makes sense given our threading model
|
||||
UIColor *backgroundColor = shadowView.backgroundColor;
|
||||
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||
RCTAssertMainThread();
|
||||
|
||||
|
@ -803,14 +829,15 @@ RCT_EXPORT_METHOD(createView:(NSNumber *)reactTag
|
|||
|
||||
// Generate default view, used for resetting default props
|
||||
if (!uiManager->_defaultViews[viewName]) {
|
||||
// Note the default is setup after the props are read for the first time ever
|
||||
// for this className - this is ok because we only use the default for restoring
|
||||
// defaults, which never happens on first creation.
|
||||
// Note the default is setup after the props are read for the first time
|
||||
// ever for this className - this is ok because we only use the default
|
||||
// for restoring defaults, which never happens on first creation.
|
||||
uiManager->_defaultViews[viewName] = [manager view];
|
||||
}
|
||||
|
||||
// Set properties
|
||||
view.reactTag = reactTag;
|
||||
view.backgroundColor = backgroundColor;
|
||||
if ([view isKindOfClass:[UIView class]]) {
|
||||
view.multipleTouchEnabled = YES;
|
||||
view.userInteractionEnabled = YES; // required for touch handling
|
||||
|
@ -859,17 +886,33 @@ RCT_EXPORT_METHOD(blur:(NSNumber *)reactTag)
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)batchDidComplete
|
||||
{
|
||||
// Gather blocks to be executed now that all view hierarchy manipulations have
|
||||
// been completed (note that these may still take place before layout has finished)
|
||||
for (RCTViewManager *manager in _viewManagers.allValues) {
|
||||
RCTViewManagerUIBlock uiBlock = [manager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry];
|
||||
if (uiBlock) {
|
||||
[self addUIBlock:uiBlock];
|
||||
}
|
||||
RCT_EXPORT_METHOD(findSubviewIn:(NSNumber *)reactTag atPoint:(CGPoint)point callback:(RCTResponseSenderBlock)callback) {
|
||||
if (!reactTag) {
|
||||
callback(@[[NSNull null]]);
|
||||
return;
|
||||
}
|
||||
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
UIView *view = viewRegistry[reactTag];
|
||||
UIView *target = [view hitTest:point withEvent:nil];
|
||||
CGRect frame = [target convertRect:target.bounds toView:view];
|
||||
|
||||
while (target.reactTag == nil && target.superview != nil) {
|
||||
target = [target superview];
|
||||
}
|
||||
|
||||
callback(@[
|
||||
target.reactTag ?: [NSNull null],
|
||||
@(frame.origin.x),
|
||||
@(frame.origin.y),
|
||||
@(frame.size.width),
|
||||
@(frame.size.height),
|
||||
]);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)batchDidComplete
|
||||
{
|
||||
// Set up next layout animation
|
||||
if (_nextLayoutAnimation) {
|
||||
RCTLayoutAnimation *layoutAnimation = _nextLayoutAnimation;
|
||||
|
@ -893,6 +936,12 @@ RCT_EXPORT_METHOD(blur:(NSNumber *)reactTag)
|
|||
_nextLayoutAnimation = nil;
|
||||
}
|
||||
|
||||
// Gather blocks to be executed now that layout is completed
|
||||
for (RCTViewManager *manager in _viewManagers.allValues) {
|
||||
RCTViewManagerUIBlock uiBlock = [manager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry];
|
||||
[self addUIBlock:uiBlock];
|
||||
}
|
||||
|
||||
[self flushUIBlocks];
|
||||
}
|
||||
|
||||
|
@ -1433,7 +1482,7 @@ RCT_EXPORT_METHOD(clearJSResponder)
|
|||
}
|
||||
|
||||
// Add native props
|
||||
constantsNamespace[@"nativeProps"] = _viewConfigs[name];
|
||||
constantsNamespace[@"NativeProps"] = _viewConfigs[name];
|
||||
|
||||
allJSConstants[name] = [constantsNamespace copy];
|
||||
}];
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTNavItem.h"
|
||||
#import "RCTScrollView.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTView.h"
|
||||
#import "RCTWrapperViewController.h"
|
||||
|
|
|
@ -10,13 +10,12 @@
|
|||
#import <UIKit/UIScrollView.h>
|
||||
|
||||
#import "RCTAutoInsetsProtocol.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTScrollableProtocol.h"
|
||||
#import "RCTView.h"
|
||||
|
||||
@protocol UIScrollViewDelegate;
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
|
||||
@interface RCTScrollView : RCTView <UIScrollViewDelegate, RCTScrollableProtocol, RCTAutoInsetsProtocol>
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
@ -48,3 +47,16 @@
|
|||
@property (nonatomic, copy) NSIndexSet *stickyHeaderIndices;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTEventDispatcher (RCTScrollView)
|
||||
|
||||
/**
|
||||
* Send a scroll event.
|
||||
* (You can send a fake scroll event by passing nil for scrollView).
|
||||
*/
|
||||
- (void)sendScrollEventWithType:(RCTScrollEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
scrollView:(UIScrollView *)scrollView
|
||||
userData:(NSDictionary *)userData;
|
||||
|
||||
@end
|
||||
|
|
|
@ -21,6 +21,107 @@
|
|||
CGFloat const ZINDEX_DEFAULT = 0;
|
||||
CGFloat const ZINDEX_STICKY_HEADER = 50;
|
||||
|
||||
@interface RCTScrollEvent : NSObject <RCTEvent>
|
||||
|
||||
- (instancetype)initWithType:(RCTScrollEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
scrollView:(UIScrollView *)scrollView
|
||||
userData:(NSDictionary *)userData NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTScrollEvent
|
||||
{
|
||||
RCTScrollEventType _type;
|
||||
UIScrollView *_scrollView;
|
||||
NSDictionary *_userData;
|
||||
}
|
||||
|
||||
@synthesize viewTag = _viewTag;
|
||||
|
||||
- (instancetype)initWithType:(RCTScrollEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
scrollView:(UIScrollView *)scrollView
|
||||
userData:(NSDictionary *)userData
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_type = type;
|
||||
_viewTag = reactTag;
|
||||
_scrollView = scrollView;
|
||||
_userData = userData;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (uint16_t)coalescingKey
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (NSDictionary *)body
|
||||
{
|
||||
NSDictionary *body = @{
|
||||
@"contentOffset": @{
|
||||
@"x": @(_scrollView.contentOffset.x),
|
||||
@"y": @(_scrollView.contentOffset.y)
|
||||
},
|
||||
@"contentInset": @{
|
||||
@"top": @(_scrollView.contentInset.top),
|
||||
@"left": @(_scrollView.contentInset.left),
|
||||
@"bottom": @(_scrollView.contentInset.bottom),
|
||||
@"right": @(_scrollView.contentInset.right)
|
||||
},
|
||||
@"contentSize": @{
|
||||
@"width": @(_scrollView.contentSize.width),
|
||||
@"height": @(_scrollView.contentSize.height)
|
||||
},
|
||||
@"layoutMeasurement": @{
|
||||
@"width": @(_scrollView.frame.size.width),
|
||||
@"height": @(_scrollView.frame.size.height)
|
||||
},
|
||||
@"zoomScale": @(_scrollView.zoomScale ?: 1),
|
||||
};
|
||||
|
||||
if (_userData) {
|
||||
NSMutableDictionary *mutableBody = [body mutableCopy];
|
||||
[mutableBody addEntriesFromDictionary:_userData];
|
||||
body = mutableBody;
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
- (NSString *)eventName
|
||||
{
|
||||
static NSString *events[] = {
|
||||
@"topScrollBeginDrag",
|
||||
@"topScroll",
|
||||
@"topScrollEndDrag",
|
||||
@"topMomentumScrollBegin",
|
||||
@"topMomentumScrollEnd",
|
||||
@"topScrollAnimationEnd",
|
||||
};
|
||||
|
||||
return events[_type];
|
||||
}
|
||||
|
||||
- (BOOL)canCoalesce
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent
|
||||
{
|
||||
return newEvent;
|
||||
}
|
||||
|
||||
+ (NSString *)moduleDotMethod
|
||||
{
|
||||
return @"RCTEventEmitter.receiveEvent";
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Include a custom scroll view subclass because we want to limit certain
|
||||
* default UIKit behaviors such as textFields automatically scrolling
|
||||
|
@ -223,6 +324,24 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
|
|||
}
|
||||
}
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
__block UIView *stickyHeader;
|
||||
|
||||
[_stickyHeaderIndices enumerateIndexesWithOptions:0 usingBlock:^(NSUInteger idx, BOOL *stop) {
|
||||
stickyHeader = [self contentView].reactSubviews[idx];
|
||||
CGPoint convertedPoint = [stickyHeader convertPoint:point fromView:self];
|
||||
|
||||
if ([stickyHeader hitTest:convertedPoint withEvent:event]) {
|
||||
*stop = YES;
|
||||
} else {
|
||||
stickyHeader = nil;
|
||||
}
|
||||
}];
|
||||
|
||||
return stickyHeader ?: [super hitTest:point withEvent:event];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTScrollView
|
||||
|
@ -424,6 +543,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, RCTScrollEventTypeMove)
|
|||
reactTag:self.reactTag
|
||||
scrollView:scrollView
|
||||
userData:userData];
|
||||
|
||||
// Update dispatch time
|
||||
_lastScrollDispatchTime = now;
|
||||
_allowNextScrollNoMatterWhat = NO;
|
||||
|
@ -612,3 +732,19 @@ RCT_SET_AND_PRESERVE_OFFSET(setScrollIndicatorInsets, UIEdgeInsets);
|
|||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTEventDispatcher (RCTScrollView)
|
||||
|
||||
- (void)sendScrollEventWithType:(RCTScrollEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
scrollView:(UIScrollView *)scrollView
|
||||
userData:(NSDictionary *)userData
|
||||
{
|
||||
RCTScrollEvent *scrollEvent = [[RCTScrollEvent alloc] initWithType:type
|
||||
reactTag:reactTag
|
||||
scrollView:scrollView
|
||||
userData:userData];
|
||||
[self sendEvent:scrollEvent];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -38,7 +38,6 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *);
|
|||
@property (nonatomic, weak, readonly) RCTShadowView *superview;
|
||||
@property (nonatomic, assign, readonly) css_node_t *cssNode;
|
||||
@property (nonatomic, copy) NSString *viewName;
|
||||
@property (nonatomic, assign) BOOL isBGColorExplicitlySet; // Used to propagate to children
|
||||
@property (nonatomic, strong) UIColor *backgroundColor; // Used to propagate to children
|
||||
@property (nonatomic, assign) RCTUpdateLifecycle layoutLifecycle;
|
||||
@property (nonatomic, assign) BOOL hasOnLayout;
|
||||
|
|
|
@ -38,6 +38,7 @@ typedef enum {
|
|||
NSMutableArray *_reactSubviews;
|
||||
BOOL _recomputePadding;
|
||||
BOOL _recomputeMargin;
|
||||
BOOL _isBGColorExplicitlySet;
|
||||
float _paddingMetaProps[META_PROP_COUNT];
|
||||
float _marginMetaProps[META_PROP_COUNT];
|
||||
}
|
||||
|
@ -180,7 +181,7 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
|||
// Update parent properties for children
|
||||
NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary:parentProperties];
|
||||
CGFloat alpha = CGColorGetAlpha(_backgroundColor.CGColor);
|
||||
if (alpha < 1.0 && alpha > 0.0) {
|
||||
if (alpha < 1.0) {
|
||||
// If we see partial transparency, start propagating full transparency
|
||||
properties[RCTBackgroundColorProp] = [UIColor clearColor];
|
||||
} else {
|
||||
|
@ -515,6 +516,7 @@ RCT_STYLE_PROPERTY(FlexWrap, flexWrap, flex_wrap, css_wrap_type_t)
|
|||
- (void)setBackgroundColor:(UIColor *)color
|
||||
{
|
||||
_backgroundColor = color;
|
||||
_isBGColorExplicitlySet = YES;
|
||||
[self dirtyPropagation];
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
|
||||
@interface RCTTabBar : UIView
|
||||
|
||||
@property (nonatomic, strong) UIColor *tintColor;
|
||||
@property (nonatomic, strong) UIColor *barTintColor;
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
|
|
@ -114,6 +114,16 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)setBarTintColor:(UIColor *)barTintColor
|
||||
{
|
||||
_tabController.tabBar.barTintColor = barTintColor;
|
||||
}
|
||||
|
||||
- (void)setTintColor:(UIColor *)tintColor
|
||||
{
|
||||
_tabController.tabBar.tintColor = tintColor;
|
||||
}
|
||||
|
||||
#pragma mark - UITabBarControllerDelegate
|
||||
|
||||
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
|
||||
|
|
|
@ -23,4 +23,7 @@ RCT_EXPORT_MODULE()
|
|||
return [[RCTTabBar alloc] initWithEventDispatcher:_bridge.eventDispatcher];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
|
||||
RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor)
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#import "RCTUtils.h"
|
||||
#import "UIView+React.h"
|
||||
|
||||
static const CGFloat RCTViewBorderThreshold = 0.001;
|
||||
|
||||
static UIView *RCTViewHitTest(UIView *view, CGPoint point, UIEvent *event)
|
||||
{
|
||||
for (UIView *subview in [view.subviews reverseObjectEnumerator]) {
|
||||
|
@ -436,19 +438,22 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
if ([_backgroundColor isEqual:backgroundColor]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_backgroundColor = backgroundColor;
|
||||
[self.layer setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (UIImage *)generateBorderImage:(out CGRect *)contentsCenter
|
||||
- (UIImage *)borderImage:(out CGRect *)contentsCenter
|
||||
{
|
||||
static const CGFloat threshold = 0.001;
|
||||
const CGFloat maxRadius = ({
|
||||
const CGRect bounds = self.bounds;
|
||||
MIN(bounds.size.height, bounds.size.width);
|
||||
});
|
||||
|
||||
const CGFloat maxRadius = MIN(self.bounds.size.height, self.bounds.size.width);
|
||||
const CGFloat radius = MAX(0, _borderRadius);
|
||||
const CGFloat topLeftRadius = MIN(_borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius, maxRadius);
|
||||
const CGFloat topRightRadius = MIN(_borderTopRightRadius >= 0 ? _borderTopRightRadius : radius, maxRadius);
|
||||
const CGFloat bottomLeftRadius = MIN(_borderBottomLeftRadius >= 0 ? _borderBottomLeftRadius : radius, maxRadius);
|
||||
const CGFloat topLeftRadius = MIN(_borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius, maxRadius);
|
||||
const CGFloat topRightRadius = MIN(_borderTopRightRadius >= 0 ? _borderTopRightRadius : radius, maxRadius);
|
||||
const CGFloat bottomLeftRadius = MIN(_borderBottomLeftRadius >= 0 ? _borderBottomLeftRadius : radius, maxRadius);
|
||||
const CGFloat bottomRightRadius = MIN(_borderBottomRightRadius >= 0 ? _borderBottomRightRadius : radius, maxRadius);
|
||||
|
||||
const CGFloat borderWidth = MAX(0, _borderWidth);
|
||||
|
@ -457,14 +462,19 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
const CGFloat bottomWidth = _borderBottomWidth >= 0 ? _borderBottomWidth : borderWidth;
|
||||
const CGFloat leftWidth = _borderLeftWidth >= 0 ? _borderLeftWidth : borderWidth;
|
||||
|
||||
if (topLeftRadius < threshold &&
|
||||
topRightRadius < threshold &&
|
||||
bottomLeftRadius < threshold &&
|
||||
bottomRightRadius < threshold &&
|
||||
topWidth < threshold &&
|
||||
rightWidth < threshold &&
|
||||
bottomWidth < threshold &&
|
||||
leftWidth < threshold) {
|
||||
const BOOL hasCornerRadii =
|
||||
topLeftRadius > RCTViewBorderThreshold ||
|
||||
topRightRadius > RCTViewBorderThreshold ||
|
||||
bottomLeftRadius > RCTViewBorderThreshold ||
|
||||
bottomRightRadius > RCTViewBorderThreshold;
|
||||
|
||||
const BOOL hasBorders =
|
||||
topWidth > RCTViewBorderThreshold ||
|
||||
rightWidth > RCTViewBorderThreshold ||
|
||||
bottomWidth > RCTViewBorderThreshold ||
|
||||
leftWidth > RCTViewBorderThreshold;
|
||||
|
||||
if (!hasCornerRadii && !hasBorders) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
@ -483,17 +493,26 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
const UIEdgeInsets edgeInsets = UIEdgeInsetsMake(topWidth + MAX(innerTopLeftRadiusY, innerTopRightRadiusY), leftWidth + MAX(innerTopLeftRadiusX, innerBottomLeftRadiusX), bottomWidth + MAX(innerBottomLeftRadiusY, innerBottomRightRadiusY), rightWidth + + MAX(innerBottomRightRadiusX, innerTopRightRadiusX));
|
||||
const CGSize size = CGSizeMake(edgeInsets.left + 1 + edgeInsets.right, edgeInsets.top + 1 + edgeInsets.bottom);
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
|
||||
const CGFloat alpha = CGColorGetAlpha(_backgroundColor.CGColor);
|
||||
const BOOL opaque = (self.clipsToBounds || !hasCornerRadii) && alpha == 1.0;
|
||||
UIGraphicsBeginImageContextWithOptions(size, opaque, 0.0);
|
||||
|
||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||
const CGRect rect = {CGPointZero, size};
|
||||
CGPathRef path = RCTPathCreateWithRoundedRect(rect, topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, bottomLeftRadius, bottomLeftRadius, bottomRightRadius, bottomRightRadius, NULL);
|
||||
const CGRect rect = {.size = size};
|
||||
|
||||
CGPathRef path;
|
||||
const BOOL hasClipping = self.clipsToBounds;
|
||||
if (hasClipping) {
|
||||
path = CGPathCreateWithRect(rect, NULL);
|
||||
} else {
|
||||
path = RCTPathCreateWithRoundedRect(rect, topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, bottomLeftRadius, bottomLeftRadius, bottomRightRadius, bottomRightRadius, NULL);
|
||||
}
|
||||
|
||||
if (_backgroundColor) {
|
||||
CGContextSaveGState(ctx);
|
||||
|
||||
CGContextAddPath(ctx, path);
|
||||
CGContextSetFillColorWithColor(ctx, _backgroundColor.CGColor);
|
||||
CGContextAddPath(ctx, path);
|
||||
CGContextFillPath(ctx);
|
||||
|
||||
CGContextRestoreGState(ctx);
|
||||
|
@ -502,29 +521,18 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
CGContextAddPath(ctx, path);
|
||||
CGPathRelease(path);
|
||||
|
||||
BOOL hasRadius = topLeftRadius > 0 || topRightRadius > 0 || bottomLeftRadius > 0 || bottomRightRadius > 0;
|
||||
if (hasRadius && topWidth > 0 && rightWidth > 0 && bottomWidth > 0 && leftWidth > 0) {
|
||||
const UIEdgeInsets insetEdgeInsets = UIEdgeInsetsMake(topWidth, leftWidth, bottomWidth, rightWidth);
|
||||
const CGRect insetRect = UIEdgeInsetsInsetRect(rect, insetEdgeInsets);
|
||||
CGPathRef insetPath = RCTPathCreateWithRoundedRect(insetRect, innerTopLeftRadiusX, innerTopLeftRadiusY, innerTopRightRadiusX, innerTopRightRadiusY, innerBottomLeftRadiusX, innerBottomLeftRadiusY, innerBottomRightRadiusX, innerBottomRightRadiusY, NULL);
|
||||
CGContextAddPath(ctx, insetPath);
|
||||
CGPathRelease(insetPath);
|
||||
}
|
||||
const BOOL hasRadius = topLeftRadius > 0 || topRightRadius > 0 || bottomLeftRadius > 0 || bottomRightRadius > 0;
|
||||
const UIEdgeInsets insetEdgeInsets = UIEdgeInsetsMake(topWidth, leftWidth, bottomWidth, rightWidth);
|
||||
CGPathRef insetPath = RCTPathCreateWithRoundedRect(UIEdgeInsetsInsetRect(rect, insetEdgeInsets), innerTopLeftRadiusX, innerTopLeftRadiusY, innerTopRightRadiusX, innerTopRightRadiusY, innerBottomLeftRadiusX, innerBottomLeftRadiusY, innerBottomRightRadiusX, innerBottomRightRadiusY, NULL);
|
||||
|
||||
CGContextAddPath(ctx, insetPath);
|
||||
CGContextEOClip(ctx);
|
||||
|
||||
BOOL hasEqualColor = !_borderTopColor && !_borderRightColor && !_borderBottomColor && !_borderLeftColor;
|
||||
BOOL hasEqualBorder = _borderWidth >= 0 && _borderTopWidth < 0 && _borderRightWidth < 0 && _borderBottomWidth < 0 && _borderLeftWidth < 0;
|
||||
if (!hasRadius && hasEqualBorder && hasEqualColor) {
|
||||
CGContextSetStrokeColorWithColor(ctx, _borderColor);
|
||||
CGContextSetLineWidth(ctx, 2 * _borderWidth);
|
||||
CGContextClipToRect(ctx, rect);
|
||||
CGContextStrokeRect(ctx, rect);
|
||||
} else if (!hasRadius && hasEqualColor) {
|
||||
if ((hasClipping || !hasRadius) && hasEqualColor) {
|
||||
CGContextSetFillColorWithColor(ctx, _borderColor);
|
||||
CGContextAddRect(ctx, rect);
|
||||
const CGRect insetRect = UIEdgeInsetsInsetRect(rect, edgeInsets);
|
||||
CGContextAddRect(ctx, insetRect);
|
||||
CGContextAddPath(ctx, insetPath);
|
||||
CGContextEOFillPath(ctx);
|
||||
} else {
|
||||
BOOL didSet = NO;
|
||||
|
@ -660,6 +668,8 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
}
|
||||
}
|
||||
|
||||
CGPathRelease(insetPath);
|
||||
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
|
@ -669,8 +679,8 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
|
||||
- (void)displayLayer:(CALayer *)layer
|
||||
{
|
||||
CGRect contentsCenter = (CGRect){CGPointZero, {1, 1}};
|
||||
UIImage *image = [self generateBorderImage:&contentsCenter];
|
||||
CGRect contentsCenter = {.size = {1, 1}};
|
||||
UIImage *image = [self borderImage:&contentsCenter];
|
||||
|
||||
if (image && RCTRunningInTestEnvironment()) {
|
||||
const CGSize size = self.bounds.size;
|
||||
|
@ -685,6 +695,46 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
layer.contentsCenter = contentsCenter;
|
||||
layer.contentsScale = image.scale ?: 1.0;
|
||||
layer.magnificationFilter = kCAFilterNearest;
|
||||
layer.needsDisplayOnBoundsChange = image != nil;
|
||||
|
||||
[self updateClippingForLayer:layer];
|
||||
}
|
||||
|
||||
- (void)updateClippingForLayer:(CALayer *)layer
|
||||
{
|
||||
CALayer *mask = nil;
|
||||
CGFloat cornerRadius = 0;
|
||||
|
||||
if (self.clipsToBounds) {
|
||||
if (_borderRadius > 0 && _borderTopLeftRadius < 0 && _borderTopRightRadius < 0 && _borderBottomLeftRadius < 0 && _borderBottomRightRadius < 0) {
|
||||
cornerRadius = _borderRadius;
|
||||
} else {
|
||||
const CGRect bounds = layer.bounds;
|
||||
const CGFloat maxRadius = MIN(bounds.size.height, bounds.size.width);
|
||||
const CGFloat radius = MAX(0, _borderRadius);
|
||||
const CGFloat topLeftRadius = MIN(_borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius, maxRadius);
|
||||
const CGFloat topRightRadius = MIN(_borderTopRightRadius >= 0 ? _borderTopRightRadius : radius, maxRadius);
|
||||
const CGFloat bottomLeftRadius = MIN(_borderBottomLeftRadius >= 0 ? _borderBottomLeftRadius : radius, maxRadius);
|
||||
const CGFloat bottomRightRadius = MIN(_borderBottomRightRadius >= 0 ? _borderBottomRightRadius : radius, maxRadius);
|
||||
|
||||
if (ABS(topLeftRadius - topRightRadius) < RCTViewBorderThreshold &&
|
||||
ABS(topLeftRadius - bottomLeftRadius) < RCTViewBorderThreshold &&
|
||||
ABS(topLeftRadius - bottomRightRadius) < RCTViewBorderThreshold) {
|
||||
cornerRadius = topLeftRadius;
|
||||
} else {
|
||||
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
|
||||
|
||||
CGPathRef path = RCTPathCreateWithRoundedRect(bounds, topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, bottomLeftRadius, bottomLeftRadius, bottomRightRadius, bottomRightRadius, NULL);
|
||||
shapeLayer.path = path;
|
||||
CGPathRelease(path);
|
||||
|
||||
mask = shapeLayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer.cornerRadius = cornerRadius;
|
||||
layer.mask = mask;
|
||||
}
|
||||
|
||||
#pragma mark Border Color
|
||||
|
|
|
@ -228,36 +228,38 @@ RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomRight)
|
|||
|
||||
#pragma mark - ShadowView properties
|
||||
|
||||
RCT_EXPORT_SHADOW_PROPERTY(top, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(right, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(bottom, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(backgroundColor, UIColor)
|
||||
|
||||
RCT_EXPORT_SHADOW_PROPERTY(top, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(right, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(bottom, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(left, CGFloat);
|
||||
|
||||
RCT_EXPORT_SHADOW_PROPERTY(width, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(height, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(width, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(height, CGFloat)
|
||||
|
||||
RCT_EXPORT_SHADOW_PROPERTY(borderTopWidth, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(borderRightWidth, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(borderBottomWidth, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(borderLeftWidth, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(borderTopWidth, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(borderRightWidth, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(borderBottomWidth, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(borderLeftWidth, CGFloat)
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(borderWidth, CGFloat, RCTShadowView) {
|
||||
[view setBorderWidth:[RCTConvert CGFloat:json]];
|
||||
}
|
||||
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginTop, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginRight, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginBottom, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginLeft, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginVertical, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginHorizontal, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(margin, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginTop, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginRight, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginBottom, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginLeft, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginVertical, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(marginHorizontal, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(margin, CGFloat)
|
||||
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingTop, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingRight, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingBottom, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingLeft, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingVertical, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingHorizontal, CGFloat);
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingTop, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingRight, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingBottom, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingLeft, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingVertical, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(paddingHorizontal, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(padding, CGFloat);
|
||||
|
||||
RCT_EXPORT_SHADOW_PROPERTY(flex, CGFloat)
|
||||
|
@ -268,12 +270,6 @@ RCT_EXPORT_SHADOW_PROPERTY(alignItems, css_align_t)
|
|||
RCT_EXPORT_SHADOW_PROPERTY(alignSelf, css_align_t)
|
||||
RCT_REMAP_SHADOW_PROPERTY(position, positionType, css_position_type_t)
|
||||
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(backgroundColor, UIColor, RCTShadowView)
|
||||
{
|
||||
view.backgroundColor = json ? [RCTConvert UIColor:json] : defaultView.backgroundColor;
|
||||
view.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
||||
}
|
||||
|
||||
RCT_REMAP_SHADOW_PROPERTY(onLayout, hasOnLayout, BOOL)
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var eslint = require('eslint');
|
||||
|
||||
var ignoredStylisticRules = {
|
||||
'key-spacing': false,
|
||||
'comma-spacing': true,
|
||||
'no-multi-spaces': true,
|
||||
'brace-style': true,
|
||||
'camelcase': true,
|
||||
'consistent-this': true,
|
||||
'eol-last': true,
|
||||
'func-names': true,
|
||||
'func-style': true,
|
||||
'new-cap': true,
|
||||
'new-parens': true,
|
||||
'no-nested-ternary': true,
|
||||
'no-array-constructor': true,
|
||||
'no-lonely-if': true,
|
||||
'no-new-object': true,
|
||||
'no-spaced-func': true,
|
||||
'no-space-before-semi': true,
|
||||
'no-ternary': true,
|
||||
'no-trailing-spaces': true,
|
||||
'no-underscore-dangle': true,
|
||||
'no-wrap-func': true,
|
||||
'no-mixed-spaces-and-tabs': true,
|
||||
'quotes': true,
|
||||
'quote-props': true,
|
||||
'semi': true,
|
||||
'sort-vars': true,
|
||||
'space-after-keywords': true,
|
||||
'space-in-brackets': true,
|
||||
'space-in-parens': true,
|
||||
'space-infix-ops': true,
|
||||
'space-return-throw-case': true,
|
||||
'space-unary-word-ops': true,
|
||||
'max-nested-callbacks': true,
|
||||
'one-var': true,
|
||||
'wrap-regex': true,
|
||||
'curly': true,
|
||||
'no-mixed-requires': true,
|
||||
};
|
||||
|
||||
function setLinterTransform(transformSource) {
|
||||
var originalVerify = eslint.linter.verify;
|
||||
eslint.linter.verify = function(text, config, filename, saveState) {
|
||||
var transformedText;
|
||||
try {
|
||||
transformedText = transformSource(text, filename);
|
||||
} catch (e) {
|
||||
return [{
|
||||
severity: 2,
|
||||
line: e.lineNumber,
|
||||
message: e.message,
|
||||
source: text
|
||||
}];
|
||||
}
|
||||
var originalLines = text.split('\n');
|
||||
var transformedLines = transformedText.split('\n');
|
||||
var warnings = originalVerify.call(eslint.linter, transformedText, config, filename, saveState);
|
||||
|
||||
// JSX and ES6 transforms usually generate pretty ugly code. Let's skip lint warnings
|
||||
// about code style for lines that have been changed by transform step.
|
||||
// Note that more important issues, like use of undefined vars, will still be reported.
|
||||
return warnings.filter(function(error) {
|
||||
var lineHasBeenTransformed = originalLines[error.line - 1] !== transformedLines[error.line - 1];
|
||||
var shouldIgnore = ignoredStylisticRules[error.ruleId] && lineHasBeenTransformed;
|
||||
return !shouldIgnore;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setLinterTransform: setLinterTransform,
|
||||
};
|
17
linter.js
|
@ -1,17 +0,0 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var transformSource = require('./jestSupport/scriptPreprocess.js').transformSource;
|
||||
var linterTransform = require('./lint/linterTransform');
|
||||
|
||||
linterTransform.setLinterTransform(transformSource);
|
||||
|
||||
// Run the original CLI
|
||||
require('eslint/bin/eslint');
|
|
@ -44,6 +44,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"absolute-path": "0.0.0",
|
||||
"babel": "5.4.3",
|
||||
"bluebird": "^2.9.21",
|
||||
"chalk": "^1.0.0",
|
||||
"connect": "2.8.3",
|
||||
|
@ -58,7 +59,7 @@
|
|||
"react-timer-mixin": "^0.13.1",
|
||||
"react-tools": "0.13.2",
|
||||
"rebound": "^0.0.12",
|
||||
"sane": "^1.1.2",
|
||||
"sane": "git://github.com/tadeuzagallo/sane.git#a029f8b04a",
|
||||
"source-map": "0.1.31",
|
||||
"stacktrace-parser": "git://github.com/frantic/stacktrace-parser.git#493c5e5638",
|
||||
"uglify-js": "~2.4.16",
|
||||
|
@ -69,6 +70,8 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"jest-cli": "0.4.5",
|
||||
"eslint": "0.9.2"
|
||||
"babel-eslint": "3.1.5",
|
||||
"eslint": "0.21.2",
|
||||
"eslint-plugin-react": "2.3.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,14 +36,18 @@ var webSocketProxy = require('./webSocketProxy.js');
|
|||
var options = parseCommandLine([{
|
||||
command: 'port',
|
||||
default: 8081,
|
||||
type: 'string',
|
||||
}, {
|
||||
command: 'root',
|
||||
type: 'string',
|
||||
description: 'add another root(s) to be used by the packager in this project',
|
||||
}, {
|
||||
command: 'assetRoots',
|
||||
type: 'string',
|
||||
description: 'specify the root directories of app assets'
|
||||
}, {
|
||||
command: 'platform',
|
||||
type: 'string',
|
||||
default: 'ios',
|
||||
description: 'Specify the platform-specific blacklist (ios, android, web).'
|
||||
}, {
|
||||
|
@ -65,13 +69,13 @@ if (options.projectRoots) {
|
|||
}
|
||||
|
||||
if (options.root) {
|
||||
if (typeof options.root === 'string') {
|
||||
options.projectRoots.push(path.resolve(options.root));
|
||||
} else {
|
||||
options.root.forEach(function(root) {
|
||||
options.projectRoots.push(path.resolve(root));
|
||||
});
|
||||
if (!Array.isArray(options.root)) {
|
||||
options.root = options.root.split(',');
|
||||
}
|
||||
|
||||
options.root.forEach(function(root) {
|
||||
options.projectRoots.push(path.resolve(root));
|
||||
});
|
||||
}
|
||||
|
||||
if (options.assetRoots) {
|
||||
|
|
|
@ -18,14 +18,17 @@ exports.middleware = function(options) {
|
|||
return server.processRequest.bind(server);
|
||||
};
|
||||
|
||||
exports.buildPackageFromUrl = function(options, reqUrl) {
|
||||
Activity.disable();
|
||||
// Don't start the filewatcher or the cache.
|
||||
if (options.nonPersistent == null) {
|
||||
options.nonPersistent = true;
|
||||
}
|
||||
exports.buildPackage = function(options, packageOptions) {
|
||||
var server = createServer(options);
|
||||
return server.buildPackage(packageOptions)
|
||||
.then(function(p) {
|
||||
server.end();
|
||||
return p;
|
||||
});
|
||||
};
|
||||
|
||||
var server = new Server(options);
|
||||
exports.buildPackageFromUrl = function(options, reqUrl) {
|
||||
var server = createServer(options);
|
||||
return server.buildPackageFromUrl(reqUrl)
|
||||
.then(function(p) {
|
||||
server.end();
|
||||
|
@ -34,13 +37,7 @@ exports.buildPackageFromUrl = function(options, reqUrl) {
|
|||
};
|
||||
|
||||
exports.getDependencies = function(options, main) {
|
||||
Activity.disable();
|
||||
// Don't start the filewatcher or the cache.
|
||||
if (options.nonPersistent == null) {
|
||||
options.nonPersistent = true;
|
||||
}
|
||||
|
||||
var server = new Server(options);
|
||||
var server = createServer(options);
|
||||
return server.getDependencies(main)
|
||||
.then(function(r) {
|
||||
server.end();
|
||||
|
@ -60,3 +57,13 @@ function useGracefulFs() {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createServer(options) {
|
||||
Activity.disable();
|
||||
// Don't start the filewatcher or the cache.
|
||||
if (options.nonPersistent == null) {
|
||||
options.nonPersistent = true;
|
||||
}
|
||||
|
||||
return new Server(options);
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ function createWatcher(rootConfig) {
|
|||
var watcher = new Watcher(rootConfig.dir, {
|
||||
glob: rootConfig.globs,
|
||||
dot: false,
|
||||
ignore: '**/node_modules/**/*',
|
||||
});
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
|
|
@ -57,25 +57,34 @@ describe('Transformer', function() {
|
|||
});
|
||||
|
||||
pit('should add file info to parse errors', function() {
|
||||
var message = 'message';
|
||||
var snippet = 'snippet';
|
||||
|
||||
require('fs').readFile.mockImpl(function(file, callback) {
|
||||
callback(null, 'var x;\nvar answer = 1 = x;');
|
||||
});
|
||||
|
||||
workers.mockImpl(function(data, callback) {
|
||||
var esprimaError = new Error('Error: Line 2: Invalid left-hand side in assignment');
|
||||
esprimaError.description = 'Invalid left-hand side in assignment';
|
||||
esprimaError.lineNumber = 2;
|
||||
esprimaError.column = 15;
|
||||
callback(null, {error: esprimaError});
|
||||
var babelError = new SyntaxError(message);
|
||||
babelError.type = 'SyntaxError';
|
||||
babelError.description = message;
|
||||
babelError.loc = {
|
||||
line: 2,
|
||||
column: 15,
|
||||
};
|
||||
babelError.codeFrame = snippet;
|
||||
callback(babelError);
|
||||
});
|
||||
|
||||
return new Transformer(OPTIONS).loadFileAndTransform('foo-file.js')
|
||||
.catch(function(error) {
|
||||
expect(error.type).toEqual('TransformError');
|
||||
expect(error.snippet).toEqual([
|
||||
'var answer = 1 = x;',
|
||||
' ^',
|
||||
].join('\n'));
|
||||
expect(error.message).toBe('SyntaxError ' + message);
|
||||
expect(error.lineNumber).toBe(2);
|
||||
expect(error.column).toBe(15);
|
||||
expect(error.filename).toBe('foo-file.js');
|
||||
expect(error.description).toBe(message);
|
||||
expect(error.snippet).toBe(snippet);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -99,7 +99,12 @@ Transformer.prototype.loadFileAndTransform = function(filePath) {
|
|||
}).then(
|
||||
function(res) {
|
||||
if (res.error) {
|
||||
throw formatError(res.error, filePath, sourceCode);
|
||||
console.warn(
|
||||
'Error property on the result value form the transformer',
|
||||
'module is deprecated and will be removed in future versions.',
|
||||
'Please pass an error object as the first argument to the callback'
|
||||
);
|
||||
throw formatError(res.error, filePath);
|
||||
}
|
||||
|
||||
return new ModuleTransport({
|
||||
|
@ -110,6 +115,8 @@ Transformer.prototype.loadFileAndTransform = function(filePath) {
|
|||
});
|
||||
}
|
||||
);
|
||||
}).catch(function(err) {
|
||||
throw formatError(err, filePath);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -118,8 +125,8 @@ function TransformError() {}
|
|||
util.inherits(TransformError, SyntaxError);
|
||||
|
||||
function formatError(err, filename, source) {
|
||||
if (err.lineNumber && err.column) {
|
||||
return formatEsprimaError(err, filename, source);
|
||||
if (err.loc) {
|
||||
return formatBabelError(err, filename, source);
|
||||
} else {
|
||||
return formatGenericError(err, filename, source);
|
||||
}
|
||||
|
@ -136,26 +143,16 @@ function formatGenericError(err, filename) {
|
|||
return error;
|
||||
}
|
||||
|
||||
function formatEsprimaError(err, filename, source) {
|
||||
var stack = err.stack.split('\n');
|
||||
stack.shift();
|
||||
|
||||
var msg = 'TransformError: ' + err.description + ' ' + filename + ':' +
|
||||
err.lineNumber + ':' + err.column;
|
||||
var sourceLine = source.split('\n')[err.lineNumber - 1];
|
||||
var snippet = sourceLine + '\n' + new Array(err.column - 1).join(' ') + '^';
|
||||
|
||||
stack.unshift(msg);
|
||||
|
||||
function formatBabelError(err, filename) {
|
||||
var error = new TransformError();
|
||||
error.message = msg;
|
||||
error.type = 'TransformError';
|
||||
error.stack = stack.join('\n');
|
||||
error.snippet = snippet;
|
||||
error.message = (err.type || error.type) + ' ' + err.message;
|
||||
error.stack = err.stack;
|
||||
error.snippet = err.codeFrame;
|
||||
error.lineNumber = err.loc.line;
|
||||
error.column = err.loc.column;
|
||||
error.filename = filename;
|
||||
error.lineNumber = err.lineNumber;
|
||||
error.column = err.column;
|
||||
error.description = err.description;
|
||||
error.description = err.message;
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ var ModuleTransport = require('../lib/ModuleTransport');
|
|||
|
||||
module.exports = Package;
|
||||
|
||||
var SOURCEMAPPING_URL = '\n\/\/@ sourceMappingURL=';
|
||||
|
||||
function Package(sourceMapUrl) {
|
||||
this._finalized = false;
|
||||
this._modules = [];
|
||||
|
@ -96,12 +98,11 @@ Package.prototype.getSource = function(options) {
|
|||
}
|
||||
|
||||
var source = this._getSource();
|
||||
source += '\n\/\/@ sourceMappingURL=';
|
||||
|
||||
if (options.inlineSourceMap) {
|
||||
source += this._getInlineSourceMap();
|
||||
} else {
|
||||
source += this._sourceMapUrl;
|
||||
source += SOURCEMAPPING_URL + this._getInlineSourceMap();
|
||||
} else if (this._sourceMapUrl) {
|
||||
source += SOURCEMAPPING_URL + this._sourceMapUrl;
|
||||
}
|
||||
|
||||
return source;
|
||||
|
|
|
@ -47,6 +47,26 @@ describe('Package', function() {
|
|||
].join('\n'));
|
||||
});
|
||||
|
||||
it('should be ok to leave out the source map url', function() {
|
||||
var p = new Package();
|
||||
p.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path',
|
||||
}));
|
||||
p.addModule(new ModuleTransport({
|
||||
code: 'transformed bar;',
|
||||
sourceCode: 'source bar',
|
||||
sourcePath: 'bar path',
|
||||
}));
|
||||
|
||||
p.finalize({});
|
||||
expect(p.getSource()).toBe([
|
||||
'transformed foo;',
|
||||
'transformed bar;',
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
it('should create a package and add run module code', function() {
|
||||
ppackage.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;',
|
||||
|
|
|
@ -230,7 +230,7 @@ describe('processRequest', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe.only('/assets endpoint', function() {
|
||||
describe('/assets endpoint', function() {
|
||||
var AssetServer;
|
||||
beforeEach(function() {
|
||||
AssetServer = require('../../AssetServer');
|
||||
|
@ -257,4 +257,30 @@ describe('processRequest', function() {
|
|||
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildPackage(options)', function() {
|
||||
it('Calls the packager with the correct args', function() {
|
||||
server.buildPackage({
|
||||
entryFile: 'foo file'
|
||||
});
|
||||
expect(Packager.prototype.package).toBeCalledWith(
|
||||
'foo file',
|
||||
true,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildPackageFromUrl(options)', function() {
|
||||
it('Calls the packager with the correct args', function() {
|
||||
server.buildPackageFromUrl('/path/to/foo.bundle?dev=false&runModule=false');
|
||||
expect(Packager.prototype.package).toBeCalledWith(
|
||||
'path/to/foo.js',
|
||||
false,
|
||||
'/path/to/foo.map',
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -129,7 +129,7 @@ Server.prototype._onFileChange = function(type, filepath, root) {
|
|||
};
|
||||
|
||||
Server.prototype._rebuildPackages = function() {
|
||||
var buildPackage = this._buildPackage.bind(this);
|
||||
var buildPackage = this.buildPackage.bind(this);
|
||||
var packages = this._packages;
|
||||
Object.keys(packages).forEach(function(key) {
|
||||
var options = getOptionsFromUrl(key);
|
||||
|
@ -171,18 +171,47 @@ Server.prototype.end = function() {
|
|||
]);
|
||||
};
|
||||
|
||||
Server.prototype._buildPackage = function(options) {
|
||||
var packageOpts = declareOpts({
|
||||
sourceMapUrl: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
},
|
||||
entryFile: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
dev: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
minify: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
runModule: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
},
|
||||
inlineSourceMap: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
Server.prototype.buildPackage = function(options) {
|
||||
var opts = packageOpts(options);
|
||||
|
||||
return this._packager.package(
|
||||
options.main,
|
||||
options.runModule,
|
||||
options.sourceMapUrl,
|
||||
options.dev
|
||||
opts.entryFile,
|
||||
opts.runModule,
|
||||
opts.sourceMapUrl,
|
||||
opts.dev
|
||||
);
|
||||
};
|
||||
|
||||
Server.prototype.buildPackageFromUrl = function(reqUrl) {
|
||||
var options = getOptionsFromUrl(reqUrl);
|
||||
return this._buildPackage(options);
|
||||
return this.buildPackage(options);
|
||||
};
|
||||
|
||||
Server.prototype.getDependencies = function(main) {
|
||||
|
@ -321,7 +350,7 @@ Server.prototype.processRequest = function(req, res, next) {
|
|||
|
||||
var startReqEventId = Activity.startEvent('request:' + req.url);
|
||||
var options = getOptionsFromUrl(req.url);
|
||||
var building = this._packages[req.url] || this._buildPackage(options);
|
||||
var building = this._packages[req.url] || this.buildPackage(options);
|
||||
|
||||
this._packages[req.url] = building;
|
||||
building.then(
|
||||
|
@ -363,7 +392,7 @@ function getOptionsFromUrl(reqUrl) {
|
|||
|
||||
return {
|
||||
sourceMapUrl: pathname.replace(/\.bundle$/, '.map'),
|
||||
main: entryFile,
|
||||
entryFile: entryFile,
|
||||
dev: getBoolOptionFromQuery(urlObj.query, 'dev', true),
|
||||
minify: getBoolOptionFromQuery(urlObj.query, 'minify'),
|
||||
runModule: getBoolOptionFromQuery(urlObj.query, 'runModule', true),
|
||||
|
|
|
@ -10,40 +10,37 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var jstransform = require('jstransform').transform;
|
||||
var babel = require('babel');
|
||||
|
||||
var reactVisitors =
|
||||
require('react-tools/vendor/fbtransform/visitors').getAllVisitors();
|
||||
var staticTypeSyntax =
|
||||
require('jstransform/visitors/type-syntax').visitorList;
|
||||
var trailingCommaVisitors =
|
||||
require('jstransform/visitors/es7-trailing-comma-visitors.js').visitorList;
|
||||
|
||||
// Note that reactVisitors now handles ES6 classes, rest parameters, arrow
|
||||
// functions, template strings, and object short notation.
|
||||
var visitorList = reactVisitors.concat(trailingCommaVisitors);
|
||||
|
||||
function transform(srcTxt, filename) {
|
||||
var options = {
|
||||
es3: true,
|
||||
sourceType: 'nonStrictModule',
|
||||
function transform(srcTxt, filename, options) {
|
||||
var result = babel.transform(srcTxt, {
|
||||
retainLines: true,
|
||||
compact: true,
|
||||
comments: false,
|
||||
filename: filename,
|
||||
whitelist: [
|
||||
'es6.arrowFunctions',
|
||||
'es6.blockScoping',
|
||||
'es6.classes',
|
||||
'es6.destructuring',
|
||||
'es6.parameters.rest',
|
||||
'es6.properties.computed',
|
||||
'es6.properties.shorthand',
|
||||
'es6.spread',
|
||||
'es6.templateLiterals',
|
||||
'es7.trailingFunctionCommas',
|
||||
'es7.objectRestSpread',
|
||||
'flow',
|
||||
'react',
|
||||
],
|
||||
sourceFileName: filename,
|
||||
sourceMaps: false,
|
||||
extra: options || {},
|
||||
});
|
||||
|
||||
return {
|
||||
code: result.code,
|
||||
};
|
||||
|
||||
// These tranforms mostly just erase type annotations and static typing
|
||||
// related statements, but they were conflicting with other tranforms.
|
||||
// Running them first solves that problem
|
||||
var staticTypeSyntaxResult = jstransform(
|
||||
staticTypeSyntax,
|
||||
srcTxt,
|
||||
options
|
||||
);
|
||||
|
||||
return jstransform(
|
||||
visitorList,
|
||||
staticTypeSyntaxResult.code,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = function(data, callback) {
|
||||
|
@ -54,15 +51,8 @@ module.exports = function(data, callback) {
|
|||
data.filename
|
||||
);
|
||||
} catch (e) {
|
||||
return callback(null, {
|
||||
error: {
|
||||
lineNumber: e.lineNumber,
|
||||
column: e.column,
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
description: e.description
|
||||
}
|
||||
});
|
||||
callback(e);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, result);
|
||||
|
|
|
@ -65,6 +65,6 @@ cd EndToEndTest
|
|||
# Make sure we installed local version of react-native
|
||||
ls `basename $MARKER` > /dev/null
|
||||
|
||||
flow
|
||||
flow --retries 10
|
||||
|
||||
xctool -scheme EndToEndTest -sdk iphonesimulator test
|
||||
|
|
|
@ -25,5 +25,5 @@ trap cleanup EXIT
|
|||
|
||||
xctool \
|
||||
-project Examples/UIExplorer/UIExplorer.xcodeproj \
|
||||
-scheme UIExplorer -sdk iphonesimulator8.1 \
|
||||
-scheme UIExplorer -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 5,OS=8.1' \
|
||||
test
|
||||
|
|