github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/webapp/javascript/components/FileList.tsx (about) 1 import React, { useMemo } from 'react'; 2 import { format, parseISO } from 'date-fns'; 3 4 import { Maybe } from '@webapp/util/fp'; 5 import { AllProfiles } from '@webapp/models/adhoc'; 6 import TableUI, { useTableSort, BodyRow } from '@webapp/ui/Table'; 7 import CheckIcon from './CheckIcon'; 8 import styles from './FileList.module.scss'; 9 10 const dateModifiedColName = 'updatedAt'; 11 const fileNameColName = 'name'; 12 const headRow = [ 13 { name: fileNameColName, label: 'Filename', sortable: 1 }, 14 { 15 name: dateModifiedColName, 16 label: 'Date Modified', 17 sortable: 1, 18 default: true, 19 }, 20 ]; 21 22 const getBodyRows = ( 23 sortedProfilesIds: AllProfiles[0][], 24 onProfileSelected: (id: string) => void, 25 selectedProfileId: Maybe<string> 26 ): BodyRow[] => { 27 return sortedProfilesIds.reduce((acc, profile) => { 28 const isRowSelected = selectedProfileId.mapOr( 29 false, 30 (profId) => profId === profile.id 31 ); 32 33 const date = parseISO(profile.updatedAt); 34 const timeString = `${format(date, 'MMM d, yyyy')} at ${format( 35 date, 36 'h:mm a' 37 )}`; 38 39 acc.push({ 40 cells: [ 41 { 42 value: ( 43 <div className={styles.profileName}> 44 <span title={profile.name}>{profile.name}</span> 45 {isRowSelected && <CheckIcon className={styles.checkIcon} />} 46 </div> 47 ), 48 }, 49 { value: timeString }, 50 ], 51 onClick: () => { 52 // Optimize to not reload the same one 53 if ( 54 selectedProfileId.isJust && 55 selectedProfileId.value === profile.id 56 ) { 57 return; 58 } 59 onProfileSelected(profile.id); 60 }, 61 isRowSelected, 62 }); 63 64 return acc; 65 }, [] as BodyRow[]); 66 }; 67 68 interface FileListProps { 69 className?: string; 70 profilesList: AllProfiles; 71 onProfileSelected: (id: string) => void; 72 selectedProfileId: Maybe<string>; 73 } 74 75 function FileList(props: FileListProps) { 76 const { 77 profilesList: profiles, 78 onProfileSelected, 79 className, 80 selectedProfileId, 81 } = props; 82 83 const { sortBy, sortByDirection, ...rest } = useTableSort(headRow); 84 const sortedProfilesIds = useMemo(() => { 85 const m = sortByDirection === 'asc' ? 1 : -1; 86 87 let sorted: AllProfiles[number][] = []; 88 89 if (profiles) { 90 const filesInfo = Object.values(profiles); 91 92 switch (sortBy) { 93 case fileNameColName: 94 sorted = filesInfo.sort( 95 (a, b) => m * a[sortBy].localeCompare(b[sortBy]) 96 ); 97 break; 98 case dateModifiedColName: 99 sorted = filesInfo.sort( 100 (a, b) => 101 m * 102 (new Date(a[sortBy]).getTime() - new Date(b[sortBy]).getTime()) 103 ); 104 break; 105 default: 106 sorted = filesInfo; 107 } 108 } 109 110 return sorted; 111 }, [profiles, sortBy, sortByDirection]); 112 113 const tableBodyProps = profiles 114 ? { 115 type: 'filled' as const, 116 bodyRows: getBodyRows( 117 sortedProfilesIds, 118 onProfileSelected, 119 selectedProfileId 120 ), 121 } 122 : { type: 'not-filled' as const, value: '' }; 123 124 return ( 125 <> 126 <div className={`${styles.tableContainer} ${className}`}> 127 <TableUI 128 /* eslint-disable-next-line react/jsx-props-no-spreading */ 129 {...rest} 130 sortBy={sortBy} 131 sortByDirection={sortByDirection} 132 table={{ headRow, ...tableBodyProps }} 133 /> 134 </div> 135 </> 136 ); 137 } 138 139 export default FileList;