InfiniteVirtualList.tsx 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  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<void> // Callback function responsible for loading the next page of items.
  13. onClick: (detail: SegmentDetailModel) => void
  14. onChangeSwitch: (segId: string, enabled: boolean) => Promise<void>
  15. onDelete: (segId: string) => Promise<void>
  16. archived?: boolean
  17. embeddingAvailable: boolean
  18. }
  19. const InfiniteVirtualList: FC<IInfiniteVirtualListProps> = ({
  20. hasNextPage,
  21. isNextPageLoading,
  22. items,
  23. loadNextPage,
  24. onClick: onClickCard,
  25. onChangeSwitch,
  26. onDelete,
  27. archived,
  28. embeddingAvailable,
  29. }) => {
  30. // If there are more items to be loaded then add an extra row to hold a loading indicator.
  31. const itemCount = hasNextPage ? items.length + 1 : items.length
  32. // Only load 1 page of items at a time.
  33. // Pass an empty callback to InfiniteLoader in case it asks us to load more than once.
  34. const loadMoreItems = isNextPageLoading ? () => { } : loadNextPage
  35. // Every row is loaded except for our loading indicator row.
  36. const isItemLoaded = (index: number) => !hasNextPage || index < items.length
  37. // Render an item or a loading indicator.
  38. const Item = ({ index, style }: { index: number; style: CSSProperties }) => {
  39. let content
  40. if (!isItemLoaded(index)) {
  41. content = (
  42. <>
  43. {[1, 2, 3].map(v => (
  44. <SegmentCard key={v} loading={true} detail={{ position: v } as any} />
  45. ))}
  46. </>
  47. )
  48. }
  49. else {
  50. content = items[index].map(segItem => (
  51. <SegmentCard
  52. key={segItem.id}
  53. detail={segItem}
  54. onClick={() => onClickCard(segItem)}
  55. onChangeSwitch={onChangeSwitch}
  56. onDelete={onDelete}
  57. loading={false}
  58. archived={archived}
  59. embeddingAvailable={embeddingAvailable}
  60. />
  61. ))
  62. }
  63. return (
  64. <div style={style} className={s.cardWrapper}>
  65. {content}
  66. </div>
  67. )
  68. }
  69. return (
  70. <InfiniteLoader
  71. itemCount={itemCount}
  72. isItemLoaded={isItemLoaded}
  73. loadMoreItems={loadMoreItems}
  74. >
  75. {({ onItemsRendered, ref }) => (
  76. <List
  77. ref={ref}
  78. className="List"
  79. height={800}
  80. width={'100%'}
  81. itemSize={200}
  82. itemCount={itemCount}
  83. onItemsRendered={onItemsRendered}
  84. >
  85. {Item}
  86. </List>
  87. )}
  88. </InfiniteLoader>
  89. )
  90. }
  91. export default InfiniteVirtualList