// Copyright 2022 The Parca Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {ReactNode} from 'react';
import {useQueryState} from 'nuqs';
import {useParcaContext} from '@parca/components';
import {ProfileSource} from '../../../ProfileSource';
import {stringParam} from '../../../hooks/urlParsers';
import {useDashboardItems} from '../../../hooks/useDashboardItems';
import Dropdown, {DropdownElement, InnerAction} from './Dropdown';
interface Props {
profileSource?: ProfileSource;
}
const ViewSelector = ({profileSource}: Props): JSX.Element => {
const {dashboardItems, setDashboardItems} = useDashboardItems();
const [, setSandwichFunctionName] = useQueryState(
'sandwich_function_name',
stringParam.withOptions({history: 'replace'})
);
const {enableSourcesView, enableSandwichView} = useParcaContext();
const allItems: Array<{
key: string;
label?: string | ReactNode;
canBeSelected: boolean;
supportingText?: string;
disabledText?: string;
}> = [
{
key: 'flamegraph',
label: 'Flame Graph',
canBeSelected: !dashboardItems.includes('flamegraph'),
},
{key: 'table', label: 'Table', canBeSelected: !dashboardItems.includes('table')},
{
key: 'flamechart',
label: (
Flame Chart
alpha
),
canBeSelected:
!dashboardItems.includes('flamechart') && profileSource?.ProfileType().delta === true,
disabledText:
!dashboardItems.includes('flamechart') && profileSource?.ProfileType().delta !== true
? 'Flamechart is not available for non-delta profiles'
: undefined,
},
];
if (enableSandwichView === true) {
allItems.push({
key: 'sandwich',
label: (
Sandwich
alpha
),
canBeSelected: !dashboardItems.includes('sandwich'),
});
}
if (enableSourcesView === true) {
allItems.push({key: 'source', label: 'Source', canBeSelected: false});
}
const getOption = ({
label,
supportingText,
}: {
key: string;
label?: string | ReactNode;
supportingText?: string;
}): DropdownElement => {
const title = (
{typeof label === 'string' ? label.replaceAll('-', ' ') : label}
);
return {
active: title,
expanded: (
<>
{title}
{supportingText !== null && {supportingText}}
>
),
};
};
const getInnerActionForItem = (item: {
key: string;
canBeSelected: boolean;
}): InnerAction | undefined => {
if (dashboardItems.length === 1 && item.key === dashboardItems[0]) return undefined;
// If we already have 2 panels and this item isn't selected, don't show any action
if (dashboardItems.length >= 2 && !dashboardItems.includes(item.key)) return undefined;
return {
text:
!item.canBeSelected && item.key === 'source'
? 'Add Panel'
: item.canBeSelected
? 'Add Panel'
: dashboardItems.includes(item.key)
? 'Close Panel'
: 'Add Panel',
onClick: () => {
if (item.canBeSelected) {
setDashboardItems([...dashboardItems, item.key]);
} else {
const newDashboardItems = dashboardItems.filter(v => v !== item.key);
setDashboardItems(newDashboardItems);
if (item.key === 'sandwich') {
void setSandwichFunctionName(null);
}
}
},
isDisabled: dashboardItems.length === 1 && dashboardItems.includes('sandwich'),
};
};
const items = allItems.map(item => ({
key: item.key,
disabled: !item.canBeSelected,
disabledText: item.disabledText,
element: getOption(item),
innerAction: getInnerActionForItem(item),
}));
const onSelection = (value: string): void => {
const isOnlyChart = dashboardItems.length === 1;
if (isOnlyChart && value === 'sandwich') {
setDashboardItems([...dashboardItems, value]);
return;
}
if (isOnlyChart) {
setDashboardItems([value]);
return;
}
const newDashboardItems = [dashboardItems[0], value];
setDashboardItems(newDashboardItems);
};
return (
= 2 ? 'Multiple' : dashboardItems[0]}
onSelection={onSelection}
placeholder={'Select view type...'}
id="h-view-selector"
optionsClassName="min-w-[260px]"
/>
);
};
export default ViewSelector;