You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

categorize-node.tsx 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import { useTranslate } from '@/hooks/common-hooks';
  2. import { Flex } from 'antd';
  3. import classNames from 'classnames';
  4. import { pick } from 'lodash';
  5. import get from 'lodash/get';
  6. import intersectionWith from 'lodash/intersectionWith';
  7. import isEqual from 'lodash/isEqual';
  8. import lowerFirst from 'lodash/lowerFirst';
  9. import { useEffect, useMemo, useState } from 'react';
  10. import { Handle, NodeProps, Position, useUpdateNodeInternals } from 'reactflow';
  11. import { Operator, operatorMap } from '../../constant';
  12. import { IPosition, NodeData } from '../../interface';
  13. import OperatorIcon from '../../operator-icon';
  14. import { buildNewPositionMap } from '../../utils';
  15. import CategorizeHandle from './categorize-handle';
  16. import NodeDropdown from './dropdown';
  17. import styles from './index.less';
  18. import NodePopover from './popover';
  19. export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
  20. const updateNodeInternals = useUpdateNodeInternals();
  21. const [postionMap, setPositionMap] = useState<Record<string, IPosition>>({});
  22. const categoryData = useMemo(
  23. () => get(data, 'form.category_description') ?? {},
  24. [data],
  25. );
  26. const style = operatorMap[data.label as Operator];
  27. const { t } = useTranslate('flow');
  28. useEffect(() => {
  29. // Cache used coordinates
  30. setPositionMap((state) => {
  31. // index in use
  32. const indexesInUse = Object.values(state).map((x) => x.idx);
  33. const categoryDataKeys = Object.keys(categoryData);
  34. const stateKeys = Object.keys(state);
  35. if (!isEqual(categoryDataKeys.sort(), stateKeys.sort())) {
  36. const intersectionKeys = intersectionWith(
  37. stateKeys,
  38. categoryDataKeys,
  39. (categoryDataKey, postionMapKey) => categoryDataKey === postionMapKey,
  40. );
  41. const newPositionMap = buildNewPositionMap(
  42. categoryDataKeys.filter(
  43. (x) => !intersectionKeys.some((y) => y === x),
  44. ),
  45. indexesInUse,
  46. );
  47. const nextPostionMap = {
  48. ...pick(state, intersectionKeys),
  49. ...newPositionMap,
  50. };
  51. return nextPostionMap;
  52. }
  53. return state;
  54. });
  55. }, [categoryData]);
  56. useEffect(() => {
  57. updateNodeInternals(id);
  58. }, [id, updateNodeInternals, postionMap]);
  59. return (
  60. <NodePopover nodeId={id}>
  61. <section
  62. className={classNames(styles.ragNode, {
  63. [styles.selectedNode]: selected,
  64. })}
  65. style={{
  66. backgroundColor: style.backgroundColor,
  67. color: style.color,
  68. }}
  69. >
  70. <Handle
  71. type="target"
  72. position={Position.Left}
  73. isConnectable
  74. className={styles.handle}
  75. id={'a'}
  76. ></Handle>
  77. <Handle
  78. type="target"
  79. position={Position.Top}
  80. isConnectable
  81. className={styles.handle}
  82. id={'b'}
  83. ></Handle>
  84. <Handle
  85. type="target"
  86. position={Position.Bottom}
  87. isConnectable
  88. className={styles.handle}
  89. id={'c'}
  90. ></Handle>
  91. {Object.keys(categoryData).map((x, idx) => {
  92. const position = postionMap[x];
  93. return (
  94. position && (
  95. <CategorizeHandle
  96. top={position.top}
  97. right={position.right}
  98. key={idx}
  99. text={x}
  100. idx={idx}
  101. ></CategorizeHandle>
  102. )
  103. );
  104. })}
  105. <Flex vertical align="center" justify="center" gap={6}>
  106. <OperatorIcon
  107. name={data.label as Operator}
  108. fontSize={24}
  109. ></OperatorIcon>
  110. <span className={styles.type}>{t(lowerFirst(data.label))}</span>
  111. <NodeDropdown id={id}></NodeDropdown>
  112. </Flex>
  113. <section className={styles.bottomBox}>
  114. <div className={styles.nodeName}>{data.name}</div>
  115. </section>
  116. </section>
  117. </NodePopover>
  118. );
  119. }