add transformation menu
This commit is contained in:
@@ -18,67 +18,76 @@ interface DetailViewProps {
|
|||||||
selectedItem: QueryTransformItem | undefined;
|
selectedItem: QueryTransformItem | undefined;
|
||||||
panel: VizPanel;
|
panel: VizPanel;
|
||||||
tabs: Array<{ tabId: TabId }>;
|
tabs: Array<{ tabId: TabId }>;
|
||||||
|
onRemoveTransform?: (index: number) => void;
|
||||||
|
onToggleTransformVisibility?: (index: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DetailView = memo(({ selectedItem, panel, tabs }: DetailViewProps) => {
|
export const DetailView = memo(
|
||||||
const styles = useStyles2(getStyles);
|
({ selectedItem, panel, tabs, onRemoveTransform, onToggleTransformVisibility }: DetailViewProps) => {
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
const renderContent = useCallback(() => {
|
const renderContent = useCallback(() => {
|
||||||
if (!selectedItem) {
|
if (!selectedItem) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.emptyState}>
|
<div className={styles.emptyState}>
|
||||||
<p>
|
<p>
|
||||||
<Trans i18nKey="dashboard-scene.panel-data-pane.empty-state">
|
<Trans i18nKey="dashboard-scene.panel-data-pane.empty-state">
|
||||||
Select a query or transformation to edit
|
Select a query or transformation to edit
|
||||||
</Trans>
|
</Trans>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedItem.type === 'query' && 'refId' in selectedItem.data) {
|
if (selectedItem.type === 'query' && 'refId' in selectedItem.data) {
|
||||||
const query = 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)) {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DetailViewHeader selectedItem={selectedItem} panel={panel} />
|
<DetailViewHeader selectedItem={selectedItem} panel={panel} />
|
||||||
<ScrollContainer>
|
<ScrollContainer>
|
||||||
<ExpressionDetailView panel={panel} expression={data} expressionIndex={selectedItem.index} />
|
<QueryDetailView panel={panel} query={query} queryIndex={selectedItem.index} />
|
||||||
</ScrollContainer>
|
</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;
|
return null;
|
||||||
}, [selectedItem, panel, tabs, styles.emptyState]);
|
}, [selectedItem, panel, tabs, styles.emptyState, onRemoveTransform, onToggleTransformVisibility]);
|
||||||
|
|
||||||
return <div className={styles.container}>{renderContent()}</div>;
|
return <div className={styles.container}>{renderContent()}</div>;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
DetailView.displayName = 'DetailView';
|
DetailView.displayName = 'DetailView';
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import { QueryTransformItem } from './QueryTransformList';
|
|||||||
interface DetailViewHeaderProps {
|
interface DetailViewHeaderProps {
|
||||||
selectedItem: QueryTransformItem;
|
selectedItem: QueryTransformItem;
|
||||||
panel: VizPanel;
|
panel: VizPanel;
|
||||||
|
onRemoveTransform?: (index: number) => void;
|
||||||
|
onToggleTransformVisibility?: (index: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ITEM_CONFIG = (theme: GrafanaTheme2) => ({
|
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 theme = useTheme2();
|
||||||
const config = useMemo(() => ITEM_CONFIG(theme)[selectedItem.type], [theme, selectedItem.type]);
|
const config = useMemo(() => ITEM_CONFIG(theme)[selectedItem.type], [theme, selectedItem.type]);
|
||||||
const styles = useStyles2(getStyles, config);
|
const styles = useStyles2(getStyles, config);
|
||||||
@@ -243,12 +250,30 @@ export const DetailViewHeader = ({ selectedItem, panel }: DetailViewHeaderProps)
|
|||||||
queryRunner?.runQueries();
|
queryRunner?.runQueries();
|
||||||
}, [panel]);
|
}, [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 refId = 'refId' in selectedItem.data ? selectedItem.data.refId : '';
|
||||||
const isHidden =
|
const isHidden =
|
||||||
(selectedItem.type === 'query' || selectedItem.type === 'expression') &&
|
(selectedItem.type === 'query' || selectedItem.type === 'expression') &&
|
||||||
'hide' in selectedItem.data &&
|
'hide' in selectedItem.data &&
|
||||||
selectedItem.data.hide;
|
selectedItem.data.hide;
|
||||||
|
|
||||||
|
const isTransformDisabled =
|
||||||
|
selectedItem.type === 'transform' && 'disabled' in selectedItem.data && selectedItem.data.disabled;
|
||||||
|
|
||||||
// Get transformation display name
|
// Get transformation display name
|
||||||
const transformationName = useMemo(() => {
|
const transformationName = useMemo(() => {
|
||||||
if (selectedItem.type === 'transform' && 'id' in selectedItem.data) {
|
if (selectedItem.type === 'transform' && 'id' in selectedItem.data) {
|
||||||
@@ -314,7 +339,7 @@ export const DetailViewHeader = ({ selectedItem, panel }: DetailViewHeaderProps)
|
|||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{/* Right side: Run Query + Actions Menu */}
|
{/* Right side: Run Query + Actions Menu for queries/expressions */}
|
||||||
{(selectedItem.type === 'query' || selectedItem.type === 'expression') && (
|
{(selectedItem.type === 'query' || selectedItem.type === 'expression') && (
|
||||||
<Stack gap={0.5} alignItems="center">
|
<Stack gap={0.5} alignItems="center">
|
||||||
<Button
|
<Button
|
||||||
@@ -361,6 +386,39 @@ export const DetailViewHeader = ({ selectedItem, panel }: DetailViewHeaderProps)
|
|||||||
</Dropdown>
|
</Dropdown>
|
||||||
</Stack>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -501,7 +501,13 @@ function PanelDataPaneRendered({ model }: SceneComponentProps<PanelDataPane>) {
|
|||||||
style={{ ...splitterProps.style, width: '16px' }}
|
style={{ ...splitterProps.style, width: '16px' }}
|
||||||
/>
|
/>
|
||||||
<div {...secondaryProps}>
|
<div {...secondaryProps}>
|
||||||
<DetailView selectedItem={selectedItem} panel={panel} tabs={tabs} />
|
<DetailView
|
||||||
|
selectedItem={selectedItem}
|
||||||
|
panel={panel}
|
||||||
|
tabs={tabs}
|
||||||
|
onRemoveTransform={handleRemoveTransform}
|
||||||
|
onToggleTransformVisibility={handleToggleTransformVisibility}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<TransformationsDrawer
|
<TransformationsDrawer
|
||||||
|
|||||||
Reference in New Issue
Block a user