mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* Styles: Refactoring and tweaks to inline form styles * Minor change * Minor fix * Updated snapshot * tweaks * minor tweak * Updated modal style * Updated snapshot * Updated more snapshots
227 lines
6.3 KiB
TypeScript
227 lines
6.3 KiB
TypeScript
import React, { useCallback, useMemo } from 'react';
|
|
import { css, cx } from 'emotion';
|
|
import { DragDropContext, Draggable, Droppable, DropResult } from 'react-beautiful-dnd';
|
|
import {
|
|
createOrderFieldsComparer,
|
|
DataFrame,
|
|
DataTransformerID,
|
|
GrafanaTheme,
|
|
OrganizeFieldsTransformerOptions,
|
|
standardTransformers,
|
|
TransformerRegistyItem,
|
|
TransformerUIProps,
|
|
} from '@grafana/data';
|
|
import { stylesFactory, useTheme } from '../../themes';
|
|
import { Button } from '../Button/Button';
|
|
import { VerticalGroup } from '../Layout/Layout';
|
|
import { Input } from '../Input/Input';
|
|
|
|
interface OrganizeFieldsTransformerEditorProps extends TransformerUIProps<OrganizeFieldsTransformerOptions> {}
|
|
|
|
const OrganizeFieldsTransformerEditor: React.FC<OrganizeFieldsTransformerEditorProps> = props => {
|
|
const { options, input, onChange } = props;
|
|
const { indexByName, excludeByName, renameByName } = options;
|
|
|
|
const fieldNames = useMemo(() => fieldNamesFromInput(input), [input]);
|
|
const orderedFieldNames = useMemo(() => orderFieldNamesByIndex(fieldNames, indexByName), [fieldNames, indexByName]);
|
|
|
|
const onToggleVisibility = useCallback(
|
|
(field: string, shouldExclude: boolean) => {
|
|
onChange({
|
|
...options,
|
|
excludeByName: {
|
|
...excludeByName,
|
|
[field]: shouldExclude,
|
|
},
|
|
});
|
|
},
|
|
[onChange, excludeByName, indexByName]
|
|
);
|
|
|
|
const onDragEnd = useCallback(
|
|
(result: DropResult) => {
|
|
if (!result || !result.destination) {
|
|
return;
|
|
}
|
|
|
|
const startIndex = result.source.index;
|
|
const endIndex = result.destination.index;
|
|
|
|
if (startIndex === endIndex) {
|
|
return;
|
|
}
|
|
|
|
onChange({
|
|
...options,
|
|
indexByName: reorderToIndex(fieldNames, startIndex, endIndex),
|
|
});
|
|
},
|
|
[onChange, indexByName, excludeByName, fieldNames]
|
|
);
|
|
|
|
const onRenameField = useCallback(
|
|
(from: string, to: string) => {
|
|
onChange({
|
|
...options,
|
|
renameByName: {
|
|
...options.renameByName,
|
|
[from]: to,
|
|
},
|
|
});
|
|
},
|
|
[onChange, fieldNames, renameByName]
|
|
);
|
|
|
|
return (
|
|
<VerticalGroup>
|
|
<DragDropContext onDragEnd={onDragEnd}>
|
|
<Droppable droppableId="sortable-fields-transformer" direction="vertical">
|
|
{provided => (
|
|
<div ref={provided.innerRef} {...provided.droppableProps}>
|
|
{orderedFieldNames.map((fieldName, index) => {
|
|
return (
|
|
<DraggableFieldName
|
|
fieldName={fieldName}
|
|
index={index}
|
|
onToggleVisibility={onToggleVisibility}
|
|
onRenameField={onRenameField}
|
|
visible={!excludeByName[fieldName]}
|
|
key={fieldName}
|
|
/>
|
|
);
|
|
})}
|
|
{provided.placeholder}
|
|
</div>
|
|
)}
|
|
</Droppable>
|
|
</DragDropContext>
|
|
</VerticalGroup>
|
|
);
|
|
};
|
|
|
|
interface DraggableFieldProps {
|
|
fieldName: string;
|
|
index: number;
|
|
visible: boolean;
|
|
onToggleVisibility: (fieldName: string, isVisible: boolean) => void;
|
|
onRenameField: (from: string, to: string) => void;
|
|
}
|
|
|
|
const DraggableFieldName: React.FC<DraggableFieldProps> = ({
|
|
fieldName,
|
|
index,
|
|
visible,
|
|
onToggleVisibility,
|
|
onRenameField,
|
|
}) => {
|
|
const theme = useTheme();
|
|
const styles = getFieldNameStyles(theme);
|
|
|
|
return (
|
|
<Draggable draggableId={fieldName} index={index}>
|
|
{provided => (
|
|
<div
|
|
className={styles.container}
|
|
ref={provided.innerRef}
|
|
{...provided.draggableProps}
|
|
{...provided.dragHandleProps}
|
|
>
|
|
<div className={styles.left}>
|
|
<i className={cx('fa fa-ellipsis-v', styles.draggable)} />
|
|
<Button
|
|
className={styles.toggle}
|
|
variant="link"
|
|
size="md"
|
|
icon={visible ? 'eye' : 'eye-slash'}
|
|
onClick={() => onToggleVisibility(fieldName, visible)}
|
|
/>
|
|
<span className={styles.name}>{fieldName}</span>
|
|
</div>
|
|
<div className={styles.right}>
|
|
<Input
|
|
placeholder={`Rename ${fieldName}`}
|
|
onChange={event => onRenameField(fieldName, event.currentTarget.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Draggable>
|
|
);
|
|
};
|
|
|
|
const getFieldNameStyles = stylesFactory((theme: GrafanaTheme) => ({
|
|
container: css`
|
|
display: flex;
|
|
align-items: center;
|
|
margin-top: 8px;
|
|
`,
|
|
left: css`
|
|
width: 35%;
|
|
padding: 0 8px;
|
|
border-radius: 3px;
|
|
background-color: ${theme.colors.bg2};
|
|
`,
|
|
right: css`
|
|
width: 65%;
|
|
margin-left: 8px;
|
|
`,
|
|
toggle: css`
|
|
padding: 5px;
|
|
margin: 0 5px;
|
|
`,
|
|
draggable: css`
|
|
font-size: ${theme.typography.size.md};
|
|
opacity: 0.4;
|
|
`,
|
|
name: css`
|
|
font-size: ${theme.typography.size.sm};
|
|
font-weight: ${theme.typography.weight.semibold};
|
|
`,
|
|
}));
|
|
|
|
const reorderToIndex = (fieldNames: string[], startIndex: number, endIndex: number) => {
|
|
const result = Array.from(fieldNames);
|
|
const [removed] = result.splice(startIndex, 1);
|
|
result.splice(endIndex, 0, removed);
|
|
|
|
return result.reduce((nameByIndex, fieldName, index) => {
|
|
nameByIndex[fieldName] = index;
|
|
return nameByIndex;
|
|
}, {} as Record<string, number>);
|
|
};
|
|
|
|
const orderFieldNamesByIndex = (fieldNames: string[], indexByName: Record<string, number> = {}): string[] => {
|
|
if (!indexByName || Object.keys(indexByName).length === 0) {
|
|
return fieldNames;
|
|
}
|
|
const comparer = createOrderFieldsComparer(indexByName);
|
|
return fieldNames.sort(comparer);
|
|
};
|
|
|
|
const fieldNamesFromInput = (input: DataFrame[]): string[] => {
|
|
if (!Array.isArray(input)) {
|
|
return [] as string[];
|
|
}
|
|
|
|
return Object.keys(
|
|
input.reduce((names, frame) => {
|
|
if (!frame || !Array.isArray(frame.fields)) {
|
|
return names;
|
|
}
|
|
|
|
return frame.fields.reduce((names, field) => {
|
|
names[field.name] = null;
|
|
return names;
|
|
}, names);
|
|
}, {} as Record<string, null>)
|
|
);
|
|
};
|
|
|
|
export const organizeFieldsTransformRegistryItem: TransformerRegistyItem<OrganizeFieldsTransformerOptions> = {
|
|
id: DataTransformerID.organize,
|
|
editor: OrganizeFieldsTransformerEditor,
|
|
transformation: standardTransformers.organizeFieldsTransformer,
|
|
name: 'Organize fields',
|
|
description: 'Order, filter and rename fields',
|
|
};
|