react-native-paper-toast
Version:
A toast implementation for React Native Paper
243 lines (241 loc) • 6.4 kB
JavaScript
import React, { createContext, useContext, useEffect, useMemo, useReducer } from 'react';
import { Keyboard, StyleSheet, View } from 'react-native';
import { Portal, Snackbar, Text } from 'react-native-paper';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { ToastActionType } from './types';
const ToastContext = /*#__PURE__*/createContext(null);
const defaults = {
message: 'Hello React Native Paper Toast!',
type: 'normal',
position: 'bottom',
duration: 2000,
visibility: false,
action: undefined,
actionLabel: 'DONE',
messageStyle: {},
messageContainerStyle: {},
snackbarStyle: {}
};
const reducer = (state, action) => {
switch (action.type) {
case ToastActionType.SHOW:
return {
...state,
...action.payload
};
case ToastActionType.HYDRATE:
return {
...state,
...action.payload,
visibility: false
};
case ToastActionType.HIDE:
return {
...state,
visibility: false
};
default:
return state;
}
};
/**
* Wrap your component tree with ToastProvider. This should be after SafeAreaProvider & PaperProvider!
* ## Usage
* ```tsx
* import React from 'react';
* import { DefaultTheme, Provider as PaperProvider } from 'react-native-paper';
* import { initialWindowMetrics, SafeAreaProvider } from 'react-native-safe-area-context';
* import { ToastProvider } from 'react-native-paper-toast';
* import Application from './application';
*
* export default function App() {
* return (
* <SafeAreaProvider initialMetrics={initialWindowMetrics}>
* <PaperProvider theme={DefaultTheme}>
* <ToastProvider overrides={{ position: 'top' }}>
* <Application />
* </ToastProvider>
* </PaperProvider>
* </SafeAreaProvider>
* );
* }
* ```
*/
const ToastProvider = ({
children,
overrides
}) => {
const initialState = useMemo(() => ({
...defaults,
...overrides
}), [overrides]);
const [state, dispatch] = useReducer(reducer, initialState);
const insets = useSafeAreaInsets();
const toast = useMemo(() => ({
show(options) {
const newState = {
...initialState,
...options,
visibility: true
};
newState.position === 'bottom' && Keyboard.dismiss();
dispatch({
type: ToastActionType.SHOW,
payload: newState
});
},
hide() {
dispatch({
type: ToastActionType.HIDE
});
}
}), [initialState]);
const computedStyle = useMemo(() => {
const base = {
position: 'absolute',
left: insets.left,
right: insets.right,
width: undefined,
alignItems: 'center',
flexDirection: 'row'
};
let style;
if (state.position === 'bottom') {
style = {
...base,
bottom: insets.bottom
};
return style;
}
if (state.position === 'top') {
style = {
...base,
top: insets.top,
bottom: undefined
};
return style;
}
style = {
...base,
top: insets.top,
bottom: insets.bottom,
justifyContent: 'center'
};
return style;
}, [insets, state.position]);
useEffect(() => {
dispatch({
type: ToastActionType.HYDRATE,
payload: initialState
});
}, [initialState]);
return /*#__PURE__*/React.createElement(ToastContext.Provider, {
value: toast
}, /*#__PURE__*/React.createElement(Portal.Host, null, children), /*#__PURE__*/React.createElement(Portal, null, /*#__PURE__*/React.createElement(Snackbar, {
onDismiss: toast.hide,
style: [types[state.type], state.snackbarStyle],
wrapperStyle: computedStyle,
duration: state.duration,
visible: state.visibility,
action: state.action ? {
label: state.actionLabel,
onPress: state.action
} : undefined
}, /*#__PURE__*/React.createElement(View, {
style: [styles.container, state.messageContainerStyle]
}, /*#__PURE__*/React.createElement(Icon, {
size: 24,
name: icons[state.type],
color: "#ffffff"
}), /*#__PURE__*/React.createElement(Text, {
style: [styles.message, state.messageStyle]
}, ` ${state.message}`)))));
};
/**
* useToast hook is used to show and hide Toast messages.
* ## Usage
* Import the `useToast` hook from the library. Calling it will return you an object with two functions `show` and `hide` to show or hide toast.
*
* ```tsx
* import { useToast } from 'react-native-paper-toast';
*
* export const Screen: React.FC<Props> = (props) => {
* const toaster = useToast();
* // You can now toast methods from handler functions, effects or onPress props!
*
* // Call from handler function
* const handleError = () =>
* toaster.show({ message: 'Invalid Username', type: 'error' });
*
* // Call from Effects
* useEffect(() => {
* login(username, password).then((v) =>
* toaster.show({ message: 'Login successful', duration: 2000 })
* );
* });
*
* return (
* <Surface>
* <Button onPress={() => toaster.show({ message: 'Here is a toast for ya!' })}>
* Show Toast
* </Button>
* <Button onPress={toaster.hide}>Hide Toast</Button>
* </Surface>
* );
* };
* ```
*/
const useToast = () => {
const toast = useContext(ToastContext);
if (!toast) {
throw new Error('useToast must be used within a ToastProvider.');
}
return toast;
};
const icons = {
normal: 'information-outline',
info: 'information-outline',
warning: 'alert-circle-outline',
success: 'check-circle-outline',
error: 'close-circle-outline'
};
const common = {
borderRadius: 20,
flex: 1
};
const types = {
info: {
...common,
backgroundColor: 'rgba(81,98,188,0.9)'
},
normal: {
...common,
backgroundColor: 'rgba(72,77,81,0.9)'
},
success: {
...common,
backgroundColor: 'rgba(75,153,79,0.9)'
},
warning: {
...common,
backgroundColor: 'rgba(254,177,25,0.9)'
},
error: {
...common,
backgroundColor: 'rgba(216,25,25,0.9)'
}
};
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center'
},
message: {
marginLeft: 4,
fontSize: 20,
color: '#ffffff'
}
});
export { ToastProvider, useToast };
//# sourceMappingURL=index.js.map