UNPKG

@sendbird/uikit-react-native-foundation

Version:

A foundational UI kit for building chat-enabled React Native apps.

187 lines (185 loc) 7.42 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _reactNative = require("react-native"); var _createStyleSheet = _interopRequireDefault(require("../../styles/createStyleSheet")); var _useHeaderStyle = _interopRequireDefault(require("../../styles/useHeaderStyle")); var _useUIKitTheme = _interopRequireDefault(require("../../theme/useUIKitTheme")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } /** * Modal Open: Triggered by Modal.props.visible state changed to true * - visible true -> modalVisible true -> animation start * * Modal Close: Triggered by Modal.props.onClose() call * - Modal.props.onClose() -> visible false -> animation start -> modalVisible false * */ const Modal = ({ children, onClose, backgroundStyle, onDismiss, type = 'fade', visible = false, disableBackgroundClose = false, enableKeyboardAvoid = false, statusBarTranslucent, ...props }) => { const { palette } = (0, _useUIKitTheme.default)(); const { content, backdrop, showTransition, hideTransition } = useModalAnimation(type); const panResponder = useModalPanResponder(type, content.translateY, showTransition, onClose); const { topInset } = (0, _useHeaderStyle.default)(); const [modalVisible, setModalVisible] = (0, _react.useState)(false); const showAction = () => setModalVisible(true); const hideAction = () => hideTransition(() => setModalVisible(false)); const { width, height } = (0, _reactNative.useWindowDimensions)(); (0, _react.useEffect)(() => { if (visible) showAction();else hideAction(); }, [visible]); useOnDismiss(modalVisible, onDismiss); return /*#__PURE__*/_react.default.createElement(_reactNative.View, null, /*#__PURE__*/_react.default.createElement(_reactNative.Modal, _extends({ statusBarTranslucent: statusBarTranslucent, transparent: true, hardwareAccelerated: true, visible: modalVisible, onRequestClose: onClose, onShow: () => showTransition(), onDismiss: onDismiss, supportedOrientations: ['portrait', 'portrait-upside-down', 'landscape', 'landscape-left', 'landscape-right'], animationType: 'none' }, props), /*#__PURE__*/_react.default.createElement(_reactNative.TouchableWithoutFeedback, { onPress: disableBackgroundClose ? undefined : onClose }, /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, { style: [_reactNative.StyleSheet.absoluteFill, { opacity: backdrop.opacity, backgroundColor: palette.onBackgroundLight03 }] })), /*#__PURE__*/_react.default.createElement(_reactNative.KeyboardAvoidingView // NOTE: This is trick for Android. // When orientation is changed on Android, the offset that to avoid soft-keyboard is not updated normally. , { key: _reactNative.Platform.OS === 'android' && enableKeyboardAvoid ? `${width}-${height}` : undefined, enabled: enableKeyboardAvoid, style: styles.background, behavior: _reactNative.Platform.select({ ios: 'padding', default: 'height' }), pointerEvents: 'box-none', keyboardVerticalOffset: enableKeyboardAvoid && statusBarTranslucent ? -topInset : 0 }, /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, _extends({ style: [styles.background, backgroundStyle, { opacity: content.opacity, transform: [{ translateY: content.translateY }] }], pointerEvents: 'box-none' }, panResponder.panHandlers), /*#__PURE__*/_react.default.createElement(_reactNative.Pressable // NOTE: https://github.com/facebook/react-native/issues/14295 // Due to 'Pressable', the width of the children must be explicitly specified as a number. , null, children))))); }; const isHideGesture = (distanceY, velocityY) => { return distanceY > 125 || distanceY > 0 && velocityY > 0.1; }; const useModalPanResponder = (type, translateY, show, hide) => { if (type === 'fade' || type === 'slide-no-gesture') return { panHandlers: {} }; return _react.default.useRef(_reactNative.PanResponder.create({ onMoveShouldSetPanResponderCapture: (_, { dy }) => dy > 8, // @ts-ignore onPanResponderGrant: () => translateY.setOffset(translateY.__getValue()), onPanResponderMove: (_, { dy }) => dy >= 0 && translateY.setValue(dy), // Animated.event([null, { dy: translateY }], { useNativeDriver: false }), onPanResponderRelease: (_, { dy, vy }) => { if (isHideGesture(dy, vy)) hide();else show(); } })).current; }; const useModalAnimation = type => { const initialY = type === 'fade' ? 0 : _reactNative.Dimensions.get('window').height; const baseAnimBackground = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current; const baseAnimContent = (0, _react.useRef)(new _reactNative.Animated.Value(initialY)).current; const content = { opacity: baseAnimBackground.interpolate({ inputRange: [0, 1], outputRange: [type === 'fade' ? 0 : 1, 1] }), translateY: baseAnimContent }; const backdrop = { opacity: baseAnimBackground.interpolate({ inputRange: [0, 1], outputRange: [0, 1] }) }; const createTransition = toValue => { const config = { duration: 250, useNativeDriver: false }; return _reactNative.Animated.parallel([_reactNative.Animated.timing(baseAnimBackground, { toValue, ...config }), _reactNative.Animated.timing(baseAnimContent, { toValue: toValue === 0 ? initialY : 0, ...config })]).start; }; return { content, backdrop, showTransition: createTransition(1), hideTransition: createTransition(0) }; }; // NOTE: onDismiss is supports iOS only const useOnDismiss = (visible, onDismiss) => { const prevVisible = usePrevProp(visible); (0, _react.useEffect)(() => { if (_reactNative.Platform.OS === 'ios') return; if (prevVisible && !visible) onDismiss === null || onDismiss === void 0 || onDismiss(); }, [prevVisible, visible]); }; const usePrevProp = prop => { const prev = (0, _react.useRef)(prop); const curr = (0, _react.useRef)(prop); (0, _react.useEffect)(() => { prev.current = curr.current; curr.current = prop; }); return prev.current; }; const styles = (0, _createStyleSheet.default)({ background: { flex: 1 } }); var _default = exports.default = Modal; //# sourceMappingURL=index.js.map