@parca/profile 0.16.335 → 0.16.336
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 +4 -0
- package/dist/ProfileIcicleGraph/ActionButtons/GroupByDropdown.d.ts +6 -0
- package/dist/ProfileIcicleGraph/ActionButtons/GroupByDropdown.js +57 -0
- package/dist/ProfileIcicleGraph/ActionButtons/RuntimeFilterDropdown.d.ts +10 -0
- package/dist/ProfileIcicleGraph/ActionButtons/RuntimeFilterDropdown.js +23 -0
- package/dist/ProfileIcicleGraph/ActionButtons/SortBySelect.d.ts +7 -0
- package/dist/ProfileIcicleGraph/ActionButtons/SortBySelect.js +44 -0
- package/dist/ProfileIcicleGraph/index.d.ts +2 -1
- package/dist/ProfileIcicleGraph/index.js +19 -86
- package/dist/ProfileMetricsGraph/index.js +10 -8
- package/dist/ProfileView/VisualizationPanel.js +1 -1
- package/dist/ProfileView/index.js +6 -30
- package/dist/SourceView/index.js +5 -4
- package/dist/Table/ColumnsVisibility.d.ts +9 -0
- package/dist/Table/ColumnsVisibility.js +22 -0
- package/dist/Table/index.d.ts +13 -0
- package/dist/Table/index.js +13 -12
- package/dist/styles.css +1 -1
- package/package.json +4 -3
- package/src/ProfileIcicleGraph/ActionButtons/GroupByDropdown.tsx +127 -0
- package/src/ProfileIcicleGraph/ActionButtons/RuntimeFilterDropdown.tsx +123 -0
- package/src/ProfileIcicleGraph/ActionButtons/SortBySelect.tsx +80 -0
- package/src/ProfileIcicleGraph/index.tsx +123 -341
- package/src/ProfileMetricsGraph/index.tsx +37 -21
- package/src/ProfileView/VisualizationPanel.tsx +19 -8
- package/src/ProfileView/index.tsx +52 -81
- package/src/SourceView/index.tsx +23 -8
- package/src/Table/ColumnsVisibility.tsx +87 -0
- package/src/Table/index.tsx +53 -86
- package/dist/QueryBrowser/index.d.ts +0 -12
- package/dist/QueryBrowser/index.js +0 -51
|
@@ -53,7 +53,6 @@ import {ProfileSource} from '../ProfileSource';
|
|
|
53
53
|
import {SourceView} from '../SourceView';
|
|
54
54
|
import Table from '../Table';
|
|
55
55
|
import ProfileShareButton from '../components/ProfileShareButton';
|
|
56
|
-
import useDelayedLoader from '../useDelayedLoader';
|
|
57
56
|
import FilterByFunctionButton from './FilterByFunctionButton';
|
|
58
57
|
import {ProfileViewContextProvider} from './ProfileViewContext';
|
|
59
58
|
import ViewSelector from './ViewSelector';
|
|
@@ -153,8 +152,7 @@ export const ProfileView = ({
|
|
|
153
152
|
const isDarkMode = useAppSelector(selectDarkMode);
|
|
154
153
|
const isMultiPanelView = dashboardItems.length > 1;
|
|
155
154
|
|
|
156
|
-
const {
|
|
157
|
-
useParcaContext();
|
|
155
|
+
const {perf, profileViewExternalMainActions, profileViewExternalSubActions} = useParcaContext();
|
|
158
156
|
|
|
159
157
|
useEffect(() => {
|
|
160
158
|
// Reset the current path when the profile source changes
|
|
@@ -169,31 +167,6 @@ export const ProfileView = ({
|
|
|
169
167
|
void loadGraphviz();
|
|
170
168
|
}, []);
|
|
171
169
|
|
|
172
|
-
const isLoading = useMemo(() => {
|
|
173
|
-
if (dashboardItems.includes('icicle')) {
|
|
174
|
-
return Boolean(flamegraphData?.loading);
|
|
175
|
-
}
|
|
176
|
-
if (dashboardItems.includes('callgraph')) {
|
|
177
|
-
return Boolean(callgraphData?.loading) || Boolean(callgraphSVG === undefined);
|
|
178
|
-
}
|
|
179
|
-
if (dashboardItems.includes('table')) {
|
|
180
|
-
return Boolean(topTableData?.loading);
|
|
181
|
-
}
|
|
182
|
-
if (dashboardItems.includes('source')) {
|
|
183
|
-
return Boolean(sourceData?.loading);
|
|
184
|
-
}
|
|
185
|
-
return false;
|
|
186
|
-
}, [
|
|
187
|
-
dashboardItems,
|
|
188
|
-
callgraphData?.loading,
|
|
189
|
-
flamegraphData?.loading,
|
|
190
|
-
topTableData?.loading,
|
|
191
|
-
sourceData?.loading,
|
|
192
|
-
callgraphSVG,
|
|
193
|
-
]);
|
|
194
|
-
|
|
195
|
-
const isLoaderVisible = useDelayedLoader(isLoading);
|
|
196
|
-
|
|
197
170
|
const maxColor: string = getNewSpanColor(isDarkMode);
|
|
198
171
|
const minColor: string = scaleLinear([isDarkMode ? 'black' : 'white', maxColor])(0.3);
|
|
199
172
|
const colorRange: [string, string] = [minColor, maxColor];
|
|
@@ -269,6 +242,7 @@ export const ProfileView = ({
|
|
|
269
242
|
loading={flamegraphData.loading}
|
|
270
243
|
setActionButtons={setActionButtons}
|
|
271
244
|
error={flamegraphData.error}
|
|
245
|
+
isHalfScreen={isHalfScreen}
|
|
272
246
|
width={
|
|
273
247
|
dimensions?.width !== undefined
|
|
274
248
|
? isHalfScreen
|
|
@@ -305,6 +279,7 @@ export const ProfileView = ({
|
|
|
305
279
|
navigateTo={navigateTo}
|
|
306
280
|
setActionButtons={setActionButtons}
|
|
307
281
|
currentSearchString={currentSearchString as string}
|
|
282
|
+
isHalfScreen={isHalfScreen}
|
|
308
283
|
/>
|
|
309
284
|
) : (
|
|
310
285
|
<></>
|
|
@@ -432,59 +407,55 @@ export const ProfileView = ({
|
|
|
432
407
|
</div>
|
|
433
408
|
|
|
434
409
|
<div className="w-full" ref={ref}>
|
|
435
|
-
{
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
)}
|
|
485
|
-
</Droppable>
|
|
486
|
-
</DragDropContext>
|
|
487
|
-
)}
|
|
410
|
+
<DragDropContext onDragEnd={onDragEnd}>
|
|
411
|
+
<Droppable droppableId="droppable" direction="horizontal">
|
|
412
|
+
{provided => (
|
|
413
|
+
<div
|
|
414
|
+
ref={provided.innerRef}
|
|
415
|
+
className={cx(
|
|
416
|
+
'grid w-full gap-2',
|
|
417
|
+
isMultiPanelView ? 'grid-cols-2' : 'grid-cols-1'
|
|
418
|
+
)}
|
|
419
|
+
{...provided.droppableProps}
|
|
420
|
+
>
|
|
421
|
+
{dashboardItems.map((dashboardItem, index) => {
|
|
422
|
+
return (
|
|
423
|
+
<Draggable
|
|
424
|
+
key={dashboardItem}
|
|
425
|
+
draggableId={dashboardItem}
|
|
426
|
+
index={index}
|
|
427
|
+
isDragDisabled={!isMultiPanelView}
|
|
428
|
+
>
|
|
429
|
+
{(provided, snapshot: {isDragging: boolean}) => (
|
|
430
|
+
<div
|
|
431
|
+
ref={provided.innerRef}
|
|
432
|
+
{...provided.draggableProps}
|
|
433
|
+
key={dashboardItem}
|
|
434
|
+
className={cx(
|
|
435
|
+
'w-full rounded p-2 shadow dark:border dark:border-gray-700 dark:bg-gray-700 min-h-96',
|
|
436
|
+
snapshot.isDragging
|
|
437
|
+
? 'bg-gray-200 dark:bg-gray-500'
|
|
438
|
+
: 'bg-white dark:bg-gray-700'
|
|
439
|
+
)}
|
|
440
|
+
>
|
|
441
|
+
<VisualizationPanel
|
|
442
|
+
handleClosePanel={handleClosePanel}
|
|
443
|
+
isMultiPanelView={isMultiPanelView}
|
|
444
|
+
dashboardItem={dashboardItem}
|
|
445
|
+
getDashboardItemByType={getDashboardItemByType}
|
|
446
|
+
dragHandleProps={provided.dragHandleProps}
|
|
447
|
+
navigateTo={navigateTo}
|
|
448
|
+
index={index}
|
|
449
|
+
/>
|
|
450
|
+
</div>
|
|
451
|
+
)}
|
|
452
|
+
</Draggable>
|
|
453
|
+
);
|
|
454
|
+
})}
|
|
455
|
+
</div>
|
|
456
|
+
)}
|
|
457
|
+
</Droppable>
|
|
458
|
+
</DragDropContext>
|
|
488
459
|
</div>
|
|
489
460
|
</ProfileViewContextProvider>
|
|
490
461
|
</KeyDownProvider>
|
package/src/SourceView/index.tsx
CHANGED
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
import React, {useEffect} from 'react';
|
|
15
15
|
|
|
16
16
|
import {tableFromIPC} from 'apache-arrow';
|
|
17
|
+
import {AnimatePresence, motion} from 'framer-motion';
|
|
17
18
|
|
|
18
19
|
import {Source} from '@parca/client';
|
|
19
|
-
import {useParcaContext, useURLState} from '@parca/components';
|
|
20
|
+
import {SourceSkeleton, useParcaContext, useURLState} from '@parca/components';
|
|
20
21
|
|
|
21
22
|
import {ExpandOnHover} from '../GraphTooltipArrow/ExpandOnHoverValue';
|
|
22
23
|
import {truncateStringReverse} from '../utils';
|
|
@@ -38,7 +39,7 @@ export const SourceView = React.memo(function SourceView({
|
|
|
38
39
|
setActionButtons,
|
|
39
40
|
}: SourceViewProps): JSX.Element {
|
|
40
41
|
const [sourceFileName] = useURLState({param: 'source_filename', navigateTo: () => {}});
|
|
41
|
-
const {
|
|
42
|
+
const {isDarkMode} = useParcaContext();
|
|
42
43
|
|
|
43
44
|
useEffect(() => {
|
|
44
45
|
setActionButtons?.(
|
|
@@ -52,7 +53,11 @@ export const SourceView = React.memo(function SourceView({
|
|
|
52
53
|
}, [sourceFileName, setActionButtons]);
|
|
53
54
|
|
|
54
55
|
if (loading) {
|
|
55
|
-
return
|
|
56
|
+
return (
|
|
57
|
+
<div className="h-auto overflow-clip">
|
|
58
|
+
<SourceSkeleton isDarkMode={isDarkMode} />
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
if (data === undefined) {
|
|
@@ -64,11 +69,21 @@ export const SourceView = React.memo(function SourceView({
|
|
|
64
69
|
const flat = table.getChild('flat');
|
|
65
70
|
|
|
66
71
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
<AnimatePresence>
|
|
73
|
+
<motion.div
|
|
74
|
+
className="h-full w-full"
|
|
75
|
+
key="source-view-loaded"
|
|
76
|
+
initial={{display: 'none', opacity: 0}}
|
|
77
|
+
animate={{display: 'block', opacity: 1}}
|
|
78
|
+
transition={{duration: 0.5}}
|
|
79
|
+
>
|
|
80
|
+
<Highlighter
|
|
81
|
+
file={sourceFileName as string}
|
|
82
|
+
content={data.source}
|
|
83
|
+
renderer={profileAwareRenderer(cumulative, flat, total, filtered)}
|
|
84
|
+
/>
|
|
85
|
+
</motion.div>
|
|
86
|
+
</AnimatePresence>
|
|
72
87
|
);
|
|
73
88
|
});
|
|
74
89
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Copyright 2022 The Parca Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
|
|
14
|
+
import {Fragment} from 'react';
|
|
15
|
+
|
|
16
|
+
import {Menu, Transition} from '@headlessui/react';
|
|
17
|
+
import {Icon} from '@iconify/react';
|
|
18
|
+
import {type VisibilityState} from '@tanstack/react-table';
|
|
19
|
+
|
|
20
|
+
import {ColumnDef} from '.';
|
|
21
|
+
|
|
22
|
+
const ColumnsVisibility = ({
|
|
23
|
+
columns,
|
|
24
|
+
visibility,
|
|
25
|
+
setVisibility,
|
|
26
|
+
}: {
|
|
27
|
+
columns: ColumnDef[];
|
|
28
|
+
visibility: VisibilityState;
|
|
29
|
+
setVisibility: (id: string, visible: boolean) => void;
|
|
30
|
+
}): React.JSX.Element => {
|
|
31
|
+
return (
|
|
32
|
+
<div>
|
|
33
|
+
<Menu as="div" className="relative text-left">
|
|
34
|
+
<div>
|
|
35
|
+
<Menu.Button className="relative w-full cursor-default rounded-md border bg-white py-2 pl-3 pr-10 text-left text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900 sm:text-sm">
|
|
36
|
+
<span className="block overflow-x-hidden text-ellipsis">Columns</span>
|
|
37
|
+
<span className="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2 text-gray-400">
|
|
38
|
+
<Icon icon="heroicons:chevron-down-20-solid" aria-hidden="true" />
|
|
39
|
+
</span>
|
|
40
|
+
</Menu.Button>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<Transition
|
|
44
|
+
as={Fragment}
|
|
45
|
+
leave="transition ease-in duration-100"
|
|
46
|
+
leaveFrom="opacity-100"
|
|
47
|
+
leaveTo="opacity-0"
|
|
48
|
+
>
|
|
49
|
+
<Menu.Items className="absolute left-0 z-10 mt-1 min-w-[400px] overflow-auto rounded-md bg-gray-50 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-gray-600 dark:bg-gray-900 dark:ring-white dark:ring-opacity-20 sm:text-sm">
|
|
50
|
+
<div className="p-4">
|
|
51
|
+
<fieldset>
|
|
52
|
+
<div className="space-y-5">
|
|
53
|
+
{columns.map(col => (
|
|
54
|
+
<div key={col.id} className="relative flex items-start">
|
|
55
|
+
<div className="flex h-6 items-center">
|
|
56
|
+
<input
|
|
57
|
+
id={col.id}
|
|
58
|
+
name={col.id}
|
|
59
|
+
type="checkbox"
|
|
60
|
+
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600"
|
|
61
|
+
checked={visibility[col.id ?? ''] ?? false}
|
|
62
|
+
onChange={() => {
|
|
63
|
+
setVisibility(col.id ?? '', !visibility[col.id ?? '']);
|
|
64
|
+
}}
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
<div className="ml-3 text-sm leading-6">
|
|
68
|
+
<label
|
|
69
|
+
htmlFor={col.id}
|
|
70
|
+
className="font-medium text-gray-900 dark:text-gray-200"
|
|
71
|
+
>
|
|
72
|
+
{col.header}
|
|
73
|
+
</label>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
))}
|
|
77
|
+
</div>
|
|
78
|
+
</fieldset>
|
|
79
|
+
</div>
|
|
80
|
+
</Menu.Items>
|
|
81
|
+
</Transition>
|
|
82
|
+
</Menu>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default ColumnsVisibility;
|
package/src/Table/index.tsx
CHANGED
|
@@ -11,14 +11,19 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import React, {
|
|
14
|
+
import React, {useCallback, useEffect, useMemo, useState} from 'react';
|
|
15
15
|
|
|
16
|
-
import {Menu, Transition} from '@headlessui/react';
|
|
17
|
-
import {Icon} from '@iconify/react';
|
|
18
|
-
import {type VisibilityState} from '@tanstack/react-table';
|
|
19
16
|
import {Vector, tableFromIPC} from 'apache-arrow';
|
|
17
|
+
import {AnimatePresence, motion} from 'framer-motion';
|
|
20
18
|
|
|
21
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
Button,
|
|
21
|
+
TableActionButtonPlaceholder,
|
|
22
|
+
Table as TableComponent,
|
|
23
|
+
TableSkeleton,
|
|
24
|
+
useParcaContext,
|
|
25
|
+
useURLState,
|
|
26
|
+
} from '@parca/components';
|
|
22
27
|
import {
|
|
23
28
|
getLastItem,
|
|
24
29
|
isSearchMatch,
|
|
@@ -29,6 +34,7 @@ import {
|
|
|
29
34
|
|
|
30
35
|
import {useProfileViewContext} from '../ProfileView/ProfileViewContext';
|
|
31
36
|
import {hexifyAddress} from '../utils';
|
|
37
|
+
import ColumnsVisibility from './ColumnsVisibility';
|
|
32
38
|
|
|
33
39
|
const FIELD_MAPPING_FILE = 'mapping_file';
|
|
34
40
|
const FIELD_LOCATION_ADDRESS = 'location_address';
|
|
@@ -51,7 +57,7 @@ interface row {
|
|
|
51
57
|
functionFileName: string;
|
|
52
58
|
}
|
|
53
59
|
|
|
54
|
-
interface ColumnDef {
|
|
60
|
+
export interface ColumnDef {
|
|
55
61
|
id: string;
|
|
56
62
|
header: string;
|
|
57
63
|
accessorKey: string;
|
|
@@ -71,6 +77,7 @@ interface TableProps {
|
|
|
71
77
|
loading: boolean;
|
|
72
78
|
currentSearchString?: string;
|
|
73
79
|
setActionButtons?: (buttons: React.JSX.Element) => void;
|
|
80
|
+
isHalfScreen: boolean;
|
|
74
81
|
}
|
|
75
82
|
|
|
76
83
|
export const Table = React.memo(function Table({
|
|
@@ -82,10 +89,12 @@ export const Table = React.memo(function Table({
|
|
|
82
89
|
loading,
|
|
83
90
|
currentSearchString,
|
|
84
91
|
setActionButtons,
|
|
92
|
+
isHalfScreen,
|
|
85
93
|
}: TableProps): React.JSX.Element {
|
|
86
94
|
const router = parseParams(window?.location.search);
|
|
87
95
|
const [rawDashboardItems] = useURLState({param: 'dashboard_items'});
|
|
88
96
|
const [filterByFunctionInput] = useURLState({param: 'filter_by_function'});
|
|
97
|
+
const {isDarkMode} = useParcaContext();
|
|
89
98
|
|
|
90
99
|
const {compareMode} = useProfileViewContext();
|
|
91
100
|
|
|
@@ -298,9 +307,15 @@ export const Table = React.memo(function Table({
|
|
|
298
307
|
}, [navigateTo, router, filterByFunctionInput]);
|
|
299
308
|
|
|
300
309
|
useEffect(() => {
|
|
310
|
+
if (loading && setActionButtons !== undefined) {
|
|
311
|
+
setActionButtons(<TableActionButtonPlaceholder />);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
301
315
|
if (setActionButtons === undefined) {
|
|
302
316
|
return;
|
|
303
317
|
}
|
|
318
|
+
|
|
304
319
|
setActionButtons(
|
|
305
320
|
<>
|
|
306
321
|
<ColumnsVisibility
|
|
@@ -330,6 +345,7 @@ export const Table = React.memo(function Table({
|
|
|
330
345
|
setActionButtons,
|
|
331
346
|
columns,
|
|
332
347
|
columnVisibility,
|
|
348
|
+
loading,
|
|
333
349
|
]);
|
|
334
350
|
|
|
335
351
|
const initialSorting = useMemo(() => {
|
|
@@ -341,7 +357,13 @@ export const Table = React.memo(function Table({
|
|
|
341
357
|
];
|
|
342
358
|
}, [compareMode]);
|
|
343
359
|
|
|
344
|
-
if (loading)
|
|
360
|
+
if (loading)
|
|
361
|
+
return (
|
|
362
|
+
<div className="overflow-clip h-[700px] min-h-[700px]">
|
|
363
|
+
<TableSkeleton isHalfScreen={isHalfScreen} isDarkMode={isDarkMode} />
|
|
364
|
+
</div>
|
|
365
|
+
);
|
|
366
|
+
|
|
345
367
|
if (data === undefined) return <div className="mx-auto text-center">Profile has no samples</div>;
|
|
346
368
|
|
|
347
369
|
const table = tableFromIPC(data);
|
|
@@ -380,87 +402,32 @@ export const Table = React.memo(function Table({
|
|
|
380
402
|
}
|
|
381
403
|
|
|
382
404
|
return (
|
|
383
|
-
<
|
|
384
|
-
<div
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}: {
|
|
405
|
-
columns: ColumnDef[];
|
|
406
|
-
visibility: VisibilityState;
|
|
407
|
-
setVisibility: (id: string, visible: boolean) => void;
|
|
408
|
-
}): React.JSX.Element => {
|
|
409
|
-
return (
|
|
410
|
-
<div>
|
|
411
|
-
<Menu as="div" className="relative text-left">
|
|
412
|
-
<div>
|
|
413
|
-
<Menu.Button className="relative w-full cursor-default rounded-md border bg-white py-2 pl-3 pr-10 text-left text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900 sm:text-sm">
|
|
414
|
-
<span className="ml-3 block overflow-x-hidden text-ellipsis">Columns</span>
|
|
415
|
-
<span className="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2 text-gray-400">
|
|
416
|
-
<Icon icon="heroicons:chevron-down-20-solid" aria-hidden="true" />
|
|
417
|
-
</span>
|
|
418
|
-
</Menu.Button>
|
|
405
|
+
<AnimatePresence>
|
|
406
|
+
<motion.div
|
|
407
|
+
className="h-full w-full"
|
|
408
|
+
key="table-loaded"
|
|
409
|
+
initial={{display: 'none', opacity: 0}}
|
|
410
|
+
animate={{display: 'block', opacity: 1}}
|
|
411
|
+
transition={{duration: 0.5}}
|
|
412
|
+
>
|
|
413
|
+
<div className="relative">
|
|
414
|
+
<div className="font-robotoMono h-[80vh] w-full">
|
|
415
|
+
<TableComponent
|
|
416
|
+
data={rows}
|
|
417
|
+
columns={columns}
|
|
418
|
+
initialSorting={initialSorting}
|
|
419
|
+
columnVisibility={columnVisibility}
|
|
420
|
+
onRowClick={onRowClick}
|
|
421
|
+
enableHighlighting={enableHighlighting}
|
|
422
|
+
shouldHighlightRow={shouldHighlightRow}
|
|
423
|
+
usePointerCursor={dashboardItems.length > 1}
|
|
424
|
+
/>
|
|
425
|
+
</div>
|
|
419
426
|
</div>
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
as={Fragment}
|
|
423
|
-
leave="transition ease-in duration-100"
|
|
424
|
-
leaveFrom="opacity-100"
|
|
425
|
-
leaveTo="opacity-0"
|
|
426
|
-
>
|
|
427
|
-
<Menu.Items className="absolute left-0 z-10 mt-1 min-w-[400px] overflow-auto rounded-md bg-gray-50 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:border-gray-600 dark:bg-gray-900 dark:ring-white dark:ring-opacity-20 sm:text-sm">
|
|
428
|
-
<div className="p-4">
|
|
429
|
-
<fieldset>
|
|
430
|
-
<div className="space-y-5">
|
|
431
|
-
{columns.map(col => (
|
|
432
|
-
<div key={col.id} className="relative flex items-start">
|
|
433
|
-
<div className="flex h-6 items-center">
|
|
434
|
-
<input
|
|
435
|
-
id={col.id}
|
|
436
|
-
name={col.id}
|
|
437
|
-
type="checkbox"
|
|
438
|
-
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600"
|
|
439
|
-
checked={visibility[col.id ?? ''] ?? false}
|
|
440
|
-
onChange={() => {
|
|
441
|
-
setVisibility(col.id ?? '', !visibility[col.id ?? '']);
|
|
442
|
-
}}
|
|
443
|
-
/>
|
|
444
|
-
</div>
|
|
445
|
-
<div className="ml-3 text-sm leading-6">
|
|
446
|
-
<label
|
|
447
|
-
htmlFor={col.id}
|
|
448
|
-
className="font-medium text-gray-900 dark:text-gray-200"
|
|
449
|
-
>
|
|
450
|
-
{col.header}
|
|
451
|
-
</label>
|
|
452
|
-
</div>
|
|
453
|
-
</div>
|
|
454
|
-
))}
|
|
455
|
-
</div>
|
|
456
|
-
</fieldset>
|
|
457
|
-
</div>
|
|
458
|
-
</Menu.Items>
|
|
459
|
-
</Transition>
|
|
460
|
-
</Menu>
|
|
461
|
-
</div>
|
|
427
|
+
</motion.div>
|
|
428
|
+
</AnimatePresence>
|
|
462
429
|
);
|
|
463
|
-
};
|
|
430
|
+
});
|
|
464
431
|
|
|
465
432
|
const addPlusSign = (num: string): string => {
|
|
466
433
|
if (num.charAt(0) === '0' || num.charAt(0) === '-') {
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { QueryServiceClient } from '@parca/client';
|
|
2
|
-
import { DateTimeRange } from '@parca/components';
|
|
3
|
-
import { QuerySelection } from '../ProfileSelector';
|
|
4
|
-
interface Props {
|
|
5
|
-
queryClient: QueryServiceClient;
|
|
6
|
-
selectQuery: (query: QuerySelection) => void;
|
|
7
|
-
enforcedProfileName: string;
|
|
8
|
-
timeRangeSelection: DateTimeRange;
|
|
9
|
-
querySelection: QuerySelection;
|
|
10
|
-
}
|
|
11
|
-
declare const QueryBrowser: ({ queryClient, enforcedProfileName, timeRangeSelection, selectQuery, querySelection, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
-
export default QueryBrowser;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
3
|
-
import { Query } from '@parca/parser';
|
|
4
|
-
import MatchersInput from '../MatchersInput';
|
|
5
|
-
const QueryBrowser = ({ queryClient, enforcedProfileName, timeRangeSelection, selectQuery, querySelection, }) => {
|
|
6
|
-
const [queryExpressionString, setQueryExpressionString] = useState(querySelection.expression);
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
if (enforcedProfileName !== '') {
|
|
9
|
-
const [q, changed] = Query.parse(querySelection.expression).setProfileName(enforcedProfileName);
|
|
10
|
-
if (changed) {
|
|
11
|
-
setQueryExpressionString(q.toString());
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
setQueryExpressionString(querySelection.expression);
|
|
16
|
-
}, [enforcedProfileName, querySelection.expression]);
|
|
17
|
-
const enforcedProfileNameQuery = () => {
|
|
18
|
-
const pq = Query.parse(queryExpressionString);
|
|
19
|
-
const [q] = pq.setProfileName(enforcedProfileName);
|
|
20
|
-
return q;
|
|
21
|
-
};
|
|
22
|
-
const setMatchersString = (matchers) => {
|
|
23
|
-
const newExpressionString = `${''}{${matchers}}`;
|
|
24
|
-
setQueryExpressionString(newExpressionString);
|
|
25
|
-
};
|
|
26
|
-
const setNewQueryExpression = (expr) => {
|
|
27
|
-
const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(expr);
|
|
28
|
-
const delta = query.profileType().delta;
|
|
29
|
-
const from = timeRangeSelection.getFromMs();
|
|
30
|
-
const to = timeRangeSelection.getToMs();
|
|
31
|
-
const mergeParams = delta
|
|
32
|
-
? {
|
|
33
|
-
mergeFrom: from,
|
|
34
|
-
mergeTo: to,
|
|
35
|
-
}
|
|
36
|
-
: {};
|
|
37
|
-
selectQuery({
|
|
38
|
-
expression: expr,
|
|
39
|
-
from,
|
|
40
|
-
to,
|
|
41
|
-
timeSelection: timeRangeSelection.getRangeKey(),
|
|
42
|
-
...mergeParams,
|
|
43
|
-
});
|
|
44
|
-
};
|
|
45
|
-
const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(queryExpressionString);
|
|
46
|
-
const setQueryExpression = () => {
|
|
47
|
-
setNewQueryExpression(query.toString());
|
|
48
|
-
};
|
|
49
|
-
return (_jsx(_Fragment, { children: _jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query }) }));
|
|
50
|
-
};
|
|
51
|
-
export default QueryBrowser;
|