import { css } from '@emotion/css'; import { useMemo, useState } from 'react'; import * as React from 'react'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { t } from '@grafana/i18n'; import { FilterInput, RadioButtonGroup, ScrollContainer, useStyles2 } from '@grafana/ui'; import { isPanelModelLibraryPanel } from '../../../library-panels/guard'; import { OptionsPaneCategory } from './OptionsPaneCategory'; import { OptionsPaneCategoryDescriptor } from './OptionsPaneCategoryDescriptor'; import { getFieldOverrideCategories } from './getFieldOverrideElements'; import { getLibraryPanelOptionsCategory } from './getLibraryPanelOptions'; import { getPanelFrameCategory } from './getPanelFrameOptions'; import { getVisualizationOptions } from './getVisualizationOptions'; import { OptionSearchEngine } from './state/OptionSearchEngine'; import { getRecentOptions } from './state/getRecentOptions'; import { OptionPaneRenderProps } from './types'; export const OptionsPaneOptions = (props: OptionPaneRenderProps) => { const { plugin, panel } = props; const [searchQuery, setSearchQuery] = useState(''); const [listMode, setListMode] = useState(OptionFilter.All); const styles = useStyles2(getStyles); const [panelFrameOptions, vizOptions, libraryPanelOptions] = useMemo( () => [getPanelFrameCategory(props), getVisualizationOptions(props), getLibraryPanelOptionsCategory(props)], // eslint-disable-next-line react-hooks/exhaustive-deps [panel.configRev, props.data, props.instanceState, searchQuery] ); const justOverrides = useMemo( () => getFieldOverrideCategories( props.panel.fieldConfig, props.plugin.fieldConfigRegistry, props.data?.series ?? [], searchQuery, props.onFieldConfigsChange ), // eslint-disable-next-line react-hooks/exhaustive-deps [panel.configRev, props.data, props.instanceState, searchQuery] ); const mainBoxElements: React.ReactNode[] = []; const isSearching = searchQuery.length > 0; const optionRadioFilters = useMemo(getOptionRadioFilters, []); const allOptions = isPanelModelLibraryPanel(panel) ? [libraryPanelOptions, panelFrameOptions, ...vizOptions] : [panelFrameOptions, ...vizOptions]; if (isSearching) { mainBoxElements.push(renderSearchHits(allOptions, justOverrides, searchQuery)); } else { switch (listMode) { case OptionFilter.All: if (isPanelModelLibraryPanel(panel)) { // Library Panel options first mainBoxElements.push(libraryPanelOptions.render()); } // Panel frame options second mainBoxElements.push(panelFrameOptions.render()); // Then add all panel and field defaults for (const item of vizOptions) { mainBoxElements.push(item.render()); } for (const item of justOverrides) { mainBoxElements.push(item.render()); } break; case OptionFilter.Overrides: for (const override of justOverrides) { mainBoxElements.push(override.render()); } break; case OptionFilter.Recent: mainBoxElements.push( {getRecentOptions(allOptions).map((item) => item.render())} ); break; } } // only show radio buttons if we are searching or if the plugin has field config const showSearchRadioButtons = !isSearching && !plugin.fieldConfigRegistry.isEmpty(); return (
{showSearchRadioButtons && (
)}
{mainBoxElements}
); }; function getOptionRadioFilters(): Array> { return [ { label: OptionFilter.All, value: OptionFilter.All }, { label: OptionFilter.Overrides, value: OptionFilter.Overrides }, ]; } export enum OptionFilter { All = 'All', Overrides = 'Overrides', Recent = 'Recent', } export function renderSearchHits( allOptions: OptionsPaneCategoryDescriptor[], overrides: OptionsPaneCategoryDescriptor[], searchQuery: string ) { const engine = new OptionSearchEngine(allOptions, overrides); const { optionHits, totalCount, overrideHits } = engine.search(searchQuery); return (
{optionHits.map((hit) => hit.render(searchQuery))} {overrideHits.map((override) => override.render(searchQuery))}
); } const getStyles = (theme: GrafanaTheme2) => ({ wrapper: css({ height: '100%', display: 'flex', flexDirection: 'column', flex: '1 1 0', '.search-fragment-highlight': { color: theme.colors.warning.text, background: 'transparent', }, }), searchBox: css({ display: 'flex', flexDirection: 'column', minHeight: 0, }), formRow: css({ marginBottom: theme.spacing(1), }), formBox: css({ padding: theme.spacing(1), background: theme.colors.background.primary, border: `1px solid ${theme.components.panel.borderColor}`, borderTopLeftRadius: theme.shape.borderRadius(1.5), borderBottom: 'none', }), closeButton: css({ marginLeft: theme.spacing(1), }), searchHits: css({ padding: theme.spacing(1, 1, 0, 1), }), searchNotice: css({ fontSize: theme.typography.size.sm, color: theme.colors.text.secondary, padding: theme.spacing(1), textAlign: 'center', }), mainBox: css({ background: theme.colors.background.primary, border: `1px solid ${theme.components.panel.borderColor}`, borderTop: 'none', flexGrow: 1, }), angularDeprecationWrapper: css({ padding: theme.spacing(1), }), });