Skip to content

Commit abea35d

Browse files
zombieJclaude
andcommitted
feat: add useNoticeTimer hook and enhance Notification component
- Add useNoticeTimer hook for auto-close timer with pause/resume support - Add onClose callback prop for timeout-based closing - Add pauseOnHover support (default: true) to pause timer on mouse hover - Add onClick handler support - Set default duration to 4.5 seconds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e1b36cd commit abea35d

2 files changed

Lines changed: 67 additions & 4 deletions

File tree

src/Notification.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,40 @@
11
import * as React from 'react';
2+
import useNoticeTimer from './hooks/useNoticeTimer';
3+
import { useEvent } from '@rc-component/util';
24

35
export interface NotificationProps {
46
content?: React.ReactNode;
57
actions?: React.ReactNode;
68
close?: React.ReactNode;
7-
duration?: number | false | null;
9+
duration?: number | false;
810
pauseOnHover?: boolean;
911
onClick?: React.MouseEventHandler<HTMLDivElement>;
12+
/** Callback when notification is closed by timeout */
13+
onClose?: () => void;
1014
}
1115

1216
const Notification = React.forwardRef<HTMLDivElement, NotificationProps>((props, ref) => {
13-
const { content, actions, close, ...restProps } = props;
17+
const { content, actions, close, duration = 4.5, pauseOnHover = true, onClick, onClose } = props;
18+
19+
// ========================= Close ==========================
20+
const onEventClose = useEvent(onClose);
21+
22+
// ======================== Duration ========================
23+
const [onResume, onPause] = useNoticeTimer(duration, onEventClose);
24+
25+
// ========================= Render =========================
1426
return (
15-
<div ref={ref} {...restProps}>
27+
<div ref={ref} onClick={onClick} onMouseEnter={onPause} onMouseLeave={onResume}>
1628
{content}
1729
{close && (
18-
<button className="close" aria-label="Close">
30+
<button
31+
className="close"
32+
aria-label="Close"
33+
onClick={(e) => {
34+
e.stopPropagation();
35+
onEventClose();
36+
}}
37+
>
1938
{close}
2039
</button>
2140
)}

src/hooks/useNoticeTimer.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as React from 'react';
2+
3+
export default function useNoticeTimer(duration: number | false, onClose: VoidFunction) {
4+
// Normalize: `false` means no auto-close
5+
const mergedDuration: number = typeof duration === 'number' ? duration : 0;
6+
7+
const startTimestampRef = React.useRef(0);
8+
const leftTimeRef = React.useRef(mergedDuration * 1000);
9+
const timerRef = React.useRef<NodeJS.Timeout>();
10+
11+
const clear = () => {
12+
clearTimeout(timerRef.current);
13+
};
14+
15+
const onResume = () => {
16+
clear();
17+
18+
// Only start timer when there is remaining time
19+
if (leftTimeRef.current > 0) {
20+
startTimestampRef.current = Date.now();
21+
timerRef.current = setTimeout(() => {
22+
onClose();
23+
}, leftTimeRef.current);
24+
}
25+
};
26+
27+
const onPause = () => {
28+
clear();
29+
30+
// Record how much time is left so onResume can continue from here
31+
leftTimeRef.current -= Date.now() - startTimestampRef.current;
32+
};
33+
34+
React.useEffect(() => {
35+
// Reset remaining time whenever duration changes, then (re)start the timer
36+
leftTimeRef.current = mergedDuration * 1000;
37+
onResume();
38+
39+
// Clear the timer on unmount or before next effect run
40+
return clear;
41+
}, []);
42+
43+
return [onResume, onPause] as const;
44+
}

0 commit comments

Comments
 (0)