index.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import {useMemo, useRef} from 'react';
  2. import {
  3. AutoSizer,
  4. CellMeasurer,
  5. List as ReactVirtualizedList,
  6. ListRowProps,
  7. } from 'react-virtualized';
  8. import Placeholder from 'sentry/components/placeholder';
  9. import {t} from 'sentry/locale';
  10. import type {ReplayFrame} from 'sentry/utils/replays/types';
  11. import BreadcrumbRow from 'sentry/views/replays/detail/breadcrumbs/breadcrumbRow';
  12. import useScrollToCurrentItem from 'sentry/views/replays/detail/breadcrumbs/useScrollToCurrentItem';
  13. import FluidHeight from 'sentry/views/replays/detail/layout/fluidHeight';
  14. import NoRowRenderer from 'sentry/views/replays/detail/noRowRenderer';
  15. import TabItemContainer from 'sentry/views/replays/detail/tabItemContainer';
  16. import useVirtualizedList from 'sentry/views/replays/detail/useVirtualizedList';
  17. import useVirtualizedInspector from '../useVirtualizedInspector';
  18. type Props = {
  19. frames: undefined | ReplayFrame[];
  20. startTimestampMs: number;
  21. };
  22. // Ensure this object is created once as it is an input to
  23. // `useVirtualizedList`'s memoization
  24. const cellMeasurer = {
  25. fixedWidth: true,
  26. minHeight: 53,
  27. };
  28. function Breadcrumbs({frames, startTimestampMs}: Props) {
  29. const listRef = useRef<ReactVirtualizedList>(null);
  30. // Keep a reference of object paths that are expanded (via <ObjectInspector>)
  31. // by log row, so they they can be restored as the Console pane is scrolling.
  32. // Due to virtualization, components can be unmounted as the user scrolls, so
  33. // state needs to be remembered.
  34. //
  35. // Note that this is intentionally not in state because we do not want to
  36. // re-render when items are expanded/collapsed, though it may work in state as well.
  37. const expandPathsRef = useRef(new Map<number, Set<string>>());
  38. const deps = useMemo(() => [frames], [frames]);
  39. const {cache, updateList} = useVirtualizedList({
  40. cellMeasurer,
  41. ref: listRef,
  42. deps,
  43. });
  44. const {handleDimensionChange} = useVirtualizedInspector({
  45. cache,
  46. listRef,
  47. expandPathsRef,
  48. });
  49. useScrollToCurrentItem({
  50. frames,
  51. ref: listRef,
  52. });
  53. const renderRow = ({index, key, style, parent}: ListRowProps) => {
  54. const item = (frames || [])[index];
  55. return (
  56. <CellMeasurer
  57. cache={cache}
  58. columnIndex={0}
  59. key={key}
  60. parent={parent}
  61. rowIndex={index}
  62. >
  63. <BreadcrumbRow
  64. index={index}
  65. frame={item}
  66. startTimestampMs={startTimestampMs}
  67. style={style}
  68. expandPaths={Array.from(expandPathsRef.current?.get(index) || [])}
  69. onDimensionChange={handleDimensionChange}
  70. />
  71. </CellMeasurer>
  72. );
  73. };
  74. return (
  75. <FluidHeight>
  76. <TabItemContainer>
  77. {frames ? (
  78. <AutoSizer onResize={updateList}>
  79. {({height, width}) => (
  80. <ReactVirtualizedList
  81. deferredMeasurementCache={cache}
  82. height={height}
  83. noRowsRenderer={() => (
  84. <NoRowRenderer unfilteredItems={frames} clearSearchTerm={() => {}}>
  85. {t('No breadcrumbs recorded')}
  86. </NoRowRenderer>
  87. )}
  88. overscanRowCount={5}
  89. ref={listRef}
  90. rowCount={frames.length}
  91. rowHeight={cache.rowHeight}
  92. rowRenderer={renderRow}
  93. width={width}
  94. />
  95. )}
  96. </AutoSizer>
  97. ) : (
  98. <Placeholder height="100%" />
  99. )}
  100. </TabItemContainer>
  101. </FluidHeight>
  102. );
  103. }
  104. export default Breadcrumbs;