ThresholdsEditor.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import React, { PureComponent } from 'react';
  2. import { Threshold } from '../../types';
  3. import { ColorPicker } from '../ColorPicker/ColorPicker';
  4. import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
  5. import { colors } from '../../utils';
  6. import { getColorFromHexRgbOrName, ThemeContext } from '@grafana/ui';
  7. export interface Props {
  8. thresholds: Threshold[];
  9. onChange: (thresholds: Threshold[]) => void;
  10. }
  11. interface State {
  12. thresholds: Threshold[];
  13. }
  14. export class ThresholdsEditor extends PureComponent<Props, State> {
  15. constructor(props: Props) {
  16. super(props);
  17. const addDefaultThreshold = this.props.thresholds.length === 0;
  18. const thresholds: Threshold[] = addDefaultThreshold
  19. ? [{ index: 0, value: -Infinity, color: colors[0] }]
  20. : props.thresholds;
  21. this.state = { thresholds };
  22. if (addDefaultThreshold) {
  23. this.onChange();
  24. }
  25. }
  26. onAddThreshold = (index: number) => {
  27. const { thresholds } = this.state;
  28. const maxValue = 100;
  29. const minValue = 0;
  30. if (index === 0) {
  31. return;
  32. }
  33. const newThresholds = thresholds.map(threshold => {
  34. if (threshold.index >= index) {
  35. const index = threshold.index + 1;
  36. threshold = { ...threshold, index };
  37. }
  38. return threshold;
  39. });
  40. // Setting value to a value between the previous thresholds
  41. const beforeThreshold = newThresholds.filter(t => t.index === index - 1 && t.index !== 0)[0];
  42. const afterThreshold = newThresholds.filter(t => t.index === index + 1 && t.index !== 0)[0];
  43. const beforeThresholdValue = beforeThreshold !== undefined ? beforeThreshold.value : minValue;
  44. const afterThresholdValue = afterThreshold !== undefined ? afterThreshold.value : maxValue;
  45. const value = afterThresholdValue - (afterThresholdValue - beforeThresholdValue) / 2;
  46. // Set a color
  47. const color = colors.filter(c => newThresholds.some(t => t.color === c) === false)[0];
  48. this.setState(
  49. {
  50. thresholds: this.sortThresholds([
  51. ...newThresholds,
  52. {
  53. index,
  54. value: value as number,
  55. color,
  56. },
  57. ]),
  58. },
  59. () => this.onChange()
  60. );
  61. };
  62. onRemoveThreshold = (threshold: Threshold) => {
  63. if (threshold.index === 0) {
  64. return;
  65. }
  66. this.setState(
  67. prevState => {
  68. const newThresholds = prevState.thresholds.map(t => {
  69. if (t.index > threshold.index) {
  70. const index = t.index - 1;
  71. t = { ...t, index };
  72. }
  73. return t;
  74. });
  75. return {
  76. thresholds: newThresholds.filter(t => t !== threshold),
  77. };
  78. },
  79. () => this.onChange()
  80. );
  81. };
  82. onChangeThresholdValue = (event: any, threshold: Threshold) => {
  83. if (threshold.index === 0) {
  84. return;
  85. }
  86. const { thresholds } = this.state;
  87. const parsedValue = parseInt(event.target.value, 10);
  88. const value = isNaN(parsedValue) ? null : parsedValue;
  89. const newThresholds = thresholds.map(t => {
  90. if (t === threshold && t.index !== 0) {
  91. t = { ...t, value: value as number };
  92. }
  93. return t;
  94. });
  95. this.setState({ thresholds: newThresholds });
  96. };
  97. onChangeThresholdColor = (threshold: Threshold, color: string) => {
  98. const { thresholds } = this.state;
  99. const newThresholds = thresholds.map(t => {
  100. if (t === threshold) {
  101. t = { ...t, color: color };
  102. }
  103. return t;
  104. });
  105. this.setState(
  106. {
  107. thresholds: newThresholds,
  108. },
  109. () => this.onChange()
  110. );
  111. };
  112. onBlur = () => {
  113. this.setState(prevState => {
  114. const sortThresholds = this.sortThresholds([...prevState.thresholds]);
  115. let index = sortThresholds.length - 1;
  116. sortThresholds.forEach(t => {
  117. t.index = index--;
  118. });
  119. return { thresholds: sortThresholds };
  120. });
  121. this.onChange();
  122. };
  123. onChange = () => {
  124. this.props.onChange(this.state.thresholds);
  125. };
  126. sortThresholds = (thresholds: Threshold[]) => {
  127. return thresholds.sort((t1, t2) => {
  128. return t2.value - t1.value;
  129. });
  130. };
  131. renderInput = (threshold: Threshold) => {
  132. const value = threshold.index === 0 ? 'Base' : threshold.value;
  133. return (
  134. <div className="thresholds-row-input-inner">
  135. <span className="thresholds-row-input-inner-arrow" />
  136. <div className="thresholds-row-input-inner-color">
  137. {threshold.color && (
  138. <div className="thresholds-row-input-inner-color-colorpicker">
  139. <ColorPicker
  140. color={threshold.color}
  141. onChange={color => this.onChangeThresholdColor(threshold, color)}
  142. />
  143. </div>
  144. )}
  145. </div>
  146. <div className="thresholds-row-input-inner-value">
  147. <input
  148. type="text"
  149. onChange={event => this.onChangeThresholdValue(event, threshold)}
  150. value={value}
  151. onBlur={this.onBlur}
  152. readOnly={threshold.index === 0}
  153. />
  154. </div>
  155. {threshold.index > 0 && (
  156. <div className="thresholds-row-input-inner-remove" onClick={() => this.onRemoveThreshold(threshold)}>
  157. <i className="fa fa-times" />
  158. </div>
  159. )}
  160. </div>
  161. );
  162. };
  163. render() {
  164. const { thresholds } = this.state;
  165. return (
  166. <ThemeContext.Consumer>
  167. {theme => {
  168. return (
  169. <PanelOptionsGroup title="Thresholds">
  170. <div className="thresholds">
  171. {thresholds.map((threshold, index) => {
  172. return (
  173. <div className="thresholds-row" key={`${threshold.index}-${index}`}>
  174. <div
  175. className="thresholds-row-add-button"
  176. onClick={() => this.onAddThreshold(threshold.index + 1)}
  177. >
  178. <i className="fa fa-plus" />
  179. </div>
  180. <div
  181. className="thresholds-row-color-indicator"
  182. style={{ backgroundColor: getColorFromHexRgbOrName(threshold.color, theme.type) }}
  183. />
  184. <div className="thresholds-row-input">{this.renderInput(threshold)}</div>
  185. </div>
  186. );
  187. })}
  188. </div>
  189. </PanelOptionsGroup>
  190. );
  191. }}
  192. </ThemeContext.Consumer>
  193. );
  194. }
  195. }