react-native-bread 0.1.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.
Files changed (79) hide show
  1. package/README.md +148 -0
  2. package/lib/commonjs/icons/CloseIcon.js +22 -0
  3. package/lib/commonjs/icons/CloseIcon.js.map +1 -0
  4. package/lib/commonjs/icons/GreenCheck.js +27 -0
  5. package/lib/commonjs/icons/GreenCheck.js.map +1 -0
  6. package/lib/commonjs/icons/InfoIcon.js +24 -0
  7. package/lib/commonjs/icons/InfoIcon.js.map +1 -0
  8. package/lib/commonjs/icons/RedX.js +27 -0
  9. package/lib/commonjs/icons/RedX.js.map +1 -0
  10. package/lib/commonjs/icons/index.js +34 -0
  11. package/lib/commonjs/icons/index.js.map +1 -0
  12. package/lib/commonjs/index.js +59 -0
  13. package/lib/commonjs/index.js.map +1 -0
  14. package/lib/commonjs/toast-api.js +127 -0
  15. package/lib/commonjs/toast-api.js.map +1 -0
  16. package/lib/commonjs/toast-provider.js +71 -0
  17. package/lib/commonjs/toast-provider.js.map +1 -0
  18. package/lib/commonjs/toast-store.js +278 -0
  19. package/lib/commonjs/toast-store.js.map +1 -0
  20. package/lib/commonjs/toast.js +445 -0
  21. package/lib/commonjs/toast.js.map +1 -0
  22. package/lib/commonjs/types.js +6 -0
  23. package/lib/commonjs/types.js.map +1 -0
  24. package/lib/module/icons/CloseIcon.js +16 -0
  25. package/lib/module/icons/CloseIcon.js.map +1 -0
  26. package/lib/module/icons/GreenCheck.js +21 -0
  27. package/lib/module/icons/GreenCheck.js.map +1 -0
  28. package/lib/module/icons/InfoIcon.js +18 -0
  29. package/lib/module/icons/InfoIcon.js.map +1 -0
  30. package/lib/module/icons/RedX.js +21 -0
  31. package/lib/module/icons/RedX.js.map +1 -0
  32. package/lib/module/icons/index.js +7 -0
  33. package/lib/module/icons/index.js.map +1 -0
  34. package/lib/module/index.js +14 -0
  35. package/lib/module/index.js.map +1 -0
  36. package/lib/module/toast-api.js +124 -0
  37. package/lib/module/toast-api.js.map +1 -0
  38. package/lib/module/toast-provider.js +67 -0
  39. package/lib/module/toast-provider.js.map +1 -0
  40. package/lib/module/toast-store.js +274 -0
  41. package/lib/module/toast-store.js.map +1 -0
  42. package/lib/module/toast.js +439 -0
  43. package/lib/module/toast.js.map +1 -0
  44. package/lib/module/types.js +4 -0
  45. package/lib/module/types.js.map +1 -0
  46. package/lib/typescript/icons/CloseIcon.d.ts +3 -0
  47. package/lib/typescript/icons/CloseIcon.d.ts.map +1 -0
  48. package/lib/typescript/icons/GreenCheck.d.ts +3 -0
  49. package/lib/typescript/icons/GreenCheck.d.ts.map +1 -0
  50. package/lib/typescript/icons/InfoIcon.d.ts +3 -0
  51. package/lib/typescript/icons/InfoIcon.d.ts.map +1 -0
  52. package/lib/typescript/icons/RedX.d.ts +3 -0
  53. package/lib/typescript/icons/RedX.d.ts.map +1 -0
  54. package/lib/typescript/icons/index.d.ts +5 -0
  55. package/lib/typescript/icons/index.d.ts.map +1 -0
  56. package/lib/typescript/index.d.ts +7 -0
  57. package/lib/typescript/index.d.ts.map +1 -0
  58. package/lib/typescript/toast-api.d.ts +109 -0
  59. package/lib/typescript/toast-api.d.ts.map +1 -0
  60. package/lib/typescript/toast-provider.d.ts +52 -0
  61. package/lib/typescript/toast-provider.d.ts.map +1 -0
  62. package/lib/typescript/toast-store.d.ts +26 -0
  63. package/lib/typescript/toast-store.d.ts.map +1 -0
  64. package/lib/typescript/toast.d.ts +2 -0
  65. package/lib/typescript/toast.d.ts.map +1 -0
  66. package/lib/typescript/types.d.ts +101 -0
  67. package/lib/typescript/types.d.ts.map +1 -0
  68. package/package.json +87 -0
  69. package/src/icons/CloseIcon.tsx +10 -0
  70. package/src/icons/GreenCheck.tsx +16 -0
  71. package/src/icons/InfoIcon.tsx +12 -0
  72. package/src/icons/RedX.tsx +16 -0
  73. package/src/icons/index.ts +4 -0
  74. package/src/index.ts +26 -0
  75. package/src/toast-api.ts +213 -0
  76. package/src/toast-provider.tsx +81 -0
  77. package/src/toast-store.ts +270 -0
  78. package/src/toast.tsx +417 -0
  79. package/src/types.ts +121 -0
@@ -0,0 +1,52 @@
1
+ import { type ReactNode } from "react";
2
+ import type { ToastConfig } from "./types";
3
+ interface BreadLoafProps {
4
+ children: ReactNode;
5
+ /**
6
+ * Configuration for customizing toast behavior and appearance.
7
+ * All properties are optional and will be merged with defaults.
8
+ *
9
+ * @property position - Where toasts appear: `'top'` (default) or `'bottom'`
10
+ * @property offset - Extra spacing from screen edge in pixels (default: `0`)
11
+ * @property stacking - Show multiple toasts stacked (default: `true`). When `false`, only one toast shows at a time
12
+ * @property defaultDuration - Default display time in ms (default: `4000`)
13
+ * @property colors - Customize colors per toast type (`success`, `error`, `info`, `loading`)
14
+ * @property toastStyle - Style overrides for the toast container (borderRadius, shadow, padding, etc.)
15
+ * @property titleStyle - Style overrides for the title text
16
+ * @property descriptionStyle - Style overrides for the description text
17
+ */
18
+ config?: ToastConfig;
19
+ }
20
+ /**
21
+ * Toast provider component that enables toast notifications in your app.
22
+ * Wrap your root component with `<BreadLoaf>` to start showing toasts.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * import { BreadLoaf } from 'react-native-bread';
27
+ *
28
+ * // Basic usage
29
+ * <BreadLoaf>
30
+ * <App />
31
+ * </BreadLoaf>
32
+ *
33
+ * // With configuration
34
+ * <BreadLoaf
35
+ * config={{
36
+ * position: 'bottom',
37
+ * stacking: false,
38
+ * defaultDuration: 5000,
39
+ * colors: {
40
+ * success: { accent: '#22c55e', background: '#f0fdf4' },
41
+ * error: { accent: '#ef4444', background: '#fef2f2' },
42
+ * },
43
+ * toastStyle: { borderRadius: 12 },
44
+ * }}
45
+ * >
46
+ * <App />
47
+ * </BreadLoaf>
48
+ * ```
49
+ */
50
+ export declare function BreadLoaf({ children, config }: BreadLoafProps): import("react/jsx-runtime").JSX.Element;
51
+ export {};
52
+ //# sourceMappingURL=toast-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast-provider.d.ts","sourceRoot":"","sources":["../../src/toast-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,OAAO,CAAC;AAIlD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,UAAU,cAAc;IACtB,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,cAAc,2CAgB7D"}
@@ -0,0 +1,26 @@
1
+ import type { Toast, ToastConfig, ToastOptions, ToastState, ToastTheme, ToastType } from "./types";
2
+ export type Listener = (state: ToastState) => void;
3
+ declare class ToastStore {
4
+ private state;
5
+ private theme;
6
+ private listeners;
7
+ private toastIdCounter;
8
+ private timeouts;
9
+ subscribe: (listener: Listener) => () => void;
10
+ private emit;
11
+ private setState;
12
+ getState: () => ToastState;
13
+ getTheme: () => ToastTheme;
14
+ setConfig: (config: ToastConfig | undefined) => void;
15
+ show: (title: string, description?: string, type?: ToastType, duration?: number, options?: ToastOptions) => string;
16
+ private addToast;
17
+ private scheduleTimeout;
18
+ private rescheduleAllTimeouts;
19
+ hide: (id: string) => void;
20
+ private removeToast;
21
+ updateToast: (id: string, data: Partial<Omit<Toast, "id" | "createdAt">>) => void;
22
+ hideAll: () => void;
23
+ }
24
+ export declare const toastStore: ToastStore;
25
+ export type { Toast, ToastState, ToastType };
26
+ //# sourceMappingURL=toast-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast-store.d.ts","sourceRoot":"","sources":["../../src/toast-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAmB,MAAM,SAAS,CAAC;AAEpH,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;AA0DnD,cAAM,UAAU;IACd,OAAO,CAAC,KAAK,CAEX;IAEF,OAAO,CAAC,KAAK,CAA6B;IAE1C,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,QAAQ,CAAoD;IAEpE,SAAS,GAAI,UAAU,QAAQ,gBAK7B;IAEF,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,QAAQ;IAKhB,QAAQ,mBAAoB;IAE5B,QAAQ,mBAAoB;IAE5B,SAAS,GAAI,QAAQ,WAAW,GAAG,SAAS,UAE1C;IAEF,IAAI,GACF,OAAO,MAAM,EACb,cAAc,MAAM,EACpB,OAAM,SAAqB,EAC3B,WAAW,MAAM,EACjB,UAAU,YAAY,KACrB,MAAM,CA8DP;IAEF,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,qBAAqB;IAW7B,IAAI,GAAI,IAAI,MAAM,UAqBhB;IAEF,OAAO,CAAC,WAAW;IAenB,WAAW,GAAI,IAAI,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,WAAW,CAAC,CAAC,UAYvE;IAEF,OAAO,aAML;CACH;AAED,eAAO,MAAM,UAAU,YAAmB,CAAC;AAE3C,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const ToastContainer: () => import("react/jsx-runtime").JSX.Element | null;
2
+ //# sourceMappingURL=toast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../src/toast.tsx"],"names":[],"mappings":"AAkFA,eAAO,MAAM,cAAc,sDAgD1B,CAAC"}
@@ -0,0 +1,101 @@
1
+ import type { ReactNode } from "react";
2
+ import type { TextStyle, ViewStyle } from "react-native";
3
+ export type ToastType = "success" | "error" | "info" | "loading";
4
+ export type ToastPosition = "top" | "bottom";
5
+ export interface ToastTypeColors {
6
+ /** Accent color used for title text and icons */
7
+ accent: string;
8
+ /** Background color of the toast */
9
+ background: string;
10
+ }
11
+ /** Props passed to custom icon render functions */
12
+ export interface IconProps {
13
+ /** The accent color from the theme for this toast type */
14
+ color: string;
15
+ /** Default icon size */
16
+ size: number;
17
+ }
18
+ /** Custom icon render function */
19
+ export type IconRenderFn = (props: IconProps) => ReactNode;
20
+ export interface ToastTheme {
21
+ /** Position of toasts on screen */
22
+ position: ToastPosition;
23
+ /** Extra offset from safe area edge (in addition to safe area insets) */
24
+ offset: number;
25
+ /** Whether to show multiple toasts stacked (default: true). When false, only one toast shows at a time. */
26
+ stacking: boolean;
27
+ /** Maximum number of toasts visible at once when stacking is enabled (default: 3) */
28
+ maxStack: number;
29
+ /** Whether toasts can be dismissed via swipe gesture (default: true) */
30
+ dismissible: boolean;
31
+ /** Whether to show the close button on toasts (default: true). Loading toasts never show close button. */
32
+ showCloseButton: boolean;
33
+ /** Colors for each toast type */
34
+ colors: Record<ToastType, ToastTypeColors>;
35
+ /** Custom icons for each toast type */
36
+ icons: Partial<Record<ToastType, IconRenderFn>>;
37
+ /** Style overrides for the toast container */
38
+ toastStyle: ViewStyle;
39
+ /** Style overrides for the title text */
40
+ titleStyle: TextStyle;
41
+ /** Style overrides for the description text */
42
+ descriptionStyle: TextStyle;
43
+ /** Default duration in ms for toasts (default: 4000) */
44
+ defaultDuration: number;
45
+ }
46
+ /** Per-toast options for customizing individual toasts */
47
+ export interface ToastOptions {
48
+ /** Description text */
49
+ description?: string;
50
+ /** Duration in ms (overrides default) */
51
+ duration?: number;
52
+ /** Custom icon (ReactNode or render function) */
53
+ icon?: ReactNode | IconRenderFn;
54
+ /** Style overrides for this toast's container */
55
+ style?: ViewStyle;
56
+ /** Style overrides for this toast's title */
57
+ titleStyle?: TextStyle;
58
+ /** Style overrides for this toast's description */
59
+ descriptionStyle?: TextStyle;
60
+ /** Whether this toast can be dismissed via swipe (overrides config) */
61
+ dismissible?: boolean;
62
+ /** Whether to show the close button on this toast (overrides config) */
63
+ showCloseButton?: boolean;
64
+ }
65
+ /** Configuration options for customizing toast behavior and appearance. All properties are optional. */
66
+ export type ToastConfig = {
67
+ [K in keyof ToastTheme]?: K extends "colors" ? Partial<Record<ToastType, Partial<ToastTypeColors>>> : K extends "icons" ? Partial<Record<ToastType, IconRenderFn>> : ToastTheme[K];
68
+ };
69
+ export interface Toast {
70
+ id: string;
71
+ title: string;
72
+ description?: string;
73
+ type: ToastType;
74
+ duration: number;
75
+ createdAt: number;
76
+ isExiting?: boolean;
77
+ /** Per-toast style/icon overrides */
78
+ options?: ToastOptions;
79
+ }
80
+ export interface ToastState {
81
+ /** Visible toasts (index 0 = front/newest) */
82
+ visibleToasts: Toast[];
83
+ }
84
+ export type MessageInput = string | {
85
+ title: string;
86
+ description?: string;
87
+ /** Override duration (ms) after promise settles */
88
+ duration?: number;
89
+ };
90
+ export type ErrorMessageInput = MessageInput | ((error: Error) => MessageInput);
91
+ export interface PromiseMessages {
92
+ loading: MessageInput;
93
+ success: MessageInput;
94
+ error: ErrorMessageInput;
95
+ }
96
+ export interface PromiseResult<T> {
97
+ data?: T;
98
+ error?: Error;
99
+ success: boolean;
100
+ }
101
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,87 @@
1
+ {
2
+ "name": "react-native-bread",
3
+ "version": "0.1.0",
4
+ "description": "A delicious toast library for React Native with beautiful animations and gesture support",
5
+ "main": "lib/commonjs/index.js",
6
+ "module": "lib/module/index.js",
7
+ "types": "lib/typescript/index.d.ts",
8
+ "react-native": "src/index.ts",
9
+ "source": "src/index.ts",
10
+ "files": ["src", "lib", "!**/__tests__", "!**/__fixtures__", "!**/__mocks__"],
11
+ "sideEffects": false,
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/AlshehriAli0/react-native-bread.git"
15
+ },
16
+ "author": "Ali <ali0fawzish@outlook.com>",
17
+ "license": "MIT",
18
+ "bugs": {
19
+ "url": "https://github.com/AlshehriAli0/react-native-bread/issues"
20
+ },
21
+ "homepage": "https://github.com/AlshehriAli0/react-native-bread#readme",
22
+ "keywords": ["react-native", "toast", "notification", "snackbar", "bread", "animation", "gesture", "reanimated"],
23
+ "scripts": {
24
+ "build": "bob build",
25
+ "typecheck": "tsc --noEmit",
26
+ "clean": "rm -rf lib",
27
+ "prepare": "bob build"
28
+ },
29
+ "devDependencies": {
30
+ "react": "19.1.0",
31
+ "react-native": "0.81.5",
32
+ "react-native-builder-bob": "^0.35.2",
33
+ "react-native-gesture-handler": "~2.28.0",
34
+ "react-native-reanimated": "~4.1.1",
35
+ "react-native-safe-area-context": "~5.4.0",
36
+ "react-native-worklets": "0.5.1",
37
+ "react-native-svg": "15.12.1",
38
+ "typescript": "~5.9.2"
39
+ },
40
+ "peerDependencies": {
41
+ "react": ">=18.0.0",
42
+ "react-native": ">=0.76.0",
43
+ "react-native-gesture-handler": ">=2.0.0",
44
+ "react-native-reanimated": ">=4.0.0",
45
+ "react-native-safe-area-context": ">=4.0.0",
46
+ "react-native-svg": ">=14.0.0",
47
+ "react-native-worklets": ">=0.4.0"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "react-native-gesture-handler": {
51
+ "optional": false
52
+ },
53
+ "react-native-reanimated": {
54
+ "optional": false
55
+ },
56
+ "react-native-safe-area-context": {
57
+ "optional": false
58
+ },
59
+ "react-native-svg": {
60
+ "optional": false
61
+ }
62
+ },
63
+ "react-native-builder-bob": {
64
+ "source": "src",
65
+ "output": "lib",
66
+ "targets": [
67
+ [
68
+ "commonjs",
69
+ {
70
+ "esm": true
71
+ }
72
+ ],
73
+ [
74
+ "module",
75
+ {
76
+ "esm": true
77
+ }
78
+ ],
79
+ [
80
+ "typescript",
81
+ {
82
+ "project": "tsconfig.build.json"
83
+ }
84
+ ]
85
+ ]
86
+ }
87
+ }
@@ -0,0 +1,10 @@
1
+ import Svg, { Path, type SvgProps } from "react-native-svg";
2
+
3
+ export const CloseIcon = (props: SvgProps) => (
4
+ <Svg viewBox="0 0 24 24" width={24} height={24} fill="none" {...props}>
5
+ <Path
6
+ fill={props.fill ?? "#8993A4"}
7
+ d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"
8
+ />
9
+ </Svg>
10
+ );
@@ -0,0 +1,16 @@
1
+ import Svg, { Path, type SvgProps } from "react-native-svg";
2
+
3
+ export const GreenCheck = (props: SvgProps) => (
4
+ <Svg viewBox="0 0 30 31" width={30} height={31} fill="none" {...props}>
5
+ <Path
6
+ fill={props.fill ?? "#28B770"}
7
+ fillRule="evenodd"
8
+ d="m19.866 13.152-5.772 5.773a.933.933 0 0 1-1.326 0L9.88 16.039a.938.938 0 0 1 1.325-1.327l2.225 2.224 5.109-5.11a.938.938 0 1 1 1.326 1.326Zm.28-9.652H9.602C5.654 3.5 3 6.276 3 10.409v9.935c0 4.131 2.654 6.906 6.602 6.906h10.543c3.95 0 6.605-2.775 6.605-6.906v-9.935c0-4.133-2.654-6.909-6.604-6.909Z"
9
+ clipRule="evenodd"
10
+ />
11
+ <Path
12
+ fill="#fff"
13
+ d="m19.866 13.152-5.772 5.773a.933.933 0 0 1-1.326 0L9.88 16.039a.938.938 0 0 1 1.325-1.327l2.225 2.224 5.109-5.11a.938.938 0 1 1 1.326 1.326Z"
14
+ />
15
+ </Svg>
16
+ );
@@ -0,0 +1,12 @@
1
+ import Svg, { Path, type SvgProps } from "react-native-svg";
2
+
3
+ export const InfoIcon = (props: SvgProps) => (
4
+ <Svg viewBox="0 0 24 24" width={24} height={24} fill="none" {...props}>
5
+ <Path
6
+ fill={props.fill ?? "#EDBE43"}
7
+ fillRule="evenodd"
8
+ d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm1 15h-2v-6h2v6Zm0-8h-2V7h2v2Z"
9
+ clipRule="evenodd"
10
+ />
11
+ </Svg>
12
+ );
@@ -0,0 +1,16 @@
1
+ import Svg, { Path, type SvgProps } from "react-native-svg";
2
+
3
+ export const RedX = (props: SvgProps) => (
4
+ <Svg viewBox="0 0 24 24" width={24} height={24} fill="none" {...props}>
5
+ <Path
6
+ fill={props.fill ?? "#F05964"}
7
+ fillRule="evenodd"
8
+ d="M15.58 15.572a.935.935 0 0 1-1.326 0l-2.258-2.258-2.251 2.252a.938.938 0 0 1-1.326-1.325l2.251-2.252-2.252-2.254A.936.936 0 1 1 9.742 8.41l2.253 2.252 2.252-2.25a.939.939 0 0 1 1.325 1.325l-2.25 2.252 2.257 2.257a.938.938 0 0 1 0 1.326ZM17.271.126H6.727C2.777.125.125 2.9.125 7.032v9.936c0 4.13 2.652 6.907 6.603 6.907H17.27c3.95 0 6.605-2.776 6.605-6.907V7.032c0-4.132-2.654-6.907-6.604-6.907Z"
9
+ clipRule="evenodd"
10
+ />
11
+ <Path
12
+ fill="#fff"
13
+ d="M15.58 15.572a.935.935 0 0 1-1.326 0l-2.258-2.258-2.251 2.252a.938.938 0 0 1-1.326-1.325l2.251-2.252-2.252-2.254A.936.936 0 1 1 9.742 8.41l2.253 2.252 2.252-2.25a.939.939 0 0 1 1.325 1.325l-2.25 2.252 2.257 2.257a.938.938 0 0 1 0 1.326Z"
14
+ />
15
+ </Svg>
16
+ );
@@ -0,0 +1,4 @@
1
+ export { CloseIcon } from "./CloseIcon";
2
+ export { GreenCheck } from "./GreenCheck";
3
+ export { InfoIcon } from "./InfoIcon";
4
+ export { RedX } from "./RedX";
package/src/index.ts ADDED
@@ -0,0 +1,26 @@
1
+ // Main exports
2
+
3
+ // Icons (for customization)
4
+ export { CloseIcon, GreenCheck, InfoIcon, RedX } from "./icons";
5
+ export { ToastContainer } from "./toast";
6
+ export { toast } from "./toast-api";
7
+ export { BreadLoaf } from "./toast-provider";
8
+
9
+ // Store (for advanced usage)
10
+ export { toastStore } from "./toast-store";
11
+ // Types
12
+ export type {
13
+ ErrorMessageInput,
14
+ IconProps,
15
+ IconRenderFn,
16
+ MessageInput,
17
+ PromiseMessages,
18
+ PromiseResult,
19
+ Toast,
20
+ ToastConfig,
21
+ ToastOptions,
22
+ ToastPosition,
23
+ ToastState,
24
+ ToastType,
25
+ ToastTypeColors,
26
+ } from "./types";
@@ -0,0 +1,213 @@
1
+ import { toastStore } from "./toast-store";
2
+ import type {
3
+ ErrorMessageInput,
4
+ MessageInput,
5
+ PromiseMessages,
6
+ PromiseResult,
7
+ ToastOptions,
8
+ ToastType,
9
+ } from "./types";
10
+
11
+ /** Second parameter can be a string (description) or options object */
12
+ type DescriptionOrOptions = string | ToastOptions;
13
+
14
+ const _toast = (title: string, description?: string, type?: ToastType, duration?: number) => {
15
+ toastStore.show(title, description, type, duration);
16
+ };
17
+
18
+ /** Helper to parse the second argument which can be string or options */
19
+ const parseDescriptionOrOptions = (
20
+ arg?: DescriptionOrOptions
21
+ ): { description?: string; duration?: number; options?: ToastOptions } => {
22
+ if (!arg) return {};
23
+ if (typeof arg === "string") return { description: arg };
24
+ return {
25
+ description: arg.description,
26
+ duration: arg.duration,
27
+ options: arg,
28
+ };
29
+ };
30
+
31
+ const parseMessage = (input: MessageInput): { title: string; description?: string; duration?: number } =>
32
+ typeof input === "string" ? { title: input } : input;
33
+
34
+ const parseErrorMessage = (
35
+ input: ErrorMessageInput,
36
+ error: Error
37
+ ): { title: string; description?: string; duration?: number } => {
38
+ if (typeof input === "function") {
39
+ return parseMessage(input(error));
40
+ }
41
+ return parseMessage(input);
42
+ };
43
+
44
+ const promiseToast = async <T>(promise: Promise<T>, messages: PromiseMessages): Promise<PromiseResult<T>> => {
45
+ const loadingCfg = parseMessage(messages.loading);
46
+
47
+ // Very long duration so it stays visible until we resolve/reject
48
+ const toastId = toastStore.show(
49
+ loadingCfg.title,
50
+ loadingCfg.description,
51
+ "loading",
52
+ loadingCfg.duration ?? 60 * 60 * 1000
53
+ );
54
+
55
+ try {
56
+ const result = await promise;
57
+
58
+ const successCfg = parseMessage(messages.success);
59
+ toastStore.updateToast(toastId, {
60
+ title: successCfg.title,
61
+ description: successCfg.description,
62
+ type: "success",
63
+ duration: successCfg.duration ?? 4000,
64
+ });
65
+
66
+ return { data: result, success: true };
67
+ } catch (err) {
68
+ const error = err instanceof Error ? err : new Error(String(err));
69
+ const errorCfg = parseErrorMessage(messages.error, error);
70
+ toastStore.updateToast(toastId, {
71
+ title: errorCfg.title,
72
+ description: errorCfg.description,
73
+ type: "error",
74
+ duration: errorCfg.duration ?? 4000,
75
+ });
76
+
77
+ return { error, success: false };
78
+ }
79
+ };
80
+
81
+ type BaseToastFn = ((title: string, description?: string, type?: ToastType, duration?: number) => void) & {
82
+ /**
83
+ * Show a success toast with a green checkmark icon.
84
+ * @param title - The toast title
85
+ * @param descriptionOrOptions - Description string OR options object with description, duration, icon, style, etc.
86
+ * @param duration - Duration in ms (default: 4000). Ignored if options object is passed.
87
+ * @example
88
+ * ```ts
89
+ * // Simple usage
90
+ * toast.success("Saved!", "Your changes have been saved");
91
+ *
92
+ * // With options
93
+ * toast.success("Saved!", {
94
+ * description: "Your changes have been saved",
95
+ * duration: 5000,
96
+ * icon: <CustomIcon />,
97
+ * style: { borderRadius: 8 },
98
+ * });
99
+ * ```
100
+ */
101
+ success: (title: string, descriptionOrOptions?: DescriptionOrOptions, duration?: number) => void;
102
+ /**
103
+ * Show an error toast with a red X icon.
104
+ * @param title - The toast title
105
+ * @param descriptionOrOptions - Description string OR options object
106
+ * @param duration - Duration in ms (default: 4000). Ignored if options object is passed.
107
+ * @example
108
+ * ```ts
109
+ * toast.error("Failed", "Something went wrong");
110
+ * toast.error("Failed", { description: "Something went wrong", icon: <CustomErrorIcon /> });
111
+ * ```
112
+ */
113
+ error: (title: string, descriptionOrOptions?: DescriptionOrOptions, duration?: number) => void;
114
+ /**
115
+ * Show an info toast with a blue info icon.
116
+ * @param title - The toast title
117
+ * @param descriptionOrOptions - Description string OR options object
118
+ * @param duration - Duration in ms (default: 4000). Ignored if options object is passed.
119
+ * @example
120
+ * ```ts
121
+ * toast.info("Tip", "Swipe up to dismiss");
122
+ * toast.info("Tip", { description: "Swipe up to dismiss", style: { backgroundColor: '#f0f9ff' } });
123
+ * ```
124
+ */
125
+ info: (title: string, descriptionOrOptions?: DescriptionOrOptions, duration?: number) => void;
126
+ /**
127
+ * Show a loading toast that automatically transitions to success or error
128
+ * based on the promise result. Great for async operations like API calls.
129
+ * @param promise - The promise to track
130
+ * @param messages - Configuration for loading, success, and error states
131
+ * @returns Promise result with `{ data, success: true }` or `{ error, success: false }`
132
+ * @example
133
+ * ```ts
134
+ * toast.promise(fetchUser(id), {
135
+ * loading: { title: "Loading...", description: "Fetching user data" },
136
+ * success: { title: "Done!", description: "User loaded" },
137
+ * error: (err) => ({ title: "Error", description: err.message }),
138
+ * });
139
+ * ```
140
+ */
141
+ promise: <T>(promise: Promise<T>, messages: PromiseMessages) => Promise<PromiseResult<T>>;
142
+ /**
143
+ * Dismiss a specific toast by its ID.
144
+ * @param id - The toast ID to dismiss
145
+ * @example
146
+ * ```ts
147
+ * toast.dismiss("toast-123");
148
+ * ```
149
+ */
150
+ dismiss: (id: string) => void;
151
+ /**
152
+ * Dismiss all visible toasts immediately.
153
+ * @example
154
+ * ```ts
155
+ * toast.dismissAll();
156
+ * ```
157
+ */
158
+ dismissAll: () => void;
159
+ };
160
+
161
+ // Build the toast API
162
+ const toastFn = _toast as unknown as BaseToastFn;
163
+
164
+ toastFn.success = (title: string, descriptionOrOptions?: DescriptionOrOptions, duration?: number) => {
165
+ const { description, duration: optDuration, options } = parseDescriptionOrOptions(descriptionOrOptions);
166
+ toastStore.show(title, description, "success", duration ?? optDuration, options);
167
+ };
168
+
169
+ toastFn.error = (title: string, descriptionOrOptions?: DescriptionOrOptions, duration?: number) => {
170
+ const { description, duration: optDuration, options } = parseDescriptionOrOptions(descriptionOrOptions);
171
+ toastStore.show(title, description, "error", duration ?? optDuration, options);
172
+ };
173
+
174
+ toastFn.info = (title: string, descriptionOrOptions?: DescriptionOrOptions, duration?: number) => {
175
+ const { description, duration: optDuration, options } = parseDescriptionOrOptions(descriptionOrOptions);
176
+ toastStore.show(title, description, "info", duration ?? optDuration, options);
177
+ };
178
+
179
+ toastFn.promise = promiseToast;
180
+
181
+ toastFn.dismiss = (id: string) => {
182
+ toastStore.hide(id);
183
+ };
184
+
185
+ toastFn.dismissAll = () => {
186
+ toastStore.hideAll();
187
+ };
188
+
189
+ /**
190
+ * Toast API for showing notifications.
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * import { toast } from 'react-native-bread';
195
+ *
196
+ * // Basic toasts
197
+ * toast.success("Saved!", "Your changes have been saved");
198
+ * toast.error("Error", "Something went wrong");
199
+ * toast.info("Tip", "Swipe up to dismiss");
200
+ *
201
+ * // Promise toast (loading → success/error)
202
+ * toast.promise(apiCall(), {
203
+ * loading: { title: "Loading..." },
204
+ * success: { title: "Done!" },
205
+ * error: (err) => ({ title: "Failed", description: err.message }),
206
+ * });
207
+ *
208
+ * // Dismiss toasts
209
+ * toast.dismiss(id);
210
+ * toast.dismissAll();
211
+ * ```
212
+ */
213
+ export const toast = toastFn;
@@ -0,0 +1,81 @@
1
+ import { type ReactNode, useEffect } from "react";
2
+ import { StyleSheet, View } from "react-native";
3
+ import { ToastContainer } from "./toast";
4
+ import { toastStore } from "./toast-store";
5
+ import type { ToastConfig } from "./types";
6
+
7
+ interface BreadLoafProps {
8
+ children: ReactNode;
9
+ /**
10
+ * Configuration for customizing toast behavior and appearance.
11
+ * All properties are optional and will be merged with defaults.
12
+ *
13
+ * @property position - Where toasts appear: `'top'` (default) or `'bottom'`
14
+ * @property offset - Extra spacing from screen edge in pixels (default: `0`)
15
+ * @property stacking - Show multiple toasts stacked (default: `true`). When `false`, only one toast shows at a time
16
+ * @property defaultDuration - Default display time in ms (default: `4000`)
17
+ * @property colors - Customize colors per toast type (`success`, `error`, `info`, `loading`)
18
+ * @property toastStyle - Style overrides for the toast container (borderRadius, shadow, padding, etc.)
19
+ * @property titleStyle - Style overrides for the title text
20
+ * @property descriptionStyle - Style overrides for the description text
21
+ */
22
+ config?: ToastConfig;
23
+ }
24
+
25
+ /**
26
+ * Toast provider component that enables toast notifications in your app.
27
+ * Wrap your root component with `<BreadLoaf>` to start showing toasts.
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * import { BreadLoaf } from 'react-native-bread';
32
+ *
33
+ * // Basic usage
34
+ * <BreadLoaf>
35
+ * <App />
36
+ * </BreadLoaf>
37
+ *
38
+ * // With configuration
39
+ * <BreadLoaf
40
+ * config={{
41
+ * position: 'bottom',
42
+ * stacking: false,
43
+ * defaultDuration: 5000,
44
+ * colors: {
45
+ * success: { accent: '#22c55e', background: '#f0fdf4' },
46
+ * error: { accent: '#ef4444', background: '#fef2f2' },
47
+ * },
48
+ * toastStyle: { borderRadius: 12 },
49
+ * }}
50
+ * >
51
+ * <App />
52
+ * </BreadLoaf>
53
+ * ```
54
+ */
55
+ export function BreadLoaf({ children, config }: BreadLoafProps) {
56
+ useEffect(() => {
57
+ toastStore.setConfig(config);
58
+ return () => {
59
+ // Reset to defaults when this provider unmounts
60
+ toastStore.setConfig(undefined);
61
+ };
62
+ }, [config]);
63
+ return (
64
+ <View style={styles.root}>
65
+ {children}
66
+ <View style={styles.portalContainer} pointerEvents="box-none">
67
+ <ToastContainer />
68
+ </View>
69
+ </View>
70
+ );
71
+ }
72
+
73
+ const styles = StyleSheet.create({
74
+ root: {
75
+ flex: 1,
76
+ },
77
+ portalContainer: {
78
+ ...StyleSheet.absoluteFillObject,
79
+ zIndex: 9999,
80
+ },
81
+ });