diff --git a/Popover.js b/Popover.js index 27620a5..97aa306 100644 --- a/Popover.js +++ b/Popover.js @@ -14,14 +14,18 @@ var flattenStyle = require('react-native/Libraries/StyleSheet/flattenStyle'); var Easing = require('react-native/Libraries/Animation/Animated/Easing'); var noop = () => {}; -var SCREEN_HEIGHT = Dimensions.get('window').height; -var SCREEN_WIDTH = Dimensions.get('window').width; +var {height: SCREEN_HEIGHT, width: SCREEN_WIDTH} = Dimensions.get('window'); function Point(x, y) { this.x = x; this.y = y; } +function Size(width, height) { + this.width = width; + this.height = height; +} + function Rect(x, y, width, height) { this.x = x; this.y = y; @@ -37,8 +41,8 @@ var Popover = React.createClass({ getInitialState() { return { contentSize: {}, + anchorPoint: {}, popoverOrigin: {}, - arrowOrigin: {}, placement: 'auto', isTransitioning: false, transform: new Animated.Value(0), @@ -50,18 +54,19 @@ var Popover = React.createClass({ return { isVisible: false, displayArea: new Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), + arrowSize: new Size(5, 5), placement: 'auto', onClose: noop, }; }, measureContent(x) { var {width, height} = x.nativeEvent.layout; - var contentSize = {width: width, height: height}; - var geom = this.computeGeometry({contentSize: contentSize}); + var contentSize = {width, height}; + var geom = this.computeGeometry({contentSize}); var awaitingShowHandler = this.state.awaitingShowHandler; - this.setState(Object.assign(geom, - {contentSize: contentSize, awaitingShowHandler: undefined}), () => { + this.setState(Object.assign(geom, + {contentSize, awaitingShowHandler: undefined}), () => { // Once state is set, call the showHandler so it can access all the geometry // from the state awaitingShowHandler && awaitingShowHandler(); @@ -69,11 +74,12 @@ var Popover = React.createClass({ }, computeGeometry({contentSize, placement}) { placement = placement || this.props.placement; - + var options = { displayArea: this.props.displayArea, fromRect: this.props.fromRect, - contentSize: contentSize, + arrowSize: this.props.arrowSize, + contentSize, } switch (placement) { @@ -89,57 +95,57 @@ var Popover = React.createClass({ return this.computeAutoGeometry(options); } }, - computeTopGeometry({displayArea, fromRect, contentSize}) { + computeTopGeometry({displayArea, fromRect, contentSize, arrowSize}) { var popoverOrigin = new Point( - Math.min(displayArea.x + displayArea.width - contentSize.width, - Math.max(displayArea.x, fromRect.x + (fromRect.width - contentSize.width) / 2)), - fromRect.y - contentSize.height - 5); - var arrowOrigin = new Point(fromRect.x - popoverOrigin.x + (fromRect.width - 10) / 2.0, contentSize.height); + Math.min(displayArea.x + displayArea.width - contentSize.width, + Math.max(displayArea.x, fromRect.x + (fromRect.width - contentSize.width) / 2)), + fromRect.y - contentSize.height - arrowSize.height); + var anchorPoint = new Point(fromRect.x + fromRect.width / 2.0, fromRect.y); return { - popoverOrigin: popoverOrigin, - arrowOrigin: arrowOrigin, + popoverOrigin, + anchorPoint, placement: 'top', } }, - computeBottomGeometry({displayArea, fromRect, contentSize}) { + computeBottomGeometry({displayArea, fromRect, contentSize, arrowSize}) { var popoverOrigin = new Point( - Math.min(displayArea.x + displayArea.width - contentSize.width, - Math.max(displayArea.x, fromRect.x + (fromRect.width - contentSize.width) / 2)), - fromRect.y + fromRect.height + 5); - var arrowOrigin = new Point(fromRect.x - popoverOrigin.x + (fromRect.width - 10) / 2.0, -10); + Math.min(displayArea.x + displayArea.width - contentSize.width, + Math.max(displayArea.x, fromRect.x + (fromRect.width - contentSize.width) / 2)), + fromRect.y + fromRect.height + arrowSize.height); + var anchorPoint = new Point(fromRect.x + fromRect.width / 2.0, fromRect.y + fromRect.height); return { - popoverOrigin: popoverOrigin, - arrowOrigin: arrowOrigin, + popoverOrigin, + anchorPoint, placement: 'bottom', } }, - computeLeftGeometry({displayArea, fromRect, contentSize}) { - var popoverOrigin = new Point(fromRect.x - contentSize.width - 5, - Math.min(displayArea.y + displayArea.height - contentSize.height, + computeLeftGeometry({displayArea, fromRect, contentSize, arrowSize}) { + var popoverOrigin = new Point(fromRect.x - contentSize.width - arrowSize.width, + Math.min(displayArea.y + displayArea.height - contentSize.height, Math.max(displayArea.y, fromRect.y + (fromRect.height - contentSize.height) / 2))); - var arrowOrigin = new Point(contentSize.width, fromRect.y - popoverOrigin.y + (fromRect.height - 10) / 2.0); + var anchorPoint = new Point(fromRect.x, fromRect.y + fromRect.height / 2.0); return { - popoverOrigin: popoverOrigin, - arrowOrigin: arrowOrigin, + popoverOrigin, + anchorPoint, placement: 'left', } }, - computeRightGeometry({displayArea, fromRect, contentSize}) { - var popoverOrigin = new Point(fromRect.x + fromRect.width + 5, - Math.min(displayArea.y + displayArea.height - contentSize.height, + computeRightGeometry({displayArea, fromRect, contentSize, arrowSize}) { + var popoverOrigin = new Point(fromRect.x + fromRect.width + arrowSize.width, + Math.min(displayArea.y + displayArea.height - contentSize.height, Math.max(displayArea.y, fromRect.y + (fromRect.height - contentSize.height) / 2))); - var arrowOrigin = new Point(-10, fromRect.y - popoverOrigin.y + (fromRect.height - 10) / 2.0); + var anchorPoint = new Point(fromRect.x + fromRect.width, fromRect.y + fromRect.height / 2.0); return { - popoverOrigin: popoverOrigin, - arrowOrigin: arrowOrigin, + popoverOrigin, + anchorPoint, placement: 'right', } }, - computeAutoGeometry({displayArea, fromRect, contentSize}) { + computeAutoGeometry({displayArea, contentSize}) { var placementsToTry = ['left', 'right', 'bottom', 'top']; for (var i = 0; i < placementsToTry.length; i++) { @@ -147,9 +153,9 @@ var Popover = React.createClass({ var geom = this.computeGeometry({contentSize: contentSize, placement: placement}); var {popoverOrigin} = geom; - if (popoverOrigin.x >= displayArea.x + if (popoverOrigin.x >= displayArea.x && popoverOrigin.x <= displayArea.x + displayArea.width - contentSize.width - && popoverOrigin.y >= displayArea.y + && popoverOrigin.y >= displayArea.y && popoverOrigin.y <= displayArea.y + displayArea.height - contentSize.height) { break; } @@ -169,6 +175,27 @@ var Popover = React.createClass({ return { borderRightColor: color }; } }, + getArrowDynamicStyle() { + var {width, height} = this.props.arrowSize; + var {anchorPoint, popoverOrigin} = this.state; + + return { + left: anchorPoint.x - popoverOrigin.x - width, + top: anchorPoint.y - popoverOrigin.y - height, + width: width * 2, + height: height * 2, + borderTopWidth: height, + borderRightWidth: width, + borderBottomWidth: height, + borderLeftWidth: width, + } + }, + 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) { var willBeVisible = nextProps.isVisible; var { @@ -179,14 +206,6 @@ var Popover = React.createClass({ if (willBeVisible !== isVisible) { var animDuration = 300; - var getTranslateOrigin = () => { - var {contentSize, popoverOrigin, arrowOrigin} = this.state; - var popoverCenter = new Point(popoverOrigin.x + contentSize.width / 2, - popoverOrigin.y + contentSize.height / 2); - var arrowTip = new Point(popoverOrigin.x + arrowOrigin.x + 5, - popoverOrigin.y + arrowOrigin.y + 5); - return new Point(arrowTip.x - popoverCenter.x, arrowTip.y - popoverCenter.y); - } var config = {velocity: 3, bounciness: 18}; var defaultShowHandler = (t) => { /*var easing = Transitions.Easings.easeOutBack; @@ -196,14 +215,14 @@ var Popover = React.createClass({ t('popover.transform.translateY', {duration: animDuration, easing: easing, begin: translateOrigin.y, end: 0,}); t('popover.transform.scaleXY', {duration: animDuration, easing: easing, begin: 0, end: 1,});*/ - var translateOrigin = getTranslateOrigin(); + var translateOrigin = this.getTranslateOrigin(); this.state.translate.setValue(translateOrigin); var commonConfig = { duration: animDuration, easing: Easing.out(Easing.back()), } - + Animated.parallel([ Animated.timing(this.state.fade, { toValue: 1, @@ -227,13 +246,13 @@ var Popover = React.createClass({ t('popover.transform.translateX', {duration: animDuration, easing: easing, end: translateOrigin.x,}); t('popover.transform.translateY', {duration: animDuration, easing: easing, end: translateOrigin.y,}); */ - + var commonConfig = { duration: animDuration, easing: Easing.inOut(Easing.quad), } - - var translateOrigin = getTranslateOrigin(); + + var translateOrigin = this.getTranslateOrigin(); this.setState({isTransitioning: true}); @@ -274,9 +293,10 @@ var Popover = React.createClass({ return null; } - var {popoverOrigin, arrowOrigin, placement} = this.state; + var {popoverOrigin, placement} = this.state; var arrowColor = flattenStyle(styles.content).backgroundColor; var arrowColorStyle = this.getArrowColorStyle(placement, arrowColor); + var arrowDynamicStyle = this.getArrowDynamicStyle(); var contentSizeAvailable = this.state.contentSize.width; var backgroundAnimatedStyle = { @@ -299,10 +319,7 @@ var Popover = React.createClass({ top: popoverOrigin.y, left: popoverOrigin.x, }, popoverAnimatedStyle /*, this.transitionStyles('popover')*/]}> - + {this.props.children} @@ -335,7 +352,7 @@ var DefaultStyles = StyleSheet.create({ position: 'absolute', backgroundColor: 'rgba(0,0,0,0.5)', }, - popover: { + popover: { backgroundColor: 'transparent', position: 'absolute', shadowColor: 'black', @@ -344,24 +361,17 @@ var DefaultStyles = StyleSheet.create({ shadowOpacity: 0.8, }, content: { - //margin: 10, borderRadius: 3, padding: 6, backgroundColor: '#fff', }, arrow: { position: 'absolute', - width: 10, - height: 10, - borderTopWidth: 5, borderTopColor: 'transparent', - borderRightWidth: 5, borderRightColor: 'transparent', - borderBottomWidth: 5, borderBottomColor: 'transparent', - borderLeftWidth: 5, borderLeftColor: 'transparent', }, }); -module.exports = Popover; \ No newline at end of file +module.exports = Popover;