mirror of
https://github.com/grafana/grafana.git
synced 2024-12-02 13:39:19 -06:00
Transformations: Add unary operations to Add field from calculation (#75946)
* Add unary operations to add field from calc transform * Refactor layout to use new UI components. fix 'as' type assertions * rename exp * more docs * fix docs
This commit is contained in:
parent
9067ca7d29
commit
8e576911b7
@ -5263,8 +5263,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Styles should be written using objects.", "0"]
|
||||
],
|
||||
"public/app/features/transformers/editors/CalculateFieldTransformerEditor.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/transformers/editors/ConvertFieldTypeTransformerEditor.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
|
@ -108,11 +108,17 @@ Use this transformation to add a new field calculated from two other fields. Eac
|
||||
|
||||
- **Mode -** Select a mode:
|
||||
- **Reduce row -** Apply selected calculation on each row of selected fields independently.
|
||||
- **Binary option -** Apply basic math operation(sum, multiply, etc) on values in a single row from two selected fields.
|
||||
- **Binary operation -** Apply basic binary operations (for example, sum or multiply) on values in a single row from two selected fields.
|
||||
- **Unary operation -** Apply basic unary operations on values in a single row from a selected field. The available operations are:
|
||||
- **Absolute value (abs)** - Returns the absolute value of a given expression. It represents its distance from zero as a positive number.
|
||||
- **Natural exponential (exp)** - Returns _e_ raised to the power of a given expression.
|
||||
- **Natural logarithm (ln)** - Returns the natural logarithm of a given expression.
|
||||
- **Floor (floor)** - Returns the largest integer less than or equal to a given expression.
|
||||
- **Ceiling (ceil)** - Returns the smallest integer greater than or equal to a given expression.
|
||||
- **Row index -** Insert a field with the row index.
|
||||
- **Field name -** Select the names of fields you want to use in the calculation for the new field.
|
||||
- **Calculation -** If you select **Reduce row** mode, then the **Calculation** field appears. Click in the field to see a list of calculation choices you can use to create the new field. For information about available calculations, refer to [Calculation types][].
|
||||
- **Operation -** If you select **Binary option** mode, then the **Operation** fields appear. These fields allow you to do basic math operations on values in a single row from two selected fields. You can also use numerical values for binary operations.
|
||||
- **Operation -** If you select **Binary operation** or **Unary operation** mode, then the **Operation** fields appear. These fields allow you to apply basic math operations on values in a single row from selected fields. You can also use numerical values for binary operations.
|
||||
- **As percentile -** If you select **Row index** mode, then the **As percentile** switch appears. This switch allows you to transform the row index as a percentage of the total number of rows.
|
||||
- **Alias -** (Optional) Enter the name of your new field. If you leave this blank, then the field will be named to match the calculation.
|
||||
- **Replace all fields -** (Optional) Select this option if you want to hide all other fields and display only your calculated field in the visualization.
|
||||
|
@ -2,7 +2,7 @@ import { DataFrameView } from '../../dataframe';
|
||||
import { toDataFrame } from '../../dataframe/processDataFrame';
|
||||
import { DataTransformContext, ScopedVars } from '../../types';
|
||||
import { FieldType } from '../../types/dataFrame';
|
||||
import { BinaryOperationID } from '../../utils';
|
||||
import { BinaryOperationID, UnaryOperationID } from '../../utils';
|
||||
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
||||
import { ReducerID } from '../fieldReducer';
|
||||
import { transformDataFrame } from '../transformDataFrame';
|
||||
@ -189,6 +189,51 @@ describe('calculateField transformer w/ timeseries', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('unary math', async () => {
|
||||
const unarySeries = toDataFrame({
|
||||
fields: [
|
||||
{ name: 'TheTime', type: FieldType.time, values: [1000, 2000, 3000, 4000] },
|
||||
{ name: 'A', type: FieldType.number, values: [1, -10, -200, 300] },
|
||||
],
|
||||
});
|
||||
|
||||
const cfg = {
|
||||
id: DataTransformerID.calculateField,
|
||||
options: {
|
||||
mode: CalculateFieldMode.UnaryOperation,
|
||||
unary: {
|
||||
fieldName: 'A',
|
||||
operator: UnaryOperationID.Abs,
|
||||
},
|
||||
replaceFields: true,
|
||||
},
|
||||
};
|
||||
|
||||
await expect(transformDataFrame([cfg], [unarySeries])).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const filtered = data[0];
|
||||
const rows = new DataFrameView(filtered).toArray();
|
||||
expect(rows).toEqual([
|
||||
{
|
||||
'abs(A)': 1,
|
||||
TheTime: 1000,
|
||||
},
|
||||
{
|
||||
'abs(A)': 10,
|
||||
TheTime: 2000,
|
||||
},
|
||||
{
|
||||
'abs(A)': 200,
|
||||
TheTime: 3000,
|
||||
},
|
||||
{
|
||||
'abs(A)': 300,
|
||||
TheTime: 4000,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('boolean field', async () => {
|
||||
const cfg = {
|
||||
id: DataTransformerID.calculateField,
|
||||
|
@ -5,6 +5,7 @@ import { getTimeField } from '../../dataframe/processDataFrame';
|
||||
import { getFieldDisplayName } from '../../field';
|
||||
import { DataFrame, DataTransformerInfo, Field, FieldType, NullValueMode } from '../../types';
|
||||
import { BinaryOperationID, binaryOperators } from '../../utils/binaryOperators';
|
||||
import { UnaryOperationID, unaryOperators } from '../../utils/unaryOperators';
|
||||
import { doStandardCalcs, fieldReducers, ReducerID } from '../fieldReducer';
|
||||
import { getFieldMatcher } from '../matchers';
|
||||
import { FieldMatcherID } from '../matchers/ids';
|
||||
@ -16,6 +17,7 @@ import { noopTransformer } from './noop';
|
||||
export enum CalculateFieldMode {
|
||||
ReduceRow = 'reduceRow',
|
||||
BinaryOperation = 'binary',
|
||||
UnaryOperation = 'unary',
|
||||
Index = 'index',
|
||||
}
|
||||
|
||||
@ -25,6 +27,11 @@ export interface ReduceOptions {
|
||||
nullValueMode?: NullValueMode;
|
||||
}
|
||||
|
||||
export interface UnaryOptions {
|
||||
operator: UnaryOperationID;
|
||||
fieldName: string;
|
||||
}
|
||||
|
||||
export interface BinaryOptions {
|
||||
left: string;
|
||||
operator: BinaryOperationID;
|
||||
@ -45,6 +52,11 @@ const defaultBinaryOptions: BinaryOptions = {
|
||||
right: '',
|
||||
};
|
||||
|
||||
const defaultUnaryOptions: UnaryOptions = {
|
||||
operator: UnaryOperationID.Abs,
|
||||
fieldName: '',
|
||||
};
|
||||
|
||||
export interface CalculateFieldTransformerOptions {
|
||||
// True/False or auto
|
||||
timeSeries?: boolean;
|
||||
@ -53,6 +65,7 @@ export interface CalculateFieldTransformerOptions {
|
||||
// Only one should be filled
|
||||
reduce?: ReduceOptions;
|
||||
binary?: BinaryOptions;
|
||||
unary?: UnaryOptions;
|
||||
index?: IndexOptions;
|
||||
|
||||
// Remove other fields
|
||||
@ -93,6 +106,8 @@ export const calculateFieldTransformer: DataTransformerInfo<CalculateFieldTransf
|
||||
|
||||
if (mode === CalculateFieldMode.ReduceRow) {
|
||||
creator = getReduceRowCreator(defaults(options.reduce, defaultReduceOptions), data);
|
||||
} else if (mode === CalculateFieldMode.UnaryOperation) {
|
||||
creator = getUnaryCreator(defaults(options.unary, defaultUnaryOptions), data);
|
||||
} else if (mode === CalculateFieldMode.BinaryOperation) {
|
||||
const binaryOptions = {
|
||||
...options.binary,
|
||||
@ -263,12 +278,41 @@ function getBinaryCreator(options: BinaryOptions, allFrames: DataFrame[]): Value
|
||||
};
|
||||
}
|
||||
|
||||
function getUnaryCreator(options: UnaryOptions, allFrames: DataFrame[]): ValuesCreator {
|
||||
const operator = unaryOperators.getIfExists(options.operator);
|
||||
|
||||
return (frame: DataFrame) => {
|
||||
let value: number[] = [];
|
||||
|
||||
for (const f of frame.fields) {
|
||||
if (options.fieldName === getFieldDisplayName(f, frame, allFrames) && f.type === FieldType.number) {
|
||||
value = f.values;
|
||||
}
|
||||
}
|
||||
|
||||
if (!value.length || !operator) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const arr = new Array(value.length);
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
arr[i] = operator.operation(value[i]);
|
||||
}
|
||||
|
||||
return arr;
|
||||
};
|
||||
}
|
||||
|
||||
export function getNameFromOptions(options: CalculateFieldTransformerOptions) {
|
||||
if (options.alias?.length) {
|
||||
return options.alias;
|
||||
}
|
||||
|
||||
switch (options.mode) {
|
||||
case CalculateFieldMode.UnaryOperation: {
|
||||
const { unary } = options;
|
||||
return `${unary?.operator ?? ''}${unary?.fieldName ? `(${unary.fieldName})` : ''}`;
|
||||
}
|
||||
case CalculateFieldMode.BinaryOperation: {
|
||||
const { binary } = options;
|
||||
return `${binary?.left ?? ''} ${binary?.operator ?? ''} ${binary?.right ?? ''}`;
|
||||
|
@ -11,6 +11,7 @@ export type BinaryOperation = (left: number, right: number) => number;
|
||||
|
||||
interface BinaryOperatorInfo extends RegistryItem {
|
||||
operation: BinaryOperation;
|
||||
binaryOperationID: BinaryOperationID;
|
||||
}
|
||||
|
||||
export const binaryOperators = new Registry<BinaryOperatorInfo>(() => {
|
||||
@ -19,21 +20,25 @@ export const binaryOperators = new Registry<BinaryOperatorInfo>(() => {
|
||||
id: BinaryOperationID.Add,
|
||||
name: 'Add',
|
||||
operation: (a: number, b: number) => a + b,
|
||||
binaryOperationID: BinaryOperationID.Add,
|
||||
},
|
||||
{
|
||||
id: BinaryOperationID.Subtract,
|
||||
name: 'Subtract',
|
||||
operation: (a: number, b: number) => a - b,
|
||||
binaryOperationID: BinaryOperationID.Subtract,
|
||||
},
|
||||
{
|
||||
id: BinaryOperationID.Multiply,
|
||||
name: 'Multiply',
|
||||
operation: (a: number, b: number) => a * b,
|
||||
binaryOperationID: BinaryOperationID.Multiply,
|
||||
},
|
||||
{
|
||||
id: BinaryOperationID.Divide,
|
||||
name: 'Divide',
|
||||
operation: (a: number, b: number) => a / b,
|
||||
binaryOperationID: BinaryOperationID.Divide,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
@ -10,6 +10,7 @@ export * from './object';
|
||||
export * from './namedColorsPalette';
|
||||
export * from './series';
|
||||
export * from './binaryOperators';
|
||||
export * from './unaryOperators';
|
||||
export * from './nodeGraph';
|
||||
export * from './selectUtils';
|
||||
export { PanelOptionsEditorBuilder, FieldConfigEditorBuilder } from './OptionsUIBuilders';
|
||||
|
51
packages/grafana-data/src/utils/unaryOperators.ts
Normal file
51
packages/grafana-data/src/utils/unaryOperators.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { Registry, RegistryItem } from './Registry';
|
||||
|
||||
export enum UnaryOperationID {
|
||||
Abs = 'abs',
|
||||
Exp = 'exp',
|
||||
Ln = 'ln',
|
||||
Floor = 'floor',
|
||||
Ceil = 'ceil',
|
||||
}
|
||||
|
||||
export type UnaryOperation = (value: number) => number;
|
||||
|
||||
interface UnaryOperatorInfo extends RegistryItem {
|
||||
operation: UnaryOperation;
|
||||
unaryOperationID: UnaryOperationID;
|
||||
}
|
||||
|
||||
export const unaryOperators = new Registry<UnaryOperatorInfo>(() => {
|
||||
return [
|
||||
{
|
||||
id: UnaryOperationID.Abs,
|
||||
name: 'Absolute value',
|
||||
operation: (value: number) => Math.abs(value),
|
||||
unaryOperationID: UnaryOperationID.Abs,
|
||||
},
|
||||
{
|
||||
id: UnaryOperationID.Exp,
|
||||
name: 'Natural exponent',
|
||||
operation: (value: number) => Math.exp(value),
|
||||
unaryOperationID: UnaryOperationID.Exp,
|
||||
},
|
||||
{
|
||||
id: UnaryOperationID.Ln,
|
||||
name: 'Natural logarithm',
|
||||
operation: (value: number) => Math.log(value),
|
||||
unaryOperationID: UnaryOperationID.Ln,
|
||||
},
|
||||
{
|
||||
id: UnaryOperationID.Floor,
|
||||
name: 'Floor',
|
||||
operation: (value: number) => Math.floor(value),
|
||||
unaryOperationID: UnaryOperationID.Floor,
|
||||
},
|
||||
{
|
||||
id: UnaryOperationID.Ceil,
|
||||
name: 'Ceiling',
|
||||
operation: (value: number) => Math.ceil(value),
|
||||
unaryOperationID: UnaryOperationID.Ceil,
|
||||
},
|
||||
];
|
||||
});
|
@ -6,6 +6,7 @@ import { map } from 'rxjs/operators';
|
||||
import {
|
||||
BinaryOperationID,
|
||||
binaryOperators,
|
||||
unaryOperators,
|
||||
DataFrame,
|
||||
DataTransformerID,
|
||||
FieldType,
|
||||
@ -17,9 +18,11 @@ import {
|
||||
TransformerRegistryItem,
|
||||
TransformerUIProps,
|
||||
TransformerCategory,
|
||||
UnaryOperationID,
|
||||
} from '@grafana/data';
|
||||
import {
|
||||
BinaryOptions,
|
||||
UnaryOptions,
|
||||
CalculateFieldMode,
|
||||
CalculateFieldTransformerOptions,
|
||||
getNameFromOptions,
|
||||
@ -27,7 +30,17 @@ import {
|
||||
ReduceOptions,
|
||||
} from '@grafana/data/src/transformations/transformers/calculateField';
|
||||
import { getTemplateSrv, config as cfg } from '@grafana/runtime';
|
||||
import { FilterPill, HorizontalGroup, Input, LegacyForms, Select, StatsPicker } from '@grafana/ui';
|
||||
import {
|
||||
FilterPill,
|
||||
HorizontalGroup,
|
||||
InlineField,
|
||||
InlineFieldRow,
|
||||
InlineLabel,
|
||||
InlineSwitch,
|
||||
Input,
|
||||
Select,
|
||||
StatsPicker,
|
||||
} from '@grafana/ui';
|
||||
|
||||
interface CalculateFieldTransformerEditorProps extends TransformerUIProps<CalculateFieldTransformerOptions> {}
|
||||
|
||||
@ -39,12 +52,15 @@ interface CalculateFieldTransformerEditorState {
|
||||
|
||||
const calculationModes = [
|
||||
{ value: CalculateFieldMode.BinaryOperation, label: 'Binary operation' },
|
||||
{ value: CalculateFieldMode.UnaryOperation, label: 'Unary operation' },
|
||||
{ value: CalculateFieldMode.ReduceRow, label: 'Reduce row' },
|
||||
{ value: CalculateFieldMode.Index, label: 'Row index' },
|
||||
];
|
||||
|
||||
const okTypes = new Set<FieldType>([FieldType.time, FieldType.number, FieldType.string]);
|
||||
|
||||
const labelWidth = 16;
|
||||
|
||||
export class CalculateFieldTransformerEditor extends React.PureComponent<
|
||||
CalculateFieldTransformerEditorProps,
|
||||
CalculateFieldTransformerEditorState
|
||||
@ -151,20 +167,20 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
||||
);
|
||||
}
|
||||
|
||||
onToggleReplaceFields = () => {
|
||||
onToggleReplaceFields = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const { options } = this.props;
|
||||
this.props.onChange({
|
||||
...options,
|
||||
replaceFields: !options.replaceFields,
|
||||
replaceFields: e.currentTarget.checked,
|
||||
});
|
||||
};
|
||||
|
||||
onToggleRowIndexAsPercentile = () => {
|
||||
onToggleRowIndexAsPercentile = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const { options } = this.props;
|
||||
this.props.onChange({
|
||||
...options,
|
||||
index: {
|
||||
asPercentile: !options.index?.asPercentile ?? false,
|
||||
asPercentile: e.currentTarget.checked,
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -227,15 +243,9 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
||||
renderRowIndex(options?: IndexOptions) {
|
||||
return (
|
||||
<>
|
||||
<div className="gf-form-inline">
|
||||
<LegacyForms.Switch
|
||||
label="As percentile"
|
||||
tooltip="Transform the row index as a percentile."
|
||||
labelClass="width-8"
|
||||
checked={!!options?.asPercentile}
|
||||
onChange={this.onToggleRowIndexAsPercentile}
|
||||
/>
|
||||
</div>
|
||||
<InlineField labelWidth={labelWidth} label="As percentile" tooltip="Transform the row index as a percentile.">
|
||||
<InlineSwitch value={!!options?.asPercentile} onChange={this.onToggleRowIndexAsPercentile} />
|
||||
</InlineField>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -246,37 +256,31 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form gf-form--grow">
|
||||
<div className="gf-form-label width-8">Field name</div>
|
||||
<HorizontalGroup spacing="xs" align="flex-start" wrap>
|
||||
{names.map((o, i) => {
|
||||
return (
|
||||
<FilterPill
|
||||
key={`${o}/${i}`}
|
||||
onClick={() => {
|
||||
this.onFieldToggle(o);
|
||||
}}
|
||||
label={o}
|
||||
selected={selected.indexOf(o) > -1}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-label width-8">Calculation</div>
|
||||
<StatsPicker
|
||||
allowMultiple={false}
|
||||
className="width-18"
|
||||
stats={[options.reducer]}
|
||||
onChange={this.onStatsChange}
|
||||
defaultStat={ReducerID.sum}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<InlineField label="Operation" labelWidth={labelWidth} grow={true}>
|
||||
<HorizontalGroup spacing="xs" align="flex-start" wrap>
|
||||
{names.map((o, i) => {
|
||||
return (
|
||||
<FilterPill
|
||||
key={`${o}/${i}`}
|
||||
onClick={() => {
|
||||
this.onFieldToggle(o);
|
||||
}}
|
||||
label={o}
|
||||
selected={selected.indexOf(o) > -1}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</HorizontalGroup>
|
||||
</InlineField>
|
||||
<InlineField label="Calculation" labelWidth={labelWidth}>
|
||||
<StatsPicker
|
||||
allowMultiple={false}
|
||||
className="width-18"
|
||||
stats={[options.reducer]}
|
||||
onChange={this.onStatsChange}
|
||||
defaultStat={ReducerID.sum}
|
||||
/>
|
||||
</InlineField>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -310,16 +314,16 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
||||
});
|
||||
};
|
||||
|
||||
onBinaryOperationChanged = (v: SelectableValue<string>) => {
|
||||
onBinaryOperationChanged = (v: SelectableValue<BinaryOperationID>) => {
|
||||
const { binary } = this.props.options;
|
||||
this.updateBinaryOptions({
|
||||
...binary!,
|
||||
operator: v.value! as BinaryOperationID,
|
||||
operator: v.value!,
|
||||
});
|
||||
};
|
||||
|
||||
renderBinaryOperation(options?: BinaryOptions) {
|
||||
options = defaults(options, { reducer: ReducerID.sum });
|
||||
options = defaults(options, { operator: BinaryOperationID.Add });
|
||||
|
||||
let foundLeft = !options?.left;
|
||||
let foundRight = !options?.right;
|
||||
@ -336,39 +340,109 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
||||
const rightNames = foundRight ? names : [...names, { label: options?.right, value: options?.right }];
|
||||
|
||||
const ops = binaryOperators.list().map((v) => {
|
||||
return { label: v.id, value: v.id };
|
||||
return { label: v.binaryOperationID, value: v.binaryOperationID };
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-label width-8">Operation</div>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<Select
|
||||
allowCustomValue={true}
|
||||
placeholder="Field or number"
|
||||
options={leftNames}
|
||||
className="min-width-18 gf-form-spacing"
|
||||
value={options?.left}
|
||||
onChange={this.onBinaryLeftChanged}
|
||||
/>
|
||||
<Select
|
||||
className="width-8 gf-form-spacing"
|
||||
options={ops}
|
||||
value={options.operator ?? ops[0].value}
|
||||
onChange={this.onBinaryOperationChanged}
|
||||
/>
|
||||
<Select
|
||||
allowCustomValue={true}
|
||||
placeholder="Field or number"
|
||||
className="min-width-10"
|
||||
options={rightNames}
|
||||
value={options?.right}
|
||||
onChange={this.onBinaryRightChanged}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Operation" labelWidth={labelWidth}>
|
||||
<Select
|
||||
allowCustomValue={true}
|
||||
placeholder="Field or number"
|
||||
options={leftNames}
|
||||
className="min-width-18"
|
||||
value={options?.left}
|
||||
onChange={this.onBinaryLeftChanged}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField>
|
||||
<Select
|
||||
className="width-4"
|
||||
options={ops}
|
||||
value={options.operator ?? ops[0].value}
|
||||
onChange={this.onBinaryOperationChanged}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField>
|
||||
<Select
|
||||
allowCustomValue={true}
|
||||
placeholder="Field or number"
|
||||
className="min-width-10"
|
||||
options={rightNames}
|
||||
value={options?.right}
|
||||
onChange={this.onBinaryRightChanged}
|
||||
/>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Unary Operator
|
||||
//---------------------------------------------------------
|
||||
|
||||
updateUnaryOptions = (v: UnaryOptions) => {
|
||||
const { options, onChange } = this.props;
|
||||
onChange({
|
||||
...options,
|
||||
mode: CalculateFieldMode.UnaryOperation,
|
||||
unary: v,
|
||||
});
|
||||
};
|
||||
|
||||
onUnaryOperationChanged = (v: SelectableValue<UnaryOperationID>) => {
|
||||
const { unary } = this.props.options;
|
||||
this.updateUnaryOptions({
|
||||
...unary!,
|
||||
operator: v.value!,
|
||||
});
|
||||
};
|
||||
|
||||
onUnaryValueChanged = (v: SelectableValue<string>) => {
|
||||
const { unary } = this.props.options;
|
||||
this.updateUnaryOptions({
|
||||
...unary!,
|
||||
fieldName: v.value!,
|
||||
});
|
||||
};
|
||||
|
||||
renderUnaryOperation(options?: UnaryOptions) {
|
||||
options = defaults(options, { operator: UnaryOperationID.Abs });
|
||||
|
||||
let found = !options?.fieldName;
|
||||
const names = this.state.names.map((v) => {
|
||||
if (v === options?.fieldName) {
|
||||
found = true;
|
||||
}
|
||||
return { label: v, value: v };
|
||||
});
|
||||
|
||||
const ops = unaryOperators.list().map((v) => {
|
||||
return { label: v.unaryOperationID, value: v.unaryOperationID };
|
||||
});
|
||||
|
||||
const fieldName = found ? names : [...names, { label: options?.fieldName, value: options?.fieldName }];
|
||||
|
||||
return (
|
||||
<>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Operation" labelWidth={labelWidth}>
|
||||
<Select options={ops} value={options.operator ?? ops[0].value} onChange={this.onUnaryOperationChanged} />
|
||||
</InlineField>
|
||||
<InlineField label="(" labelWidth={2}>
|
||||
<Select
|
||||
placeholder="Field"
|
||||
className="min-width-11"
|
||||
options={fieldName}
|
||||
value={options?.fieldName}
|
||||
onChange={this.onUnaryValueChanged}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineLabel width={2}>)</InlineLabel>
|
||||
</InlineFieldRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -382,43 +456,31 @@ export class CalculateFieldTransformerEditor extends React.PureComponent<
|
||||
const mode = options.mode ?? CalculateFieldMode.BinaryOperation;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-label width-8">Mode</div>
|
||||
<Select
|
||||
className="width-18"
|
||||
options={calculationModes}
|
||||
value={calculationModes.find((v) => v.value === mode)}
|
||||
onChange={this.onModeChanged}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<>
|
||||
<InlineField labelWidth={labelWidth} label="Mode">
|
||||
<Select
|
||||
className="width-18"
|
||||
options={calculationModes}
|
||||
value={calculationModes.find((v) => v.value === mode)}
|
||||
onChange={this.onModeChanged}
|
||||
/>
|
||||
</InlineField>
|
||||
{mode === CalculateFieldMode.BinaryOperation && this.renderBinaryOperation(options.binary)}
|
||||
{mode === CalculateFieldMode.UnaryOperation && this.renderUnaryOperation(options.unary)}
|
||||
{mode === CalculateFieldMode.ReduceRow && this.renderReduceRow(options.reduce)}
|
||||
{mode === CalculateFieldMode.Index && this.renderRowIndex(options.index)}
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-label width-8">Alias</div>
|
||||
<Input
|
||||
className="width-18"
|
||||
value={options.alias ?? ''}
|
||||
placeholder={getNameFromOptions(options)}
|
||||
onChange={this.onAliasChanged}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<LegacyForms.Switch
|
||||
label="Replace all fields"
|
||||
labelClass="width-8"
|
||||
checked={!!options.replaceFields}
|
||||
onChange={this.onToggleReplaceFields}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<InlineField labelWidth={labelWidth} label="Alias">
|
||||
<Input
|
||||
className="width-18"
|
||||
value={options.alias ?? ''}
|
||||
placeholder={getNameFromOptions(options)}
|
||||
onChange={this.onAliasChanged}
|
||||
/>
|
||||
</InlineField>
|
||||
<InlineField labelWidth={labelWidth} label="Replace all fields">
|
||||
<InlineSwitch value={!!options.replaceFields} onChange={this.onToggleReplaceFields} />
|
||||
</InlineField>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user