// 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;