@backpackapp-io/react-native-toast
Version:
A toasting library for React Native. Built in features such as swipe to dismiss, multiple toasts, & no context power this library.
239 lines (237 loc) • 11.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Toast = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
var _reactNativeGestureHandler = require("react-native-gesture-handler");
var _types = require("../core/types");
var _utils = require("../utils");
var _headless = require("../headless");
var _jsxRuntime = require("react/jsx-runtime");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const AnimatedPressable = _reactNativeReanimated.default.createAnimatedComponent(_reactNative.Pressable);
const DEFAULT_TOAST_HEIGHT = 50;
const MAX_WIDTH = 360;
const Toast = ({
toast,
updateHeight,
offset,
startPause,
endPause,
overrideDarkMode,
onToastHide,
onToastPress,
onToastShow,
extraInsets,
defaultStyle,
keyboardVisible,
keyboardHeight
}) => {
const insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
const {
width,
height
} = (0, _reactNative.useWindowDimensions)();
const isSystemDarkMode = (0, _reactNative.useColorScheme)() === 'dark';
const [toastHeight, setToastHeight] = (0, _react.useState)(toast?.height ? toast.height : DEFAULT_TOAST_HEIGHT);
const maxWidth = toast?.maxWidth ? toast.maxWidth : MAX_WIDTH;
const [toastWidth, setToastWidth] = (0, _react.useState)(toast?.width ? toast.width : width - 32 > maxWidth ? maxWidth : width - 32);
const isDarkMode = overrideDarkMode !== undefined ? overrideDarkMode : isSystemDarkMode;
const getStartingPosition = (0, _react.useMemo)(() => {
let leftPosition = (width - toastWidth) / 2; // Default to center
if (toast.position === _types.ToastPosition.TOP_LEFT || toast.position === _types.ToastPosition.BOTTOM_LEFT) {
leftPosition = insets.left + 16 + (extraInsets?.left ?? 0);
}
if (toast.position === _types.ToastPosition.TOP_RIGHT || toast.position === _types.ToastPosition.BOTTOM_RIGHT) {
leftPosition = width - toastWidth - insets.right - 16 - (extraInsets?.right ?? 0);
}
let startY = 0;
if (toast.position === _types.ToastPosition.TOP || toast.position === _types.ToastPosition.TOP_LEFT || toast.position === _types.ToastPosition.TOP_RIGHT) {
startY = -(toast.height || DEFAULT_TOAST_HEIGHT) - insets.top - 50;
} else {
startY = height - insets.bottom - _reactNative.Platform.select({
ios: 16,
default: 32
});
}
return {
startY,
leftPosition
};
}, [height, width, toastWidth, toast.position, toast.height, insets, extraInsets]);
const {
startY,
leftPosition
} = getStartingPosition;
const opacity = (0, _reactNativeReanimated.useSharedValue)(0);
const position = (0, _reactNativeReanimated.useSharedValue)(startY);
const offsetY = (0, _reactNativeReanimated.useSharedValue)(startY);
const onPress = (0, _react.useCallback)(() => {
if (toast.onPress) {
toast.onPress(toast);
}
if (onToastPress) {
onToastPress(toast);
}
}, [toast, onToastPress]);
const dismiss = (0, _react.useCallback)((id, reason) => {
_headless.toast.dismiss(id, reason);
}, []);
const getSwipeDirection = (0, _react.useCallback)(() => {
if (toast.position === _types.ToastPosition.TOP || toast.position === _types.ToastPosition.TOP_LEFT || toast.position === _types.ToastPosition.TOP_RIGHT) {
return _reactNativeGestureHandler.Directions.UP;
} else {
return _reactNativeGestureHandler.Directions.DOWN;
}
}, [toast.position]);
const setPosition = (0, _react.useCallback)(() => {
let timingConfig = {
duration: 300
};
let springConfig = {
stiffness: 80
};
if (toast.animationConfig) {
const {
duration = 300,
easing = _reactNativeReanimated.Easing.inOut(_reactNativeReanimated.Easing.quad),
reduceMotion = _reactNativeReanimated.ReduceMotion.System,
...spring
} = toast.animationConfig;
timingConfig = {
duration,
easing,
reduceMotion
};
springConfig = spring;
}
const useSpringAnimation = toast.animationType === 'spring';
const animation = useSpringAnimation ? _reactNativeReanimated.withSpring : _reactNativeReanimated.withTiming;
if (toast.position === _types.ToastPosition.TOP || toast.position === _types.ToastPosition.TOP_LEFT || toast.position === _types.ToastPosition.TOP_RIGHT) {
offsetY.value = animation(toast.visible ? offset : startY, useSpringAnimation ? springConfig : timingConfig);
position.value = animation(toast.visible ? offset : startY, useSpringAnimation ? springConfig : timingConfig);
} else {
let kbHeight = keyboardVisible ? keyboardHeight : 0;
const val = toast.visible ? startY - toastHeight - offset - kbHeight - insets.bottom - (extraInsets?.bottom ?? 0) - _reactNative.Platform.select({
ios: 32,
default: 24
}) : startY;
offsetY.value = animation(val, useSpringAnimation ? springConfig : timingConfig);
position.value = animation(val, useSpringAnimation ? springConfig : timingConfig);
}
}, [offset, toast.visible, keyboardVisible, keyboardHeight, toastHeight, insets.bottom, position, startY, toast.position, offsetY, extraInsets, toast.animationConfig, toast.animationType]);
const composedGesture = (0, _react.useMemo)(() => {
const panGesture = _reactNativeGestureHandler.Gesture.Pan().onUpdate(e => {
offsetY.value = e.translationY / 4 + position.value;
}).onEnd(() => {
(0, _reactNativeReanimated.runOnJS)(setPosition)();
});
const flingGesture = _reactNativeGestureHandler.Gesture.Fling().direction(getSwipeDirection()).onEnd(() => {
offsetY.value = (0, _reactNativeReanimated.withTiming)(startY, {
duration: toast?.animationConfig?.flingPositionReturnDuration ?? 40
});
(0, _reactNativeReanimated.runOnJS)(dismiss)(toast.id, _types.DismissReason.SWIPE);
});
return toast.isSwipeable ? _reactNativeGestureHandler.Gesture.Simultaneous(flingGesture, panGesture) : panGesture;
}, [offsetY, startY, position, setPosition, toast.id, dismiss, toast.isSwipeable, toast.animationConfig, getSwipeDirection]);
(0, _utils.useVisibilityChange)(() => {
if (toast.onShow) {
toast.onShow(toast);
}
onToastShow?.(toast);
}, reason => {
if (toast.onHide) {
toast.onHide(toast, reason || _types.DismissReason.PROGRAMMATIC);
}
onToastHide?.(toast, reason || _types.DismissReason.PROGRAMMATIC);
}, toast.visible, toast.dismissReason);
(0, _react.useEffect)(() => {
setToastHeight(toast?.height ? toast.height : DEFAULT_TOAST_HEIGHT);
}, [toast.height]);
(0, _react.useEffect)(() => {
setToastWidth(toast?.width ? toast.width : width - 32 > maxWidth ? maxWidth : width - 32);
}, [toast.width, maxWidth, width]);
(0, _react.useEffect)(() => {
opacity.value = (0, _reactNativeReanimated.withTiming)(toast.visible ? 1 : 0, {
duration: toast?.animationConfig?.duration ?? 300
});
}, [toast.visible, opacity, toast.animationConfig]);
(0, _react.useEffect)(() => {
setPosition();
}, [offset, toast.visible, keyboardVisible, keyboardHeight, toastHeight, setPosition]);
const style = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
//Control opacity and translation of toast
return {
opacity: opacity.value,
transform: [{
translateY: offsetY.value
}]
};
});
const resolvedValue = (0, _types.resolveValue)(toast.message, toast);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureDetector, {
gesture: composedGesture,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(AnimatedPressable, {
testID: "toast-pressable",
onPressIn: startPause,
onPressOut: () => {
endPause();
},
onPress: onPress,
style: [{
backgroundColor: !toast.customToast ? isDarkMode ? _utils.colors.backgroundDark : _utils.colors.backgroundLight : undefined,
borderRadius: 8,
position: 'absolute',
left: leftPosition,
zIndex: toast.visible ? 9999 : undefined,
alignItems: 'center',
justifyContent: 'center'
}, style, !toast.disableShadow && (0, _utils.ConstructShadow)('#181821', 0.15, false), defaultStyle?.pressable, toast.styles?.pressable],
children: toast.customToast ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
testID: "toast-view",
onLayout: event => updateHeight(toast.id, event.nativeEvent.layout.height),
children: toast.customToast({
...toast,
height: toastHeight,
width: toastWidth
})
}, toast.id) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
testID: "toast-view",
onLayout: event => updateHeight(toast.id, event.nativeEvent.layout.height),
style: [{
minHeight: toastHeight,
width: toastWidth,
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 16
}, defaultStyle?.view, toast.styles?.view],
children: [(toast.type === 'error' || toast.type === 'success') && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
style: [{
backgroundColor: toast.type === 'error' ? _utils.colors.error : toast.type === 'success' ? _utils.colors.success : isDarkMode ? _utils.colors.backgroundDark : _utils.colors.backgroundLight,
width: 3,
height: '100%',
borderRadius: 12,
marginRight: 12
}, defaultStyle?.indicator, toast?.styles?.indicator]
}), typeof toast.icon === 'string' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
children: toast.icon
}) : toast.icon, typeof resolvedValue === 'string' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
style: [{
color: isDarkMode ? _utils.colors.textLight : _utils.colors.textDark,
padding: 4,
flex: 1
}, defaultStyle?.text, toast?.styles?.text],
children: resolvedValue
}) : resolvedValue]
}, toast.id)
})
}, toast.id);
};
exports.Toast = Toast;
//# sourceMappingURL=Toast.js.map