11import * as React from 'react' ;
22import { clsx } from 'clsx' ;
3+ import pickAttrs from '@rc-component/util/lib/pickAttrs' ;
34import useNoticeTimer from './hooks/useNoticeTimer' ;
45import { useEvent } from '@rc-component/util' ;
56
67export interface NotificationClassNames {
8+ wrapper ?: string ;
79 root ?: string ;
10+ content ?: string ;
811 close ?: string ;
12+ progress ?: string ;
913}
1014
1115export interface NotificationStyles {
16+ wrapper ?: React . CSSProperties ;
1217 root ?: React . CSSProperties ;
18+ content ?: React . CSSProperties ;
1319 close ?: React . CSSProperties ;
20+ progress ?: React . CSSProperties ;
1421}
1522
1623export interface NotificationProps {
24+ prefixCls ?: string ;
1725 content ?: React . ReactNode ;
1826 actions ?: React . ReactNode ;
1927 close ?: React . ReactNode ;
20- duration ?: number | false ;
28+ closable ?:
29+ | boolean
30+ | ( { closeIcon ?: React . ReactNode ; onClose ?: VoidFunction } & React . AriaAttributes ) ;
31+ duration ?: number | false | null ;
32+ showProgress ?: boolean ;
33+ times ?: number ;
34+ hovering ?: boolean ;
2135 offset ?: {
2236 x : number ;
2337 y : number ;
@@ -27,45 +41,91 @@ export interface NotificationProps {
2741 style ?: React . CSSProperties ;
2842 classNames ?: NotificationClassNames ;
2943 styles ?: NotificationStyles ;
44+ props ?: React . HTMLAttributes < HTMLDivElement > & Record < string , any > ;
3045 onClick ?: React . MouseEventHandler < HTMLDivElement > ;
31- /** Callback when notification is closed by timeout */
3246 onClose ?: ( ) => void ;
47+ onCloseInternal ?: VoidFunction ;
3348}
3449
3550const Notification = React . forwardRef < HTMLDivElement , NotificationProps > ( ( props , ref ) => {
3651 const {
52+ prefixCls = 'rc-notification' ,
3753 content,
3854 actions,
3955 close,
56+ closable,
4057 duration = 4.5 ,
58+ showProgress,
59+ hovering : forcedHovering ,
4160 offset,
4261 pauseOnHover = true ,
4362 className,
4463 style,
4564 classNames,
4665 styles,
66+ props : divProps ,
4767 onClick,
4868 onClose,
69+ onCloseInternal,
4970 } = props ;
71+ const [ hovering , setHovering ] = React . useState ( false ) ;
72+ const [ percent , setPercent ] = React . useState ( 0 ) ;
73+ const noticePrefixCls = `${ prefixCls } -notice` ;
5074
5175 // ========================= Close ==========================
5276 const onEventClose = useEvent ( onClose ) ;
77+ const onEventCloseInternal = useEvent ( onCloseInternal ) ;
5378 const offsetRef = React . useRef ( offset ) ;
79+ const closableObj = React . useMemo ( ( ) => {
80+ if ( typeof closable === 'object' && closable !== null ) {
81+ return closable ;
82+ }
83+
84+ return { } ;
85+ } , [ closable ] ) ;
86+ const closeContent = close === undefined ? ( closableObj . closeIcon ?? 'x' ) : close ;
87+ const mergedClosable = close !== undefined ? close !== null : ! ! closable ;
88+ const ariaProps = pickAttrs ( closableObj , true ) ;
5489
5590 if ( offset ) {
5691 offsetRef . current = offset ;
5792 }
5893
5994 // ======================== Duration ========================
60- const [ onResume , onPause ] = useNoticeTimer ( duration , onEventClose , ( ) => { } ) ;
95+ const [ onResume , onPause ] = useNoticeTimer (
96+ duration ,
97+ ( ) => {
98+ closableObj . onClose ?.( ) ;
99+ onEventClose ( ) ;
100+ onEventCloseInternal ( ) ;
101+ } ,
102+ setPercent ,
103+ ! ! showProgress ,
104+ ) ;
61105
62106 const mergedOffset = offset ?? offsetRef . current ;
107+ const validPercent = 100 - Math . min ( Math . max ( percent * 100 , 0 ) , 100 ) ;
108+
109+ React . useEffect ( ( ) => {
110+ if ( ! pauseOnHover ) {
111+ return ;
112+ }
113+
114+ if ( forcedHovering ) {
115+ onPause ( ) ;
116+ } else if ( ! hovering ) {
117+ onResume ( ) ;
118+ }
119+ } , [ forcedHovering , hovering , onPause , onResume , pauseOnHover ] ) ;
63120
64121 // ========================= Render =========================
65122 return (
66123 < div
124+ { ...divProps }
67125 ref = { ref }
68- className = { clsx ( className , classNames ?. root ) }
126+ className = { clsx ( noticePrefixCls , className , classNames ?. root , {
127+ [ `${ noticePrefixCls } -closable` ] : mergedClosable ,
128+ } ) }
69129 style = { {
70130 ...styles ?. root ,
71131 ...( mergedOffset
@@ -77,23 +137,64 @@ const Notification = React.forwardRef<HTMLDivElement, NotificationProps>((props,
77137 ...style ,
78138 } }
79139 onClick = { onClick }
80- onMouseEnter = { pauseOnHover ? onPause : undefined }
81- onMouseLeave = { pauseOnHover ? onResume : undefined }
140+ onMouseEnter = { ( event ) => {
141+ setHovering ( true ) ;
142+ if ( pauseOnHover ) {
143+ onPause ( ) ;
144+ }
145+ divProps ?. onMouseEnter ?.( event ) ;
146+ } }
147+ onMouseLeave = { ( event ) => {
148+ setHovering ( false ) ;
149+ if ( pauseOnHover && ! forcedHovering ) {
150+ onResume ( ) ;
151+ }
152+ divProps ?. onMouseLeave ?.( event ) ;
153+ } }
82154 >
83- { content }
84- { close && (
155+ < div
156+ className = { clsx ( `${ noticePrefixCls } -content` , classNames ?. content ) }
157+ style = { styles ?. content }
158+ >
159+ { content }
160+ </ div >
161+
162+ { mergedClosable && (
85163 < button
86- className = { clsx ( ' close' , classNames ?. close ) }
164+ className = { clsx ( ` ${ noticePrefixCls } - close` , classNames ?. close ) }
87165 aria-label = "Close"
166+ { ...ariaProps }
88167 style = { styles ?. close }
168+ onKeyDown = { ( event ) => {
169+ if ( event . key === 'Enter' || event . code === 'Enter' ) {
170+ closableObj . onClose ?.( ) ;
171+ onEventClose ( ) ;
172+ onEventCloseInternal ( ) ;
173+ }
174+ } }
89175 onClick = { ( e ) => {
176+ e . preventDefault ( ) ;
90177 e . stopPropagation ( ) ;
178+ closableObj . onClose ?.( ) ;
91179 onEventClose ( ) ;
180+ onEventCloseInternal ( ) ;
92181 } }
93182 >
94- { close }
183+ { closeContent }
95184 </ button >
96185 ) }
186+
187+ { showProgress && typeof duration === 'number' && duration > 0 && (
188+ < progress
189+ className = { clsx ( `${ noticePrefixCls } -progress` , classNames ?. progress ) }
190+ max = "100"
191+ value = { validPercent }
192+ style = { styles ?. progress }
193+ >
194+ { validPercent } %
195+ </ progress >
196+ ) }
197+
97198 { actions && < div className = "actions" > { actions } </ div > }
98199 </ div >
99200 ) ;
0 commit comments