mirror of
https://github.com/grafana/grafana.git
synced 2024-12-29 10:21:41 -06:00
Canvas: Add tabs to inline editor (#57778)
This commit is contained in:
parent
3294918e9f
commit
a5c492b033
@ -7316,10 +7316,11 @@ exports[`better eslint`] = {
|
|||||||
"public/app/plugins/panel/canvas/InlineEditBody.tsx:5381": [
|
"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.", "0"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
[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, "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.", "4"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
|
[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": [
|
"public/app/plugins/panel/canvas/editor/APIEditor.tsx:5381": [
|
||||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||||
|
@ -1,24 +1,44 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
import { get as lodashGet } from 'lodash';
|
import { get as lodashGet } from 'lodash';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { useObservable } from 'react-use';
|
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 { PanelOptionsSupplier } from '@grafana/data/src/panel/PanelPlugin';
|
||||||
import { NestedValueAccess } from '@grafana/data/src/utils/OptionsUIBuilders';
|
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 { FrameState } from 'app/features/canvas/runtime/frame';
|
||||||
import { OptionsPaneCategory } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategory';
|
import { OptionsPaneCategory } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategory';
|
||||||
import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor';
|
import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor';
|
||||||
import { fillOptionsPaneItems } from 'app/features/dashboard/components/PanelEditor/getVisualizationOptions';
|
import { fillOptionsPaneItems } from 'app/features/dashboard/components/PanelEditor/getVisualizationOptions';
|
||||||
import { setOptionImmutably } from 'app/features/dashboard/components/PanelEditor/utils';
|
import { setOptionImmutably } from 'app/features/dashboard/components/PanelEditor/utils';
|
||||||
|
|
||||||
|
import { CanvasElementOptions, canvasElementRegistry } from '../../../features/canvas';
|
||||||
|
|
||||||
import { activePanelSubject, InstanceState } from './CanvasPanel';
|
import { activePanelSubject, InstanceState } from './CanvasPanel';
|
||||||
|
import { TabsEditor } from './editor/TabsEditor';
|
||||||
import { getElementEditor } from './editor/elementEditor';
|
import { getElementEditor } from './editor/elementEditor';
|
||||||
import { getLayerEditor } from './editor/layerEditor';
|
import { getLayerEditor } from './editor/layerEditor';
|
||||||
import { addStandardCanvasEditorOptions } from './module';
|
import { addStandardCanvasEditorOptions } from './module';
|
||||||
|
import { InlineEditTabs } from './types';
|
||||||
|
import { getElementTypes } from './utils';
|
||||||
|
|
||||||
export function InlineEditBody() {
|
export function InlineEditBody() {
|
||||||
const activePanel = useObservable(activePanelSubject);
|
const activePanel = useObservable(activePanelSubject);
|
||||||
const instanceState = activePanel?.panel.context?.instanceState;
|
const instanceState = activePanel?.panel.context?.instanceState;
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
|
const [activeTab, setActiveTab] = useState<string>(InlineEditTabs.SelectedElement);
|
||||||
|
|
||||||
const pane = useMemo(() => {
|
const pane = useMemo(() => {
|
||||||
const p = activePanel?.panel;
|
const p = activePanel?.panel;
|
||||||
const state: InstanceState = instanceState;
|
const state: InstanceState = instanceState;
|
||||||
@ -27,10 +47,12 @@ export function InlineEditBody() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const supplier = (builder: PanelOptionsEditorBuilder<any>, context: StandardEditorContext<any>) => {
|
const supplier = (builder: PanelOptionsEditorBuilder<any>, context: StandardEditorContext<any>) => {
|
||||||
builder.addNestedOptions(getLayerEditor(instanceState));
|
if (activeTab === InlineEditTabs.ElementManagement) {
|
||||||
|
builder.addNestedOptions(getLayerEditor(instanceState));
|
||||||
|
}
|
||||||
|
|
||||||
const selection = state.selected;
|
const selection = state.selected;
|
||||||
if (selection?.length === 1) {
|
if (selection?.length === 1 && activeTab === InlineEditTabs.SelectedElement) {
|
||||||
const element = selection[0];
|
const element = selection[0];
|
||||||
if (element && !(element instanceof FrameState)) {
|
if (element && !(element instanceof FrameState)) {
|
||||||
builder.addNestedOptions(
|
builder.addNestedOptions(
|
||||||
@ -54,17 +76,51 @@ export function InlineEditBody() {
|
|||||||
},
|
},
|
||||||
supplier
|
supplier
|
||||||
);
|
);
|
||||||
}, [instanceState, activePanel]);
|
}, [instanceState, activePanel, activeTab]);
|
||||||
|
|
||||||
const topLevelItemsContainerStyle = {
|
const topLevelItemsContainerStyle = {
|
||||||
marginLeft: 15,
|
marginLeft: 15,
|
||||||
marginTop: 10,
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{pane.categories.map((p) => renderOptionsPaneCategoryDescriptor(p))}
|
|
||||||
<div style={topLevelItemsContainerStyle}>{pane.items.map((item) => item.render())}</div>
|
<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);
|
fillOptionsPaneItems(supplier, access, getOptionsPaneCategory, context);
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
selectElement: css`
|
||||||
|
color: ${theme.colors.text.secondary};
|
||||||
|
padding: ${theme.spacing(2)};
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
38
public/app/plugins/panel/canvas/editor/TabsEditor.tsx
Normal file
38
public/app/plugins/panel/canvas/editor/TabsEditor.tsx
Normal file
@ -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<string>(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 (
|
||||||
|
<>
|
||||||
|
<TabsBar>
|
||||||
|
{tabs.map((t, index) => (
|
||||||
|
<Tab
|
||||||
|
key={`${t.value}-${index}`}
|
||||||
|
label={t.label}
|
||||||
|
active={t.value === activeTab}
|
||||||
|
onChangeTab={() => onCurrentTabChange(t.value!)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</TabsBar>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -15,3 +15,8 @@ export interface DragNode {
|
|||||||
export interface DropNode extends DragNode {
|
export interface DropNode extends DragNode {
|
||||||
pos: string;
|
pos: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum InlineEditTabs {
|
||||||
|
ElementManagement = 'element-management',
|
||||||
|
SelectedElement = 'selected-element',
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user