@parca/profile 0.16.269 → 0.16.274
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/GraphTooltipArrow/Content.js +9 -36
- package/dist/GraphTooltipArrow/DockedGraphTooltip/index.js +16 -25
- package/dist/GraphTooltipArrow/index.d.ts +2 -1
- package/dist/GraphTooltipArrow/index.js +3 -5
- package/dist/MetricsGraph/MetricsContextMenu/index.d.ts +16 -0
- package/dist/MetricsGraph/MetricsContextMenu/index.js +27 -0
- package/dist/MetricsGraph/MetricsTooltip/index.d.ts +1 -2
- package/dist/MetricsGraph/MetricsTooltip/index.js +3 -4
- package/dist/MetricsGraph/index.d.ts +9 -3
- package/dist/MetricsGraph/index.js +23 -17
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts +19 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.js +75 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.d.ts +3 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.js +7 -8
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/index.js +20 -3
- package/dist/ProfileMetricsGraph/index.d.ts +7 -1
- package/dist/ProfileMetricsGraph/index.js +1 -1
- package/dist/ProfileSelector/index.js +23 -4
- package/dist/styles.css +1 -1
- package/package.json +10 -9
- package/src/GraphTooltipArrow/Content.tsx +24 -112
- package/src/GraphTooltipArrow/DockedGraphTooltip/index.tsx +34 -128
- package/src/GraphTooltipArrow/index.tsx +4 -6
- package/src/MetricsGraph/MetricsContextMenu/index.tsx +81 -0
- package/src/MetricsGraph/MetricsTooltip/index.tsx +17 -21
- package/src/MetricsGraph/index.tsx +48 -27
- package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.tsx +181 -0
- package/src/ProfileIcicleGraph/IcicleGraphArrow/IcicleGraphNodes.tsx +10 -5
- package/src/ProfileIcicleGraph/IcicleGraphArrow/index.tsx +63 -21
- package/src/ProfileMetricsGraph/index.tsx +4 -2
- package/src/ProfileSelector/index.tsx +27 -4
- package/yarn-error.log +28744 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [0.16.274](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.273...@parca/profile@0.16.274) (2023-10-10)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @parca/profile
|
|
9
|
+
|
|
10
|
+
## [0.16.273](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.272...@parca/profile@0.16.273) (2023-10-10)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @parca/profile
|
|
13
|
+
|
|
14
|
+
## [0.16.272](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.271...@parca/profile@0.16.272) (2023-10-10)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @parca/profile
|
|
17
|
+
|
|
18
|
+
## [0.16.271](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.270...@parca/profile@0.16.271) (2023-10-10)
|
|
19
|
+
|
|
20
|
+
**Note:** Version bump only for package @parca/profile
|
|
21
|
+
|
|
22
|
+
## [0.16.270](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.269...@parca/profile@0.16.270) (2023-10-10)
|
|
23
|
+
|
|
24
|
+
**Note:** Version bump only for package @parca/profile
|
|
25
|
+
|
|
6
26
|
## [0.16.269](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.268...@parca/profile@0.16.269) (2023-10-04)
|
|
7
27
|
|
|
8
28
|
**Note:** Version bump only for package @parca/profile
|
|
@@ -1,33 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
import { useState } from 'react';
|
|
15
|
-
import cx from 'classnames';
|
|
16
|
-
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|
17
|
-
import { Tooltip } from 'react-tooltip';
|
|
18
|
-
import { Button, IconButton, useParcaContext } from '@parca/components';
|
|
19
|
-
import { USER_PREFERENCES, useUserPreference } from '@parca/hooks';
|
|
2
|
+
import { Icon } from '@iconify/react';
|
|
20
3
|
import { getLastItem } from '@parca/utilities';
|
|
21
4
|
import { hexifyAddress, truncateString, truncateStringReverse } from '../utils';
|
|
22
5
|
import { ExpandOnHover } from './ExpandOnHoverValue';
|
|
23
6
|
import { useGraphTooltip } from './useGraphTooltip';
|
|
24
7
|
import { useGraphTooltipMetaInfo } from './useGraphTooltipMetaInfo';
|
|
25
|
-
let timeoutHandle = null;
|
|
26
8
|
const NoData = () => {
|
|
27
9
|
return _jsx("span", { className: "rounded bg-gray-200 px-2 dark:bg-gray-800", children: "Not available" });
|
|
28
10
|
};
|
|
29
11
|
const GraphTooltipArrowContent = ({ table, unit, total, totalUnfiltered, row, level, isFixed, navigateTo, }) => {
|
|
30
|
-
const [isCopied, setIsCopied] = useState(false);
|
|
31
12
|
const graphTooltipData = useGraphTooltip({
|
|
32
13
|
table,
|
|
33
14
|
unit,
|
|
@@ -36,29 +17,21 @@ const GraphTooltipArrowContent = ({ table, unit, total, totalUnfiltered, row, le
|
|
|
36
17
|
row,
|
|
37
18
|
level,
|
|
38
19
|
});
|
|
39
|
-
const [_, setIsDocked] = useUserPreference(USER_PREFERENCES.GRAPH_METAINFO_DOCKED.key);
|
|
40
20
|
if (graphTooltipData === null) {
|
|
41
21
|
return _jsx(_Fragment, {});
|
|
42
22
|
}
|
|
43
|
-
const onCopy = () => {
|
|
44
|
-
setIsCopied(true);
|
|
45
|
-
if (timeoutHandle !== null) {
|
|
46
|
-
clearTimeout(timeoutHandle);
|
|
47
|
-
}
|
|
48
|
-
timeoutHandle = setTimeout(() => setIsCopied(false), 3000);
|
|
49
|
-
};
|
|
50
23
|
const { name, locationAddress, cumulativeText, diffText, diff, row: rowNumber } = graphTooltipData;
|
|
51
|
-
return (_jsx("div", { className: `flex text-sm ${isFixed ? 'w-full' : ''}`, children: _jsx("div", { className: `m-auto w-full ${isFixed ? 'w-full' : ''}`, children: _jsxs("div", { className: "min-h-52 flex w-[500px] flex-col justify-between rounded-lg border border-gray-300 bg-gray-50 p-3 shadow-lg dark:border-gray-500 dark:bg-gray-900", children: [_jsx("div", { className: "flex flex-row", children: _jsxs("div", { className: "mx-2", children: [
|
|
24
|
+
return (_jsx("div", { className: `flex text-sm ${isFixed ? 'w-full' : ''}`, children: _jsx("div", { className: `m-auto w-full ${isFixed ? 'w-full' : ''}`, children: _jsxs("div", { className: "min-h-52 flex w-[500px] flex-col justify-between rounded-lg border border-gray-300 bg-gray-50 p-3 shadow-lg dark:border-gray-500 dark:bg-gray-900", children: [_jsx("div", { className: "flex flex-row", children: _jsxs("div", { className: "mx-2", children: [_jsx("div", { className: "flex h-10 items-start justify-between gap-4 break-all font-semibold", children: row === 0 ? (_jsx("p", { children: "root" })) : (_jsx("p", { children: name !== ''
|
|
25
|
+
? name
|
|
26
|
+
: locationAddress !== 0n
|
|
27
|
+
? hexifyAddress(locationAddress)
|
|
28
|
+
: 'unknown' })) }), _jsx("table", { className: "my-2 w-full table-fixed pr-0 text-gray-700 dark:text-gray-300", children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Cumulative" }), _jsx("td", { className: "w-3/4", children: _jsx("div", { children: cumulativeText }) })] }), diff !== 0n && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Diff" }), _jsx("td", { className: "w-3/4", children: _jsx("div", { children: diffText }) })] })), _jsx(TooltipMetaInfo, { table: table, row: rowNumber, navigateTo: navigateTo })] }) })] }) }), _jsxs("div", { className: "flex w-full items-center gap-1 text-xs text-gray-500", children: [_jsx(Icon, { icon: "iconoir:mouse-button-right" }), _jsx("div", { children: "Right click to show context menu" })] })] }) }) }));
|
|
52
29
|
};
|
|
53
|
-
const TooltipMetaInfo = ({ table,
|
|
54
|
-
|
|
55
|
-
// totalUnfiltered,
|
|
56
|
-
onCopy, row, navigateTo, }) => {
|
|
57
|
-
const { labelPairs, functionFilename, file, openFile, isSourceAvailable, locationAddress, mappingFile, mappingBuildID, inlined, } = useGraphTooltipMetaInfo({ table, row, navigateTo });
|
|
58
|
-
const { enableSourcesView } = useParcaContext();
|
|
30
|
+
const TooltipMetaInfo = ({ table, row, navigateTo, }) => {
|
|
31
|
+
const { labelPairs, functionFilename, file, locationAddress, mappingFile, mappingBuildID, inlined, } = useGraphTooltipMetaInfo({ table, row, navigateTo });
|
|
59
32
|
const labels = labelPairs.map((l) => (_jsx("span", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", children: `${l[0]}="${l[1]}"` }, l[0])));
|
|
60
33
|
const isMappingBuildIDAvailable = mappingBuildID !== null && mappingBuildID !== '';
|
|
61
34
|
const inlinedText = inlined === null ? 'merged' : inlined ? 'yes' : 'no';
|
|
62
|
-
return (_jsxs(_Fragment, { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "File" }), _jsx("td", { className: "w-3/4 break-all", children: functionFilename === '' ? (_jsx(NoData, {})) : (
|
|
35
|
+
return (_jsxs(_Fragment, { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "File" }), _jsx("td", { className: "w-3/4 break-all", children: functionFilename === '' ? (_jsx(NoData, {})) : (_jsx("div", { className: "flex gap-4", children: _jsx("div", { className: "whitespace-nowrap text-left", children: _jsx(ExpandOnHover, { value: file, displayValue: truncateStringReverse(file, 30) }) }) })) })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Address" }), _jsx("td", { className: "w-3/4 break-all", children: locationAddress === 0n ? _jsx(NoData, {}) : _jsx("div", { children: hexifyAddress(locationAddress) }) })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Inlined" }), _jsx("td", { className: "w-3/4 break-all", children: inlinedText })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Binary" }), _jsx("td", { className: "w-3/4 break-all", children: (mappingFile != null ? getLastItem(mappingFile) : null) ?? _jsx(NoData, {}) })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Build Id" }), _jsx("td", { className: "w-3/4 break-all", children: isMappingBuildIDAvailable ? (_jsx("div", { children: truncateString(getLastItem(mappingBuildID), 28) })) : (_jsx(NoData, {})) })] }), labelPairs.length > 0 && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Labels" }), _jsx("td", { className: "w-3/4 break-all", children: labels })] }))] }));
|
|
63
36
|
};
|
|
64
37
|
export default GraphTooltipArrowContent;
|
|
@@ -11,35 +11,25 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
11
11
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
// See the License for the specific language governing permissions and
|
|
13
13
|
// limitations under the License.
|
|
14
|
-
import {
|
|
14
|
+
import { Icon } from '@iconify/react';
|
|
15
15
|
import cx from 'classnames';
|
|
16
|
-
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|
17
|
-
import { Tooltip } from 'react-tooltip';
|
|
18
16
|
import { useWindowSize } from 'react-use';
|
|
19
|
-
import {
|
|
20
|
-
import { USER_PREFERENCES, useUserPreference } from '@parca/hooks';
|
|
17
|
+
import { useParcaContext } from '@parca/components';
|
|
21
18
|
import { getLastItem } from '@parca/utilities';
|
|
22
19
|
import { hexifyAddress, truncateString, truncateStringReverse } from '../../utils';
|
|
23
|
-
import { ExpandOnHover } from '../ExpandOnHoverValue';
|
|
24
20
|
import { useGraphTooltip } from '../useGraphTooltip';
|
|
25
21
|
import { useGraphTooltipMetaInfo } from '../useGraphTooltipMetaInfo';
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
const InfoSection = ({ title, value, minWidth = '', }) => {
|
|
23
|
+
return (_jsxs("div", { className: cx('flex shrink-0 flex-col gap-1 p-2', { [minWidth]: minWidth != null }), children: [_jsx("p", { className: "text-sm font-medium leading-5 text-gray-500 dark:text-gray-400", children: title }), _jsx("div", { className: "text-lg font-normal text-gray-900 dark:text-gray-50", children: value })] }));
|
|
24
|
+
};
|
|
25
|
+
const NoData = () => {
|
|
26
|
+
return _jsx("span", { className: "rounded bg-gray-200 px-2 dark:bg-gray-800", children: "Not available" });
|
|
29
27
|
};
|
|
30
28
|
export const DockedGraphTooltip = ({ table, unit, total, totalUnfiltered, row, level, }) => {
|
|
31
29
|
let { width } = useWindowSize();
|
|
32
|
-
const { profileExplorer, navigateTo
|
|
30
|
+
const { profileExplorer, navigateTo } = useParcaContext();
|
|
33
31
|
const { PaddingX } = profileExplorer ?? { PaddingX: 0 };
|
|
34
32
|
width = width - PaddingX - 24;
|
|
35
|
-
const [isCopied, setIsCopied] = useState(false);
|
|
36
|
-
const onCopy = () => {
|
|
37
|
-
setIsCopied(true);
|
|
38
|
-
if (timeoutHandle !== null) {
|
|
39
|
-
clearTimeout(timeoutHandle);
|
|
40
|
-
}
|
|
41
|
-
timeoutHandle = setTimeout(() => setIsCopied(false), 3000);
|
|
42
|
-
};
|
|
43
33
|
const graphTooltipData = useGraphTooltip({
|
|
44
34
|
table,
|
|
45
35
|
unit,
|
|
@@ -48,17 +38,18 @@ export const DockedGraphTooltip = ({ table, unit, total, totalUnfiltered, row, l
|
|
|
48
38
|
row,
|
|
49
39
|
level,
|
|
50
40
|
});
|
|
51
|
-
const { labelPairs, functionFilename, file,
|
|
52
|
-
const [_, setIsDocked] = useUserPreference(USER_PREFERENCES.GRAPH_METAINFO_DOCKED.key);
|
|
41
|
+
const { labelPairs, functionFilename, file, locationAddress, mappingFile, mappingBuildID, inlined, } = useGraphTooltipMetaInfo({ table, row: row ?? 0, navigateTo });
|
|
53
42
|
if (graphTooltipData === null) {
|
|
54
43
|
return _jsx(_Fragment, {});
|
|
55
44
|
}
|
|
56
45
|
const { name, cumulativeText, diffText, diff } = graphTooltipData;
|
|
57
46
|
const labels = labelPairs.map((l) => (_jsx("span", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", children: `${l[0]}="${l[1]}"` }, l[0])));
|
|
47
|
+
const isMappingBuildIDAvailable = mappingBuildID !== null && mappingBuildID !== '';
|
|
58
48
|
const inlinedText = inlined === null ? 'merged' : inlined ? 'yes' : 'no';
|
|
59
|
-
const addressText = locationAddress !== 0n ? hexifyAddress(locationAddress) :
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
49
|
+
const addressText = locationAddress !== 0n ? hexifyAddress(locationAddress) : _jsx(NoData, {});
|
|
50
|
+
return (_jsxs("div", { className: "fixed bottom-0 z-20 overflow-hidden rounded-t-lg border-l border-r border-t border-gray-400 bg-white bg-opacity-90 px-8 py-3 dark:border-gray-600 dark:bg-black dark:bg-opacity-80", style: { width }, children: [_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx("div", { className: "flex justify-between gap-4", children: row === 0 ? (_jsx("p", { children: "root" })) : (_jsx("p", { children: name !== ''
|
|
51
|
+
? name
|
|
52
|
+
: locationAddress !== 0n
|
|
53
|
+
? hexifyAddress(locationAddress)
|
|
54
|
+
: 'unknown' })) }), _jsxs("div", { className: "flex justify-between gap-3", children: [_jsx(InfoSection, { title: "Cumulative", value: cumulativeText, minWidth: "w-44" }), diff !== 0n ? _jsx(InfoSection, { title: "Diff", value: diffText, minWidth: "w-44" }) : null, _jsx(InfoSection, { title: "File", value: functionFilename !== '' ? truncateStringReverse(file, 45) : _jsx(NoData, {}), minWidth: 'w-[460px]' }), _jsx(InfoSection, { title: "Address", value: addressText, minWidth: "w-44" }), _jsx(InfoSection, { title: "Inlined", value: inlinedText, minWidth: "w-44" }), _jsx(InfoSection, { title: "Binary", value: (mappingFile != null ? getLastItem(mappingFile) : null) ?? _jsx(NoData, {}), minWidth: "w-44" }), _jsx(InfoSection, { title: "Build ID", value: isMappingBuildIDAvailable ? (_jsx("div", { children: truncateString(getLastItem(mappingBuildID), 28) })) : (_jsx(NoData, {})) })] }), _jsx("div", { children: _jsx("div", { className: "flex h-5 gap-1", children: labels }) })] }), _jsxs("div", { className: "flex w-full items-center gap-1 text-xs text-gray-500", children: [_jsx(Icon, { icon: "iconoir:mouse-button-right" }), _jsx("div", { children: "Right click to show context menu" })] })] }));
|
|
64
55
|
};
|
|
@@ -6,6 +6,7 @@ interface GraphTooltipProps {
|
|
|
6
6
|
contextElement: Element | null;
|
|
7
7
|
isFixed?: boolean;
|
|
8
8
|
virtualContextElement?: boolean;
|
|
9
|
+
isContextMenuOpen?: boolean;
|
|
9
10
|
}
|
|
10
|
-
declare const GraphTooltip: ({ children, x, y, contextElement, isFixed, virtualContextElement, }: GraphTooltipProps) => React.JSX.Element;
|
|
11
|
+
declare const GraphTooltip: ({ children, x, y, contextElement, isFixed, virtualContextElement, isContextMenuOpen, }: GraphTooltipProps) => React.JSX.Element;
|
|
11
12
|
export default GraphTooltip;
|
|
@@ -14,7 +14,6 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
|
14
14
|
import { useEffect, useState } from 'react';
|
|
15
15
|
import { pointer } from 'd3-selection';
|
|
16
16
|
import { usePopper } from 'react-popper';
|
|
17
|
-
import { useKeyDown } from '@parca/components';
|
|
18
17
|
const virtualElement = {
|
|
19
18
|
getBoundingClientRect: () =>
|
|
20
19
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -40,7 +39,7 @@ function generateGetBoundingClientRect(contextElement, x = 0, y = 0) {
|
|
|
40
39
|
bottom: domRect.y + y,
|
|
41
40
|
});
|
|
42
41
|
}
|
|
43
|
-
const GraphTooltip = ({ children, x, y, contextElement, isFixed = false, virtualContextElement = true, }) => {
|
|
42
|
+
const GraphTooltip = ({ children, x, y, contextElement, isFixed = false, virtualContextElement = true, isContextMenuOpen = false, }) => {
|
|
44
43
|
const [popperElement, setPopperElement] = useState(null);
|
|
45
44
|
const { styles, attributes, ...popperProps } = usePopper(virtualContextElement ? virtualElement : contextElement, popperElement, {
|
|
46
45
|
placement: 'bottom-start',
|
|
@@ -61,12 +60,11 @@ const GraphTooltip = ({ children, x, y, contextElement, isFixed = false, virtual
|
|
|
61
60
|
},
|
|
62
61
|
],
|
|
63
62
|
});
|
|
64
|
-
const { isShiftDown } = useKeyDown();
|
|
65
63
|
useEffect(() => {
|
|
66
64
|
if (contextElement === null)
|
|
67
65
|
return;
|
|
68
66
|
const onMouseMove = (e) => {
|
|
69
|
-
if (
|
|
67
|
+
if (isContextMenuOpen) {
|
|
70
68
|
return;
|
|
71
69
|
}
|
|
72
70
|
let tooltipX = x;
|
|
@@ -83,7 +81,7 @@ const GraphTooltip = ({ children, x, y, contextElement, isFixed = false, virtual
|
|
|
83
81
|
return () => {
|
|
84
82
|
contextElement.removeEventListener('mousemove', onMouseMove);
|
|
85
83
|
};
|
|
86
|
-
}, [contextElement, popperProps,
|
|
84
|
+
}, [contextElement, popperProps, x, y, isContextMenuOpen]);
|
|
87
85
|
return isFixed ? (_jsx(_Fragment, { children: children })) : (_jsx("div", { ref: setPopperElement, style: styles.popper, ...attributes.popper, className: "z-10", children: children }));
|
|
88
86
|
};
|
|
89
87
|
export default GraphTooltip;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { HighlightedSeries } from '../';
|
|
3
|
+
interface MetricsContextMenuProps {
|
|
4
|
+
menuId: string;
|
|
5
|
+
onAddLabelMatcher: (labels: {
|
|
6
|
+
key: string;
|
|
7
|
+
value: string;
|
|
8
|
+
} | Array<{
|
|
9
|
+
key: string;
|
|
10
|
+
value: string;
|
|
11
|
+
}>) => void;
|
|
12
|
+
highlighted: HighlightedSeries | null;
|
|
13
|
+
trackVisibility: (isVisible: boolean) => void;
|
|
14
|
+
}
|
|
15
|
+
declare const MetricsContextMenu: ({ menuId, onAddLabelMatcher, highlighted, trackVisibility, }: MetricsContextMenuProps) => JSX.Element;
|
|
16
|
+
export default MetricsContextMenu;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Copyright 2022 The Parca Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
import { Icon } from '@iconify/react';
|
|
15
|
+
import { Item, Menu, Submenu } from 'react-contexify';
|
|
16
|
+
const MetricsContextMenu = ({ menuId, onAddLabelMatcher, highlighted, trackVisibility, }) => {
|
|
17
|
+
const labels = highlighted?.labels.filter((label) => label.name !== '__name__');
|
|
18
|
+
const handleFocusOnSingleSeries = () => {
|
|
19
|
+
const labelsToAdd = labels?.map((label) => ({
|
|
20
|
+
key: label.name,
|
|
21
|
+
value: label.value,
|
|
22
|
+
}));
|
|
23
|
+
labelsToAdd !== undefined && onAddLabelMatcher(labelsToAdd);
|
|
24
|
+
};
|
|
25
|
+
return (_jsxs(Menu, { id: menuId, onVisibilityChange: trackVisibility, children: [_jsx(Item, { id: "focus-on-single-series", onClick: handleFocusOnSingleSeries, children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "ph:star" }), _jsx("div", { children: "Focus only on this series" })] }) }), _jsx(Submenu, { label: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "material-symbols:add" }), _jsx("div", { children: "Add to query" })] }), style: { maxHeight: '300px', overflow: 'scroll' }, children: labels?.map((label) => (_jsx(Item, { id: label.name, onClick: () => onAddLabelMatcher({ key: label.name, value: label.value }), style: { maxWidth: '400px', overflow: 'hidden' }, children: _jsx("div", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", children: `${label.name}="${label.value}"` }) }, label.name))) })] }));
|
|
26
|
+
};
|
|
27
|
+
export default MetricsContextMenu;
|
|
@@ -4,10 +4,9 @@ interface Props {
|
|
|
4
4
|
x: number;
|
|
5
5
|
y: number;
|
|
6
6
|
highlighted: HighlightedSeries;
|
|
7
|
-
onLabelClick: (labelName: string, labelValue: string) => void;
|
|
8
7
|
contextElement: Element | null;
|
|
9
8
|
sampleUnit: string;
|
|
10
9
|
delta: boolean;
|
|
11
10
|
}
|
|
12
|
-
declare const MetricsTooltip: ({ x, y, highlighted,
|
|
11
|
+
declare const MetricsTooltip: ({ x, y, highlighted, contextElement, sampleUnit, delta, }: Props) => JSX.Element;
|
|
13
12
|
export default MetricsTooltip;
|
|
@@ -12,6 +12,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
// See the License for the specific language governing permissions and
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
import { useEffect, useState } from 'react';
|
|
15
|
+
import { Icon } from '@iconify/react';
|
|
15
16
|
import { usePopper } from 'react-popper';
|
|
16
17
|
import { TextWithTooltip } from '@parca/components';
|
|
17
18
|
import { formatDate, valueFormatter } from '@parca/utilities';
|
|
@@ -42,7 +43,7 @@ function generateGetBoundingClientRect(contextElement, x = 0, y = 0) {
|
|
|
42
43
|
bottom: domRect.y + y,
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
|
-
const MetricsTooltip = ({ x, y, highlighted,
|
|
46
|
+
const MetricsTooltip = ({ x, y, highlighted, contextElement, sampleUnit, delta, }) => {
|
|
46
47
|
const [popperElement, setPopperElement] = useState(null);
|
|
47
48
|
const { styles, attributes, ...popperProps } = usePopper(virtualElement, popperElement, {
|
|
48
49
|
placement: 'auto-start',
|
|
@@ -74,8 +75,6 @@ const MetricsTooltip = ({ x, y, highlighted, onLabelClick, contextElement, sampl
|
|
|
74
75
|
const highlightedNameLabel = nameLabel !== undefined ? nameLabel : { name: '', value: '' };
|
|
75
76
|
return (_jsx("div", { ref: setPopperElement, style: styles.popper, ...attributes.popper, className: "z-10", children: _jsx("div", { className: "flex max-w-md", children: _jsx("div", { className: "m-auto", children: _jsx("div", { className: "rounded-lg border-gray-300 bg-gray-50 p-3 opacity-90 shadow-lg dark:border-gray-500 dark:bg-gray-900", style: { borderWidth: 1 }, children: _jsx("div", { className: "flex flex-row", children: _jsxs("div", { className: "ml-2 mr-6", children: [_jsx("span", { className: "font-semibold", children: highlightedNameLabel.value }), _jsx("span", { className: "my-2 block text-gray-700 dark:text-gray-300", children: _jsx("table", { className: "table-auto", children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Value" }), _jsx("td", { className: "w-3/4", children: valueFormatter(highlighted.valuePerSecond, sampleUnit, 5) })] }), delta && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Total" }), _jsx("td", { className: "w-3/4", children: valueFormatter(highlighted.value, sampleUnit, 2) })] })), highlighted.duration > 0 && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Duration" }), _jsx("td", { className: "w-3/4", children: valueFormatter(highlighted.duration, 'nanoseconds', 2) })] })), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "At" }), _jsx("td", { className: "w-3/4", children: formatDate(highlighted.timestamp, timeFormat) })] })] }) }) }), _jsx("span", { className: "my-2 block text-gray-500", children: highlighted.labels
|
|
76
77
|
.filter((label) => label.name !== '__name__')
|
|
77
|
-
.map(
|
|
78
|
-
return (_jsx("button", { type: "button", className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", onClick: () => onLabelClick(label.name, label.value), children: _jsx(TextWithTooltip, { text: `${label.name}="${label.value}"`, maxTextLength: 37, id: `tooltip-${label.name}-${label.value}` }) }, label.name));
|
|
79
|
-
}) }), _jsx("span", { className: "block text-xs text-gray-500", children: "Hold shift and click label to add to query." })] }) }) }) }) }) }));
|
|
78
|
+
.map((label) => (_jsx("div", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", children: _jsx(TextWithTooltip, { text: `${label.name}="${label.value}"`, maxTextLength: 37, id: `tooltip-${label.name}-${label.value}` }) }, label.name))) }), _jsxs("div", { className: "flex w-full items-center gap-1 text-xs text-gray-500", children: [_jsx(Icon, { icon: "iconoir:mouse-button-right" }), _jsx("div", { children: "Right click to add labels to query." })] })] }) }) }) }) }) }));
|
|
80
79
|
};
|
|
81
80
|
export default MetricsTooltip;
|
|
@@ -8,7 +8,13 @@ interface Props {
|
|
|
8
8
|
to: number;
|
|
9
9
|
profile: MergedProfileSelection | null;
|
|
10
10
|
onSampleClick: (timestamp: number, value: number, labels: Label[]) => void;
|
|
11
|
-
|
|
11
|
+
addLabelMatcher: (labels: {
|
|
12
|
+
key: string;
|
|
13
|
+
value: string;
|
|
14
|
+
} | Array<{
|
|
15
|
+
key: string;
|
|
16
|
+
value: string;
|
|
17
|
+
}>) => void;
|
|
12
18
|
setTimeRange: (range: DateTimeRange) => void;
|
|
13
19
|
sampleUnit: string;
|
|
14
20
|
width?: number;
|
|
@@ -25,7 +31,7 @@ export interface HighlightedSeries {
|
|
|
25
31
|
x: number;
|
|
26
32
|
y: number;
|
|
27
33
|
}
|
|
28
|
-
declare const MetricsGraph: ({ data, from, to, profile, onSampleClick,
|
|
34
|
+
declare const MetricsGraph: ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, sampleUnit, width, height, margin, }: Props) => JSX.Element;
|
|
29
35
|
export default MetricsGraph;
|
|
30
36
|
export declare const parseValue: (value: string) => number | null;
|
|
31
|
-
export declare const RawMetricsGraph: ({ data, from, to, profile, onSampleClick,
|
|
37
|
+
export declare const RawMetricsGraph: ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, width, height, margin, sampleUnit, }: Props) => JSX.Element;
|
|
@@ -11,17 +11,19 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
11
11
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
// See the License for the specific language governing permissions and
|
|
13
13
|
// limitations under the License.
|
|
14
|
-
import { Fragment, useRef, useState } from 'react';
|
|
14
|
+
import { Fragment, useCallback, useRef, useState } from 'react';
|
|
15
15
|
import * as d3 from 'd3';
|
|
16
16
|
import { pointer } from 'd3-selection';
|
|
17
17
|
import throttle from 'lodash.throttle';
|
|
18
|
-
import {
|
|
18
|
+
import { useContextMenu } from 'react-contexify';
|
|
19
|
+
import { DateTimeRange } from '@parca/components';
|
|
19
20
|
import { formatDate, formatForTimespan, sanitizeHighlightedValues, valueFormatter, } from '@parca/utilities';
|
|
20
21
|
import MetricsCircle from '../MetricsCircle';
|
|
21
22
|
import MetricsSeries from '../MetricsSeries';
|
|
23
|
+
import MetricsContextMenu from './MetricsContextMenu';
|
|
22
24
|
import MetricsTooltip from './MetricsTooltip';
|
|
23
|
-
const MetricsGraph = ({ data, from, to, profile, onSampleClick,
|
|
24
|
-
return (_jsx(RawMetricsGraph, { data: data, from: from, to: to, profile: profile, onSampleClick: onSampleClick,
|
|
25
|
+
const MetricsGraph = ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, sampleUnit, width = 0, height = 0, margin = 0, }) => {
|
|
26
|
+
return (_jsx(RawMetricsGraph, { data: data, from: from, to: to, profile: profile, onSampleClick: onSampleClick, addLabelMatcher: addLabelMatcher, setTimeRange: setTimeRange, sampleUnit: sampleUnit, width: width, height: height, margin: margin }));
|
|
25
27
|
};
|
|
26
28
|
export default MetricsGraph;
|
|
27
29
|
export const parseValue = (value) => {
|
|
@@ -32,14 +34,14 @@ export const parseValue = (value) => {
|
|
|
32
34
|
};
|
|
33
35
|
const lineStroke = '1px';
|
|
34
36
|
const lineStrokeHover = '2px';
|
|
35
|
-
export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick,
|
|
37
|
+
export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, addLabelMatcher, setTimeRange, width, height = 50, margin = 0, sampleUnit, }) => {
|
|
36
38
|
const graph = useRef(null);
|
|
37
39
|
const [dragging, setDragging] = useState(false);
|
|
38
40
|
const [hovering, setHovering] = useState(false);
|
|
39
41
|
const [relPos, setRelPos] = useState(-1);
|
|
40
42
|
const [pos, setPos] = useState([0, 0]);
|
|
43
|
+
const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);
|
|
41
44
|
const metricPointRef = useRef(null);
|
|
42
|
-
const { isShiftDown } = useKeyDown();
|
|
43
45
|
// the time of the selected point is the start of the merge window
|
|
44
46
|
const time = parseFloat(profile?.HistoryParams().merge_from);
|
|
45
47
|
if (width === undefined || width == null) {
|
|
@@ -118,10 +120,6 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, onLabe
|
|
|
118
120
|
};
|
|
119
121
|
const highlighted = getClosest();
|
|
120
122
|
const onMouseDown = (e) => {
|
|
121
|
-
// if shift is down, disable mouse behavior
|
|
122
|
-
if (isShiftDown) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
123
|
// only left mouse button
|
|
126
124
|
if (e.button !== 0) {
|
|
127
125
|
return;
|
|
@@ -144,9 +142,6 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, onLabe
|
|
|
144
142
|
}
|
|
145
143
|
};
|
|
146
144
|
const onMouseUp = (e) => {
|
|
147
|
-
if (isShiftDown) {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
145
|
setDragging(false);
|
|
151
146
|
if (relPos === -1) {
|
|
152
147
|
// MouseDown happened outside of this element.
|
|
@@ -173,8 +168,7 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, onLabe
|
|
|
173
168
|
};
|
|
174
169
|
const throttledSetPos = throttle(setPos, 20);
|
|
175
170
|
const onMouseMove = (e) => {
|
|
176
|
-
|
|
177
|
-
if (isShiftDown) {
|
|
171
|
+
if (isContextMenuOpen) {
|
|
178
172
|
return;
|
|
179
173
|
}
|
|
180
174
|
// X/Y coordinate array relative to svg
|
|
@@ -228,9 +222,21 @@ export const RawMetricsGraph = ({ data, from, to, profile, onSampleClick, onLabe
|
|
|
228
222
|
};
|
|
229
223
|
};
|
|
230
224
|
const selected = findSelectedProfile();
|
|
231
|
-
|
|
225
|
+
const MENU_ID = 'metrics-context-menu';
|
|
226
|
+
const { show } = useContextMenu({
|
|
227
|
+
id: MENU_ID,
|
|
228
|
+
});
|
|
229
|
+
const displayMenu = useCallback((e) => {
|
|
230
|
+
show({
|
|
231
|
+
event: e,
|
|
232
|
+
});
|
|
233
|
+
}, [show]);
|
|
234
|
+
const trackVisibility = (isVisible) => {
|
|
235
|
+
setIsContextMenuOpen(isVisible);
|
|
236
|
+
};
|
|
237
|
+
return (_jsxs(_Fragment, { children: [_jsx(MetricsContextMenu, { onAddLabelMatcher: addLabelMatcher, menuId: MENU_ID, highlighted: highlighted, trackVisibility: trackVisibility }), highlighted != null && hovering && !dragging && pos[0] !== 0 && pos[1] !== 0 && (_jsx("div", { onMouseMove: onMouseMove, onMouseEnter: () => setHovering(true), onMouseLeave: () => setHovering(false), children: !isContextMenuOpen && (_jsx(MetricsTooltip, { x: pos[0] + margin, y: pos[1] + margin, highlighted: highlighted, contextElement: graph.current, sampleUnit: sampleUnit, delta: profile !== null ? profile?.query.profType.delta : false })) })), _jsx("div", { ref: graph, onMouseEnter: function () {
|
|
232
238
|
setHovering(true);
|
|
233
|
-
}, onMouseLeave: () => setHovering(false), children: _jsxs("svg", { width: `${width}px`, height: `${height + margin}px`, onMouseDown: onMouseDown, onMouseUp: onMouseUp, onMouseMove: onMouseMove, children: [_jsx("g", { transform: `translate(${margin}, 0)`, children: dragging && (_jsx("g", { className: "zoom-time-rect", children: _jsx("rect", { className: "bar", x: pos[0] - relPos < 0 ? pos[0] : relPos, y: 0, height: height, width: Math.abs(pos[0] - relPos), fill: 'rgba(0, 0, 0, 0.125)' }) })) }), _jsxs("g", { transform: `translate(${margin}, ${margin})`, children: [_jsxs("g", { className: "y axis", textAnchor: "end", fontSize: "10", fill: "none", children: [yScale.ticks(5).map((d, i) => (_jsxs(Fragment, { children: [_jsxs("g", { className: "tick",
|
|
239
|
+
}, onMouseLeave: () => setHovering(false), onContextMenu: displayMenu, children: _jsxs("svg", { width: `${width}px`, height: `${height + margin}px`, onMouseDown: onMouseDown, onMouseUp: onMouseUp, onMouseMove: onMouseMove, children: [_jsx("g", { transform: `translate(${margin}, 0)`, children: dragging && (_jsx("g", { className: "zoom-time-rect", children: _jsx("rect", { className: "bar", x: pos[0] - relPos < 0 ? pos[0] : relPos, y: 0, height: height, width: Math.abs(pos[0] - relPos), fill: 'rgba(0, 0, 0, 0.125)' }) })) }), _jsxs("g", { transform: `translate(${margin}, ${margin})`, children: [_jsxs("g", { className: "y axis", textAnchor: "end", fontSize: "10", fill: "none", children: [yScale.ticks(5).map((d, i) => (_jsxs(Fragment, { children: [_jsxs("g", { className: "tick",
|
|
234
240
|
/* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
|
|
235
241
|
transform: `translate(0, ${yScale(d)})`, children: [_jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x2: -6 }), _jsx("text", { fill: "currentColor", x: -9, dy: '0.32em', children: valueFormatter(d, sampleUnit, 1) })] }, `tick-${i}`), _jsx("g", { children: _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: xScale(from), x2: xScale(to), y1: yScale(d), y2: yScale(d) }) }, `grid-${i}`)] }, `${i.toString()}-${d.toString()}`))), _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: 0, x2: 0, y1: 0, y2: height - margin }), _jsx("line", { className: "stroke-gray-300 dark:stroke-gray-500", x1: xScale(to), x2: xScale(to), y1: 0, y2: height - margin })] }), _jsxs("g", { className: "x axis", fill: "none", fontSize: "10", textAnchor: "middle", transform: `translate(0,${height - margin})`, children: [xScale.ticks(5).map((d, i) => (_jsxs(Fragment, { children: [_jsxs("g", { className: "tick",
|
|
236
242
|
/* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { Table } from 'apache-arrow';
|
|
3
|
+
import { type NavigateFunction } from '@parca/utilities';
|
|
4
|
+
interface ContextMenuProps {
|
|
5
|
+
menuId: string;
|
|
6
|
+
table: Table<any>;
|
|
7
|
+
unit: string;
|
|
8
|
+
total: bigint;
|
|
9
|
+
totalUnfiltered: bigint;
|
|
10
|
+
row: number;
|
|
11
|
+
level: number;
|
|
12
|
+
navigateTo: NavigateFunction;
|
|
13
|
+
trackVisibility: (isVisible: boolean) => void;
|
|
14
|
+
curPath: string[];
|
|
15
|
+
setCurPath: (path: string[]) => void;
|
|
16
|
+
hideMenu: () => void;
|
|
17
|
+
}
|
|
18
|
+
declare const ContextMenu: ({ menuId, table, unit, total, totalUnfiltered, row, level, navigateTo, trackVisibility, curPath, setCurPath, hideMenu, }: ContextMenuProps) => JSX.Element;
|
|
19
|
+
export default ContextMenu;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Copyright 2022 The Parca Authors
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
import { Icon } from '@iconify/react';
|
|
15
|
+
import { Item, Menu, Separator, Submenu } from 'react-contexify';
|
|
16
|
+
import { Tooltip } from 'react-tooltip';
|
|
17
|
+
import { useParcaContext } from '@parca/components';
|
|
18
|
+
import { USER_PREFERENCES, useUserPreference } from '@parca/hooks';
|
|
19
|
+
import { useGraphTooltip } from '../../GraphTooltipArrow/useGraphTooltip';
|
|
20
|
+
import { useGraphTooltipMetaInfo } from '../../GraphTooltipArrow/useGraphTooltipMetaInfo';
|
|
21
|
+
import { hexifyAddress, truncateString } from '../../utils';
|
|
22
|
+
const ContextMenu = ({ menuId, table, unit, total, totalUnfiltered, row, level, navigateTo, trackVisibility, curPath, setCurPath, hideMenu, }) => {
|
|
23
|
+
const { enableSourcesView } = useParcaContext();
|
|
24
|
+
const [isGraphTooltipDocked, setIsDocked] = useUserPreference(USER_PREFERENCES.GRAPH_METAINFO_DOCKED.key);
|
|
25
|
+
const contextMenuData = useGraphTooltip({
|
|
26
|
+
table,
|
|
27
|
+
unit,
|
|
28
|
+
total,
|
|
29
|
+
totalUnfiltered,
|
|
30
|
+
row,
|
|
31
|
+
level,
|
|
32
|
+
});
|
|
33
|
+
const { functionFilename, file, openFile, isSourceAvailable, locationAddress, mappingFile, mappingBuildID, inlined, } = useGraphTooltipMetaInfo({ table, row, navigateTo });
|
|
34
|
+
if (contextMenuData === null) {
|
|
35
|
+
return _jsx(_Fragment, {});
|
|
36
|
+
}
|
|
37
|
+
const { name, cumulativeText, diffText, diff } = contextMenuData;
|
|
38
|
+
const isMappingBuildIDAvailable = mappingBuildID !== null && mappingBuildID !== '';
|
|
39
|
+
const handleViewSourceFile = () => openFile();
|
|
40
|
+
const handleResetView = () => {
|
|
41
|
+
setCurPath([]);
|
|
42
|
+
return hideMenu();
|
|
43
|
+
};
|
|
44
|
+
const handleDockTooltip = () => {
|
|
45
|
+
return isGraphTooltipDocked ? setIsDocked(false) : setIsDocked(true);
|
|
46
|
+
};
|
|
47
|
+
const handleCopyItem = (text) => {
|
|
48
|
+
void navigator.clipboard.writeText(text);
|
|
49
|
+
};
|
|
50
|
+
const functionName = row === 0
|
|
51
|
+
? ''
|
|
52
|
+
: name !== ''
|
|
53
|
+
? name
|
|
54
|
+
: locationAddress !== 0n
|
|
55
|
+
? hexifyAddress(locationAddress)
|
|
56
|
+
: '';
|
|
57
|
+
const buildIdText = !isMappingBuildIDAvailable ? '' : mappingBuildID;
|
|
58
|
+
const inlinedText = inlined === null ? 'merged' : inlined ? 'yes' : 'no';
|
|
59
|
+
const valuesToCopy = [
|
|
60
|
+
{ id: 'Function name', value: functionName },
|
|
61
|
+
{ id: 'Cumulative', value: cumulativeText ?? '' },
|
|
62
|
+
{ id: 'Diff', value: diff !== 0n ? diffText : '' },
|
|
63
|
+
{
|
|
64
|
+
id: 'File',
|
|
65
|
+
value: functionFilename === '' ? functionFilename : file,
|
|
66
|
+
},
|
|
67
|
+
{ id: 'Address', value: locationAddress === 0n ? '' : hexifyAddress(locationAddress) },
|
|
68
|
+
{ id: 'Inlined', value: inlinedText },
|
|
69
|
+
{ id: 'Binary', value: mappingFile ?? '' },
|
|
70
|
+
{ id: 'Build Id', value: buildIdText },
|
|
71
|
+
];
|
|
72
|
+
const nonEmptyValuesToCopy = valuesToCopy.filter(({ value }) => value !== '');
|
|
73
|
+
return (_jsxs(Menu, { id: menuId, onVisibilityChange: trackVisibility, children: [_jsxs(Item, { id: "view-source-file", onClick: handleViewSourceFile, disabled: enableSourcesView === false || !isSourceAvailable, children: [_jsx("div", { "data-tooltip-id": "view-source-file-help", "data-tooltip-content": "There is no source code uploaded for this build", children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "wpf:view-file" }), _jsx("div", { children: "View source file" })] }) }), !isSourceAvailable ? _jsx(Tooltip, { id: "view-source-file-help" }) : null] }), _jsx(Item, { id: "reset-view", onClick: handleResetView, disabled: curPath.length === 0, children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "system-uicons:reset" }), _jsx("div", { children: "Reset view" })] }) }), _jsx(Submenu, { label: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "ph:copy" }), _jsx("div", { children: "Copy" })] }), children: nonEmptyValuesToCopy.map(({ id, value }) => (_jsx(Item, { id: id, onClick: () => handleCopyItem(value), children: _jsxs("div", { className: "flex flex-col", children: [_jsx("div", { className: "text-sm", children: id }), _jsx("div", { className: "text-xs", children: truncateString(value, 30) })] }) }, id))) }), _jsx(Separator, {}), _jsx(Item, { id: "dock-tooltip", onClick: handleDockTooltip, children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx(Icon, { icon: "bx:dock-bottom" }), isGraphTooltipDocked ? 'Undock tooltip' : 'Dock tooltip'] }) })] }));
|
|
74
|
+
};
|
|
75
|
+
export default ContextMenu;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Table } from 'apache-arrow';
|
|
3
|
+
import 'react-contexify/dist/ReactContexify.css';
|
|
3
4
|
export declare const RowHeight = 26;
|
|
4
5
|
interface IcicleGraphNodesProps {
|
|
5
6
|
table: Table<any>;
|
|
@@ -21,6 +22,7 @@ interface IcicleGraphNodesProps {
|
|
|
21
22
|
sortBy: string;
|
|
22
23
|
darkMode: boolean;
|
|
23
24
|
compareMode: boolean;
|
|
25
|
+
isContextMenuOpen: boolean;
|
|
24
26
|
}
|
|
25
27
|
export declare const IcicleGraphNodes: React.NamedExoticComponent<IcicleGraphNodesProps>;
|
|
26
28
|
export interface mappingColors {
|
|
@@ -47,6 +49,7 @@ interface IcicleNodeProps {
|
|
|
47
49
|
sortBy: string;
|
|
48
50
|
darkMode: boolean;
|
|
49
51
|
compareMode: boolean;
|
|
52
|
+
isContextMenuOpen: boolean;
|
|
50
53
|
}
|
|
51
54
|
export declare const IcicleNode: React.NamedExoticComponent<IcicleNodeProps>;
|
|
52
55
|
export {};
|