@@ -107,7 +107,7 @@ interface ListState<T> {
107
107
* # located item
108
108
* The base position item which other items position calculate base on.
109
109
*/
110
- class List < T > extends React . Component < ListProps < T > , ListState < T > > {
110
+ class List < T = any > extends React . Component < ListProps < T > , ListState < T > > {
111
111
static defaultProps = {
112
112
itemHeight : 15 ,
113
113
data : [ ] ,
@@ -404,7 +404,7 @@ class List<T> extends React.Component<ListProps<T>, ListState<T>> {
404
404
return this . getItemKey ( item , mergedProps ) ;
405
405
} ;
406
406
407
- public getItemKey = ( item : T , props ?: Partial < ListProps < T > > ) => {
407
+ public getItemKey = ( item : T , props ?: Partial < ListProps < T > > ) : string => {
408
408
const { itemKey } = props || this . props ;
409
409
410
410
return typeof itemKey === 'function' ? itemKey ( item ) : item [ itemKey ] ;
@@ -413,8 +413,8 @@ class List<T> extends React.Component<ListProps<T>, ListState<T>> {
413
413
/**
414
414
* Collect current rendered dom element item heights
415
415
*/
416
- public collectItemHeights = ( ) => {
417
- const { startIndex, endIndex } = this . state ;
416
+ public collectItemHeights = ( range ?: { startIndex : number ; endIndex : number } ) => {
417
+ const { startIndex, endIndex } = range || this . state ;
418
418
const { data } = this . props ;
419
419
420
420
// Record here since measure item height will get warning in `render`
@@ -429,8 +429,127 @@ class List<T> extends React.Component<ListProps<T>, ListState<T>> {
429
429
}
430
430
} ;
431
431
432
- public scrollTo ( scrollTop : number ) {
433
- this . listRef . current . scrollTop = scrollTop ;
432
+ public scrollTo ( scrollTop : number ) : void ;
433
+
434
+ public scrollTo ( config : { index : number ; align ?: 'top' | 'bottom' | 'auto' } ) : void ;
435
+
436
+ public scrollTo ( arg0 : any ) {
437
+ // Number top
438
+ if ( typeof arg0 === 'object' ) {
439
+ const { isVirtual } = this . state ;
440
+ const { height, itemHeight, data } = this . props ;
441
+ const { index, align = 'auto' } = arg0 ;
442
+ const itemCount = Math . ceil ( height / itemHeight ) ;
443
+ const item = data [ index ] ;
444
+ if ( item ) {
445
+ const { clientHeight } = this . listRef . current ;
446
+
447
+ if ( isVirtual ) {
448
+ // Calculate related data
449
+ const { itemIndex, itemOffsetPtg, startIndex, endIndex } = this . state ;
450
+
451
+ const relativeLocatedItemTop = getItemRelativeTop ( {
452
+ itemIndex,
453
+ itemOffsetPtg,
454
+ itemElementHeights : this . itemElementHeights ,
455
+ scrollPtg : getElementScrollPercentage ( this . listRef . current ) ,
456
+ clientHeight,
457
+ getItemKey : this . getIndexKey ,
458
+ } ) ;
459
+
460
+ // We will force render related items to collect height for re-location
461
+ this . setState (
462
+ {
463
+ startIndex : Math . max ( 0 , index - itemCount ) ,
464
+ endIndex : Math . min ( data . length - 1 , index + itemCount ) ,
465
+ } ,
466
+ ( ) => {
467
+ this . collectItemHeights ( ) ;
468
+
469
+ // Calculate related top
470
+ let relativeTop : number ;
471
+ let mergedAlgin = align ;
472
+
473
+ if ( align === 'auto' ) {
474
+ let shouldChange = true ;
475
+
476
+ // Check if exist in the visible range
477
+ if ( Math . abs ( itemIndex - index ) < itemCount ) {
478
+ let itemTop = relativeLocatedItemTop ;
479
+ if ( index < itemIndex ) {
480
+ for ( let i = index ; i < itemIndex ; i += 1 ) {
481
+ const eleKey = this . getIndexKey ( i ) ;
482
+ itemTop -= this . itemElementHeights [ eleKey ] || 0 ;
483
+ }
484
+ } else {
485
+ for ( let i = itemIndex ; i <= index ; i += 1 ) {
486
+ const eleKey = this . getIndexKey ( i ) ;
487
+ itemTop += this . itemElementHeights [ eleKey ] || 0 ;
488
+ }
489
+ }
490
+
491
+ shouldChange = itemTop <= 0 || itemTop >= clientHeight ;
492
+ }
493
+
494
+ if ( shouldChange ) {
495
+ // Out of range will fall back to position align
496
+ mergedAlgin = index < itemIndex ? 'top' : 'bottom' ;
497
+ } else {
498
+ this . setState ( {
499
+ startIndex,
500
+ endIndex,
501
+ } ) ;
502
+ return ;
503
+ }
504
+ }
505
+
506
+ // Align with position should make scroll happen
507
+ if ( mergedAlgin === 'top' ) {
508
+ relativeTop = 0 ;
509
+ } else if ( mergedAlgin === 'bottom' ) {
510
+ const eleKey = this . getItemKey ( item ) ;
511
+
512
+ relativeTop = clientHeight - this . itemElementHeights [ eleKey ] || 0 ;
513
+ }
514
+
515
+ this . internalScrollTo ( {
516
+ itemIndex : index ,
517
+ relativeTop,
518
+ } ) ;
519
+ } ,
520
+ ) ;
521
+ } else {
522
+ // Raw list without virtual scroll set position directly
523
+ this . collectItemHeights ( { startIndex : 0 , endIndex : data . length - 1 } ) ;
524
+ let mergedAlgin = align ;
525
+
526
+ // Collection index item position
527
+ const indexItemHeight = this . itemElementHeights [ this . getIndexKey ( index ) ] ;
528
+ let itemTop = 0 ;
529
+ for ( let i = 0 ; i < index ; i += 1 ) {
530
+ const eleKey = this . getIndexKey ( i ) ;
531
+ itemTop += this . itemElementHeights [ eleKey ] || 0 ;
532
+ }
533
+ const itemBottom = itemTop + indexItemHeight ;
534
+
535
+ if ( mergedAlgin === 'auto' ) {
536
+ if ( itemTop < this . listRef . current . scrollTop ) {
537
+ mergedAlgin = 'top' ;
538
+ } else if ( itemBottom > this . listRef . current . scrollTop + clientHeight ) {
539
+ mergedAlgin = 'bottom' ;
540
+ }
541
+ }
542
+
543
+ if ( mergedAlgin === 'top' ) {
544
+ this . listRef . current . scrollTop = itemTop ;
545
+ } else if ( mergedAlgin === 'bottom' ) {
546
+ this . listRef . current . scrollTop = itemTop - ( clientHeight - indexItemHeight ) ;
547
+ }
548
+ }
549
+ }
550
+ } else {
551
+ this . listRef . current . scrollTop = arg0 ;
552
+ }
434
553
}
435
554
436
555
public internalScrollTo ( relativeScroll : RelativeScroll ) : void {
0 commit comments