@sendbird/uikit-react-native-foundation
Version:
A foundational UI kit for building chat-enabled React Native apps.
187 lines (185 loc) • 7.42 kB
JavaScript
;
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