feat(ui) - overview - vertical line follow and jump
This commit is contained in:
parent
8805c84735
commit
930e502f25
8 changed files with 144 additions and 75 deletions
|
|
@ -10,6 +10,8 @@ import FeatureSelection from './components/FeatureSelection/FeatureSelection';
|
|||
import TimelinePointer from './components/TimelinePointer';
|
||||
import VerticalPointerLine from './components/VerticalPointerLine';
|
||||
import cn from 'classnames';
|
||||
import VerticalLine from './components/VerticalLine';
|
||||
import OverviewPanelContainer from './components/OverviewPanelContainer';
|
||||
|
||||
interface Props {
|
||||
resourceList: any[];
|
||||
|
|
@ -21,22 +23,24 @@ interface Props {
|
|||
}
|
||||
function OverviewPanel(props: Props) {
|
||||
const { resourceList, exceptionsList, eventsList, stackEventList, issuesList } = props;
|
||||
|
||||
const clickRageList = React.useMemo(() => {
|
||||
return eventsList.filter((item: any) => item.type === TYPES.CLICKRAGE);
|
||||
}, [eventsList]);
|
||||
// const scale = 100 / endTime;
|
||||
const [selectedFeatures, setSelectedFeatures] = React.useState(['PERFORMANCE', 'ERRORS', 'EVENTS']);
|
||||
|
||||
const resources: any = {
|
||||
NETWORK: resourceList,
|
||||
ERRORS: exceptionsList,
|
||||
EVENTS: stackEventList,
|
||||
CLICKRAGE: clickRageList,
|
||||
PERFORMANCE: issuesList,
|
||||
};
|
||||
const resources: any = React.useMemo(() => {
|
||||
return {
|
||||
NETWORK: resourceList,
|
||||
ERRORS: exceptionsList,
|
||||
EVENTS: stackEventList,
|
||||
CLICKRAGE: clickRageList,
|
||||
PERFORMANCE: issuesList,
|
||||
};
|
||||
}, [resourceList, exceptionsList, stackEventList, clickRageList, issuesList]);
|
||||
|
||||
return (
|
||||
<BottomBlock style={{ height: '263px' }}>
|
||||
<BottomBlock style={{ height: '260px' }}>
|
||||
<BottomBlock.Header>
|
||||
<span className="font-semibold color-gray-medium mr-4">Overview</span>
|
||||
<div className="flex items-center h-20">
|
||||
|
|
@ -44,9 +48,9 @@ function OverviewPanel(props: Props) {
|
|||
</div>
|
||||
</BottomBlock.Header>
|
||||
<BottomBlock.Content>
|
||||
<div className="overflow-x-auto overflow-y-hidden bg-gray-lightest">
|
||||
<OverviewPanelContainer>
|
||||
<TimelineScale />
|
||||
<div style={{ width: '100%' }} className="transition relative">
|
||||
<div style={{ width: '100%', height: '200px' }} className="transition relative">
|
||||
<VerticalPointerLine />
|
||||
{selectedFeatures.map((feature: any, index: number) => (
|
||||
<div className={cn('', { 'bg-white border-t border-b': index % 2 })}>
|
||||
|
|
@ -59,17 +63,20 @@ function OverviewPanel(props: Props) {
|
|||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</OverviewPanelContainer>
|
||||
</BottomBlock.Content>
|
||||
</BottomBlock>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect((state: any) => ({
|
||||
issuesList: state.getIn(['sessions', 'current', 'issues']),
|
||||
}), {
|
||||
toggleBottomBlock,
|
||||
})(
|
||||
export default connect(
|
||||
(state: any) => ({
|
||||
issuesList: state.getIn(['sessions', 'current', 'issues']),
|
||||
}),
|
||||
{
|
||||
toggleBottomBlock,
|
||||
}
|
||||
)(
|
||||
connectPlayer((state: any) => ({
|
||||
resourceList: state.resourceList.filter((r: any) => r.isRed() || r.isYellow()),
|
||||
exceptionsList: state.exceptionsList,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import React from 'react';
|
||||
import VerticalLine from '../VerticalLine';
|
||||
import { connectPlayer, Controls } from 'App/player';
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
endTime: number;
|
||||
}
|
||||
|
||||
function OverviewPanelContainer(props: Props) {
|
||||
const { endTime } = props;
|
||||
const [mouseX, setMouseX] = React.useState(0);
|
||||
const [mouseIn, setMouseIn] = React.useState(false);
|
||||
const onClickTrack = (e: any) => {
|
||||
const p = e.nativeEvent.offsetX / e.target.offsetWidth;
|
||||
const time = Math.max(Math.round(p * endTime), 0);
|
||||
Controls.jump(time);
|
||||
};
|
||||
|
||||
const onMouseMoveCapture = (e: any) => {
|
||||
if (!mouseIn) {
|
||||
return;
|
||||
}
|
||||
const p = e.nativeEvent.offsetX / e.target.offsetWidth;
|
||||
setMouseX(p * 100);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="overflow-x-auto overflow-y-hidden bg-gray-lightest"
|
||||
onClick={onClickTrack}
|
||||
onMouseMoveCapture={onMouseMoveCapture}
|
||||
// onMouseOver={() => setMouseIn(true)}
|
||||
// onMouseOut={() => setMouseIn(false)}
|
||||
>
|
||||
{mouseIn && <VerticalLine left={mouseX} className="border-gray-medium" />}
|
||||
<div className="">{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectPlayer((state: any) => ({
|
||||
endTime: state.endTime,
|
||||
}))(OverviewPanelContainer);
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './OverviewPanelContainer';
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
|
||||
interface Props {
|
||||
left: number;
|
||||
className?: string;
|
||||
height?: string;
|
||||
width?: string;
|
||||
}
|
||||
function VerticalLine(props: Props) {
|
||||
const { left, className = 'border-gray-dark', height = '221px', width = '1px' } = props;
|
||||
return <div className={cn('absolute border-r border-dashed z-10', className)} style={{ left: `${left}%`, height, width }} />;
|
||||
}
|
||||
|
||||
export default VerticalLine;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './VerticalLine'
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import { connectPlayer } from 'App/player';
|
||||
import VerticalLine from '../VerticalLine';
|
||||
|
||||
interface Props {
|
||||
time: number;
|
||||
|
|
@ -8,7 +9,7 @@ interface Props {
|
|||
function VerticalPointerLine(props: Props) {
|
||||
const { time, scale } = props;
|
||||
const left = time * scale;
|
||||
return <div className="absolute border-r border-teal border-dashed z-10" style={{ left: `${left}%`, height: '250px', width: '1px' }} />;
|
||||
return <VerticalLine left={left} className="border-teal" />;
|
||||
}
|
||||
|
||||
export default connectPlayer((state: any) => ({
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import cn from 'classnames';
|
||||
// import cn from 'classnames';
|
||||
import { connectPlayer, Controls } from 'Player';
|
||||
import { TimelinePointer, Icon } from 'UI';
|
||||
// import { TimelinePointer, Icon } from 'UI';
|
||||
import TimeTracker from './TimeTracker';
|
||||
import stl from './timeline.module.css';
|
||||
import { TYPES } from 'Types/session/event';
|
||||
|
|
@ -10,7 +10,7 @@ import { setTimelinePointer } from 'Duck/sessions';
|
|||
import DraggableCircle from './DraggableCircle';
|
||||
import CustomDragLayer from './CustomDragLayer';
|
||||
import { debounce } from 'App/utils';
|
||||
import { Tooltip } from 'react-tippy';
|
||||
// import { Tooltip } from 'react-tippy';
|
||||
|
||||
const BOUNDRY = 0;
|
||||
|
||||
|
|
@ -20,46 +20,46 @@ function getTimelinePosition(value, scale) {
|
|||
return pos > 100 ? 100 : pos;
|
||||
}
|
||||
|
||||
const getPointerIcon = (type) => {
|
||||
// exception,
|
||||
switch (type) {
|
||||
case 'fetch':
|
||||
return 'funnel/file-earmark-minus-fill';
|
||||
case 'exception':
|
||||
return 'funnel/exclamation-circle-fill';
|
||||
case 'log':
|
||||
return 'funnel/exclamation-circle-fill';
|
||||
case 'stack':
|
||||
return 'funnel/patch-exclamation-fill';
|
||||
case 'resource':
|
||||
return 'funnel/file-earmark-minus-fill';
|
||||
// const getPointerIcon = (type) => {
|
||||
// // exception,
|
||||
// switch (type) {
|
||||
// case 'fetch':
|
||||
// return 'funnel/file-earmark-minus-fill';
|
||||
// case 'exception':
|
||||
// return 'funnel/exclamation-circle-fill';
|
||||
// case 'log':
|
||||
// return 'funnel/exclamation-circle-fill';
|
||||
// case 'stack':
|
||||
// return 'funnel/patch-exclamation-fill';
|
||||
// case 'resource':
|
||||
// return 'funnel/file-earmark-minus-fill';
|
||||
|
||||
case 'dead_click':
|
||||
return 'funnel/dizzy';
|
||||
case 'click_rage':
|
||||
return 'funnel/dizzy';
|
||||
case 'excessive_scrolling':
|
||||
return 'funnel/mouse';
|
||||
case 'bad_request':
|
||||
return 'funnel/file-medical-alt';
|
||||
case 'missing_resource':
|
||||
return 'funnel/file-earmark-minus-fill';
|
||||
case 'memory':
|
||||
return 'funnel/sd-card';
|
||||
case 'cpu':
|
||||
return 'funnel/microchip';
|
||||
case 'slow_resource':
|
||||
return 'funnel/hourglass-top';
|
||||
case 'slow_page_load':
|
||||
return 'funnel/hourglass-top';
|
||||
case 'crash':
|
||||
return 'funnel/file-exclamation';
|
||||
case 'js_exception':
|
||||
return 'funnel/exclamation-circle-fill';
|
||||
}
|
||||
// case 'dead_click':
|
||||
// return 'funnel/dizzy';
|
||||
// case 'click_rage':
|
||||
// return 'funnel/dizzy';
|
||||
// case 'excessive_scrolling':
|
||||
// return 'funnel/mouse';
|
||||
// case 'bad_request':
|
||||
// return 'funnel/file-medical-alt';
|
||||
// case 'missing_resource':
|
||||
// return 'funnel/file-earmark-minus-fill';
|
||||
// case 'memory':
|
||||
// return 'funnel/sd-card';
|
||||
// case 'cpu':
|
||||
// return 'funnel/microchip';
|
||||
// case 'slow_resource':
|
||||
// return 'funnel/hourglass-top';
|
||||
// case 'slow_page_load':
|
||||
// return 'funnel/hourglass-top';
|
||||
// case 'crash':
|
||||
// return 'funnel/file-exclamation';
|
||||
// case 'js_exception':
|
||||
// return 'funnel/exclamation-circle-fill';
|
||||
// }
|
||||
|
||||
return 'info';
|
||||
};
|
||||
// return 'info';
|
||||
// };
|
||||
|
||||
let deboucneJump = () => null;
|
||||
@connectPlayer((state) => ({
|
||||
|
|
@ -73,11 +73,11 @@ let deboucneJump = () => null;
|
|||
disabled: state.cssLoading || state.messagesLoading || state.markedTargets,
|
||||
endTime: state.endTime,
|
||||
live: state.live,
|
||||
logList: state.logList,
|
||||
exceptionsList: state.exceptionsList,
|
||||
resourceList: state.resourceList,
|
||||
stackList: state.stackList,
|
||||
fetchList: state.fetchList,
|
||||
// logList: state.logList,
|
||||
// exceptionsList: state.exceptionsList,
|
||||
// resourceList: state.resourceList,
|
||||
// stackList: state.stackList,
|
||||
// fetchList: state.fetchList,
|
||||
}))
|
||||
@connect(
|
||||
(state) => ({
|
||||
|
|
@ -141,14 +141,14 @@ export default class Timeline extends React.PureComponent {
|
|||
skipIntervals,
|
||||
disabled,
|
||||
endTime,
|
||||
live,
|
||||
logList,
|
||||
exceptionsList,
|
||||
resourceList,
|
||||
clickRageTime,
|
||||
stackList,
|
||||
fetchList,
|
||||
issues,
|
||||
// live,
|
||||
// logList,
|
||||
// exceptionsList,
|
||||
// resourceList,
|
||||
// clickRageTime,
|
||||
// stackList,
|
||||
// fetchList,
|
||||
// issues,
|
||||
} = this.props;
|
||||
|
||||
const scale = 100 / endTime;
|
||||
|
|
@ -175,9 +175,9 @@ export default class Timeline extends React.PureComponent {
|
|||
/>
|
||||
))}
|
||||
<div className={stl.timeline} />
|
||||
{/* {events.map((e) => (
|
||||
{events.map((e) => (
|
||||
<div key={e.key} className={stl.event} style={{ left: `${getTimelinePosition(e.time, scale)}%` }} />
|
||||
))} */}
|
||||
))}
|
||||
{/* {issues.map((iss) => (
|
||||
<div
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ function ErrorDetails(props: Props) {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<div className="bg-white p-5 h-screen" style={{ width: '700px' }}>
|
||||
<div className="bg-white p-5 h-screen">
|
||||
{!sourcemapUploaded && (
|
||||
<div
|
||||
style={{ backgroundColor: 'rgba(204, 0, 0, 0.1)' }}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue