Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 61 additions & 41 deletions Popover.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

import React, { PropTypes } from 'react';
import React from 'react';
import {
StyleSheet,
Dimensions,
Expand All @@ -10,6 +10,7 @@ import {
View,
Easing
} from 'react-native';
import PropTypes from 'prop-types';

var noop = () => {};

Expand All @@ -33,13 +34,25 @@ function Rect(x, y, width, height) {
this.height = height;
}

var Popover = React.createClass({
propTypes: {
export default class Popover extends React.Component {

static propTypes = {
isVisible: PropTypes.bool,
onClose: PropTypes.func,
},
getInitialState() {
return {
}

static defaultProps = {
isVisible: false,
displayArea: new Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
arrowSize: DEFAULT_ARROW_SIZE,
placement: 'auto',
onClose: noop,
}


constructor(props) {
super(props);
this.state = {
contentSize: {},
anchorPoint: {},
popoverOrigin: {},
Expand All @@ -51,16 +64,8 @@ var Popover = React.createClass({
fade: new Animated.Value(0),
},
};
},
getDefaultProps() {
return {
isVisible: false,
displayArea: new Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
arrowSize: DEFAULT_ARROW_SIZE,
placement: 'auto',
onClose: noop,
};
},
}

measureContent(x) {
var {width, height} = x.nativeEvent.layout;
var contentSize = {width, height};
Expand All @@ -73,7 +78,8 @@ var Popover = React.createClass({
// from the state
isAwaitingShow && this._startAnimation({show: true});
});
},
}

computeGeometry({contentSize, placement}) {
placement = placement || this.props.placement;

Expand All @@ -96,7 +102,8 @@ var Popover = React.createClass({
default:
return this.computeAutoGeometry(options);
}
},
}

computeTopGeometry({displayArea, fromRect, contentSize, arrowSize}) {
var popoverOrigin = new Point(
Math.min(displayArea.x + displayArea.width - contentSize.width,
Expand All @@ -109,7 +116,8 @@ var Popover = React.createClass({
anchorPoint,
placement: 'top',
}
},
}

computeBottomGeometry({displayArea, fromRect, contentSize, arrowSize}) {
var popoverOrigin = new Point(
Math.min(displayArea.x + displayArea.width - contentSize.width,
Expand All @@ -122,7 +130,8 @@ var Popover = React.createClass({
anchorPoint,
placement: 'bottom',
}
},
}

computeLeftGeometry({displayArea, fromRect, contentSize, arrowSize}) {
var popoverOrigin = new Point(fromRect.x - contentSize.width - arrowSize.width,
Math.min(displayArea.y + displayArea.height - contentSize.height,
Expand All @@ -134,7 +143,8 @@ var Popover = React.createClass({
anchorPoint,
placement: 'left',
}
},
}

computeRightGeometry({displayArea, fromRect, contentSize, arrowSize}) {
var popoverOrigin = new Point(fromRect.x + fromRect.width + arrowSize.width,
Math.min(displayArea.y + displayArea.height - contentSize.height,
Expand All @@ -146,7 +156,8 @@ var Popover = React.createClass({
anchorPoint,
placement: 'right',
}
},
}

computeAutoGeometry({displayArea, contentSize}) {
var placementsToTry = ['left', 'right', 'bottom', 'top'];

Expand All @@ -164,7 +175,8 @@ var Popover = React.createClass({
}

return geom;
},
}

getArrowSize(placement) {
var size = this.props.arrowSize;
switch(placement) {
Expand All @@ -174,10 +186,12 @@ var Popover = React.createClass({
default:
return size;
}
},
}

getArrowColorStyle(color) {
return { borderTopColor: color };
},
}

getArrowRotation(placement) {
switch (placement) {
case 'bottom':
Expand All @@ -189,7 +203,8 @@ var Popover = React.createClass({
default:
return '0deg';
}
},
}

getArrowDynamicStyle() {
var {anchorPoint, popoverOrigin} = this.state;
var arrowSize = this.props.arrowSize;
Expand All @@ -211,14 +226,16 @@ var Popover = React.createClass({
borderBottomWidth: height / 2,
borderLeftWidth: width / 2,
}
},
}

getTranslateOrigin() {
var {contentSize, popoverOrigin, anchorPoint} = this.state;
var popoverCenter = new Point(popoverOrigin.x + contentSize.width / 2,
popoverOrigin.y + contentSize.height / 2);
return new Point(anchorPoint.x - popoverCenter.x, anchorPoint.y - popoverCenter.y);
},
componentWillReceiveProps(nextProps:any) {
}

componentWillReceiveProps(nextProps) {
var willBeVisible = nextProps.isVisible;
var {
isVisible,
Expand All @@ -233,12 +250,14 @@ var Popover = React.createClass({
this._startAnimation({show: false});
}
}
},
}

_startAnimation({show}) {
var handler = this.props.startCustomAnimation || this._startDefaultAnimation;
var handler = this.props.startCustomAnimation || this._startDefaultAnimation.bind(this);
handler({show, doneCallback: () => this.setState({isTransitioning: false})});
this.setState({isTransitioning: true});
},
}

_startDefaultAnimation({show, doneCallback}) {
var animDuration = 300;
var values = this.state.defaultAnimatedValues;
Expand Down Expand Up @@ -267,7 +286,8 @@ var Popover = React.createClass({
...commonConfig,
})
]).start(doneCallback);
},
}

_getDefaultAnimatedStyles() {
// If there's a custom animation handler,
// we don't return the default animated styles
Expand Down Expand Up @@ -300,7 +320,8 @@ var Popover = React.createClass({
],
}
};
},
}

_getExtendedStyles() {
var background = [];
var popover = [];
Expand All @@ -322,7 +343,8 @@ var Popover = React.createClass({
arrow,
content,
}
},
}

render() {
if (!this.props.isVisible && !this.state.isTransitioning) {
return null;
Expand All @@ -344,22 +366,22 @@ var Popover = React.createClass({

return (
<TouchableWithoutFeedback onPress={this.props.onClose}>
<View style={[styles.container, contentSizeAvailable && styles.containerVisible ]}>
<View style={[styles.container, this.props.style, contentSizeAvailable && styles.containerVisible ]}>
<Animated.View style={[styles.background, ...extendedStyles.background]}/>
<Animated.View style={[styles.popover, {
top: popoverOrigin.y,
left: popoverOrigin.x,
}, ...extendedStyles.popover]}>
<Animated.View style={arrowStyle}/>
<Animated.View ref='content' onLayout={this.measureContent} style={contentStyle}>
<Animated.View ref='content' onLayout={this.measureContent.bind(this)} style={contentStyle}>
{this.props.children}
</Animated.View>
</Animated.View>
</View>
</TouchableWithoutFeedback>
);
}
});
}


var styles = StyleSheet.create({
Expand Down Expand Up @@ -403,6 +425,4 @@ var styles = StyleSheet.create({
borderBottomColor: 'transparent',
borderLeftColor: 'transparent',
},
});

module.exports = Popover;
})
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
},
"devDependencies": {
"react": "^15.0.2",
"react-native": "^0.26.2"
"react-native": "^0.26.2",
"prop-types": "^15.5.10"
}
}