|
|
|
@ -1,24 +1,44 @@ |
|
|
|
|
import { css } from '@emotion/css'; |
|
|
|
|
import { get as lodashGet } from 'lodash'; |
|
|
|
|
import React, { useMemo } from 'react'; |
|
|
|
|
import React, { useMemo, useState } from 'react'; |
|
|
|
|
import { useObservable } from 'react-use'; |
|
|
|
|
|
|
|
|
|
import { DataFrame, PanelOptionsEditorBuilder, StandardEditorContext } from '@grafana/data'; |
|
|
|
|
import { |
|
|
|
|
DataFrame, |
|
|
|
|
GrafanaTheme2, |
|
|
|
|
PanelOptionsEditorBuilder, |
|
|
|
|
SelectableValue, |
|
|
|
|
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 { notFoundItem } from 'app/features/canvas/elements/notFound'; |
|
|
|
|
import { ElementState } from 'app/features/canvas/runtime/element'; |
|
|
|
|
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 { CanvasElementOptions, canvasElementRegistry } from '../../../features/canvas'; |
|
|
|
|
|
|
|
|
|
import { activePanelSubject, InstanceState } from './CanvasPanel'; |
|
|
|
|
import { TabsEditor } from './editor/TabsEditor'; |
|
|
|
|
import { getElementEditor } from './editor/elementEditor'; |
|
|
|
|
import { getLayerEditor } from './editor/layerEditor'; |
|
|
|
|
import { addStandardCanvasEditorOptions } from './module'; |
|
|
|
|
import { InlineEditTabs } from './types'; |
|
|
|
|
import { getElementTypes } from './utils'; |
|
|
|
|
|
|
|
|
|
export function InlineEditBody() { |
|
|
|
|
const activePanel = useObservable(activePanelSubject); |
|
|
|
|
const instanceState = activePanel?.panel.context?.instanceState; |
|
|
|
|
const styles = useStyles2(getStyles); |
|
|
|
|
|
|
|
|
|
const [activeTab, setActiveTab] = useState<string>(InlineEditTabs.SelectedElement); |
|
|
|
|
|
|
|
|
|
const pane = useMemo(() => { |
|
|
|
|
const p = activePanel?.panel; |
|
|
|
|
const state: InstanceState = instanceState; |
|
|
|
@ -27,10 +47,12 @@ export function InlineEditBody() { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const supplier = (builder: PanelOptionsEditorBuilder<any>, context: StandardEditorContext<any>) => { |
|
|
|
|
builder.addNestedOptions(getLayerEditor(instanceState)); |
|
|
|
|
if (activeTab === InlineEditTabs.ElementManagement) { |
|
|
|
|
builder.addNestedOptions(getLayerEditor(instanceState)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const selection = state.selected; |
|
|
|
|
if (selection?.length === 1) { |
|
|
|
|
if (selection?.length === 1 && activeTab === InlineEditTabs.SelectedElement) { |
|
|
|
|
const element = selection[0]; |
|
|
|
|
if (element && !(element instanceof FrameState)) { |
|
|
|
|
builder.addNestedOptions( |
|
|
|
@ -54,17 +76,51 @@ export function InlineEditBody() { |
|
|
|
|
}, |
|
|
|
|
supplier |
|
|
|
|
); |
|
|
|
|
}, [instanceState, activePanel]); |
|
|
|
|
}, [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 onAddItem = (sel: SelectableValue<string>) => { |
|
|
|
|
const newItem = canvasElementRegistry.getIfExists(sel.value) ?? notFoundItem; |
|
|
|
|
const newElementOptions = newItem.getNewOptions() as CanvasElementOptions; |
|
|
|
|
newElementOptions.type = newItem.id; |
|
|
|
|
if (newItem.defaultSize) { |
|
|
|
|
newElementOptions.placement = { ...newElementOptions.placement, ...newItem.defaultSize }; |
|
|
|
|
} |
|
|
|
|
if (rootLayer) { |
|
|
|
|
const newElement = new ElementState(newItem, newElementOptions, rootLayer); |
|
|
|
|
newElement.updateData(rootLayer.scene.context); |
|
|
|
|
rootLayer.elements.push(newElement); |
|
|
|
|
rootLayer.scene.save(); |
|
|
|
|
|
|
|
|
|
rootLayer.reinitializeMoveable(); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const noElementSelected = |
|
|
|
|
instanceState && activeTab === InlineEditTabs.SelectedElement && instanceState.selected.length === 0; |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
{pane.categories.map((p) => renderOptionsPaneCategoryDescriptor(p))} |
|
|
|
|
<div style={topLevelItemsContainerStyle}>{pane.items.map((item) => item.render())}</div> |
|
|
|
|
<div style={topLevelItemsContainerStyle}> |
|
|
|
|
<AddLayerButton onChange={onAddItem} options={typeOptions} label={'Add item'} /> |
|
|
|
|
</div> |
|
|
|
|
<div style={topLevelItemsContainerStyle}> |
|
|
|
|
<TabsEditor onTabChange={onTabChange} /> |
|
|
|
|
{pane.categories.map((p) => renderOptionsPaneCategoryDescriptor(p))} |
|
|
|
|
{noElementSelected && <div className={styles.selectElement}>Please select an element</div>} |
|
|
|
|
</div> |
|
|
|
|
</> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
@ -119,3 +175,10 @@ function getOptionsPaneCategoryDescriptor<T = any>( |
|
|
|
|
fillOptionsPaneItems(supplier, access, getOptionsPaneCategory, context); |
|
|
|
|
return root; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const getStyles = (theme: GrafanaTheme2) => ({ |
|
|
|
|
selectElement: css` |
|
|
|
|
color: ${theme.colors.text.secondary}; |
|
|
|
|
padding: ${theme.spacing(2)}; |
|
|
|
|
`,
|
|
|
|
|
}); |
|
|
|
|