InfiniteVirtualList.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import type { CSSProperties, FC } from 'react'
  2. import React from 'react'
  3. import { FixedSizeList as List } from 'react-window'
  4. import InfiniteLoader from 'react-window-infinite-loader'
  5. import SegmentCard from './SegmentCard'
  6. import s from './style.module.css'
  7. import type { SegmentDetailModel } from '@/models/datasets'
  8. type IInfiniteVirtualListProps = {
  9. hasNextPage?: boolean // Are there more items to load? (This information comes from the most recent API request.)
  10. isNextPageLoading: boolean // Are we currently loading a page of items? (This may be an in-flight flag in your Redux store for example.)
  11. items: Array<SegmentDetailModel[]> // Array of items loaded so far.
  12. loadNextPage: () => Promise<any> // Callback function responsible for loading the next page of items.
  13. onClick: (detail: SegmentDetailModel) => void
  14. onChangeSwitch: (segId: string, enabled: boolean) => Promise<void>
  15. }
  16. const InfiniteVirtualList: FC<IInfiniteVirtualListProps> = ({
  17. hasNextPage,
  18. isNextPageLoading,
  19. items,
  20. loadNextPage,
  21. onClick: onClickCard,
  22. onChangeSwitch,
  23. }) => {
  24. // If there are more items to be loaded then add an extra row to hold a loading indicator.
  25. const itemCount = hasNextPage ? items.length + 1 : items.length
  26. // Only load 1 page of items at a time.
  27. // Pass an empty callback to InfiniteLoader in case it asks us to load more than once.
  28. const loadMoreItems = isNextPageLoading ? () => { } : loadNextPage
  29. // Every row is loaded except for our loading indicator row.
  30. const isItemLoaded = (index: number) => !hasNextPage || index < items.length
  31. // Render an item or a loading indicator.
  32. const Item = ({ index, style }: { index: number; style: CSSProperties }) => {
  33. let content
  34. if (!isItemLoaded(index)) {
  35. content = (
  36. <>
  37. {[1, 2, 3].map(v => (
  38. <SegmentCard loading={true} detail={{ position: v } as any} />
  39. ))}
  40. </>
  41. )
  42. }
  43. else {
  44. content = items[index].map(segItem => (
  45. <SegmentCard
  46. key={segItem.id}
  47. detail={segItem}
  48. onClick={() => onClickCard(segItem)}
  49. onChangeSwitch={onChangeSwitch}
  50. loading={false}
  51. />
  52. ))
  53. }
  54. return (
  55. <div style={style} className={s.cardWrapper}>
  56. {content}
  57. </div>
  58. )
  59. }
  60. return (
  61. <InfiniteLoader
  62. itemCount={itemCount}
  63. isItemLoaded={isItemLoaded}
  64. loadMoreItems={loadMoreItems}
  65. >
  66. {({ onItemsRendered, ref }) => (
  67. <List
  68. ref={ref}
  69. className="List"
  70. height={800}
  71. width={'100%'}
  72. itemSize={200}
  73. itemCount={itemCount}
  74. onItemsRendered={onItemsRendered}
  75. >
  76. {Item}
  77. </List>
  78. )}
  79. </InfiniteLoader>
  80. )
  81. }
  82. export default InfiniteVirtualList