From aa3c0c60cf2d3c1440a2de10f662d8a9e2921bb1 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Thu, 24 Dec 2020 12:02:08 +0100 Subject: [PATCH 01/39] Lift position wrapper to Toaster --- src/components/toast-bar.tsx | 66 +++++++----------------------------- src/components/toaster.tsx | 59 ++++++++++++++++++++++++++------ 2 files changed, 61 insertions(+), 64 deletions(-) diff --git a/src/components/toast-bar.tsx b/src/components/toast-bar.tsx index 335df70..a9edfc4 100644 --- a/src/components/toast-bar.tsx +++ b/src/components/toast-bar.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { useCallback } from 'react'; -import { styled, keyframes, CSSAttribute } from 'goober'; +import { styled, keyframes } from 'goober'; import { Toast, ToastPosition, resolveValueOrFunction } from '../core/types'; import { Indicator } from './indicator'; @@ -41,42 +41,11 @@ const Message = styled('div')` interface ToastBarProps { toast: Toast; - offset: number; onHeight: (height: number) => void; position: ToastPosition; } -const getPositionStyle = ( - position: ToastPosition, - offset: number -): React.CSSProperties => { - const top = position.includes('top'); - const verticalStyle = top ? { top: 0 } : { bottom: 0 }; - - const horizontalStyle: CSSAttribute = position.includes('left') - ? { - left: 0, - } - : position.includes('right') - ? { - right: 0, - } - : { - left: 0, - pointerEvents: 'none', - right: 0, - justifyContent: 'center', - }; - return { - position: 'fixed', - transition: 'all 230ms cubic-bezier(.21,1.02,.73,1)', - transform: `translateY(${offset * (top ? 1 : -1)}px)`, - ...verticalStyle, - ...horizontalStyle, - }; -}; - const getAnimationStyle = ( position: ToastPosition, visible: boolean @@ -98,15 +67,14 @@ const getAnimationStyle = ( }; export const ToastBar: React.FC = React.memo( - ({ toast, position, ...props }) => { + ({ toast, position, onHeight }) => { const ref = useCallback((el: HTMLElement | null) => { if (el) { const boundingRect = el.getBoundingClientRect(); - props.onHeight(boundingRect.height); + onHeight(boundingRect.height); } }, []); - const positionStyle = getPositionStyle(position, props.offset); const animationStyle = toast?.height ? getAnimationStyle(position, toast.visible) : { opacity: 0 }; @@ -125,27 +93,19 @@ export const ToastBar: React.FC = React.memo( }; return ( -
- - {renderIcon()} - - {resolveValueOrFunction(toast.message, toast)} - - -
+ {renderIcon()} + + {resolveValueOrFunction(toast.message, toast)} + + ); } ); diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index d6a99a8..cd90d94 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -1,13 +1,11 @@ import * as React from 'react'; -import { setup } from 'goober'; +import { CSSAttribute } from 'goober'; import CSS from 'csstype'; import { useToaster } from '../core/use-toaster'; import { ToastBar } from './toast-bar'; import { ToastPosition, DefaultToastOptions } from '../core/types'; -setup(React.createElement); - interface ToasterProps { position?: ToastPosition; reverseOrder?: boolean; @@ -16,6 +14,36 @@ interface ToasterProps { toastOptions?: DefaultToastOptions; } +const getPositionStyle = ( + position: ToastPosition, + offset: number +): React.CSSProperties => { + const top = position.includes('top'); + const verticalStyle: CSSAttribute = top ? { top: 0 } : { bottom: 0 }; + const horizontalStyle: CSSAttribute = position.includes('left') + ? { + left: 0, + } + : position.includes('right') + ? { + right: 0, + } + : { + left: 0, + pointerEvents: 'none', + right: 0, + justifyContent: 'center', + }; + return { + display: 'flex', + position: 'fixed', + transition: 'all 230ms cubic-bezier(.21,1.02,.73,1)', + transform: `translateY(${offset * (top ? 1 : -1)}px)`, + ...verticalStyle, + ...horizontalStyle, + }; +}; + export const Toaster: React.FC = ({ reverseOrder, position = 'top-center', @@ -35,16 +63,25 @@ export const Toaster: React.FC = ({ onMouseLeave={handlers.endPause} > {toasts.map((t) => { + const offset = handlers.calculateOffset(t.id, { + reverseOrder, + }); + const positionStyle = getPositionStyle(position, offset); + return ( - handlers.updateHeight(t.id, height)} - toast={t} - offset={handlers.calculateOffset(t.id, { - reverseOrder, - })} - position={position} - /> + style={{ + zIndex: t.visible ? 9999 : undefined, + ...positionStyle, + }} + > + handlers.updateHeight(t.id, height)} + toast={t} + position={position} + /> + ); })} From b43caa1edd96539680a904776a1c10c1b54973a0 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Thu, 24 Dec 2020 12:10:55 +0100 Subject: [PATCH 02/39] Add missing goober setup --- src/components/toaster.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index cd90d94..87dee0b 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -1,18 +1,12 @@ import * as React from 'react'; -import { CSSAttribute } from 'goober'; +import { CSSAttribute, setup } from 'goober'; import CSS from 'csstype'; import { useToaster } from '../core/use-toaster'; import { ToastBar } from './toast-bar'; import { ToastPosition, DefaultToastOptions } from '../core/types'; -interface ToasterProps { - position?: ToastPosition; - reverseOrder?: boolean; - containerStyle?: CSS.Properties; - - toastOptions?: DefaultToastOptions; -} +setup(React.createElement); const getPositionStyle = ( position: ToastPosition, @@ -44,6 +38,14 @@ const getPositionStyle = ( }; }; +interface ToasterProps { + position?: ToastPosition; + reverseOrder?: boolean; + containerStyle?: CSS.Properties; + + toastOptions?: DefaultToastOptions; +} + export const Toaster: React.FC = ({ reverseOrder, position = 'top-center', From 633cb6f0b13a0098a7415c9bf6bbfa2e9ece9c39 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Tue, 2 Mar 2021 20:17:49 +0100 Subject: [PATCH 03/39] Increase default duration from loading toasts to Infinity Fixes #45 --- site/pages/docs/toast.mdx | 2 +- src/core/store.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/pages/docs/toast.mdx b/site/pages/docs/toast.mdx index 6536bad..60bf039 100644 --- a/site/pages/docs/toast.mdx +++ b/site/pages/docs/toast.mdx @@ -119,7 +119,7 @@ Every type has their own duration. You can overwrite them `duration` with the to | `blank` | 4000 | | `error` | 4000 | | `success` | 2000 | -| `loading` | 30000 | +| `loading` | Infinity | ### Dismiss toast programmatically diff --git a/src/core/store.ts b/src/core/store.ts index 1639fdd..515b169 100644 --- a/src/core/store.ts +++ b/src/core/store.ts @@ -131,7 +131,7 @@ const defaultTimeouts: { blank: 4000, error: 4000, success: 2000, - loading: 30000, + loading: Infinity, }; export const useStore = (toastOptions: DefaultToastOptions = {}): State => { From 06e271c594129423411379814b0c53e8d949f2e2 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 14 Mar 2021 21:37:12 +0100 Subject: [PATCH 04/39] Move height ref to --- src/components/toast-bar.tsx | 16 +--------------- src/components/toaster.tsx | 15 ++++++++++----- src/core/utils.ts | 11 +++++++++++ 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/components/toast-bar.tsx b/src/components/toast-bar.tsx index 5cc9bba..68af70b 100644 --- a/src/components/toast-bar.tsx +++ b/src/components/toast-bar.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { useCallback } from 'react'; import { styled, keyframes } from 'goober'; import { Toast, ToastPosition, resolveValueOrFunction } from '../core/types'; @@ -25,7 +24,6 @@ const ToastBarBase = styled('div', React.forwardRef)` 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; - margin: 16px; pointer-events: auto; padding: 8px 10px; border-radius: 8px; @@ -41,8 +39,6 @@ const Message = styled('div')` interface ToastBarProps { toast: Toast; - onHeight: (height: number) => void; - position: ToastPosition; } @@ -67,16 +63,7 @@ const getAnimationStyle = ( }; export const ToastBar: React.FC = React.memo( - ({ toast, position, onHeight }) => { - const ref = useCallback((el: HTMLElement | null) => { - if (el) { - setTimeout(() => { - const boundingRect = el.getBoundingClientRect(); - onHeight(boundingRect.height); - }); - } - }, []); - + ({ toast, position }) => { const animationStyle = toast?.height ? getAnimationStyle(position, toast.visible) : { opacity: 0 }; @@ -96,7 +83,6 @@ export const ToastBar: React.FC = React.memo( return ( = ({ }); const positionStyle = getPositionStyle(position, offset); + const ref = t.height + ? undefined + : createRectRef((rect) => { + handlers.updateHeight(t.id, rect.height); + }); + return (
- handlers.updateHeight(t.id, height)} - toast={t} - position={position} - /> +
); })} diff --git a/src/core/utils.ts b/src/core/utils.ts index fdaca37..5393032 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -4,3 +4,14 @@ export const genId = (() => { return (++count).toString(); }; })(); + +export const createRectRef = (onRect: (rect: DOMRect) => void) => ( + el: HTMLElement | null +) => { + if (el) { + setTimeout(() => { + const boundingRect = el.getBoundingClientRect(); + onRect(boundingRect); + }); + } +}; From 781b16ff0dc899f67bb7d2b3448ee7a7fe2eed3d Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 21 Mar 2021 18:59:22 +0100 Subject: [PATCH 05/39] Change toast positioning to absolute This allows you to overwrite position fixed, making it possible to position the Toaster relative Fixes #60 --- src/components/toaster.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index e98b2d1..58ab33f 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -31,7 +31,7 @@ const getPositionStyle = ( }; return { display: 'flex', - position: 'fixed', + position: 'absolute', transition: 'all 230ms cubic-bezier(.21,1.02,.73,1)', transform: `translateY(${offset * (top ? 1 : -1)}px)`, ...verticalStyle, @@ -58,8 +58,13 @@ export const Toaster: React.FC = ({ return (
= ({ key={t.id} style={{ zIndex: t.visible ? 9999 : undefined, - margin: 16, ...positionStyle, }} > From 396db8b8f6057b31bd5c99f308764e00a9d31de6 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 21 Mar 2021 19:18:21 +0100 Subject: [PATCH 06/39] Improve animations and exit positioning - Hidden toasts now stay at their position - Use percentages instead of px for animation --- src/components/toast-bar.tsx | 6 +++--- src/core/use-toaster.ts | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/components/toast-bar.tsx b/src/components/toast-bar.tsx index 68af70b..40c5985 100644 --- a/src/components/toast-bar.tsx +++ b/src/components/toast-bar.tsx @@ -6,13 +6,13 @@ import { Indicator } from './indicator'; import { AnimatedIconWrapper } from './icon-wrapper'; const enterAnimation = (factor: number) => ` -0% {transform: translate3d(0,${factor * -80}px,0) scale(.6); opacity:.5;} +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 * -130}px,-1px) scale(.5); opacity:0;} +100% {transform: translate3d(0,${factor * -150}%,-1px) scale(.6); opacity:0;} `; const ToastBarBase = styled('div', React.forwardRef)` @@ -57,7 +57,7 @@ const getAnimationStyle = ( : { animation: `${keyframes`${exitAnimation( factor - )}`} 0.8s forwards cubic-bezier(.06,.71,.55,1)`, + )}`} 0.4s forwards cubic-bezier(.06,.71,.55,1)`, pointerEvents: 'none', }; }; diff --git a/src/core/use-toaster.ts b/src/core/use-toaster.ts index c4f83dc..736c638 100644 --- a/src/core/use-toaster.ts +++ b/src/core/use-toaster.ts @@ -58,18 +58,19 @@ export const useToaster = (toastOptions?: DefaultToastOptions) => { opts?: { reverseOrder?: boolean; margin?: number } ) => { const { reverseOrder = false, margin = 8 } = opts || {}; - const index = visibleToasts.findIndex((toast) => toast.id === toastId); - const offset = - index !== -1 - ? visibleToasts - .slice(...(reverseOrder ? [index + 1] : [0, index])) - .reduce((acc, t) => acc + (t.height || 0) + margin, 0) - : 0; + const toastIndex = toasts.findIndex((toast) => toast.id === toastId); + const toastsBefore = toasts.filter( + (toast, i) => i < toastIndex && toast.visible + ).length; + + const offset = visibleToasts + .slice(...(reverseOrder ? [toastsBefore + 1] : [0, toastsBefore])) + .reduce((acc, t) => acc + (t.height || 0) + margin, 0); return offset; }, }), - [visibleToasts, pausedAt] + [toasts, visibleToasts, pausedAt] ); return { From d60adb6501af27dd0d3bf9fde75498dd3ce253a5 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 21 Mar 2021 19:26:48 +0100 Subject: [PATCH 07/39] Update goober & add `csstype` as dev dependency Closes #44 --- package-lock.json | 12 ++++++------ package.json | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5232397..8d40562 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4322,9 +4322,9 @@ } }, "csstype": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", - "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz", + "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==", "dev": true }, "cyclist": { @@ -6029,9 +6029,9 @@ "dev": true }, "goober": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/goober/-/goober-2.0.18.tgz", - "integrity": "sha512-XaYq9GqRkWHS0DFPQZQLhrKGWWbiz+ydiEbMRN8eGJZ7enDjmWnRv3RWn5RwdukjnHUA6b/AQcf4QWjellKOCw==" + "version": "2.0.35", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.0.35.tgz", + "integrity": "sha512-800lO2kBZg2UEpGDST2Jsx8FDaEaSuEXBYV6HlmPBX4HPaDCM/qeMtHM20a7ZmGxDjHONx5+CUKPLYTrcf7Lwg==" }, "graceful-fs": { "version": "4.2.4", diff --git a/package.json b/package.json index dd37f2a..208c56f 100644 --- a/package.json +++ b/package.json @@ -60,10 +60,11 @@ "@size-limit/preset-small-lib": "^4.9.1", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", + "csstype": "^3.0.7", "size-limit": "^4.9.1", "tsdx": "^0.14.1" }, "dependencies": { - "goober": "^2.0.15" + "goober": "^2.0.35" } } From 8f82fec390b9179dd5f0cd6bd3e0db8033c6ec64 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 21 Mar 2021 19:27:59 +0100 Subject: [PATCH 08/39] Breaking: No longer expose `dispatch` Closes #59 --- src/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.tsx b/src/index.tsx index 79629d3..2efc798 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -6,7 +6,6 @@ import { ToastPosition as _ToastPosition, } from './core/types'; export { useToaster } from './core/use-toaster'; -export { dispatch, ActionType } from './core/store'; export { ToastBar } from './components/toast-bar'; export { Toaster } from './components/toaster'; export { useStore as useToasterStore } from './core/store'; From 641369f967b0e427d072880bd6c75cb17044c180 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 21 Mar 2021 19:31:19 +0100 Subject: [PATCH 09/39] Set `sideEffects: false` in package.json This might help with #39 --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 208c56f..309eaf6 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "1.0.2", "author": "Timo Lins", "license": "MIT", + "sideEffects": false, "repository": "timolins/react-hot-toast", "keywords": [ "react", From 207bf66fd4f3aafc4e3afb18a42e7fd4cc6161cb Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 21 Mar 2021 19:40:05 +0100 Subject: [PATCH 10/39] Add `renderToast` option to Toatser - Allows you to replace default toast with custom component (Closes #13) --- src/components/toaster.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index 58ab33f..4861445 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -4,7 +4,7 @@ import CSS from 'csstype'; import { useToaster } from '../core/use-toaster'; import { ToastBar } from './toast-bar'; -import { ToastPosition, DefaultToastOptions } from '../core/types'; +import { ToastPosition, DefaultToastOptions, Toast } from '../core/types'; import { createRectRef } from '../core/utils'; setup(React.createElement); @@ -45,6 +45,7 @@ interface ToasterProps { containerStyle?: CSS.Properties; toastOptions?: DefaultToastOptions; + renderToast?: (toast: Toast) => JSX.Element; } export const Toaster: React.FC = ({ @@ -52,6 +53,7 @@ export const Toaster: React.FC = ({ position = 'top-center', containerStyle, toastOptions, + renderToast, }) => { const { toasts, handlers } = useToaster(toastOptions); @@ -91,7 +93,11 @@ export const Toaster: React.FC = ({ ...positionStyle, }} > - + {renderToast ? ( + renderToast(t) + ) : ( + + )}
); })} From 345aac0d38f867d730ebde0c80b89d9aa9ec624b Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 21 Mar 2021 20:26:38 +0100 Subject: [PATCH 11/39] Add support for per toast position - Breaking: Remove visibleToasts - Rename `margin` to `gutter` --- site/components/sections/toast-example.tsx | 12 ++++++++ site/pages/docs/use-toaster.mdx | 14 ++++++---- src/components/toaster.tsx | 8 ++++-- src/core/types.ts | 1 + src/core/use-toaster.ts | 32 ++++++++++++++-------- 5 files changed, 47 insertions(+), 20 deletions(-) diff --git a/site/components/sections/toast-example.tsx b/site/components/sections/toast-example.tsx index 4ac4478..a2ebb23 100644 --- a/site/components/sections/toast-example.tsx +++ b/site/components/sections/toast-example.tsx @@ -169,6 +169,18 @@ const examples: Array<{ }); }, }, + { + title: 'Custom Position', + emoji: '⬇️', + snippet: `toast.success('Always at the bottom.', { + position: "bottom-center" +})`, + action: () => { + toast.success('Always at the bottom.', { + position: 'bottom-center', + }); + }, + }, ]; export const ToastExample = () => { diff --git a/site/pages/docs/use-toaster.mdx b/site/pages/docs/use-toaster.mdx index 748da2b..0177feb 100644 --- a/site/pages/docs/use-toaster.mdx +++ b/site/pages/docs/use-toaster.mdx @@ -23,16 +23,18 @@ Headless mode is perfectly suited to add notifications to your React Native app. ```jsx const Notifications = () => { - const { visibleToasts, handlers } = useToaster(); + const { toasts, handlers } = useToaster(); const { startPause, endPause } = handlers; return (
- {visibleToasts.map((toast) => ( -
- {toast.message} -
- ))} + {toasts + .filter((toast) => toast.visible) + .map((toast) => ( +
+ {toast.message} +
+ ))}
); }; diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index 4861445..133fbcf 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -73,10 +73,12 @@ export const Toaster: React.FC = ({ onMouseLeave={handlers.endPause} > {toasts.map((t) => { - const offset = handlers.calculateOffset(t.id, { + const toastPosition = t.position || position; + const offset = handlers.calculateOffset(t, { reverseOrder, + defaultPosition: position, }); - const positionStyle = getPositionStyle(position, offset); + const positionStyle = getPositionStyle(toastPosition, offset); const ref = t.height ? undefined @@ -96,7 +98,7 @@ export const Toaster: React.FC = ({ {renderToast ? ( renderToast(t) ) : ( - + )} ); diff --git a/src/core/types.ts b/src/core/types.ts index e680861..16a1f5b 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -38,6 +38,7 @@ export interface Toast { icon?: Renderable; duration?: number; pauseDuration: number; + position?: ToastPosition; role: 'status' | 'alert'; ariaLive: 'assertive' | 'off' | 'polite'; diff --git a/src/core/use-toaster.ts b/src/core/use-toaster.ts index 736c638..73cf841 100644 --- a/src/core/use-toaster.ts +++ b/src/core/use-toaster.ts @@ -1,11 +1,10 @@ import { useEffect, useMemo } from 'react'; import { dispatch, ActionType, useStore } from './store'; import { toast } from './toast'; -import { DefaultToastOptions } from './types'; +import { DefaultToastOptions, Toast, ToastPosition } from './types'; export const useToaster = (toastOptions?: DefaultToastOptions) => { const { toasts, pausedAt } = useStore(toastOptions); - const visibleToasts = toasts.filter((t) => t.visible); useEffect(() => { if (pausedAt) { @@ -54,28 +53,39 @@ export const useToaster = (toastOptions?: DefaultToastOptions) => { toast: { id: toastId, height }, }), calculateOffset: ( - toastId: string, - opts?: { reverseOrder?: boolean; margin?: number } + toast: Toast, + opts?: { + reverseOrder?: boolean; + gutter?: number; + defaultPosition?: ToastPosition; + } ) => { - const { reverseOrder = false, margin = 8 } = opts || {}; - const toastIndex = toasts.findIndex((toast) => toast.id === toastId); - const toastsBefore = toasts.filter( + const { reverseOrder = false, gutter = 8, defaultPosition } = + opts || {}; + + const relevantToasts = toasts.filter( + (t) => + (t.position || defaultPosition) === + (toast.position || defaultPosition) + ); + const toastIndex = relevantToasts.findIndex((t) => t.id === toast.id); + const toastsBefore = relevantToasts.filter( (toast, i) => i < toastIndex && toast.visible ).length; - const offset = visibleToasts + const offset = relevantToasts + .filter((t) => t.visible) .slice(...(reverseOrder ? [toastsBefore + 1] : [0, toastsBefore])) - .reduce((acc, t) => acc + (t.height || 0) + margin, 0); + .reduce((acc, t) => acc + (t.height || 0) + gutter, 0); return offset; }, }), - [toasts, visibleToasts, pausedAt] + [toasts, pausedAt] ); return { toasts, - visibleToasts, handlers, }; }; From 2be3b239537543f03b606bebde83e9e1fdbc76bf Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 21 Mar 2021 20:31:59 +0100 Subject: [PATCH 12/39] Add `containerClassName` prop - Closes #43 --- src/components/toaster.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index 133fbcf..45852d9 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -43,6 +43,7 @@ interface ToasterProps { position?: ToastPosition; reverseOrder?: boolean; containerStyle?: CSS.Properties; + containerClassName?: string; toastOptions?: DefaultToastOptions; renderToast?: (toast: Toast) => JSX.Element; @@ -53,6 +54,7 @@ export const Toaster: React.FC = ({ position = 'top-center', containerStyle, toastOptions, + containerClassName, renderToast, }) => { const { toasts, handlers } = useToaster(toastOptions); @@ -69,6 +71,7 @@ export const Toaster: React.FC = ({ position: 'fixed', ...containerStyle, }} + className={containerClassName} onMouseEnter={handlers.startPause} onMouseLeave={handlers.endPause} > From e58d6a8690e3329dcf2cbd2d7ac3c156a2e7a5b9 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Mon, 22 Mar 2021 12:44:47 +0100 Subject: [PATCH 13/39] Use React.CSSProperties instead csstype --- src/components/toaster.tsx | 9 ++++----- src/core/types.ts | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index 45852d9..c37d70d 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; -import { CSSAttribute, setup } from 'goober'; -import CSS from 'csstype'; +import { setup } from 'goober'; import { useToaster } from '../core/use-toaster'; import { ToastBar } from './toast-bar'; @@ -14,8 +13,8 @@ const getPositionStyle = ( offset: number ): React.CSSProperties => { const top = position.includes('top'); - const verticalStyle: CSSAttribute = top ? { top: 0 } : { bottom: 0 }; - const horizontalStyle: CSSAttribute = position.includes('left') + const verticalStyle: React.CSSProperties = top ? { top: 0 } : { bottom: 0 }; + const horizontalStyle: React.CSSProperties = position.includes('left') ? { left: 0, } @@ -42,7 +41,7 @@ const getPositionStyle = ( interface ToasterProps { position?: ToastPosition; reverseOrder?: boolean; - containerStyle?: CSS.Properties; + containerStyle?: React.CSSProperties; containerClassName?: string; toastOptions?: DefaultToastOptions; diff --git a/src/core/types.ts b/src/core/types.ts index 16a1f5b..45fb72c 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -1,4 +1,4 @@ -import { Properties } from 'csstype'; +import { CSSProperties } from 'react'; export type ToastType = 'success' | 'error' | 'loading' | 'blank'; export type ToastPosition = @@ -43,7 +43,7 @@ export interface Toast { role: 'status' | 'alert'; ariaLive: 'assertive' | 'off' | 'polite'; - style?: Properties; + style?: CSSProperties; className?: string; iconTheme?: IconTheme; From b7509bd7cf0f8445e4f30e1977f2e5e92bb64554 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Mon, 22 Mar 2021 12:50:06 +0100 Subject: [PATCH 14/39] Add support for `toast.custom` - Render any JSX without additional styles --- src/components/toast-bar.tsx | 6 ++---- src/components/toaster.tsx | 11 +++++++++-- src/core/store.ts | 1 + src/core/toast.ts | 5 +++-- src/core/types.ts | 4 ++-- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/components/toast-bar.tsx b/src/components/toast-bar.tsx index 40c5985..aa07b3d 100644 --- a/src/components/toast-bar.tsx +++ b/src/components/toast-bar.tsx @@ -1,9 +1,7 @@ import * as React from 'react'; import { styled, keyframes } from 'goober'; -import { Toast, ToastPosition, resolveValueOrFunction } from '../core/types'; -import { Indicator } from './indicator'; -import { AnimatedIconWrapper } from './icon-wrapper'; +import { Toast, ToastPosition, resolveValue } from '../core/types'; const enterAnimation = (factor: number) => ` 0% {transform: translate3d(0,${factor * -200}%,0) scale(.6); opacity:.5;} @@ -91,7 +89,7 @@ export const ToastBar: React.FC = React.memo( > {renderIcon()} - {resolveValueOrFunction(toast.message, toast)} + {resolveValue(toast.message, toast)}
); diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index c37d70d..9aa2e0a 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -3,7 +3,12 @@ import { setup } from 'goober'; import { useToaster } from '../core/use-toaster'; import { ToastBar } from './toast-bar'; -import { ToastPosition, DefaultToastOptions, Toast } from '../core/types'; +import { + ToastPosition, + DefaultToastOptions, + Toast, + resolveValue, +} from '../core/types'; import { createRectRef } from '../core/utils'; setup(React.createElement); @@ -97,7 +102,9 @@ export const Toaster: React.FC = ({ ...positionStyle, }} > - {renderToast ? ( + {t.type === 'custom' ? ( + resolveValue(t.message, t) + ) : renderToast ? ( renderToast(t) ) : ( diff --git a/src/core/store.ts b/src/core/store.ts index e741df6..b488920 100644 --- a/src/core/store.ts +++ b/src/core/store.ts @@ -173,6 +173,7 @@ const defaultTimeouts: { error: 4000, success: 2000, loading: Infinity, + custom: 30000, }; export const useStore = (toastOptions: DefaultToastOptions = {}): State => { diff --git a/src/core/toast.ts b/src/core/toast.ts index 34a17ef..f08f0d2 100644 --- a/src/core/toast.ts +++ b/src/core/toast.ts @@ -45,6 +45,7 @@ const toast = (message: Message, opts?: ToastOptions) => toast.error = createHandler('error'); toast.success = createHandler('success'); toast.loading = createHandler('loading'); +toast.custom = createHandler('custom'); toast.dismiss = (toastId?: string) => { dispatch({ @@ -69,7 +70,7 @@ toast.promise = ( promise .then((p) => { - toast.success(resolveValueOrFunction(msgs.success, p), { + toast.success(resolveValue(msgs.success, p), { id, ...opts, ...opts?.success, @@ -77,7 +78,7 @@ toast.promise = ( return p; }) .catch((e) => { - toast.error(resolveValueOrFunction(msgs.error, e), { + toast.error(resolveValue(msgs.error, e), { id, ...opts, ...opts?.error, diff --git a/src/core/types.ts b/src/core/types.ts index 45fb72c..0373569 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -9,7 +9,7 @@ export type ToastPosition = | 'bottom-center' | 'bottom-right'; -export type Renderable = JSX.Element | string | number | null; +export type Renderable = JSX.Element | string | null; export interface IconTheme { primary: string; @@ -26,7 +26,7 @@ const isFunction = ( ): valOrFunction is ValueFunction => typeof valOrFunction === 'function'; -export const resolveValueOrFunction = ( +export const resolveValue = ( valOrFunction: ValueOrFunction, arg: TArg ): TValue => (isFunction(valOrFunction) ? valOrFunction(arg) : valOrFunction); From 25690e02e97050288672cabc6264663133674bfc Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Mon, 22 Mar 2021 12:57:54 +0100 Subject: [PATCH 15/39] Refactor `Indicator` to `ToastIcon` component - Also export resolveValue to render custom JSX content --- src/components/icon-wrapper.tsx | 22 --------- src/components/indicator.tsx | 52 ---------------------- src/components/toast-bar.tsx | 20 +++------ src/components/toast-icon.tsx | 79 +++++++++++++++++++++++++++++++++ src/index.tsx | 6 +-- 5 files changed, 86 insertions(+), 93 deletions(-) delete mode 100644 src/components/icon-wrapper.tsx delete mode 100644 src/components/indicator.tsx create mode 100644 src/components/toast-icon.tsx diff --git a/src/components/icon-wrapper.tsx b/src/components/icon-wrapper.tsx deleted file mode 100644 index 6716ac3..0000000 --- a/src/components/icon-wrapper.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { styled, keyframes } from 'goober'; - -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; -`; diff --git a/src/components/indicator.tsx b/src/components/indicator.tsx deleted file mode 100644 index 4c3a980..0000000 --- a/src/components/indicator.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from 'react'; -import { styled } from 'goober'; - -import { ToastType, IconTheme } 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; -`; - -export type IconThemes = Partial<{ - success: CheckmarkTheme; - error: ErrorTheme; - loading: LoaderTheme; -}>; - -interface IndicatorProps { - type: ToastType; - theme?: IconTheme; -} - -export const Indicator: React.FC = ({ type, theme }) => { - if (type === 'blank') { - return null; - } - - return ( - - - {type !== 'loading' && ( - - {type === 'error' ? ( - - ) : ( - - )} - - )} - - ); -}; diff --git a/src/components/toast-bar.tsx b/src/components/toast-bar.tsx index aa07b3d..e38fd02 100644 --- a/src/components/toast-bar.tsx +++ b/src/components/toast-bar.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { styled, keyframes } from 'goober'; import { Toast, ToastPosition, resolveValue } from '../core/types'; +import { ToastIcon } from './toast-icon'; const enterAnimation = (factor: number) => ` 0% {transform: translate3d(0,${factor * -200}%,0) scale(.6); opacity:.5;} @@ -38,6 +39,7 @@ const Message = styled('div')` interface ToastBarProps { toast: Toast; position: ToastPosition; + style?: React.CSSProperties; } const getAnimationStyle = ( @@ -61,33 +63,21 @@ const getAnimationStyle = ( }; export const ToastBar: React.FC = React.memo( - ({ toast, position }) => { + ({ toast, position, style }) => { const animationStyle = toast?.height ? getAnimationStyle(position, toast.visible) : { opacity: 0 }; - const renderIcon = () => { - const { icon, type, iconTheme } = toast; - if (icon !== undefined) { - if (typeof icon === 'string') { - return {icon}; - } else { - return icon; - } - } - - return ; - }; - return ( - {renderIcon()} + {resolveValue(toast.message, toast)} diff --git a/src/components/toast-icon.tsx b/src/components/toast-icon.tsx new file mode 100644 index 0000000..0726a6c --- /dev/null +++ b/src/components/toast-icon.tsx @@ -0,0 +1,79 @@ +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 {icon}; + } else { + return icon; + } + } + + if (type === 'blank') { + return null; + } + + return ( + + + {type !== 'loading' && ( + + {type === 'error' ? ( + + ) : ( + + )} + + )} + + ); +}; diff --git a/src/index.tsx b/src/index.tsx index 2efc798..01bb29d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -7,12 +7,10 @@ import { } from './core/types'; export { useToaster } from './core/use-toaster'; export { ToastBar } from './components/toast-bar'; +export { ToastIcon } from './components/toast-icon'; export { Toaster } from './components/toaster'; export { useStore as useToasterStore } from './core/store'; - -export { CheckmarkIcon } from './components/checkmark'; -export { ErrorIcon } from './components/error'; -export { LoaderIcon } from './components/loader'; +export { resolveValue } from './core/types'; export type ToastOptions = _ToastOptions; export type ToastPosition = _ToastPosition; From 664bcbf510b075f70193ef6deb5c0b37c3c81569 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Mon, 22 Mar 2021 16:06:02 +0100 Subject: [PATCH 16/39] Fix invalid types/imports --- src/core/toast.ts | 2 +- src/core/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/toast.ts b/src/core/toast.ts index f08f0d2..b9be8bf 100644 --- a/src/core/toast.ts +++ b/src/core/toast.ts @@ -5,7 +5,7 @@ import { ToastType, DefaultToastOptions, ValueOrFunction, - resolveValueOrFunction, + resolveValue, } from './types'; import { genId } from './utils'; import { dispatch, ActionType } from './store'; diff --git a/src/core/types.ts b/src/core/types.ts index 0373569..472f4ff 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -1,6 +1,6 @@ import { CSSProperties } from 'react'; -export type ToastType = 'success' | 'error' | 'loading' | 'blank'; +export type ToastType = 'success' | 'error' | 'loading' | 'blank' | 'custom'; export type ToastPosition = | 'top-left' | 'top-center' From 80e936f79d661dfcf23d1738ae9614b058c26864 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Mon, 22 Mar 2021 17:28:38 +0100 Subject: [PATCH 17/39] Allow position to be configured per toast --- src/core/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/types.ts b/src/core/types.ts index 472f4ff..bd4780a 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -62,6 +62,7 @@ export type ToastOptions = Partial< | 'ariaLive' | 'className' | 'style' + | 'position' | 'iconTheme' > >; From 6639eea25931d66dbefcf5c78360a87039d56f97 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Mon, 22 Mar 2021 18:14:35 +0100 Subject: [PATCH 18/39] Use dynamic year in footer --- site/components/sections/footer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/components/sections/footer.tsx b/site/components/sections/footer.tsx index 43afbee..fe73bf0 100644 --- a/site/components/sections/footer.tsx +++ b/site/components/sections/footer.tsx @@ -19,7 +19,7 @@ export function Footer() {
- © 2020 react-hot-toast + © {new Date().getFullYear()} react-hot-toast {' · '} Built by From b650ea039bb1629da57251d0652f3ff497d36623 Mon Sep 17 00:00:00 2001 From: Dylan Marriott Date: Thu, 6 May 2021 00:04:01 +0200 Subject: [PATCH 19/39] CSS fix for IE --- src/components/toast-bar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/toast-bar.tsx b/src/components/toast-bar.tsx index 9cae15a..cc4d8e5 100644 --- a/src/components/toast-bar.tsx +++ b/src/components/toast-bar.tsx @@ -36,7 +36,7 @@ const Message = styled('div')` justify-content: center; margin: 4px 10px; color: inherit; - flex: 1; + flex: 1 1 auto; `; interface ToastBarProps { From 036301165450b1df584c63f64e41e34014110c5d Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 16 May 2021 19:03:23 +0200 Subject: [PATCH 20/39] Add reduce motion support Closes #34 --- src/components/toast-bar.tsx | 13 +++++++++---- src/components/toaster.tsx | 6 ++++-- src/core/store.ts | 18 +++++++++++------- src/core/use-toaster.ts | 2 +- src/core/utils.ts | 13 +++++++++++++ 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/components/toast-bar.tsx b/src/components/toast-bar.tsx index e38fd02..a626f59 100644 --- a/src/components/toast-bar.tsx +++ b/src/components/toast-bar.tsx @@ -3,6 +3,7 @@ import { styled, keyframes } from 'goober'; import { Toast, ToastPosition, resolveValue } 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;} @@ -38,7 +39,7 @@ const Message = styled('div')` interface ToastBarProps { toast: Toast; - position: ToastPosition; + position?: ToastPosition; style?: React.CSSProperties; } @@ -63,9 +64,13 @@ const getAnimationStyle = ( }; export const ToastBar: React.FC = React.memo( - ({ toast, position, style }) => { - const animationStyle = toast?.height - ? getAnimationStyle(position, toast.visible) + const animationStyle: React.CSSProperties = toast?.height + ? prefersReducedMotion() + ? {} + : getAnimationStyle( + toast.position || position || 'top-center', + toast.visible + ) : { opacity: 0 }; return ( diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index 9aa2e0a..493625d 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -9,7 +9,7 @@ import { Toast, resolveValue, } from '../core/types'; -import { createRectRef } from '../core/utils'; +import { createRectRef, prefersReducedMotion } from '../core/utils'; setup(React.createElement); @@ -36,7 +36,9 @@ const getPositionStyle = ( return { display: 'flex', position: 'absolute', - transition: 'all 230ms cubic-bezier(.21,1.02,.73,1)', + transition: `all ${ + prefersReducedMotion() ? 0 : 230 + }ms cubic-bezier(.21,1.02,.73,1)`, transform: `translateY(${offset * (top ? 1 : -1)}px)`, ...verticalStyle, ...horizontalStyle, diff --git a/src/core/store.ts b/src/core/store.ts index b488920..643f630 100644 --- a/src/core/store.ts +++ b/src/core/store.ts @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; import { DefaultToastOptions, Toast, ToastType } from './types'; +import { prefersReducedMotion } from './utils'; const TOAST_LIMIT = 20; @@ -55,13 +56,16 @@ const addToRemoveQueue = (toastId: string) => { return; } - const timeout = setTimeout(() => { - toastTimeouts.delete(toastId); - dispatch({ - type: ActionType.REMOVE_TOAST, - toastId: toastId, - }); - }, 1000); + const timeout = setTimeout( + () => { + toastTimeouts.delete(toastId); + dispatch({ + type: ActionType.REMOVE_TOAST, + toastId: toastId, + }); + }, + prefersReducedMotion() ? 0 : 1000 + ); toastTimeouts.set(toastId, timeout); }; diff --git a/src/core/use-toaster.ts b/src/core/use-toaster.ts index 73cf841..ceb9d32 100644 --- a/src/core/use-toaster.ts +++ b/src/core/use-toaster.ts @@ -66,7 +66,7 @@ export const useToaster = (toastOptions?: DefaultToastOptions) => { const relevantToasts = toasts.filter( (t) => (t.position || defaultPosition) === - (toast.position || defaultPosition) + (toast.position || defaultPosition) && t.height ); const toastIndex = relevantToasts.findIndex((t) => t.id === toast.id); const toastsBefore = relevantToasts.filter( diff --git a/src/core/utils.ts b/src/core/utils.ts index 5393032..098159a 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -15,3 +15,16 @@ export const createRectRef = (onRect: (rect: DOMRect) => void) => ( }); } }; + +export const prefersReducedMotion = (() => { + // Cache result + let shouldReduceMotion: boolean | undefined = undefined; + + return () => { + if (shouldReduceMotion === undefined) { + const mediaQuery = matchMedia('(prefers-reduced-motion: reduce)'); + shouldReduceMotion = !mediaQuery || mediaQuery.matches; + } + return shouldReduceMotion; + }; +})(); From f1d367c3fa118f8dfbf6425156aa499b5de47bfb Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 16 May 2021 19:05:44 +0200 Subject: [PATCH 21/39] Scope aria props and add render function support --- src/components/toast-bar.tsx | 25 +++++++++++++++++++------ src/core/toast.ts | 6 ++++-- src/core/types.ts | 9 +++++---- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/components/toast-bar.tsx b/src/components/toast-bar.tsx index a626f59..441af5f 100644 --- a/src/components/toast-bar.tsx +++ b/src/components/toast-bar.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { styled, keyframes } from 'goober'; -import { Toast, ToastPosition, resolveValue } from '../core/types'; +import { Toast, ToastPosition, resolveValue, Renderable } from '../core/types'; import { ToastIcon } from './toast-icon'; import { prefersReducedMotion } from '../core/utils'; @@ -41,6 +41,10 @@ interface ToastBarProps { toast: Toast; position?: ToastPosition; style?: React.CSSProperties; + children?: (components: { + icon: Renderable; + message: Renderable; + }) => Renderable; } const getAnimationStyle = ( @@ -59,11 +63,11 @@ const getAnimationStyle = ( animation: `${keyframes`${exitAnimation( factor )}`} 0.4s forwards cubic-bezier(.06,.71,.55,1)`, - pointerEvents: 'none', }; }; export const ToastBar: React.FC = React.memo( + ({ toast, position, style, children }) => { const animationStyle: React.CSSProperties = toast?.height ? prefersReducedMotion() ? {} @@ -73,6 +77,13 @@ export const ToastBar: React.FC = React.memo( ) : { opacity: 0 }; + const icon = ; + const message = ( + + {resolveValue(toast.message, toast)} + + ); + return ( = React.memo( ...toast.style, }} > - - - {resolveValue(toast.message, toast)} - + {typeof children === 'function' + ? children({ + icon, + message, + }) + : [icon, message]} ); } diff --git a/src/core/toast.ts b/src/core/toast.ts index b9be8bf..ec04865 100644 --- a/src/core/toast.ts +++ b/src/core/toast.ts @@ -22,8 +22,10 @@ const createToast = ( createdAt: Date.now(), visible: true, type, - role: 'status', - ariaLive: 'polite', + ariaProps: { + role: 'status', + 'aria-live': 'polite', + }, message, pauseDuration: 0, ...opts, diff --git a/src/core/types.ts b/src/core/types.ts index bd4780a..4ea02e4 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -40,8 +40,10 @@ export interface Toast { pauseDuration: number; position?: ToastPosition; - role: 'status' | 'alert'; - ariaLive: 'assertive' | 'off' | 'polite'; + ariaProps: { + role: 'status' | 'alert'; + 'aria-live': 'assertive' | 'off' | 'polite'; + }; style?: CSSProperties; className?: string; @@ -58,8 +60,7 @@ export type ToastOptions = Partial< | 'id' | 'icon' | 'duration' - | 'role' - | 'ariaLive' + | 'ariaProps' | 'className' | 'style' | 'position' From 466825d5f2ff469e94e6142f0d2c3f56339ff00e Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 16 May 2021 19:06:21 +0200 Subject: [PATCH 22/39] Reduce custom toast duration to 4 seconds --- src/core/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/store.ts b/src/core/store.ts index 643f630..44359ef 100644 --- a/src/core/store.ts +++ b/src/core/store.ts @@ -177,7 +177,7 @@ const defaultTimeouts: { error: 4000, success: 2000, loading: Infinity, - custom: 30000, + custom: 4000, }; export const useStore = (toastOptions: DefaultToastOptions = {}): State => { From 273e29e2664aa0de9bd80743472c62bb398895f7 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 16 May 2021 19:07:20 +0200 Subject: [PATCH 23/39] Expose `gutter` prop --- src/components/toaster.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index 493625d..531e90e 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -48,6 +48,7 @@ const getPositionStyle = ( interface ToasterProps { position?: ToastPosition; reverseOrder?: boolean; + gutter?: number; containerStyle?: React.CSSProperties; containerClassName?: string; @@ -85,6 +86,7 @@ export const Toaster: React.FC = ({ const toastPosition = t.position || position; const offset = handlers.calculateOffset(t, { reverseOrder, + gutter, defaultPosition: position, }); const positionStyle = getPositionStyle(toastPosition, offset); From 9381c26994f87d634f56e741106f150bdfb19026 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 16 May 2021 19:08:58 +0200 Subject: [PATCH 24/39] Use flexbox for toast positioning --- src/components/toaster.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index 531e90e..0de032d 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -19,21 +19,18 @@ const getPositionStyle = ( ): React.CSSProperties => { const top = position.includes('top'); const verticalStyle: React.CSSProperties = top ? { top: 0 } : { bottom: 0 }; - const horizontalStyle: React.CSSProperties = position.includes('left') + const horizontalStyle: React.CSSProperties = position.includes('center') ? { - left: 0, + justifyContent: 'center', } : position.includes('right') ? { - right: 0, + justifyContent: 'flex-end', } - : { - left: 0, - pointerEvents: 'none', - right: 0, - justifyContent: 'center', - }; + : {}; return { + left: 0, + right: 0, display: 'flex', position: 'absolute', transition: `all ${ From ff9a762e134ff3304f63ae696d1563263ccfd0c1 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 16 May 2021 19:09:32 +0200 Subject: [PATCH 25/39] Extract default offset to variable --- src/components/toaster.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index 0de032d..fbe1c37 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -52,10 +52,12 @@ interface ToasterProps { toastOptions?: DefaultToastOptions; renderToast?: (toast: Toast) => JSX.Element; } +const DEFAULT_OFFSET = 16; export const Toaster: React.FC = ({ reverseOrder, position = 'top-center', + gutter, containerStyle, toastOptions, containerClassName, @@ -66,13 +68,13 @@ export const Toaster: React.FC = ({ return (
Date: Sun, 16 May 2021 19:12:30 +0200 Subject: [PATCH 26/39] Use children instead of render prop --- src/components/toaster.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index fbe1c37..f424f60 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -44,13 +44,12 @@ const getPositionStyle = ( interface ToasterProps { position?: ToastPosition; + toastOptions?: DefaultToastOptions; reverseOrder?: boolean; gutter?: number; containerStyle?: React.CSSProperties; containerClassName?: string; - - toastOptions?: DefaultToastOptions; - renderToast?: (toast: Toast) => JSX.Element; + children?: (toast: Toast) => JSX.Element; } const DEFAULT_OFFSET = 16; @@ -58,6 +57,7 @@ export const Toaster: React.FC = ({ reverseOrder, position = 'top-center', gutter, + children, containerStyle, toastOptions, containerClassName, @@ -107,8 +107,8 @@ export const Toaster: React.FC = ({ > {t.type === 'custom' ? ( resolveValue(t.message, t) - ) : renderToast ? ( - renderToast(t) + ) : children ? ( + children(t) ) : ( )} From 2036a3f1defa236e6af3a4792636aad93f477993 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 16 May 2021 19:14:28 +0200 Subject: [PATCH 27/39] Use activeClass for pointer events --- src/components/toaster.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/components/toaster.tsx b/src/components/toaster.tsx index f424f60..2f31b50 100644 --- a/src/components/toaster.tsx +++ b/src/components/toaster.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { setup } from 'goober'; +import { setup, css } from 'goober'; import { useToaster } from '../core/use-toaster'; import { ToastBar } from './toast-bar'; @@ -42,6 +42,13 @@ const getPositionStyle = ( }; }; +const activeClass = css` + z-index: 9999; + > * { + pointer-events: auto; + } +`; + interface ToasterProps { position?: ToastPosition; toastOptions?: DefaultToastOptions; @@ -51,17 +58,17 @@ interface ToasterProps { containerClassName?: string; children?: (toast: Toast) => JSX.Element; } + const DEFAULT_OFFSET = 16; export const Toaster: React.FC = ({ reverseOrder, position = 'top-center', + toastOptions, gutter, children, containerStyle, - toastOptions, containerClassName, - renderToast, }) => { const { toasts, handlers } = useToaster(toastOptions); @@ -99,11 +106,9 @@ export const Toaster: React.FC = ({ return (
{t.type === 'custom' ? ( resolveValue(t.message, t) From 03d75e9cedffc543c92d4553b3b4c3da020cdd8e Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 16 May 2021 19:14:42 +0200 Subject: [PATCH 28/39] Fix sticky nav in docs --- site/styles/main.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/styles/main.css b/site/styles/main.css index 1b0f255..f36b5cd 100644 --- a/site/styles/main.css +++ b/site/styles/main.css @@ -4,7 +4,7 @@ html, body, body > div { - @apply flex flex-col justify-between flex-1 min-h-full overflow-x-hidden text-toast-900; + @apply flex flex-col justify-between flex-1 min-h-full text-toast-900; } button, From cfbf32afcdf5fbb2425e1b2efc15d030ffa11ae1 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 16 May 2021 19:15:46 +0200 Subject: [PATCH 29/39] Add tailwind animation for examples --- site/tailwind.config.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/site/tailwind.config.js b/site/tailwind.config.js index e1558a6..612f2f6 100644 --- a/site/tailwind.config.js +++ b/site/tailwind.config.js @@ -18,12 +18,17 @@ module.exports = { animation: { enter: 'enter 200ms ease-out', 'slide-in': 'slide-in 1.2s cubic-bezier(.41,.73,.51,1.02)', + leave: 'leave 150ms ease-in forwards', }, keyframes: { enter: { '0%': { transform: 'scale(0.9)', opacity: 0 }, '100%': { transform: 'scale(1)', opacity: 1 }, }, + leave: { + '0%': { transform: 'scale(1)', opacity: 1 }, + '100%': { transform: 'scale(0.9)', opacity: 0 }, + }, 'slide-in': { '0%': { transform: 'translateY(-100%)' }, '100%': { transform: 'translateY(0)' }, From ad49ba0c60550caf3f50f6ed8d63f75342833f54 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 16 May 2021 19:31:31 +0200 Subject: [PATCH 30/39] 2.0.0-beta.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8d40562..6c5d5e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react-hot-toast", - "version": "1.0.2", + "version": "2.0.0-beta.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 309eaf6..301c402 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-hot-toast", - "version": "1.0.2", + "version": "2.0.0-beta.0", "author": "Timo Lins", "license": "MIT", "sideEffects": false, From b2f461166b5f84cfcf00d261125656e06f832e64 Mon Sep 17 00:00:00 2001 From: Timo Lins Date: Sun, 16 May 2021 21:18:49 +0200 Subject: [PATCH 31/39] Update docs for 2.0 --- site/components/docs-layout.tsx | 11 ++- site/pages/docs/styling.mdx | 53 +++++++++-- site/pages/docs/toast-bar.mdx | 50 ++++++++++ site/pages/docs/toast.mdx | 93 ++++++++++++------- site/pages/docs/toaster.mdx | 110 ++++++++++++++++------ site/pages/docs/use-toaster.mdx | 8 +- site/pages/docs/version-2.mdx | 160 ++++++++++++++++++++++++++++++++ 7 files changed, 413 insertions(+), 72 deletions(-) create mode 100644 site/pages/docs/toast-bar.mdx create mode 100644 site/pages/docs/version-2.mdx diff --git a/site/components/docs-layout.tsx b/site/components/docs-layout.tsx index fe07050..2ad3b51 100644 --- a/site/components/docs-layout.tsx +++ b/site/components/docs-layout.tsx @@ -9,7 +9,7 @@ const TableItem: React.FC<{ href: string; }> = ({ children, href }) => ( - + {children} @@ -53,8 +53,8 @@ export default function DocsLayout({ children, meta }) { GitHub -
-
- +
+
); diff --git a/site/package-lock.json b/site/package-lock.json index 35ffa77..84275fc 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -1139,37 +1139,11 @@ } }, "@fullhuman/postcss-purgecss": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-3.0.0.tgz", - "integrity": "sha512-cvuOgMwIVlfgWcUMqg5p33NbGUxLwMrKtDKkm3QRfOo4PRVNR6+y/xd9OyXTVZiB1bIpKNJ0ZObYPWD3DRQDtw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-3.1.3.tgz", + "integrity": "sha512-kwOXw8fZ0Lt1QmeOOrd+o4Ibvp4UTEBFQbzvWldjlKv5n+G9sXfIPn1hh63IQIL8K8vbvv1oYMJiIUbuy9bGaA==", "requires": { - "postcss": "7.0.32", - "purgecss": "^3.0.0" - }, - "dependencies": { - "postcss": { - "version": "7.0.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", - "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - } + "purgecss": "^3.1.3" } }, "@hapi/accept": { @@ -1355,6 +1329,29 @@ "resolved": "https://registry.npmjs.org/@next/react-refresh-utils/-/react-refresh-utils-10.0.3.tgz", "integrity": "sha512-XtzzPX2R4+MIyu1waEQUo2tiNwWVEkmObA6pboRCDTPOs4Ri8ckaIE08lN5A5opyF6GVN+IEq/J8KQrgsePsZQ==" }, + "@nodelib/fs.scandir": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "requires": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==" + }, + "@nodelib/fs.walk": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "requires": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + } + }, "@svgr/babel-plugin-add-jsx-attribute": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", @@ -3159,6 +3156,11 @@ } } }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, "dom-serializer": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", @@ -3632,11 +3634,50 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "dependencies": { + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "dependencies": { + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" + } + } + } + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "requires": { + "reusify": "^1.0.4" + } + }, "figgy-pudding": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", @@ -3767,14 +3808,14 @@ "optional": true }, "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "requires": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", - "universalify": "^1.0.0" + "universalify": "^2.0.0" } }, "fs-write-stream-atomic": { @@ -3900,6 +3941,38 @@ "path-is-absolute": "^1.0.0" } }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, "glob-parent": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", @@ -4417,6 +4490,11 @@ } } }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" + }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -4596,13 +4674,6 @@ "requires": { "graceful-fs": "^4.1.6", "universalify": "^2.0.0" - }, - "dependencies": { - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - } } }, "kind-of": { @@ -4667,6 +4738,11 @@ "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" }, + "lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", + "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=" + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -4810,6 +4886,11 @@ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -5008,9 +5089,9 @@ "optional": true }, "modern-normalize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.0.0.tgz", - "integrity": "sha512-1lM+BMLGuDfsdwf3rsgBSrxJwAZHFIrQ8YR61xIqdHo0uNKI9M52wNpHSrliZATJp51On6JD0AfRxd4YGSU0lw==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz", + "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==" }, "move-concurrently": { "version": "1.0.1", @@ -5449,9 +5530,9 @@ } }, "object-hash": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz", - "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" }, "object-inspect": { "version": "1.9.0", @@ -5631,6 +5712,32 @@ "is-hexadecimal": "^1.0.0" } }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, "parse-json": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", @@ -5928,9 +6035,9 @@ } }, "postcss-nested": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.3.tgz", - "integrity": "sha512-R2LHPw+u5hFfDgJG748KpGbJyTv7Yr33/2tIMWxquYuHTd9EXu27PYnKi7BxMXLtzKC0a0WVsqHtd7qIluQu/g==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.5.tgz", + "integrity": "sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew==", "requires": { "postcss-selector-parser": "^6.0.4" } @@ -6124,43 +6231,20 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "purgecss": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-3.0.0.tgz", - "integrity": "sha512-t3FGCwyX9XWV3ffvnAXTw6Y3Z9kNlcgm14VImNK66xKi5sdqxSA2I0SFYxtmZbAKuIZVckPdazw5iKL/oY/2TA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-3.1.3.tgz", + "integrity": "sha512-hRSLN9mguJ2lzlIQtW4qmPS2kh6oMnA9RxdIYK8sz18QYqd6ePp4GNDl18oWHA1f2v2NEQIh51CO8s/E3YGckQ==", "requires": { "commander": "^6.0.0", "glob": "^7.0.0", - "postcss": "7.0.32", + "postcss": "^8.2.1", "postcss-selector-parser": "^6.0.2" }, "dependencies": { "commander": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", - "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==" - }, - "postcss": { - "version": "7.0.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", - "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" } } }, @@ -6179,6 +6263,16 @@ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -6241,7 +6335,7 @@ "react-hot-toast": { "version": "file:..", "requires": { - "goober": "^2.0.15" + "goober": "^2.0.35" }, "dependencies": { "@babel/code-frame": { @@ -14891,9 +14985,9 @@ } }, "reduce-css-calc": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz", - "integrity": "sha512-fDnlZ+AybAS3C7Q9xDq5y8A2z+lT63zLbynew/lur/IR24OQF5x98tfNwf79mzEdfywZ0a2wpM860FhFfMxZlA==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", "requires": { "css-unit-converter": "^1.1.1", "postcss-value-parser": "^3.3.0" @@ -15165,6 +15259,11 @@ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, "rework": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", @@ -15203,6 +15302,14 @@ "inherits": "^2.0.1" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -15913,30 +16020,37 @@ } }, "tailwindcss": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.0.2.tgz", - "integrity": "sha512-nO9JRE1pO7SF9RnYAl6g7uzeHdrmKAFqNjT9NtZUfxqimJZAOOLOEyIEUiMq12+xIc7mC2Ey3Vf90XjHpWKfbw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.1.2.tgz", + "integrity": "sha512-T5t+wwd+/hsOyRw2HJuFuv0LTUm3MUdHm2DJ94GPVgzqwPPFa9XxX0KlwLWupUuiOUj6uiKURCzYPHFcuPch/w==", "requires": { - "@fullhuman/postcss-purgecss": "^3.0.0", + "@fullhuman/postcss-purgecss": "^3.1.3", "bytes": "^3.0.0", "chalk": "^4.1.0", + "chokidar": "^3.5.1", "color": "^3.1.3", "detective": "^5.2.0", "didyoumean": "^1.2.1", - "fs-extra": "^9.0.1", + "dlv": "^1.1.3", + "fast-glob": "^3.2.5", + "fs-extra": "^9.1.0", "html-tags": "^3.1.0", - "lodash": "^4.17.20", + "lodash": "^4.17.21", + "lodash.topath": "^4.5.2", "modern-normalize": "^1.0.0", "node-emoji": "^1.8.1", - "object-hash": "^2.0.3", + "normalize-path": "^3.0.0", + "object-hash": "^2.1.1", + "parse-glob": "^3.0.4", "postcss-functions": "^3", "postcss-js": "^3.0.3", - "postcss-nested": "^5.0.1", + "postcss-nested": "5.0.5", "postcss-selector-parser": "^6.0.4", "postcss-value-parser": "^4.1.0", "pretty-hrtime": "^1.0.3", - "reduce-css-calc": "^2.1.6", - "resolve": "^1.19.0" + "quick-lru": "^5.1.1", + "reduce-css-calc": "^2.1.8", + "resolve": "^1.20.0" }, "dependencies": { "ansi-styles": { @@ -15948,14 +16062,29 @@ } }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -15969,11 +16098,31 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -16446,9 +16595,9 @@ } }, "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" }, "unpipe": { "version": "1.0.0", diff --git a/site/package.json b/site/package.json index 89a0a90..31e31a4 100644 --- a/site/package.json +++ b/site/package.json @@ -20,7 +20,7 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-hot-toast": "file:../", - "tailwindcss": "^2.0.2" + "tailwindcss": "^2.1.2" }, "devDependencies": { "@types/node": "^14.14.13", diff --git a/site/pages/docs/toaster.mdx b/site/pages/docs/toaster.mdx index 3bb4a17..a4c4710 100644 --- a/site/pages/docs/toaster.mdx +++ b/site/pages/docs/toaster.mdx @@ -74,7 +74,7 @@ These will act as default options for all toasts. See [`toast()`](/docs/toast) f You can change the defaults for a specific type by adding, `success: {}`, `error: {}`, `loading: {}` or `custom: {}`. -## Use a custom render function +## Using a custom render function You can provide your **own render function** to the Toaster by passing it as children. It will be called for each [Toast](https://github.com/timolins/react-hot-toast/blob/main/src/core/types.ts#L34) allowing you to render any component based on the toast state. diff --git a/site/pages/docs/version-2.mdx b/site/pages/docs/version-2.mdx index f49e970..1fd41d1 100644 --- a/site/pages/docs/version-2.mdx +++ b/site/pages/docs/version-2.mdx @@ -9,16 +9,38 @@ export default ({ children, meta }) => {children}; # What's new in react-hot-toast 2.0 -This release is all about **flexibility**. -It is now even simpler to create the notification system of your dreams. Before we dig deeper into the new APIs, I'll walk you through new possibilities. +This release is all about **flexibility**. It allows you to create the notification system of your dreams, even simpler. Before we dig deeper into the new APIs, check out what's included in this release: -From now on, you can **render your own components**. Create **one-off custom toasts** with `toast.custom()`. **Animations are now smoother** and overwritable. Better accessibility by supporting **reduced motion**. Toasts can be **positioned individually**. And a lot more. +
+ toast.custom() - Dispatch components as toast +
+ +👀 + Reduced-motion support + + - Automatically adapts to system preference + +
+ +🔀 + Individually position toasts{' '} + +
+ + 🧈 + Smoother exit animations +
+ +⚙️ Custom Renderer API - Supply your own render function +
+ +As well as a many other improvements and fixes. ## Introducing `toast.custom()` -This new function allows you to **render any React component** on the fly. Pass in JSX and it will be rendered inside the notification stack. There are no default styles applied, giving you complete control. +This new function allows you to **render any React component** on the fly. Pass in JSX, and it will add it to the notification stack. There are no default styles applied, giving you complete control. -This makes it super easy to add [Tailwind UI Notifications](https://tailwindui.com/components/application-ui/overlays/notifications) to your React app. +This API makes it super easy to add [Tailwind UI Notifications](https://tailwindui.com/components/application-ui/overlays/notifications) to your React app. ```jsx // Minimal Example @@ -27,7 +49,7 @@ toast.custom(
Minimal Example
); // Tailwind Example toast.custom((t) => (
@@ -36,56 +58,36 @@ toast.custom((t) => ( )); ``` - +
+ +
In the example above, we pass in a **function that returns JSX**. This allows us to access the current toast state and toggle between the enter and exit animation. -## Support for custom render functions - -It is now possible to **replace the default renderer**. You can do this by passing a function as child to the [``](/docs/toaster). It will receive a [Toast](https://github.com/timolins/react-hot-toast/blob/main/src/core/types.ts#L34) as the first argument, giving you the flexibility to render whatever you want. +Instead of CSS keyframe animations, you can use TailwindCSS classes by wrapping it in the [Transition](https://headlessui.dev/react/transition) component from [@headlessui/react](https://headlessui.dev/). -This is a great higher-level alternative to the [`useToaster()`](/docs/use-toaster) hook. - -This API allows us to dynamically react to to current state our toasts. This can be used to **change the default animations**, add **a custom dismiss button** or render a custom notification, like [TailwindUI Notifications](https://tailwindui.com/components/application-ui/overlays/notifications). +## Better accessibility -```jsx -import { Toaster, ToastBar } from 'react-hot-toast'; +The prefers reduced motion is now respected by default. If react-hot-toast detects this setting, it will use fade transitions instead of sliding. -const CustomToaster = () => ( - - {(t) => ( - - {({ icon, message }) => ( - <> - {icon} - {message} - {t.type !== 'loading' && ( - - )} - - )} - - )} - -); -``` +## Smoother exit animation -This example adapts the ToastBar with its new render function API. You can read more about the APIs in the [Toaster](/docs/toaster) & [ToastBar](/docs/toast-bar) docs. +The exit animation is now less hectic when you have multiple toasts stacked. ## Per toast positioning @@ -97,20 +99,22 @@ toast.success('Always at the bottom', { }); ``` - +
+ +
## Relative positioning -Previously the Toaster was at a fixed position to the window. With this release, you can overwrite this behaviour and position it anywhere you want. +You can now overwrite the default position of the toaster and place it anywhere you want. ```jsx @@ -124,15 +128,59 @@ There is now a `gutter` option to control the gap between toasts. ``` -The offset to the edge is now controlled by the Toaster and can be changed by overwriting the `top`, `right`, `bottom` and `left` styles. +The offset is now controlled by the Toaster and can be changed by overwriting the `top`, `right`, `bottom` and `left` styles. ```jsx - + +``` + +## Custom Renderer API + +You can now use the [``](/docs/toaster) to render your own components. Pass in a function that receives a [Toast](https://github.com/timolins/react-hot-toast/blob/main/src/core/types.ts#L34) as the first argument, allowing you to render whatever you please. + +This is a great alternative if you are using [`useToaster()`](/docs/use-toaster) to render create custom notfications. + +This API allows us to dynamically react to to current state our toasts. This can be used to **change the default animations**, add **a custom dismiss button** or render a custom notification, like [TailwindUI Notifications](https://tailwindui.com/components/application-ui/overlays/notifications). + +```jsx +import { Toaster, ToastBar } from 'react-hot-toast'; + +const CustomToaster = () => ( + + {(t) => ( + + {({ icon, message }) => ( + <> + {icon} + {message} + {t.type !== 'loading' && ( + + )} + + )} + + )} + +); +``` + +This example adapts the [ToastBar](/docs/toast-bar) with its new render function API. You can read more about the APIs in the [Toaster](/docs/toaster) & [ToastBar](/docs/toast-bar) docs. + +## Available now + +Get react-hot-toast 2.0 while it's hot. Upgrading from 1.0.0 should be seamless for most users. + +```sh +yarn add react-hot-toast ``` ## The future and beyond -With this version, React Hot Toast got a lot more flexible, laying the **foundation for future releases**. With the next releases, I plan to add the [most requested feature](https://github.com/timolins/react-hot-toast/issues/7): a dismiss button. As well as support for [custom toast types](https://github.com/timolins/react-hot-toast/issues/23). +React Hot Toast got a lot more flexible with this version, laying the **foundation for future releases**. Thanks to everyone who helped out; much appreciated! + +In the next releases, I plan to add the [most requested feature](https://github.com/timolins/react-hot-toast/issues/7): a dismiss button. As well as support for [custom toast types](https://github.com/timolins/react-hot-toast/issues/23). + +--- ## Changelog diff --git a/site/pages/index.tsx b/site/pages/index.tsx index e354947..36ba7c9 100644 --- a/site/pages/index.tsx +++ b/site/pages/index.tsx @@ -104,7 +104,7 @@ export default function Home() { const shouldFade = allToasts.filter((t) => t.visible).length && position.includes('top'); return ( -
+
Date: Mon, 31 May 2021 15:35:08 +0200 Subject: [PATCH 39/39] Add rehype-slug for same page linking --- site/next.config.js | 9 +++++-- site/package-lock.json | 50 +++++++++++++++++++++++++++++++---- site/package.json | 1 + site/pages/docs/toast.mdx | 10 +++---- site/pages/docs/version-2.mdx | 35 ++++++++++++++---------- 5 files changed, 79 insertions(+), 26 deletions(-) diff --git a/site/next.config.js b/site/next.config.js index f3c606f..65fd246 100644 --- a/site/next.config.js +++ b/site/next.config.js @@ -1,6 +1,11 @@ const withTM = require('next-transpile-modules')(['react-hot-toast']); +const remarkSlugs = require('rehype-slug'); + const withMDX = require('@next/mdx')({ - extension: /\.mdx?$/, + extension: /.mdx?$/, + options: { + rehypePlugins: [remarkSlugs], + }, }); const withPlugins = require('next-compose-plugins'); @@ -8,7 +13,7 @@ const withSvgr = (nextConfig = {}, nextComposePlugins = {}) => { return Object.assign({}, nextConfig, { webpack(config, options) { config.module.rules.push({ - test: /\.svg$/, + test: /.svg$/, use: ['@svgr/webpack'], }); diff --git a/site/package-lock.json b/site/package-lock.json index 84275fc..b1519da 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -3272,6 +3272,11 @@ } } }, + "emoji-regex": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", + "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=" + }, "emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", @@ -3928,6 +3933,14 @@ "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", "optional": true }, + "github-slugger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz", + "integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==", + "requires": { + "emoji-regex": ">=6.0.0 <=6.1.1" + } + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -3991,6 +4004,11 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, + "goober": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.0.37.tgz", + "integrity": "sha512-wgj5C7IJX2+MyHZ4TgOA+hLL1mCDW+jGorQ5KCMLF1ofE9gQkfbo1s8txIrzIsAX65XRTm0qaipolId2KMYUkw==" + }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", @@ -4127,6 +4145,16 @@ "web-namespaces": "^1.0.0" } }, + "hast-util-has-property": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-1.0.4.tgz", + "integrity": "sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg==" + }, + "hast-util-heading-rank": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-1.0.1.tgz", + "integrity": "sha512-P6Hq7RCky9syMevlrN90QWpqWDXCxwIVOfQR2rK6P4GpY4bqjKEuCzoWSRORZ7vz+VgRpLnXimh+mkwvVFjbyQ==" + }, "hast-util-parse-selector": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", @@ -4161,6 +4189,11 @@ "zwitch": "^1.0.0" } }, + "hast-util-to-string": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-1.0.4.tgz", + "integrity": "sha512-eK0MxRX47AV2eZ+Lyr18DCpQgodvaS3fAQO2+b9Two9F5HEoRPhiUMNzoXArMJfZi2yieFzUBMRl3HNJ3Jus3w==" + }, "hastscript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", @@ -10106,11 +10139,6 @@ "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" }, - "goober": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/goober/-/goober-2.0.18.tgz", - "integrity": "sha512-XaYq9GqRkWHS0DFPQZQLhrKGWWbiz+ydiEbMRN8eGJZ7enDjmWnRv3RWn5RwdukjnHUA6b/AQcf4QWjellKOCw==" - }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", @@ -15073,6 +15101,18 @@ } } }, + "rehype-slug": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-4.0.1.tgz", + "integrity": "sha512-KIlJALf9WfHFF21icwTd2yI2IP+RQRweaxH9ChVGQwRYy36+hiomG4ZSe0yQRyCt+D/vE39LbAcOI/h4O4GPhA==", + "requires": { + "github-slugger": "^1.1.1", + "hast-util-has-property": "^1.0.0", + "hast-util-heading-rank": "^1.0.0", + "hast-util-to-string": "^1.0.0", + "unist-util-visit": "^2.0.0" + } + }, "remark-footnotes": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz", diff --git a/site/package.json b/site/package.json index 31e31a4..d482826 100644 --- a/site/package.json +++ b/site/package.json @@ -20,6 +20,7 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-hot-toast": "file:../", + "rehype-slug": "^4.0.1", "tailwindcss": "^2.1.2" }, "devDependencies": { diff --git a/site/pages/docs/toast.mdx b/site/pages/docs/toast.mdx index 4b59d1b..e460e7d 100644 --- a/site/pages/docs/toast.mdx +++ b/site/pages/docs/toast.mdx @@ -46,7 +46,7 @@ toast('Hello World', { toast('Hello World'); ``` -The most basic variant. It does not have an icon by default, but you can provide one via the options. +The most basic variant. It does not have an icon by default, but you can provide one via the options. If you don't want any default styles, use `toast.custom()` instead. ### Success @@ -78,7 +78,7 @@ Creates a custom notification with JSX without default styles. toast.loading('Waiting...'); ``` -This will create a loading notification. Most likely, you want to update it manually afterwards. For a friendly alternative, check out `toast.promise()`, which takes care of that automatically. +This will create a loading notification. Most likely, you want to update it afterwards. For a friendly alternative, check out `toast.promise()`, which takes care of that automatically. ### Promise @@ -153,7 +153,7 @@ You can dismiss all toasts at once, by leaving out the `toastId`. #### Dismiss all toasts at one ```js -toast.dismiss() +toast.dismiss(); ``` To remove toasts instantly without any animations, use `toast.remove`. @@ -161,11 +161,11 @@ To remove toasts instantly without any animations, use `toast.remove`. #### Remove toasts instanstly ```js -toast.remove(toastId) +toast.remove(toastId); // or -toast.remove() +toast.remove(); ``` ### Update an existing toast diff --git a/site/pages/docs/version-2.mdx b/site/pages/docs/version-2.mdx index 1fd41d1..f511a28 100644 --- a/site/pages/docs/version-2.mdx +++ b/site/pages/docs/version-2.mdx @@ -11,30 +11,37 @@ export default ({ children, meta }) => {children}; This release is all about **flexibility**. It allows you to create the notification system of your dreams, even simpler. Before we dig deeper into the new APIs, check out what's included in this release: -
- toast.custom() - Dispatch components as toast +
+ toast.custom() - Dispatch components as toast
-👀 +👀 Reduced-motion support - - - Automatically adapts to system preference - + - Automatically adapts to system preference
-🔀 +🔀 Individually position toasts{' '} - +
- - 🧈 - Smoother exit animations +🧈 + Smoother exit animations +
-⚙️ Custom Renderer API - Supply your own render function +⚙️ Custom Renderer API - Supply your own render function
-As well as a many other improvements and fixes. +As well as a many [other improvements and fixes](#changelog). ## Introducing `toast.custom()` @@ -136,7 +143,7 @@ The offset is now controlled by the Toaster and can be changed by overwriting th ## Custom Renderer API -You can now use the [``](/docs/toaster) to render your own components. Pass in a function that receives a [Toast](https://github.com/timolins/react-hot-toast/blob/main/src/core/types.ts#L34) as the first argument, allowing you to render whatever you please. +You can now use the [``](/docs/toaster#using-a-custom-render-function) to render your own components. Pass in a function that receives a [Toast](https://github.com/timolins/react-hot-toast/blob/main/src/core/types.ts#L34) as the first argument, allowing you to render whatever you please. This is a great alternative if you are using [`useToaster()`](/docs/use-toaster) to render create custom notfications.