Condition.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. const conditionValueWithNoSpace = conditionValue.replaceAll(' ', '');
  77. switch (type) {
  78. case 'int':
  79. isLegal = isIn
  80. ? regIntInterval.test(conditionValueWithNoSpace)
  81. : regInt.test(conditionValueWithNoSpace);
  82. break;
  83. case 'float':
  84. isLegal = isIn
  85. ? regFloatInterval.test(conditionValueWithNoSpace)
  86. : regFloat.test(conditionValueWithNoSpace);
  87. break;
  88. default:
  89. isLegal = false;
  90. break;
  91. }
  92. setIsValueLegal(isLegal);
  93. triggerChange(id, {
  94. field: conditionField,
  95. op: operator,
  96. value: conditionValue,
  97. isCorrect: isLegal,
  98. id,
  99. });
  100. // No need for 'id' and 'triggerChange'.
  101. // eslint-disable-next-line react-hooks/exhaustive-deps
  102. }, [conditionField, operator, conditionValue]);
  103. const classes = useStyles();
  104. // Logic operator input change.
  105. const handleOpChange = (event: React.ChangeEvent<{ value: unknown }>) => {
  106. setOperator(event.target.value);
  107. };
  108. // Field Name input change.
  109. const handleFieldNameChange = (
  110. event: React.ChangeEvent<{ value: unknown }>
  111. ) => {
  112. const value = event.target.value;
  113. const target = fields.find(field => field.name === value);
  114. target && setConditionField(target);
  115. };
  116. // Value input change.
  117. const handleValueChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  118. const value = event.target.value;
  119. setConditionValue(value);
  120. };
  121. return (
  122. <div className={`${classes.wrapper} ${className}`} {...others}>
  123. <CustomSelector
  124. label="Field Name"
  125. value={conditionField?.name}
  126. onChange={handleFieldNameChange}
  127. options={fields.map(i => ({ value: i.name, label: i.name }))}
  128. variant="filled"
  129. wrapperClass={classes.fieldName}
  130. />
  131. <CustomSelector
  132. label="Logic"
  133. value={operator}
  134. onChange={handleOpChange}
  135. options={LogicalOperators}
  136. variant="filled"
  137. wrapperClass={classes.logic}
  138. />
  139. <TextField
  140. className={classes.value}
  141. label="Value"
  142. variant="filled"
  143. // size="small"
  144. onChange={handleValueChange}
  145. value={conditionValue}
  146. error={!isValuelegal}
  147. />
  148. <IconButton
  149. aria-label="close"
  150. className={classes.closeButton}
  151. onClick={onDelete}
  152. size="small"
  153. >
  154. <CloseIcon />
  155. </IconButton>
  156. </div>
  157. );
  158. };
  159. Condition.displayName = 'Condition';
  160. const useStyles = makeStyles((theme: Theme) =>
  161. createStyles({
  162. root: {},
  163. wrapper: {
  164. minWidth: '466px',
  165. minHeight: '62px',
  166. background: '#FFFFFF',
  167. padding: '12px 16px',
  168. display: 'flex',
  169. flexDirection: 'row',
  170. alignItems: 'center',
  171. },
  172. closeButton: {},
  173. fieldName: {
  174. minHeight: '38px',
  175. minWidth: '130px',
  176. },
  177. logic: { minHeight: '38px', minWidth: '70px', margin: '0 24px' },
  178. value: { minHeight: '38px', minWidth: '130px' },
  179. })
  180. );
  181. export default Condition;