@parca/profile 0.19.9 → 0.19.11
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 +8 -0
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.d.ts.map +1 -1
- package/dist/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.js +2 -2
- package/dist/ProfileView/components/ActionButtons/GroupByDropdown.d.ts +0 -1
- package/dist/ProfileView/components/ActionButtons/GroupByDropdown.d.ts.map +1 -1
- package/dist/ProfileView/components/ActionButtons/GroupByDropdown.js +4 -141
- package/dist/ProfileView/components/GroupByLabelsDropdown/index.d.ts +8 -0
- package/dist/ProfileView/components/GroupByLabelsDropdown/index.d.ts.map +1 -0
- package/dist/ProfileView/components/GroupByLabelsDropdown/index.js +57 -0
- package/dist/ProfileView/components/InvertCallStack/index.d.ts +3 -0
- package/dist/ProfileView/components/InvertCallStack/index.d.ts.map +1 -0
- package/dist/ProfileView/components/InvertCallStack/index.js +21 -0
- package/dist/ProfileView/components/ShareButton/index.d.ts.map +1 -1
- package/dist/ProfileView/components/ShareButton/index.js +1 -1
- package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts +3 -0
- package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts.map +1 -1
- package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.js +78 -20
- package/dist/ProfileView/components/Toolbars/SwitchMenuItem.d.ts +9 -0
- package/dist/ProfileView/components/Toolbars/SwitchMenuItem.d.ts.map +1 -0
- package/dist/ProfileView/components/Toolbars/SwitchMenuItem.js +22 -0
- package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
- package/dist/ProfileView/components/Toolbars/index.js +4 -1
- package/dist/ProfileView/components/ViewSelector/Dropdown.js +1 -1
- package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
- package/dist/ProfileView/components/ViewSelector/index.js +9 -12
- package/dist/ProfileView/hooks/useVisualizationState.d.ts.map +1 -1
- package/dist/ProfileView/hooks/useVisualizationState.js +16 -6
- package/dist/styles.css +1 -1
- package/package.json +2 -2
- package/src/ProfileIcicleGraph/IcicleGraphArrow/ContextMenu.tsx +7 -2
- package/src/ProfileView/components/ActionButtons/GroupByDropdown.tsx +7 -323
- package/src/ProfileView/components/GroupByLabelsDropdown/index.tsx +92 -0
- package/src/ProfileView/components/InvertCallStack/index.tsx +34 -0
- package/src/ProfileView/components/ShareButton/index.tsx +8 -4
- package/src/ProfileView/components/Toolbars/MultiLevelDropdown.tsx +134 -22
- package/src/ProfileView/components/Toolbars/SwitchMenuItem.tsx +50 -0
- package/src/ProfileView/components/Toolbars/index.tsx +25 -9
- package/src/ProfileView/components/ViewSelector/Dropdown.tsx +1 -1
- package/src/ProfileView/components/ViewSelector/index.tsx +15 -14
- package/src/ProfileView/hooks/useVisualizationState.ts +25 -6
|
@@ -11,341 +11,25 @@
|
|
|
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 from 'react';
|
|
15
15
|
|
|
16
|
-
import
|
|
17
|
-
import {Icon} from '@iconify/react';
|
|
18
|
-
import Select from 'react-select';
|
|
19
|
-
|
|
20
|
-
import {Button} from '@parca/components';
|
|
21
|
-
|
|
22
|
-
import {
|
|
23
|
-
FIELD_FUNCTION_FILE_NAME,
|
|
24
|
-
FIELD_FUNCTION_NAME,
|
|
25
|
-
FIELD_LABELS,
|
|
26
|
-
FIELD_LOCATION_ADDRESS,
|
|
27
|
-
FIELD_MAPPING_FILE,
|
|
28
|
-
} from '../../../ProfileIcicleGraph/IcicleGraphArrow';
|
|
29
|
-
|
|
30
|
-
interface LabelSelectorProps {
|
|
31
|
-
labels: string[];
|
|
32
|
-
groupBy: string[];
|
|
33
|
-
setGroupByLabels: (labels: string[]) => void;
|
|
34
|
-
isOpen: boolean;
|
|
35
|
-
labelsButtonRef: React.RefObject<HTMLDivElement>;
|
|
36
|
-
setIsLabelSelectorOpen: (isOpen: boolean) => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface LabelSelectorProps {
|
|
40
|
-
labels: string[];
|
|
41
|
-
groupBy: string[];
|
|
42
|
-
setGroupByLabels: (labels: string[]) => void;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface LabelOption {
|
|
46
|
-
label: string;
|
|
47
|
-
value: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
interface GroupByDropdownProps {
|
|
51
|
-
groupBy: string[];
|
|
52
|
-
toggleGroupBy: (key: string) => void;
|
|
53
|
-
onLabelClick: () => void;
|
|
54
|
-
labelsButtonRef: React.RefObject<HTMLDivElement>;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const groupByOptions = [
|
|
58
|
-
{
|
|
59
|
-
value: FIELD_FUNCTION_NAME,
|
|
60
|
-
label: 'Function Name',
|
|
61
|
-
description: 'Stacktraces are grouped by function names.',
|
|
62
|
-
disabled: true,
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
value: FIELD_FUNCTION_FILE_NAME,
|
|
66
|
-
label: 'Filename',
|
|
67
|
-
description: 'Stacktraces are grouped by filenames.',
|
|
68
|
-
disabled: false,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
value: FIELD_LOCATION_ADDRESS,
|
|
72
|
-
label: 'Address',
|
|
73
|
-
description: 'Stacktraces are grouped by addresses.',
|
|
74
|
-
disabled: false,
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
value: FIELD_MAPPING_FILE,
|
|
78
|
-
label: 'Binary',
|
|
79
|
-
description: 'Stacktraces are grouped by binaries.',
|
|
80
|
-
disabled: false,
|
|
81
|
-
},
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
const LabelSelector: React.FC<LabelSelectorProps> = ({
|
|
85
|
-
labels,
|
|
86
|
-
groupBy,
|
|
87
|
-
setGroupByLabels,
|
|
88
|
-
isOpen,
|
|
89
|
-
labelsButtonRef,
|
|
90
|
-
setIsLabelSelectorOpen,
|
|
91
|
-
}) => {
|
|
92
|
-
const [position, setPosition] = useState({top: 0, left: 0});
|
|
93
|
-
|
|
94
|
-
useEffect(() => {
|
|
95
|
-
if (isOpen && labelsButtonRef.current !== null) {
|
|
96
|
-
const rect = labelsButtonRef.current.getBoundingClientRect();
|
|
97
|
-
const parentRect = labelsButtonRef.current.offsetParent?.getBoundingClientRect() ?? {
|
|
98
|
-
top: 0,
|
|
99
|
-
left: 0,
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
setPosition({
|
|
103
|
-
top: rect.bottom - parentRect.top,
|
|
104
|
-
left: rect.right - parentRect.left + 4,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}, [isOpen, labelsButtonRef]);
|
|
108
|
-
|
|
109
|
-
if (!isOpen) return null;
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<div
|
|
113
|
-
className="absolute w-64 ml-4 z-20"
|
|
114
|
-
style={{
|
|
115
|
-
top: `${position.top}px`,
|
|
116
|
-
left: `${position.left}px`,
|
|
117
|
-
}}
|
|
118
|
-
>
|
|
119
|
-
<Select<LabelOption, true>
|
|
120
|
-
isMulti
|
|
121
|
-
name="labels"
|
|
122
|
-
options={labels.map(label => ({label, value: `${FIELD_LABELS}.${label}`}))}
|
|
123
|
-
className="parca-select-container text-sm w-full border-gray-300 border rounded-md"
|
|
124
|
-
classNamePrefix="parca-select"
|
|
125
|
-
value={groupBy
|
|
126
|
-
.filter(l => l.startsWith(FIELD_LABELS))
|
|
127
|
-
.map(l => ({value: l, label: l.slice(FIELD_LABELS.length + 1)}))}
|
|
128
|
-
onChange={newValue => {
|
|
129
|
-
setGroupByLabels(newValue.map(option => option.value));
|
|
130
|
-
setIsLabelSelectorOpen(false);
|
|
131
|
-
}}
|
|
132
|
-
placeholder="Select labels..."
|
|
133
|
-
styles={{
|
|
134
|
-
menu: provided => ({
|
|
135
|
-
...provided,
|
|
136
|
-
position: 'relative',
|
|
137
|
-
marginBottom: 0,
|
|
138
|
-
boxShadow: 'none',
|
|
139
|
-
marginTop: 0,
|
|
140
|
-
}),
|
|
141
|
-
control: provided => ({
|
|
142
|
-
...provided,
|
|
143
|
-
boxShadow: 'none',
|
|
144
|
-
borderBottom: '1px solid #e2e8f0',
|
|
145
|
-
borderRight: 0,
|
|
146
|
-
borderLeft: 0,
|
|
147
|
-
borderTop: 0,
|
|
148
|
-
borderBottomLeftRadius: 0,
|
|
149
|
-
borderBottomRightRadius: 0,
|
|
150
|
-
':hover': {
|
|
151
|
-
borderColor: '#e2e8f0',
|
|
152
|
-
borderBottomLeftRadius: 0,
|
|
153
|
-
borderBottomRightRadius: 0,
|
|
154
|
-
},
|
|
155
|
-
}),
|
|
156
|
-
}}
|
|
157
|
-
menuIsOpen={true}
|
|
158
|
-
/>
|
|
159
|
-
</div>
|
|
160
|
-
);
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const GroupByDropdown: React.FC<GroupByDropdownProps> = ({
|
|
164
|
-
groupBy,
|
|
165
|
-
toggleGroupBy,
|
|
166
|
-
onLabelClick,
|
|
167
|
-
labelsButtonRef,
|
|
168
|
-
}) => {
|
|
169
|
-
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
170
|
-
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
171
|
-
|
|
172
|
-
useEffect(() => {
|
|
173
|
-
const handleClickOutside = (event: MouseEvent): void => {
|
|
174
|
-
if (
|
|
175
|
-
isDropdownOpen &&
|
|
176
|
-
dropdownRef.current != null &&
|
|
177
|
-
!dropdownRef.current.contains(event.target as Node)
|
|
178
|
-
) {
|
|
179
|
-
setIsDropdownOpen(false);
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
184
|
-
return () => {
|
|
185
|
-
document.removeEventListener('mousedown', handleClickOutside);
|
|
186
|
-
};
|
|
187
|
-
}, [isDropdownOpen]);
|
|
188
|
-
|
|
189
|
-
const label =
|
|
190
|
-
groupBy.length === 0
|
|
191
|
-
? 'Nothing'
|
|
192
|
-
: groupBy.length === 1
|
|
193
|
-
? groupByOptions.find(option => option.value === groupBy[0])?.label
|
|
194
|
-
: 'Multiple';
|
|
195
|
-
|
|
196
|
-
const selectedLabels = groupBy
|
|
197
|
-
.filter(l => l.startsWith(FIELD_LABELS))
|
|
198
|
-
.map(l => l.slice(FIELD_LABELS.length + 1));
|
|
199
|
-
|
|
200
|
-
return (
|
|
201
|
-
<div className="relative" ref={dropdownRef}>
|
|
202
|
-
<label className="text-sm">Group by</label>
|
|
203
|
-
<div className="relative text-left" id="h-group-by-filter">
|
|
204
|
-
<Button
|
|
205
|
-
variant="neutral"
|
|
206
|
-
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
|
207
|
-
className="relative w-max cursor-default rounded-md border bg-white py-2 pl-3 pr-[1.7rem] text-left text-sm shadow-sm dark:border-gray-600 dark:bg-gray-900 sm:text-sm"
|
|
208
|
-
>
|
|
209
|
-
<span className="block overflow-x-hidden text-ellipsis">{label}</span>
|
|
210
|
-
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2 text-gray-400">
|
|
211
|
-
<Icon icon="heroicons:chevron-down-20-solid" aria-hidden="true" />
|
|
212
|
-
</span>
|
|
213
|
-
</Button>
|
|
214
|
-
|
|
215
|
-
<Transition
|
|
216
|
-
as="div"
|
|
217
|
-
leave="transition ease-in duration-100"
|
|
218
|
-
leaveFrom="opacity-100"
|
|
219
|
-
leaveTo="opacity-0"
|
|
220
|
-
show={isDropdownOpen}
|
|
221
|
-
>
|
|
222
|
-
<div 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">
|
|
223
|
-
<div className="p-4">
|
|
224
|
-
<fieldset>
|
|
225
|
-
<div className="space-y-5">
|
|
226
|
-
{groupByOptions.map(({value, label, description, disabled}) => (
|
|
227
|
-
<div key={value} className="relative flex items-start">
|
|
228
|
-
<div className="flex h-6 items-center">
|
|
229
|
-
<input
|
|
230
|
-
id={value}
|
|
231
|
-
name={value}
|
|
232
|
-
type="checkbox"
|
|
233
|
-
disabled={disabled}
|
|
234
|
-
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600"
|
|
235
|
-
checked={groupBy.includes(value)}
|
|
236
|
-
onChange={() => toggleGroupBy(value)}
|
|
237
|
-
/>
|
|
238
|
-
</div>
|
|
239
|
-
<div className="ml-3 text-sm leading-6">
|
|
240
|
-
<label
|
|
241
|
-
htmlFor={value}
|
|
242
|
-
className="font-medium text-gray-900 dark:text-gray-200"
|
|
243
|
-
>
|
|
244
|
-
{label}
|
|
245
|
-
</label>
|
|
246
|
-
<p className="text-gray-500 dark:text-gray-400">{description}</p>
|
|
247
|
-
</div>
|
|
248
|
-
</div>
|
|
249
|
-
))}
|
|
250
|
-
<div
|
|
251
|
-
className="ml-7 flex flex-col items-start text-sm leading-6 cursor-pointer"
|
|
252
|
-
onClick={onLabelClick}
|
|
253
|
-
ref={labelsButtonRef}
|
|
254
|
-
>
|
|
255
|
-
<div className="flex justify-between w-full items-center">
|
|
256
|
-
<div>
|
|
257
|
-
<span className="font-medium text-gray-900 dark:text-gray-200">Labels</span>
|
|
258
|
-
<p className="text-gray-500 dark:text-gray-400">
|
|
259
|
-
Stacktraces are grouped by labels.
|
|
260
|
-
</p>
|
|
261
|
-
</div>
|
|
262
|
-
|
|
263
|
-
<Icon icon="flowbite:caret-right-solid" className="h-[14px] w-[14px]" />
|
|
264
|
-
</div>
|
|
265
|
-
|
|
266
|
-
{selectedLabels.length > 0 && (
|
|
267
|
-
<div className="flex gap-2 flex-wrap">
|
|
268
|
-
<span className="text-gray-500 dark:text-gray-200">Selected labels:</span>
|
|
269
|
-
|
|
270
|
-
<div className="flex flex-wrap gap-3">
|
|
271
|
-
{selectedLabels.map(label => (
|
|
272
|
-
<span
|
|
273
|
-
key={label}
|
|
274
|
-
className="mr-2 px-3 py-1 text-xs text-gray-700 dark:text-gray-200 bg-gray-200 rounded-md dark:bg-gray-800"
|
|
275
|
-
>
|
|
276
|
-
{label}
|
|
277
|
-
</span>
|
|
278
|
-
))}
|
|
279
|
-
</div>
|
|
280
|
-
</div>
|
|
281
|
-
)}
|
|
282
|
-
</div>
|
|
283
|
-
</div>
|
|
284
|
-
</fieldset>
|
|
285
|
-
</div>
|
|
286
|
-
</div>
|
|
287
|
-
</Transition>
|
|
288
|
-
</div>
|
|
289
|
-
</div>
|
|
290
|
-
);
|
|
291
|
-
};
|
|
16
|
+
import GroupByLabelsDropdown from '../GroupByLabelsDropdown';
|
|
292
17
|
|
|
293
18
|
interface GroupByControlsProps {
|
|
294
19
|
groupBy: string[];
|
|
295
20
|
labels: string[];
|
|
296
|
-
toggleGroupBy: (key: string) => void;
|
|
297
21
|
setGroupByLabels: (labels: string[]) => void;
|
|
298
22
|
}
|
|
299
23
|
|
|
300
|
-
const GroupByControls: React.FC<GroupByControlsProps> = ({
|
|
301
|
-
groupBy,
|
|
302
|
-
labels,
|
|
303
|
-
toggleGroupBy,
|
|
304
|
-
setGroupByLabels,
|
|
305
|
-
}) => {
|
|
306
|
-
const [isLabelSelectorOpen, setIsLabelSelectorOpen] = useState(false);
|
|
307
|
-
|
|
308
|
-
const labelsButton = useRef<HTMLDivElement>(null);
|
|
309
|
-
const labelSelectorRef = useRef<HTMLDivElement>(null);
|
|
310
|
-
|
|
311
|
-
useEffect(() => {
|
|
312
|
-
const handleClickOutside = (event: MouseEvent): void => {
|
|
313
|
-
if (
|
|
314
|
-
isLabelSelectorOpen &&
|
|
315
|
-
labelSelectorRef.current !== null &&
|
|
316
|
-
!labelSelectorRef.current.contains(event.target as Node) &&
|
|
317
|
-
labelsButton.current !== null &&
|
|
318
|
-
!labelsButton.current.contains(event.target as Node)
|
|
319
|
-
) {
|
|
320
|
-
setIsLabelSelectorOpen(false);
|
|
321
|
-
}
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
325
|
-
return () => {
|
|
326
|
-
document.removeEventListener('mousedown', handleClickOutside);
|
|
327
|
-
};
|
|
328
|
-
}, [isLabelSelectorOpen]);
|
|
329
|
-
|
|
24
|
+
const GroupByControls: React.FC<GroupByControlsProps> = ({groupBy, labels, setGroupByLabels}) => {
|
|
330
25
|
return (
|
|
331
26
|
<div className="inline-flex items-start">
|
|
332
|
-
<div className="relative flex items-start">
|
|
333
|
-
<
|
|
27
|
+
<div className="relative flex gap-3 items-start">
|
|
28
|
+
<GroupByLabelsDropdown
|
|
29
|
+
labels={labels}
|
|
334
30
|
groupBy={groupBy}
|
|
335
|
-
|
|
336
|
-
onLabelClick={() => setIsLabelSelectorOpen(!isLabelSelectorOpen)}
|
|
337
|
-
labelsButtonRef={labelsButton}
|
|
31
|
+
setGroupByLabels={setGroupByLabels}
|
|
338
32
|
/>
|
|
339
|
-
<div ref={labelSelectorRef}>
|
|
340
|
-
<LabelSelector
|
|
341
|
-
labels={labels}
|
|
342
|
-
groupBy={groupBy}
|
|
343
|
-
setGroupByLabels={setGroupByLabels}
|
|
344
|
-
isOpen={isLabelSelectorOpen}
|
|
345
|
-
labelsButtonRef={labelsButton}
|
|
346
|
-
setIsLabelSelectorOpen={setIsLabelSelectorOpen}
|
|
347
|
-
/>
|
|
348
|
-
</div>
|
|
349
33
|
</div>
|
|
350
34
|
</div>
|
|
351
35
|
);
|
|
@@ -0,0 +1,92 @@
|
|
|
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 Select from 'react-select';
|
|
15
|
+
|
|
16
|
+
import {FIELD_LABELS} from '../../../ProfileIcicleGraph/IcicleGraphArrow';
|
|
17
|
+
|
|
18
|
+
interface LabelOption {
|
|
19
|
+
label: string;
|
|
20
|
+
value: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
labels: string[];
|
|
25
|
+
groupBy: string[];
|
|
26
|
+
setGroupByLabels: (labels: string[]) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const GroupByLabelsDropdown = ({labels, groupBy, setGroupByLabels}: Props): JSX.Element => {
|
|
30
|
+
return (
|
|
31
|
+
<div>
|
|
32
|
+
<div className="flex items-center justify-between">
|
|
33
|
+
<label className="text-sm">Group by</label>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<Select<LabelOption, true>
|
|
37
|
+
id="h-group-by-labels-selector"
|
|
38
|
+
isMulti
|
|
39
|
+
defaultMenuIsOpen={false}
|
|
40
|
+
defaultValue={undefined}
|
|
41
|
+
name="labels"
|
|
42
|
+
options={labels.map(label => ({label, value: `${FIELD_LABELS}.${label}`}))}
|
|
43
|
+
className="parca-select-container text-sm w-full rounded-md bg-white"
|
|
44
|
+
classNamePrefix="parca-select"
|
|
45
|
+
value={groupBy
|
|
46
|
+
.filter(l => l.startsWith(FIELD_LABELS))
|
|
47
|
+
.map(l => ({value: l, label: l.slice(FIELD_LABELS.length + 1)}))}
|
|
48
|
+
onChange={newValue => {
|
|
49
|
+
setGroupByLabels(newValue.map(option => option.value));
|
|
50
|
+
}}
|
|
51
|
+
placeholder="Select labels..."
|
|
52
|
+
styles={{
|
|
53
|
+
menu: provided => ({
|
|
54
|
+
...provided,
|
|
55
|
+
marginBottom: 0,
|
|
56
|
+
boxShadow: 'none',
|
|
57
|
+
marginTop: 0,
|
|
58
|
+
zIndex: 1000,
|
|
59
|
+
minWidth: '320px',
|
|
60
|
+
}),
|
|
61
|
+
control: provided => ({
|
|
62
|
+
...provided,
|
|
63
|
+
position: 'relative',
|
|
64
|
+
boxShadow: 'none',
|
|
65
|
+
borderBottom: '1px solid #e2e8f0',
|
|
66
|
+
borderRight: '1px solid #e2e8f0',
|
|
67
|
+
borderLeft: '1px solid #e2e8f0',
|
|
68
|
+
borderTop: '1px solid #e2e8f0',
|
|
69
|
+
':hover': {
|
|
70
|
+
borderColor: '#e2e8f0',
|
|
71
|
+
borderBottomLeftRadius: 0,
|
|
72
|
+
borderBottomRightRadius: 0,
|
|
73
|
+
},
|
|
74
|
+
}),
|
|
75
|
+
option: provided => ({
|
|
76
|
+
...provided,
|
|
77
|
+
':hover': {
|
|
78
|
+
backgroundColor: '#4f46e5',
|
|
79
|
+
color: '#ffffff',
|
|
80
|
+
},
|
|
81
|
+
':focus': {
|
|
82
|
+
backgroundColor: '#4f46e5',
|
|
83
|
+
color: '#ffffff',
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default GroupByLabelsDropdown;
|
|
@@ -0,0 +1,34 @@
|
|
|
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 {Icon} from '@iconify/react';
|
|
15
|
+
|
|
16
|
+
import {Button, useURLState} from '@parca/components';
|
|
17
|
+
|
|
18
|
+
const InvertCallStack = (): JSX.Element => {
|
|
19
|
+
const [invertStack = '', setInvertStack] = useURLState('invert_call_stack');
|
|
20
|
+
const isInvert = invertStack === 'true';
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Button
|
|
24
|
+
variant="neutral"
|
|
25
|
+
className="flex items-center gap-2"
|
|
26
|
+
onClick={() => setInvertStack(isInvert ? '' : 'true')}
|
|
27
|
+
>
|
|
28
|
+
<Icon icon={isInvert ? 'ph:sort-ascending' : 'ph:sort-descending'} className="h-4 w-4" />
|
|
29
|
+
{isInvert ? 'Original' : 'Invert'} Call Stack
|
|
30
|
+
</Button>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default InvertCallStack;
|
|
@@ -180,18 +180,22 @@ const ShareButton = ({
|
|
|
180
180
|
<Dropdown
|
|
181
181
|
dropdownWidth="w-48"
|
|
182
182
|
element={
|
|
183
|
-
<Button
|
|
183
|
+
<Button
|
|
184
|
+
variant="neutral"
|
|
185
|
+
className="flex items-center gap-2"
|
|
186
|
+
id="h-share-dropdown-button"
|
|
187
|
+
>
|
|
188
|
+
<Icon icon="material-symbols:share" className="h-4 w-4" />
|
|
184
189
|
Share
|
|
185
|
-
<Icon icon="material-symbols:share" className="h-5 w-5 ml-2" />
|
|
186
190
|
</Button>
|
|
187
191
|
}
|
|
188
192
|
>
|
|
189
193
|
<span className="text-xs text-gray-400 capitalize px-2">actions</span>
|
|
190
194
|
{actions.map(item => (
|
|
191
195
|
<Dropdown.Item key={item.key} onSelect={item.onSelect}>
|
|
192
|
-
<div id={item.id} className="flex items-center">
|
|
196
|
+
<div id={item.id} className="flex items-center gap-2">
|
|
197
|
+
<Icon icon={item.icon} className="h-4 w-4" />
|
|
193
198
|
<span>{item.label}</span>
|
|
194
|
-
<Icon icon={item.icon} className="ml-2 h-4 w-4" />
|
|
195
199
|
</div>
|
|
196
200
|
</Dropdown.Item>
|
|
197
201
|
))}
|