react-native-feather-toast
Version:
A lightweight, customizable toast notification library for React Native.
442 lines (432 loc) • 14.7 kB
JavaScript
"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
});