UNPKG

react-native-feather-toast

Version:

A lightweight, customizable toast notification library for React Native.

442 lines (432 loc) 14.7 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { Toast: () => Toast, ToastRoot: () => ToastRoot, showToast: () => showToast }); module.exports = __toCommonJS(index_exports); // src/Toast.tsx var import_react2 = __toESM(require("react")); // src/styles.ts var import_react_native = require("react-native"); var styles = import_react_native.StyleSheet.create({ rootWrapper: { ...import_react_native.StyleSheet.absoluteFillObject, elevation: 999999, zIndex: 999999 }, toastWrapper: { ...import_react_native.StyleSheet.absoluteFillObject, backgroundColor: "transparent" }, toastContainer: { position: "absolute", left: 16, right: 16, paddingVertical: 15, paddingHorizontal: 10, borderRadius: 12, minHeight: 46, marginTop: import_react_native.Platform.OS === "ios" ? 50 : import_react_native.StatusBar.currentHeight, ...import_react_native.Platform.select({ ios: { shadowColor: "#000", shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.15, shadowRadius: 12 }, android: { elevation: 8 } }) }, toastContent: { flexDirection: "row", alignItems: "center" }, iconContainer: { marginRight: 12 }, textContainer: { flex: 1 }, toastText: { color: "#fff", fontSize: 14, fontWeight: "600", letterSpacing: 0.2 }, descriptionText: { color: "rgba(255, 255, 255, 0.9)", fontSize: 12, marginTop: 4, lineHeight: 20 }, dismissButton: { padding: 3, marginLeft: 8, borderRadius: 20, backgroundColor: "rgba(255, 255, 255, 0.1)" } }); // src/toastConfig.ts var getToastConfig = (type = "info") => { switch (type) { case "success": return { backgroundColor: "#10B981", icon: "check-circle", iconColor: "#ffffff" }; case "error": return { backgroundColor: "#EF4444", icon: "x-circle", iconColor: "#ffffff" }; case "warning": return { backgroundColor: "#F59E0B", icon: "alert-circle", iconColor: "#ffffff" }; default: return { backgroundColor: "#3B82F6", icon: "info", iconColor: "#ffffff" }; } }; // src/icons.tsx var import_react = __toESM(require("react")); var import_react_native_svg = require("react-native-svg"); var defaultProps = { size: 18, color: "#FFFFFF" }; var Icons = { info: ({ size = defaultProps.size, color = defaultProps.color }) => /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.Svg, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.Circle, { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "2" }), /* @__PURE__ */ import_react.default.createElement( import_react_native_svg.Path, { d: "M12 8h.01", stroke: color, strokeWidth: "2", strokeLinecap: "round" } ), /* @__PURE__ */ import_react.default.createElement( import_react_native_svg.Path, { d: "M11 12h1v4h1", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" } )), success: ({ size = defaultProps.size, color = defaultProps.color }) => /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.Svg, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.Circle, { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "2" }), /* @__PURE__ */ import_react.default.createElement( import_react_native_svg.Path, { d: "M8 12l3 3 5-5", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" } )), error: ({ size = defaultProps.size, color = defaultProps.color }) => /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.Svg, { width: size, height: size, viewBox: "0 0 15 15", fill: "none" }, /* @__PURE__ */ import_react.default.createElement( import_react_native_svg.Path, { fillRule: "evenodd", clipRule: "evenodd", d: "M8.445.609a1.1 1.1 0 00-1.89 0L.161 11.337A1.1 1.1 0 001.106 13h12.788a1.1 1.1 0 00.945-1.663L8.445.609zm-1.03.512a.1.1 0 01.17 0l6.395 10.728a.1.1 0 01-.086.151H1.106a.1.1 0 01-.086-.151L7.414 1.12zm-.588 3.365a.674.674 0 111.346 0L8.02 8.487a.52.52 0 01-1.038 0l-.154-4zm1.423 5.99a.75.75 0 11-1.5 0 .75.75 0 011.5 0z", fill: color } )), warning: ({ size = defaultProps.size, color = defaultProps.color }) => /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.Svg, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.Circle, { cx: "12", cy: "12", r: "10", stroke: color, strokeWidth: "2" }), /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.Path, { d: "M12 8v4", stroke: color, strokeWidth: "2", strokeLinecap: "round" }), /* @__PURE__ */ import_react.default.createElement( import_react_native_svg.Path, { d: "M12 16h.01", stroke: color, strokeWidth: "2", strokeLinecap: "round" } )), close: ({ size = defaultProps.size, color = defaultProps.color }) => /* @__PURE__ */ import_react.default.createElement(import_react_native_svg.Svg, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ import_react.default.createElement( import_react_native_svg.Path, { d: "M18 6L6 18", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" } ), /* @__PURE__ */ import_react.default.createElement( import_react_native_svg.Path, { d: "M6 6L18 18", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" } )) }; // src/Toast.tsx var import_react_native2 = require("react-native"); var Toast = ({ title, description, type, fadeAnim, slideAnim, onDismiss }) => { const config = getToastConfig(type); const Icon = Icons[type] || Icons.info; return /* @__PURE__ */ import_react2.default.createElement( import_react_native2.Animated.View, { style: [ styles.toastContainer, { opacity: fadeAnim, transform: [{ translateY: slideAnim }], backgroundColor: config.backgroundColor } ] }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.toastContent }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.iconContainer }, /* @__PURE__ */ import_react2.default.createElement(Icon, { color: config.iconColor, size: 18 })), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.textContainer }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.toastText }, title), description && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.descriptionText }, description)), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: onDismiss, style: styles.dismissButton }, /* @__PURE__ */ import_react2.default.createElement(Icons.close, { color: config.iconColor, size: 14 }))) ); }; // src/ToastRoot.tsx var import_react4 = __toESM(require("react")); var import_react_native4 = require("react-native"); // src/useToast.tsx var import_react3 = require("react"); var import_react_native3 = require("react-native"); var { height, width } = import_react_native3.Dimensions.get("window"); var useToast = () => { const fadeAnim = (0, import_react3.useRef)(new import_react_native3.Animated.Value(0)).current; const slideAnim = (0, import_react3.useRef)(new import_react_native3.Animated.Value(-100)).current; const timeoutRef = (0, import_react3.useRef)(); const [toastTitle, setToastTitle] = (0, import_react3.useState)(""); const [toastDescription, setToastDescription] = (0, import_react3.useState)(""); const [toastType, setToastType] = (0, import_react3.useState)("info"); const [isVisible, setIsVisible] = (0, import_react3.useState)(false); const handleDismiss = (0, import_react3.useCallback)(() => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } import_react_native3.Animated.parallel([ import_react_native3.Animated.timing(fadeAnim, { toValue: 0, duration: 200, useNativeDriver: true }), import_react_native3.Animated.timing(slideAnim, { toValue: -100, duration: 200, useNativeDriver: true }) ]).start(() => { setToastTitle(""); setToastDescription(""); setIsVisible(false); }); }, [fadeAnim, slideAnim]); const getToastPosition = (0, import_react3.useCallback)((position) => { if (position === "top") { return import_react_native3.Platform.OS === "ios" ? 50 : 40; } const statusBarHeight = import_react_native3.Platform.OS === "android" ? import_react_native3.StatusBar.currentHeight || 0 : 0; const homeIndicatorHeight = import_react_native3.Platform.OS === "ios" && height >= 812 ? 34 : 0; const safePadding = 16; return height - (statusBarHeight + homeIndicatorHeight + safePadding + 100); }, []); const handleShowToast = (0, import_react3.useCallback)( ({ title, description, type = "info", duration = 3e3, position = "top" }) => { setToastTitle(title); setToastDescription(description || ""); setToastType(type); setIsVisible(true); if (timeoutRef.current) { clearTimeout(timeoutRef.current); } fadeAnim.setValue(0); slideAnim.setValue(position === "top" ? -100 : height); const targetPosition = getToastPosition(position); import_react_native3.Animated.parallel([ import_react_native3.Animated.timing(fadeAnim, { toValue: 1, duration: 300, useNativeDriver: true }), import_react_native3.Animated.spring(slideAnim, { toValue: targetPosition, speed: 12, bounciness: 8, useNativeDriver: true }) ]).start(); timeoutRef.current = setTimeout(() => { import_react_native3.Animated.parallel([ import_react_native3.Animated.timing(fadeAnim, { toValue: 0, duration: 300, useNativeDriver: true }), import_react_native3.Animated.timing(slideAnim, { toValue: position === "top" ? -100 : height, duration: 300, useNativeDriver: true }) ]).start(() => { setToastTitle(""); setToastDescription(""); setIsVisible(false); }); }, duration); }, [getToastPosition] ); (0, import_react3.useEffect)(() => { return () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } }; }, []); return { toastTitle, toastDescription, toastType, isVisible, fadeAnim, slideAnim, handleShowToast, handleDismiss }; }; // src/ToastRoot.tsx var ToastEmitter = class _ToastEmitter { static instance; listeners = []; constructor() { } static getInstance() { if (!_ToastEmitter.instance) { _ToastEmitter.instance = new _ToastEmitter(); } return _ToastEmitter.instance; } addListener(callback) { this.listeners.push(callback); return () => { this.listeners = this.listeners.filter( (listener) => listener !== callback ); }; } emit(config) { this.listeners.forEach((listener) => listener(config)); } }; function showToast(config) { ToastEmitter.getInstance().emit(config); } var ToastRoot = () => { const { toastTitle, toastDescription, toastType, isVisible, fadeAnim, slideAnim, handleShowToast, handleDismiss } = useToast(); (0, import_react4.useEffect)(() => { const unsubscribe = ToastEmitter.getInstance().addListener(handleShowToast); return () => unsubscribe(); }, [handleShowToast]); if (!isVisible) return null; return /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: [styles.rootWrapper], pointerEvents: "box-none" }, /* @__PURE__ */ import_react4.default.createElement( import_react_native4.KeyboardAvoidingView, { style: styles.toastWrapper, pointerEvents: "box-none", behavior: import_react_native4.Platform.OS === "ios" ? "padding" : void 0 }, /* @__PURE__ */ import_react4.default.createElement( Toast, { title: toastTitle, description: toastDescription, type: toastType, fadeAnim, slideAnim, onDismiss: handleDismiss } ) )); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Toast, ToastRoot, showToast });