diff --git a/.betterer.results b/.betterer.results index 7d474a677d1..446992a0562 100644 --- a/.betterer.results +++ b/.betterer.results @@ -7316,10 +7316,11 @@ exports[`better eslint`] = { "public/app/plugins/panel/canvas/InlineEditBody.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"], + [0, 0, 0, "Do not use any type assertions.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"], - [0, 0, 0, "Unexpected any. Specify a different type.", "5"] + [0, 0, 0, "Unexpected any. Specify a different type.", "4"], + [0, 0, 0, "Do not use any type assertions.", "5"], + [0, 0, 0, "Unexpected any. Specify a different type.", "6"] ], "public/app/plugins/panel/canvas/editor/APIEditor.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], diff --git a/public/app/plugins/panel/canvas/InlineEditBody.tsx b/public/app/plugins/panel/canvas/InlineEditBody.tsx index c6d1c10f876..5f49051f5b4 100644 --- a/public/app/plugins/panel/canvas/InlineEditBody.tsx +++ b/public/app/plugins/panel/canvas/InlineEditBody.tsx @@ -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(InlineEditTabs.SelectedElement); + const pane = useMemo(() => { const p = activePanel?.panel; const state: InstanceState = instanceState; @@ -27,10 +47,12 @@ export function InlineEditBody() { } const supplier = (builder: PanelOptionsEditorBuilder, context: StandardEditorContext) => { - 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) => { + 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))}
{pane.items.map((item) => item.render())}
+
+ +
+
+ + {pane.categories.map((p) => renderOptionsPaneCategoryDescriptor(p))} + {noElementSelected &&
Please select an element
} +
); } @@ -119,3 +175,10 @@ function getOptionsPaneCategoryDescriptor( fillOptionsPaneItems(supplier, access, getOptionsPaneCategory, context); return root; } + +const getStyles = (theme: GrafanaTheme2) => ({ + selectElement: css` + color: ${theme.colors.text.secondary}; + padding: ${theme.spacing(2)}; + `, +}); diff --git a/public/app/plugins/panel/canvas/editor/TabsEditor.tsx b/public/app/plugins/panel/canvas/editor/TabsEditor.tsx new file mode 100644 index 00000000000..d142f1f21a6 --- /dev/null +++ b/public/app/plugins/panel/canvas/editor/TabsEditor.tsx @@ -0,0 +1,38 @@ +import React, { useState } from 'react'; + +import { Tab, TabsBar } from '@grafana/ui/src'; + +import { InlineEditTabs } from '../types'; + +type Props = { + onTabChange: (v: string) => void; +}; + +export const TabsEditor = ({ onTabChange }: Props) => { + const [activeTab, setActiveTab] = useState(InlineEditTabs.SelectedElement); + + const tabs = [ + { label: 'Selected Element', value: InlineEditTabs.SelectedElement }, + { label: 'Element Management', value: InlineEditTabs.ElementManagement }, + ]; + + const onCurrentTabChange = (value: string) => { + onTabChange(value); + setActiveTab(value); + }; + + return ( + <> + + {tabs.map((t, index) => ( + onCurrentTabChange(t.value!)} + /> + ))} + + + ); +}; diff --git a/public/app/plugins/panel/canvas/types.ts b/public/app/plugins/panel/canvas/types.ts index 25898071058..5dfbd4fe6bb 100644 --- a/public/app/plugins/panel/canvas/types.ts +++ b/public/app/plugins/panel/canvas/types.ts @@ -15,3 +15,8 @@ export interface DragNode { export interface DropNode extends DragNode { pos: string; } + +export enum InlineEditTabs { + ElementManagement = 'element-management', + SelectedElement = 'selected-element', +}