Canvas: Add tabs to inline editor (#57778)

This commit is contained in:
Adela Almasan 2022-10-28 12:36:19 -05:00 committed by GitHub
parent 3294918e9f
commit a5c492b033
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 116 additions and 9 deletions

View File

@ -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"],

View File

@ -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>) => {
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)};
`,
});

View 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>
</>
);
};

View File

@ -15,3 +15,8 @@ export interface DragNode {
export interface DropNode extends DragNode {
pos: string;
}
export enum InlineEditTabs {
ElementManagement = 'element-management',
SelectedElement = 'selected-element',
}