Condition.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import React, { useState, useEffect, FC } from 'react';
  2. import {
  3. makeStyles,
  4. Theme,
  5. createStyles,
  6. IconButton,
  7. TextField,
  8. } from '@material-ui/core';
  9. import CloseIcon from '@material-ui/icons/Close';
  10. import { ConditionProps, Field } from './Types';
  11. import CustomSelector from '../customSelector/CustomSelector';
  12. // Todo: Move to corrsponding Constant file.
  13. // Static logical operators.
  14. const LogicalOperators = [
  15. {
  16. value: '<',
  17. label: '<',
  18. },
  19. {
  20. value: '<=',
  21. label: '<=',
  22. },
  23. {
  24. value: '>',
  25. label: '>',
  26. },
  27. {
  28. value: '>=',
  29. label: '>=',
  30. },
  31. {
  32. value: '==',
  33. label: '==',
  34. },
  35. {
  36. value: '!=',
  37. label: '!=',
  38. },
  39. {
  40. value: 'in',
  41. label: 'in',
  42. },
  43. ];
  44. const Condition: FC<ConditionProps> = props => {
  45. const {
  46. onDelete,
  47. triggerChange,
  48. fields = [],
  49. id = '',
  50. initData,
  51. className = '',
  52. ...others
  53. } = props;
  54. const [operator, setOperator] = useState(
  55. initData?.op || LogicalOperators[0].value
  56. );
  57. const [conditionField, setConditionField] = useState<Field | any>(
  58. initData?.field || fields[0] || {}
  59. );
  60. const [conditionValue, setConditionValue] = useState(initData?.value || '');
  61. const [isValuelegal, setIsValueLegal] = useState(
  62. initData?.isCorrect || false
  63. );
  64. /**
  65. * Check condition's value by field's and operator's type.
  66. * Trigger condition change event.
  67. */
  68. useEffect(() => {
  69. const regInt = /^\d+$/;
  70. const regFloat = /^\d+\.\d+$/;
  71. const regIntInterval = /^\[\d+,\d+\]$/;
  72. const regFloatInterval = /^\[\d+\.\d+,\d+\.\d+]$/;
  73. const type = conditionField?.type;
  74. const isIn = operator === 'in';
  75. let isLegal = false;
  76. switch (type) {
  77. case 'int':
  78. isLegal = isIn
  79. ? regIntInterval.test(conditionValue)
  80. : regInt.test(conditionValue);
  81. break;
  82. case 'float':
  83. isLegal = isIn
  84. ? regFloatInterval.test(conditionValue)
  85. : regFloat.test(conditionValue);
  86. break;
  87. default:
  88. isLegal = false;
  89. break;
  90. }
  91. setIsValueLegal(isLegal);
  92. triggerChange(id, {
  93. field: conditionField,
  94. op: operator,
  95. value: conditionValue,
  96. isCorrect: isLegal,
  97. id,
  98. });
  99. // No need for 'id' and 'triggerChange'.
  100. // eslint-disable-next-line react-hooks/exhaustive-deps
  101. }, [conditionField, operator, conditionValue]);
  102. const classes = useStyles();
  103. // Logic operator input change.
  104. const handleOpChange = (event: React.ChangeEvent<{ value: unknown }>) => {
  105. setOperator(event.target.value);
  106. };
  107. // Field Name input change.
  108. const handleFieldNameChange = (
  109. event: React.ChangeEvent<{ value: unknown }>
  110. ) => {
  111. const value = event.target.value;
  112. const target = fields.find(field => field.name === value);
  113. target && setConditionField(target);
  114. };
  115. // Value input change.
  116. const handleValueChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  117. const value = event.target.value;
  118. setConditionValue(value);
  119. };
  120. return (
  121. <div className={`${classes.wrapper} ${className}`} {...others}>
  122. <CustomSelector
  123. label="Field Name"
  124. value={conditionField?.name}
  125. onChange={handleFieldNameChange}
  126. options={fields.map(i => ({ value: i.name, label: i.name }))}
  127. variant="filled"
  128. wrapperClass={classes.fieldName}
  129. />
  130. <CustomSelector
  131. label="Logic"
  132. value={operator}
  133. onChange={handleOpChange}
  134. options={LogicalOperators}
  135. variant="filled"
  136. wrapperClass={classes.logic}
  137. />
  138. <TextField
  139. className={classes.value}
  140. label="Value"
  141. variant="filled"
  142. // size="small"
  143. onChange={handleValueChange}
  144. value={conditionValue}
  145. error={!isValuelegal}
  146. />
  147. <IconButton
  148. aria-label="close"
  149. className={classes.closeButton}
  150. onClick={onDelete}
  151. size="small"
  152. >
  153. <CloseIcon />
  154. </IconButton>
  155. </div>
  156. );
  157. };
  158. Condition.displayName = 'Condition';
  159. const useStyles = makeStyles((theme: Theme) =>
  160. createStyles({
  161. root: {},
  162. wrapper: {
  163. minWidth: '466px',
  164. minHeight: '62px',
  165. background: '#FFFFFF',
  166. padding: '12px 16px',
  167. display: 'flex',
  168. flexDirection: 'row',
  169. alignItems: 'center',
  170. },
  171. closeButton: {},
  172. fieldName: {
  173. minHeight: '38px',
  174. minWidth: '130px',
  175. },
  176. logic: { minHeight: '38px', minWidth: '70px', margin: '0 24px' },
  177. value: { minHeight: '38px', minWidth: '130px' },
  178. })
  179. );
  180. export default Condition;