Condition.tsx 5.3 KB

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