import { css } from '@emotion/css'; import { get as lodashGet } from 'lodash'; import React, { useMemo, useState } from 'react'; import { useObservable } from 'react-use'; import { DataFrame, GrafanaTheme2, PanelOptionsEditorBuilder, StandardEditorContext } from '@grafana/data'; import { PanelOptionsSupplier } from '@grafana/data/src/panel/PanelPlugin'; import { NestedValueAccess } from '@grafana/data/src/utils/OptionsUIBuilders'; import { useStyles2 } from '@grafana/ui/src'; import { AddLayerButton } from 'app/core/components/Layers/AddLayerButton'; import { FrameState } from 'app/features/canvas/runtime/frame'; import { OptionsPaneCategory } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategory'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; import { fillOptionsPaneItems } from 'app/features/dashboard/components/PanelEditor/getVisualizationOptions'; import { setOptionImmutably } from 'app/features/dashboard/components/PanelEditor/utils'; import { activePanelSubject, InstanceState } from '../../CanvasPanel'; import { addStandardCanvasEditorOptions } from '../../module'; import { InlineEditTabs } from '../../types'; import { getElementTypes, onAddItem } from '../../utils'; import { getConnectionEditor } from '../connectionEditor'; import { getElementEditor } from '../element/elementEditor'; import { getLayerEditor } from '../layer/layerEditor'; import { TabsEditor } from './TabsEditor'; export function InlineEditBody() { const activePanel = useObservable(activePanelSubject); const instanceState = activePanel?.panel.context?.instanceState; const styles = useStyles2(getStyles); const [activeTab, setActiveTab] = useState(InlineEditTabs.SelectedElement); const pane = useMemo(() => { const p = activePanel?.panel; const state: InstanceState = instanceState; if (!(state && state.scene) || !p) { return new OptionsPaneCategoryDescriptor({ id: 'root', title: 'root' }); } const supplier = (builder: PanelOptionsEditorBuilder) => { if (activeTab === InlineEditTabs.ElementManagement) { builder.addNestedOptions(getLayerEditor(instanceState)); } const selectedConnection = state.selectedConnection; if (selectedConnection && activeTab === InlineEditTabs.SelectedElement) { builder.addNestedOptions( getConnectionEditor({ category: [`Selected connection`], connection: selectedConnection, scene: state.scene, }) ); } const selection = state.selected; if (selection?.length === 1 && activeTab === InlineEditTabs.SelectedElement) { const element = selection[0]; if (element && !(element instanceof FrameState)) { builder.addNestedOptions( getElementEditor({ category: [`Selected element (${element.options.name})`], element, scene: state.scene, }) ); } } addStandardCanvasEditorOptions(builder); }; return getOptionsPaneCategoryDescriptor( { options: p.props.options, onChange: p.props.onOptionsChange, data: p.props.data?.series, }, supplier ); }, [instanceState, activePanel, activeTab]); const topLevelItemsContainerStyle = { marginLeft: 15, marginTop: 10, }; const onTabChange = (tab: string) => { setActiveTab(tab); }; const typeOptions = getElementTypes(instanceState?.scene.shouldShowAdvancedTypes).options; const rootLayer: FrameState | undefined = instanceState?.layer; const noElementSelected = instanceState && activeTab === InlineEditTabs.SelectedElement && instanceState.selected.length === 0 && instanceState.selectedConnection === undefined; return ( <>
{pane.items.map((item) => item.render())}
onAddItem(sel, rootLayer)} options={typeOptions} label={'Add item'} />
{pane.categories.map((p) => renderOptionsPaneCategoryDescriptor(p))} {noElementSelected &&
Please select an element
}
); } // Recursively render options function renderOptionsPaneCategoryDescriptor(pane: OptionsPaneCategoryDescriptor) { return (
{pane.items.map((v) => v.render())}
{pane.categories.map((c) => renderOptionsPaneCategoryDescriptor(c))}
); } interface EditorProps { onChange: (v: T) => void; options: T; data?: DataFrame[]; } function getOptionsPaneCategoryDescriptor( props: EditorProps, supplier: PanelOptionsSupplier ): OptionsPaneCategoryDescriptor { const context: StandardEditorContext = { data: props.data ?? [], options: props.options, }; const root = new OptionsPaneCategoryDescriptor({ id: 'root', title: 'root' }); const getOptionsPaneCategory = (categoryNames?: string[]): OptionsPaneCategoryDescriptor => { if (categoryNames?.length) { const key = categoryNames[0]; let sub = root.categories.find((v) => v.props.id === key); if (!sub) { sub = new OptionsPaneCategoryDescriptor({ id: key, title: key }); root.categories.push(sub); } return sub; } return root; }; const access: NestedValueAccess = { getValue: (path) => lodashGet(props.options, path), onChange: (path, value) => { props.onChange(setOptionImmutably(props.options as any, path, value)); }, }; // Use the panel options loader fillOptionsPaneItems(supplier, access, getOptionsPaneCategory, context); return root; } const getStyles = (theme: GrafanaTheme2) => ({ selectElement: css({ color: theme.colors.text.secondary, padding: theme.spacing(2), }), });