@@ -8,6 +8,7 @@ import Notification, {
88 type NotificationStyles ,
99} from './Notification' ;
1010import useListPosition from './hooks/useListPosition' ;
11+ import useListScroll from './hooks/useListScroll' ;
1112
1213export type Placement = 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight' ;
1314
@@ -32,14 +33,9 @@ export interface NotificationListProps {
3233 classNames ?: NotificationClassNames ;
3334 styles ?: NotificationStyles ;
3435 stack ?: StackConfig ;
35- maxCount ?: number ;
3636 motion ?: CSSMotionProps | ( ( placement : Placement ) => CSSMotionProps ) ;
3737}
3838
39- function clampScrollOffset ( offset : number , maxScroll : number ) {
40- return Math . min ( 0 , Math . max ( - maxScroll , offset ) ) ;
41- }
42-
4339function assignRef < T > ( ref : React . Ref < T > , value : T | null ) {
4440 if ( typeof ref === 'function' ) {
4541 ref ( value ) ;
@@ -48,36 +44,19 @@ function assignRef<T>(ref: React.Ref<T>, value: T | null) {
4844 }
4945}
5046
51- function getNoticeStyle ( nodePosition ?: { x : number ; y : number } ) : React . CSSProperties | undefined {
52- if ( ! nodePosition ) {
53- return undefined ;
54- }
55-
56- return {
57- '--notification-x' : `${ nodePosition . x } px` ,
58- '--notification-y' : `${ nodePosition . y } px` ,
59- } as React . CSSProperties ;
60- }
61-
6247const NotificationList : React . FC < NotificationListProps > = ( props ) => {
6348 const {
6449 configList = [ ] ,
6550 prefixCls = 'rc-notification' ,
6651 pauseOnHover,
6752 classNames,
6853 styles,
69- maxCount,
7054 motion,
7155 placement,
7256 } = props ;
7357
7458 // ========================== Data ==========================
75- const mergedConfigList = React . useMemo ( ( ) => {
76- const list =
77- typeof maxCount === 'number' && maxCount > 0 ? configList . slice ( - maxCount ) : configList ;
78-
79- return list . slice ( ) . reverse ( ) ;
80- } , [ configList , maxCount ] ) ;
59+ const mergedConfigList = React . useMemo ( ( ) => configList . slice ( ) . reverse ( ) , [ configList ] ) ;
8160
8261 const keys = React . useMemo (
8362 ( ) =>
@@ -92,107 +71,15 @@ const NotificationList: React.FC<NotificationListProps> = (props) => {
9271 // ========================= Motion =========================
9372 const placementMotion = typeof motion === 'function' ? motion ( placement ) : motion ;
9473
95- // ========================= Scroll =========================
96- const viewportRef = React . useRef < HTMLDivElement > ( null ) ;
97- const contentRef = React . useRef < HTMLDivElement > ( null ) ;
98- const prevKeyListRef = React . useRef < string [ ] > ( keyList ) ;
99- const prevNotificationPositionRef = React . useRef < Map < string , { x : number ; y : number } > > (
100- new Map ( ) ,
101- ) ;
102- const notificationPositionCacheRef = React . useRef < Map < string , { x : number ; y : number } > > (
103- new Map ( ) ,
104- ) ;
105- const scrollOffsetRef = React . useRef ( 0 ) ;
106- const [ scrollOffset , setScrollOffset ] = React . useState ( 0 ) ;
10774 const [ notificationPosition , setNodeSize ] = useListPosition ( mergedConfigList ) ;
108-
109- const syncScrollOffset = React . useCallback ( ( nextOffset : number ) => {
110- const viewportHeight = viewportRef . current ?. clientHeight ?? 0 ;
111- const measuredContentHeight = contentRef . current ?. scrollHeight ?? 0 ;
112- const maxScroll = Math . max ( measuredContentHeight - viewportHeight , 0 ) ;
113- const mergedOffset = clampScrollOffset ( nextOffset , maxScroll ) ;
114-
115- scrollOffsetRef . current = mergedOffset ;
116- setScrollOffset ( ( prev ) => ( prev === mergedOffset ? prev : mergedOffset ) ) ;
117- } , [ ] ) ;
118-
119- React . useLayoutEffect ( ( ) => {
120- notificationPosition . forEach ( ( position , key ) => {
121- notificationPositionCacheRef . current . set ( key , position ) ;
122- } ) ;
123- } , [ notificationPosition ] ) ;
124-
125- React . useLayoutEffect ( ( ) => {
126- const prevKeyList = prevKeyListRef . current ;
127- const prevNotificationPosition = prevNotificationPositionRef . current ;
128-
129- if ( scrollOffsetRef . current < 0 ) {
130- const prependCount = prevKeyList . length
131- ? keyList . findIndex ( ( key ) => key === prevKeyList [ 0 ] )
132- : - 1 ;
133- const removedCount = keyList . length ? prevKeyList . findIndex ( ( key ) => key === keyList [ 0 ] ) : - 1 ;
134-
135- if ( prependCount > 0 ) {
136- const prependHeight = notificationPosition . get ( prevKeyList [ 0 ] ) ?. y ?? 0 ;
137- syncScrollOffset ( scrollOffsetRef . current - prependHeight ) ;
138- } else if ( removedCount > 0 ) {
139- const removedHeight = keyList [ 0 ] ? ( prevNotificationPosition . get ( keyList [ 0 ] ) ?. y ?? 0 ) : 0 ;
140- syncScrollOffset ( scrollOffsetRef . current + removedHeight ) ;
141- } else {
142- syncScrollOffset ( scrollOffsetRef . current ) ;
143- }
144- } else {
145- syncScrollOffset ( scrollOffsetRef . current ) ;
146- }
147-
148- prevKeyListRef . current = keyList ;
149- prevNotificationPositionRef . current = new Map ( notificationPosition ) ;
150- } , [ keyList , notificationPosition , syncScrollOffset ] ) ;
151-
152- React . useLayoutEffect ( ( ) => {
153- const viewportNode = viewportRef . current ;
154- const contentNode = contentRef . current ;
155-
156- if ( ! viewportNode || ! contentNode || typeof ResizeObserver === 'undefined' ) {
157- return ;
158- }
159-
160- const resizeObserver = new ResizeObserver ( ( ) => {
161- syncScrollOffset ( scrollOffsetRef . current ) ;
162- } ) ;
163-
164- resizeObserver . observe ( viewportNode ) ;
165- resizeObserver . observe ( contentNode ) ;
166-
167- return ( ) => {
168- resizeObserver . disconnect ( ) ;
169- } ;
170- } , [ syncScrollOffset ] ) ;
171-
172- const onWheel = React . useCallback (
173- ( event : React . WheelEvent < HTMLDivElement > ) => {
174- const viewportHeight = viewportRef . current ?. clientHeight ?? 0 ;
175- const measuredContentHeight = contentRef . current ?. scrollHeight ?? 0 ;
176- const maxScroll = Math . max ( measuredContentHeight - viewportHeight , 0 ) ;
177-
178- if ( ! maxScroll ) {
179- return ;
180- }
181-
182- const nextOffset = clampScrollOffset ( scrollOffsetRef . current - event . deltaY , maxScroll ) ;
183-
184- if ( nextOffset !== scrollOffsetRef . current ) {
185- event . preventDefault ( ) ;
186- syncScrollOffset ( nextOffset ) ;
187- }
188- } ,
189- [ syncScrollOffset ] ,
75+ const { contentRef, onWheel, scrollOffset, viewportRef } = useListScroll (
76+ keyList ,
77+ notificationPosition ,
19078 ) ;
19179
19280 // ========================= Render =========================
19381 const listPrefixCls = `${ prefixCls } -list` ;
19482 const itemPrefixCls = `${ listPrefixCls } -item` ;
195- const motionPrefixCls = `${ itemPrefixCls } -motion` ;
19683
19784 return (
19885 < div
@@ -215,45 +102,34 @@ const NotificationList: React.FC<NotificationListProps> = (props) => {
215102 return (
216103 < div
217104 key = { key }
218- className = { itemPrefixCls }
105+ className = { clsx ( itemPrefixCls , className ) }
219106 ref = { ( node ) => {
107+ assignRef ( nodeRef , node ) ;
220108 setNodeSize ( strKey , node ) ;
221109 } }
222- style = { {
223- ...getNoticeStyle (
224- notificationPosition . get ( strKey ) ??
225- notificationPositionCacheRef . current . get ( strKey ) ,
226- ) ,
227- } }
110+ style = { style }
228111 >
229- < div
230- ref = { ( node ) => {
231- assignRef ( nodeRef , node ) ;
112+ < Notification
113+ { ...notificationConfig }
114+ offset = { notificationPosition . get ( strKey ) }
115+ className = { config . className }
116+ style = { config . style }
117+ classNames = { {
118+ root : clsx ( classNames ?. root , config . classNames ?. root ) ,
119+ close : clsx ( classNames ?. close , config . classNames ?. close ) ,
120+ } }
121+ styles = { {
122+ root : {
123+ ...styles ?. root ,
124+ ...config . styles ?. root ,
125+ } ,
126+ close : {
127+ ...styles ?. close ,
128+ ...config . styles ?. close ,
129+ } ,
232130 } }
233- className = { clsx ( motionPrefixCls , className ) }
234- style = { style }
235- >
236- < Notification
237- { ...notificationConfig }
238- className = { config . className }
239- style = { config . style }
240- classNames = { {
241- root : clsx ( classNames ?. root , config . classNames ?. root ) ,
242- close : clsx ( classNames ?. close , config . classNames ?. close ) ,
243- } }
244- styles = { {
245- root : {
246- ...styles ?. root ,
247- ...config . styles ?. root ,
248- } ,
249- close : {
250- ...styles ?. close ,
251- ...config . styles ?. close ,
252- } ,
253- } }
254- pauseOnHover = { config . pauseOnHover ?? pauseOnHover }
255- />
256- </ div >
131+ pauseOnHover = { config . pauseOnHover ?? pauseOnHover }
132+ />
257133 </ div >
258134 ) ;
259135 } }
0 commit comments