You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
229 lines
6.6 KiB
229 lines
6.6 KiB
import { IconPosition, Icon, Condition } from 'types'; |
|
|
|
import { GrafanaTheme, SelectableValue, StandardEditorProps } from '@grafana/data'; |
|
import { |
|
Button, |
|
HorizontalGroup, |
|
IconButton, |
|
Input, |
|
RadioButtonGroup, |
|
Slider, |
|
stylesFactory, |
|
ThemeContext, |
|
} from '@grafana/ui'; |
|
import { FieldNamePicker } from '../../grafana/MatchersUI/FieldNamePicker'; |
|
|
|
import { css } from 'emotion'; |
|
import React from 'react'; |
|
|
|
import * as _ from 'lodash'; |
|
|
|
const positionOptions: Array<SelectableValue<IconPosition>> = [ |
|
{ |
|
label: 'Upper-Left', |
|
value: IconPosition.UPPER_LEFT, |
|
}, |
|
{ |
|
label: 'Middle', |
|
value: IconPosition.MIDDLE, |
|
}, |
|
{ |
|
label: 'Upper-Right', |
|
value: IconPosition.UPPER_RIGHT, |
|
}, |
|
]; |
|
|
|
const conditionOptions: Array<SelectableValue<Condition>> = [ |
|
{ |
|
label: '>=', |
|
value: Condition.GREATER_OR_EQUAL, |
|
}, |
|
{ |
|
label: '>', |
|
value: Condition.GREATER, |
|
}, |
|
{ |
|
label: '=', |
|
value: Condition.EQUAL, |
|
}, |
|
{ |
|
label: '<', |
|
value: Condition.LESS, |
|
}, |
|
{ |
|
label: '<=', |
|
value: Condition.LESS_OR_EQUAL, |
|
}, |
|
]; |
|
|
|
const DEFAULT_ICON: Icon = { |
|
position: IconPosition.UPPER_LEFT, |
|
url: '', |
|
size: 40, |
|
metrics: [], |
|
conditions: [], |
|
values: [], |
|
}; |
|
|
|
const fieldNamePickerSettings = { |
|
settings: { width: 24 }, |
|
} as any; |
|
|
|
export function IconsEditor({ onChange, value, context }: StandardEditorProps<Icon[]>) { |
|
const icons = value; |
|
|
|
const addIcon = () => { |
|
onChange(_.concat(icons, _.cloneDeep(DEFAULT_ICON))); |
|
}; |
|
|
|
const removeIcon = (idx: number) => { |
|
onChange(_.filter(icons, (icon, iconIdx) => iconIdx !== idx)); |
|
}; |
|
|
|
const addCondition = (iconIdx: number) => { |
|
icons[iconIdx].conditions.push(Condition.GREATER_OR_EQUAL); |
|
icons[iconIdx].metrics.push(''); |
|
icons[iconIdx].values.push(0); |
|
|
|
onChange(icons); |
|
}; |
|
|
|
const removeCondition = (iconIdx: number, conditionIdx: number) => { |
|
icons[iconIdx].conditions.splice(conditionIdx, 1); |
|
icons[iconIdx].metrics.splice(conditionIdx, 1); |
|
icons[iconIdx].values.splice(conditionIdx, 1); |
|
|
|
onChange(icons); |
|
}; |
|
|
|
const onIconFieldChange = (iconIdx: number, field: keyof Icon, value: any) => { |
|
// @ts-ignore |
|
icons[iconIdx][field] = value; |
|
|
|
onChange(icons); |
|
}; |
|
|
|
const onConditionChange = (iconIdx: number, conditionIdx: number, field: keyof Icon, value: any) => { |
|
// @ts-ignore |
|
icons[iconIdx][field][conditionIdx] = value; |
|
|
|
onChange(icons); |
|
}; |
|
|
|
return ( |
|
<ThemeContext.Consumer> |
|
{(theme) => { |
|
const styles = getStyles(theme.v1); |
|
return ( |
|
<div> |
|
<div className={styles.icons}> |
|
{icons.map((icon, iconIdx) => { |
|
return ( |
|
<div key={iconIdx} className={styles.icon}> |
|
<IconButton name="trash-alt" onClick={() => removeIcon(iconIdx)} tooltip="Delete Icon"></IconButton> |
|
<div className={styles.row}> |
|
<Input |
|
type="url" |
|
placeholder="Image URL" |
|
value={icon.url} |
|
onChange={(evt) => onIconFieldChange(iconIdx, 'url', (evt.target as any).value)} |
|
/> |
|
</div> |
|
<RadioButtonGroup |
|
value={icon.position} |
|
options={positionOptions} |
|
onChange={(newVal) => onIconFieldChange(iconIdx, 'position', newVal)} |
|
/> |
|
<div className={styles.slider}> |
|
<Slider |
|
value={icon.size} |
|
min={1} |
|
max={100} |
|
step={1} |
|
onAfterChange={(newVal) => onIconFieldChange(iconIdx, 'size', newVal)} |
|
/> |
|
</div> |
|
|
|
{icon.conditions.map((condition, conditionIdx) => { |
|
return ( |
|
<div key={conditionIdx} className={styles.condition}> |
|
<HorizontalGroup key={conditionIdx}> |
|
<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)} |
|
tooltip="Delete Condition" |
|
></IconButton> |
|
</HorizontalGroup> |
|
</div> |
|
); |
|
})} |
|
<Button variant="secondary" onClick={() => addCondition(iconIdx)}> |
|
Add Condition |
|
</Button> |
|
</div> |
|
); |
|
})} |
|
</div> |
|
<Button variant="secondary" onClick={addIcon}> |
|
Add Icon |
|
</Button> |
|
</div> |
|
); |
|
}} |
|
</ThemeContext.Consumer> |
|
); |
|
} |
|
|
|
interface IconsEditorStyles { |
|
icons: string; |
|
icon: string; |
|
condition: string; |
|
slider: string; |
|
row: string; |
|
} |
|
|
|
const getStyles = stylesFactory((theme: GrafanaTheme): IconsEditorStyles => { |
|
return { |
|
icons: css` |
|
display: flex; |
|
flex-direction: column; |
|
margin-bottom: ${theme.spacing.formSpacingBase * 2}px; |
|
`, |
|
icon: css` |
|
margin-bottom: ${theme.spacing.sm}; |
|
border-bottom: 1px solid ${theme.colors.panelBorder}; |
|
padding-bottom: ${theme.spacing.formSpacingBase * 2}px; |
|
|
|
&:last-child { |
|
margin-bottom: 0; |
|
} |
|
`, |
|
condition: css` |
|
margin-bottom: ${theme.spacing.xxs}; |
|
`, |
|
slider: css` |
|
padding-bottom: ${theme.spacing.xxs}; |
|
`, |
|
row: css` |
|
margin-bottom: ${theme.spacing.sm}; |
|
`, |
|
}; |
|
});
|
|
|