diff --git a/Examples/UIExplorer/js/RTLExample.js b/Examples/UIExplorer/js/RTLExample.js
new file mode 100644
index 00000000000..82e50c7b006
--- /dev/null
+++ b/Examples/UIExplorer/js/RTLExample.js
@@ -0,0 +1,411 @@
+/**
+ * Copyright (c) 2013-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.
+ *
+ * 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.
+ *
+ * Here's the Right-to-Left(RTL) example page with:
+ * - a button to force flip UI layout to RTL
+ * - base layout example and text example
+ * - two custom examples for RTL design
+ *
+ * @flow
+ */
+'use strict';
+
+const React = require('react');
+const ReactNative = require('react-native');
+const {
+ Alert,
+ Animated,
+ I18nManager,
+ Image,
+ PanResponder,
+ PixelRatio,
+ ScrollView,
+ StyleSheet,
+ Text,
+ TouchableWithoutFeedback,
+ Switch,
+ View,
+} = ReactNative;
+
+const UIExplorerPage = require('./UIExplorerPage');
+const UIExplorerBlock = require('./UIExplorerBlock');
+
+const AnimatedImage = Animated.createAnimatedComponent(Image);
+
+type State = {
+ toggleStatus: any,
+ pan: Object,
+ linear: Object,
+ isRTL: boolean,
+ windowWidth: number,
+};
+
+const SCALE = PixelRatio.get();
+const IMAGE_DIMENSION = 100 * SCALE;
+const IMAGE_SIZE = [IMAGE_DIMENSION, IMAGE_DIMENSION];
+
+const IS_RTL = I18nManager.isRTL;
+
+function ListItem(props) {
+ return (
+
+
+
+
+
+
+
+ Text
+ Text
+ Text
+
+
+
+
+
+
+ Button
+
+
+
+
+ );
+}
+
+function TextAlignmentExample(props) {
+ return (
+
+
+
+ Left-to-Right language without text alignment.
+
+
+ من اليمين إلى اليسار اللغة دون محاذاة النص .
+
+
+ מימין לשמאל השפה בלי יישור טקסט .
+
+
+
+ );
+}
+
+function AnimationBlock(props) {
+ return (
+
+
+
+
+
+ );
+}
+
+class RTLExample extends React.Component {
+ static title = 'RTLExample';
+ static description = 'Examples to show how to apply components to RTL layout.';
+
+ state: State;
+ props: any;
+ _panResponder: Object;
+
+ constructor(props: Object) {
+ super(props);
+ const pan = new Animated.ValueXY();
+
+ this._panResponder = PanResponder.create({
+ onStartShouldSetPanResponder: () => true,
+ onPanResponderGrant: this._onPanResponderGrant,
+ onPanResponderMove: Animated.event([
+ null, {dx: pan.x, dy: pan.y},
+ ]),
+ onPanResponderRelease: this._onPanResponderEnd,
+ onPanResponderTerminate: this._onPanResponderEnd,
+ });
+
+ this.state = {
+ toggleStatus: {},
+ pan,
+ linear: new Animated.Value(0),
+ isRTL: IS_RTL,
+ windowWidth: 0,
+ };
+ }
+
+ render() {
+ return (
+
+
+
+
+
+ {this.state.isRTL ? 'Right-to-Left' : 'Left-to-Right'}
+
+
+
+
+
+
+ forceRTL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Without directional meaning
+
+
+
+
+
+ With directional meaning
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ _onLayout = (e: Object) => {
+ this.setState({
+ windowWidth: e.nativeEvent.layout.width,
+ });
+ };
+
+ _onDirectionChange = () => {
+ I18nManager.forceRTL(!this.state.isRTL);
+ this.setState({isRTL: !this.state.isRTL});
+ Alert.alert('Reload this page',
+ 'Please reload this page to change the UI direction! ' +
+ 'All examples in this app will be affected. ' +
+ 'Check them out to see what they look like in RTL layout.'
+ );
+ };
+
+ _linearTap = (refName: string, e: Object) => {
+ this.setState({
+ toggleStatus: {
+ ...this.state.toggleStatus,
+ [refName]: !this.state.toggleStatus[refName],
+ },
+ });
+ const offset = IMAGE_SIZE[0] / SCALE / 2 + 10;
+ const toMaxDistance =
+ IS_RTL ?
+ -this.state.windowWidth / 2 + offset :
+ this.state.windowWidth / 2 - offset;
+ Animated.timing(this.state.linear, {
+ toValue: this.state.toggleStatus[refName] ? toMaxDistance : 0,
+ duration: 2000,
+ }).start();
+ };
+
+ _onPanResponderGrant = (e: Object, gestureState: Object) => {
+ this.state.pan.stopAnimation(value => {
+ this.state.pan.setOffset(value);
+ });
+ };
+
+ _onPanResponderEnd = (e: Object, gestureState: Object) => {
+ this.state.pan.flattenOffset();
+ Animated.sequence([
+ Animated.decay(this.state.pan, {
+ velocity: {x: gestureState.vx, y: gestureState.vy},
+ deceleration: 0.995,
+ }),
+ Animated.spring(this.state.pan, {toValue: {x: 0, y: 0}}),
+ ]).start();
+ };
+}
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: '#e9eaed',
+ paddingTop: 15,
+ },
+ directionBox: {
+ flex: 1,
+ backgroundColor: '#f8f8f8',
+ borderWidth: 0.5,
+ borderColor: 'black',
+ },
+ directionText: {
+ padding: 10,
+ fontSize: 16,
+ textAlign: 'center',
+ fontWeight: 'bold',
+ },
+ switchRowTextView: {
+ flex: 1,
+ marginBottom: 5,
+ marginTop: 5,
+ textAlign: 'center',
+ },
+ switchRowSwitchView: {
+ flex: 3,
+ },
+ rightAlignStyle: {
+ right: 10,
+ position: 'absolute',
+ },
+ list: {
+ height: 120,
+ marginBottom: 5,
+ borderTopWidth: 0.5,
+ borderLeftWidth: 0.5,
+ borderRightWidth: 0.5,
+ borderColor: '#e5e5e5',
+ },
+ row: {
+ height: 60,
+ flexDirection: 'row',
+ borderBottomWidth: 0.5,
+ borderColor: '#e5e5e5',
+ },
+ column1: {
+ width: 60,
+ },
+ column2: {
+ flex: 2.5,
+ padding: 6,
+ },
+ column3: {
+ flex: 1.5,
+ },
+ icon: {
+ width: 48,
+ height: 48,
+ margin: 6,
+ borderWidth: 0.5,
+ borderColor: '#e5e5e5',
+ },
+ withRTLStyle: {
+ transform: [{scaleX: IS_RTL ? -1 : 1}],
+ },
+ image: {
+ left: 30,
+ width: 48,
+ height: 48,
+ },
+ img: {
+ width: IMAGE_SIZE[0] / SCALE,
+ height: IMAGE_SIZE[1] / SCALE,
+ },
+ view: {
+ flex: 1,
+ },
+ block: {
+ padding: 10,
+ alignItems: 'center',
+ },
+ smallButton: {
+ top: 18,
+ borderRadius: 5,
+ height: 24,
+ width: 64,
+ backgroundColor: '#e5e5e5',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ fontSizeSmall:{
+ fontSize: 10,
+ },
+ fontSizeExtraSmall:{
+ fontSize: 8,
+ },
+ textAlignLeft: {
+ textAlign: 'left',
+ },
+ textAlignRight: {
+ textAlign: 'right',
+ },
+ textBox: {
+ width: 28,
+ },
+ flexDirectionRow: {
+ flexDirection: 'row',
+ },
+});
+
+module.exports = RTLExample;
diff --git a/Examples/UIExplorer/js/UIExplorerList.android.js b/Examples/UIExplorer/js/UIExplorerList.android.js
index f35a34db437..2d169317265 100644
--- a/Examples/UIExplorer/js/UIExplorerList.android.js
+++ b/Examples/UIExplorer/js/UIExplorerList.android.js
@@ -189,6 +189,10 @@ const APIExamples = [
key: 'PointerEventsExample',
module: require('./PointerEventsExample'),
},
+ {
+ key: 'RTLExample',
+ module: require('./RTLExample'),
+ },
{
key: 'ShareExample',
module: require('./ShareExample'),
diff --git a/Examples/UIExplorer/js/UIExplorerList.ios.js b/Examples/UIExplorer/js/UIExplorerList.ios.js
index 2e996c06580..ecbb0f44e72 100644
--- a/Examples/UIExplorer/js/UIExplorerList.ios.js
+++ b/Examples/UIExplorer/js/UIExplorerList.ios.js
@@ -251,6 +251,10 @@ const APIExamples: Array = [
key: 'RCTRootViewIOSExample',
module: require('./RCTRootViewIOSExample'),
},
+ {
+ key: 'RTLExample',
+ module: require('./RTLExample'),
+ },
{
key: 'ShareExample',
module: require('./ShareExample'),
diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js
index c3f7f2bcb41..11dec816d3b 100644
--- a/Libraries/react-native/react-native.js
+++ b/Libraries/react-native/react-native.js
@@ -87,6 +87,7 @@ const ReactNative = {
get DatePickerAndroid() { return require('DatePickerAndroid'); },
get Dimensions() { return require('Dimensions'); },
get Easing() { return require('Easing'); },
+ get I18nManager() { return require('I18nManager'); },
get ImagePickerIOS() { return require('ImagePickerIOS'); },
get IntentAndroid() { return require('IntentAndroid'); },
get InteractionManager() { return require('InteractionManager'); },
diff --git a/Libraries/react-native/react-native.js.flow b/Libraries/react-native/react-native.js.flow
index d7c9108e198..020ddf885d3 100644
--- a/Libraries/react-native/react-native.js.flow
+++ b/Libraries/react-native/react-native.js.flow
@@ -99,6 +99,7 @@ var ReactNative = {
DatePickerAndroid: require('DatePickerAndroid'),
Dimensions: require('Dimensions'),
Easing: require('Easing'),
+ I18nManager: require('I18nManager'),
ImagePickerIOS: require('ImagePickerIOS'),
IntentAndroid: require('IntentAndroid'),
InteractionManager: require('InteractionManager'),