Skip to content

Commit

Permalink
Open source Modal
Browse files Browse the repository at this point in the history
Summary: This open sources an internal Modal View

Reviewed By: mkonicek

Differential Revision: D3065229

fb-gh-sync-id: 763996aef375883d94f70e617bfc7835a9cecb6f
shipit-source-id: 763996aef375883d94f70e617bfc7835a9cecb6f
  • Loading branch information
Dave Miller authored and Facebook Github Bot 2 committed Mar 18, 2016
1 parent c76523f commit db7a154
Show file tree
Hide file tree
Showing 14 changed files with 569 additions and 7 deletions.
10 changes: 6 additions & 4 deletions Examples/UIExplorer/ModalExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var React = require('react-native');
var {
Modal,
StyleSheet,
SwitchIOS,
Switch,
Text,
TouchableHighlight,
View,
Expand Down Expand Up @@ -96,7 +96,9 @@ var ModalExample = React.createClass({
<Modal
animated={this.state.animated}
transparent={this.state.transparent}
visible={this.state.modalVisible}>
visible={this.state.modalVisible}
onRequestClose={() => {this._setModalVisible(false)}}
>
<View style={[styles.container, modalBackgroundStyle]}>
<View style={[styles.innerContainer, innerContainerTransparentStyle]}>
<Text>This modal was presented {this.state.animated ? 'with' : 'without'} animation.</Text>
Expand All @@ -111,12 +113,12 @@ var ModalExample = React.createClass({

<View style={styles.row}>
<Text style={styles.rowTitle}>Animated</Text>
<SwitchIOS value={this.state.animated} onValueChange={this._toggleAnimated} />
<Switch value={this.state.animated} onValueChange={this._toggleAnimated} />
</View>

<View style={styles.row}>
<Text style={styles.rowTitle}>Transparent</Text>
<SwitchIOS value={this.state.transparent} onValueChange={this._toggleTransparent} />
<Switch value={this.state.transparent} onValueChange={this._toggleTransparent} />
</View>

<Button onPress={this._setModalVisible.bind(this, true)}>
Expand Down
4 changes: 4 additions & 0 deletions Examples/UIExplorer/UIExplorerList.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ var ComponentExamples: Array<UIExplorerExample> = [
key: 'ListViewExample',
module: require('./ListViewExample'),
},
{
key: 'ModalExample',
module: require('./ModalExample'),
},
{
key: 'PickerAndroidExample',
module: require('./PickerAndroidExample'),
Expand Down
2 changes: 0 additions & 2 deletions Libraries/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ var RCTModalHostView = requireNativeComponent('RCTModalHostView', null);
* Navigator instead of Modal. With a top-level Navigator, you have more control
* over how to present the modal scene over the rest of your app by using the
* configureScene property.
*
* This component is only available in iOS at this time.
*/
class Modal extends React.Component {
render(): ?ReactElement {
Expand Down
2 changes: 1 addition & 1 deletion ReactAndroid/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ android {
sourceSets.main {
jni.srcDirs = []
jniLibs.srcDir "$buildDir/react-ndk/exported"
res.srcDirs = ['src/main/res/devsupport', 'src/main/res/shell']
res.srcDirs = ['src/main/res/devsupport', 'src/main/res/shell', 'src/main/res/views/modal']
java {
srcDirs = ['src/main/java', 'src/main/libraries/soloader']
exclude 'com/facebook/react/processing'
Expand Down
25 changes: 25 additions & 0 deletions ReactAndroid/src/main/java/com/facebook/react/views/modal/BUCK
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
include_defs('//ReactAndroid/DEFS')

android_library(
name = 'modal',
srcs = glob(['*.java']),
deps = [
react_native_target('res:modal'),
react_native_target('java/com/facebook/react/bridge:bridge'),
react_native_target('java/com/facebook/react/common:common'),
react_native_target('java/com/facebook/csslayout:csslayout'),
react_native_target('java/com/facebook/react/touch:touch'),
react_native_target('java/com/facebook/react/uimanager:uimanager'),
react_native_target('java/com/facebook/react/uimanager/annotations:annotations'),
react_native_target('java/com/facebook/react/views/view:view'),
react_native_dep('third-party/java/infer-annotations:infer-annotations'),
react_native_dep('third-party/java/jsr-305:jsr-305'),
],
visibility = [
'PUBLIC',
],
)

project_config(
src_target = ':modal',
)
Original file line number Diff line number Diff line change
@@ -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.
*/

package com.facebook.react.views.modal;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Point;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;

import com.facebook.react.uimanager.LayoutShadowNode;

/**
* We implement the Modal by using an Android Dialog. That will fill the entire window of the
* application. To get layout to work properly, we need to layout all the elements within the
* Modal as if they can fill the entire window. To do that, we need to explicitly set the
* styleWidth and styleHeight on the LayoutShadowNode to be the window size. This will then cause
* the children to layout as if they can fill the window.
*
* To get that we use information from the WindowManager and default Display. We don't use
* DisplayMetricsHolder because it returns values that include the status bar. We only want the
* values of what will actually be shown on screen.
*/
class ModalHostShadowNode extends LayoutShadowNode {

private final Point mMinPoint = new Point();
private final Point mMaxPoint = new Point();
/**
* Once we have all the properties for the we need to measure the window and set the style
* width and height appropriately so that layout is done properly for the view assuming it
* fills the entire window instead of the place it is in the view tree
*/
@Override
@TargetApi(16)
public void onAfterUpdateTransaction() {
super.onAfterUpdateTransaction();

Context context = getThemedContext();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
// getCurrentSizeRange will return the min and max width and height that the window can be
display.getCurrentSizeRange(mMinPoint, mMaxPoint);

int width, height;
int rotation = display.getRotation();
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
// If we are vertical the width value comes from min width and height comes from max height
width = mMinPoint.x;
height = mMaxPoint.y;
} else {
// If we are horizontal the width value comes from max width and height comes from min height
width = mMaxPoint.x;
height = mMinPoint.y;
}
setStyleWidth(width);
setStyleHeight(height);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* 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.
*/

package com.facebook.react.views.modal;

import java.util.Map;

import android.content.DialogInterface;

import com.facebook.react.common.MapBuilder;
import com.facebook.react.common.SystemClock;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.EventDispatcher;

/**
* View manager for {@link ReactModalHostView} components.
*/
public class ReactModalHostManager extends ViewGroupManager<ReactModalHostView> {

private static final String REACT_CLASS = "RCTModalHostView";

@Override
public String getName() {
return REACT_CLASS;
}

@Override
protected ReactModalHostView createViewInstance(ThemedReactContext reactContext) {
return new ReactModalHostView(reactContext);
}

@Override
public LayoutShadowNode createShadowNodeInstance() {
return new ModalHostShadowNode();
}

@Override
public Class<? extends LayoutShadowNode> getShadowNodeClass() {
return ModalHostShadowNode.class;
}

@Override
public void onDropViewInstance(ReactModalHostView view) {
super.onDropViewInstance(view);
view.dismiss();
}

@ReactProp(name = "animated")
public void setAnimated(ReactModalHostView view, boolean animated) {
view.setAnimated(animated);
}

@ReactProp(name = "transparent")
public void setTransparent(ReactModalHostView view, boolean transparent) {
view.setTransparent(transparent);
}

@Override
protected void addEventEmitters(
ThemedReactContext reactContext,
final ReactModalHostView view) {
final EventDispatcher dispatcher =
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
view.setOnRequestCloseListener(
new ReactModalHostView.OnRequestCloseListener() {
@Override
public void onRequestClose(DialogInterface dialog) {
dispatcher.dispatchEvent(new RequestCloseEvent(view.getId(), SystemClock.nanoTime()));
}
});
view.setOnShowListener(
new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
dispatcher.dispatchEvent(new ShowEvent(view.getId(), SystemClock.nanoTime()));
}
});
}

@Override
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
return MapBuilder.<String, Object>builder()
.put(RequestCloseEvent.EVENT_NAME, MapBuilder.of("registrationName", "onRequestClose"))
.put(ShowEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShow"))
.build();
}

@Override
protected void onAfterUpdateTransaction(ReactModalHostView view) {
super.onAfterUpdateTransaction(view);
view.showOrUpdate();
}
}
Loading

0 comments on commit db7a154

Please sign in to comment.