Condition.tsx 4.7 KB

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