UNPKG

react-native-modal-popover

Version:
306 lines 12.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Popover = void 0; const debounce_1 = __importDefault(require("lodash/debounce")); const PropTypes = __importStar(require("prop-types")); const React = __importStar(require("react")); const react_native_1 = require("react-native"); const PopoverGeometry_1 = require("./PopoverGeometry"); const styles = react_native_1.StyleSheet.create({ container: Object.assign(Object.assign({}, react_native_1.StyleSheet.absoluteFillObject), { opacity: 0, backgroundColor: 'transparent' }), containerVisible: { opacity: 1, }, background: Object.assign(Object.assign({}, react_native_1.StyleSheet.absoluteFillObject), { backgroundColor: 'rgba(0,0,0,0.5)' }), popover: Object.assign(Object.assign({}, react_native_1.Platform.select({ ios: { shadowColor: 'black', shadowOffset: { width: 0, height: 2 }, shadowRadius: 2, shadowOpacity: 0.4, backgroundColor: 'transparent', }, })), { position: 'absolute' }), content: { flexDirection: 'column', position: 'absolute', backgroundColor: '#f2f2f2', padding: 8, }, arrow: { position: 'absolute', borderTopColor: '#f2f2f2', borderRightColor: 'transparent', borderBottomColor: 'transparent', borderLeftColor: 'transparent', }, }); const ARROW_DEG = { bottom: '-180deg', start: react_native_1.I18nManager.isRTL ? '90deg' : '-90deg', end: react_native_1.I18nManager.isRTL ? '-90deg' : '90deg', top: '0deg', }; class Popover extends React.PureComponent { constructor(props) { super(props); this.computeGeometry = (props, contentSize) => (0, PopoverGeometry_1.computeGeometry)(contentSize || this.state.contentSize, props.placement, props.fromRect, props.displayArea || this.defaultDisplayArea, props.arrowSize); this.onOrientationChange = () => { var _a; const dimensions = react_native_1.Dimensions.get('window'); this.defaultDisplayArea = { x: 10, y: 10, width: dimensions.width - 20, height: dimensions.height - 20 - (this.props.calculateStatusBar ? (_a = react_native_1.StatusBar.currentHeight) !== null && _a !== void 0 ? _a : 0 : 0), }; }; this.updateState = (0, debounce_1.default)(this.setState, 100); this.measureContent = ({ nativeEvent: { layout: { width, height }, }, }) => { if (width && height) { const contentSize = { width, height }; const geom = this.computeGeometry(this.props, contentSize); const isAwaitingShow = this.state.isAwaitingShow; this.updateState(Object.assign(Object.assign({}, geom), { contentSize }), () => { if (isAwaitingShow) { this.startAnimation(true); } }); } }; this.getTranslateOrigin = () => { const { contentSize, origin, anchor } = this.state; const popoverCenter = { x: origin.x + contentSize.width / 2, y: origin.y + contentSize.height / 2, }; return { x: anchor.x - popoverCenter.x, y: anchor.y - popoverCenter.y }; }; this.startAnimation = (show) => { const doneCallback = show ? undefined : this.onHidden; react_native_1.Animated.timing(this.state.animation, { toValue: show ? 1 : 0, duration: this.props.duration, easing: this.props.easing(show), useNativeDriver: !!this.props.useNativeDriver, }).start(doneCallback); }; this.onHidden = () => this.setState({ visible: false, isAwaitingShow: false }); this.computeStyles = () => { const { animation, anchor, origin } = this.state; const translateOrigin = this.getTranslateOrigin(); const arrowSize = this.props.arrowSize; const width = arrowSize.width + 2; const height = arrowSize.height * 2 + 2; return { background: [ styles.background, this.props.backgroundStyle, { opacity: animation.interpolate({ inputRange: [0, 1], outputRange: [0, 1], extrapolate: 'clamp', }), }, ], arrow: [ styles.arrow, this.props.arrowStyle, { width, height, borderTopWidth: height / 2, left: anchor.x - origin.x - width / 2, top: anchor.y - origin.y - height / 2, borderEndWidth: width / 2, borderBottomWidth: height / 2, borderStartWidth: width / 2, transform: [ { rotate: animation.interpolate({ inputRange: [0, 1], outputRange: [ ARROW_DEG[this.state.placement], ARROW_DEG[this.state.placement], ], extrapolate: 'clamp', }), }, { scale: animation.interpolate({ inputRange: [0, 1], outputRange: [0, 1], extrapolate: 'clamp', }), }, ], }, ], popover: [ styles.popover, this.props.popoverStyle, { top: origin.y, left: origin.x }, ], content: [ styles.content, this.props.contentStyle, { transform: [ { translateX: animation.interpolate({ inputRange: [0, 1], outputRange: [translateOrigin.x, 0], extrapolate: 'clamp', }), }, { translateY: animation.interpolate({ inputRange: [0, 1], outputRange: [translateOrigin.y, 0], extrapolate: 'clamp', }), }, { scale: animation }, ], }, ], }; }; this.state = { contentSize: { width: 0, height: 0 }, anchor: { x: 0, y: 0 }, origin: { x: 0, y: 0 }, placement: props.placement === 'auto' ? 'top' : props.placement, visible: false, isAwaitingShow: false, animation: new react_native_1.Animated.Value(0), }; this.onOrientationChange(); } componentDidMount() { this.dimensionsSub = react_native_1.Dimensions.addEventListener('change', this.onOrientationChange); } componentWillUnmount() { var _a; (_a = this.dimensionsSub) === null || _a === void 0 ? void 0 : _a.remove(); } UNSAFE_componentWillReceiveProps(nextProps) { const willBeVisible = nextProps.visible; const { visible, fromRect, displayArea } = this.props; if (willBeVisible !== visible) { if (willBeVisible) { this.setState({ contentSize: { width: 0, height: 0 }, isAwaitingShow: true, visible: true, }); } else { this.startAnimation(false); } } else if (willBeVisible && (fromRect !== nextProps.fromRect || displayArea !== nextProps.displayArea)) { const geom = this.computeGeometry(nextProps, this.state.contentSize); const isAwaitingShow = this.state.isAwaitingShow; this.setState(Object.assign({}, geom), () => { if (isAwaitingShow) { this.startAnimation(true); } }); } } render() { const { visible } = this.state; const { onClose, onDismiss, supportedOrientations } = this.props; const computedStyles = this.computeStyles(); const contentSizeAvailable = this.state.contentSize.width; return (<react_native_1.Modal transparent visible={visible} onRequestClose={onClose} onDismiss={onDismiss} supportedOrientations={supportedOrientations} onOrientationChange={this.onOrientationChange}> <react_native_1.View style={[ styles.container, !!contentSizeAvailable && styles.containerVisible, ]}> <react_native_1.TouchableWithoutFeedback onPress={this.props.onClose}> <react_native_1.Animated.View style={computedStyles.background}/> </react_native_1.TouchableWithoutFeedback> <react_native_1.Animated.View style={computedStyles.popover}> <react_native_1.Animated.View onLayout={this.measureContent} style={computedStyles.content}> {this.props.children} </react_native_1.Animated.View> <react_native_1.Animated.View style={computedStyles.arrow}/> </react_native_1.Animated.View> </react_native_1.View> </react_native_1.Modal>); } } exports.Popover = Popover; Popover.propTypes = { visible: PropTypes.bool, onClose: PropTypes.func, onDismiss: PropTypes.func, arrowSize: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number, }), placement: PropTypes.oneOf(['left', 'top', 'right', 'bottom', 'auto']), fromRect: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number, width: PropTypes.number, height: PropTypes.number, }), displayArea: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number, width: PropTypes.number, height: PropTypes.number, }), backgroundStyle: PropTypes.any, arrowStyle: PropTypes.any, popoverStyle: PropTypes.any, contentStyle: PropTypes.any, duration: PropTypes.number, easing: PropTypes.func, }; Popover.defaultProps = { visible: false, onClose: () => { }, onDismiss: () => { }, arrowSize: { width: 16, height: 8 }, placement: 'auto', duration: 300, easing: (show) => show ? react_native_1.Easing.out(react_native_1.Easing.back(1.70158)) : react_native_1.Easing.inOut(react_native_1.Easing.quad), useNativeDriver: false, calculateStatusBar: false, }; Popover.displayName = 'Popover'; //# sourceMappingURL=Popover.js.map