Condition.tsx 5.1 KB

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