Skip to content

Commit 7a341cd

Browse files
committed
Merge remote-tracking branch 'origin/flamecharts-zoom-feature' into flamecharts-zoom-feature
2 parents a724346 + 8da3391 commit 7a341cd

File tree

4 files changed

+87
-61
lines changed

4 files changed

+87
-61
lines changed

ui/packages/shared/profile/src/ProfileFlameChart/index.tsx

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -278,19 +278,24 @@ export const ProfileFlameChart = ({
278278
)}
279279

280280
{/* Selected timeframe description + zoom controls */}
281-
{selectedTimeframe != null && (() => {
282-
const labels = selectedTimeframe.labels.labels.map(l => `${l.name} = ${l.value}`).join(', ');
283-
const durationMs = selectedTimeframe.bounds[1] - selectedTimeframe.bounds[0];
284-
const duration = formatDuration({[TimeUnits.Milliseconds]: durationMs});
285-
return (
286-
<div className="flex items-center justify-between px-2 py-1">
287-
<div className="text-xs font-medium text-gray-500 dark:text-gray-400">
288-
Samples matching {labels} over {duration} from {formatDateTimeDownToMS(selectedTimeframe.bounds[0])} to {formatDateTimeDownToMS(selectedTimeframe.bounds[1])}
281+
{selectedTimeframe != null &&
282+
(() => {
283+
const labels = selectedTimeframe.labels.labels
284+
.map(l => `${l.name} = ${l.value}`)
285+
.join(', ');
286+
const durationMs = selectedTimeframe.bounds[1] - selectedTimeframe.bounds[0];
287+
const duration = formatDuration({[TimeUnits.Milliseconds]: durationMs});
288+
return (
289+
<div className="flex items-center justify-between px-2 py-1">
290+
<div className="text-xs font-medium text-gray-500 dark:text-gray-400">
291+
Samples matching {labels} over {duration} from{' '}
292+
{formatDateTimeDownToMS(selectedTimeframe.bounds[0])} to{' '}
293+
{formatDateTimeDownToMS(selectedTimeframe.bounds[1])}
294+
</div>
295+
<div ref={zoomControlsRef} />
289296
</div>
290-
<div ref={zoomControlsRef} />
291-
</div>
292-
);
293-
})()}
297+
);
298+
})()}
294299

295300
{/* Flamegraph visualization - only shown when a time range is selected in the strips */}
296301
{selectedTimeframe != null && filteredProfileSource != null ? (

ui/packages/shared/profile/src/ProfileFlameGraph/FlameGraphArrow/MiniMap.tsx

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
import React, { useCallback, useEffect, useRef } from 'react';
14+
import React, {useCallback, useEffect, useRef} from 'react';
1515

16-
import { Table } from '@uwdata/flechette';
16+
import {Table} from '@uwdata/flechette';
1717

18-
import { EVERYTHING_ELSE } from '@parca/store';
19-
import { getLastItem } from '@parca/utilities';
18+
import {EVERYTHING_ELSE} from '@parca/store';
19+
import {getLastItem} from '@parca/utilities';
2020

21-
import { ProfileSource } from '../../ProfileSource';
22-
import { RowHeight, type colorByColors } from './FlameGraphNodes';
21+
import {ProfileSource} from '../../ProfileSource';
22+
import {RowHeight, type colorByColors} from './FlameGraphNodes';
2323
import {
2424
FIELD_CUMULATIVE,
2525
FIELD_DEPTH,
2626
FIELD_FUNCTION_FILE_NAME,
2727
FIELD_MAPPING_FILE,
2828
FIELD_TIMESTAMP,
2929
} from './index';
30-
import { arrowToString, boundsFromProfileSource } from './utils';
30+
import {arrowToString, boundsFromProfileSource} from './utils';
3131

3232
const MINIMAP_HEIGHT = 20;
3333

@@ -122,14 +122,24 @@ export const MiniMap = React.memo(function MiniMap({
122122
colorBy === 'filename'
123123
? arrowToString(filenameCol?.get(row))
124124
: colorBy === 'binary'
125-
? arrowToString(mappingCol?.get(row))
126-
: null;
125+
? arrowToString(mappingCol?.get(row))
126+
: null;
127127

128128
const color = colors[getLastItem(colorAttribute ?? '') ?? EVERYTHING_ELSE];
129129
ctx.fillStyle = color ?? (isDarkMode ? '#6b7280' : '#9ca3af');
130130
ctx.fillRect(x, y, Math.max(0.5, nodeWidth), h);
131131
}
132-
}, [table, width, zoomedWidth, totalHeight, maxDepth, colorBy, colors, isDarkMode, profileSource]);
132+
}, [
133+
table,
134+
width,
135+
zoomedWidth,
136+
totalHeight,
137+
maxDepth,
138+
colorBy,
139+
colors,
140+
isDarkMode,
141+
profileSource,
142+
]);
133143

134144
const isZoomed = zoomedWidth > width;
135145
const sliderWidth = Math.max(20, (width / zoomedWidth) * width);
@@ -212,7 +222,7 @@ export const MiniMap = React.memo(function MiniMap({
212222
);
213223
};
214224

215-
el.addEventListener('wheel', handleWheel, { passive: false });
225+
el.addEventListener('wheel', handleWheel, {passive: false});
216226
return () => {
217227
el.removeEventListener('wheel', handleWheel);
218228
};
@@ -224,29 +234,34 @@ export const MiniMap = React.memo(function MiniMap({
224234
<div
225235
ref={containerElRef}
226236
className="relative select-none"
227-
style={{ width, height: MINIMAP_HEIGHT, cursor: isZoomed ? 'pointer' : 'default' }}
237+
style={{width, height: MINIMAP_HEIGHT, cursor: isZoomed ? 'pointer' : 'default'}}
228238
onMouseDown={isZoomed ? handleMouseDown : undefined}
229239
>
230240
<canvas
231241
ref={canvasRef}
232-
style={{ width, height: MINIMAP_HEIGHT, display: 'block', visibility: isZoomed ? 'visible' : 'hidden' }}
242+
style={{
243+
width,
244+
height: MINIMAP_HEIGHT,
245+
display: 'block',
246+
visibility: isZoomed ? 'visible' : 'hidden',
247+
}}
233248
/>
234249
{isZoomed && (
235250
<>
236251
{/* Left overlay */}
237252
<div
238253
className="absolute top-0 bottom-0 bg-black/30 dark:bg-black/50"
239-
style={{ left: 0, width: Math.max(0, sliderLeft) }}
254+
style={{left: 0, width: Math.max(0, sliderLeft)}}
240255
/>
241256
{/* Viewport slider */}
242257
<div
243258
className="absolute top-0 bottom-0 border-x-2 border-gray-500"
244-
style={{ left: sliderLeft, width: sliderWidth }}
259+
style={{left: sliderLeft, width: sliderWidth}}
245260
/>
246261
{/* Right overlay */}
247262
<div
248263
className="absolute top-0 bottom-0 bg-black/30 dark:bg-black/50"
249-
style={{ left: sliderLeft + sliderWidth, right: 0 }}
264+
style={{left: sliderLeft + sliderWidth, right: 0}}
250265
/>
251266
</>
252267
)}

ui/packages/shared/profile/src/ProfileFlameGraph/FlameGraphArrow/ZoomControls.tsx

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
// limitations under the License.
1313

1414
import React from 'react';
15-
import {createPortal} from 'react-dom';
1615

1716
import {Icon} from '@iconify/react';
17+
import {createPortal} from 'react-dom';
1818

1919
interface ZoomControlsProps {
2020
zoomLevel: number;
@@ -33,30 +33,30 @@ export const ZoomControls = ({
3333
}: ZoomControlsProps): React.JSX.Element => {
3434
const controls = (
3535
<div className="flex items-center gap-1 rounded-md border border-gray-200 bg-white/90 px-1 py-0.5 shadow-sm backdrop-blur-sm dark:border-gray-600 dark:bg-gray-800/90">
36-
<button
37-
onClick={zoomOut}
38-
disabled={zoomLevel <= 1}
39-
className="rounded p-1 text-gray-600 hover:bg-gray-100 disabled:opacity-30 dark:text-gray-300 dark:hover:bg-gray-700"
40-
title="Zoom out"
41-
>
42-
<Icon icon="mdi:minus" width={16} height={16} />
43-
</button>
44-
<button
45-
onClick={resetZoom}
46-
className="min-w-[3rem] px-1 text-center text-xs text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 rounded"
47-
title="Reset zoom"
48-
>
49-
{Math.round(zoomLevel * 100)}%
50-
</button>
51-
<button
52-
onClick={zoomIn}
53-
disabled={zoomLevel >= 20}
54-
className="rounded p-1 text-gray-600 hover:bg-gray-100 disabled:opacity-30 dark:text-gray-300 dark:hover:bg-gray-700"
55-
title="Zoom in"
56-
>
57-
<Icon icon="mdi:plus" width={16} height={16} />
58-
</button>
59-
</div>
36+
<button
37+
onClick={zoomOut}
38+
disabled={zoomLevel <= 1}
39+
className="rounded p-1 text-gray-600 hover:bg-gray-100 disabled:opacity-30 dark:text-gray-300 dark:hover:bg-gray-700"
40+
title="Zoom out"
41+
>
42+
<Icon icon="mdi:minus" width={16} height={16} />
43+
</button>
44+
<button
45+
onClick={resetZoom}
46+
className="min-w-[3rem] px-1 text-center text-xs text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700 rounded"
47+
title="Reset zoom"
48+
>
49+
{Math.round(zoomLevel * 100)}%
50+
</button>
51+
<button
52+
onClick={zoomIn}
53+
disabled={zoomLevel >= 20}
54+
className="rounded p-1 text-gray-600 hover:bg-gray-100 disabled:opacity-30 dark:text-gray-300 dark:hover:bg-gray-700"
55+
title="Zoom in"
56+
>
57+
<Icon icon="mdi:plus" width={16} height={16} />
58+
</button>
59+
</div>
6060
);
6161

6262
if (portalRef?.current != null) {

ui/packages/shared/profile/src/ProfileFlameGraph/FlameGraphArrow/index.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,18 @@ import {type ColorConfig} from '@parca/utilities';
3434
import {ProfileSource} from '../../ProfileSource';
3535
import {useProfileFilters} from '../../ProfileView/components/ProfileFilters/useProfileFilters';
3636
import {useProfileViewContext} from '../../ProfileView/context/ProfileViewContext';
37-
import { TimelineGuide } from '../../TimelineGuide';
37+
import {TimelineGuide} from '../../TimelineGuide';
3838
import {alignedUint8Array} from '../../utils';
3939
import ContextMenuWrapper, {ContextMenuWrapperRef} from './ContextMenuWrapper';
4040
import {FlameNode, RowHeight, colorByColors} from './FlameGraphNodes';
4141
import {MemoizedTooltip} from './MemoizedTooltip';
42-
import { MiniMap } from './MiniMap';
42+
import {MiniMap} from './MiniMap';
4343
import {TooltipProvider} from './TooltipContext';
44-
import { ZoomControls } from './ZoomControls';
44+
import {ZoomControls} from './ZoomControls';
4545
import {useBatchedRendering} from './useBatchedRendering';
4646
import {useScrollViewport} from './useScrollViewport';
4747
import {useVisibleNodes} from './useVisibleNodes';
48-
import { useZoom } from './useZoom';
48+
import {useZoom} from './useZoom';
4949
import {
5050
CurrentPathFrame,
5151
boundsFromProfileSource,
@@ -269,8 +269,10 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({
269269

270270
const isZoomEnabled = isFlameChart;
271271

272-
const { zoomLevel, zoomIn, zoomOut, resetZoom } = useZoom(isZoomEnabled ? containerRef : { current: null });
273-
const zoomedWidth = isZoomEnabled ? Math.round((width ?? 1) * zoomLevel) : (width ?? 0);
272+
const {zoomLevel, zoomIn, zoomOut, resetZoom} = useZoom(
273+
isZoomEnabled ? containerRef : {current: null}
274+
);
275+
const zoomedWidth = isZoomEnabled ? Math.round((width ?? 1) * zoomLevel) : width ?? 0;
274276

275277
// Reset zoom when the data changes (e.g. new query, different time range)
276278
useEffect(() => {
@@ -394,10 +396,14 @@ export const FlameGraphArrow = memo(function FlameGraphArrow({
394396
)}
395397
<div
396398
ref={containerRef}
397-
className={`${isZoomEnabled ? '[scrollbar-width:none] [&::-webkit-scrollbar]:hidden' : ''} will-change-transform webkit-overflow-scrolling-touch contain ${!isZoomEnabled ? 'overflow-auto' : ''}`}
399+
className={`${
400+
isZoomEnabled ? '[scrollbar-width:none] [&::-webkit-scrollbar]:hidden' : ''
401+
} will-change-transform webkit-overflow-scrolling-touch contain ${
402+
!isZoomEnabled ? 'overflow-auto' : ''
403+
}`}
398404
style={{
399405
width: width ?? '100%',
400-
...(isZoomEnabled ? { overflowX: 'scroll' as const, overflowY: 'auto' as const } : {}),
406+
...(isZoomEnabled ? {overflowX: 'scroll' as const, overflowY: 'auto' as const} : {}),
401407
contain: 'layout style paint',
402408
visibility: !showSkeleton ? 'visible' : 'hidden',
403409
}}

0 commit comments

Comments
 (0)