added backup and email client
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Timo Lins
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<a href="https://react-hot-toast.com/"><img alt="react-hot-toast - Try it out" src="https://github.com/timolins/react-hot-toast/raw/main/assets/header.svg"/></a>
|
||||
|
||||
<div align="center">
|
||||
<img src="https://badgen.net/npm/v/react-hot-toast" alt="NPM Version" />
|
||||
<img src="https://badgen.net/bundlephobia/minzip/react-hot-toast" alt="minzipped size"/>
|
||||
<img src="https://github.com/timolins/react-hot-toast/workflows/CI/badge.svg" alt="Build Status" />
|
||||
</a>
|
||||
</div>
|
||||
<br />
|
||||
<div align="center"><strong>Smoking hot Notifications for React.</strong></div>
|
||||
<div align="center"> Lightweight, customizable and beautiful by default.</div>
|
||||
<br />
|
||||
<div align="center">
|
||||
<a href="https://react-hot-toast.com/">Website</a>
|
||||
<span> · </span>
|
||||
<a href="https://react-hot-toast.com/docs">Documentation</a>
|
||||
<span> · </span>
|
||||
<a href="https://twitter.com/timolins">Twitter</a>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<div align="center">
|
||||
<sub>Cooked by <a href="https://twitter.com/timolins">Timo Lins</a> 👨🍳</sub>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
## Features
|
||||
|
||||
- 🔥 **Hot by default**
|
||||
- 🔩 **Easily Customizable**
|
||||
- ⏳ **Promise API** - _Automatic loader from a promise_
|
||||
- 🕊 **Lightweight** - _less than 5kb including styles_
|
||||
- ✅ **Accessible**
|
||||
- 🤯 **Headless Hooks** - _Create your own with [`useToaster()`](https://react-hot-toast.com/docs/use-toaster)_
|
||||
|
||||
## Installation
|
||||
|
||||
#### With pnpm
|
||||
|
||||
```sh
|
||||
pnpm add react-hot-toast
|
||||
```
|
||||
|
||||
#### With NPM
|
||||
|
||||
```sh
|
||||
npm install react-hot-toast
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
Add the Toaster to your app first. It will take care of rendering all notifications emitted. Now you can trigger `toast()` from anywhere!
|
||||
|
||||
```jsx
|
||||
import toast, { Toaster } from 'react-hot-toast';
|
||||
|
||||
const notify = () => toast('Here is your toast.');
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={notify}>Make me a toast</button>
|
||||
<Toaster />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Find the full API reference on [official documentation](https://react-hot-toast.com/docs).
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
import * as react from 'react';
|
||||
import { CSSProperties } from 'react';
|
||||
import * as goober from 'goober';
|
||||
|
||||
type ToastType = 'success' | 'error' | 'loading' | 'blank' | 'custom';
|
||||
type ToastPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
||||
type Renderable = React.ReactElement | string | null;
|
||||
interface IconTheme {
|
||||
primary: string;
|
||||
secondary: string;
|
||||
}
|
||||
type ValueFunction<TValue, TArg> = (arg: TArg) => TValue;
|
||||
type ValueOrFunction<TValue, TArg> = TValue | ValueFunction<TValue, TArg>;
|
||||
declare const resolveValue: <TValue, TArg>(valOrFunction: ValueOrFunction<TValue, TArg>, arg: TArg) => TValue;
|
||||
interface Toast {
|
||||
type: ToastType;
|
||||
id: string;
|
||||
toasterId?: string;
|
||||
message: ValueOrFunction<Renderable, Toast>;
|
||||
icon?: Renderable;
|
||||
duration?: number;
|
||||
pauseDuration: number;
|
||||
position?: ToastPosition;
|
||||
removeDelay?: number;
|
||||
ariaProps: {
|
||||
role: 'status' | 'alert';
|
||||
'aria-live': 'assertive' | 'off' | 'polite';
|
||||
};
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
iconTheme?: IconTheme;
|
||||
createdAt: number;
|
||||
visible: boolean;
|
||||
dismissed: boolean;
|
||||
height?: number;
|
||||
}
|
||||
type ToastOptions = Partial<Pick<Toast, 'id' | 'icon' | 'duration' | 'ariaProps' | 'className' | 'style' | 'position' | 'iconTheme' | 'toasterId' | 'removeDelay'>>;
|
||||
type DefaultToastOptions = ToastOptions & {
|
||||
[key in ToastType]?: ToastOptions;
|
||||
};
|
||||
interface ToasterProps {
|
||||
position?: ToastPosition;
|
||||
toastOptions?: DefaultToastOptions;
|
||||
reverseOrder?: boolean;
|
||||
gutter?: number;
|
||||
containerStyle?: React.CSSProperties;
|
||||
containerClassName?: string;
|
||||
toasterId?: string;
|
||||
children?: (toast: Toast) => React.ReactElement;
|
||||
}
|
||||
|
||||
type Message = ValueOrFunction<Renderable, Toast>;
|
||||
type ToastHandler = (message: Message, options?: ToastOptions) => string;
|
||||
declare const toast: {
|
||||
(message: Message, opts?: ToastOptions): string;
|
||||
error: ToastHandler;
|
||||
success: ToastHandler;
|
||||
loading: ToastHandler;
|
||||
custom: ToastHandler;
|
||||
dismiss(toastId?: string, toasterId?: string): void;
|
||||
dismissAll(toasterId?: string): void;
|
||||
remove(toastId?: string, toasterId?: string): void;
|
||||
removeAll(toasterId?: string): void;
|
||||
promise<T>(promise: Promise<T> | (() => Promise<T>), msgs: {
|
||||
loading: Renderable;
|
||||
success?: ValueOrFunction<Renderable, T>;
|
||||
error?: ValueOrFunction<Renderable, any>;
|
||||
}, opts?: DefaultToastOptions): Promise<T>;
|
||||
};
|
||||
|
||||
declare const useToaster: (toastOptions?: DefaultToastOptions, toasterId?: string) => {
|
||||
toasts: Toast[];
|
||||
handlers: {
|
||||
updateHeight: (toastId: string, height: number) => void;
|
||||
startPause: () => void;
|
||||
endPause: () => void;
|
||||
calculateOffset: (toast: Toast, opts?: {
|
||||
reverseOrder?: boolean;
|
||||
gutter?: number;
|
||||
defaultPosition?: ToastPosition;
|
||||
}) => number;
|
||||
};
|
||||
};
|
||||
|
||||
interface ToasterSettings {
|
||||
toastLimit: number;
|
||||
}
|
||||
interface ToasterState {
|
||||
toasts: Toast[];
|
||||
settings: ToasterSettings;
|
||||
pausedAt: number | undefined;
|
||||
}
|
||||
declare const useStore: (toastOptions?: DefaultToastOptions, toasterId?: string) => ToasterState;
|
||||
|
||||
interface ToastBarProps {
|
||||
toast: Toast;
|
||||
position?: ToastPosition;
|
||||
style?: react.CSSProperties;
|
||||
children?: (components: {
|
||||
icon: Renderable;
|
||||
message: Renderable;
|
||||
}) => Renderable;
|
||||
}
|
||||
declare const ToastBar: react.FC<ToastBarProps>;
|
||||
|
||||
interface ErrorTheme {
|
||||
primary?: string;
|
||||
secondary?: string;
|
||||
}
|
||||
declare const ErrorIcon: goober.StyledVNode<Omit<react.ClassAttributes<HTMLDivElement> & react.HTMLAttributes<HTMLDivElement> & goober.DefaultTheme & ErrorTheme, never>>;
|
||||
|
||||
interface LoaderTheme {
|
||||
primary?: string;
|
||||
secondary?: string;
|
||||
}
|
||||
declare const LoaderIcon: goober.StyledVNode<Omit<react.ClassAttributes<HTMLDivElement> & react.HTMLAttributes<HTMLDivElement> & goober.DefaultTheme & LoaderTheme, never>>;
|
||||
|
||||
interface CheckmarkTheme {
|
||||
primary?: string;
|
||||
secondary?: string;
|
||||
}
|
||||
declare const CheckmarkIcon: goober.StyledVNode<Omit<react.ClassAttributes<HTMLDivElement> & react.HTMLAttributes<HTMLDivElement> & goober.DefaultTheme & CheckmarkTheme, never>>;
|
||||
|
||||
declare const ToastIcon: react.FC<{
|
||||
toast: Toast;
|
||||
}>;
|
||||
|
||||
declare const Toaster: react.FC<ToasterProps>;
|
||||
|
||||
export { CheckmarkIcon, DefaultToastOptions, ErrorIcon, IconTheme, LoaderIcon, Renderable, Toast, ToastBar, ToastIcon, ToastOptions, ToastPosition, ToastType, Toaster, ToasterProps, ValueFunction, ValueOrFunction, toast as default, resolveValue, toast, useToaster, useStore as useToasterStore };
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
"use client";
|
||||
"use strict";var se=Object.create;var k=Object.defineProperty;var ae=Object.getOwnPropertyDescriptor;var ie=Object.getOwnPropertyNames;var ne=Object.getPrototypeOf,ce=Object.prototype.hasOwnProperty;var pe=(e,t)=>{for(var o in t)k(e,o,{get:t[o],enumerable:!0})},G=(e,t,o,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of ie(t))!ce.call(e,a)&&a!==o&&k(e,a,{get:()=>t[a],enumerable:!(s=ae(t,a))||s.enumerable});return e};var j=(e,t,o)=>(o=e!=null?se(ne(e)):{},G(t||!e||!e.__esModule?k(o,"default",{value:e,enumerable:!0}):o,e)),de=e=>G(k({},"__esModule",{value:!0}),e);var Ue={};pe(Ue,{CheckmarkIcon:()=>U,ErrorIcon:()=>C,LoaderIcon:()=>L,ToastBar:()=>N,ToastIcon:()=>$,Toaster:()=>oe,default:()=>Le,resolveValue:()=>g,toast:()=>i,useToaster:()=>M,useToasterStore:()=>w});module.exports=de(Ue);var me=e=>typeof e=="function",g=(e,t)=>me(e)?e(t):e;var J=(()=>{let e=0;return()=>(++e).toString()})(),_=(()=>{let e;return()=>{if(e===void 0&&typeof window<"u"){let t=matchMedia("(prefers-reduced-motion: reduce)");e=!t||t.matches}return e}})();var A=require("react");var le=20,Y="default";var K=(e,t)=>{let{toastLimit:o}=e.settings;switch(t.type){case 0:return{...e,toasts:[t.toast,...e.toasts].slice(0,o)};case 1:return{...e,toasts:e.toasts.map(r=>r.id===t.toast.id?{...r,...t.toast}:r)};case 2:let{toast:s}=t;return K(e,{type:e.toasts.find(r=>r.id===s.id)?1:0,toast:s});case 3:let{toastId:a}=t;return{...e,toasts:e.toasts.map(r=>r.id===a||a===void 0?{...r,dismissed:!0,visible:!1}:r)};case 4:return t.toastId===void 0?{...e,toasts:[]}:{...e,toasts:e.toasts.filter(r=>r.id!==t.toastId)};case 5:return{...e,pausedAt:t.time};case 6:let n=t.time-(e.pausedAt||0);return{...e,pausedAt:void 0,toasts:e.toasts.map(r=>({...r,pauseDuration:r.pauseDuration+n}))}}},V=[],Z={toasts:[],pausedAt:void 0,settings:{toastLimit:le}},T={},ee=(e,t=Y)=>{T[t]=K(T[t]||Z,e),V.forEach(([o,s])=>{o===t&&s(T[t])})},Q=e=>Object.keys(T).forEach(t=>ee(e,t)),te=e=>Object.keys(T).find(t=>T[t].toasts.some(o=>o.id===e)),P=(e=Y)=>t=>{ee(t,e)},ue={blank:4e3,error:4e3,success:2e3,loading:1/0,custom:4e3},w=(e={},t=Y)=>{let[o,s]=(0,A.useState)(T[t]||Z),a=(0,A.useRef)(T[t]);(0,A.useEffect)(()=>(a.current!==T[t]&&s(T[t]),V.push([t,s]),()=>{let r=V.findIndex(([u])=>u===t);r>-1&&V.splice(r,1)}),[t]);let n=o.toasts.map(r=>{var u,x,y;return{...e,...e[r.type],...r,removeDelay:r.removeDelay||((u=e[r.type])==null?void 0:u.removeDelay)||(e==null?void 0:e.removeDelay),duration:r.duration||((x=e[r.type])==null?void 0:x.duration)||(e==null?void 0:e.duration)||ue[r.type],style:{...e.style,...(y=e[r.type])==null?void 0:y.style,...r.style}}});return{...o,toasts:n}};var Te=(e,t="blank",o)=>({createdAt:Date.now(),visible:!0,dismissed:!1,type:t,ariaProps:{role:"status","aria-live":"polite"},message:e,pauseDuration:0,...o,id:(o==null?void 0:o.id)||J()}),D=e=>(t,o)=>{let s=Te(t,e,o);return P(s.toasterId||te(s.id))({type:2,toast:s}),s.id},i=(e,t)=>D("blank")(e,t);i.error=D("error");i.success=D("success");i.loading=D("loading");i.custom=D("custom");i.dismiss=(e,t)=>{let o={type:3,toastId:e};t?P(t)(o):Q(o)};i.dismissAll=e=>i.dismiss(void 0,e);i.remove=(e,t)=>{let o={type:4,toastId:e};t?P(t)(o):Q(o)};i.removeAll=e=>i.remove(void 0,e);i.promise=(e,t,o)=>{let s=i.loading(t.loading,{...o,...o==null?void 0:o.loading});return typeof e=="function"&&(e=e()),e.then(a=>{let n=t.success?g(t.success,a):void 0;return n?i.success(n,{id:s,...o,...o==null?void 0:o.success}):i.dismiss(s),a}).catch(a=>{let n=t.error?g(t.error,a):void 0;n?i.error(n,{id:s,...o,...o==null?void 0:o.error}):i.dismiss(s)}),e};var l=require("react");var ye=1e3,M=(e,t="default")=>{let{toasts:o,pausedAt:s}=w(e,t),a=(0,l.useRef)(new Map).current,n=(0,l.useCallback)((c,m=ye)=>{if(a.has(c))return;let p=setTimeout(()=>{a.delete(c),r({type:4,toastId:c})},m);a.set(c,p)},[]);(0,l.useEffect)(()=>{if(s)return;let c=Date.now(),m=o.map(p=>{if(p.duration===1/0)return;let I=(p.duration||0)+p.pauseDuration-(c-p.createdAt);if(I<0){p.visible&&i.dismiss(p.id);return}return setTimeout(()=>i.dismiss(p.id,t),I)});return()=>{m.forEach(p=>p&&clearTimeout(p))}},[o,s,t]);let r=(0,l.useCallback)(P(t),[t]),u=(0,l.useCallback)(()=>{r({type:5,time:Date.now()})},[r]),x=(0,l.useCallback)((c,m)=>{r({type:1,toast:{id:c,height:m}})},[r]),y=(0,l.useCallback)(()=>{s&&r({type:6,time:Date.now()})},[s,r]),d=(0,l.useCallback)((c,m)=>{let{reverseOrder:p=!1,gutter:I=8,defaultPosition:X}=m||{},W=o.filter(f=>(f.position||X)===(c.position||X)&&f.height),re=W.findIndex(f=>f.id===c.id),q=W.filter((f,H)=>H<re&&f.visible).length;return W.filter(f=>f.visible).slice(...p?[q+1]:[0,q]).reduce((f,H)=>f+(H.height||0)+I,0)},[o]);return(0,l.useEffect)(()=>{o.forEach(c=>{if(c.dismissed)n(c.id,c.removeDelay);else{let m=a.get(c.id);m&&(clearTimeout(m),a.delete(c.id))}})},[o,n]),{toasts:o,handlers:{updateHeight:x,startPause:u,endPause:y,calculateOffset:d}}};var h=j(require("react")),v=require("goober");var S=j(require("react")),E=require("goober");var R=require("goober"),ge=R.keyframes`
|
||||
from {
|
||||
transform: scale(0) rotate(45deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1) rotate(45deg);
|
||||
opacity: 1;
|
||||
}`,he=R.keyframes`
|
||||
from {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}`,xe=R.keyframes`
|
||||
from {
|
||||
transform: scale(0) rotate(90deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1) rotate(90deg);
|
||||
opacity: 1;
|
||||
}`,C=(0,R.styled)("div")`
|
||||
width: 20px;
|
||||
opacity: 0;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
background: ${e=>e.primary||"#ff4b4b"};
|
||||
position: relative;
|
||||
transform: rotate(45deg);
|
||||
|
||||
animation: ${ge} 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
||||
forwards;
|
||||
animation-delay: 100ms;
|
||||
|
||||
&:after,
|
||||
&:before {
|
||||
content: '';
|
||||
animation: ${he} 0.15s ease-out forwards;
|
||||
animation-delay: 150ms;
|
||||
position: absolute;
|
||||
border-radius: 3px;
|
||||
opacity: 0;
|
||||
background: ${e=>e.secondary||"#fff"};
|
||||
bottom: 9px;
|
||||
left: 4px;
|
||||
height: 2px;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
animation: ${xe} 0.15s ease-out forwards;
|
||||
animation-delay: 180ms;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
`;var F=require("goober"),be=F.keyframes`
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
`,L=(0,F.styled)("div")`
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid;
|
||||
border-radius: 100%;
|
||||
border-color: ${e=>e.secondary||"#e0e0e0"};
|
||||
border-right-color: ${e=>e.primary||"#616161"};
|
||||
animation: ${be} 1s linear infinite;
|
||||
`;var O=require("goober"),Se=O.keyframes`
|
||||
from {
|
||||
transform: scale(0) rotate(45deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1) rotate(45deg);
|
||||
opacity: 1;
|
||||
}`,Ae=O.keyframes`
|
||||
0% {
|
||||
height: 0;
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
40% {
|
||||
height: 0;
|
||||
width: 6px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
height: 10px;
|
||||
}`,U=(0,O.styled)("div")`
|
||||
width: 20px;
|
||||
opacity: 0;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
background: ${e=>e.primary||"#61d345"};
|
||||
position: relative;
|
||||
transform: rotate(45deg);
|
||||
|
||||
animation: ${Se} 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
||||
forwards;
|
||||
animation-delay: 100ms;
|
||||
&:after {
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
animation: ${Ae} 0.2s ease-out forwards;
|
||||
opacity: 0;
|
||||
animation-delay: 200ms;
|
||||
position: absolute;
|
||||
border-right: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-color: ${e=>e.secondary||"#fff"};
|
||||
bottom: 6px;
|
||||
left: 6px;
|
||||
height: 10px;
|
||||
width: 6px;
|
||||
}
|
||||
`;var Pe=(0,E.styled)("div")`
|
||||
position: absolute;
|
||||
`,Re=(0,E.styled)("div")`
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
`,Ee=E.keyframes`
|
||||
from {
|
||||
transform: scale(0.6);
|
||||
opacity: 0.4;
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}`,ve=(0,E.styled)("div")`
|
||||
position: relative;
|
||||
transform: scale(0.6);
|
||||
opacity: 0.4;
|
||||
min-width: 20px;
|
||||
animation: ${Ee} 0.3s 0.12s cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
||||
forwards;
|
||||
`,$=({toast:e})=>{let{icon:t,type:o,iconTheme:s}=e;return t!==void 0?typeof t=="string"?S.createElement(ve,null,t):t:o==="blank"?null:S.createElement(Re,null,S.createElement(L,{...s}),o!=="loading"&&S.createElement(Pe,null,o==="error"?S.createElement(C,{...s}):S.createElement(U,{...s})))};var De=e=>`
|
||||
0% {transform: translate3d(0,${e*-200}%,0) scale(.6); opacity:.5;}
|
||||
100% {transform: translate3d(0,0,0) scale(1); opacity:1;}
|
||||
`,Oe=e=>`
|
||||
0% {transform: translate3d(0,0,-1px) scale(1); opacity:1;}
|
||||
100% {transform: translate3d(0,${e*-150}%,-1px) scale(.6); opacity:0;}
|
||||
`,Ie="0%{opacity:0;} 100%{opacity:1;}",ke="0%{opacity:1;} 100%{opacity:0;}",_e=(0,v.styled)("div")`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
color: #363636;
|
||||
line-height: 1.3;
|
||||
will-change: transform;
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1), 0 3px 3px rgba(0, 0, 0, 0.05);
|
||||
max-width: 350px;
|
||||
pointer-events: auto;
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
`,Ve=(0,v.styled)("div")`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 4px 10px;
|
||||
color: inherit;
|
||||
flex: 1 1 auto;
|
||||
white-space: pre-line;
|
||||
`,we=(e,t)=>{let s=e.includes("top")?1:-1,[a,n]=_()?[Ie,ke]:[De(s),Oe(s)];return{animation:t?`${(0,v.keyframes)(a)} 0.35s cubic-bezier(.21,1.02,.73,1) forwards`:`${(0,v.keyframes)(n)} 0.4s forwards cubic-bezier(.06,.71,.55,1)`}},N=h.memo(({toast:e,position:t,style:o,children:s})=>{let a=e.height?we(e.position||t||"top-center",e.visible):{opacity:0},n=h.createElement($,{toast:e}),r=h.createElement(Ve,{...e.ariaProps},g(e.message,e));return h.createElement(_e,{className:e.className,style:{...a,...o,...e.style}},typeof s=="function"?s({icon:n,message:r}):h.createElement(h.Fragment,null,n,r))});var B=require("goober"),b=j(require("react"));(0,B.setup)(b.createElement);var Me=({id:e,className:t,style:o,onHeightUpdate:s,children:a})=>{let n=b.useCallback(r=>{if(r){let u=()=>{let x=r.getBoundingClientRect().height;s(e,x)};u(),new MutationObserver(u).observe(r,{subtree:!0,childList:!0,characterData:!0})}},[e,s]);return b.createElement("div",{ref:n,className:t,style:o},a)},Ce=(e,t)=>{let o=e.includes("top"),s=o?{top:0}:{bottom:0},a=e.includes("center")?{justifyContent:"center"}:e.includes("right")?{justifyContent:"flex-end"}:{};return{left:0,right:0,display:"flex",position:"absolute",transition:_()?void 0:"all 230ms cubic-bezier(.21,1.02,.73,1)",transform:`translateY(${t*(o?1:-1)}px)`,...s,...a}},Fe=B.css`
|
||||
z-index: 9999;
|
||||
> * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
`,z=16,oe=({reverseOrder:e,position:t="top-center",toastOptions:o,gutter:s,children:a,toasterId:n,containerStyle:r,containerClassName:u})=>{let{toasts:x,handlers:y}=M(o,n);return b.createElement("div",{"data-rht-toaster":n||"",style:{position:"fixed",zIndex:9999,top:z,left:z,right:z,bottom:z,pointerEvents:"none",...r},className:u,onMouseEnter:y.startPause,onMouseLeave:y.endPause},x.map(d=>{let c=d.position||t,m=y.calculateOffset(d,{reverseOrder:e,gutter:s,defaultPosition:t}),p=Ce(c,m);return b.createElement(Me,{id:d.id,key:d.id,onHeightUpdate:y.updateHeight,className:d.visible?Fe:"",style:p},d.type==="custom"?g(d.message,d):a?a(d):b.createElement(N,{toast:d,position:c}))}))};var Le=i;0&&(module.exports={CheckmarkIcon,ErrorIcon,LoaderIcon,ToastBar,ToastIcon,Toaster,resolveValue,toast,useToaster,useToasterStore});
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+179
@@ -0,0 +1,179 @@
|
||||
"use client";
|
||||
var Z=e=>typeof e=="function",h=(e,t)=>Z(e)?e(t):e;var W=(()=>{let e=0;return()=>(++e).toString()})(),E=(()=>{let e;return()=>{if(e===void 0&&typeof window<"u"){let t=matchMedia("(prefers-reduced-motion: reduce)");e=!t||t.matches}return e}})();import{useEffect as ee,useState as te,useRef as oe}from"react";var re=20,k="default";var H=(e,t)=>{let{toastLimit:o}=e.settings;switch(t.type){case 0:return{...e,toasts:[t.toast,...e.toasts].slice(0,o)};case 1:return{...e,toasts:e.toasts.map(r=>r.id===t.toast.id?{...r,...t.toast}:r)};case 2:let{toast:s}=t;return H(e,{type:e.toasts.find(r=>r.id===s.id)?1:0,toast:s});case 3:let{toastId:a}=t;return{...e,toasts:e.toasts.map(r=>r.id===a||a===void 0?{...r,dismissed:!0,visible:!1}:r)};case 4:return t.toastId===void 0?{...e,toasts:[]}:{...e,toasts:e.toasts.filter(r=>r.id!==t.toastId)};case 5:return{...e,pausedAt:t.time};case 6:let i=t.time-(e.pausedAt||0);return{...e,pausedAt:void 0,toasts:e.toasts.map(r=>({...r,pauseDuration:r.pauseDuration+i}))}}},v=[],j={toasts:[],pausedAt:void 0,settings:{toastLimit:re}},f={},Y=(e,t=k)=>{f[t]=H(f[t]||j,e),v.forEach(([o,s])=>{o===t&&s(f[t])})},_=e=>Object.keys(f).forEach(t=>Y(e,t)),Q=e=>Object.keys(f).find(t=>f[t].toasts.some(o=>o.id===e)),S=(e=k)=>t=>{Y(t,e)},se={blank:4e3,error:4e3,success:2e3,loading:1/0,custom:4e3},V=(e={},t=k)=>{let[o,s]=te(f[t]||j),a=oe(f[t]);ee(()=>(a.current!==f[t]&&s(f[t]),v.push([t,s]),()=>{let r=v.findIndex(([l])=>l===t);r>-1&&v.splice(r,1)}),[t]);let i=o.toasts.map(r=>{var l,g,T;return{...e,...e[r.type],...r,removeDelay:r.removeDelay||((l=e[r.type])==null?void 0:l.removeDelay)||(e==null?void 0:e.removeDelay),duration:r.duration||((g=e[r.type])==null?void 0:g.duration)||(e==null?void 0:e.duration)||se[r.type],style:{...e.style,...(T=e[r.type])==null?void 0:T.style,...r.style}}});return{...o,toasts:i}};var ie=(e,t="blank",o)=>({createdAt:Date.now(),visible:!0,dismissed:!1,type:t,ariaProps:{role:"status","aria-live":"polite"},message:e,pauseDuration:0,...o,id:(o==null?void 0:o.id)||W()}),P=e=>(t,o)=>{let s=ie(t,e,o);return S(s.toasterId||Q(s.id))({type:2,toast:s}),s.id},n=(e,t)=>P("blank")(e,t);n.error=P("error");n.success=P("success");n.loading=P("loading");n.custom=P("custom");n.dismiss=(e,t)=>{let o={type:3,toastId:e};t?S(t)(o):_(o)};n.dismissAll=e=>n.dismiss(void 0,e);n.remove=(e,t)=>{let o={type:4,toastId:e};t?S(t)(o):_(o)};n.removeAll=e=>n.remove(void 0,e);n.promise=(e,t,o)=>{let s=n.loading(t.loading,{...o,...o==null?void 0:o.loading});return typeof e=="function"&&(e=e()),e.then(a=>{let i=t.success?h(t.success,a):void 0;return i?n.success(i,{id:s,...o,...o==null?void 0:o.success}):n.dismiss(s),a}).catch(a=>{let i=t.error?h(t.error,a):void 0;i?n.error(i,{id:s,...o,...o==null?void 0:o.error}):n.dismiss(s)}),e};import{useEffect as X,useCallback as A,useRef as ne}from"react";var ce=1e3,w=(e,t="default")=>{let{toasts:o,pausedAt:s}=V(e,t),a=ne(new Map).current,i=A((c,m=ce)=>{if(a.has(c))return;let p=setTimeout(()=>{a.delete(c),r({type:4,toastId:c})},m);a.set(c,p)},[]);X(()=>{if(s)return;let c=Date.now(),m=o.map(p=>{if(p.duration===1/0)return;let R=(p.duration||0)+p.pauseDuration-(c-p.createdAt);if(R<0){p.visible&&n.dismiss(p.id);return}return setTimeout(()=>n.dismiss(p.id,t),R)});return()=>{m.forEach(p=>p&&clearTimeout(p))}},[o,s,t]);let r=A(S(t),[t]),l=A(()=>{r({type:5,time:Date.now()})},[r]),g=A((c,m)=>{r({type:1,toast:{id:c,height:m}})},[r]),T=A(()=>{s&&r({type:6,time:Date.now()})},[s,r]),d=A((c,m)=>{let{reverseOrder:p=!1,gutter:R=8,defaultPosition:z}=m||{},O=o.filter(u=>(u.position||z)===(c.position||z)&&u.height),K=O.findIndex(u=>u.id===c.id),B=O.filter((u,I)=>I<K&&u.visible).length;return O.filter(u=>u.visible).slice(...p?[B+1]:[0,B]).reduce((u,I)=>u+(I.height||0)+R,0)},[o]);return X(()=>{o.forEach(c=>{if(c.dismissed)i(c.id,c.removeDelay);else{let m=a.get(c.id);m&&(clearTimeout(m),a.delete(c.id))}})},[o,i]),{toasts:o,handlers:{updateHeight:g,startPause:l,endPause:T,calculateOffset:d}}};import*as y from"react";import{styled as J,keyframes as G}from"goober";import*as b from"react";import{styled as U,keyframes as xe}from"goober";import{styled as pe,keyframes as M}from"goober";var de=M`
|
||||
from {
|
||||
transform: scale(0) rotate(45deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1) rotate(45deg);
|
||||
opacity: 1;
|
||||
}`,me=M`
|
||||
from {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}`,le=M`
|
||||
from {
|
||||
transform: scale(0) rotate(90deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1) rotate(90deg);
|
||||
opacity: 1;
|
||||
}`,C=pe("div")`
|
||||
width: 20px;
|
||||
opacity: 0;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
background: ${e=>e.primary||"#ff4b4b"};
|
||||
position: relative;
|
||||
transform: rotate(45deg);
|
||||
|
||||
animation: ${de} 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
||||
forwards;
|
||||
animation-delay: 100ms;
|
||||
|
||||
&:after,
|
||||
&:before {
|
||||
content: '';
|
||||
animation: ${me} 0.15s ease-out forwards;
|
||||
animation-delay: 150ms;
|
||||
position: absolute;
|
||||
border-radius: 3px;
|
||||
opacity: 0;
|
||||
background: ${e=>e.secondary||"#fff"};
|
||||
bottom: 9px;
|
||||
left: 4px;
|
||||
height: 2px;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
animation: ${le} 0.15s ease-out forwards;
|
||||
animation-delay: 180ms;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
`;import{styled as ue,keyframes as fe}from"goober";var Te=fe`
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
`,F=ue("div")`
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid;
|
||||
border-radius: 100%;
|
||||
border-color: ${e=>e.secondary||"#e0e0e0"};
|
||||
border-right-color: ${e=>e.primary||"#616161"};
|
||||
animation: ${Te} 1s linear infinite;
|
||||
`;import{styled as ye,keyframes as q}from"goober";var ge=q`
|
||||
from {
|
||||
transform: scale(0) rotate(45deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1) rotate(45deg);
|
||||
opacity: 1;
|
||||
}`,he=q`
|
||||
0% {
|
||||
height: 0;
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
40% {
|
||||
height: 0;
|
||||
width: 6px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
height: 10px;
|
||||
}`,L=ye("div")`
|
||||
width: 20px;
|
||||
opacity: 0;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
background: ${e=>e.primary||"#61d345"};
|
||||
position: relative;
|
||||
transform: rotate(45deg);
|
||||
|
||||
animation: ${ge} 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
||||
forwards;
|
||||
animation-delay: 100ms;
|
||||
&:after {
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
animation: ${he} 0.2s ease-out forwards;
|
||||
opacity: 0;
|
||||
animation-delay: 200ms;
|
||||
position: absolute;
|
||||
border-right: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-color: ${e=>e.secondary||"#fff"};
|
||||
bottom: 6px;
|
||||
left: 6px;
|
||||
height: 10px;
|
||||
width: 6px;
|
||||
}
|
||||
`;var be=U("div")`
|
||||
position: absolute;
|
||||
`,Se=U("div")`
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
`,Ae=xe`
|
||||
from {
|
||||
transform: scale(0.6);
|
||||
opacity: 0.4;
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}`,Pe=U("div")`
|
||||
position: relative;
|
||||
transform: scale(0.6);
|
||||
opacity: 0.4;
|
||||
min-width: 20px;
|
||||
animation: ${Ae} 0.3s 0.12s cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
||||
forwards;
|
||||
`,$=({toast:e})=>{let{icon:t,type:o,iconTheme:s}=e;return t!==void 0?typeof t=="string"?b.createElement(Pe,null,t):t:o==="blank"?null:b.createElement(Se,null,b.createElement(F,{...s}),o!=="loading"&&b.createElement(be,null,o==="error"?b.createElement(C,{...s}):b.createElement(L,{...s})))};var Re=e=>`
|
||||
0% {transform: translate3d(0,${e*-200}%,0) scale(.6); opacity:.5;}
|
||||
100% {transform: translate3d(0,0,0) scale(1); opacity:1;}
|
||||
`,Ee=e=>`
|
||||
0% {transform: translate3d(0,0,-1px) scale(1); opacity:1;}
|
||||
100% {transform: translate3d(0,${e*-150}%,-1px) scale(.6); opacity:0;}
|
||||
`,ve="0%{opacity:0;} 100%{opacity:1;}",De="0%{opacity:1;} 100%{opacity:0;}",Oe=J("div")`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
color: #363636;
|
||||
line-height: 1.3;
|
||||
will-change: transform;
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1), 0 3px 3px rgba(0, 0, 0, 0.05);
|
||||
max-width: 350px;
|
||||
pointer-events: auto;
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
`,Ie=J("div")`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 4px 10px;
|
||||
color: inherit;
|
||||
flex: 1 1 auto;
|
||||
white-space: pre-line;
|
||||
`,ke=(e,t)=>{let s=e.includes("top")?1:-1,[a,i]=E()?[ve,De]:[Re(s),Ee(s)];return{animation:t?`${G(a)} 0.35s cubic-bezier(.21,1.02,.73,1) forwards`:`${G(i)} 0.4s forwards cubic-bezier(.06,.71,.55,1)`}},N=y.memo(({toast:e,position:t,style:o,children:s})=>{let a=e.height?ke(e.position||t||"top-center",e.visible):{opacity:0},i=y.createElement($,{toast:e}),r=y.createElement(Ie,{...e.ariaProps},h(e.message,e));return y.createElement(Oe,{className:e.className,style:{...a,...o,...e.style}},typeof s=="function"?s({icon:i,message:r}):y.createElement(y.Fragment,null,i,r))});import{css as _e,setup as Ve}from"goober";import*as x from"react";Ve(x.createElement);var we=({id:e,className:t,style:o,onHeightUpdate:s,children:a})=>{let i=x.useCallback(r=>{if(r){let l=()=>{let g=r.getBoundingClientRect().height;s(e,g)};l(),new MutationObserver(l).observe(r,{subtree:!0,childList:!0,characterData:!0})}},[e,s]);return x.createElement("div",{ref:i,className:t,style:o},a)},Me=(e,t)=>{let o=e.includes("top"),s=o?{top:0}:{bottom:0},a=e.includes("center")?{justifyContent:"center"}:e.includes("right")?{justifyContent:"flex-end"}:{};return{left:0,right:0,display:"flex",position:"absolute",transition:E()?void 0:"all 230ms cubic-bezier(.21,1.02,.73,1)",transform:`translateY(${t*(o?1:-1)}px)`,...s,...a}},Ce=_e`
|
||||
z-index: 9999;
|
||||
> * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
`,D=16,Fe=({reverseOrder:e,position:t="top-center",toastOptions:o,gutter:s,children:a,toasterId:i,containerStyle:r,containerClassName:l})=>{let{toasts:g,handlers:T}=w(o,i);return x.createElement("div",{"data-rht-toaster":i||"",style:{position:"fixed",zIndex:9999,top:D,left:D,right:D,bottom:D,pointerEvents:"none",...r},className:l,onMouseEnter:T.startPause,onMouseLeave:T.endPause},g.map(d=>{let c=d.position||t,m=T.calculateOffset(d,{reverseOrder:e,gutter:s,defaultPosition:t}),p=Me(c,m);return x.createElement(we,{id:d.id,key:d.id,onHeightUpdate:T.updateHeight,className:d.visible?Ce:"",style:p},d.type==="custom"?h(d.message,d):a?a(d):x.createElement(N,{toast:d,position:c}))}))};var zt=n;export{L as CheckmarkIcon,C as ErrorIcon,F as LoaderIcon,N as ToastBar,$ as ToastIcon,Fe as Toaster,zt as default,h as resolveValue,n as toast,w as useToaster,V as useToasterStore};
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
+1
File diff suppressed because one or more lines are too long
+93
@@ -0,0 +1,93 @@
|
||||
import { CSSProperties } from 'react';
|
||||
|
||||
type ToastType = 'success' | 'error' | 'loading' | 'blank' | 'custom';
|
||||
type ToastPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
||||
type Renderable = React.ReactElement | string | null;
|
||||
interface IconTheme {
|
||||
primary: string;
|
||||
secondary: string;
|
||||
}
|
||||
type ValueFunction<TValue, TArg> = (arg: TArg) => TValue;
|
||||
type ValueOrFunction<TValue, TArg> = TValue | ValueFunction<TValue, TArg>;
|
||||
declare const resolveValue: <TValue, TArg>(valOrFunction: ValueOrFunction<TValue, TArg>, arg: TArg) => TValue;
|
||||
interface Toast {
|
||||
type: ToastType;
|
||||
id: string;
|
||||
toasterId?: string;
|
||||
message: ValueOrFunction<Renderable, Toast>;
|
||||
icon?: Renderable;
|
||||
duration?: number;
|
||||
pauseDuration: number;
|
||||
position?: ToastPosition;
|
||||
removeDelay?: number;
|
||||
ariaProps: {
|
||||
role: 'status' | 'alert';
|
||||
'aria-live': 'assertive' | 'off' | 'polite';
|
||||
};
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
iconTheme?: IconTheme;
|
||||
createdAt: number;
|
||||
visible: boolean;
|
||||
dismissed: boolean;
|
||||
height?: number;
|
||||
}
|
||||
type ToastOptions = Partial<Pick<Toast, 'id' | 'icon' | 'duration' | 'ariaProps' | 'className' | 'style' | 'position' | 'iconTheme' | 'toasterId' | 'removeDelay'>>;
|
||||
type DefaultToastOptions = ToastOptions & {
|
||||
[key in ToastType]?: ToastOptions;
|
||||
};
|
||||
interface ToasterProps {
|
||||
position?: ToastPosition;
|
||||
toastOptions?: DefaultToastOptions;
|
||||
reverseOrder?: boolean;
|
||||
gutter?: number;
|
||||
containerStyle?: React.CSSProperties;
|
||||
containerClassName?: string;
|
||||
toasterId?: string;
|
||||
children?: (toast: Toast) => React.ReactElement;
|
||||
}
|
||||
|
||||
type Message = ValueOrFunction<Renderable, Toast>;
|
||||
type ToastHandler = (message: Message, options?: ToastOptions) => string;
|
||||
declare const toast: {
|
||||
(message: Message, opts?: ToastOptions): string;
|
||||
error: ToastHandler;
|
||||
success: ToastHandler;
|
||||
loading: ToastHandler;
|
||||
custom: ToastHandler;
|
||||
dismiss(toastId?: string, toasterId?: string): void;
|
||||
dismissAll(toasterId?: string): void;
|
||||
remove(toastId?: string, toasterId?: string): void;
|
||||
removeAll(toasterId?: string): void;
|
||||
promise<T>(promise: Promise<T> | (() => Promise<T>), msgs: {
|
||||
loading: Renderable;
|
||||
success?: ValueOrFunction<Renderable, T>;
|
||||
error?: ValueOrFunction<Renderable, any>;
|
||||
}, opts?: DefaultToastOptions): Promise<T>;
|
||||
};
|
||||
|
||||
declare const useToaster: (toastOptions?: DefaultToastOptions, toasterId?: string) => {
|
||||
toasts: Toast[];
|
||||
handlers: {
|
||||
updateHeight: (toastId: string, height: number) => void;
|
||||
startPause: () => void;
|
||||
endPause: () => void;
|
||||
calculateOffset: (toast: Toast, opts?: {
|
||||
reverseOrder?: boolean;
|
||||
gutter?: number;
|
||||
defaultPosition?: ToastPosition;
|
||||
}) => number;
|
||||
};
|
||||
};
|
||||
|
||||
interface ToasterSettings {
|
||||
toastLimit: number;
|
||||
}
|
||||
interface ToasterState {
|
||||
toasts: Toast[];
|
||||
settings: ToasterSettings;
|
||||
pausedAt: number | undefined;
|
||||
}
|
||||
declare const useStore: (toastOptions?: DefaultToastOptions, toasterId?: string) => ToasterState;
|
||||
|
||||
export { DefaultToastOptions, IconTheme, Renderable, Toast, ToastOptions, ToastPosition, ToastType, ToasterProps, ValueFunction, ValueOrFunction, toast as default, resolveValue, toast, useToaster, useStore as useToasterStore };
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";var x=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var Q=(e,t)=>{for(var s in t)x(e,s,{get:t[s],enumerable:!0})},Y=(e,t,s,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of H(t))!j.call(e,n)&&n!==s&&x(e,n,{get:()=>t[n],enumerable:!(r=C(t,n))||r.enumerable});return e};var B=e=>Y(x({},"__esModule",{value:!0}),e);var Z={};Q(Z,{default:()=>K,resolveValue:()=>A,toast:()=>i,useToaster:()=>w,useToasterStore:()=>b});module.exports=B(Z);var W=e=>typeof e=="function",A=(e,t)=>W(e)?e(t):e;var I=(()=>{let e=0;return()=>(++e).toString()})(),se=(()=>{let e;return()=>{if(e===void 0&&typeof window<"u"){let t=matchMedia("(prefers-reduced-motion: reduce)");e=!t||t.matches}return e}})();var m=require("react");var X=20,R="default";var M=(e,t)=>{let{toastLimit:s}=e.settings;switch(t.type){case 0:return{...e,toasts:[t.toast,...e.toasts].slice(0,s)};case 1:return{...e,toasts:e.toasts.map(o=>o.id===t.toast.id?{...o,...t.toast}:o)};case 2:let{toast:r}=t;return M(e,{type:e.toasts.find(o=>o.id===r.id)?1:0,toast:r});case 3:let{toastId:n}=t;return{...e,toasts:e.toasts.map(o=>o.id===n||n===void 0?{...o,dismissed:!0,visible:!1}:o)};case 4:return t.toastId===void 0?{...e,toasts:[]}:{...e,toasts:e.toasts.filter(o=>o.id!==t.toastId)};case 5:return{...e,pausedAt:t.time};case 6:let T=t.time-(e.pausedAt||0);return{...e,pausedAt:void 0,toasts:e.toasts.map(o=>({...o,pauseDuration:o.pauseDuration+T}))}}},E=[],U={toasts:[],pausedAt:void 0,settings:{toastLimit:X}},p={},F=(e,t=R)=>{p[t]=M(p[t]||U,e),E.forEach(([s,r])=>{s===t&&r(p[t])})},_=e=>Object.keys(p).forEach(t=>F(e,t)),k=e=>Object.keys(p).find(t=>p[t].toasts.some(s=>s.id===e)),S=(e=R)=>t=>{F(t,e)},q={blank:4e3,error:4e3,success:2e3,loading:1/0,custom:4e3},b=(e={},t=R)=>{let[s,r]=(0,m.useState)(p[t]||U),n=(0,m.useRef)(p[t]);(0,m.useEffect)(()=>(n.current!==p[t]&&r(p[t]),E.push([t,r]),()=>{let o=E.findIndex(([f])=>f===t);o>-1&&E.splice(o,1)}),[t]);let T=s.toasts.map(o=>{var f,g,D;return{...e,...e[o.type],...o,removeDelay:o.removeDelay||((f=e[o.type])==null?void 0:f.removeDelay)||(e==null?void 0:e.removeDelay),duration:o.duration||((g=e[o.type])==null?void 0:g.duration)||(e==null?void 0:e.duration)||q[o.type],style:{...e.style,...(D=e[o.type])==null?void 0:D.style,...o.style}}});return{...s,toasts:T}};var G=(e,t="blank",s)=>({createdAt:Date.now(),visible:!0,dismissed:!1,type:t,ariaProps:{role:"status","aria-live":"polite"},message:e,pauseDuration:0,...s,id:(s==null?void 0:s.id)||I()}),y=e=>(t,s)=>{let r=G(t,e,s);return S(r.toasterId||k(r.id))({type:2,toast:r}),r.id},i=(e,t)=>y("blank")(e,t);i.error=y("error");i.success=y("success");i.loading=y("loading");i.custom=y("custom");i.dismiss=(e,t)=>{let s={type:3,toastId:e};t?S(t)(s):_(s)};i.dismissAll=e=>i.dismiss(void 0,e);i.remove=(e,t)=>{let s={type:4,toastId:e};t?S(t)(s):_(s)};i.removeAll=e=>i.remove(void 0,e);i.promise=(e,t,s)=>{let r=i.loading(t.loading,{...s,...s==null?void 0:s.loading});return typeof e=="function"&&(e=e()),e.then(n=>{let T=t.success?A(t.success,n):void 0;return T?i.success(T,{id:r,...s,...s==null?void 0:s.success}):i.dismiss(r),n}).catch(n=>{let T=t.error?A(t.error,n):void 0;T?i.error(T,{id:r,...s,...s==null?void 0:s.error}):i.dismiss(r)}),e};var c=require("react");var J=1e3,w=(e,t="default")=>{let{toasts:s,pausedAt:r}=b(e,t),n=(0,c.useRef)(new Map).current,T=(0,c.useCallback)((a,d=J)=>{if(n.has(a))return;let u=setTimeout(()=>{n.delete(a),o({type:4,toastId:a})},d);n.set(a,u)},[]);(0,c.useEffect)(()=>{if(r)return;let a=Date.now(),d=s.map(u=>{if(u.duration===1/0)return;let O=(u.duration||0)+u.pauseDuration-(a-u.createdAt);if(O<0){u.visible&&i.dismiss(u.id);return}return setTimeout(()=>i.dismiss(u.id,t),O)});return()=>{d.forEach(u=>u&&clearTimeout(u))}},[s,r,t]);let o=(0,c.useCallback)(S(t),[t]),f=(0,c.useCallback)(()=>{o({type:5,time:Date.now()})},[o]),g=(0,c.useCallback)((a,d)=>{o({type:1,toast:{id:a,height:d}})},[o]),D=(0,c.useCallback)(()=>{r&&o({type:6,time:Date.now()})},[r,o]),N=(0,c.useCallback)((a,d)=>{let{reverseOrder:u=!1,gutter:O=8,defaultPosition:V}=d||{},h=s.filter(l=>(l.position||V)===(a.position||V)&&l.height),L=h.findIndex(l=>l.id===a.id),v=h.filter((l,P)=>P<L&&l.visible).length;return h.filter(l=>l.visible).slice(...u?[v+1]:[0,v]).reduce((l,P)=>l+(P.height||0)+O,0)},[s]);return(0,c.useEffect)(()=>{s.forEach(a=>{if(a.dismissed)T(a.id,a.removeDelay);else{let d=n.get(a.id);d&&(clearTimeout(d),n.delete(a.id))}})},[s,T]),{toasts:s,handlers:{updateHeight:g,startPause:f,endPause:D,calculateOffset:N}}};var K=i;0&&(module.exports={resolveValue,toast,useToaster,useToasterStore});
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+2
@@ -0,0 +1,2 @@
|
||||
var N=e=>typeof e=="function",D=(e,t)=>N(e)?e(t):e;var V=(()=>{let e=0;return()=>(++e).toString()})(),J=(()=>{let e;return()=>{if(e===void 0&&typeof window<"u"){let t=matchMedia("(prefers-reduced-motion: reduce)");e=!t||t.matches}return e}})();import{useEffect as L,useState as C,useRef as H}from"react";var j=20,h="default";var v=(e,t)=>{let{toastLimit:s}=e.settings;switch(t.type){case 0:return{...e,toasts:[t.toast,...e.toasts].slice(0,s)};case 1:return{...e,toasts:e.toasts.map(o=>o.id===t.toast.id?{...o,...t.toast}:o)};case 2:let{toast:r}=t;return v(e,{type:e.toasts.find(o=>o.id===r.id)?1:0,toast:r});case 3:let{toastId:a}=t;return{...e,toasts:e.toasts.map(o=>o.id===a||a===void 0?{...o,dismissed:!0,visible:!1}:o)};case 4:return t.toastId===void 0?{...e,toasts:[]}:{...e,toasts:e.toasts.filter(o=>o.id!==t.toastId)};case 5:return{...e,pausedAt:t.time};case 6:let T=t.time-(e.pausedAt||0);return{...e,pausedAt:void 0,toasts:e.toasts.map(o=>({...o,pauseDuration:o.pauseDuration+T}))}}},O=[],I={toasts:[],pausedAt:void 0,settings:{toastLimit:j}},l={},M=(e,t=h)=>{l[t]=v(l[t]||I,e),O.forEach(([s,r])=>{s===t&&r(l[t])})},P=e=>Object.keys(l).forEach(t=>M(e,t)),U=e=>Object.keys(l).find(t=>l[t].toasts.some(s=>s.id===e)),f=(e=h)=>t=>{M(t,e)},Q={blank:4e3,error:4e3,success:2e3,loading:1/0,custom:4e3},x=(e={},t=h)=>{let[s,r]=C(l[t]||I),a=H(l[t]);L(()=>(a.current!==l[t]&&r(l[t]),O.push([t,r]),()=>{let o=O.findIndex(([p])=>p===t);o>-1&&O.splice(o,1)}),[t]);let T=s.toasts.map(o=>{var p,A,y;return{...e,...e[o.type],...o,removeDelay:o.removeDelay||((p=e[o.type])==null?void 0:p.removeDelay)||(e==null?void 0:e.removeDelay),duration:o.duration||((A=e[o.type])==null?void 0:A.duration)||(e==null?void 0:e.duration)||Q[o.type],style:{...e.style,...(y=e[o.type])==null?void 0:y.style,...o.style}}});return{...s,toasts:T}};var B=(e,t="blank",s)=>({createdAt:Date.now(),visible:!0,dismissed:!1,type:t,ariaProps:{role:"status","aria-live":"polite"},message:e,pauseDuration:0,...s,id:(s==null?void 0:s.id)||V()}),S=e=>(t,s)=>{let r=B(t,e,s);return f(r.toasterId||U(r.id))({type:2,toast:r}),r.id},n=(e,t)=>S("blank")(e,t);n.error=S("error");n.success=S("success");n.loading=S("loading");n.custom=S("custom");n.dismiss=(e,t)=>{let s={type:3,toastId:e};t?f(t)(s):P(s)};n.dismissAll=e=>n.dismiss(void 0,e);n.remove=(e,t)=>{let s={type:4,toastId:e};t?f(t)(s):P(s)};n.removeAll=e=>n.remove(void 0,e);n.promise=(e,t,s)=>{let r=n.loading(t.loading,{...s,...s==null?void 0:s.loading});return typeof e=="function"&&(e=e()),e.then(a=>{let T=t.success?D(t.success,a):void 0;return T?n.success(T,{id:r,...s,...s==null?void 0:s.success}):n.dismiss(r),a}).catch(a=>{let T=t.error?D(t.error,a):void 0;T?n.error(T,{id:r,...s,...s==null?void 0:s.error}):n.dismiss(r)}),e};import{useEffect as F,useCallback as m,useRef as W}from"react";var X=1e3,q=(e,t="default")=>{let{toasts:s,pausedAt:r}=x(e,t),a=W(new Map).current,T=m((i,c=X)=>{if(a.has(i))return;let u=setTimeout(()=>{a.delete(i),o({type:4,toastId:i})},c);a.set(i,u)},[]);F(()=>{if(r)return;let i=Date.now(),c=s.map(u=>{if(u.duration===1/0)return;let g=(u.duration||0)+u.pauseDuration-(i-u.createdAt);if(g<0){u.visible&&n.dismiss(u.id);return}return setTimeout(()=>n.dismiss(u.id,t),g)});return()=>{c.forEach(u=>u&&clearTimeout(u))}},[s,r,t]);let o=m(f(t),[t]),p=m(()=>{o({type:5,time:Date.now()})},[o]),A=m((i,c)=>{o({type:1,toast:{id:i,height:c}})},[o]),y=m(()=>{r&&o({type:6,time:Date.now()})},[r,o]),k=m((i,c)=>{let{reverseOrder:u=!1,gutter:g=8,defaultPosition:R}=c||{},E=s.filter(d=>(d.position||R)===(i.position||R)&&d.height),w=E.findIndex(d=>d.id===i.id),_=E.filter((d,b)=>b<w&&d.visible).length;return E.filter(d=>d.visible).slice(...u?[_+1]:[0,_]).reduce((d,b)=>d+(b.height||0)+g,0)},[s]);return F(()=>{s.forEach(i=>{if(i.dismissed)T(i.id,i.removeDelay);else{let c=a.get(i.id);c&&(clearTimeout(c),a.delete(i.id))}})},[s,T]),{toasts:s,handlers:{updateHeight:A,startPause:p,endPause:y,calculateOffset:k}}};var Ae=n;export{Ae as default,D as resolveValue,n as toast,q as useToaster,x as useToasterStore};
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
+1
File diff suppressed because one or more lines are too long
+103
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"name": "react-hot-toast",
|
||||
"description": "Smoking hot React Notifications. Lightweight, customizable and beautiful by default.",
|
||||
"version": "2.6.0",
|
||||
"author": "Timo Lins",
|
||||
"license": "MIT",
|
||||
"repository": "timolins/react-hot-toast",
|
||||
"keywords": [
|
||||
"react",
|
||||
"notifications",
|
||||
"toast",
|
||||
"snackbar"
|
||||
],
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.js"
|
||||
},
|
||||
"./headless": {
|
||||
"types": "./headless/index.d.ts",
|
||||
"import": "./headless/index.mjs",
|
||||
"require": "./headless/index.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"headless",
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "prettier src --ignore-unknown --write"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 80,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"arrowParens": "always",
|
||||
"trailingComma": "es5"
|
||||
},
|
||||
"size-limit": [
|
||||
{
|
||||
"path": "dist/index.js",
|
||||
"limit": "5.5 KB"
|
||||
},
|
||||
{
|
||||
"path": "dist/index.mjs",
|
||||
"limit": "5.5 KB"
|
||||
},
|
||||
{
|
||||
"path": "headless/index.js",
|
||||
"limit": "2.5 KB"
|
||||
},
|
||||
{
|
||||
"path": "headless/index.mjs",
|
||||
"limit": "2.5 KB"
|
||||
}
|
||||
],
|
||||
"devDependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@size-limit/preset-small-lib": "^7.0.8",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"esbuild-minify-templates": "^0.13.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"prettier": "^2.8.8",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"size-limit": "^7.0.8",
|
||||
"ts-jest": "^29.2.5",
|
||||
"tslib": "^2.8.1",
|
||||
"tsup": "^6.7.0",
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"csstype": "^3.1.3",
|
||||
"goober": "^2.1.16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16",
|
||||
"react-dom": ">=16"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "tsup --watch",
|
||||
"build": "tsup",
|
||||
"test": "jest --runInBand",
|
||||
"setup": "pnpm i && cd site && pnpm i && cd .. && pnpm run link",
|
||||
"link": "pnpm link ./site/node_modules/react && pnpm link ./site/node_modules/react-dom",
|
||||
"size": "size-limit"
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import { styled, keyframes } from 'goober';
|
||||
|
||||
const circleAnimation = keyframes`
|
||||
from {
|
||||
transform: scale(0) rotate(45deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1) rotate(45deg);
|
||||
opacity: 1;
|
||||
}`;
|
||||
|
||||
const checkmarkAnimation = keyframes`
|
||||
0% {
|
||||
height: 0;
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
40% {
|
||||
height: 0;
|
||||
width: 6px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
height: 10px;
|
||||
}`;
|
||||
|
||||
export interface CheckmarkTheme {
|
||||
primary?: string;
|
||||
secondary?: string;
|
||||
}
|
||||
|
||||
export const CheckmarkIcon = styled('div')<CheckmarkTheme>`
|
||||
width: 20px;
|
||||
opacity: 0;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
background: ${(p) => p.primary || '#61d345'};
|
||||
position: relative;
|
||||
transform: rotate(45deg);
|
||||
|
||||
animation: ${circleAnimation} 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
||||
forwards;
|
||||
animation-delay: 100ms;
|
||||
&:after {
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
animation: ${checkmarkAnimation} 0.2s ease-out forwards;
|
||||
opacity: 0;
|
||||
animation-delay: 200ms;
|
||||
position: absolute;
|
||||
border-right: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-color: ${(p) => p.secondary || '#fff'};
|
||||
bottom: 6px;
|
||||
left: 6px;
|
||||
height: 10px;
|
||||
width: 6px;
|
||||
}
|
||||
`;
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import { styled, keyframes } from 'goober';
|
||||
|
||||
const circleAnimation = keyframes`
|
||||
from {
|
||||
transform: scale(0) rotate(45deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1) rotate(45deg);
|
||||
opacity: 1;
|
||||
}`;
|
||||
|
||||
const firstLineAnimation = keyframes`
|
||||
from {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}`;
|
||||
|
||||
const secondLineAnimation = keyframes`
|
||||
from {
|
||||
transform: scale(0) rotate(90deg);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1) rotate(90deg);
|
||||
opacity: 1;
|
||||
}`;
|
||||
|
||||
export interface ErrorTheme {
|
||||
primary?: string;
|
||||
secondary?: string;
|
||||
}
|
||||
|
||||
export const ErrorIcon = styled('div')<ErrorTheme>`
|
||||
width: 20px;
|
||||
opacity: 0;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
background: ${(p) => p.primary || '#ff4b4b'};
|
||||
position: relative;
|
||||
transform: rotate(45deg);
|
||||
|
||||
animation: ${circleAnimation} 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
||||
forwards;
|
||||
animation-delay: 100ms;
|
||||
|
||||
&:after,
|
||||
&:before {
|
||||
content: '';
|
||||
animation: ${firstLineAnimation} 0.15s ease-out forwards;
|
||||
animation-delay: 150ms;
|
||||
position: absolute;
|
||||
border-radius: 3px;
|
||||
opacity: 0;
|
||||
background: ${(p) => p.secondary || '#fff'};
|
||||
bottom: 9px;
|
||||
left: 4px;
|
||||
height: 2px;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
animation: ${secondLineAnimation} 0.15s ease-out forwards;
|
||||
animation-delay: 180ms;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
`;
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import { styled, keyframes } from 'goober';
|
||||
|
||||
const rotate = keyframes`
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
`;
|
||||
|
||||
export interface LoaderTheme {
|
||||
primary?: string;
|
||||
secondary?: string;
|
||||
}
|
||||
|
||||
export const LoaderIcon = styled('div')<LoaderTheme>`
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid;
|
||||
border-radius: 100%;
|
||||
border-color: ${(p) => p.secondary || '#e0e0e0'};
|
||||
border-right-color: ${(p) => p.primary || '#616161'};
|
||||
animation: ${rotate} 1s linear infinite;
|
||||
`;
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
import * as React from 'react';
|
||||
import { styled, keyframes } from 'goober';
|
||||
|
||||
import { Toast, ToastPosition, resolveValue, Renderable } from '../core/types';
|
||||
import { ToastIcon } from './toast-icon';
|
||||
import { prefersReducedMotion } from '../core/utils';
|
||||
|
||||
const enterAnimation = (factor: number) => `
|
||||
0% {transform: translate3d(0,${factor * -200}%,0) scale(.6); opacity:.5;}
|
||||
100% {transform: translate3d(0,0,0) scale(1); opacity:1;}
|
||||
`;
|
||||
|
||||
const exitAnimation = (factor: number) => `
|
||||
0% {transform: translate3d(0,0,-1px) scale(1); opacity:1;}
|
||||
100% {transform: translate3d(0,${factor * -150}%,-1px) scale(.6); opacity:0;}
|
||||
`;
|
||||
|
||||
const fadeInAnimation = `0%{opacity:0;} 100%{opacity:1;}`;
|
||||
const fadeOutAnimation = `0%{opacity:1;} 100%{opacity:0;}`;
|
||||
|
||||
const ToastBarBase = styled('div')`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
color: #363636;
|
||||
line-height: 1.3;
|
||||
will-change: transform;
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1), 0 3px 3px rgba(0, 0, 0, 0.05);
|
||||
max-width: 350px;
|
||||
pointer-events: auto;
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
`;
|
||||
|
||||
const Message = styled('div')`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 4px 10px;
|
||||
color: inherit;
|
||||
flex: 1 1 auto;
|
||||
white-space: pre-line;
|
||||
`;
|
||||
|
||||
interface ToastBarProps {
|
||||
toast: Toast;
|
||||
position?: ToastPosition;
|
||||
style?: React.CSSProperties;
|
||||
children?: (components: {
|
||||
icon: Renderable;
|
||||
message: Renderable;
|
||||
}) => Renderable;
|
||||
}
|
||||
|
||||
const getAnimationStyle = (
|
||||
position: ToastPosition,
|
||||
visible: boolean
|
||||
): React.CSSProperties => {
|
||||
const top = position.includes('top');
|
||||
const factor = top ? 1 : -1;
|
||||
|
||||
const [enter, exit] = prefersReducedMotion()
|
||||
? [fadeInAnimation, fadeOutAnimation]
|
||||
: [enterAnimation(factor), exitAnimation(factor)];
|
||||
|
||||
return {
|
||||
animation: visible
|
||||
? `${keyframes(enter)} 0.35s cubic-bezier(.21,1.02,.73,1) forwards`
|
||||
: `${keyframes(exit)} 0.4s forwards cubic-bezier(.06,.71,.55,1)`,
|
||||
};
|
||||
};
|
||||
|
||||
export const ToastBar: React.FC<ToastBarProps> = React.memo(
|
||||
({ toast, position, style, children }) => {
|
||||
const animationStyle: React.CSSProperties = toast.height
|
||||
? getAnimationStyle(
|
||||
toast.position || position || 'top-center',
|
||||
toast.visible
|
||||
)
|
||||
: { opacity: 0 };
|
||||
|
||||
const icon = <ToastIcon toast={toast} />;
|
||||
const message = (
|
||||
<Message {...toast.ariaProps}>
|
||||
{resolveValue(toast.message, toast)}
|
||||
</Message>
|
||||
);
|
||||
|
||||
return (
|
||||
<ToastBarBase
|
||||
className={toast.className}
|
||||
style={{
|
||||
...animationStyle,
|
||||
...style,
|
||||
...toast.style,
|
||||
}}
|
||||
>
|
||||
{typeof children === 'function' ? (
|
||||
children({
|
||||
icon,
|
||||
message,
|
||||
})
|
||||
) : (
|
||||
<>
|
||||
{icon}
|
||||
{message}
|
||||
</>
|
||||
)}
|
||||
</ToastBarBase>
|
||||
);
|
||||
}
|
||||
);
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import * as React from 'react';
|
||||
import { styled, keyframes } from 'goober';
|
||||
|
||||
import { Toast } from '../core/types';
|
||||
import { ErrorIcon, ErrorTheme } from './error';
|
||||
import { LoaderIcon, LoaderTheme } from './loader';
|
||||
import { CheckmarkIcon, CheckmarkTheme } from './checkmark';
|
||||
|
||||
const StatusWrapper = styled('div')`
|
||||
position: absolute;
|
||||
`;
|
||||
|
||||
const IndicatorWrapper = styled('div')`
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
`;
|
||||
|
||||
const enter = keyframes`
|
||||
from {
|
||||
transform: scale(0.6);
|
||||
opacity: 0.4;
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}`;
|
||||
|
||||
export const AnimatedIconWrapper = styled('div')`
|
||||
position: relative;
|
||||
transform: scale(0.6);
|
||||
opacity: 0.4;
|
||||
min-width: 20px;
|
||||
animation: ${enter} 0.3s 0.12s cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
||||
forwards;
|
||||
`;
|
||||
|
||||
export type IconThemes = Partial<{
|
||||
success: CheckmarkTheme;
|
||||
error: ErrorTheme;
|
||||
loading: LoaderTheme;
|
||||
}>;
|
||||
|
||||
export const ToastIcon: React.FC<{
|
||||
toast: Toast;
|
||||
}> = ({ toast }) => {
|
||||
const { icon, type, iconTheme } = toast;
|
||||
if (icon !== undefined) {
|
||||
if (typeof icon === 'string') {
|
||||
return <AnimatedIconWrapper>{icon}</AnimatedIconWrapper>;
|
||||
} else {
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'blank') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<IndicatorWrapper>
|
||||
<LoaderIcon {...iconTheme} />
|
||||
{type !== 'loading' && (
|
||||
<StatusWrapper>
|
||||
{type === 'error' ? (
|
||||
<ErrorIcon {...iconTheme} />
|
||||
) : (
|
||||
<CheckmarkIcon {...iconTheme} />
|
||||
)}
|
||||
</StatusWrapper>
|
||||
)}
|
||||
</IndicatorWrapper>
|
||||
);
|
||||
};
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
import { css, setup } from 'goober';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
resolveValue,
|
||||
ToasterProps,
|
||||
ToastPosition,
|
||||
ToastWrapperProps,
|
||||
} from '../core/types';
|
||||
import { useToaster } from '../core/use-toaster';
|
||||
import { prefersReducedMotion } from '../core/utils';
|
||||
import { ToastBar } from './toast-bar';
|
||||
|
||||
setup(React.createElement);
|
||||
|
||||
const ToastWrapper = ({
|
||||
id,
|
||||
className,
|
||||
style,
|
||||
onHeightUpdate,
|
||||
children,
|
||||
}: ToastWrapperProps) => {
|
||||
const ref = React.useCallback(
|
||||
(el: HTMLElement | null) => {
|
||||
if (el) {
|
||||
const updateHeight = () => {
|
||||
const height = el.getBoundingClientRect().height;
|
||||
onHeightUpdate(id, height);
|
||||
};
|
||||
updateHeight();
|
||||
new MutationObserver(updateHeight).observe(el, {
|
||||
subtree: true,
|
||||
childList: true,
|
||||
characterData: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
[id, onHeightUpdate]
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={ref} className={className} style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getPositionStyle = (
|
||||
position: ToastPosition,
|
||||
offset: number
|
||||
): React.CSSProperties => {
|
||||
const top = position.includes('top');
|
||||
const verticalStyle: React.CSSProperties = top ? { top: 0 } : { bottom: 0 };
|
||||
const horizontalStyle: React.CSSProperties = position.includes('center')
|
||||
? {
|
||||
justifyContent: 'center',
|
||||
}
|
||||
: position.includes('right')
|
||||
? {
|
||||
justifyContent: 'flex-end',
|
||||
}
|
||||
: {};
|
||||
return {
|
||||
left: 0,
|
||||
right: 0,
|
||||
display: 'flex',
|
||||
position: 'absolute',
|
||||
transition: prefersReducedMotion()
|
||||
? undefined
|
||||
: `all 230ms cubic-bezier(.21,1.02,.73,1)`,
|
||||
transform: `translateY(${offset * (top ? 1 : -1)}px)`,
|
||||
...verticalStyle,
|
||||
...horizontalStyle,
|
||||
};
|
||||
};
|
||||
|
||||
const activeClass = css`
|
||||
z-index: 9999;
|
||||
> * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
const DEFAULT_OFFSET = 16;
|
||||
|
||||
export const Toaster: React.FC<ToasterProps> = ({
|
||||
reverseOrder,
|
||||
position = 'top-center',
|
||||
toastOptions,
|
||||
gutter,
|
||||
children,
|
||||
toasterId,
|
||||
containerStyle,
|
||||
containerClassName,
|
||||
}) => {
|
||||
const { toasts, handlers } = useToaster(toastOptions, toasterId);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-rht-toaster={toasterId || ''}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
zIndex: 9999,
|
||||
top: DEFAULT_OFFSET,
|
||||
left: DEFAULT_OFFSET,
|
||||
right: DEFAULT_OFFSET,
|
||||
bottom: DEFAULT_OFFSET,
|
||||
pointerEvents: 'none',
|
||||
...containerStyle,
|
||||
}}
|
||||
className={containerClassName}
|
||||
onMouseEnter={handlers.startPause}
|
||||
onMouseLeave={handlers.endPause}
|
||||
>
|
||||
{toasts.map((t) => {
|
||||
const toastPosition = t.position || position;
|
||||
const offset = handlers.calculateOffset(t, {
|
||||
reverseOrder,
|
||||
gutter,
|
||||
defaultPosition: position,
|
||||
});
|
||||
const positionStyle = getPositionStyle(toastPosition, offset);
|
||||
|
||||
return (
|
||||
<ToastWrapper
|
||||
id={t.id}
|
||||
key={t.id}
|
||||
onHeightUpdate={handlers.updateHeight}
|
||||
className={t.visible ? activeClass : ''}
|
||||
style={positionStyle}
|
||||
>
|
||||
{t.type === 'custom' ? (
|
||||
resolveValue(t.message, t)
|
||||
) : children ? (
|
||||
children(t)
|
||||
) : (
|
||||
<ToastBar toast={t} position={toastPosition} />
|
||||
)}
|
||||
</ToastWrapper>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { DefaultToastOptions, Toast, ToastType } from './types';
|
||||
|
||||
export const TOAST_EXPIRE_DISMISS_DELAY = 1000;
|
||||
export const TOAST_LIMIT = 20;
|
||||
export const DEFAULT_TOASTER_ID = 'default';
|
||||
|
||||
interface ToasterSettings {
|
||||
toastLimit: number;
|
||||
}
|
||||
|
||||
export enum ActionType {
|
||||
ADD_TOAST,
|
||||
UPDATE_TOAST,
|
||||
UPSERT_TOAST,
|
||||
DISMISS_TOAST,
|
||||
REMOVE_TOAST,
|
||||
START_PAUSE,
|
||||
END_PAUSE,
|
||||
}
|
||||
|
||||
export type Action =
|
||||
| {
|
||||
type: ActionType.ADD_TOAST;
|
||||
toast: Toast;
|
||||
}
|
||||
| {
|
||||
type: ActionType.UPSERT_TOAST;
|
||||
toast: Toast;
|
||||
}
|
||||
| {
|
||||
type: ActionType.UPDATE_TOAST;
|
||||
toast: Partial<Toast>;
|
||||
}
|
||||
| {
|
||||
type: ActionType.DISMISS_TOAST;
|
||||
toastId?: string;
|
||||
}
|
||||
| {
|
||||
type: ActionType.REMOVE_TOAST;
|
||||
toastId?: string;
|
||||
}
|
||||
| {
|
||||
type: ActionType.START_PAUSE;
|
||||
time: number;
|
||||
}
|
||||
| {
|
||||
type: ActionType.END_PAUSE;
|
||||
time: number;
|
||||
};
|
||||
|
||||
interface ToasterState {
|
||||
toasts: Toast[];
|
||||
settings: ToasterSettings;
|
||||
pausedAt: number | undefined;
|
||||
}
|
||||
|
||||
interface State {
|
||||
[toasterId: string]: ToasterState;
|
||||
}
|
||||
|
||||
export const reducer = (state: ToasterState, action: Action): ToasterState => {
|
||||
const { toastLimit } = state.settings;
|
||||
|
||||
switch (action.type) {
|
||||
case ActionType.ADD_TOAST:
|
||||
return {
|
||||
...state,
|
||||
toasts: [action.toast, ...state.toasts].slice(0, toastLimit),
|
||||
};
|
||||
|
||||
case ActionType.UPDATE_TOAST:
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
||||
),
|
||||
};
|
||||
|
||||
case ActionType.UPSERT_TOAST:
|
||||
const { toast } = action;
|
||||
return reducer(state, {
|
||||
type: state.toasts.find((t) => t.id === toast.id)
|
||||
? ActionType.UPDATE_TOAST
|
||||
: ActionType.ADD_TOAST,
|
||||
toast,
|
||||
});
|
||||
|
||||
case ActionType.DISMISS_TOAST:
|
||||
const { toastId } = action;
|
||||
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === toastId || toastId === undefined
|
||||
? {
|
||||
...t,
|
||||
dismissed: true,
|
||||
visible: false,
|
||||
}
|
||||
: t
|
||||
),
|
||||
};
|
||||
case ActionType.REMOVE_TOAST:
|
||||
if (action.toastId === undefined) {
|
||||
return {
|
||||
...state,
|
||||
toasts: [],
|
||||
};
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
||||
};
|
||||
|
||||
case ActionType.START_PAUSE:
|
||||
return {
|
||||
...state,
|
||||
pausedAt: action.time,
|
||||
};
|
||||
|
||||
case ActionType.END_PAUSE:
|
||||
const diff = action.time - (state.pausedAt || 0);
|
||||
|
||||
return {
|
||||
...state,
|
||||
pausedAt: undefined,
|
||||
toasts: state.toasts.map((t) => ({
|
||||
...t,
|
||||
pauseDuration: t.pauseDuration + diff,
|
||||
})),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const listeners: Array<
|
||||
[toasterId: string, reducer: (state: ToasterState) => void]
|
||||
> = [];
|
||||
|
||||
const defaultToasterState: ToasterState = {
|
||||
toasts: [],
|
||||
pausedAt: undefined,
|
||||
settings: {
|
||||
toastLimit: TOAST_LIMIT,
|
||||
},
|
||||
};
|
||||
let memoryState: State = {};
|
||||
|
||||
export const dispatch = (action: Action, toasterId = DEFAULT_TOASTER_ID) => {
|
||||
memoryState[toasterId] = reducer(
|
||||
memoryState[toasterId] || defaultToasterState,
|
||||
action
|
||||
);
|
||||
listeners.forEach(([id, listener]) => {
|
||||
if (id === toasterId) {
|
||||
listener(memoryState[toasterId]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const dispatchAll = (action: Action) =>
|
||||
Object.keys(memoryState).forEach((toasterId) => dispatch(action, toasterId));
|
||||
|
||||
export const getToasterIdFromToastId = (toastId: string) =>
|
||||
Object.keys(memoryState).find((toasterId) =>
|
||||
memoryState[toasterId].toasts.some((t) => t.id === toastId)
|
||||
);
|
||||
|
||||
export const createDispatch =
|
||||
(toasterId = DEFAULT_TOASTER_ID) =>
|
||||
(action: Action) => {
|
||||
dispatch(action, toasterId);
|
||||
};
|
||||
|
||||
export const defaultTimeouts: {
|
||||
[key in ToastType]: number;
|
||||
} = {
|
||||
blank: 4000,
|
||||
error: 4000,
|
||||
success: 2000,
|
||||
loading: Infinity,
|
||||
custom: 4000,
|
||||
};
|
||||
|
||||
export const useStore = (
|
||||
toastOptions: DefaultToastOptions = {},
|
||||
toasterId: string = DEFAULT_TOASTER_ID
|
||||
): ToasterState => {
|
||||
const [state, setState] = useState<ToasterState>(
|
||||
memoryState[toasterId] || defaultToasterState
|
||||
);
|
||||
const initial = useRef(memoryState[toasterId]);
|
||||
|
||||
// TODO: Switch to useSyncExternalStore when targeting React 18+
|
||||
useEffect(() => {
|
||||
if (initial.current !== memoryState[toasterId]) {
|
||||
setState(memoryState[toasterId]);
|
||||
}
|
||||
listeners.push([toasterId, setState]);
|
||||
return () => {
|
||||
const index = listeners.findIndex(([id]) => id === toasterId);
|
||||
if (index > -1) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}, [toasterId]);
|
||||
|
||||
const mergedToasts = state.toasts.map((t) => ({
|
||||
...toastOptions,
|
||||
...toastOptions[t.type],
|
||||
...t,
|
||||
removeDelay:
|
||||
t.removeDelay ||
|
||||
toastOptions[t.type]?.removeDelay ||
|
||||
toastOptions?.removeDelay,
|
||||
duration:
|
||||
t.duration ||
|
||||
toastOptions[t.type]?.duration ||
|
||||
toastOptions?.duration ||
|
||||
defaultTimeouts[t.type],
|
||||
style: {
|
||||
...toastOptions.style,
|
||||
...toastOptions[t.type]?.style,
|
||||
...t.style,
|
||||
},
|
||||
}));
|
||||
|
||||
return {
|
||||
...state,
|
||||
toasts: mergedToasts,
|
||||
};
|
||||
};
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
import {
|
||||
Renderable,
|
||||
Toast,
|
||||
ToastOptions,
|
||||
ToastType,
|
||||
DefaultToastOptions,
|
||||
ValueOrFunction,
|
||||
resolveValue,
|
||||
} from './types';
|
||||
import { genId } from './utils';
|
||||
import {
|
||||
createDispatch,
|
||||
Action,
|
||||
ActionType,
|
||||
dispatchAll,
|
||||
getToasterIdFromToastId,
|
||||
} from './store';
|
||||
|
||||
type Message = ValueOrFunction<Renderable, Toast>;
|
||||
|
||||
type ToastHandler = (message: Message, options?: ToastOptions) => string;
|
||||
|
||||
const createToast = (
|
||||
message: Message,
|
||||
type: ToastType = 'blank',
|
||||
opts?: ToastOptions
|
||||
): Toast => ({
|
||||
createdAt: Date.now(),
|
||||
visible: true,
|
||||
dismissed: false,
|
||||
type,
|
||||
ariaProps: {
|
||||
role: 'status',
|
||||
'aria-live': 'polite',
|
||||
},
|
||||
message,
|
||||
pauseDuration: 0,
|
||||
...opts,
|
||||
id: opts?.id || genId(),
|
||||
});
|
||||
|
||||
const createHandler =
|
||||
(type?: ToastType): ToastHandler =>
|
||||
(message, options) => {
|
||||
const toast = createToast(message, type, options);
|
||||
|
||||
const dispatch = createDispatch(
|
||||
toast.toasterId || getToasterIdFromToastId(toast.id)
|
||||
);
|
||||
|
||||
dispatch({ type: ActionType.UPSERT_TOAST, toast });
|
||||
return toast.id;
|
||||
};
|
||||
|
||||
const toast = (message: Message, opts?: ToastOptions) =>
|
||||
createHandler('blank')(message, opts);
|
||||
|
||||
toast.error = createHandler('error');
|
||||
toast.success = createHandler('success');
|
||||
toast.loading = createHandler('loading');
|
||||
toast.custom = createHandler('custom');
|
||||
|
||||
/**
|
||||
* Dismisses the toast with the given id. If no id is given, dismisses all toasts.
|
||||
* The toast will transition out and then be removed from the DOM.
|
||||
* Applies to all toasters, except when a `toasterId` is given.
|
||||
*/
|
||||
toast.dismiss = (toastId?: string, toasterId?: string) => {
|
||||
const action: Action = {
|
||||
type: ActionType.DISMISS_TOAST,
|
||||
toastId,
|
||||
};
|
||||
|
||||
if (toasterId) {
|
||||
createDispatch(toasterId)(action);
|
||||
} else {
|
||||
dispatchAll(action);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dismisses all toasts.
|
||||
*/
|
||||
toast.dismissAll = (toasterId?: string) => toast.dismiss(undefined, toasterId);
|
||||
|
||||
/**
|
||||
* Removes the toast with the given id.
|
||||
* The toast will be removed from the DOM without any transition.
|
||||
*/
|
||||
toast.remove = (toastId?: string, toasterId?: string) => {
|
||||
const action: Action = {
|
||||
type: ActionType.REMOVE_TOAST,
|
||||
toastId,
|
||||
};
|
||||
if (toasterId) {
|
||||
createDispatch(toasterId)(action);
|
||||
} else {
|
||||
dispatchAll(action);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes all toasts.
|
||||
*/
|
||||
toast.removeAll = (toasterId?: string) => toast.remove(undefined, toasterId);
|
||||
|
||||
/**
|
||||
* Create a loading toast that will automatically updates with the promise.
|
||||
*/
|
||||
toast.promise = <T>(
|
||||
promise: Promise<T> | (() => Promise<T>),
|
||||
msgs: {
|
||||
loading: Renderable;
|
||||
success?: ValueOrFunction<Renderable, T>;
|
||||
error?: ValueOrFunction<Renderable, any>;
|
||||
},
|
||||
opts?: DefaultToastOptions
|
||||
) => {
|
||||
const id = toast.loading(msgs.loading, { ...opts, ...opts?.loading });
|
||||
|
||||
if (typeof promise === 'function') {
|
||||
promise = promise();
|
||||
}
|
||||
|
||||
promise
|
||||
.then((p) => {
|
||||
const successMessage = msgs.success
|
||||
? resolveValue(msgs.success, p)
|
||||
: undefined;
|
||||
|
||||
if (successMessage) {
|
||||
toast.success(successMessage, {
|
||||
id,
|
||||
...opts,
|
||||
...opts?.success,
|
||||
});
|
||||
} else {
|
||||
toast.dismiss(id);
|
||||
}
|
||||
return p;
|
||||
})
|
||||
.catch((e) => {
|
||||
const errorMessage = msgs.error ? resolveValue(msgs.error, e) : undefined;
|
||||
|
||||
if (errorMessage) {
|
||||
toast.error(errorMessage, {
|
||||
id,
|
||||
...opts,
|
||||
...opts?.error,
|
||||
});
|
||||
} else {
|
||||
toast.dismiss(id);
|
||||
}
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
export { toast };
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
import { CSSProperties } from 'react';
|
||||
|
||||
export type ToastType = 'success' | 'error' | 'loading' | 'blank' | 'custom';
|
||||
export type ToastPosition =
|
||||
| 'top-left'
|
||||
| 'top-center'
|
||||
| 'top-right'
|
||||
| 'bottom-left'
|
||||
| 'bottom-center'
|
||||
| 'bottom-right';
|
||||
|
||||
export type Renderable = React.ReactElement | string | null;
|
||||
|
||||
export interface IconTheme {
|
||||
primary: string;
|
||||
secondary: string;
|
||||
}
|
||||
|
||||
export type ValueFunction<TValue, TArg> = (arg: TArg) => TValue;
|
||||
export type ValueOrFunction<TValue, TArg> =
|
||||
| TValue
|
||||
| ValueFunction<TValue, TArg>;
|
||||
|
||||
const isFunction = <TValue, TArg>(
|
||||
valOrFunction: ValueOrFunction<TValue, TArg>
|
||||
): valOrFunction is ValueFunction<TValue, TArg> =>
|
||||
typeof valOrFunction === 'function';
|
||||
|
||||
export const resolveValue = <TValue, TArg>(
|
||||
valOrFunction: ValueOrFunction<TValue, TArg>,
|
||||
arg: TArg
|
||||
): TValue => (isFunction(valOrFunction) ? valOrFunction(arg) : valOrFunction);
|
||||
|
||||
export interface Toast {
|
||||
type: ToastType;
|
||||
id: string;
|
||||
toasterId?: string;
|
||||
message: ValueOrFunction<Renderable, Toast>;
|
||||
icon?: Renderable;
|
||||
duration?: number;
|
||||
pauseDuration: number;
|
||||
position?: ToastPosition;
|
||||
removeDelay?: number;
|
||||
|
||||
ariaProps: {
|
||||
role: 'status' | 'alert';
|
||||
'aria-live': 'assertive' | 'off' | 'polite';
|
||||
};
|
||||
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
iconTheme?: IconTheme;
|
||||
|
||||
createdAt: number;
|
||||
visible: boolean;
|
||||
dismissed: boolean;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export type ToastOptions = Partial<
|
||||
Pick<
|
||||
Toast,
|
||||
| 'id'
|
||||
| 'icon'
|
||||
| 'duration'
|
||||
| 'ariaProps'
|
||||
| 'className'
|
||||
| 'style'
|
||||
| 'position'
|
||||
| 'iconTheme'
|
||||
| 'toasterId'
|
||||
| 'removeDelay'
|
||||
>
|
||||
>;
|
||||
|
||||
export type DefaultToastOptions = ToastOptions & {
|
||||
[key in ToastType]?: ToastOptions;
|
||||
};
|
||||
|
||||
export interface ToasterProps {
|
||||
position?: ToastPosition;
|
||||
toastOptions?: DefaultToastOptions;
|
||||
reverseOrder?: boolean;
|
||||
gutter?: number;
|
||||
containerStyle?: React.CSSProperties;
|
||||
containerClassName?: string;
|
||||
toasterId?: string;
|
||||
children?: (toast: Toast) => React.ReactElement;
|
||||
}
|
||||
|
||||
export interface ToastWrapperProps {
|
||||
id: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
onHeightUpdate: (id: string, height: number) => void;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
import { useEffect, useCallback, useRef } from 'react';
|
||||
import { createDispatch, ActionType, useStore, dispatch } from './store';
|
||||
import { toast } from './toast';
|
||||
import { DefaultToastOptions, Toast, ToastPosition } from './types';
|
||||
|
||||
export const REMOVE_DELAY = 1000;
|
||||
|
||||
export const useToaster = (
|
||||
toastOptions?: DefaultToastOptions,
|
||||
toasterId: string = 'default'
|
||||
) => {
|
||||
const { toasts, pausedAt } = useStore(toastOptions, toasterId);
|
||||
const toastTimeouts = useRef(
|
||||
new Map<Toast['id'], ReturnType<typeof setTimeout>>()
|
||||
).current;
|
||||
|
||||
const addToRemoveQueue = useCallback(
|
||||
(toastId: string, removeDelay = REMOVE_DELAY) => {
|
||||
if (toastTimeouts.has(toastId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
toastTimeouts.delete(toastId);
|
||||
dispatch({
|
||||
type: ActionType.REMOVE_TOAST,
|
||||
toastId: toastId,
|
||||
});
|
||||
}, removeDelay);
|
||||
|
||||
toastTimeouts.set(toastId, timeout);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (pausedAt) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const timeouts = toasts.map((t) => {
|
||||
if (t.duration === Infinity) {
|
||||
return;
|
||||
}
|
||||
|
||||
const durationLeft =
|
||||
(t.duration || 0) + t.pauseDuration - (now - t.createdAt);
|
||||
|
||||
if (durationLeft < 0) {
|
||||
if (t.visible) {
|
||||
toast.dismiss(t.id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return setTimeout(() => toast.dismiss(t.id, toasterId), durationLeft);
|
||||
});
|
||||
|
||||
return () => {
|
||||
timeouts.forEach((timeout) => timeout && clearTimeout(timeout));
|
||||
};
|
||||
}, [toasts, pausedAt, toasterId]);
|
||||
|
||||
const dispatch = useCallback(createDispatch(toasterId), [toasterId]);
|
||||
|
||||
const startPause = useCallback(() => {
|
||||
dispatch({
|
||||
type: ActionType.START_PAUSE,
|
||||
time: Date.now(),
|
||||
});
|
||||
}, [dispatch]);
|
||||
|
||||
const updateHeight = useCallback(
|
||||
(toastId: string, height: number) => {
|
||||
dispatch({
|
||||
type: ActionType.UPDATE_TOAST,
|
||||
toast: { id: toastId, height },
|
||||
});
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const endPause = useCallback(() => {
|
||||
if (pausedAt) {
|
||||
dispatch({ type: ActionType.END_PAUSE, time: Date.now() });
|
||||
}
|
||||
}, [pausedAt, dispatch]);
|
||||
|
||||
const calculateOffset = useCallback(
|
||||
(
|
||||
toast: Toast,
|
||||
opts?: {
|
||||
reverseOrder?: boolean;
|
||||
gutter?: number;
|
||||
defaultPosition?: ToastPosition;
|
||||
}
|
||||
) => {
|
||||
const { reverseOrder = false, gutter = 8, defaultPosition } = opts || {};
|
||||
|
||||
const relevantToasts = toasts.filter(
|
||||
(t) =>
|
||||
(t.position || defaultPosition) ===
|
||||
(toast.position || defaultPosition) && t.height
|
||||
);
|
||||
const toastIndex = relevantToasts.findIndex((t) => t.id === toast.id);
|
||||
const toastsBefore = relevantToasts.filter(
|
||||
(toast, i) => i < toastIndex && toast.visible
|
||||
).length;
|
||||
|
||||
const offset = relevantToasts
|
||||
.filter((t) => t.visible)
|
||||
.slice(...(reverseOrder ? [toastsBefore + 1] : [0, toastsBefore]))
|
||||
.reduce((acc, t) => acc + (t.height || 0) + gutter, 0);
|
||||
|
||||
return offset;
|
||||
},
|
||||
[toasts]
|
||||
);
|
||||
|
||||
// Keep track of dismissed toasts and remove them after the delay
|
||||
useEffect(() => {
|
||||
toasts.forEach((toast) => {
|
||||
if (toast.dismissed) {
|
||||
addToRemoveQueue(toast.id, toast.removeDelay);
|
||||
} else {
|
||||
// If toast becomes visible again, remove it from the queue
|
||||
const timeout = toastTimeouts.get(toast.id);
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
toastTimeouts.delete(toast.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [toasts, addToRemoveQueue]);
|
||||
|
||||
return {
|
||||
toasts,
|
||||
handlers: {
|
||||
updateHeight,
|
||||
startPause,
|
||||
endPause,
|
||||
calculateOffset,
|
||||
},
|
||||
};
|
||||
};
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
export const genId = (() => {
|
||||
let count = 0;
|
||||
return () => {
|
||||
return (++count).toString();
|
||||
};
|
||||
})();
|
||||
|
||||
export const prefersReducedMotion = (() => {
|
||||
// Cache result
|
||||
let shouldReduceMotion: boolean | undefined = undefined;
|
||||
|
||||
return () => {
|
||||
if (shouldReduceMotion === undefined && typeof window !== 'undefined') {
|
||||
const mediaQuery = matchMedia('(prefers-reduced-motion: reduce)');
|
||||
shouldReduceMotion = !mediaQuery || mediaQuery.matches;
|
||||
}
|
||||
return shouldReduceMotion;
|
||||
};
|
||||
})();
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { toast } from '../core/toast';
|
||||
|
||||
export type {
|
||||
DefaultToastOptions,
|
||||
IconTheme,
|
||||
Renderable,
|
||||
Toast,
|
||||
ToasterProps,
|
||||
ToastOptions,
|
||||
ToastPosition,
|
||||
ToastType,
|
||||
ValueFunction,
|
||||
ValueOrFunction,
|
||||
} from '../core/types';
|
||||
|
||||
export { resolveValue } from '../core/types';
|
||||
export { useToaster } from '../core/use-toaster';
|
||||
export { useStore as useToasterStore } from '../core/store';
|
||||
|
||||
export { toast };
|
||||
export default toast;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { toast } from './core/toast';
|
||||
|
||||
export * from './headless';
|
||||
|
||||
export { ToastBar } from './components/toast-bar';
|
||||
export { ToastIcon } from './components/toast-icon';
|
||||
export { Toaster } from './components/toaster';
|
||||
export { CheckmarkIcon } from './components/checkmark';
|
||||
export { ErrorIcon } from './components/error';
|
||||
export { LoaderIcon } from './components/loader';
|
||||
|
||||
export { toast };
|
||||
export default toast;
|
||||
Reference in New Issue
Block a user