list.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import {Fragment} from 'react';
  2. import {AutoSizer, List as ReactVirtualizedList} from 'react-virtualized';
  3. import Row from './row';
  4. import {ItemsAfterFilter} from './types';
  5. type RowProps = Pick<
  6. React.ComponentProps<typeof Row>,
  7. 'itemSize' | 'inputValue' | 'getItemProps' | 'registerVisibleItem'
  8. >;
  9. type Props = {
  10. /**
  11. * The item index that is currently isActive
  12. */
  13. highlightedIndex: number;
  14. /**
  15. * Flat item array or grouped item array
  16. */
  17. items: ItemsAfterFilter;
  18. /**
  19. * Max height of dropdown menu. Units are assumed as `px`
  20. */
  21. maxHeight: number;
  22. /**
  23. * Callback for when dropdown menu is being scrolled
  24. */
  25. onScroll?: () => void;
  26. /**
  27. * Supplying this height will force the dropdown menu to be a virtualized
  28. * list. This is very useful (and probably required) if you have a large list.
  29. * e.g. Project selector with many projects.
  30. *
  31. * Currently, our implementation of the virtualized list requires a fixed
  32. * height.
  33. */
  34. virtualizedHeight?: number;
  35. /**
  36. * If you use grouping with virtualizedHeight, the labels will be that height
  37. * unless specified here
  38. */
  39. virtualizedLabelHeight?: number;
  40. } & RowProps;
  41. function getHeight(
  42. items: ItemsAfterFilter,
  43. maxHeight: number,
  44. virtualizedHeight: number,
  45. virtualizedLabelHeight?: number
  46. ) {
  47. const minHeight = virtualizedLabelHeight
  48. ? items.reduce(
  49. (a, r) => a + (r.groupLabel ? virtualizedLabelHeight : virtualizedHeight),
  50. 0
  51. )
  52. : items.length * virtualizedHeight;
  53. return Math.min(minHeight, maxHeight);
  54. }
  55. const List = ({
  56. virtualizedHeight,
  57. virtualizedLabelHeight,
  58. onScroll,
  59. items,
  60. highlightedIndex,
  61. maxHeight,
  62. ...rowProps
  63. }: Props) => {
  64. if (virtualizedHeight) {
  65. return (
  66. <AutoSizer disableHeight>
  67. {({width}) => (
  68. <ReactVirtualizedList
  69. style={{outline: 'none'}}
  70. width={width}
  71. height={getHeight(
  72. items,
  73. maxHeight,
  74. virtualizedHeight,
  75. virtualizedLabelHeight
  76. )}
  77. onScroll={onScroll}
  78. rowCount={items.length}
  79. rowHeight={({index}) =>
  80. items[index].groupLabel && virtualizedLabelHeight
  81. ? virtualizedLabelHeight
  82. : virtualizedHeight
  83. }
  84. rowRenderer={({key, index, style}) => (
  85. <Row
  86. key={key}
  87. style={style}
  88. item={items[index]}
  89. isHighlighted={items[index].index === highlightedIndex}
  90. {...rowProps}
  91. />
  92. )}
  93. />
  94. )}
  95. </AutoSizer>
  96. );
  97. }
  98. return (
  99. <Fragment>
  100. {items.map((item, index) => (
  101. <Row
  102. // Using only the index of the row might not re-render properly,
  103. // because the items shift around the list
  104. key={`${item.value}-${index}`}
  105. item={item}
  106. isHighlighted={item.index === highlightedIndex}
  107. {...rowProps}
  108. />
  109. ))}
  110. </Fragment>
  111. );
  112. };
  113. export default List;