1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import * as React from 'react';
    
  11. import {memo} from 'react';
    
  12. import {areEqual} from 'react-window';
    
  13. import {getGradientColor} from './utils';
    
  14. 
    
  15. import styles from './SnapshotCommitListItem.css';
    
  16. 
    
  17. import type {ItemData} from './SnapshotCommitList';
    
  18. 
    
  19. type Props = {
    
  20.   data: ItemData,
    
  21.   index: number,
    
  22.   style: Object,
    
  23.   ...
    
  24. };
    
  25. 
    
  26. function SnapshotCommitListItem({data: itemData, index, style}: Props) {
    
  27.   const {
    
  28.     filteredCommitIndices,
    
  29.     maxDuration,
    
  30.     selectedCommitIndex,
    
  31.     selectCommitIndex,
    
  32.     setHoveredCommitIndex,
    
  33.     startCommitDrag,
    
  34.     totalDurations,
    
  35.   } = itemData;
    
  36. 
    
  37.   index = filteredCommitIndices[index];
    
  38. 
    
  39.   const totalDuration = totalDurations[index];
    
  40. 
    
  41.   // Use natural cbrt for bar height.
    
  42.   // This prevents one (or a few) outliers from squishing the majority of other commits.
    
  43.   // So rather than e.g. _█_ we get something more like e.g. ▄█_
    
  44.   const heightScale =
    
  45.     Math.min(
    
  46.       1,
    
  47.       Math.max(0, Math.cbrt(totalDuration) / Math.cbrt(maxDuration)),
    
  48.     ) || 0;
    
  49. 
    
  50.   // Use a linear scale for color.
    
  51.   // This gives some visual contrast between cheaper and more expensive commits
    
  52.   // and somewhat compensates for the cbrt scale height.
    
  53.   const colorScale = Math.min(1, Math.max(0, totalDuration / maxDuration)) || 0;
    
  54. 
    
  55.   const isSelected = selectedCommitIndex === index;
    
  56. 
    
  57.   // Leave a 1px gap between snapshots
    
  58.   const width = parseFloat(style.width) - 1;
    
  59. 
    
  60.   const handleMouseDown = ({buttons, target}: any) => {
    
  61.     if (buttons === 1) {
    
  62.       selectCommitIndex(index);
    
  63.       startCommitDrag({
    
  64.         commitIndex: index,
    
  65.         left: target.getBoundingClientRect().left,
    
  66.         sizeIncrement: parseFloat(style.width),
    
  67.       });
    
  68.     }
    
  69.   };
    
  70. 
    
  71.   let backgroundColor;
    
  72.   if (!isSelected && totalDuration > 0) {
    
  73.     backgroundColor = getGradientColor(colorScale);
    
  74.   }
    
  75. 
    
  76.   return (
    
  77.     <div
    
  78.       className={styles.Outer}
    
  79.       onMouseDown={handleMouseDown}
    
  80.       onMouseEnter={() => setHoveredCommitIndex(index)}
    
  81.       style={{
    
  82.         ...style,
    
  83.         width,
    
  84.         borderBottom: isSelected
    
  85.           ? '3px solid var(--color-tab-selected-border)'
    
  86.           : undefined,
    
  87.       }}>
    
  88.       <div
    
  89.         className={isSelected ? styles.InnerSelected : styles.Inner}
    
  90.         style={{
    
  91.           height: `${Math.round(heightScale * 100)}%`,
    
  92.           backgroundColor,
    
  93.         }}
    
  94.       />
    
  95.     </div>
    
  96.   );
    
  97. }
    
  98. 
    
  99. export default (memo(
    
  100.   SnapshotCommitListItem,
    
  101.   areEqual,
    
  102. ): React.ComponentType<Props>);