|
|
|
@ -1,7 +1,16 @@
|
|
|
|
|
import { IconPosition } from 'types'; |
|
|
|
|
|
|
|
|
|
import { GrafanaTheme, SelectableValue, StandardEditorProps } from '@grafana/data'; |
|
|
|
|
import { Button, HorizontalGroup, IconButton, Input, RadioButtonGroup, Slider, stylesFactory, ThemeContext } from '@grafana/ui'; |
|
|
|
|
import { |
|
|
|
|
Button, |
|
|
|
|
HorizontalGroup, |
|
|
|
|
IconButton, |
|
|
|
|
Input, |
|
|
|
|
RadioButtonGroup, |
|
|
|
|
Slider, |
|
|
|
|
stylesFactory, |
|
|
|
|
ThemeContext, |
|
|
|
|
} from '@grafana/ui'; |
|
|
|
|
import { FieldNamePicker } from '../../grafana/MatchersUI/FieldNamePicker'; |
|
|
|
|
|
|
|
|
|
import { css } from 'emotion'; |
|
|
|
@ -9,7 +18,6 @@ import React from 'react';
|
|
|
|
|
|
|
|
|
|
import * as _ from 'lodash'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type IconConfig = { |
|
|
|
|
position: IconPosition; |
|
|
|
|
url: string; |
|
|
|
@ -17,7 +25,7 @@ type IconConfig = {
|
|
|
|
|
conditions: Condition[]; |
|
|
|
|
values: number[]; |
|
|
|
|
size: number; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
enum Condition { |
|
|
|
|
EQUAL = '=', |
|
|
|
@ -27,7 +35,6 @@ enum Condition {
|
|
|
|
|
LESS_OR_EQUAL = '<=', |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const positionOptions: Array<SelectableValue<IconPosition>> = [ |
|
|
|
|
{ |
|
|
|
|
label: 'Upper-Left', |
|
|
|
@ -73,7 +80,7 @@ const DEFAULT_ICON: IconConfig = {
|
|
|
|
|
metrics: [], |
|
|
|
|
conditions: [], |
|
|
|
|
values: [], |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const fieldNamePickerSettings = { |
|
|
|
|
settings: { width: 24 }, |
|
|
|
@ -83,18 +90,14 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ar
|
|
|
|
|
const icons = value; |
|
|
|
|
|
|
|
|
|
const addIcon = () => { |
|
|
|
|
onChange( |
|
|
|
|
_.concat(icons, _.cloneDeep(DEFAULT_ICON)) |
|
|
|
|
); |
|
|
|
|
onChange(_.concat(icons, _.cloneDeep(DEFAULT_ICON))); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const removeIcon = (idx: number) => { |
|
|
|
|
onChange( |
|
|
|
|
_.filter(icons, (icon, iconIdx) => iconIdx !== idx) |
|
|
|
|
); |
|
|
|
|
onChange(_.filter(icons, (icon, iconIdx) => iconIdx !== idx)); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const addCondition = (iconIdx:number) => { |
|
|
|
|
const addCondition = (iconIdx: number) => { |
|
|
|
|
icons[iconIdx].conditions.push(Condition.GREATER_OR_EQUAL); |
|
|
|
|
icons[iconIdx].metrics.push(''); |
|
|
|
|
icons[iconIdx].values.push(0); |
|
|
|
@ -118,7 +121,7 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ar
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const onConditionChange = (iconIdx: number, conditionIdx: number, field: keyof IconConfig, value: any) => { |
|
|
|
|
console.log(value) |
|
|
|
|
console.log(value); |
|
|
|
|
// @ts-ignore
|
|
|
|
|
icons[iconIdx][field][conditionIdx] = value; |
|
|
|
|
|
|
|
|
@ -128,72 +131,76 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ar
|
|
|
|
|
return ( |
|
|
|
|
<ThemeContext.Consumer> |
|
|
|
|
{(theme) => { |
|
|
|
|
const styles = getStyles(theme.v1); |
|
|
|
|
return ( |
|
|
|
|
<div> |
|
|
|
|
<div className={styles.icons}> |
|
|
|
|
{ |
|
|
|
|
icons.map((icon, iconIdx) => { |
|
|
|
|
return ( |
|
|
|
|
<div className={styles.icon}> |
|
|
|
|
<IconButton name="trash-alt" onClick={() => removeIcon(iconIdx)}></IconButton> |
|
|
|
|
<Input |
|
|
|
|
type="url" |
|
|
|
|
placeholder="Image URL" |
|
|
|
|
value={icon.url} |
|
|
|
|
onChange={evt => onIconFieldChange(iconIdx, 'url', (evt.target as any).value)} |
|
|
|
|
/> |
|
|
|
|
<RadioButtonGroup |
|
|
|
|
value={icon.position} |
|
|
|
|
options={positionOptions} |
|
|
|
|
onChange={newVal => onIconFieldChange(iconIdx, 'position', newVal)} |
|
|
|
|
/> |
|
|
|
|
<Slider |
|
|
|
|
value={icon.size} |
|
|
|
|
min={1} |
|
|
|
|
max={100} |
|
|
|
|
step={1} |
|
|
|
|
onAfterChange={newVal => onIconFieldChange(iconIdx, 'size', newVal)} |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
icon.conditions.map((condition, conditionIdx) => { |
|
|
|
|
return ( |
|
|
|
|
<HorizontalGroup> |
|
|
|
|
<FieldNamePicker |
|
|
|
|
context={context} |
|
|
|
|
value={icon.metrics[conditionIdx]} |
|
|
|
|
onChange={(newVal: any) => onConditionChange(iconIdx, conditionIdx, 'metrics', newVal) } |
|
|
|
|
item={fieldNamePickerSettings} |
|
|
|
|
/> |
|
|
|
|
<RadioButtonGroup |
|
|
|
|
value={icon.conditions[conditionIdx]} |
|
|
|
|
options={conditionOptions} |
|
|
|
|
onChange={newVal => onConditionChange(iconIdx, conditionIdx, 'conditions', newVal)} |
|
|
|
|
/> |
|
|
|
|
<Input |
|
|
|
|
placeholder="value" |
|
|
|
|
value={icon.values[conditionIdx]} |
|
|
|
|
onChange={evt => onConditionChange(iconIdx, conditionIdx, 'values', (evt.target as any).value)} |
|
|
|
|
/> |
|
|
|
|
<IconButton name="trash-alt" onClick={() => removeCondition(iconIdx, conditionIdx)}></IconButton> |
|
|
|
|
</HorizontalGroup> |
|
|
|
|
) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
<Button variant="secondary" onClick={() => addCondition(iconIdx)}>Add Condition</Button> |
|
|
|
|
</div> |
|
|
|
|
) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
</div> |
|
|
|
|
<Button variant="secondary" onClick={addIcon}>Add Icon</Button> |
|
|
|
|
const styles = getStyles(theme.v1); |
|
|
|
|
return ( |
|
|
|
|
<div> |
|
|
|
|
<div className={styles.icons}> |
|
|
|
|
{icons.map((icon, iconIdx) => { |
|
|
|
|
return ( |
|
|
|
|
<div className={styles.icon}> |
|
|
|
|
<IconButton name="trash-alt" onClick={() => removeIcon(iconIdx)}></IconButton> |
|
|
|
|
<Input |
|
|
|
|
type="url" |
|
|
|
|
placeholder="Image URL" |
|
|
|
|
value={icon.url} |
|
|
|
|
onChange={(evt) => onIconFieldChange(iconIdx, 'url', (evt.target as any).value)} |
|
|
|
|
/> |
|
|
|
|
<RadioButtonGroup |
|
|
|
|
value={icon.position} |
|
|
|
|
options={positionOptions} |
|
|
|
|
onChange={(newVal) => onIconFieldChange(iconIdx, 'position', newVal)} |
|
|
|
|
/> |
|
|
|
|
<Slider |
|
|
|
|
value={icon.size} |
|
|
|
|
min={1} |
|
|
|
|
max={100} |
|
|
|
|
step={1} |
|
|
|
|
onAfterChange={(newVal) => onIconFieldChange(iconIdx, 'size', newVal)} |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
{icon.conditions.map((condition, conditionIdx) => { |
|
|
|
|
return ( |
|
|
|
|
<HorizontalGroup> |
|
|
|
|
<FieldNamePicker |
|
|
|
|
context={context} |
|
|
|
|
value={icon.metrics[conditionIdx]} |
|
|
|
|
onChange={(newVal: any) => onConditionChange(iconIdx, conditionIdx, 'metrics', newVal)} |
|
|
|
|
item={fieldNamePickerSettings} |
|
|
|
|
/> |
|
|
|
|
<RadioButtonGroup |
|
|
|
|
value={icon.conditions[conditionIdx]} |
|
|
|
|
options={conditionOptions} |
|
|
|
|
onChange={(newVal) => onConditionChange(iconIdx, conditionIdx, 'conditions', newVal)} |
|
|
|
|
/> |
|
|
|
|
<Input |
|
|
|
|
placeholder="value" |
|
|
|
|
value={icon.values[conditionIdx]} |
|
|
|
|
onChange={(evt) => |
|
|
|
|
onConditionChange(iconIdx, conditionIdx, 'values', (evt.target as any).value) |
|
|
|
|
} |
|
|
|
|
/> |
|
|
|
|
<IconButton |
|
|
|
|
name="trash-alt" |
|
|
|
|
onClick={() => removeCondition(iconIdx, conditionIdx)} |
|
|
|
|
></IconButton> |
|
|
|
|
</HorizontalGroup> |
|
|
|
|
); |
|
|
|
|
})} |
|
|
|
|
<Button variant="secondary" onClick={() => addCondition(iconIdx)}> |
|
|
|
|
Add Condition |
|
|
|
|
</Button> |
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
})} |
|
|
|
|
</div> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
<Button variant="secondary" onClick={addIcon}> |
|
|
|
|
Add Icon |
|
|
|
|
</Button> |
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
}} |
|
|
|
|
</ThemeContext.Consumer> |
|
|
|
|
) |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
interface IconsEditorStyles { |
|
|
|
@ -218,5 +225,5 @@ const getStyles = stylesFactory((theme: GrafanaTheme): IconsEditorStyles => {
|
|
|
|
|
margin-bottom: 0; |
|
|
|
|
} |
|
|
|
|
`,
|
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
}); |
|
|
|
|