ThresholdsEditor.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import React, { PureComponent } from 'react';
  2. import tinycolor, { ColorInput } from 'tinycolor2';
  3. import { Threshold, BasicGaugeColor } from '../../types';
  4. import { ColorPicker } from '../ColorPicker/ColorPicker';
  5. import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
  6. export interface Props {
  7. thresholds: Threshold[];
  8. onChange: (thresholds: Threshold[]) => void;
  9. }
  10. interface State {
  11. thresholds: Threshold[];
  12. baseColor: string;
  13. }
  14. export class ThresholdsEditor extends PureComponent<Props, State> {
  15. constructor(props: Props) {
  16. super(props);
  17. this.state = { thresholds: props.thresholds, baseColor: BasicGaugeColor.Green };
  18. }
  19. onAddThreshold = (index: number) => {
  20. const maxValue = 100; // hardcoded for now before we add the base threshold
  21. const minValue = 0; // hardcoded for now before we add the base threshold
  22. const { thresholds } = this.state;
  23. const newThresholds = thresholds.map(threshold => {
  24. if (threshold.index >= index) {
  25. threshold = {
  26. ...threshold,
  27. index: threshold.index + 1,
  28. };
  29. }
  30. return threshold;
  31. });
  32. // Setting value to a value between the previous thresholds
  33. let value;
  34. if (index === 0 && thresholds.length === 0) {
  35. value = maxValue - (maxValue - minValue) / 2;
  36. } else if (index === 0 && thresholds.length > 0) {
  37. value = newThresholds[index + 1].value - (newThresholds[index + 1].value - minValue) / 2;
  38. } else if (index > newThresholds[newThresholds.length - 1].index) {
  39. value = maxValue - (maxValue - newThresholds[index - 1].value) / 2;
  40. }
  41. // Set a color that lies between the previous thresholds
  42. let color;
  43. if (index === 0 && thresholds.length === 0) {
  44. color = tinycolor.mix(BasicGaugeColor.Green, BasicGaugeColor.Red, 50).toRgbString();
  45. } else {
  46. color = tinycolor.mix(thresholds[index - 1].color as ColorInput, BasicGaugeColor.Red, 50).toRgbString();
  47. }
  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.updateGauge()
  60. );
  61. };
  62. onRemoveThreshold = (threshold: Threshold) => {
  63. this.setState(
  64. prevState => ({ thresholds: prevState.thresholds.filter(t => t !== threshold) }),
  65. () => this.updateGauge()
  66. );
  67. };
  68. onChangeThresholdValue = (event: any, threshold: Threshold) => {
  69. const { thresholds } = this.state;
  70. const newThresholds = thresholds.map(t => {
  71. if (t === threshold) {
  72. t = { ...t, value: event.target.value };
  73. }
  74. return t;
  75. });
  76. this.setState({ thresholds: newThresholds });
  77. };
  78. onChangeThresholdColor = (threshold: Threshold, color: string) => {
  79. const { thresholds } = this.state;
  80. const newThresholds = thresholds.map(t => {
  81. if (t === threshold) {
  82. t = { ...t, color: color };
  83. }
  84. return t;
  85. });
  86. this.setState(
  87. {
  88. thresholds: newThresholds,
  89. },
  90. () => this.updateGauge()
  91. );
  92. };
  93. onChangeBaseColor = (color: string) => this.props.onChange(this.state.thresholds);
  94. onBlur = () => {
  95. this.setState(prevState => ({ thresholds: this.sortThresholds(prevState.thresholds) }));
  96. this.updateGauge();
  97. };
  98. updateGauge = () => {
  99. this.props.onChange(this.state.thresholds);
  100. };
  101. sortThresholds = (thresholds: Threshold[]) => {
  102. return thresholds.sort((t1, t2) => {
  103. return t2.value - t1.value;
  104. });
  105. };
  106. renderThresholds() {
  107. const { thresholds } = this.state;
  108. return thresholds.map((threshold, index) => {
  109. return (
  110. <div className="threshold-row" key={`${threshold.index}-${index}`}>
  111. <div className="threshold-row-inner">
  112. <div className="threshold-row-color">
  113. {threshold.color && (
  114. <div className="threshold-row-color-inner">
  115. <ColorPicker
  116. color={threshold.color}
  117. onChange={color => this.onChangeThresholdColor(threshold, color)}
  118. />
  119. </div>
  120. )}
  121. </div>
  122. <input
  123. className="threshold-row-input"
  124. type="text"
  125. onChange={event => this.onChangeThresholdValue(event, threshold)}
  126. value={threshold.value}
  127. onBlur={this.onBlur}
  128. />
  129. <div onClick={() => this.onRemoveThreshold(threshold)} className="threshold-row-remove">
  130. <i className="fa fa-times" />
  131. </div>
  132. </div>
  133. </div>
  134. );
  135. });
  136. }
  137. renderIndicator() {
  138. const { thresholds } = this.state;
  139. return thresholds.map((t, i) => {
  140. return (
  141. <div key={`${t.value}-${i}`} className="indicator-section">
  142. <div onClick={() => this.onAddThreshold(t.index + 1)} style={{ height: '50%', backgroundColor: t.color }} />
  143. <div onClick={() => this.onAddThreshold(t.index)} style={{ height: '50%', backgroundColor: t.color }} />
  144. </div>
  145. );
  146. });
  147. }
  148. renderBaseIndicator() {
  149. return (
  150. <div className="indicator-section" style={{ height: '100%' }}>
  151. <div
  152. onClick={() => this.onAddThreshold(0)}
  153. style={{ height: '100%', backgroundColor: BasicGaugeColor.Green }}
  154. />
  155. </div>
  156. );
  157. }
  158. renderBase() {
  159. const baseColor = BasicGaugeColor.Green;
  160. return (
  161. <div className="threshold-row threshold-row-base">
  162. <div className="threshold-row-inner threshold-row-inner--base">
  163. <div className="threshold-row-color">
  164. <div className="threshold-row-color-inner">
  165. <ColorPicker color={baseColor} onChange={color => this.onChangeBaseColor(color)} />
  166. </div>
  167. </div>
  168. <div className="threshold-row-label">Base</div>
  169. </div>
  170. </div>
  171. );
  172. }
  173. render() {
  174. return (
  175. <PanelOptionsGroup title="Thresholds">
  176. <div className="thresholds">
  177. <div className="color-indicators">
  178. {this.renderIndicator()}
  179. {this.renderBaseIndicator()}
  180. </div>
  181. <div className="threshold-rows">
  182. {this.renderThresholds()}
  183. {this.renderBase()}
  184. </div>
  185. </div>
  186. </PanelOptionsGroup>
  187. );
  188. }
  189. }