react-native-bread 0.1.3 → 0.2.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.
- package/README.md +4 -3
- package/lib/commonjs/toast.js +1 -1
- package/lib/module/toast.js +1 -1
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# React Native Bread 🍞
|
|
2
2
|
|
|
3
|
-
An extremely lightweight, opinionated toast component for React Native. Inspired by
|
|
3
|
+
An extremely lightweight, opinionated toast component for React Native. Inspired by Sonner, built for mobile with smooth premium-feeling 60fps animations and complex swipe gestures.
|
|
4
4
|
|
|
5
5
|
https://github.com/user-attachments/assets/8a862dba-422c-4573-9f12-0a36cf6efe49
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- **Extremely lightweight** package, only 13.8KB packed size
|
|
9
|
+
- **Extremely lightweight** package, only 13.8KB packed size
|
|
10
10
|
- Clean, imperative API inspired by [Sonner](https://sonner.emilkowal.ski/)
|
|
11
11
|
- Zero setup - add one component, start toasting. No hooks, no providers
|
|
12
|
-
- Built for mobile with smooth 60fps animations
|
|
12
|
+
- Built for mobile with smooth 60fps animations powered by Reanimated
|
|
13
13
|
- Natural swipe gestures that feel native to the platform
|
|
14
14
|
- Multiple toast types: `success`, `error`, `info`, and `promise`
|
|
15
15
|
- Promise handling with automatic loading → success/error states
|
|
16
|
+
- Complex animations and gestures but with high performance
|
|
16
17
|
- Toast stacking with configurable limits
|
|
17
18
|
- Position toasts at top or bottom of screen
|
|
18
19
|
- Completely customizable - colors, icons, styles, animations
|
package/lib/commonjs/toast.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ToastContainer=void 0;var e=require("react"),t=require("react-native"),i=require("react-native-gesture-handler"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ToastContainer=void 0;var e=require("react"),t=require("react-native"),i=require("react-native-gesture-handler"),n=function(e,t){if("function"==typeof WeakMap)var i=new WeakMap,n=new WeakMap;return function(e,t){if(!t&&e&&e.__esModule)return e;var o,s,a={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return a;if(o=t?n:i){if(o.has(e))return o.get(e);o.set(e,a)}for(const t in e)"default"!==t&&{}.hasOwnProperty.call(e,t)&&((s=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,t))&&(s.get||s.set)?o(a,t,s):a[t]=e[t]);return a}(e,t)}(require("react-native-reanimated")),o=require("react-native-safe-area-context"),s=require("react-native-worklets"),a=require("./icons/index.js"),r=require("./toast-store.js"),l=require("react/jsx-runtime");const u=28,c=350,d=n.Easing.bezier(.25,.1,.25,1),p=Array.from({length:5},()=>({progress:(0,n.makeMutable)(0),translationY:(0,n.makeMutable)(0),stackIndex:(0,n.makeMutable)(0)})),f=Array.from({length:5},()=>({wasExiting:!1,prevIndex:0,initialized:!1})),g=new Map,h=new Set,m=(e,i,n,o)=>{if(n)return"function"==typeof n?n({color:i,size:u}):n;if(o)return o({color:i,size:u});switch(e){case"success":default:return(0,l.jsx)(a.GreenCheck,{width:36,height:36,fill:i});case"error":return(0,l.jsx)(a.RedX,{width:u,height:u,fill:i});case"loading":return(0,l.jsx)(t.ActivityIndicator,{size:u,color:i});case"info":return(0,l.jsx)(a.InfoIcon,{width:u,height:u,fill:i})}},x=(0,e.memo)(({type:t,color:i,custom:o,config:s})=>{const a=(0,n.useSharedValue)(0);(0,e.useEffect)(()=>{a.value=(0,n.withTiming)(1,{duration:350,easing:n.Easing.out(n.Easing.back(1.5))})},[a]);const r=(0,n.useAnimatedStyle)(()=>({opacity:a.value,transform:[{scale:.7+.3*a.value}]}));return(0,l.jsx)(n.default.View,{style:r,children:m(t,i,o,s)})});exports.ToastContainer=()=>{const[a,u]=(0,e.useState)([]),[p,f]=(0,e.useState)(()=>r.toastStore.getTheme()),{top:g,bottom:h}=(0,o.useSafeAreaInsets)(),m=(0,e.useRef)((0,n.makeMutable)(null)),x=(0,e.useRef)((0,n.makeMutable)("bottom"===p.position)),v=(0,e.useRef)((0,n.makeMutable)(!0)),b=(0,n.useSharedValue)(!1);(0,e.useEffect)(()=>{u(r.toastStore.getState().visibleToasts);let e=null,t=null;return r.toastStore.subscribe(i=>{e=i.visibleToasts,null===t&&(t=requestAnimationFrame(()=>{e&&(u(e),e=null),t=null;const i=r.toastStore.getTheme();f(e=>e===i?e:i)}))})},[]);const T=(0,e.useMemo)(()=>{const e=new Map;let t=0;for(const i of a)e.set(i.id,i.isExiting?-1:t),i.isExiting||t++;return[...a].reverse().map(t=>({toast:t,index:e.get(t.id)??0}))},[a]),S="bottom"===p.position,j=a.find(e=>!e.isExiting),k=j?.options?.dismissible??p.dismissible;(0,e.useEffect)(()=>{x.current.value=S,v.current.value=k},[S,k]);const E=(0,e.useMemo)(()=>i.Gesture.Pan().onStart(()=>{"worklet";b.value=!1}).onUpdate(e=>{"worklet";if(!v.current.value)return;const t=m.current.value;if(!t)return;const{slot:i}=t,n=x.current.value,o=e.translationY,s=n?o:-o,a=n?-o:o;if(s>0){const t=n?Math.min(o,180):Math.max(o,-180);i.translationY.value=t;const a=s>40||(n?e.velocityY>300:e.velocityY<-300);b.value=a}else{const e=60*(1-Math.exp(-a/250));i.translationY.value=n?-Math.min(e,60):Math.min(e,60),b.value=!1}}).onEnd(()=>{"worklet";if(!v.current.value)return;const e=m.current.value;if(!e)return;const{slot:t}=e,i=x.current.value;if(b.value){t.progress.value=(0,n.withTiming)(0,{duration:c,easing:d});const o=i?200:-200;t.translationY.value=(0,n.withTiming)(t.translationY.value+o,{duration:c,easing:d}),(0,s.scheduleOnRN)(e.dismiss)}else t.translationY.value=(0,n.withTiming)(0,{duration:650,easing:d})}),[b]),M=(0,e.useCallback)(e=>{m.current.value=e},[]);if(0===a.length)return null;const I=S?h:g,C=S?{bottom:I+p.offset+2}:{top:I+p.offset+2};return(0,l.jsx)(i.GestureDetector,{gesture:E,children:(0,l.jsx)(t.View,{style:[w.container,C],pointerEvents:"box-none",children:T.map(({toast:e,index:t})=>(0,l.jsx)(y,{toast:e,index:t,theme:p,position:p.position,isTopToast:0===t,registerTopToast:M},e.id))})})};const v=({toast:i,index:o,theme:s,position:u,isTopToast:v,registerTopToast:y})=>{const b=(0,e.useRef)(null);null===b.current&&(b.current=(e=>{if(g.has(e))return g.get(e)??0;for(let t=0;t<5;t++)if(!h.has(t))return g.set(e,t),h.add(t),f[t].initialized=!1,f[t].wasExiting=!1,f[t].prevIndex=0,t;return 0})(i.id));const T=b.current,S=p[T],j=f[T],k="bottom"===u,E=k?80:-80,M=k?100:-100,I=(0,e.useRef)(i.type),[C,z]=(0,e.useState)(!1);(0,e.useEffect)(()=>()=>{(e=>{const t=g.get(e);void 0!==t&&(h.delete(t),g.delete(e),f[t].initialized=!1,f[t].wasExiting=!1,f[t].prevIndex=0)})(i.id)},[i.id]),(0,e.useEffect)(()=>{S.progress.value=0,S.translationY.value=0,S.stackIndex.value=o,S.progress.value=(0,n.withTiming)(1,{duration:400,easing:d})},[]),(0,e.useEffect)(()=>{i.isExiting&&!j.wasExiting&&(j.wasExiting=!0,S.progress.value=(0,n.withTiming)(0,{duration:c,easing:d}),S.translationY.value=(0,n.withTiming)(M,{duration:c,easing:d}))},[i.isExiting,S,j,M]),(0,e.useEffect)(()=>{j.initialized&&o!==j.prevIndex&&(S.stackIndex.value=(0,n.withTiming)(o,{duration:300,easing:d})),j.prevIndex=o,j.initialized=!0},[o,S,j]),(0,e.useEffect)(()=>{const e=setTimeout(()=>z(!0),50);return()=>clearTimeout(e)},[]);const Y="loading"===I.current&&"loading"!==i.type;I.current=i.type;const q=(0,e.useCallback)(()=>{r.toastStore.hide(i.id)},[i.id]);(0,e.useEffect)(()=>{if(v)return y({slot:S,dismiss:q}),()=>y(null)},[v,y,S,q]);const R=(0,n.useAnimatedStyle)(()=>{const e=(0,n.interpolate)(S.progress.value,[0,1],[E,0]),t=k?10*S.stackIndex.value:-10*S.stackIndex.value,i=1-.05*S.stackIndex.value,o=e+S.translationY.value+t,s=(0,n.interpolate)(S.progress.value,[0,1],[0,1]),a=k?S.translationY.value:-S.translationY.value,r=s*(a>0?(0,n.interpolate)(a,[0,130],[1,0],"clamp"):1);return{transform:[{translateY:o},{scale:i*(0,n.interpolate)(Math.abs(S.translationY.value),[0,50],[1,.98],"clamp")}],opacity:r,zIndex:1e3-Math.round(S.stackIndex.value)}}),{options:O}=i,V=s.colors[i.type],_="loading"!==i.type&&(O?.showCloseButton??s.showCloseButton);return(0,l.jsxs)(n.default.View,{style:[w.toast,k?w.toastBottom:w.toastTop,{backgroundColor:V.background},s.toastStyle,O?.style,R],children:[(0,l.jsx)(t.View,{style:w.iconContainer,children:C&&(Y?(0,l.jsx)(x,{type:i.type,color:V.accent,custom:O?.icon,config:s.icons[i.type]},i.type):m(i.type,V.accent,O?.icon,s.icons[i.type]))}),(0,l.jsxs)(t.View,{style:w.textContainer,children:[(0,l.jsx)(t.Text,{maxFontSizeMultiplier:1.35,allowFontScaling:!1,style:[w.title,{color:V.accent},s.titleStyle,O?.titleStyle],children:i.title}),i.description&&(0,l.jsx)(t.Text,{allowFontScaling:!1,maxFontSizeMultiplier:1.35,style:[w.description,s.descriptionStyle,O?.descriptionStyle],children:i.description})]}),_&&(0,l.jsx)(t.Pressable,{style:w.closeButton,onPress:q,hitSlop:12,children:(0,l.jsx)(a.CloseIcon,{width:20,height:20})})]})},y=(0,e.memo)(v,(e,t)=>e.toast.id===t.toast.id&&e.toast.type===t.toast.type&&e.toast.title===t.toast.title&&e.toast.description===t.toast.description&&e.toast.isExiting===t.toast.isExiting&&e.index===t.index&&e.position===t.position&&e.theme===t.theme&&e.isTopToast===t.isTopToast),w=t.StyleSheet.create({container:{position:"absolute",left:16,right:16,zIndex:1e3},toast:{flexDirection:"row",alignItems:"center",gap:12,minHeight:36,borderRadius:20,borderCurve:"continuous",position:"absolute",left:0,right:0,paddingHorizontal:12,paddingVertical:10,shadowColor:"#000",shadowOffset:{width:0,height:8},shadowOpacity:.05,shadowRadius:24,elevation:8},toastTop:{top:0},toastBottom:{bottom:0},iconContainer:{width:48,height:48,alignItems:"center",justifyContent:"center",marginLeft:8},textContainer:{flex:1,gap:1,justifyContent:"center"},title:{fontSize:14,fontWeight:"700",lineHeight:20},description:{color:"#6B7280",fontSize:12,fontWeight:"500",lineHeight:16},closeButton:{padding:4,alignItems:"center",justifyContent:"center"}});
|
package/lib/module/toast.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{memo as t,useCallback as e,useEffect as
|
|
1
|
+
"use strict";import{memo as t,useCallback as e,useEffect as i,useMemo as n,useRef as o,useState as s}from"react";import{ActivityIndicator as r,Pressable as a,StyleSheet as l,Text as c,View as u}from"react-native";import{Gesture as d,GestureDetector as p}from"react-native-gesture-handler";import g,{Easing as m,interpolate as h,makeMutable as v,useAnimatedStyle as f,useSharedValue as x,withTiming as y}from"react-native-reanimated";import{useSafeAreaInsets as w}from"react-native-safe-area-context";import{scheduleOnRN as b}from"react-native-worklets";import{CloseIcon as T,GreenCheck as I,InfoIcon as z,RedX as Y}from"./icons/index.js";import{toastStore as S}from"./toast-store.js";import{jsx as k,jsxs as E}from"react/jsx-runtime";const C=28,M=350,j=m.bezier(.25,.1,.25,1),B=Array.from({length:5},()=>({progress:v(0),translationY:v(0),stackIndex:v(0)})),F=Array.from({length:5},()=>({wasExiting:!1,prevIndex:0,initialized:!1})),H=new Map,A=new Set,V=(t,e,i,n)=>{if(i)return"function"==typeof i?i({color:e,size:C}):i;if(n)return n({color:e,size:C});switch(t){case"success":default:return k(I,{width:36,height:36,fill:e});case"error":return k(Y,{width:C,height:C,fill:e});case"loading":return k(r,{size:C,color:e});case"info":return k(z,{width:C,height:C,fill:e})}},O=t(({type:t,color:e,custom:n,config:o})=>{const s=x(0);i(()=>{s.value=y(1,{duration:350,easing:m.out(m.back(1.5))})},[s]);const r=f(()=>({opacity:s.value,transform:[{scale:.7+.3*s.value}]}));return k(g.View,{style:r,children:V(t,e,n,o)})});export const ToastContainer=()=>{const[t,r]=s([]),[a,l]=s(()=>S.getTheme()),{top:c,bottom:g}=w(),m=o(v(null)),h=o(v("bottom"===a.position)),f=o(v(!0)),T=x(!1);i(()=>{r(S.getState().visibleToasts);let t=null,e=null;return S.subscribe(i=>{t=i.visibleToasts,null===e&&(e=requestAnimationFrame(()=>{t&&(r(t),t=null),e=null;const i=S.getTheme();l(t=>t===i?t:i)}))})},[]);const I=n(()=>{const e=new Map;let i=0;for(const n of t)e.set(n.id,n.isExiting?-1:i),n.isExiting||i++;return[...t].reverse().map(t=>({toast:t,index:e.get(t.id)??0}))},[t]),z="bottom"===a.position,Y=t.find(t=>!t.isExiting),E=Y?.options?.dismissible??a.dismissible;i(()=>{h.current.value=z,f.current.value=E},[z,E]);const C=n(()=>d.Pan().onStart(()=>{"worklet";T.value=!1}).onUpdate(t=>{"worklet";if(!f.current.value)return;const e=m.current.value;if(!e)return;const{slot:i}=e,n=h.current.value,o=t.translationY,s=n?o:-o,r=n?-o:o;if(s>0){const e=n?Math.min(o,180):Math.max(o,-180);i.translationY.value=e;const r=s>40||(n?t.velocityY>300:t.velocityY<-300);T.value=r}else{const t=60*(1-Math.exp(-r/250));i.translationY.value=n?-Math.min(t,60):Math.min(t,60),T.value=!1}}).onEnd(()=>{"worklet";if(!f.current.value)return;const t=m.current.value;if(!t)return;const{slot:e}=t,i=h.current.value;if(T.value){e.progress.value=y(0,{duration:M,easing:j});const n=i?200:-200;e.translationY.value=y(e.translationY.value+n,{duration:M,easing:j}),b(t.dismiss)}else e.translationY.value=y(0,{duration:650,easing:j})}),[T]),B=e(t=>{m.current.value=t},[]);if(0===t.length)return null;const F=z?g:c,H=z?{bottom:F+a.offset+2}:{top:F+a.offset+2};return k(p,{gesture:C,children:k(u,{style:[R.container,H],pointerEvents:"box-none",children:I.map(({toast:t,index:e})=>k(P,{toast:t,index:e,theme:a,position:a.position,isTopToast:0===e,registerTopToast:B},t.id))})})};const P=t(({toast:t,index:n,theme:r,position:l,isTopToast:d,registerTopToast:p})=>{const m=o(null);null===m.current&&(m.current=(t=>{if(H.has(t))return H.get(t)??0;for(let e=0;e<5;e++)if(!A.has(e))return H.set(t,e),A.add(e),F[e].initialized=!1,F[e].wasExiting=!1,F[e].prevIndex=0,e;return 0})(t.id));const v=m.current,x=B[v],w=F[v],b="bottom"===l,I=b?80:-80,z=b?100:-100,Y=o(t.type),[C,P]=s(!1);i(()=>()=>{(t=>{const e=H.get(t);void 0!==e&&(A.delete(e),H.delete(t),F[e].initialized=!1,F[e].wasExiting=!1,F[e].prevIndex=0)})(t.id)},[t.id]),i(()=>{x.progress.value=0,x.translationY.value=0,x.stackIndex.value=n,x.progress.value=y(1,{duration:400,easing:j})},[]),i(()=>{t.isExiting&&!w.wasExiting&&(w.wasExiting=!0,x.progress.value=y(0,{duration:M,easing:j}),x.translationY.value=y(z,{duration:M,easing:j}))},[t.isExiting,x,w,z]),i(()=>{w.initialized&&n!==w.prevIndex&&(x.stackIndex.value=y(n,{duration:300,easing:j})),w.prevIndex=n,w.initialized=!0},[n,x,w]),i(()=>{const t=setTimeout(()=>P(!0),50);return()=>clearTimeout(t)},[]);const W="loading"===Y.current&&"loading"!==t.type;Y.current=t.type;const q=e(()=>{S.hide(t.id)},[t.id]);i(()=>{if(d)return p({slot:x,dismiss:q}),()=>p(null)},[d,p,x,q]);const D=f(()=>{const t=h(x.progress.value,[0,1],[I,0]),e=b?10*x.stackIndex.value:-10*x.stackIndex.value,i=1-.05*x.stackIndex.value,n=t+x.translationY.value+e,o=h(x.progress.value,[0,1],[0,1]),s=b?x.translationY.value:-x.translationY.value,r=o*(s>0?h(s,[0,130],[1,0],"clamp"):1);return{transform:[{translateY:n},{scale:i*h(Math.abs(x.translationY.value),[0,50],[1,.98],"clamp")}],opacity:r,zIndex:1e3-Math.round(x.stackIndex.value)}}),{options:L}=t,U=r.colors[t.type],G="loading"!==t.type&&(L?.showCloseButton??r.showCloseButton);return E(g.View,{style:[R.toast,b?R.toastBottom:R.toastTop,{backgroundColor:U.background},r.toastStyle,L?.style,D],children:[k(u,{style:R.iconContainer,children:C&&(W?k(O,{type:t.type,color:U.accent,custom:L?.icon,config:r.icons[t.type]},t.type):V(t.type,U.accent,L?.icon,r.icons[t.type]))}),E(u,{style:R.textContainer,children:[k(c,{maxFontSizeMultiplier:1.35,allowFontScaling:!1,style:[R.title,{color:U.accent},r.titleStyle,L?.titleStyle],children:t.title}),t.description&&k(c,{allowFontScaling:!1,maxFontSizeMultiplier:1.35,style:[R.description,r.descriptionStyle,L?.descriptionStyle],children:t.description})]}),G&&k(a,{style:R.closeButton,onPress:q,hitSlop:12,children:k(T,{width:20,height:20})})]})},(t,e)=>t.toast.id===e.toast.id&&t.toast.type===e.toast.type&&t.toast.title===e.toast.title&&t.toast.description===e.toast.description&&t.toast.isExiting===e.toast.isExiting&&t.index===e.index&&t.position===e.position&&t.theme===e.theme&&t.isTopToast===e.isTopToast),R=l.create({container:{position:"absolute",left:16,right:16,zIndex:1e3},toast:{flexDirection:"row",alignItems:"center",gap:12,minHeight:36,borderRadius:20,borderCurve:"continuous",position:"absolute",left:0,right:0,paddingHorizontal:12,paddingVertical:10,shadowColor:"#000",shadowOffset:{width:0,height:8},shadowOpacity:.05,shadowRadius:24,elevation:8},toastTop:{top:0},toastBottom:{bottom:0},iconContainer:{width:48,height:48,alignItems:"center",justifyContent:"center",marginLeft:8},textContainer:{flex:1,gap:1,justifyContent:"center"},title:{fontSize:14,fontWeight:"700",lineHeight:20},description:{color:"#6B7280",fontSize:12,fontWeight:"500",lineHeight:16},closeButton:{padding:4,alignItems:"center",justifyContent:"center"}});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-bread",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A lightweight sonner-inspired toast library for React Native with premium-feeling animations and complex gesture support",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
7
7
|
"types": "lib/typescript/index.d.ts",
|
|
@@ -18,7 +18,18 @@
|
|
|
18
18
|
"url": "https://github.com/AlshehriAli0/react-native-bread/issues"
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://github.com/AlshehriAli0/react-native-bread#readme",
|
|
21
|
-
"keywords": [
|
|
21
|
+
"keywords": [
|
|
22
|
+
"react-native",
|
|
23
|
+
"toast",
|
|
24
|
+
"react-native-toast",
|
|
25
|
+
"react-hot-toast",
|
|
26
|
+
"notification",
|
|
27
|
+
"snackbar",
|
|
28
|
+
"bread",
|
|
29
|
+
"animation",
|
|
30
|
+
"gesture",
|
|
31
|
+
"reanimated"
|
|
32
|
+
],
|
|
22
33
|
"scripts": {
|
|
23
34
|
"build": "bob build",
|
|
24
35
|
"minify": "find lib -name '*.js' -not -path '*/node_modules/*' -exec terser {} -o {} -c directives=false -m --module \\;",
|