@@ -3,7 +3,7 @@ import { useRef, useState } from 'react';
3
3
import classNames from 'classnames' ;
4
4
import Filler from './Filler' ;
5
5
import type { InnerProps } from './Filler' ;
6
- import type { ScrollBarDirectionType } from './ScrollBar' ;
6
+ import type { ScrollBarDirectionType , ScrollBarRef } from './ScrollBar' ;
7
7
import ScrollBar from './ScrollBar' ;
8
8
import type { RenderFunc , SharedConfig , GetKey } from './interface' ;
9
9
import useChildren from './hooks/useChildren' ;
@@ -52,6 +52,12 @@ export interface ListProps<T> extends Omit<React.HTMLAttributes<any>, 'children'
52
52
/** Set `false` will always use real scroll instead of virtual one */
53
53
virtual ?: boolean ;
54
54
direction ?: ScrollBarDirectionType ;
55
+ /**
56
+ * By default `scrollWidth` is same as container.
57
+ * When set this, it will show the horizontal scrollbar and
58
+ * `scrollWidth` will be used as the real width instead of container width.
59
+ */
60
+ scrollWidth ?: number ;
55
61
56
62
onScroll ?: React . UIEventHandler < HTMLElement > ;
57
63
/** Trigger when render list item changed */
@@ -74,6 +80,7 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
74
80
itemKey,
75
81
virtual,
76
82
direction,
83
+ scrollWidth,
77
84
component : Component = 'div' ,
78
85
onScroll,
79
86
onVisibleChange,
@@ -84,19 +91,26 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
84
91
// ================================= MISC =================================
85
92
const useVirtual = ! ! ( virtual !== false && height && itemHeight ) ;
86
93
const inVirtual = useVirtual && data && itemHeight * data . length > height ;
94
+ const isRTL = direction === 'rtl' ;
87
95
88
- const [ scrollTop , setScrollTop ] = useState ( 0 ) ;
89
- const [ scrollMoving , setScrollMoving ] = useState ( false ) ;
90
-
91
- const mergedClassName = classNames (
92
- prefixCls ,
93
- { [ `${ prefixCls } -rtl` ] : direction === 'rtl' } ,
94
- className ,
95
- ) ;
96
+ const mergedClassName = classNames ( prefixCls , { [ `${ prefixCls } -rtl` ] : isRTL } , className ) ;
96
97
const mergedData = data || EMPTY_DATA ;
97
98
const componentRef = useRef < HTMLDivElement > ( ) ;
98
99
const fillerInnerRef = useRef < HTMLDivElement > ( ) ;
99
- const scrollBarRef = useRef < any > ( ) ; // Hack on scrollbar to enable flash call
100
+ const scrollBarRef = useRef < ScrollBarRef > ( ) ; // Hack on scrollbar to enable flash call
101
+
102
+ // =============================== Item Key ===============================
103
+
104
+ const [ offsetTop , setOffsetTop ] = useState ( 0 ) ;
105
+ const [ offsetLeft , setOffsetLeft ] = useState ( 0 ) ;
106
+ const [ scrollMoving , setScrollMoving ] = useState ( false ) ;
107
+
108
+ const onScrollbarStartMove = ( ) => {
109
+ setScrollMoving ( true ) ;
110
+ } ;
111
+ const onScrollbarStopMove = ( ) => {
112
+ setScrollMoving ( false ) ;
113
+ } ;
100
114
101
115
// =============================== Item Key ===============================
102
116
const getKey = React . useCallback < GetKey < T > > (
@@ -115,7 +129,7 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
115
129
116
130
// ================================ Scroll ================================
117
131
function syncScrollTop ( newTop : number | ( ( prev : number ) => number ) ) {
118
- setScrollTop ( ( origin ) => {
132
+ setOffsetTop ( ( origin ) => {
119
133
let value : number ;
120
134
if ( typeof newTop === 'function' ) {
121
135
value = newTop ( origin ) ;
@@ -180,13 +194,13 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
180
194
const currentItemBottom = itemTop + ( cacheHeight === undefined ? itemHeight : cacheHeight ) ;
181
195
182
196
// Check item top in the range
183
- if ( currentItemBottom >= scrollTop && startIndex === undefined ) {
197
+ if ( currentItemBottom >= offsetTop && startIndex === undefined ) {
184
198
startIndex = i ;
185
199
startOffset = itemTop ;
186
200
}
187
201
188
202
// Check item bottom in the range. We will render additional one item for motion usage
189
- if ( currentItemBottom > scrollTop + height && endIndex === undefined ) {
203
+ if ( currentItemBottom > offsetTop + height && endIndex === undefined ) {
190
204
endIndex = i ;
191
205
}
192
206
@@ -213,7 +227,7 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
213
227
end : endIndex ,
214
228
offset : startOffset ,
215
229
} ;
216
- } , [ inVirtual , useVirtual , scrollTop , mergedData , heightUpdatedMark , height ] ) ;
230
+ } , [ inVirtual , useVirtual , offsetTop , mergedData , heightUpdatedMark , height ] ) ;
217
231
218
232
rangeRef . current . start = start ;
219
233
rangeRef . current . end = end ;
@@ -232,21 +246,26 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
232
246
return newTop ;
233
247
}
234
248
235
- const isScrollAtTop = scrollTop <= 0 ;
236
- const isScrollAtBottom = scrollTop >= maxScrollHeight ;
249
+ const isScrollAtTop = offsetTop <= 0 ;
250
+ const isScrollAtBottom = offsetTop >= maxScrollHeight ;
237
251
238
252
const originScroll = useOriginScroll ( isScrollAtTop , isScrollAtBottom ) ;
239
253
240
254
// ================================ Scroll ================================
241
- function onScrollBar ( newScrollTop : number ) {
242
- const newTop = newScrollTop ;
243
- syncScrollTop ( newTop ) ;
255
+ function onScrollBar ( newScrollOffset : number , horizontal ?: boolean ) {
256
+ const newOffset = newScrollOffset ;
257
+
258
+ if ( horizontal ) {
259
+ setOffsetLeft ( newOffset ) ;
260
+ } else {
261
+ syncScrollTop ( newOffset ) ;
262
+ }
244
263
}
245
264
246
265
// When data size reduce. It may trigger native scroll event back to fit scroll position
247
266
function onFallbackScroll ( e : React . UIEvent < HTMLDivElement > ) {
248
267
const { scrollTop : newScrollTop } = e . currentTarget ;
249
- if ( newScrollTop !== scrollTop ) {
268
+ if ( newScrollTop !== offsetTop ) {
250
269
syncScrollTop ( newScrollTop ) ;
251
270
}
252
271
@@ -285,19 +304,15 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
285
304
}
286
305
}
287
306
288
- componentRef . current . addEventListener ( 'wheel' , onRawWheel ) ;
289
- componentRef . current . addEventListener ( 'DOMMouseScroll' , onFireFoxScroll as any ) ;
290
- componentRef . current . addEventListener ( 'MozMousePixelScroll' , onMozMousePixelScroll ) ;
307
+ const componentEle = componentRef . current ;
308
+ componentEle . addEventListener ( 'wheel' , onRawWheel ) ;
309
+ componentEle . addEventListener ( 'DOMMouseScroll' , onFireFoxScroll as any ) ;
310
+ componentEle . addEventListener ( 'MozMousePixelScroll' , onMozMousePixelScroll ) ;
291
311
292
312
return ( ) => {
293
- if ( componentRef . current ) {
294
- componentRef . current . removeEventListener ( 'wheel' , onRawWheel ) ;
295
- componentRef . current . removeEventListener ( 'DOMMouseScroll' , onFireFoxScroll as any ) ;
296
- componentRef . current . removeEventListener (
297
- 'MozMousePixelScroll' ,
298
- onMozMousePixelScroll as any ,
299
- ) ;
300
- }
313
+ componentEle . removeEventListener ( 'wheel' , onRawWheel ) ;
314
+ componentEle . removeEventListener ( 'DOMMouseScroll' , onFireFoxScroll as any ) ;
315
+ componentEle . removeEventListener ( 'MozMousePixelScroll' , onMozMousePixelScroll as any ) ;
301
316
} ;
302
317
} , [ useVirtual ] ) ;
303
318
@@ -339,19 +354,29 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
339
354
if ( useVirtual ) {
340
355
componentStyle . overflowY = 'hidden' ;
341
356
357
+ if ( scrollWidth ) {
358
+ componentStyle . overflowX = 'hidden' ;
359
+ }
360
+
342
361
if ( scrollMoving ) {
343
362
componentStyle . pointerEvents = 'none' ;
344
363
}
345
364
}
346
365
}
347
366
367
+ const containerProps : React . HTMLAttributes < HTMLDivElement > = { } ;
368
+ if ( isRTL ) {
369
+ containerProps . dir = 'rtl' ;
370
+ }
371
+
348
372
return (
349
373
< div
350
374
style = { {
351
375
...style ,
352
376
position : 'relative' ,
353
377
} }
354
378
className = { mergedClassName }
379
+ { ...containerProps }
355
380
{ ...restProps }
356
381
>
357
382
< Component
@@ -363,10 +388,13 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
363
388
< Filler
364
389
prefixCls = { prefixCls }
365
390
height = { scrollHeight }
366
- offset = { offset }
391
+ offsetX = { offsetLeft }
392
+ offsetY = { offset }
393
+ scrollWidth = { scrollWidth }
367
394
onInnerResize = { collectHeight }
368
395
ref = { fillerInnerRef }
369
396
innerProps = { innerProps }
397
+ rtl = { isRTL }
370
398
>
371
399
{ listChildren }
372
400
</ Filler >
@@ -376,18 +404,27 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
376
404
< ScrollBar
377
405
ref = { scrollBarRef }
378
406
prefixCls = { prefixCls }
379
- scrollTop = { scrollTop }
407
+ scrollOffset = { offsetTop }
408
+ scrollRange = { scrollHeight }
409
+ rtl = { isRTL }
410
+ onScroll = { onScrollBar }
411
+ onStartMove = { onScrollbarStartMove }
412
+ onStopMove = { onScrollbarStopMove }
380
413
height = { height }
381
- scrollHeight = { scrollHeight }
382
- count = { mergedData . length }
383
- direction = { direction }
414
+ />
415
+ ) }
416
+
417
+ { useVirtual && scrollWidth && (
418
+ < ScrollBar
419
+ ref = { scrollBarRef }
420
+ prefixCls = { prefixCls }
421
+ scrollOffset = { offsetLeft }
422
+ scrollRange = { scrollWidth }
423
+ rtl = { isRTL }
384
424
onScroll = { onScrollBar }
385
- onStartMove = { ( ) => {
386
- setScrollMoving ( true ) ;
387
- } }
388
- onStopMove = { ( ) => {
389
- setScrollMoving ( false ) ;
390
- } }
425
+ onStartMove = { onScrollbarStartMove }
426
+ onStopMove = { onScrollbarStopMove }
427
+ horizontal
391
428
/>
392
429
) }
393
430
</ div >
0 commit comments