UNPKG

react-native-ui-lib

Version:

[![Build Status](https://travis-ci.org/wix/react-native-ui-lib.svg?branch=master)](https://travis-ci.org/wix/react-native-ui-lib) [![npm](https://img.shields.io/npm/v/react-native-ui-lib.svg)](https://www.npmjs.com/package/react-native-ui-lib) [![NPM Down

241 lines (213 loc) • 6.8 kB
import _ from 'lodash'; import PropTypes from 'prop-types'; import React from 'react'; import {StyleSheet, TouchableWithoutFeedback, SafeAreaView, Animated, Easing, TouchableOpacity} from 'react-native'; import {View as AnimatableView} from 'react-native-animatable'; import {Constants} from '../../helpers'; import {AnimatableManager, Colors} from '../../style'; import {BaseComponent} from '../../commons'; import Modal from '../../screensComponents/modal'; import View from '../view'; import PanGestureView from '../panGestureView'; /*eslint-disable*/ /** * @description: Dialog component for displaying custom content inside a popup dialog * @notes: Use alignment modifiers to control the dialog positon (top, bottom, centerV, centerH, etc... by default the dialog is align to center) * @modifiers: alignment * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/DialogScreen.js * @gif: https://media.giphy.com/media/9S58XdLCoUiLzAc1b1/giphy.gif */ /*eslint-enable*/ const SWIPE_DIRECTIONS = { UP: 'up', DOWN: 'down' }; // DEFRECATED class Dialog extends BaseComponent { static displayName = 'Dialog' static propTypes = { /** * Control visibility of the dialog */ visible: PropTypes.bool, /** * Dismiss callback for when clicking on the background */ onDismiss: PropTypes.func, /** * The direction of the swipe to dismiss the dialog (default is 'down') */ dismissSwipeDirection: PropTypes.oneOf(Object.values(SWIPE_DIRECTIONS)), // DEFRECATED /** * The color of the overlay background */ overlayBackgroundColor: PropTypes.string, /** * The dialog width (default: 90%) */ width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** * The dialog height (default: 70%) */ height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** * The animation configuration to pass to the dialog (ex. {animation, delay, duration, easing}) */ animationConfig: PropTypes.object, /** * The dialog container style */ containerStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]), /** * Disable the pan gesture recognizer */ disablePan: PropTypes.bool, /** * Whether to display the dialog in a modal */ useModal: PropTypes.bool }; static defaultProps = { overlayBackgroundColor: Colors.rgba(Colors.dark10, 0.6), width: '90%', height: '70%', useModal: true }; static swipeDirections = SWIPE_DIRECTIONS; // DEFRECATED constructor(props) { super(props); this.initialPosition = props.top ? -Constants.screenHeight : Constants.screenHeight; this.state = { alignments: this.state.alignments, deltaY: new Animated.Value(this.initialPosition) }; if (props.dismissSwipeDirection) { console.warn('Dialog component\'s prop \'dismissSwipeDirection\' is deprecated, please remove it'); } if (props.visible) { this.animateContent(); } } componentDidUpdate(prevProps) { if (!prevProps.visible && this.props.visible) { this.animateContent(); } } generateStyles() { this.styles = createStyles(this.props); } onDismiss = () => { this.initPositions(); _.invoke(this.props, 'onDismiss'); } initPositions() { this.setState({ deltaY: new Animated.Value(this.initialPosition) }); } animateContent() { const {animationConfig} = this.getThemeProps(); const {deltaY} = this.state; Animated.timing(deltaY, { toValue: 0, duration: _.get(animationConfig, 'duration', 400), delay: _.get(animationConfig, 'delay', 250), easing: _.get(animationConfig, 'easing', Easing.bezier(0.165, 0.84, 0.44, 1)), useNativeDriver: _.get(animationConfig, 'useNativeDriver', true) }).start(); } renderContent() { const {bottom} = this.getThemeProps(); const bottomInsets = Constants.getSafeAreaInsets().paddingBottom; return ( <TouchableWithoutFeedback> <SafeAreaView style={{flexGrow: 1}}> {this.props.children} {Constants.isIphoneX && bottom && <View style={{height: bottomInsets}}/>} </SafeAreaView> </TouchableWithoutFeedback> ); } renderDraggableContainer() { const {style, top, disablePan} = this.getThemeProps(); const Container = disablePan ? View : PanGestureView; return ( <Container style={[this.styles.dialogContainer, style]} direction={top && PanGestureView.directions.UP} onDismiss={this.onDismiss} > {this.renderContent()} </Container> ); } renderAnimationContainer() { const {animationConfig, top} = this.getThemeProps(); const {alignments, deltaY} = this.state; const centerByDefault = _.isEmpty(alignments); const hasCustomAnimation = (animationConfig && animationConfig.animation); const Container = hasCustomAnimation ? AnimatableView : Animated.View; const defaultAnimation = top ? AnimatableManager.presets.slideInDown : AnimatableManager.presets.slideInUp; const animation = hasCustomAnimation ? Object.assign(defaultAnimation, animationConfig) : {}; return ( <Container style={[ this.styles.overlay, {...alignments}, centerByDefault && this.styles.centerContent, !hasCustomAnimation && { transform: [{ translateY: deltaY }] } ]} pointerEvents="box-none" {...animation} > {this.renderDraggableContainer()} </Container> ); } render() { const {visible, overlayBackgroundColor, useModal} = this.getThemeProps(); if (useModal) { return ( <Modal transparent visible={visible} animationType={'fade'} onBackgroundPress={this.onDismiss} onRequestClose={this.onDismiss} overlayBackgroundColor={overlayBackgroundColor} > {this.renderAnimationContainer()} </Modal> ); } else { return ( <TouchableOpacity onPress={this.onDismiss} activeOpacity={1} style={{position: 'absolute', bottom: 0, top: 0, left: 0, right: 0, backgroundColor: overlayBackgroundColor}} > {this.renderAnimationContainer()} </TouchableOpacity> ); } } } function createStyles({width, height}) { return StyleSheet.create({ overlay: { flex: 1 }, dialogContainer: { width, height }, centerContent: { justifyContent: 'center', alignItems: 'center' } }); } export default Dialog;