8563 workflow workflow node does not open on step click (#8582)

- fix multiple node selection
- fix console error
- fix close right drawer no unselect nodes
- fix edges not selectable
- fix sometime node not selected when clicking

## After


https://github.com/user-attachments/assets/ceec847f-5b7d-4452-9685-81a845bbf21e
This commit is contained in:
martmull 2024-11-19 14:51:52 +01:00 committed by GitHub
parent 86c2e9f0e4
commit 1e55010e26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 49 additions and 19 deletions

View File

@ -10,12 +10,7 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { useRef } from 'react'; import { useRef } from 'react';
import { import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
useRecoilCallback,
useRecoilState,
useRecoilValue,
useSetRecoilState,
} from 'recoil';
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
@ -24,8 +19,8 @@ import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
import { rightDrawerPageState } from '../states/rightDrawerPageState'; import { rightDrawerPageState } from '../states/rightDrawerPageState';
import { RightDrawerHotkeyScope } from '../types/RightDrawerHotkeyScope'; import { RightDrawerHotkeyScope } from '../types/RightDrawerHotkeyScope';
import { emitRightDrawerCloseEvent } from '@/ui/layout/right-drawer/utils/emitRightDrawerCloseEvent';
import { RightDrawerRouter } from './RightDrawerRouter'; import { RightDrawerRouter } from './RightDrawerRouter';
import { workflowReactFlowRefState } from '@/workflow/states/workflowReactFlowRefState';
const StyledContainer = styled(motion.div)<{ isRightDrawerMinimized: boolean }>` const StyledContainer = styled(motion.div)<{ isRightDrawerMinimized: boolean }>`
background: ${({ theme }) => theme.background.primary}; background: ${({ theme }) => theme.background.primary};
@ -94,9 +89,7 @@ export const RightDrawer = () => {
type RightDrawerAnimationVariant = keyof typeof animationVariants; type RightDrawerAnimationVariant = keyof typeof animationVariants;
const [isRightDrawerOpen, setIsRightDrawerOpen] = useRecoilState( const isRightDrawerOpen = useRecoilValue(isRightDrawerOpenState);
isRightDrawerOpenState,
);
const isRightDrawerMinimized = useRecoilValue(isRightDrawerMinimizedState); const isRightDrawerMinimized = useRecoilValue(isRightDrawerMinimizedState);
@ -109,13 +102,17 @@ export const RightDrawer = () => {
const { closeRightDrawer } = useRightDrawer(); const { closeRightDrawer } = useRightDrawer();
const rightDrawerRef = useRef<HTMLDivElement>(null); const rightDrawerRef = useRef<HTMLDivElement>(null);
const workflowReactFlowRef = useRecoilValue(workflowReactFlowRefState);
const { useListenClickOutside } = useClickOutsideListener( const { useListenClickOutside } = useClickOutsideListener(
RIGHT_DRAWER_CLICK_OUTSIDE_LISTENER_ID, RIGHT_DRAWER_CLICK_OUTSIDE_LISTENER_ID,
); );
useListenClickOutside({ useListenClickOutside({
refs: [rightDrawerRef], refs: [
rightDrawerRef,
...(workflowReactFlowRef ? [workflowReactFlowRef] : []),
],
callback: useRecoilCallback( callback: useRecoilCallback(
({ snapshot, set }) => ({ snapshot, set }) =>
(event) => { (event) => {
@ -128,7 +125,6 @@ export const RightDrawer = () => {
if (isRightDrawerOpen && !isRightDrawerMinimized) { if (isRightDrawerOpen && !isRightDrawerMinimized) {
set(rightDrawerCloseEventState, event); set(rightDrawerCloseEventState, event);
emitRightDrawerCloseEvent();
closeRightDrawer(); closeRightDrawer();
} }
@ -141,10 +137,12 @@ export const RightDrawer = () => {
useScopedHotkeys( useScopedHotkeys(
[Key.Escape], [Key.Escape],
() => { () => {
closeRightDrawer(); if (isRightDrawerOpen && !isRightDrawerMinimized) {
closeRightDrawer();
}
}, },
RightDrawerHotkeyScope.RightDrawer, RightDrawerHotkeyScope.RightDrawer,
[setIsRightDrawerOpen], [isRightDrawerOpen, isRightDrawerMinimized],
); );
const isMobile = useIsMobile(); const isMobile = useIsMobile();

View File

@ -6,6 +6,7 @@ import { rightDrawerCloseEventState } from '@/ui/layout/right-drawer/states/righ
import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState'; import { isRightDrawerOpenState } from '../states/isRightDrawerOpenState';
import { rightDrawerPageState } from '../states/rightDrawerPageState'; import { rightDrawerPageState } from '../states/rightDrawerPageState';
import { RightDrawerPages } from '../types/RightDrawerPages'; import { RightDrawerPages } from '../types/RightDrawerPages';
import { emitRightDrawerCloseEvent } from '@/ui/layout/right-drawer/utils/emitRightDrawerCloseEvent';
export const useRightDrawer = () => { export const useRightDrawer = () => {
const isRightDrawerOpen = useRecoilValue(isRightDrawerOpenState); const isRightDrawerOpen = useRecoilValue(isRightDrawerOpenState);
@ -28,6 +29,7 @@ export const useRightDrawer = () => {
() => { () => {
set(isRightDrawerOpenState, false); set(isRightDrawerOpenState, false);
set(isRightDrawerMinimizedState, false); set(isRightDrawerMinimizedState, false);
emitRightDrawerCloseEvent();
}, },
[], [],
); );

View File

@ -28,6 +28,8 @@ import '@xyflow/react/dist/style.css';
import React, { useEffect, useMemo, useRef } from 'react'; import React, { useEffect, useMemo, useRef } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilValue, useSetRecoilState } from 'recoil';
import { GRAY_SCALE, isDefined, THEME_COMMON } from 'twenty-ui'; import { GRAY_SCALE, isDefined, THEME_COMMON } from 'twenty-ui';
import { useListenRightDrawerClose } from '@/ui/layout/right-drawer/hooks/useListenRightDrawerClose';
import { workflowReactFlowRefState } from '@/workflow/states/workflowReactFlowRefState';
const StyledResetReactflowStyles = styled.div` const StyledResetReactflowStyles = styled.div`
height: 100%; height: 100%;
@ -86,6 +88,9 @@ export const WorkflowDiagramCanvasBase = ({
children?: React.ReactNode; children?: React.ReactNode;
}) => { }) => {
const reactflow = useReactFlow(); const reactflow = useReactFlow();
const setWorkflowReactFlowRefState = useSetRecoilState(
workflowReactFlowRefState,
);
const { nodes, edges } = useMemo( const { nodes, edges } = useMemo(
() => getOrganizedDiagram(diagram), () => getOrganizedDiagram(diagram),
@ -144,6 +149,12 @@ export const WorkflowDiagramCanvasBase = ({
}); });
}; };
useListenRightDrawerClose(() => {
reactflow.setNodes((nodes) =>
nodes.map((node) => ({ ...node, selected: false })),
);
});
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
@ -176,6 +187,11 @@ export const WorkflowDiagramCanvasBase = ({
return ( return (
<StyledResetReactflowStyles ref={containerRef}> <StyledResetReactflowStyles ref={containerRef}>
<ReactFlow <ReactFlow
ref={(node) => {
if (isDefined(node)) {
setWorkflowReactFlowRefState({ current: node });
}
}}
onInit={() => { onInit={() => {
if (!isDefined(containerRef.current)) { if (!isDefined(containerRef.current)) {
throw new Error('Expect the container ref to be defined'); throw new Error('Expect the container ref to be defined');
@ -192,11 +208,16 @@ export const WorkflowDiagramCanvasBase = ({
minZoom={defaultFitViewOptions.minZoom} minZoom={defaultFitViewOptions.minZoom}
maxZoom={defaultFitViewOptions.maxZoom} maxZoom={defaultFitViewOptions.maxZoom}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
nodes={nodes.map((node) => ({ ...node, draggable: false }))} nodes={nodes}
edges={edges} edges={edges}
onNodesChange={handleNodesChange} onNodesChange={handleNodesChange}
onEdgesChange={handleEdgesChange} onEdgesChange={handleEdgesChange}
proOptions={{ hideAttribution: true }} proOptions={{ hideAttribution: true }}
multiSelectionKeyCode={null}
nodesFocusable={false}
edgesFocusable={false}
nodesDraggable={false}
paneClickDistance={10} // Fix small unwanted user dragging does not select node
> >
<Background color={GRAY_SCALE.gray25} size={2} /> <Background color={GRAY_SCALE.gray25} size={2} />

View File

@ -1,3 +1,5 @@
import { ReactNode, Fragment } from 'react';
import styled from '@emotion/styled';
import { useGetManyServerlessFunctions } from '@/settings/serverless-functions/hooks/useGetManyServerlessFunctions'; import { useGetManyServerlessFunctions } from '@/settings/serverless-functions/hooks/useGetManyServerlessFunctions';
import { Select, SelectOption } from '@/ui/input/components/Select'; import { Select, SelectOption } from '@/ui/input/components/Select';
import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase'; import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
@ -8,8 +10,6 @@ import { getDefaultFunctionInputFromInputSchema } from '@/workflow/utils/getDefa
import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/utils/mergeDefaultFunctionInputAndFunctionInput'; import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/utils/mergeDefaultFunctionInputAndFunctionInput';
import { setNestedValue } from '@/workflow/utils/setNestedValue'; import { setNestedValue } from '@/workflow/utils/setNestedValue';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { ReactNode } from 'react';
import { HorizontalSeparator, IconCode, isDefined } from 'twenty-ui'; import { HorizontalSeparator, IconCode, isDefined } from 'twenty-ui';
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
@ -165,12 +165,12 @@ export const WorkflowEditActionFormServerlessFunction = ({
if (inputValue !== null && typeof inputValue === 'object') { if (inputValue !== null && typeof inputValue === 'object') {
if (isRoot) { if (isRoot) {
return ( return (
<> <Fragment key={pathKey}>
{displaySeparator(functionInput) && ( {displaySeparator(functionInput) && (
<HorizontalSeparator noMargin /> <HorizontalSeparator noMargin />
)} )}
{renderFields(inputValue, currentPath, false)} {renderFields(inputValue, currentPath, false)}
</> </Fragment>
); );
} }
return ( return (

View File

@ -0,0 +1,8 @@
import { createState } from 'twenty-ui';
import { RefObject } from 'react';
export const workflowReactFlowRefState =
createState<RefObject<HTMLDivElement> | null>({
key: 'workflowReactFlowRefState',
defaultValue: null,
});

View File

@ -70,6 +70,7 @@ export const generateWorkflowDiagram = ({
type: MarkerType.ArrowClosed, type: MarkerType.ArrowClosed,
}, },
deletable: false, deletable: false,
selectable: false,
}); });
return nodeId; return nodeId;