fix(ui): do not rerender edges

This commit is contained in:
psychedelicious 2023-08-16 22:39:29 +10:00
parent 1f194e3688
commit c2e7f62701

View File

@ -2,8 +2,9 @@ import { Badge, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store'; import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens'; import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens';
import { useMemo } from 'react'; import { memo, useMemo } from 'react';
import { import {
BaseEdge, BaseEdge,
EdgeLabelRenderer, EdgeLabelRenderer,
@ -20,78 +21,165 @@ const makeEdgeSelector = (
targetHandleId: string | null | undefined, targetHandleId: string | null | undefined,
selected?: boolean selected?: boolean
) => ) =>
createSelector(stateSelector, ({ nodes }) => { createSelector(
const sourceNode = nodes.nodes.find((node) => node.id === source); stateSelector,
const targetNode = nodes.nodes.find((node) => node.id === target); ({ nodes }) => {
const sourceNode = nodes.nodes.find((node) => node.id === source);
const targetNode = nodes.nodes.find((node) => node.id === target);
const isInvocationToInvocationEdge = const isInvocationToInvocationEdge =
isInvocationNode(sourceNode) && isInvocationNode(targetNode); isInvocationNode(sourceNode) && isInvocationNode(targetNode);
const isSelected = sourceNode?.selected || targetNode?.selected || selected; const isSelected =
const sourceType = isInvocationToInvocationEdge sourceNode?.selected || targetNode?.selected || selected;
? sourceNode?.data?.outputs[sourceHandleId || '']?.type const sourceType = isInvocationToInvocationEdge
: undefined; ? sourceNode?.data?.outputs[sourceHandleId || '']?.type
: undefined;
const stroke = const stroke =
sourceType && nodes.shouldColorEdges sourceType && nodes.shouldColorEdges
? colorTokenToCssVar(FIELDS[sourceType].color) ? colorTokenToCssVar(FIELDS[sourceType].color)
: colorTokenToCssVar('base.500'); : colorTokenToCssVar('base.500');
return { return {
isSelected, isSelected,
shouldAnimate: nodes.shouldAnimateEdges && isSelected, shouldAnimate: nodes.shouldAnimateEdges && isSelected,
stroke, stroke,
}; };
}); },
defaultSelectorOptions
const CollapsedEdge = ({
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
markerEnd,
data,
selected,
source,
target,
sourceHandleId,
targetHandleId,
}: EdgeProps<{ count: number }>) => {
const selector = useMemo(
() =>
makeEdgeSelector(
source,
sourceHandleId,
target,
targetHandleId,
selected
),
[selected, source, sourceHandleId, target, targetHandleId]
); );
const { isSelected, shouldAnimate } = useAppSelector(selector); const CollapsedEdge = memo(
({
const [edgePath, labelX, labelY] = getBezierPath({
sourceX, sourceX,
sourceY, sourceY,
sourcePosition,
targetX, targetX,
targetY, targetY,
sourcePosition,
targetPosition, targetPosition,
}); markerEnd,
data,
selected,
source,
target,
sourceHandleId,
targetHandleId,
}: EdgeProps<{ count: number }>) => {
const selector = useMemo(
() =>
makeEdgeSelector(
source,
sourceHandleId,
target,
targetHandleId,
selected
),
[selected, source, sourceHandleId, target, targetHandleId]
);
const { base500 } = useChakraThemeTokens(); const { isSelected, shouldAnimate } = useAppSelector(selector);
return ( const [edgePath, labelX, labelY] = getBezierPath({
<> sourceX,
sourceY,
sourcePosition,
targetX,
targetY,
targetPosition,
});
const { base500 } = useChakraThemeTokens();
return (
<>
<BaseEdge
path={edgePath}
markerEnd={markerEnd}
style={{
strokeWidth: isSelected ? 3 : 2,
stroke: base500,
opacity: isSelected ? 0.8 : 0.5,
animation: shouldAnimate
? 'dashdraw 0.5s linear infinite'
: undefined,
strokeDasharray: shouldAnimate ? 5 : 'none',
}}
/>
{data?.count && data.count > 1 && (
<EdgeLabelRenderer>
<Flex
sx={{
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
}}
className="nodrag nopan"
>
<Badge
variant="solid"
sx={{
bg: 'base.500',
opacity: isSelected ? 0.8 : 0.5,
boxShadow: 'base',
}}
>
{data.count}
</Badge>
</Flex>
</EdgeLabelRenderer>
)}
</>
);
}
);
CollapsedEdge.displayName = 'CollapsedEdge';
const DefaultEdge = memo(
({
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
markerEnd,
selected,
source,
target,
sourceHandleId,
targetHandleId,
}: EdgeProps) => {
const selector = useMemo(
() =>
makeEdgeSelector(
source,
sourceHandleId,
target,
targetHandleId,
selected
),
[source, sourceHandleId, target, targetHandleId, selected]
);
const { isSelected, shouldAnimate, stroke } = useAppSelector(selector);
const [edgePath] = getBezierPath({
sourceX,
sourceY,
sourcePosition,
targetX,
targetY,
targetPosition,
});
return (
<BaseEdge <BaseEdge
path={edgePath} path={edgePath}
markerEnd={markerEnd} markerEnd={markerEnd}
style={{ style={{
strokeWidth: isSelected ? 3 : 2, strokeWidth: isSelected ? 3 : 2,
stroke: base500, stroke,
opacity: isSelected ? 0.8 : 0.5, opacity: isSelected ? 0.8 : 0.5,
animation: shouldAnimate animation: shouldAnimate
? 'dashdraw 0.5s linear infinite' ? 'dashdraw 0.5s linear infinite'
@ -99,83 +187,11 @@ const CollapsedEdge = ({
strokeDasharray: shouldAnimate ? 5 : 'none', strokeDasharray: shouldAnimate ? 5 : 'none',
}} }}
/> />
{data?.count && data.count > 1 && ( );
<EdgeLabelRenderer> }
<Flex );
sx={{
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
}}
className="nodrag nopan"
>
<Badge
variant="solid"
sx={{
bg: 'base.500',
opacity: isSelected ? 0.8 : 0.5,
boxShadow: 'base',
}}
>
{data.count}
</Badge>
</Flex>
</EdgeLabelRenderer>
)}
</>
);
};
const DefaultEdge = ({ DefaultEdge.displayName = 'DefaultEdge';
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
markerEnd,
selected,
source,
target,
sourceHandleId,
targetHandleId,
}: EdgeProps) => {
const selector = useMemo(
() =>
makeEdgeSelector(
source,
sourceHandleId,
target,
targetHandleId,
selected
),
[source, sourceHandleId, target, targetHandleId, selected]
);
const { isSelected, shouldAnimate, stroke } = useAppSelector(selector);
const [edgePath] = getBezierPath({
sourceX,
sourceY,
sourcePosition,
targetX,
targetY,
targetPosition,
});
return (
<BaseEdge
path={edgePath}
markerEnd={markerEnd}
style={{
strokeWidth: isSelected ? 3 : 2,
stroke,
opacity: isSelected ? 0.8 : 0.5,
animation: shouldAnimate ? 'dashdraw 0.5s linear infinite' : undefined,
strokeDasharray: shouldAnimate ? 5 : 'none',
}}
/>
);
};
export const edgeTypes = { export const edgeTypes = {
collapsed: CollapsedEdge, collapsed: CollapsedEdge,