Thresholds.tsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. import React, { PureComponent } from 'react';
  2. import classNames from 'classnames/bind';
  3. import { ColorPicker } from 'app/core/components/colorpicker/ColorPicker';
  4. import { OptionModuleProps } from './module';
  5. import { Threshold } from 'app/types';
  6. interface State {
  7. thresholds: Threshold[];
  8. }
  9. enum BasicGaugeColor {
  10. Green = 'rgba(50, 172, 45, 0.97)',
  11. Orange = 'rgba(237, 129, 40, 0.89)',
  12. Red = 'rgb(212, 74, 58)',
  13. }
  14. export default class Thresholds extends PureComponent<OptionModuleProps, State> {
  15. constructor(props) {
  16. super(props);
  17. this.state = {
  18. thresholds: this.props.options.thresholds || [
  19. { index: 0, label: 'Min', value: 0, canRemove: false, color: BasicGaugeColor.Green },
  20. { index: 1, label: 'Max', value: 100, canRemove: false },
  21. ],
  22. };
  23. }
  24. onAddThreshold = index => {
  25. const { thresholds } = this.state;
  26. const newThresholds = thresholds.map(threshold => {
  27. if (threshold.index >= index) {
  28. threshold = { ...threshold, index: threshold.index + 1 };
  29. }
  30. return threshold;
  31. });
  32. // Setting value to a value between the new threshold.
  33. const value = newThresholds[index].value - (newThresholds[index].value - newThresholds[index - 1].value) / 2;
  34. this.setState(
  35. {
  36. thresholds: this.sortThresholds([
  37. ...newThresholds,
  38. { index: index, label: '', value: value, canRemove: true, color: BasicGaugeColor.Orange },
  39. ]),
  40. },
  41. () => this.updateGauge()
  42. );
  43. };
  44. onRemoveThreshold = threshold => {
  45. this.setState(
  46. prevState => ({
  47. thresholds: prevState.thresholds.filter(t => t !== threshold),
  48. }),
  49. () => this.updateGauge()
  50. );
  51. };
  52. onChangeThresholdValue = (event, threshold) => {
  53. const { thresholds } = this.state;
  54. const newThresholds = thresholds.map(t => {
  55. if (t === threshold) {
  56. t = { ...t, value: event.target.value };
  57. }
  58. return t;
  59. });
  60. this.setState({
  61. thresholds: newThresholds,
  62. });
  63. };
  64. onChangeThresholdColor = (threshold, color) => {
  65. const { thresholds } = this.state;
  66. const newThresholds = thresholds.map(t => {
  67. if (t === threshold) {
  68. t = { ...t, color: color };
  69. }
  70. return t;
  71. });
  72. this.setState(
  73. {
  74. thresholds: newThresholds,
  75. },
  76. () => this.updateGauge()
  77. );
  78. };
  79. onBlur = () => {
  80. this.setState(prevState => ({
  81. thresholds: this.sortThresholds(prevState.thresholds),
  82. }));
  83. this.updateGauge();
  84. };
  85. updateGauge = () => {
  86. this.props.onChange({ ...this.props.options, thresholds: this.state.thresholds });
  87. };
  88. sortThresholds = thresholds => {
  89. return thresholds.sort((t1, t2) => {
  90. return t1.value - t2.value;
  91. });
  92. };
  93. getIndicatorColor = index => {
  94. const { thresholds } = this.state;
  95. if (index === 0) {
  96. return thresholds[0].color;
  97. }
  98. return index < thresholds.length ? thresholds[index].color : BasicGaugeColor.Red;
  99. };
  100. renderNoThresholds() {
  101. const { thresholds } = this.state;
  102. const min = thresholds[0];
  103. const max = thresholds[1];
  104. return [
  105. <div className="threshold-row threshold-row-min" key="min">
  106. <div className="threshold-row-inner">
  107. <div className="threshold-row-color">
  108. <div className="threshold-row-color-inner">
  109. <ColorPicker color={min.color} onChange={color => this.onChangeThresholdColor(min, color)} />
  110. </div>
  111. </div>
  112. <input
  113. className="threshold-row-input"
  114. onBlur={this.onBlur}
  115. onChange={event => this.onChangeThresholdValue(event, min)}
  116. value={min.value}
  117. />
  118. <div className="threshold-row-label">{min.label}</div>
  119. </div>
  120. </div>,
  121. <div className="threshold-row" key="add">
  122. <div className="threshold-row-inner">
  123. <div onClick={() => this.onAddThreshold(1)} className="threshold-row-add">
  124. <i className="fa fa-plus" />
  125. </div>
  126. <div className="threshold-row-add-label">Add new threshold by clicking the line.</div>
  127. </div>
  128. </div>,
  129. <div className="threshold-row threshold-row-max" key="max">
  130. <div className="threshold-row-inner">
  131. <div className="threshold-row-color" />
  132. <input
  133. className="threshold-row-input"
  134. onBlur={this.onBlur}
  135. onChange={event => this.onChangeThresholdValue(event, max)}
  136. value={max.value}
  137. />
  138. <div className="threshold-row-label">{max.label}</div>
  139. </div>
  140. </div>,
  141. ];
  142. }
  143. renderThresholds() {
  144. const { thresholds } = this.state;
  145. return thresholds.map((threshold, index) => {
  146. const rowStyle = classNames({
  147. 'threshold-row': true,
  148. 'threshold-row-min': index === 0,
  149. 'threshold-row-max': index === thresholds.length - 1,
  150. });
  151. return (
  152. <div className={rowStyle} key={`${threshold.index}-${index}`}>
  153. <div className="threshold-row-inner">
  154. <div className="threshold-row-color">
  155. {threshold.color && (
  156. <div className="threshold-row-color-inner">
  157. <ColorPicker
  158. color={threshold.color}
  159. onChange={color => this.onChangeThresholdColor(threshold, color)}
  160. />
  161. </div>
  162. )}
  163. </div>
  164. <input
  165. className="threshold-row-input"
  166. type="text"
  167. onChange={event => this.onChangeThresholdValue(event, threshold)}
  168. value={threshold.value}
  169. onBlur={this.onBlur}
  170. />
  171. {threshold.canRemove ? (
  172. <div onClick={() => this.onRemoveThreshold(threshold)} className="threshold-row-remove">
  173. <i className="fa fa-times" />
  174. </div>
  175. ) : (
  176. <div className="threshold-row-label">{threshold.label}</div>
  177. )}
  178. </div>
  179. </div>
  180. );
  181. });
  182. }
  183. insertAtIndex(index) {
  184. const { thresholds } = this.state;
  185. // If thresholds.length is greater or equal to 3
  186. // it means a user has added one threshold
  187. if (thresholds.length < 3 || index < 0) {
  188. return 1;
  189. }
  190. return index;
  191. }
  192. renderIndicatorSection(index) {
  193. const { thresholds } = this.state;
  194. const indicators = thresholds.length - 1;
  195. if (index === 0 || index === thresholds.length) {
  196. return (
  197. <div
  198. key={index}
  199. className="indicator-section"
  200. style={{
  201. height: `calc(100%/${indicators})`,
  202. }}
  203. >
  204. <div
  205. onClick={() => this.onAddThreshold(this.insertAtIndex(index - 1))}
  206. style={{
  207. height: '100%',
  208. background: this.getIndicatorColor(index),
  209. }}
  210. />
  211. </div>
  212. );
  213. }
  214. return (
  215. <div
  216. key={index}
  217. className="indicator-section"
  218. style={{
  219. height: `calc(100%/${indicators})`,
  220. }}
  221. >
  222. <div
  223. onClick={() => this.onAddThreshold(this.insertAtIndex(index))}
  224. style={{
  225. height: '50%',
  226. background: this.getIndicatorColor(index),
  227. }}
  228. />
  229. <div
  230. onClick={() => this.onAddThreshold(this.insertAtIndex(index + 1))}
  231. style={{
  232. height: `50%`,
  233. background: this.getIndicatorColor(index),
  234. }}
  235. />
  236. </div>
  237. );
  238. }
  239. renderIndicator() {
  240. const { thresholds } = this.state;
  241. return thresholds.map((t, i) => {
  242. if (i <= thresholds.length - 1) {
  243. return this.renderIndicatorSection(i);
  244. }
  245. return null;
  246. });
  247. }
  248. render() {
  249. const { thresholds } = this.state;
  250. return (
  251. <div className="section gf-form-group">
  252. <h5 className="page-heading">Thresholds</h5>
  253. <div className="thresholds">
  254. <div className="color-indicators">{this.renderIndicator()}</div>
  255. <div className="threshold-rows">
  256. {thresholds.length > 2 ? this.renderThresholds() : this.renderNoThresholds()}
  257. </div>
  258. </div>
  259. </div>
  260. );
  261. }
  262. }