@mehul214/react-native-flash-toast 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,48 @@
1
+ import React, { createContext, useContext, useMemo } from 'react';
2
+ import { FlashMessage } from './FlashMessage';
3
+ import { flashManager } from './FlashMessageManager';
4
+ import type { FlashMessageContainerProps, FlashMessageOptions } from './types';
5
+
6
+ interface FlashMessageContextValue {
7
+ show: (options: FlashMessageOptions) => void;
8
+ hide: () => void;
9
+ }
10
+
11
+ const FlashMessageContext = createContext<FlashMessageContextValue | null>(null);
12
+
13
+ export interface FlashMessageProviderProps extends FlashMessageContainerProps {
14
+ children: React.ReactNode;
15
+ }
16
+
17
+ export const FlashMessageProvider: React.FC<FlashMessageProviderProps> = ({
18
+ children,
19
+ ...containerProps
20
+ }) => {
21
+ const contextValue = useMemo<FlashMessageContextValue>(
22
+ () => ({
23
+ show: (options: FlashMessageOptions) => flashManager.showMessage(options),
24
+ hide: () => flashManager.hideMessage(),
25
+ }),
26
+ []
27
+ );
28
+
29
+ return (
30
+ <FlashMessageContext.Provider value={contextValue}>
31
+ {children}
32
+ <FlashMessage {...containerProps} />
33
+ </FlashMessageContext.Provider>
34
+ );
35
+ };
36
+
37
+ export function useFlashMessage(): FlashMessageContextValue {
38
+ const context = useContext(FlashMessageContext);
39
+ if (!context) {
40
+ // Graceful fallback to global manager even if Provider is omitted
41
+ return {
42
+ show: (options: FlashMessageOptions) => flashManager.showMessage(options),
43
+ hide: () => flashManager.hideMessage(),
44
+ };
45
+ }
46
+ return context;
47
+ }
48
+
@@ -0,0 +1,2 @@
1
+ export { useFlashMessage } from '../FlashMessageProvider';
2
+
package/src/icons.tsx ADDED
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+ import { View, Text, StyleSheet } from 'react-native';
3
+ import type { FlashMessageType } from './types';
4
+
5
+ interface BuiltInIconProps {
6
+ type: FlashMessageType;
7
+ size?: number;
8
+ color?: string;
9
+ }
10
+
11
+ export const BuiltInIcon: React.FC<BuiltInIconProps> = ({
12
+ type,
13
+ size = 22,
14
+ color = '#FFFFFF',
15
+ }) => {
16
+ const containerStyle = [
17
+ styles.circle,
18
+ {
19
+ width: size,
20
+ height: size,
21
+ borderRadius: size / 2,
22
+ borderColor: color,
23
+ },
24
+ ];
25
+
26
+ switch (type) {
27
+ case 'success':
28
+ return (
29
+ <View style={containerStyle}>
30
+ <Text style={[styles.symbol, { color, fontSize: size * 0.65 }]}>✓</Text>
31
+ </View>
32
+ );
33
+ case 'danger':
34
+ return (
35
+ <View style={containerStyle}>
36
+ <Text style={[styles.symbol, { color, fontSize: size * 0.65 }]}>✕</Text>
37
+ </View>
38
+ );
39
+ case 'warning':
40
+ return (
41
+ <View style={containerStyle}>
42
+ <Text style={[styles.symbol, { color, fontSize: size * 0.65 }]}>!</Text>
43
+ </View>
44
+ );
45
+ case 'info':
46
+ return (
47
+ <View style={containerStyle}>
48
+ <Text style={[styles.symbol, { color, fontSize: size * 0.65 }]}>ℹ</Text>
49
+ </View>
50
+ );
51
+ case 'default':
52
+ default:
53
+ return (
54
+ <View style={containerStyle}>
55
+ <Text style={[styles.symbol, { color, fontSize: size * 0.6 }]}>●</Text>
56
+ </View>
57
+ );
58
+ }
59
+ };
60
+
61
+ const styles = StyleSheet.create({
62
+ circle: {
63
+ borderWidth: 1.5,
64
+ justifyContent: 'center',
65
+ alignItems: 'center',
66
+ },
67
+ symbol: {
68
+ fontWeight: '800',
69
+ textAlign: 'center',
70
+ includeFontPadding: false,
71
+ lineHeight: 22,
72
+ },
73
+ });
74
+
package/src/index.ts ADDED
@@ -0,0 +1,31 @@
1
+ import { FlashMessage } from './FlashMessage';
2
+
3
+ export { FlashMessage };
4
+ export default FlashMessage;
5
+
6
+ export {
7
+ flashManager,
8
+ showMessage,
9
+ hideMessage,
10
+ flash,
11
+ dismissFlash,
12
+ } from './FlashMessageManager';
13
+
14
+ export {
15
+ FlashMessageProvider,
16
+ useFlashMessage,
17
+ } from './FlashMessageProvider';
18
+
19
+ export { BuiltInIcon } from './icons';
20
+ export { COLORS, styles } from './styles';
21
+
22
+ export type {
23
+ FlashMessageAction,
24
+ FlashMessageContainerProps,
25
+ FlashMessageIcon,
26
+ FlashMessageOptions,
27
+ FlashMessagePosition,
28
+ FlashMessageRef,
29
+ FlashMessageTransition,
30
+ FlashMessageType,
31
+ } from './types';
package/src/styles.ts ADDED
@@ -0,0 +1,135 @@
1
+ import { StyleSheet, Platform } from 'react-native';
2
+ import type { FlashMessageType } from './types';
3
+
4
+ export const COLORS: Record<FlashMessageType, { background: string; text: string; icon: string }> = {
5
+ default: {
6
+ background: '#1F2937', // Slate 800
7
+ text: '#FFFFFF',
8
+ icon: '#9CA3AF',
9
+ },
10
+ success: {
11
+ background: '#059669', // Emerald 600
12
+ text: '#FFFFFF',
13
+ icon: '#D1FAE5',
14
+ },
15
+ danger: {
16
+ background: '#DC2626', // Red 600
17
+ text: '#FFFFFF',
18
+ icon: '#FEE2E2',
19
+ },
20
+ warning: {
21
+ background: '#D97706', // Amber 600
22
+ text: '#FFFFFF',
23
+ icon: '#FEF3C7',
24
+ },
25
+ info: {
26
+ background: '#2563EB', // Blue 600
27
+ text: '#FFFFFF',
28
+ icon: '#DBEAFE',
29
+ },
30
+ };
31
+
32
+ export const styles = StyleSheet.create({
33
+ rootOverlay: {
34
+ position: 'absolute',
35
+ left: 0,
36
+ right: 0,
37
+ zIndex: 99999,
38
+ elevation: 99999,
39
+ },
40
+ rootTop: {
41
+ top: 0,
42
+ },
43
+ rootBottom: {
44
+ bottom: 0,
45
+ },
46
+ rootCenter: {
47
+ top: 0,
48
+ bottom: 0,
49
+ justifyContent: 'center',
50
+ alignItems: 'center',
51
+ },
52
+ toastContainer: {
53
+ flexDirection: 'row',
54
+ alignItems: 'center',
55
+ overflow: 'hidden',
56
+ ...Platform.select({
57
+ ios: {
58
+ shadowColor: '#000000',
59
+ shadowOffset: { width: 0, height: 6 },
60
+ shadowOpacity: 0.18,
61
+ shadowRadius: 12,
62
+ },
63
+ android: {
64
+ elevation: 8,
65
+ },
66
+ web: {
67
+ boxShadow: '0 10px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.1)',
68
+ } as any,
69
+ }),
70
+ },
71
+ floatingContainer: {
72
+ marginHorizontal: 16,
73
+ borderRadius: 14,
74
+ borderWidth: StyleSheet.hairlineWidth,
75
+ borderColor: 'rgba(255, 255, 255, 0.15)',
76
+ },
77
+ edgeToEdgeContainer: {
78
+ marginHorizontal: 0,
79
+ borderRadius: 0,
80
+ },
81
+ contentWrapper: {
82
+ flex: 1,
83
+ flexDirection: 'row',
84
+ alignItems: 'center',
85
+ paddingVertical: 14,
86
+ paddingHorizontal: 16,
87
+ },
88
+ iconContainer: {
89
+ marginRight: 12,
90
+ justifyContent: 'center',
91
+ alignItems: 'center',
92
+ },
93
+ textContainer: {
94
+ flex: 1,
95
+ justifyContent: 'center',
96
+ },
97
+ titleText: {
98
+ fontSize: 15,
99
+ fontWeight: '600',
100
+ letterSpacing: -0.2,
101
+ color: '#FFFFFF',
102
+ },
103
+ descriptionText: {
104
+ fontSize: 13,
105
+ fontWeight: '400',
106
+ color: 'rgba(255, 255, 255, 0.88)',
107
+ marginTop: 2,
108
+ lineHeight: 18,
109
+ },
110
+ actionButton: {
111
+ marginLeft: 10,
112
+ paddingHorizontal: 12,
113
+ paddingVertical: 6,
114
+ borderRadius: 8,
115
+ backgroundColor: 'rgba(255, 255, 255, 0.2)',
116
+ },
117
+ actionButtonText: {
118
+ fontSize: 13,
119
+ fontWeight: '700',
120
+ color: '#FFFFFF',
121
+ },
122
+ progressBarContainer: {
123
+ position: 'absolute',
124
+ bottom: 0,
125
+ left: 0,
126
+ right: 0,
127
+ height: 3,
128
+ backgroundColor: 'rgba(255, 255, 255, 0.25)',
129
+ },
130
+ progressBarFill: {
131
+ height: '100%',
132
+ backgroundColor: '#FFFFFF',
133
+ },
134
+ });
135
+
package/src/types.ts ADDED
@@ -0,0 +1,101 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
3
+
4
+ export type FlashMessageType = 'default' | 'success' | 'danger' | 'warning' | 'info';
5
+
6
+ export type FlashMessagePosition = 'top' | 'bottom' | 'center';
7
+
8
+ export type FlashMessageTransition = 'slide' | 'fade' | 'bounce';
9
+
10
+ export type FlashMessageIcon =
11
+ | FlashMessageType
12
+ | 'none'
13
+ | ReactNode
14
+ | ((props: { size: number; color: string }) => ReactNode);
15
+
16
+ export interface FlashMessageAction {
17
+ label: string;
18
+ onPress: () => void;
19
+ style?: StyleProp<ViewStyle>;
20
+ textStyle?: StyleProp<TextStyle>;
21
+ }
22
+
23
+ export interface FlashMessageOptions {
24
+ /** Unique ID for the message. Auto-generated if not provided */
25
+ id?: string;
26
+ /** Primary title or content */
27
+ message: string | ReactNode;
28
+ /** Secondary subtitle or body content */
29
+ description?: string | ReactNode;
30
+ /** Preset semantic style */
31
+ type?: FlashMessageType;
32
+ /** Screen position: 'top', 'bottom', or 'center'. Default: 'top' */
33
+ position?: FlashMessagePosition;
34
+ /** Display duration in milliseconds before auto-hiding. Default: 3000ms. Set to 0 to disable */
35
+ duration?: number;
36
+ /** Whether the message auto-dismisses after duration. Default: true */
37
+ autoHide?: boolean;
38
+ /** Floating pill with rounded corners vs edge-to-edge bar. Default: true */
39
+ floating?: boolean;
40
+ /** Preset or custom icon */
41
+ icon?: FlashMessageIcon;
42
+ /** Interactive action button (e.g. Undo, Retry) */
43
+ action?: FlashMessageAction;
44
+ /** Callback fired when the flash message card is tapped */
45
+ onPress?: () => void;
46
+ /** Callback fired when the flash message is long-pressed */
47
+ onLongPress?: () => void;
48
+ /** Callback fired after the message appears */
49
+ onShow?: () => void;
50
+ /** Callback fired after the message has fully hidden */
51
+ onHide?: () => void;
52
+ /** Custom container style */
53
+ style?: StyleProp<ViewStyle>;
54
+ /** Custom title text style */
55
+ titleStyle?: StyleProp<TextStyle>;
56
+ /** Custom description text style */
57
+ textStyle?: StyleProp<TextStyle>;
58
+ /** Override background color */
59
+ backgroundColor?: string;
60
+ /** Override text and icon color */
61
+ color?: string;
62
+ /** Dismiss when tapped. Default: true */
63
+ hideOnPress?: boolean;
64
+ /** Enable swipe up/down gesture to dismiss. Default: true */
65
+ swipeToDismiss?: boolean;
66
+ /** Temporarily pause the autoHide timer while finger touches the banner. Default: true */
67
+ pauseOnPress?: boolean;
68
+ /** Show an animated countdown progress bar at the bottom of the card. Default: false */
69
+ showProgressBar?: boolean;
70
+ /** Transition animation style: 'slide' | 'fade' | 'bounce'. Default: 'slide' */
71
+ transition?: FlashMessageTransition;
72
+ /** Override manual top inset / status bar height if needed */
73
+ statusBarHeight?: number;
74
+ /** Custom render function to completely override inner layout */
75
+ customComponent?: (options: FlashMessageOptions, hide: () => void) => ReactNode;
76
+ }
77
+
78
+ export interface FlashMessageRef {
79
+ showMessage: (options: FlashMessageOptions) => void;
80
+ hideMessage: () => void;
81
+ }
82
+
83
+ export interface FlashMessageContainerProps {
84
+ /** Default position for all messages if not specified in options */
85
+ position?: FlashMessagePosition;
86
+ /** Default duration in ms. Default: 3000 */
87
+ duration?: number;
88
+ /** Default floating style. Default: true */
89
+ floating?: boolean;
90
+ /** Default transition. Default: 'slide' */
91
+ transition?: FlashMessageTransition;
92
+ /** Optional top status bar offset */
93
+ statusBarOffset?: number;
94
+ /** Optional bottom navigation bar offset */
95
+ bottomOffset?: number;
96
+ /** Extra container style */
97
+ style?: StyleProp<ViewStyle>;
98
+ /** Can message replace current one immediately or queue. Default: 'replace' */
99
+ mode?: 'replace' | 'queue';
100
+ }
101
+
@@ -0,0 +1,45 @@
1
+ import { Platform, StatusBar, Dimensions } from 'react-native';
2
+
3
+ export interface EdgeInsets {
4
+ top: number;
5
+ bottom: number;
6
+ left: number;
7
+ right: number;
8
+ }
9
+
10
+ /**
11
+ * Returns safe default insets when react-native-safe-area-context is not present.
12
+ * If the user's app wraps FlashMessage in SafeAreaProvider, safe-area context takes precedence.
13
+ */
14
+ export function getDefaultSafeAreaInsets(): EdgeInsets {
15
+ const { width, height } = Dimensions.get('window');
16
+ const isLandscape = width > height;
17
+
18
+ if (Platform.OS === 'android') {
19
+ return {
20
+ top: StatusBar.currentHeight ?? 24,
21
+ bottom: 16,
22
+ left: 0,
23
+ right: 0,
24
+ };
25
+ }
26
+
27
+ if (Platform.OS === 'ios') {
28
+ // Dynamic island & modern notch iPhones typically have 47-59pt top inset
29
+ const isModernIphone = !Platform.isPad && !Platform.isTV && (height >= 812 || width >= 812);
30
+ return {
31
+ top: isModernIphone ? (isLandscape ? 0 : 50) : 20,
32
+ bottom: isModernIphone ? 34 : 0,
33
+ left: 0,
34
+ right: 0,
35
+ };
36
+ }
37
+
38
+ return {
39
+ top: 16,
40
+ bottom: 16,
41
+ left: 0,
42
+ right: 0,
43
+ };
44
+ }
45
+