chore: implement splitter + minor changes
This commit is contained in:
@@ -78,6 +78,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
background: theme.colors.background.primary,
|
||||
}),
|
||||
emptyState: css({
|
||||
|
||||
@@ -128,6 +128,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
flexDirection: 'column',
|
||||
gap: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
width: '100%',
|
||||
}),
|
||||
expressionContent: css({
|
||||
display: 'flex',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { DataTransformerConfig, GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
VizPanel,
|
||||
SceneDataTransformer,
|
||||
} from '@grafana/scenes';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { useStyles2, useSplitter } from '@grafana/ui';
|
||||
import { getConfig } from 'app/core/config';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { getRulesPermissions } from 'app/features/alerting/unified/utils/access-control';
|
||||
@@ -398,30 +398,34 @@ function PanelDataPaneRendered({ model }: SceneComponentProps<PanelDataPane>) {
|
||||
const sourceData = queryRunner?.useState();
|
||||
const series = sourceData?.data?.series || [];
|
||||
|
||||
const { containerProps, primaryProps, secondaryProps, splitterProps } = useSplitter({
|
||||
direction: 'row',
|
||||
initialSize: 0.2,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={styles.dataPane} data-testid={selectors.components.PanelEditor.DataPane.content}>
|
||||
<div className={styles.unifiedLayout}>
|
||||
<div className={styles.splitLayout}>
|
||||
<div className={styles.leftPanel}>
|
||||
<QueryTransformList
|
||||
items={items}
|
||||
selectedId={effectiveSelectedId}
|
||||
onSelect={handleSelect}
|
||||
onAddQuery={handleAddQuery}
|
||||
onAddTransform={handleAddTransform}
|
||||
onAddExpression={handleAddExpression}
|
||||
onDuplicateQuery={handleDuplicateQuery}
|
||||
onRemoveQuery={handleRemoveQuery}
|
||||
onToggleQueryVisibility={handleToggleQueryVisibility}
|
||||
onDuplicateExpression={handleDuplicateExpression}
|
||||
onRemoveExpression={handleRemoveExpression}
|
||||
onToggleExpressionVisibility={handleToggleExpressionVisibility}
|
||||
onRemoveTransform={handleRemoveTransform}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.rightPanel}>
|
||||
<DetailView selectedItem={selectedItem} panel={panel} tabs={tabs} />
|
||||
</div>
|
||||
<div {...containerProps} className={cx(containerProps.className, styles.unifiedLayout)}>
|
||||
<div {...primaryProps}>
|
||||
<QueryTransformList
|
||||
items={items}
|
||||
selectedId={effectiveSelectedId}
|
||||
onSelect={handleSelect}
|
||||
onAddQuery={handleAddQuery}
|
||||
onAddTransform={handleAddTransform}
|
||||
onAddExpression={handleAddExpression}
|
||||
onDuplicateQuery={handleDuplicateQuery}
|
||||
onRemoveQuery={handleRemoveQuery}
|
||||
onToggleQueryVisibility={handleToggleQueryVisibility}
|
||||
onDuplicateExpression={handleDuplicateExpression}
|
||||
onRemoveExpression={handleRemoveExpression}
|
||||
onToggleExpressionVisibility={handleToggleExpressionVisibility}
|
||||
onRemoveTransform={handleRemoveTransform}
|
||||
/>
|
||||
</div>
|
||||
<div {...splitterProps} className={cx(splitterProps.className, styles.splitter)} />
|
||||
<div {...secondaryProps}>
|
||||
<DetailView selectedItem={selectedItem} panel={panel} tabs={tabs} />
|
||||
</div>
|
||||
</div>
|
||||
<TransformationsDrawer
|
||||
@@ -468,22 +472,18 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
borderTopRightRadius: theme.shape.radius.default,
|
||||
overflow: 'hidden',
|
||||
}),
|
||||
splitLayout: css({
|
||||
display: 'flex',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}),
|
||||
leftPanel: css({
|
||||
width: '25%',
|
||||
minWidth: '200px',
|
||||
maxWidth: '400px',
|
||||
flexShrink: 0,
|
||||
borderRight: `1px solid ${theme.colors.border.weak}`,
|
||||
}),
|
||||
rightPanel: css({
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
overflow: 'hidden',
|
||||
splitter: css({
|
||||
position: 'relative',
|
||||
'&::before': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: '1px',
|
||||
background: theme.colors.border.weak,
|
||||
transform: 'translateX(-50%)',
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -207,6 +207,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
flexDirection: 'column',
|
||||
gap: theme.spacing(2),
|
||||
padding: theme.spacing(2),
|
||||
width: '100%',
|
||||
}),
|
||||
datasourceSection: css({
|
||||
paddingBottom: theme.spacing(2),
|
||||
|
||||
@@ -149,11 +149,11 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
card: css({
|
||||
cursor: 'pointer',
|
||||
marginBottom: theme.spacing(1),
|
||||
border: `1px solid ${theme.colors.border.weak}`,
|
||||
borderRadius: theme.shape.radius.default,
|
||||
overflow: 'hidden',
|
||||
background: theme.colors.background.primary,
|
||||
width: '100%',
|
||||
[`&:hover .${actionsClass}`]: {
|
||||
opacity: 1,
|
||||
},
|
||||
|
||||
+45
-82
@@ -3,7 +3,7 @@ import { memo } from 'react';
|
||||
|
||||
import { DataTransformerConfig, GrafanaTheme2 } from '@grafana/data';
|
||||
import { SceneDataQuery } from '@grafana/scenes';
|
||||
import { ScrollContainer, useStyles2 } from '@grafana/ui';
|
||||
import { ScrollContainer, Stack, useStyles2 } from '@grafana/ui';
|
||||
import { ExpressionQueryType } from 'app/features/expressions/types';
|
||||
|
||||
import { AddDataItemMenu } from './AddDataItemMenu';
|
||||
@@ -50,79 +50,54 @@ export const QueryTransformList = memo(
|
||||
}: QueryTransformListProps) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const queries = items.filter((item) => item.type === 'query');
|
||||
const expressions = items.filter((item) => item.type === 'expression');
|
||||
const transforms = items.filter((item) => item.type === 'transform');
|
||||
const getHandlers = (item: QueryTransformItem) => {
|
||||
switch (item.type) {
|
||||
case 'query':
|
||||
return {
|
||||
onDuplicate: onDuplicateQuery ? () => onDuplicateQuery(item.index) : undefined,
|
||||
onRemove: onRemoveQuery ? () => onRemoveQuery(item.index) : undefined,
|
||||
onToggleVisibility: onToggleQueryVisibility ? () => onToggleQueryVisibility(item.index) : undefined,
|
||||
};
|
||||
case 'expression':
|
||||
return {
|
||||
onDuplicate: onDuplicateExpression ? () => onDuplicateExpression(item.index) : undefined,
|
||||
onRemove: onRemoveExpression ? () => onRemoveExpression(item.index) : undefined,
|
||||
onToggleVisibility: onToggleExpressionVisibility
|
||||
? () => onToggleExpressionVisibility(item.index)
|
||||
: undefined,
|
||||
};
|
||||
case 'transform':
|
||||
return {
|
||||
onDuplicate: undefined,
|
||||
onRemove: onRemoveTransform ? () => onRemoveTransform(item.index) : undefined,
|
||||
onToggleVisibility: undefined,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.scrollContainer}>
|
||||
<ScrollContainer>
|
||||
<div className={styles.content}>
|
||||
{queries.length > 0 && (
|
||||
<div className={styles.section}>
|
||||
{queries.map((item) => (
|
||||
<QueryTransformCard
|
||||
key={item.id}
|
||||
item={item.data}
|
||||
type="query"
|
||||
index={item.index}
|
||||
isSelected={selectedId === item.id}
|
||||
onClick={() => onSelect(item.id)}
|
||||
onDuplicate={onDuplicateQuery ? () => onDuplicateQuery(item.index) : undefined}
|
||||
onRemove={onRemoveQuery ? () => onRemoveQuery(item.index) : undefined}
|
||||
onToggleVisibility={
|
||||
onToggleQueryVisibility ? () => onToggleQueryVisibility(item.index) : undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{expressions.length > 0 && (
|
||||
<div className={styles.section}>
|
||||
{expressions.map((item) => (
|
||||
<QueryTransformCard
|
||||
key={item.id}
|
||||
item={item.data}
|
||||
type="expression"
|
||||
index={item.index}
|
||||
isSelected={selectedId === item.id}
|
||||
onClick={() => onSelect(item.id)}
|
||||
onDuplicate={onDuplicateExpression ? () => onDuplicateExpression(item.index) : undefined}
|
||||
onRemove={onRemoveExpression ? () => onRemoveExpression(item.index) : undefined}
|
||||
onToggleVisibility={
|
||||
onToggleExpressionVisibility ? () => onToggleExpressionVisibility(item.index) : undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{transforms.length > 0 && (
|
||||
<div className={styles.section}>
|
||||
{transforms.map((item) => (
|
||||
<QueryTransformCard
|
||||
key={item.id}
|
||||
item={item.data}
|
||||
type="transform"
|
||||
index={item.index}
|
||||
isSelected={selectedId === item.id}
|
||||
onClick={() => onSelect(item.id)}
|
||||
onRemove={onRemoveTransform ? () => onRemoveTransform(item.index) : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.addButton}>
|
||||
<AddDataItemMenu
|
||||
onAddQuery={onAddQuery}
|
||||
onAddTransform={onAddTransform}
|
||||
onAddExpression={onAddExpression}
|
||||
<Stack direction="column" gap={2}>
|
||||
{items.map((item) => (
|
||||
<QueryTransformCard
|
||||
key={item.id}
|
||||
item={item.data}
|
||||
type={item.type}
|
||||
index={item.index}
|
||||
isSelected={selectedId === item.id}
|
||||
onClick={() => onSelect(item.id)}
|
||||
{...getHandlers(item)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<AddDataItemMenu
|
||||
onAddQuery={onAddQuery}
|
||||
onAddTransform={onAddTransform}
|
||||
onAddExpression={onAddExpression}
|
||||
/>
|
||||
</Stack>
|
||||
</ScrollContainer>
|
||||
</div>
|
||||
</div>
|
||||
@@ -138,26 +113,14 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
background: theme.colors.background.primary,
|
||||
borderRight: `1px solid ${theme.colors.border.weak}`,
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
}),
|
||||
scrollContainer: css({
|
||||
padding: theme.spacing(2),
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
overflow: 'auto',
|
||||
}),
|
||||
content: css({
|
||||
padding: theme.spacing(2),
|
||||
}),
|
||||
section: css({
|
||||
'&:not(:first-child)': {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
}),
|
||||
addButton: css({
|
||||
marginTop: theme.spacing(2),
|
||||
paddingTop: theme.spacing(2),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user