add transformation menu
This commit is contained in:
@@ -18,67 +18,76 @@ interface DetailViewProps {
|
||||
selectedItem: QueryTransformItem | undefined;
|
||||
panel: VizPanel;
|
||||
tabs: Array<{ tabId: TabId }>;
|
||||
onRemoveTransform?: (index: number) => void;
|
||||
onToggleTransformVisibility?: (index: number) => void;
|
||||
}
|
||||
|
||||
export const DetailView = memo(({ selectedItem, panel, tabs }: DetailViewProps) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
export const DetailView = memo(
|
||||
({ selectedItem, panel, tabs, onRemoveTransform, onToggleTransformVisibility }: DetailViewProps) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const renderContent = useCallback(() => {
|
||||
if (!selectedItem) {
|
||||
return (
|
||||
<div className={styles.emptyState}>
|
||||
<p>
|
||||
<Trans i18nKey="dashboard-scene.panel-data-pane.empty-state">
|
||||
Select a query or transformation to edit
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const renderContent = useCallback(() => {
|
||||
if (!selectedItem) {
|
||||
return (
|
||||
<div className={styles.emptyState}>
|
||||
<p>
|
||||
<Trans i18nKey="dashboard-scene.panel-data-pane.empty-state">
|
||||
Select a query or transformation to edit
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedItem.type === 'query' && 'refId' in selectedItem.data) {
|
||||
const query = selectedItem.data;
|
||||
return (
|
||||
<>
|
||||
<DetailViewHeader selectedItem={selectedItem} panel={panel} />
|
||||
<ScrollContainer>
|
||||
<QueryDetailView panel={panel} query={query} queryIndex={selectedItem.index} />
|
||||
</ScrollContainer>
|
||||
</>
|
||||
);
|
||||
} else if (selectedItem.type === 'expression' && 'refId' in selectedItem.data) {
|
||||
const data = selectedItem.data;
|
||||
if (isExpressionQuery(data)) {
|
||||
if (selectedItem.type === 'query' && 'refId' in selectedItem.data) {
|
||||
const query = selectedItem.data;
|
||||
return (
|
||||
<>
|
||||
<DetailViewHeader selectedItem={selectedItem} panel={panel} />
|
||||
<ScrollContainer>
|
||||
<ExpressionDetailView panel={panel} expression={data} expressionIndex={selectedItem.index} />
|
||||
<QueryDetailView panel={panel} query={query} queryIndex={selectedItem.index} />
|
||||
</ScrollContainer>
|
||||
</>
|
||||
);
|
||||
} else if (selectedItem.type === 'expression' && 'refId' in selectedItem.data) {
|
||||
const data = selectedItem.data;
|
||||
if (isExpressionQuery(data)) {
|
||||
return (
|
||||
<>
|
||||
<DetailViewHeader selectedItem={selectedItem} panel={panel} />
|
||||
<ScrollContainer>
|
||||
<ExpressionDetailView panel={panel} expression={data} expressionIndex={selectedItem.index} />
|
||||
</ScrollContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const transformsTab = tabs.find((t): t is PanelDataTransformationsTab => t.tabId === TabId.Transformations);
|
||||
if (transformsTab && 'id' in selectedItem.data) {
|
||||
return (
|
||||
<>
|
||||
<DetailViewHeader
|
||||
selectedItem={selectedItem}
|
||||
panel={panel}
|
||||
onRemoveTransform={onRemoveTransform}
|
||||
onToggleTransformVisibility={onToggleTransformVisibility}
|
||||
/>
|
||||
<ScrollContainer>
|
||||
<Container>
|
||||
<PanelDataTransformationsTabRendered model={transformsTab} selectedIdx={selectedItem.index} />
|
||||
</Container>
|
||||
</ScrollContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const transformsTab = tabs.find((t): t is PanelDataTransformationsTab => t.tabId === TabId.Transformations);
|
||||
if (transformsTab && 'id' in selectedItem.data) {
|
||||
return (
|
||||
<>
|
||||
<DetailViewHeader selectedItem={selectedItem} panel={panel} />
|
||||
<ScrollContainer>
|
||||
<Container>
|
||||
<PanelDataTransformationsTabRendered model={transformsTab} selectedIdx={selectedItem.index} />
|
||||
</Container>
|
||||
</ScrollContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [selectedItem, panel, tabs, styles.emptyState]);
|
||||
return null;
|
||||
}, [selectedItem, panel, tabs, styles.emptyState, onRemoveTransform, onToggleTransformVisibility]);
|
||||
|
||||
return <div className={styles.container}>{renderContent()}</div>;
|
||||
});
|
||||
return <div className={styles.container}>{renderContent()}</div>;
|
||||
}
|
||||
);
|
||||
|
||||
DetailView.displayName = 'DetailView';
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ import { QueryTransformItem } from './QueryTransformList';
|
||||
interface DetailViewHeaderProps {
|
||||
selectedItem: QueryTransformItem;
|
||||
panel: VizPanel;
|
||||
onRemoveTransform?: (index: number) => void;
|
||||
onToggleTransformVisibility?: (index: number) => void;
|
||||
}
|
||||
|
||||
const ITEM_CONFIG = (theme: GrafanaTheme2) => ({
|
||||
@@ -49,7 +51,12 @@ const ITEM_CONFIG = (theme: GrafanaTheme2) => ({
|
||||
},
|
||||
});
|
||||
|
||||
export const DetailViewHeader = ({ selectedItem, panel }: DetailViewHeaderProps) => {
|
||||
export const DetailViewHeader = ({
|
||||
selectedItem,
|
||||
panel,
|
||||
onRemoveTransform,
|
||||
onToggleTransformVisibility,
|
||||
}: DetailViewHeaderProps) => {
|
||||
const theme = useTheme2();
|
||||
const config = useMemo(() => ITEM_CONFIG(theme)[selectedItem.type], [theme, selectedItem.type]);
|
||||
const styles = useStyles2(getStyles, config);
|
||||
@@ -243,12 +250,30 @@ export const DetailViewHeader = ({ selectedItem, panel }: DetailViewHeaderProps)
|
||||
queryRunner?.runQueries();
|
||||
}, [panel]);
|
||||
|
||||
// Transformation action handlers
|
||||
const onRemoveTransformation = useCallback(() => {
|
||||
if (selectedItem.type !== 'transform' || selectedItem.index === undefined) {
|
||||
return;
|
||||
}
|
||||
onRemoveTransform?.(selectedItem.index);
|
||||
}, [selectedItem, onRemoveTransform]);
|
||||
|
||||
const onToggleTransformationVisibility = useCallback(() => {
|
||||
if (selectedItem.type !== 'transform' || selectedItem.index === undefined) {
|
||||
return;
|
||||
}
|
||||
onToggleTransformVisibility?.(selectedItem.index);
|
||||
}, [selectedItem, onToggleTransformVisibility]);
|
||||
|
||||
const refId = 'refId' in selectedItem.data ? selectedItem.data.refId : '';
|
||||
const isHidden =
|
||||
(selectedItem.type === 'query' || selectedItem.type === 'expression') &&
|
||||
'hide' in selectedItem.data &&
|
||||
selectedItem.data.hide;
|
||||
|
||||
const isTransformDisabled =
|
||||
selectedItem.type === 'transform' && 'disabled' in selectedItem.data && selectedItem.data.disabled;
|
||||
|
||||
// Get transformation display name
|
||||
const transformationName = useMemo(() => {
|
||||
if (selectedItem.type === 'transform' && 'id' in selectedItem.data) {
|
||||
@@ -314,7 +339,7 @@ export const DetailViewHeader = ({ selectedItem, panel }: DetailViewHeaderProps)
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{/* Right side: Run Query + Actions Menu */}
|
||||
{/* Right side: Run Query + Actions Menu for queries/expressions */}
|
||||
{(selectedItem.type === 'query' || selectedItem.type === 'expression') && (
|
||||
<Stack gap={0.5} alignItems="center">
|
||||
<Button
|
||||
@@ -361,6 +386,39 @@ export const DetailViewHeader = ({ selectedItem, panel }: DetailViewHeaderProps)
|
||||
</Dropdown>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{/* Right side: Actions Menu for transformations */}
|
||||
{selectedItem.type === 'transform' && (
|
||||
<Stack gap={0.5} alignItems="center">
|
||||
<Dropdown
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
label={
|
||||
isTransformDisabled
|
||||
? t('dashboard-scene.detail-view-header.enable-transform', 'Enable')
|
||||
: t('dashboard-scene.detail-view-header.disable-transform', 'Disable')
|
||||
}
|
||||
icon={isTransformDisabled ? 'eye' : 'eye-slash'}
|
||||
onClick={onToggleTransformationVisibility}
|
||||
/>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
label={t('dashboard-scene.detail-view-header.remove-transform', 'Remove')}
|
||||
icon="trash-alt"
|
||||
onClick={onRemoveTransformation}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
name="ellipsis-v"
|
||||
variant="secondary"
|
||||
tooltip={t('dashboard-scene.detail-view-header.actions', 'Actions')}
|
||||
/>
|
||||
</Dropdown>
|
||||
</Stack>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -501,7 +501,13 @@ function PanelDataPaneRendered({ model }: SceneComponentProps<PanelDataPane>) {
|
||||
style={{ ...splitterProps.style, width: '16px' }}
|
||||
/>
|
||||
<div {...secondaryProps}>
|
||||
<DetailView selectedItem={selectedItem} panel={panel} tabs={tabs} />
|
||||
<DetailView
|
||||
selectedItem={selectedItem}
|
||||
panel={panel}
|
||||
tabs={tabs}
|
||||
onRemoveTransform={handleRemoveTransform}
|
||||
onToggleTransformVisibility={handleToggleTransformVisibility}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<TransformationsDrawer
|
||||
|
||||
Reference in New Issue
Block a user