openreplay/frontend/app/components/Session_/OverviewPanel/components/EventRow/EventRow.tsx
Delirium 38594319f0
Player improvs (#2835)
* ui: fix performance bottlenecks, split data sources in devtools panes

* ui: move xray warn

* Player ux improvements (#2834)

* Player UX improvements.

DevTools (Including multi-tab)
Actions panel (User events, Click maps, Tag Elements)

* ui: remove unused imports, remove str templ classnames

---------

Co-authored-by: Sudheer Salavadi <connect.uxmaster@gmail.com>

---------

Co-authored-by: Sudheer Salavadi <connect.uxmaster@gmail.com>
2024-12-10 10:31:09 +01:00

141 lines
4.3 KiB
TypeScript

import React from 'react';
import cn from 'classnames';
import { getTimelinePosition } from 'App/utils';
import { Icon } from 'UI';
import { InfoCircleOutlined} from '@ant-design/icons'
import {Tooltip} from 'antd';
import PerformanceGraph from '../PerformanceGraph';
interface Props {
list?: any[];
title: string;
message?: string;
className?: string;
endTime?: number;
renderElement?: (item: any, isGrouped: boolean) => React.ReactNode;
isGraph?: boolean;
zIndex?: number;
noMargin?: boolean;
disabled?: boolean;
}
const EventRow = React.memo((props: Props) => {
const { title, className, list = [], endTime = 0, isGraph = false, message = '', disabled } = props;
const scale = 100 / endTime;
const _list =
isGraph ? [] :
React.useMemo(() => {
const tolerance = 2; // within what %s to group items
const groupedItems = [];
let currentGroup = [];
let currentLeft = 0;
for (let i = 0; i < list.length; i++) {
const item = list[i];
const spread = item.toJS ? { ...item.toJS() } : item;
const left: number = getTimelinePosition(item.time, scale);
const itemWithLeft = { ...spread, left };
if (currentGroup.length === 0) {
currentGroup.push(itemWithLeft);
currentLeft = left;
} else {
if (Math.abs(left - currentLeft) <= tolerance) {
currentGroup.push(itemWithLeft);
} else {
if (currentGroup.length > 1) {
const leftValues = currentGroup.map(item => item.left);
const minLeft = Math.min(...leftValues);
const maxLeft = Math.max(...leftValues);
const middleLeft = (minLeft + maxLeft) / 2;
groupedItems.push({
isGrouped: true,
items: currentGroup,
left: middleLeft,
});
} else {
groupedItems.push({
isGrouped: false,
items: [currentGroup[0]],
left: currentGroup[0].left,
});
}
currentGroup = [itemWithLeft];
currentLeft = left;
}
}
}
if (currentGroup.length > 1) {
const leftValues = currentGroup.map(item => item.left);
const minLeft = Math.min(...leftValues);
const maxLeft = Math.max(...leftValues);
const middleLeft = (minLeft + maxLeft) / 2;
groupedItems.push({
isGrouped: true,
items: currentGroup,
left: middleLeft,
});
} else if (currentGroup.length === 1) {
groupedItems.push({
isGrouped: false,
items: [currentGroup[0]],
left: currentGroup[0].left,
});
}
return groupedItems;
}, [list.length]);
return (
<div
className={cn('w-full flex flex-col py-2', className)}
style={{ height: isGraph ? 60 : 50 }}
>
<div
className={cn(
'uppercase text-sm flex items-center py-1 gap-1',
props.noMargin ? '' : 'ml-2'
)}
>
<div
style={{ zIndex: props.zIndex ? props.zIndex : undefined }}
className="leading-none mt-0.5"
>
{title}
</div>
<Tooltip title={message} placement='left'>
<InfoCircleOutlined className='text-neutral-400' />
</Tooltip>
</div>
<div className="relative w-full" style={{ zIndex: props.zIndex ? props.zIndex : undefined }}>
{isGraph ? (
<PerformanceGraph disabled={disabled} list={list} />
) : _list.length > 0 ? (
_list.map((item: { items: any[], left: number, isGrouped: boolean }, index: number) => {
const left = item.left
return (
<div
key={index}
className="absolute"
style={{
left: `clamp(0%, calc(${left}% - 7px), calc(100% - 14px))`,
zIndex: props.zIndex ? props.zIndex : undefined,
}}
>
{props.renderElement ? props.renderElement(item.items, item.isGrouped) : null}
</div>
);
})
) : (
<div className={cn('color-gray-medium text-xs', props.noMargin ? '' : 'ml-2')}>
None captured.
</div>
)}
</div>
</div>
);
});
export default EventRow;