From 53d3f94175d42689e28aebd99abf97ccec3d73c2 Mon Sep 17 00:00:00 2001 From: Mengjue Wang Date: Thu, 18 Aug 2016 20:49:25 -0700 Subject: [PATCH] Create a RTLExample for OSS Reviewed By: fkgozali Differential Revision: D3740559 fbshipit-source-id: 79271abe7ef5c723e934da24884ff777aea0acc3 --- Examples/UIExplorer/js/RTLExample.js | 411 ++++++++++++++++++ .../UIExplorer/js/UIExplorerList.android.js | 4 + Examples/UIExplorer/js/UIExplorerList.ios.js | 4 + Libraries/react-native/react-native.js | 1 + Libraries/react-native/react-native.js.flow | 1 + 5 files changed, 421 insertions(+) create mode 100644 Examples/UIExplorer/js/RTLExample.js 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'),