Release React Native for Android
This is an early release and there are several things that are known not to work if you're porting your iOS app to Android. See the Known Issues guide on the website. We will work with the community to reach platform parity with iOS.
|
@ -25,6 +25,12 @@ project.xcworkspace
|
|||
# OS X
|
||||
.DS_Store
|
||||
|
||||
# Android/IJ
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
|
||||
# Node
|
||||
node_modules
|
||||
*.log
|
||||
|
|
|
@ -37,14 +37,14 @@
|
|||
* on the same Wi-Fi network.
|
||||
*/
|
||||
|
||||
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/Movies/MoviesApp.ios.bundle?platform=ios&dev=true"];
|
||||
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/Movies/MoviesApp.ios.includeRequire.runModule.bundle"];
|
||||
|
||||
/**
|
||||
* OPTION 2
|
||||
* Load from pre-bundled file on disk. To re-generate the static bundle, `cd`
|
||||
* to your Xcode project folder in the terminal, and run
|
||||
*
|
||||
* $ curl 'http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle' -o main.jsbundle
|
||||
* $ curl 'http://localhost:8081/Examples/Movies/MoviesApp.ios.includeRequire.runModule.bundle' -o main.jsbundle
|
||||
*
|
||||
* then add the `main.jsbundle` file to your project and uncomment this line:
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* @providesModule MoviesApp
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
AppRegistry,
|
||||
BackAndroid,
|
||||
Navigator,
|
||||
StyleSheet,
|
||||
ToolbarAndroid,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var MovieScreen = require('./MovieScreen');
|
||||
var SearchScreen = require('./SearchScreen');
|
||||
|
||||
var _navigator;
|
||||
BackAndroid.addEventListener('hardwareBackPress', () => {
|
||||
if (_navigator && _navigator.getCurrentRoutes().length > 1) {
|
||||
_navigator.pop();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
var RouteMapper = function(route, navigationOperations, onComponentRef) {
|
||||
_navigator = navigationOperations;
|
||||
if (route.name === 'search') {
|
||||
return (
|
||||
<SearchScreen navigator={navigationOperations} />
|
||||
);
|
||||
} else if (route.name === 'movie') {
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<ToolbarAndroid
|
||||
actions={[]}
|
||||
navIcon={require('image!android_back_white')}
|
||||
onIconClicked={navigationOperations.pop}
|
||||
style={styles.toolbar}
|
||||
titleColor="white"
|
||||
title={route.movie.title} />
|
||||
<MovieScreen
|
||||
style={{flex: 1}}
|
||||
navigator={navigationOperations}
|
||||
movie={route.movie}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
var MoviesApp = React.createClass({
|
||||
render: function() {
|
||||
var initialRoute = {name: 'search'};
|
||||
return (
|
||||
<Navigator
|
||||
style={styles.container}
|
||||
initialRoute={initialRoute}
|
||||
configureScene={() => Navigator.SceneConfigs.FadeAndroid}
|
||||
renderScene={RouteMapper}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: 'white',
|
||||
},
|
||||
toolbar: {
|
||||
backgroundColor: '#a9a9a9',
|
||||
height: 56,
|
||||
},
|
||||
});
|
||||
|
||||
AppRegistry.registerComponent('MoviesApp', () => MoviesApp);
|
||||
|
||||
module.exports = MoviesApp;
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* @providesModule SearchBar
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
Image,
|
||||
Platform,
|
||||
ProgressBarAndroid,
|
||||
TextInput,
|
||||
StyleSheet,
|
||||
TouchableNativeFeedback,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var IS_RIPPLE_EFFECT_SUPPORTED = Platform.Version >= 21;
|
||||
|
||||
var SearchBar = React.createClass({
|
||||
render: function() {
|
||||
var loadingView;
|
||||
if (this.props.isLoading) {
|
||||
loadingView = (
|
||||
<ProgressBarAndroid
|
||||
styleAttr="Large"
|
||||
style={styles.spinner}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
loadingView = <View style={styles.spinner} />;
|
||||
}
|
||||
var background = IS_RIPPLE_EFFECT_SUPPORTED ?
|
||||
TouchableNativeFeedback.SelectableBackgroundBorderless() :
|
||||
TouchableNativeFeedback.SelectableBackground();
|
||||
return (
|
||||
<View style={styles.searchBar}>
|
||||
<TouchableNativeFeedback
|
||||
background={background}
|
||||
onPress={() => this.refs.input && this.refs.input.focus()}>
|
||||
<View>
|
||||
<Image
|
||||
source={require('image!android_search_white')}
|
||||
style={styles.icon}
|
||||
/>
|
||||
</View>
|
||||
</TouchableNativeFeedback>
|
||||
<TextInput
|
||||
ref="input"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
autoFocus={true}
|
||||
onChange={this.props.onSearchChange}
|
||||
placeholder="Search a movie..."
|
||||
placeholderTextColor="rgba(255, 255, 255, 0.5)"
|
||||
onFocus={this.props.onFocus}
|
||||
style={styles.searchBarInput}
|
||||
/>
|
||||
{loadingView}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
searchBar: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#a9a9a9',
|
||||
height: 56,
|
||||
},
|
||||
searchBarInput: {
|
||||
flex: 1,
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: 'white',
|
||||
height: 50,
|
||||
padding: 0,
|
||||
backgroundColor: 'transparent'
|
||||
},
|
||||
spinner: {
|
||||
width: 30,
|
||||
height: 30,
|
||||
},
|
||||
icon: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
marginHorizontal: 8,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = SearchBar;
|
|
@ -0,0 +1,35 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "23.0.1"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.facebook.react.movies"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:22.2.0'
|
||||
|
||||
// Depend on pre-built React Native
|
||||
compile 'com.facebook.react:react-native:0.11.+'
|
||||
|
||||
// Depend on React Native source.
|
||||
// This is useful for testing your changes when working on React Native.
|
||||
// compile project(':ReactAndroid')
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
|
@ -0,0 +1,21 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.facebook.react.movies">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@drawable/rotten_tomatoes_icon"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MoviesActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.movies;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.facebook.react.LifecycleState;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
|
||||
public class MoviesActivity extends Activity implements DefaultHardwareBackBtnHandler {
|
||||
|
||||
private ReactInstanceManager mReactInstanceManager;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
mReactInstanceManager = ReactInstanceManager.builder()
|
||||
.setApplication(getApplication())
|
||||
.setBundleAssetName("MoviesApp.android.bundle")
|
||||
.setJSMainModuleName("Examples/Movies/MoviesApp.android")
|
||||
.addPackage(new MainReactPackage())
|
||||
.setUseDeveloperSupport(true)
|
||||
.setInitialLifecycleState(LifecycleState.RESUMED)
|
||||
.build();
|
||||
|
||||
((ReactRootView) findViewById(R.id.react_root_view))
|
||||
.startReactApplication(mReactInstanceManager, "MoviesApp", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
|
||||
mReactInstanceManager.showDevOptionsDialog();
|
||||
return true;
|
||||
}
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onPause();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onResume(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onBackPressed();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDefaultOnBackPressed() {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
Двоичные данные
Examples/Movies/android/app/src/main/res/drawable-hdpi/android_back_white.png
Normal file
После Ширина: | Высота: | Размер: 237 B |
Двоичные данные
Examples/Movies/android/app/src/main/res/drawable-hdpi/android_search_white.png
Executable file
После Ширина: | Высота: | Размер: 575 B |
Двоичные данные
Examples/Movies/android/app/src/main/res/drawable-mdpi/android_back_white.png
Normal file
После Ширина: | Высота: | Размер: 190 B |
Двоичные данные
Examples/Movies/android/app/src/main/res/drawable-mdpi/android_search_white.png
Executable file
После Ширина: | Высота: | Размер: 337 B |
Двоичные данные
Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_back_white.png
Normal file
После Ширина: | Высота: | Размер: 266 B |
Двоичные данные
Examples/Movies/android/app/src/main/res/drawable-xhdpi/android_search_white.png
Executable file
После Ширина: | Высота: | Размер: 581 B |
Двоичные данные
Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_back_white.png
Normal file
После Ширина: | Высота: | Размер: 337 B |
Двоичные данные
Examples/Movies/android/app/src/main/res/drawable-xxhdpi/android_search_white.png
Executable file
После Ширина: | Высота: | Размер: 930 B |
Двоичные данные
Examples/Movies/android/app/src/main/res/drawable/rotten_tomatoes_icon.png
Normal file
После Ширина: | Высота: | Размер: 57 KiB |
|
@ -0,0 +1,12 @@
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MoviesApp">
|
||||
|
||||
<com.facebook.react.ReactRootView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/react_root_view"/>
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">MoviesApp</string>
|
||||
</resources>
|
|
@ -0,0 +1,8 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,35 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.facebook.react.sample"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:23.0.0'
|
||||
|
||||
// Depend on pre-built React Native
|
||||
compile 'com.facebook.react:react-native:0.11.+'
|
||||
|
||||
// Depend on React Native source.
|
||||
// This is useful for testing your changes when working on React Native.
|
||||
// compile project(':ReactAndroid')
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
|
@ -0,0 +1,21 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.facebook.react.sample">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.sample;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.facebook.react.LifecycleState;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
|
||||
|
||||
private ReactInstanceManager mReactInstanceManager;
|
||||
private ReactRootView mReactRootView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
SoLoader.init(this, false);
|
||||
mReactRootView = new ReactRootView(this);
|
||||
|
||||
mReactInstanceManager = ReactInstanceManager.builder()
|
||||
.setApplication(getApplication())
|
||||
.setBundleAssetName("index.android.bundle")
|
||||
.setJSMainModuleName("Examples/SampleApp/index.android")
|
||||
.addPackage(new MainReactPackage())
|
||||
.setUseDeveloperSupport(BuildConfig.DEBUG)
|
||||
.setInitialLifecycleState(LifecycleState.RESUMED)
|
||||
.build();
|
||||
|
||||
mReactRootView.startReactApplication(
|
||||
mReactInstanceManager,
|
||||
"SampleApp",
|
||||
null);
|
||||
|
||||
setContentView(mReactRootView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
|
||||
mReactInstanceManager.showDevOptionsDialog();
|
||||
return true;
|
||||
}
|
||||
return super.onKeyUp(keyCode, event);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDefaultOnBackPressed() {
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onPause();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onResume(this);
|
||||
}
|
||||
}
|
||||
}
|
После Ширина: | Высота: | Размер: 3.3 KiB |
После Ширина: | Высота: | Размер: 2.2 KiB |
Двоичные данные
Examples/SampleApp/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
После Ширина: | Высота: | Размер: 4.7 KiB |
Двоичные данные
Examples/SampleApp/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
После Ширина: | Высота: | Размер: 7.5 KiB |
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">SampleApp</string>
|
||||
</resources>
|
|
@ -0,0 +1,8 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Sample React Native App
|
||||
* https://github.com/facebook/react-native
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
AppRegistry,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var SampleApp = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.welcome}>
|
||||
Welcome to React Native!
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
To get started, edit index.android.js
|
||||
</Text>
|
||||
<Text style={styles.instructions}>
|
||||
Shake or press menu button for dev menu
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
instructions: {
|
||||
textAlign: 'center',
|
||||
color: '#333333',
|
||||
marginBottom: 5,
|
||||
},
|
||||
});
|
||||
|
||||
AppRegistry.registerComponent('SampleApp', () => SampleApp);
|
|
@ -0,0 +1,213 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
ToastAndroid,
|
||||
TouchableWithoutFeedback,
|
||||
} = React;
|
||||
|
||||
var UIExplorerBlock = require('./UIExplorerBlock');
|
||||
var UIExplorerPage = require('./UIExplorerPage');
|
||||
|
||||
var importantForAccessibilityValues = ['auto', 'yes', 'no', 'no-hide-descendants'];
|
||||
|
||||
var AccessibilityAndroidExample = React.createClass({
|
||||
|
||||
statics: {
|
||||
title: 'Accessibility',
|
||||
description: 'Examples of using Accessibility API.',
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
count: 0,
|
||||
backgroundImportantForAcc: 0,
|
||||
forgroundImportantForAcc: 0,
|
||||
};
|
||||
},
|
||||
|
||||
_addOne: function() {
|
||||
this.setState({
|
||||
count: ++this.state.count,
|
||||
});
|
||||
},
|
||||
|
||||
_changeBackgroundImportantForAcc: function() {
|
||||
this.setState({
|
||||
backgroundImportantForAcc: (this.state.backgroundImportantForAcc + 1) % 4,
|
||||
});
|
||||
},
|
||||
|
||||
_changeForgroundImportantForAcc: function() {
|
||||
this.setState({
|
||||
forgroundImportantForAcc: (this.state.forgroundImportantForAcc + 1) % 4,
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<UIExplorerPage title={'Accessibility'}>
|
||||
|
||||
<UIExplorerBlock title="Nonaccessible view with TextViews">
|
||||
<View>
|
||||
<Text style={{color: 'green',}}>
|
||||
This is
|
||||
</Text>
|
||||
<Text style={{color: 'blue'}}>
|
||||
nontouchable normal view.
|
||||
</Text>
|
||||
</View>
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Accessible view with TextViews wihout label">
|
||||
<View accessible={true}>
|
||||
<Text style={{color: 'green',}}>
|
||||
This is
|
||||
</Text>
|
||||
<Text style={{color: 'blue'}}>
|
||||
nontouchable accessible view without label.
|
||||
</Text>
|
||||
</View>
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Accessible view with TextViews with label">
|
||||
<View accessible={true}
|
||||
accessibilityLabel="I have label, so I read it instead of embedded text.">
|
||||
<Text style={{color: 'green',}}>
|
||||
This is
|
||||
</Text>
|
||||
<Text style={{color: 'blue'}}>
|
||||
nontouchable accessible view with label.
|
||||
</Text>
|
||||
</View>
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Touchable with component type = button">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={() => ToastAndroid.show('Toasts work by default', ToastAndroid.SHORT)}
|
||||
accessibilityComponentType="button">
|
||||
<View style={styles.embedded}>
|
||||
<Text>Click me</Text>
|
||||
<Text>Or not</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="LiveRegion">
|
||||
<TouchableWithoutFeedback onPress={this._addOne}>
|
||||
<View style={styles.embedded}>
|
||||
<Text>Click me</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
<Text accessibilityLiveRegion="polite">
|
||||
Clicked {this.state.count} times
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Overlapping views and importantForAccessibility property">
|
||||
<View style={styles.container}>
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 10,
|
||||
top: 10,
|
||||
right: 10,
|
||||
height: 100,
|
||||
backgroundColor: 'green'}}
|
||||
accessible={true}
|
||||
accessibilityLabel="First layout"
|
||||
importantForAccessibility={
|
||||
importantForAccessibilityValues[this.state.backgroundImportantForAcc]}>
|
||||
<View accessible={true}>
|
||||
<Text style={{fontSize: 25}}>
|
||||
Hello
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 10,
|
||||
top: 25,
|
||||
right: 10,
|
||||
height: 110,
|
||||
backgroundColor: 'yellow', opacity: 0.5}}
|
||||
accessible={true}
|
||||
accessibilityLabel="Second layout"
|
||||
importantForAccessibility={
|
||||
importantForAccessibilityValues[this.state.forgroundImportantForAcc]}>
|
||||
<View accessible={true}>
|
||||
<Text style={{fontSize: 20}}>
|
||||
world
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<TouchableWithoutFeedback onPress={this._changeBackgroundImportantForAcc}>
|
||||
<View style={styles.embedded}>
|
||||
<Text>
|
||||
Change importantForAccessibility for background layout.
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
<View accessible={true}>
|
||||
<Text>
|
||||
Background layout importantForAccessibility
|
||||
</Text>
|
||||
<Text>
|
||||
{importantForAccessibilityValues[this.state.backgroundImportantForAcc]}
|
||||
</Text>
|
||||
</View>
|
||||
<TouchableWithoutFeedback onPress={this._changeForgroundImportantForAcc}>
|
||||
<View style={styles.embedded}>
|
||||
<Text>
|
||||
Change importantForAccessibility for forground layout.
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
<View accessible={true}>
|
||||
<Text>
|
||||
Forground layout importantForAccessibility
|
||||
</Text>
|
||||
<Text>
|
||||
{importantForAccessibilityValues[this.state.forgroundImportantForAcc]}
|
||||
</Text>
|
||||
</View>
|
||||
</UIExplorerBlock>
|
||||
|
||||
</UIExplorerPage>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
embedded: {
|
||||
backgroundColor: 'yellow',
|
||||
padding:10,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: 'white',
|
||||
padding: 10,
|
||||
height:150,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = AccessibilityAndroidExample;
|
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* 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 ProgressBar = require('ProgressBarAndroid');
|
||||
var React = require('React');
|
||||
var UIExplorerBlock = require('UIExplorerBlock');
|
||||
var UIExplorerPage = require('UIExplorerPage');
|
||||
|
||||
var ProgressBarAndroidExample = React.createClass({
|
||||
|
||||
statics: {
|
||||
title: '<ProgressBarAndroid>',
|
||||
description: 'Visual indicator of progress of some operation. ' +
|
||||
'Shows either a cyclic animation or a horizontal bar.',
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<UIExplorerPage title="ProgressBar Examples">
|
||||
<UIExplorerBlock title="Default ProgressBar">
|
||||
<ProgressBar />
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Small ProgressBar">
|
||||
<ProgressBar styleAttr="Small" />
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Large ProgressBar">
|
||||
<ProgressBar styleAttr="Large" />
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Inverse ProgressBar">
|
||||
<ProgressBar styleAttr="Inverse" />
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Small Inverse ProgressBar">
|
||||
<ProgressBar styleAttr="SmallInverse" />
|
||||
</UIExplorerBlock>
|
||||
|
||||
<UIExplorerBlock title="Large Inverse ProgressBar">
|
||||
<ProgressBar styleAttr="LargeInverse" />
|
||||
</UIExplorerBlock>
|
||||
</UIExplorerPage>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ProgressBarAndroidExample;
|
|
@ -30,6 +30,7 @@ var ScrollViewSimpleExample = React.createClass({
|
|||
title: '<ScrollView>',
|
||||
description: 'Component that enables scrolling through child components.'
|
||||
},
|
||||
|
||||
makeItems: function(nItems: number, styles): Array<any> {
|
||||
var items = [];
|
||||
for (var i = 0; i < nItems; i++) {
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
var SwitchAndroid = require('SwitchAndroid');
|
||||
var Text = require('Text');
|
||||
var UIExplorerBlock = require('UIExplorerBlock');
|
||||
var UIExplorerPage = require('UIExplorerPage');
|
||||
|
||||
var SwitchAndroidExample = React.createClass({
|
||||
statics: {
|
||||
title: '<SwitchAndroid>',
|
||||
description: 'Standard Android two-state toggle component'
|
||||
},
|
||||
|
||||
getInitialState : function() {
|
||||
return {
|
||||
trueSwitchIsOn: true,
|
||||
falseSwitchIsOn: false,
|
||||
colorTrueSwitchIsOn: true,
|
||||
colorFalseSwitchIsOn: false,
|
||||
eventSwitchIsOn: false,
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<UIExplorerPage title="<SwitchAndroid>">
|
||||
<UIExplorerBlock title="Switches can be set to true or false">
|
||||
<SwitchAndroid
|
||||
onValueChange={(value) => this.setState({falseSwitchIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.falseSwitchIsOn} />
|
||||
<SwitchAndroid
|
||||
onValueChange={(value) => this.setState({trueSwitchIsOn: value})}
|
||||
value={this.state.trueSwitchIsOn} />
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Switches can be disabled">
|
||||
<SwitchAndroid
|
||||
disabled={true}
|
||||
style={{marginBottom: 10}}
|
||||
value={true} />
|
||||
<SwitchAndroid
|
||||
disabled={true}
|
||||
value={false} />
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Change events can be detected">
|
||||
<SwitchAndroid
|
||||
onValueChange={(value) => this.setState({eventSwitchIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventSwitchIsOn} />
|
||||
<SwitchAndroid
|
||||
onValueChange={(value) => this.setState({eventSwitchIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventSwitchIsOn} />
|
||||
<Text>{this.state.eventSwitchIsOn ? "On" : "Off"}</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Switches are controlled components">
|
||||
<SwitchAndroid />
|
||||
<SwitchAndroid value={true} />
|
||||
</UIExplorerBlock>
|
||||
</UIExplorerPage>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SwitchAndroidExample;
|
|
@ -0,0 +1,349 @@
|
|||
/**
|
||||
* 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 {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = React;
|
||||
var UIExplorerBlock = require('./UIExplorerBlock');
|
||||
var UIExplorerPage = require('./UIExplorerPage');
|
||||
|
||||
var Entity = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<Text style={{fontWeight: 'bold', color: '#527fe4'}}>
|
||||
{this.props.children}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var AttributeToggler = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {fontWeight: 'bold', fontSize: 15};
|
||||
},
|
||||
toggleWeight: function() {
|
||||
this.setState({
|
||||
fontWeight: this.state.fontWeight === 'bold' ? 'normal' : 'bold'
|
||||
});
|
||||
},
|
||||
increaseSize: function() {
|
||||
this.setState({
|
||||
fontSize: this.state.fontSize + 1
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
var curStyle = {fontWeight: this.state.fontWeight, fontSize: this.state.fontSize};
|
||||
return (
|
||||
<View>
|
||||
<Text style={curStyle}>
|
||||
Tap the controls below to change attributes.
|
||||
</Text>
|
||||
<Text>
|
||||
<Text>See how it will even work on <Text style={curStyle}>this nested text</Text></Text>
|
||||
</Text>
|
||||
<Text>
|
||||
<Text onPress={this.toggleWeight}>Toggle Weight</Text>
|
||||
{' (with highlight onPress)'}
|
||||
</Text>
|
||||
<Text onPress={this.increaseSize} suppressHighlighting={true}>
|
||||
Increase Size (suppressHighlighting true)
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var TextExample = React.createClass({
|
||||
statics: {
|
||||
title: '<Text>',
|
||||
description: 'Base component for rendering styled text.',
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<UIExplorerPage title="<Text>">
|
||||
<UIExplorerBlock title="Wrap">
|
||||
<Text>
|
||||
The text should wrap if it goes on multiple lines.
|
||||
See, this is going to the next line.
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Padding">
|
||||
<Text style={{padding: 10}}>
|
||||
This text is indented by 10px padding on all sides.
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Font Family">
|
||||
<Text style={{fontFamily: 'sans-serif'}}>
|
||||
Sans-Serif
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif', fontWeight: 'bold'}}>
|
||||
Sans-Serif Bold
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'serif'}}>
|
||||
Serif
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'serif', fontWeight: 'bold'}}>
|
||||
Serif Bold
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'monospace'}}>
|
||||
Monospace
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'monospace', fontWeight: 'bold'}}>
|
||||
Monospace Bold (After 5.0)
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Android Material Design fonts">
|
||||
<View style={{flexDirection: 'row', alignItems: 'flex-start'}}>
|
||||
<View style={{flex: 1}}>
|
||||
<Text style={{fontFamily: 'sans-serif'}}>
|
||||
Roboto Regular
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif', fontStyle: 'italic'}}>
|
||||
Roboto Italic
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif', fontWeight: 'bold'}}>
|
||||
Roboto Bold
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif', fontStyle: 'italic', fontWeight: 'bold'}}>
|
||||
Roboto Bold Italic
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif-light'}}>
|
||||
Roboto Light
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif-light', fontStyle: 'italic'}}>
|
||||
Roboto Light Italic
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif-thin'}}>
|
||||
Roboto Thin (After 4.2)
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif-thin', fontStyle: 'italic'}}>
|
||||
Roboto Thin Italic (After 4.2)
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif-condensed'}}>
|
||||
Roboto Condensed
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif-condensed', fontStyle: 'italic'}}>
|
||||
Roboto Condensed Italic
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif-condensed', fontWeight: 'bold'}}>
|
||||
Roboto Condensed Bold
|
||||
</Text>
|
||||
<Text style={{
|
||||
fontFamily: 'sans-serif-condensed',
|
||||
fontStyle: 'italic',
|
||||
fontWeight: 'bold'}}>
|
||||
Roboto Condensed Bold Italic
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif-medium'}}>
|
||||
Roboto Medium (After 5.0)
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'sans-serif-medium', fontStyle: 'italic'}}>
|
||||
Roboto Medium Italic (After 5.0)
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Font Size">
|
||||
<Text style={{fontSize: 23}}>
|
||||
Size 23
|
||||
</Text>
|
||||
<Text style={{fontSize: 8}}>
|
||||
Size 8
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Color">
|
||||
<Text style={{color: 'red'}}>
|
||||
Red color
|
||||
</Text>
|
||||
<Text style={{color: 'blue'}}>
|
||||
Blue color
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Font Weight">
|
||||
<Text style={{fontWeight: 'bold'}}>
|
||||
Move fast and be bold
|
||||
</Text>
|
||||
<Text style={{fontWeight: 'normal'}}>
|
||||
Move fast and be bold
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Font Style">
|
||||
<Text style={{fontStyle: 'italic'}}>
|
||||
Move fast and be bold
|
||||
</Text>
|
||||
<Text style={{fontStyle: 'normal'}}>
|
||||
Move fast and be bold
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Font Style and Weight">
|
||||
<Text style={{fontStyle: 'italic', fontWeight: 'bold'}}>
|
||||
Move fast and be bold
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Nested">
|
||||
<Text onPress={() => console.log('1st')}>
|
||||
(Normal text,
|
||||
<Text style={{fontWeight: 'bold'}} onPress={() => console.log('2nd')}>
|
||||
(and bold
|
||||
<Text style={{fontStyle: 'italic', fontSize: 11, color: '#527fe4'}} onPress={() => console.log('3rd')}>
|
||||
(and tiny bold italic blue
|
||||
<Text style={{fontWeight: 'normal', fontStyle: 'normal'}} onPress={() => console.log('4th')}>
|
||||
(and tiny normal blue)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'serif'}} onPress={() => console.log('1st')}>
|
||||
(Serif
|
||||
<Text style={{fontStyle: 'italic', fontWeight: 'bold'}} onPress={() => console.log('2nd')}>
|
||||
(Serif Bold Italic
|
||||
<Text
|
||||
style={{fontFamily: 'monospace', fontStyle: 'normal', fontWeight: 'normal'}}
|
||||
onPress={() => console.log('3rd')}>
|
||||
(Monospace Normal
|
||||
<Text
|
||||
style={{fontFamily: 'sans-serif', fontWeight: 'bold'}}
|
||||
onPress={() => console.log('4th')}>
|
||||
(Sans-Serif Bold
|
||||
<Text style={{fontWeight: 'normal'}} onPress={() => console.log('5th')}>
|
||||
(and Sans-Serif Normal)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
<Text style={{fontSize: 12}}>
|
||||
<Entity>Entity Name</Entity>
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Text Align">
|
||||
<Text>
|
||||
auto (default) - english LTR
|
||||
</Text>
|
||||
<Text>
|
||||
أحب اللغة العربية auto (default) - arabic RTL
|
||||
</Text>
|
||||
<Text style={{textAlign: 'left'}}>
|
||||
left left left left left left left left left left left left left left left
|
||||
</Text>
|
||||
<Text style={{textAlign: 'center'}}>
|
||||
center center center center center center center center center center center
|
||||
</Text>
|
||||
<Text style={{textAlign: 'right'}}>
|
||||
right right right right right right right right right right right right right
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Unicode">
|
||||
<View style={{flex: 1}}>
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<Text style={{backgroundColor: 'red'}}>
|
||||
星际争霸是世界上最好的游戏。
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{flex: 1}}>
|
||||
<Text style={{backgroundColor: 'red'}}>
|
||||
星际争霸是世界上最好的游戏。
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{flex: 1, alignItems: 'center'}}>
|
||||
<Text style={{backgroundColor: 'red'}}>
|
||||
星际争霸是世界上最好的游戏。
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{flex: 1}}>
|
||||
<Text style={{backgroundColor: 'red'}}>
|
||||
星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。星际争霸是世界上最好的游戏。
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Spaces">
|
||||
<Text>
|
||||
A {'generated'} {' '} {'string'} and some spaces
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Line Height">
|
||||
<Text style={{lineHeight: 35}}>
|
||||
Holisticly formulate inexpensive ideas before best-of-breed benefits. <Text style={{fontSize: 20}}>Continually</Text> expedite magnetic potentialities rather than client-focused interfaces.
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Empty Text">
|
||||
<Text />
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Toggling Attributes">
|
||||
<AttributeToggler />
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="backgroundColor attribute">
|
||||
<Text style={{backgroundColor: '#ffaaaa'}}>
|
||||
Red background,
|
||||
<Text style={{backgroundColor: '#aaaaff'}}>
|
||||
{' '}blue background,
|
||||
<Text>
|
||||
{' '}inherited blue background,
|
||||
<Text style={{backgroundColor: '#aaffaa'}}>
|
||||
{' '}nested green background.
|
||||
</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="containerBackgroundColor attribute">
|
||||
<View style={{flexDirection: 'row', height: 85}}>
|
||||
<View style={{backgroundColor: '#ffaaaa', width: 150}} />
|
||||
<View style={{backgroundColor: '#aaaaff', width: 150}} />
|
||||
</View>
|
||||
<Text style={[styles.backgroundColorText, {top: -80}]}>
|
||||
Default containerBackgroundColor (inherited) + backgroundColor wash
|
||||
</Text>
|
||||
<Text style={[styles.backgroundColorText, {top: -70, backgroundColor: 'transparent'}]}>
|
||||
{"containerBackgroundColor: 'transparent' + backgroundColor wash"}
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="numberOfLines attribute">
|
||||
<Text numberOfLines={1}>
|
||||
Maximum of one line no matter now much I write here. If I keep writing it{"'"}ll just truncate after one line
|
||||
</Text>
|
||||
<Text numberOfLines={2} style={{marginTop: 20}}>
|
||||
Maximum of two lines no matter now much I write here. If I keep writing it{"'"}ll just truncate after two lines
|
||||
</Text>
|
||||
<Text style={{marginTop: 20}}>
|
||||
No maximum lines specified no matter now much I write here. If I keep writing it{"'"}ll just keep going and going
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
</UIExplorerPage>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
backgroundColorText: {
|
||||
left: 5,
|
||||
backgroundColor: 'rgba(100, 100, 100, 0.3)'
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = TextExample;
|
|
@ -0,0 +1,316 @@
|
|||
/**
|
||||
* 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,
|
||||
TextInput,
|
||||
View,
|
||||
StyleSheet,
|
||||
} = React;
|
||||
|
||||
var TextEventsExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
curText: '<No Event>',
|
||||
prevText: '<No Event>',
|
||||
prev2Text: '<No Event>',
|
||||
};
|
||||
},
|
||||
|
||||
updateText: function(text) {
|
||||
this.setState((state) => {
|
||||
return {
|
||||
curText: text,
|
||||
prevText: state.curText,
|
||||
prev2Text: state.prevText,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
autoCapitalize="none"
|
||||
placeholder="Enter text to see events"
|
||||
autoCorrect={false}
|
||||
onFocus={() => this.updateText('onFocus')}
|
||||
onBlur={() => this.updateText('onBlur')}
|
||||
onChange={(event) => this.updateText(
|
||||
'onChange text: ' + event.nativeEvent.text
|
||||
)}
|
||||
onEndEditing={(event) => this.updateText(
|
||||
'onEndEditing text: ' + event.nativeEvent.text
|
||||
)}
|
||||
onSubmitEditing={(event) => this.updateText(
|
||||
'onSubmitEditing text: ' + event.nativeEvent.text
|
||||
)}
|
||||
style={styles.singleLine}
|
||||
/>
|
||||
<Text style={styles.eventLabel}>
|
||||
{this.state.curText}{'\n'}
|
||||
(prev: {this.state.prevText}){'\n'}
|
||||
(prev2: {this.state.prev2Text})
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
class RewriteExample extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {text: ''};
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<TextInput
|
||||
onChangeText={(text) => {
|
||||
text = text.replace(/ /g, '_');
|
||||
this.setState({text});
|
||||
}}
|
||||
style={styles.singleLine}
|
||||
value={this.state.text}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
multiline: {
|
||||
height: 60,
|
||||
fontSize: 16,
|
||||
padding: 4,
|
||||
marginBottom: 10,
|
||||
},
|
||||
eventLabel: {
|
||||
margin: 3,
|
||||
fontSize: 12,
|
||||
},
|
||||
singleLine: {
|
||||
fontSize: 16,
|
||||
padding: 4,
|
||||
},
|
||||
singleLineWithHeightTextInput: {
|
||||
height: 30,
|
||||
},
|
||||
});
|
||||
|
||||
exports.title = '<TextInput>';
|
||||
exports.description = 'Single and multi-line text inputs.';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'Auto-focus',
|
||||
render: function() {
|
||||
return <TextInput autoFocus={true} style={styles.singleLine} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Live Re-Write (<sp> -> '_')",
|
||||
render: function() {
|
||||
return <RewriteExample />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Auto-capitalize',
|
||||
render: function() {
|
||||
var autoCapitalizeTypes = [
|
||||
'none',
|
||||
'sentences',
|
||||
'words',
|
||||
'characters',
|
||||
];
|
||||
var examples = autoCapitalizeTypes.map((type) => {
|
||||
return (
|
||||
<TextInput
|
||||
autoCapitalize={type}
|
||||
placeholder={'autoCapitalize: ' + type}
|
||||
style={styles.singleLine}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return <View>{examples}</View>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Auto-correct',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
autoCorrect={true}
|
||||
placeholder="This has autoCorrect"
|
||||
style={styles.singleLine}
|
||||
/>
|
||||
<TextInput
|
||||
autoCorrect={false}
|
||||
placeholder="This does not have autoCorrect"
|
||||
style={styles.singleLine}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Keyboard types',
|
||||
render: function() {
|
||||
var keyboardTypes = [
|
||||
'default',
|
||||
'email-address',
|
||||
'numeric',
|
||||
];
|
||||
var examples = keyboardTypes.map((type) => {
|
||||
return (
|
||||
<TextInput
|
||||
keyboardType={type}
|
||||
placeholder={'keyboardType: ' + type}
|
||||
style={styles.singleLine}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return <View>{examples}</View>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Event handling',
|
||||
render: function(): ReactElement { return <TextEventsExample />; },
|
||||
},
|
||||
{
|
||||
title: 'Colors and text inputs',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
style={[styles.singleLine]}
|
||||
defaultValue="Default color text"
|
||||
/>
|
||||
<TextInput
|
||||
style={[styles.singleLine, {color: 'green'}]}
|
||||
defaultValue="Green Text"
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Default placeholder text color"
|
||||
style={styles.singleLine}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Red placeholder text color"
|
||||
placeholderTextColor="red"
|
||||
style={styles.singleLine}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Default underline color"
|
||||
style={styles.singleLine}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Blue underline color"
|
||||
style={styles.singleLine}
|
||||
underlineColorAndroid="blue"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Text input, themes and heights',
|
||||
render: function() {
|
||||
return (
|
||||
<TextInput
|
||||
placeholder="If you set height, beware of padding set from themes"
|
||||
style={[styles.singleLineWithHeightTextInput]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Passwords',
|
||||
render: function() {
|
||||
return (
|
||||
<TextInput
|
||||
defaultValue="iloveturtles"
|
||||
password={true}
|
||||
style={styles.singleLine}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Editable',
|
||||
render: function() {
|
||||
return (
|
||||
<TextInput
|
||||
defaultValue="Can't touch this! (>'-')> ^(' - ')^ <('-'<) (>'-')> ^(' - ')^"
|
||||
editable={false}
|
||||
style={styles.singleLine}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Multiline',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
autoCorrect={true}
|
||||
placeholder="multiline, aligned top-left"
|
||||
placeholderTextColor="red"
|
||||
multiline={true}
|
||||
textAlign="start"
|
||||
textAlignVertical="top"
|
||||
style={styles.multiline}
|
||||
/>
|
||||
<TextInput
|
||||
autoCorrect={true}
|
||||
placeholder="multiline, aligned center"
|
||||
placeholderTextColor="green"
|
||||
multiline={true}
|
||||
textAlign="center"
|
||||
textAlignVertical="center"
|
||||
style={[styles.multiline]}
|
||||
/>
|
||||
<TextInput
|
||||
autoCorrect={true}
|
||||
multiline={true}
|
||||
textAlign="end"
|
||||
textAlignVertical="bottom"
|
||||
style={[styles.multiline, {color: 'blue'}]}>
|
||||
<Text style={styles.multiline}>multiline with children, aligned bottom-right</Text>
|
||||
</TextInput>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Fixed number of lines',
|
||||
platform: 'android',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TextInput numberOfLines={2}
|
||||
multiline={true}
|
||||
placeholder="Two line input"
|
||||
/>
|
||||
<TextInput numberOfLines={5}
|
||||
multiline={true}
|
||||
placeholder="Five line input"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
];
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* 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 {
|
||||
StyleSheet,
|
||||
Text,
|
||||
ToastAndroid,
|
||||
TouchableWithoutFeedback
|
||||
} = React;
|
||||
|
||||
var UIExplorerBlock = require('UIExplorerBlock');
|
||||
var UIExplorerPage = require('UIExplorerPage');
|
||||
|
||||
var ToastExample = React.createClass({
|
||||
|
||||
statics: {
|
||||
title: 'Toast Example',
|
||||
description: 'Toast Example',
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<UIExplorerPage title="ToastAndroid">
|
||||
<UIExplorerBlock title="Simple toast">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={() =>
|
||||
ToastAndroid.show('This is a toast with short duration', ToastAndroid.SHORT)}>
|
||||
<Text style={styles.text}>Click me.</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Toast with long duration">
|
||||
<TouchableWithoutFeedback
|
||||
onPress={() =>
|
||||
ToastAndroid.show('This is a toast with long duration', ToastAndroid.LONG)}>
|
||||
<Text style={styles.text}>Click me too.</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
</UIExplorerBlock>
|
||||
</UIExplorerPage>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
text: {
|
||||
color: 'black',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ToastExample;
|
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
* 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 {
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} = React;
|
||||
var UIExplorerBlock = require('./UIExplorerBlock');
|
||||
var UIExplorerPage = require('./UIExplorerPage');
|
||||
|
||||
var SwitchAndroid = require('SwitchAndroid');
|
||||
var ToolbarAndroid = require('ToolbarAndroid');
|
||||
|
||||
var ToolbarAndroidExample = React.createClass({
|
||||
statics: {
|
||||
title: '<ToolbarAndroid>',
|
||||
description: 'Examples of using the Android toolbar.'
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {
|
||||
actionText: 'Example app with toolbar component',
|
||||
toolbarSwitch: false,
|
||||
colorProps: {
|
||||
titleColor: '#3b5998',
|
||||
subtitleColor: '#6a7180',
|
||||
},
|
||||
};
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<UIExplorerPage title="<ToolbarAndroid>">
|
||||
<UIExplorerBlock title="Toolbar with title/subtitle and actions">
|
||||
<ToolbarAndroid
|
||||
actions={toolbarActions}
|
||||
navIcon={require('image!ic_menu_black_24dp')}
|
||||
onActionSelected={this._onActionSelected}
|
||||
onIconClicked={() => this.setState({actionText: 'Icon clicked'})}
|
||||
style={styles.toolbar}
|
||||
subtitle={this.state.actionText}
|
||||
title="Toolbar" />
|
||||
<Text>{this.state.actionText}</Text>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Toolbar with logo & custom title view (a View with Switch+Text)">
|
||||
<ToolbarAndroid
|
||||
logo={require('image!launcher_icon')}
|
||||
style={styles.toolbar}>
|
||||
<View style={{height: 56, flexDirection: 'row', alignItems: 'center'}}>
|
||||
<SwitchAndroid
|
||||
value={this.state.toolbarSwitch}
|
||||
onValueChange={(value) => this.setState({'toolbarSwitch': value})} />
|
||||
<Text>{'\'Tis but a switch'}</Text>
|
||||
</View>
|
||||
</ToolbarAndroid>
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Toolbar with no icon">
|
||||
<ToolbarAndroid
|
||||
actions={toolbarActions}
|
||||
style={styles.toolbar}
|
||||
subtitle="There be no icon here" />
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Toolbar with navIcon & logo, no title">
|
||||
<ToolbarAndroid
|
||||
actions={toolbarActions}
|
||||
logo={require('image!launcher_icon')}
|
||||
navIcon={require('image!ic_menu_black_24dp')}
|
||||
style={styles.toolbar} />
|
||||
</UIExplorerBlock>
|
||||
<UIExplorerBlock title="Toolbar with custom title colors">
|
||||
<ToolbarAndroid
|
||||
navIcon={require('image!ic_menu_black_24dp')}
|
||||
onIconClicked={() => this.setState({colorProps: {}})}
|
||||
title="Wow, such toolbar"
|
||||
style={styles.toolbar}
|
||||
subtitle="Much native"
|
||||
{...this.state.colorProps} />
|
||||
<Text>
|
||||
Touch the icon to reset the custom colors to the default (theme-provided) ones.
|
||||
</Text>
|
||||
</UIExplorerBlock>
|
||||
</UIExplorerPage>
|
||||
);
|
||||
},
|
||||
_onActionSelected: function(position) {
|
||||
this.setState({
|
||||
actionText: 'Selected ' + toolbarActions[position].title,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
var toolbarActions = [
|
||||
{title: 'Create', icon: require('image!ic_create_black_48dp'), show: 'always'},
|
||||
{title: 'Filter'},
|
||||
{title: 'Settings', icon: require('image!ic_settings_black_48dp'), show: 'always'},
|
||||
];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
toolbar: {
|
||||
backgroundColor: '#e9eaed',
|
||||
height: 56,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ToolbarAndroidExample;
|
|
@ -18,34 +18,42 @@
|
|||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
AppRegistry,
|
||||
BackAndroid,
|
||||
Dimensions,
|
||||
DrawerLayoutAndroid,
|
||||
StyleSheet,
|
||||
ToolbarAndroid,
|
||||
View,
|
||||
} = React;
|
||||
var UIExplorerList = require('./UIExplorerList');
|
||||
|
||||
// TODO: these should be exposed by the 'react-native' module.
|
||||
var DrawerLayoutAndroid = require('DrawerLayoutAndroid');
|
||||
var ToolbarAndroid = require('ToolbarAndroid');
|
||||
var UIExplorerList = require('./UIExplorerList.android');
|
||||
|
||||
var DRAWER_WIDTH_LEFT = 56;
|
||||
|
||||
var UIExplorerApp = React.createClass({
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
example: {
|
||||
title: 'UIExplorer',
|
||||
component: this._renderHome(),
|
||||
},
|
||||
example: this._getUIExplorerHome(),
|
||||
};
|
||||
},
|
||||
|
||||
_getUIExplorerHome: function() {
|
||||
return {
|
||||
title: 'UIExplorer',
|
||||
component: this._renderHome(),
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
BackAndroid.addEventListener('hardwareBackPress', this._handleBackButtonPress);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<DrawerLayoutAndroid
|
||||
drawerPosition={DrawerLayoutAndroid.positions.Left}
|
||||
drawerWidth={Dimensions.get('window').width - DRAWER_WIDTH_LEFT}
|
||||
keyboardDismissMode="on-drag"
|
||||
ref={(drawer) => { this.drawer = drawer; }}
|
||||
renderNavigationView={this._renderNavigationView}>
|
||||
{this._renderNavigation()}
|
||||
|
@ -64,14 +72,11 @@ var UIExplorerApp = React.createClass({
|
|||
|
||||
onSelectExample: function(example) {
|
||||
this.drawer.closeDrawer();
|
||||
if (example.title === 'UIExplorer') {
|
||||
example.component = this._renderHome();
|
||||
if (example.title === this._getUIExplorerHome().title) {
|
||||
example = this._getUIExplorerHome();
|
||||
}
|
||||
this.setState({
|
||||
example: {
|
||||
title: example.title,
|
||||
component: example.component,
|
||||
},
|
||||
example: example,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -105,16 +110,16 @@ var UIExplorerApp = React.createClass({
|
|||
);
|
||||
},
|
||||
|
||||
_handleBackButtonPress: function() {
|
||||
if (this.state.example.title !== this._getUIExplorerHome().title) {
|
||||
this.onSelectExample(this._getUIExplorerHome());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
messageText: {
|
||||
fontSize: 17,
|
||||
fontWeight: '500',
|
||||
padding: 15,
|
||||
marginTop: 50,
|
||||
marginLeft: 15,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
|
@ -124,4 +129,6 @@ var styles = StyleSheet.create({
|
|||
},
|
||||
});
|
||||
|
||||
AppRegistry.registerComponent('UIExplorerApp', () => UIExplorerApp);
|
||||
|
||||
module.exports = UIExplorerApp;
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* 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 {
|
||||
StyleSheet,
|
||||
View,
|
||||
} = React;
|
||||
var UIExplorerListBase = require('./UIExplorerListBase');
|
||||
|
||||
var COMPONENTS = [
|
||||
require('./ImageExample'),
|
||||
require('./ProgressBarAndroidExample'),
|
||||
require('./ScrollViewSimpleExample'),
|
||||
require('./SwitchAndroidExample'),
|
||||
require('./TextExample.android'),
|
||||
require('./TextInputExample.android'),
|
||||
require('./ToolbarAndroidExample'),
|
||||
require('./TouchableExample'),
|
||||
require('./ViewExample'),
|
||||
];
|
||||
|
||||
var APIS = [
|
||||
require('./AccessibilityAndroidExample.android'),
|
||||
require('./BorderExample'),
|
||||
require('./LayoutEventsExample'),
|
||||
require('./LayoutExample'),
|
||||
require('./PanResponderExample'),
|
||||
require('./PointerEventsExample'),
|
||||
require('./TimerExample'),
|
||||
require('./ToastAndroidExample.android'),
|
||||
require('./XHRExample'),
|
||||
];
|
||||
|
||||
type Props = {
|
||||
onSelectExample: Function,
|
||||
isInDrawer: bool,
|
||||
};
|
||||
|
||||
class UIExplorerList extends React.Component {
|
||||
props: Props;
|
||||
|
||||
render() {
|
||||
return (
|
||||
<UIExplorerListBase
|
||||
components={COMPONENTS}
|
||||
apis={APIS}
|
||||
searchText=""
|
||||
renderAdditionalView={this.renderAdditionalView.bind(this)}
|
||||
onPressRow={this.onPressRow.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderAdditionalView(renderRow, renderTextInput): React.Component {
|
||||
if (this.props.isInDrawer) {
|
||||
var homePage = renderRow({
|
||||
title: 'UIExplorer',
|
||||
description: 'List of examples',
|
||||
}, -1);
|
||||
return (
|
||||
<View>
|
||||
{homePage}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return renderTextInput(styles.searchTextInput);
|
||||
}
|
||||
|
||||
onPressRow(example: any) {
|
||||
var Component = UIExplorerListBase.makeRenderable(example);
|
||||
this.props.onSelectExample({
|
||||
title: Component.title,
|
||||
component: Component,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
searchTextInput: {
|
||||
padding: 2,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = UIExplorerList;
|
|
@ -0,0 +1,326 @@
|
|||
/**
|
||||
* 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 {
|
||||
PixelRatio,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
// TODO t7093728 This is a simlified XHRExample.ios.js.
|
||||
// Once we have Camera roll, Toast, Intent (for opening URLs)
|
||||
// we should make this consistent with iOS.
|
||||
|
||||
class Downloader extends React.Component {
|
||||
|
||||
xhr: XMLHttpRequest;
|
||||
cancelled: boolean;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.cancelled = false;
|
||||
this.state = {
|
||||
status: '',
|
||||
contentSize: 1,
|
||||
downloaded: 0,
|
||||
};
|
||||
}
|
||||
|
||||
download() {
|
||||
this.xhr && this.xhr.abort();
|
||||
|
||||
var xhr = this.xhr || new XMLHttpRequest();
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === xhr.HEADERS_RECEIVED) {
|
||||
var contentSize = parseInt(xhr.getResponseHeader('Content-Length'), 10);
|
||||
this.setState({
|
||||
contentSize: contentSize,
|
||||
downloaded: 0,
|
||||
});
|
||||
} else if (xhr.readyState === xhr.LOADING) {
|
||||
this.setState({
|
||||
downloaded: xhr.responseText.length,
|
||||
});
|
||||
console.log(xhr.responseText.length);
|
||||
} else if (xhr.readyState === xhr.DONE) {
|
||||
if (this.cancelled) {
|
||||
this.cancelled = false;
|
||||
return;
|
||||
}
|
||||
if (xhr.status === 200) {
|
||||
this.setState({
|
||||
status: 'Download complete!',
|
||||
});
|
||||
} else if (xhr.status !== 0) {
|
||||
this.setState({
|
||||
status: 'Error: Server returned HTTP status of ' + xhr.status + ' ' + xhr.responseText,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
status: 'Error: ' + xhr.responseText,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.open('GET', 'http://www.gutenberg.org/cache/epub/100/pg100.txt');
|
||||
xhr.send();
|
||||
this.xhr = xhr;
|
||||
|
||||
this.setState({status: 'Downloading...'});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.cancelled = true;
|
||||
this.xhr && this.xhr.abort();
|
||||
}
|
||||
|
||||
render() {
|
||||
var button = this.state.status === 'Downloading...' ? (
|
||||
<View style={styles.wrapper}>
|
||||
<View style={styles.button}>
|
||||
<Text>...</Text>
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={this.download.bind(this)}>
|
||||
<View style={styles.button}>
|
||||
<Text>Download 5MB Text File</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
|
||||
return (
|
||||
<View>
|
||||
{button}
|
||||
<Text>{this.state.status}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FormUploader extends React.Component {
|
||||
|
||||
_isMounted: boolean;
|
||||
_addTextParam: () => void;
|
||||
_upload: () => void;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isUploading: false,
|
||||
uploadProgress: null,
|
||||
textParams: [],
|
||||
};
|
||||
this._isMounted = true;
|
||||
this._addTextParam = this._addTextParam.bind(this);
|
||||
this._upload = this._upload.bind(this);
|
||||
}
|
||||
|
||||
_addTextParam() {
|
||||
var textParams = this.state.textParams;
|
||||
textParams.push({name: '', value: ''});
|
||||
this.setState({textParams});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
_onTextParamNameChange(index, text) {
|
||||
var textParams = this.state.textParams;
|
||||
textParams[index].name = text;
|
||||
this.setState({textParams});
|
||||
}
|
||||
|
||||
_onTextParamValueChange(index, text) {
|
||||
var textParams = this.state.textParams;
|
||||
textParams[index].value = text;
|
||||
this.setState({textParams});
|
||||
}
|
||||
|
||||
_upload() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', 'http://posttestserver.com/post.php');
|
||||
xhr.onload = () => {
|
||||
this.setState({isUploading: false});
|
||||
if (xhr.status !== 200) {
|
||||
console.log(
|
||||
'Upload failed',
|
||||
'Expected HTTP 200 OK response, got ' + xhr.status
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!xhr.responseText) {
|
||||
console.log(
|
||||
'Upload failed',
|
||||
'No response payload.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
var index = xhr.responseText.indexOf('http://www.posttestserver.com/');
|
||||
if (index === -1) {
|
||||
console.log(
|
||||
'Upload failed',
|
||||
'Invalid response payload.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
var url = xhr.responseText.slice(index).split('\n')[0];
|
||||
console.log('Upload successful: ' + url);
|
||||
};
|
||||
var formdata = new FormData();
|
||||
this.state.textParams.forEach(
|
||||
(param) => formdata.append(param.name, param.value)
|
||||
);
|
||||
if (xhr.upload) {
|
||||
xhr.upload.onprogress = (event) => {
|
||||
console.log('upload onprogress', event);
|
||||
if (event.lengthComputable) {
|
||||
this.setState({uploadProgress: event.loaded / event.total});
|
||||
}
|
||||
};
|
||||
}
|
||||
xhr.send(formdata);
|
||||
this.setState({isUploading: true});
|
||||
}
|
||||
|
||||
render() {
|
||||
var textItems = this.state.textParams.map((item, index) => (
|
||||
<View style={styles.paramRow}>
|
||||
<TextInput
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
onChangeText={this._onTextParamNameChange.bind(this, index)}
|
||||
placeholder="name..."
|
||||
style={styles.textInput}
|
||||
/>
|
||||
<Text style={styles.equalSign}>=</Text>
|
||||
<TextInput
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
onChangeText={this._onTextParamValueChange.bind(this, index)}
|
||||
placeholder="value..."
|
||||
style={styles.textInput}
|
||||
/>
|
||||
</View>
|
||||
));
|
||||
var uploadButtonLabel = this.state.isUploading ? 'Uploading...' : 'Upload';
|
||||
var uploadProgress = this.state.uploadProgress;
|
||||
if (uploadProgress !== null) {
|
||||
uploadButtonLabel += ' ' + Math.round(uploadProgress * 100) + '%';
|
||||
}
|
||||
var uploadButton = (
|
||||
<View style={styles.uploadButtonBox}>
|
||||
<Text style={styles.uploadButtonLabel}>{uploadButtonLabel}</Text>
|
||||
</View>
|
||||
);
|
||||
if (!this.state.isUploading) {
|
||||
uploadButton = (
|
||||
<TouchableHighlight onPress={this._upload}>
|
||||
{uploadButton}
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View>
|
||||
{textItems}
|
||||
<View>
|
||||
<Text
|
||||
style={[styles.textButton, styles.addTextParamButton]}
|
||||
onPress={this._addTextParam}>
|
||||
Add a text param
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.uploadButton}>
|
||||
{uploadButton}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
exports.framework = 'React';
|
||||
exports.title = 'XMLHttpRequest';
|
||||
exports.description = 'XMLHttpRequest';
|
||||
exports.examples = [{
|
||||
title: 'File Download',
|
||||
render() {
|
||||
return <Downloader/>;
|
||||
}
|
||||
}, {
|
||||
title: 'multipart/form-data Upload',
|
||||
render() {
|
||||
return <FormUploader/>;
|
||||
}
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
borderRadius: 5,
|
||||
marginBottom: 5,
|
||||
},
|
||||
button: {
|
||||
backgroundColor: '#eeeeee',
|
||||
padding: 8,
|
||||
},
|
||||
paramRow: {
|
||||
flexDirection: 'row',
|
||||
paddingVertical: 8,
|
||||
alignItems: 'center',
|
||||
borderBottomWidth: 1 / PixelRatio.get(),
|
||||
borderBottomColor: 'grey',
|
||||
},
|
||||
textButton: {
|
||||
color: 'blue',
|
||||
},
|
||||
addTextParamButton: {
|
||||
marginTop: 8,
|
||||
},
|
||||
textInput: {
|
||||
flex: 1,
|
||||
borderRadius: 3,
|
||||
borderColor: 'grey',
|
||||
borderWidth: 1,
|
||||
height: 30,
|
||||
paddingLeft: 8,
|
||||
},
|
||||
equalSign: {
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
uploadButton: {
|
||||
marginTop: 16,
|
||||
},
|
||||
uploadButtonBox: {
|
||||
flex: 1,
|
||||
paddingVertical: 12,
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'blue',
|
||||
borderRadius: 4,
|
||||
},
|
||||
uploadButtonLabel: {
|
||||
color: 'white',
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.facebook.react.uiapp"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:23.0.0'
|
||||
|
||||
// Depend on pre-built React Native
|
||||
compile 'com.facebook.react:react-native:0.11.+'
|
||||
|
||||
// Depend on React Native source.
|
||||
// This is useful for testing your changes when working on React Native.
|
||||
// compile project(':ReactAndroid')
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.facebook.react.uiapp" >
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/launcher_icon"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.ReactNative.AppCompat.Light" >
|
||||
<activity
|
||||
android:name=".UIExplorerActivity"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.uiapp;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.facebook.react.LifecycleState;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
|
||||
public class UIExplorerActivity extends Activity implements DefaultHardwareBackBtnHandler {
|
||||
|
||||
private ReactInstanceManager mReactInstanceManager;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
mReactInstanceManager = ReactInstanceManager.builder()
|
||||
.setApplication(getApplication())
|
||||
.setBundleAssetName("UIExplorerApp.android.bundle")
|
||||
.setJSMainModuleName("Examples/UIExplorer/UIExplorerApp.android")
|
||||
.addPackage(new MainReactPackage())
|
||||
.setUseDeveloperSupport(true)
|
||||
.setInitialLifecycleState(LifecycleState.RESUMED)
|
||||
.build();
|
||||
|
||||
((ReactRootView) findViewById(R.id.react_root_view))
|
||||
.startReactApplication(mReactInstanceManager, "UIExplorerApp", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
|
||||
mReactInstanceManager.showDevOptionsDialog();
|
||||
return true;
|
||||
}
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onPause();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onResume(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onBackPressed();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDefaultOnBackPressed() {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
Двоичные данные
Examples/UIExplorer/android/app/src/main/res/drawable/ic_create_black_48dp.png
Normal file
После Ширина: | Высота: | Размер: 406 B |
Двоичные данные
Examples/UIExplorer/android/app/src/main/res/drawable/ic_menu_black_24dp.png
Normal file
После Ширина: | Высота: | Размер: 179 B |
Двоичные данные
Examples/UIExplorer/android/app/src/main/res/drawable/ic_settings_black_48dp.png
Normal file
После Ширина: | Высота: | Размер: 1.2 KiB |
После Ширина: | Высота: | Размер: 9.4 KiB |
Двоичные данные
Examples/UIExplorer/android/app/src/main/res/drawable/uie_comment_highlighted.png
Normal file
После Ширина: | Высота: | Размер: 403 B |
Двоичные данные
Examples/UIExplorer/android/app/src/main/res/drawable/uie_comment_normal.png
Normal file
После Ширина: | Высота: | Размер: 420 B |
Двоичные данные
Examples/UIExplorer/android/app/src/main/res/drawable/uie_thumb_normal.png
Normal file
После Ширина: | Высота: | Размер: 850 B |
Двоичные данные
Examples/UIExplorer/android/app/src/main/res/drawable/uie_thumb_selected.png
Normal file
После Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -0,0 +1,12 @@
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".UIExplorerApp">
|
||||
|
||||
<com.facebook.react.ReactRootView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/react_root_view"/>
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">UIExplorer App</string>
|
||||
</resources>
|
|
@ -0,0 +1,8 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -16,11 +16,11 @@ var warning = require('warning');
|
|||
class AppStateIOS {
|
||||
|
||||
static addEventListener(type, handler) {
|
||||
warning('Cannot listen to AppStateIOS events on Android.');
|
||||
warning(false, 'Cannot listen to AppStateIOS events on Android.');
|
||||
}
|
||||
|
||||
static removeEventListener(type, handler) {
|
||||
warning('Cannot remove AppStateIOS listener on Android.');
|
||||
warning(false, 'Cannot remove AppStateIOS listener on Android.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* 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 ActivityIndicatorIOS
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
var View = require('View');
|
||||
|
||||
var ActivityIndicatorIOS = React.createClass({
|
||||
render(): ReactElement {
|
||||
return <View {...this.props} />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = ActivityIndicatorIOS;
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* 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 DatePickerIOS
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var Text = require('Text');
|
||||
var View = require('View');
|
||||
|
||||
var DummyDatePickerIOS = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<View style={[styles.dummyDatePickerIOS, this.props.style]}>
|
||||
<Text style={styles.datePickerText}>DatePickerIOS is not supported on this platform!</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
dummyDatePickerIOS: {
|
||||
height: 100,
|
||||
width: 300,
|
||||
backgroundColor: '#ffbcbc',
|
||||
borderWidth: 1,
|
||||
borderColor: 'red',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
datePickerText: {
|
||||
color: '#333333',
|
||||
margin: 20,
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = DummyDatePickerIOS;
|
|
@ -0,0 +1,224 @@
|
|||
/**
|
||||
* 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 DrawerLayoutAndroid
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var DrawerConsts = require('NativeModules').UIManager.AndroidDrawerLayout.Constants;
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var React = require('React');
|
||||
var ReactPropTypes = require('ReactPropTypes');
|
||||
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
||||
var RCTUIManager = require('NativeModules').UIManager;
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var View = require('View');
|
||||
|
||||
var createReactNativeComponentClass = require('createReactNativeComponentClass');
|
||||
var dismissKeyboard = require('dismissKeyboard');
|
||||
var merge = require('merge');
|
||||
|
||||
var RK_DRAWER_REF = 'drawerlayout';
|
||||
var INNERVIEW_REF = 'innerView';
|
||||
|
||||
var DrawerLayoutValidAttributes = {
|
||||
drawerWidth: true,
|
||||
drawerPosition: true,
|
||||
};
|
||||
|
||||
var DRAWER_STATES = [
|
||||
'Idle',
|
||||
'Dragging',
|
||||
'Settling',
|
||||
];
|
||||
|
||||
/**
|
||||
* React component that wraps the platform `DrawerLayout` (Android only). The
|
||||
* Drawer (typically used for navigation) is rendered with `renderNavigationView`
|
||||
* and direct children are the main view (where your content goes). The navigation
|
||||
* view is initially not visible on the screen, but can be pulled in from the
|
||||
* side of the window specified by the `drawerPosition` prop and its width can
|
||||
* be set by the `drawerWidth` prop.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* render: function() {
|
||||
* var navigationView = (
|
||||
* <Text style={{margin: 10, fontSize: 15, textAlign: 'left'}}>I'm in the Drawer!</Text>
|
||||
* );
|
||||
* return (
|
||||
* <DrawerLayoutAndroid
|
||||
* drawerWidth={300}
|
||||
* drawerPosition={DrawerLayoutAndroid.positions.Left}
|
||||
* renderNavigationView={() => navigationView}>
|
||||
* <Text style={{10, fontSize: 15, textAlign: 'right'}}>Hello</Text>
|
||||
* <Text style={{10, fontSize: 15, textAlign: 'right'}}>World!</Text>
|
||||
* </DrawerLayoutAndroid>
|
||||
* );
|
||||
* },
|
||||
* ```
|
||||
*/
|
||||
var DrawerLayoutAndroid = React.createClass({
|
||||
statics: {
|
||||
positions: DrawerConsts.DrawerPosition,
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
/**
|
||||
* Determines whether the keyboard gets dismissed in response to a drag.
|
||||
* - 'none' (the default), drags do not dismiss the keyboard.
|
||||
* - 'on-drag', the keyboard is dismissed when a drag begins.
|
||||
*/
|
||||
keyboardDismissMode: ReactPropTypes.oneOf([
|
||||
'none', // default
|
||||
'on-drag',
|
||||
]),
|
||||
/**
|
||||
* Specifies the side of the screen from which the drawer will slide in.
|
||||
*/
|
||||
drawerPosition: ReactPropTypes.oneOf([
|
||||
DrawerConsts.DrawerPosition.Left,
|
||||
DrawerConsts.DrawerPosition.Right
|
||||
]),
|
||||
/**
|
||||
* Specifies the width of the drawer, more precisely the width of the view that be pulled in
|
||||
* from the edge of the window.
|
||||
*/
|
||||
drawerWidth: ReactPropTypes.number,
|
||||
/**
|
||||
* Function called whenever there is an interaction with the navigation view.
|
||||
*/
|
||||
onDrawerSlide: ReactPropTypes.func,
|
||||
/**
|
||||
* Function called when the drawer state has changed. The drawer can be in 3 states:
|
||||
* - idle, meaning there is no interaction with the navigation view happening at the time
|
||||
* - dragging, meaning there is currently an interation with the navigation view
|
||||
* - settling, meaning that there was an interaction with the navigation view, and the
|
||||
* navigation view is now finishing it's closing or opening animation
|
||||
*/
|
||||
onDrawerStateChanged: ReactPropTypes.func,
|
||||
/**
|
||||
* Function called whenever the navigation view has been opened.
|
||||
*/
|
||||
onDrawerOpen: ReactPropTypes.func,
|
||||
/**
|
||||
* Function called whenever the navigation view has been closed.
|
||||
*/
|
||||
onDrawerClose: ReactPropTypes.func,
|
||||
/**
|
||||
* The navigation view that will be rendered to the side of the screen and can be pulled in.
|
||||
*/
|
||||
renderNavigationView: ReactPropTypes.func.isRequired,
|
||||
},
|
||||
|
||||
mixins: [NativeMethodsMixin],
|
||||
|
||||
getInnerViewNode: function() {
|
||||
return this.refs[INNERVIEW_REF].getInnerViewNode();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var drawerViewWrapper =
|
||||
<View style={[styles.drawerSubview, {width: this.props.drawerWidth}]} collapsable={false}>
|
||||
{this.props.renderNavigationView()}
|
||||
</View>;
|
||||
var childrenWrapper =
|
||||
<View ref={INNERVIEW_REF} style={styles.mainSubview} collapsable={false}>
|
||||
{this.props.children}
|
||||
</View>;
|
||||
return (
|
||||
<AndroidDrawerLayout
|
||||
{...this.props}
|
||||
ref={RK_DRAWER_REF}
|
||||
drawerWidth={this.props.drawerWidth}
|
||||
drawerPosition={this.props.drawerPosition}
|
||||
style={styles.base}
|
||||
onDrawerSlide={this._onDrawerSlide}
|
||||
onDrawerOpen={this._onDrawerOpen}
|
||||
onDrawerClose={this._onDrawerClose}
|
||||
onDrawerStateChanged={this._onDrawerStateChanged}>
|
||||
{childrenWrapper}
|
||||
{drawerViewWrapper}
|
||||
</AndroidDrawerLayout>
|
||||
);
|
||||
},
|
||||
|
||||
_onDrawerSlide: function(event) {
|
||||
if (this.props.onDrawerSlide) {
|
||||
this.props.onDrawerSlide(event);
|
||||
}
|
||||
if (this.props.keyboardDismissMode === 'on-drag') {
|
||||
dismissKeyboard();
|
||||
}
|
||||
},
|
||||
|
||||
_onDrawerOpen: function() {
|
||||
if (this.props.onDrawerOpen) {
|
||||
this.props.onDrawerOpen();
|
||||
}
|
||||
},
|
||||
|
||||
_onDrawerClose: function() {
|
||||
if (this.props.onDrawerClose) {
|
||||
this.props.onDrawerClose();
|
||||
}
|
||||
},
|
||||
|
||||
_onDrawerStateChanged: function(event) {
|
||||
if (this.props.onDrawerStateChanged) {
|
||||
this.props.onDrawerStateChanged(DRAWER_STATES[event.nativeEvent.drawerState]);
|
||||
}
|
||||
},
|
||||
|
||||
openDrawer: function() {
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
this._getDrawerLayoutHandle(),
|
||||
RCTUIManager.AndroidDrawerLayout.Commands.openDrawer,
|
||||
null
|
||||
);
|
||||
},
|
||||
|
||||
closeDrawer: function() {
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
this._getDrawerLayoutHandle(),
|
||||
RCTUIManager.AndroidDrawerLayout.Commands.closeDrawer,
|
||||
null
|
||||
);
|
||||
},
|
||||
|
||||
_getDrawerLayoutHandle: function() {
|
||||
return React.findNodeHandle(this.refs[RK_DRAWER_REF]);
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
base: {
|
||||
flex: 1,
|
||||
},
|
||||
mainSubview: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
drawerSubview: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
// The View that contains both the actual drawer and the main view
|
||||
var AndroidDrawerLayout = createReactNativeComponentClass({
|
||||
validAttributes: merge(ReactNativeViewAttributes.UIView, DrawerLayoutValidAttributes),
|
||||
uiViewClassName: 'AndroidDrawerLayout',
|
||||
});
|
||||
|
||||
module.exports = DrawerLayoutAndroid;
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* 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 NavigatorIOS
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
module.exports = require('UnimplementedView');
|
|
@ -0,0 +1,217 @@
|
|||
/**
|
||||
* 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 NavigatorBreadcrumbNavigationBarStyles
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Dimensions = require('Dimensions');
|
||||
var NavigatorNavigationBarStyles = require('NavigatorNavigationBarStyles');
|
||||
|
||||
var buildStyleInterpolator = require('buildStyleInterpolator');
|
||||
var merge = require('merge');
|
||||
|
||||
var SCREEN_WIDTH = Dimensions.get('window').width;
|
||||
var NAV_BAR_HEIGHT = NavigatorNavigationBarStyles.General.NavBarHeight;
|
||||
|
||||
var SPACING = 8;
|
||||
var ICON_WIDTH = 40;
|
||||
var SEPARATOR_WIDTH = 9;
|
||||
var CRUMB_WIDTH = ICON_WIDTH + SEPARATOR_WIDTH;
|
||||
var NAV_ELEMENT_HEIGHT = NAV_BAR_HEIGHT;
|
||||
|
||||
var OPACITY_RATIO = 100;
|
||||
var ICON_INACTIVE_OPACITY = 0.6;
|
||||
var MAX_BREADCRUMBS = 10;
|
||||
|
||||
var CRUMB_BASE = {
|
||||
position: 'absolute',
|
||||
flexDirection: 'row',
|
||||
top: 0,
|
||||
width: CRUMB_WIDTH,
|
||||
height: NAV_ELEMENT_HEIGHT,
|
||||
backgroundColor: 'transparent',
|
||||
};
|
||||
|
||||
var ICON_BASE = {
|
||||
width: ICON_WIDTH,
|
||||
height: NAV_ELEMENT_HEIGHT,
|
||||
};
|
||||
|
||||
var SEPARATOR_BASE = {
|
||||
width: SEPARATOR_WIDTH,
|
||||
height: NAV_ELEMENT_HEIGHT,
|
||||
};
|
||||
|
||||
var TITLE_BASE = {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
height: NAV_ELEMENT_HEIGHT,
|
||||
backgroundColor: 'transparent',
|
||||
alignItems: 'flex-start',
|
||||
};
|
||||
|
||||
var FIRST_TITLE_BASE = merge(TITLE_BASE, {
|
||||
left: 0,
|
||||
right: 0,
|
||||
});
|
||||
|
||||
var RIGHT_BUTTON_BASE = {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
overflow: 'hidden',
|
||||
opacity: 1,
|
||||
height: NAV_ELEMENT_HEIGHT,
|
||||
backgroundColor: 'transparent',
|
||||
};
|
||||
|
||||
/**
|
||||
* Precompute crumb styles so that they don't need to be recomputed on every
|
||||
* interaction.
|
||||
*/
|
||||
var LEFT = [];
|
||||
var CENTER = [];
|
||||
var RIGHT = [];
|
||||
for (var i = 0; i < MAX_BREADCRUMBS; i++) {
|
||||
var crumbLeft = CRUMB_WIDTH * i + SPACING;
|
||||
LEFT[i] = {
|
||||
Crumb: merge(CRUMB_BASE, { left: crumbLeft }),
|
||||
Icon: merge(ICON_BASE, { opacity: ICON_INACTIVE_OPACITY }),
|
||||
Separator: merge(SEPARATOR_BASE, { opacity: 1 }),
|
||||
Title: merge(TITLE_BASE, { left: crumbLeft, opacity: 0 }),
|
||||
RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }),
|
||||
};
|
||||
CENTER[i] = {
|
||||
Crumb: merge(CRUMB_BASE, { left: crumbLeft }),
|
||||
Icon: merge(ICON_BASE, { opacity: 1 }),
|
||||
Separator: merge(SEPARATOR_BASE, { opacity: 0 }),
|
||||
Title: merge(TITLE_BASE, {
|
||||
left: crumbLeft + ICON_WIDTH,
|
||||
opacity: 1,
|
||||
}),
|
||||
RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 1 }),
|
||||
};
|
||||
var crumbRight = crumbLeft + 50;
|
||||
RIGHT[i] = {
|
||||
Crumb: merge(CRUMB_BASE, { left: crumbRight}),
|
||||
Icon: merge(ICON_BASE, { opacity: 0 }),
|
||||
Separator: merge(SEPARATOR_BASE, { opacity: 0 }),
|
||||
Title: merge(TITLE_BASE, {
|
||||
left: crumbRight + ICON_WIDTH,
|
||||
opacity: 0,
|
||||
}),
|
||||
RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }),
|
||||
};
|
||||
}
|
||||
|
||||
// Special case the CENTER state of the first scene.
|
||||
CENTER[0] = {
|
||||
Crumb: merge(CRUMB_BASE, {left: SPACING + CRUMB_WIDTH}),
|
||||
Icon: merge(ICON_BASE, {opacity: 0}),
|
||||
Separator: merge(SEPARATOR_BASE, {opacity: 0}),
|
||||
Title: merge(FIRST_TITLE_BASE, {opacity: 1}),
|
||||
RightItem: CENTER[0].RightItem,
|
||||
};
|
||||
LEFT[0].Title = merge(FIRST_TITLE_BASE, {opacity: 0});
|
||||
RIGHT[0].Title = merge(FIRST_TITLE_BASE, {opacity: 0});
|
||||
|
||||
|
||||
var buildIndexSceneInterpolator = function(startStyles, endStyles) {
|
||||
return {
|
||||
Crumb: buildStyleInterpolator({
|
||||
translateX: {
|
||||
type: 'linear',
|
||||
from: 0,
|
||||
to: endStyles.Crumb.left - startStyles.Crumb.left,
|
||||
min: 0,
|
||||
max: 1,
|
||||
extrapolate: true,
|
||||
},
|
||||
left: {
|
||||
value: startStyles.Crumb.left,
|
||||
type: 'constant'
|
||||
},
|
||||
}),
|
||||
Icon: buildStyleInterpolator({
|
||||
opacity: {
|
||||
type: 'linear',
|
||||
from: startStyles.Icon.opacity,
|
||||
to: endStyles.Icon.opacity,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
}),
|
||||
Separator: buildStyleInterpolator({
|
||||
opacity: {
|
||||
type: 'linear',
|
||||
from: startStyles.Separator.opacity,
|
||||
to: endStyles.Separator.opacity,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
}),
|
||||
Title: buildStyleInterpolator({
|
||||
opacity: {
|
||||
type: 'linear',
|
||||
from: startStyles.Title.opacity,
|
||||
to: endStyles.Title.opacity,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
translateX: {
|
||||
type: 'linear',
|
||||
from: 0,
|
||||
to: endStyles.Title.left - startStyles.Title.left,
|
||||
min: 0,
|
||||
max: 1,
|
||||
extrapolate: true,
|
||||
},
|
||||
left: {
|
||||
value: startStyles.Title.left,
|
||||
type: 'constant'
|
||||
},
|
||||
}),
|
||||
RightItem: buildStyleInterpolator({
|
||||
opacity: {
|
||||
type: 'linear',
|
||||
from: startStyles.RightItem.opacity,
|
||||
to: endStyles.RightItem.opacity,
|
||||
min: 0,
|
||||
max: 1,
|
||||
round: OPACITY_RATIO,
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
var Interpolators = CENTER.map(function(_, ii) {
|
||||
return {
|
||||
// Animating *into* the center stage from the right
|
||||
RightToCenter: buildIndexSceneInterpolator(RIGHT[ii], CENTER[ii]),
|
||||
// Animating out of the center stage, to the left
|
||||
CenterToLeft: buildIndexSceneInterpolator(CENTER[ii], LEFT[ii]),
|
||||
// Both stages (animating *past* the center stage)
|
||||
RightToLeft: buildIndexSceneInterpolator(RIGHT[ii], LEFT[ii]),
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Contains constants that are used in constructing both `StyleSheet`s and
|
||||
* inline styles during transitions.
|
||||
*/
|
||||
module.exports = {
|
||||
Interpolators,
|
||||
Left: LEFT,
|
||||
Center: CENTER,
|
||||
Right: RIGHT,
|
||||
IconWidth: ICON_WIDTH,
|
||||
IconHeight: NAV_BAR_HEIGHT,
|
||||
SeparatorWidth: SEPARATOR_WIDTH,
|
||||
SeparatorHeight: NAV_BAR_HEIGHT,
|
||||
};
|
|
@ -0,0 +1,159 @@
|
|||
/**
|
||||
* 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 NavigatorNavigationBarStyles
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var buildStyleInterpolator = require('buildStyleInterpolator');
|
||||
var merge = require('merge');
|
||||
|
||||
// Android Material Design
|
||||
var NAV_BAR_HEIGHT = 56;
|
||||
var TITLE_LEFT = 72;
|
||||
var BUTTON_SIZE = 24;
|
||||
var TOUCH_TARGT_SIZE = 48;
|
||||
var BUTTON_HORIZONTAL_MARGIN = 16;
|
||||
|
||||
var BUTTON_EFFECTIVE_MARGIN = BUTTON_HORIZONTAL_MARGIN - (TOUCH_TARGT_SIZE - BUTTON_SIZE) / 2;
|
||||
var NAV_ELEMENT_HEIGHT = NAV_BAR_HEIGHT;
|
||||
|
||||
var BASE_STYLES = {
|
||||
Title: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
alignItems: 'flex-start',
|
||||
height: NAV_ELEMENT_HEIGHT,
|
||||
backgroundColor: 'transparent',
|
||||
marginLeft: TITLE_LEFT,
|
||||
},
|
||||
LeftButton: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: BUTTON_EFFECTIVE_MARGIN,
|
||||
overflow: 'hidden',
|
||||
height: NAV_ELEMENT_HEIGHT,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
RightButton: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: BUTTON_EFFECTIVE_MARGIN,
|
||||
overflow: 'hidden',
|
||||
alignItems: 'flex-end',
|
||||
height: NAV_ELEMENT_HEIGHT,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
};
|
||||
|
||||
// There are 3 stages: left, center, right. All previous navigation
|
||||
// items are in the left stage. The current navigation item is in the
|
||||
// center stage. All upcoming navigation items are in the right stage.
|
||||
// Another way to think of the stages is in terms of transitions. When
|
||||
// we move forward in the navigation stack, we perform a
|
||||
// right-to-center transition on the new navigation item and a
|
||||
// center-to-left transition on the current navigation item.
|
||||
var Stages = {
|
||||
Left: {
|
||||
Title: merge(BASE_STYLES.Title, { opacity: 0 }),
|
||||
LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 0 }),
|
||||
RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }),
|
||||
},
|
||||
Center: {
|
||||
Title: merge(BASE_STYLES.Title, { opacity: 1 }),
|
||||
LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 1 }),
|
||||
RightButton: merge(BASE_STYLES.RightButton, { opacity: 1 }),
|
||||
},
|
||||
Right: {
|
||||
Title: merge(BASE_STYLES.Title, { opacity: 0 }),
|
||||
LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 0 }),
|
||||
RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
var opacityRatio = 100;
|
||||
|
||||
function buildSceneInterpolators(startStyles, endStyles) {
|
||||
return {
|
||||
Title: buildStyleInterpolator({
|
||||
opacity: {
|
||||
type: 'linear',
|
||||
from: startStyles.Title.opacity,
|
||||
to: endStyles.Title.opacity,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
left: {
|
||||
type: 'linear',
|
||||
from: startStyles.Title.left,
|
||||
to: endStyles.Title.left,
|
||||
min: 0,
|
||||
max: 1,
|
||||
extrapolate: true,
|
||||
},
|
||||
}),
|
||||
LeftButton: buildStyleInterpolator({
|
||||
opacity: {
|
||||
type: 'linear',
|
||||
from: startStyles.LeftButton.opacity,
|
||||
to: endStyles.LeftButton.opacity,
|
||||
min: 0,
|
||||
max: 1,
|
||||
round: opacityRatio,
|
||||
},
|
||||
left: {
|
||||
type: 'linear',
|
||||
from: startStyles.LeftButton.left,
|
||||
to: endStyles.LeftButton.left,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
}),
|
||||
RightButton: buildStyleInterpolator({
|
||||
opacity: {
|
||||
type: 'linear',
|
||||
from: startStyles.RightButton.opacity,
|
||||
to: endStyles.RightButton.opacity,
|
||||
min: 0,
|
||||
max: 1,
|
||||
round: opacityRatio,
|
||||
},
|
||||
left: {
|
||||
type: 'linear',
|
||||
from: startStyles.RightButton.left,
|
||||
to: endStyles.RightButton.left,
|
||||
min: 0,
|
||||
max: 1,
|
||||
extrapolate: true,
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
var Interpolators = {
|
||||
// Animating *into* the center stage from the right
|
||||
RightToCenter: buildSceneInterpolators(Stages.Right, Stages.Center),
|
||||
// Animating out of the center stage, to the left
|
||||
CenterToLeft: buildSceneInterpolators(Stages.Center, Stages.Left),
|
||||
// Both stages (animating *past* the center stage)
|
||||
RightToLeft: buildSceneInterpolators(Stages.Right, Stages.Left),
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
General: {
|
||||
NavBarHeight: NAV_BAR_HEIGHT,
|
||||
StatusBarHeight: 0,
|
||||
TotalNavHeight: NAV_BAR_HEIGHT,
|
||||
},
|
||||
Interpolators,
|
||||
Stages,
|
||||
};
|
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* 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 ProgressBarAndroid
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var React = require('React');
|
||||
var ReactPropTypes = require('ReactPropTypes');
|
||||
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
||||
|
||||
var createReactNativeComponentClass = require('createReactNativeComponentClass');
|
||||
|
||||
var STYLE_ATTRIBUTES = [
|
||||
'Horizontal',
|
||||
'Small',
|
||||
'Large',
|
||||
'Inverse',
|
||||
'SmallInverse',
|
||||
'LargeInverse'
|
||||
];
|
||||
|
||||
/**
|
||||
* React component that wraps the Android-only `ProgressBar`. This component is used to indicate
|
||||
* that the app is loading or there is some activity in the app.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* render: function() {
|
||||
* var progressBar =
|
||||
* <View style={styles.container}>
|
||||
* <ProgressBar styleAttr="Inverse" />
|
||||
* </View>;
|
||||
|
||||
* return (
|
||||
* <MyLoadingComponent
|
||||
* componentView={componentView}
|
||||
* loadingView={progressBar}
|
||||
* style={styles.loadingComponent}
|
||||
* />
|
||||
* );
|
||||
* },
|
||||
* ```
|
||||
*/
|
||||
var ProgressBarAndroid = React.createClass({
|
||||
propTypes: {
|
||||
/**
|
||||
* Style of the ProgressBar. One of:
|
||||
*
|
||||
* - Horizontal
|
||||
* - Small
|
||||
* - Large
|
||||
* - Inverse
|
||||
* - SmallInverse
|
||||
* - LargeInverse
|
||||
*/
|
||||
styleAttr: ReactPropTypes.oneOf(STYLE_ATTRIBUTES),
|
||||
/**
|
||||
* Used to locate this view in end-to-end tests.
|
||||
*/
|
||||
testID: ReactPropTypes.string,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
styleAttr: 'Large',
|
||||
};
|
||||
},
|
||||
|
||||
mixins: [NativeMethodsMixin],
|
||||
|
||||
render: function() {
|
||||
return <AndroidProgressBar {...this.props} />;
|
||||
},
|
||||
});
|
||||
|
||||
var AndroidProgressBar = createReactNativeComponentClass({
|
||||
validAttributes: {
|
||||
...ReactNativeViewAttributes.UIView,
|
||||
styleAttr: true,
|
||||
},
|
||||
uiViewClassName: 'AndroidProgressBar',
|
||||
});
|
||||
|
||||
module.exports = ProgressBarAndroid;
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule SliderIOS
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = require('UnimplementedView');
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule StatusBarIOS
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
module.exports = null;
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* 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 SwitchAndroid
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var PropTypes = require('ReactPropTypes');
|
||||
var React = require('React');
|
||||
|
||||
var requireNativeComponent = require('requireNativeComponent');
|
||||
|
||||
var SWITCH = 'switch';
|
||||
|
||||
/**
|
||||
* Standard Android two-state toggle component
|
||||
*/
|
||||
var SwitchAndroid = React.createClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
|
||||
propTypes: {
|
||||
/**
|
||||
* Boolean value of the switch.
|
||||
*/
|
||||
value: PropTypes.bool,
|
||||
/**
|
||||
* If `true`, this component can't be interacted with.
|
||||
*/
|
||||
disabled: PropTypes.bool,
|
||||
/**
|
||||
* Invoked with the new value when the value chages.
|
||||
*/
|
||||
onValueChange: PropTypes.func,
|
||||
/**
|
||||
* Used to locate this view in end-to-end tests.
|
||||
*/
|
||||
testID: PropTypes.string,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
value: false,
|
||||
disabled: false,
|
||||
};
|
||||
},
|
||||
|
||||
_onChange: function(event) {
|
||||
this.props.onChange && this.props.onChange(event);
|
||||
this.props.onValueChange && this.props.onValueChange(event.nativeEvent.value);
|
||||
|
||||
// The underlying switch might have changed, but we're controlled,
|
||||
// and so want to ensure it represents our value.
|
||||
this.refs[SWITCH].setNativeProps({on: this.props.value});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<RKSwitch
|
||||
ref={SWITCH}
|
||||
style={this.props.style}
|
||||
enabled={!this.props.disabled}
|
||||
on={this.props.value}
|
||||
onChange={this._onChange}
|
||||
testID={this.props.testID}
|
||||
onStartShouldSetResponder={() => true}
|
||||
onResponderTerminationRequest={() => false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var RKSwitch = requireNativeComponent('AndroidSwitch', null);
|
||||
|
||||
module.exports = SwitchAndroid;
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ToastAndroid
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var RCTToastAndroid = require('NativeModules').ToastAndroid;
|
||||
|
||||
/**
|
||||
* This exposes the native ToastAndroid module as a JS module. This has a function 'showText'
|
||||
* which takes the following parameters:
|
||||
*
|
||||
* 1. String message: A string with the text to toast
|
||||
* 2. int duration: The duration of the toast. May be ToastAndroid.SHORT or ToastAndroid.LONG
|
||||
*/
|
||||
|
||||
var ToastAndroid = {
|
||||
|
||||
SHORT: RCTToastAndroid.SHORT,
|
||||
LONG: RCTToastAndroid.LONG,
|
||||
|
||||
show: function (
|
||||
message: string,
|
||||
duration: number
|
||||
): void {
|
||||
RCTToastAndroid.show(message, duration);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = ToastAndroid;
|
|
@ -0,0 +1,174 @@
|
|||
/**
|
||||
* 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 ToolbarAndroid
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var Image = require('Image');
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var React = require('React');
|
||||
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
||||
var ReactPropTypes = require('ReactPropTypes');
|
||||
|
||||
var createReactNativeComponentClass = require('createReactNativeComponentClass');
|
||||
|
||||
/**
|
||||
* React component that wraps the Android-only [`Toolbar` widget][0]. A Toolbar can display a logo,
|
||||
* navigation icon (e.g. hamburger menu), a title & subtitle and a list of actions. The title and
|
||||
* subtitle are expanded so the logo and navigation icons are displayed on the left, title and
|
||||
* subtitle in the middle and the actions on the right.
|
||||
*
|
||||
* If the toolbar has an only child, it will be displayed between the title and actions.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* render: function() {
|
||||
* return (
|
||||
* <ToolbarAndroid
|
||||
* logo={require('image!app_logo')}
|
||||
* title="AwesomeApp"
|
||||
* actions={[{title: 'Settings', icon: require('image!icon_settings'), show: 'always'}]}
|
||||
* onActionSelected={this.onActionSelected} />
|
||||
* )
|
||||
* },
|
||||
* onActionSelected: function(position) {
|
||||
* if (position === 0) { // index of 'Settings'
|
||||
* showSettings();
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* [0]: https://developer.android.com/reference/android/support/v7/widget/Toolbar.html
|
||||
*/
|
||||
var ToolbarAndroid = React.createClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
|
||||
propTypes: {
|
||||
/**
|
||||
* Sets possible actions on the toolbar as part of the action menu. These are displayed as icons
|
||||
* or text on the right side of the widget. If they don't fit they are placed in an 'overflow'
|
||||
* menu.
|
||||
*
|
||||
* This property takes an array of objects, where each object has the following keys:
|
||||
*
|
||||
* * `title`: **required**, the title of this action
|
||||
* * `icon`: the icon for this action, e.g. `require('image!some_icon')`
|
||||
* * `show`: when to show this action as an icon or hide it in the overflow menu: `always`,
|
||||
* `ifRoom` or `never`
|
||||
* * `showWithText`: boolean, whether to show text alongside the icon or not
|
||||
*/
|
||||
actions: ReactPropTypes.arrayOf(ReactPropTypes.shape({
|
||||
title: ReactPropTypes.string.isRequired,
|
||||
icon: Image.propTypes.source,
|
||||
show: ReactPropTypes.oneOf(['always', 'ifRoom', 'never']),
|
||||
showWithText: ReactPropTypes.bool
|
||||
})),
|
||||
/**
|
||||
* Sets the toolbar logo.
|
||||
*/
|
||||
logo: Image.propTypes.source,
|
||||
/**
|
||||
* Sets the navigation icon.
|
||||
*/
|
||||
navIcon: Image.propTypes.source,
|
||||
/**
|
||||
* Callback that is called when an action is selected. The only argument that is passeed to the
|
||||
* callback is the position of the action in the actions array.
|
||||
*/
|
||||
onActionSelected: ReactPropTypes.func,
|
||||
/**
|
||||
* Callback called when the icon is selected.
|
||||
*/
|
||||
onIconClicked: ReactPropTypes.func,
|
||||
/**
|
||||
* Sets the toolbar subtitle.
|
||||
*/
|
||||
subtitle: ReactPropTypes.string,
|
||||
/**
|
||||
* Sets the toolbar subtitle color.
|
||||
*/
|
||||
subtitleColor: ReactPropTypes.string,
|
||||
/**
|
||||
* Sets the toolbar title.
|
||||
*/
|
||||
title: ReactPropTypes.string,
|
||||
/**
|
||||
* Sets the toolbar title color.
|
||||
*/
|
||||
titleColor: ReactPropTypes.string,
|
||||
/**
|
||||
* Used to locate this view in end-to-end tests.
|
||||
*/
|
||||
testID: ReactPropTypes.string,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var nativeProps = {
|
||||
...this.props,
|
||||
};
|
||||
if (this.props.logo) {
|
||||
if (!this.props.logo.isStatic) {
|
||||
throw 'logo prop should be a static image (obtained via ix)';
|
||||
}
|
||||
nativeProps.logo = this.props.logo.uri;
|
||||
}
|
||||
if (this.props.navIcon) {
|
||||
if (!this.props.navIcon.isStatic) {
|
||||
throw 'navIcon prop should be static image (obtained via ix)';
|
||||
}
|
||||
nativeProps.navIcon = this.props.navIcon.uri;
|
||||
}
|
||||
if (this.props.actions) {
|
||||
nativeProps.actions = [];
|
||||
for (var i = 0; i < this.props.actions.length; i++) {
|
||||
var action = {
|
||||
...this.props.actions[i],
|
||||
};
|
||||
if (action.icon) {
|
||||
if (!action.icon.isStatic) {
|
||||
throw 'action icons should be static images (obtained via ix)';
|
||||
}
|
||||
action.icon = action.icon.uri;
|
||||
}
|
||||
nativeProps.actions.push(action);
|
||||
}
|
||||
}
|
||||
|
||||
return <NativeToolbar onSelect={this._onSelect} {...nativeProps} />;
|
||||
},
|
||||
|
||||
_onSelect: function(event) {
|
||||
var position = event.nativeEvent.position;
|
||||
if (position === -1) {
|
||||
this.props.onIconClicked && this.props.onIconClicked();
|
||||
} else {
|
||||
this.props.onActionSelected && this.props.onActionSelected(position);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
var toolbarAttributes = {
|
||||
...ReactNativeViewAttributes.UIView,
|
||||
actions: true,
|
||||
logo: true,
|
||||
navIcon: true,
|
||||
subtitle: true,
|
||||
subtitleColor: true,
|
||||
title: true,
|
||||
titleColor: true,
|
||||
};
|
||||
|
||||
var NativeToolbar = createReactNativeComponentClass({
|
||||
validAttributes: toolbarAttributes,
|
||||
uiViewClassName: 'ToolbarAndroid',
|
||||
});
|
||||
|
||||
module.exports = ToolbarAndroid;
|
|
@ -0,0 +1,219 @@
|
|||
/**
|
||||
* 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 TouchableNativeFeedback
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var PropTypes = require('ReactPropTypes');
|
||||
var RCTUIManager = require('NativeModules').UIManager;
|
||||
var React = require('React');
|
||||
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
||||
var Touchable = require('Touchable');
|
||||
var TouchableWithoutFeedback = require('TouchableWithoutFeedback');
|
||||
|
||||
var createReactNativeComponentClass = require('createReactNativeComponentClass');
|
||||
var createStrictShapeTypeChecker = require('createStrictShapeTypeChecker');
|
||||
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
|
||||
var onlyChild = require('onlyChild');
|
||||
|
||||
var rippleBackgroundPropType = createStrictShapeTypeChecker({
|
||||
type: React.PropTypes.oneOf(['RippleAndroid']),
|
||||
color: PropTypes.string,
|
||||
borderless: PropTypes.bool,
|
||||
});
|
||||
|
||||
var themeAttributeBackgroundPropType = createStrictShapeTypeChecker({
|
||||
type: React.PropTypes.oneOf(['ThemeAttrAndroid']),
|
||||
attribute: PropTypes.string.isRequired,
|
||||
});
|
||||
|
||||
var backgroundPropType = PropTypes.oneOfType([
|
||||
rippleBackgroundPropType,
|
||||
themeAttributeBackgroundPropType,
|
||||
]);
|
||||
|
||||
var TouchableView = createReactNativeComponentClass({
|
||||
validAttributes: {
|
||||
...ReactNativeViewAttributes.UIView,
|
||||
nativeBackgroundAndroid: backgroundPropType,
|
||||
},
|
||||
uiViewClassName: 'RCTView',
|
||||
});
|
||||
|
||||
var PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30};
|
||||
|
||||
/**
|
||||
* A wrapper for making views respond properly to touches (Android only).
|
||||
* On Android this component uses native state drawable to display touch
|
||||
* feedback. At the moment it only supports having a single View instance as a
|
||||
* child node, as it's implemented by replacing that View with another instance
|
||||
* of RCTView node with some additional properties set.
|
||||
*
|
||||
* Background drawable of native feedback touchable can be customized with
|
||||
* `background` property.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* renderButton: function() {
|
||||
* return (
|
||||
* <TouchableNativeFeedback
|
||||
* onPress={this._onPressButton}
|
||||
* background={TouchableNativeFeedback.SelectableBackground()}>
|
||||
* <View style={{width: 150, height: 100, backgroundColor: 'red'}}>
|
||||
* <Text style={{margin: 30}}>Button</Text>
|
||||
* </View>
|
||||
* </TouchableHighlight>
|
||||
* );
|
||||
* },
|
||||
* ```
|
||||
*/
|
||||
|
||||
var TouchableNativeFeedback = React.createClass({
|
||||
propTypes: {
|
||||
...TouchableWithoutFeedback.propTypes,
|
||||
|
||||
/**
|
||||
* Determines the type of background drawable that's going to be used to
|
||||
* display feedback. It takes an object with `type` property and extra data
|
||||
* depending on the `type`. It's recommended to use one of the following
|
||||
* static methods to generate that dictionary:
|
||||
*
|
||||
* 1) TouchableNativeFeedback.SelectableBackground() - will create object
|
||||
* that represents android theme's default background for selectable
|
||||
* elements (?android:attr/selectableItemBackground)
|
||||
*
|
||||
* 2) TouchableNativeFeedback.SelectableBackgroundBorderless() - will create
|
||||
* object that represent android theme's default background for borderless
|
||||
* selectable elements (?android:attr/selectableItemBackgroundBorderless).
|
||||
* Available on android API level 21+
|
||||
*
|
||||
* 3) TouchableNativeFeedback.RippleAndroid(color, borderless) - will create
|
||||
* object that represents ripple drawable with specified color (as a
|
||||
* string). If property `borderless` evaluates to true the ripple will
|
||||
* render outside of the view bounds (see native actionbar buttons as an
|
||||
* example of that behavior). This background type is available on Android
|
||||
* API level 21+
|
||||
*/
|
||||
background: backgroundPropType,
|
||||
},
|
||||
|
||||
statics: {
|
||||
SelectableBackground: function() {
|
||||
return {type: 'ThemeAttrAndroid', attribute: 'selectableItemBackground'};
|
||||
},
|
||||
SelectableBackgroundBorderless: function() {
|
||||
return {type: 'ThemeAttrAndroid', attribute: 'selectableItemBackgroundBorderless'};
|
||||
},
|
||||
Ripple: function(color, borderless) {
|
||||
return {type: 'RippleAndroid', color: color, borderless: borderless};
|
||||
},
|
||||
},
|
||||
|
||||
mixins: [Touchable.Mixin],
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
background: this.SelectableBackground(),
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return this.touchableGetInitialState();
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
ensurePositiveDelayProps(this.props);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
ensurePositiveDelayProps(nextProps);
|
||||
},
|
||||
|
||||
/**
|
||||
* `Touchable.Mixin` self callbacks. The mixin will invoke these if they are
|
||||
* defined on your component.
|
||||
*/
|
||||
touchableHandleActivePressIn: function() {
|
||||
this.props.onPressIn && this.props.onPressIn();
|
||||
this._dispatchPressedStateChange(true);
|
||||
this._dispatchHotspotUpdate(this.pressInLocation.pageX, this.pressInLocation.pageY);
|
||||
},
|
||||
|
||||
touchableHandleActivePressOut: function() {
|
||||
this.props.onPressOut && this.props.onPressOut();
|
||||
this._dispatchPressedStateChange(false);
|
||||
},
|
||||
|
||||
touchableHandlePress: function() {
|
||||
this.props.onPress && this.props.onPress();
|
||||
},
|
||||
|
||||
touchableHandleLongPress: function() {
|
||||
this.props.onLongPress && this.props.onLongPress();
|
||||
},
|
||||
|
||||
touchableGetPressRectOffset: function() {
|
||||
return PRESS_RECT_OFFSET; // Always make sure to predeclare a constant!
|
||||
},
|
||||
|
||||
touchableGetHighlightDelayMS: function() {
|
||||
return this.props.delayPressIn;
|
||||
},
|
||||
|
||||
touchableGetLongPressDelayMS: function() {
|
||||
return this.props.delayLongPress;
|
||||
},
|
||||
|
||||
touchableGetPressOutDelayMS: function() {
|
||||
return this.props.delayPressOut;
|
||||
},
|
||||
|
||||
_handleResponderMove: function(e) {
|
||||
this.touchableHandleResponderMove(e);
|
||||
this._dispatchHotspotUpdate(e.nativeEvent.pageX, e.nativeEvent.pageY);
|
||||
},
|
||||
|
||||
_dispatchHotspotUpdate: function(destX, destY) {
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
React.findNodeHandle(this),
|
||||
RCTUIManager.RCTView.Commands.hotspotUpdate,
|
||||
[destX || 0, destY || 0]
|
||||
);
|
||||
},
|
||||
|
||||
_dispatchPressedStateChange: function(pressed) {
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
React.findNodeHandle(this),
|
||||
RCTUIManager.RCTView.Commands.setPressed,
|
||||
[pressed]
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var childProps = {
|
||||
...onlyChild(this.props.children).props,
|
||||
nativeBackgroundAndroid: this.props.background,
|
||||
accessible: this.props.accessible !== false,
|
||||
accessibilityComponentType: this.props.accessibilityComponentType,
|
||||
accessibilityTraits: this.props.accessibilityTraits,
|
||||
testID: this.props.testID,
|
||||
onLayout: this.props.onLayout,
|
||||
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
||||
onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest,
|
||||
onResponderGrant: this.touchableHandleResponderGrant,
|
||||
onResponderMove: this._handleResponderMove,
|
||||
onResponderRelease: this.touchableHandleResponderRelease,
|
||||
onResponderTerminate: this.touchableHandleResponderTerminate,
|
||||
};
|
||||
return <TouchableView {...childProps}/>;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = TouchableNativeFeedback;
|
|
@ -0,0 +1,169 @@
|
|||
/**
|
||||
* 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 Image
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var NativeModules = require('NativeModules');
|
||||
var ImageResizeMode = require('ImageResizeMode');
|
||||
var ImageStylePropTypes = require('ImageStylePropTypes');
|
||||
var PropTypes = require('ReactPropTypes');
|
||||
var React = require('React');
|
||||
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var StyleSheetPropType = require('StyleSheetPropType');
|
||||
var View = require('View');
|
||||
|
||||
var createReactNativeComponentClass = require('createReactNativeComponentClass');
|
||||
var flattenStyle = require('flattenStyle');
|
||||
var invariant = require('invariant');
|
||||
var merge = require('merge');
|
||||
var resolveAssetSource = require('resolveAssetSource');
|
||||
|
||||
/**
|
||||
* <Image> - A react component for displaying different types of images,
|
||||
* including network images, static resources, temporary local images, and
|
||||
* images from local disk, such as the camera roll. Example usage:
|
||||
*
|
||||
* renderImages: function() {
|
||||
* return (
|
||||
* <View>
|
||||
* <Image
|
||||
* style={styles.icon}
|
||||
* source={require('image!myIcon')}
|
||||
* />
|
||||
* <Image
|
||||
* style={styles.logo}
|
||||
* source={{uri: 'http://facebook.github.io/react/img/logo_og.png'}}
|
||||
* />
|
||||
* </View>
|
||||
* );
|
||||
* },
|
||||
*
|
||||
* More example code in ImageExample.js
|
||||
*/
|
||||
|
||||
var ImageViewAttributes = merge(ReactNativeViewAttributes.UIView, {
|
||||
src: true,
|
||||
resizeMode: true,
|
||||
});
|
||||
|
||||
var Image = React.createClass({
|
||||
propTypes: {
|
||||
source: PropTypes.shape({
|
||||
/**
|
||||
* A string representing the resource identifier for the image, which
|
||||
* could be an http address, a local file path, or the name of a static image
|
||||
* resource (which should be wrapped in the `ix` function).
|
||||
*/
|
||||
uri: PropTypes.string,
|
||||
}).isRequired,
|
||||
style: StyleSheetPropType(ImageStylePropTypes),
|
||||
/**
|
||||
* Used to locate this view in end-to-end tests.
|
||||
*/
|
||||
testID: PropTypes.string,
|
||||
},
|
||||
|
||||
statics: {
|
||||
resizeMode: ImageResizeMode,
|
||||
},
|
||||
|
||||
mixins: [NativeMethodsMixin],
|
||||
|
||||
/**
|
||||
* `NativeMethodsMixin` will look for this when invoking `setNativeProps`. We
|
||||
* make `this` look like an actual native component class. Since it can render
|
||||
* as 3 different native components we need to update viewConfig accordingly
|
||||
*/
|
||||
viewConfig: {
|
||||
uiViewClassName: 'RCTView',
|
||||
validAttributes: ReactNativeViewAttributes.RKView
|
||||
},
|
||||
|
||||
_updateViewConfig: function(props) {
|
||||
if (props.children) {
|
||||
this.viewConfig = {
|
||||
uiViewClassName: 'RCTView',
|
||||
validAttributes: ReactNativeViewAttributes.RKView,
|
||||
};
|
||||
} else {
|
||||
this.viewConfig = {
|
||||
uiViewClassName: 'RCTImageView',
|
||||
validAttributes: ImageViewAttributes,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this._updateViewConfig(this.props);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
this._updateViewConfig(nextProps);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var source = resolveAssetSource(this.props.source);
|
||||
if (source && source.uri) {
|
||||
var isNetwork = source.uri.match(/^https?:/);
|
||||
invariant(
|
||||
!(isNetwork && source.isStatic),
|
||||
'Static image URIs cannot start with "http": "' + source.uri + '"'
|
||||
);
|
||||
|
||||
var {width, height} = source;
|
||||
var style = flattenStyle([{width, height}, styles.base, this.props.style]);
|
||||
|
||||
var nativeProps = merge(this.props, {
|
||||
style,
|
||||
src: source.uri,
|
||||
});
|
||||
|
||||
if (nativeProps.children) {
|
||||
// TODO(6033040): Consider implementing this as a separate native component
|
||||
var imageProps = merge(nativeProps, {
|
||||
style: styles.absoluteImage,
|
||||
children: undefined,
|
||||
});
|
||||
return (
|
||||
<View style={nativeProps.style}>
|
||||
<RKImage {...imageProps}/>
|
||||
{this.props.children}
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
return <RKImage {...nativeProps}/>;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
base: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
absoluteImage: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
position: 'absolute'
|
||||
}
|
||||
});
|
||||
|
||||
var RKImage = createReactNativeComponentClass({
|
||||
validAttributes: ImageViewAttributes,
|
||||
uiViewClassName: 'RCTImageView',
|
||||
});
|
||||
|
||||
module.exports = Image;
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* 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 RCTNetworking
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
// Do not require the native RCTNetworking module directly! Use this wrapper module instead.
|
||||
// It will add the necessary requestId, so that you don't have to generate it yourself.
|
||||
var RCTNetworkingNative = require('NativeModules').Networking;
|
||||
|
||||
var _requestId = 1;
|
||||
var generateRequestId = function() {
|
||||
return _requestId++;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is a wrapper around the native RCTNetworking module. It adds a necessary unique
|
||||
* requestId to each network request that can be used to abort that request later on.
|
||||
*/
|
||||
class RCTNetworking {
|
||||
|
||||
static sendRequest(method, url, headers, data, callback) {
|
||||
var requestId = generateRequestId();
|
||||
RCTNetworkingNative.sendRequest(
|
||||
method,
|
||||
url,
|
||||
requestId,
|
||||
headers,
|
||||
data,
|
||||
callback);
|
||||
return requestId;
|
||||
}
|
||||
|
||||
static abortRequest(requestId) {
|
||||
RCTNetworkingNative.abortRequest(requestId);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RCTNetworking;
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* 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 XMLHttpRequest
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var FormData = require('FormData');
|
||||
var RCTNetworking = require('RCTNetworking');
|
||||
var XMLHttpRequestBase = require('XMLHttpRequestBase');
|
||||
|
||||
type Header = [string, string];
|
||||
|
||||
function convertHeadersMapToArray(headers: Object): Array<Header> {
|
||||
var headerArray = [];
|
||||
for (var name in headers) {
|
||||
headerArray.push([name, headers[name]]);
|
||||
}
|
||||
return headerArray;
|
||||
}
|
||||
|
||||
class XMLHttpRequest extends XMLHttpRequestBase {
|
||||
|
||||
_requestId: ?number;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._requestId = null;
|
||||
}
|
||||
|
||||
sendImpl(method: ?string, url: ?string, headers: Object, data: any): void {
|
||||
var body;
|
||||
if (typeof data === 'string') {
|
||||
body = {string: data};
|
||||
} else if (data instanceof FormData) {
|
||||
body = {
|
||||
formData: data.getParts().map((part) => {
|
||||
part.headers = convertHeadersMapToArray(part.headers);
|
||||
return part;
|
||||
}),
|
||||
};
|
||||
} else {
|
||||
body = data;
|
||||
}
|
||||
|
||||
this._requestId = RCTNetworking.sendRequest(
|
||||
method,
|
||||
url,
|
||||
convertHeadersMapToArray(headers),
|
||||
body,
|
||||
this.callback.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
abortImpl(): void {
|
||||
this._requestId && RCTNetworking.abortRequest(this._requestId);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = XMLHttpRequest;
|
|
@ -0,0 +1,132 @@
|
|||
/**
|
||||
* 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 renderApplication
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Inspector = require('Inspector');
|
||||
var Portal = require('Portal');
|
||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var Subscribable = require('Subscribable');
|
||||
var View = require('View');
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
// require BackAndroid so it sets the default handler that exits the app if no listeners respond
|
||||
require('BackAndroid');
|
||||
|
||||
var AppContainer = React.createClass({
|
||||
mixins: [Subscribable.Mixin],
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
enabled: __DEV__,
|
||||
inspectorVisible: false,
|
||||
rootNodeHandle: null,
|
||||
rootImportanceForAccessibility: 'auto',
|
||||
};
|
||||
},
|
||||
|
||||
toggleElementInspector: function() {
|
||||
this.setState({
|
||||
inspectorVisible: !this.state.inspectorVisible,
|
||||
rootNodeHandle: React.findNodeHandle(this.refs.main),
|
||||
});
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.addListenerOn(
|
||||
RCTDeviceEventEmitter,
|
||||
'toggleElementInspector',
|
||||
this.toggleElementInspector
|
||||
);
|
||||
|
||||
this._unmounted = false;
|
||||
},
|
||||
|
||||
renderInspector: function() {
|
||||
return this.state.inspectorVisible ?
|
||||
<Inspector
|
||||
rootTag={this.props.rootTag}
|
||||
inspectedViewTag={this.state.rootNodeHandle}
|
||||
/> :
|
||||
null;
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this._unmounted = true;
|
||||
},
|
||||
|
||||
setRootAccessibility: function(modalVisible) {
|
||||
if (this._unmounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
rootImportanceForAccessibility: modalVisible ? 'no-hide-descendants' : 'auto',
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var RootComponent = this.props.rootComponent;
|
||||
var appView =
|
||||
<View
|
||||
ref="main"
|
||||
collapsable={!this.state.inspectorVisible}
|
||||
style={styles.appContainer}>
|
||||
<RootComponent
|
||||
{...this.props.initialProps}
|
||||
rootTag={this.props.rootTag}
|
||||
importantForAccessibility={this.state.rootImportanceForAccessibility}/>
|
||||
<Portal
|
||||
onModalVisibilityChanged={this.setRootAccessibility}/>
|
||||
</View>;
|
||||
|
||||
return this.state.enabled ?
|
||||
<View style={styles.appContainer}>
|
||||
{appView}
|
||||
{this.renderInspector()}
|
||||
</View> :
|
||||
appView;
|
||||
}
|
||||
});
|
||||
|
||||
function renderApplication<D, P, S>(
|
||||
RootComponent: ReactClass<D, P, S>,
|
||||
initialProps: P,
|
||||
rootTag: any
|
||||
) {
|
||||
invariant(
|
||||
rootTag,
|
||||
'Expect to have a valid rootTag, instead got ', rootTag
|
||||
);
|
||||
React.render(
|
||||
<AppContainer
|
||||
rootComponent={RootComponent}
|
||||
initialProps={initialProps}
|
||||
rootTag={rootTag} />,
|
||||
rootTag
|
||||
);
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
// This is needed so the application covers the whole screen
|
||||
// and therefore the contents of the Portal are not clipped.
|
||||
appContainer: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = renderApplication;
|
|
@ -0,0 +1,233 @@
|
|||
/**
|
||||
* 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 AsyncStorage
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var RCTAsyncStorage = require('NativeModules').AsyncSQLiteDBStorage;
|
||||
|
||||
/**
|
||||
* AsyncStorage is a simple, asynchronous, persistent, global, key-value storage system.
|
||||
*
|
||||
* It is recommended that you use an abstraction on top of AsyncStorage instead of AsyncStorage
|
||||
* directly for anything more than light usage since it operates globally.
|
||||
*
|
||||
* This JS code is a simple facade over the native android implementation to provide a clear
|
||||
* JS API, real Error objects, and simple non-multi functions.
|
||||
*/
|
||||
var AsyncStorage = {
|
||||
/**
|
||||
* Fetches `key` and passes the result to `callback`, along with an `Error` if
|
||||
* there is any. Returns a `Promise` object.
|
||||
*/
|
||||
getItem: function(
|
||||
key: string,
|
||||
callback?: ?(error: ?Error, result: ?string) => void
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiGet([key], function(error, result) {
|
||||
var value = (result && result[0] && result[0][1]) ? result[0][1] : null;
|
||||
callback && callback((error && convertError(error)) || null, value);
|
||||
if (error) {
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Sets `value` for `key` and calls `callback` on completion, along with an
|
||||
* `Error` if there is any. Returns a `Promise` object.
|
||||
*/
|
||||
setItem: function(
|
||||
key: string,
|
||||
value: string,
|
||||
callback?: ?(error: ?Error) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiSet([[key,value]], function(error) {
|
||||
callback && callback((error && convertError(error)) || null);
|
||||
if (error) {
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Returns a `Promise` object.
|
||||
*/
|
||||
removeItem: function(
|
||||
key: string,
|
||||
callback?: ?(error: ?Error) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiRemove([key], function(error) {
|
||||
callback && callback((error && convertError(error)) || null);
|
||||
if (error) {
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Merges existing value with input value, assuming they are stringified json.
|
||||
* Returns a `Promise` object.
|
||||
*/
|
||||
mergeItem: function(
|
||||
key: string,
|
||||
value: string,
|
||||
callback?: ?(error: ?Error) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiMerge([[key,value]], function(error) {
|
||||
callback && callback((error && convertError(error)) || null);
|
||||
if (error) {
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Erases *all* AsyncStorage for all clients, libraries, etc. You probably
|
||||
* don't want to call this - use removeItem or multiRemove to clear only your
|
||||
* own keys instead. Returns a `Promise` object.
|
||||
*/
|
||||
clear: function(callback?: ?(error: ?Error) => void): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.clear(function(error) {
|
||||
callback && callback(convertError(error) || null);
|
||||
if (error) {
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Gets *all* keys known to the app, for all callers, libraries, etc. Returns a `Promise` object.
|
||||
*/
|
||||
getAllKeys: function(callback?: ?(error: ?Error, keys: ?Array<string>) => void): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.getAllKeys(function(error, keys) {
|
||||
callback && callback((error && convertError(error)) || null, keys);
|
||||
if (error) {
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(keys);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* The following batched functions are useful for executing a lot of
|
||||
* operations at once, allowing for native optimizations and provide the
|
||||
* convenience of a single callback after all operations are complete.
|
||||
*
|
||||
* In case of errors, these functions return the first encountered error and abort.
|
||||
*/
|
||||
|
||||
/**
|
||||
* multiGet invokes callback with an array of key-value pair arrays that
|
||||
* matches the input format of multiSet. Returns a `Promise` object.
|
||||
*
|
||||
* multiGet(['k1', 'k2'], cb) -> cb([['k1', 'val1'], ['k2', 'val2']])
|
||||
*/
|
||||
multiGet: function(
|
||||
keys: Array<string>,
|
||||
callback?: ?(errors: ?Array<Error>, result: ?Array<Array<string>>) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiGet(keys, function(error, result) {
|
||||
callback && callback((error && convertError(error)) || null, result);
|
||||
if (error) {
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* multiSet and multiMerge take arrays of key-value array pairs that match
|
||||
* the output of multiGet, e.g. Returns a `Promise` object.
|
||||
*
|
||||
* multiSet([['k1', 'val1'], ['k2', 'val2']], cb);
|
||||
*/
|
||||
multiSet: function(
|
||||
keyValuePairs: Array<Array<string>>,
|
||||
callback?: ?(errors: ?Array<Error>) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiSet(keyValuePairs, function(error) {
|
||||
callback && callback((error && convertError(error)) || null);
|
||||
if (error) {
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Delete all the keys in the `keys` array. Returns a `Promise` object.
|
||||
*/
|
||||
multiRemove: function(
|
||||
keys: Array<string>,
|
||||
callback?: ?(errors: ?Array<Error>) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiRemove(keys, function(error) {
|
||||
callback && callback((error && convertError(error)) || null);
|
||||
if (error) {
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Merges existing values with input values, assuming they are stringified
|
||||
* json. Returns a `Promise` object.
|
||||
*/
|
||||
multiMerge: function(
|
||||
keyValuePairs: Array<Array<string>>,
|
||||
callback?: ?(errors: ?Array<Error>) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiMerge(keyValuePairs, function(error) {
|
||||
callback && callback((error && convertError(error)) || null);
|
||||
if (error) {
|
||||
reject(convertError(error));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
function convertError(error) {
|
||||
if (!error) {
|
||||
return null;
|
||||
}
|
||||
var out = new Error(error.message);
|
||||
return [out];
|
||||
}
|
||||
|
||||
module.exports = AsyncStorage;
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* Detect hardware back button presses, and programatically invoke the default back button
|
||||
* functionality to exit the app if there are no listeners or if none of the listeners return true.
|
||||
*
|
||||
* @providesModule BackAndroid
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var Set = require('Set');
|
||||
var DeviceEventManager = require('NativeModules').DeviceEventManager;
|
||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
|
||||
var DEVICE_BACK_EVENT = 'hardwareBackPress';
|
||||
|
||||
type BackPressEventName = $Enum<{
|
||||
backPress: string;
|
||||
}>;
|
||||
|
||||
var _backPressSubscriptions = new Set();
|
||||
|
||||
RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, function() {
|
||||
var invokeDefault = true;
|
||||
_backPressSubscriptions.forEach((subscription) => {
|
||||
if (subscription()) {
|
||||
invokeDefault = false;
|
||||
}
|
||||
});
|
||||
if (invokeDefault) {
|
||||
BackAndroid.exitApp();
|
||||
}
|
||||
});
|
||||
|
||||
var BackAndroid = {
|
||||
|
||||
exitApp: function() {
|
||||
DeviceEventManager.invokeDefaultBackPressHandler();
|
||||
},
|
||||
|
||||
addEventListener: function (
|
||||
eventName: BackPressEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
_backPressSubscriptions.add(handler);
|
||||
},
|
||||
|
||||
removeEventListener: function(
|
||||
eventName: BackPressEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
_backPressSubscriptions.delete(handler);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = BackAndroid;
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* 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 Platform
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var AndroidConstants = require('NativeModules').AndroidConstants;
|
||||
|
||||
var Platform = {
|
||||
OS: 'android',
|
||||
Version: AndroidConstants.Version,
|
||||
};
|
||||
|
||||
module.exports = Platform;
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Copyright 2013-2014 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @providesModule ExecutionEnvironment
|
||||
*
|
||||
* NB: This is a temporary override that has not yet been merged upstream. It is NOT actually part
|
||||
* of react-core (yet)
|
||||
*
|
||||
* Stubs SignedSource<<7afee88a05412d0c4eef54817419648e>>
|
||||
*/
|
||||
|
||||
/*jslint evil: true */
|
||||
|
||||
"use strict";
|
||||
|
||||
var canUseDOM = false;
|
||||
|
||||
/**
|
||||
* Simple, lightweight module assisting with the detection and context of
|
||||
* Worker. Helps avoid circular dependencies and allows code to reason about
|
||||
* whether or not they are in a Worker, even if they never include the main
|
||||
* `ReactWorker` dependency.
|
||||
*/
|
||||
var ExecutionEnvironment = {
|
||||
|
||||
canUseDOM: canUseDOM,
|
||||
|
||||
canUseWorkers: typeof Worker !== 'undefined',
|
||||
|
||||
canUseEventListeners:
|
||||
canUseDOM && !!(window.addEventListener || window.attachEvent),
|
||||
|
||||
canUseViewport: canUseDOM && !!window.screen,
|
||||
|
||||
isInWorker: !canUseDOM // For now, this is true - might change in the future.
|
||||
|
||||
};
|
||||
|
||||
module.exports = ExecutionEnvironment;
|
|
@ -55,11 +55,11 @@ Now open any example (the `.xcodeproj` file in each of the `Examples` subdirecto
|
|||
- Looking for a component? [react.parts](http://react.parts/)
|
||||
- Fellow developers write and publish React Native modules to npm and open source them on GitHub.
|
||||
- Making modules helps grow the React Native ecosystem and community. We recommend writing modules for your use cases and sharing them on npm.
|
||||
- Read the [Native Modules iOS](http://facebook.github.io/react-native/docs/native-modules-ios.html#content) and [Native UI Components iOS](http://facebook.github.io/react-native/docs/native-components-ios.html#content) guides in the documentation if you are interested in extending native functionality.
|
||||
- Read the guides on Native Modules ([iOS](http://facebook.github.io/react-native/docs/native-modules-ios.html), [Android](http://facebook.github.io/react-native/docs/native-modules-android.html)) and Native UI Components ([iOS](http://facebook.github.io/react-native/docs/native-components-ios.html), [Android](http://facebook.github.io/react-native/docs/native-components-android.html)) if you are interested in extending native functionality.
|
||||
|
||||
## Opening Issues
|
||||
|
||||
If you encounter a bug with React Native we would like to hear about it. Search the [existing issues](https://github.com/facebook/react-native/issues) and try to make sure your problem doesn’t already exist before opening a new issue. It’s helpful if you include the version of React Native and iOS you’re using. Please include a stack trace and reduced repro case when appropriate, too.
|
||||
If you encounter a bug with React Native we would like to hear about it. Search the [existing issues](https://github.com/facebook/react-native/issues) and try to make sure your problem doesn’t already exist before opening a new issue. It’s helpful if you include the version of React Native and OS you’re using. Please include a stack trace and reduced repro case when appropriate, too.
|
||||
|
||||
The GitHub issues are intended for bug reports and feature requests. For help and questions with using React Native please make use of the resources listed in the [Getting Help](#getting-help) section. There are limited resources available for handling issues and by keeping the list of open issues lean we can respond in a timely manner.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = "React"
|
||||
s.version = "0.8.0"
|
||||
s.version = "0.11.0-rc"
|
||||
s.summary = "Build high quality mobile apps using React."
|
||||
s.description = <<-DESC
|
||||
React Native apps are built using the React JS
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
Here's how to test the whole dev experience end-to-end. This will be eventually merged into the [Getting Started guide](https://facebook.github.io/react-native/docs/getting-started.html).
|
||||
|
||||
Assuming you have the [Android SDK](https://developer.android.com/sdk/installing/index.html) installed, run `android` to open the Android SDK Manager.
|
||||
|
||||
Make sure you have the following installed:
|
||||
|
||||
- Android SDK version 23
|
||||
- SDK build tools version 23
|
||||
- Android Support Repository 17 (for Android Support Library)
|
||||
|
||||
Follow steps on https://github.com/facebook/react-native/blob/master/react-native-cli/CONTRIBUTING.md, but be sure to bump the verison of react-native in package.json to some version > 0.9 (latest published npm version) or set up proxying properly for react-native
|
||||
|
||||
- From the react-native-android repo:
|
||||
- `./gradlew :ReactAndroid:installArchives`
|
||||
- *Assuming you already have android-jsc installed to local maven repo, no steps included here*
|
||||
- `react-native init ProjectName`
|
||||
- Open up your Android emulator (Genymotion is recommended)
|
||||
- `cd ProjectName`
|
||||
- `react-native run-android`
|
||||
|
||||
In case the app crashed:
|
||||
|
||||
- Run `adb logcat` and try to find a Java exception
|
|
@ -0,0 +1,101 @@
|
|||
# Building React Native for Android
|
||||
|
||||
This guide contains instructions for building the Android code and running the sample apps.
|
||||
|
||||
## Supported Operating Systems
|
||||
|
||||
This setup has only been tested on Mac OS so far.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Assuming you have the [Android SDK](https://developer.android.com/sdk/installing/index.html) installed, run `android` to open the Android SDK Manager.
|
||||
|
||||
Make sure you have the following installed:
|
||||
|
||||
- Android SDK version 22 (compileSdkVersion in [`build.gradle`](build.gradle))
|
||||
- SDK build tools version 22.0.1 (buildToolsVersion in [`build.gradle`](build.gradle))
|
||||
- Android Support Repository 17 (for Android Support Library)
|
||||
|
||||
Point Gradle to your Android SDK - in the root of your clone of the github repo, create a file called `local.properties` with the following contents:
|
||||
|
||||
sdk.dir=absolute_path_to_android_sdk
|
||||
ndk.dir=absolute_path_to_android_ndk
|
||||
|
||||
Example:
|
||||
|
||||
sdk.dir=/Users/your_unix_name/android-sdk-macosx
|
||||
ndk.dir=/Users/your_unix_name/android-ndk/android-ndk-r10c
|
||||
|
||||
## Run `npm install`
|
||||
|
||||
This is needed to fetch the dependencies for the packager.
|
||||
|
||||
```bash
|
||||
cd react-native-android
|
||||
npm install
|
||||
```
|
||||
|
||||
## Building from the command line
|
||||
|
||||
To build the framework code:
|
||||
|
||||
```bash
|
||||
cd react-native-android
|
||||
./gradlew :ReactAndroid:assembleDebug
|
||||
```
|
||||
|
||||
To install a snapshot version of the framework code in your local Maven repo:
|
||||
|
||||
```bash
|
||||
./gradlew :ReactAndroid:installArchives
|
||||
```
|
||||
|
||||
## Running the examples
|
||||
|
||||
To run the Sample app:
|
||||
|
||||
```bash
|
||||
cd react-native-android
|
||||
./gradlew :Examples:SampleApp:android:app:installDebug
|
||||
# Start the packager in a separate shell:
|
||||
# Make sure you ran npm install
|
||||
./packager/packager.sh
|
||||
# Open SampleApp in your emulator, Menu button -> Reload JS should work
|
||||
```
|
||||
|
||||
You can run any other sample app the same way, e.g.:
|
||||
|
||||
```bash
|
||||
./gradlew :Examples:Movies:android:app:installDebug
|
||||
./gradlew :Examples:UIExplorer:android:app:installDebug
|
||||
```
|
||||
|
||||
## Building from Android Studio
|
||||
|
||||
You'll need to do one additional step until we release the React Native Gradle plugin to Maven central. This is because Android Studio has its own local Maven repo:
|
||||
|
||||
mkdir -p /Applications/Android\ Studio.app/Contents/gradle/m2repository/com/facebook/react
|
||||
cp -r ~/.m2/repository/com/facebook/react/gradleplugin /Applications/Android\ Studio.app/Contents/gradle/m2repository/com/facebook/react/
|
||||
|
||||
Now, open Android Studio, click _Import Non-Android Studio project_ and find your `react-native-android` repo.
|
||||
|
||||
In the configurations dropdown, _app_ should be selected. Click _Run_.
|
||||
|
||||
## Installing the React Native .aar in your local Maven repo
|
||||
|
||||
In some cases, for example when working on the `react-native-cli` it's useful to publish a snapshot version of React Native into your local Maven repo. This way, Gradle can pick it up when building projects that have a Maven dependency on React Native.
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd react-native-android
|
||||
./gradlew :ReactAndroid:installArchives
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Gradle build fails in `ndk-build`. See the section about `local.properties` file above.
|
||||
|
||||
Gradle build fails "Could not find any version that matches com.facebook.react:gradleplugin:...". See the section about the React Native Gradle plugin above.
|
||||
|
||||
Packager throws an error saying a module is not found. Try running `npm install` in the root of the repo.
|
|
@ -0,0 +1,246 @@
|
|||
// Copyright 2015-present Facebook. All Rights Reserved.
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'maven'
|
||||
|
||||
apply plugin: 'de.undercouch.download'
|
||||
|
||||
import de.undercouch.gradle.tasks.download.Download
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
// We download various C++ open-source dependencies into downloads.
|
||||
// We then copy both downloaded code and our custom makefiles and headers into third-party-ndk
|
||||
// After that we build native code from src/main/jni with module path pointing at third-party-ndk
|
||||
|
||||
def downloadsDir = new File("$buildDir/downloads")
|
||||
def thirdPartyNdkDir = new File("$buildDir/third-party-ndk")
|
||||
|
||||
task createNativeDepsDirectories {
|
||||
downloadsDir.mkdirs()
|
||||
thirdPartyNdkDir.mkdirs()
|
||||
}
|
||||
|
||||
task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) {
|
||||
// Use ZIP version as it's faster this way to selectively extract some parts of the archive
|
||||
src 'https://downloads.sourceforge.net/project/boost/boost/1.57.0/boost_1_57_0.zip'
|
||||
onlyIfNewer true
|
||||
overwrite false
|
||||
dest new File(downloadsDir, 'boost_1_57_0.zip')
|
||||
}
|
||||
|
||||
task prepareBoost(dependsOn: downloadBoost, type: Copy) {
|
||||
from zipTree(downloadBoost.dest)
|
||||
from 'src/main/jni/third-party/boost/Android.mk'
|
||||
include 'boost_1_57_0/boost/**/*.hpp', 'Android.mk'
|
||||
into "$thirdPartyNdkDir/boost"
|
||||
}
|
||||
|
||||
task downloadDoubleConversion(dependsOn: createNativeDepsDirectories, type: Download) {
|
||||
src 'https://github.com/google/double-conversion/archive/v1.1.1.tar.gz'
|
||||
onlyIfNewer true
|
||||
overwrite false
|
||||
dest new File(downloadsDir, 'double-conversion-1.1.1.tar.gz')
|
||||
}
|
||||
|
||||
task prepareDoubleConversion(dependsOn: downloadDoubleConversion, type: Copy) {
|
||||
from tarTree(downloadDoubleConversion.dest)
|
||||
from 'src/main/jni/third-party/double-conversion/Android.mk'
|
||||
include 'double-conversion-1.1.1/src/**/*', 'Android.mk'
|
||||
filesMatching('*/src/**/*', {fname -> fname.path = "double-conversion/${fname.name}"})
|
||||
includeEmptyDirs = false
|
||||
into "$thirdPartyNdkDir/double-conversion"
|
||||
}
|
||||
|
||||
task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) {
|
||||
src 'https://github.com/facebook/folly/archive/v0.50.0.tar.gz'
|
||||
onlyIfNewer true
|
||||
overwrite false
|
||||
dest new File(downloadsDir, 'folly-0.50.0.tar.gz');
|
||||
}
|
||||
|
||||
task prepareFolly(dependsOn: downloadFolly, type: Copy) {
|
||||
from tarTree(downloadFolly.dest)
|
||||
from 'src/main/jni/third-party/folly/Android.mk'
|
||||
include 'folly-0.50.0/folly/**/*', 'Android.mk'
|
||||
eachFile {fname -> fname.path = (fname.path - "folly-0.50.0/")}
|
||||
includeEmptyDirs = false
|
||||
into "$thirdPartyNdkDir/folly"
|
||||
}
|
||||
|
||||
task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) {
|
||||
src 'https://github.com/google/glog/archive/v0.3.3.tar.gz'
|
||||
onlyIfNewer true
|
||||
overwrite false
|
||||
dest new File(downloadsDir, 'glog-0.3.3.tar.gz')
|
||||
}
|
||||
|
||||
// Prepare glog sources to be compiled, this task will perform steps that normally shoudl've been
|
||||
// executed by automake. This way we can avoid dependencies on make/automake
|
||||
task prepareGlog(dependsOn: downloadGlog, type: Copy) {
|
||||
from tarTree(downloadGlog.dest)
|
||||
from 'src/main/jni/third-party/glog/'
|
||||
include 'glog-0.3.3/src/**/*', 'Android.mk', 'config.h'
|
||||
includeEmptyDirs = false
|
||||
filesMatching('**/*.h.in') {
|
||||
filter(ReplaceTokens, tokens: [
|
||||
ac_cv_have_unistd_h: '1',
|
||||
ac_cv_have_stdint_h: '1',
|
||||
ac_cv_have_systypes_h: '1',
|
||||
ac_cv_have_inttypes_h: '1',
|
||||
ac_cv_have_libgflags: '0',
|
||||
ac_google_start_namespace: 'namespace google {',
|
||||
ac_cv_have_uint16_t: '1',
|
||||
ac_cv_have_u_int16_t: '1',
|
||||
ac_cv_have___uint16: '0',
|
||||
ac_google_end_namespace: '}',
|
||||
ac_cv_have___builtin_expect: '1',
|
||||
ac_google_namespace: 'google',
|
||||
ac_cv___attribute___noinline: '__attribute__ ((noinline))',
|
||||
ac_cv___attribute___noreturn: '__attribute__ ((noreturn))',
|
||||
ac_cv___attribute___printf_4_5: '__attribute__((__format__ (__printf__, 4, 5)))'
|
||||
])
|
||||
it.path = (it.name - '.in')
|
||||
}
|
||||
into "$thirdPartyNdkDir/glog"
|
||||
}
|
||||
|
||||
task downloadJSCHeaders(type: Download) {
|
||||
def jscAPIBaseURL = 'https://svn.webkit.org/repository/webkit/!svn/bc/174650/trunk/Source/JavaScriptCore/API/'
|
||||
def jscHeaderFiles = ['JSBase.h', 'JSContextRef.h', 'JSObjectRef.h', 'JSRetainPtr.h', 'JSStringRef.h', 'JSValueRef.h', 'WebKitAvailability.h']
|
||||
def output = new File(downloadsDir, 'jsc')
|
||||
output.mkdirs()
|
||||
src(jscHeaderFiles.collect { headerName -> "$jscAPIBaseURL$headerName" })
|
||||
onlyIfNewer true
|
||||
overwrite false
|
||||
dest output
|
||||
}
|
||||
|
||||
// Create Android.mk library module based on so files from mvn + include headers fetched from webkit.org
|
||||
task prepareJSC(dependsOn: downloadJSCHeaders) << {
|
||||
copy {
|
||||
from zipTree(configurations.compile.fileCollection { dep -> dep.name == 'android-jsc' }.singleFile)
|
||||
from {downloadJSCHeaders.dest}
|
||||
from 'src/main/jni/third-party/jsc/Android.mk'
|
||||
include 'jni/**/*.so', '*.h', 'Android.mk'
|
||||
filesMatching('*.h', { fname -> fname.path = "JavaScriptCore/${fname.path}"})
|
||||
into "$thirdPartyNdkDir/jsc";
|
||||
}
|
||||
}
|
||||
|
||||
def getNdkBuildName() {
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
return "ndk-build.cmd"
|
||||
} else {
|
||||
return "ndk-build"
|
||||
}
|
||||
}
|
||||
|
||||
def findNdkBuildFullPath() {
|
||||
// we allow to provide full path to ndk-build tool
|
||||
if (hasProperty('ndk.command')) {
|
||||
return property('ndk.command')
|
||||
}
|
||||
// or just a path to the containing directory
|
||||
if (hasProperty('ndk.path')) {
|
||||
def ndkDir = property('ndk.path')
|
||||
return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
|
||||
}
|
||||
if (System.getenv('ANDROID_NDK') != null) {
|
||||
def ndkDir = System.getenv('ANDROID_NDK')
|
||||
return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
|
||||
}
|
||||
def ndkDir = android.hasProperty('plugin') ? android.plugin.ndkFolder :
|
||||
plugins.getPlugin('com.android.library').sdkHandler.getNdkFolder()
|
||||
if (ndkDir) {
|
||||
return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
def getNdkBuildFullPath() {
|
||||
def ndkBuildFullPath = findNdkBuildFullPath()
|
||||
if (ndkBuildFullPath == null || !new File(ndkBuildFullPath).canExecute()) {
|
||||
throw new GradleScriptException(
|
||||
"ndk-build binary cannot be found, check if you've set " +
|
||||
"\$ANDROID_NDK environment variable correctly or if ndk.dir is " +
|
||||
"setup in local.properties",
|
||||
null)
|
||||
}
|
||||
return ndkBuildFullPath
|
||||
}
|
||||
|
||||
task buildReactNdkLib(dependsOn: [prepareJSC, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog], type: Exec) {
|
||||
inputs.file('src/main/jni/react')
|
||||
outputs.dir("$buildDir/react-ndk/all")
|
||||
commandLine getNdkBuildFullPath(),
|
||||
'NDK_PROJECT_PATH=null',
|
||||
"NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk",
|
||||
'NDK_OUT=' + temporaryDir,
|
||||
"NDK_LIBS_OUT=$buildDir/react-ndk/all",
|
||||
"THIRD_PARTY_NDK_DIR=$buildDir/third-party-ndk",
|
||||
'-C', file('src/main/jni/react/jni').absolutePath,
|
||||
'--jobs', Runtime.runtime.availableProcessors()
|
||||
}
|
||||
|
||||
task cleanReactNdkLib(type: Exec) {
|
||||
commandLine getNdkBuildFullPath(),
|
||||
'-C', file('src/main/jni/react/jni').absolutePath,
|
||||
'clean'
|
||||
}
|
||||
|
||||
task packageReactNdkLibs(dependsOn: buildReactNdkLib, type: Copy) {
|
||||
from "$buildDir/react-ndk/all"
|
||||
exclude '**/libjsc.so'
|
||||
into "$buildDir/react-ndk/exported"
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "22.0.1"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
ndk {
|
||||
moduleName "reactnativejni"
|
||||
}
|
||||
|
||||
buildConfigField 'boolean', 'IS_INTERNAL_BUILD', 'false'
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
jni.srcDirs = []
|
||||
jniLibs.srcDir "$buildDir/react-ndk/exported"
|
||||
res.srcDirs = ['src/main/res/devsupport', 'src/main/res/shell']
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
compileTask -> compileTask.dependsOn packageReactNdkLibs
|
||||
}
|
||||
|
||||
clean.dependsOn cleanReactNdkLib
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:22.2.0'
|
||||
compile 'com.facebook.fresco:fresco:0.6.1'
|
||||
compile 'com.facebook.fresco:imagepipeline-okhttp:0.6.1'
|
||||
compile 'com.fasterxml.jackson.core:jackson-core:2.2.3'
|
||||
compile 'com.google.code.findbugs:jsr305:3.0.0'
|
||||
compile 'com.squareup.okhttp:okhttp:2.4.0'
|
||||
compile 'com.squareup.okhttp:okhttp-ws:2.4.0'
|
||||
compile 'com.squareup.okio:okio:1.5.0'
|
||||
compile 'org.webkit:android-jsc:r174650'
|
||||
}
|
||||
|
||||
apply from: 'release.gradle'
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
VERSION_NAME=0.11.1-SNAPSHOT
|
||||
GROUP=com.facebook.react
|
||||
|
||||
POM_NAME=ReactNative
|
||||
POM_ARTIFACT_ID=react-native
|
||||
POM_PACKAGING=aar
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright 2015-present Facebook. All Rights Reserved.
|
||||
|
||||
apply plugin: 'maven'
|
||||
apply plugin: 'signing'
|
||||
|
||||
// Gradle tasks for publishing to maven
|
||||
// 1) To install in local maven repo use :installArchives task
|
||||
// 2) To upload artifact to maven central use: :uploadArchives (you'd need to have the permission to do that)
|
||||
|
||||
def isReleaseBuild() {
|
||||
return VERSION_NAME.contains('SNAPSHOT') == false
|
||||
}
|
||||
|
||||
def getRepositoryUrl() {
|
||||
return hasProperty('repositoryUrl') ? property('repositoryUrl') : 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
|
||||
}
|
||||
|
||||
def getRepositoryUsername() {
|
||||
return hasProperty('repositoryUsername') ? property('repositoryUsername') : ''
|
||||
}
|
||||
|
||||
def getRepositoryPassword() {
|
||||
return hasProperty('repositoryPassword') ? property('repositoryPassword') : ''
|
||||
}
|
||||
|
||||
def configureReactNativePom(def pom) {
|
||||
pom.project {
|
||||
name POM_NAME
|
||||
artifactId POM_ARTIFACT_ID
|
||||
packaging POM_PACKAGING
|
||||
description 'A framework for building native apps with React'
|
||||
url 'https://github.com/facebook/react-native'
|
||||
|
||||
scm {
|
||||
url 'https://github.com/facebook/react-native.git'
|
||||
connection 'scm:git:https://github.com/facebook/react-native.git'
|
||||
developerConnection 'scm:git:git@github.com:facebook/react-native.git'
|
||||
}
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name 'BSD License'
|
||||
url 'https://github.com/facebook/react-native/blob/master/LICENSE'
|
||||
distribution 'repo'
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id 'facebook'
|
||||
name 'Facebook'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate { project ->
|
||||
|
||||
task androidJavadoc(type: Javadoc) {
|
||||
source = android.sourceSets.main.java.srcDirs
|
||||
classpath += files(android.bootClasspath)
|
||||
classpath += files(project.getConfigurations().getByName('compile').asList())
|
||||
include '**/*.java'
|
||||
exclude '**/ReactBuildConfig.java'
|
||||
}
|
||||
|
||||
task androidJavadocJar(type: Jar, dependsOn: androidJavadoc) {
|
||||
classifier = 'javadoc'
|
||||
from androidJavadoc.destinationDir
|
||||
}
|
||||
|
||||
task androidSourcesJar(type: Jar) {
|
||||
classifier = 'sources'
|
||||
from android.sourceSets.main.java.srcDirs
|
||||
include '**/*.java'
|
||||
}
|
||||
|
||||
android.libraryVariants.all { variant ->
|
||||
def name = variant.name.capitalize()
|
||||
task "jar${name}"(type: Jar, dependsOn: variant.javaCompile) {
|
||||
from variant.javaCompile.destinationDir
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives androidSourcesJar
|
||||
// TODO Make Javadoc generation work with Java 1.8, currently only works with 1.7
|
||||
// archives androidJavadocJar
|
||||
}
|
||||
|
||||
version = VERSION_NAME
|
||||
group = GROUP
|
||||
|
||||
signing {
|
||||
required { isReleaseBuild() && gradle.taskGraph.hasTask('uploadArchives') }
|
||||
sign configurations.archives
|
||||
}
|
||||
|
||||
uploadArchives {
|
||||
configuration = configurations.archives
|
||||
repositories.mavenDeployer {
|
||||
beforeDeployment {
|
||||
MavenDeployment deployment -> signing.signPom(deployment)
|
||||
}
|
||||
|
||||
repository(url: getRepositoryUrl()) {
|
||||
authentication(
|
||||
userName: getRepositoryUsername(),
|
||||
password: getRepositoryPassword())
|
||||
|
||||
}
|
||||
|
||||
configureReactNativePom pom
|
||||
}
|
||||
}
|
||||
|
||||
task installArchives(type: Upload) {
|
||||
configuration = configurations.archives
|
||||
repositories.mavenDeployer {
|
||||
beforeDeployment {
|
||||
MavenDeployment deployment -> signing.signPom(deployment)
|
||||
}
|
||||
|
||||
repository url: "file://${System.properties['user.home']}/.m2/repository"
|
||||
configureReactNativePom pom
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.facebook.react">
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application />
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<0a1e3b1f834f027e7a5bc5303f945b0e>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
public enum CSSAlign {
|
||||
AUTO,
|
||||
FLEX_START,
|
||||
CENTER,
|
||||
FLEX_END,
|
||||
STRETCH,
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<755069c4747cc9fc5624d70e5130e3d1>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
public class CSSConstants {
|
||||
|
||||
public static final float UNDEFINED = Float.NaN;
|
||||
|
||||
public static boolean isUndefined(float value) {
|
||||
return Float.compare(value, UNDEFINED) == 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<5dc7f205706089599859188712b3bd8a>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
public enum CSSDirection {
|
||||
INHERIT,
|
||||
LTR,
|
||||
RTL,
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<6183a87290f3acd1caef7b6301bbf3a7>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
public enum CSSFlexDirection {
|
||||
COLUMN,
|
||||
COLUMN_REVERSE,
|
||||
ROW,
|
||||
ROW_REVERSE
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<619fbefba1cfee797bbc7dd18e22f50c>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
public enum CSSJustify {
|
||||
FLEX_START,
|
||||
CENTER,
|
||||
FLEX_END,
|
||||
SPACE_BETWEEN,
|
||||
SPACE_AROUND,
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<153b6759d2dd8fe8cf6d58a422450b96>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
/**
|
||||
* Where the output of {@link LayoutEngine#layoutNode(CSSNode, float)} will go in the CSSNode.
|
||||
*/
|
||||
public class CSSLayout {
|
||||
|
||||
public float top;
|
||||
public float left;
|
||||
public float right;
|
||||
public float bottom;
|
||||
public float width = CSSConstants.UNDEFINED;
|
||||
public float height = CSSConstants.UNDEFINED;
|
||||
public CSSDirection direction = CSSDirection.LTR;
|
||||
|
||||
/**
|
||||
* This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)}
|
||||
*/
|
||||
public void resetResult() {
|
||||
left = 0;
|
||||
top = 0;
|
||||
right = 0;
|
||||
bottom = 0;
|
||||
width = CSSConstants.UNDEFINED;
|
||||
height = CSSConstants.UNDEFINED;
|
||||
direction = CSSDirection.LTR;
|
||||
}
|
||||
|
||||
public void copy(CSSLayout layout) {
|
||||
left = layout.left;
|
||||
top = layout.top;
|
||||
right = layout.right;
|
||||
bottom = layout.bottom;
|
||||
width = layout.width;
|
||||
height = layout.height;
|
||||
direction = layout.direction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "layout: {" +
|
||||
"left: " + left + ", " +
|
||||
"top: " + top + ", " +
|
||||
"width: " + width + ", " +
|
||||
"height: " + height +
|
||||
"direction: " + direction +
|
||||
"}";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<9d48f3d4330e7b6cba0fff7d8f1e8b0c>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
/**
|
||||
* A context for holding values local to a given instance of layout computation.
|
||||
*
|
||||
* This is necessary for making layout thread-safe. A separate instance should
|
||||
* be used when {@link CSSNode#calculateLayout} is called concurrently on
|
||||
* different node hierarchies.
|
||||
*/
|
||||
public class CSSLayoutContext {
|
||||
/*package*/ final MeasureOutput measureOutput = new MeasureOutput();
|
||||
}
|
|
@ -0,0 +1,396 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<c3a1298e36789dcda4cc2776d48646a7>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
|
||||
/**
|
||||
* A CSS Node. It has a style object you can manipulate at {@link #style}. After calling
|
||||
* {@link #calculateLayout()}, {@link #layout} will be filled with the results of the layout.
|
||||
*/
|
||||
public class CSSNode {
|
||||
|
||||
private static enum LayoutState {
|
||||
/**
|
||||
* Some property of this node or its children has changes and the current values in
|
||||
* {@link #layout} are not valid.
|
||||
*/
|
||||
DIRTY,
|
||||
|
||||
/**
|
||||
* This node has a new layout relative to the last time {@link #markLayoutSeen()} was called.
|
||||
*/
|
||||
HAS_NEW_LAYOUT,
|
||||
|
||||
/**
|
||||
* {@link #layout} is valid for the node's properties and this layout has been marked as
|
||||
* having been seen.
|
||||
*/
|
||||
UP_TO_DATE,
|
||||
}
|
||||
|
||||
public static interface MeasureFunction {
|
||||
|
||||
/**
|
||||
* Should measure the given node and put the result in the given MeasureOutput.
|
||||
*
|
||||
* NB: measure is NOT guaranteed to be threadsafe/re-entrant safe!
|
||||
*/
|
||||
public void measure(CSSNode node, float width, MeasureOutput measureOutput);
|
||||
}
|
||||
|
||||
// VisibleForTesting
|
||||
/*package*/ final CSSStyle style = new CSSStyle();
|
||||
/*package*/ final CSSLayout layout = new CSSLayout();
|
||||
/*package*/ final CachedCSSLayout lastLayout = new CachedCSSLayout();
|
||||
|
||||
public int lineIndex = 0;
|
||||
|
||||
private @Nullable ArrayList<CSSNode> mChildren;
|
||||
private @Nullable CSSNode mParent;
|
||||
private @Nullable MeasureFunction mMeasureFunction = null;
|
||||
private LayoutState mLayoutState = LayoutState.DIRTY;
|
||||
|
||||
public int getChildCount() {
|
||||
return mChildren == null ? 0 : mChildren.size();
|
||||
}
|
||||
|
||||
public CSSNode getChildAt(int i) {
|
||||
Assertions.assertNotNull(mChildren);
|
||||
return mChildren.get(i);
|
||||
}
|
||||
|
||||
public void addChildAt(CSSNode child, int i) {
|
||||
if (child.mParent != null) {
|
||||
throw new IllegalStateException("Child already has a parent, it must be removed first.");
|
||||
}
|
||||
if (mChildren == null) {
|
||||
// 4 is kinda arbitrary, but the default of 10 seems really high for an average View.
|
||||
mChildren = new ArrayList<>(4);
|
||||
}
|
||||
|
||||
mChildren.add(i, child);
|
||||
child.mParent = this;
|
||||
dirty();
|
||||
}
|
||||
|
||||
public CSSNode removeChildAt(int i) {
|
||||
Assertions.assertNotNull(mChildren);
|
||||
CSSNode removed = mChildren.remove(i);
|
||||
removed.mParent = null;
|
||||
dirty();
|
||||
return removed;
|
||||
}
|
||||
|
||||
public @Nullable CSSNode getParent() {
|
||||
return mParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the index of the given child, or -1 if the child doesn't exist in this node.
|
||||
*/
|
||||
public int indexOf(CSSNode child) {
|
||||
Assertions.assertNotNull(mChildren);
|
||||
return mChildren.indexOf(child);
|
||||
}
|
||||
|
||||
public void setMeasureFunction(MeasureFunction measureFunction) {
|
||||
if (!valuesEqual(mMeasureFunction, measureFunction)) {
|
||||
mMeasureFunction = measureFunction;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMeasureDefined() {
|
||||
return mMeasureFunction != null;
|
||||
}
|
||||
|
||||
/*package*/ MeasureOutput measure(MeasureOutput measureOutput, float width) {
|
||||
if (!isMeasureDefined()) {
|
||||
throw new RuntimeException("Measure function isn't defined!");
|
||||
}
|
||||
measureOutput.height = CSSConstants.UNDEFINED;
|
||||
measureOutput.width = CSSConstants.UNDEFINED;
|
||||
Assertions.assertNotNull(mMeasureFunction).measure(this, width, measureOutput);
|
||||
return measureOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual layout and saves the results in {@link #layout}
|
||||
*/
|
||||
public void calculateLayout(CSSLayoutContext layoutContext) {
|
||||
layout.resetResult();
|
||||
LayoutEngine.layoutNode(layoutContext, this, CSSConstants.UNDEFINED, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link LayoutState#DIRTY}.
|
||||
*/
|
||||
protected boolean isDirty() {
|
||||
return mLayoutState == LayoutState.DIRTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link LayoutState#HAS_NEW_LAYOUT}.
|
||||
*/
|
||||
public boolean hasNewLayout() {
|
||||
return mLayoutState == LayoutState.HAS_NEW_LAYOUT;
|
||||
}
|
||||
|
||||
protected void dirty() {
|
||||
if (mLayoutState == LayoutState.DIRTY) {
|
||||
return;
|
||||
} else if (mLayoutState == LayoutState.HAS_NEW_LAYOUT) {
|
||||
throw new IllegalStateException("Previous layout was ignored! markLayoutSeen() never called");
|
||||
}
|
||||
|
||||
mLayoutState = LayoutState.DIRTY;
|
||||
|
||||
if (mParent != null) {
|
||||
mParent.dirty();
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ void markHasNewLayout() {
|
||||
mLayoutState = LayoutState.HAS_NEW_LAYOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the node that the current values in {@link #layout} have been seen. Subsequent calls
|
||||
* to {@link #hasNewLayout()} will return false until this node is laid out with new parameters.
|
||||
* You must call this each time the layout is generated if the node has a new layout.
|
||||
*/
|
||||
public void markLayoutSeen() {
|
||||
if (!hasNewLayout()) {
|
||||
throw new IllegalStateException("Expected node to have a new layout to be seen!");
|
||||
}
|
||||
|
||||
mLayoutState = LayoutState.UP_TO_DATE;
|
||||
}
|
||||
|
||||
private void toStringWithIndentation(StringBuilder result, int level) {
|
||||
// Spaces and tabs are dropped by IntelliJ logcat integration, so rely on __ instead.
|
||||
StringBuilder indentation = new StringBuilder();
|
||||
for (int i = 0; i < level; ++i) {
|
||||
indentation.append("__");
|
||||
}
|
||||
|
||||
result.append(indentation.toString());
|
||||
result.append(layout.toString());
|
||||
|
||||
if (getChildCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
result.append(", children: [\n");
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
getChildAt(i).toStringWithIndentation(result, level + 1);
|
||||
result.append("\n");
|
||||
}
|
||||
result.append(indentation + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
this.toStringWithIndentation(sb, 0);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected boolean valuesEqual(float f1, float f2) {
|
||||
return FloatUtil.floatsEqual(f1, f2);
|
||||
}
|
||||
|
||||
protected <T> boolean valuesEqual(@Nullable T o1, @Nullable T o2) {
|
||||
if (o1 == null) {
|
||||
return o2 == null;
|
||||
}
|
||||
return o1.equals(o2);
|
||||
}
|
||||
|
||||
public void setDirection(CSSDirection direction) {
|
||||
if (!valuesEqual(style.direction, direction)) {
|
||||
style.direction = direction;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFlexDirection(CSSFlexDirection flexDirection) {
|
||||
if (!valuesEqual(style.flexDirection, flexDirection)) {
|
||||
style.flexDirection = flexDirection;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setJustifyContent(CSSJustify justifyContent) {
|
||||
if (!valuesEqual(style.justifyContent, justifyContent)) {
|
||||
style.justifyContent = justifyContent;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setAlignItems(CSSAlign alignItems) {
|
||||
if (!valuesEqual(style.alignItems, alignItems)) {
|
||||
style.alignItems = alignItems;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setAlignSelf(CSSAlign alignSelf) {
|
||||
if (!valuesEqual(style.alignSelf, alignSelf)) {
|
||||
style.alignSelf = alignSelf;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setPositionType(CSSPositionType positionType) {
|
||||
if (!valuesEqual(style.positionType, positionType)) {
|
||||
style.positionType = positionType;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setWrap(CSSWrap flexWrap) {
|
||||
if (!valuesEqual(style.flexWrap, flexWrap)) {
|
||||
style.flexWrap = flexWrap;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFlex(float flex) {
|
||||
if (!valuesEqual(style.flex, flex)) {
|
||||
style.flex = flex;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setMargin(int spacingType, float margin) {
|
||||
if (style.margin.set(spacingType, margin)) {
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setPadding(int spacingType, float padding) {
|
||||
if (style.padding.set(spacingType, padding)) {
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setBorder(int spacingType, float border) {
|
||||
if (style.border.set(spacingType, border)) {
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setPositionTop(float positionTop) {
|
||||
if (!valuesEqual(style.positionTop, positionTop)) {
|
||||
style.positionTop = positionTop;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setPositionBottom(float positionBottom) {
|
||||
if (!valuesEqual(style.positionBottom, positionBottom)) {
|
||||
style.positionBottom = positionBottom;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setPositionLeft(float positionLeft) {
|
||||
if (!valuesEqual(style.positionLeft, positionLeft)) {
|
||||
style.positionLeft = positionLeft;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setPositionRight(float positionRight) {
|
||||
if (!valuesEqual(style.positionRight, positionRight)) {
|
||||
style.positionRight = positionRight;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setStyleWidth(float width) {
|
||||
if (!valuesEqual(style.width, width)) {
|
||||
style.width = width;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setStyleHeight(float height) {
|
||||
if (!valuesEqual(style.height, height)) {
|
||||
style.height = height;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public float getLayoutX() {
|
||||
return layout.left;
|
||||
}
|
||||
|
||||
public float getLayoutY() {
|
||||
return layout.top;
|
||||
}
|
||||
|
||||
public float getLayoutWidth() {
|
||||
return layout.width;
|
||||
}
|
||||
|
||||
public float getLayoutHeight() {
|
||||
return layout.height;
|
||||
}
|
||||
|
||||
public CSSDirection getLayoutDirection() {
|
||||
return layout.direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's padding, as defined by style + default padding.
|
||||
*/
|
||||
public Spacing getStylePadding() {
|
||||
return style.padding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's width, as defined in the style.
|
||||
*/
|
||||
public float getStyleWidth() {
|
||||
return style.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's height, as defined in the style.
|
||||
*/
|
||||
public float getStyleHeight() {
|
||||
return style.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's direction, as defined in the style.
|
||||
*/
|
||||
public CSSDirection getStyleDirection() {
|
||||
return style.direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a default padding (left/top/right/bottom) for this node.
|
||||
*/
|
||||
public void setDefaultPadding(int spacingType, float padding) {
|
||||
if (style.padding.setDefault(spacingType, padding)) {
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<a53da9b13bd6e7b03fd743adf0e536b3>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
public enum CSSPositionType {
|
||||
RELATIVE,
|
||||
ABSOLUTE,
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<ec76950a22dda1c6e98eafc15ccf7cd3>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
/**
|
||||
* The CSS style definition for a {@link CSSNode}.
|
||||
*/
|
||||
public class CSSStyle {
|
||||
|
||||
public CSSDirection direction = CSSDirection.INHERIT;
|
||||
public CSSFlexDirection flexDirection = CSSFlexDirection.COLUMN;
|
||||
public CSSJustify justifyContent = CSSJustify.FLEX_START;
|
||||
public CSSAlign alignContent = CSSAlign.FLEX_START;
|
||||
public CSSAlign alignItems = CSSAlign.STRETCH;
|
||||
public CSSAlign alignSelf = CSSAlign.AUTO;
|
||||
public CSSPositionType positionType = CSSPositionType.RELATIVE;
|
||||
public CSSWrap flexWrap = CSSWrap.NOWRAP;
|
||||
public float flex;
|
||||
|
||||
public Spacing margin = new Spacing();
|
||||
public Spacing padding = new Spacing();
|
||||
public Spacing border = new Spacing();
|
||||
|
||||
public float positionTop = CSSConstants.UNDEFINED;
|
||||
public float positionBottom = CSSConstants.UNDEFINED;
|
||||
public float positionLeft = CSSConstants.UNDEFINED;
|
||||
public float positionRight = CSSConstants.UNDEFINED;
|
||||
|
||||
public float width = CSSConstants.UNDEFINED;
|
||||
public float height = CSSConstants.UNDEFINED;
|
||||
|
||||
public float minWidth = CSSConstants.UNDEFINED;
|
||||
public float minHeight = CSSConstants.UNDEFINED;
|
||||
|
||||
public float maxWidth = CSSConstants.UNDEFINED;
|
||||
public float maxHeight = CSSConstants.UNDEFINED;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<21dab9bd1acf5892ad09370b69b7dd71>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
public enum CSSWrap {
|
||||
NOWRAP,
|
||||
WRAP,
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
|
||||
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
||||
// @generated SignedSource<<8276834951a75286a0b6d4a980bc43ce>>
|
||||
|
||||
package com.facebook.csslayout;
|
||||
|
||||
/**
|
||||
* CSSLayout with additional information about the conditions under which it was generated.
|
||||
* {@link #requestedWidth} and {@link #requestedHeight} are the width and height the parent set on
|
||||
* this node before calling layout visited us.
|
||||
*/
|
||||
public class CachedCSSLayout extends CSSLayout {
|
||||
|
||||
public float requestedWidth = CSSConstants.UNDEFINED;
|
||||
public float requestedHeight = CSSConstants.UNDEFINED;
|
||||
public float parentMaxWidth = CSSConstants.UNDEFINED;
|
||||
}
|