index.tsx 3.9 KB

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