Skip to content

Commit 13047cb

Browse files
committed
fix(toast): restore the timer after remove the element from the stack top
1 parent e7ce959 commit 13047cb

File tree

4 files changed

+35
-7
lines changed

4 files changed

+35
-7
lines changed

components/geist-provider/geist-provider.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import React, { PropsWithChildren, useMemo } from 'react'
1+
import React, { PropsWithChildren, useMemo, useState } from 'react'
22
import {
33
GeistUIContent,
44
GeistUIContextParams,
55
UpdateToastsFunction,
6+
UpdateToastsIDFunction,
67
UpdateToastsLayoutFunction,
78
} from '../utils/use-geist-ui-context'
89
import ThemeProvider from './theme-provider'
@@ -21,6 +22,8 @@ const GeistProvider: React.FC<PropsWithChildren<GeistProviderProps>> = ({
2122
themeType,
2223
children,
2324
}) => {
25+
const [lastUpdateToastId, setLastUpdateToastId] =
26+
useState<GeistUIContextParams['lastUpdateToastId']>(null)
2427
const [toasts, setToasts, toastsRef] = useCurrentState<GeistUIContextParams['toasts']>(
2528
[],
2629
)
@@ -34,15 +37,20 @@ const GeistProvider: React.FC<PropsWithChildren<GeistProviderProps>> = ({
3437
const nextLayout = fn(toastLayoutRef.current)
3538
setToastLayout(nextLayout)
3639
}
40+
const updateLastToastId: UpdateToastsIDFunction = fn => {
41+
setLastUpdateToastId(fn())
42+
}
3743

3844
const initialValue = useMemo<GeistUIContextParams>(
3945
() => ({
4046
toasts,
4147
toastLayout,
4248
updateToasts,
49+
lastUpdateToastId,
4350
updateToastLayout,
51+
updateLastToastId,
4452
}),
45-
[toasts, toastLayout],
53+
[toasts, toastLayout, lastUpdateToastId],
4654
)
4755

4856
return (

components/use-toasts/toast-container.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import { useGeistUIContext } from '../utils/use-geist-ui-context'
66
import ToastItem from './toast-item'
77
import useClasses from '../use-classes'
88
import { isLeftPlacement, isTopPlacement } from './helpers'
9+
import useCurrentState from '../utils/use-current-state'
910

1011
const ToastContainer: React.FC<React.PropsWithChildren<unknown>> = () => {
1112
const theme = useTheme()
1213
const portal = usePortal('toast')
13-
const { toasts, updateToasts, toastLayout } = useGeistUIContext()
14+
const [, setHovering, hoveringRef] = useCurrentState<boolean>(false)
15+
const { toasts, updateToasts, toastLayout, lastUpdateToastId } = useGeistUIContext()
1416
const memoizedLayout = useMemo(() => toastLayout, [toastLayout])
1517
const toastElements = useMemo(
1618
() =>
@@ -28,7 +30,8 @@ const ToastContainer: React.FC<React.PropsWithChildren<unknown>> = () => {
2830
[memoizedLayout],
2931
)
3032
const hoverHandler = (isHovering: boolean) => {
31-
if (isHovering)
33+
setHovering(isHovering)
34+
if (isHovering) {
3235
return updateToasts(last =>
3336
last.map(toast => {
3437
if (!toast.visible) return toast
@@ -39,6 +42,7 @@ const ToastContainer: React.FC<React.PropsWithChildren<unknown>> = () => {
3942
}
4043
}),
4144
)
45+
}
4246

4347
updateToasts(last =>
4448
last.map((toast, index) => {
@@ -58,6 +62,15 @@ const ToastContainer: React.FC<React.PropsWithChildren<unknown>> = () => {
5862
)
5963
}
6064

65+
useEffect(() => {
66+
const index = toasts.findIndex(r => r._internalIdent === lastUpdateToastId)
67+
const toast = toasts[index]
68+
if (!toast || toast.visible || !hoveringRef.current) return
69+
const hasVisible = toasts.find((r, i) => i < index && r.visible)
70+
if (hasVisible || !hoveringRef.current) return
71+
hoverHandler(false)
72+
}, [toasts, lastUpdateToastId])
73+
6174
useEffect(() => {
6275
let timeout: null | number = null
6376
const timer = window.setInterval(() => {
@@ -80,8 +93,8 @@ const ToastContainer: React.FC<React.PropsWithChildren<unknown>> = () => {
8093
return createPortal(
8194
<div
8295
className={classNames}
83-
onMouseOver={() => hoverHandler(true)}
84-
onMouseOut={() => hoverHandler(false)}>
96+
onMouseEnter={() => hoverHandler(true)}
97+
onMouseLeave={() => hoverHandler(false)}>
8598
{toastElements}
8699
<style jsx>{`
87100
.toasts {

components/use-toasts/use-toast.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ export type ToastHooksResult = {
5656
}
5757

5858
const useToasts = (layout?: ToastLayout): ToastHooksResult => {
59-
const { updateToasts, toasts, updateToastLayout } = useGeistUIContext()
59+
const { updateToasts, toasts, updateToastLayout, updateLastToastId } =
60+
useGeistUIContext()
6061

6162
useEffect(() => {
6263
if (!layout) return
@@ -77,6 +78,7 @@ const useToasts = (layout?: ToastLayout): ToastHooksResult => {
7778
return { ...item, visible: false }
7879
}),
7980
)
81+
updateLastToastId(() => internalId)
8082
}
8183
const removeAll = () => {
8284
updateToasts(last => last.map(toast => ({ ...toast, visible: false })))

components/utils/use-geist-ui-context.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,24 @@ export type UpdateToastsFunction = (fn: (toasts: Array<Toast>) => Array<Toast>)
55
export type UpdateToastsLayoutFunction = (
66
fn: (layout: Required<ToastLayout>) => Required<ToastLayout>,
77
) => any
8+
export type UpdateToastsIDFunction = (fn: () => string | null) => any
89

910
export interface GeistUIContextParams {
1011
toasts: Array<Toast>
1112
updateToasts: UpdateToastsFunction
1213
toastLayout: Required<ToastLayout>
1314
updateToastLayout: UpdateToastsLayoutFunction
15+
lastUpdateToastId: string | null
16+
updateLastToastId: UpdateToastsIDFunction
1417
}
1518

1619
const defaultParams: GeistUIContextParams = {
1720
toasts: [],
1821
toastLayout: defaultToastLayout,
1922
updateToastLayout: t => t,
2023
updateToasts: t => t,
24+
lastUpdateToastId: null,
25+
updateLastToastId: () => null,
2126
}
2227

2328
export const GeistUIContent: React.Context<GeistUIContextParams> =

0 commit comments

Comments
 (0)